It's been said that Tkinter's strongest points may be its text and canvas widgets. Both provide a remarkable amount of functionality. For instance, the Tkinter Text widget was powerful enough to implement the Grail web browser, discussed in Chapter 15; it supports complex font style settings, embedded images, and much more. The Tkinter Canvas widget, a general-purpose drawing device, has also been the basis of sophisticated image processing and visualization applications.
In Chapter 9, we'll put these two widgets to use to implement text editors (PyEdit), paint programs (PyDraw), clock GUIs (PyClock), and photo slideshows (PyView). For the purposes of this tour chapter, though, let's start out using these widgets in simpler ways. Example 8-10 implements a simple scrolled-text display, which knows how to fill its display with a text string or file.
Example 8-10. PP2EGuiTourscrolledtext.py
# a simple text or file viewer component print 'PP2E scrolledtext' from Tkinter import * class ScrolledText(Frame): def __init__(self, parent=None, text='', file=None): Frame.__init__(self, parent) self.pack(expand=YES, fill=BOTH) # make me expandable self.makewidgets() self.settext(text, file) def makewidgets(self): sbar = Scrollbar(self) text = Text(self, relief=SUNKEN) sbar.config(command=text.yview) # xlink sbar and text text.config(yscrollcommand=sbar.set) # move one moves other sbar.pack(side=RIGHT, fill=Y) # pack first=clip last text.pack(side=LEFT, expand=YES, fill=BOTH) # text clipped first self.text = text def settext(self, text='', file=None): if file: text = open(file, 'r').read() self.text.delete('1.0', END) # delete current text self.text.insert('1.0', text) # add at line 1, col 0 self.text.mark_set(INSERT, '1.0') # set insert cursor self.text.focus() # save user a click def gettext(self): # returns a string return self.text.get('1.0', END+'-1c') # first through last if __name__ == '__main__': root = Tk() try: st = ScrolledText(file=sys.argv[1]) # filename on cmdline except IndexError: st = ScrolledText(text='Words go here') # or not: 2 lines def show(event): print repr(st.gettext()) # show as raw string root.bind('', show) # esc = dump text root.mainloop()
Like the ScrolledList of Example 8-9, the ScrolledText object in this file is designed to be a reusable component, but can also be run standalone to display text file contents. Also like the last section, this script is careful to pack the scrollbar first so that it is cut out of the display last as the window shrinks, and arranges for the embedded Text object to expand in both directions as the window grows. When run with a filename argument, this script makes the window shown in Figure 8-15; it embeds a Text widget on the left, and a cross-linked Scrollbar on the right.
Figure 8-15. scrolledtext in action
Just for fun, I populated the text file displayed in the window with the following code and command lines (and not just because I happen to live near an infamous hotel in Colorado):
C:...PP2EGuiTour>type temp.py f = open('temp.txt', 'w') for i in range(250): f.write('%03d) All work and no play makes Jack a dull boy. ' % i) f.close() C:...PP2EGuiTour>python temp.py C:...PP2EGuiTour>python scrolledtext.py temp.txt PP2E scrolledtext
To view a file, pass its name on the command line -- its text is automatically displayed in the new window. By default, it is shown in a non-fixed-width font, but we'll pass a font option to the text widget in the next example to change that.
Notice the "PP2E scrolledtext" message printed when this script runs. Because there is also a ScrolledText.py file in the standard Python distribution with a very different interface, the one here identifies itself when run or imported so you can tell which one you've got. If the standard one ever goes away, import the one listed here for a simple text browser, and adjust configuration calls to include a ".text" qualifier level (the library version subclasses Text, not Frame).
8.4.1 Programming the Text Widget
To understand how this script works at all, though, we have to detour into a few Text widget details here. Earlier we met the Entry and Message widgets, which address a subset of the Text widget's uses. The Text widget is much richer in both features and interfaces -- it supports both input and display of multiple lines of text, editing operations for both programs and interactive users, multiple fonts and colors, and much more. Text objects are created, configured, and packed just like any other widget, but they have properties all their own.
8.4.1.1 Text is a Python string
Although the Text widget is a powerful tool, its interface seems to boil down to two core concepts. First of all, the content of a Text widget is represented as a string in Python scripts, and multiple lines are separated with the normal line terminator. The string 'Words go here', for instance, represents two lines when stored in or fetched from a Text widget; it would normally have a trailing too, but doesn't have to.
To help illustrate this point, this script binds the Escape key press to fetch and print the entire contents of the Text widget it embeds:
C:...PP2EGuiTour>python scrolledtext.py PP2E scrolledtext 'Words 12go here' 'Always look 12on the bright 12side of life 12'
When run with arguments, the script stores a file's contents in the text widget. When run without arguments, the script stuffs a simple literal string into the widget, displayed by the first Escape press output here (recall that