New ErrorDialogs from Chris Fama

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@12393 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2001-11-13 03:09:28 +00:00
parent 5b119149c0
commit 5e217089d4
6 changed files with 831 additions and 389 deletions

View File

@@ -24,7 +24,7 @@ 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
write() method. Such calls which take place with a new value of
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
@@ -58,12 +58,10 @@ for unhandled errors, or
returnval = wxpyNonFatalError (<frame (can be None)>,
<HTML message>
[,<OPTIONS>])
[NOTE: NOT IMPLEMENTED {IN THIS VERSION} YET...]
or
returnval = wxPyFatalError (<HTML message>,
[,<OPTIONS, an extra one of which may be
'frame=' <frame (defaults to None)>>])
[NOTE: NOT IMPLEMENTED {IN THIS VERSION} YET...]
or
wxPybNonWindowingError (message)
@@ -146,11 +144,13 @@ FOR INTERNATIONAL [NON-ENGLISH-SPEAKING] USE:
_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, tempfile, os
import string, sys, traceback, time, rexec, operator, types, cStringIO, os
#from wxPython.lib.createandsendHTMLmail import *# now inline
import MimeWriter, mimetools, cStringIO, smtplib
#import MimeWriter, mimetools, tempfile, smtplib
import urllib, webbrowser
from ErrorDialogs_wdr import *
@@ -168,8 +168,11 @@ from ErrorDialogs_wdr import *
# 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 wxStaticBoxSizer, *NOT* THE wxStaticBox
# ITSELF...]
# 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):
@@ -243,7 +246,7 @@ def wxPyNonWindowingError(msg,#output=None,errors=None,
#f.flush()
fl.close()
if fatal and stderr is sys.__stderr__:
if sys.platform in ["windows",'nt',"win32"]:
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()
@@ -272,24 +275,25 @@ class wxPythonRExec (rexec.RExec):
class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
this_exception = 0
populate_function = populate_wxNonFatalErrorDialogWithTraceback
populate_function = populate_wxPyNonFatalErrorDialogWithTraceback
no_continue_button = false
fatal = false
modal = true
exitjustreturns = false # really only for testing!
def __init__(self, parent, id,
pos = wxPyDefaultPosition, size = wxPyDefaultSize,
style = wxDEFAULT_DIALOG_STYLE,
programname = "Python program",
version = "?",
mailto = None,
whendismissed = "",
disable_exit_button = false):
if _debug:
sys.stdout.write('\nwxPyNonFatalErrorWindow.__init__: '
'STARTING...\n\n')
pos=wxPyDefaultPosition,
size=wxPyDefaultSize,
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 = ""
@@ -309,6 +313,15 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
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)
@@ -320,15 +333,12 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
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)
self.nonwindowingerror = wxPyNonWindowingErrorHandler(file=sys.__stderr__,
fatal=0)
if _debug:
sys.stdout.write('\nwxPyNonFatalErrorWindow.__init__: '
'DONE.\n\n')
def GetExtraInformation(self):
return self.extraexceptioninformation
@@ -393,7 +403,7 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
pw,ph,d,e = t = c.GetFullTextExtent(v)
if _debug:
print v, t
h = h + ph + e# + d# + e
h = h + ph + e# + d
pw = pw + wxSystemSettings_GetSystemMetric(wxSYS_VSCROLL_X)
if pw > w:
w = pw
@@ -457,9 +467,9 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
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()
# 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
@@ -469,7 +479,7 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
wxBell()
if _debug:
sys.stdout.write(
if sys.stdout: sys.stdout.write(
'in %s.write(): ' % self)
self.exceptiontype = sys.last_type
@@ -488,6 +498,7 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
self.topsizer.Fit(self)
self.topsizer.SetSizeHints(self)
self.CentreOnScreen()
if self.modal:
self.ShowModal()
@@ -499,13 +510,11 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
c = cStringIO.StringIO()
c.write("[Exception occurred before data from "
"sys.last_traceback available]")
## c2 = cStringIO.StringIO()
## traceback.print_exception(None,c2)
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.__stderr__,
stderr=sys.stdout,
last=0)
@@ -517,13 +526,13 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
parent = self.parent # so whendismissed can refer to "parent"
if 1:
if _debug:
sys.stdout.write("exec '''%s''': "
if sys.stdout: sys.stdout.write("exec '''%s''': "
% (self.whendismissed))
exec self.whendismissed
if _debug: print "\n",
else:
if _debug:
sys.stdout.write("wxPythonRExec(%s).r_exec('''%s'''): "
if sys.stdout: sys.stdout.write("wxPythonRExec(%s).r_exec('''%s'''): "
% (self.securityhole,
self.whendismissed))
wxPythonRExec(self.securityhole).r_exec(self.whendismissed)
@@ -548,13 +557,32 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
"a %s(%s) error was encountered trying to "
"handle the exception)\n\n"
% tuple(sys.exc_info()[:2]),
stderr=sys.__stderr__,
stderr=sys.stdout,
last=0)
MessageTemplate = "<head>"\
"</head>"\
'<body text="#000000" bgcolor="#FFFFFF">'\
"<p>"\
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"\
@@ -576,8 +604,10 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
'<i><b>Yours sincerely,</b></i>\n<p>'\
'<font color="#CC6C00">'\
"[insert your name here]\n"\
"</font>"\
"</body>"
"</font>\n"\
"</body>\n"\
"</html>\n"
# text="#000000" bgcolor="#FFFFFF">\n'\
def OnMail(self,event):
try:
@@ -590,25 +620,24 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
programname = self.programname
traceback = self.traceback
mailto = self.mailto
message = self.MessageTemplate % vars()
subject = "Un-caught exception when running %s." % programname
if _debug:
print 'message:',message
print 'subject:,',subject
print 'sent to:',mailto
mailfrom = self.FindWindowById (wxPyError_ID_ADDRESS)
mailfrom = None#self.FindWindowById (wxPyError_ID_ADDRESS)
if mailfrom:
mailfrom = mailfrom.GetValue()
if _startmailerwithhtml(mailto,subject,message,text="",mailfrom=mailfrom):
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"
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.__stderr__,
stderr=sys.stdout,
last=0)
def OnExit(self, event):
@@ -618,46 +647,52 @@ class wxPyNonFatalErrorDialogWithTraceback(wxDialog):
return
wxGetApp().ExitMainLoop()
## if isinstance(sys.stderr,wxPyNonFatalErrorDialogWithTraceback):
## if sys.stderr == self:
## selfdestroyed = 1
## sys.stderr.Destroy()
## sys.stderr = wxPyNonWindowingErrorHandler(sys.__stderr__)
## wxSafeYield() # so remaining events are processed...
## if self.parent not in [None,NULL]:
## self.parent.Destroy()
## elif "selfdestroyed" not in locals().keys():
## self.Destroy()
def SetText(self,number,string):
self.FindWindowById(eval("wxPyError_ID_TEXT%d"
% number)).SetLabel(string)
self.topsizer.Layout()
class wxPyFatalErrorDialogWithTraceback(wxPyNonFatalErrorDialogWithTraceback):
populate_function = populate_wxFatalErrorDialogWithTraceback
populate_function = populate_wxPyFatalErrorDialogWithTraceback
no_continue_button = true
fatal = true
class wxPyNonFatalErrorDialog(wxPyNonFatalErrorDialogWithTraceback):
populate_function = populate_wxNonFatalErrorDialog
populate_function = populate_wxPyNonFatalErrorDialog
class wxPyFatalErrorDialog(wxPyFatalErrorDialogWithTraceback):
populate_function = populate_wxFatalErrorDialog
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(
string.replace(text,'\n','\r\n'),
""))
def _startmailerwithhtml(mailto,subject,html,text,mailfrom=None):
if sys.platform in ["windows",'nt',"win32"] and sys.hexversion >= 0x02000000:
name = tempfile.mktemp(".eml")
f = open(name,"w")
f.write(_createhtmlmail(html,text,subject,to=mailto,
mailfrom=mailfrom))
f.close()
try:
os.startfile(name)
except WindowsError:
# probably no association with eml files
return _writehtmlmessage(mailto,subject,html,text,mailfrom=mailfrom)
# 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)
@@ -679,14 +714,14 @@ def _writehtmlmessage(mailto,subject,html,text=None,parent=None,mailfrom=None):
else:
return 0
# PLEASE NOTE THAT THE CODE BELOW FOR STARTING MAILER WITH A GIVEN
# 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)
"""
# imported above #import MimeWriter, mimetools, cStringIO
import MimeWriter, mimetools, cStringIO
out = cStringIO.StringIO() # output buffer for our message
htmlin = cStringIO.StringIO(html)
@@ -741,7 +776,104 @@ def _createhtmlmail (html, text, subject, to=None, mailfrom=None):
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 = string.atoi(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 = string.atoi(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)