Read Programming Python Online

Authors: Mark Lutz

Tags: #COMPUTERS / Programming Languages / Python

Programming Python (69 page)

BOOK: Programming Python
8.96Mb size Format: txt, pdf, ePub
ads
Adding Text-Editing Operations

Example 9-11
puts some
of these concepts to work. It extends
Example 9-10
to add support for
four common text-editing operations—file save, text cut and paste, and
string find searching—by subclassing
ScrolledText
to
provide additional buttons and methods. The
Text
widget comes with a set of default
keyboard bindings that perform some common editing operations, too, but
they might not be what is expected on every platform; it’s more common
and user friendly to provide GUI interfaces to editing operations in a
GUI text editor.

Example 9-11. PP4E\Gui\Tour\simpleedit.py

"""
add common edit tools to ScrolledText by inheritance;
composition (embedding) would work just as well here;
this is not robust!--see PyEdit for a feature superset;
"""
from tkinter import *
from tkinter.simpledialog import askstring
from tkinter.filedialog import asksaveasfilename
from quitter import Quitter
from scrolledtext import ScrolledText # here, not Python's
class SimpleEditor(ScrolledText): # see PyEdit for more
def __init__(self, parent=None, file=None):
frm = Frame(parent)
frm.pack(fill=X)
Button(frm, text='Save', command=self.onSave).pack(side=LEFT)
Button(frm, text='Cut', command=self.onCut).pack(side=LEFT)
Button(frm, text='Paste', command=self.onPaste).pack(side=LEFT)
Button(frm, text='Find', command=self.onFind).pack(side=LEFT)
Quitter(frm).pack(side=LEFT)
ScrolledText.__init__(self, parent, file=file)
self.text.config(font=('courier', 9, 'normal'))
def onSave(self):
filename = asksaveasfilename()
if filename:
alltext = self.gettext() # first through last
open(filename, 'w').write(alltext) # store text in file
def onCut(self):
text = self.text.get(SEL_FIRST, SEL_LAST) # error if no select
self.text.delete(SEL_FIRST, SEL_LAST) # should wrap in try
self.clipboard_clear()
self.clipboard_append(text)
def onPaste(self): # add clipboard text
try:
text = self.selection_get(selection='CLIPBOARD')
self.text.insert(INSERT, text)
except TclError:
pass # not to be pasted
def onFind(self):
target = askstring('SimpleEditor', 'Search String?')
if target:
where = self.text.search(target, INSERT, END) # from insert cursor
if where: # returns an index
print(where)
pastit = where + ('+%dc' % len(target)) # index past target
#self.text.tag_remove(SEL, '1.0', END) # remove selection
self.text.tag_add(SEL, where, pastit) # select found target
self.text.mark_set(INSERT, pastit) # set insert mark
self.text.see(INSERT) # scroll display
self.text.focus() # select text widget
if __name__ == '__main__':
if len(sys.argv) > 1:
SimpleEditor(file=sys.argv[1]).mainloop() # filename on command line
else:
SimpleEditor().mainloop() # or not: start empty

This, too, was written with one eye toward reuse—the
SimpleEditor
class
it defines could be attached or subclassed by other GUI
code. As I’ll explain at the end of this section, though, it’s not yet
as robust as a general-purpose library tool should be. Still, it
implements a functional text editor in a small amount of portable code.
When run standalone, it brings up the window in
Figure 9-19
(shown editing itself and running on
Windows); index positions are printed on
stdout
after each successful find
operation—
here, for two “def” finds, with
prior selection removal logic commented-out in the script (uncomment
this line in the script to get single-selection behavior for
finds):

C:\...\PP4E\Gui\Tour>
python simpleedit.py simpleedit.py
PP4E scrolledtext
14.4
25.4

Figure 9-19. simpleedit in action

The save operation pops up the common save dialog that is
available in tkinter and is tailored to look native on each platform.
Figure 9-20
shows this dialog in
action on Windows 7. Find operations also pop up a standard dialog box
to input a search string (
Figure 9-21
); in a
full-blown editor, you might want to save this string away to repeat the
find again (we will, in
Chapter 11
’s more
full-featured PyEdit example). Quit operations reuse the verifying Quit
button component we coded in
Chapter 8
yet again; code reuse means
never having to say you’re quitting without warning…

Figure 9-20. Save pop-up dialog on Windows

Figure 9-21. Find pop-up dialog

Using the clipboard

Besides
Text
widget
operations,
Example 9-11
applies the tkinter
clipboard interfaces in its cut-and-paste functions. Together, these
operations allow you to move text within a file (cut in one place,
paste in another). The clipboard they use is just a place to store
data temporarily—deleted text is placed on the clipboard on a cut, and
text is inserted from the clipboard on a paste. If we restrict our
focus to this program alone, there really is no reason that the text
string cut couldn’t simply be stored in a Python instance variable.
But the clipboard is actually a much larger concept.

The clipboard used by this script is an interface to a
system-wide storage space, shared by all programs on your computer.
Because of that, it can be used to transfer data between applications,
even ones that know nothing of tkinter. For instance, text cut or
copied in a Microsoft Word session can be pasted in a
SimpleEditor
window,
and text cut in
SimpleEditor
can be
pasted in a Microsoft Notepad window (try it). By using the clipboard
for cut and paste,
SimpleEditor
automatically integrates with the window system at large. Moreover,
the clipboard is not just for the
Text
widget—it can also be used to cut and
paste graphical objects in the
Canvas
widget (discussed next).

As used in the script of
Example 9-11
, the basic tkinter
clipboard interface looks like this:

self.clipboard_clear()                            # clear the clipboard
self.clipboard_append(text) # store a text string on it
text = self.selection_get(selection='CLIPBOARD') # fetch contents, if any

All of these calls are available as methods inherited by all
tkinter widget objects because they are global in nature. The
CLIPBOARD
selection used by this script is
available on all platforms (a
PRIMARY
selection is also available, but it
is only generally useful on X Windows, so we’ll ignore it here).
Notice that the clipboard
selection_get
call throws a
TclError
exception if it fails; this script
simply ignores it and abandons a paste request, but we’ll do better
later.

Composition versus inheritance

As coded,
SimpleEditor
uses inheritance to extend
ScrolledText
with extra buttons and callback methods. As we’ve seen,
it’s also reasonable to attach (embed) GUI objects coded as
components, such as
ScrolledText
.
The attachment model is usually called composition; some people find
it simpler to understand and less prone to name clashes than extension
by inheritance.

To give you an idea of the differences between these two
approaches, the following sketches the sort of code you would write to
attach
ScrolledText
to
SimpleEditor
with changed lines in bold font
(see the file
simpleedit2.py
in the book’s
examples distribution for a complete composition implementation). It’s
mostly a matter of passing in the right parents and adding an extra
st
attribute name anytime you need
to get to the
Text
widget’s
methods:

class SimpleEditor(Frame):
def __init__(self, parent=None, file=None):
Frame.__init__(self, parent)
self.pack()
frm = Frame(self)
frm.pack(fill=X)
Button(frm, text='Save', command=self.onSave).pack(side=LEFT)
...more...
Quitter(frm).pack(side=LEFT)
self.st = ScrolledText(self, file=file) # attach, not subclass
self.st.text.config(font=('courier', 9, 'normal'))
def onSave(self):
filename = asksaveasfilename()
if filename:
alltext = self.st.gettext() # go through attribute
open(filename, 'w').write(alltext)
def onCut(self):
text = self.st.text.get(SEL_FIRST, SEL_LAST)
self.st.text.delete(SEL_FIRST, SEL_LAST)
...more...

This code doesn’t need to subclass
Frame
necessarily (it could add widgets to
the passed-in parent directly), but being a frame allows the full
package here to be embedded and configured as well. The window looks
identical when such code is run. I’ll let you be the judge of whether
composition or inheritance is better here. If you code your Python GUI
classes right, they will work under either regime.

It’s called “Simple” for a reason: PyEdit (ahead)

Finally, before you change your system registry to
make
SimpleEditor
your
default text file viewer, I should mention that although it shows the
basics, it’s something of a stripped-down version (really, a
prototype) of the PyEdit example we’ll meet in
Chapter 11
. In fact, you may wish to study
that example now if you’re looking for more complete tkinter
text-processing code in general. There, we’ll also use more advanced
text operations, such as the undo/redo interface, case-insensitive
searches, external files search, and more. Because the
Text
widget is so powerful, it’s difficult
to demonstrate more of its features without the volume of code that is
already listed in the PyEdit program.

I should also point out that
SimpleEditor
not only is limited in
function, but also is just plain careless—many boundary cases go
unchecked and trigger uncaught exceptions that don’t kill the GUI, but
are not handled or reported well. Even errors that are caught are not
reported to the user (e.g., a paste with nothing to be pasted). Be
sure to see the PyEdit example for a more robust and complete
implementation of the operations
introduced in
SimpleEditor
.

BOOK: Programming Python
8.96Mb size Format: txt, pdf, ePub
ads

Other books

The Cobra Event by Richard Preston
Circus Shoes by Noel Streatfeild
Serendipity by Joanna Wylde
Octopus by Roland C. Anderson
Kull: Exile of Atlantis by Howard, Robert E.
The Diviner by Melanie Rawn
Silent Echo by Elisa Freilich