Merged the wxPy_newswig branch into the HEAD branch (main trunk)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@24541 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -1,8 +1,879 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# 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}).
|
||||
|
||||
"""Renamer stub: provides a way to drop the wx prefix from wxPython objects."""
|
||||
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...
|
||||
|
||||
"""
|
||||
|
||||
_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)
|
||||
|
||||
from wx import _rename
|
||||
from wxPython.lib import ErrorDialogs
|
||||
_rename(globals(), ErrorDialogs.__dict__, modulename='lib.ErrorDialogs')
|
||||
del ErrorDialogs
|
||||
del _rename
|
||||
|
Reference in New Issue
Block a user