0

I understand ScrolledText is constructed as a Text object (but has a scrollbar attached together in a frame). But the following code throws an error when the window is closed and the printText() method is called:

import Tkinter as tk
import ttk
import ScrolledText as st
class tkGui(object):
    def printText(self, event):
        print "It works!"
        self.mText.get("1.0", 'end-1c')

    def __init__(self, window):
        # create widgets
        self.frame=tk.Frame(window)
        self.mText = st.ScrolledText(self.frame)
        self.mText.bind('<Destroy>',self.printText)
        # place widgets
        self.frame.pack()
        self.mText.pack()

window = tk.Tk()
app = tkGui(window)
window.mainloop()

The error:

[...]
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 3077, in get
  return self.tk.call(self._w, 'get', index1, index2)
TclError: invalid command name ".140506094171344.140506094172280.140506094172496"

What am I doing wrong?

2 Answers 2

2

Once you destroy the main window, all its sub-widgets are destroyed, and you can't access them. (This is not technically correct, as Bryan Oakley's answer points out, but it's how I think about it. Trying to work with the widgets after destruction is problematical, at best.)

You need to use wm_protocol

http://nullege.com/codes/search/Tkinter.Tk.wm_protocol

import Tkinter as tk
import ttk
import ScrolledText as st
class tkGui(object):
    def printText(self):
        print "It works!"
        self.mText.get("1.0", 'end-1c')
        self.window.destroy()

    def __init__(self, window):
        # create widgets
        self.window = window
        self.frame=tk.Frame(window)
        self.mText = st.ScrolledText(self.frame)
        #self.mText.bind('<Destroy>',self.printText)
        window.wm_protocol("WM_DELETE_WINDOW", self.printText)
        # place widgets
        self.frame.pack()
        self.mText.pack()

window = tk.Tk()
app = tkGui(window)
window.mainloop()

There are a couple of changes here. I removed the event parameter from printText and added a call to self.window.destroy. Note that I had to add the self.window attribute to make this call. The basic change is using wm_protocol instead of binding to the event; the others are necessary consequences.

Sign up to request clarification or add additional context in comments.

Comments

2

You should not assume you can get data out of a widget when handling the <Destroy> event for that widget.

From the official documentation:

When the Destroy event is delivered to a widget, it is in a “half-dead” state: the widget still exists, but most operations on it will fail.

2 Comments

@DhaLee: what do you mean "unreliably"? The widget is destroyed, why should you be able to interact with a deleted object?
@DhaLee: you can use it, you just can't use it to get attributes of the destroyed object. Most often this is used to do some clean-up of related data. This event has had this behavior for decades and nobody has complained about it. It simply wasn't designed for the purpose you wish it had been designed for. If your goal is to save the contents of a window when it is being destroyed, there are better ways that are specifically created for that purpose (see the other answer that mentions wm_protocol)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.