git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@26346 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			897 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			897 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#----------------------------------------------------------------------------
 | 
						|
# Name:         ErrorDialogs.py
 | 
						|
# Version:      1.0
 | 
						|
# Created:      September--October 2001
 | 
						|
# Author:       Chris Fama of Wholly Snakes Software,
 | 
						|
#               Chris.Fama@whollysnakes.com
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
"""
 | 
						|
ErrorDialogs.py: by Christopher J. Fama (trading under the name
 | 
						|
Wholly Snakes Software {Australian Business Number: 28379514278}).
 | 
						|
 | 
						|
This code is released under the auspices of the modified version of
 | 
						|
the GNU Library Public License (LGPL) that constitutes the license
 | 
						|
under which the GUI package wxPython is released [see the file
 | 
						|
LICENSE.TXT accompanying that distribution).  I must also thank Graham
 | 
						|
Boyd, of Boyd Mining, and CEO of the Minserve group of companies
 | 
						|
(www.minserve.com.au), for kindly allowing the release of this
 | 
						|
module under a "free" license, despite a certain part of it's
 | 
						|
development having taken place while working for him...
 | 
						|
 | 
						|
Please note that this code, written for Python 2.1, is derives from
 | 
						|
code written when Python 1.5.2 was current.  Although since Python 2.0
 | 
						|
the sys.excepthook variable has been available, for ease and potential
 | 
						|
backwards compatibility (or to be more realistic
 | 
						|
backwards-PARTIAL-compatibility!), the code catches errors by
 | 
						|
assigning a custom object to sys.stderr and monitoring calls to it's
 | 
						|
writ) method.  Such calls which take place with a new value of
 | 
						|
sys.last_traceback stand as evidence that at interpreter error has
 | 
						|
just occurred; please note that this means that NO OTHER OUTPUT TO
 | 
						|
sys.stderr WILL BE DISPLAYED.  THIS INCLUDES "warnings" generated by
 | 
						|
the interpreter.  As far as I am aware, unless your code itself writes
 | 
						|
to sys.stderr itself, these will be the only things you miss out
 | 
						|
on--and many, if not most or all, of these will occur before your code
 | 
						|
even gets the opportunity to set one of these file-like objects in
 | 
						|
place.  If this is a problem for you and you can't work around it,
 | 
						|
please contact means or the wxPython-users mailing list.
 | 
						|
 | 
						|
 | 
						|
DOCUMENTATION:
 | 
						|
 | 
						|
This is a module to display errors--either explicitly requested by a
 | 
						|
script or arbitrary interpreter errors--these needs arose in the
 | 
						|
course of a commercial (contract) project, but were not developed in
 | 
						|
the course of work on such [well, very little, albeit concurrently]...
 | 
						|
[NBNB.Does not currently support display of other than interpreter errors.]
 | 
						|
 | 
						|
Usage, after 'from wxPython.lib.ErrorDialogs import *' (all
 | 
						|
identifiers defined in this module begin with "wxPy", and many of them
 | 
						|
with "wxPyError_", so there should be no namespace conflicts...):
 | 
						|
 | 
						|
    wxPyNewErrorDialog (<win> (['frame='] <frame (can be None)>,
 | 
						|
                                         <OPTIONS>))
 | 
						|
                   ...
 | 
						|
    wxPyDestroyErrorDialogIfPresent () # e.g. when top frame destroyed)
 | 
						|
 | 
						|
for unhandled errors, or
 | 
						|
 | 
						|
    returnval = wxpyNonFatalError (<frame (can be None)>,
 | 
						|
                          <HTML message>
 | 
						|
                          [,<OPTIONS>])
 | 
						|
or
 | 
						|
    returnval = wxPyFatalError (<HTML message>,
 | 
						|
                       [,<OPTIONS, an extra one of which may be
 | 
						|
                          'frame=' <frame (defaults to None)>>])
 | 
						|
or
 | 
						|
 | 
						|
    wxPybNonWindowingError (message)
 | 
						|
 | 
						|
for explicit errors.
 | 
						|
 | 
						|
<win> is one of
 | 
						|
    wxPyNonFatalErrorDialog
 | 
						|
    wxPyFatalErrorDialog
 | 
						|
    wxPyFatalErrorDialogWithTraceback
 | 
						|
    wxPyNonFatalErrorDialogWithTraceback
 | 
						|
    wxPyNonWindowingErrorHandler
 | 
						|
 | 
						|
and the OPTIONS (with defaults) are: (please note that the options for
 | 
						|
wxPyNonWindowingErrorHandler / wxPyNonWindowingError are 'almost' a (small))
 | 
						|
subset of these):
 | 
						|
 | 
						|
    'modal' [default 1]: block until dismissed.
 | 
						|
 | 
						|
    'programname' [default "Python program"]: appears inThe
 | 
						|
    caption of the dialog, amidst it's text and (by default) in mailings.
 | 
						|
 | 
						|
    'whendismissed' option, if given, this should be a string, to be
 | 
						|
    executed in a restricted environment (see rexec module) after
 | 
						|
    dialog dismissal.  Typically, this should be Python code to "clean
 | 
						|
    up" after what was presumably the partial execution of some
 | 
						|
    operation started by the user, after the unexpected interruption
 | 
						|
    by some error [which--at least the way I work, means an unexpected
 | 
						|
    error in the code, since exceptions that may be raised by some
 | 
						|
    foreseen (in a programmatic sense) should normally be handled by
 | 
						|
    try...except clauses].
 | 
						|
    NOTE THAT CURRENTLY THE rexec CODE IS NOT WORKING, SO THIS IS JUST DONE
 | 
						|
    BY exec...
 | 
						|
 | 
						|
    'mailto': if None, give no "e-mail support" option, otherwise this
 | 
						|
    is meant to be an address for 'bug reports'; a command button will
 | 
						|
    be created for this, which pops up another dialog  [Linux], or a window of
 | 
						|
    another program [Windows].
 | 
						|
 | 
						|
    On Windows, this will launch your mailer (assuming your mailer
 | 
						|
    would start when a file with the '.eml'extension is double-clicked
 | 
						|
    in Windows Explorer--this will be the case for Microsoft Outlook
 | 
						|
    Express [tm and all those legal necessities] if installed, and the
 | 
						|
    association has not been taken over by some other program.  On
 | 
						|
    Linux, it will open a file dialog to save the message, by default
 | 
						|
    as a '.html' file...{Please note that, on Windows and with current
 | 
						|
    (when I got the machine I'm writing this on--early 2000) versions
 | 
						|
    of Outlook Express, you may need to enter your e-mail address in
 | 
						|
    the box beside the "mail support" button.)
 | 
						|
 | 
						|
    The template for the mail message is in the 'MessageTemplate'
 | 
						|
    attribute of the dialog-derived class in question (e.g.,
 | 
						|
    wxPyNonFatalErrorDialog).  Search for this in the code below to
 | 
						|
    see the default (note that this template is in HTML format).  This
 | 
						|
    attributes uses the '%{name}s' string conversion feature of Python
 | 
						|
    [see sec.2 of the Python Library Reference]; allowed Lance are:
 | 
						|
    programname version exceptionname exceptionvalue
 | 
						|
    extraexceptioninformation traceback
 | 
						|
 | 
						|
    'version': str(this) will appear after 'Version' below "Error in
 | 
						|
    <programname>" at the top of the dialog.
 | 
						|
 | 
						|
EXAMPLES:
 | 
						|
 | 
						|
    sys.stderr = wxPyNonFatalErrorWindowWithTraceback (
 | 
						|
        parentframe,
 | 
						|
        programname='sumthing',
 | 
						|
        mailto='me@sumwear',
 | 
						|
        whendismissed="from wxPython.wx import * ; wxBell()")
 | 
						|
 | 
						|
FOR INTERNATIONAL [NON-ENGLISH-SPEAKING] USE:
 | 
						|
    wxPyNonFatalErrorDialog and relatives have the method
 | 
						|
        SetText(Number NUMBER, STRING)
 | 
						|
 | 
						|
    where STRING is to displayed in the wxStaticText control with ID
 | 
						|
    wxPyError_ID_TEXT<NUMBER>--see the automatically-generated code
 | 
						|
    for information about the meaning of these...
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------
 | 
						|
import warnings
 | 
						|
 | 
						|
warningmsg = r"""\
 | 
						|
 | 
						|
##############################################################\
 | 
						|
# THIS MODULE IS DEPRECATED and NOT MAINTAINED                |
 | 
						|
##############################################################/
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
warnings.warn(warningmsg, DeprecationWarning, stacklevel=2)
 | 
						|
 | 
						|
#----------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
_debug = 0
 | 
						|
#_debug = 1 # uncomment to display some information (to stdout)
 | 
						|
Version = 1.3
 | 
						|
 | 
						|
from wxPython.wx import *
 | 
						|
import string, sys, traceback, time, rexec, operator, types, cStringIO, os
 | 
						|
#from wxPython.lib.createandsendHTMLmail import *# now inline
 | 
						|
#import MimeWriter, mimetools, tempfile, smtplib
 | 
						|
import urllib, webbrowser
 | 
						|
 | 
						|
from ErrorDialogs_wdr import *
 | 
						|
 | 
						|
# You may see from the above line that I used the excellent RAD tool
 | 
						|
# wxDesigner, by Robert Roebling, to accelerate development of this
 | 
						|
# module...  The above is left for the convenience of making future
 | 
						|
# changes with wxDesigner; also so the wxDesigner-generated codedoes
 | 
						|
# not need to precede the "hand-generated" code in this file; finally,
 | 
						|
# as a personal endorsement: it is truly a brilliant time-saver!
 | 
						|
# Please note that, at the time of writing, the wxDesigner-generated
 | 
						|
# output requires manual removal of the PythonBitmaps function--an
 | 
						|
# appropriate version of this function will be imported from a
 | 
						|
# similarly- named module.  Another manual change will have to be made
 | 
						|
# to the automatically-generated source: "parent.sizerAroundText = "
 | 
						|
# should be added [immediately] before the text similar to "item13 =
 | 
						|
# wxStaticBoxSizer( item14, wxVERTICAL )", this sizer being the one
 | 
						|
# containing the wxTextCtrl...  [IMPORTANT NOTE: THIS LINE SHOULD BE
 | 
						|
# THE ONE INSTANTIATING A wxStat2icBoxSizer, *NOT* THE wxStaticBox
 | 
						|
# ITSELF...]  As of version 1.2 [November 2001], this also needs to be
 | 
						|
# done for the {sizers around the}   wxPyClickableHtmlWindow's generated in
 | 
						|
# populate_wxPyNonFatalError and populate_wxPyFatalError--note that
 | 
						|
# for ease this sizer is still called "sizerAroundText"...
 | 
						|
 | 
						|
def wxPyDestroyErrorDialogIfPresent():
 | 
						|
    if isinstance(sys.stderr,wxPyNonFatalErrorDialog):
 | 
						|
        sys.stderr.Destroy()
 | 
						|
    sys.stderr = None
 | 
						|
 | 
						|
def wxPyNewErrorDialog(dlg):
 | 
						|
    wxPyDestroyErrorDialogIfPresent()
 | 
						|
    sys.stderr = dlg
 | 
						|
 | 
						|
class wxPyNonWindowingErrorHandler:
 | 
						|
    this_exception = 0
 | 
						|
    softspace = 0
 | 
						|
    def __init__(self,fatal=0,file=sys.__stderr__):
 | 
						|
        self.fatal = fatal
 | 
						|
        self.file = file
 | 
						|
    def write(self,s):
 | 
						|
        import sys
 | 
						|
        if s.find("Warning") <> 0\
 | 
						|
           and  self.this_exception is not sys.last_traceback:
 | 
						|
            wxPyNonWindowingError("The Python interpreter encountered an error "
 | 
						|
                              "not handled by any\nexception handler--this "
 | 
						|
                              "may represent some programming error.",
 | 
						|
                              fatal=self.fatal,
 | 
						|
                               stderr=self.file,
 | 
						|
                              last=1)
 | 
						|
        self.this_exception = sys.last_traceback
 | 
						|
 | 
						|
def wxPyNonWindowingError(msg,#output=None,errors=None,
 | 
						|
                          stderr=sys.__stderr__,
 | 
						|
                          fatal=1,
 | 
						|
                          last=None):
 | 
						|
    if os.path.exists("wxPyNonWindowingErrors.txt"):
 | 
						|
        mode = 'a+'
 | 
						|
    else:
 | 
						|
        mode = 'w'
 | 
						|
    fl = open("wxPyNonWindowingErrors.txt",mode)
 | 
						|
    if stderr is not None:
 | 
						|
        l = [fl,stderr] # so that the error will be written to the file
 | 
						|
            # before any potential error in stderr.write()... (this is largely
 | 
						|
            # for my own sake in developing...)
 | 
						|
    else:
 | 
						|
        l = [fl]
 | 
						|
    for f in l:
 | 
						|
        f.write(time.ctime (time.time ()) + ": ")
 | 
						|
        f.write(msg)
 | 
						|
        #f.flush()
 | 
						|
        if sys.exc_info () [0] is not None:
 | 
						|
            if last:
 | 
						|
                f.write('Currently handled exception:\n')
 | 
						|
                f.flush()
 | 
						|
            traceback.print_exc(file=f)
 | 
						|
            if last:
 | 
						|
                f.write('\nPrevious (?) error:\n')
 | 
						|
        elif last or sys.last_traceback:
 | 
						|
            f.write("\n\n(For wizards only) ")
 | 
						|
        if last:
 | 
						|
            if type(last) <> types.ListType or len(last) < 3:
 | 
						|
                if (hasattr(sys,"last_traceback") and sys.last_traceback):
 | 
						|
                    last = [sys.last_type ,sys.last_value,sys.last_traceback]
 | 
						|
            if type(last) == types.ListType:
 | 
						|
                traceback.print_exception(last[0],last[1],last[2],
 | 
						|
                                          None,f)
 | 
						|
         #f.flush()
 | 
						|
        if f is sys.__stderr__:
 | 
						|
            s = ' (see the file "wxPyNonWindowingErrors.txt")'
 | 
						|
        else:
 | 
						|
            s = ""
 | 
						|
        f.write("Please contact the author with a copy of this\n"
 | 
						|
                "message%s.\n" % s)
 | 
						|
        #f.flush()
 | 
						|
    fl.close()
 | 
						|
    if fatal and stderr is sys.__stderr__:
 | 
						|
        if sys.__stderr__ and sys.platform in ["windows",'nt',"win32"]:
 | 
						|
            sys.__stderr__.write(
 | 
						|
                "\nYou may have to manually close this window to exit.")
 | 
						|
        sys.exit()
 | 
						|
 | 
						|
class wxPythonRExec (rexec.RExec):
 | 
						|
    def __init__(self,securityhole=0,*args,**kwargs):
 | 
						|
        apply(rexec.RExec.__init__, (self,) + args, kwargs)
 | 
						|
        if securityhole:
 | 
						|
            self.ok_builtin_modules = self.ok_builtin_modules + \
 | 
						|
            ('wxPython', 'wxPython.wxc','wxPython.wx','wxPython.misc',
 | 
						|
             'wxPython.misc2', 'wxPython.windows', 'wxPython.gdi',
 | 
						|
             'wxPython.clip_dnd', 'wxPython.events', 'wxPython.mdi',
 | 
						|
             'wxPython.frames', 'wxPython.stattool', 'wxPython.controls',
 | 
						|
             'wxPython.controls2', 'wxPython.windows2', 'wxPython.cmndlgs',
 | 
						|
             'wxPython.windows3', 'wxPython.image', 'wxPython.printfw',
 | 
						|
             'wxc','misc', 'misc2', 'windows', 'gdi', 'clip_dnd', 'events',
 | 
						|
             'mdi', 'frames', 'stattool', 'controls', 'controls2', 'windows2',
 | 
						|
             'cmndlgs', 'windows3', 'image', 'printfw', 'wx')
 | 
						|
            # possible security hole!
 | 
						|
 | 
						|
##def wxPyFatalError(msg,frame=None,**kwargs):
 | 
						|
##    kwargs.update({'fatal' : 1})
 | 
						|
##    apply(wxPyNonFatalError,
 | 
						|
##          (frame,msg),
 | 
						|
##          kwargs)
 | 
						|
 | 
						|
class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
 | 
						|
    this_exception = 0
 | 
						|
    populate_function = populate_wxPyNonFatalErrorDialogWithTraceback
 | 
						|
    no_continue_button = False
 | 
						|
    fatal = False
 | 
						|
    modal = True
 | 
						|
    exitjustreturns = False # really only for testing!
 | 
						|
 | 
						|
    def __init__(self, parent, id,
 | 
						|
                 pos=wxDefaultPosition,
 | 
						|
                 size=wxDefaultSize,
 | 
						|
                 style=wxDEFAULT_DIALOG_STYLE,
 | 
						|
                 programname="Python program",
 | 
						|
                 version="?",
 | 
						|
                 mailto=None,
 | 
						|
                 whendismissed="",
 | 
						|
                 extraversioninformation="",
 | 
						|
                 caption="Python error!",
 | 
						|
                 versionname=None,
 | 
						|
                 errorname=None,
 | 
						|
                 disable_exit_button=False):
 | 
						|
 | 
						|
        if self.fatal:
 | 
						|
            whetherNF = ""
 | 
						|
        else:
 | 
						|
            whetherNF = "non-"
 | 
						|
        title = "A (%sfatal) error has occurred in %s!"\
 | 
						|
                % (whetherNF,programname)
 | 
						|
        self.programname = programname # save for later use
 | 
						|
        self.mailto = mailto # save for later use
 | 
						|
        self.parent = parent # save for later use
 | 
						|
        self.whendismissed = whendismissed # save for later use
 | 
						|
        self.dialogtitle = title # save for later use
 | 
						|
 | 
						|
        wxDialog.__init__(self, parent, id, title, pos, size, style)
 | 
						|
 | 
						|
        self.topsizer = self.populate_function( False,True )
 | 
						|
 | 
						|
        self.SetProgramName(programname)
 | 
						|
        self.SetVersion(version)
 | 
						|
        if errorname:
 | 
						|
            self.FindWindowById(wxPyError_ID_TEXT1).SetLabel(str(errorname))
 | 
						|
        if versionname:
 | 
						|
            self.FindWindowById(wxPyError_ID_TEXT2).SetLabel(str(versionname))
 | 
						|
        self.FindWindowById(wxPyError_ID_VERSIONNUMBER).SetLabel(str(version))
 | 
						|
        self.FindWindowById(wxPyError_ID_EXTRA_VERSION_INFORMATION).SetLabel(str(
 | 
						|
            extraversioninformation))
 | 
						|
        if caption:
 | 
						|
            self.SetTitle(caption)
 | 
						|
 | 
						|
        if not self.no_continue_button:
 | 
						|
            EVT_BUTTON(self, wxPyError_ID_CONTINUE, self.OnContinue)
 | 
						|
        if mailto:
 | 
						|
            disable_mail_button = 0
 | 
						|
        else:
 | 
						|
            disable_mail_button = 1
 | 
						|
        if not disable_mail_button:
 | 
						|
            EVT_BUTTON(self, wxPyError_ID_MAIL, self.OnMail)
 | 
						|
        else:
 | 
						|
            self.GetMailButton().Enable(False)
 | 
						|
        # disable the entry box for an e-mail address by default (NOT PROPERLY DOCUMENTED)
 | 
						|
        if not hasattr(self,"enable_mail_address_box"):
 | 
						|
            self.FindWindowById(wxPyError_ID_ADDRESS).Enable(False)
 | 
						|
        if not disable_exit_button:
 | 
						|
            EVT_BUTTON(self, wxPyError_ID_EXIT, self.OnExit)
 | 
						|
 | 
						|
    def GetExtraInformation(self):
 | 
						|
        return self.extraexceptioninformation
 | 
						|
 | 
						|
    def SetExtraInformation(self,value):
 | 
						|
        self.extraexceptioninformation = value
 | 
						|
        c = self.GetExtraInformationCtrl()
 | 
						|
        if c is not None:
 | 
						|
            c.SetLabel(str(value))
 | 
						|
            self.topsizer.Layout()
 | 
						|
 | 
						|
    def GetExtraInformationCtrl(self):
 | 
						|
        return self.FindWindowById(wxPyError_ID_EXTRAINFORMATION)
 | 
						|
 | 
						|
    def GetExceptionName(self):
 | 
						|
        return str(self.exceptiontype)
 | 
						|
 | 
						|
    def SetExceptionName(self,value):
 | 
						|
        self.exceptiontype = str(value)
 | 
						|
        c = self.GetExceptionNameCtrl()
 | 
						|
        if c is not None:
 | 
						|
            c.SetLabel(str(value))
 | 
						|
            self.topsizer.Layout()
 | 
						|
 | 
						|
    def GetExceptionNameCtrl(self):
 | 
						|
        return self.FindWindowById(wxPyError_ID_EXCEPTIONNAME)
 | 
						|
 | 
						|
    def GetTraceback(self):
 | 
						|
        try:
 | 
						|
            return self.traceback
 | 
						|
        except AttributeError:
 | 
						|
            return None
 | 
						|
 | 
						|
    def SetTraceback(self,value):
 | 
						|
        self.traceback = value
 | 
						|
        c = self.GetTracebackCtrl()
 | 
						|
        if c is not None:
 | 
						|
            s,cs = c.GetSize(), c.GetClientSize()
 | 
						|
            if value[-1] == '\n':
 | 
						|
                value = value[:-1]
 | 
						|
            if _debug:
 | 
						|
                print "%s.SetTraceback(): ...SetValue('%s' (^M=\\r; ^J=\\n))"\
 | 
						|
                      % (self,value.replace('\n',"^J"))
 | 
						|
            c.SetValue(value)
 | 
						|
 | 
						|
            # Despite using the wxADJUST_MINSIZE flag in the
 | 
						|
            # appropriate AddWindow method of the sizer, this doesn't
 | 
						|
            # size the control appropriately... evidently the control's
 | 
						|
            # GetBestSize method is not returning the "correct"
 | 
						|
            # value...  So we perform a rather ugly "fix"... note that
 | 
						|
            # this also requires that we remove the wxADJUST_MINSIZE
 | 
						|
            # flag from the AddWindow method of the sizer containing
 | 
						|
            # the wxTextCtrl, which adds the wxTextCtrl...  (this
 | 
						|
            # amounts, as of wxDesigner 2.6, to only a few mouse
 | 
						|
            # clicks...)
 | 
						|
 | 
						|
            if _debug:
 | 
						|
                size = c.GetBestSize()
 | 
						|
                print "%s.SetTraceback(): %s.GetBestSize() = (%s,%s)"\
 | 
						|
                      % (self,c,size.width,size.height)
 | 
						|
            w,h = 0,0
 | 
						|
            for v in value.split("\n"):
 | 
						|
                pw,ph,d,e = t = c.GetFullTextExtent(v)
 | 
						|
                if _debug:
 | 
						|
                    print v, t
 | 
						|
                h = h + ph + e# + d
 | 
						|
                pw = pw + wxSystemSettings_GetSystemMetric(wxSYS_VSCROLL_X)
 | 
						|
                if pw > w:
 | 
						|
                    w = pw
 | 
						|
            w = w + s.width - cs.width
 | 
						|
            h = h + s.height - cs.height
 | 
						|
            if _debug:
 | 
						|
                print "%s.SetTraceback(): calculated w,h =" % c,\
 | 
						|
                      w,h,"and sys.platform = '%s'" % sys.platform
 | 
						|
            self.sizerAroundText.SetItemMinSize (c,w,h)
 | 
						|
            c.SetSize ((w,h))
 | 
						|
            c.SetSizeHints (w,h,w,h)
 | 
						|
            c.Refresh()#.SetAutoLayout(False)
 | 
						|
 | 
						|
            #^ the reason we need the above seems to be to replace the
 | 
						|
            #faulty GetBestSize of wxTextCtrl...
 | 
						|
            #self.sizerAroundText.Layout()
 | 
						|
            self.topsizer.Layout()
 | 
						|
 | 
						|
    def GetTracebackCtrl(self):
 | 
						|
        return self.FindWindowById(wxPyError_ID_TEXTCTRL)
 | 
						|
 | 
						|
    def GetVersion(self):
 | 
						|
        return self.version
 | 
						|
 | 
						|
    def SetVersion(self,value):
 | 
						|
        self.version = value
 | 
						|
        c = self.GetVersionNumberCtrl()
 | 
						|
        if c is not None:
 | 
						|
            c.SetLabel(value)
 | 
						|
            self.topsizer.Layout()
 | 
						|
 | 
						|
    def GetVersionNumberCtrl(self):
 | 
						|
        return self.FindWindowById(wxPyError_ID_VERSIONNUMBER)
 | 
						|
 | 
						|
    def GetProgramName(self):
 | 
						|
        return self.programname
 | 
						|
 | 
						|
    def SetProgramName(self,value):
 | 
						|
        self.programname = value
 | 
						|
        c = self.GetProgramNameCtrl()
 | 
						|
        if c is not None:
 | 
						|
            c.SetLabel(value)
 | 
						|
            self.topsizer.Layout()
 | 
						|
 | 
						|
    def GetProgramNameCtrl(self):
 | 
						|
        return self.FindWindowById(wxPyError_ID_PROGRAMNAME)
 | 
						|
 | 
						|
    def GetContinueButton(self):
 | 
						|
        return self.FindWindowById(wxPyError_ID_CONTINUE)
 | 
						|
 | 
						|
    def GetMailButton(self):
 | 
						|
        return self.FindWindowById(wxPyError_ID_MAIL)
 | 
						|
 | 
						|
    def GetExitButton(self):
 | 
						|
        return self.FindWindowById(wxPyError_ID_EXIT)
 | 
						|
 | 
						|
    # write handler (which is really the guts of the thing...
 | 
						|
    # [Note that this doesn't use sys.excepthook because I already had a
 | 
						|
    # working body of code...
 | 
						|
 | 
						|
    def write(self,s):
 | 
						|
        if self.this_exception is not sys.last_traceback:
 | 
						|
          if not wxThread_IsMain():
 | 
						|
             # Aquire the GUI mutex before making GUI calls.  Mutex is released
 | 
						|
             # when locker is deleted at the end of this function.
 | 
						|
             locker = wxMutexGuiLocker()
 | 
						|
 | 
						|
          self.this_exception = sys.last_traceback
 | 
						|
            # this is meant to be done once per traceback's sys.stderr.write's
 | 
						|
            # - on the first in fact.....
 | 
						|
          try:
 | 
						|
            #from wxPython.wx import wxBell
 | 
						|
            wxBell()
 | 
						|
 | 
						|
            if _debug:
 | 
						|
                if sys.stdout: sys.stdout.write(
 | 
						|
                    'in  %s.write(): ' % self)
 | 
						|
 | 
						|
            self.exceptiontype = sys.last_type
 | 
						|
            self.extraexceptioninformation = sys.last_value
 | 
						|
            c = cStringIO.StringIO()
 | 
						|
            traceback.print_last(None,c)
 | 
						|
            self.traceback = c.getvalue()
 | 
						|
 | 
						|
            if _debug:
 | 
						|
                #import traceback
 | 
						|
                traceback.print_last(None,sys.stdout)
 | 
						|
 | 
						|
            self.SetExceptionName(str(self.exceptiontype))
 | 
						|
            self.SetExtraInformation(str(self.extraexceptioninformation))
 | 
						|
            self.SetTraceback(str(self.traceback))
 | 
						|
 | 
						|
            self.topsizer.Fit(self)
 | 
						|
            self.topsizer.SetSizeHints(self)
 | 
						|
            self.CentreOnScreen()
 | 
						|
 | 
						|
            if self.modal:
 | 
						|
                self.ShowModal()
 | 
						|
            else:
 | 
						|
                self.Show(True)
 | 
						|
 | 
						|
          except:
 | 
						|
              if not locals().has_key("c"):
 | 
						|
                  c = cStringIO.StringIO()
 | 
						|
                  c.write("[Exception occurred before data from "
 | 
						|
                          "sys.last_traceback available]")
 | 
						|
              wxPyNonWindowingError("Warning: "
 | 
						|
                                "a %s error was encountered trying to "
 | 
						|
                                "handle the exception\n%s\nThis was:"#%s\n"
 | 
						|
                                % (sys.exc_type, c.getvalue()),#, c2.getvalue()),
 | 
						|
                                stderr=sys.stdout,
 | 
						|
                                last=0)
 | 
						|
 | 
						|
 | 
						|
    # button handlers:
 | 
						|
 | 
						|
    def OnContinue(self, event):
 | 
						|
     try:
 | 
						|
        if self.whendismissed:
 | 
						|
            parent = self.parent # so whendismissed can refer to "parent"
 | 
						|
            if 1:
 | 
						|
                if _debug:
 | 
						|
                    if sys.stdout: sys.stdout.write("exec '''%s''': "
 | 
						|
                                         % (self.whendismissed))
 | 
						|
                exec self.whendismissed
 | 
						|
                if _debug: print "\n",
 | 
						|
            else:
 | 
						|
                if _debug:
 | 
						|
                    if sys.stdout: sys.stdout.write("wxPythonRExec(%s).r_exec('''%s'''): "
 | 
						|
                                         % (self.securityhole,
 | 
						|
                                            self.whendismissed))
 | 
						|
                wxPythonRExec(self.securityhole).r_exec(self.whendismissed)
 | 
						|
                if _debug: print "\n",
 | 
						|
        if self.modal:
 | 
						|
            self.EndModal(wxID_OK)
 | 
						|
        else:
 | 
						|
            self.Close ()
 | 
						|
        if _debug: print "reimporting ",
 | 
						|
        for  m in sys.modules.values():
 | 
						|
            if m and m.__dict__["__name__"][0] in string.uppercase:#hack!
 | 
						|
                if _debug:
 | 
						|
                    print m.__dict__["__name__"],
 | 
						|
                reload (m)
 | 
						|
                if _debug:
 | 
						|
                    print ' ',
 | 
						|
        if _debug:
 | 
						|
            print '\nENDING %s.OnContinue()..\n\n' % (self,),
 | 
						|
     except:
 | 
						|
      wxPyNonWindowingError("Warning: the following exception information"
 | 
						|
                        " may not be the full story.. (because "
 | 
						|
                        "a %s(%s) error was encountered trying to "
 | 
						|
                        "handle the exception)\n\n"
 | 
						|
                        % tuple(sys.exc_info()[:2]),
 | 
						|
                        stderr=sys.stdout,
 | 
						|
                        last=0)
 | 
						|
 | 
						|
    PlainMessageTemplate = \
 | 
						|
                 "Hello,\n\n"\
 | 
						|
                 '%(programname)s'\
 | 
						|
                 " error.\n\n"\
 | 
						|
                 "I encountered the following error when running your "\
 | 
						|
                 'program %(programname)s,'\
 | 
						|
                 "at %(date)s.\n\n"\
 | 
						|
                 "(The following has been automatically generated...)\n"\
 | 
						|
                 '%(traceback)s\n\n'\
 | 
						|
                 "More information follows:\n\n"\
 | 
						|
                 '[Insert more '\
 | 
						|
                 "information about the error here, such as what you were "\
 | 
						|
                 "trying to do at the time of the error.  Please "\
 | 
						|
                 "understand that failure to fill in this field will be "\
 | 
						|
                 "interpreted as an invitation to consign this e-mail "\
 | 
						|
                 "straight to the trash can!]\n\n"\
 | 
						|
                 'Yours sincerely,\n'\
 | 
						|
                 "[insert your name here]\n"
 | 
						|
 | 
						|
    HTMLMessageTemplate = \
 | 
						|
                 '<html>\n'\
 | 
						|
                 '<body>'\
 | 
						|
                 "<p>\n"\
 | 
						|
                 "<i><b>Hello,</b></i>\n<p>\n"\
 | 
						|
                 '<p><h2><font color="#CC6C00">%(programname)s</font>'\
 | 
						|
                 " error.</h2>\n"\
 | 
						|
                 "I encountered the following error when running your "\
 | 
						|
                 'program <font color="#CC6C00">%(programname)s</font>,'\
 | 
						|
                 "at %(date)s.\n<p>\n"\
 | 
						|
                 "<p>"\
 | 
						|
                 "<h2>Traceback (automatically generated):</h2>\n"\
 | 
						|
                 '<p><font size="-1">\n<pre>%(traceback)s</pre>\n<p></font><p>'\
 | 
						|
                 "\n<p>\n<h2>More information follows:</h2>\n<p>\n"\
 | 
						|
                 '<font color="#CC6C00">'\
 | 
						|
                 '<i>[Insert more '\
 | 
						|
                 "information about the error here, such as what you were "\
 | 
						|
                 "trying to do at the time of the error.  Please "\
 | 
						|
                 "understand that failure to fill in this field will be "\
 | 
						|
                 "interpreted as an invitation to consign this e-mail "\
 | 
						|
                 "straight to the trash can!]\n</i><p>\n"\
 | 
						|
                 "</font><p>\n"\
 | 
						|
                 '<i><b>Yours sincerely,</b></i>\n<p>'\
 | 
						|
                 '<font color="#CC6C00">'\
 | 
						|
                 "[insert your name here]\n"\
 | 
						|
                 "</font>\n"\
 | 
						|
                 "</body>\n"\
 | 
						|
                 "</html>\n"
 | 
						|
    # text="#000000" bgcolor="#FFFFFF">\n'\
 | 
						|
 | 
						|
    def OnMail(self,event):
 | 
						|
      try:
 | 
						|
        if _debug:
 | 
						|
            print 'Attempting to write mail message.\n',
 | 
						|
        gmtdate = time.asctime(time.gmtime(time.time())) + ' GMT'
 | 
						|
        tm = time.localtime(time.time())
 | 
						|
        date = time.asctime(tm) + ' ' +\
 | 
						|
               time.strftime("%Z",tm)
 | 
						|
        programname = self.programname
 | 
						|
        traceback = self.traceback
 | 
						|
        mailto = self.mailto
 | 
						|
 | 
						|
        subject = "Un-caught exception when running %s." % programname
 | 
						|
        mailfrom = None#self.FindWindowById (wxPyError_ID_ADDRESS)
 | 
						|
        if mailfrom:
 | 
						|
            mailfrom = mailfrom.GetValue()
 | 
						|
        if _startmailerwithhtml(mailto,subject,
 | 
						|
                                self.HTMLMessageTemplate % vars(),
 | 
						|
                                text=self.PlainMessageTemplate % vars(),
 | 
						|
                                mailfrom=mailfrom):
 | 
						|
          if not (hasattr(self,"fatal") and self.fatal):
 | 
						|
            self.OnContinue(event) # if ok, then act as if "Continue" selected
 | 
						|
      except:
 | 
						|
          wxPyNonWindowingError("Warning: the following exception information"
 | 
						|
                                " may not be the full story... (because "
 | 
						|
                                "a %s error was encountered trying to "
 | 
						|
                                "handle the original exception)\n\n"#%s"
 | 
						|
                                % (sys.exc_type,),#self.msg),
 | 
						|
                                stderr=sys.stdout,
 | 
						|
                                last=0)
 | 
						|
 | 
						|
    def OnExit(self, event):
 | 
						|
        if self.IsModal():
 | 
						|
            self.EndModal(wxID_CANCEL)
 | 
						|
        if self.exitjustreturns:
 | 
						|
            return
 | 
						|
        wxGetApp().ExitMainLoop()
 | 
						|
 | 
						|
    def SetText(self,number,string):
 | 
						|
        self.FindWindowById(eval("wxPyError_ID_TEXT%d"
 | 
						|
                                 % number)).SetLabel(string)
 | 
						|
        self.topsizer.Layout()
 | 
						|
 | 
						|
class wxPyFatalErrorDialogWithTraceback(wxPyNonFatalErrorDialogWithTraceback):
 | 
						|
    populate_function = populate_wxPyFatalErrorDialogWithTraceback
 | 
						|
    no_continue_button = True
 | 
						|
    fatal = True
 | 
						|
 | 
						|
class wxPyNonFatalErrorDialog(wxPyNonFatalErrorDialogWithTraceback):
 | 
						|
    populate_function = populate_wxPyNonFatalErrorDialog
 | 
						|
 | 
						|
class wxPyFatalErrorDialog(wxPyFatalErrorDialogWithTraceback):
 | 
						|
    populate_function = populate_wxPyFatalErrorDialog
 | 
						|
 | 
						|
def _startmailerwithhtml(mailto,subject,html,text=None,mailfrom=None):
 | 
						|
    if sys.hexversion >= 0x02000000:#\
 | 
						|
        # and sys.platform in ["windows",'nt',"w is in32"]:
 | 
						|
        s = 'mailto:%s?subject=%s&body=%s' % (mailto,
 | 
						|
                                               urllib.quote(subject),
 | 
						|
                                               urllib.quote(
 | 
						|
            text.replace('\n','\r\n'),
 | 
						|
            ""))
 | 
						|
 | 
						|
        # Note that RFC 2368 requires that line breaks in the body of
 | 
						|
        # a message contained in a mailto URL MUST be encoded with
 | 
						|
        # "%0D%0A"--even on Unix/Linux.  Also note that there appears
 | 
						|
        # to be no way to specify that the body of a mailto tag be
 | 
						|
        # interpreted as HTML (mailto tags shouldn't stuff around with
 | 
						|
        # the MIME-Version/Content-Type headers, the RFC says, so I
 | 
						|
        # can't even be bothered trying, as the Python
 | 
						|
        # webbrowser/urllib modules quite likely won't allow
 | 
						|
        # this... anyway, a plain text message is [at least!] fine...).
 | 
						|
 | 
						|
        if _debug:
 | 
						|
            t = urllib.quote(text)
 | 
						|
            if len(t) > 20 * 80:
 | 
						|
                t = t[0:20 * 80] + "..."
 | 
						|
                print "\nSummarizing (only shortened version of argument "\
 | 
						|
                      "printed here), ",
 | 
						|
            print 'webbrowser.open("' \
 | 
						|
                  'mailto:%s?subject=%s&body=%s"' % (mailto,
 | 
						|
                                                     urllib.quote(subject),
 | 
						|
                                                     t)
 | 
						|
        webbrowser.open(s)
 | 
						|
        return 1
 | 
						|
    else:
 | 
						|
        return _writehtmlmessage(mailto,subject,html,text,mailfrom=mailfrom)
 | 
						|
 | 
						|
def _writehtmlmessage(mailto,subject,html,text=None,parent=None,mailfrom=None):
 | 
						|
    dlg = wxFileDialog (parent,
 | 
						|
                        "Please choose a a file to save the message to...",
 | 
						|
                        ".",
 | 
						|
                        "bug-report",
 | 
						|
                        "HTML files (*.htm,*.html)|*.htm,*.html|"
 | 
						|
                        "All files (*)|*",
 | 
						|
                        wxSAVE | wxHIDE_READONLY)
 | 
						|
    if dlg.ShowModal() <> wxID_CANCEL:
 | 
						|
        f = open(dlg.GetPath(),"w")
 | 
						|
        dlg.Destroy()
 | 
						|
        f.write(_createhtmlmail(html,text,subject,to=mailto,mailfrom=mailfrom))
 | 
						|
        f.close()
 | 
						|
        return 1
 | 
						|
    else:
 | 
						|
        return 0
 | 
						|
 | 
						|
# PLEASE NOTE THAT THE CODE BELOW FOR WRITING A GIVEN
 | 
						|
#(HTML) MESSAGE IS BY ART GILLESPIE [with slight modifications by yours truly].
 | 
						|
 | 
						|
def _createhtmlmail (html, text, subject, to=None, mailfrom=None):
 | 
						|
    """Create a mime-message that will render HTML in popular
 | 
						|
       MUAs, text in better ones (if indeed text is not unTrue (e.g. None)
 | 
						|
       """
 | 
						|
    import MimeWriter, mimetools, cStringIO
 | 
						|
 | 
						|
    out = cStringIO.StringIO() # output buffer for our message
 | 
						|
    htmlin = cStringIO.StringIO(html)
 | 
						|
    if text:
 | 
						|
            txtin = cStringIO.StringIO(text)
 | 
						|
 | 
						|
    writer = MimeWriter.MimeWriter(out)
 | 
						|
    #
 | 
						|
    # set up some basic headers... we put subject here
 | 
						|
    # because smtplib.sendmail expects it to be in the
 | 
						|
    # message body
 | 
						|
    #
 | 
						|
    if mailfrom:
 | 
						|
            writer.addheader("From", mailfrom)
 | 
						|
            #writer.addheader("Reply-to", mailfrom)
 | 
						|
    writer.addheader("Subject", subject)
 | 
						|
    if to:
 | 
						|
            writer.addheader("To", to)
 | 
						|
    writer.addheader("MIME-Version", "1.0")
 | 
						|
    #
 | 
						|
    # start the multipart section of the message
 | 
						|
    # multipart/alternative seems to work better
 | 
						|
    # on some MUAs than multipart/mixed
 | 
						|
    #
 | 
						|
    writer.startmultipartbody("alternative")
 | 
						|
    writer.flushheaders()
 | 
						|
    #
 | 
						|
    # the plain text section
 | 
						|
    #
 | 
						|
    if text:
 | 
						|
        subpart = writer.nextpart()
 | 
						|
        subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
 | 
						|
        pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
 | 
						|
        mimetools.encode(txtin, pout, 'quoted-printable')
 | 
						|
        txtin.close()
 | 
						|
    #
 | 
						|
    # start the html subpart of the message
 | 
						|
    #
 | 
						|
    subpart = writer.nextpart()
 | 
						|
    subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
 | 
						|
    pout = subpart.startbody("text/html", [("charset", 'us-ascii')])
 | 
						|
    mimetools.encode(htmlin, pout, 'quoted-printable')
 | 
						|
    htmlin.close()
 | 
						|
    #
 | 
						|
    # Now that we're done, close our writer and
 | 
						|
    # return the message body
 | 
						|
    #
 | 
						|
    writer.lastpart()
 | 
						|
    msg = out.getvalue()
 | 
						|
    out.close()
 | 
						|
    return msg
 | 
						|
 | 
						|
def _sendmail(mailto,subject,html,text):# currently unused
 | 
						|
    """For illustration only--this function is not actually used."""
 | 
						|
    import smtplib
 | 
						|
    message = _createhtmlmail(html, text, subject)
 | 
						|
    server = smtplib.SMTP("localhost")
 | 
						|
    server.sendmail(mailto, subject, message)
 | 
						|
    server.quit()
 | 
						|
 | 
						|
def wxPyFatalError(parent,msg,**kw):
 | 
						|
    return wxPyFatalOrNonFatalError(parent,msg,fatal=1,**kw)
 | 
						|
 | 
						|
def wxPyNonFatalError(parent,msg,**kw):
 | 
						|
    return wxPyFatalOrNonFatalError(parent,msg,fatal=0,**kw)
 | 
						|
 | 
						|
def wxPyResizeHTMLWindowToDispelScrollbar(window,
 | 
						|
                                         fraction,
 | 
						|
                                         sizer=None,
 | 
						|
                                         defaultfraction=0.7):
 | 
						|
    # Try to `grow' parent window (typically a dialog), only so far as
 | 
						|
    # no scrollbar is necessary, mantaining aspect ratio of display.
 | 
						|
    # Will go no further than specified fraction of display size.
 | 
						|
    w = 200
 | 
						|
    if type(fraction) == type(''):
 | 
						|
        fraction = int(fraction[:-1]) / 100.
 | 
						|
    ds = wxDisplaySize ()
 | 
						|
    c = window.GetInternalRepresentation ()
 | 
						|
    while w < ds[0] * fraction:
 | 
						|
        c.Layout(w)
 | 
						|
        if _debug:
 | 
						|
            print '(c.GetHeight() + 20, w * ds[1]/ds[0]):',\
 | 
						|
                  (c.GetHeight() + 20, w * ds[1]/ds[0])
 | 
						|
        if c.GetHeight() + 20 < w * ds[1]/ds[0]:
 | 
						|
            size = (w,min(int ((w) * ds[1]/ds[0]),
 | 
						|
                          c.GetHeight()))
 | 
						|
            break
 | 
						|
        w = w + 20
 | 
						|
    else:
 | 
						|
        if type(defaultfraction) == type(''):
 | 
						|
            defaultfraction = int(defaultfraction[:-1]) / 100.
 | 
						|
        defaultsize = (defaultfraction * ds[0], defaultfraction * ds[1])
 | 
						|
        if _debug:
 | 
						|
            print 'defaultsize =',defaultsize
 | 
						|
        size = defaultsize
 | 
						|
    window.SetSize(size)
 | 
						|
    if sizer is not None:
 | 
						|
        sizer.SetMinSize(size)
 | 
						|
        sizer.Fit(window)
 | 
						|
        #sizer.SetSizeHints(window)
 | 
						|
 | 
						|
def wxPyFatalOrNonFatalError(parent,
 | 
						|
                             msg,
 | 
						|
                             fatal=0,
 | 
						|
                             extraversioninformation="",
 | 
						|
                             caption=None,
 | 
						|
                             versionname=None,
 | 
						|
                             errorname=None,
 | 
						|
                             version="?",
 | 
						|
                             programname="Python program",
 | 
						|
                             tback=None,# backwards compatibility, and for
 | 
						|
                             #possible future inclusion of ability to display
 | 
						|
                             #a traceback along with the given message
 | 
						|
                             #"msg"... currently ignored though...
 | 
						|
                             modal=0):
 | 
						|
    if not wxThread_IsMain():
 | 
						|
        # Aquire the GUI mutex before making GUI calls.  Mutex is released
 | 
						|
        # when locker is deleted at the end of this function.
 | 
						|
        locker = wxMutexGuiLocker()
 | 
						|
 | 
						|
    dlg = wxDialog(parent,-1,"Error!")
 | 
						|
 | 
						|
    if fatal:
 | 
						|
        populate_function = populate_wxPyFatalError
 | 
						|
    else:
 | 
						|
        populate_function = populate_wxPyNonFatalError
 | 
						|
 | 
						|
    sizer = populate_function(dlg,False,True)
 | 
						|
 | 
						|
    window = dlg.FindWindowById(wxPyError_ID_HTML)
 | 
						|
    window.SetPage(msg)
 | 
						|
    wxPyResizeHTMLWindowToDispelScrollbar(window,
 | 
						|
                                          "85%",
 | 
						|
                                          sizer=dlg.sizerAroundText)
 | 
						|
    dlg.FindWindowById(wxPyError_ID_PROGRAMNAME).SetLabel(str(programname))
 | 
						|
    if errorname:
 | 
						|
        dlg.FindWindowById(wxPyError_ID_TEXT1).SetLabel(str(errorname))
 | 
						|
    if versionname:
 | 
						|
        dlg.FindWindowById(wxPyError_ID_TEXT2).SetLabel(str(versionname))
 | 
						|
    dlg.FindWindowById(wxPyError_ID_VERSIONNUMBER).SetLabel(str(version))
 | 
						|
    dlg.FindWindowById(wxPyError_ID_EXTRA_VERSION_INFORMATION).SetLabel(str(
 | 
						|
        extraversioninformation))
 | 
						|
    if caption:
 | 
						|
        dlg.SetTitle(caption)
 | 
						|
    sizer.Fit(dlg)
 | 
						|
    sizer.SetSizeHints(dlg)
 | 
						|
    dlg.CentreOnScreen()
 | 
						|
 | 
						|
    if modal:
 | 
						|
        v = dlg.ShowModal()
 | 
						|
        dlg.Destroy()
 | 
						|
        return v
 | 
						|
    else:
 | 
						|
        dlg.Show(True)
 | 
						|
 |