-1

I was trying to replace (at runtime) the inherited insert() method for a subclass of tkinter.Text. The replacement method executes a few lines of code before calling the parent class' (tkinter.Text) insert() method. However, at runtime python shows the following error.

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tkinter/__init__.py", line 1885, in __call__
    return self.func(*args)
  File ".../expts/test.py", line 20, in callback
    text.insert("1.0", "howdy")
  File ".../expts/test.py", line 14, in new_insert
    tk.Text.insert(text, index, chars, *args)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tkinter/__init__.py", line 3740, in insert
    self.tk.call((self._w, 'insert', index, chars) + args)
AttributeError: type object 'Text' has no attribute 'tk'

The following code is a simplified version of my main code.

import tkinter as tk
import types

class TextChild(tk.Text):
    def __init__(self, *args, **kwargs):
        tk.Text.__init__(self, *args, **kwargs)

root = tk.Tk()
text = TextChild(root)
text.pack()

def new_insert(text, index, chars, *args):
    print("Does some work...")
    tk.Text.insert(text, index, chars, *args)

text.insert = types.MethodType(new_insert, tk.Text)

def callback():
    global text
    text.insert("1.0", "howdy")

button = tk.Button(master=root,text="Button", command=callback)
button.pack()

root.mainloop()
Sam
  • 23
  • 2

2 Answers2

3

It is more easier to do it inside the inherited class:

class TextChild(tk.Text):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def insert(self, index, chars, *args):
        print("Does some work ...")
        # call original insert()
        super().insert(index, chars, *args)
acw1668
  • 40,144
  • 5
  • 22
  • 34
  • I'm developing a plug-in for an existing app. So, I can't update existing definitions. I can only change methods/callbacks over the life of the plug-in. – Sam Jan 27 '21 at 04:10
  • 1
    @SamZeleke Then for your case, `TextChild` is not necessary and you can use `lambda` instead: `text.insert = lambda *args: new_insert(text, *args)`. – acw1668 Jan 27 '21 at 06:15
  • You're right. TextChild is as simple because I simplified the code for question. The class is actually much bigger. – Sam Jan 27 '21 at 17:05
1

To address your original method, you are mixing up the arguments for types.MethodType. Arguments are method and the object.

text.insert = types.MethodType(new_insert, text)
busybear
  • 10,194
  • 1
  • 25
  • 42