Read Programming Python Online

Authors: Mark Lutz

Tags: #COMPUTERS / Programming Languages / Python

Programming Python (139 page)

BOOK: Programming Python
13.54Mb size Format: txt, pdf, ePub
ads
Alternative Configurations and Accounts

So far, we’ve mostly
seen PyMailGUI being run on an email account I created for
this book’s examples, but it’s easy to tailor its
mailconfig
module for different accounts, as
well as different visual effects. For example,
Figure 14-45
captures the scene
with PyMailGUI being run on three different email accounts I use for
books and training. All three instances are run in independent processes
here. Each main list window is displaying a different email account’s
messages, and each customizes appearance or behavior in some fashion.
The message view window at the top, opened from the server list window
in the lower left also applies custom color and displayed headers
schemes.

Figure 14-45. Alternative accounts and configurations

You can always change
mailconfigs
in-place for a specific account if
you use just one, but we’ll later see how the
altconfigs
subdirectory applies one possible
solution to allow configuring for multiple accounts such as these,
completely external to the original source code. The
altconfigs
option renders the windows in
Figure 14-45
, and suffices as my
launching interface; see its code ahead.

Multiple Windows and Status Messages

Finally, PyMailGUI is
really meant to be a multiple-window interface—a detail
that most of the earlier screenshots haven’t really done justice to. For
example,
Figure 14-46
shows PyMailGUI with the main server list window, two save-file list
windows, two message view windows, and help. All these windows are
nonmodal; that is, they are all active and independent, and do not block
other windows from being selected, even though they are all running a
single PyMailGUI process.

Figure 14-46. PyMailGUI multiple windows and text editors

In general, you can have any number of mail view or compose
windows up at once, and cut and paste between them. This matters,
because PyMailGUI must take care to make sure that each window has a
distinct text-editor object. If the text-editor object were a global, or
used globals internally, you’d likely see the same text in each window
(and the Send operations might wind up sending text from another
window). To avoid this, PyMailGUI creates and attaches a new
TextEditor
instance to each view and compose
window it creates, and associates the new editor with the Send button’s
callback handler to make sure we get the right text. This is just the
usual OOP state retention, but it acquires a tangible benefit
here.

Though not GUI-related, PyMailGUI also prints a variety of status
messages as it runs, but you see them only if you launch the program
from the system command-line console window (e.g., a DOS box on Windows
or an xterm on Linux) or by double-clicking on its filename icon (its
main script is a
.py
, not a
.pyw
). On Windows, you won’t see these messages
when PyMailGUI is started from another program, such as the
PyDemos or PyGadgets launcher bar GUIs. These status
messages print server information; show mail loading status; and trace
the load, store, and delete threads that are spawned along the way. If
you want PyMailGUI to be more verbose, launch it from a command line and
watch:

C:\...\PP4E\Internet\Email\PyMailGui>
PyMailGui.py
user: [email protected]
loading headers
Connecting...
b'+OK <[email protected]>'
load headers exit
synch check
Connecting...
b'+OK <[email protected]>'
Same headers text
loading headers
Connecting...
b'+OK <[email protected]>'
load headers exit
synch check
Connecting...
b'+OK <[email protected]>'
Same headers text
load 16
Connecting...
b'+OK <[email protected]>'
Sending to...['[email protected]', '[email protected]']
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
From: [email protected]
To: [email protected]
Subject: Already got one...
Date: Fri, 04 Jun 2010 06:30:26 −0000
X-Mailer: PyMailGUI 3.0 (Python)
> -----Origin
Send exit

You can also double-click on the
PyMailGui.py
filename in your file explorer GUI and monitor the popped-up DOS console
box on Windows. Console messages are mostly intended for debugging, but
they can be used to help understand the system’s operation as
well.

For more details on using PyMailGUI, see its help display (press
the help bar at the top of its main server list windows), or read the
help string in the module
PyMailGuiHelp.py
, described in the next
section.

[
56
]
Actually, the help display started life even less fancy: it
originally displayed help text in a standard information pop up
common dialog, generated by the tkinter
showinfo
call used earlier in the book.
This worked fine on Windows (at least with a small amount of help
text), but it failed on Linux because of a default line-length limit
in information pop-up boxes; lines were broken so badly as to be
illegible. Over the years, common dialogs were replaced by scrolled
text, which has now been largely replaced by HTML; I suppose the
next edition will require a holographic help interface…

PyMailGUI Implementation

Last but not least, we get to the
code. PyMailGUI consists of the new modules listed near the
start of this chapter, along with a handful of peripheral files described
there. The source code for these modules is listed in this section. Before
we get started, here are two quick reminders to help you study:

Code reuse

Besides the code here, PyMailGUI also gets a lot of mileage
out of reusing modules we wrote earlier and won’t repeat here:
mailtools
for mail loads,
composition, parsing, and delete operations;
threadtools
for managing server and local
file access threads; the GUI section’s
TextEditor
for displaying and editing mail
message text; and so on. See the example numbers list earlier in
this chapter.

In addition, standard Python modules and packages such as
poplib
,
smtplib
, and
email
hide most of the details of pushing
bytes around the Net and extracting and building message components.
As usual, the
tkinter
standard
library module also implements GUI components in a portable
fashion.

Code structure

As mentioned earlier, PyMailGUI applies code factoring and OOP
to leverage code reuse. For instance, list view windows are
implemented as a common superclass that codes most actions, along
with one subclass for the server inbox list window and one for local
save-file list windows. The subclasses customize the common
superclass for their specific mail media.

This design reflects the operation of the GUI itself—server
list windows load mail over POP, and save-file list windows load
from local files. The basic operation of list window layout and
actions, though, is similar for both and is shared in the common
superclass to avoid redundancy and simplify the code. Message view
windows are similarly factored: a common view window superclass is
reused and customized for write, reply, and forward view
windows.

To make the code easier to follow, it is divided into two main
modules that reflect the structure of the GUI—one for the
implementation of list window actions and one for view window
actions. If you are looking for the implementation of a button that
appears in a mail view or edit window, for instance, see the view
window module and search for a method whose name begins with the
word
on
—the convention used for callback
handler methods. A specific button’s text can also be located in
name/callback tables used to build the windows. Actions initiated on
list windows are coded in the list window module instead.

In addition, the message cache is split off into an object and
module of its own, and potentially reusable tools are coded in
importable modules (e.g., line wrapping and utility pop ups).
PyMailGUI also includes a main module that defines startup window
classes, a simple HTML to plain-text parser, a module that contains
the help text as a string, the
mailconfig
user settings module (a version
specific to PyMailGUI is used here), and a small handful of related
files.

The following subsections present each of PyMailGUI’s source code
files for you to study. As you read, refer back to the demo earlier in
this chapter and run the program live to map its behavior back to its
code.

One accounting note up-front: the only one of PyMailGUI’s 18 new
source files not listed in this section is its
__init__.py
package initialization file. This
file is mostly empty except for a comment string and is unused in the
system today. It exists only for future expansion, in case PyMailGUI is
ever used as a package in the future—some of its modules may be useful in
other programs. As is, though, same-directory internal imports here are
not package-relative, so they assume this system is either run as a
top-level program (to import from “
.
”)
or is listed on
sys.path
directly (to
use absolute imports). In Python 3.X, a package’s directory is not
included on
sys.path
automatically, so
future use as a package would require changes to internal imports (e.g.,
moving the main script up one level and using
from . import module
throughout). See resources
such as the book
Learning
Python
for more on packages and package
imports.

PyMailGUI: The Main Module

Example 14-1
defines the file run to start PyMailGUI. It implements
top-level list windows in the system—combinations of PyMailGUI’s
application logic and the window protocol superclasses we wrote earlier
in the text. The latter of these define window titles, icons, and close
behavior.

The main internal, nonuser documentation is also in this module,
as well as command-line logic—the program accepts the names of one or
more save-mail files on the
command
line, and automatically opens them
when the GUI starts up. This is used by the PyDemos launcher of
Chapter 10
, for example.

Example 14-1. PP4E\Internet\Email\PyMailGui\PyMailGui.py

"""
##################################################################################
PyMailGui 3.0 - A Python/tkinter email client.
A client-side tkinter-based GUI interface for sending and receiving email.
See the help string in PyMailGuiHelp.py for usage details, and a list of
enhancements in this version.
Version 2.0 was a major, complete rewrite. The changes from 2.0 (July '05)
to 2.1 (Jan '06) were quick-access part buttons on View windows, threaded
loads and deletes of local save-mail files, and checks for and recovery from
message numbers out-of-synch with mail server inbox on deletes, index loads,
and message loads.
Version 3.0 (4E) is a port to Python 3.X; uses grids instead of packed column
frames for better form layout of headers in view windows; runs update() after
inserting into a new text editor for accurate line positioning (see PyEdit
loadFirst changes in Chapter 11); provides an HTML-based version of its help
text; extracts plain-text from HTML main/only parts for display and quoting;
supports separators in toolbars; addresses both message content and header
Unicode/I18N encodings for fetched, sent, and saved mails (see Ch13 and Ch14);
and much more (see Ch14 for the full rundown on 3.0 upgrades); fetched message
decoding happens deep in the mailtools package, on mail cache load operations
here; mailtools also fixes a few email package bugs (see Ch13);
This file implements the top-level windows and interface. PyMailGui uses a
number of modules that know nothing about this GUI, but perform related tasks,
some of which are developed in other sections of the book. The mailconfig
module is expanded for this program.
==Modules defined elsewhere and reused here:==
mailtools (package)
client-side scripting chapter
server sends and receives, parsing, construction (Example 13-21+)
threadtools.py
GUI tools chapter
thread queue manangement for GUI callbacks (Example 10-20)
windows.py
GUI tools chapter
border configuration for top-level windows (Example 10-16)
textEditor.py
GUI programs chapter
text widget used in mail view windows, some pop ups (Example 11-4)
==Generally useful modules defined here:==
popuputil.py
help and busy windows, for general use
messagecache.py
a cache that keeps track of mail already loaded
wraplines.py
utility for wrapping long lines of messages
html2text.py
rudimentary HTML parser for extracting plain text
mailconfig.py
user configuration parameters: server names, fonts, etc.
==Program-specific modules defined here:==
SharedNames.py
objects shared between window classes and main file
ViewWindows.py
implementation of view, write, reply, forward windows
ListWindows.py
implementation of mail-server and local-file list windows
PyMailGuiHelp.py (see also PyMailGuiHelp.html)
user-visible help text, opened by main window bar
PyMailGui.py
main, top-level file (run this), with main window types
##################################################################################
"""
import mailconfig, sys
from SharedNames import appname, windows
from ListWindows import PyMailServer, PyMailFile
###############################################################################
# top-level window classes
#
# View, Write, Reply, Forward, Help, BusyBox all inherit from PopupWindow
# directly: only usage; askpassword calls PopupWindow and attaches; the
# order matters here!--PyMail classes redef some method defaults in the
# Window classes, like destroy and okayToExit: must be leftmost; to use
# PyMailFileWindow standalone, imitate logic in PyMailCommon.onOpenMailFile;
###############################################################################
# uses icon file in cwd or default in tools dir
srvrname = mailconfig.popservername or 'Server'
class PyMailServerWindow(PyMailServer, windows.MainWindow):
"a Tk, with extra protocol and mixed-in methods"
def __init__(self):
windows.MainWindow.__init__(self, appname, srvrname)
PyMailServer.__init__(self)
class PyMailServerPopup(PyMailServer, windows.PopupWindow):
"a Toplevel, with extra protocol and mixed-in methods"
def __init__(self):
windows.PopupWindow.__init__(self, appname, srvrnane)
PyMailServer.__init__(self)
class PyMailServerComponent(PyMailServer, windows.ComponentWindow):
"a Frame, with extra protocol and mixed-in methods"
def __init__(self):
windows.ComponentWindow.__init__(self)
PyMailServer.__init__(self)
class PyMailFileWindow(PyMailFile, windows.PopupWindow):
"a Toplevel, with extra protocol and mixed-in methods"
def __init__(self, filename):
windows.PopupWindow.__init__(self, appname, filename)
PyMailFile.__init__(self, filename)
###############################################################################
# when run as a top-level program: create main mail-server list window
###############################################################################
if __name__ == '__main__':
rootwin = PyMailServerWindow() # open server window
if len(sys.argv) > 1: # 3.0: fix to add len()
for savename in sys.argv[1:]:
rootwin.onOpenMailFile(savename) # open save file windows (demo)
rootwin.lift() # save files loaded in threads
rootwin.mainloop()
SharedNames: Program-Wide Globals

The
module in
Example 14-2
implements a shared,
system-wide namespace that collects resources used in most modules in
the system and defines global objects that span files. This allows other
files to avoid redundantly repeating common imports and encapsulates the
locations of package imports; it is the only file that must be updated
if paths change in the future. Using globals can make programs more
difficult to understand in general (the source of some names is not as
clear), but it is reasonable if all such names are collected in a single
expected module such as this one, because there is only one place to
search for unknown names.

Example 14-2. PP4E\Internet\Email\PyMailGui\SharedNames.py

"""
##############################################################################
objects shared by all window classes and main file: program-wide globals
##############################################################################
"""
# used in all window, icon titles
appname = 'PyMailGUI 3.0'
# used for list save, open, delete; also for sent messages file
saveMailSeparator = 'PyMailGUI' + ('-'*60) + 'PyMailGUI\n'
# currently viewed mail save files; also for sent-mail file
openSaveFiles = {} # 1 window per file,{name:win}
# standard library services
import sys, os, email.utils, email.message, webbrowser, mimetypes
from tkinter import *
from tkinter.simpledialog import askstring
from tkinter.filedialog import SaveAs, Open, Directory
from tkinter.messagebox import showinfo, showerror, askyesno
# reuse book examples
from PP4E.Gui.Tools import windows # window border, exit protocols
from PP4E.Gui.Tools import threadtools # thread callback queue checker
from PP4E.Internet.Email import mailtools # load,send,parse,build utilities
from PP4E.Gui.TextEditor import textEditor # component and pop up
# modules defined here
import mailconfig # user params: servers, fonts, etc.
import popuputil # help, busy, passwd pop-up windows
import wraplines # wrap long message lines
import messagecache # remember already loaded mail
import html2text # simplistic html->plaintext extract
import PyMailGuiHelp # user documentation
def printStack(exc_info):
"""
debugging: show exception and stack traceback on stdout;
3.0: change to print stack trace to a real log file if print
to sys.stdout fails: it does when launched from another program
on Windows; without this workaround, PMailGUI aborts and exits
altogether, as this is called from the main thread on spawned
thread failures; likely a Python 3.1 bug: it doesn't occur in
2.5 or 2.6, and the traceback object works fine if print to file;
oddly, the print() calls here work (but go nowhere) if spawned;
"""
print(exc_info[0])
print(exc_info[1])
import traceback
try:
traceback.print_tb(exc_info[2], file=sys.stdout) # ok unless spawned!
except:
log = open('_pymailerrlog.txt', 'a') # use a real file
log.write('-'*80) # else gui may exit
traceback.print_tb(exc_info[2], file=log) # in 3.X, not 2.5/6
# thread busy counters for threads run by this GUI
# sendingBusy shared by all send windows, used by main window quit
loadingHdrsBusy = threadtools.ThreadCounter() # only 1
deletingBusy = threadtools.ThreadCounter() # only 1
loadingMsgsBusy = threadtools.ThreadCounter() # poss many
sendingBusy = threadtools.ThreadCounter() # poss many
BOOK: Programming Python
13.54Mb size Format: txt, pdf, ePub
ads

Other books

Guardsmen of Tomorrow by Martin H. & Segriff Greenberg, Larry Segriff
They Say Love Is Blind by Pepper Pace
As It Is On Telly by Marshall, Jill
Blazing Hot Bad Boys Boxed Set - A MC Romance Bundle by Glass, Evelyn, Day, Laura, Thomas, Kathryn, Love, Amy, Summers, A. L., Faye, Carmen, Knowles, Tamara, Owen, Candice
Finding Elizabeth by Louise Forster