Details
A simple, working, does-what-you-expect read-only Text widget. It inherits from the normal Text widget and can be used just like it, the only difference being that the GUI interface will not be able to change the widget's contents.
This first version uses the WidgetRedirector class from IDLE, which is included in any recent CPython installation. This may change sometime in the near future.
Code
1 from Tkinter import Text
2 from idlelib.WidgetRedirector import WidgetRedirector
3
4 class ReadOnlyText(Text):
5 def __init__(self, *args, **kwargs):
6 Text.__init__(self, *args, **kwargs)
7 self.redirector = WidgetRedirector(self)
8 self.insert = \
9 self.redirector.register("insert", lambda *args, **kw: "break")
10 self.delete = \
11 self.redirector.register("delete", lambda *args, **kw: "break")
12
Notes
This is based on information from this page in the Tcl/Tk wiki.
There are several possible methods to make a Text widget read-only: ("Amongst our weaponry are such diverse elements as...")
Setting the widget's state to "disabled": Using this method, programmatically changing the Text widget's contents requires changing its state back to "normal", making the changes, and then changing its state back to "disabled". Whenever the widget's state is changed back to "normal", even momentarily, it remembers previous actions and executes them. Therefore this method is not actually useful unless you never wish to change the text widget's contents.
Binding <KeyPress> (and perhaps <Paste> and/or <PasteSelection>) to empty functions which return "break": This doesn't allow browsing the text with the insert cursor, selecting text with the keyboard, or copying on Windows. One can manually bind <Control-c> back to copying, but that still leaves the other deficiencies, and require binding the correct key combination per platform.
Renaming the underlying Tcl variable representing the Text widget, thus "capturing" all of its events, and overriding "insert" and "delete": This is the best method as far as I could tell, so it is what I used here (it's what the WidgetRedirector class does).