Sending and Viewing Email with the PyMailGUI Client

In this third part of a six-part series, you will learn how to send and view email with attachments with the PyMailGUI client, as well as how to accomplish other tasks. This article is excerpted from chapter 15 of Programming Python, Third Edition, written by Mark Lutz (O’Reilly, 2006; ISBN: 0596009259). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.

Offline Processing with Save and Open

To save mails in a local file for offline processing, select the desired messages in any mail list window and press the Save action button (any number of messages may be selected for saving). A standard file-selection dialog appears, like that in Figure 15-11, and the mails are saved to the end of the chosen text file.

Figure 15-11.  Save mail selection dialog

To view saved emails later, select the Open action at the bottom of any list window and pick your save file in the selection dialog. A new mail index list window appears for the save file and it is filled with your saved messages eventually—there may be a slight delay for large save files, because of the work involved. PyMailGUI runs file loads and deletions in threads to avoid blocking the rest of the GUI; these threads
can overlap with operations on other open save-mail files, server transfer threads, and the GUI at large.

While a mail save file is being loaded in a parallel thread, its window title is set to “Loading…” as a status indication; the rest of the GUI remains active during the load (you can fetch and delete server messages, view mails in other files, write new messages, and so on). The window title changes to the loaded file’s name after the load is finished. Once filled, a message index appears in the save file’s window, like the one captured in Figure 15-12 (this window also has three mails selected for processing).

Figure 15-12.  List window for mail save file, multiple selections

In general, there may be one server mail list window and any number of save-mail file list windows open at any time. Save-mail file list windows like that in Figure 15-12 can be opened at any time, even before fetching any mail from the server. They are identical to the server’s inbox list window, but there is no help bar and no Load action button, and all other action buttons are mapped to the save file, not to the server.

For example, View opens the selected message in a normal mail view window identical to that in Figure 15-4, but the mail originates from the local file. Similarly, Delete removes the message from the save file, instead of from the server’s inbox. Deletions from save-mail files are also run in a thread, to avoid blocking the rest of the GUI—the window title changes to “Deleting…” during the delete as a status indicator. Status indicators for loads and deletions in the server inbox window use popups instead, because the wait is longer and there is progress to display (see Figure 15-7).

Technically, saves always append raw message text to the chosen file; the file is opened in ‘a’ mode, which creates the file if needed, and writes at its end. The Save and Open operations are also smart enough to remember the last directory you selected; the file dialogs begin navigation there the next time you press Save or Open.

You may also save mails from a saved file’s window—use Save and Delete to move mails from file to file. In addition, saving to a file whose window is open for viewing automatically updates that file’s list window in the GUI. This is also true for the automatically written sent-mail save file, described in the next section.

{mospagebreak title=Sending Email and Attachments}

Once we’ve loaded email from the server or opened a local save file, we can process our messages with the action buttons at the bottom of list windows. We can also send new emails at any time, even before a load or open. Pressing the Write button in a list window generates a mail composition window; one has been captured in Figure 15-13.

Figure 15-13.  PyMailGUI write-mail compose window

This window is much like the message view window we saw in Figure 15-4, except there are no quick-access part buttons in the middle (this window is a new mail). It has fields for entering header line details, and an attached TextEditor object for writing the body of the new email.

For write operations, PyMailGUI automatically fills the “From:” line and inserts a signature text line (“–Mark…”), from your mailconfig module settings. You can change these to any text you like in the GUI, but the defaults are filled in automatically from your mailconfig . When the mail is sent, an email.Utils call handles date and time formatting in the mailtools module in Chapter 14.

There is also a new set of action buttons in the upper left here: Cancel closes the window (if verified), and Send delivers the mail—when you press the Send button, the text you typed into the body of this window is mailed to the addresses you typed into the “To:”, “Cc:”, and “Bcc:” lines, using Python’s smtplib module. PyMailGUI adds the header fields you type as mail header lines in the sent message. To send to more than one address, separate them with a “;” character in header fields. In this mail, I fill in the “To:” header with my own email address in order to send the message to myself for illustration purposes.

Also in compose windows, the Attach button issues a file selection dialog for attaching a file to your message, as in Figure 15-14. The Parts button pops up a dialog displaying files already attached, like that in Figure 15-15. When your message is sent, the text in the edit portion of the window is sent as the main message text, and any attached part files are sent as attachments properly encoded according to their type.

Figure 15-14.  Attachment file dialog for attach

Figure 15-15.  Attached parts list dialog for Parts

As we’ve seen, smtplib ultimately sends bytes to a server over a socket. Since this can be a long-running operation, PyMailGUI delegates this operation to a spawned thread, too. While the send thread runs, a nonblocking wait window appears and the entire GUI stays alive; redrawand move events are handled in the main program thread while the send thread talks to the SMTP server, and the user may perform other tasks in parallel.

You’ll get an error popup if Python cannot send a message to any of the target recipients for any reason, and the mail composition window will pop up so that you may try again or save its text for later use. If you don’t get an error popup, everything worked correctly, and your mail will show up in the recipients’ mailboxes on their email servers. Since I sent the earlier message to myself, it shows up in mine the next time I press the main window’s Load button, as we see in Figure 15-16.

Figure 15-16.  PyMailGUI main window after, loading sent mail

If you look back to the last main window shot, you’ll notice that there is only one new email now—PyMailGUI is smart enough to download only the one new
massage’s header text and tack it onto the end of the loaded email list. Mail send operations automatically save sent mails in a save file that you name in your configuration module; use Open to view sent messages in offline mode and Delete to clean up the sent mail file if it grows too large (you can also Save from the sent-mail file to another file).

{mospagebreak title=Viewing Email and Attachments}

Now let’s view the mail message that was sent and received. PyMailGUI lets us view email in formatted or raw mode. First, highlight (single-click) the mail you want to see in the main window, and press the View button. After the full message text is downloaded (unless it is already cached), a formatted mail viewer window like that shown in Figure 15-17 appears. If multiple messages are selected, the View button will download all that are not already cached (i.e., that have not already been fetched) and will pop up a view window for each. Like all long-running operations, full message downloads are run in parallel threads to avoid blocking.

Figure 15-17.  PyMailGUI view incoming mail window

Python’s email module is used to parse out header lines from the raw text of the email message; their text is placed in the fields in the top right of the window. The message’s main text is fetched from its body and stuffed into a new TextEditor object for display (it is also displayed in a web browser automatically if it is HTML text). PyMailGUI uses heuristics to extract the main text of the message to display, if there is one; it does not blindly show the entire raw text of the mail.

Any other parts of the message attached are displayed and opened with quick-access buttons in the middle. They are also listed by the Parts popup dialog, and they can be saved and opened all at once with Split. Figure 15-18 shows this window’s Parts list popup, and Figure 15-19 displays this window’s Split dialog in action.

Figure 15-18.  Parts dialog listing all message parts

Figure 15-19.  Split dialog selection

When the Split dialog in Figure 15-19 is submitted, all message parts are saved to the directory you select, and known parts are automatically opened. Individual parts are also automatically opened by the row of quick-access buttons labeled with the part’s filename in the middle of the view window, after being saved to a temporary directory.

For instance, Figure 15-20 shows one of the image parts open on my Windows laptop, in a standard image viewer on that platform; other platforms may open this in a web browser instead. Click the image filename’s quick-access button in Figure 15-17 to view it immediately, or run Split to open all parts at once.

By this point, the photo attachment displayed in Figure 15-20 has really gotten around: it has been encoded, attached, and sent, and then fetched, parsed, and decoded. Along the way, it has moved through multiple machines—from the client to the SMTP server, to the POP server, and back to the client.

In terms of user interaction, we attached the image to the email in Figure 15-13 using the dialog in Figure 15-14 before we sent the email. To access it later, we selected the email for viewing in Figure 15-16 and clicked on its quick-access button in Figure 15-17. PyMailGUI encoded it in base64 form, inserted it in the email’s text, and later extracted and decoded it to get the original photo. With Python email tools, this just works.

Note that the main message text counts as a mail part, too—when selected, it opens in a PyEdit window, like that captured in Figure 15-21, from which it can be processed and saved (you can also save the main mail text with the Save button in the View window itself). The main part is included, because not all mails have a text

Figure 15-20.  PyMailGUI opening image parts in a viewer or browser

part. For messages that have only HTML for their main text part, PyMailGUI displays the HTML text in its window, and opens a web browser to view the mail with its HTML formatting.

Figure 15-21.  Main text part opened in PyEdit

PyMailGUI also opens HTML and XML parts in a web browser and uses the Windows Registry to open well-known Windows document types. For example, .doc, .xls, and .pdf files usually open, respectively, in Word, Excel, and Adobe Reader. Figure 15-22 captures the response to the calendar.html quick-access part button in Figure 15-17 on my Windows laptop (Firefox is my default web browser).

Figure 15-22.  Attached HTML part opened in a web browser

The quick-access buttons in the middle of the Figure 15-17 view window are a more direct way to open parts than Split—you don’t need to select a save directory, and you can open just the part you want to view. The Split button, though, allows all parts to be opened in a single step; allows you to choose where to save parts; and supports an arbitrary number of parts. Files that cannot be opened automatically because of their type can be inspected in the local save directory, after both Split and quick-access button selections (popup dialogs name the directory to use).

After a fixed maximum number of parts, the quick-access row ends with a button labeled “…”, which simply runs Split to save and open additional parts when selected. Figure 15-23 captures one such message in the GUI; this was message 10 in Figure 15-9, if you’re keeping track—a very complex mail, with 5 photos and 12 total parts.

Like much of PyMailGUI’s behavior, the maximum number of part buttons to display in view windows can be configured in the user settings module.

Figure 15-23.  View window for a mail with many parts

That setting specified five buttons in Figure 15-23. Figure 15-24 shows a different mail with many attachments being viewed; the part buttons setting has been changed to a maximum of eight (this mail has seven parts). The setting can be higher, but at some point the buttons may become unreadable (use Split instead).

Figure 15-24.  View window with part buttons setting increased

Figures 15-25 and 15-26 show what happens when the and ch19.pdf buttons in Figure 15-24 are pressed on my Windows laptop. The results vary per machine; the audio file opens in Windows Media Player, the MP3 file opens in iTunes instead, and some platforms may open such files directly in a web browser.

Figure 15-25.  An audio part opened by PyMailGUI

Figure 15-26.  A PDF part opened in PyMailGUI

Besides the nicely formatted view window, PyMailGUI also lets us see the raw text of a mail message. Double-click on a message’s entry in the main window’s list to bring up a simple unformatted display of the mail’s raw text (its full text is downloaded in a thread if it hasn’t yet been fetched and cached). The raw version of the mail I sent to myself in Figure 15-17 is shown in Figure 15-27.

This raw text display can be useful to see special mail headers not shown in the formatted view. For instance, the optional “X-Mailer:” header in the raw text display identifies the program that transmitted a message; PyMailGUI adds it automatically,

Figure 15-27.  PyMailGUI raw mail text view window

along with standard headers like “From:” and “To:”. Other headers are added as the mail is transmitted: the “Received:” headers name machines that the message was routed through on its way to our email server, and “Content-Type:” is added and parsed by Python’s email package.

And really, the raw text form is all there is to an email message—it’s what is transferred from machine to machine when mail is sent. The nicely formatted display of the GUI’s view windows simply parses out and decodes components from the mail’s raw text with standard Python tools, and places them in the associated fields of the display.

{mospagebreak title=Email Replies and Forwards}

Besides allowing users to read and write email, PyMailGUI also lets users forward and reply to incoming email sent from others. To reply to an email, select its entry in the main window’s list and click the Reply button. If I reply to the mail I just sent to myself (arguably narcissistic, but demonstrative), the mail composition window shown in Figure 15-28 appears.


Figure 15-28.  PyMailGUI reply compose window

This window is identical in format to the one we saw for the Write operation, except that PyMailGUI fills in some parts automatically:

  1. The “From” line is set to your email address in your mailconfig module.
  2. The “To:” line is initialized to the original message’s “From” address (we’re replying to the original sender, after all). An email.Utils call processes the “To:”.
  3. The “Subject:” line is set to the original message’s subject line, prepended with a “Re:”, the standard follow-up subject line form (unless it already has one).
  4. The body of the reply is initialized with the signature line in mailconfig , along with the original message’s text. The original message text is quoted with > characters and is prepended with a few header lines extracted from the original message to give some context.

Luckily, all of this is much easier than it may sound. Python’s standard email module extracts all of the original message’s header lines, and a single string replace method call does the work of adding the > quotes to the original message body. I simply type what I wish to say in reply (the initial paragraph in the mail’s text area) and press the Send button to route the reply message to the mailbox on my mail server again. Physically sending the reply works the same as sending a brand-new message—the mail is routed to your SMTP server in a spawned send-mail thread, and the send-mail wait popup appears while the thread runs.

Forwarding a message is similar to replying: select the message in the main window, press the Fwd button, and fill in the fields and text area of the popped-up composition window. Figure 15-29 shows the window created to forward the mail we originally wrote and received.

Figure 15-29 PyMailGUI forward compose window

Much like with replies, “From:” is filled from mailconfig , the original text is automatically quoted in the message body again, and the subject line is preset to the original message’s subject, prepended with the string “Fwd:”. I have to fill in the “To:” line manually, though, because this is not a direct reply (it doesn’t necessarily go back to the original sender).

Notice that I’m forwarding this message to two different addresses; multiple recipient addresses are separated with a semicolon (;) in the “To:”, “Cc:”, and “Bcc:” header fields. The Send button in this window fires the forwarded message off to all addresses listed in these headers.

I’ve now written a new message, replied to it, and forwarded it. The reply and forward were sent to my email address, too; if we press the main window’s Load button again, the reply and forward messages should show up in the main window’s list. In Figure 15-30, they appear as messages 22 and 21 (the order they appear in may depend on timing issues at your server).

Figure 15-30.  PyMailGUI mail list after sends and load

Keep in mind that PyMailGUI runs on the local computer, but the messages you see in the main window’s list actually live in a mailbox on your email server machine. Every time we press Load, PyMailGUI downloads but does not delete newly arrived email from the server to your computer. The three messages we just wrote (20 through 22) will also appear in any other email program you use on your account (e.g., in Outlook, or in a web mail interface). PyMailGUI does not automatically delete messages as they are downloaded, but simply stores them in your computer’s memory for processing. If we now select message 21 and press View, we see the forward message we sent, as in Figure 15-31. This message went from my machine to a remote email server and was downloaded from there into a Python list from which it is displayed.

Figure 15-32 shows what the forward message’s raw text looks like; again, double-click on a main window’s entry to display this form. The formatted display in Figure 15-31 simply extracts bits and pieces out of the text shown in the raw display form.

Please check back next week for the continuation of this article.

Google+ Comments

Google+ Comments