-2

I have looked at most of the available Tkinter ScrolledText StackOverflow posts including descriptions of inheritance found : Inheritance Tutorial and Inheritance with Classes. Yet, I cannot seem to understand in this specific example, why I get the following error:

    textBoxClass(self.parent).textDropIn(self.parent).insert(tk.INSERT,"This is the text to add.")
AttributeError: 'NoneType' object has no attribute 'insert'

I understand that I do not have the attribute of 'insert' present, but I do not understand why the textDropIn function does not have the attributes from ScrolledText based on my class definition call of class textBoxClass(tkst.ScrolledText):, however I suspect it is improper instantiation (?) that is the reason why the inheritance of the ScrolledText attributes are not available in the function.

Another part of me suspects that I have to inherit attributes from ScrolledText within the someGui class in order to call them in the class methods, but I am not sure.

Full code:

from tkinter import *
from tkinter import ttk
import tkinter as tk
import tkinter.scrolledtext as tkst


class someGui(tk.Tk):
    def __init__(self,parent):
        self.parent=parent
        self.Window()
        textBoxInstance=textBoxClass(self.parent)

    def Window(self): 
        self.parent.configure(bg='white')
        self.parent.geometry("1000x500")
        self.parent.title("Example Window")
        self.someFrame = ttk.Frame(self.parent)
        self.someFrame.grid(row=0, column=0, sticky=(N,S,E,W))

        textBoxSeparate=textBoxClass(self.parent)
        self.someFunction()

    def someFunction(self):
        #otherstuff
        textBoxClass(self.parent).textDropIn(self.parent).insert(tk.INSERT,"This is the text to add.")

class textBoxClass(tkst.ScrolledText):
    def __init__(self,parent):
        self.root=parent
        self.textDropIn(self.root)

    def textDropIn(self,parent):
        self.someText = tkst.ScrolledText(master=self.root, wrap=tk.WORD, width=50, height=20)
        self.someText.grid(row=0, column=4, rowspan=7, columnspan=4, pady=20, padx=20)


def main(): 
    root =tk.Tk()
    sg=someGui(root)
    root.mainloop()

if __name__=='__main__':
    main()

This question has been marked as a duplicate as another tkinter python post with a None return in the context of a get() attribute call, but I have made the same line separation edit that was recommended to that user without fixing the problem. If someone can explain, in detail, why it is a duplicate, I would be happy to remove the question. But, I cannot understand why it is.

Edit based on first answer by Bryan This was my understanding. I made edits but ran into several errors along the way. I removed the tkst.ScrolledText because I was improperly inheriting attributes and calling and instance of it. I removed parent as an attribute in textDropIn function and its respective call in __init__ in the textBoxClass definition. I have also added the self.textBox=textBoxClass(self.parent) to the __init__ of the someGui class, but I have run into TypeError and RecursionError based on my edits. At present, it is a RecursionError with the code in the current version. Which is on account of the self.textBox.textDropIn() with no arguments passed through.

from tkinter import ttk
import tkinter as tk
import tkinter.scrolledtext as tkst


class someGui(tk.Tk):
    def __init__(self,parent):
        self.parent=parent
        self.Window()
        self.textBox=textBoxClass(self.parent) #saving the instance 

    def Window(self): 
        self.parent.configure(bg='white')
        self.parent.geometry("1000x500")
        self.parent.title("Example Window")
        self.someFrame = ttk.Frame(self.parent)
        self.someFrame.grid(row=0, column=0, sticky='nesw') #changed sticky definition for tk requirements

        textBoxSeparate=textBoxClass(self.parent) # the initial inclusion of the textbox in the frame
        self.someFunction() #no input needed

    def someFunction(self):
        #otherstuff
        self.textBox.textDropIn() #there is no parent attribute in textDropIn, so I removed it
        self.textBox.insert(tk.INSERT, "Some test text.") #split call to two lines and changed to tk.INSERT

class textBoxClass(): #removed tkst.ScrolledText in class call because instance was created in textDropIn
    def __init__(self,parent):
        self.root=parent
        super().__init__() #kept receiving TypeError: object.__init__() takes no arguments, thus removed args
        self.textDropIn() #removed parent attribute from function call

    def textDropIn(self): #removed parent attribute from definition
        self.someText = tkst.ScrolledText(master=self.root, wrap=tk.WORD, width=50, height=20)
        self.someText.grid(row=0, column=4, rowspan=7, columnspan=4, pady=20, padx=20)


def main(): 
    root =tk.Tk()
    sg=someGui(root)
    root.mainloop()

if __name__=='__main__':
    main()
whisperquiet
  • 27
  • 2
  • 8
  • 1
    Your method `textDropIn` doesn't return anything, yet you are trying to use a method `insert` with its return. Likely you want the object from `textBoxClass`. You likely have the same issue as [this post](https://stackoverflow.com/questions/1101750/tkinter-attributeerror-nonetype-object-has-no-attribute-get). – busybear Jan 08 '19 at 01:01
  • Possible duplicate of [Tkinter: AttributeError: NoneType object has no attribute get](https://stackoverflow.com/questions/1101750/tkinter-attributeerror-nonetype-object-has-no-attribute-get) – busybear Jan 08 '19 at 01:03
  • @busybear I do not understand. Do I have to have a return from my function `textDropIn`? And what do you mean by "...you want the object from `textBoxClass`"? – whisperquiet Jan 08 '19 at 01:11
  • Might need to call the parent classes init under your classes init? – Gerald Leese Jan 08 '19 at 01:17
  • @GeraldLeese would that look like `textBoxClass.__init__()` in the `someGui` `__init__` definition? – whisperquiet Jan 08 '19 at 01:25
  • @whisperquiet I was thinking in the textBoxClass – Gerald Leese Jan 08 '19 at 01:27
  • @GeraldLeese : when I added `someGui.__init__(self,parent)` to the `__init__` of `textBoxClass` that throws an `AttributeError: 'textBoxClass' object has no attribute 'Window'` – whisperquiet Jan 08 '19 at 01:31
  • @whisperquiet I mean the tkinter TextButtons init method. – Gerald Leese Jan 08 '19 at 01:36
  • I got rid of the error but the text doesn't get inserted into the TextBox :\ – Gerald Leese Jan 08 '19 at 02:18
  • You may want to ask a new question about the recursion error. I got stuck at the recursion error part also. I too am curious about the answer now :p its just the insert line causing the error too hmmm – Gerald Leese Jan 08 '19 at 15:25
  • @GeraldLeese While I have not found a solution yet, I first want to make sure that I did not misunderstand @BryanOakley and his answer below. I have another suspicion that my current errors are somewhat tied to the `super.__init__()` call as well. – whisperquiet Jan 08 '19 at 15:40
  • super I believe is for multi inheritance what I was suggesting earlier is similar but only single inheritance. OOP is a little alien to me still haha – Gerald Leese Jan 08 '19 at 15:42
  • On my lunch breaks I’m gonna give er another shot – Gerald Leese Jan 08 '19 at 15:47
  • I got it I’ll post an answer here ASAP – Gerald Leese Jan 08 '19 at 21:29
  • @GeraldLeese: `super().__init__(...)` is for every kind of inheritance. Without it, the object won't be properly initialized unless you duplicate all of the code in the superclass `__init__` (which obviates the need for inheritance...). – Bryan Oakley Jan 09 '19 at 00:05
  • Why are you both creating a standard root window (`root = tk.Tk()`) and also creating another root window (`sg=someGui(root)`? Also, almost certainly part of the problem is that `someGui` inherits from `tk.Tk` but does not properly initialize it (via `super().__init__()`) – Bryan Oakley Jan 09 '19 at 00:29
  • @BryanOakley can multiple inheritance be done without super? – Gerald Leese Jan 09 '19 at 00:34
  • @BryanOakley oops I didn’t even notice that part was going on – Gerald Leese Jan 09 '19 at 00:43
  • @GeraldLeese it doesn’t matter what type of inheritance you use. If you want to inherit from another class and have a custom `__init__`, you must call the `__init__` of the super class. There are exceptions of course, but is up to you to properly initialize the class. – Bryan Oakley Jan 09 '19 at 00:50
  • @BryanOakley Hey I edited my answer got it all working Thanks for pointing out the SomeGui class I completely looked all of that by – Gerald Leese Jan 09 '19 at 01:47

3 Answers3

2

The error is telling you that you're trying to call insert on an object that is None. So, let's look at where you're calling insert:

textBoxClass(self.parent).textDropIn(self.parent).insert(tk.INSERT,"This is the text to add.")

Based on the error message we must conclude that textBoxClass(self.parent).textDropIn(self.parent) is None. And sure enough, when we look at the definition of the textDropIn method, it doesn't explicitly return anything. Because it doesn't explicitly return anything, it will return None. Thus, the code is the same as if you had done None.insert(...), and hence the error that you get.

There are two obvious solutions. If you want to be able to chain methods together like this (eg: .textDropIn(...).insert(...)), you need to make sure every step in the chain returns the original object. You can do this like so:

def someFunction(self):
    #otherstuff
    textBoxClass(self.parent).textDropIn(self.parent).insert(tk.INSERT,"This is the text to add.")
    return self

The other method is to break that one long statement into two:

textBoxClass(self.parent).textDropIn(self.parent)
textboxClass(self.parent).insert(tk.INSERT,"This is the text to add.")

However, that is not the proper way to call textDropIn and insert. Instead, you need to be calling it directly on the instance of the class. Unfortunately, you aren't saving a reference to the instance, so the first thing is to fix that by saving the instance:

class someGui(tk.Tk):
    def __init__(self,parent):
        self.parent=parent
        self.Window()
        self.textBox = textBoxClass(self.parent)

Then, you can call the methods on that instance:

def someFunction(self):
    #otherstuff
    self.textBox.textDropIn(self.parent)
    self.textbox.insert(tk.INSERT,"This is the text to add.")

Since you never use the parent attribute in textDropIn, I recommend removing that parameter both from the definition and from the call.

Also, your code will be easier to understand if you start all class names with an uppercase letter. You should change textBoxClass to TextBoxClass and someGui to SomeGui. This naming convention is universal in the python world. For more information on standard naming conventions, see PEP8.

There's another problem. The textBoxClass is both inheriting from ScrolledText and creating an instance of it. You should do one or the other. I can't quite tell what you're trying to accomplish, but the normal way to extend an existing class is with something like this (note the user of super):

class textBoxClass(tkst.ScrolledText):
    def __init__(self,parent):
        self.root=parent
        super().__init__(self, parent)
        self.textDropIn(self.root)

Yet another problem in the code is that you are importing tkinter twice:

from tkinter import *
...
import tkinter as tk

This makes your code very hard to understand. You need to pick one method of importing and stick with it. Personally I think the second version is the best because it adheres to PEP8 as well as the zen of python (explicit is better than implicit).

Finally, there is one more problem. You are creating two root windows, and a tkinter program can only have one (except under very unusual circumstances, which this is not). One is created when you do root = tk.Tk(), and the second is when you do sg=someGui(root), since someGui inherits from tk.Tk. To compound the problem you aren't properly calling the superclass __init__ method, so the someGui instance is not properly constructed. That is the root of the recursion error you wrote about in your update.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you very much for your answer. I understood most all of what you said, or so I thought, however I have run into a recursion error related to the `self.textDropIn()` call. I have added the updated code with some comments to reflect the specific edits. One thing I forgot to mention was that I fixed the import calls as well! – whisperquiet Jan 08 '19 at 05:20
0
from tkinter import ttk
import tkinter as tk
import tkinter.scrolledtext as tkst


class SomeGui(tk.Tk):
    def __init__(self):
        super().__init__()

    self.textBox=TextBoxClass(self) #saving the instance 
    self.configure(bg='white')
    self.geometry("1000x500")
    self.title("Example Window")
    self.someFrame = ttk.Frame(self)
    self.someFrame.grid(row=0, column=0, sticky='nesw') #changed sticky definition for tk requirements

    self.someFunction() #no input needed

    def someFunction(self):
        #otherstuff
        self.textBox.textDropIn() #there is no parent attribute in textDropIn, so I removed it
        self.textBox.someText.insert(tk.INSERT, "here we go")

class TextBoxClass(tkst.ScrolledText): #removed tkst.ScrolledText in class call because instance was created in textDropIn
    def __init__(self,parent):
        self.root=parent
        tkst.ScrolledText.__init__(self) #kept receiving TypeError: object.__init__() takes no arguments, thus removed args
        self.textDropIn()

    def textDropIn(self): #removed parent attribute from definition
        self.someText = tkst.ScrolledText(master=self.root, wrap=tk.WORD, width=50, height=20)
        self.someText.grid(row=0, column=4, rowspan=7, columnspan=4, pady=20, padx=20)


def main(): 
    sg=someGui()
    sg.mainloop()

if __name__=='__main__':
    main()

Ok so Ive made some changes and got it all working with your someFunctionmethod. I realised after talking with Bryan in the comments that we forgot to initialize the parent class in your SomeGui class which was probably the cause for the recursion error we were getting. There is probably a way to tidy up TextBoxClass that I am overlooking too.

Gerald Leese
  • 355
  • 4
  • 13
0

By the contribution of a friend and my own experimentation integrating elements of what the commenters said, I came to a solution for my problem a couple weeks ago.

I know that this has been downvoted by virtue of the Tkinter AttributeError that was needed for the solution, but a simple integration of the AttributeError fix did not solve my problem and that was an unknown error related to my original question. Here is the best answer to answer my original question.

The biggest issue for me to comprehend was the super().__init__ call in the textDropIn function within the textBoxClass. Prior to that I was inheriting from tkst.ScrolledText but I was creating the widget improperly.

The final solution allows me to call upon the class instance of textBoxClass and write text to the window in all of the child functions associated with the someGui class, which was my original goal.

I have left the commented code in there to reflect some of the past ideas that were nonfunctional.

from tkinter import ttk
import tkinter as tk
import tkinter.scrolledtext as tkst


class someGui(tk.Tk):
    def __init__(self, parent):
        self.parent=parent
        self.textBox=textBoxClass(self.parent) #saving the instance
        self.Window()

    def Window(self): 
        print("window")
        self.parent.configure(bg='white')
        self.parent.geometry("1000x500")
        self.parent.title("Example Window")
        self.someFrame = ttk.Frame(self.parent)
        self.someFrame.grid(row=0, column=0, sticky='nesw') #changed sticky definition for tk requirements

        # textBoxSeparate=textBoxClass(self.parent) # the initial inclusion of the textbox in the frame
        # textBoxSeparate.place(relx=0.5, rely=0.025, anchor='nw') #added this so textBoxSeparate doesn't overlap textbox
        # textBoxSeparate.insert(tk.INSERT, "textBoxSeparate sample text")
        self.someFunction() #no input needed
        # self.newFunction()

    def someFunction(self):
        #Both of the following ways of adding text work
        self.textBox.textDropIn() #there is no parent attribute in textDropIn, so I removed it
        self.textBox.insert(tk.INSERT, "textbox sample text\n") #split call to two lines and changed to tk.INSERT
        self.newFunction()

    def newFunction(self):
        self.textBox.insert(tk.INSERT,"another line of text")

class textBoxClass(tkst.ScrolledText): 
    def __init__(self, parent):
        self.root = parent
        #super().__init__(...) allows this class to inherit the tkst.ScrolledText class. Therefore, initializing the textBoxClass 
        #will automaticaly allow it to have all the same methods/attributes as initializing tkst.ScrolledText(), in addition to the methods/attr you add below.
        super().__init__(master=self.root, wrap=tk.WORD, borderwidth=1, relief="solid",width=50, height=20) #added a border for better visualization
        #self.textDropIn() #removed parent attribute from function call

    def textDropIn(self): #removed parent attribute from definition
        self.grid(row=0, column=4, rowspan=7, columnspan=4, pady=20, padx=20)
        # self.insert(tk.INSERT, "textDropIn sample text\n")

def main(): 
    root =tk.Tk()
    sg=someGui(root)
    root.mainloop()

if __name__=='__main__':
    main()
whisperquiet
  • 27
  • 2
  • 8