Python
  Home arrow Python arrow Page 4 - The PyMailGUI Module
Dev Shed Forums 
Administration  
AJAX  
Apache  
BrainDump  
DHTML  
Flash  
Java  
JavaScript  
Multimedia  
MySQL  
Oracle  
Perl  
PHP  
Practices  
Python  
Reviews  
Security  
Style-Sheets  
Web Services  
XML  
Zend  
Zope  
Forums Sitemap 
IBM® developerWorks 
Sun Developer Network 
E-Commerce Hosting 
Linux Web Hosting 
Managed Hosting 
Small Business Hosting 
Mobile Linux 
App Generation ROI 
VPS Hosting 
Weekly Newsletter

 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
PYTHON

The PyMailGUI Module
By: O'Reilly Media
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 2
    2007-08-08

    Table of Contents:
  • The PyMailGUI Module
  • SharedNames: Program-Wide Globals
  • ListWindows: Message List Windows
  • ViewWindows: Message View Windows

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    The PyMailGUI Module - ViewWindows: Message View Windows


    (Page 4 of 4 )

    Example 15-4 lists the implementation of mail view and edit windows. These windows are created in response to actions in list windows—View, Write, Reply, and Forward buttons. See the callbacks for these actions in the list window module of Example 15-3 for view window initiation calls.

    As in the prior module (Example 15-3), this file is really one common class and a handful of customizations. The mail view window is nearly identical to the mail edit window, used for Write, Reply, and Forward requests. Consequently, this example defines the common appearance and behavior in the view
    window superclass, and extends it by subclassing for edit windows.

    Replies and forwards are hardly different from the write window here, because their details (e.g., From: and To: addresses, and quoted message text) are worked out in the list window implementation before an edit window is created.

    Example 15-4. PP3E\Internet\Email\PyMailGui\ViewWindows.py

    ############################################################### # Implementation of View, Write, Reply, Forward windows: one class per kind.
    # Code is factored here for reuse: a Write window is a customized View window,
    # and Reply and Forward are custom Write windows. Windows defined in this
    # file are created by the list windows, in response to user actions. Caveat:
    # the 'split' pop ups for opening parts/attachments feel a bit nonintuitive.
    # 2.1: this caveat was addressed, by adding quick-access attachment buttons.
    # TBD: could avoid verifying quits unless text area modified (like PyEdit2.0),
    # but these windows are larger, and would not catch headers already changed.
    # TBD: should Open dialog in write windows be program-wide? (per-window now). ###############################################################

    from SharedNames import *    # program-wide global objects

    ############################################################### # message view window - also a superclass of write, reply, forward ###############################################################

    class ViewWindow(windows.PopupWindow, mailtools.MailParser):
        """
        a custom Toplevel, with embedded TextEditor
        inherits saveParts,partsList from mailtools.MailParser
       
    """
        # class attributes
        modelabel     = 'View'         # used in window titles
        from mailconfig import
    okayToOpenParts                    # open any attachments at all?
        from mailconfig import
    verifyPartOpens                    # ask before open each part?
        from mailconfig import
    maxPartButtons                     # show up to this many + '...'
        tempPartDir    = 'TempParts'   # where 1 selected part saved

        # all view windows use same dialog: remembers last dir
        partsDialog = Directory(title=appname + ': Select parts save directory')

        def __init__(self, headermap, showtext, origmessage=None):
            """
            header map is origmessage, or custom hdr dict for writing;
            showtext is main text part of the message: parsed or custom;
            origmessage is parsed email.Message for view mail windows
            """
            windows.PopupWindow.__init__(self, appname, self.modelabel)
            self.origMessage = origmessage
            self.makeWidgets(headermap, showtext)

        def makeWidgets(self, headermap, showtext):
            """
            add headers, actions, attachments, text editor
            """
            actionsframe = self.makeHeaders(headermap)
            if self.origMessage and self.okayToOpenParts:
               
    self.makePartButtons()
            self.editor  = textEditor.TextEditorComponentMinimal(self)
            myactions    = self.actionButtons()
            for (label, callback) in myactions:
               
    b = Button(actionsframe, text=label, command=callback)
                b.config(bg='beige', relief=RIDGE, bd=2)
                b.pack(side=TOP, expand=YES, fill=BOTH)

            # body text, pack last=clip first
           
    self.editor.pack
    (side=BOTTOM)                        # may be multiple editors
            self.editor.setAllText
    (showtext)                           # each has own content
            lines = len(showtext.splitlines( ))

            lines = min(lines + 3, mailconfig.viewheight or 20) 
            self.editor.setHeight(lines)  # else height=24, width=80
            self.editor.setWidth(80)      # or from PyEdit textConfig
            if mailconfig.viewbg:
               
    self.editor.setBg(mailconfig.viewbg)                   # colors, font in mailconfig
            if mailconfig.viewfg:
                self.editor.setFg(mailconfig.viewfg)
            if mailconfig.viewfont:       # also via editor Tools menu
                self.editor.setFont(mailconfig.viewfont)

        def makeHeaders(self, headermap):
            """
            add header entry fields, return action buttons frame
            """
            top    = Frame(self); top.pack (side=TOP,   fill=X)
            left   = Frame(top);  left.pack (side=LEFT,  expand=NO,   fill=BOTH)
            middle = Frame(top);  middle.pack(side=LEFT,  expand=NO,   fill=NONE)
            right  = Frame(top);  right.pack (side=RIGHT, expand=YES,  fill=BOTH)

            # headers set may be extended in mailconfig (Bcc)
            self.userHdrs = ()
            showhdrs = ('From', 'To', 'Cc', 'Subject')
            if hasattr(mailconfig, 'viewheaders') and mailconfig.viewheaders:
                self.userHdrs = mailconfig.viewheaders
                showhdrs += self.userHdrs

            self.hdrFields = []
           
    for header in showhdrs:
                lab = Label(middle, text=header+':', justify=LEFT)
                ent = Entry(right)
                lab.pack(side=TOP, expand=YES, fill=X)
                ent.pack(side=TOP, expand=YES, fill=X)
                ent.insert('0', headermap.get(header, '?'))
               
    self.hdrFields.append
    (ent)                           # order matters in onSend
            return left

        def actionButtons(self):    # must be method for self
            return [('Cancel',
    self.destroy),                  # close view window silently
                    ('Parts',
    self.onParts),                  # multiparts list or the body
                    ('Split', self.onSplit)]

        def makePartButtons(self):
            """
           
    add up to N buttons that open attachments/parts
            when clicked; alternative to Parts/Split (2.1);
            okay that temp dir is shared by all open messages:
            part file not saved till later selected and opened;
            partname=partname is required in lambda in Py2.4;
            caveat: we could try to skip the main text part;
            """
            def makeButton(parent, text, callback): 
               
    link = Button(parent, text=text, command=callback, relief=SUNKEN)
                if mailconfig.partfg: link.config(fg=mailconfig.partfg)
                if mailconfig.partbg: link.config(bg=mailconfig.partbg)
                link.pack(side=LEFT, fill=X, expand=YES)

            parts = Frame(self)
            parts.pack(side=TOP, expand=NO, fill=X)
            for (count, partname) in enumerate(self.partsList(self.origMessage)):
               
    if count == self.maxPartButtons:
                    makeButton(parts, '...', self.onSplit)
                    break
               
    openpart = (lambda partname=partname: self.onOnePart(partname))
                makeButton(parts, partname, openpart)

        def onOnePart(self, partname):
            """
            locate selected part for button and save and open
            okay if multiple mails open: resaves each time selected
            we could probably just use web browser directly here
            caveat: tempPartDir is relative to cwd - poss anywhere
            caveat: tempPartDir is never cleaned up: might be large,
            could use tempfile module like HTML main text part code;
            """
            try:
               
    savedir = self.tempPartDir
                message = self.origMessage
                (contype, savepath) = self.saveOnePart(savedir, partname, message)

            except:
                showerror(appname, 'Error while writing part file')
                printStack(sys.exc_info())
           
    else:
                self.openParts([(contype, os.path.abspath(savepath))])

        def onParts(self):
            """
            show message part/attachments in pop-up window;
            uses same file naming scheme as save on Split;
            if non-multipart, single part = full body text
            """
            partnames = self.partsList(self.origMessage)
            msg = '\n'.join(['Message parts:\n'] + partnames)
            showinfo(appname, msg)

        def onSplit(self):
            """
            pop up save dir dialog and save all parts/attachments there;
            if desired, pop up HTML and multimedia parts in web browser,
            text in TextEditor, and well-known doc types on windows;
            could show parts in View windows where embedded text editor
            would provide a save button, but most are not readable text;
           
    """
            savedir =
    self.partsDialog.show()     # class attr: at prior dir
            if savedir:         # tk dir chooser, not file
                try:
                   
    partfiles = self.saveParts(savedir, self.origMessage)
               
    except:
                    showerror(appname, 'Error while writing part files')
                    printStack(sys.exc_info())

                else:
                    if self.okayToOpenParts: self.openParts(partfiles)

        def askOpen(self, appname, prompt):
            if not self.verifyPartOpens:
                return True
            else:
                return askyesno
    (appname, prompt)         # pop-up dialog

        def openParts(self, partfiles):
            """
            auto-open well known and safe file types, but
            only if verified by the user in a pop up; other
            types must be opened manually from save dir;
            caveat: punts for type application/octet-stream
            even if safe filename extension such as .html;
            caveat: image/audio/video could use playfile.py;
            """
            for (contype, fullfilename) in partfiles:
               
    maintype  =
    contype.split('/')[0]                 # left side
                extension =
    os.path.splitext(fullfilename)[1]     # or [-4:]
                basename  =
    os.path.basename(fullfilename)        # strip dir

                # HTML and XML text, web pages, some media
                if contype in ['text/html', 'text/xml']:
                    if self.askOpen(appname, 'Open "%s" in browser?' % basename):
                        try:
                            webbrowser.open_new('file://' + fullfilename)
                        
    except:
                            showerror(appname, 'Browser failed: using editor') 
                  textEditor.TextEditorMainPopup(self, fullfilename)

                # text/plain, text/x-python, etc.
                elif maintype == 'text':
                    if self.askOpen(appname, 'Open text part "%s"?' % basename): 
                  textEditor.TextEditorMainPopup(self, fullfilename)

                # multimedia types: Windows opens mediaplayer, imageviewer, etc.
                elif maintype in ['image', 'audio', 'video']:
                    if self.askOpen(appname, 'Open media part "%s"?' % basename):
                        try:
                           webbrowser.open_new('file://' + fullfilename)
                        except:
                           showerror(appname, 'Error opening browser')

                # common Windows documents: Word, Adobe, Excel, archives, etc.
               
    elif (sys.platform[:3] == 'win' and
                      maintype == 'application' and
                      extension in ['.doc', '.pdf', '.xls', '.zip','.tar', '.wmv']):
                       
    if self.askOpen(appname, 'Open part "%s"?' % basename):
                            os.startfile(fullfilename)

                else: # punt!
                    msg = 'Cannot open part: "%s"\nOpen manually in: "%s"'
                    msg = msg % (basename, os.path.dirname(fullfilename))
                    showinfo(appname, msg)

    ############################################################### # message edit windows - write, reply, forward ###############################################################

    if mailconfig.smtpuser:         # user set in mailconfig?
        MailSenderClass = mailtools.MailSenderAuth        # login/password required
    else:
        MailSenderClass = mailtools.MailSender

    class WriteWindow(ViewWindow, MailSenderClass):
        """
        customize view display for composing new mail
        inherits sendMessage from mailtools.MailSender
        """
        modelabel = 'Write'

        def __init__(self, headermap, starttext):
            ViewWindow.__init__(self, headermap, starttext)
            MailSenderClass.__init_ _(self)
            self.attaches  = []                # each win has own open dialog
            self.openDialog = None             # dialog remembers last dir

        def actionButtons(self):
            return [('Cancel', self.quit),     # need method to use self
                    ('Parts', self.onParts),   # PopupWindow verifies cancel
                    ('Attach', self.onAttach),
                    ('Send ', self.onSend)]

        def onParts(self):
           
    # caveat: deletes not currently supported
            if not self.attaches:
                showinfo(appname, 'Nothing attached')
           
    else:
                msg = '\n'.join(['Already attached:\n'] + self.attaches)
                showinfo(appname, msg)

        def onAttach(self):
            """
            attach a file to the mail: name added
            here will be added as a part on Send;
            """
            if not self.openDialog:
               
    self.openDialog = Open(title=appname + ': Select Attachment File')
            filename =
    self.openDialog.show()      # remember prior dir
            if filename:
               
    self.attaches.append
    (filename)                  # to be opened in send method

        def onSend(self):
            """
            threaded: mail edit window send button press
            may overlap with any other thread, disables none but quit
            Exit,Fail run by threadChecker via queue in after callback
            caveat: no progress here, because send mail call is atomic
            assumes multiple recipient addrs are separated with ';'

            caveat: should parse To,Cc,Bcc instead of splitting on ';',
            or use a multiline input widgets instead of simple entry;
            as is, reply logic and GUI user must avoid embedded ';'
            characters in addresses - very unlikely but not impossible;
            mailtools module saves sent message text in a local file
            """
            fieldvalues = [entry.get() for entry in self.hdrFields]
            From, To, Cc, Subj = fieldvalues[:4]
            extraHdrs  = [('Cc', Cc), ('X-Mailer', appname + ' (Python)')]
            extraHdrs += zip(self.userHdrs, fieldvalues[4:])
            bodytext = self.editor.getAllText()

            # split multiple recipient lists, fix empty fields
            Tos = To.split(';')                # split to list
            Tos = [addr.strip()
    for addr in Tos]                           # spaces around
            for (ix, (name, value))
    in enumerate(extraHdrs):
               
    if value:                    # ignored if ''
                    if value == '?':         # ? not replaced
                        extraHdrs[ix] = (name, '')
                    elif name.lower( ) in ['cc', 'bcc']:
                        values = value.split(';')
                        extraHdrs[ix] = (name, [addr.strip() for addr in values])

            # withdraw to disallow send during send
            # caveat: withdraw not foolproof- user may deiconify
            self.withdraw()
            self.getPassword()      # if needed; don't run pop up in send thread!
            popup = popuputil.BusyBoxNowait(appname, 'Sending message')
            sendingBusy.incr()
            threadtools.startThread(
               
    action = self.sendMessage,
                args   = (From, Tos, Subj, extraHdrs, bodytext, self.attaches,
                    saveMailSeparator),
                context = (popup,),
                onExit  = self.onSendExit,
                onFail = self.onSendFail)

        def onSendExit(self, popup):
            # erase wait window, erase view window, decr send count
            # sendMessage call auto saves sent message in local file
            # can't use window.addSavedMails: mail text unavailable
            popup.quit()
            self.destroy()
            sendingBusy.decr()

            # poss \ when opened, / in mailconfig
            sentname = os.path.abspath(mailconfig.sentmailfile)     # also expands '.'
            if sentname in
    openSaveFiles.keys():         # sent file open?
               
    window =
    openSaveFiles[sentname]       # update list,raise
                window.loadMailFileThread()

        def onSendFail(self, exc_info, popup):
            # pop-up error, keep msg window to save or retry, redraw actions frame
            popup.quit()
            self.deiconify()
            self.lift()
            showerror(appname, 'Send failed: \n%s\n%s' % exc_info[:2])
            printStack(exc_info)
            self.smtpPassword = None       # try again
            sendingBusy.decr()

        def askSmtpPassword(self):
            """
            get password if needed from GUI here, in main thread
            caveat: may try this again in thread if no input first
            time, so goes into a loop until input is provided; see
            pop paswd input logic for a nonlooping alternative
            """
           
    password = ''
            while not password:
                prompt = ('Password for %s on %s?' %
                         (self.smtpUser, self.smtpServerName))
                password = popuputil.askPasswordWindow(appname, prompt)
            return password

    class ReplyWindow(WriteWindow):
        """
        customize write display for replying
        text and headers set up by list window
        """
        modelabel = 'Reply'

    class ForwardWindow(WriteWindow):
        """
        customize reply display for forwarding
        text and headers set up by list window
        """
        modelabel = 'Forward'

    Please check back tomorrow for the conclusion to this article.


    DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

       · This article is an excerpt from the book "Programming Python, Third Edition,"...
     

    Buy this book now. This article is excerpted from chapter 15 of the book Programming Python, Third Edition, written by Mark Lutz (O'Reilly, 2006; ISBN: 0596009259). Check it out today at your favorite bookstore. Buy this book now.

       

    PYTHON ARTICLES

    - Nested Functions in Python
    - Python Parameters, Functions and Arguments
    - Python Statements and Functions
    - Statements and Iterators in Python
    - Sequences and Sets in Python
    - Python Expressions and Operators
    - Dictionaries, Variables and Statements in Py...
    - Data Types in Python
    - The Python Language
    - SSH with Twisted
    - Mobile Programming in Python using PyS60: UI...
    - Python: Count on It
    - Python Strings: Spinning Yarns
    - Python: More Fun with Strings
    - Python: Stringing You Along

     
    Application Delivery: Everything You Wanted to Know, but Didn`t Know You Needed to Ask
    A comprehensive guide to examining the topics of Wide-area Data Services and app....

     
    Best Practices: Safe and Secure Hardware Asset Recovery
    Companies increasingly must meet EPA and local requirements for the disposal of ....

     
    Managing SSL Security in Multi-Server Environments
    Read this white paper to learn how to simplify management of your organization's....

     
    Open Source Security Myths
    Open Source Software (OSS) is computer software whose source code is available t....

     
    Power and Cooling Capacity Management for Data Centers
    This paper describes the principles for achieving power and cooling capacity man....

     




    © 2003-2008 by Developer Shed. All rights reserved. DS Cluster 5 hosted by Hostway
    Stay green...Green IT