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,125 +1,19 @@
|
||||
# Name: CDate.py
|
||||
# Purpose: Date and Calendar classes
|
||||
#
|
||||
# Author: Lorne White (email: lwhite1@planet.eon.net)
|
||||
#
|
||||
# Created:
|
||||
# Version 0.2 1999/11/08
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import time
|
||||
import wx.lib.CDate
|
||||
|
||||
Month = {2: 'February', 3: 'March', None: 0, 'July': 7, 11:
|
||||
'November', 'December': 12, 'June': 6, 'January': 1, 'September': 9,
|
||||
'August': 8, 'March': 3, 'November': 11, 'April': 4, 12: 'December',
|
||||
'May': 5, 10: 'October', 9: 'September', 8: 'August', 7: 'July', 6:
|
||||
'June', 5: 'May', 4: 'April', 'October': 10, 'February': 2, 1:
|
||||
'January', 0: None}
|
||||
|
||||
# Number of days per month (except for February in leap years)
|
||||
mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
||||
|
||||
# Full and abbreviated names of weekdays
|
||||
day_name = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
|
||||
day_abbr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', ]
|
||||
|
||||
# Return number of leap years in range [y1, y2)
|
||||
# Assume y1 <= y2 and no funny (non-leap century) years
|
||||
|
||||
def leapdays(y1, y2):
|
||||
return (y2+3)/4 - (y1+3)/4
|
||||
|
||||
# Return 1 for leap years, 0 for non-leap years
|
||||
def isleap(year):
|
||||
return year % 4 == 0 and (year % 100 <> 0 or year % 400 == 0)
|
||||
|
||||
def FillDate(val):
|
||||
s = str(val)
|
||||
if len(s) < 2:
|
||||
s = '0' + s
|
||||
return s
|
||||
|
||||
|
||||
def julianDay(year, month, day):
|
||||
b = 0L
|
||||
year, month, day = long(year), long(month), long(day)
|
||||
if month > 12L:
|
||||
year = year + month/12L
|
||||
month = month%12
|
||||
elif month < 1L:
|
||||
month = -month
|
||||
year = year - month/12L - 1L
|
||||
month = 12L - month%12L
|
||||
if year > 0L:
|
||||
yearCorr = 0L
|
||||
else:
|
||||
yearCorr = 3L
|
||||
if month < 3L:
|
||||
year = year - 1L
|
||||
month = month + 12L
|
||||
if year*10000L + month*100L + day > 15821014L:
|
||||
b = 2L - year/100L + year/400L
|
||||
return (1461L*year - yearCorr)/4L + 306001L*(month + 1L)/10000L + day + 1720994L + b
|
||||
|
||||
|
||||
def TodayDay():
|
||||
date = time.localtime(time.time())
|
||||
year = date[0]
|
||||
month = date[1]
|
||||
day = date[2]
|
||||
julian = julianDay(year, month, day)
|
||||
daywk = dayOfWeek(julian)
|
||||
daywk = day_name[daywk]
|
||||
return(daywk)
|
||||
|
||||
def FormatDay(value):
|
||||
date = FromFormat(value)
|
||||
daywk = DateCalc.dayOfWeek(date)
|
||||
daywk = day_name[daywk]
|
||||
return(daywk)
|
||||
|
||||
def FromJulian(julian):
|
||||
julian = long(julian)
|
||||
if (julian < 2299160L):
|
||||
b = julian + 1525L
|
||||
else:
|
||||
alpha = (4L*julian - 7468861L)/146097L
|
||||
b = julian + 1526L + alpha - alpha/4L
|
||||
c = (20L*b - 2442L)/7305L
|
||||
d = 1461L*c/4L
|
||||
e = 10000L*(b - d)/306001L
|
||||
day = int(b - d - 306001L*e/10000L)
|
||||
if e < 14L:
|
||||
month = int(e - 1L)
|
||||
else:
|
||||
month = int(e - 13L)
|
||||
if month > 2:
|
||||
year = c - 4716L
|
||||
else:
|
||||
year = c - 4715L
|
||||
year = int(year)
|
||||
return year, month, day
|
||||
|
||||
def dayOfWeek(julian):
|
||||
return int((julian + 1L)%7L)
|
||||
|
||||
def daysPerMonth(month, year):
|
||||
ndays = mdays[month] + (month == 2 and isleap(year))
|
||||
return ndays
|
||||
|
||||
class now:
|
||||
def __init__(self):
|
||||
self.date = time.localtime(time.time())
|
||||
self.year = self.date[0]
|
||||
self.month = self.date[1]
|
||||
self.day = self.date[2]
|
||||
|
||||
class Date:
|
||||
def __init__(self, year, month, day):
|
||||
self.julian = julianDay(year, month, day)
|
||||
self.month = month
|
||||
self.year = year
|
||||
self.day_of_week = dayOfWeek(self.julian)
|
||||
self.days_in_month = daysPerMonth(self.month, self.year)
|
||||
__doc__ = wx.lib.CDate.__doc__
|
||||
|
||||
Date = wx.lib.CDate.Date
|
||||
FillDate = wx.lib.CDate.FillDate
|
||||
FormatDay = wx.lib.CDate.FormatDay
|
||||
FromJulian = wx.lib.CDate.FromJulian
|
||||
TodayDay = wx.lib.CDate.TodayDay
|
||||
dayOfWeek = wx.lib.CDate.dayOfWeek
|
||||
daysPerMonth = wx.lib.CDate.daysPerMonth
|
||||
isleap = wx.lib.CDate.isleap
|
||||
julianDay = wx.lib.CDate.julianDay
|
||||
leapdays = wx.lib.CDate.leapdays
|
||||
now = wx.lib.CDate.now
|
||||
|
||||
@@ -1,49 +1,9 @@
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
"""
|
||||
sorry no documentation...
|
||||
Christopher J. Fama
|
||||
"""
|
||||
|
||||
|
||||
|
||||
from wxPython.wx import *
|
||||
from wxPython.html import *
|
||||
|
||||
class wxPyClickableHtmlWindow(wxHtmlWindow):
|
||||
"""
|
||||
Class for a wxHtmlWindow which responds to clicks on links by opening a
|
||||
browser pointed at that link, and to shift-clicks by copying the link
|
||||
to the clipboard.
|
||||
"""
|
||||
def __init__(self,parent,ID,**kw):
|
||||
apply(wxHtmlWindow.__init__,(self,parent,ID),kw)
|
||||
|
||||
def OnLinkClicked(self,link):
|
||||
self.link = wxTextDataObject(link.GetHref())
|
||||
if link.GetEvent().ShiftDown():
|
||||
if wxTheClipboard.Open():
|
||||
wxTheClipboard.SetData(self.link)
|
||||
wxTheClipboard.Close()
|
||||
else:
|
||||
dlg = wxMessageDialog(self,"Couldn't open clipboard!\n",wxOK)
|
||||
wxBell()
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
else:
|
||||
if 0: # Chris's original code...
|
||||
if sys.platform not in ["windows",'nt'] :
|
||||
#TODO: A MORE APPROPRIATE COMMAND LINE FOR Linux
|
||||
#[or rather, non-Windows platforms... as of writing,
|
||||
#this MEANS Linux, until wxPython for wxMac comes along...]
|
||||
command = "/usr/bin/netscape"
|
||||
else:
|
||||
command = "start"
|
||||
command = "%s \"%s\"" % (command,
|
||||
self.link.GetText ())
|
||||
os.system (command)
|
||||
|
||||
else: # My alternative
|
||||
import webbrowser
|
||||
webbrowser.open(link.GetHref())
|
||||
import wx.lib.ClickableHtmlWindow
|
||||
|
||||
__doc__ = wx.lib.ClickableHtmlWindow.__doc__
|
||||
|
||||
wxPyClickableHtmlWindow = wx.lib.ClickableHtmlWindow.wxPyClickableHtmlWindow
|
||||
|
||||
@@ -1,879 +1,25 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# 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...
|
||||
|
||||
"""
|
||||
|
||||
_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=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 = ""
|
||||
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)
|
||||
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.ErrorDialogs
|
||||
|
||||
__doc__ = wx.lib.ErrorDialogs.__doc__
|
||||
|
||||
_createhtmlmail = wx.lib.ErrorDialogs._createhtmlmail
|
||||
_sendmail = wx.lib.ErrorDialogs._sendmail
|
||||
_startmailerwithhtml = wx.lib.ErrorDialogs._startmailerwithhtml
|
||||
_writehtmlmessage = wx.lib.ErrorDialogs._writehtmlmessage
|
||||
wxPyDestroyErrorDialogIfPresent = wx.lib.ErrorDialogs.wxPyDestroyErrorDialogIfPresent
|
||||
wxPyFatalError = wx.lib.ErrorDialogs.wxPyFatalError
|
||||
wxPyFatalErrorDialog = wx.lib.ErrorDialogs.wxPyFatalErrorDialog
|
||||
wxPyFatalErrorDialogWithTraceback = wx.lib.ErrorDialogs.wxPyFatalErrorDialogWithTraceback
|
||||
wxPyFatalOrNonFatalError = wx.lib.ErrorDialogs.wxPyFatalOrNonFatalError
|
||||
wxPyNewErrorDialog = wx.lib.ErrorDialogs.wxPyNewErrorDialog
|
||||
wxPyNonFatalError = wx.lib.ErrorDialogs.wxPyNonFatalError
|
||||
wxPyNonFatalErrorDialog = wx.lib.ErrorDialogs.wxPyNonFatalErrorDialog
|
||||
wxPyNonFatalErrorDialogWithTraceback = wx.lib.ErrorDialogs.wxPyNonFatalErrorDialogWithTraceback
|
||||
wxPyNonWindowingError = wx.lib.ErrorDialogs.wxPyNonWindowingError
|
||||
wxPyNonWindowingErrorHandler = wx.lib.ErrorDialogs.wxPyNonWindowingErrorHandler
|
||||
wxPyResizeHTMLWindowToDispelScrollbar = wx.lib.ErrorDialogs.wxPyResizeHTMLWindowToDispelScrollbar
|
||||
wxPythonRExec = wx.lib.ErrorDialogs.wxPythonRExec
|
||||
|
||||
Binary file not shown.
@@ -1,776 +1,14 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Python source generated by wxDesigner from file: ErrorDialogs.wdr
|
||||
# Do not modify this file, all changes will be lost!
|
||||
#-----------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
# Include wxWindows' modules
|
||||
from wxPython.wx import *
|
||||
import wx.lib.ErrorDialogs_wdr
|
||||
|
||||
# Custom source
|
||||
from wxPython.lib.PythonBitmaps import *
|
||||
from wxPython.html import *
|
||||
from wxPython.lib.ClickableHtmlWindow import *
|
||||
__doc__ = wx.lib.ErrorDialogs_wdr.__doc__
|
||||
|
||||
# Window functions
|
||||
|
||||
wxPyError_ID_TEXT1 = 10000
|
||||
wxPyError_ID_PROGRAMNAME = 10001
|
||||
wxPyError_ID_TEXT2 = 10002
|
||||
wxPyError_ID_VERSIONNUMBER = 10003
|
||||
wxPyError_ID_EXTRA_VERSION_INFORMATION = 10004
|
||||
wxPyError_ID_STATICBITMAP1 = 10005
|
||||
wxPyError_ID_STATICBITMAP2 = 10006
|
||||
wxPyError_ID_TEXT3 = 10007
|
||||
wxPyError_ID_TEXT4 = 10008
|
||||
wxPyError_ID_TEXTCTRL = 10009
|
||||
wxPyError_ID_TEXT5 = 10010
|
||||
wxPyError_ID_CONTINUE = 10011
|
||||
wxPyError_ID_MAIL = 10012
|
||||
wxPyError_ID_TEXT6 = 10013
|
||||
wxPyError_ID_ADDRESS = 10014
|
||||
wxPyError_ID_EXIT = 10015
|
||||
wxPyError_ID_TEXT7 = 10016
|
||||
wxPyError_ID_TEXT8 = 10017
|
||||
wxPyError_ID_TEXT9 = 10018
|
||||
wxPyError_ID_TEXT10 = 10019
|
||||
wxPyError_ID_TEXT11 = 10020
|
||||
wxPyError_ID_TEXT12 = 10021
|
||||
|
||||
def populate_wxPyNonFatalErrorDialogWithTraceback( parent, call_fit = true, set_sizer = true ):
|
||||
item0 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item1 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item3 = wxStaticBox( parent, -1, "Non-fatal" )
|
||||
item3.SetFont( wxFont( 9, wxSWISS, wxNORMAL, wxBOLD ) )
|
||||
item2 = wxStaticBoxSizer( item3, wxVERTICAL )
|
||||
|
||||
item4 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item5 = wxStaticText( parent, wxPyError_ID_TEXT1, "Error in ", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item5.SetForegroundColour( wxWHITE )
|
||||
item5.SetBackgroundColour( wxRED )
|
||||
item5.SetFont( wxFont( 21, wxSCRIPT, wxNORMAL, wxBOLD ) )
|
||||
item4.AddWindow( item5, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item6 = wxStaticText( parent, wxPyError_ID_PROGRAMNAME, "wxPyError_ID_PROGRAMNAME", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item6.SetFont( wxFont( 21, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item4.AddWindow( item6, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item4, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item7 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item8 = wxStaticText( parent, wxPyError_ID_TEXT2, "Version ", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item8.SetFont( wxFont( 9, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item7.AddWindow( item8, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item9 = wxStaticText( parent, wxPyError_ID_VERSIONNUMBER, "wxPyError_ID_VERSIONNUMBER", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item9.SetFont( wxFont( 12, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item7.AddWindow( item9, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item7, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item10 = wxStaticText( parent, wxPyError_ID_EXTRA_VERSION_INFORMATION, "wxPyError_ID_EXTRA_VERSION_INFORMATION", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item10.SetFont( wxFont( 7, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item2.AddWindow( item10, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item1.AddSizer( item2, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item11 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP1, PythonBitmaps( 0 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item11, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item12 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP2, PythonBitmaps( 1 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item12, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item1, 0, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item13 = wxStaticText( parent, wxPyError_ID_TEXT3, "The Python interpreter has encountered a so-called \"un-caught exception\".", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item0.AddWindow( item13, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item15 = wxStaticBox( parent, -1, "Traceback" )
|
||||
item15.SetFont( wxFont( 6, wxSWISS, wxITALIC, wxNORMAL ) )
|
||||
parent.sizerAroundText = item14 = wxStaticBoxSizer( item15, wxVERTICAL )
|
||||
|
||||
item16 = wxStaticText( parent, wxPyError_ID_TEXT4,
|
||||
"Please don't worry if this doesn't mean anything to you.\n"
|
||||
"It will be included in the \"bug report\" mentioned below.",
|
||||
wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item16.SetFont( wxFont( 8, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item14.AddWindow( item16, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item17 = wxTextCtrl( parent, wxPyError_ID_TEXTCTRL, "wxPyError_ID_TEXTCTRL", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY )
|
||||
item17.SetFont( wxFont( 9, wxSWISS, wxNORMAL, wxNORMAL ) )
|
||||
item17.SetToolTip( wxToolTip("A \"traceback\" reports the nature and location of a Python error.") )
|
||||
item14.AddWindow( item17, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item14, 1, wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item18 = wxStaticText( parent, wxPyError_ID_TEXT5, "Please select one of the options below.", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item18.SetFont( wxFont( 8, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item0.AddWindow( item18, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item19 = wxFlexGridSizer( 3, 0, 0, 6 )
|
||||
item19.AddGrowableCol( 0 )
|
||||
item19.AddGrowableCol( 1 )
|
||||
item19.AddGrowableCol( 2 )
|
||||
|
||||
item20 = wxButton( parent, wxPyError_ID_CONTINUE, "Continue", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item20.SetDefault()
|
||||
item19.AddWindow( item20, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item21 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item22 = wxButton( parent, wxPyError_ID_MAIL, "E-mail support", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item21.AddWindow( item22, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item23 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item24 = wxStaticText( parent, wxPyError_ID_TEXT6, "Your e-mail address:", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item24.SetFont( wxFont( 8, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item23.AddWindow( item24, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item25 = wxTextCtrl( parent, wxPyError_ID_ADDRESS, "", wxDefaultPosition, wxSize(80,-1), 0 )
|
||||
item23.AddWindow( item25, 2, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item21.AddSizer( item23, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item19.AddSizer( item21, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item26 = wxButton( parent, wxPyError_ID_EXIT, "Exit immediately", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item19.AddWindow( item26, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item27 = wxStaticText( parent, wxPyError_ID_TEXT7, "Attempt to continue.", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item19.AddWindow( item27, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item28 = wxStaticText( parent, wxPyError_ID_TEXT8, "E-mail a \"bug report\" (if this is indeed a bug!).", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item19.AddWindow( item28, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item29 = wxStaticText( parent, wxPyError_ID_TEXT9, "Attempt to exit immediately.", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item19.AddWindow( item29, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE, 5 )
|
||||
|
||||
item30 = wxStaticText( parent, wxPyError_ID_TEXT10, "", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item30.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item19.AddWindow( item30, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item31 = wxStaticText( parent, wxPyError_ID_TEXT11, "(Please read any accompanying documentation first!)", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item31.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item19.AddWindow( item31, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item32 = wxStaticText( parent, wxPyError_ID_TEXT12, "(Please note that no attempt to save unsaved data will be made.)", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item32.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item19.AddWindow( item32, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item19, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
if set_sizer == true:
|
||||
parent.SetAutoLayout( true )
|
||||
parent.SetSizer( item0 )
|
||||
if call_fit == true:
|
||||
item0.Fit( parent )
|
||||
item0.SetSizeHints( parent )
|
||||
|
||||
return item0
|
||||
|
||||
wxPyError_wxPyError_ID_TEXT3 = 10022
|
||||
wxPyError_ID_EXCEPTIONNAME = 10023
|
||||
wxPyError_ID_EXTRAINFORMATION = 10024
|
||||
wxPyError_ID_TEXT13 = 10025
|
||||
wxPyError_ID_TEXT14 = 10026
|
||||
|
||||
def populate_wxPyNonFatalErrorDialog( parent, call_fit = true, set_sizer = true ):
|
||||
item0 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item1 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item2 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item4 = wxStaticBox( parent, -1, "Non-fatal" )
|
||||
item4.SetFont( wxFont( 9, wxSWISS, wxNORMAL, wxBOLD ) )
|
||||
item3 = wxStaticBoxSizer( item4, wxVERTICAL )
|
||||
|
||||
item5 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item6 = wxStaticText( parent, wxPyError_ID_TEXT1, "Error in ", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item6.SetForegroundColour( wxWHITE )
|
||||
item6.SetBackgroundColour( wxRED )
|
||||
item6.SetFont( wxFont( 21, wxSCRIPT, wxNORMAL, wxBOLD ) )
|
||||
item5.AddWindow( item6, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item7 = wxStaticText( parent, wxPyError_ID_PROGRAMNAME, "wxPyError_ID_PROGRAMNAME", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item7.SetFont( wxFont( 21, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item5.AddWindow( item7, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item3.AddSizer( item5, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item8 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item9 = wxStaticText( parent, wxPyError_ID_TEXT2, "Version ", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item9.SetFont( wxFont( 9, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item8.AddWindow( item9, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item10 = wxStaticText( parent, wxPyError_ID_VERSIONNUMBER, "wxPyError_ID_VERSIONNUMBER", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item10.SetFont( wxFont( 12, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item8.AddWindow( item10, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item3.AddSizer( item8, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item11 = wxStaticText( parent, wxPyError_ID_EXTRA_VERSION_INFORMATION, "wxPyError_ID_EXTRA_VERSION_INFORMATION", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item11.SetFont( wxFont( 7, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item3.AddWindow( item11, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item3, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item12 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP1, PythonBitmaps( 0 ), wxDefaultPosition, wxDefaultSize )
|
||||
item2.AddWindow( item12, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item13 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP2, PythonBitmaps( 1 ), wxDefaultPosition, wxDefaultSize )
|
||||
item2.AddWindow( item13, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item1.AddSizer( item2, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item1, 1, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item14 = wxStaticText( parent, wxPyError_wxPyError_ID_TEXT3, "The Python interpreter has encountered a so-called \"un-caught exception\".", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item0.AddWindow( item14, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item16 = wxStaticBox( parent, -1, "Exception information" )
|
||||
item16.SetFont( wxFont( 6, wxSWISS, wxITALIC, wxNORMAL ) )
|
||||
item15 = wxStaticBoxSizer( item16, wxVERTICAL )
|
||||
|
||||
item17 = wxStaticText( parent, wxPyError_ID_TEXT4,
|
||||
"Please don't worry if this doesn't mean anything to you.\n"
|
||||
"It will be included in the \"bug report\" mentioned below, along with a \"stack traceback\".",
|
||||
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item17.SetFont( wxFont( 8, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item15.AddWindow( item17, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item18 = wxFlexGridSizer( 2, 0, 1, 1 )
|
||||
item18.AddGrowableCol( 1 )
|
||||
|
||||
item19 = wxStaticText( parent, wxPyError_ID_TEXT5, "Name:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item19.SetFont( wxFont( 10, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item19.SetToolTip( wxToolTip("This gives the type of the error.") )
|
||||
item18.AddWindow( item19, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item20 = wxStaticText( parent, wxPyError_ID_EXCEPTIONNAME, "wxPyError_ID_EXCEPTIONNAME", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item18.AddWindow( item20, 0, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item21 = wxStaticText( parent, wxPyError_ID_TEXT6,
|
||||
"Extra\n"
|
||||
"information:",
|
||||
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item21.SetFont( wxFont( 10, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item18.AddWindow( item21, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item22 = wxStaticText( parent, wxPyError_ID_EXTRAINFORMATION, "wxPyError_ID_EXTRAINFORMATION", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item18.AddWindow( item22, 0, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item15.AddSizer( item18, 1, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item15, 0, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 10 )
|
||||
|
||||
item23 = wxStaticText( parent, wxPyError_ID_TEXT7, "Please select one of the options below.", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item23.SetFont( wxFont( 8, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item0.AddWindow( item23, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item24 = wxFlexGridSizer( 3, 0, 0, 6 )
|
||||
item24.AddGrowableCol( 0 )
|
||||
item24.AddGrowableCol( 1 )
|
||||
item24.AddGrowableCol( 2 )
|
||||
|
||||
item25 = wxButton( parent, wxPyError_ID_CONTINUE, "Continue", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item25.SetDefault()
|
||||
item24.AddWindow( item25, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item26 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item27 = wxButton( parent, wxPyError_ID_MAIL, "E-mail support", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item26.AddWindow( item27, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item28 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item29 = wxStaticText( parent, wxPyError_ID_TEXT8, "Your e-mail address:", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item29.SetFont( wxFont( 8, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item28.AddWindow( item29, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item30 = wxTextCtrl( parent, wxPyError_ID_ADDRESS, "", wxDefaultPosition, wxSize(80,-1), 0 )
|
||||
item28.AddWindow( item30, 2, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item26.AddSizer( item28, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item24.AddSizer( item26, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item31 = wxButton( parent, wxPyError_ID_EXIT, "Exit immediately", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item24.AddWindow( item31, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item32 = wxStaticText( parent, wxPyError_ID_TEXT9, "Attempt to continue.", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item24.AddWindow( item32, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item33 = wxStaticText( parent, wxPyError_ID_TEXT10, "E-mail a \"bug report\" (if this is indeed a bug!).", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item24.AddWindow( item33, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item34 = wxStaticText( parent, wxPyError_ID_TEXT11, "Attempt to exit immediately.", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item24.AddWindow( item34, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE, 5 )
|
||||
|
||||
item35 = wxStaticText( parent, wxPyError_ID_TEXT12, "", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item35.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item24.AddWindow( item35, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item36 = wxStaticText( parent, wxPyError_ID_TEXT13, "(Please read any accompanying documentation first!)", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item36.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item24.AddWindow( item36, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item37 = wxStaticText( parent, wxPyError_ID_TEXT14, "(Please note that no attempt to save unsaved data will be made.)", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item37.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item24.AddWindow( item37, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item24, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
if set_sizer == true:
|
||||
parent.SetAutoLayout( true )
|
||||
parent.SetSizer( item0 )
|
||||
if call_fit == true:
|
||||
item0.Fit( parent )
|
||||
item0.SetSizeHints( parent )
|
||||
|
||||
return item0
|
||||
|
||||
|
||||
def populate_wxPyFatalErrorDialogWithTraceback( parent, call_fit = true, set_sizer = true ):
|
||||
item0 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item1 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item3 = wxStaticBox( parent, -1, "Fatal" )
|
||||
item3.SetFont( wxFont( 9, wxSWISS, wxNORMAL, wxBOLD ) )
|
||||
item2 = wxStaticBoxSizer( item3, wxVERTICAL )
|
||||
|
||||
item4 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item5 = wxStaticText( parent, wxPyError_ID_TEXT1, "Error in ", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item5.SetForegroundColour( wxWHITE )
|
||||
item5.SetBackgroundColour( wxRED )
|
||||
item5.SetFont( wxFont( 21, wxSCRIPT, wxNORMAL, wxBOLD ) )
|
||||
item4.AddWindow( item5, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item6 = wxStaticText( parent, wxPyError_ID_PROGRAMNAME, "wxPyError_ID_PROGRAMNAME", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item6.SetFont( wxFont( 21, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item4.AddWindow( item6, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item4, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item7 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item8 = wxStaticText( parent, wxPyError_ID_TEXT2, "Version ", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item8.SetFont( wxFont( 9, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item7.AddWindow( item8, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item9 = wxStaticText( parent, wxPyError_ID_VERSIONNUMBER, "wxPyError_ID_VERSIONNUMBER", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item9.SetFont( wxFont( 12, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item7.AddWindow( item9, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item7, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item10 = wxStaticText( parent, wxPyError_ID_EXTRA_VERSION_INFORMATION, "wxPyError_ID_EXTRA_VERSION_INFORMATION", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item10.SetFont( wxFont( 7, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item2.AddWindow( item10, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item1.AddSizer( item2, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item11 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP1, PythonBitmaps( 0 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item11, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item12 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP2, PythonBitmaps( 1 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item12, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item1, 1, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item13 = wxStaticText( parent, wxPyError_ID_TEXT3, "The Python interpreter has encountered a so-called \"un-caught exception\".", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item0.AddWindow( item13, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item15 = wxStaticBox( parent, -1, "Traceback" )
|
||||
item15.SetFont( wxFont( 6, wxSWISS, wxITALIC, wxNORMAL ) )
|
||||
parent.sizerAroundText = item14 = wxStaticBoxSizer( item15, wxVERTICAL )
|
||||
|
||||
item16 = wxStaticText( parent, wxPyError_ID_TEXT4,
|
||||
"Please don't worry if this doesn't mean anything to you.\n"
|
||||
"It will be included in the \"bug report\" mentioned below.",
|
||||
wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item16.SetFont( wxFont( 8, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item14.AddWindow( item16, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item17 = wxTextCtrl( parent, wxPyError_ID_TEXTCTRL, "wxPyError_ID_TEXTCTRL", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY )
|
||||
item17.SetFont( wxFont( 9, wxSWISS, wxNORMAL, wxNORMAL ) )
|
||||
item17.SetToolTip( wxToolTip("A \"traceback\" reports the nature and location of a Python error.") )
|
||||
item14.AddWindow( item17, 0, wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item14, 0, wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item18 = wxStaticText( parent, wxPyError_ID_TEXT5, "Please select one of the options below.", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item18.SetFont( wxFont( 8, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item0.AddWindow( item18, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item19 = wxFlexGridSizer( 3, 0, 0, 6 )
|
||||
item19.AddGrowableCol( 0 )
|
||||
item19.AddGrowableCol( 1 )
|
||||
item19.AddGrowableCol( 2 )
|
||||
|
||||
item20 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item21 = wxButton( parent, wxPyError_ID_MAIL, "E-mail support", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item20.AddWindow( item21, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item22 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item23 = wxStaticText( parent, wxPyError_ID_TEXT6, "Your e-mail address:", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item23.SetFont( wxFont( 8, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item22.AddWindow( item23, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item24 = wxTextCtrl( parent, wxPyError_ID_ADDRESS, "", wxDefaultPosition, wxSize(80,-1), 0 )
|
||||
item22.AddWindow( item24, 2, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item20.AddSizer( item22, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item19.AddSizer( item20, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item25 = wxButton( parent, wxPyError_ID_EXIT, "Exit immediately", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item25.SetDefault()
|
||||
item19.AddWindow( item25, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item26 = wxStaticText( parent, wxPyError_ID_TEXT7, "E-mail a \"bug report\" (if this is indeed a bug!).", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item19.AddWindow( item26, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item27 = wxStaticText( parent, wxPyError_ID_TEXT8, "Attempt to exit immediately.", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item19.AddWindow( item27, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE, 5 )
|
||||
|
||||
item28 = wxStaticText( parent, wxPyError_ID_TEXT9, "(Please read any accompanying documentation first!)", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item28.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item19.AddWindow( item28, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item29 = wxStaticText( parent, wxPyError_ID_TEXT10, "(Please note that no attempt to save unsaved data will be made.)", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item29.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item19.AddWindow( item29, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item19, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
if set_sizer == true:
|
||||
parent.SetAutoLayout( true )
|
||||
parent.SetSizer( item0 )
|
||||
if call_fit == true:
|
||||
item0.Fit( parent )
|
||||
item0.SetSizeHints( parent )
|
||||
|
||||
return item0
|
||||
|
||||
|
||||
def populate_wxPyFatalErrorDialog( parent, call_fit = true, set_sizer = true ):
|
||||
item0 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item1 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item3 = wxStaticBox( parent, -1, "Fatal" )
|
||||
item3.SetFont( wxFont( 9, wxSWISS, wxNORMAL, wxBOLD ) )
|
||||
item2 = wxStaticBoxSizer( item3, wxVERTICAL )
|
||||
|
||||
item4 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item5 = wxStaticText( parent, wxPyError_ID_TEXT1, "Error in ", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item5.SetForegroundColour( wxWHITE )
|
||||
item5.SetBackgroundColour( wxRED )
|
||||
item5.SetFont( wxFont( 21, wxSCRIPT, wxNORMAL, wxBOLD ) )
|
||||
item4.AddWindow( item5, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item6 = wxStaticText( parent, wxPyError_ID_PROGRAMNAME, "wxPyError_ID_PROGRAMNAME", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item6.SetFont( wxFont( 21, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item4.AddWindow( item6, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item4, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item7 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item8 = wxStaticText( parent, wxPyError_ID_TEXT2, "Version ", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item8.SetFont( wxFont( 9, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item7.AddWindow( item8, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item9 = wxStaticText( parent, wxPyError_ID_VERSIONNUMBER, "wxPyError_ID_VERSIONNUMBER", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item9.SetFont( wxFont( 12, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item7.AddWindow( item9, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item7, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item10 = wxStaticText( parent, wxPyError_ID_EXTRA_VERSION_INFORMATION, "wxPyError_ID_EXTRA_VERSION_INFORMATION", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item10.SetFont( wxFont( 7, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item2.AddWindow( item10, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item1.AddSizer( item2, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item11 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP1, PythonBitmaps( 0 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item11, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item12 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP2, PythonBitmaps( 1 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item12, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item1, 1, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item13 = wxStaticText( parent, wxPyError_ID_TEXT3, "The Python interpreter has encountered a so-called \"un-caught exception\".", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item0.AddWindow( item13, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item15 = wxStaticBox( parent, -1, "Exception information" )
|
||||
item15.SetFont( wxFont( 6, wxSWISS, wxITALIC, wxNORMAL ) )
|
||||
item14 = wxStaticBoxSizer( item15, wxVERTICAL )
|
||||
|
||||
item16 = wxStaticText( parent, wxPyError_ID_TEXT4,
|
||||
"Please don't worry if this doesn't mean anything to you.\n"
|
||||
"It will be included in the \"bug report\" mentioned below, along with a \"stack traceback\".",
|
||||
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item16.SetFont( wxFont( 8, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item14.AddWindow( item16, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item17 = wxFlexGridSizer( 2, 0, 1, 1 )
|
||||
item17.AddGrowableCol( 1 )
|
||||
|
||||
item18 = wxStaticText( parent, wxPyError_ID_TEXT5, "Name:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item18.SetFont( wxFont( 10, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item18.SetToolTip( wxToolTip("This gives the type of the error.") )
|
||||
item17.AddWindow( item18, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item19 = wxStaticText( parent, wxPyError_ID_EXCEPTIONNAME, "wxPyError_ID_EXCEPTIONNAME", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item17.AddWindow( item19, 0, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item20 = wxStaticText( parent, wxPyError_ID_TEXT6,
|
||||
"Extra\n"
|
||||
"information:",
|
||||
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item20.SetFont( wxFont( 10, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item17.AddWindow( item20, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item21 = wxStaticText( parent, wxPyError_ID_EXTRAINFORMATION, "wxPyError_ID_EXTRAINFORMATION", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item17.AddWindow( item21, 0, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item14.AddSizer( item17, 0, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item14, 1, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 10 )
|
||||
|
||||
item22 = wxStaticText( parent, wxPyError_ID_TEXT7, "Please select one of the options below.", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item22.SetFont( wxFont( 8, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item0.AddWindow( item22, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item23 = wxFlexGridSizer( 3, 0, 0, 6 )
|
||||
item23.AddGrowableCol( 0 )
|
||||
item23.AddGrowableCol( 1 )
|
||||
item23.AddGrowableCol( 2 )
|
||||
|
||||
item24 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item25 = wxButton( parent, wxPyError_ID_MAIL, "E-mail support", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item24.AddWindow( item25, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item26 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item27 = wxStaticText( parent, wxPyError_ID_TEXT8, "Your e-mail address:", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item27.SetFont( wxFont( 8, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item26.AddWindow( item27, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item28 = wxTextCtrl( parent, wxPyError_ID_ADDRESS, "", wxDefaultPosition, wxSize(80,-1), 0 )
|
||||
item26.AddWindow( item28, 2, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
item24.AddSizer( item26, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item23.AddSizer( item24, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item29 = wxButton( parent, wxPyError_ID_EXIT, "Exit immediately", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item29.SetDefault()
|
||||
item23.AddWindow( item29, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item30 = wxStaticText( parent, wxPyError_ID_TEXT9, "E-mail a \"bug report\" (if this is indeed a bug!).", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item23.AddWindow( item30, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item31 = wxStaticText( parent, wxPyError_ID_TEXT10, "Attempt to exit immediately.", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item23.AddWindow( item31, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE, 5 )
|
||||
|
||||
item32 = wxStaticText( parent, wxPyError_ID_TEXT11, "(Please read any accompanying documentation first!)", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item32.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item23.AddWindow( item32, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item33 = wxStaticText( parent, wxPyError_ID_TEXT12, "(Please note that no attempt to save unsaved data will be made.)", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item33.SetFont( wxFont( 7, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item23.AddWindow( item33, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item23, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
if set_sizer == true:
|
||||
parent.SetAutoLayout( true )
|
||||
parent.SetSizer( item0 )
|
||||
if call_fit == true:
|
||||
item0.Fit( parent )
|
||||
item0.SetSizeHints( parent )
|
||||
|
||||
return item0
|
||||
|
||||
wxPyError_ID_HTML = 10027
|
||||
|
||||
def populate_wxPyNonFatalError( parent, call_fit = true, set_sizer = true ):
|
||||
item0 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item1 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item3 = wxStaticBox( parent, -1, "Non-fatal" )
|
||||
item3.SetFont( wxFont( 9, wxSWISS, wxNORMAL, wxBOLD ) )
|
||||
item2 = wxStaticBoxSizer( item3, wxVERTICAL )
|
||||
|
||||
item4 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item5 = wxStaticText( parent, wxPyError_ID_TEXT1, "Error in ", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item5.SetForegroundColour( wxWHITE )
|
||||
item5.SetBackgroundColour( wxRED )
|
||||
item5.SetFont( wxFont( 21, wxSCRIPT, wxNORMAL, wxBOLD ) )
|
||||
item4.AddWindow( item5, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item6 = wxStaticText( parent, wxPyError_ID_PROGRAMNAME, "wxPyError_ID_PROGRAMNAME", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item6.SetFont( wxFont( 21, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item4.AddWindow( item6, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item4, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item7 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item8 = wxStaticText( parent, wxPyError_ID_TEXT2, "Version ", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item8.SetFont( wxFont( 9, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item7.AddWindow( item8, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item9 = wxStaticText( parent, wxPyError_ID_VERSIONNUMBER, "wxPyError_ID_VERSIONNUMBER", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item9.SetFont( wxFont( 12, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item7.AddWindow( item9, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item7, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item10 = wxStaticText( parent, wxPyError_ID_EXTRA_VERSION_INFORMATION, "wxPyError_ID_EXTRA_VERSION_INFORMATION", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item10.SetFont( wxFont( 7, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item2.AddWindow( item10, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item1.AddSizer( item2, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item11 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP1, PythonBitmaps( 0 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item11, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item12 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP2, PythonBitmaps( 1 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item12, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item1, 0, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
parent.sizerAroundText = item13 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item14 = parent.HTMLWindow = wxPyClickableHtmlWindow( parent, wxPyError_ID_HTML )
|
||||
item13.AddWindow( item14, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item13, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item15 = wxFlexGridSizer( 3, 0, 0, 6 )
|
||||
item15.AddGrowableCol( 0 )
|
||||
item15.AddGrowableCol( 1 )
|
||||
item15.AddGrowableCol( 2 )
|
||||
|
||||
item16 = wxButton( parent, wxID_OK, "OK", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item16.SetDefault()
|
||||
item15.AddWindow( item16, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item17 = wxButton( parent, wxID_CANCEL, "Cancel", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item15.AddWindow( item17, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item18 = wxStaticText( parent, wxPyError_ID_TEXT1, "", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item15.AddWindow( item18, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item19 = wxStaticText( parent, wxPyError_ID_TEXT2, "", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT )
|
||||
item15.AddWindow( item19, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE, 5 )
|
||||
|
||||
item0.AddSizer( item15, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
if set_sizer == true:
|
||||
parent.SetAutoLayout( true )
|
||||
parent.SetSizer( item0 )
|
||||
if call_fit == true:
|
||||
item0.Fit( parent )
|
||||
item0.SetSizeHints( parent )
|
||||
|
||||
return item0
|
||||
|
||||
|
||||
def populate_wxPyFatalError( parent, call_fit = true, set_sizer = true ):
|
||||
item0 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item1 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item3 = wxStaticBox( parent, -1, "Fatal" )
|
||||
item3.SetFont( wxFont( 9, wxSWISS, wxNORMAL, wxBOLD ) )
|
||||
item2 = wxStaticBoxSizer( item3, wxVERTICAL )
|
||||
|
||||
item4 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item5 = wxStaticText( parent, wxPyError_ID_TEXT1, "Error in ", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item5.SetForegroundColour( wxWHITE )
|
||||
item5.SetBackgroundColour( wxRED )
|
||||
item5.SetFont( wxFont( 21, wxSCRIPT, wxNORMAL, wxBOLD ) )
|
||||
item4.AddWindow( item5, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item6 = wxStaticText( parent, wxPyError_ID_PROGRAMNAME, "wxPyError_ID_PROGRAMNAME", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item6.SetFont( wxFont( 21, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item4.AddWindow( item6, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item4, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item7 = wxBoxSizer( wxHORIZONTAL )
|
||||
|
||||
item8 = wxStaticText( parent, wxPyError_ID_TEXT2, "Version ", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item8.SetFont( wxFont( 9, wxROMAN, wxNORMAL, wxNORMAL ) )
|
||||
item7.AddWindow( item8, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item9 = wxStaticText( parent, wxPyError_ID_VERSIONNUMBER, "wxPyError_ID_VERSIONNUMBER", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item9.SetFont( wxFont( 12, wxROMAN, wxNORMAL, wxBOLD ) )
|
||||
item7.AddWindow( item9, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item2.AddSizer( item7, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item10 = wxStaticText( parent, wxPyError_ID_EXTRA_VERSION_INFORMATION, "wxPyError_ID_EXTRA_VERSION_INFORMATION", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE )
|
||||
item10.SetFont( wxFont( 7, wxROMAN, wxITALIC, wxNORMAL ) )
|
||||
item2.AddWindow( item10, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item1.AddSizer( item2, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item11 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP1, PythonBitmaps( 0 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item11, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item12 = wxStaticBitmap( parent, wxPyError_ID_STATICBITMAP2, PythonBitmaps( 1 ), wxDefaultPosition, wxDefaultSize )
|
||||
item1.AddWindow( item12, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item1, 1, wxADJUST_MINSIZE|wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 )
|
||||
|
||||
parent.sizerAroundText = item13 = wxBoxSizer( wxVERTICAL )
|
||||
|
||||
item14 = parent.HTMLWindow = wxPyClickableHtmlWindow( parent, wxPyError_ID_HTML )
|
||||
item13.AddWindow( item14, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item13, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item15 = wxFlexGridSizer( 2, 0, 0, 6 )
|
||||
item15.AddGrowableCol( 0 )
|
||||
|
||||
item16 = wxButton( parent, wxID_OK, "OK", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item16.SetDefault()
|
||||
item15.AddWindow( item16, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item18 = wxStaticText( parent, wxPyError_ID_TEXT3, "", wxDefaultPosition, wxDefaultSize, 0 )
|
||||
item15.AddWindow( item18, 0, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
item0.AddSizer( item15, 1, wxADJUST_MINSIZE|wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
if set_sizer == true:
|
||||
parent.SetAutoLayout( true )
|
||||
parent.SetSizer( item0 )
|
||||
if call_fit == true:
|
||||
item0.Fit( parent )
|
||||
item0.SetSizeHints( parent )
|
||||
|
||||
return item0
|
||||
|
||||
# End of generated file
|
||||
populate_wxPyFatalError = wx.lib.ErrorDialogs_wdr.populate_wxPyFatalError
|
||||
populate_wxPyFatalErrorDialog = wx.lib.ErrorDialogs_wdr.populate_wxPyFatalErrorDialog
|
||||
populate_wxPyFatalErrorDialogWithTraceback = wx.lib.ErrorDialogs_wdr.populate_wxPyFatalErrorDialogWithTraceback
|
||||
populate_wxPyNonFatalError = wx.lib.ErrorDialogs_wdr.populate_wxPyNonFatalError
|
||||
populate_wxPyNonFatalErrorDialog = wx.lib.ErrorDialogs_wdr.populate_wxPyNonFatalErrorDialog
|
||||
populate_wxPyNonFatalErrorDialogWithTraceback = wx.lib.ErrorDialogs_wdr.populate_wxPyNonFatalErrorDialogWithTraceback
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
from wxPython.py.PyCrust import *
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
from wxPython.py.PyFilling import *
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
from wxPython.py.PyShell import *
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
# Change this to an ImportError after the next release
|
||||
import warnings
|
||||
warnings.warn("PyCrust has been renamed to Py. Use 'from wx import py'", stacklevel=2)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
from wxPython.py.crust import *
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
from wxPython.py.filling import *
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
from wxPython.py.shell import *
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
|
||||
from wxPython.py.PyWrap import *
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.path.insert(0, os.curdir)
|
||||
main(sys.argv)
|
||||
@@ -1,857 +1,15 @@
|
||||
#----------------------------------------------------------------------
|
||||
# This file was generated by C:\Python21\wxPython\tools\img2py.py
|
||||
# [NBNBNB with an addendum at the end, and...]
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: PythonBitmaps.py
|
||||
# Version: 1.0
|
||||
# Created: October 2001
|
||||
# Author: Chris Fama of Wholly Snakes Software,
|
||||
# Chris.Fama@whollysnakes.com
|
||||
#----------------------------------------------------------------------------
|
||||
"""
|
||||
This file was [mainly] generated by img2py. The functions of interest are:
|
||||
import wx.lib.PythonBitmaps
|
||||
|
||||
getPythonPoweredBitmap
|
||||
getPythonPoweredImage
|
||||
getwxPythonBitmap
|
||||
getwxPythonImage
|
||||
__doc__ = wx.lib.PythonBitmaps.__doc__
|
||||
|
||||
--The meaning of these is obvious. They take no arguments.
|
||||
|
||||
PythonBitmaps
|
||||
|
||||
--This takes a single argument. If it tests True,
|
||||
getPythonPoweredBitmap() is returned, else getwxPythonBitmap() is
|
||||
returned.
|
||||
|
||||
Sorry no demo; see the demo for ErrorDialogs.py.
|
||||
"""
|
||||
|
||||
from wxPython.wx import wxBitmapFromXPMData, wxImageFromBitmap
|
||||
import cPickle, zlib
|
||||
|
||||
|
||||
def getwxPythonData():
|
||||
return cPickle.loads(zlib.decompress(
|
||||
'x\xdau}iw\xdb:\xb2\xed\xf7\xfb+\x94C\x8aN$\x92\x16\'Q\xa2H.N\x92\xe7\xc8Cl\
|
||||
\xd3\xc9Jb;\x1e\xe2x\xa0g;\xb1\xe3\xdf\xfe@\xd6.\xe8\xf4\xed\xfb\x1a+\x89\
|
||||
\x0e\x9a\x04P\x05\xa0P\xc3F\xf1\xfd\xc5\xb5\xf5?[sV\xaf\xd7r\x9d\x96m\xb9n\
|
||||
\xcb\x9e\xfb\x9f\x83\xad\xb9~\xd5\xfa\xd1Rz\xbdI\xda\xeb5\x15\xfb]Q\x91]\x1c\
|
||||
\xfc8o\xfe3\xee\xd4\xff\x7f:I\x079\xfd\xff\xddnS\xe1\x0c\xc7\xd9\xb0\xa9xS\
|
||||
\xeb\x8a\xbc\xc8\x9d\x9c*^[ME\x9e{c\xea\xe3\x83)_I\x9b\x8aVPW\x8c\xdd\xdc\
|
||||
\x1e\xd0\x13_gOdM\xc5\xc7Hv\xeb4\x15\xa3\xb8y\xc5r\\\x8f\x9exj\x06\xde\x9f\
|
||||
\xf43\xdfm*\xfe4\xaf\x8c\xed|<\xa0\x8a\xa8\xa9\xc8\xadl\x98{M\xc5\xb5\xde4\
|
||||
\xdaK\x07\xe3\xa2\xa90*\xd9\xed\xb8\xa9\x18\xdb\xcd+^\xee\x8e\xa9\xe2\xd6\
|
||||
\xa9+\xb2,\xb3\xf3\t\x91\x9f4\xdd\x0e\xfb\xee\x80\xf8Q\x8d\xea\x8aI6\xf1\'T\
|
||||
\xf1j\xc8\n\xab\xa9h\x8f\x98A\x05U$V]QL\xfa\xbeO\x15\xf9\xb4\xae\xb0\xf2I\
|
||||
\xdf"\x06}W\x88\xb8\xac\x00-\x0b\xedf\x9a\xdca\xafG\x15?\xa8\xc2\xeb\x15="n\
|
||||
\xbf!n\xe8\r\x07\x18\xfa\x16MC?\xb7\x0b\x1a\xfa7\xad\xe9\xa5\x98\xf4,\xaa\
|
||||
\xf8\xa2\xd2\xdc{}\xcc\xfd\xef\x868\xb7\xe7Z.M\xe5C\xc3\xa0\xc2\xc9\xd3\x82&\
|
||||
j\xbb\x19z\xe6\xa5\x93\x9c\xa6a\xa5a\x90?\xf0\xfd1Uh\xf4\x8a\x9d\xe7\xe3\xbc\
|
||||
\xa9(C\xf9\nU\x1c;</\x05\xd1\xf2\xa1l*\xfc\xbc\x97\xd3\xbc\xac5\xbd\x8c{i\
|
||||
\x965\xc4\xfd3\xd7\x0c\xcc\x9e\xd8c\xa7\xffO\xfdD\xd6L\xd4\xb0\x18f\x98\xb9\
|
||||
\t\xd6\xc7\xc0\x1e\x10qW\xb6|\xc5o*z]\xac\xf41\xa8\xfd\x10H\xe2\x06M\x85\xdf\
|
||||
\xf4"\xb83\xe8\xd1\xbc(\xcd\n\x1aX~> ~$\xcdn\x18{EV\xd0D}\nx\xe8`\xd0\x97f\
|
||||
\xe8\xc3\xc1\xd0\xce\xa8\xc2l\xb8\xee\xa5\x9e\xe5\xd0Jvt"n0\x18R\xc5RC\xbe]\
|
||||
\x8cm\x87\xf8\xa1x\rO3\xdf\x1b\xd2\xdc\xf6\x9b\n\xc7\xab\x0b\xf52\xeb\xb6\
|
||||
\xdfT\x9c\x86\xd4h\xdf\xeb\x13?\xde5+\xc8\x1a\xd4\x85\xb6\x07-\x98^\x9a\x83\
|
||||
\xfcu\x1a\x87\x95z\xd8\xd9{\x16\xd6z\xbf\xa0\n\xbd\x19\xd8p<\x1cf\xc4\x8f\
|
||||
\xc9\x88\xc9\x1fS\x85\xd1,\xdc\xdeD,\\\xa2\xb6l\xba\xf5\xdd|\xec\x13\x83\xde\
|
||||
\xd9\xb2Qzb\xa7Y\x1fC1M)-\x87\x9b\xe6\t\xd7r&.\x91\xff\xd2\x92\xc4\xd1r\xd87\
|
||||
\xe58\x88|\xb5\x19X\x9a\xa6NFO\xfcj*\xb2~Z`\xab\xef5\xe3\xf0\xc4\xae\xf5\xe8\
|
||||
\x89%\x8d\xa5CJO\x18\xcd*\x1c\xf4\xfd!\x0b\x03S6J,\xbc\x99\xcan\x89\xc9a\xb3\
|
||||
_\x9c\x9e\x9d\xdb\xb4\xc6\x86r\x1c`a\x8a5V\xf8`\xf2{\xdeb,^\xbf4\xe3\xc8\xf2\
|
||||
\xcc\xa5W\xfe\x99k\xc8/\x86\x855\xee5K{\x81\xf7m\x1f\xe2\xf3\xc9\x95\xbd\xd0\
|
||||
\x82i5,\xcczi\x9a\x11\x0b\'\x06\x11W\xa4}b\xe1\xa6\x85-V`\x8b\xbd\x98\xb2["\
|
||||
\xff\x9c\xf7m\x06\xf2\'\x91l\x94\xc8_\x8f\xe4+D\xfe\xbc-\x1b\xa5\x8am\xda\
|
||||
\xc8\xbdl\x02\x91\xf3R\xca6\x88Aw\x95l\x836a\x97$\xae\x9b\xf62Z\x96?\x9b\x1d\
|
||||
e\x8f\xc7=\x87\x18\xb4%\xc7\x01\x9e&\xb2\xd1\x9c\x16\xdd\xaf.\xb6i\x8em\xfaf\
|
||||
\xca\'\x88A?H\xc0\xdaEoB\x15\x9b\xf4J\xd1\x13"\x86dr3tw\xe8\x0e!\xa5Z4Q\xde\
|
||||
\xd8\x85<\xcd\x9b\xfd2\x9c\x0c\xb3!\r}\x1a\xca^\xa8b\xdf\x92\xc4\x11\xb5{\
|
||||
\xcdD\r\x9dA\x91\xd2\x13\x8bM\xb7vj\x0fA\xdchD\xeb4\xb3@\\X\xa2\x17\x1f\xe2\
|
||||
\xe2\xde\x95\xbd\xd0\x06j7B\xc9\x17L\x1e\x10-\x9f\x9b\x81M\xf2\xc9\x10\xc4M:\
|
||||
\xb2\x82&\xfbw,+ .\x9a\xa1\x17\xe2p\xc5\xec_\xd0\xe2\xb7\x87\x1ef\xff\xb1Y\
|
||||
\x96N\xdfq\x1c\x9a\xca\xfbY\x1b4\xd9_i\xe1\x16\x99\x8d\xb9\xfd\x93`i\xb3<]BE\
|
||||
\x91\xa1"$9\xd6\xf7{C\x9c\x95t\xaa{\xe2\xbc\x85\x80\r$?H\xc0\x0eC\x92\x0eC\
|
||||
\x07Ol\x90\x1cs\xeaB<%\x19$\x04_A\x07N\x12\xca6\x88\x96\x95J\xbeBC\xff\x94P\
|
||||
\xc5\xb0\x87\x8a\xd6\xec\x15\x9c\xfb\xcd\n\xea\xbb}\xc7\'\xe2,\x9a\x97\xe1\
|
||||
\xd0M\xa9\xe2\x99v\xe5X(58+#~\x05\x8a\xc0\xa3)\x89#Z"\x03ZN:\xa4\x8a\xebf\
|
||||
\x03\x89]\x9c\xbaD\\4{\x85hi\xcf*hY\xf6\x15\xf9\n\xcd\xadN<\xcd}\x7fH\x15[\
|
||||
\xa4K\x89c~@s{\xe8\xd2\x82\xe9\x17\x03"\xae\xd0H\xdd\xc8\x87\x1e\xd1\xf2@\
|
||||
\xa7\x98S\xf4\x86T1\xc6\xd9\xe0\xb0\xd8\xf2\xd1hQ\x80\xda\xab\x86\x85\xb9#t)\
|
||||
\xa2\xe5\x894\x94L\xe8A\xb4p\'6\xcd\xcb\xb07\xa4\x85;&\xe90\x10\xfa\x07U<\
|
||||
\x93\xb4\xccR\x0f\xc4]B\x0f\x12\x8d\x12-\xddf\x13\x8a3\xcc\xf2\x89\x96m\x12\
|
||||
\xc1\xce\xd0J\x89\x96\xf9\x86\xa7\x99/\x94\t\x9a\xca\xdbf\x9d\x16i\xe1\x14\
|
||||
\xf4\x84\xebB\xdb\xca \x94F\xa6\xec\x96\xa8]#\x9eZ\xf6\x18b\xfc\x1dt`\xa1\
|
||||
\x19\x11\xb5\x87\xe0G\xdfF\xc5\xb8\xc3\xe3\xc8\xa8\xe2W\xd3h>\x14\xf2dHb\x9c\
|
||||
\xe4\xc7p\x92\xf5\x86\x8d\x18?\xae\xe4\x13D\xfeS(\xdbp\xe8\x15K\xd2\x925\xaf\
|
||||
\x94\r\xb5\x99\x9d\xf6\xc1\xa0\xaa#\xdb\xa0\nc\x84M\xd8\x07\xc7\xceB\xe6zJ\
|
||||
\x15\x95#\x1b\xa5WN\x9b\n\xaf\xefy\x90t\'\xb3\n\xda/I\xcc\x92\x0e\x0c\xcae\
|
||||
\x05\xa4\xd4[\xb3\xd6\x85*\xc5\xe7\xfe&\x8d\xc3J\xf3\x9cf\xff\xb8Y\x1fb\x14~\
|
||||
J\xdb4iX(\x94 Qh\xb2y\x03\r\xc74\xd2\xef\xcd\xb2\xcc\xc5q\x03j;\x8dB2\x10\
|
||||
\xcb\x16\x9a\xc1\x8fY/\xb4\x1c\x1e\x02\xe6GA\x15\x13\x129\xe3\xba4\x15\x83)i\
|
||||
}B\x97\x82\xd6G+\xc8J-\xc8\xe4T\x97\x8dR\x85\xd2\xe2\x15\x04\x05\xde6\xe5\
|
||||
\x13tF\xfd5\xb1\xf8\x0bT\x8c\x1b\xb9\xee:Be\xa1W\xd6\xe8X\xebIM\xe9\x9a\x8c\
|
||||
\x11+\xf7\xa0I\x0fb\xd9(U\xa8!O%\xac\x82\x1ftF\xd9\xe2\xdc#\x16\xa6\xa4nd\
|
||||
\xbe\x03\x16\xf6H\x11\xf0R\x1b\xe7\\\xd8\xb0P\x9c\x14\x0eX\x18\x92\xc93\xc9}\
|
||||
\x9c\xfb\xbb\rq\xe3|\xec\xe2(\xd8\xd6XU\x80\x1ed\x91P\xf2\x85n\t\xb5G\x91\
|
||||
\xb4\x10\x83.H\x18XBg\xa3\x8a\xc7\x86\x85\x93\xa2\xb6q\xe8l\x08\xb1\x91\x0b\
|
||||
\xf0\xf4\x0b\xc9 \xdfgm<L\xb8\x17p\xec\x9a$\xbf\x9f9\xe0\xe9b(\x1b\xa5\x8a\
|
||||
\xbb\x16\x04}\n;j\xa3\x99\xca~\x9a\xbb>\xb1\xd0\x07\x93\x8bAA\x15\xfb\xb6l\
|
||||
\x83*\xe6\x9bq\x0c\x1c?\x87\x08~oI\x06\xd1\xae\xbc\x89\xe58\x88\xc9\xeb\xb3n\
|
||||
\x89\x85\xa6\xc1S\xe9Q\xc5*i(c\xa9K\xbd#-\xb8\xe8\xa7=\xa2\xf6,\x96\x03\xa3\
|
||||
\x8a\xf7\x0e\xaf\xa0\x82\x18\xd4!Y\xd8\x97z\xd0Q\x97\x18$\x146b\x90\x86\xd3t\
|
||||
hA\x06}o\xf1\xd0\xa1\x17\x9e\xd1\xcc\t\x03\xc6\xa5\x8a\x85f\xe62\'\x1d\xa4D\
|
||||
\x8bOr\xac/\x14zZ0\xddf\xe8\xb6\xd0\x9dl\xaa(g\xc4\xd1+w$O\xbd|\\@\\\xf0a\
|
||||
\xe1B\xf9\x1eir\xe8\xb4`\xbe\x91t\x18O\x86\x90\xb8\xeb8\xf8\n{H\x15!\x1d\x16\
|
||||
\xfd\xbe\xd3\'~l\x85\xd8\xc8.\xf8q\xe7r\xa3\x109\x7fh\xd1\xd9\xc3\xde\x80f\
|
||||
\xff\xa5aP6\x10g6|\x06\rq\xb9\x9d\xf50\xf4n)\x9f\xa0\x89\xdah6\xd0\xd0/2X\
|
||||
\xd1w\xcd+\x83\x9e0\xab\xb1_h\xb2\xfbckB\xb2p\x95x:q&\x10\x8e\x1fh*\xfdb2\
|
||||
\xa6\x03\xc7i\x0e`\xa1\x9d\xf7\x07T\xf1\xd8\x92\xb4\xc0\xac&c5-\n\x9c/w\x01\
|
||||
\xb6\xa9\x0f\x01\xbb#_\x81<\x9do1\xd7\xc7\xb4N{\n\x1d\'u!\xf9\x11r\x1b\xd0\
|
||||
\x82\x7f`W\xf6\'\xb0\xb4\xaaY\x1b\xc4\x8f#\x12[inci/\x94\xb2\r\x9a\xfdq#\xb6\
|
||||
\x1c\xdb\xe9\xc16=\xedb\xef\x17PHvM\xd9(ql\xd0\xbc\xe2\t\xe9\xe0Q\x85\xd6\
|
||||
\x96\xafP\xc5\xfbJvK,,\xb0\x1b\x86\xfd\x01Ux\xa1\x9c(<1%\xb7K6\xc4\x92\xbaWd\
|
||||
\xa3XA\xae|\x85*VigO\x06iJ\xd3\xa0\xd2q\xd2\xcf2\x88\x9c3R\x14m7u\xa0\x9eOy\
|
||||
\x15Bc+y\x8by\xd8b;\xcd\x82\xb1\x84\x94\xb6\xa8b\xa5i\xa3\xe8\xe79\\\x04\x1e\
|
||||
\x8cw\xdfC\x1b\xcf\xb6d!M\xd4\xef\x12\x87\xf8\x18\xeb\xd4\xf3d\xb7T\xe1\xc4\
|
||||
\xd0\xc7\x8a\x01&\x8a\x0f\x8b!\x8c\xd5\n\x8a\x91\x9fA\xa7k5\x8dN\x9cq\x81U\
|
||||
\xa86\x0c*r\xa1\xf6P\xc5VS1H\x07\x1e\x96e\x9b\xa4\xd40\x9d\x80\x85\xbf\x03\
|
||||
\x9e\x17\xbc\xb2\xda\x85\t\xe8\x0e\x88\xb8\xf7M/\xc2H\x1a\xe2H\xfaL\x1c\x9b\
|
||||
\x14\xc3>\x11\xf75\x91\xafP\x853\xe3\x18U(#90Z\x96o\x95|\x82\xa8\x8dH\x1d\
|
||||
\xed\x0b\x81B\x8bN%O^\xaf\xdf\xf3i\x05}\xc6\x82\x91zahA\x85ck\xed\xcd\x91\
|
||||
\xb4PE/\x90\xdd\x12\x0b7,\xc91\xaa\xf8\xe2@\xc2\xb0\xd4>\xa6\xbd/8\n\x0bg\
|
||||
\xa5$3ab\xc3LX#\x9d.\x13:\x1dU|\xee\xca\'Hl\xed\x8e \x93\xb31\xad\xb1>-\x07q\
|
||||
\xe2`\xd1\xddc\x8d\x8d\xd9(R\x9aF\x05\xbb2\x98\t\xbb\x01\xc4g\x81C\xab\x08\
|
||||
\xe54PE.\x97\xc3\x90\xe6\xe5\xa7\xc3\x92\x1f\xf3rI\x03\xebe6\xfc0\xab\xb36h\
|
||||
\x1a\x9c\xae\xa4\x85\xa6\xe1-\x91\xe3\xa0\x8aQ(\xc7A<\xfdn\xcb6\xa8\xe2\xc2\
|
||||
\x82\x8b\xd1\xc2Q\xf0\xc7\x85,L\xb1\xb3\x1f\x1a\x9eN&\x13\x7f\x02\xdb#\xc2\
|
||||
\x91\x94\xd3*\xfcg\x8e<\x02\x13\xa1\xc1\xfa\x8d\xd2\xbb\xd4\xc2Y\x99\xe3\xdc\
|
||||
_\x81\x0fe8\x04\xc7\n\xf2\xb0\n\xeb\x16\nZ*\x1b\x85,\xbciv\xa5\xeb\x8a\xcd\
|
||||
\x00\x16\x96\x90\x0e\xe3\x94\xc8\xdf\xd2\xb0\xc5\n\x8f*65\x1ck9\x0c\xefa\xd3\
|
||||
hQ\x14\x16\x1cU\xbf\xd8\xd9\xe5\x92A\xf3\xcf\x1cUX^\xcfK\x9b\xa1\xf7\xa8\xdb\
|
||||
\xdc\xcd\xc6\xd4\xe8\x19\xc9B?\xb5\xd9\x8dM\x82\xde\xcd&`\xe1\x87\x16\xf3\
|
||||
\x14Z\xf02\x1c\xbbu\xa1\xc3\x02\x1a\xbd?\xc4\xa1\xf5=\x91O\x10\xd7\x97U\xd9\
|
||||
\x0b\xbc_t\x12\n\x16\xa3"\xa3\xb3\xc1\xc9-\x08\x94#G\x8e\x94*\xe6M\xd9\x06M\
|
||||
\xd4Z\xd3Fa\xe5>\x04\xec\xf1\xec\tZ\xc9Q \xb9N\x8b\x7f\xde\xc2F\xee\xe1\xe0;\
|
||||
+!\xc6YO>\xef\xd0T\xbalWv<\x96R)Ul\x87\xbc\x1c r\xda1\xce\xfd\x01\xdc\x1d\
|
||||
\xd7\x95l\x14\xfeB\r^\'\x17\x07\xce\xc1\x88\xa9\x85\xf7\xabOm\xf4r?%j\xd7\
|
||||
\x1b\xf2\x87\xf9 \x05\xf9\xdb\x06\xcf-N\x8f.[\xd1cT\xfc\x9ab\xdf\xb2M\xf8\
|
||||
\x89\x8c\x91\xa1TX\xefJ\xc8\x8f1\x0e\xf1\x1e\xc9\xb1\\\xea\xa7\xa3)\xb9\xd3\
|
||||
\x8b\xd4\xa5\xf5\xf1uJ\xa6W>\xe9\xc3\xb5G\xf2T\xa8E\xa0v,\x97%\x8e\xe8\xbf\
|
||||
\x92ZTl\x07r\x1c\xb4\xc6\x86\r\xb5i\x91\x0e\xf1\xc4\x06\xd6\xe90\x85M\xf8n\
|
||||
\xf6\x04U\xac\x90\xadn\xfb\x16$\xee\xb4\xc5S\xc9\xce\xd0HvK\x8bN\x89e\xa3T\
|
||||
\xb1\x14\xf2\xbc\xc0\x16["\xefWo\x90\xe3\x89t\xf6\n\xf1t)\x86\xef\x82Ms\x8b\
|
||||
\xb4\x9c\x9e0\xb3\x89\xc9\xbb6\x13\x07\x17t\xe4\xc9F\x89\xeb\x0f\xac\xa0A\
|
||||
\xe9\xfdg\xce\x81\x1c\x9b\xe4v\xb3+\xdb\xbc=\xfa\xd8\x84\x8fm\xf2\x9e\xe7ch9\
|
||||
\xbdY\x1bTqA\x15\x8e8\xc6a\x04\x90\xfb8\xf7\x86XR\xc6\xec\t\xe2\xd8\x92.\x89\
|
||||
#\x8eufO@s$\x938\x1f\xf6\xe1>\xfe\xa3\xc9F\xb1\xc6H\x16\xa6\x99\x85-6\xc2a\
|
||||
\x91\x0f\xc0\x8fs\x12\xb0~\xdf\xc3\x11\xbd`\xf0Z\xc7\x1a\xb3h\x1a2\xa1}\xd2\
|
||||
\x1a\xb3iI\t\xc1\x87\xf0B\x9b\xb7\x98\xb0p)@Ej\xe0X\xa8\xa3D\x8bO>\xb6\xbc.4\
|
||||
\xf46\xa4T\x81\xf3\xf6O%_!\xe2\x96\x15\xd6a\xb0>\xb6\xc8\xde\x17\xda\xbaEO\
|
||||
\xdc*\xb2\r\x9a\xec%\x07fS\x8e\n\xcd\x95\x03\x03\x83f\x03#~\x9c$\x88$\xda\
|
||||
\x109\xf7\xcd\xd9 \xb4\x11\x07~\x98uRXm\xa1(\x12\x83\x9eu\x88q\xf6\xd3\rM\
|
||||
\xf9\x044\x148\xcbs\x1f\x96\xc5\xcd\xec\tb\xe1\xa9B\xbe\x0b\xb1^\xa0\xc2ud\
|
||||
\xb7T\xb1k\xf0H\xa1\xe4}R\xc9\x8b\x9f\xa7,\xf9m\xf9\n\xc2>\x1d,\xfe\x1c\x82\
|
||||
\xbe \rV(\x92Cbrd\x91}\xdb\x1fc\x13\xdad\xac\x8a\x9d\x8f\x8a\x82\xddr\xfd!q\
|
||||
\xec\x9e\xcf[\xf1?R\xf2pz\x08\x89C\x1c[\xa1x\xe5@*\xbd\x7f\x10\x91H\xd9\xae<\
|
||||
\xd3\x10\xb0\x1bB\xb0E\x1d\xec\xfd\x01*6\xdb\xb4>\xc4\x96#\x8e\xf5IeI\'}\x98\
|
||||
\xe6\xdfK\xf9\n\xb1\xf0\x98\xbc\xe7\xd6x\xd0\'~\xa404\x0b64?\xc2\xc0\xf3lT,6\
|
||||
mx\x96\x10\x1fP\xf2(^\x99\x8f\'\x16-\x98m\x12\xb0\xe3~\n/\xed\x84T\x85\xfe$\
|
||||
\xed\x11\xb5\x1b%\x8c\x80\t\xbc,\xdb4\x0e\x7fh\xa5T\xf1\x97\x0f\x0b\xcf\xa7n\
|
||||
\xaf`G\t\xbd\x99&\xaa\xa2\xfd\xe2\n\x05\x9e\x9e\xd8\xe10\xc7\x00\x1b(nX8\xc8\
|
||||
\x8b\x0c\xe2\xf3\x06\xc1\xd8t\x8c\xb9\xdd$\x7f\xb2-TZ\x9a\xb9\xcd\x88\x99\\ \
|
||||
\xa2\x19B:\xa4Y\xd1\x88-\xd5\x95\xdd\xd2+\xdf:\xe0\x98\xe5\x13\xf9\x06I\x87\
|
||||
\xa2\xe8\xc1\xcd\xb0\xa3\xc9^\xa8B\xe7\x10\xa7\x0c?\xc960\xfb\xfa\x88_A\xc5&\
|
||||
G#-\xf8\xe8\xbb\x0es\x0c\xae\xac\x81\'_\x81\x13a\x04\xf3>\x85R\xb3C\xde\x9e\
|
||||
\xd4w\xe1\x93\x0eCI-\xd1\x12\xd3\xc0\xfc\xbc\x80&\x1d\x90\r4\xc8X\x16\xc6R\
|
||||
\x8cC:,\x92\xd3\xde\xaf\x0b\x89\xbe\x08q\xe4\x1c\xd2\xa1oA|z\x88\xbf|4\x11\
|
||||
\x14\x1e@:\xa4!/\x07\x98o\x8b\nB\xbe>\xbb\xb1\xc9n\x18\x08\x1d\x86*6ln\x94\
|
||||
\x14\x92\x7f\xe6\xa6\xb2Q:_\x8eKYA\xfc\xc8(\xa2i\te\x93\x16\x8c\xab`%\x8f\
|
||||
\xb1\xb4\xe7g\xbdP\xc5\x9e#{\xa16\x9eI\x06\r\x85\xb6E<\xfd\xdd\xcc\x9c7\xf6\
|
||||
\x86.\\\xbf\xa1l\x03\xbe\x8bJ\x8e\x03QQU\xb6A\\\xbf\xb7\xe4+pC\x91\xc8\xe9\
|
||||
\xf9\x1c\xd3Zg\xcf7\x1f\xc0\xab\x1aw\xeb\x81\x85\xb3W\x88\x85\xbbT\x91\x0b\
|
||||
\x15\x8eD\xdf\x06\xcd\xad\xe3Zp2\x15\x88\x04\xa4\xcc\xa0+\x07b\x9cC6\x07&\
|
||||
\x82\xc2\x0e\x14\x92\xcf!\xb6X\x0eeb\r\xbb\xd2\xf1<(h\xbc\xa3\\\xacuG\x01vc\
|
||||
\x027\xe5\x14N{\xb1\xfc\xe9\x89\xf3\x11\xa4e\nY\xf8F6\xa1\'\xe4\t=\xb1\x80\
|
||||
\x91\n!<\xa1\xc9\x86B\xd2\x13\xc2\xb1\x99\xec_d%\xb9\xc2\x8c\xa2\xddp\xa0A\
|
||||
\xd0\x8f\xa1\xb0\xfel\x86\xee\x0e\xdc\x01\x8c\x80g\x0b\x9a\xf4dB\x0c\xd2M`YX\
|
||||
A{*\xe5\xd0!\x83\xe8\xd0\x1a\x17\xfe\x181>\x86\xd0\xf4a&\xfc6\xe5+4\xfbiC\
|
||||
\xed8\x15:,\xac\x02\x9aJ\xc1C\xb8\xc2\xb7I\xdb\x1aKmk@\xd6I_\x88 "\xff\x96\
|
||||
\xf4SO\xcc%t\xdc\x08\xb1\x13\x17\xeaW\xe2\xca\xa1\xd3\xec\xafq(\xbe\x0f\xe90\
|
||||
-\x11\x14N\xb1\xa3"\x04Br\x0f\x9e\xab\x03\x97\'\x1b\x1c3K\x00d\x06`\xd0m\xc8\
|
||||
\xc4\x81A\xc70V\xb3!\xc2\xb5\x17\x06\xf6m\x06#\xe0\x17\x85\xaf\x85\x06\x0b\r\
|
||||
e\xb9\x8b\x80\x7f\x0e\x93g\xb9y\xc5\x9bx\xbeK\x15;\xa4\xd1\xfb\x93\x1c2\xe8>\
|
||||
\x90\xd4B\x06Qp\xc9\xcbzP7^H\xb0yB=\xc7\x81\xc3\xb1y\x07\xda\xc5\x84\xe3\xfb\
|
||||
,q\xbf\x91\\\xb7\x866d\xd0<I\xed\xc1\xa4\x0f~l\xcez\x01^*\x96\xe3\xa0\x8a.\
|
||||
\x9d\xb7\xb6\xc5\xd6\xfc\xdb\xacQ\xaaxf\xac\xd3\x04J\xde\xa9.\x1b\xa5%u\xd6B\
|
||||
\xfc\xd6\x865\x9f\x90cfRx\xec\xd8\x9d\xbd\x02{\x9f\xe2/\xb9\xcf.\x93\xd7\xd9\
|
||||
\x13T\xf1\xc4\xc8\x9d\x14\x8b\xae0\x81vq\x10\x9fk\xd3\x11\xed\xf4m\x1cZ\x7fY\
|
||||
!\xc9\xa1\x05\'\xcdD\xf5\xad\xdce_p\xc9m\x80c6C\xd2\xd8\xf4\xca4n\x14\x87\
|
||||
\x96\xc2\xd8\x9e\t\x9cn\x03R\xbe3g\x88\xc8\xea-\xb9\xd3\x85\xda\x0c\x8d\xfe\
|
||||
\xac#9F\x15\x1dX\'\xfd\x1e4\x94}\x17\xa8\x9b>x\xfa\xd4\xc297F$`\xaa\xc0\xe3<\
|
||||
\x80\xd8z\x8a\x81T\xb1\x10n\xb9\x0f\x81\xa9\xe1\x08\xde;\x92\x96\xbeP\xf2\
|
||||
\x88\x85\xd7\tp\x06\x16V\xe1\x8f\xd9T\x12\xc7\xbe\x06\xb2Qz\xa2\xb2%\x0b\xe9\
|
||||
\x89KR6\xfd\x9c\xa3\xd5m\xb6\n\nDh\x86]\xe0a\xc6p\xa7_\x98\xb2\x82\x18T6<\
|
||||
\x1d\xb8\xbe\x0f\xdb\xf4\xa4\x92O\x00q\xe7\xcaq\x10\x83\xfe(\x88x\xf7\x80\
|
||||
\xb8\x9b \xb0\x9c\x8d\xa1\x07\x1d\xb9\x10[lgO\xc9/%\x848PH\xf3S\xec\x86\x14\
|
||||
\xbba\x9b15\x85C\x03\xd3C\xe0al\x98\xd5\xef\x19\x12\xd0\x83\xc8\xf9\x12\xcb\
|
||||
\'h7\xfcv\xe4\xd0i\xa4E#\x82=\xcfs!\x1dF\x16\x0e-\x0b\x03\xdb\xb2(\x82\xe7\
|
||||
\xb2j\xfd\xbb+\x89\x83\xa3\x8a\xe1/^N\x90\xb3_\x01D\x1f\x03d6`\xad\xf5\xd8Qe\
|
||||
+\x98l\x17g\xe5G\x15H\x95\x02O\xb8dX\xa5B\xed\x81\x82\xd6\xe6n\xb1\xb4\x1f\
|
||||
\xe8\x04\xf2\x85*\x89\x88\x15q\xcc\x16:\x1d\xc2>\x89l\x94*,\x03\x90\x91\x0c\
|
||||
\xc6\xc8+#\x00\xd2\x01\xcd\xedE\x0b\x12f\x80\xf3eBaRo\xd2C\x1c\xe8m\xd6\x06l\
|
||||
d\x12\x17\x9e\xcfN\xea\x08\xd2aP@\xcb9i\xc9i &\x17\xd0O\xebB\xa7\xd8\x08\x91\
|
||||
U\x0e\xd7\xe6&\x02\xcb\x03\x88\x0b5B\xb4\x9a\x97\xf6+\\\xe1\xd9\xd8\x85\xb8\
|
||||
\x98\xf5\x02d\x86)GJk}\xc7\x90\xaf\x10\xd7_t\xf9\x04U\x98.@G\x16\x16\xbf\xc7\
|
||||
\xa1V\x0f,|\xa1\xa1\xd7\x81\x00\xe2\xd8\xaa\t(\x0f\xebt\xbb\x1d\xd9\x0bU|+\
|
||||
\xc9\x890\x19\xc0#\xf0c\nx\xe5\x10V\xf4\xd9\x8c\x16\x84\xe2m\x80l\\\xbc\xf2\
|
||||
\x13v\xb6t\xdc\xed3\xc8\x86\x81mG\x8c\xebK\x81\x88\xb8\xb2\x00\xe5\xe1\x98x\
|
||||
\x8746\xd7\x9d\x80c\xfa\xac\x17h\xd2\x90\xa7\x1e\xcb\xd3-\x151`\x07N\x95\x17\
|
||||
\xd9-\x18\xf4\x87qJ6\xce\xa8\x96d\xb2G\x1c\x0b\x03\xf9\x04q\xac\xd3\xc2\xf9\
|
||||
\xc2\xb0\xa4\x9e\x83\x80?k}\x1fuY\x81HQ\x17Q\xd1\x14\xd4\x8eT\x84}\xfap!\r$\
|
||||
\xd7=\xaa0u\x88`V\x14\x97"9t\x1c\xe2\xae|\x82*\xde\xd1Ih\x8dS\x8eV\xb7\xe48\
|
||||
\xe0\xd9,\x81\xbaaUr\xd3\x86\xdaS`\xd1\x998\xe6{\xbc\x91\xbf\xc86 \xa5\xbe\
|
||||
\xaa\xd8\xa6\x19\x96\xd4\x90\xc1>\x19\xa2\xb3\x1b3Z\x10\xaf\x8c\xa1\xd4L\xc0\
|
||||
\xb1\x96)\x1b%\x01;&\x1bh0\xe9aI\xdd\xc9%\x05\x1b9\x9b\xb5\x01\xf7\xb1\x0e\
|
||||
\xa9\xcdp\x8f}\x12(\xeex\x02\xf7q\xd8\x91\xd4\x12\xd7O`\xcf\xe5\x13D\x9b\x1e\
|
||||
I\xe2\xf6\x86\x8c\x93^ q\xd1\x1f\x8fm\xe2\xa9\xab\xb25\x0fo\xf1\xb0\x99\xec\
|
||||
\xc2\xcd\x0b\xc0p\xe2\xb6$\x9fx\xea\xb5\xe1\x98\xb1}\xec\xdb\x0e\xcf>6\xf23\
|
||||
\xc3=\x86\xa8\xd0\x1d\xe8\x1f.\xef}W\x0e\x1d _\x96tcF\xfd*\x08\xe7s\x1c\xf9\
|
||||
\x0b\xce\xfd\xc2\x87\xb9\xf2\x1e\xde\xd1\xfe\xc0\x87s\x87qJ\x1c\xb1z\x0c\xe4\
|
||||
\x134/:)\x02\xde\xb8\x07]\xea0\x82\x97\xd6\xc2:5m\x9c\x84)v\xa5\xe9\x01\x1e\
|
||||
\xe5\x81\xa7k-\xd9\x0b1\xa8\x8a\x11\xd3\xeaC\xf4\x1dN)f!\x0c:\xa2eU\x95OP\
|
||||
\xc5i\x8b\x99<\xa6}\x9b\xcaF\x19\xa6\xa5\xd1\xf1Z\x17\x12\xb0:\xb7\xc1\x08U\
|
||||
\x8dW\x10B%W#\xc91\xaa\xf8f\xcaW\x88A\x89.\x9f \x06\xad\xcc\xc6A+9\xa8\xe4+\
|
||||
\xc4\xb1s\x8a\xbc\x8fe \xe4\xd2\x00\xf8\xc9\x86M\xb8JaA\xcbv-Z\xb8_\x0c\xc4\
|
||||
\xc4-\xa8\xa3/6\xaf\x0f\x86\x89+8=r,\\C\x97\x8d\x02\x03\xef\xc8\x91\xc2\x08 \
|
||||
\xf5k<\x18b\xa2\xeeg\xbd\x10O\x17I\xd0\xa7R\xfb<T\x80\xd7\xee\xe3\x88\x9eo\
|
||||
\x03\x94\xc6\xda\xe79Y|\xb63\xee\x13\x93\x7f\xd8\x00\x94\xb1:\xfa\xce\xc0J.\
|
||||
\xc0\x8f\x12\x17\x1e\n\x07>\x94;Kv\x0b~\x18\xf2\t\xa2\xd6\n \xd8|\xf0\xe3K3\
|
||||
\x0e7s\xd8\x1d\xf6)f\xc5\x08\x06\xde\xd8b\xe2 \xfaZ\xe4\xfdrk\\\x16m\xb1\x0e\
|
||||
\x85\xd1\xd31\x96\xd4;\x08\xa5\xdc\x86M\xb8\x1e\xca6\x80\xfe!\xdfg6\xb4\x10\
|
||||
\xb2\xd9!oqj\r\x11\xf2\xfd\xc41\x0b\xf6\x08\x94\x8cSr\xb0\x82\x0cW6J\xcba\
|
||||
\xbb\xa4\xb8G\xd6\x07\xb5\xd7SH\xfe\x14fB\x9b4Gw\xec!\x18\xbbDX\xda\xc1\xb8o\
|
||||
\x83\xda\x11\xa4e\n\xf2\x1dF2\xf5q\xf0\xed\xb7\xb9\x8215\xba\xec\x05\xfe \
|
||||
\x87\xa5\x14\xa6\xf2\xa8#i\xc1U\x95\x08\x98\t\x17\xb4\xd8\xael\x83\x9eX\xeb\
|
||||
\xc0\xaf\xeda\xb2\x17\x08\x8f;\x988\xd8\x1ef\x05H@\x06\xab \x9b\xbdB\xe4_\
|
||||
\xb8\x10\x17C\x00S\xbe(\x08\xd7\xba\xd0\xa5\xaeh^\xec\x9c#4#\xc6)e\x08_\xef\
|
||||
\x93\xa2(6:\x9c\xa1\x03RG\x85,`\x14#\xf9\xb5{u\xa1^L\x1e\x18\x88\x8b\x19\xdb\
|
||||
c\x81\xb8\xdf.6\xe1\x18\xc2\xf1\x17\xa3\x90X\x95\\\x83\xfbg\xc2\x18\xa3K\x92\
|
||||
AN]\xc8\x01\xc0\xf0\x86>\x0e\xbe\x83@vKO<\x98\xb2[\xc0^g\x15 \x7f\x8ahB\x1f;\
|
||||
\xfbG\xc3\x0f\x7f2`\xf8\xfe\xfd\xec\x15\xc0\x92\xda\xd8b>\x96\xd4)[Z\xc2\xfa\
|
||||
\xa2\xdd` :\x9bCw\xe8\xcc\x06F\x15\xd7\x1ab\xe2)N\xa0\x1et~\x97yzb\xf0\x13\
|
||||
\x08NOu\x9e[\xf8P\x16I\x06\r\x07.6\xd0\xb8\xa4+D\xf9\x04\xd7\x7ft\xc4\x92\
|
||||
\xb2>\xd4\xafO\x14J\xf3\x84\xf6I[\xec\xb4\x94#\xa5i\xd0\x08\x99\xd1\xf3\n\
|
||||
\xa0\x7f.\x08`\'\xb4@\xc4,\xae"\x16(@d\xb6p\xa9f8\x84\x05\xfc\xb7\xcd\xf3\
|
||||
\x82 \xd7\x1eod\x96\x96{\xba|\xa5 \xa3\x88!V6\xa2\xf7\x17\x08\x92\xe7\x0cy\
|
||||
\x7f\xe3\xe0\xa3\x03l\xf1\x1f\x8bG\n\xf1\xb9\x90@\x180\x94\xf8\xa7\x9c\x17\
|
||||
\x1cI\xbd\x0e\xe2\xb7y\x9f\x88\xbb\x1c\xc9n\xa1\x9eSlm\x92r\x0c8\xed\x00\xa8\
|
||||
\xc3\x17\x1e\xaa\x00\x11^\x0b>\xe9mI\x0b\xa2\xa2\xeb\x8c \xcaPaN\x11\xe0\x1e\
|
||||
b\xd1E\t6\xe1\x00\x9b\xf0\xbd\x0e\x17\x01\xdb\xb7\x86E\x9e\xef\x01\xef\xca=\
|
||||
\x1b\x18V\x0e\xb4\xa7\x0cmb\xab\xf1x\xd6\x06\x0cM\x86\x04\x08u\xb4\xe1\xe9\
|
||||
\x90\xb0\xf8cq4\x10\x0b}\x169,\xb5\xa7\x11@G\x96\x03@j$\xc9\xa7\x8ae\xf8\x0c\
|
||||
\xfc>\xb8\xbedH~\x00+\x19r\xa3Xt\xaf\x1a\x8f\x14\xc2\xe0\xbe#{\xc1\xa2\xb3d\
|
||||
\x05\xad\xa0s\xf2\x08\xa4\xbe\x0f\x07\xc0eG\xb6\x81P<\xcd\xed\xa0`8\xe1;L\
|
||||
\x83\xe70~\xcc\x96\x8dR\xc5\x83\x83\x03\xc7\xe3\xcb_\x0c\\r\nZt]FT\xb9pw\xdc\
|
||||
\x10\x0b\xb3\xa2\x8fM\x98L\x11M\xc8\xe13xVp\xe0\x8c\x11\xf4\xfb\xd8A\xa4\xb9\
|
||||
\x07\xf2?\xd8\xb2\x17b\xd0\xde\xacQ\xe2GoVA;\xca(e/@2\xb5!\x1csH\xfe\xe7\n\
|
||||
\x91w\xf6\x07i\x1an?y}\xaa\x08\xa6X\xc9\xec\xc5\xffD\xd8\xe2q\x9a\xc2\xe4y\
|
||||
\xaf\xc8FiI}e\x9d\x8eW\xa1\xcd\x91U\x04\x97\xe6\x165\xde@\xf0\xc2}\x89\xe48\
|
||||
\xe0\x0b\x06\xb0m\xe0#\x92\xf8\x8d\x01T.\xf4\xd3{\xba\x96\xe5\xba6\xec\xcaO\
|
||||
\xe4\xda\x13f7\x14\xf8\x83\x19\x93\xa9\xe2/\x1f\x9e,\x0crF\x97\x8e]\xa8\x1b,\
|
||||
\x1d|H\x87\xebXvK\x15\x7f[<\rPi}S\xf6\x82\x1bC\x8c\xa8\xe2\x8bl\xcf\x0co`\
|
||||
\x1d\xb77{\x85\xa6\xd2\x94\xe4c\xb2S\x1b\x12\x97\xbd\xd6\x9d\x08\xb1h\x0e\
|
||||
\xe8^\xd3\xd0\xc7c\xbe\xc4\xfa2\x05"s\x0c\x00\x95J.\xa4\xbe\xeba\xb2\xcf\x19\
|
||||
\x1e\xc5:\xee\xc8\x83\x8f\x8d}\x8e\xa7&\x0e\x8b1\x0c\x89\x16\x8b\x9c\x02"\'\
|
||||
\x98\x8d\x03\xf0l\xc6\x19\xf8\x90\xc9\x1fX~p\x94\xb8\xe5\xc8\x95L\x15\xbb\
|
||||
\xa5l\x83fN\x0b\x11G\xe6\xdb\x82\xd3\x04\xb0\x02\x1b\x0e\xc4\x16C\xac<\x9cs!\
|
||||
\x00\xcb9\xbb]JDW\x86|\x91\xed\xcc\x95\x8d\x02kM\x9e\xbc|\xe2@\\<\x00\xcaSd`\
|
||||
aF~\xed~\x96B\xe7\xff\xce\x88*\x0b\xc2\xe0\x9b\x8a\xd8\xc9\x00\xb4h\x1d\xb6\
|
||||
\x1ayi\xb3`KaX\x9d\x03D\x91\xf2\x1d\xde[M\xb6\x01\x9fR\x87\x99\x0c~\xfcV\x11\
|
||||
\x9bg\xb5x\x01qS\x97q\x17\xb9J\x10^!\x84qW\x14\x17\x1d\xc5Pi\x05\rY\x8e\xf9}\
|
||||
\x04B\x1c\xd9\x0b\xf1\xe3\xdc\xe0q\x00P\xf61\x94O\x10\x83\xfe\xda<Qx\xa2\xa7\
|
||||
!\xc0\xed\xc3\'\xed\xcbm\x8a+\x11\x87\x16\x84\xe3\x90\xd1\xb6l\x8a\x0e1\xb7\
|
||||
\t\xc3\x1b\x06\x88\xac.\x13\xac\xd1\x9dX\x08\xa4\x1eUX0|)o\x19\xe2\xa2\xe08\
|
||||
\xf27R\x15\x06c{\x0cx6\t\xe9\xa1P\xad\xe9\x89\xed\x11\x04\xfd\x18Z\x8eI\xbbr\
|
||||
\x92\x15`\xd0g\xdebc\xf0\xe3\xa5B\x8c\xcf\xc3\x16[\xd5\xe5\x13\xc4\x8f\x8c\
|
||||
\xac\xf9\xa1\xef\xc0\x03\xaf(ptOp\xaa\x7fb\x9c\x12\xfbq\xcfX\xa3\xe7\xdd\xd0\
|
||||
\x9f=\x81\x00D\x85\xb8:\xdf\x14\xbe#\x89\x9b\xa6\x1c\xb38!\x0ck\xbf.\xd4\xa8\
|
||||
!yJO<\xccz\xc1\x1d\tG\x92\x8f%\xc5@\x8c\x1c\xc1\xc7\xccD|\xdf\xc7\x05\xb2\
|
||||
\x9e\x81\xc9f\xbf\xa5\x12\xcai\xa0\'R\x86#\xf3\x85\xbam\\\x9a\xe8\xf3\x9d\
|
||||
\x80\xaf\x11\xd3\x02IwH\'\xd0@(\xbd\xc4\xe4\x98\xe4\xa9\x9f\r\xa0K\xb9%\x00T\
|
||||
\x05\x0c\x1au\xca\x03C,\xe9\x91\xfc\x96\xe9d\x80\xcb\xe7\xf3\xb4\xa4\x86\xf5\
|
||||
\x1d\x08\xd2\xb5M\xd9\x0b-\xcb\xaf\xa5\x1c\x07\x02v\xb3F\x11;\xd1\xc8Xu}\xf8\
|
||||
>\x17\r\xc0\xa3\x1c\x1c\xf3\x93P\xce-Ux\x04\\\x9a\xf8)n=\xefZ\xf2\t\xe8c\xb6\
|
||||
l\x03\x10\x89)\xa0^>\xe6\xf6\x83+\xdb\xa0y\xd9\x9d\x92.\xe5:pC\x15]\x80Jr`Y*\
|
||||
\x866\r`\xf2\x1c$\xc0\xe5XCb\x90F\xd2\xc1\xcd8\xc0\x9dw\x00+\xc8\x11o\xd8p\
|
||||
\x00\xc2\xf2`\xdf\xfe-e\xa3\x18\xa9\xc2;\x1bb++\x01:\x1a\xc3:\xa9p\x015e\xb0\
|
||||
\xf1\x18\x08Da\x02\x02\xae\x0e\x18\xf0\xc0\x05T\xe3\xd5\x82\xe68\x06\x0c\'6\
|
||||
\xb9[l1\xb3\x91\xb8\x9e\x9f\x8f\x018\xb4\xc98\xcbz)\x1c\xdd\x03Zt\xf6x\x00\
|
||||
\xe2^\xbb\xf2\thl\xb8[-t\x1a@\xbd\x88\x85\xc3\xc2\x07\xd4k\xd3\x05\x00"\xc3\
|
||||
\x19\xf5\x9b\xfc\x85\x03\xdb\x87\xad\x9e3\xdeA\xa6Qp\xe5H\xa1*0\xaa\xc2\x83\
|
||||
\x1f&\xc0\x81c38\xdfC\xc0.OaG-\xa8\xe0i\x06,\xdc-\xbd"\x94\x07\xc4N\x9e\x811\
|
||||
\x12Z01h?\x06p\xc9\x87\n\xb7\xac\xf34`%\x7f\xa2q\xd8\x19_\xa8\xebP\xa4\xd9\
|
||||
\xf2{p\xa8\xbe7\xb0\xf8\xf9\n\xa2EW2\xdd\xfe\x18\x17.7K\xa4s\xe0D\x0bk\x91|\
|
||||
\x85\xa8\x9d\xe8\xf0\xb0Z\x90\x0eq \x9f -G\x8f\x01\xc4\xe8#6\xdfn\xe1$d\xcc\
|
||||
\xd5o\xba\xbc\x91:\x03x\x9d\x8e\x02r\xa9\xd5\xff#[]\x87\xc4e<\xffn\x04\x9c\
|
||||
\x01\x83\x05\x8ft\xd9-\x90\x08tD\x17V\x06\x8f\xb3\xee\xca6\x10\xf43\x81\x0f\
|
||||
\xb2\x01|\xac\\H\x07\x8fU\x16\xda\xfb\x960\xc5pG\x93\x93yHW\'\xa3\x19XstJ\
|
||||
\xd9(\xa0\t\x01\x10D)_\x98\xb2\x80\xd9,\xfap\x0e\x93\xd8\xf2&\x0eB\x8b\x87\t\
|
||||
!\x00\xdc\x1c\x1c\xd3\x01\xe6\xf0\x19zo\x07\x88\xde\xf7ph\x1d\xc7\xb2\x17j\
|
||||
\xe3\xc0f\x9eB\xe2\x06\x14GN\xc7|\xbdc\xcc!=\x1b\x97\xcf\xdb\x1d\xd8\xd9\x19\
|
||||
\xf4 eF\x1cp\xe3\x0c\x8f\x12\x9aac\xbf\xfc@n\x86\xb1\x85+\x11\xdfb90b\xf2\
|
||||
\x11\xed\xfd\xf1\xa4\x8f\x8b(\xc7\n\xd2\x8e\xb0\x13\xb2\xe0t\x0e\x9c\xbbc\
|
||||
\x91\xf7\x0bG#\xb7;\xc0K\x15|c\xa8\x04>\xc8a\x9cc%\x9f \xe9\xf0\xcd\x91\xe3\
|
||||
\xa0\'\xa6\x1e\x04\x1b_\xbcx\xd2\xb1\x1b\\\x9c\xb7\x8b\x1d\xe0Pl\x9c\x95\xef\
|
||||
\x81\xec*,T\x1c\xcc*\x80r\xadx\x1c8\xc5t\xbeI\xce\x92NG\xb8\xa5`\x8c\xa2>{\
|
||||
\x05\x19SF\x88{\x0c\xb0\xb4+\x13\x99[8k\xc2\xa6J>\xd8\xbc\xc0}\xe4S\n\x94\r\
|
||||
\x84\xe5\t\xd7^\x07\xc0\x14v\xed}%\x7f\x90/\x15\x92S\x0b\x82m\x82\xb3r\x81\
|
||||
\x01\x98\x19\x9c*\xbfB\x8a\xbey\xec\xeb\xbbq\xe5Hq\xcbw\xd6hN\xf8dK\xbeB<uG\
|
||||
\x10}\xecw\xf8\x04[\xbd\xc6\x11\x90\xd8*\xe5H\x01\xacg\xa0\x9fL\xe6A"G\xc8\
|
||||
\x068eo\x13\xf9\x04\x80K\x88$\n\xc5\x001>\xc6\x18\xf1}\xc2\x1bDE\'=\x84\xb0\
|
||||
\xdeu\x01)\xcaa\xf2|\xd4\xb0M\xfb8\xe6]\x03;\x9b3/X1\xc2\x93\x1c6v\xc8\xb3\
|
||||
\xe9x\x8cp?!\xd0\x91Sx\xd0\x0c6;\xb2\x17\xaax\r$\xb5T\xd1m\xf1\n\x82\xfa\x15\
|
||||
\x9a\xb2[b\xd0\xb3*\xdb\xa0\'\xba\xbal\x03J^\x89\x108\x83\x05\xd78w\x87\x05\
|
||||
\x19\x14\x11<\xcaI{\xf0\xb2\x9cU\xfc\n\xc4\xc5\x0exjs\x14\xf0\xae\xcb\xaf`Y\
|
||||
\x1a\x1e\xcc\xfb\x02\x8e\xbbuSV\xd0\x92\xca\x01\x9c.8\x1fJ\x01\xf0\xb5P\xbf\
|
||||
\xa0\x07q\x86\x10\x86\x02~\xe7\x10V\x0f\xce\xe1\x9f6\xd2\xc1\xb0.u\xd0\xe2M\
|
||||
\x88[\xbe*\x83\x9f\x06\x00?\x05\x16L\x1e\xde\xb7\xae\x07\x81\xd2\x87B\xf2\
|
||||
\xc9\x04\xc6\x88\xd5\xe2l\x04\xb0O\x06\x16~nF:\xe9Oz\xb0\nl\x88\x1c\x8f\xcd&\
|
||||
\xad\x85\xf3\x85\x91]G%\xb6i\x0ej\x7f\x92L\xae\xcfF\x80\xe3\x02\xc4\xf8,\xec\
|
||||
\xb9\x03C\xf6B+\xe8W\xcc\xe3\x80p\xbct\xe4\xbc\x10\xf9\xd7\xa4|\xf7\xc6\x9c\
|
||||
\xab\xe2\xd9\x81v\x91\xc3z\xddg\x00\x04\xa3\xc2\xfb\xa4\x18\x89#\x1b\x06\xde\
|
||||
\x02Y\xf3=g\x82H\xc0\x16\xa3n\x18Y\xee\x90\xb2i\xa7)t\xedW\x001\xfc\x02[\xec\
|
||||
F\x85\x1d\xe5\xc2\xe4\x99\xd7pX\xa4x\xe2\x94\x8f\xb51\x18\xb4\x1cs\x88\x02\
|
||||
\xe8\xc1\x96\x0b)\xe5Cc\xcb\xe1\xc8\xccml\xa0\x9dX\xb6\x81+"t\xf0\xa5\xc2\
|
||||
\xe2#\x06-\xaa\x00-\xf2\xcd\xfa\xaf\xb6\x9c\x06$X\xd2e\x05\x92E\x91\x9br\x98\
|
||||
q\xe6\x16\x8b\x8f\x02\x8e\xbd~\xaf\x10Wg\xb7\xed\xa3\x83H\xb3\x8d\x83\xef\t\
|
||||
\x11\x9a"\xb3\x91\xfb\x87}\xb0\x05$\x8c\x0f\xa3\xb9\xef#\xf6\x1a3\xc4j2A\x9c\
|
||||
p\x8a\xd0b\n\x85$K\x90\x88\xc3\x86\xc4]\xea\x00\xec3\x81\x8b\xf1\x13e\x90q\
|
||||
\xebB\x1a\n]v\x9a\xb8|gu\x0f\x9bPh[\xc0\xd1\xcf\x9e\x80c\xb7\xc5\xfc\xc0\xfd\
|
||||
\xca\x15\x15\xd2\xc1\x83\xcaR\xd1Z\xf7\xebB\xe7\xbe.\x9f\xc0-_E>A\x0bf\x89\
|
||||
\xb1\x81,\xa4\xb7\xe4+8\xf8\\SV\x00\x955\x1b\x07\xb10\xae\xe4\x13\xc4\xb1\
|
||||
\x1f\x1a\x1c\xcc\x03\x04\xdb\x16\x19\x92\xc8\x01\x99{\x8d\xcf(`\x8bW;\xccST|\
|
||||
\xb5$\x0b\x89\xc9-\xd2\xe8\'\xe3\x14,|\x88\x80u\xe2\x88\x95\xc6\x08\xb31\xdc\
|
||||
\xe9\xa9#\xdb\xa0\'\x16\x1c`\xae\x18x\xd0J\x80\xec\x1a#i\x95\xa7\xca\x81Q\
|
||||
\xc5\xc7J\xce-dr\x82d@6nN\xff,!\x0b\xfb8\xf8\x1em\xd9(n\x0bN\xb1\xc5\xc6\xd8\
|
||||
b\x19\xe7\x0f\xe2\xcb\x81\x7f\xba\xb8V1@\xa8\xe472\xc8\xb8\x03 3,>\x92\n`{>\
|
||||
\xf2I\xc8F\xd1\xc7\x91\x1c\x07=\x11\xd2&,\xf2\t0\x02\xb6\x07$B\x86\x83o\x95\
|
||||
\xec\x86\xf1\x801h\xaf\x15\xb0N\xec\xeb\xdbV\x801\x9a0\xda\x85\x0c\x9at<\xec\
|
||||
SE\xcfB|\x9f\x01T\x8b\x14L\x11\x87\x07\x16\xeeu\x89 (_\xdb[f\xa8F\x06\xa8FK\
|
||||
\x97mP\x85\x1fJ\x06a\xad\x07\xb2\x82\x96\xf6i,_\xa1\x8a3h\xd2\x0e\xdbb\xef\
|
||||
\xc8e"4#\xa8\x1b6i\xe3=\x99\x08\xec\xd5\x01\xe6\xca\xc6\xc2\x8d\\\xd9(M\xc3\
|
||||
\x072\x13\xc6\xee\x10\\_1%?\x80Pe$\x93\x07{?\x9cU\x00v\xc2\xf8\x8f\t\xc3M\
|
||||
\x15\xf9\x04\xad\xa0\xd76.*\xf9\xb0\x1b\n\x1d(\x02No\xf1.\x84;\x9d\xd5\xd1^)\
|
||||
+h\x05\x9d(\xf2\x15$\x9e\x88\x01~\xe2\x94\x87\xbb\x94\xf4.\xcb\'8\xc5\xb6\
|
||||
\x12`\x04&\x80W*\x14\x14\xf6\'6v\xb6A\xb0\x824\x1b\xe3"\x8a\xeap \x04\x07\
|
||||
\xdfO\x97\x17.\xebRmd;\xc9\x01\xe5\xf1\x19R4A\xac\xe0\'\x87\xe3\xfa\xf0\xa1\
|
||||
\x18\x8c\xcaJq4\x06\x0c\xb2\x91i\xbeb\x827\xf8}(W6\xdcr\x83\x1c\xe6\xec\x1bA\
|
||||
\xab\x1d\xaf\x0f/\xbe\xd1\x014\x81\x1d\x88k6\xc9\x8f\xba\x90\x0cb,>\xbb\xb2~\
|
||||
\xe8L>Gh\x12\x92tE\x0f\xe6\xec\x11g\x08q\x00\x04\xfdc \xee\xe1\x81\xc9\xc7\
|
||||
\x96l\x83\x9e8/\x99\x16\xac1\x97T\xc9&\xc2M\xc7I\x0ci\x99C\xfd\xda\x03\xd4K(\
|
||||
y\xb8\x06=c!Ux\x86l\x94v\xf6{S\xbeB\xcbr7A\xc4\xbb\x8f\xdb>O\xb3\'h\x9d\xbe\
|
||||
\xb5!\xd8\xfap\x99h\x81l\x14^|\x8a\xf2\x0c\xc6\x8cs\xccu\xc9Sd\x91\x00\\\xbd\
|
||||
\x18\xe38\xf9\xc6\xb1\x13\xce\x88\xd1I\x90^\xab\x0f\x98\xe7\x01+\x8a|6\xdc\
|
||||
\xabd\xad\xf5\x0b\xac1\xa5\r\x9cA\x1f\xcaD\xd0\x01b\xc6\x02\xb5\x1f8\xa7J\
|
||||
\x1f\x87\xc5wx\xbf\x86|\xb7\xa9\xad\x00\xf8X@\xfb\xcc\x19R4\x81\xfe\xb15B\
|
||||
\x1a\x16NH\xd6W\xe5+\xc4\xa0?\x0c\x80\xe0\xeb\xc7\xa7|\xa7\xa8\x87E\xd7bD\
|
||||
\x15\xbbK\xd78\xa5\x0c\'\x13\xebq>%\xbe\x94\x97"\xc0-3b< -\xcdp\x00\xdda~\
|
||||
\xf6\nUh|\xc3\xdf\x82\xed\xf13\x06\xde\x81\x95\xcd?!\x8cw\xc6\x19t*\x9c\xa6\
|
||||
\xec\x0b\x9e\xf2\xed|\x0f\xdes\x83n\xa6\xf8\xc3\x1c\x8e\xaa[(\x02\x16\xa7\
|
||||
\x93J\x1c\x08\x94\x02"gJ!_Kf\x91x\xc2\x19%\x14C8w\x14\xa0\x198k\xc2g\xbe\xd1\
|
||||
\xee`\xe1\xda0\xbddZ\x89\xb6-\x1b\x85\xdb\xb6\x02\x8a\x80\xef\x01\x7f4\x00mb\
|
||||
\xd0\x91n\xb1\xd4\xa6C\xeb\x9f9]V\xd05\xb5_\xae|\x05\x0b\x97\xe6\xa5\xf0}\
|
||||
\xdc)\xfaY\x01c4\x01q%4i\xd1-b\'\x9cq\xc9\x83\x16<\x84&\x9d\xb9\x8c\xc7\x8d\
|
||||
\x801J\xe1\x93\xbe\t\x10{e\x90\xde\'\x0b\xfb\x85\xa3+\xa3\x12>\xfa\x0c\x0c\
|
||||
\xfaQ\x01\xb9\xe3a\xab\x97\x80h\xd6\xce\x1cZ\x1f\x1a\xd4\xc0\x0c\'\xd0#\x07S\
|
||||
\xfaX0y({\x81\xbd\xaf\xe2\xbc\xcdp\xaa/pr\xb5\x0c7\xfd\xbes(>E\x08\xeb\xb7\r\
|
||||
\xa8\xb9\xe5\xd2\xde\x7f\x9b\xca\x91\x12\x0b\x15\xdab\xde\x90o\xa6\x1c\xb4\
|
||||
\xe5\xd0\x11\xae5\xe5+\xc4\xe4\x1f\xb1|\x027\x97fO\x0ch\xa2pf\x8f\xf9\xcc\
|
||||
\x8eG\xfc\n\xe6e#\x94\x15\xb8\xe9\xa7!\xbb\x87\x8d\r\x14xH\x9f4\x86\x0f\xe5R\
|
||||
\xe7i\x00B\xf5\xde\x91\x93\x8d3\x1b\xb4\xb8)\xce\xa8y\x9db\x8d\xbe\x8d\r\xf4\
|
||||
@\x1a\x8a%\x94M\xa4\x91\xe4|J}($;#\xc4\x80{\x8c.\xb5\xb0+\'\xd0a.m\xec\xca\
|
||||
\x14\x1c\xdb\xafd\x1b\xc4\xa0\xcft\x00\x8f\xf3\xb1\x87e\xa9!(<D\xd0\xef3\xef\
|
||||
\xdb!\xf6\xad\xeb\xc8^\xe0\r\x9c5J\x9bp\x89\x8e5k\xc0\x16\xdf\xc0\x00`\x88\
|
||||
\xf3\xa0\xbd\xb3\x80\xfdr\xb1\xb4;\n\x0e\x0b^t\xfb\x86l\x94\x16\xdd\x83+\xbb\
|
||||
\xc5mAN\xc3\xe2s\x10\x94s\xbb8l\xab\xb7\x91m\xad\x80\xe6\xf8\x82\\72\xe1\xc3\
|
||||
N@\xb7l\xc4\x11EO|\'Y\xe8\x16\xec"\x189\x00P1\x8a\xa0\x9cuK\\Oh\xe8\xc2\xae\
|
||||
\xc6}\xd3%\x00\xc93>\xd53\x086\x97\xfdt\xddP\xf2\x838vAk=+x\x05]\xcb\x91\xb2\
|
||||
}\x8b\xec/\x1e\xebA\x07\x0e$\xdd\x00\xfbv\xaa\x02\r5\x81\xf9vE\xf1\x97L\xecA\
|
||||
\xa2e3\xe0\xa1\xf3\xf5\xc1X6\x8a\xab\xa1p\xdd\xf4\xd9\x16{a\xacB\n\x0c\xfcH\
|
||||
\xe7W\xe0a=1\xb1\x91\xf9\xa6\x9f\xd5\xe1\x15\x045\xf0\xa3\xcb\xfb\x05\x91"\
|
||||
\xdf\x83\ng\x03\x84\xf5\xd8A\xd0\x8f\xb37\\\xc4@2q\xf6\x06\x17Y\x9b\n\x0e/|\
|
||||
\xabd\xb7\xb4\xc5n\x18\rU\x002\x92\xb2Ym\xc3\x16{\x9b5\nS\x83\xb3\x02r\x06\
|
||||
\x88\xd8\xe2\x03\x07\xbdx\xb3n\x89\xa7\xe7\x9c\xe4\xcd\xc5\x92\xda\xd1qXp~\
|
||||
\x87\xf5Y\x1bp\xfc{\xe4\x81\xaf\x0b\xed(\x13\xc1\xe9>\\Ho\x01+$p\xfdf\xa1\
|
||||
\x1c)\xe6\xc5\x91\xbd\xd0\xae\xbc*\x11\xd2\xe3\xc4h\xaf|x2\xf0`\xdb\x04n+\
|
||||
\x87\x8f\xeds\xc9\xdd\x02\xec\xf3\xd1\x92\xe4#m\x91\x03\xe18\x81\xa4\xf3\xc9\
|
||||
U1\xcc\xc6pU\xb4g\xdd"WV%\x1b\x05\xa49\x92s\x8b\x99\xd3\x90u%\x87^xc\xc9\x99\
|
||||
C\x88\xd3\x92\xaf \xc3\xa1){!\x16\xfe\xe4\xd8+gM\xf8\xa3\xcb6ps\xda\xc3-\xce\
|
||||
!4\xfa\xc1l\xa4\xc4\xe4k\x85L\r\xbf\xef\xa2B\x05~,\x87n\xf9\xd9\x95mP\xc5:y\
|
||||
\xbf\xea\xdb\xd8\xf0}r8\x9f\xb5\xadKz\xc5\xeeO\xa0mM\xa7\x80\xd0\xf8X\xfc\
|
||||
\x01\x9c\xd49\xa7;\xfd\xa9\xc26eL\xef\xd4D^\'\x1bG\xe3\x0e\xe3\xa5\n\xa8\xc5\
|
||||
{\x0cJ\xb3\xb1\xe8\xfe\x98\xb2\x17\\#\x98"\xe3\x92\x8b\xd8|\x1f\x9e\xef1\xfb\
|
||||
`+\x05AaN <dH3cz\xbf2\xc6\x88\x9d\x7f\xbf\x18\xa8\xc3\x98\xef3\x07\xa6F\x01\
|
||||
\xeb\xe4\x9d\x1c\x07\x16L\x90 \xaf\x13_0\\\x0ee\xa3T\xe1V\x08\xa4Z\xd0a\xfa\
|
||||
\x1d\xd9\x0bU\xfc\x01\xd4\xab\x18\x00\x0e\xf4\x05\xb2\xd0c\x05m7\x94\xddBa\
|
||||
\x85\xf2\xed\r\xa0|G\x94\xb3K\xact\xdc\x07\xb2T\xc4\xa29xp`\x01V\xc0\xc9\xd5\
|
||||
Vq\xf7m\xd8\xe7\xadnB(\rp\xde\xbe\xb4ax\x0f\xb0`R\x06/p\xf6\xdb\xcf\x90\xb89\
|
||||
\xc7oo]9R@\xf0*\xdc\xf0w`\xae<B\xff\xb0\xf8\xb0\xb8\xa2\xd8\xeb\xa4\xc7\xd0\
|
||||
\xd9M\xdc\x8cu\xf9\xd2\xd5\x8a\xce\xeb\x03\xa7\xe9>\xe3\x838G\xc4\xdeH\x0e\
|
||||
\x1d\x80\xc3@>\x81$\x1a\xb1\xac\xc0\x8e\x8a\x81\xba\xe1\xc3b7\x06`\xc8\xe7d@\
|
||||
6;\x11P\xf1\xd7EV\x9e\x02i\xadL\x13X\'\x8ei\xdd\x92f`\x89S\r1`N\x91a#\x00\
|
||||
\xb1@\xd7\xe5\xc4\xf6\x81,|\x1c\xc9q\x10\xc7\xc2.\xddM\x18\xf2\x813\x01\xc7\
|
||||
\\\x8e\xe0\x9dP@\xb7W\x17\x1aX\xc0\xe4#"\x113\x8a \x83\xe8\xbbd\xd4\xef\x00\
|
||||
\xbb\xb2\xd2\xe5+\xc8\xdd\x11\xcaq\xc0I]\x01\xa74\xe4\x14\xf6\x14\xf3\x9c\
|
||||
\xf4=\xf8\xc7\xd6\x03\xf9\nM\xd4\xeb\x14\xf7\n\xf8.\xe0_]>\x01\xf1i"\xde`\
|
||||
\xc1\x97\xf3\xdb\x90\xbd`\xab3\xcc\x93\xf5\xc2+S\xd2B<\xb5K\xd9\x06\xb10P\
|
||||
\x10\xb1\xeaa\xe1v\x19\xeb\xc4Y$\x0c\xbeE\x91c^\xfa\x0c\xa0\xe2\x1b\x87\xab#\
|
||||
9\x0e\xe0.p\xd1@\xe6ts\x19\xeb\xc4)\xdc\x8e\xe0\xcc\x98\xb0\x96S1^\x8a\xb3j|\
|
||||
e\xe0\x12#3\xde\x14I\x0b1yY\xaa\x1bp\x96\x7f\x02xR"\xed\x8f5I\x1cU\xfc4\xe4+\
|
||||
TQ2\x92)\xc7\x19\x15R:\xcb\xacN\x00K\xdd"\xa5\x8c\xd0\xd7a\xf1!|\xed\xe58\
|
||||
\x81\xdeE\xb2Q\xaa\xf8\x1bK~P\x859\x02D\x82#E\xe3\x98\x0fOT\x0c\x1c\xf9\nU8\
|
||||
\x8c\xb9\xb2a\x12o\xce^\xa1\n\x7f\xf6\x04\xae\x99\xa8dz\x15.\xd4\xf3\x88SA\
|
||||
\xb1\x8d\xec\xa1b\xc8\xb7\x16\x07\x01\x0f\x1db<\xa1};\xf6\xf8\x1eN\xce0\x0bv\
|
||||
\xfd.\x8c\xe4+T\xb1\x1aS\xc6\x03\x99(n\x9e\x81\\\x13\xcc\xcb\x1d9U\xec\x9c\
|
||||
\x81\xf5=J\xf6"\x8e_\xa4o<\xd7d/\xd8/\x15D0\xdf\xf6\xf9I\\\x1f\x16\x13\xe4\
|
||||
\x97z+\xc9(\x12F\x00-~\x87!V\x16X\xb8\x87k\xe1\x03\xbe\x16nvd\xa3Tq\x1c2\x0b\
|
||||
\xe1\xc9{h\x03w\xc1\t\xda\xf6d\x05\x0c<#\x90#%\x06uH\xed)\xbc\xdc&jKG>\x01\
|
||||
\xfcX$\xbb\xc5%E\x8a\x9a\x17\xde\x10\xd9oG|f\xb3n\xb9\xde\x01\xb6\x87o`\xfeh\
|
||||
\xe1\xb0\xe0\xfb\x95\xb7|"O8\xbbz\x05\xecF\x0f\xd8\x8d\x0e\x94\xbc\t\x1b\xab\
|
||||
\x17\x1a"xC\xd0\xd2\xf2\x00\xf6\x19 \x13\xd6\x95\xec\x05sk\xc6\xf2\x15\xaap\
|
||||
\x0cY\x81hS\x08\x08\rG\xbc\xff\x92;\xdd\x16:?\x8c\x91Y\xa3@C\x99\xd8\x95.\
|
||||
\xf8qJ\x17/\xc6\xd2m{\x1fAJyP\x14\x0fc\x00\xa88\xd3\xa2c\xcaq\xd0\xecW\xa5l\
|
||||
\x14\xd1Y\x15\x18\x81\x0c\x87\xf8\x83\xc5\xd3\x00i\xb9\xc3\xa9mX\xb5\xfe\xc8\
|
||||
`\x8e\x1e\xa4eQ\xc9^0\r*d\x10\'\x00\xbd\x05rx\xe0cW~\xd6\xe5\xcc\x01\xf3\r\
|
||||
\xaf\x93\xb0\x82\xf0\x01\x83@6J\x15{\xa1|\x85\xa6\xf2q6\x0e\x9a\x17%\xe1n1s\
|
||||
\xc7\xb3F\xe9\t\x97S\x0eqJ\xaa%\x84\x9e\xa5F\x7f;{\x05\x190)\xc4)\xa44\x14\
|
||||
\x81S\x95\xf9\x01\xae[#9s@c\x93\x18w\xa5\xbe\xfe\xd6\xe2=\x07\xafd\xe9!5\x96\
|
||||
\x07\x85\xb5\x0f\xac\xa4\x846Et\xcee)_0<m\xb3-\x86\x89*\xc8\xad\xef\x8d\'c\
|
||||
\xe4\xc9\xd3\x90i1u\x10\x7f!u\xc3\x9d%\x15\x81`\xf3db\xe76\x96v\x0e~\xd8\x0c\
|
||||
\xd3\xf2\xe0\x88\xe8\x84\xbc`Pa\x93P\x9a\xe4>\xc0\x1cA {!\x8eM9|\xcdF\xd1J\
|
||||
\x04D\x04{zKF\x87qX\xf0\xd0\x91\xbdd\xe4S\xc2\xf92\x18\xa6n\xe3S\xd2\x80\xe8\
|
||||
\xf6} \x11\x0eY\xfdJ\x11\xb1:d\r\x96\xef\xbf\x1ci@\xc93\x00\xd3`\xe8\nk9/\
|
||||
\x0e\xc4\x16\xc7\xf8F\xfc}\x0f\x07^\x96\'rc\x0b\x11\r\xa0\xdf\xcf\x00\'P\x81\
|
||||
H\x80\xcf\xb8-V\xcf\x8fU\xd9\x06\xed\xec\xcfp\xfe\x8d9k\xc2\xbe\x14J\xf0LD\
|
||||
\x9c\xb4\x8a\xbf\xed\xa32\x90\x8b\x93!op21v\x1f\x1f\xc5\xf2\x15z\xe2\xb0\x03\
|
||||
\xac\x02g\x08\x89*\xd9\x06\x04}W\xf2\x03\xeeA\r\xa1\xd6\x01\x0c\xbc#\xfe~\
|
||||
\x14\xeb\xebc\x06\xd9\xf0\x9d\x80\x97Y\xb7\xc8m\xab\x03\xfc\xc4\xd9,\x1e]\
|
||||
\xb9\xa4p\x83\x9b4\x83\xbe\xccC\xb2\xc2\x01]\x1f~\xdcA\x17`0\xbed\xf4c\xd6\
|
||||
\x060\x9bSlu\x17[\xfd\x02S\x99r\xf2\x9bcC\x92OO\x9c\xc0\x03\xdf\xef\xfb\xf4\
|
||||
\x84\xd5\xc2\x89<\x9e\xe0\xab\x08S\xe4v\xe9\x01\x12pk\xf3\x06B\xe4l\x93n-6f4\
|
||||
\xbd\xc2\x89\xc0\x18\xb3y\xed"\x04\xce\xd7K\'%\xb0,\xbd\x14nl9\xd9\xbc\x92\
|
||||
\xf9\xe2\'\x7f\xa5\xea\xba-\x89\x03\x83:\x88\x9c9\x10\xc1\xcf!\x94+v\x98e-\
|
||||
\xd9-\x04l)\xdb\xa0\x8a+\x83\xaed\xd6\x85\xe6\xa5++\x90J?\x96\xaf\x10\x9379K\
|
||||
Q\n;\xca\xd0\xf8\t\xc8\xd3;\r\x1f\xa5\xca .\xcc\x16/\x871\xbeE\xd1Ez\x9c\xc2\
|
||||
\xb1\x1aj\xf7#\x9c/\x9c\x16\xaf\x17K\x16\xe2\xfe\xcbH>A\\w\x03@\x8a\x1c\x9c\
|
||||
\x95K\x81|\x05h}\x03"\x98S\x1e\x8eg\xaf\xd0\xbc\xac\xc4@\xba\xb9\xfcm0\x0f9D\
|
||||
\xf8+\x11\xeb\xb6|\x02\xa9\x9f+\xb9\x1cp\xcd\x95Az\x8c.=\x00\x88"\xe7+\x00I\
|
||||
\x85\xfcAl&\x1c\xda\x92|b\xb2\xcf\t\x85\xd8g\xf0\x17\x0bW\xde\x8c\xbd\xc4U\
|
||||
\xd9:\x8aAG\x01\xef\xb9\x0c\xca\xc4\x9a\xc1\x03\x83\xaa\xf0\x89O1\x1f\x97\
|
||||
\x15~\x9a\xc0\x18\xf5a\x9b&\x9e|\x05\xe9\x1cB\xd9(\x8c\xf7\x08\xc9M86\xbf\
|
||||
\x1c\xc8\'`\x03\x85</\xf0\x8fe\x18\xba\xcdf\xb5\xce\xb05\xfe\xa4\xd0\x05#C\
|
||||
\x19\xf6\xaa&\xf2\t\x1az\x8b\xbf\r\xe6#\xba\xb2\xd3\x95\x15p\xb9\xf2\x07\xa5\
|
||||
<\x867\x8cp6\x0c0Q7dG\xf9\xfd\t\xae\xfeY\x16\x8bq<\x113J\xcdE\xd0/\x98U\xd0D\
|
||||
](,\xfa PJ\x13;\x8a\x9d*\x8a\xc3!,D\xcdO]\xc0\xd6|\xc4<#\x86\x9c\xf1\xb7\x8e\
|
||||
68\xf1\x95\x8b\x94\xfe1E\x02\xd2\xc2G\x90\xfc/\xc3\xa3&8\xd5\xbf\xf1\x87\xad\
|
||||
8\xa9\xc82\xe5\xdd\x18\xd7\x854\xb6\x04\xa1x\x0fw\xdf\xf6:\xc0L\x0c\xb1o\x87\
|
||||
\x01\xc4\x85\x0f\xa5\xf7\xba\x8b]9\x81O\xe9\x90s\x88\xc8d\xc8\xae\xec\x16\
|
||||
\xa8NF\xa9\xb1J{\xc1\x9f\xbeb\x95\xd6\xc0\xb1V\x17jT\xe7n\xf1\xca^%\x89C\xe4\
|
||||
,\x91\xaf\xe0$\x8cd\xa3\x88Y\xb4\xb1\x81X\x83\r\r\xf9\x04Ul9\x88\xcd\xf3G\
|
||||
\xfe\x169y\x96\xdd\xc7\xfd\x06\xba4!\xc4+\x0c\xefXA\x80\xdb\x87O\xe9\xc4\x92\
|
||||
\xbdP\xc5\x84Sc\xf1W"\xd2\x11fn\x00#\xd1\r\x81\xbaae\xf3\x8a\xf3L\xb0\xb2\
|
||||
\xd9\xa3\x1b\xfe\x13\xb1\x07\xa9b\xc2\x8b\x9f\xad\xc6G\x03P\rN\x08\xe2\x04\
|
||||
\x88\xe0\xb9p\x88x\x01w\x0beb\x97|l\xc3\xcc\x81\t\x98\x92\x8bq<\xc9\xa1J^sZM\
|
||||
\x96c\xc7th\x15u\x86mZ\x0e\xb3ni\xef\xabd\x13\n\xa9\r\xbd\xf0n\x84x\x83\x0f\
|
||||
\xb7\xed=\xee{\x14\xec\xe8^eE\x91\xc5\xd6W\x05\x92\x7f\x82\xfcR]\xfe\x90\xe4\
|
||||
\xd8\xa7\x8a\xdfm\x84J\x06\xfc\xd5\x0c\x03\xd1j\x06\x83u:\x80\xe1p\x9a\r\xd7\
|
||||
\x92#\x85\x07\xcd\x813\x83\xb7z\xca\x1f\x81d\xc7\xbf\x19\xcaW\xa8\xa2\xdb\
|
||||
\x01\xb2\x8bs\xee\xa8#\xd9-\x0e>\x0b[\x9d\x93w\xb6\xa7\x80H\xf0\x87\xbe\xbe#\
|
||||
\x83n\xc1\x8e\xaaa\x1bP/\xce\xaaq\xc9\xb7k\xf9\x16\xf8\x11\'\xae) \x93U[\xbe\
|
||||
BO\x84#\xa4\xd7b\xf0\xe4\x91%\xbb\xa5\x8ao\xf0\xb0\n\xbd\x10\xf7=X1\x9a@S\
|
||||
\x1az\xb2\r8\x10\x19c\xd4\xc7T\xaek\xf2\x15\xa4\xb5\xe2\x8ftq\x9e\xde\xf3\
|
||||
\x18+\xd9\xc7J>%X\xa3\x9f\x8f1\x95\x8f\x08P9\x8c\xe7\x1fp\xe2+\x17\xcb\xf2\
|
||||
\x8e1W\x9cw\xc3\xe7\xef\xcf\xf9\xe0\xd8\x1ag\xb2\xb1\xe0\xcc\x08\xf8\xebr)\
|
||||
\xdf\xf5\xe2\r\xe4B:\xec\x84\xb2\r\xa4\x1a\xb4e\x05\xc0\x82\x0c*\xf1`\xef/\
|
||||
\x84\xc8\xa7\xc4\xaaS\xe0\xcanqI\x00\x11+a#\xc3\xa1\xca`0\x07\x07\xdfwS\x0e\
|
||||
\x0c\xb9\x18K90b\xe1w\xba\xb8U\x14\x19\xe4\xc7\x99J(\xa4\x82\xb37\x0c+\xd9\
|
||||
\x06t)U\xbeB\x15\xc7\xb3q c,\xe3Os\x88\xf1{\xce\x7f*qJ\x0cm\xe2 \xf9]\x87\
|
||||
\xe7\x16\xb6\xe9\x16\x89-\xdb/p\x91\xbe\x17\x01:\xcbhJ\x93?\xc1\xc5\xceP\x9f\
|
||||
3a\r\xa0\x9f\xde1\xd4\x8bS\xa7\x8d9O\x8d\x0f&?\x94r\x1cD\xfe!\xc3\x92\xf8\
|
||||
\x1a\xe3\x0f\xe4b\xcc{\x90\x96\x9b\x0e\x00\xbac\xa8\n\xc7\x11\xbf\x02ZnB\xec\
|
||||
\xec!t\xcbkS>\x81K\x02$r\x062\xfb\\\xa0\x01\xda\xc4\x17\x0c\x1f\xe0\x92w}XZ\
|
||||
\xde\x149\xaal\xe8\x0e\x15_\xfa\xe6\xef\xf2l\xc8\xa1s\xfeS\xc4\xe7\x8a!\x14\
|
||||
\xc5!\xbc\xb4>\x7f\x97\xe7]\x02\x9c\x92\x8f\x15tJ\xc1\x94"\xe5t\xeb[\xb3^\
|
||||
\x90\x10$\x01\x021C\xb4\xfa\x8e\xbf\xda\xc5\xde/\x85q\x17\x9c\x1e\xf8\xd5f&\
|
||||
\xc3\x9d\xbeM\x17\xd9\xeaT\xaepUD@2\xf17\xa8n\r\xd9(\xe2\xd9\x1cju`Ff\x90\
|
||||
\xc9B\xc6\x00\xf9\xe7J\xf2\xa9\xc2s!\x83|l\xc2e\x92\xb8\xae/o\x84\x10D\xc2\
|
||||
\x1f\xa76\xbefB\xf2c\x92\xf27\xdb\x9el\x80\'\xc70#\xd7\xf9K$}H\xdc\x8f1\xae\
|
||||
\x00\x14\x80F\x9e\'|ha}|\xe3\x0c2Ch(#\x05_d\xe3\xef7|\xc0\xd7\xf62\x1b\x15\
|
||||
\xdf\x03\xd9\x0bU\x04:\xf7\x02c\xe4\xd6\x92\x1c\x83\x91\xa8\xcbW\xe0\x99\xe0\
|
||||
O-x\x88\x9aor:\xa9\t\xb4\x8bU:\tE\x0b\xf0\xa0e\xa4(\xda\xf9\x10\xba\xf6F$i\
|
||||
\xa1\'~0\xaa\x82\xbf\x91\xda\xb5y\x1apD\x8fg\xbd\x10\x93\x87\xb3^\xa8\xc2\
|
||||
\xeeP$\xc0\xe2\x94T\xdf\x19v\x92#\x14\xbf\x81+\xcc\x13\x0e`>C\xed\x19\x0cag;\
|
||||
\x1a\xa0\t\xf8r\xf1\x9cgI\x16\xc2jtd\xa3\xc8\xab\xe0\xc8\'\x90\xde\x13\xdf\
|
||||
\x06\x93\xe9\xe8N4|\xee\x85\x13\x82lq\xd2*\xbe,\xe9\x97@e\xf1W\xa9w\xa0\x07\
|
||||
\t\xcd\x19\x15\x11\x90\x19\xecP\xfdR\xca\n\xdc\'T\xf0\x05\xb2\x1c;\xfbP\x03\
|
||||
\xb2k\x00\'\xd3q\x80\x8fA\x15\x98\xca\xa5\xa9\x1c\x07M\xe5\n\xe3\xd88e\xc8\
|
||||
\x86&i\xc1\x9d\xf7H\x8e\x94^\xd9S%\xc7 >9W\x96\xc7\x1f<\xe5\x1b\x10|\x85h\
|
||||
\xe0\xca^`W\x9a\xb2\x17@\x12\xbbr`\x98JW\xd2\x82\xa9\x9c\xbd\x82KW\n3\x19\
|
||||
\x88\x99\xf36"\x016|[\x9fY\xe4\xb8\x109\xd3\x11\xbe\r\xc6\xc1\xfa\xb6\'{\x81\
|
||||
1\xc2\xb9\x7fl\xe4\xbb0a\xbeel\x9cm\xa3\xc2\xe1\x13\xe8\xa4\x8d\xa4\x11\x05t\
|
||||
\xba\xbd\x08\x10\t\xfe"\xec3I\\\xb7\x9f\xe2\x93\xa8\xf3\n>\xc3\xc3\x1c\xfb\
|
||||
\xa5\x01f\xd1\xc3nx\xe7 \xe7\x8e\x83\xfd\xf2JY\xce\x8a\xdc\x01\xa8$\xc4\xc7\
|
||||
\x8ee\x1e\x92{\x1d\xdfL\xe1KW\xd7|\x8a\xf1\xa5\x9a\xc0\x04~\x8c\xf3n\\\x91Sv\
|
||||
8p=h\x9f\xc8\x05-\x0cmZ0\x87#90\xaaX`Cb\x0c\xc3\xfb+\x9f/\x13\xcc\xedgG\xbe\
|
||||
\x02\xae\xbb\xb2[|\xc8iV\x81K\xdf\xfc\x91.\xce\x15\xaeG\x00\x94Y\x10\xf4\x1e\
|
||||
\xac\xd7\x94\xb34\xc7|\xc3\x9fCG\xdft|\xe2\x91\xf3\xe4\xad\xb4\x01\xf6\xe1\
|
||||
\xcfx-G\x08\x1d\xf1e\xeb\xd56v\x14\xc7\x81\x02d\x03.2\x9cs\xcb\x16t\x07\x06\
|
||||
\x93\x1eqJ\x19\xfe\xde\xbc?E\x16+\xfe\x08\xb5\xaf\x02\xfb\xe5\xc2O\xb7\xab\
|
||||
\xc9^\xa8\xa2\xe4\x0f}q\x02\x0c/\xc2\xa7\xaf\xf82\xcb\x9a\x06 \xd7\x04G\xa36\
|
||||
{\x85*>X\x92AH\xceh\xcb^\x90\x12\x93\xb1_lXm\x86\xf2\tZ\xa7\xfb\t\xf7\x82\
|
||||
\xeb\xc7\xab\x81|\x82X\x98\xe9\xb2\x17\x04\xa7\x19T\xc2\xea\xe8\xda\xacQ\\\
|
||||
\x12\xe0\xd46\xac\x9f\x9e\x93\xd4\xcesN\r>"I\xe7\xa4\xac\xc0_r\xf6\x97\x14+y\
|
||||
\xd9\x04\x10\xa3\x07\xddA)e\xb7\x08\n\xf3\x97\xbf,\xa0L\xee8\xabW\x8e`\xdb\
|
||||
\x11\x7f\xd7\x8bs\xcb\x1dv\x91\xb7\xa7\x0f!m\xcf\x9e \x9e&\xb3q\x10\x83\xa6\
|
||||
\x0cJ\x9b@H;0\xcd\xfb>\xacy\x95?~\xe4\xe0\xf3j\xf31\x80m\xfc\x85\xcb\xef\xb1\
|
||||
\x1c\x18"#\x0c\xaeeO\xde\x1ao1\x97Y\x18\xcanq\xcb\xd7e\t\x03\r\xe5\x83\x8a\
|
||||
\x1c\x11|w\xe5\xa2B8\x9f\xe3Q]\\U\xc93\x08\xb6\x92\x13\xc6\xb0\xc7\xf9%\x80\
|
||||
\x94\x1asbV\x0bj\x0f\x87\x8d/\x13 \x11X\xa5u\xf9B\x7f\x0ffSB\x87E:\xcc\x90\
|
||||
\x1a\xbc\xcd\t\xc92\xe0P\xc6\xbc\xb3\x07\xd8\xd9\xebtug\x92\x17\x88\x14\xbd\
|
||||
\xd3\xf0\xbdW\x8e\x89?\x90\xa5e\xd7_~#\xef\x17!v\xc5\x89\x03\xcf\xd5:b\xaf\
|
||||
\x13\x1f^\xdaE\x1d\xe08\x8b\xbf\x0c\x87\xd8k>\x81\xa7w\xdaA\x00\xa2\x0f{N\
|
||||
\xef\x00\x95\xc5\x9f\xf2Y!\xaf\xf5`\xd2\xb3\xa1k[L>d\x90\x95\xf0+\x9c\x81\
|
||||
\x8a\xbft\xc5\xba\xe5"2\x82f94\xa5\x82\xf1\x0e}\x80\'\x9d\x11@\x14>\x0e\xcf>\
|
||||
\xc4x\xd6\xc3\xc1\xd7\xe7\xefGqF\xe1\x1b\xec}\xa1n`{\xb4\x00\xd3r8\xf3\xf5\
|
||||
\xec\x95q\xe3>\xdeb\xf0\x82\xcf9fX\x85\xe3DO\x87\x9c\xb5IB\xac8\xa7\x1b\xdfW\
|
||||
/9I\xd3\x00\x87\x85m\xc8\n\x18\x12\x0c\xa0\xe2\xcb\x1bC2FR\x99\xab\xe2\xa6\
|
||||
\x94\xaf\xe0f[\x8b\xe7\x05:\xddE\x08\xf7 W|\xb1\x99\xa7\x9c\xbe\xa0\x92m \
|
||||
\x7fr\x1b\xdb\x94s0\xff\xd4%\x83\x10\t\xb0`\x9cqLK\rd\x1bP\xe1B\xf9\n\xa2\
|
||||
\xb3$\xd8\xc6\x05\x7fu\xf8\xa0+{\xa1\xf5\xf1\xcb@r\x13\x06>~wd\x1bXA\xfc=\
|
||||
\xad\x0cm(\x95\xac\xc0\xb7\xb0\xbaH\xe3\xc4Z\xdfG\x05a\xe3\x14L^b\x10\x16K\
|
||||
\xed\x1b\xceB\xc3\xb78\xe7q\xde\xca\x14\x19\xef\x0390zBC\x8e\x19\xa9\xc2\xfd\
|
||||
\xe14,\xac\xe4-q\xec\xd5\x01\x93\r\x06aq\x92\xb7g|\x88\xd6\x1f\x00\x00\xb1\
|
||||
\x17\xc8q\x00\xe1\xce\x9f\xdf\xe3/\xc2\x9e\xcd\x9e ~\xa8- \xee8\xc9\xdb\x85\
|
||||
\x8a\xaf\xffx0g\x7f[r\x1c\xc8mK\xca\x9503\xb1\x82\x1c\xfe\x16\x16c\x9d\xecY\
|
||||
\xa3\xc8\xd9\x15!\xc0\xcd\xaa\xd3\x06\'z\x1a\xe2\xfb@\x8e\x05\xe1\xc8\xf6\
|
||||
\xfe\xc0\x92\x8d\xd2\x13\xbb\x8e\x1c\x07\x9e\x98\xb5\x81;4\xael\x03Y\x9a\xc9\
|
||||
\xb93(2\xa0\x90\xe69b\xc5Aa\x8faI\x9c\x9ab\xc9\x06\xcc\xb3\x00\xfc\xc5\xa1\r\
|
||||
d\x8d\xf9\xca\xff\to\xc2\x14:L\xc2\x90\x00\xa4\xa6\x98S;H[\xc4\xe18\x9d1\x13\
|
||||
\x9c\xc9w\xab\x92\xaf\xe0\xcb\x1bt\xc7\xca+\xf8s\x84\xe7\x1c|\xe4K\x8a\x97\
|
||||
\x9cn\x8c\x91]\x07\xf4\xa5\xbcl\xc2_/\xfd\xad\xb3\xd9\x04\xb5\xe7\x85\xaf\
|
||||
\xa7\xdb\xf0\x07=\xbb\x00\x93f\xb8\x0fdu\xe1\xe7\xe7\xed\xb1\xc8`\x9f\x02;\
|
||||
\xea@\xe5oa\xf1\xc7\x8fl90\x18\xab-\xb8\xb2\xf8K\x9b\x87\x15b\x16\x0e\'\x8aS\
|
||||
\xb8Q\x06\n\x1b\xb2\x02\x17\xd8\x9b\x91\xda\xcd\xffh^8\xc1\xd2\x04X\xb8\x0c)\
|
||||
\x87\xc6\x054\xd8\r\x06.\xb1\x1a\xa8k\xb2[\xdc:j\xe18I\x81"(Zr\xe8\xc8se\xca\
|
||||
W\xa09"]P]\xe84Md\x05\xf0A\xaa|\x05\tQg\xb4\xd0\x13\xde\xacQ\xa4\x81\x9b\x8d\
|
||||
\x838v+\x9f\x80@\x19"\x88\x91\xb3\xfd\x92\xd0\x17\xa5\xc5"\x03\xfcv\x14\xc8^\
|
||||
\xe0\xb6\xe5\x108;!\xdft\xdc\'t\xf8\xb3\x99\x16\x0e\x1cV\xf2:S\xe0r8\xb8\xf4\
|
||||
DBZ\xec0T<s~)\x86%\xa5\x9cA\x86\x95<_Ct\xc5\x03\xc7\xb4\x11\x84#\x7f`\xeb\
|
||||
\x05\xc1i\xa1o\x00\xbb\x01\xbf\x94\xc7;\xea\xbb\x01a\xc0\xba\x94\xa2c{\xf0\
|
||||
\xe5\xc0v +\xa8\x8d\xc0\xc1\xd2fg\xf9\x16\xebt\x19"#!\xeeW\xa6\x9c\xdd\xe3/>\
|
||||
Q?\xe0\xd3\xf4\xbd\x86\x18\x1fG\x8a\x9e8\xae\xee\xf1\x17\r8\x15T\x1f\xee\xe3\
|
||||
e2h\x069\xe3\xd8vfO\x00S\xd3\x02R%\xc7V\x9frN\x15F \x96]|q\xab\x8f\xb0\xe0\
|
||||
\x83!\x1b\xa5\x8a\t\xa7?\xe1\x14\xb2\xbf\x18d\xe3`\xad\xf7]$I`\xf1y\x11!\x8f\
|
||||
\xd1\x80?\x82` J\xcc\xdf\xb1z\x1d\xc96\xa0\xd1\xcf\x1a\x85inC\x81gH\xe2.K~\
|
||||
\xfe\x9c\xd83\x85l\xf2\xac\x00V\xa1\x9f 8\xedC=/;\x08\xa4\xf2%\xe7\xfd\x00\
|
||||
\x11M\x0f\x07\xce\x07\xfe\xe2\x16\x7f\r\xe9\x0c\x1ft\x90\xa9\xc2\xfe\x1a\xf8\
|
||||
~T\x0f_\xe9>\x89e\x1b\xa8p\x01)\xeaC\x81\xffH\x01\x99\xc9\x98\x9d]\xbbm\xe4\
|
||||
\x99(\x00oX\x9b\xe2\x83R\x8c\xb8\xeb;\xb2Q$\xcb\xe6\xccO\x03\x00\x97l|\xddE4\
|
||||
\x81/oP\xbaB[~z\xe2}\x02\xd4^\x8a\xf8\\\x9b?\xef\xca\x19\x0f\\:\x1a\xc5^\x00\
|
||||
`\xf9\x03c\xad3`i\xf7b\xf8.8\xbf\xf6YW\xb6\x81;g\xb8qXp\xe6Z\x87\xbf\x94\x97\
|
||||
\x81\xa7\xad\x96)J\xd9\xaaZ\xd3V\xb7\x95\x88?JKm\xb5\x9b\xa2\xb5:\xad\xa8e\
|
||||
\xb4F\xad\xa0\x15\xb7tQ\xc2\x96%\xfe;\x12\xc5n9-W\xfc\xf1Z\xfd\x96\xdf\x1a\
|
||||
\xb4\x86\xad^S|YnDyj\x1d\xb7n[\xf7\xa2\xfcn=\xb4\xceZ\xd7\xad\x83\xd6]\xeb\
|
||||
\xa8u\xd2:m\xfdl\xfdj\x9d\xb7.Z\x7fZ\xcfbL\x8f\xad\xc3\xd6\x95\xf8})\xfe^k}l\
|
||||
e\xad\x9dV\xde\xfa,\xfe\xdei\xa5\xad-Q\xb2V!J\xd6\x94\xfa\xdf\xban\xd2Zh-\
|
||||
\x8a\xb2\xdcj\xc8%RVZ\xab\x82\x98D\x10\xc2\xc4h\r)\xeb\x82\x90%A\x8a\x05bt\
|
||||
\xf1\xdbhm\x8b\xb2\xd7\xfa\xd4\xda\x14\x7f\xc6 \xa6\xf7\xff!f\x17\xc4\xd4e\
|
||||
\xa3\xf5\xae\xf5\xd6\xfa\xd6\x103\xdfz/\xc8\xf9 \x88\xf9\xde\xdao\xcd\xb5\
|
||||
\xbe\x8a\xbf\xbf\x08b\xfe\x08\xa2\xd6Z/\xad\xbf\xad\xd7\x86\xcfDL\xda\x10\
|
||||
\x93\x81\xa0B\x10\xfb\xb1\xf9\xd74K\xb32\xa7f\xd7\xfc\x872M\x94\xad\xc4TL\
|
||||
\xd5l\x9b\x9a\xd91#\xf1\xc70Gf Jl\xeaf(\x8ae\xda\xb28\xa6kz\xa2\xf4M\xdf\x1c\
|
||||
\x98C\xb3g\xde\x98O\xe6\xb1y+\xca\xb1yo\xfeFy0\xcfD\xb96\x0f\xcc;\xf3\xc8<1O\
|
||||
\xcd\x9f\xe6/\xf3\xdc\xbc0\xff\x98\xcf\xe6\x0f\xf3\xd1<4\xaf\xccKs\xcd\xfchf\
|
||||
\xe6\x8e\x99\x9b\x9f\xcd\xd4\xdc2\x0bsb.\x88R\x88\xdf\x8b\xa2,\xff\xaf\xb2b\
|
||||
\xae\x8a\xb2\xde\x94%s\xdb\xdc3?\x99\x9b\xe6\xd8lff\xa5\xb5\x0bR6\x04\x19\
|
||||
\xff&\xe6\x9d \xe5\xed\xbf\x88\xf9f\xce\x9b\xefE\xf9`~7\xf7\x05As\x82\x98\
|
||||
\xdf\r15)O\xe6W\x90B\xc4|1_\xcc\xbf\xe6\xab\x98|\xb3,\xcb\xaa\x9c\x96\xdd2)\
|
||||
\x95R-\xdb\xa5Vv\xca\xa84D\x19\x95A\x19\x97\xba\xf8;,\xad\xb2\x10\x84X\xe5\
|
||||
\x96 \xa6\xfe\xbd\x00"\xec\x92\x8a\x83\x7f\x89,\xb7\xf4\xca~\xb9i\xfa\xe5?L\
|
||||
\x0c\x912(7\xc4\xdf51\xc3\xb2W217\xa5\x03\x82\xa8x\xe6S9\x14320\x8f\xcb\xe3r\
|
||||
\x88\xe9\t\x02\x9e\x1aBj2\xbe6\xa5&\xe6\xb6\xbc/\x7f\x97\x0f\xe5Yy]\x1e\x94\
|
||||
w\xe5QyR\x9e\x96?\xcb_\xe5yyQ\xfe)\x9f\xcb\x1f\xe5cyX^\x95\x97\xe5Z\xf9\xb1\
|
||||
\xcc\xca\x1dQ\x82r\xcd\xcc\x05)\x9f\xcbz\x96\x16\xc4<M\xcc\x14d8\xe5V\xf3\
|
||||
\xb7\xd3\x90D\xc4\x14\xe5\xa4\xacg\xe6\x7f\x11S\x97\xf6\xbf\x88\xa1e\xb6P.\
|
||||
\x947(\xf5\xef\'A\xc0b\xb9\\\xae\x94\xab\xe2\xcfz\xb9Tn\x97\xf5R\xbb\x01\x19\
|
||||
u\xd9+kb>\x95\x9b\xe5\xb8\xdc-7\xcaw\xe5[\xf9\xad\x9c/\xdf\x97\x1f\xca\xef\
|
||||
\xe5~9W~-\xbf\x94/\xe5\xdf\xf2UH\x1e\xb3*\xab\xaa\x9aV5Y\xddj\xd4\x90S\x17"\
|
||||
\xa6\x9e\xa7\xa4bR\xb6J\x9e\xa3zf\xd6M\xa5r\x051j\xd5\xae\x1ab\xb4\xaaSE\x95\
|
||||
Q=5\x9c\xad\x17\xc9\xbd\xe0\xfe\xa8r\xc5\xde\xf8\xd6\x90R\x97\xa0\x8a+\xbd\
|
||||
\xaa\x17R\xcf\x0c+\xab\xb2+\xa7r+\xaf\xea\x8b_~5\xa8\x86\xa2\xcc\x99L\x14\
|
||||
\x11\xd3\x13\xff\xdfM\xf5T\x1dW\xb7\xe2\xd7}\xf5\xbbz\xa8\xce\xaa\xeb\xea\
|
||||
\xa0\xba\xab\x8e\xaa\x93\xea\xb4\xfaY\xfd\xaa\xce\xab\x8b\xeaO\xf5\\\xfd\xa8\
|
||||
\x1e\xab\xc3\xea\xaa\xba\xac\xd6\xaa\x8fb\x1f\x119VY\x97\x8f\x15\x93B\xc4\
|
||||
\xd0\xcc,7\xbb&\xabv\xaa\xbc\xfa\xcc\xc4\xa4\xd5VCLM\xca\x8d$\xa6.59E5\xa9\
|
||||
\x16\xca\xb8\xbam\xf6\xc3\x8d9\xac\x16\xaa\xd5\xd2\xae\x16\xab\xe5j\xa5Z\xad\
|
||||
\xd6\xc5@\x97\xaa\xed*l\xc8\xa9\x89\xd9\xab03\xe2\xff\xd9\xac\xc6\xd5n\xb5!~\
|
||||
\xbd\xab\xde\xaao\xd5|\xf5\xbe\xfaP}\xaf\xf6\xab\xb9\xeak\xf5\xa5z\xa9\xfeV\
|
||||
\xaf\xe2T0\xa7\xe5\xb4\x9aN\xa7\xddi2U\xa6\x99\x10\t\xba\xd8I\xc7\xa5:eb\xb6\
|
||||
P\xec\x86\xac\xf6\xb4=u\x049\xab\r1\xda\xb43%8_CL45\xa6\xa3\xe9\xbd $\x98\
|
||||
\xc6S}\x1aNG\xd57\xd3\x9a\xdaSg\xeaN\xbdi\x7f\xbaP\xfa\xd3\xc1t8\xedMo\xa6+\
|
||||
\r1O\xd3\xe3\xe9Bu;\x15\\\x9f\xfe\x9e>L\xcf\xa6\xd7\xd3\x83\xe9^u7=\x9a\x9eL\
|
||||
O\xa7?\xc5\xff\xf3kz>\xbd\x98\xfe\x99\xd6\xc4<O\x7f\x88\x7f\x1f\xa7\x87\xd3\
|
||||
\xab\xe9\xe5tm\xfaq\x9aMwDM>\xfd<M\xa7[\xd3b:\x99.L\x17\xa7\xcb\xd3\x95\xe9\
|
||||
\xeat}\x1a\x94yI\x82\xe0c\xc5\xdb\xdfn\xc8hO\xb7@\xd0\xd2t[\x94\xceto:7[f\
|
||||
\x9f\xa6\x9b\xd3\xf1twzln\x88\xe6E\xd7\xd3\xb7\xe9\xb7\xe9\xfc\xf4\xfd\xf4\
|
||||
\xc3\xf4\xbb\xa8\xd9\x9f\xceM\xbf\x8a\x7f\xbfL_\xa6\x7f%17\xd3Wq\xd2\xda\x95\
|
||||
\xd9\x15\x8b]\xfc;\xed\x1eL\xbb\xddD\xfcR\xbaj\xb7\xdd\xad\x89\xd1\xba\x9dn$\
|
||||
~\x19\xddQ7\xe8\xc6\xe2\x97\xde\r\xbbV\xd7\xee:\xe2\xb7\xdb\xf5\xba}\xf1\xaf\
|
||||
\xdf\x1dt\x87\xdd\x9e(7\xdd\xa7\xeeq\xd7\x17c\xb8\xed\x86e-\x06\xac2\xa9\xea\
|
||||
\x1dB\x85e\xdaV\xb9$Xy<\xbd\xef\xdew\x7fw\x1f\xba\xcd2;\xeb^w\x0f\xbaw\xdd#\
|
||||
\xd1\xe4I\xf7\xb4\xfb\xb3\xfb\xab{\xde\xbd\xe8\xfe\xe9>\x8b\x9a\x1f\xdd\xc7\
|
||||
\xeea\xf7J\xfc\xba\xec\xaeu?v\xb3\xeeN7\xef~\x16\xff\x9dv\xb7\xbaEw"~\x9dw\
|
||||
\x17\xba\x8b\xdde\xf1k\xa5\xbb\xda]\xef.\x89_\xdb\xdd\xbd\xee\xa7n\xbd\xcc6\
|
||||
\xbb\xe3\xeen\xb7f\xd0F\xf7]\xf7\xad\xfbM\xfc\x9e\xef\xbe\xef~\x10\xff~\xef\
|
||||
\xeew\xe7\xba_\xbb\xba \xe2K\xf7\xa5y\xeao\xf7U("fbW\x82\x86\x84\x89Y\x91g\
|
||||
\xcb\xba\xf8U\x93\xb4U.\x88\xa5}#\x08Z\x9aN\x93n\xf2\x0fi@I\xa2$j\xd2\x16\
|
||||
\xafkI\'\x89\x12#\x19%\xfdn\x90\xc4\xa2FO\xc2\xc4J\xecDo:r\x127\xf1\x92~\xf2\
|
||||
\xb9\xf9/\xc1\xcfd\x90\x0c\x93^r\x93<%\xc7\xc9mr\x9f\xfcN\x1e\x92\xb3\xe4:\
|
||||
\xd9\xef\x1e$wIM\xccQr\x92\x9c\x8a_?E\xdd\xaf\xe4<\xb9\x10\xbf\xff$\xcf\xc9\
|
||||
\x0f\xf1\xefcr\x98\\%\x97\xc9\x9a\xf8\xfd1\xc9\x92\xba\xcd\x9d$Ov\xca\xcf\
|
||||
\xe2w\x9al\tb\xd4i\x91L\x92\x05\xf1\xf6b\xb2\x9c,\x89-_\x93\xb3l\xd63S\x13s,\
|
||||
\xcaJ2M\xe6\xfe=3\xab\xc9z\xb2\x94l\x8bF\xf6\x92O\xf50\x92q\xb2\x9bl$\xef\
|
||||
\x92\xb7\xe4[2\xdft\xb4\xd0}\x9f|\x001\x8b\xd5\xbe\xe0\xeb\xf7d?\x99K\xbe&\
|
||||
\xeb\xddn\xf7\x8bx\xe6%\xf9+\xfe~\x15z\x91\xa9\x94JML\xa5L\x95\xaeR\xef\x99D\
|
||||
Q\x14Ui+\xf5\xbb\x9a\xd2\x11\xffF\x8a\xa1\x8c\x94@\x89\xc5o]\t\x95\xba\x0fK\
|
||||
\xb1\x15Gq\xc5oO\xe9+\xc7\xa5\xaf\x0cD\x196\x7f\x0f\x94\x9eR/5\x92h\xedf\x99\
|
||||
\xdd(7\xca\x93r\xac\xfcC_\x1b?\xeb\xde+\xf5\xdc\xfc\x16\r<\x88?g\xca\xb5\xf8\
|
||||
\xfbB\x0c\xf8@\xb9S\x8e\x94\x13\xe5T\xfc\xf7O\xf1\xe7\x97r\xae\xf4\x93\x0b\
|
||||
\x85\x88\xf9#\xfe\x9f\x0be\xbd[\x13RK\xb0\xe7f\x00?\x14\xbfzT\x0e\x95+\xe5\
|
||||
\xb2!fM\xf9\xa8d\r1;J\xae|V\xd2\x86\x98-\xa5\x10\xffN\x94\x05eQYVV\xc4\xefUe\
|
||||
\xbd!fI\xd9V\xda\xe5^\xd3\xd6\'eS\x19+\xbe\xb2\xabL\x92\r\xe5]C\xce\x86\xc2\
|
||||
\xc7f-\x02\x96\xa6\x89\xf9\xa6|S\xe6\x95ff\xde+\xb7\xca\x07\xa5\x9e\x9b\xef\
|
||||
\xa2\x81^w_\x99S\xbe*_\x94\x9e\x18\xf0\x8b\xf2WyK^E}K5\xd5R\xb5\xabJ\x9d\xaa\
|
||||
oI\xd5\x10\xd3U\x13UQ\x1bB\xaa\x07SU\xdb\xaa\xa6v\xd4^7R\ru\xa4\x06j,\x9e\
|
||||
\xd7\xd5P\xb5T[\xad\x89qTW\xf5\xd4\xbeZ\xbf\xeb\xab\x03\xf1\xefP\xed\xa97\
|
||||
\xea\x93x\xe7X\xbdU\xefU"\xe6\xb7\xfa\xa0\x9e\xa951\xd7\xea\x86r\xa0\x1e\xa8\
|
||||
w\xeaAS\x8e\xd4\x81r\xa2nI1]\x13s\xaa\xde\x08b~\xaa\x8d\x00\xf8\xa5\x121Jr\
|
||||
\xae^\x88&\xfe\xa8\xcf\xea\x0f\xf5Q\xad%\xd1\xa1z\xa5^\xaak\xeaG5Sw\xd4\\\
|
||||
\xfd,jSuK-\xc4\xbf\x13uA]TU\x9c*\xcb\xea\x8a\xba\xaa\xae\xd3p\xd4muO\xfd\xa4\
|
||||
n\xaa5C\xc6\xea\xae\xba\xa1\xd6\xe2\xfb\x9d\xfa\xa6~S\xe7\xc5\xef\xf7\xea\
|
||||
\x07\xf5\xbb\xba/~\xf5\xab9\xf1\xf7W\xf5\x8b\xfa\xa2\xfeU\xd7\xc4\xf3\xafBe_\
|
||||
\x16*\x89\x10\x00\xedkq\xb4\x1f\xa8\xd3v\xb7\xf9{\xda6\x041I\xfb\xdf\x82yI\
|
||||
\x9c6KS\xb1v\xdb\xf4\x91\xeev\xbb\xad\xb5;\xed\x1fj\xd46\xda\xa3v\xd0\x8e\
|
||||
\xdbz;l[m\xbb\xed\xb4\xdd\xb6\xd7\xee\xb7\xfd\xba\x99\xf6\xb0\xddk\xdf\xb4\
|
||||
\x97\x94\xa7\xf6q\xfb\xb6}\xdf\xfe\xdd~h/\xaa{\xa5(\xd5Y\xfb\xba}\xd0\xbe\
|
||||
\x13C8jo$\'\xed\xd3\xf6\xcf\xf6\xaf\xf6y\xfb\xa2\xfd\xa7\xfd\xdc\xfe\xd1~l\
|
||||
\x1f\xb6\xaf\xda\x97\xed\xb5\xf6\xc7v\xd6\xdei\xe7\xed\xcf\xed\xb4\xbd\xd5.\
|
||||
\xda\x93\xf6B{\xb1\xbd\xdc^i\xaf\xb6\xd7\xdbK\xed\xfd\xeev{\xaf\xfd\xa9\xbd\
|
||||
\xd9\x1e\xb7w\xdb\x93\xa4&c1\x99\x11\xb3\x9cl\xb4\xdf\xb5\xdf\xda\xdf\xc4\
|
||||
\x9fw\xed\r\xf1\xf7[{\xbe\xfd\xbe\xdd,\xb3\x0f \xa6&Go\x7fo\xef\xb7\xe7\xda\
|
||||
\x91\xf8\xfd\xb5\xfd\xa5\xfd\xd2\xfe\xdb~\x15\xa6\x8d\xa9\x95\x9a8f\xb5\xae\
|
||||
\x96h\x8a\xa6jmM\xd3:Z\xa4\x19\xdaH\xabI\t\xb4X\xd3\xb5P\xb3\xb4Z<\xdb\x9a\
|
||||
\xa3\xb9\x9a\xa7\xf55_\x1bhCQz\xda\x8d\xf6$~\x1fk\xb7\xda\xbd\xf6[{\xd0\xce\
|
||||
\xb4\xfb\xd6\xb5v\xa0\xddiG\xda\x89v\xaa\xfd\xd4~i\xe7\xda\x85\xf6G{\xd6Tu\
|
||||
\xaf=\xd2~h\x8f\xda\xa16n\x96\xd9\x95F\xcb\xecR\x1b(k\x9a\xa1\xd6\xe4\xbckH\
|
||||
\xd9h\x7f\xd4>j\x99\xb6\xa35\xcb\xecV\xb9\x16\x02\xe0\xa0{\xd0\xc845\xb9\x13\
|
||||
\x7f+\xc9A7\xd7>k\xa9\xb6\xa5\x15\xdaD[\xd0\x16\xb5emE[\xd5\xd6\xb5%m[\xdb\
|
||||
\xd3>i\x9b\xdaX\xdb\xd5\xf6\xda\x0f\xe6\x86\x16h\xef\xb47\xed\x9b\xd6\xeb\
|
||||
\xcek\xef\xb5\x0f\xdawm_\x9b\xd3\xbej_\xb4\x17\xed\xaf\xf6*JKX\x17e\xa7\xeaL\
|
||||
;\x9e\xda\xed$\x1d\xa5\xa3\n\xfei\x9d\x87\xaa\xd3\x89:Fg\xd4\t:qgU=\x103\xac\
|
||||
\xaaz\'\xec\xa8\xa6\xd5\xe9\xb6\xedNO\x0c\xdf\xe9\xecw\xdd\x8e\xdb!\x89\xb6\
|
||||
\xdf\xbd\xd4\xae\xb4\xa1\xe2u\xea\xd2\x17e\x07\xc4\xcc\xd5\xa2\xf9^\xa1R\xef\
|
||||
\x9c\xba\x1c\x88\x1a\xbf3\xe8|\xd6\x86\x9d^\xe7\xa6\xf3\xd49\xee\xdcv\xee;\
|
||||
\xbf;\x0f\x9d\xb3\xceu\xe7\xa0s\xd79\xea\x9ctv5\xbd2\xcc\xeb\xf6Y\xfb\xb4\
|
||||
\xf3\xb3\xf3\xabsS\x9dw\x96\xdb\x17\x9d?\x9d\xe7\xce\x8f\xcec\xe7\xb0s\xd5\
|
||||
\xb9\xec\xacu>v\xb2\xceN\'\xef|\xee\xa4\x9d\xadN\xd1\x99t\x16:\x8b\x9d\xe5\
|
||||
\xceJg\xb5S\x13\xb3\xdeY\xealw\xf6:5)+\xaa\xaa\x8e\xb4E\xf5D\xfd\x84\xc1\xff\
|
||||
\xef\xb2\xdf\xa5\x7f7\x05\xb1c\x10\x93i\xf2\x9c\xa9\t\xf9 JM\x0e\x13\xb6+H\
|
||||
\xd9\xe8lt\xdeu\xde:\xdf:\xf3\x9d\xf7\x82\x98\x0f\x9d\xef\x9d\xfd\xce\\\xe7k\
|
||||
\xe7K\xe7\xa5\xf3\xb7\xf3\xdaiEf\xb4*6\x7f\x19\t\xf5.\xeaFI\xa4Db\xf3EZ\xd4\
|
||||
\x89\xd6:Q\xb4[\x1a\xd1(:0\x83(\x8e\xf4(\x8c\xac\xc8\x8e\x9c\xc8\x8d\xbc\xa8\
|
||||
\x1f\xf9\xd1 \x1aF\xbd(\xea\xdcDO\xd1qt\x1b11ag1\xf9\xbf\t\xa9\x8bP\xae\xba\
|
||||
\xfc\xfbD\xedw\xee\xa3\xdf\xa2`\x99\x9dukB\x1e\xa2\x87\xa8\x96j\x1f\x14*g\
|
||||
\xd1ut\x10\xddEG\xd1It\x1a\xfd\x8c~E\xe7\xd1E\xf4\'z\x8e~D\x8f\xd1at\x15]FkQ\
|
||||
+\xfa\x18e\xd1N\x94G\x9fEI\xa3\xad\xa8\x88&\xd1B\xb4\x18-G+\xd1j\xb4\x1e\xdd\
|
||||
UK\xd1v\xb4\x17}\x8a6\xa3q\xb4\x1bmD\xef\xa2\xb7\xe8\xbe\xf5-\x9a\x8f\xdeG\
|
||||
\x1f\xa2\xef\xd1~4\x17\xfd\xd2\xbeF_\x1abV\x051/\xd1\xff\x8f\x8c\x81B\xba\
|
||||
\xc7\x9d\xcauGjM\xcc\xdf\x86\x98\xb9\xfa\x9c\xa9\tab\xf8\xf7k\xd42La\xd3V\
|
||||
\xc6\xd4\xe8\x1a\x89\xa1\x18\xe7\xe2\x00i\x1b\x9a\xd11"\xc30FF`\xc4\x86n\x84\
|
||||
\x86e\xd8\x86c\xb8\xc6\xd7\xd23>G}\xc37\xce\xb5\x8114\xd6\xa3\x9eqc<\x19\xc7\
|
||||
\xc6\xadqo\xfc6\x1e\x8c3\xe3\xda80\xee\x8c#\xe3\xc485~\x1a\xbf\x8cs\xe3\xc2\
|
||||
\xf8c<\x1b?\x8cG\xe3\xd0Xi\x04\xfc\xb2\xba\xdc\x1e(>N\xfc\xff&\xa4.\xb3\x99\
|
||||
\xbb2.\x8d5\xe3\xa31G\xe7\xcc{IL\xbd\xcc2\xa3.\x0f\xd1\x8e\x91\x1b\xaf\xd1g#\
|
||||
5\xb6\x8c\xc2\x98\x18\x0b\xc6\xa2\xb1l\xac\x18\xab\xc6\xba\xb1dl\x1b{\xc6\'c\
|
||||
\xd3\x18\x1b\xbb\x82\x94\x9a\x98\r\xe3\x9d\xf1&H\xfa&\xca\xbc\xf1\xde\xf8`|7\
|
||||
\xf6\x8d9\xe3\xab\xf1\xc5(\xcb\x17\xe3\xaf\xf1j\xb4F\xe6\xa8\x14\x86\xd2t\
|
||||
\xd4\x1d%#e\xa4\x8e\x84\xd8\x1auF\xd1\xc8\x18\x8dF\xc1\xa8&&\x1e-\xab3\xbe\
|
||||
\xff\'!\x97\xda\x91J\xc4L\xfe\xb5\x0c7;\xfa(\x1c\xfdC\xb7\x19\xed\x913rG\xde\
|
||||
\xa8?\xf2G\x03Q\x86\xa3\xde\xe8f\xf44:\x1e\xdd\x8e\xeeG\xbfG\x0f\xa3\xb3\xd1\
|
||||
\xf5\xe8`\xb4h\xdc\x8d\x8eF\'\xa3\xd3\xd1\xcf\xd1\xaf\xd1\xf9\xe8b\xf4g\xf4<\
|
||||
2#\xa7\x99\x977\xe3\xc7\xe8\xc7hWH\xb8\xc7\xd1\xe1\xe8jt9Z\x1b}\x1ce\xa3\x9d\
|
||||
Q>\xfa<JG[\xa3b4\x19-\x8c\x16G\xbb\x912Z\x1e\xad\x8cVG\xeb\xa3\xa5\xd1\xf6ho\
|
||||
\xf4i\xf4\x14m\x8e\xfe\x94\xb5(y4\xd7\xbbU\xbb\x1e0\x13\xc13b\x08\xcd\x82\
|
||||
\xfekM\xab\xff\x95\xa4\nb\xd6hf\xc6\xa3]A\x8a;\xda\x18\xbd\x1b\xbd\x8d\xbe\
|
||||
\x8d\xe6Ey/\xca\x87\xd1wQ\xf6Gs\xa3\xaf\xa3/\xa3\x97\xd1\xdf\xd1\xeb\xe8n\
|
||||
\xd4\n\xcc@\xd8\xd0\xc14\x10ju\xa0\x04j\xd0\x0e\xb4\xa0\x13D\x81Xu\xc1(\x08\
|
||||
\x82\xc7Q\x1c\xe8\xa2\x84\xc1\xe5\xc8\n\xec\xc0\t\xdc\xc0\x0b\xfa\x81\x1f\
|
||||
\x0c\x82a\xd0\x0bn\x82\xa7\xe08\xb8\r\xd6G\xf7\xc1\xef\xe0!8\x0b\xac\xe0:8\
|
||||
\x08.\xca\xbb\xe0\xba\xad7\xe7\xd6r{\xb6\xa0\xa8\x1c\x05\'\xc1\x9aFd\xf1\xff\
|
||||
\xcb\xc4\xaci\xfaHof\xe6\x9f\xd3\xe0g\xf0K\x94\rA\xceyp\x11\xfc\t\x9e\x83\
|
||||
\xf9\xd1\x8f\xe018\x0c\xae\x82\xcb`-\xd8\x1f}\x0cN\xa3,x\x1d\xed\x04y\xf09H\
|
||||
\x83\xad\xa0\x08&\xc1B\xb0i,\x06\xcb\xa2\xac\x04\xab\xc1zS\x96\x1a\x92j\xa2\
|
||||
\xc2`;\xd8\x0b>\x05\x9b\xc18\xd8\r6\x82w\xc1[\xf0-\x98\x0f\xde\x07\x1f\x82\
|
||||
\xef\xc1~0\x17|\r\xbe\x04/\xc1\xdf\xe05h\xc5f,,\xf3\xd8\x10j\xd1\xb2\xd0)T\
|
||||
\xd3\xed\xfc{f6\x94\xa3\xe0(\xa0\xff:R\xaf\x0c^zT^"a\x1c\xc7\xf4!\xad\xb8\
|
||||
\x1b\'\xf1\xafZ\x0f\x17E\x8d\xdb\xb1\x16\xd7\xb3\xf3(\xc8\xe9\xc4Q|)\xf6\xb9\
|
||||
`v\x1c\xc7\xaf#=\x0ec+\xb6c\'vc/\xee\xc7~<\x88\x87q[\x90c\x9b\xabA]\xc8\xb7\
|
||||
\xb6*H\xea\xc57\xf1Y\xf0\x14\x1f\xc7\xb7\xf1}\xfc;~\x88\xcf\xe2\xeb\xf8 \xbe\
|
||||
\x8b\x8f\xe2\x93\xf8T\x94\x97\xe0g\xfc+>\x8f/\xe2?\xf1s\xfc#\xbe-\x1f\xe3\
|
||||
\xc7\xf8A\x15J\xab\x99\xa8\x87\xf1\x95\xb1\xdc\xbe\x8a\xaf\xe2\x93\xa0Hj\x02\
|
||||
h\x86.\xe3+\xed?g\xa6j\xaf\xc5\x1f\xe3f\x99e\xf1N\x9c\x8b\xf29N\xe3-Q\x8ax\
|
||||
\xd2\x10\xb3\x10\xd7\xe4,\nb\xf6\xc5r[\x8eW\x041\xab\xf1z\xbc\x14o\xc7{\xf1\
|
||||
\xa7x3\x1e\xc7\xbb\xf1F\xfc\xee_\xc4\xd4\x84\xac\x80\xa0^\xfc\x16\xff\r\xbe\
|
||||
\xc5\xf3\xf1\xfb\xf8C\xfc=\xde\x8f\xe7\xe2\xaf\xf1\x97\xf8%\xfe\x1b\xbf\xc6-\
|
||||
\xdd\xd4K\xbd\xd2\xa7zWOtE\x17\x9a\x94(51bf*E\xd5\xf4Km\x92\xd4\xa4\x1c\xc6U\
|
||||
\x9bw\xcd\x91\xda\xd1#\xbd\xde\xfc\xff\x9e\x17C\xac\xb2@\xff\x87\xbe\xeb\x9b\
|
||||
\xc5\xb1\x1e\xeb\xba\x1e\xea59\x96n\xeb\x8e\xee\xea\x9e\xde\xd7}}\xa0\x0f\
|
||||
\xf5}\xb1szzM\xcc\x8d\xfe\xa4\x1f\xeb\xb7\xfa\xbd\xfe[\x7f\xd0\xcf\xf4k\xfd@\
|
||||
\xbf\xd3\xebEv\xa4\x9f\xe8G\xfa\xba\xf8\xc5\x84\x9d\xea?\xf5_\xfa\xb9~\xa1\
|
||||
\xff\xd1\x9f\xf5\x1f\xfa\xa3~\xa8_\xe9\x97\xfa\x9a\xfeQ\xcf\xf4\x1d\xfdW\\\
|
||||
\xe9\xb9\xfeY?\xd5S}\x18\x131[\x82\x9c\xbd\xb2\xdb\xd5\xf4B\'Rf\xdb\xbf\x12\
|
||||
\xca\xe8D_\xd0\xeb\x05\xf8\xef\xed\x7f\x18\xd7\xc4\x8c\xf4ff\x16\xf5E}Y_\xd1\
|
||||
W\xf5u}I\xdf\xd6\xf7\xc4\x9fO\xfa\xa6>\xd6w\x051\xbe\xbe\xa1\xbf\x13\xe4\xbc\
|
||||
\tb\x16\x8do\xfa\xbc\xfe^\xff\xa0\x7f\xd7\xf7\x1bb\xe6\xf4\xaf\xfa\x17\xfdE\
|
||||
\xff\xab\xbf\xea\xad\xd0\x0c\xcb\xb0\n\xa7a7<\x12\xd6\xb6\x12\xaaa;\xd4\xc2N\
|
||||
\x18\x85F\xf8\x10\x8f\xc2 \x8cC=\x0cC+\xb4\xc3\x9f\xb1\x13\xba\xa1\'J?\xf4\
|
||||
\xc3a<\x08U1;\x8fq\xed\x97\x9b3/\x84\xd1G\xa4\xf0\x8c\x1c\xa9\xc3\xb0\x17\
|
||||
\x1a\xeaM\xf8\xefY\xa9\xcf\xa1\xa7\xf0)\xac\x89i\x04\xc0qx\x1c\xde\x86\xcb\
|
||||
\x82\x98\xfb\xf0w\xf8\x10n\xebg\xe1ux\x10\xde\x85\xbb\x82\x9c\xa3\xf0$<\r\
|
||||
\xf7G?\xc3_\xe1\xa2q\x1e^\x84\x7f\xc2\xe7\xf0G\xf8\x18\x9e\tb\x0e\xc3\xab\
|
||||
\xf02|\xd1\xd7\xc2\x9a\x98WA\xd4\x9a\xf8\xaf\x8fa\x12f\xe1N\x98\x87\x9f\xc34\
|
||||
\xdc\n\x8bp\x12>\xeb\x0b\xe1b\xb8\x1c\xae\x84\xab\xe1z\xb8\x14\x96\xfav\xb8\
|
||||
\x17~\x12\xc4l\x86\xe3p7\xdc\x10\xe5]HN\xc69\xf3\xa4!\xa5H^\xa2\r\xb1#\xf6\
|
||||
\xbbo\xe1\xb7p>\xdc\xef\xfe\xe7^\xa9\x8d\xe9\x89\xfe^\x10\xf3A\x94\x86\x98\
|
||||
\xef\xe1\xf7p?\x9c\x0b\xbf\x86_\xc2\x97\xf0o\xf8*J\xcb\xaa]\x89\x955\xb5\xba\
|
||||
Vb\x9d\x84\x8a\xa5ZmK\xb3:Vd\xfd\x11G\xbe\x90\xb9\x96X\x8fVhY\x96mY&I2\xc7:\
|
||||
\x113r\xa2\xd7\xe8i\xcf\xea[~\x9d\x1a\xd8\xeaY\xf3\xf1\x8d\xf55~\xb2\x8e\xad\
|
||||
[\xeb\xdez\x89\x7f[\x0f\xd6\x99um\x1d\x88R\xcf\xccfh\x99\x8e\xb9 N\xd3c\xf3\
|
||||
\xa9!\xe6*\xbe\xb3\xae\xe2#\xeb\xc8:\xb1\x0c\xfd)<\xb5\x0c\x95$\xd8\xbfI\xf9\
|
||||
i\xfd\xb2\xfa\x9ds\xeb\xc2\xfa#JC\xcc\xb3\xf5\xc3z\xb4\x0e\xad\xaf\xe1\x95ui\
|
||||
\xadY\x1f\xadm\xb1\xd02k\xc7\xca\xad\xcf\x82\x9c\xd4\xf2\xf5\xc7`\xcb*\xac\
|
||||
\x89\xb5`-\nb\x96\xad\x15k\xd5Z\x17\xe50\\\x12\xc4\xd8 f=\xa8\x89q\xacmQNu\
|
||||
\xcf\xda\xb3>Y\x9b\xd6\xd8*\xc2]k\xc3zg\xbdY\xdf\xacy\xeb\xbd\xf5\xc1\xfanm\
|
||||
\x87\xfb\x82\x949\xeb\xab\xb5\x14\xf4\x0517\xe57\xd3k\x9c\xef7\x82\x98~R\x13\
|
||||
\xf3\xa5!\xe6\xc5\xaa\xd5\xff\xff=\'B\'\x0b~\tb\xee\xa3\x9a\x98\xbfL\xcc\xab\
|
||||
\xf5j\xcd\x8551-\xbb\xf6\xb0U\xf6\xd4\xde\xd6\xbbvb+vm\xbc\x0b\x83\xd3\xd6\
|
||||
\xec\x8e\x1d\xd5Z\x8b-\xd4\x05;\xb0c[o\xfe\xe8v(h\xd1m\xbb\xb94m\xdb\xae\xed\
|
||||
\x89\xbf\xfb\xa2\xd4\xbf|{`\x0f\xed\x9e}c?\xd9\xc7\xf6\xad}o\xff\x16\xbf\x1f\
|
||||
\xec3\xfbZ\xfc?\x07\xe2\x99;\xfb\xa8\xf9sb\x9f\xda?\xed_\xf6\xb9}a\xff\xb1\
|
||||
\x9f\xed\x1f\xf6\xa3}h_\x89\xbf\x15\xbb6\xc8\xfe\xf3\\\x19(\x97\xf6\x9a}\xa2\
|
||||
\x0e\x955[\x98f\xf6\x8b\xb5\x98\x8c\x15\xcc\xcc\xb3\x95\xd9s\xe1\x8e\x9d\xdb\
|
||||
\x9f\xed\xd4\xde\xb2\x0b\xfb\xb5Yl\x13{\xc1^\xb4\x97\xed\xf9\xd1\x8a\xbdj\
|
||||
\xaf\x0bR\x96\xecm\xfb\x8f\xd85{\xf6\'\xfbA\xdf\x8c7\xedM{l;\xd6zs\xc6\xac\
|
||||
\x07I\xb8kg\xe2\xcf\x86\xfd\xce~\xb3\xbf\xd9\xf3\xf6{\xfb\x83\xfd\xdd\xde\
|
||||
\xb7W\xc29{\xde\xfa*~\x8f\xad/\xf6\x8b\xfd\xd7~\xb5[\x8e\xe9\x94N\xe5L\x9d\
|
||||
\xd0\xfc\xd6\x84\xaf\xc8y\xdf\x133sdu\x9dzf\xde\xda\'\xc1\x7f\xcf\n\x138P\
|
||||
\x8a\xe4\xa3]\x8b\xee\x81\xf2\xaf\x99\xc9\xecG\xb1\xd8\x12\xa7.\xf5/*\xf5\
|
||||
\x7f)\x8e\xea\xb4\x1d\xcd)\xac\x9a\x98\x8eS\xabb#\'p\x1e\xf4\xd8\xd1\x1d\xb1\
|
||||
\xaa\x9c\x13\xbd&\xa7\xde351\xefD\xc9C\xdbq\x1cW\x14\xcf\xe9;~\x9dg\xac)\x03\
|
||||
\xa7\xe7\x9c\xc67\xce\x93s\xec\xfc\xb5o\x9d{\xe7\xb7(L\xcc\xbcy\x8f\x85v\xa1\
|
||||
<8\xf5\x8e\xb9\xb3\x0c\x95%\xda\x8c\x94\xffTC\x0fc\xfa\x97\xdc\xb3\xce\xb5s\
|
||||
\xe0\x1cZ59\xf5\x0c\xcd\xc8I\x9c}!\x1aT\xe7\xce9rN\x9cS\'\xb2\x7f:\xbf\x041\
|
||||
\xe7\xce\x85\xf3\xc79\x133\xf3\xec\xfch\x88\xa9\xb7}\r\xc6\xaf\xa5\xd8\xc6\
|
||||
\x7f\x10\xf3\xe8\x1c:W\x82\x04_\x94\x9e\xf8u\xe9\xac9\x1f\x9dL\x90\xb3\xe3\
|
||||
\xe4\xceg\'u\xeaY\r\xc5\xf6w\xcdQU\x87\x12\xeb@\xe2[r\xa1|H\x1e\x9c;\xebH\
|
||||
\xfd\xefE\xf6\x7f\x97\x7f\x113\x17\xd6\x83\xcf\xec\x9a(.43[\x82\x98\xc2\x998\
|
||||
\x0b\x8ea/:\xcb\xce\x8a g\xd5Ywj\xd1\xbc\xe4l;{N\x12~\x12\x7fj\x82\xea_\xf5"\
|
||||
{\xb3kb6\x9bR_l\xdfu6\xc4\x9fC\xe7\x9d\xf3\xe6|s\xe6\x9d\xf7\x82\x98\x0f\xce\
|
||||
wg_\x10S\xeb\r$\xcd\xbe\x99B%\xa8\xea\xb9\x89\xaa\x9a\x9c\xbe \xe7R\xfb\xef\
|
||||
\x99\xf9\xbf\xca\x90\xf7L\xbd\xcc\x1e-!\x9e\xc5\xd0\xe7\xc2\xba\x1c\n\xe9\
|
||||
\xc6K\xef\xab\xf3\xc5yq\xfe:\xaf\xce\xc4j\xb9\xa6[\xba\x95;u\xbbn\xe2*\xae\
|
||||
\xea&a\xdb\xadI r\xea_43\x9a\xdbq\xddfn\xe6\x85\x95\xbc\xe6\xd4\xc5\x10\xca\
|
||||
\xb9P\xa1\xddoB\xb5\xd3]5\x0c\xddT\x10C\xcaO\xd8\x90#t\xcd\xaa^j\x96\x1bU\
|
||||
\xb6[/\xb6\xc3x\xa69\xff\xa7}\xf3\xefr\xa59M\xba\x85\x7f\\\xf7\xda\xe1\x81\
|
||||
\xd7\xcb\xccs\xebEW\xd7\xd4\xf3R\x93\xd8w\xeb\xe2\xbb\x03w\xe8\xd6n\xdc\x9e{\
|
||||
\xe3>\xb9\xc7\xee\xad{\xef\xd6\xc7\xe4\x89^/\xae\r{\x17\xc5v~\xbb\x0f\x82\
|
||||
\x98M\xe7\xcc\xbd\x16C\xe72j\x88y/Hyr\x0e\\\x9a\x97\xd4\xa9\x17g-\xd0k\xf1,l\
|
||||
\x0b17\xb7\xe6\x9dKs\xf3!\x11\x02@.4.w\xea\xbfI\x1c*\x86\xfa\xd7\x021G\xae\
|
||||
\xeb\x9e\xb8\xa7\xeeO\xf1\xe7T\xfc\xfa%\xcay\xf3\xdf\x17M\xf9\xe3>\xbb?Dy\
|
||||
\x14\xe5\xd0\xbd\x12\x7f.\xdd5\xf7\xa3\x9b\xb9;n\xee~vSw\xcb-\xdc\x89(\x0b\
|
||||
\xa2L\xc4\xefEw\xd9]qW\xdduQ\x96\xdcmw\xaf)\xdb\xa2|\x12e\xd3\x1d\xbb\xbb\
|
||||
\xee\x86\xfb\xce}s\xbf\xb9\xf3\xee{\x94\x0f\xeeww\xdf\x9ds\xbf\xba_\xdc\x17\
|
||||
\xf7\xaf\xfb\xea\xb6<\xd3+\xbd\xf9v\xe5\xf1\xcc\xd8\xd5;e9Y\x13\xcaXO\xc8\
|
||||
\xb1\x93\xe0\xca\xb0;\x7f\xad\xba\x80\x98\xa9\xd7\xf5\xce\xc5\xf0OD\xa9\xffM\
|
||||
<&\x86\xc8\xfb\trjb\xae\x04!DLM\x8a\xe2\xa9^\xdb#b\x16Pj\x92\x16]\xcd[m\xc8\
|
||||
\xd1\xbc\x8e\x17\xa1\x18(+\x82T\xcd[\x14\xcf\x8d\xbc\xc0\xdbj\x88\xf8.,\x8a\
|
||||
\x9a\xa0\xcfB\xbf\xad\xc9\t\xbd:}_\xcbs<\xd7\xf3\xbc\xdfQ\x91\xf4=_\x94\xa3 \
|
||||
\x14\x06\xf2\x9aQ\x933\xf0\x12Q\x86\x9eC\xab\xb9&\xc6\xfc\x7f\x91\x8f)\xf6' ))
|
||||
|
||||
def getwxPythonBitmap():
|
||||
return wxBitmapFromXPMData(getwxPythonData())
|
||||
|
||||
def getwxPythonImage():
|
||||
return wxImageFromBitmap(getwxPythonBitmap())
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getPythonPoweredData():
|
||||
return cPickle.loads(zlib.decompress(
|
||||
'x\xda\xcdXMo\xdb0\x0c\xbd\xf7W\x08\xb0\x9c\x0e\x11"4m\xda\xb50\x0cxE\xbb\
|
||||
\xe3|\xe8E\xd7\xa2\xd8i\xc5\xb4\xff\x7f\x9aH}\x92\x92\x13\x17]\xd1IH\xa4G\
|
||||
\xd1~\xa6)\x93\xb4\xbf\xbc\xfe\xd9\x9f=\x9d\xef\xf7\x17\xe2p\x10\xfbk\xb1??{\
|
||||
~:7\xe2E\xdc\xbf>\xbf\xfcB4;\xd4\xedo\xa1#\x9e\x00?<BG,\x01?~\x85\x8ex\x00|u\
|
||||
\x07\x1dq\x0f\xf8;6\xc4\x16\xf0\xe1\x12:\xe2\x1d\xe0\x8b[\xe8\x885\xe2\x8b\
|
||||
\x9b\xabo\xd7\x88G\xc0\x97w\xd0\x11+\xc0\xb7\x07\xe8\x88\x85\xc3?\xec\xef\
|
||||
\x9f\x086xq7\xd0\x11o\xf1b\xf6\xd0\x11w\x80\xef\x1f\xa0\x87\x83W7\xbd\xd0\
|
||||
\xdep\x8a\xb5\x94ze\xfb7\x94\xfa\xcd\xed=\x94\xfa\x1d\xed\x8d\x94\x867w\n\
|
||||
\xd3j\xa7\xe4\xab)\x9d\xaa\x9d\xb1\x03\xb9\x80\x7f\xed\xc7(\x9fq\x9e\xe5\xf1\
|
||||
\x08\xaeoVS\n5u\xd2\xf5I\xcd0Wx&7\xceA\x8e+\x85\\\x18\xbf\xc2\xf5\x81[\xaf\
|
||||
\xa0t\xf7\xc4N\xbdoR\xcdn>Yw\n7J\x15\xe5}?)\x99\xe4\x93\x9d\x1d\xaa\xf5q\xac\
|
||||
\xdcZQ\x82\xe7\xcd\xdc\xf5\x12\xad\xd9H\xb5\xed\xfanv\x87:Y\xb7\x05[$\xacML\
|
||||
\xee4\x1b\xfa\xb3\xa7\xa4\xa4\x8c\xd2o6c%^/\\\xbb\x84\x1f^5\x8cQ\x06~+\xe4\
|
||||
\x8a\xe9\xbb\xfbk\xe2z\xb5\x81\te\xdc\xdfx\x85\xa3\xd9\x99\xd1\x8d\x03\xbb\
|
||||
\xea\xd68\xd0\xb9\x04NbeIZR\xa6\xc5\xcaJ8C\xf2]i]\xe3N\xa0Wa\x85Z\x9993e\xf1\
|
||||
\x14\x13_\xce\xe0\'5\xe2\xbf\xa0\xd6y\x1d\xd9\xb0\xd2\xe9\x0e\xdc\xcaH\x9a(\
|
||||
\xcb\x15\xb8\xda\xb4c\xd1>wf\xef!be\x9f\xb4\x98_\xbb\xf0\xb3\x8cR\x97\x94d!Y\
|
||||
0!\x0b\xda\xb9\t\x8ck\xac\xec\x068B\xd6V"g\xa0\xa4\xf2\xe4\'\xebC\xdf\xac\
|
||||
\x92\x8d\xab|)\xc3sZ[\t\x9cm\xcahIl%^\xb1c;\xf4F\xd3\xcaD\xa9\x1bV\xda\x1c\
|
||||
\xa3\x0b\xac\x89\x95K\xcf\xa5\rw\xa6a\xa5^\xa0$V\x96\xb8\x98\xcf+\xa3O\xcc,\
|
||||
\x94RWV\xe6\xf8\xc8\xb1\xe11\xf4T\x8c\r\x99%\xb76e\xcc\x06\r\xcc3\xc5\xa9L\
|
||||
\x923\xcaq\xca\x90\x03M\x03\xa79\x1bY\xbe\xcc#\xbbc-J\x7f\xf3w\xd1\x8d<\xdb\
|
||||
\xa75>\xee\x92\xd2\x8e\x1cR\xc5\xda\x8a\x92T\x04z1\xdb3\xb9H\xcfp\x96\xc79\
|
||||
\xdf\xb95eY\x11\x98E\x1fQ\xf9\xe8\x10\xd7\xf7\xf3\xa9\x8e\xb5\xd5CB*\x02\xb3\
|
||||
\xb4\x13\xa9\x1c\xe2i\xa5\x1f\xe6U\xac\xad\x9f\xcb:\x8b4\xab\x03"\x97\x88\
|
||||
\x98~\x98W\xb1\xb6\x0ex\x18M:\xab\xac\xbb\xba\xa9\x8a*\xc17*\xeb\xc4\xfc8\
|
||||
\xaa\xa0_\xc8\xbd\xb4g\xd5AM\xb9T\xf7\x0c\xbe\xf6A\xd9@\xb3\x88\xaf\x02\xfc\
|
||||
\x1a\xcb.U\xacm$/\x92+\xab8\x9a\x7f$W\xe2n\x11x\x07x\x0ee\xb1\xb6\x95\xa2I\
|
||||
\xae\xe4\xd9\xc2\x86_\xb2\xb2\xa8\xf4L\x99C\x0b\xf9\xc8+\xbd\xaa\x10!\xb9\
|
||||
\x12k\xd7\xc2\x971\xdb\x07\x9f\rj\x9b\x9f\xbb\x10i\xb2\xbc86Z\xd9.\xb7H\x06\
|
||||
\x10|\x07nB}\x10\xe5\xc5\x9d \xfa\xf8\xbc\x86y\xf2\xe5RQ\x993@\xac\x07Z5P\
|
||||
\x94OvWf\x99B\x9e\xe7}X_,\x9dS\x94\x89\x9c$\x9a@\x9c!QF\x90\xacS\xca\xe3<\
|
||||
\xa2\xe5\x17\x04\x9aEx\xcc\x14E\x9e`\x99\xa3\xa9\x9f\xdf\xdf\x8eP\n\x9a\xc8\
|
||||
\xcd\xeavJ\xff\xd8\x9b\x17\xad\x1d\xca\x93.\xcc\xd7\xe8\x1f\xa7\x14\xb4Zi\
|
||||
\xbe1\xebF&=\xa6_\xbe\xdb\x1e\xf9V\xa0\xdf\xdd\xfe\xf3\xcf\x13\x9f\xf8\x11\
|
||||
\xe6\xb3>5}\xc0\x075\xfd\x17\x19\xafZq' ))
|
||||
|
||||
def getPythonPoweredBitmap():
|
||||
return wxBitmapFromXPMData(getPythonPoweredData())
|
||||
|
||||
def getPythonPoweredImage():
|
||||
return wxImageFromBitmap(getPythonPoweredBitmap())
|
||||
|
||||
# The following could appear at the end of (e.g.) the
|
||||
# otherwise-wxDesigner-generated Python file ErrorDialogs_wdr.py,
|
||||
# instead of the function of the same name there... However, instead of cut-
|
||||
# and-pasting, the version here will be imported, so all you need do is
|
||||
# delete the similarly-named function automatically generated at the end of
|
||||
# the ..._wdr.py file...
|
||||
|
||||
def PythonBitmaps( index ):
|
||||
if index:
|
||||
return getPythonPoweredBitmap()
|
||||
else:
|
||||
return getwxPythonBitmap()
|
||||
return wxNullBitmap
|
||||
PythonBitmaps = wx.lib.PythonBitmaps.PythonBitmaps
|
||||
getPythonPoweredBitmap = wx.lib.PythonBitmaps.getPythonPoweredBitmap
|
||||
getPythonPoweredData = wx.lib.PythonBitmaps.getPythonPoweredData
|
||||
getPythonPoweredImage = wx.lib.PythonBitmaps.getPythonPoweredImage
|
||||
getwxPythonBitmap = wx.lib.PythonBitmaps.getwxPythonBitmap
|
||||
getwxPythonData = wx.lib.PythonBitmaps.getwxPythonData
|
||||
getwxPythonImage = wx.lib.PythonBitmaps.getwxPythonImage
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
#
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,141 +1,14 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.activexwrapper
|
||||
# Purpose: a wxWindow derived class that can hold an ActiveX control
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2000 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
try:
|
||||
import win32ui
|
||||
import pywin.mfc.activex
|
||||
import win32com.client
|
||||
except ImportError:
|
||||
raise ImportError( "ActiveXWrapper requires PythonWin. Please install the win32all-xxx.exe package.")
|
||||
|
||||
##from win32con import WS_TABSTOP, WS_VISIBLE
|
||||
WS_TABSTOP = 0x00010000
|
||||
WS_VISIBLE = 0x10000000
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
def MakeActiveXClass(CoClass, eventClass=None, eventObj=None):
|
||||
"""
|
||||
Dynamically construct a new class that derives from wxWindow, the
|
||||
ActiveX control and the appropriate COM classes. This new class
|
||||
can be used just like the wxWindow class, but will also respond
|
||||
appropriately to the methods and properties of the COM object. If
|
||||
this class, a derived class or a mix-in class has method names
|
||||
that match the COM object's event names, they will be called
|
||||
automatically.
|
||||
|
||||
CoClass -- A COM control class from a module generated by
|
||||
makepy.py from a COM TypeLibrary. Can also accept a
|
||||
CLSID.
|
||||
|
||||
eventClass -- If given, this class will be added to the set of
|
||||
base classes that the new class is drived from. It is
|
||||
good for mix-in classes for catching events.
|
||||
|
||||
eventObj -- If given, this object will be searched for attributes
|
||||
by the new class's __getattr__ method, (like a mix-in
|
||||
object.) This is useful if you want to catch COM
|
||||
callbacks in an existing object, (such as the parent
|
||||
window.)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
if type(CoClass) == type(""):
|
||||
# use the CLSID to get the real class
|
||||
CoClass = win32com.client.CLSIDToClass(CoClass)
|
||||
|
||||
# determine the base classes
|
||||
axEventClass = CoClass.default_source
|
||||
baseClasses = [wxWindow, pywin.mfc.activex.Control, CoClass, axEventClass]
|
||||
if eventClass:
|
||||
baseClasses.append(eventClass)
|
||||
baseClasses = tuple(baseClasses)
|
||||
|
||||
# define the class attributes
|
||||
className = 'AXControl_'+CoClass.__name__
|
||||
classDict = { '__init__' : axw__init__,
|
||||
'__getattr__' : axw__getattr__,
|
||||
'axw_OnSize' : axw_OnSize,
|
||||
'axw_OEB' : axw_OEB,
|
||||
'_name' : className,
|
||||
'_eventBase' : axEventClass,
|
||||
'_eventObj' : eventObj,
|
||||
'Cleanup' : axw_Cleanup,
|
||||
}
|
||||
|
||||
# make a new class object
|
||||
import new
|
||||
classObj = new.classobj(className, baseClasses, classDict)
|
||||
return classObj
|
||||
|
||||
|
||||
|
||||
|
||||
# These functions will be used as methods in the new class
|
||||
def axw__init__(self, parent, ID, pos=wxDefaultPosition, size=wxDefaultSize, style=0):
|
||||
# init base classes
|
||||
pywin.mfc.activex.Control.__init__(self)
|
||||
wxWindow.__init__(self, parent, -1, pos, size, style|wxNO_FULL_REPAINT_ON_RESIZE)
|
||||
|
||||
win32ui.EnableControlContainer()
|
||||
self._eventObj = self._eventObj # move from class to instance
|
||||
|
||||
# create a pythonwin wrapper around this wxWindow
|
||||
handle = self.GetHandle()
|
||||
self._wnd = win32ui.CreateWindowFromHandle(handle)
|
||||
|
||||
# create the control
|
||||
sz = self.GetSize()
|
||||
self.CreateControl(self._name, WS_TABSTOP | WS_VISIBLE,
|
||||
(0, 0, sz.width, sz.height), self._wnd, ID)
|
||||
|
||||
# init the ax events part of the object
|
||||
self._eventBase.__init__(self, self._dispobj_)
|
||||
|
||||
# hook some wx events
|
||||
EVT_SIZE(self, self.axw_OnSize)
|
||||
#EVT_ERASE_BACKGROUND(self, self.axw_OEB)
|
||||
|
||||
|
||||
def axw__getattr__(self, attr):
|
||||
try:
|
||||
return pywin.mfc.activex.Control.__getattr__(self, attr)
|
||||
except AttributeError:
|
||||
try:
|
||||
eo = self.__dict__['_eventObj']
|
||||
return getattr(eo, attr)
|
||||
except AttributeError:
|
||||
raise AttributeError('Attribute not found: %s' % attr)
|
||||
|
||||
|
||||
def axw_OnSize(self, event):
|
||||
sz = self.GetClientSize() # get wxWindow size
|
||||
self.MoveWindow((0, 0, sz.width, sz.height), 1) # move the AXControl
|
||||
|
||||
|
||||
def axw_OEB(self, event):
|
||||
pass
|
||||
|
||||
|
||||
def axw_Cleanup(self):
|
||||
del self._wnd
|
||||
self.close()
|
||||
pass
|
||||
## anything else???
|
||||
|
||||
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.activexwrapper
|
||||
|
||||
__doc__ = wx.lib.activexwrapper.__doc__
|
||||
|
||||
MakeActiveXClass = wx.lib.activexwrapper.MakeActiveXClass
|
||||
axw_Cleanup = wx.lib.activexwrapper.axw_Cleanup
|
||||
axw_OEB = wx.lib.activexwrapper.axw_OEB
|
||||
axw_OnSize = wx.lib.activexwrapper.axw_OnSize
|
||||
axw__getattr__ = wx.lib.activexwrapper.axw__getattr__
|
||||
axw__init__ = wx.lib.activexwrapper.axw__init__
|
||||
|
||||
@@ -1,201 +1,9 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.analogclock
|
||||
# Purpose: A simple analog clock window
|
||||
#
|
||||
# Author: several folks on wxPython-users
|
||||
#
|
||||
# Created: 16-April-2003
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2003 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
import math, sys, string, time
|
||||
from wxPython.wx import *
|
||||
|
||||
|
||||
|
||||
class AnalogClockWindow(wxWindow):
|
||||
"""A simple analog clock window"""
|
||||
|
||||
TICKS_NONE = 0
|
||||
TICKS_SQUARE = 1
|
||||
TICKS_CIRCLE = 2
|
||||
|
||||
def __init__(self, parent, ID=-1, pos=wxDefaultPosition, size=wxDefaultSize,
|
||||
style=0, name="clock"):
|
||||
# Initialize the wxWindow...
|
||||
wxWindow.__init__(self, parent, ID, pos, size, style, name)
|
||||
|
||||
# Initialize the default clock settings...
|
||||
self.minuteMarks = 60
|
||||
self.hourMarks = 12
|
||||
self.tickMarksBrushC = self.GetForegroundColour()
|
||||
self.tickMarksPenC = self.GetForegroundColour()
|
||||
self.tickMarkStyle = self.TICKS_SQUARE
|
||||
|
||||
# Make an initial bitmap for the face, it will be updated and
|
||||
# painted at the first EVT_SIZE event.
|
||||
W, H = size
|
||||
self.faceBitmap = wxEmptyBitmap(max(W,1), max(H,1))
|
||||
|
||||
# Initialize the timer that drives the update of the clock
|
||||
# face. Update every half second to ensure that there is at
|
||||
# least one true update during each realtime second.
|
||||
self.timer = wxTimer(self)
|
||||
self.timer.Start(500)
|
||||
|
||||
# Set event handlers...
|
||||
EVT_PAINT(self, self.OnPaint)
|
||||
EVT_ERASE_BACKGROUND(self, lambda x: None)
|
||||
EVT_SIZE(self, self.OnSize)
|
||||
EVT_TIMER(self, -1, self.OnTimerExpire)
|
||||
EVT_WINDOW_DESTROY(self, self.OnQuit)
|
||||
|
||||
|
||||
def SetTickMarkStyle(self, style):
|
||||
"""
|
||||
Set the style of the marks around the edge of the clock.
|
||||
Options are TICKS_NONE, TICKS_SQUARE, and TICKS_CIRCLE
|
||||
"""
|
||||
self.tickMarkStyle = style
|
||||
|
||||
|
||||
def SetTickMarkColours(self, brushC, penC="BLACK"):
|
||||
"""
|
||||
Set the brush colour and optionally the pen colour of
|
||||
the marks around the edge of the clock.
|
||||
"""
|
||||
self.tickMarksBrushC = brushC
|
||||
self.tickMarksPenC = penC
|
||||
|
||||
SetTickMarkColour = SetTickMarkColours
|
||||
|
||||
|
||||
def SetHandsColour(self, c):
|
||||
"""An alias for SetForegroundColour"""
|
||||
self.SetForegroundColour(c) # the hands just use the foreground colour
|
||||
|
||||
|
||||
|
||||
# Using the current settings, render the points and line endings for the
|
||||
# circle inside the specified device context. In this case, the DC is
|
||||
# a memory based device context that will be blitted to the actual
|
||||
# display DC inside the OnPaint() event handler.
|
||||
def OnSize(self, event):
|
||||
# The faceBitmap init is done here, to make sure the buffer is always
|
||||
# the same size as the Window
|
||||
size = self.GetClientSize()
|
||||
self.faceBitmap = wxEmptyBitmap(size.width, size.height)
|
||||
self.DrawFace()
|
||||
|
||||
|
||||
def OnPaint(self, event):
|
||||
self.DrawHands(wxPaintDC(self))
|
||||
|
||||
|
||||
def OnQuit(self, event):
|
||||
self.timer.Stop()
|
||||
del self.timer
|
||||
|
||||
|
||||
def OnTimerExpire(self, event):
|
||||
self.DrawHands(wxClientDC(self))
|
||||
|
||||
|
||||
def DrawHands(self, drawDC):
|
||||
# Start by drawing the face bitmap
|
||||
drawDC.DrawBitmap(self.faceBitmap,0,0)
|
||||
|
||||
currentTime = time.localtime(time.time())
|
||||
hour, minutes, seconds = currentTime[3:6]
|
||||
|
||||
W,H = self.faceBitmap.GetWidth(), self.faceBitmap.GetHeight()
|
||||
centerX = W / 2
|
||||
centerY = H / 2
|
||||
|
||||
radius = min(centerX, centerY)
|
||||
hour += minutes / 60.0 # added so the hour hand moves continuously
|
||||
x, y = self.point(hour, 12, (radius * .65))
|
||||
hourX, hourY = (x + centerX), (centerY - y)
|
||||
x, y = self.point(minutes, 60, (radius * .85))
|
||||
minutesX, minutesY = (x + centerX), (centerY - y)
|
||||
x, y = self.point(seconds, 60, (radius * .85))
|
||||
secondsX, secondsY = (x + centerX), (centerY - y)
|
||||
|
||||
# Draw the hour hand...
|
||||
drawDC.SetPen(wxPen(self.GetForegroundColour(), 5, wxSOLID))
|
||||
drawDC.DrawLine(centerX, centerY, hourX, hourY)
|
||||
|
||||
# Draw the minutes hand...
|
||||
drawDC.SetPen(wxPen(self.GetForegroundColour(), 3, wxSOLID))
|
||||
drawDC.DrawLine(centerX, centerY, minutesX, minutesY)
|
||||
|
||||
# Draw the seconds hand...
|
||||
drawDC.SetPen(wxPen(self.GetForegroundColour(), 1, wxSOLID))
|
||||
drawDC.DrawLine(centerX, centerY, secondsX, secondsY)
|
||||
|
||||
|
||||
# Draw the specified set of line marks inside the clock face for the
|
||||
# hours or minutes...
|
||||
def DrawFace(self):
|
||||
backgroundBrush = wxBrush(self.GetBackgroundColour(), wxSOLID)
|
||||
drawDC = wxMemoryDC()
|
||||
drawDC.SelectObject(self.faceBitmap)
|
||||
drawDC.SetBackground(backgroundBrush)
|
||||
drawDC.Clear()
|
||||
|
||||
W,H = self.faceBitmap.GetWidth(), self.faceBitmap.GetHeight()
|
||||
centerX = W / 2
|
||||
centerY = H / 2
|
||||
|
||||
# Draw the marks for hours and minutes...
|
||||
self.DrawTimeMarks(drawDC, self.minuteMarks, centerX, centerY, 4)
|
||||
self.DrawTimeMarks(drawDC, self.hourMarks, centerX, centerY, 9)
|
||||
|
||||
|
||||
def DrawTimeMarks(self, drawDC, markCount, centerX, centerY, markSize):
|
||||
for i in range(markCount):
|
||||
x, y = self.point(i + 1, markCount, min(centerX,centerY) - 16)
|
||||
scaledX = x + centerX - markSize/2
|
||||
scaledY = centerY - y - markSize/2
|
||||
|
||||
drawDC.SetBrush(wxBrush(self.tickMarksBrushC, wxSOLID))
|
||||
drawDC.SetPen(wxPen(self.tickMarksPenC, 1, wxSOLID))
|
||||
if self.tickMarkStyle != self.TICKS_NONE:
|
||||
if self.tickMarkStyle == self.TICKS_CIRCLE:
|
||||
drawDC.DrawEllipse(scaledX - 2, scaledY, markSize, markSize)
|
||||
else:
|
||||
drawDC.DrawRectangle(scaledX - 3, scaledY, markSize, markSize)
|
||||
|
||||
|
||||
def point(self, tick, range, radius):
|
||||
angle = tick * (360.0 / range)
|
||||
radiansPerDegree = math.pi / 180
|
||||
pointX = int(round(radius * math.sin(angle * radiansPerDegree)))
|
||||
pointY = int(round(radius * math.cos(angle * radiansPerDegree)))
|
||||
return wxPoint(pointX, pointY)
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
class App(wxApp):
|
||||
def OnInit(self):
|
||||
frame = wxFrame(None, -1, "AnalogClockWindow Test", size=(375,375))
|
||||
|
||||
clock = AnalogClockWindow(frame)
|
||||
clock.SetTickMarkColours("RED")
|
||||
clock.SetHandsColour("WHITE")
|
||||
clock.SetBackgroundColour("BLUE")
|
||||
|
||||
frame.Centre(wxBOTH)
|
||||
frame.Show(True)
|
||||
self.SetTopWindow(frame)
|
||||
return true
|
||||
|
||||
theApp = App(0)
|
||||
theApp.MainLoop()
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.analogclock
|
||||
|
||||
__doc__ = wx.lib.analogclock.__doc__
|
||||
|
||||
AnalogClockWindow = wx.lib.analogclock.AnalogClockWindow
|
||||
|
||||
@@ -1,91 +1,9 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.anchors
|
||||
# Purpose: A class that provides an easy to use interface over layout
|
||||
# constraints for anchored layout.
|
||||
#
|
||||
# Author: Riaan Booysen
|
||||
#
|
||||
# Created: 15-Dec-2000
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2000 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython.wx import wxLayoutConstraints, wxTop, wxLeft, wxBottom, wxRight, \
|
||||
wxHeight, wxWidth
|
||||
import wx.lib.anchors
|
||||
|
||||
class LayoutAnchors(wxLayoutConstraints):
|
||||
""" A class that implements Delphi's Anchors with wxLayoutConstraints.
|
||||
__doc__ = wx.lib.anchors.__doc__
|
||||
|
||||
Anchored sides maintain the distance from the edge of the
|
||||
control to the same edge of the parent.
|
||||
When neither side is selected, the control keeps the same
|
||||
relative position to both sides.
|
||||
|
||||
The current position and size of the control and it's parent
|
||||
is used when setting up the constraints. To change the size or
|
||||
position of an already anchored control, set the constraints to
|
||||
None, reposition or resize and reapply the anchors.
|
||||
|
||||
Examples:
|
||||
|
||||
Let's anchor the right and bottom edge of a control and
|
||||
resize it's parent.
|
||||
|
||||
ctrl.SetConstraints(LayoutAnchors(ctrl, left=0, top=0, right=1, bottom=1))
|
||||
|
||||
+=========+ +===================+
|
||||
| +-----+ | | |
|
||||
| | * | -> | |
|
||||
| +--*--+ | | +-----+ |
|
||||
+---------+ | | * |
|
||||
| +--*--+ |
|
||||
+-------------------+
|
||||
* = anchored edge
|
||||
|
||||
When anchored on both sides the control will stretch horizontally.
|
||||
|
||||
ctrl.SetConstraints(LayoutAnchors(ctrl, 1, 0, 1, 1))
|
||||
|
||||
+=========+ +===================+
|
||||
| +-----+ | | |
|
||||
| * * | -> | |
|
||||
| +--*--+ | | +---------------+ |
|
||||
+---------+ | * ctrl * |
|
||||
| +-------*-------+ |
|
||||
+-------------------+
|
||||
* = anchored edge
|
||||
"""
|
||||
def __init__(self, control, left = 1, top = 1, right = 0, bottom = 0):
|
||||
wxLayoutConstraints.__init__(self)
|
||||
parent = control.GetParent()
|
||||
if not parent: return
|
||||
|
||||
pPos, pSize = parent.GetPosition(), parent.GetClientSize()
|
||||
cPos, cSize = control.GetPosition(), control.GetSize()
|
||||
|
||||
self.setConstraintSides(self.left, wxLeft, left,
|
||||
self.right, wxRight, right,
|
||||
self.width, wxWidth, self.centreX,
|
||||
cPos.x, cSize.x, pSize.x, parent)
|
||||
|
||||
self.setConstraintSides(self.top, wxTop, top,
|
||||
self.bottom, wxBottom, bottom,
|
||||
self.height, wxHeight, self.centreY,
|
||||
cPos.y, cSize.y, pSize.y, parent)
|
||||
|
||||
def setConstraintSides(self, side1, side1Edge, side1Anchor,
|
||||
side2, side2Edge, side2Anchor,
|
||||
size, sizeEdge, centre,
|
||||
cPos, cSize, pSize, parent):
|
||||
if side2Anchor:
|
||||
side2.SameAs(parent, side2Edge, pSize - (cPos + cSize))
|
||||
if side1Anchor:
|
||||
side1.SameAs(parent, side1Edge, cPos)
|
||||
if not side2Anchor:
|
||||
size.AsIs()
|
||||
else:
|
||||
size.AsIs()
|
||||
if not side2Anchor:
|
||||
centre.PercentOf(parent, sizeEdge,
|
||||
int(((cPos + cSize / 2.0) / pSize)*100))
|
||||
LayoutAnchors = wx.lib.anchors.LayoutAnchors
|
||||
|
||||
@@ -1,554 +1,14 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.buttons
|
||||
# Purpose: Various kinds of generic buttons, (not native controls but
|
||||
# self-drawn.)
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 9-Dec-1999
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 1999 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file reverse renames symbols in the wx package to give
|
||||
## them their wx prefix again, for backwards compatibility.
|
||||
|
||||
"""
|
||||
This module implements various forms of generic buttons, meaning that
|
||||
they are not built on native controls but are self-drawn.
|
||||
|
||||
The wxGenButton is the base. It acts like a normal button but you
|
||||
are able to better control how it looks, bevel width, colours, etc.
|
||||
|
||||
wxGenBitmapButton is a button with one or more bitmaps that show
|
||||
the various states the button can be in.
|
||||
|
||||
wxGenToggleButton stays depressed when clicked, until clicked again.
|
||||
|
||||
wxGenBitmapToggleButton the same but with bitmaps.
|
||||
|
||||
"""
|
||||
|
||||
from wxPython.wx import *
|
||||
import imageutils
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class wxGenButtonEvent(wxPyCommandEvent):
|
||||
def __init__(self, eventType, ID):
|
||||
wxPyCommandEvent.__init__(self, eventType, ID)
|
||||
self.isDown = False
|
||||
self.theButton = None
|
||||
|
||||
def SetIsDown(self, isDown):
|
||||
self.isDown = isDown
|
||||
|
||||
def GetIsDown(self):
|
||||
return self.isDown
|
||||
|
||||
def SetButtonObj(self, btn):
|
||||
self.theButton = btn
|
||||
|
||||
def GetButtonObj(self):
|
||||
return self.theButton
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class wxGenButton(wxPyControl):
|
||||
labelDelta = 1
|
||||
|
||||
def __init__(self, parent, ID, label,
|
||||
pos = wxDefaultPosition, size = wxDefaultSize,
|
||||
style = 0, validator = wxDefaultValidator,
|
||||
name = "genbutton"):
|
||||
if style == 0:
|
||||
style = wxNO_BORDER
|
||||
wxPyControl.__init__(self, parent, ID, pos, size, style, validator, name)
|
||||
|
||||
self.up = True
|
||||
self.bezelWidth = 2
|
||||
self.hasFocus = False
|
||||
self.useFocusInd = True
|
||||
|
||||
self.SetLabel(label)
|
||||
self.SetPosition(pos)
|
||||
font = parent.GetFont()
|
||||
if not font.Ok():
|
||||
font = wxSystemSettings_GetSystemFont(wxSYS_DEFAULT_GUI_FONT)
|
||||
self.SetFont(font)
|
||||
self.SetBestSize(size)
|
||||
self.InitColours()
|
||||
|
||||
EVT_LEFT_DOWN(self, self.OnLeftDown)
|
||||
EVT_LEFT_UP(self, self.OnLeftUp)
|
||||
if wxPlatform == '__WXMSW__':
|
||||
EVT_LEFT_DCLICK(self, self.OnLeftDown)
|
||||
EVT_MOTION(self, self.OnMotion)
|
||||
EVT_SET_FOCUS(self, self.OnGainFocus)
|
||||
EVT_KILL_FOCUS(self, self.OnLoseFocus)
|
||||
EVT_KEY_DOWN(self, self.OnKeyDown)
|
||||
EVT_KEY_UP(self, self.OnKeyUp)
|
||||
EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
|
||||
EVT_PAINT(self, self.OnPaint)
|
||||
|
||||
|
||||
def SetBestSize(self, size=None):
|
||||
"""
|
||||
Given the current font and bezel width settings, calculate
|
||||
and set a good size.
|
||||
"""
|
||||
if size is None:
|
||||
size = wxSize(-1,-1)
|
||||
if type(size) == type(()):
|
||||
size = wxSize(size[0], size[1])
|
||||
size = wxSize(size.width, size.height) # make a copy
|
||||
|
||||
best = self.GetBestSize()
|
||||
if size.width == -1:
|
||||
size.width = best.width
|
||||
if size.height == -1:
|
||||
size.height = best.height
|
||||
|
||||
self.SetSize(size)
|
||||
|
||||
|
||||
def DoGetBestSize(self):
|
||||
"""Overridden base class virtual. Determines the best size of the
|
||||
button based on the label and bezel size."""
|
||||
w, h, useMin = self._GetLabelSize()
|
||||
defSize = wxButton_GetDefaultSize()
|
||||
width = 12 + w
|
||||
if useMin and width < defSize.width:
|
||||
width = defSize.width
|
||||
height = 11 + h
|
||||
if useMin and height < defSize.height:
|
||||
height = defSize.height
|
||||
width = width + self.bezelWidth - 1
|
||||
height = height + self.bezelWidth - 1
|
||||
return (width, height)
|
||||
|
||||
|
||||
def AcceptsFocus(self):
|
||||
"""Overridden base class virtual."""
|
||||
return self.IsShown() and self.IsEnabled()
|
||||
|
||||
|
||||
def Enable(self, enable=True):
|
||||
wxPyControl.Enable(self, enable)
|
||||
self.Refresh()
|
||||
|
||||
|
||||
def SetBezelWidth(self, width):
|
||||
"""Set the width of the 3D effect"""
|
||||
self.bezelWidth = width
|
||||
|
||||
def GetBezelWidth(self):
|
||||
"""Return the width of the 3D effect"""
|
||||
return self.bezelWidth
|
||||
|
||||
def SetUseFocusIndicator(self, flag):
|
||||
"""Specifiy if a focus indicator (dotted line) should be used"""
|
||||
self.useFocusInd = flag
|
||||
|
||||
def GetUseFocusIndicator(self):
|
||||
"""Return focus indicator flag"""
|
||||
return self.useFocusInd
|
||||
|
||||
|
||||
def InitColours(self):
|
||||
faceClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNFACE)
|
||||
textClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNTEXT)
|
||||
self.faceDnClr = faceClr
|
||||
self.SetBackgroundColour(faceClr)
|
||||
self.SetForegroundColour(textClr)
|
||||
|
||||
shadowClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNSHADOW)
|
||||
highlightClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNHIGHLIGHT)
|
||||
self.shadowPen = wxPen(shadowClr, 1, wxSOLID)
|
||||
self.highlightPen = wxPen(highlightClr, 1, wxSOLID)
|
||||
if wxPlatform == "__WXMAC__":
|
||||
self.focusIndPen = wxPen(textClr, 1, wxSOLID)
|
||||
else:
|
||||
self.focusIndPen = wxPen(textClr, 1, wxUSER_DASH)
|
||||
self.focusIndPen.SetDashes([1,1])
|
||||
self.focusIndPen.SetCap(wxCAP_BUTT)
|
||||
self.focusClr = highlightClr
|
||||
|
||||
|
||||
def SetBackgroundColour(self, colour):
|
||||
wxPyControl.SetBackgroundColour(self, colour)
|
||||
colour = self.GetBackgroundColour()
|
||||
|
||||
# Calculate a new set of highlight and shadow colours based on
|
||||
# the new background colour. Works okay if the colour is dark...
|
||||
r, g, b = colour.Get()
|
||||
fr, fg, fb = min(255,r+32), min(255,g+32), min(255,b+32)
|
||||
self.faceDnClr = wxColour(fr, fg, fb)
|
||||
sr, sg, sb = max(0,r-32), max(0,g-32), max(0,b-32)
|
||||
self.shadowPen = wxPen(wxColour(sr,sg,sb), 1, wxSOLID)
|
||||
hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64)
|
||||
self.highlightPen = wxPen(wxColour(hr,hg,hb), 1, wxSOLID)
|
||||
self.focusClr = wxColour(hr, hg, hb)
|
||||
|
||||
|
||||
def _GetLabelSize(self):
|
||||
""" used internally """
|
||||
w, h = self.GetTextExtent(self.GetLabel())
|
||||
return w, h, True
|
||||
|
||||
|
||||
def Notify(self):
|
||||
evt = wxGenButtonEvent(wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
|
||||
evt.SetIsDown(not self.up)
|
||||
evt.SetButtonObj(self)
|
||||
evt.SetEventObject(self)
|
||||
self.GetEventHandler().ProcessEvent(evt)
|
||||
|
||||
|
||||
def DrawBezel(self, dc, x1, y1, x2, y2):
|
||||
# draw the upper left sides
|
||||
if self.up:
|
||||
dc.SetPen(self.highlightPen)
|
||||
else:
|
||||
dc.SetPen(self.shadowPen)
|
||||
for i in range(self.bezelWidth):
|
||||
dc.DrawLine(x1+i, y1, x1+i, y2-i)
|
||||
dc.DrawLine(x1, y1+i, x2-i, y1+i)
|
||||
|
||||
# draw the lower right sides
|
||||
if self.up:
|
||||
dc.SetPen(self.shadowPen)
|
||||
else:
|
||||
dc.SetPen(self.highlightPen)
|
||||
for i in range(self.bezelWidth):
|
||||
dc.DrawLine(x1+i, y2-i, x2+1, y2-i)
|
||||
dc.DrawLine(x2-i, y1+i, x2-i, y2)
|
||||
|
||||
|
||||
def DrawLabel(self, dc, width, height, dw=0, dy=0):
|
||||
dc.SetFont(self.GetFont())
|
||||
if self.IsEnabled():
|
||||
dc.SetTextForeground(self.GetForegroundColour())
|
||||
else:
|
||||
dc.SetTextForeground(wxSystemSettings_GetSystemColour(wxSYS_COLOUR_GRAYTEXT))
|
||||
label = self.GetLabel()
|
||||
tw, th = dc.GetTextExtent(label)
|
||||
if not self.up:
|
||||
dw = dy = self.labelDelta
|
||||
dc.DrawText(label, (width-tw)/2+dw, (height-th)/2+dy)
|
||||
|
||||
|
||||
def DrawFocusIndicator(self, dc, w, h):
|
||||
bw = self.bezelWidth
|
||||
## if self.hasFocus:
|
||||
## self.focusIndPen.SetColour(self.GetForegroundColour())
|
||||
## else:
|
||||
## #self.focusIndPen.SetColour(self.GetBackgroundColour())
|
||||
## self.focusIndPen.SetColour(self.GetForegroundColour())
|
||||
self.focusIndPen.SetColour(self.focusClr)
|
||||
dc.SetLogicalFunction(wxINVERT)
|
||||
dc.SetPen(self.focusIndPen)
|
||||
dc.SetBrush(wxTRANSPARENT_BRUSH)
|
||||
dc.DrawRectangle(bw+2,bw+2, w-bw*2-4, h-bw*2-4)
|
||||
dc.SetLogicalFunction(wxCOPY)
|
||||
|
||||
|
||||
def OnPaint(self, event):
|
||||
(width, height) = self.GetClientSizeTuple()
|
||||
x1 = y1 = 0
|
||||
x2 = width-1
|
||||
y2 = height-1
|
||||
dc = wxBufferedPaintDC(self)
|
||||
if self.up:
|
||||
dc.SetBackground(wxBrush(self.GetBackgroundColour(), wxSOLID))
|
||||
else:
|
||||
dc.SetBackground(wxBrush(self.faceDnClr, wxSOLID))
|
||||
dc.Clear()
|
||||
self.DrawBezel(dc, x1, y1, x2, y2)
|
||||
self.DrawLabel(dc, width, height)
|
||||
if self.hasFocus and self.useFocusInd:
|
||||
self.DrawFocusIndicator(dc, width, height)
|
||||
|
||||
|
||||
def OnEraseBackground(self, event):
|
||||
pass
|
||||
|
||||
|
||||
def OnLeftDown(self, event):
|
||||
if not self.IsEnabled():
|
||||
return
|
||||
self.up = False
|
||||
self.CaptureMouse()
|
||||
self.SetFocus()
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
|
||||
|
||||
def OnLeftUp(self, event):
|
||||
if not self.IsEnabled() or not self.HasCapture():
|
||||
return
|
||||
if self.HasCapture():
|
||||
self.ReleaseMouse()
|
||||
if not self.up: # if the button was down when the mouse was released...
|
||||
self.Notify()
|
||||
self.up = True
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
|
||||
|
||||
def OnMotion(self, event):
|
||||
if not self.IsEnabled() or not self.HasCapture():
|
||||
return
|
||||
if event.LeftIsDown() and self.HasCapture():
|
||||
x,y = event.GetPositionTuple()
|
||||
w,h = self.GetClientSizeTuple()
|
||||
if self.up and x<w and x>=0 and y<h and y>=0:
|
||||
self.up = False
|
||||
self.Refresh()
|
||||
return
|
||||
if not self.up and (x<0 or y<0 or x>=w or y>=h):
|
||||
self.up = True
|
||||
self.Refresh()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
|
||||
def OnGainFocus(self, event):
|
||||
self.hasFocus = True
|
||||
dc = wxClientDC(self)
|
||||
w, h = self.GetClientSizeTuple()
|
||||
if self.useFocusInd:
|
||||
self.DrawFocusIndicator(dc, w, h)
|
||||
|
||||
|
||||
def OnLoseFocus(self, event):
|
||||
self.hasFocus = False
|
||||
dc = wxClientDC(self)
|
||||
w, h = self.GetClientSizeTuple()
|
||||
if self.useFocusInd:
|
||||
self.DrawFocusIndicator(dc, w, h)
|
||||
|
||||
|
||||
def OnKeyDown(self, event):
|
||||
if self.hasFocus and event.KeyCode() == ord(" "):
|
||||
self.up = False
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
|
||||
|
||||
def OnKeyUp(self, event):
|
||||
if self.hasFocus and event.KeyCode() == ord(" "):
|
||||
self.up = True
|
||||
self.Notify()
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class wxGenBitmapButton(wxGenButton):
|
||||
def __init__(self, parent, ID, bitmap,
|
||||
pos = wxDefaultPosition, size = wxDefaultSize,
|
||||
style = 0, validator = wxDefaultValidator,
|
||||
name = "genbutton"):
|
||||
self.bmpDisabled = None
|
||||
self.bmpFocus = None
|
||||
self.bmpSelected = None
|
||||
self.SetBitmapLabel(bitmap)
|
||||
wxGenButton.__init__(self, parent, ID, "", pos, size, style, validator, name)
|
||||
|
||||
|
||||
def GetBitmapLabel(self):
|
||||
return self.bmpLabel
|
||||
def GetBitmapDisabled(self):
|
||||
return self.bmpDisabled
|
||||
def GetBitmapFocus(self):
|
||||
return self.bmpFocus
|
||||
def GetBitmapSelected(self):
|
||||
return self.bmpSelected
|
||||
|
||||
|
||||
def SetBitmapDisabled(self, bitmap):
|
||||
"""Set bitmap to display when the button is disabled"""
|
||||
self.bmpDisabled = bitmap
|
||||
|
||||
def SetBitmapFocus(self, bitmap):
|
||||
"""Set bitmap to display when the button has the focus"""
|
||||
self.bmpFocus = bitmap
|
||||
self.SetUseFocusIndicator(False)
|
||||
|
||||
def SetBitmapSelected(self, bitmap):
|
||||
"""Set bitmap to display when the button is selected (pressed down)"""
|
||||
self.bmpSelected = bitmap
|
||||
|
||||
def SetBitmapLabel(self, bitmap, createOthers=True):
|
||||
"""
|
||||
Set the bitmap to display normally.
|
||||
This is the only one that is required. If
|
||||
createOthers is True, then the other bitmaps
|
||||
will be generated on the fly. Currently,
|
||||
only the disabled bitmap is generated.
|
||||
"""
|
||||
self.bmpLabel = bitmap
|
||||
if bitmap is not None and createOthers:
|
||||
image = wxImageFromBitmap(bitmap)
|
||||
imageutils.grayOut(image)
|
||||
self.SetBitmapDisabled(wxBitmapFromImage(image))
|
||||
|
||||
|
||||
def _GetLabelSize(self):
|
||||
""" used internally """
|
||||
if not self.bmpLabel:
|
||||
return -1, -1, False
|
||||
return self.bmpLabel.GetWidth()+2, self.bmpLabel.GetHeight()+2, False
|
||||
|
||||
def DrawLabel(self, dc, width, height, dw=0, dy=0):
|
||||
bmp = self.bmpLabel
|
||||
if self.bmpDisabled and not self.IsEnabled():
|
||||
bmp = self.bmpDisabled
|
||||
if self.bmpFocus and self.hasFocus:
|
||||
bmp = self.bmpFocus
|
||||
if self.bmpSelected and not self.up:
|
||||
bmp = self.bmpSelected
|
||||
bw,bh = bmp.GetWidth(), bmp.GetHeight()
|
||||
if not self.up:
|
||||
dw = dy = self.labelDelta
|
||||
hasMask = bmp.GetMask() != None
|
||||
dc.DrawBitmap(bmp, (width-bw)/2+dw, (height-bh)/2+dy, hasMask)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
class wxGenBitmapTextButton(wxGenBitmapButton): # generic bitmapped button with Text Label
|
||||
def __init__(self, parent, ID, bitmap, label,
|
||||
pos = wxDefaultPosition, size = wxDefaultSize,
|
||||
style = 0, validator = wxDefaultValidator,
|
||||
name = "genbutton"):
|
||||
wxGenBitmapButton.__init__(self, parent, ID, bitmap, pos, size, style, validator, name)
|
||||
self.SetLabel(label)
|
||||
|
||||
|
||||
def _GetLabelSize(self):
|
||||
""" used internally """
|
||||
w, h = self.GetTextExtent(self.GetLabel())
|
||||
if not self.bmpLabel:
|
||||
return w, h, True # if there isn't a bitmap use the size of the text
|
||||
|
||||
w_bmp = self.bmpLabel.GetWidth()+2
|
||||
h_bmp = self.bmpLabel.GetHeight()+2
|
||||
width = w + w_bmp
|
||||
if h_bmp > h:
|
||||
height = h_bmp
|
||||
else:
|
||||
height = h
|
||||
return width, height, True
|
||||
|
||||
|
||||
def DrawLabel(self, dc, width, height, dw=0, dy=0):
|
||||
bmp = self.bmpLabel
|
||||
if bmp != None: # if the bitmap is used
|
||||
if self.bmpDisabled and not self.IsEnabled():
|
||||
bmp = self.bmpDisabled
|
||||
if self.bmpFocus and self.hasFocus:
|
||||
bmp = self.bmpFocus
|
||||
if self.bmpSelected and not self.up:
|
||||
bmp = self.bmpSelected
|
||||
bw,bh = bmp.GetWidth(), bmp.GetHeight()
|
||||
if not self.up:
|
||||
dw = dy = self.labelDelta
|
||||
hasMask = bmp.GetMask() != None
|
||||
else:
|
||||
bw = bh = 0 # no bitmap -> size is zero
|
||||
|
||||
dc.SetFont(self.GetFont())
|
||||
if self.IsEnabled():
|
||||
dc.SetTextForeground(self.GetForegroundColour())
|
||||
else:
|
||||
dc.SetTextForeground(wxSystemSettings_GetSystemColour(wxSYS_COLOUR_GRAYTEXT))
|
||||
|
||||
label = self.GetLabel()
|
||||
tw, th = dc.GetTextExtent(label) # size of text
|
||||
if not self.up:
|
||||
dw = dy = self.labelDelta
|
||||
|
||||
pos_x = (width-bw-tw)/2+dw # adjust for bitmap and text to centre
|
||||
if bmp !=None:
|
||||
dc.DrawBitmap(bmp, pos_x, (height-bh)/2+dy, hasMask) # draw bitmap if available
|
||||
pos_x = pos_x + 2 # extra spacing from bitmap
|
||||
|
||||
dc.DrawText(label, pos_x + dw+bw, (height-th)/2+dy) # draw the text
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
class __ToggleMixin:
|
||||
def SetToggle(self, flag):
|
||||
self.up = not flag
|
||||
self.Refresh()
|
||||
SetValue = SetToggle
|
||||
|
||||
def GetToggle(self):
|
||||
return not self.up
|
||||
GetValue = GetToggle
|
||||
|
||||
def OnLeftDown(self, event):
|
||||
if not self.IsEnabled():
|
||||
return
|
||||
self.saveUp = self.up
|
||||
self.up = not self.up
|
||||
self.CaptureMouse()
|
||||
self.SetFocus()
|
||||
self.Refresh()
|
||||
|
||||
def OnLeftUp(self, event):
|
||||
if not self.IsEnabled() or not self.HasCapture():
|
||||
return
|
||||
if self.HasCapture():
|
||||
if self.up != self.saveUp:
|
||||
self.Notify()
|
||||
self.ReleaseMouse()
|
||||
self.Refresh()
|
||||
|
||||
def OnKeyDown(self, event):
|
||||
event.Skip()
|
||||
|
||||
def OnMotion(self, event):
|
||||
if not self.IsEnabled():
|
||||
return
|
||||
if event.LeftIsDown() and self.HasCapture():
|
||||
x,y = event.GetPositionTuple()
|
||||
w,h = self.GetClientSizeTuple()
|
||||
if x<w and x>=0 and y<h and y>=0:
|
||||
self.up = not self.saveUp
|
||||
self.Refresh()
|
||||
return
|
||||
if (x<0 or y<0 or x>=w or y>=h):
|
||||
self.up = self.saveUp
|
||||
self.Refresh()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
def OnKeyUp(self, event):
|
||||
if self.hasFocus and event.KeyCode() == ord(" "):
|
||||
self.up = not self.up
|
||||
self.Notify()
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
|
||||
|
||||
|
||||
|
||||
class wxGenToggleButton(__ToggleMixin, wxGenButton):
|
||||
pass
|
||||
|
||||
class wxGenBitmapToggleButton(__ToggleMixin, wxGenBitmapButton):
|
||||
pass
|
||||
|
||||
class wxGenBitmapTextToggleButton(__ToggleMixin, wxGenBitmapTextButton):
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
import wx.lib.buttons
|
||||
|
||||
__doc__ = wx.lib.buttons.__doc__
|
||||
|
||||
wxGenButtonEvent = wx.lib.buttons.GenButtonEvent
|
||||
wxGenButton = wx.lib.buttons.GenButton
|
||||
wxGenBitmapButton = wx.lib.buttons.GenBitmapButton
|
||||
wxGenBitmapTextButton = wx.lib.buttons.GenBitmapTextButton
|
||||
wxGenToggleButton = wx.lib.buttons.GenToggleButton
|
||||
wxGenBitmapToggleButton = wx.lib.buttons.GenBitmapToggleButton
|
||||
wxGenBitmapTextToggleButton = wx.lib.buttons.GenBitmapTextToggleButton
|
||||
|
||||
@@ -1,799 +1,13 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: calendar.py
|
||||
# Purpose: Calendar display control
|
||||
#
|
||||
# Author: Lorne White (email: lorne.white@telusplanet.net)
|
||||
#
|
||||
# Created:
|
||||
# Version 0.92
|
||||
# Date: Nov 26, 2001
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
from CDate import *
|
||||
|
||||
|
||||
CalDays = [6, 0, 1, 2, 3, 4, 5]
|
||||
AbrWeekday = {6:"Sun", 0:"Mon", 1:"Tue", 2:"Wed", 3:"Thu", 4:"Fri", 5:"Sat"}
|
||||
_MIDSIZE = 180
|
||||
|
||||
BusCalDays = [0, 1, 2, 3, 4, 5, 6]
|
||||
|
||||
# calendar drawing routing
|
||||
|
||||
def GetMonthList():
|
||||
monthlist = []
|
||||
for i in range(13):
|
||||
name = Month[i]
|
||||
if name != None:
|
||||
monthlist.append(name)
|
||||
return monthlist
|
||||
|
||||
class CalDraw:
|
||||
def __init__(self, parent):
|
||||
self.pwidth = 1
|
||||
self.pheight = 1
|
||||
try:
|
||||
self.scale = parent.scale
|
||||
except:
|
||||
self.scale = 1
|
||||
|
||||
self.DefParms()
|
||||
|
||||
def DefParms(self):
|
||||
self.num_auto = True # auto scale of the cal number day size
|
||||
self.num_size = 12 # default size of calendar if no auto size
|
||||
self.max_num_size = 12 # maximum size for calendar number
|
||||
|
||||
self.num_align_horz = wxALIGN_CENTRE # alignment of numbers
|
||||
self.num_align_vert = wxALIGN_CENTRE
|
||||
self.num_indent_horz = 0 # points indent from position, used to offset if not centered
|
||||
self.num_indent_vert = 0
|
||||
|
||||
self.week_auto = True # auto scale of week font text
|
||||
self.week_size = 10
|
||||
self.max_week_size = 12
|
||||
|
||||
self.grid_color = 'BLACK' # grid and selection colors
|
||||
self.back_color = 'WHITE'
|
||||
self.sel_color = 'RED'
|
||||
|
||||
self.high_color = 'LIGHT BLUE'
|
||||
self.border_color = 'BLACK'
|
||||
self.week_color = 'LIGHT GREY'
|
||||
|
||||
self.week_font_color = 'BLACK' # font colors
|
||||
self.day_font_color = 'BLACK'
|
||||
|
||||
self.font = wxSWISS
|
||||
self.bold = wxNORMAL
|
||||
|
||||
self.hide_title = False
|
||||
self.hide_grid = False
|
||||
self.outer_border = True
|
||||
|
||||
self.title_offset = 0
|
||||
self.cal_week_scale = 0.7
|
||||
self.show_weekend = False
|
||||
self.cal_type = "NORMAL"
|
||||
|
||||
def SetWeekColor(self, font_color, week_color): # set font and background color for week title
|
||||
self.week_font_color = font_color
|
||||
self.week_color = week_color
|
||||
|
||||
def SetSize(self, size):
|
||||
self.set_sizew = size.width
|
||||
self.set_sizeh = size.height
|
||||
|
||||
def InitValues(self): # default dimensions of various elements of the calendar
|
||||
self.rg = {}
|
||||
self.cal_sel = {}
|
||||
self.set_cy_st = 0 # start position
|
||||
self.set_cx_st = 0
|
||||
|
||||
self.set_y_mrg = 15 # start of vertical draw default
|
||||
self.set_x_mrg = 10
|
||||
self.set_y_end = 10
|
||||
|
||||
def SetPos(self, xpos, ypos):
|
||||
self.set_cx_st = xpos
|
||||
self.set_cy_st = ypos
|
||||
|
||||
def SetMarg(self, xmarg, ymarg):
|
||||
self.set_x_st = xmarg
|
||||
self.set_y_st = ymarg
|
||||
self.set_y_end = ymarg
|
||||
|
||||
def InitScale(self): # scale position values
|
||||
self.sizew = self.set_sizew * self.pwidth
|
||||
self.sizeh = self.set_sizeh * self.pheight
|
||||
|
||||
self.cx_st = self.set_cx_st * self.pwidth # draw start position
|
||||
self.cy_st = self.set_cy_st * self.pheight
|
||||
|
||||
self.x_mrg = self.set_x_mrg * self.pwidth # calendar draw margins
|
||||
self.y_mrg = self.set_y_mrg * self.pheight
|
||||
self.y_end = self.set_y_end * self.pheight
|
||||
|
||||
def DrawCal(self, DC, sel_lst=[]):
|
||||
self.DC = DC
|
||||
self.InitScale()
|
||||
|
||||
self.DrawBorder()
|
||||
if self.hide_title is False:
|
||||
self.DrawMonth()
|
||||
|
||||
self.Center()
|
||||
|
||||
self.DrawGrid()
|
||||
self.GetRect()
|
||||
|
||||
if self.show_weekend is True: # highlight weekend dates
|
||||
self.SetWeekEnd()
|
||||
|
||||
self.AddSelect(sel_lst) # overrides the weekend highlight
|
||||
|
||||
self.DrawSel() # highlighted days
|
||||
self.DrawWeek()
|
||||
self.DrawNum()
|
||||
|
||||
def AddSelect(self, list, cfont=None, cbackgrd = None):
|
||||
if cfont is None:
|
||||
cfont = self.sel_color # font digit color
|
||||
if cbackgrd is None:
|
||||
cbackgrd = self.high_color # select background color
|
||||
|
||||
for val in list:
|
||||
self.cal_sel[val] = (cfont, cbackgrd)
|
||||
|
||||
def DrawBorder(self): # draw border around the outside of the main display rectangle
|
||||
brush = wxBrush(wxNamedColour(self.back_color), wxSOLID)
|
||||
self.DC.SetBrush(brush)
|
||||
self.DC.SetPen(wxPen(wxNamedColour(self.border_color), 1))
|
||||
|
||||
if self.outer_border is True:
|
||||
rect = wxRect(self.cx_st, self.cy_st, self.sizew, self.sizeh) # full display window area
|
||||
self.DC.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
|
||||
|
||||
def DrawNumVal(self):
|
||||
self.DrawNum()
|
||||
|
||||
# calculate the calendar days and offset position
|
||||
|
||||
def SetCal(self, year, month):
|
||||
self.InitValues() # reset initial values
|
||||
|
||||
self.year = year
|
||||
self.month = month
|
||||
|
||||
day = 1
|
||||
t = Date(year, month, day)
|
||||
dow = self.dow = t.day_of_week # start day in month
|
||||
dim = self.dim = t.days_in_month # number of days in month
|
||||
if self.cal_type == "NORMAL":
|
||||
start_pos = dow+1
|
||||
else:
|
||||
start_pos = dow
|
||||
|
||||
self.st_pos = start_pos
|
||||
|
||||
self.cal = []
|
||||
for i in range(start_pos):
|
||||
self.cal.append('')
|
||||
i = 1
|
||||
while i <= dim:
|
||||
self.cal.append(str(i))
|
||||
i = i + 1
|
||||
return start_pos
|
||||
|
||||
def SetWeekEnd(self, font_color='BLACK', backgrd = 'LIGHT GREY'):
|
||||
date = 6 - int(self.dow) # start day of first saturday
|
||||
while date <= self.dim:
|
||||
self.cal_sel[date] = (font_color, backgrd) # Saturday
|
||||
date = date + 1
|
||||
if date <= self.dim:
|
||||
self.cal_sel[date] = (font_color, backgrd) # Sunday
|
||||
date = date + 6
|
||||
else:
|
||||
date = date + 7
|
||||
|
||||
def GetRect(self): # get the display rectange list of the day grid
|
||||
cnt = 0
|
||||
for y in self.gridy[1:-1]:
|
||||
for x in self.gridx[:-1]:
|
||||
rect = wxRect(x, y, self.dl_w, self.dl_h) # create rect region
|
||||
self.rg[cnt] = rect
|
||||
cnt = cnt + 1
|
||||
return self.rg
|
||||
|
||||
def GetCal(self):
|
||||
return self.cal
|
||||
|
||||
def GetOffset(self):
|
||||
return self.st_pos
|
||||
|
||||
def DrawMonth(self): # month and year title
|
||||
month = Month[self.month]
|
||||
|
||||
sizef = 11
|
||||
if self.sizeh < _MIDSIZE:
|
||||
sizef = 10
|
||||
|
||||
f = wxFont(sizef, self.font, wxNORMAL, self.bold)
|
||||
self.DC.SetFont(f)
|
||||
|
||||
tw,th = self.DC.GetTextExtent(month)
|
||||
adjust = self.cx_st + (self.sizew-tw)/2
|
||||
self.DC.DrawText(month, adjust, self.cy_st + th)
|
||||
|
||||
year = str(self.year)
|
||||
tw,th = self.DC.GetTextExtent(year)
|
||||
adjust = self.sizew - tw - self.x_mrg
|
||||
|
||||
self.title_offset = th * 2
|
||||
|
||||
f = wxFont(sizef, self.font, wxNORMAL, self.bold)
|
||||
self.DC.SetFont(f)
|
||||
self.DC.DrawText(year, self.cx_st + adjust, self.cy_st + th)
|
||||
|
||||
def DrawWeek(self): # draw the week days
|
||||
width = self.gridx[1]-self.gridx[0]
|
||||
height = self.gridy[1] - self.gridy[0]
|
||||
rect_w = self.gridx[7]-self.gridx[0]
|
||||
|
||||
f = wxFont(10, self.font, wxNORMAL, self.bold) # initial font setting
|
||||
if self.week_auto == True:
|
||||
test_size = self.max_week_size # max size
|
||||
test_day = ' Sun '
|
||||
while test_size > 2:
|
||||
f.SetPointSize(test_size)
|
||||
self.DC.SetFont(f)
|
||||
tw,th = self.DC.GetTextExtent(test_day)
|
||||
if tw < width and th < height:
|
||||
break
|
||||
test_size = test_size - 1
|
||||
else:
|
||||
f.SetPointSize(self.week_size) # set fixed size
|
||||
self.DC.SetFont(f)
|
||||
|
||||
self.DC.SetTextForeground(wxNamedColour(self.week_font_color))
|
||||
|
||||
cnt_x = 0
|
||||
cnt_y = 0
|
||||
|
||||
brush = wxBrush(wxNamedColour(self.week_color), wxSOLID)
|
||||
self.DC.SetBrush(brush)
|
||||
# self.DC.DrawRectangle(self.gridx[0], self.gridy[0], rect_w+1, height)
|
||||
|
||||
if self.cal_type == "NORMAL":
|
||||
cal_days = CalDays
|
||||
else:
|
||||
cal_days = BusCalDays
|
||||
|
||||
for val in cal_days:
|
||||
day = AbrWeekday[val]
|
||||
if self.sizew < 200:
|
||||
day = day[0]
|
||||
dw,dh = self.DC.GetTextExtent(day)
|
||||
diffx = (width-dw)/2
|
||||
diffy = (height-dh)/2
|
||||
|
||||
x = self.gridx[cnt_x]
|
||||
y = self.gridy[cnt_y]
|
||||
self.DC.DrawRectangle(self.gridx[cnt_x], self.gridy[0], width+1, height)
|
||||
self.DC.DrawText(day, x+diffx, y+diffy)
|
||||
cnt_x = cnt_x + 1
|
||||
|
||||
def DrawNum(self): # draw the day numbers
|
||||
f = wxFont(10, self.font, wxNORMAL, self.bold) # initial font setting
|
||||
if self.num_auto == True:
|
||||
test_size = self.max_num_size # max size
|
||||
test_day = ' 99 '
|
||||
while test_size > 2:
|
||||
f.SetPointSize(test_size)
|
||||
self.DC.SetFont(f)
|
||||
tw,th = self.DC.GetTextExtent(test_day)
|
||||
if tw < self.dl_w and th < self.dl_h:
|
||||
sizef = test_size
|
||||
break
|
||||
test_size = test_size - 1
|
||||
else:
|
||||
f.SetPointSize(self.num_size) # set fixed size
|
||||
self.DC.SetFont(f)
|
||||
|
||||
cnt_x = 0
|
||||
cnt_y = 1
|
||||
for val in self.cal:
|
||||
x = self.gridx[cnt_x]
|
||||
y = self.gridy[cnt_y]
|
||||
|
||||
try:
|
||||
num_val = int(val)
|
||||
num_color = self.cal_sel[num_val][0]
|
||||
except:
|
||||
num_color = self.day_font_color
|
||||
|
||||
self.DC.SetTextForeground(wxNamedColour(num_color))
|
||||
self.DC.SetFont(f)
|
||||
|
||||
tw,th = self.DC.GetTextExtent(val)
|
||||
if self.num_align_horz == wxALIGN_CENTRE:
|
||||
adj_h = (self.dl_w - tw)/2
|
||||
elif self.num_align_horz == wxALIGN_RIGHT:
|
||||
adj_h = self.dl_w - tw
|
||||
else:
|
||||
adj_h = 0 # left alignment
|
||||
|
||||
adj_h = adj_h + self.num_indent_horz
|
||||
|
||||
if self.num_align_vert == wxALIGN_CENTRE:
|
||||
adj_v = (self.dl_h - th)/2
|
||||
elif self.num_align_horz == wxALIGN_RIGHT:
|
||||
adj_v = self.dl_h - th
|
||||
else:
|
||||
adj_v = 0 # left alignment
|
||||
|
||||
adj_v = adj_v + self.num_indent_vert
|
||||
|
||||
self.DC.DrawText(val, x+adj_h, y+adj_v)
|
||||
if cnt_x < 6:
|
||||
cnt_x = cnt_x + 1
|
||||
else:
|
||||
cnt_x = 0
|
||||
cnt_y = cnt_y + 1
|
||||
|
||||
def Center(self): # calculate the dimensions in the center of the drawing area
|
||||
bdw = self.x_mrg * 2
|
||||
bdh = self.y_mrg + self.y_end + self.title_offset
|
||||
|
||||
self.dl_w = int((self.sizew-bdw)/7)
|
||||
self.dl_h = int((self.sizeh-bdh)/7)
|
||||
|
||||
self.dl_th = int(self.dl_h*self.cal_week_scale) # week title adjustment
|
||||
self.cwidth = self.dl_w * 7
|
||||
self.cheight = self.dl_h * 6 + self.dl_th
|
||||
|
||||
def DrawSel(self): # highlighted selected days
|
||||
for key in self.cal_sel.keys():
|
||||
sel_color = self.cal_sel[key][1]
|
||||
brush = wxBrush(wxNamedColour(sel_color), wxSOLID)
|
||||
self.DC.SetBrush(brush)
|
||||
|
||||
if self.hide_grid is False:
|
||||
self.DC.SetPen(wxPen(wxNamedColour(self.grid_color), 0))
|
||||
else:
|
||||
self.DC.SetPen(wxPen(wxNamedColour(self.back_color), 0))
|
||||
nkey = key + self.st_pos -1
|
||||
rect = self.rg[nkey]
|
||||
self.DC.DrawRectangle(rect.x, rect.y, rect.width+1, rect.height+1)
|
||||
|
||||
def DrawGrid(self): # calculate and draw the grid lines
|
||||
self.DC.SetPen(wxPen(wxNamedColour(self.grid_color), 0))
|
||||
|
||||
self.gridx = []
|
||||
self.gridy = []
|
||||
|
||||
self.x_st = self.cx_st + self.x_mrg
|
||||
self.y_st = self.cy_st + self.y_mrg + self.title_offset # start postion of draw
|
||||
|
||||
x1 = self.x_st
|
||||
y1 = self.y_st
|
||||
|
||||
y2 = y1 + self.cheight
|
||||
for i in range(8):
|
||||
if self.hide_grid is False:
|
||||
self.DC.DrawLine(x1, y1, x1, y2)
|
||||
self.gridx.append(x1)
|
||||
x1 = x1 + self.dl_w
|
||||
|
||||
x1 = self.x_st
|
||||
y1 = self.y_st
|
||||
|
||||
x2 = x1 + self.cwidth
|
||||
for i in range(8):
|
||||
if self.hide_grid is False:
|
||||
self.DC.DrawLine(x1, y1, x2, y1)
|
||||
self.gridy.append(y1)
|
||||
if i == 0:
|
||||
y1 = y1 + self.dl_th
|
||||
else:
|
||||
y1 = y1 + self.dl_h
|
||||
|
||||
|
||||
class PrtCalDraw(CalDraw):
|
||||
def InitValues(self):
|
||||
self.rg = {}
|
||||
self.cal_sel = {}
|
||||
self.set_cx_st = 1.0 # start draw border location
|
||||
self.set_cy_st = 1.0
|
||||
|
||||
self.set_y_mrg = 0.2 # draw offset position
|
||||
self.set_x_mrg = 0.2
|
||||
self.set_y_end = 0.2
|
||||
|
||||
def SetPSize(self, pwidth, pheight): # calculate the dimensions in the center of the drawing area
|
||||
self.pwidth = int(pwidth)/self.scale
|
||||
self.pheight = int(pheight)/self.scale
|
||||
|
||||
def SetPreview(self, preview):
|
||||
self.preview = preview
|
||||
|
||||
class wxCalendar(wxWindow):
|
||||
def __init__(self, parent, id, pos=wxDefaultPosition, size=wxDefaultSize):
|
||||
wxWindow.__init__(self, parent, id, pos, size)
|
||||
|
||||
# set the calendar control attributes
|
||||
|
||||
self.grid_color = 'BLACK'
|
||||
self.back_color = 'WHITE'
|
||||
self.hide_grid = False
|
||||
self.sel_color = 'RED'
|
||||
self.hide_title = False
|
||||
self.show_weekend = False
|
||||
self.cal_type = "NORMAL"
|
||||
|
||||
self.week_color = 'LIGHT GREY'
|
||||
self.week_font_color = 'BLACK' # font colors
|
||||
|
||||
self.select_list = []
|
||||
|
||||
self.SetBackgroundColour(wxNamedColor(self.back_color))
|
||||
self.Connect(-1, -1, wxEVT_LEFT_DOWN, self.OnLeftEvent)
|
||||
self.Connect(-1, -1, wxEVT_LEFT_DCLICK, self.OnLeftDEvent)
|
||||
self.Connect(-1, -1, wxEVT_RIGHT_DOWN, self.OnRightEvent)
|
||||
self.Connect(-1, -1, wxEVT_RIGHT_DCLICK, self.OnRightDEvent)
|
||||
|
||||
self.sel_key = None # last used by
|
||||
self.sel_lst = [] # highlighted selected days
|
||||
|
||||
self.SetNow() # default calendar for current month
|
||||
|
||||
self.size = None
|
||||
self.set_day = None
|
||||
|
||||
EVT_PAINT(self, self.OnPaint)
|
||||
EVT_SIZE(self, self.OnSize)
|
||||
|
||||
# control some of the main calendar attributes
|
||||
|
||||
def HideTitle(self):
|
||||
self.hide_title = True
|
||||
|
||||
def HideGrid(self):
|
||||
self.hide_grid = True
|
||||
|
||||
# determine the calendar rectangle click area and draw a selection
|
||||
|
||||
def ProcessClick(self, event):
|
||||
self.x, self.y = event.GetX(), event.GetY()
|
||||
key = self.GetDayHit(self.x, self.y)
|
||||
self.SelectDay(key)
|
||||
|
||||
# tab mouse click events and process
|
||||
|
||||
def OnLeftEvent(self, event):
|
||||
self.click = 'LEFT'
|
||||
self.shiftkey = event.ShiftDown()
|
||||
self.ctrlkey = event.ControlDown()
|
||||
self.ProcessClick(event)
|
||||
|
||||
def OnLeftDEvent(self, event):
|
||||
self.click = 'DLEFT'
|
||||
self.ProcessClick(event)
|
||||
|
||||
def OnRightEvent(self, event):
|
||||
self.click = 'RIGHT'
|
||||
self.ProcessClick(event)
|
||||
|
||||
def OnRightDEvent(self, event):
|
||||
self.click = 'DRIGHT'
|
||||
self.ProcessClick(event)
|
||||
|
||||
def SetSize(self, set_size):
|
||||
self.size = set_size
|
||||
|
||||
def SetSelDay(self, sel):
|
||||
self.sel_lst = sel # list of highlighted days
|
||||
|
||||
# get the current date
|
||||
|
||||
def SetNow(self):
|
||||
dt = now()
|
||||
self.month = dt.month
|
||||
self.year = dt.year
|
||||
self.day = dt.day
|
||||
|
||||
# set the current day
|
||||
|
||||
def SetCurrentDay(self):
|
||||
self.SetNow()
|
||||
self.set_day = self.day
|
||||
|
||||
# get the date, day, month, year set in calendar
|
||||
|
||||
def GetDate(self):
|
||||
return self.day, self.month, self.year
|
||||
|
||||
def GetDay(self):
|
||||
return self.day
|
||||
|
||||
def GetMonth(self):
|
||||
return self.month
|
||||
|
||||
def GetYear(self):
|
||||
return self.year
|
||||
|
||||
# set the day, month, and year
|
||||
|
||||
def SetDayValue(self, day):
|
||||
self.set_day = day
|
||||
|
||||
def SetMonth(self, month):
|
||||
if month >= 1 and month <= 12:
|
||||
self.month = month
|
||||
else:
|
||||
self.month = 1
|
||||
self.set_day = None
|
||||
|
||||
def SetYear(self, year):
|
||||
self.year = year
|
||||
|
||||
# increment year and month
|
||||
|
||||
def IncYear(self):
|
||||
self.year = self.year + 1
|
||||
self.set_day = None
|
||||
|
||||
def DecYear(self):
|
||||
self.year = self.year - 1
|
||||
self.set_day = None
|
||||
|
||||
def IncMonth(self):
|
||||
self.month = self.month + 1
|
||||
if self.month > 12:
|
||||
self.month = 1
|
||||
self.year = self.year + 1
|
||||
self.set_day = None
|
||||
|
||||
def DecMonth(self):
|
||||
self.month = self.month - 1
|
||||
if self.month < 1:
|
||||
self.month = 12
|
||||
self.year = self.year - 1
|
||||
self.set_day = None
|
||||
|
||||
# test to see if the selection has a date and create event
|
||||
|
||||
def TestDay(self, key):
|
||||
try:
|
||||
self.day = int(self.cal[key])
|
||||
except:
|
||||
return None
|
||||
if self.day == "":
|
||||
return None
|
||||
else:
|
||||
evt = wxPyCommandEvent(2100, self.GetId())
|
||||
evt.click, evt.day, evt.month, evt.year = self.click, self.day, self.month, self.year
|
||||
evt.shiftkey = self.shiftkey
|
||||
evt.ctrlkey = self.ctrlkey
|
||||
self.GetEventHandler().ProcessEvent(evt)
|
||||
|
||||
self.set_day = self.day
|
||||
return key
|
||||
|
||||
# find the clicked area rectangle
|
||||
|
||||
def GetDayHit(self, mx, my):
|
||||
for key in self.rg.keys():
|
||||
val = self.rg[key]
|
||||
ms_rect = wxRect(mx, my, 1, 1)
|
||||
if wxIntersectRect(ms_rect, val) is not None:
|
||||
result = self.TestDay(key)
|
||||
return result
|
||||
return None
|
||||
|
||||
# calendar drawing
|
||||
|
||||
def SetWeekColor(self, font_color, week_color): # set font and background color for week title
|
||||
self.week_font_color = font_color
|
||||
self.week_color = week_color
|
||||
|
||||
def AddSelect(self, list, font_color, back_color):
|
||||
list_val = [list, font_color, back_color]
|
||||
self.select_list.append(list_val)
|
||||
|
||||
def ShowWeekEnd(self):
|
||||
self.show_weekend = True # highlight weekend
|
||||
|
||||
def SetBusType(self):
|
||||
self.cal_type = "BUS"
|
||||
|
||||
def OnSize(self, evt):
|
||||
self.Refresh(False)
|
||||
evt.Skip()
|
||||
|
||||
def OnPaint(self, event):
|
||||
DC = wxPaintDC(self)
|
||||
self.DoDrawing(DC)
|
||||
|
||||
def DoDrawing(self, DC):
|
||||
DC = wxPaintDC(self)
|
||||
DC.BeginDrawing()
|
||||
|
||||
self.cal = cal = CalDraw(self)
|
||||
|
||||
cal.grid_color = self.grid_color
|
||||
cal.back_color = self.back_color
|
||||
cal.hide_grid = self.hide_grid
|
||||
cal.grid_color = self.grid_color
|
||||
cal.hide_title = self.hide_title
|
||||
cal.show_weekend = self.show_weekend
|
||||
cal.cal_type = self.cal_type
|
||||
|
||||
if self.size is None:
|
||||
size = self.GetClientSize()
|
||||
else:
|
||||
size = self.size
|
||||
|
||||
# drawing attributes
|
||||
|
||||
cal.week_font_color = self.week_font_color
|
||||
cal.week_color = self.week_color
|
||||
|
||||
cal.SetSize(size)
|
||||
cal.SetCal(self.year, self.month)
|
||||
for val in self.select_list:
|
||||
cal.AddSelect(val[0], val[1], val[2])
|
||||
|
||||
cal.DrawCal(DC, self.sel_lst)
|
||||
|
||||
self.rg = cal.GetRect()
|
||||
self.cal = cal.GetCal()
|
||||
self.st_pos = cal.GetOffset()
|
||||
self.ymax = DC.MaxY()
|
||||
|
||||
if self.set_day != None:
|
||||
self.SetDay(self.set_day)
|
||||
DC.EndDrawing()
|
||||
|
||||
# draw the selection rectangle
|
||||
|
||||
def DrawRect(self, key, color = 'BLACK', width = 0):
|
||||
if key == None:
|
||||
return
|
||||
DC = wxClientDC(self)
|
||||
DC.BeginDrawing()
|
||||
|
||||
brush = wxBrush(wxColour(0, 0xFF, 0x80), wxTRANSPARENT)
|
||||
DC.SetBrush(brush)
|
||||
DC.SetPen(wxPen(wxNamedColour(color), width))
|
||||
|
||||
rect = self.rg[key]
|
||||
DC.DrawRectangle(rect.x, rect.y, rect.width+1, rect.height+1)
|
||||
|
||||
DC.EndDrawing()
|
||||
|
||||
# set the day selection
|
||||
|
||||
def SetDay(self, day):
|
||||
day = day + self.st_pos - 1
|
||||
self.SelectDay(day)
|
||||
|
||||
def SelectDay(self, key):
|
||||
sel_size = 1
|
||||
self.DrawRect(self.sel_key, self.back_color, sel_size) # clear large selection
|
||||
if self.hide_grid is False:
|
||||
self.DrawRect(self.sel_key, self.grid_color)
|
||||
|
||||
self.DrawRect(key, self.sel_color, sel_size)
|
||||
self.sel_key = key # store last used by
|
||||
self.select_day = None
|
||||
|
||||
def ClearDsp(self):
|
||||
self.Clear()
|
||||
|
||||
class CalenDlg(wxDialog):
|
||||
def __init__(self, parent, month=None, day = None, year=None):
|
||||
wxDialog.__init__(self, parent, -1, "Event Calendar", wxPyDefaultPosition, wxSize(280, 360))
|
||||
|
||||
# set the calendar and attributes
|
||||
self.calend = wxCalendar(self, -1, wxPoint(20, 60), wxSize(240, 200))
|
||||
if month == None:
|
||||
self.calend.SetCurrentDay()
|
||||
start_month = self.calend.GetMonth()
|
||||
start_year = self.calend.GetYear()
|
||||
else:
|
||||
self.calend.month = start_month = month
|
||||
self.calend.year = start_year = year
|
||||
self.calend.SetDayValue(day)
|
||||
|
||||
self.calend.HideTitle()
|
||||
self.ResetDisplay()
|
||||
|
||||
# get month list from DateTime
|
||||
monthlist = GetMonthList()
|
||||
|
||||
# select the month
|
||||
mID = wxNewId()
|
||||
self.date = wxComboBox(self, mID, Month[start_month], wxPoint(20, 20), wxSize(90, -1), monthlist, wxCB_DROPDOWN)
|
||||
EVT_COMBOBOX(self, mID, self.EvtComboBox)
|
||||
|
||||
# alternate spin button to control the month
|
||||
mID = wxNewId()
|
||||
h = self.date.GetSize().height
|
||||
self.m_spin = wxSpinButton(self, mID, wxPoint(130, 20), wxSize(h*2, h), wxSP_VERTICAL)
|
||||
self.m_spin.SetRange(1, 12)
|
||||
self.m_spin.SetValue(start_month)
|
||||
|
||||
EVT_SPIN(self, mID, self.OnMonthSpin)
|
||||
|
||||
# spin button to control the year
|
||||
mID = wxNewId()
|
||||
self.dtext = wxTextCtrl(self, -1, str(start_year), wxPoint(160, 20), wxSize(60, -1))
|
||||
h = self.dtext.GetSize().height
|
||||
|
||||
self.y_spin = wxSpinButton(self, mID, wxPoint(220, 20), wxSize(h*2, h), wxSP_VERTICAL)
|
||||
self.y_spin.SetRange(1980, 2010)
|
||||
self.y_spin.SetValue(start_year)
|
||||
|
||||
EVT_SPIN(self, mID, self.OnYrSpin)
|
||||
|
||||
self.Connect(self.calend.GetId(), -1, 2100, self.MouseClick)
|
||||
|
||||
x_pos = 50
|
||||
y_pos = 280
|
||||
but_size = wxSize(60, 25)
|
||||
|
||||
mID = wxNewId()
|
||||
wxButton(self, mID, ' Ok ', wxPoint(x_pos, y_pos), but_size)
|
||||
EVT_BUTTON(self, mID, self.OnOk)
|
||||
|
||||
mID = wxNewId()
|
||||
wxButton(self, mID, ' Close ', wxPoint(x_pos + 120, y_pos), but_size)
|
||||
EVT_BUTTON(self, mID, self.OnCancel)
|
||||
|
||||
def OnOk(self, event):
|
||||
self.EndModal(wxID_OK)
|
||||
|
||||
def OnCancel(self, event):
|
||||
self.EndModal(wxID_CANCEL)
|
||||
|
||||
# log the mouse clicks
|
||||
def MouseClick(self, evt):
|
||||
self.month = evt.month
|
||||
self.result = [evt.click, str(evt.day), Month[evt.month], str(evt.year)] # result click type and date
|
||||
|
||||
if evt.click == 'DLEFT':
|
||||
self.EndModal(wxID_OK)
|
||||
|
||||
# month and year spin selection routines
|
||||
def OnMonthSpin(self, event):
|
||||
month = event.GetPosition()
|
||||
self.date.SetValue(Month[month])
|
||||
self.calend.SetMonth(month)
|
||||
self.calend.Refresh()
|
||||
|
||||
def OnYrSpin(self, event):
|
||||
year = event.GetPosition()
|
||||
self.dtext.SetValue(str(year))
|
||||
self.calend.SetYear(year)
|
||||
self.calend.Refresh()
|
||||
|
||||
def EvtComboBox(self, event):
|
||||
name = event.GetString()
|
||||
monthval = self.date.FindString(name)
|
||||
self.m_spin.SetValue(monthval+1)
|
||||
|
||||
self.calend.SetMonth(monthval+1)
|
||||
self.ResetDisplay()
|
||||
|
||||
# set the calendar for highlighted days
|
||||
|
||||
def ResetDisplay(self):
|
||||
month = self.calend.GetMonth()
|
||||
self.calend.Refresh()
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.calendar
|
||||
|
||||
__doc__ = wx.lib.calendar.__doc__
|
||||
|
||||
CalDraw = wx.lib.calendar.CalDraw
|
||||
CalenDlg = wx.lib.calendar.CalenDlg
|
||||
GetMonthList = wx.lib.calendar.GetMonthList
|
||||
PrtCalDraw = wx.lib.calendar.PrtCalDraw
|
||||
wxCalendar = wx.lib.calendar.wxCalendar
|
||||
|
||||
@@ -1,26 +1 @@
|
||||
"""
|
||||
wxPyColourChooser
|
||||
Copyright (C) 2002 Michael Gilfix <mgilfix@eecs.tufts.edu>
|
||||
|
||||
This file is part of wxPyColourChooser.
|
||||
|
||||
This version of wxPyColourChooser is open source; you can redistribute it
|
||||
and/or modify it under the licensed terms.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
"""
|
||||
|
||||
from pycolourchooser import *
|
||||
|
||||
# For the American in you
|
||||
wxPyColorChooser = wxPyColourChooser
|
||||
|
||||
__all__ = [
|
||||
'canvas',
|
||||
'pycolourbox',
|
||||
'pycolourchooser',
|
||||
'pycolourslider',
|
||||
'pypalette',
|
||||
]
|
||||
#
|
||||
|
||||
@@ -1,120 +1,10 @@
|
||||
"""
|
||||
wxPyColourChooser
|
||||
Copyright (C) 2002 Michael Gilfix <mgilfix@eecs.tufts.edu>
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
This file is part of wxPyColourChooser.
|
||||
import wx.lib.colourchooser.canvas
|
||||
|
||||
This version of wxPyColourChooser is open source; you can redistribute it
|
||||
and/or modify it under the licensed terms.
|
||||
__doc__ = wx.lib.colourchooser.canvas.__doc__
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
"""
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
class BitmapBuffer(wxMemoryDC):
|
||||
"""A screen buffer class.
|
||||
|
||||
This class implements a screen output buffer. Data is meant to
|
||||
be drawn in the buffer class and then blitted directly to the
|
||||
output device, or on-screen window.
|
||||
"""
|
||||
def __init__(self, width, height, colour):
|
||||
"""Initialize the empty buffer object."""
|
||||
wxMemoryDC.__init__(self)
|
||||
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.colour = colour
|
||||
|
||||
self.bitmap = wxEmptyBitmap(self.width, self.height)
|
||||
self.SelectObject(self.bitmap)
|
||||
|
||||
# Initialize the buffer to the background colour
|
||||
self.SetBackground(wxBrush(self.colour, wxSOLID))
|
||||
self.Clear()
|
||||
|
||||
# Make each logical unit of the buffer equal to 1 pixel
|
||||
self.SetMapMode(wxMM_TEXT)
|
||||
|
||||
def GetBitmap(self):
|
||||
"""Returns the internal bitmap for direct drawing."""
|
||||
return self.bitmap
|
||||
|
||||
class Canvas(wxWindow):
|
||||
"""A canvas class for arbitrary drawing.
|
||||
|
||||
The Canvas class implements a window that allows for drawing
|
||||
arbitrary graphics. It implements a double buffer scheme and
|
||||
blits the off-screen buffer to the window during paint calls
|
||||
by the windowing system for speed.
|
||||
|
||||
Some other methods for determining the canvas colour and size
|
||||
are also provided.
|
||||
"""
|
||||
def __init__(self, parent, id,
|
||||
pos=wxDefaultPosition,
|
||||
size=wxDefaultSize,
|
||||
style=wxSIMPLE_BORDER):
|
||||
"""Creates a canvas instance and initializes the off-screen
|
||||
buffer. Also sets the handler for rendering the canvas
|
||||
automatically via size and paint calls from the windowing
|
||||
system."""
|
||||
wxWindow.__init__(self, parent, id, pos, size, style)
|
||||
|
||||
# Perform an intial sizing
|
||||
self.ReDraw()
|
||||
|
||||
# Register event handlers
|
||||
EVT_SIZE(self, self.onSize)
|
||||
EVT_PAINT(self, self.onPaint)
|
||||
|
||||
def MakeNewBuffer(self):
|
||||
size = self.GetSizeTuple()
|
||||
self.buffer = BitmapBuffer(size[0], size[1],
|
||||
self.GetBackgroundColour())
|
||||
|
||||
def onSize(self, event):
|
||||
"""Perform actual redraw to off-screen buffer only when the
|
||||
size of the canvas has changed. This saves a lot of computation
|
||||
since the same image can be re-used, provided the canvas size
|
||||
hasn't changed."""
|
||||
self.MakeNewBuffer()
|
||||
self.DrawBuffer()
|
||||
self.Refresh()
|
||||
|
||||
def ReDraw(self):
|
||||
"""Explicitly tells the canvas to redraw it's contents."""
|
||||
self.onSize(None)
|
||||
|
||||
def Refresh(self):
|
||||
"""Re-draws the buffer contents on-screen."""
|
||||
dc = wxClientDC(self)
|
||||
self.Blit(dc)
|
||||
|
||||
def onPaint(self, event):
|
||||
"""Renders the off-screen buffer on-screen."""
|
||||
dc = wxPaintDC(self)
|
||||
self.Blit(dc)
|
||||
|
||||
def Blit(self, dc):
|
||||
"""Performs the blit of the buffer contents on-screen."""
|
||||
width, height = self.buffer.GetSize()
|
||||
dc.BeginDrawing()
|
||||
dc.Blit(0, 0, width, height, self.buffer, 0, 0)
|
||||
dc.EndDrawing()
|
||||
|
||||
def GetBoundingRect(self):
|
||||
"""Returns a tuple that contains the co-ordinates of the
|
||||
top-left and bottom-right corners of the canvas."""
|
||||
x, y = self.GetPositionTuple()
|
||||
w, h = self.GetSize()
|
||||
return(x, y + h, x + w, y)
|
||||
|
||||
def DrawBuffer(self):
|
||||
"""Actual drawing function for drawing into the off-screen
|
||||
buffer. To be overrideen in the implementing class. Do nothing
|
||||
by default."""
|
||||
pass
|
||||
BitmapBuffer = wx.lib.colourchooser.canvas.BitmapBuffer
|
||||
Canvas = wx.lib.colourchooser.canvas.Canvas
|
||||
|
||||
@@ -1,24 +1,8 @@
|
||||
"""
|
||||
wxPyColourChooser
|
||||
Copyright (C) 2002 Michael Gilfix <mgilfix@eecs.tufts.edu>
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
This file is part of wxPyColourChooser.
|
||||
import wx.lib.colourchooser.intl
|
||||
|
||||
This version of wxPyColourChooser is open source; you can redistribute it
|
||||
and/or modify it under the licensed terms.
|
||||
__doc__ = wx.lib.colourchooser.intl.__doc__
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
"""
|
||||
|
||||
try:
|
||||
import gettext
|
||||
|
||||
gettext.bindtextdomain('wxpycolourchooser')
|
||||
gettext.textdomain('wxpycolourchooser')
|
||||
_ = gettext.gettext
|
||||
except Exception, strerror:
|
||||
print "Warning: Couldn't import translation function: %(str)s" %{ 'str' : strerror }
|
||||
print "Defaulting to En"
|
||||
_ = lambda x: x
|
||||
|
||||
@@ -1,79 +1,9 @@
|
||||
"""
|
||||
wxPyColourChooser
|
||||
Copyright (C) 2002 Michael Gilfix <mgilfix@eecs.tufts.edu>
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
This file is part of wxPyColourChooser.
|
||||
import wx.lib.colourchooser.pycolourbox
|
||||
|
||||
This version of wxPyColourChooser is open source; you can redistribute it
|
||||
and/or modify it under the licensed terms.
|
||||
__doc__ = wx.lib.colourchooser.pycolourbox.__doc__
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
"""
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
class PyColourBox(wxPanel):
|
||||
"""A Colour Selection Box
|
||||
|
||||
The Colour selection box implements button like behavior but contains
|
||||
a solid-filled, coloured sub-box. Placing the colour in a sub-box allows
|
||||
for filling in the main panel's background for a high-lighting effect.
|
||||
"""
|
||||
def __init__(self, parent, id, colour=(0, 0, 0), size=(25, 20)):
|
||||
"""Creates a new colour box instance and initializes the colour
|
||||
content."""
|
||||
wxPanel.__init__(self, parent, id,
|
||||
size=wxSize(size[0], size[1]))
|
||||
|
||||
self.colour_box = wxPanel(self, -1, style=wxSIMPLE_BORDER)
|
||||
|
||||
sizer = wxGridSizer(1, 1)
|
||||
sizer.Add(self.colour_box, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL)
|
||||
sizer.SetItemMinSize(self.colour_box, size[0] - 5, size[1] - 5)
|
||||
self.SetAutoLayout(True)
|
||||
self.SetSizer(sizer)
|
||||
self.Layout()
|
||||
|
||||
self.real_bg = self.GetBackgroundColour()
|
||||
self.SetColourTuple(colour)
|
||||
|
||||
def GetColourBox(self):
|
||||
"""Returns a reference to the internal box object containing the
|
||||
color. This function is useful for setting up event handlers for
|
||||
the box."""
|
||||
return self.colour_box
|
||||
|
||||
def GetColour(self):
|
||||
"""Returns a wxColour object indicating the box's current colour."""
|
||||
return self.colour_box.GetBackgroundColour()
|
||||
|
||||
def SetColour(self, colour):
|
||||
"""Accepts a wxColour object and sets the box's current color."""
|
||||
self.colour_box.SetBackgroundColour(colour)
|
||||
self.colour_box.Refresh()
|
||||
|
||||
def SetColourTuple(self, colour):
|
||||
"""Sets the box's current couple to the given tuple."""
|
||||
self.colour = colour
|
||||
self.colour_box.SetBackgroundColour(wxColour(*self.colour))
|
||||
|
||||
def Update(self):
|
||||
wxPanel.Update(self)
|
||||
self.colour_box.Update()
|
||||
|
||||
def SetHighlight(self, val):
|
||||
"""Accepts a boolean 'val' toggling the box's highlighting."""
|
||||
# XXX This code has been disabled for now until I can figure out
|
||||
# how to get this to work reliably across all platforms.
|
||||
if val:
|
||||
#A wxColourPtr is returned in windows, making this difficult
|
||||
red =(self.real_bg.Red() - 45) % 255
|
||||
green =(self.real_bg.Green() - 45) % 255
|
||||
blue =(self.real_bg.Blue() - 45) % 255
|
||||
new_colour = wxColour(red, green, blue)
|
||||
self.SetBackgroundColour(new_colour)
|
||||
else:
|
||||
self.SetBackgroundColour(self.real_bg)
|
||||
self.Refresh()
|
||||
PyColourBox = wx.lib.colourchooser.pycolourbox.PyColourBox
|
||||
|
||||
@@ -1,385 +1,10 @@
|
||||
"""
|
||||
wxPyColourChooser
|
||||
Copyright (C) 2002 Michael Gilfix <mgilfix@eecs.tufts.edu>
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
This file is part of wxPyColourChooser.
|
||||
import wx.lib.colourchooser.pycolourchooser
|
||||
|
||||
This version of wxPyColourChooser is open source; you can redistribute it
|
||||
and/or modify it under the licensed terms.
|
||||
__doc__ = wx.lib.colourchooser.pycolourchooser.__doc__
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
"""
|
||||
|
||||
import pycolourbox
|
||||
import pypalette
|
||||
import pycolourslider
|
||||
import colorsys
|
||||
from intl import _
|
||||
from wxPython.wx import *
|
||||
|
||||
class wxPyColourChooser(wxPanel):
|
||||
"""A Pure-Python implementation of the colour chooser dialog.
|
||||
|
||||
The PyColourChooser is a pure python implementation of the colour
|
||||
chooser dialog. It's useful for embedding the colour choosing functionality
|
||||
inside other widgets, when the pop-up dialog is undesirable. It can also
|
||||
be used as a drop-in replacement on the GTK platform, as the native
|
||||
dialog is kind of ugly.
|
||||
"""
|
||||
|
||||
colour_names = [
|
||||
'ORANGE',
|
||||
'GOLDENROD',
|
||||
'WHEAT',
|
||||
'SPRING GREEN',
|
||||
'SKY BLUE',
|
||||
'SLATE BLUE',
|
||||
'MEDIUM VIOLET RED',
|
||||
'PURPLE',
|
||||
|
||||
'RED',
|
||||
'YELLOW',
|
||||
'MEDIUM SPRING GREEN',
|
||||
'PALE GREEN',
|
||||
'CYAN',
|
||||
'LIGHT STEEL BLUE',
|
||||
'ORCHID',
|
||||
'LIGHT MAGENTA',
|
||||
|
||||
'BROWN',
|
||||
'YELLOW',
|
||||
'GREEN',
|
||||
'CADET BLUE',
|
||||
'MEDIUM BLUE',
|
||||
'MAGENTA',
|
||||
'MAROON',
|
||||
'ORANGE RED',
|
||||
|
||||
'FIREBRICK',
|
||||
'CORAL',
|
||||
'FOREST GREEN',
|
||||
'AQUAMARINE',
|
||||
'BLUE',
|
||||
'NAVY',
|
||||
'THISTLE',
|
||||
'MEDIUM VIOLET RED',
|
||||
|
||||
'INDIAN RED',
|
||||
'GOLD',
|
||||
'MEDIUM SEA GREEN',
|
||||
'MEDIUM BLUE',
|
||||
'MIDNIGHT BLUE',
|
||||
'GREY',
|
||||
'PURPLE',
|
||||
'KHAKI',
|
||||
|
||||
'BLACK',
|
||||
'MEDIUM FOREST GREEN',
|
||||
'KHAKI',
|
||||
'DARK GREY',
|
||||
'SEA GREEN',
|
||||
'LIGHT GREY',
|
||||
'MEDIUM SLATE BLUE',
|
||||
'WHITE',
|
||||
]
|
||||
|
||||
# Generate the custom colours. These colours are shared across
|
||||
# all instances of the colour chooser
|
||||
NO_CUSTOM_COLOURS = 16
|
||||
custom_colours = [ (wxColour(255, 255, 255),
|
||||
pycolourslider.PyColourSlider.HEIGHT / 2)
|
||||
] * NO_CUSTOM_COLOURS
|
||||
last_custom = 0
|
||||
|
||||
idADD_CUSTOM = wxNewId()
|
||||
idSCROLL = wxNewId()
|
||||
|
||||
def __init__(self, parent, id):
|
||||
"""Creates an instance of the colour chooser. Note that it is best to
|
||||
accept the given size of the colour chooser as it is currently not
|
||||
resizeable."""
|
||||
wxPanel.__init__(self, parent, id)
|
||||
|
||||
self.basic_label = wxStaticText(self, -1, _("Basic Colours:"))
|
||||
self.custom_label = wxStaticText(self, -1, _("Custom Colours:"))
|
||||
self.add_button = wxButton(self, self.idADD_CUSTOM, _("Add to Custom Colours"))
|
||||
|
||||
EVT_BUTTON(self, self.idADD_CUSTOM, self.onAddCustom)
|
||||
|
||||
# Since we're going to be constructing widgets that require some serious
|
||||
# computation, let's process any events (like redraws) right now
|
||||
wxYield()
|
||||
|
||||
# Create the basic colours palette
|
||||
self.colour_boxs = [ ]
|
||||
colour_grid = wxGridSizer(6, 8)
|
||||
for name in self.colour_names:
|
||||
new_id = wxNewId()
|
||||
box = pycolourbox.PyColourBox(self, new_id)
|
||||
EVT_LEFT_DOWN(box.GetColourBox(), lambda x, b=box: self.onBasicClick(x, b))
|
||||
self.colour_boxs.append(box)
|
||||
colour_grid.Add(box, 0, wxEXPAND)
|
||||
|
||||
# Create the custom colours palette
|
||||
self.custom_boxs = [ ]
|
||||
custom_grid = wxGridSizer(2, 8)
|
||||
for wxcolour, slidepos in self.custom_colours:
|
||||
new_id = wxNewId()
|
||||
custom = pycolourbox.PyColourBox(self, new_id)
|
||||
EVT_LEFT_DOWN(custom.GetColourBox(), lambda x, b=custom: self.onCustomClick(x, b))
|
||||
custom.SetColour(wxcolour)
|
||||
custom_grid.Add(custom, 0, wxEXPAND)
|
||||
self.custom_boxs.append(custom)
|
||||
|
||||
csizer = wxBoxSizer(wxVERTICAL)
|
||||
csizer.Add(1, 25)
|
||||
csizer.Add(self.basic_label, 0, wxEXPAND)
|
||||
csizer.Add(1, 5)
|
||||
csizer.Add(colour_grid, 0, wxEXPAND)
|
||||
csizer.Add(1, 25)
|
||||
csizer.Add(self.custom_label, 0, wxEXPAND)
|
||||
csizer.Add(1, 5)
|
||||
csizer.Add(custom_grid, 0, wxEXPAND)
|
||||
csizer.Add(1, 5)
|
||||
csizer.Add(self.add_button, 0, wxEXPAND)
|
||||
|
||||
self.palette = pypalette.PyPalette(self, -1)
|
||||
self.colour_slider = pycolourslider.PyColourSlider(self, -1)
|
||||
self.slider = wxSlider(self, self.idSCROLL, 86, 0, self.colour_slider.HEIGHT - 1,
|
||||
style=wxSL_VERTICAL, size=wxSize(15, self.colour_slider.HEIGHT))
|
||||
EVT_COMMAND_SCROLL(self, self.idSCROLL, self.onScroll)
|
||||
psizer = wxBoxSizer(wxHORIZONTAL)
|
||||
psizer.Add(self.palette, 0, 0)
|
||||
psizer.Add(10, 1)
|
||||
psizer.Add(self.colour_slider, 0, wxALIGN_CENTER_VERTICAL)
|
||||
psizer.Add(self.slider, 0, wxALIGN_CENTER_VERTICAL)
|
||||
|
||||
# Register mouse events for dragging across the palette
|
||||
EVT_LEFT_DOWN(self.palette, self.onPaletteDown)
|
||||
EVT_LEFT_UP(self.palette, self.onPaletteUp)
|
||||
EVT_MOTION(self.palette, self.onPaletteMotion)
|
||||
self.mouse_down = False
|
||||
|
||||
self.solid = pycolourbox.PyColourBox(self, -1, size=wxSize(75, 50))
|
||||
slabel = wxStaticText(self, -1, _("Solid Colour"))
|
||||
ssizer = wxBoxSizer(wxVERTICAL)
|
||||
ssizer.Add(self.solid, 0, 0)
|
||||
ssizer.Add(1, 2)
|
||||
ssizer.Add(slabel, 0, wxALIGN_CENTER_HORIZONTAL)
|
||||
|
||||
hlabel = wxStaticText(self, -1, _("H:"))
|
||||
self.hentry = wxTextCtrl(self, -1)
|
||||
self.hentry.SetSize((40, -1))
|
||||
slabel = wxStaticText(self, -1, _("S:"))
|
||||
self.sentry = wxTextCtrl(self, -1)
|
||||
self.sentry.SetSize((40, -1))
|
||||
vlabel = wxStaticText(self, -1, _("V:"))
|
||||
self.ventry = wxTextCtrl(self, -1)
|
||||
self.ventry.SetSize((40, -1))
|
||||
hsvgrid = wxFlexGridSizer(1, 6, 2, 2)
|
||||
hsvgrid.AddMany ([
|
||||
(hlabel, 0, wxALIGN_CENTER_VERTICAL), (self.hentry, 0, 0),
|
||||
(slabel, 0, wxALIGN_CENTER_VERTICAL), (self.sentry, 0, 0),
|
||||
(vlabel, 0, wxALIGN_CENTER_VERTICAL), (self.ventry, 0, 0),
|
||||
])
|
||||
|
||||
rlabel = wxStaticText(self, -1, _("R:"))
|
||||
self.rentry = wxTextCtrl(self, -1)
|
||||
self.rentry.SetSize((40, -1))
|
||||
glabel = wxStaticText(self, -1, _("G:"))
|
||||
self.gentry = wxTextCtrl(self, -1)
|
||||
self.gentry.SetSize((40, -1))
|
||||
blabel = wxStaticText(self, -1, _("B:"))
|
||||
self.bentry = wxTextCtrl(self, -1)
|
||||
self.bentry.SetSize((40, -1))
|
||||
lgrid = wxFlexGridSizer(1, 6, 2, 2)
|
||||
lgrid.AddMany([
|
||||
(rlabel, 0, wxALIGN_CENTER_VERTICAL), (self.rentry, 0, 0),
|
||||
(glabel, 0, wxALIGN_CENTER_VERTICAL), (self.gentry, 0, 0),
|
||||
(blabel, 0, wxALIGN_CENTER_VERTICAL), (self.bentry, 0, 0),
|
||||
])
|
||||
|
||||
gsizer = wxGridSizer(2, 1)
|
||||
gsizer.SetVGap (10)
|
||||
gsizer.SetHGap (2)
|
||||
gsizer.Add(hsvgrid, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL)
|
||||
gsizer.Add(lgrid, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL)
|
||||
|
||||
hsizer = wxBoxSizer(wxHORIZONTAL)
|
||||
hsizer.Add(ssizer, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL)
|
||||
hsizer.Add(gsizer, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL)
|
||||
|
||||
vsizer = wxBoxSizer(wxVERTICAL)
|
||||
vsizer.Add(1, 5)
|
||||
vsizer.Add(psizer, 0, 0)
|
||||
vsizer.Add(1, 15)
|
||||
vsizer.Add(hsizer, 0, wxEXPAND)
|
||||
|
||||
sizer = wxBoxSizer(wxHORIZONTAL)
|
||||
sizer.Add(5, 1)
|
||||
sizer.Add(csizer, 0, wxEXPAND)
|
||||
sizer.Add(10, 1)
|
||||
sizer.Add(vsizer, 0, wxEXPAND)
|
||||
self.SetAutoLayout(True)
|
||||
self.SetSizer(sizer)
|
||||
sizer.Fit(self)
|
||||
|
||||
self.InitColours()
|
||||
self.UpdateColour(self.solid.GetColour())
|
||||
|
||||
def InitColours(self):
|
||||
"""Initializes the pre-set palette colours."""
|
||||
for i in range(len(self.colour_names)):
|
||||
colour = wxTheColourDatabase.FindColour(self.colour_names[i])
|
||||
self.colour_boxs[i].SetColourTuple((colour.Red(),
|
||||
colour.Green(),
|
||||
colour.Blue()))
|
||||
|
||||
def onBasicClick(self, event, box):
|
||||
"""Highlights the selected colour box and updates the solid colour
|
||||
display and colour slider to reflect the choice."""
|
||||
if hasattr(self, '_old_custom_highlight'):
|
||||
self._old_custom_highlight.SetHighlight(False)
|
||||
if hasattr(self, '_old_colour_highlight'):
|
||||
self._old_colour_highlight.SetHighlight(False)
|
||||
box.SetHighlight(True)
|
||||
self._old_colour_highlight = box
|
||||
self.UpdateColour(box.GetColour())
|
||||
|
||||
def onCustomClick(self, event, box):
|
||||
"""Highlights the selected custom colour box and updates the solid
|
||||
colour display and colour slider to reflect the choice."""
|
||||
if hasattr(self, '_old_colour_highlight'):
|
||||
self._old_colour_highlight.SetHighlight(False)
|
||||
if hasattr(self, '_old_custom_highlight'):
|
||||
self._old_custom_highlight.SetHighlight(False)
|
||||
box.SetHighlight(True)
|
||||
self._old_custom_highlight = box
|
||||
|
||||
# Update the colour panel and then the slider accordingly
|
||||
box_index = self.custom_boxs.index(box)
|
||||
base_colour, slidepos = self.custom_colours[box_index]
|
||||
self.UpdateColour(box.GetColour())
|
||||
self.slider.SetValue(slidepos)
|
||||
|
||||
def onAddCustom(self, event):
|
||||
"""Adds a custom colour to the custom colour box set. Boxes are
|
||||
chosen in a round-robin fashion, eventually overwriting previously
|
||||
added colours."""
|
||||
# Store the colour and slider position so we can restore the
|
||||
# custom colours just as they were
|
||||
self.setCustomColour(self.last_custom,
|
||||
self.solid.GetColour(),
|
||||
self.colour_slider.GetBaseColour(),
|
||||
self.slider.GetValue())
|
||||
self.last_custom = (self.last_custom + 1) % self.NO_CUSTOM_COLOURS
|
||||
|
||||
def setCustomColour (self, index, true_colour, base_colour, slidepos):
|
||||
"""Sets the custom colour at the given index. true_colour is wxColour
|
||||
object containing the actual rgb value of the custom colour.
|
||||
base_colour (wxColour) and slidepos (int) are used to configure the
|
||||
colour slider and set everything to its original position."""
|
||||
self.custom_boxs[index].SetColour(true_colour)
|
||||
self.custom_colours[index] = (base_colour, slidepos)
|
||||
|
||||
def UpdateColour(self, colour):
|
||||
"""Performs necessary updates for when the colour selection has
|
||||
changed."""
|
||||
# Reset the palette to erase any highlighting
|
||||
self.palette.ReDraw()
|
||||
|
||||
# Set the color info
|
||||
self.solid.SetColour(colour)
|
||||
self.colour_slider.SetBaseColour(colour)
|
||||
self.colour_slider.ReDraw()
|
||||
self.slider.SetValue(0)
|
||||
self.UpdateEntries(colour)
|
||||
|
||||
def UpdateEntries(self, colour):
|
||||
"""Updates the color levels to display the new values."""
|
||||
# Temporary bindings
|
||||
r = colour.Red()
|
||||
g = colour.Green()
|
||||
b = colour.Blue()
|
||||
|
||||
# Update the RGB entries
|
||||
self.rentry.SetValue(str(r))
|
||||
self.gentry.SetValue(str(g))
|
||||
self.bentry.SetValue(str(b))
|
||||
|
||||
# Convert to HSV
|
||||
h,s,v = colorsys.rgb_to_hsv(r / 255.0, g / 255.0, b / 255.0)
|
||||
self.hentry.SetValue("%.2f" % (h))
|
||||
self.sentry.SetValue("%.2f" % (s))
|
||||
self.ventry.SetValue("%.2f" % (v))
|
||||
|
||||
def onPaletteDown(self, event):
|
||||
"""Stores state that the mouse has been pressed and updates
|
||||
the selected colour values."""
|
||||
self.mouse_down = True
|
||||
self.palette.ReDraw()
|
||||
self.doPaletteClick(event.m_x, event.m_y)
|
||||
|
||||
def onPaletteUp(self, event):
|
||||
"""Stores state that the mouse is no longer depressed."""
|
||||
self.mouse_down = False
|
||||
|
||||
def onPaletteMotion(self, event):
|
||||
"""Updates the colour values during mouse motion while the
|
||||
mouse button is depressed."""
|
||||
if self.mouse_down:
|
||||
self.doPaletteClick(event.m_x, event.m_y)
|
||||
|
||||
def doPaletteClick(self, m_x, m_y):
|
||||
"""Updates the colour values based on the mouse location
|
||||
over the palette."""
|
||||
# Get the colour value and update
|
||||
colour = self.palette.GetValue(m_x, m_y)
|
||||
self.UpdateColour(colour)
|
||||
|
||||
# Highlight a fresh selected area
|
||||
self.palette.ReDraw()
|
||||
self.palette.HighlightPoint(m_x, m_y)
|
||||
|
||||
# Force an onscreen update
|
||||
self.solid.Update()
|
||||
self.colour_slider.Refresh()
|
||||
|
||||
def onScroll(self, event):
|
||||
"""Updates the solid colour display to reflect the changing slider."""
|
||||
value = self.slider.GetValue()
|
||||
colour = self.colour_slider.GetValue(value)
|
||||
self.solid.SetColour(colour)
|
||||
self.UpdateEntries(colour)
|
||||
|
||||
def SetValue(self, colour):
|
||||
"""Updates the colour chooser to reflect the given wxColour."""
|
||||
self.UpdateColour(colour)
|
||||
|
||||
def GetValue(self):
|
||||
"""Returns a wxColour object indicating the current colour choice."""
|
||||
return self.solid.GetColour()
|
||||
|
||||
def main():
|
||||
"""Simple test display."""
|
||||
class App(wxApp):
|
||||
def OnInit(self):
|
||||
frame = wxFrame(NULL, -1, 'PyColourChooser Test')
|
||||
|
||||
chooser = wxPyColourChooser(frame, -1)
|
||||
sizer = wxBoxSizer(wxVERTICAL)
|
||||
sizer.Add(chooser, 0, 0)
|
||||
frame.SetAutoLayout(True)
|
||||
frame.SetSizer(sizer)
|
||||
sizer.Fit(frame)
|
||||
|
||||
frame.Show(True)
|
||||
self.SetTopWindow(frame)
|
||||
return True
|
||||
app = App()
|
||||
app.MainLoop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main = wx.lib.colourchooser.pycolourchooser.main
|
||||
wxPyColourChooser = wx.lib.colourchooser.pycolourchooser.wxPyColourChooser
|
||||
|
||||
@@ -1,82 +1,9 @@
|
||||
"""
|
||||
wxPyColourChooser
|
||||
Copyright (C) 2002 Michael Gilfix
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
This file is part of wxPyColourChooser.
|
||||
import wx.lib.colourchooser.pycolourslider
|
||||
|
||||
You should have received a file COPYING containing license terms
|
||||
along with this program; if not, write to Michael Gilfix
|
||||
(mgilfix@eecs.tufts.edu) for a copy.
|
||||
__doc__ = wx.lib.colourchooser.pycolourslider.__doc__
|
||||
|
||||
This version of wxPyColourChooser is open source; you can redistribute it and/or
|
||||
modify it under the terms listed in the file COPYING.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
"""
|
||||
|
||||
import canvas
|
||||
import colorsys
|
||||
from wxPython.wx import *
|
||||
|
||||
class PyColourSlider(canvas.Canvas):
|
||||
"""A Pure-Python Colour Slider
|
||||
|
||||
The colour slider displays transitions from value 0 to value 1 in
|
||||
HSV, allowing the user to select a colour within the transition
|
||||
spectrum.
|
||||
|
||||
This class is best accompanying by a wxSlider that allows the user
|
||||
to select a particular colour shade.
|
||||
"""
|
||||
|
||||
HEIGHT = 172
|
||||
WIDTH = 12
|
||||
|
||||
def __init__(self, parent, id, colour=None):
|
||||
"""Creates a blank slider instance. A colour must be set before the
|
||||
slider will be filled in."""
|
||||
# Set the base colour first since our base class calls the buffer
|
||||
# drawing function
|
||||
self.SetBaseColour(colour)
|
||||
|
||||
canvas.Canvas.__init__(self, parent, id,
|
||||
size=wxSize(self.WIDTH, self.HEIGHT))
|
||||
|
||||
def SetBaseColour(self, colour):
|
||||
"""Sets the base, or target colour, to use as the central colour
|
||||
when calculating colour transitions."""
|
||||
self.base_colour = colour
|
||||
|
||||
def GetBaseColour(self):
|
||||
"""Return the current colour used as a colour base for filling out
|
||||
the slider."""
|
||||
return self.base_colour
|
||||
|
||||
def GetValue(self, pos):
|
||||
"""Returns the colour value for a position on the slider. The position
|
||||
must be within the valid height of the slider, or results can be
|
||||
unpredictable."""
|
||||
return self.buffer.GetPixel(0, pos)
|
||||
|
||||
def DrawBuffer(self):
|
||||
"""Actual implementation of the widget's drawing. We simply draw
|
||||
from value 0.0 to value 1.0 in HSV."""
|
||||
if self.base_colour is None:
|
||||
return
|
||||
|
||||
target_red = self.base_colour.Red()
|
||||
target_green = self.base_colour.Green()
|
||||
target_blue = self.base_colour.Blue()
|
||||
|
||||
h,s,v = colorsys.rgb_to_hsv(target_red / 255.0, target_green / 255.0,
|
||||
target_blue / 255.0)
|
||||
v = 1.0
|
||||
vstep = 1.0 / self.HEIGHT
|
||||
for y_pos in range(0, self.HEIGHT):
|
||||
r,g,b = [c * 255.0 for c in colorsys.hsv_to_rgb(h,s,v)]
|
||||
colour = wxColour(int(r), int(g), int(b))
|
||||
self.buffer.SetPen(wxPen(colour, 1, wxSOLID))
|
||||
self.buffer.DrawRectangle(0, y_pos, 15, 1)
|
||||
v = v - vstep
|
||||
PyColourSlider = wx.lib.colourchooser.pycolourslider.PyColourSlider
|
||||
|
||||
@@ -1,214 +1,12 @@
|
||||
"""
|
||||
wxPyColourChooser
|
||||
Copyright (C) 2002 Michael Gilfix
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
This file is part of wxPyColourChooser.
|
||||
import wx.lib.colourchooser.pypalette
|
||||
|
||||
You should have received a file COPYING containing license terms
|
||||
along with this program; if not, write to Michael Gilfix
|
||||
(mgilfix@eecs.tufts.edu) for a copy.
|
||||
__doc__ = wx.lib.colourchooser.pypalette.__doc__
|
||||
|
||||
This version of wxPyColourChooser is open source; you can redistribute it and/or
|
||||
modify it under the terms listed in the file COPYING.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
"""
|
||||
|
||||
import canvas
|
||||
import colorsys
|
||||
import cStringIO, zlib
|
||||
from wxPython.wx import *
|
||||
|
||||
# Bitmap functions generated from img2py
|
||||
def getData():
|
||||
return zlib.decompress(
|
||||
'x\xdaUV{<\x93{\x18\x1f:\xe3\xc8Xj\xa3a\xe6R\x9db*B.3\x93\xeb\xe9\x1c\xb7\
|
||||
\xe4\xb2r9\x8c\xe5\x12e\xc9\xda\x88\x9a\xb4\x83\xcet\xa1N\xae\x85\xe3\xb2\
|
||||
\xe1,\xb7\xc2\x8a\xd0\x99D\xc8t\xdc\x86r\x1f\x96\\\x16k:\xebC\xe78\xef\xe7\
|
||||
\xf3\xbe\xbf?\xde\xf7\xf9=\xcf\xf3\xbd<\xbf\xf7WgG;\x90,L\x16\x00\x00\x80\
|
||||
\x1c\xec\xad]\xc5+[|7\xc8\x00\xc5O\xccv\xddE\xf1\xb2\x8d`\xe5\xe0&#\xbebdpT\
|
||||
\x00`\xbb\x94\x835\xc6\x8d\x989\x97\xd5N\xca\xb3\x05\xdb\xdc\x9c[\xd0\xf4\
|
||||
\xd2\xe6\x7f\xb9\xd3\xaa\xe9\x85\xd3\xd5\xd9\xbe]\xa7\xf2\xf1\xf2\x1b\xef\
|
||||
\x1b\xe6\xb7\tF\xca\t`\xef\xf9\xb5\xa2\x87\xe1\x8d\xd3\x8ft\xef\x06z\xa5\x8f\
|
||||
\xd7\x04\xf6y\xaf\xfbT\x0e\xad~\xce\xf1E\xa7\xc7\xe9\r\x8c\xab\xb7\xad_\xe4/\
|
||||
r\xf9\xb1\xbe\x97[\xb3\x88\xfcx\xd6\\\xdf\x15\x88\xca\xcc\xc0\xb0\x10\xef\
|
||||
\x81B\x9a\xf3Xzq\x1c\xb9\xa2E\xb2\xd3v\x80\xfd90@Ju[\xc3H\xe0\xea\xca\xdc\
|
||||
\xfbr\xde\xbb\xf0U\xcd\xf4\xd7\xfe\xabc\xbaN\xc2\xe3\xcc>T\xa6)(\xaa\x9f\xe7\
|
||||
\n\xaa\xe9\x00gcz\xa8Y\xb4\x8a\xc4\x0fr\x9a\x89\xe4\x9eJ\xe7\xa6z\xaf\xf7\
|
||||
\xcc\xc4\x11YI\xbfx\n@y\x04\x01\x00\nL\xd8\xb3#\xfa\x0e\'\xb7\xc1n\x98\xd3h\
|
||||
\xd5\x14\xd50\xeaA\x0bd\x8a\x16\x07\x96\xa0#\x13/!\xbf\xf1\xe5\x94)\xd5\xc3\
|
||||
\xfe\x9ej\x0c<\x8b<8\x89\x98\xcb\x9ek\x18X^\xf9\xa9\xffJ\r/\xd93\xda\xc2\xfd\
|
||||
\xfa\tt\xb9\xa3\x07\xe8u1\xc5\\O\xe9w\x94\xd6\n\x1e\xe8\xb1Q#@Z\xe2\x10\xd1\
|
||||
\xcb\xc7\x17\xbd\x9e\x94\x14~\xf9\xd6\xe0\xb1\xb3\xe8\xd8\x07E\xe4\xa6F\x87\
|
||||
\xbe6:K\xf8\xfanrn\x8b\xa4=e\xb3\x98\xf3\xc8\xde|\x0b\xf0\xb5\xb7\xfb\x0e\
|
||||
\xf5\x83:m3\xcbs\xfe*T\xc7\x89\xe1\x1a\xc8X=\xbfZ\xc4#\xd0\xee\x93d\xb6lL\
|
||||
\x15\xe4\xe3\xf7\xd8w\r\x1eK\xe0\xce\xad-`\x19g\xe6\x1f\xc8}\xcc}\xee\xecF\
|
||||
\x19_\xa6PUq\xee\xa2\x80\x1d\xbc\xf5\x14g\xe04\xa4\xc0EZB\xe6[\x1al\xb2!,\
|
||||
\xac\xa4\xf3O\x83\xa5y\x96(\xa7\xdd\xc61\x1aX\xa4X\xa8\x96\xdf2\x93\x067\x1c\
|
||||
\xf0\xed\x10\xaa\xe3\xe9\x96\xac\'l\x14\xb7\xc1\x0ed\x85\x96\xb1\x84m&d\x872\
|
||||
\x1f)\xe6wt\xd6\tV\xbe\xd4\xbe\xeaE\xcf\xaca\x92f4*Z\xce\xf8\xa0\xd9\x97\xa2\
|
||||
+\xcc\x90$\xbb\xac8\x0b\xf7\x93\xfa\xb6\xf2\x92\x9d\xa0M\xec\xc6\xaa<\x17\
|
||||
\xad\x8d\'\xc29\xd1uQjcU}\x8a\x1c\xbf\x9fg\x12\x9c\x1f\\4RZ8\xe2\xf5s@J\t\
|
||||
\xc5\rU\x92\xab\xf6B\xb3\x97\x8f^\xb8\xa5_\xd0\x98tQ\xa6KA\x12\x0c\x14gl\xc0\
|
||||
\x00\xe4f*=\xa3\x1ef\x0c\rU<\xb1sO\xaa\x0c\x82;\r)\xc8\x9c\xc0\x1f9CMch\xd4\
|
||||
\x9fn\xde/g\xc3C\xb0\xb0\x8cuS6\xf3;\x8d2q\x7fG7\x88;\xb4~9\xd5e\xa1}\x7f\
|
||||
\xd5\xa9Z\x88\x99\xaft+\xeca?\xa2qh\xaf\x8af\xbf\x82\xfe%\xac\xf6\nC_\xc6D\
|
||||
\xc6Ry\xb3\xb7N\x11"\xcfU\xbb@m\x86AY\na\xfa;\xa1\x93\x96\xf2i\xd0\x04S\x90~\
|
||||
\x1b\xb8z[1C\xde\x15w\xed\x0b\xd8\xd0\xbe\\\x19\x84\x84\xfe\x1bE\re\xda\x1az\
|
||||
6\xe6\xbe!+\x86V\xbd\xfb\xc3\xfb\xd9[\xff\xc0\x8c\xc5\nH<\x82U#2S\t\xc8\x1en\
|
||||
\xa7\xb5E\xde\x14W\xd2w\xe3\xf0\x18\x02\xa0\xf7\xad\xb6\xb2\x96\xfb\xbbH\x02\
|
||||
\x0b\x85P\xb7\xe4\x02!\xe6a\xc4d\xe8]\x17\xa4\x8bk2G\xaf\xa2p\\\x8e\xd0\x8e\
|
||||
\t\nf\xce\xf0\x10}O\xc1\x95\x9e\x80\xa6\x91\x8d\xae0\xcf\xa0\xc7\x97-h\xc3\
|
||||
\x1f\xb8"l\x14\xcaz9\xffYl\x15.l|\xc4\x94\xdd&\x9c\x9f\xf8\xb8f7\x08cA\x1e\
|
||||
\x85\x11,\xb0\xba\xf1\x87639\xfbJ\xef~e\xf3\xdbK\xd4\xe7\xec\xa5\x92\x17\xf7\
|
||||
\x1aO\xe3*\xd5\xf3\xcbA\xef\xf4\xa4[\x1d\xaf\xd7>M-\xf1\xb9@\xea\x96x\xd9(\
|
||||
\x06Z\xec+J\xed\xe5\xd45\x95\xe1\xba\xeb\xf4\xa4\xa3i\xbb\x82}\xd0\xf6Lh\xe8\
|
||||
E4;0\x9aPk\x0emo~G\x04\xb6&u\xb31\x80\xdao\x01\xf5P\x1d\xd8\x05\x92qmV\xf6R\
|
||||
\x17\x89\x1a-\xf4\x15P*\xf9?|\xcea\xa9\x8f\x99~6\x92^\xf8\x03s\x11;v\xe2D\
|
||||
\xc4^\x1c>Q\xbfY\xd0n\xbaE\xc0b=\x91\x0c=[U\x86\xfb\x06\xb52\x92\x19M1uz<\
|
||||
\xb1\xa4r~4\x83E\xe2y\x08\x0f\nQ\x84\xe8\xfa\xfa\xea\x13\x0e\xd4\x95\x91\\\
|
||||
\x7f\xf6)\x08`\xb6\x89\xc5\x95^\xff\xe2\\\x03X\xe4\xbe\x88x\x8f\xe34\xb7\xe6\
|
||||
\xbe\t\xf8\\w\x9c\xd3\x1a\xee\x98\xeeW\x92\xad\x83\x99\xb6B\xcb\x8f\xbdD\x86\
|
||||
\xc6\xe3\xab\x1b\xb2\xdf\x08:9\xbc\x9e\xf3\xf9h\xd1\xec\xd98\xc8%\x0b\x87c\
|
||||
\xb8\xbc\x0f\xad\x89\xca\xa5\x94J\xa9\x88j\x1ddo\x91u\x84\xf3\xcd\xea\xc5\
|
||||
\xf6\x99\xab\xe0\xd7-\x92\xff:\xe6)T\x07\x0bd\x1b\xa9+9\xa4\x86\xec2F\xa1\
|
||||
\xa6\x7f\xbc\xd1C\x9e\xf4=D\x12\xa2\x07\x94\t\xb1\xe8\xb5\xfb\x94\x14q~R\xa1\
|
||||
\xe6Y<\xad\xcb\x94j\xbc\xb3##\x0f\xd0 \xbf\xc9\x01\xf8\xad\xb8V\x82sdO7\xbd\
|
||||
\xbe\xd5Bd\x9f\xc0m#\xd4h<j\xf5*\x84\x86VKt\x0c4\xc1QkD\xbd\xee\xd0\xdc\xcc\
|
||||
\xad\xc5bN\'\x8f\x1b\x92\x95\x8e\xdf,\xb1\xfa\xe0\xc7f\xd5\xc7\x95\xd1&\xe06\
|
||||
\xcb\xb4t/\xa0KTj\xd0\xe2\xfb\xd2\xc3!\xf1\xcb?\r7_\x14K$xs(\xfa}~\xe2\xd9\
|
||||
\xe5gP\xd4\xfaq\x97\xb1\x0b\xd2]\xe5|\x19o2(Vm\xfe>\xe5\x13jZ\xdan\x98\xf3\
|
||||
\xe4:\x1ep\x93B\xec6?\tO\x0eaB\x99>\xc6Zkr\xcf!\x1b\x84|\xb5\xdb\x8b*O\xb4\
|
||||
\xe7\x14Ko\xa0\x93\xecmq\xd7\xf0\xbd\x12\x07\x8d\x95\xd7\x7f\xf5&\xb8bmj\xda\
|
||||
/&`>e\xeb\xfc\x14a\x19\x94S\x7f\xd2\xb5:\x8c\x04\x8f\x91\x03\xc2Q\x0e\xff{\
|
||||
\x93\xc7\xea\xd6\xbb\x1b\x0e7\xe7E\xa6\xae\x9d\xc6\x85%\x9e\xfbnc\xe3\xff\
|
||||
\xd4\xa2`9\x13\xa3\x97\x9e\xa4\x9b\x06\xa5\x9f\xec\x9f\x1a\x0c\xf0\xfe\xcd\
|
||||
\x021\x9b\x0cM\xc06\xfd u:\xe7:g\x02\xc1r\x926\x9b\x7f\xe2\xf9\xe3\xed\xf1qU\
|
||||
\xbe\xbf\xe8\x91\t\x0c0\xfb-\xe5%d\xef\x19v\x966\xec\xaaB\xe2`N[\x8c\xda\x98\
|
||||
\xf4\xb4\x83\x13\xcc\x8a\x83\x81\xa3\x91%\xdb\xad\xab\xff\x87\xe1\xba\xda\
|
||||
\xb5\xdb\xf0\x17\xfd\xf4F\x18KTNH\xf5J\xbc\x97\xdfB:\xa7\x96\xdf/k\x1c\xeaF_\
|
||||
\x8c\xfc\xdfap\x1e\x99\xae8\x94b\xa1t<\xb54.3c\xd9\xe8y=u9FM;D\xa6\xc0\xea~\
|
||||
\x0f"O\xee\x81\xdc\xa3\xb2\x1a\xa0\xa7\x02\xb9\x7f>\xfdg\x974\xc8\x0b\xbaU6P\
|
||||
\xe7\x14\xd8\xd5 \x90\xbc\x0b\xf0\xb7\xc4\x7f\x08\xfaPl\xf5\xa7\x96\xac\xc2\
|
||||
\x0f*\x05\xf3\x83\xe8\xce\xa7\xc4\x8c\xdbX\xa4U\x9b\xeeW\xe9\xf1\xbf\xa4Q#\
|
||||
\xcbDQ\x18h\x02\xca\'\xca\xee),1"\x8d\xfb<\x15\xafl\xb8\xb3z\x18\x18\xaf\xb6\
|
||||
I$\xa2\xbc\xe5\xe5J\xbe\x00\r\x08&<\x0eK\x98\x0e.[\xd1\xea`\xa9\xe2\x96\xae-\
|
||||
d9%\xc0 \x85\xc5{c_\x03x\xaf\x8f\x98P= 0\x8e\xff\xaa\xf5>7\xfeO\x7f\x1b\xcbm\
|
||||
\xb1W\xa7\t\x9b\xe1w\x02\xc5\xb5\x9fM/\x8d\xab\xe4u\r\x06\xa0\xd6\xc9\xb5\
|
||||
\xf2\xb7J\x01\xda<\r\x9f\xd1\x06\x03\xea>\xab\x9d\xe6\xde\xb4\xbb\xb1\xc6\
|
||||
\xa3EP\x1e\x17\x16\xf2\x1c\xa7\x823\xa7\xcc~\xd1\xdb\xb2\xcb\xbd\xe1\xdb\xf0\
|
||||
W(,\xe9XD)3I\xc4\x15z_\x98\x8b\xb2v\xdb\x13\xd0\xb8\xf3U"\xb713\xaf\xa0\x1dC\
|
||||
j\x0b\xb0\xf9\xfd\xef\x0ex\xd7l\xa5\xc1\xf7Z\xd2\x12\x1f\xbe\r\x87Shjm\xe3\
|
||||
\x1c\x92\xbc\xc7^\x9e\xe5\x84\xf9\xb8\xcb\x88+\x12\xb4M\xee\xb0\xbb\xcd\xc4\
|
||||
\x9c\xc7V\x1f\xde\x1b\x02\xb0!\x0c\xbeY&\xf6\xe9\xdd[u:0\x0f)(\xc5g\n\xd5\
|
||||
\xb6\xcc\xf0st\n\x113\x81Q\xcc\xef\xaa\x1b\x9a#\xad|\x12\x98\xd8\xf7"\xa2\
|
||||
\xa2\xd7\xdbwz+\x08\xb8\x0c\x9d&mZ\\-<\xbaG6j\x9cy@\x8ah\x10@\x8e\xd9\x12\
|
||||
\x9dK\x00\xf3\xabdo\x1f\x8b\x80\x9c\xf7i^#\xc1X\x7f-\x13R](\x17\xa0\x89p\x9c\
|
||||
L\x1a\xc5\x9c\x88U\xde\xc7\x9d\xd04\x04\x8en\x11\x8112\xbd\xf6J\x96wP\x06\
|
||||
\xcd\xa9x\x08\xf7\xc3\xfc\x0e\xceE\xef\xf9j\xd50\x19~\x02~\xf7C\x13\xae\xd8\
|
||||
\xea\x8e\xc9\x8c\xafpU\xc8\x8d\xaa\xe5\x88Q\xfan\x93\xf7\xf0(\xb0\x93\xf5NH\
|
||||
\x1f\xae\xc5\xf8\xaa\x97F4\x19;\x19\xe4=\x89\xe0\xae\x15\xc9\xb6\xfe\xe2\xce\
|
||||
\x1e\xca\xe6\x1a\n<\t\xa9].x\x03\xfd\x1c\x86Fd?\xbd\x17|z\x03\xa8\xafX[N"|\
|
||||
\x16\xa3#\x0e\x92\xf0h{^+K\x04/!\x8f\xac\xf4\xe4\xbbH\xa9.\x85q\xdd\x93\xc7\
|
||||
\xbb`\x96\xbb\xb5\xefQ\xdc\x9ch+G\xf8\xbf\xf6b\xdc\xfbww\xcf\xc7\x85\xf7\n@\
|
||||
\x8d[\xdc\x1b\x8e\xd5\x85\x1c\xf0@JG\x08\xc9;\n\xfb\x9dX\xc5\x8e\t\\\xb3g#\
|
||||
\xa0\xa2\xb7`\n\x96\x116?\xda\x83\xea\xa1\x7f.Y\x9f\xcb\xda_\x8c\xe9\x01s\
|
||||
\x0f\xf6\x03\xb7:\xa0\xc6\x94\xaat\xc4\x96r\x1c\x12\x06\x1dZ\xf7\x10V\xd5\
|
||||
\x088\x02N\xc6\xcc\x05y\xd7\xc0T\x07,c\xea\xb2\xcf\xc7=>y\x87M_\x9a\x86\x12\
|
||||
\xa5\x92\x83\n_"\x84\xff\x8b7\x95\xfeu\x02\x9c\xf7\xe4\xfacQyo\xda\xbb\t\xed\
|
||||
\xdeS\xd3\xb7\x04/j\xdb\x96\xae\xec\xd3\x01\xb8P\x8ap\x8c7\x88\xc2\xa8\xfd\
|
||||
\x1d\xd5\xd1=\xab$*\x8c\x9dd\xacu\x07\xe3\xa6X\xed\x1d\xb9eHd@\x8f\xb7\xd4V\
|
||||
\xdc\x95\x0f\xa91\xba\xe3s?\n\x12\xf2\x97\xefh\xf4\x1d\x89\x04\xccC)\x8f\x83\
|
||||
\xbf\x84\xd5\xe0A\xb7\xccC\xf9\xc3fGA\x92\xe4\x12\x89\x03\x14bb\xdfe\xd9\x7f\
|
||||
\x0f\x86\xc6R\xf9wC\x114\xe0\xdd\xae9\xc9ef\x92\xb6\x12\x1eU\'ZW\xa2\xe9\xa7\
|
||||
4\x15\xfdb\nr\x17\xf1\xe15IkA\xe5\x12aM[&\x93T\x16\xa5\x92x\xf8\xc3\xd4\xca\
|
||||
\xd8[\x96]wPNO\t!5\xaf&\xfarlLC\xdd\x00\xdd\x8e\x13qc\xea&]nAb\x8b1>)9\x047\
|
||||
\xc5\x8e\x1a\xd5\x84\x8b\x7f\x8f\x01\x0e6\x8e\xd6eV~W\xff\x01[x\x1b=' )
|
||||
|
||||
def getBitmap():
|
||||
return wxBitmapFromImage(getImage())
|
||||
|
||||
def getImage():
|
||||
stream = cStringIO.StringIO(getData())
|
||||
return wxImageFromStream(stream)
|
||||
|
||||
class PyPalette(canvas.Canvas):
|
||||
"""The Pure-Python Palette
|
||||
|
||||
The PyPalette is a pure python implementation of a colour palette. The
|
||||
palette implementation here imitates the palette layout used by MS
|
||||
Windows and Adobe Photoshop.
|
||||
|
||||
The actual palette image has been embedded as an XPM for speed. The
|
||||
actual reverse-engineered drawing algorithm is provided in the
|
||||
GeneratePaletteBMP() method. The algorithm is tweakable by supplying
|
||||
the granularity factor to improve speed at the cost of display
|
||||
beauty. Since the generator isn't used in real time, no one will
|
||||
likely care :) But if you need it for some sort of unforeseen realtime
|
||||
application, it's there.
|
||||
"""
|
||||
|
||||
HORIZONTAL_STEP = 2
|
||||
VERTICAL_STEP = 4
|
||||
|
||||
def __init__(self, parent, id):
|
||||
"""Creates a palette object."""
|
||||
# Load the pre-generated palette XPM
|
||||
self.palette = getBitmap ()
|
||||
canvas.Canvas.__init__ (self, parent, id, size=wxSize(200, 192))
|
||||
|
||||
def GetValue(self, x, y):
|
||||
"""Returns a colour value at a specific x, y coordinate pair. This
|
||||
is useful for determining the colour found a specific mouse click
|
||||
in an external event handler."""
|
||||
return self.buffer.GetPixel(x, y)
|
||||
|
||||
def DrawBuffer(self):
|
||||
"""Draws the palette XPM into the memory buffer."""
|
||||
#self.GeneratePaletteBMP ("foo.bmp")
|
||||
self.buffer.DrawBitmap(self.palette, 0, 0, 0)
|
||||
|
||||
def HighlightPoint(self, x, y):
|
||||
"""Highlights an area of the palette with a little circle around
|
||||
the coordinate point"""
|
||||
colour = wxColour(0, 0, 0)
|
||||
self.buffer.SetPen(wxPen(colour, 1, wxSOLID))
|
||||
self.buffer.SetBrush(wxBrush(colour, wxTRANSPARENT))
|
||||
self.buffer.DrawCircle(x, y, 3)
|
||||
self.Refresh()
|
||||
|
||||
def GeneratePaletteBMP(self, file_name, granularity=1):
|
||||
"""The actual palette drawing algorithm.
|
||||
|
||||
This used to be 100% reverse engineered by looking at the
|
||||
values on the MS map, but has since been redone Correctly(tm)
|
||||
according to the HSV (hue, saturation, value) colour model by
|
||||
Charl P. Botha <http://cpbotha.net/>.
|
||||
|
||||
Speed is tweakable by changing the granularity factor, but
|
||||
that affects how nice the output looks (makes the vertical
|
||||
blocks bigger. This method was used to generate the embedded
|
||||
XPM data."""
|
||||
self.vertical_step = self.VERTICAL_STEP * granularity
|
||||
width, height = self.GetSize ()
|
||||
|
||||
# simply iterate over hue (horizontal) and saturation (vertical)
|
||||
value = 1.0
|
||||
for y in range(0, height, self.vertical_step):
|
||||
saturation = 1.0 - float(y) / float(height)
|
||||
for x in range(0, width, self.HORIZONTAL_STEP):
|
||||
hue = float(x) / float(width)
|
||||
r,g,b = colorsys.hsv_to_rgb(hue, saturation, value)
|
||||
colour = wxColour(int(r * 255.0), int(g * 255.0), int(b * 255.0))
|
||||
self.buffer.SetPen(wxPen(colour, 1, wxSOLID))
|
||||
self.buffer.SetBrush(wxBrush(colour, wxSOLID))
|
||||
self.buffer.DrawRectangle(x, y,
|
||||
self.HORIZONTAL_STEP,
|
||||
self.vertical_step)
|
||||
|
||||
# this code is now simpler (and works)
|
||||
bitmap = self.buffer.GetBitmap()
|
||||
image = wxImageFromBitmap(bitmap)
|
||||
image.SaveFile (file_name, wxBITMAP_TYPE_XPM)
|
||||
PyPalette = wx.lib.colourchooser.pypalette.PyPalette
|
||||
getBitmap = wx.lib.colourchooser.pypalette.getBitmap
|
||||
getData = wx.lib.colourchooser.pypalette.getData
|
||||
getImage = wx.lib.colourchooser.pypalette.getImage
|
||||
|
||||
@@ -1,669 +1,11 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.colourdb.py
|
||||
# Purpose: Adds a bunch of colour names and RGB values to the
|
||||
# colour database so they can be found by name
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 13-March-2001
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2001 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
"""
|
||||
Load color names/values from the rgb.txt file on my system...
|
||||
"""
|
||||
|
||||
|
||||
def getColourList():
|
||||
return [ x[0] for x in getColourInfoList() ]
|
||||
|
||||
|
||||
|
||||
def getColourInfoList():
|
||||
return [
|
||||
("SNOW", 255, 250, 250),
|
||||
("GHOST WHITE", 248, 248, 255),
|
||||
("GHOSTWHITE", 248, 248, 255),
|
||||
("WHITE SMOKE", 245, 245, 245),
|
||||
("WHITESMOKE", 245, 245, 245),
|
||||
("GAINSBORO", 220, 220, 220),
|
||||
("FLORAL WHITE", 255, 250, 240),
|
||||
("FLORALWHITE", 255, 250, 240),
|
||||
("OLD LACE", 253, 245, 230),
|
||||
("OLDLACE", 253, 245, 230),
|
||||
("LINEN", 250, 240, 230),
|
||||
("ANTIQUE WHITE", 250, 235, 215),
|
||||
("ANTIQUEWHITE", 250, 235, 215),
|
||||
("PAPAYA WHIP", 255, 239, 213),
|
||||
("PAPAYAWHIP", 255, 239, 213),
|
||||
("BLANCHED ALMOND", 255, 235, 205),
|
||||
("BLANCHEDALMOND", 255, 235, 205),
|
||||
("BISQUE", 255, 228, 196),
|
||||
("PEACH PUFF", 255, 218, 185),
|
||||
("PEACHPUFF", 255, 218, 185),
|
||||
("NAVAJO WHITE", 255, 222, 173),
|
||||
("NAVAJOWHITE", 255, 222, 173),
|
||||
("MOCCASIN", 255, 228, 181),
|
||||
("CORNSILK", 255, 248, 220),
|
||||
("IVORY", 255, 255, 240),
|
||||
("LEMON CHIFFON", 255, 250, 205),
|
||||
("LEMONCHIFFON", 255, 250, 205),
|
||||
("SEASHELL", 255, 245, 238),
|
||||
("HONEYDEW", 240, 255, 240),
|
||||
("MINT CREAM", 245, 255, 250),
|
||||
("MINTCREAM", 245, 255, 250),
|
||||
("AZURE", 240, 255, 255),
|
||||
("ALICE BLUE", 240, 248, 255),
|
||||
("ALICEBLUE", 240, 248, 255),
|
||||
("LAVENDER", 230, 230, 250),
|
||||
("LAVENDER BLUSH", 255, 240, 245),
|
||||
("LAVENDERBLUSH", 255, 240, 245),
|
||||
("MISTY ROSE", 255, 228, 225),
|
||||
("MISTYROSE", 255, 228, 225),
|
||||
("WHITE", 255, 255, 255),
|
||||
("BLACK", 0, 0, 0),
|
||||
("DARK SLATE GREY", 47, 79, 79),
|
||||
("DARKSLATEGREY", 47, 79, 79),
|
||||
("DIM GREY", 105, 105, 105),
|
||||
("DIMGREY", 105, 105, 105),
|
||||
("SLATE GREY", 112, 128, 144),
|
||||
("SLATEGREY", 112, 128, 144),
|
||||
("LIGHT SLATE GREY", 119, 136, 153),
|
||||
("LIGHTSLATEGREY", 119, 136, 153),
|
||||
("GREY", 190, 190, 190),
|
||||
("LIGHT GREY", 211, 211, 211),
|
||||
("LIGHTGREY", 211, 211, 211),
|
||||
("MIDNIGHT BLUE", 25, 25, 112),
|
||||
("MIDNIGHTBLUE", 25, 25, 112),
|
||||
("NAVY", 0, 0, 128),
|
||||
("NAVY BLUE", 0, 0, 128),
|
||||
("NAVYBLUE", 0, 0, 128),
|
||||
("CORNFLOWER BLUE", 100, 149, 237),
|
||||
("CORNFLOWERBLUE", 100, 149, 237),
|
||||
("DARK SLATE BLUE", 72, 61, 139),
|
||||
("DARKSLATEBLUE", 72, 61, 139),
|
||||
("SLATE BLUE", 106, 90, 205),
|
||||
("SLATEBLUE", 106, 90, 205),
|
||||
("MEDIUM SLATE BLUE", 123, 104, 238),
|
||||
("MEDIUMSLATEBLUE", 123, 104, 238),
|
||||
("LIGHT SLATE BLUE", 132, 112, 255),
|
||||
("LIGHTSLATEBLUE", 132, 112, 255),
|
||||
("MEDIUM BLUE", 0, 0, 205),
|
||||
("MEDIUMBLUE", 0, 0, 205),
|
||||
("ROYAL BLUE", 65, 105, 225),
|
||||
("ROYALBLUE", 65, 105, 225),
|
||||
("BLUE", 0, 0, 255),
|
||||
("DODGER BLUE", 30, 144, 255),
|
||||
("DODGERBLUE", 30, 144, 255),
|
||||
("DEEP SKY BLUE", 0, 191, 255),
|
||||
("DEEPSKYBLUE", 0, 191, 255),
|
||||
("SKY BLUE", 135, 206, 235),
|
||||
("SKYBLUE", 135, 206, 235),
|
||||
("LIGHT SKY BLUE", 135, 206, 250),
|
||||
("LIGHTSKYBLUE", 135, 206, 250),
|
||||
("STEEL BLUE", 70, 130, 180),
|
||||
("STEELBLUE", 70, 130, 180),
|
||||
("LIGHT STEEL BLUE", 176, 196, 222),
|
||||
("LIGHTSTEELBLUE", 176, 196, 222),
|
||||
("LIGHT BLUE", 173, 216, 230),
|
||||
("LIGHTBLUE", 173, 216, 230),
|
||||
("POWDER BLUE", 176, 224, 230),
|
||||
("POWDERBLUE", 176, 224, 230),
|
||||
("PALE TURQUOISE", 175, 238, 238),
|
||||
("PALETURQUOISE", 175, 238, 238),
|
||||
("DARK TURQUOISE", 0, 206, 209),
|
||||
("DARKTURQUOISE", 0, 206, 209),
|
||||
("MEDIUM TURQUOISE", 72, 209, 204),
|
||||
("MEDIUMTURQUOISE", 72, 209, 204),
|
||||
("TURQUOISE", 64, 224, 208),
|
||||
("CYAN", 0, 255, 255),
|
||||
("LIGHT CYAN", 224, 255, 255),
|
||||
("LIGHTCYAN", 224, 255, 255),
|
||||
("CADET BLUE", 95, 158, 160),
|
||||
("CADETBLUE", 95, 158, 160),
|
||||
("MEDIUM AQUAMARINE", 102, 205, 170),
|
||||
("MEDIUMAQUAMARINE", 102, 205, 170),
|
||||
("AQUAMARINE", 127, 255, 212),
|
||||
("DARK GREEN", 0, 100, 0),
|
||||
("DARKGREEN", 0, 100, 0),
|
||||
("DARK OLIVE GREEN", 85, 107, 47),
|
||||
("DARKOLIVEGREEN", 85, 107, 47),
|
||||
("DARK SEA GREEN", 143, 188, 143),
|
||||
("DARKSEAGREEN", 143, 188, 143),
|
||||
("SEA GREEN", 46, 139, 87),
|
||||
("SEAGREEN", 46, 139, 87),
|
||||
("MEDIUM SEA GREEN", 60, 179, 113),
|
||||
("MEDIUMSEAGREEN", 60, 179, 113),
|
||||
("LIGHT SEA GREEN", 32, 178, 170),
|
||||
("LIGHTSEAGREEN", 32, 178, 170),
|
||||
("PALE GREEN", 152, 251, 152),
|
||||
("PALEGREEN", 152, 251, 152),
|
||||
("SPRING GREEN", 0, 255, 127),
|
||||
("SPRINGGREEN", 0, 255, 127),
|
||||
("LAWN GREEN", 124, 252, 0),
|
||||
("LAWNGREEN", 124, 252, 0),
|
||||
("GREEN", 0, 255, 0),
|
||||
("CHARTREUSE", 127, 255, 0),
|
||||
("MEDIUM SPRING GREEN", 0, 250, 154),
|
||||
("MEDIUMSPRINGGREEN", 0, 250, 154),
|
||||
("GREEN YELLOW", 173, 255, 47),
|
||||
("GREENYELLOW", 173, 255, 47),
|
||||
("LIME GREEN", 50, 205, 50),
|
||||
("LIMEGREEN", 50, 205, 50),
|
||||
("YELLOW GREEN", 154, 205, 50),
|
||||
("YELLOWGREEN", 154, 205, 50),
|
||||
("FOREST GREEN", 34, 139, 34),
|
||||
("FORESTGREEN", 34, 139, 34),
|
||||
("OLIVE DRAB", 107, 142, 35),
|
||||
("OLIVEDRAB", 107, 142, 35),
|
||||
("DARK KHAKI", 189, 183, 107),
|
||||
("DARKKHAKI", 189, 183, 107),
|
||||
("KHAKI", 240, 230, 140),
|
||||
("PALE GOLDENROD", 238, 232, 170),
|
||||
("PALEGOLDENROD", 238, 232, 170),
|
||||
("LIGHT GOLDENROD YELLOW", 250, 250, 210),
|
||||
("LIGHTGOLDENRODYELLOW", 250, 250, 210),
|
||||
("LIGHT YELLOW", 255, 255, 224),
|
||||
("LIGHTYELLOW", 255, 255, 224),
|
||||
("YELLOW", 255, 255, 0),
|
||||
("GOLD", 255, 215, 0),
|
||||
("LIGHT GOLDENROD", 238, 221, 130),
|
||||
("LIGHTGOLDENROD", 238, 221, 130),
|
||||
("GOLDENROD", 218, 165, 32),
|
||||
("DARK GOLDENROD", 184, 134, 11),
|
||||
("DARKGOLDENROD", 184, 134, 11),
|
||||
("ROSY BROWN", 188, 143, 143),
|
||||
("ROSYBROWN", 188, 143, 143),
|
||||
("INDIAN RED", 205, 92, 92),
|
||||
("INDIANRED", 205, 92, 92),
|
||||
("SADDLE BROWN", 139, 69, 19),
|
||||
("SADDLEBROWN", 139, 69, 19),
|
||||
("SIENNA", 160, 82, 45),
|
||||
("PERU", 205, 133, 63),
|
||||
("BURLYWOOD", 222, 184, 135),
|
||||
("BEIGE", 245, 245, 220),
|
||||
("WHEAT", 245, 222, 179),
|
||||
("SANDY BROWN", 244, 164, 96),
|
||||
("SANDYBROWN", 244, 164, 96),
|
||||
("TAN", 210, 180, 140),
|
||||
("CHOCOLATE", 210, 105, 30),
|
||||
("FIREBRICK", 178, 34, 34),
|
||||
("BROWN", 165, 42, 42),
|
||||
("DARK SALMON", 233, 150, 122),
|
||||
("DARKSALMON", 233, 150, 122),
|
||||
("SALMON", 250, 128, 114),
|
||||
("LIGHT SALMON", 255, 160, 122),
|
||||
("LIGHTSALMON", 255, 160, 122),
|
||||
("ORANGE", 255, 165, 0),
|
||||
("DARK ORANGE", 255, 140, 0),
|
||||
("DARKORANGE", 255, 140, 0),
|
||||
("CORAL", 255, 127, 80),
|
||||
("LIGHT CORAL", 240, 128, 128),
|
||||
("LIGHTCORAL", 240, 128, 128),
|
||||
("TOMATO", 255, 99, 71),
|
||||
("ORANGE RED", 255, 69, 0),
|
||||
("ORANGERED", 255, 69, 0),
|
||||
("RED", 255, 0, 0),
|
||||
("HOT PINK", 255, 105, 180),
|
||||
("HOTPINK", 255, 105, 180),
|
||||
("DEEP PINK", 255, 20, 147),
|
||||
("DEEPPINK", 255, 20, 147),
|
||||
("PINK", 255, 192, 203),
|
||||
("LIGHT PINK", 255, 182, 193),
|
||||
("LIGHTPINK", 255, 182, 193),
|
||||
("PALE VIOLET RED", 219, 112, 147),
|
||||
("PALEVIOLETRED", 219, 112, 147),
|
||||
("MAROON", 176, 48, 96),
|
||||
("MEDIUM VIOLET RED", 199, 21, 133),
|
||||
("MEDIUMVIOLETRED", 199, 21, 133),
|
||||
("VIOLET RED", 208, 32, 144),
|
||||
("VIOLETRED", 208, 32, 144),
|
||||
("MAGENTA", 255, 0, 255),
|
||||
("VIOLET", 238, 130, 238),
|
||||
("PLUM", 221, 160, 221),
|
||||
("ORCHID", 218, 112, 214),
|
||||
("MEDIUM ORCHID", 186, 85, 211),
|
||||
("MEDIUMORCHID", 186, 85, 211),
|
||||
("DARK ORCHID", 153, 50, 204),
|
||||
("DARKORCHID", 153, 50, 204),
|
||||
("DARK VIOLET", 148, 0, 211),
|
||||
("DARKVIOLET", 148, 0, 211),
|
||||
("BLUE VIOLET", 138, 43, 226),
|
||||
("BLUEVIOLET", 138, 43, 226),
|
||||
("PURPLE", 160, 32, 240),
|
||||
("MEDIUM PURPLE", 147, 112, 219),
|
||||
("MEDIUMPURPLE", 147, 112, 219),
|
||||
("THISTLE", 216, 191, 216),
|
||||
("SNOW1", 255, 250, 250),
|
||||
("SNOW2", 238, 233, 233),
|
||||
("SNOW3", 205, 201, 201),
|
||||
("SNOW4", 139, 137, 137),
|
||||
("SEASHELL1", 255, 245, 238),
|
||||
("SEASHELL2", 238, 229, 222),
|
||||
("SEASHELL3", 205, 197, 191),
|
||||
("SEASHELL4", 139, 134, 130),
|
||||
("ANTIQUEWHITE1", 255, 239, 219),
|
||||
("ANTIQUEWHITE2", 238, 223, 204),
|
||||
("ANTIQUEWHITE3", 205, 192, 176),
|
||||
("ANTIQUEWHITE4", 139, 131, 120),
|
||||
("BISQUE1", 255, 228, 196),
|
||||
("BISQUE2", 238, 213, 183),
|
||||
("BISQUE3", 205, 183, 158),
|
||||
("BISQUE4", 139, 125, 107),
|
||||
("PEACHPUFF1", 255, 218, 185),
|
||||
("PEACHPUFF2", 238, 203, 173),
|
||||
("PEACHPUFF3", 205, 175, 149),
|
||||
("PEACHPUFF4", 139, 119, 101),
|
||||
("NAVAJOWHITE1", 255, 222, 173),
|
||||
("NAVAJOWHITE2", 238, 207, 161),
|
||||
("NAVAJOWHITE3", 205, 179, 139),
|
||||
("NAVAJOWHITE4", 139, 121, 94),
|
||||
("LEMONCHIFFON1", 255, 250, 205),
|
||||
("LEMONCHIFFON2", 238, 233, 191),
|
||||
("LEMONCHIFFON3", 205, 201, 165),
|
||||
("LEMONCHIFFON4", 139, 137, 112),
|
||||
("CORNSILK1", 255, 248, 220),
|
||||
("CORNSILK2", 238, 232, 205),
|
||||
("CORNSILK3", 205, 200, 177),
|
||||
("CORNSILK4", 139, 136, 120),
|
||||
("IVORY1", 255, 255, 240),
|
||||
("IVORY2", 238, 238, 224),
|
||||
("IVORY3", 205, 205, 193),
|
||||
("IVORY4", 139, 139, 131),
|
||||
("HONEYDEW1", 240, 255, 240),
|
||||
("HONEYDEW2", 224, 238, 224),
|
||||
("HONEYDEW3", 193, 205, 193),
|
||||
("HONEYDEW4", 131, 139, 131),
|
||||
("LAVENDERBLUSH1", 255, 240, 245),
|
||||
("LAVENDERBLUSH2", 238, 224, 229),
|
||||
("LAVENDERBLUSH3", 205, 193, 197),
|
||||
("LAVENDERBLUSH4", 139, 131, 134),
|
||||
("MISTYROSE1", 255, 228, 225),
|
||||
("MISTYROSE2", 238, 213, 210),
|
||||
("MISTYROSE3", 205, 183, 181),
|
||||
("MISTYROSE4", 139, 125, 123),
|
||||
("AZURE1", 240, 255, 255),
|
||||
("AZURE2", 224, 238, 238),
|
||||
("AZURE3", 193, 205, 205),
|
||||
("AZURE4", 131, 139, 139),
|
||||
("SLATEBLUE1", 131, 111, 255),
|
||||
("SLATEBLUE2", 122, 103, 238),
|
||||
("SLATEBLUE3", 105, 89, 205),
|
||||
("SLATEBLUE4", 71, 60, 139),
|
||||
("ROYALBLUE1", 72, 118, 255),
|
||||
("ROYALBLUE2", 67, 110, 238),
|
||||
("ROYALBLUE3", 58, 95, 205),
|
||||
("ROYALBLUE4", 39, 64, 139),
|
||||
("BLUE1", 0, 0, 255),
|
||||
("BLUE2", 0, 0, 238),
|
||||
("BLUE3", 0, 0, 205),
|
||||
("BLUE4", 0, 0, 139),
|
||||
("DODGERBLUE1", 30, 144, 255),
|
||||
("DODGERBLUE2", 28, 134, 238),
|
||||
("DODGERBLUE3", 24, 116, 205),
|
||||
("DODGERBLUE4", 16, 78, 139),
|
||||
("STEELBLUE1", 99, 184, 255),
|
||||
("STEELBLUE2", 92, 172, 238),
|
||||
("STEELBLUE3", 79, 148, 205),
|
||||
("STEELBLUE4", 54, 100, 139),
|
||||
("DEEPSKYBLUE1", 0, 191, 255),
|
||||
("DEEPSKYBLUE2", 0, 178, 238),
|
||||
("DEEPSKYBLUE3", 0, 154, 205),
|
||||
("DEEPSKYBLUE4", 0, 104, 139),
|
||||
("SKYBLUE1", 135, 206, 255),
|
||||
("SKYBLUE2", 126, 192, 238),
|
||||
("SKYBLUE3", 108, 166, 205),
|
||||
("SKYBLUE4", 74, 112, 139),
|
||||
("LIGHTSKYBLUE1", 176, 226, 255),
|
||||
("LIGHTSKYBLUE2", 164, 211, 238),
|
||||
("LIGHTSKYBLUE3", 141, 182, 205),
|
||||
("LIGHTSKYBLUE4", 96, 123, 139),
|
||||
("LIGHTSTEELBLUE1", 202, 225, 255),
|
||||
("LIGHTSTEELBLUE2", 188, 210, 238),
|
||||
("LIGHTSTEELBLUE3", 162, 181, 205),
|
||||
("LIGHTSTEELBLUE4", 110, 123, 139),
|
||||
("LIGHTBLUE1", 191, 239, 255),
|
||||
("LIGHTBLUE2", 178, 223, 238),
|
||||
("LIGHTBLUE3", 154, 192, 205),
|
||||
("LIGHTBLUE4", 104, 131, 139),
|
||||
("LIGHTCYAN1", 224, 255, 255),
|
||||
("LIGHTCYAN2", 209, 238, 238),
|
||||
("LIGHTCYAN3", 180, 205, 205),
|
||||
("LIGHTCYAN4", 122, 139, 139),
|
||||
("PALETURQUOISE1", 187, 255, 255),
|
||||
("PALETURQUOISE2", 174, 238, 238),
|
||||
("PALETURQUOISE3", 150, 205, 205),
|
||||
("PALETURQUOISE4", 102, 139, 139),
|
||||
("CADETBLUE1", 152, 245, 255),
|
||||
("CADETBLUE2", 142, 229, 238),
|
||||
("CADETBLUE3", 122, 197, 205),
|
||||
("CADETBLUE4", 83, 134, 139),
|
||||
("TURQUOISE1", 0, 245, 255),
|
||||
("TURQUOISE2", 0, 229, 238),
|
||||
("TURQUOISE3", 0, 197, 205),
|
||||
("TURQUOISE4", 0, 134, 139),
|
||||
("CYAN1", 0, 255, 255),
|
||||
("CYAN2", 0, 238, 238),
|
||||
("CYAN3", 0, 205, 205),
|
||||
("CYAN4", 0, 139, 139),
|
||||
("AQUAMARINE1", 127, 255, 212),
|
||||
("AQUAMARINE2", 118, 238, 198),
|
||||
("AQUAMARINE3", 102, 205, 170),
|
||||
("AQUAMARINE4", 69, 139, 116),
|
||||
("DARKSEAGREEN1", 193, 255, 193),
|
||||
("DARKSEAGREEN2", 180, 238, 180),
|
||||
("DARKSEAGREEN3", 155, 205, 155),
|
||||
("DARKSEAGREEN4", 105, 139, 105),
|
||||
("SEAGREEN1", 84, 255, 159),
|
||||
("SEAGREEN2", 78, 238, 148),
|
||||
("SEAGREEN3", 67, 205, 128),
|
||||
("SEAGREEN4", 46, 139, 87),
|
||||
("PALEGREEN1", 154, 255, 154),
|
||||
("PALEGREEN2", 144, 238, 144),
|
||||
("PALEGREEN3", 124, 205, 124),
|
||||
("PALEGREEN4", 84, 139, 84),
|
||||
("SPRINGGREEN1", 0, 255, 127),
|
||||
("SPRINGGREEN2", 0, 238, 118),
|
||||
("SPRINGGREEN3", 0, 205, 102),
|
||||
("SPRINGGREEN4", 0, 139, 69),
|
||||
("GREEN1", 0, 255, 0),
|
||||
("GREEN2", 0, 238, 0),
|
||||
("GREEN3", 0, 205, 0),
|
||||
("GREEN4", 0, 139, 0),
|
||||
("CHARTREUSE1", 127, 255, 0),
|
||||
("CHARTREUSE2", 118, 238, 0),
|
||||
("CHARTREUSE3", 102, 205, 0),
|
||||
("CHARTREUSE4", 69, 139, 0),
|
||||
("OLIVEDRAB1", 192, 255, 62),
|
||||
("OLIVEDRAB2", 179, 238, 58),
|
||||
("OLIVEDRAB3", 154, 205, 50),
|
||||
("OLIVEDRAB4", 105, 139, 34),
|
||||
("DARKOLIVEGREEN1", 202, 255, 112),
|
||||
("DARKOLIVEGREEN2", 188, 238, 104),
|
||||
("DARKOLIVEGREEN3", 162, 205, 90),
|
||||
("DARKOLIVEGREEN4", 110, 139, 61),
|
||||
("KHAKI1", 255, 246, 143),
|
||||
("KHAKI2", 238, 230, 133),
|
||||
("KHAKI3", 205, 198, 115),
|
||||
("KHAKI4", 139, 134, 78),
|
||||
("LIGHTGOLDENROD1", 255, 236, 139),
|
||||
("LIGHTGOLDENROD2", 238, 220, 130),
|
||||
("LIGHTGOLDENROD3", 205, 190, 112),
|
||||
("LIGHTGOLDENROD4", 139, 129, 76),
|
||||
("LIGHTYELLOW1", 255, 255, 224),
|
||||
("LIGHTYELLOW2", 238, 238, 209),
|
||||
("LIGHTYELLOW3", 205, 205, 180),
|
||||
("LIGHTYELLOW4", 139, 139, 122),
|
||||
("YELLOW1", 255, 255, 0),
|
||||
("YELLOW2", 238, 238, 0),
|
||||
("YELLOW3", 205, 205, 0),
|
||||
("YELLOW4", 139, 139, 0),
|
||||
("GOLD1", 255, 215, 0),
|
||||
("GOLD2", 238, 201, 0),
|
||||
("GOLD3", 205, 173, 0),
|
||||
("GOLD4", 139, 117, 0),
|
||||
("GOLDENROD1", 255, 193, 37),
|
||||
("GOLDENROD2", 238, 180, 34),
|
||||
("GOLDENROD3", 205, 155, 29),
|
||||
("GOLDENROD4", 139, 105, 20),
|
||||
("DARKGOLDENROD1", 255, 185, 15),
|
||||
("DARKGOLDENROD2", 238, 173, 14),
|
||||
("DARKGOLDENROD3", 205, 149, 12),
|
||||
("DARKGOLDENROD4", 139, 101, 8),
|
||||
("ROSYBROWN1", 255, 193, 193),
|
||||
("ROSYBROWN2", 238, 180, 180),
|
||||
("ROSYBROWN3", 205, 155, 155),
|
||||
("ROSYBROWN4", 139, 105, 105),
|
||||
("INDIANRED1", 255, 106, 106),
|
||||
("INDIANRED2", 238, 99, 99),
|
||||
("INDIANRED3", 205, 85, 85),
|
||||
("INDIANRED4", 139, 58, 58),
|
||||
("SIENNA1", 255, 130, 71),
|
||||
("SIENNA2", 238, 121, 66),
|
||||
("SIENNA3", 205, 104, 57),
|
||||
("SIENNA4", 139, 71, 38),
|
||||
("BURLYWOOD1", 255, 211, 155),
|
||||
("BURLYWOOD2", 238, 197, 145),
|
||||
("BURLYWOOD3", 205, 170, 125),
|
||||
("BURLYWOOD4", 139, 115, 85),
|
||||
("WHEAT1", 255, 231, 186),
|
||||
("WHEAT2", 238, 216, 174),
|
||||
("WHEAT3", 205, 186, 150),
|
||||
("WHEAT4", 139, 126, 102),
|
||||
("TAN1", 255, 165, 79),
|
||||
("TAN2", 238, 154, 73),
|
||||
("TAN3", 205, 133, 63),
|
||||
("TAN4", 139, 90, 43),
|
||||
("CHOCOLATE1", 255, 127, 36),
|
||||
("CHOCOLATE2", 238, 118, 33),
|
||||
("CHOCOLATE3", 205, 102, 29),
|
||||
("CHOCOLATE4", 139, 69, 19),
|
||||
("FIREBRICK1", 255, 48, 48),
|
||||
("FIREBRICK2", 238, 44, 44),
|
||||
("FIREBRICK3", 205, 38, 38),
|
||||
("FIREBRICK4", 139, 26, 26),
|
||||
("BROWN1", 255, 64, 64),
|
||||
("BROWN2", 238, 59, 59),
|
||||
("BROWN3", 205, 51, 51),
|
||||
("BROWN4", 139, 35, 35),
|
||||
("SALMON1", 255, 140, 105),
|
||||
("SALMON2", 238, 130, 98),
|
||||
("SALMON3", 205, 112, 84),
|
||||
("SALMON4", 139, 76, 57),
|
||||
("LIGHTSALMON1", 255, 160, 122),
|
||||
("LIGHTSALMON2", 238, 149, 114),
|
||||
("LIGHTSALMON3", 205, 129, 98),
|
||||
("LIGHTSALMON4", 139, 87, 66),
|
||||
("ORANGE1", 255, 165, 0),
|
||||
("ORANGE2", 238, 154, 0),
|
||||
("ORANGE3", 205, 133, 0),
|
||||
("ORANGE4", 139, 90, 0),
|
||||
("DARKORANGE1", 255, 127, 0),
|
||||
("DARKORANGE2", 238, 118, 0),
|
||||
("DARKORANGE3", 205, 102, 0),
|
||||
("DARKORANGE4", 139, 69, 0),
|
||||
("CORAL1", 255, 114, 86),
|
||||
("CORAL2", 238, 106, 80),
|
||||
("CORAL3", 205, 91, 69),
|
||||
("CORAL4", 139, 62, 47),
|
||||
("TOMATO1", 255, 99, 71),
|
||||
("TOMATO2", 238, 92, 66),
|
||||
("TOMATO3", 205, 79, 57),
|
||||
("TOMATO4", 139, 54, 38),
|
||||
("ORANGERED1", 255, 69, 0),
|
||||
("ORANGERED2", 238, 64, 0),
|
||||
("ORANGERED3", 205, 55, 0),
|
||||
("ORANGERED4", 139, 37, 0),
|
||||
("RED1", 255, 0, 0),
|
||||
("RED2", 238, 0, 0),
|
||||
("RED3", 205, 0, 0),
|
||||
("RED4", 139, 0, 0),
|
||||
("DEEPPINK1", 255, 20, 147),
|
||||
("DEEPPINK2", 238, 18, 137),
|
||||
("DEEPPINK3", 205, 16, 118),
|
||||
("DEEPPINK4", 139, 10, 80),
|
||||
("HOTPINK1", 255, 110, 180),
|
||||
("HOTPINK2", 238, 106, 167),
|
||||
("HOTPINK3", 205, 96, 144),
|
||||
("HOTPINK4", 139, 58, 98),
|
||||
("PINK1", 255, 181, 197),
|
||||
("PINK2", 238, 169, 184),
|
||||
("PINK3", 205, 145, 158),
|
||||
("PINK4", 139, 99, 108),
|
||||
("LIGHTPINK1", 255, 174, 185),
|
||||
("LIGHTPINK2", 238, 162, 173),
|
||||
("LIGHTPINK3", 205, 140, 149),
|
||||
("LIGHTPINK4", 139, 95, 101),
|
||||
("PALEVIOLETRED1", 255, 130, 171),
|
||||
("PALEVIOLETRED2", 238, 121, 159),
|
||||
("PALEVIOLETRED3", 205, 104, 137),
|
||||
("PALEVIOLETRED4", 139, 71, 93),
|
||||
("MAROON1", 255, 52, 179),
|
||||
("MAROON2", 238, 48, 167),
|
||||
("MAROON3", 205, 41, 144),
|
||||
("MAROON4", 139, 28, 98),
|
||||
("VIOLETRED1", 255, 62, 150),
|
||||
("VIOLETRED2", 238, 58, 140),
|
||||
("VIOLETRED3", 205, 50, 120),
|
||||
("VIOLETRED4", 139, 34, 82),
|
||||
("MAGENTA1", 255, 0, 255),
|
||||
("MAGENTA2", 238, 0, 238),
|
||||
("MAGENTA3", 205, 0, 205),
|
||||
("MAGENTA4", 139, 0, 139),
|
||||
("ORCHID1", 255, 131, 250),
|
||||
("ORCHID2", 238, 122, 233),
|
||||
("ORCHID3", 205, 105, 201),
|
||||
("ORCHID4", 139, 71, 137),
|
||||
("PLUM1", 255, 187, 255),
|
||||
("PLUM2", 238, 174, 238),
|
||||
("PLUM3", 205, 150, 205),
|
||||
("PLUM4", 139, 102, 139),
|
||||
("MEDIUMORCHID1", 224, 102, 255),
|
||||
("MEDIUMORCHID2", 209, 95, 238),
|
||||
("MEDIUMORCHID3", 180, 82, 205),
|
||||
("MEDIUMORCHID4", 122, 55, 139),
|
||||
("DARKORCHID1", 191, 62, 255),
|
||||
("DARKORCHID2", 178, 58, 238),
|
||||
("DARKORCHID3", 154, 50, 205),
|
||||
("DARKORCHID4", 104, 34, 139),
|
||||
("PURPLE1", 155, 48, 255),
|
||||
("PURPLE2", 145, 44, 238),
|
||||
("PURPLE3", 125, 38, 205),
|
||||
("PURPLE4", 85, 26, 139),
|
||||
("MEDIUMPURPLE1", 171, 130, 255),
|
||||
("MEDIUMPURPLE2", 159, 121, 238),
|
||||
("MEDIUMPURPLE3", 137, 104, 205),
|
||||
("MEDIUMPURPLE4", 93, 71, 139),
|
||||
("THISTLE1", 255, 225, 255),
|
||||
("THISTLE2", 238, 210, 238),
|
||||
("THISTLE3", 205, 181, 205),
|
||||
("THISTLE4", 139, 123, 139),
|
||||
("GREY0", 0, 0, 0),
|
||||
("GREY1", 3, 3, 3),
|
||||
("GREY2", 5, 5, 5),
|
||||
("GREY3", 8, 8, 8),
|
||||
("GREY4", 10, 10, 10),
|
||||
("GREY5", 13, 13, 13),
|
||||
("GREY6", 15, 15, 15),
|
||||
("GREY7", 18, 18, 18),
|
||||
("GREY8", 20, 20, 20),
|
||||
("GREY9", 23, 23, 23),
|
||||
("GREY10", 26, 26, 26),
|
||||
("GREY11", 28, 28, 28),
|
||||
("GREY12", 31, 31, 31),
|
||||
("GREY13", 33, 33, 33),
|
||||
("GREY14", 36, 36, 36),
|
||||
("GREY15", 38, 38, 38),
|
||||
("GREY16", 41, 41, 41),
|
||||
("GREY17", 43, 43, 43),
|
||||
("GREY18", 46, 46, 46),
|
||||
("GREY19", 48, 48, 48),
|
||||
("GREY20", 51, 51, 51),
|
||||
("GREY21", 54, 54, 54),
|
||||
("GREY22", 56, 56, 56),
|
||||
("GREY23", 59, 59, 59),
|
||||
("GREY24", 61, 61, 61),
|
||||
("GREY25", 64, 64, 64),
|
||||
("GREY26", 66, 66, 66),
|
||||
("GREY27", 69, 69, 69),
|
||||
("GREY28", 71, 71, 71),
|
||||
("GREY29", 74, 74, 74),
|
||||
("GREY30", 77, 77, 77),
|
||||
("GREY31", 79, 79, 79),
|
||||
("GREY32", 82, 82, 82),
|
||||
("GREY33", 84, 84, 84),
|
||||
("GREY34", 87, 87, 87),
|
||||
("GREY35", 89, 89, 89),
|
||||
("GREY36", 92, 92, 92),
|
||||
("GREY37", 94, 94, 94),
|
||||
("GREY38", 97, 97, 97),
|
||||
("GREY39", 99, 99, 99),
|
||||
("GREY40", 102, 102, 102),
|
||||
("GREY41", 105, 105, 105),
|
||||
("GREY42", 107, 107, 107),
|
||||
("GREY43", 110, 110, 110),
|
||||
("GREY44", 112, 112, 112),
|
||||
("GREY45", 115, 115, 115),
|
||||
("GREY46", 117, 117, 117),
|
||||
("GREY47", 120, 120, 120),
|
||||
("GREY48", 122, 122, 122),
|
||||
("GREY49", 125, 125, 125),
|
||||
("GREY50", 127, 127, 127),
|
||||
("GREY51", 130, 130, 130),
|
||||
("GREY52", 133, 133, 133),
|
||||
("GREY53", 135, 135, 135),
|
||||
("GREY54", 138, 138, 138),
|
||||
("GREY55", 140, 140, 140),
|
||||
("GREY56", 143, 143, 143),
|
||||
("GREY57", 145, 145, 145),
|
||||
("GREY58", 148, 148, 148),
|
||||
("GREY59", 150, 150, 150),
|
||||
("GREY60", 153, 153, 153),
|
||||
("GREY61", 156, 156, 156),
|
||||
("GREY62", 158, 158, 158),
|
||||
("GREY63", 161, 161, 161),
|
||||
("GREY64", 163, 163, 163),
|
||||
("GREY65", 166, 166, 166),
|
||||
("GREY66", 168, 168, 168),
|
||||
("GREY67", 171, 171, 171),
|
||||
("GREY68", 173, 173, 173),
|
||||
("GREY69", 176, 176, 176),
|
||||
("GREY70", 179, 179, 179),
|
||||
("GREY71", 181, 181, 181),
|
||||
("GREY72", 184, 184, 184),
|
||||
("GREY73", 186, 186, 186),
|
||||
("GREY74", 189, 189, 189),
|
||||
("GREY75", 191, 191, 191),
|
||||
("GREY76", 194, 194, 194),
|
||||
("GREY77", 196, 196, 196),
|
||||
("GREY78", 199, 199, 199),
|
||||
("GREY79", 201, 201, 201),
|
||||
("GREY80", 204, 204, 204),
|
||||
("GREY81", 207, 207, 207),
|
||||
("GREY82", 209, 209, 209),
|
||||
("GREY83", 212, 212, 212),
|
||||
("GREY84", 214, 214, 214),
|
||||
("GREY85", 217, 217, 217),
|
||||
("GREY86", 219, 219, 219),
|
||||
("GREY87", 222, 222, 222),
|
||||
("GREY88", 224, 224, 224),
|
||||
("GREY89", 227, 227, 227),
|
||||
("GREY90", 229, 229, 229),
|
||||
("GREY91", 232, 232, 232),
|
||||
("GREY92", 235, 235, 235),
|
||||
("GREY93", 237, 237, 237),
|
||||
("GREY94", 240, 240, 240),
|
||||
("GREY95", 242, 242, 242),
|
||||
("GREY96", 245, 245, 245),
|
||||
("GREY97", 247, 247, 247),
|
||||
("GREY98", 250, 250, 250),
|
||||
("GREY99", 252, 252, 252),
|
||||
("GREY100", 255, 255, 255),
|
||||
("DARK GREY", 169, 169, 169),
|
||||
("DARKGREY", 169, 169, 169),
|
||||
("DARK BLUE", 0, 0, 139),
|
||||
("DARKBLUE", 0, 0, 139),
|
||||
("DARK CYAN", 0, 139, 139),
|
||||
("DARKCYAN", 0, 139, 139),
|
||||
("DARK MAGENTA", 139, 0, 139),
|
||||
("DARKMAGENTA", 139, 0, 139),
|
||||
("DARK RED", 139, 0, 0),
|
||||
("DARKRED", 139, 0, 0),
|
||||
("LIGHT GREEN", 144, 238, 144),
|
||||
("LIGHTGREEN", 144, 238, 144),
|
||||
]
|
||||
|
||||
|
||||
_haveUpdated = False
|
||||
|
||||
def updateColourDB():
|
||||
global _haveUpdated
|
||||
if not _haveUpdated:
|
||||
from wxPython.wx import wxTheColourDatabase
|
||||
cl = getColourInfoList()
|
||||
for info in cl:
|
||||
wxTheColourDatabase.Append(*info)
|
||||
import wx.lib.colourdb
|
||||
|
||||
__doc__ = wx.lib.colourdb.__doc__
|
||||
|
||||
getColourInfoList = wx.lib.colourdb.getColourInfoList
|
||||
getColourList = wx.lib.colourdb.getColourList
|
||||
updateColourDB = wx.lib.colourdb.updateColourDB
|
||||
|
||||
@@ -1,90 +1,11 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: ColourSelect.py
|
||||
# Purpose: Colour Box Selection Control
|
||||
#
|
||||
# Author: Lorne White, Lorne.White@telusplanet.net
|
||||
#
|
||||
# Created: Feb 25, 2001
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython.wx import *
|
||||
import wx.lib.colourselect
|
||||
|
||||
# creates a colour wxButton with selectable color
|
||||
# button click provides a colour selection box
|
||||
# button colour will change to new colour
|
||||
# GetColour method to get the selected colour
|
||||
|
||||
# Updates:
|
||||
# call back to function if changes made
|
||||
|
||||
# Cliff Wells, logiplexsoftware@earthlink.net:
|
||||
# - Made ColourSelect into "is a button" rather than "has a button"
|
||||
# - Added label parameter and logic to adjust the label colour according to the background
|
||||
# colour
|
||||
# - Added id argument
|
||||
# - Rearranged arguments to more closely follow wx conventions
|
||||
# - Simplified some of the code
|
||||
|
||||
# Cliff Wells, 2002/02/07
|
||||
# - Added ColourSelect Event
|
||||
|
||||
EVT_COMMAND_COLOURSELECT = wxNewId()
|
||||
|
||||
class ColourSelectEvent(wxPyCommandEvent):
|
||||
def __init__(self, id, value):
|
||||
wxPyCommandEvent.__init__(self, id = id)
|
||||
self.SetEventType(EVT_COMMAND_COLOURSELECT)
|
||||
self.value = value
|
||||
|
||||
def GetValue(self):
|
||||
return self.value
|
||||
|
||||
def EVT_COLOURSELECT(win, id, func):
|
||||
win.Connect(id, -1, EVT_COMMAND_COLOURSELECT, func)
|
||||
|
||||
class ColourSelect(wxButton):
|
||||
def __init__(self, parent, id, label = "", bcolour=(0, 0, 0),
|
||||
pos = wxDefaultPosition, size = wxDefaultSize,
|
||||
callback = None):
|
||||
wxButton.__init__(self, parent, id, label, pos=pos, size=size)
|
||||
self.SetColour(bcolour)
|
||||
self.callback = callback
|
||||
EVT_BUTTON(parent, self.GetId(), self.OnClick)
|
||||
|
||||
def GetColour(self):
|
||||
return self.set_colour
|
||||
|
||||
def GetValue(self):
|
||||
return self.set_colour
|
||||
|
||||
def SetColour(self, bcolour):
|
||||
self.set_colour_val = wxColor(bcolour[0], bcolour[1], bcolour[2])
|
||||
self.SetBackgroundColour(self.set_colour_val)
|
||||
avg = reduce(lambda a, b: a + b, bcolour) / 3
|
||||
fcolour = avg > 128 and (0, 0, 0) or (255, 255, 255)
|
||||
self.SetForegroundColour(apply(wxColour, fcolour))
|
||||
self.set_colour = bcolour
|
||||
|
||||
def SetValue(self, bcolour):
|
||||
self.SetColour(bcolour)
|
||||
|
||||
def OnChange(self):
|
||||
wxPostEvent(self, ColourSelectEvent(self.GetId(), self.GetValue()))
|
||||
if self.callback is not None:
|
||||
self.callback()
|
||||
|
||||
def OnClick(self, event):
|
||||
data = wxColourData()
|
||||
data.SetChooseFull(True)
|
||||
data.SetColour(self.set_colour_val)
|
||||
dlg = wxColourDialog(self.GetParent(), data)
|
||||
changed = dlg.ShowModal() == wxID_OK
|
||||
if changed:
|
||||
data = dlg.GetColourData()
|
||||
self.SetColour(data.GetColour().Get())
|
||||
dlg.Destroy()
|
||||
|
||||
if changed:
|
||||
self.OnChange() # moved after dlg.Destroy, since who knows what the callback will do...
|
||||
__doc__ = wx.lib.colourselect.__doc__
|
||||
|
||||
ColourSelect = wx.lib.colourselect.ColourSelect
|
||||
ColourSelectEvent = wx.lib.colourselect.ColourSelectEvent
|
||||
EVT_COLOURSELECT = wx.lib.colourselect.EVT_COLOURSELECT
|
||||
|
||||
@@ -1,385 +1,27 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.dialogs
|
||||
# Purpose: wxScrolledMessageDialog, wxMultipleChoiceDialog and
|
||||
# function wrappers for the common dialogs by Kevin Altis.
|
||||
#
|
||||
# Author: Various
|
||||
#
|
||||
# Created: 3-January-2002
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2002 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython import wx
|
||||
from layoutf import Layoutf
|
||||
import wx.lib.dialogs
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
__doc__ = wx.lib.dialogs.__doc__
|
||||
|
||||
class wxScrolledMessageDialog(wx.wxDialog):
|
||||
def __init__(self, parent, msg, caption, pos = wx.wxDefaultPosition, size = (500,300)):
|
||||
wx.wxDialog.__init__(self, parent, -1, caption, pos, size)
|
||||
x, y = pos
|
||||
if x == -1 and y == -1:
|
||||
self.CenterOnScreen(wx.wxBOTH)
|
||||
text = wx.wxTextCtrl(self, -1, msg, wx.wxDefaultPosition,
|
||||
wx.wxDefaultSize,
|
||||
wx.wxTE_MULTILINE | wx.wxTE_READONLY)
|
||||
ok = wx.wxButton(self, wx.wxID_OK, "OK")
|
||||
text.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok)))
|
||||
ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self,)))
|
||||
self.SetAutoLayout(1)
|
||||
self.Layout()
|
||||
|
||||
|
||||
class wxMultipleChoiceDialog(wx.wxDialog):
|
||||
def __init__(self, parent, msg, title, lst, pos = wx.wxDefaultPosition,
|
||||
size = (200,200), style = wx.wxDEFAULT_DIALOG_STYLE):
|
||||
wx.wxDialog.__init__(self, parent, -1, title, pos, size, style)
|
||||
x, y = pos
|
||||
if x == -1 and y == -1:
|
||||
self.CenterOnScreen(wx.wxBOTH)
|
||||
dc = wx.wxClientDC(self)
|
||||
height = 0
|
||||
for line in msg.splitlines():
|
||||
height = height + dc.GetTextExtent(line)[1] + 2
|
||||
stat = wx.wxStaticText(self, -1, msg)
|
||||
self.lbox = wx.wxListBox(self, 100, wx.wxDefaultPosition,
|
||||
wx.wxDefaultSize, lst, wx.wxLB_MULTIPLE)
|
||||
ok = wx.wxButton(self, wx.wxID_OK, "OK")
|
||||
cancel = wx.wxButton(self, wx.wxID_CANCEL, "Cancel")
|
||||
stat.SetConstraints(Layoutf('t=t10#1;l=l5#1;r=r5#1;h!%d' % (height,),
|
||||
(self,)))
|
||||
self.lbox.SetConstraints(Layoutf('t=b10#2;l=l5#1;r=r5#1;b=t5#3',
|
||||
(self, stat, ok)))
|
||||
ok.SetConstraints(Layoutf('b=b5#1;x%w25#1;w!80;h!25', (self,)))
|
||||
cancel.SetConstraints(Layoutf('b=b5#1;x%w75#1;w!80;h!25', (self,)))
|
||||
self.SetAutoLayout(1)
|
||||
self.lst = lst
|
||||
self.Layout()
|
||||
|
||||
def GetValue(self):
|
||||
return self.lbox.GetSelections()
|
||||
|
||||
def GetValueString(self):
|
||||
sel = self.lbox.GetSelections()
|
||||
val = []
|
||||
for i in sel:
|
||||
val.append(self.lst[i])
|
||||
return tuple(val)
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
"""
|
||||
function wrappers for wxPython system dialogs
|
||||
Author: Kevin Altis
|
||||
Date: 2003-1-2
|
||||
Rev: 3
|
||||
|
||||
This is the third refactor of the PythonCard dialog.py module
|
||||
for inclusion in the main wxPython distribution. There are a number of
|
||||
design decisions and subsequent code refactoring to be done, so I'm
|
||||
releasing this just to get some feedback.
|
||||
|
||||
rev 3:
|
||||
- result dictionary replaced by DialogResults class instance
|
||||
- should message arg be replaced with msg? most wxWindows dialogs
|
||||
seem to use the abbreviation?
|
||||
|
||||
rev 2:
|
||||
- All dialog classes have been replaced by function wrappers
|
||||
- Changed arg lists to more closely match wxWindows docs and wxPython.lib.dialogs
|
||||
- changed 'returned' value to the actual button id the user clicked on
|
||||
- added a returnedString value for the string version of the return value
|
||||
- reworked colorDialog and fontDialog so you can pass in just a color or font
|
||||
for the most common usage case
|
||||
- probably need to use colour instead of color to match the English English
|
||||
spelling in wxWindows (sigh)
|
||||
- I still think we could lose the parent arg and just always use None
|
||||
"""
|
||||
|
||||
class DialogResults:
|
||||
def __init__(self, returned):
|
||||
self.returned = returned
|
||||
self.accepted = returned in (wx.wxID_OK, wx.wxID_YES)
|
||||
self.returnedString = returnedString(returned)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.__dict__)
|
||||
|
||||
def returnedString(ret):
|
||||
if ret == wx.wxID_OK:
|
||||
return "Ok"
|
||||
elif ret == wx.wxID_CANCEL:
|
||||
return "Cancel"
|
||||
elif ret == wx.wxID_YES:
|
||||
return "Yes"
|
||||
elif ret == wx.wxID_NO:
|
||||
return "No"
|
||||
|
||||
|
||||
# findDialog was created before wxPython got a Find/Replace dialog
|
||||
# but it may be instructive as to how a function wrapper can
|
||||
# be added for your own custom dialogs
|
||||
# this dialog is always modal, while wxFindReplaceDialog is
|
||||
# modeless and so doesn't lend itself to a function wrapper
|
||||
def findDialog(parent=None, searchText='', wholeWordsOnly=0, caseSensitive=0):
|
||||
dlg = wx.wxDialog(parent, -1, "Find", wx.wxDefaultPosition, wx.wxSize(370, 120))
|
||||
|
||||
wx.wxStaticText(dlg, -1, 'Find what:', wx.wxPoint(7, 10))
|
||||
wSearchText = wx.wxTextCtrl(dlg, -1, searchText,
|
||||
wx.wxPoint(70, 7), wx.wxSize(195, -1))
|
||||
wSearchText.SetValue(searchText)
|
||||
wx.wxButton(dlg, wx.wxID_OK, "Find Next", wx.wxPoint(280, 5), wx.wxDefaultSize).SetDefault()
|
||||
wx.wxButton(dlg, wx.wxID_CANCEL, "Cancel", wx.wxPoint(280, 35), wx.wxDefaultSize)
|
||||
wWholeWord = wx.wxCheckBox(dlg, -1, 'Match whole word only',
|
||||
wx.wxPoint(7, 35), wx.wxDefaultSize, wx.wxNO_BORDER)
|
||||
if wholeWordsOnly:
|
||||
wWholeWord.SetValue(1)
|
||||
wCase = wx.wxCheckBox(dlg, -1, 'Match case',
|
||||
wx.wxPoint(7, 55), wx.wxDefaultSize, wx.wxNO_BORDER)
|
||||
if caseSensitive:
|
||||
wCase.SetValue(1)
|
||||
wSearchText.SetSelection(0, len(wSearchText.GetValue()))
|
||||
wSearchText.SetFocus()
|
||||
|
||||
result = DialogResults(dlg.ShowModal())
|
||||
result.text = wSearchText.GetValue()
|
||||
result.wholeword = wWholeWord.GetValue()
|
||||
result.casesensitive = wCase.GetValue()
|
||||
dlg.Destroy()
|
||||
return result
|
||||
|
||||
|
||||
def colorDialog(parent=None, colorData=None, color=None):
|
||||
if colorData:
|
||||
dialog = wx.wxColourDialog(parent, colorData)
|
||||
else:
|
||||
dialog = wx.wxColourDialog(parent)
|
||||
dialog.GetColourData().SetChooseFull(1)
|
||||
if color is not None:
|
||||
dialog.GetColourData().SetColour(color)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
result.colorData = dialog.GetColourData()
|
||||
result.color = result.colorData.GetColour().Get()
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
# it is easier to just duplicate the code than
|
||||
# try and replace color with colour in the result
|
||||
def colourDialog(parent=None, colourData=None, colour=None):
|
||||
if colourData:
|
||||
dialog = wx.wxColourDialog(parent, colourData)
|
||||
else:
|
||||
dialog = wx.wxColourDialog(parent)
|
||||
dialog.GetColourData().SetChooseFull(1)
|
||||
if colour is not None:
|
||||
dialog.GetColourData().SetColour(color)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
result.colourData = dialog.GetColourData()
|
||||
result.colour = result.colourData.GetColour().Get()
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
|
||||
def fontDialog(parent=None, fontData=None, font=None):
|
||||
if fontData is None:
|
||||
fontData = wx.wxFontData()
|
||||
if font is not None:
|
||||
aFontData.SetInitialFont(font)
|
||||
dialog = wx.wxFontDialog(parent, fontData)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
if result.accepted:
|
||||
fontData = dialog.GetFontData()
|
||||
result.fontData = fontData
|
||||
result.color = fontData.GetColour().Get()
|
||||
result.colour = result.color
|
||||
result.font = fontData.GetChosenFont()
|
||||
else:
|
||||
result.color = None
|
||||
result.colour = None
|
||||
result.font = None
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
|
||||
def textEntryDialog(parent=None, message='', title='', defaultText='', style=wx.wxOK | wx.wxCANCEL):
|
||||
dialog = wx.wxTextEntryDialog(parent, message, title, defaultText, style)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
result.text = dialog.GetValue()
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
|
||||
def messageDialog(parent=None, message='', title='Message box',
|
||||
aStyle = wx.wxOK | wx.wxCANCEL | wx.wxCENTRE,
|
||||
pos=wx.wxDefaultPosition):
|
||||
dialog = wx.wxMessageDialog(parent, message, title, aStyle, pos)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
|
||||
# KEA alerts are common, so I'm providing a class rather than
|
||||
# requiring the user code to set up the right icons and buttons
|
||||
# the with messageDialog function
|
||||
def alertDialog(parent=None, message='', title='Alert', pos=wx.wxDefaultPosition):
|
||||
return messageDialog(parent, message, title, wx.wxICON_EXCLAMATION | wx.wxOK, pos)
|
||||
|
||||
|
||||
def scrolledMessageDialog(parent=None, message='', title='', pos=wx.wxDefaultPosition, size=(500,300)):
|
||||
dialog = wxScrolledMessageDialog(parent, message, title, pos, size)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
|
||||
def fileDialog(parent=None, title='Open', directory='', filename='', wildcard='*.*',
|
||||
style=wx.wxOPEN | wx.wxMULTIPLE):
|
||||
dialog = wx.wxFileDialog(parent, title, directory, filename, wildcard, style)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
if result.accepted:
|
||||
result.paths = dialog.GetPaths()
|
||||
else:
|
||||
result.paths = None
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
|
||||
# openFileDialog and saveFileDialog are convenience functions
|
||||
# they represent the most common usages of the fileDialog
|
||||
# with the most common style options
|
||||
def openFileDialog(parent=None, title='Open', directory='', filename='',
|
||||
wildcard='All Files (*.*)|*.*',
|
||||
style=wx.wxOPEN | wx.wxMULTIPLE):
|
||||
return fileDialog(parent, title, directory, filename, wildcard, style)
|
||||
|
||||
|
||||
def saveFileDialog(parent=None, title='Save', directory='', filename='',
|
||||
wildcard='All Files (*.*)|*.*',
|
||||
style=wx.wxSAVE | wx.wxHIDE_READONLY | wx.wxOVERWRITE_PROMPT):
|
||||
return fileDialog(parent, title, directory, filename, wildcard, style)
|
||||
|
||||
|
||||
def dirDialog(parent=None, message='Choose a directory', path='', style=0,
|
||||
pos=wx.wxDefaultPosition, size=wx.wxDefaultSize):
|
||||
dialog = wx.wxDirDialog(parent, message, path, style, pos, size)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
if result.accepted:
|
||||
result.path = dialog.GetPath()
|
||||
else:
|
||||
result.path = None
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
directoryDialog = dirDialog
|
||||
|
||||
|
||||
def singleChoiceDialog(parent=None, message='', title='', lst=[],
|
||||
style=wx.wxOK | wx.wxCANCEL | wx.wxCENTRE):
|
||||
dialog = wx.wxSingleChoiceDialog(parent,
|
||||
message,
|
||||
title,
|
||||
lst,
|
||||
style)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
result.selection = dialog.GetStringSelection()
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
|
||||
def multipleChoiceDialog(parent=None, message='', title='', lst=[], pos=wx.wxDefaultPosition, size=(200,200)):
|
||||
dialog = wxMultipleChoiceDialog(parent, message, title, lst, pos, size)
|
||||
result = DialogResults(dialog.ShowModal())
|
||||
result.selection = dialog.GetValueString()
|
||||
dialog.Destroy()
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
class MyApp(wx.wxApp):
|
||||
|
||||
def OnInit(self):
|
||||
frame = wx.wxFrame(wx.NULL, -1, "Dialogs", size=(400, 200))
|
||||
panel = wx.wxPanel(frame, -1)
|
||||
self.panel = panel
|
||||
|
||||
frame.Show(1)
|
||||
|
||||
dialogNames = [
|
||||
'alertDialog',
|
||||
'colorDialog',
|
||||
'directoryDialog',
|
||||
'fileDialog',
|
||||
'findDialog',
|
||||
'fontDialog',
|
||||
'messageDialog',
|
||||
'multipleChoiceDialog',
|
||||
'openFileDialog',
|
||||
'saveFileDialog',
|
||||
'scrolledMessageDialog',
|
||||
'singleChoiceDialog',
|
||||
'textEntryDialog',
|
||||
]
|
||||
self.nameList = wx.wxListBox(panel, -1, (0, 0), (130, 180), dialogNames, style=wx.wxLB_SINGLE)
|
||||
wx.EVT_LISTBOX(panel, self.nameList.GetId(), self.OnNameListSelected)
|
||||
|
||||
tstyle = wx.wxTE_RICH2 | wx.wxTE_PROCESS_TAB | wx.wxTE_MULTILINE
|
||||
self.text1 = wx.wxTextCtrl(panel, -1, pos=(150, 0), size=(200, 180), style=tstyle)
|
||||
|
||||
self.SetTopWindow(frame)
|
||||
|
||||
return 1
|
||||
|
||||
def OnNameListSelected(self, evt):
|
||||
import pprint
|
||||
sel = evt.GetString()
|
||||
result = None
|
||||
if sel == 'alertDialog':
|
||||
result = alertDialog(message='Danger Will Robinson')
|
||||
elif sel == 'colorDialog':
|
||||
result = colorDialog()
|
||||
elif sel == 'directoryDialog':
|
||||
result = directoryDialog()
|
||||
elif sel == 'fileDialog':
|
||||
wildcard = "JPG files (*.jpg;*.jpeg)|*.jpeg;*.JPG;*.JPEG;*.jpg|GIF files (*.gif)|*.GIF;*.gif|All Files (*.*)|*.*"
|
||||
result = fileDialog(None, 'Open', '', '', wildcard)
|
||||
elif sel == 'findDialog':
|
||||
result = findDialog()
|
||||
elif sel == 'fontDialog':
|
||||
result = fontDialog()
|
||||
elif sel == 'messageDialog':
|
||||
result = messageDialog(None, 'Hello from Python and wxPython!',
|
||||
'A Message Box', wx.wxOK | wx.wxICON_INFORMATION)
|
||||
#wx.wxYES_NO | wx.wxNO_DEFAULT | wx.wxCANCEL | wx.wxICON_INFORMATION)
|
||||
#result = messageDialog(None, 'message', 'title')
|
||||
elif sel == 'multipleChoiceDialog':
|
||||
result = multipleChoiceDialog(None, "message", "title", ['one', 'two', 'three'])
|
||||
elif sel == 'openFileDialog':
|
||||
result = openFileDialog()
|
||||
elif sel == 'saveFileDialog':
|
||||
result = saveFileDialog()
|
||||
elif sel == 'scrolledMessageDialog':
|
||||
msg = "Can't find the file dialog.py"
|
||||
try:
|
||||
# read this source file and then display it
|
||||
import sys
|
||||
filename = sys.argv[-1]
|
||||
fp = open(filename)
|
||||
message = fp.read()
|
||||
fp.close()
|
||||
except:
|
||||
pass
|
||||
result = scrolledMessageDialog(None, message, filename)
|
||||
elif sel == 'singleChoiceDialog':
|
||||
result = singleChoiceDialog(None, "message", "title", ['one', 'two', 'three'])
|
||||
elif sel == 'textEntryDialog':
|
||||
result = textEntryDialog(None, "message", "title", "text")
|
||||
|
||||
if result:
|
||||
#self.text1.SetValue(pprint.pformat(result.__dict__))
|
||||
self.text1.SetValue(str(result))
|
||||
|
||||
app = MyApp(0)
|
||||
app.MainLoop()
|
||||
DialogResults = wx.lib.dialogs.DialogResults
|
||||
alertDialog = wx.lib.dialogs.alertDialog
|
||||
colorDialog = wx.lib.dialogs.colorDialog
|
||||
colourDialog = wx.lib.dialogs.colourDialog
|
||||
dirDialog = wx.lib.dialogs.dirDialog
|
||||
directoryDialog = wx.lib.dialogs.directoryDialog
|
||||
fileDialog = wx.lib.dialogs.fileDialog
|
||||
findDialog = wx.lib.dialogs.findDialog
|
||||
fontDialog = wx.lib.dialogs.fontDialog
|
||||
messageDialog = wx.lib.dialogs.messageDialog
|
||||
multipleChoiceDialog = wx.lib.dialogs.multipleChoiceDialog
|
||||
openFileDialog = wx.lib.dialogs.openFileDialog
|
||||
returnedString = wx.lib.dialogs.returnedString
|
||||
saveFileDialog = wx.lib.dialogs.saveFileDialog
|
||||
scrolledMessageDialog = wx.lib.dialogs.scrolledMessageDialog
|
||||
singleChoiceDialog = wx.lib.dialogs.singleChoiceDialog
|
||||
textEntryDialog = wx.lib.dialogs.textEntryDialog
|
||||
wxMultipleChoiceDialog = wx.lib.dialogs.wxMultipleChoiceDialog
|
||||
wxScrolledMessageDialog = wx.lib.dialogs.wxScrolledMessageDialog
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
wxEditor component
|
||||
------------------
|
||||
|
||||
The wxEditor class implements a simple text editor using wxPython. You
|
||||
can create a custom editor by subclassing wxEditor. Even though much of
|
||||
the editor is implemented in Python, it runs surprisingly smoothly on
|
||||
normal hardware with small files.
|
||||
|
||||
|
||||
Keys
|
||||
----
|
||||
Keys are similar to Windows-based editors:
|
||||
|
||||
Tab: 1 to 4 spaces (to next tab stop)
|
||||
Cursor movement: Arrow keys
|
||||
Beginning of line: Home
|
||||
End of line: End
|
||||
Beginning of buffer: Control-Home
|
||||
End of the buffer: Control-End
|
||||
Select text: Hold down Shift while moving the cursor
|
||||
Copy: Shift-Insert, Control-C
|
||||
Cut: Shift-Delete, Control-X
|
||||
Paste: Control-Insert, Control-V
|
||||
|
||||
How to use it
|
||||
-------------
|
||||
The demo code (demo/wxEditor.py) shows how to use it as a simple text
|
||||
box. Use the SetText() and GetText() methods to set or get text from
|
||||
the component; these both return a list of strings.
|
||||
|
||||
The samples/FrogEdit directory has an example of a simple text editor
|
||||
application that uses the wxEditor component.
|
||||
|
||||
Subclassing
|
||||
-----------
|
||||
To add or change functionality, you can subclass this
|
||||
component. One example of this might be to change the key
|
||||
Alt key commands. In that case you would (for example) override the
|
||||
SetAltFuncs() method.
|
||||
|
||||
History
|
||||
-------
|
||||
The original author of this component was Dirk Holtwic. It originally
|
||||
had limited support for syntax highlighting, but was not a usable text
|
||||
editor, as it didn't implement select (with keys or mouse), or any of
|
||||
the usual key sequences you'd expect in an editor. Robin Dunn did some
|
||||
refactoring work to make it more usable. Steve Howell and Adam Feuer
|
||||
did a lot of refactoring, and added some functionality, including
|
||||
keyboard and mouse select, properly working scrollbars, and
|
||||
overridable keys. Adam and Steve also removed support for
|
||||
syntax-highlighting while refactoring the code.
|
||||
|
||||
To do
|
||||
-----
|
||||
Alt/Ctrl Arrow keys move by word
|
||||
Descriptive help text for keys
|
||||
Speed improvements
|
||||
Different fonts/colors
|
||||
|
||||
|
||||
Authors
|
||||
-------
|
||||
Steve Howell, Adam Feuer, Dirk Holtwic, Robin Dunn
|
||||
|
||||
|
||||
Contact
|
||||
-------
|
||||
You can find the latest code for wxEditor here:
|
||||
http://www.pobox.com/~adamf/software/
|
||||
|
||||
We're not actively maintaining this code, but we can answer
|
||||
questions about it. You can email us at:
|
||||
|
||||
Adam Feuer <adamf at pobox dot com>
|
||||
Steve Howell <showell at zipcon dot net>
|
||||
|
||||
29 November 2001
|
||||
@@ -1,17 +1 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.editor
|
||||
# Purpose: A package containing a colourizable text editror
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 30-Dec-1999
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 1999 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
# This file makes this directory into a Python package
|
||||
|
||||
|
||||
# import the main classes into the package namespace.
|
||||
from editor import wxEditor
|
||||
|
||||
@@ -1,956 +1,13 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.editor.wxEditor
|
||||
# Purpose: An intelligent text editor with colorization capabilities.
|
||||
#
|
||||
# Original
|
||||
# Authors: Dirk Holtwic, Robin Dunn
|
||||
#
|
||||
# New
|
||||
# Authors: Adam Feuer, Steve Howell
|
||||
#
|
||||
# History:
|
||||
# This code used to support a fairly complex subclass that did
|
||||
# syntax coloring and outliner collapse mode. Adam and Steve
|
||||
# inherited the code, and added a lot of basic editor
|
||||
# functionality that had not been there before, such as cut-and-paste.
|
||||
#
|
||||
#
|
||||
# Created: 15-Dec-1999
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 1999 by Dirk Holtwick, 1999
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import os, time
|
||||
import wx.lib.editor.editor
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
import selection
|
||||
import images
|
||||
|
||||
#----------------------------
|
||||
|
||||
def ForceBetween(min, val, max):
|
||||
if val > max:
|
||||
return max
|
||||
if val < min:
|
||||
return min
|
||||
return val
|
||||
|
||||
|
||||
def LineTrimmer(lineOfText):
|
||||
if len(lineOfText) == 0:
|
||||
return ""
|
||||
elif lineOfText[-1] == '\r':
|
||||
return lineOfText[:-1]
|
||||
else:
|
||||
return lineOfText
|
||||
|
||||
def LineSplitter(text):
|
||||
return map (LineTrimmer, text.split('\n'))
|
||||
|
||||
|
||||
#----------------------------
|
||||
|
||||
class Scroller:
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self.ow = 0
|
||||
self.oh = 0
|
||||
self.ox = 0
|
||||
self.oy = 0
|
||||
|
||||
def SetScrollbars(self, fw, fh, w, h, x, y):
|
||||
if (self.ow != w or self.oh != h or self.ox != x or self.oy != y):
|
||||
self.parent.SetScrollbars(fw, fh, w, h, x, y)
|
||||
self.ow = w
|
||||
self.oh = h
|
||||
self.ox = x
|
||||
self.oy = y
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class wxEditor(wxScrolledWindow):
|
||||
|
||||
def __init__(self, parent, id,
|
||||
pos=wxDefaultPosition, size=wxDefaultSize, style=0):
|
||||
|
||||
wxScrolledWindow.__init__(self, parent, id,
|
||||
pos, size,
|
||||
style|wxWANTS_CHARS)
|
||||
|
||||
self.isDrawing = False
|
||||
|
||||
self.InitCoords()
|
||||
self.InitFonts()
|
||||
self.SetColors()
|
||||
self.MapEvents()
|
||||
self.LoadImages()
|
||||
self.InitDoubleBuffering()
|
||||
self.InitScrolling()
|
||||
self.SelectOff()
|
||||
self.SetFocus()
|
||||
self.SetText([""])
|
||||
self.SpacesPerTab = 4
|
||||
|
||||
##------------------ Init stuff
|
||||
|
||||
def InitCoords(self):
|
||||
self.cx = 0
|
||||
self.cy = 0
|
||||
self.oldCx = 0
|
||||
self.oldCy = 0
|
||||
self.sx = 0
|
||||
self.sy = 0
|
||||
self.sw = 0
|
||||
self.sh = 0
|
||||
self.sco_x = 0
|
||||
self.sco_y = 0
|
||||
|
||||
def MapEvents(self):
|
||||
EVT_LEFT_DOWN(self, self.OnLeftDown)
|
||||
EVT_LEFT_UP(self, self.OnLeftUp)
|
||||
EVT_MOTION(self, self.OnMotion)
|
||||
EVT_SCROLLWIN(self, self.OnScroll)
|
||||
EVT_CHAR(self, self.OnChar)
|
||||
EVT_PAINT(self, self.OnPaint)
|
||||
EVT_SIZE(self, self.OnSize)
|
||||
EVT_WINDOW_DESTROY(self, self.OnDestroy)
|
||||
EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
|
||||
|
||||
##------------------- Platform-specific stuff
|
||||
|
||||
def NiceFontForPlatform(self):
|
||||
if wxPlatform == "__WXMSW__":
|
||||
return wxFont(10, wxMODERN, wxNORMAL, wxNORMAL)
|
||||
else:
|
||||
return wxFont(12, wxMODERN, wxNORMAL, wxNORMAL, False)
|
||||
|
||||
def UnixKeyHack(self, key):
|
||||
# this will be obsolete when we get the new wxWindows patch
|
||||
if key <= 26:
|
||||
key += ord('a') - 1
|
||||
return key
|
||||
|
||||
##-------------------- UpdateView/Cursor code
|
||||
|
||||
def OnSize(self, event):
|
||||
self.AdjustScrollbars()
|
||||
self.SetFocus()
|
||||
|
||||
def SetCharDimensions(self):
|
||||
# TODO: We need a code review on this. It appears that Linux
|
||||
# improperly reports window dimensions when the scrollbar's there.
|
||||
self.bw, self.bh = self.GetClientSizeTuple()
|
||||
|
||||
if wxPlatform == "__WXMSW__":
|
||||
self.sh = self.bh / self.fh
|
||||
self.sw = (self.bw / self.fw) - 1
|
||||
else:
|
||||
self.sh = self.bh / self.fh
|
||||
if self.LinesInFile() >= self.sh:
|
||||
self.bw = self.bw - wxSystemSettings_GetSystemMetric(wxSYS_VSCROLL_X)
|
||||
self.sw = (self.bw / self.fw) - 1
|
||||
|
||||
self.sw = (self.bw / self.fw) - 1
|
||||
if self.CalcMaxLineLen() >= self.sw:
|
||||
self.bh = self.bh - wxSystemSettings_GetSystemMetric(wxSYS_HSCROLL_Y)
|
||||
self.sh = self.bh / self.fh
|
||||
|
||||
|
||||
def UpdateView(self, dc = None):
|
||||
if dc is None:
|
||||
dc = wxClientDC(self)
|
||||
if dc.Ok():
|
||||
self.SetCharDimensions()
|
||||
self.KeepCursorOnScreen()
|
||||
self.DrawSimpleCursor(0,0,dc, True)
|
||||
self.Draw(dc)
|
||||
|
||||
def OnPaint(self, event):
|
||||
dc = wxPaintDC(self)
|
||||
if self.isDrawing:
|
||||
return
|
||||
self.isDrawing = True
|
||||
self.UpdateView(dc)
|
||||
wxCallAfter(self.AdjustScrollbars)
|
||||
self.isDrawing = False
|
||||
|
||||
def OnEraseBackground(self, evt):
|
||||
pass
|
||||
|
||||
##-------------------- Drawing code
|
||||
|
||||
def InitFonts(self):
|
||||
dc = wxClientDC(self)
|
||||
self.font = self.NiceFontForPlatform()
|
||||
dc.SetFont(self.font)
|
||||
self.fw = dc.GetCharWidth()
|
||||
self.fh = dc.GetCharHeight()
|
||||
|
||||
def SetColors(self):
|
||||
self.fgColor = wxNamedColour('black')
|
||||
self.bgColor = wxNamedColour('white')
|
||||
self.selectColor = wxColour(238, 220, 120) # r, g, b = emacsOrange
|
||||
|
||||
def InitDoubleBuffering(self):
|
||||
pass
|
||||
|
||||
def DrawEditText(self, t, x, y, dc):
|
||||
dc.DrawText(t, x * self.fw, y * self.fh)
|
||||
|
||||
def DrawLine(self, line, dc):
|
||||
if self.IsLine(line):
|
||||
l = line
|
||||
t = self.lines[l]
|
||||
dc.SetTextForeground(self.fgColor)
|
||||
fragments = selection.Selection(
|
||||
self.SelectBegin, self.SelectEnd,
|
||||
self.sx, self.sw, line, t)
|
||||
x = 0
|
||||
for (data, selected) in fragments:
|
||||
if selected:
|
||||
dc.SetTextBackground(self.selectColor)
|
||||
if x == 0 and len(data) == 0 and len(fragments) == 1:
|
||||
data = ' '
|
||||
else:
|
||||
dc.SetTextBackground(self.bgColor)
|
||||
self.DrawEditText(data, x, line - self.sy, dc)
|
||||
x += len(data)
|
||||
|
||||
def Draw(self, odc=None):
|
||||
if not odc:
|
||||
odc = wxClientDC(self)
|
||||
|
||||
bmp = wxEmptyBitmap(max(1,self.bw), max(1,self.bh))
|
||||
dc = wxBufferedDC(odc, bmp)
|
||||
if dc.Ok():
|
||||
dc.SetFont(self.font)
|
||||
dc.SetBackgroundMode(wxSOLID)
|
||||
dc.SetTextBackground(self.bgColor)
|
||||
dc.SetTextForeground(self.fgColor)
|
||||
dc.Clear()
|
||||
for line in range(self.sy, self.sy + self.sh):
|
||||
self.DrawLine(line, dc)
|
||||
if len(self.lines) < self.sh + self.sy:
|
||||
self.DrawEofMarker(dc)
|
||||
self.DrawCursor(dc)
|
||||
|
||||
##------------------ eofMarker stuff
|
||||
|
||||
def LoadImages(self):
|
||||
self.eofMarker = images.GetBitmap(images.EofImageData)
|
||||
|
||||
def DrawEofMarker(self,dc):
|
||||
x = 0
|
||||
y = (len(self.lines) - self.sy) * self.fh
|
||||
hasTransparency = 1
|
||||
dc.DrawBitmap(self.eofMarker, x, y, hasTransparency)
|
||||
|
||||
##------------------ cursor-related functions
|
||||
|
||||
def DrawCursor(self, dc = None):
|
||||
if not dc:
|
||||
dc = wxClientDC(self)
|
||||
|
||||
if (self.LinesInFile())<self.cy: #-1 ?
|
||||
self.cy = self.LinesInFile()-1
|
||||
s = self.lines[self.cy]
|
||||
|
||||
x = self.cx - self.sx
|
||||
y = self.cy - self.sy
|
||||
self.DrawSimpleCursor(x, y, dc)
|
||||
|
||||
|
||||
def DrawSimpleCursor(self, xp, yp, dc = None, old=False):
|
||||
if not dc:
|
||||
dc = wxClientDC(self)
|
||||
|
||||
if old:
|
||||
xp = self.sco_x
|
||||
yp = self.sco_y
|
||||
|
||||
szx = self.fw
|
||||
szy = self.fh
|
||||
x = xp * szx
|
||||
y = yp * szy
|
||||
dc.Blit(x,y,szx,szy,dc,x,y,wxSRC_INVERT)
|
||||
self.sco_x = xp
|
||||
self.sco_y = yp
|
||||
|
||||
##-------- Enforcing screen boundaries, cursor movement
|
||||
|
||||
def CalcMaxLineLen(self):
|
||||
"""get length of longest line on screen"""
|
||||
maxlen = 0
|
||||
for line in self.lines[self.sy:self.sy+self.sh]:
|
||||
if len(line) >maxlen:
|
||||
maxlen = len(line)
|
||||
return maxlen
|
||||
|
||||
def KeepCursorOnScreen(self):
|
||||
self.sy = ForceBetween(max(0, self.cy-self.sh), self.sy, self.cy)
|
||||
self.sx = ForceBetween(max(0, self.cx-self.sw), self.sx, self.cx)
|
||||
self.AdjustScrollbars()
|
||||
|
||||
def HorizBoundaries(self):
|
||||
self.SetCharDimensions()
|
||||
maxLineLen = self.CalcMaxLineLen()
|
||||
self.sx = ForceBetween(0, self.sx, max(self.sw, maxLineLen - self.sw + 1))
|
||||
self.cx = ForceBetween(self.sx, self.cx, self.sx + self.sw - 1)
|
||||
|
||||
def VertBoundaries(self):
|
||||
self.SetCharDimensions()
|
||||
self.sy = ForceBetween(0, self.sy, max(self.sh, self.LinesInFile() - self.sh + 1))
|
||||
self.cy = ForceBetween(self.sy, self.cy, self.sy + self.sh - 1)
|
||||
|
||||
def cVert(self, num):
|
||||
self.cy = self.cy + num
|
||||
self.cy = ForceBetween(0, self.cy, self.LinesInFile() - 1)
|
||||
self.sy = ForceBetween(self.cy - self.sh + 1, self.sy, self.cy)
|
||||
self.cx = min(self.cx, self.CurrentLineLength())
|
||||
|
||||
def cHoriz(self, num):
|
||||
self.cx = self.cx + num
|
||||
self.cx = ForceBetween(0, self.cx, self.CurrentLineLength())
|
||||
self.sx = ForceBetween(self.cx - self.sw + 1, self.sx, self.cx)
|
||||
|
||||
def AboveScreen(self, row):
|
||||
return row < self.sy
|
||||
|
||||
def BelowScreen(self, row):
|
||||
return row >= self.sy + self.sh
|
||||
|
||||
def LeftOfScreen(self, col):
|
||||
return col < self.sx
|
||||
|
||||
def RightOfScreen(self, col):
|
||||
return col >= self.sx + self.sw
|
||||
|
||||
##----------------- data structure helper functions
|
||||
|
||||
def GetText(self):
|
||||
return self.lines
|
||||
|
||||
def SetText(self, lines):
|
||||
self.InitCoords()
|
||||
self.lines = lines
|
||||
self.UnTouchBuffer()
|
||||
self.SelectOff()
|
||||
self.AdjustScrollbars()
|
||||
self.UpdateView(None)
|
||||
|
||||
def IsLine(self, lineNum):
|
||||
return (0<=lineNum) and (lineNum<self.LinesInFile())
|
||||
|
||||
def GetTextLine(self, lineNum):
|
||||
if self.IsLine(lineNum):
|
||||
return self.lines[lineNum]
|
||||
return ""
|
||||
|
||||
def SetTextLine(self, lineNum, text):
|
||||
if self.IsLine(lineNum):
|
||||
self.lines[lineNum] = text
|
||||
|
||||
def CurrentLineLength(self):
|
||||
return len(self.lines[self.cy])
|
||||
|
||||
def LinesInFile(self):
|
||||
return len(self.lines)
|
||||
|
||||
def UnTouchBuffer(self):
|
||||
self.bufferTouched = False
|
||||
|
||||
def BufferWasTouched(self):
|
||||
return self.bufferTouched
|
||||
|
||||
def TouchBuffer(self):
|
||||
self.bufferTouched = True
|
||||
|
||||
|
||||
##-------------------------- Mouse scroll timing functions
|
||||
|
||||
def InitScrolling(self):
|
||||
# we don't rely on the windows system to scroll for us; we just
|
||||
# redraw the screen manually every time
|
||||
self.EnableScrolling(False, False)
|
||||
self.nextScrollTime = 0
|
||||
self.SCROLLDELAY = 0.050 # seconds
|
||||
self.scrollTimer = wxTimer(self)
|
||||
self.scroller = Scroller(self)
|
||||
|
||||
def CanScroll(self):
|
||||
if time.time() > self.nextScrollTime:
|
||||
self.nextScrollTime = time.time() + self.SCROLLDELAY
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def SetScrollTimer(self):
|
||||
oneShot = True
|
||||
self.scrollTimer.Start(1000*self.SCROLLDELAY/2, oneShot)
|
||||
EVT_TIMER(self, -1, self.OnTimer)
|
||||
|
||||
def OnTimer(self, event):
|
||||
screenX, screenY = wxGetMousePosition()
|
||||
x, y = self.ScreenToClientXY(screenX, screenY)
|
||||
self.MouseToRow(y)
|
||||
self.MouseToCol(x)
|
||||
self.SelectUpdate()
|
||||
|
||||
##-------------------------- Mouse off screen functions
|
||||
|
||||
def HandleAboveScreen(self, row):
|
||||
self.SetScrollTimer()
|
||||
if self.CanScroll():
|
||||
row = self.sy - 1
|
||||
row = max(0, row)
|
||||
self.cy = row
|
||||
|
||||
def HandleBelowScreen(self, row):
|
||||
self.SetScrollTimer()
|
||||
if self.CanScroll():
|
||||
row = self.sy + self.sh
|
||||
row = min(row, self.LinesInFile() - 1)
|
||||
self.cy = row
|
||||
|
||||
def HandleLeftOfScreen(self, col):
|
||||
self.SetScrollTimer()
|
||||
if self.CanScroll():
|
||||
col = self.sx - 1
|
||||
col = max(0,col)
|
||||
self.cx = col
|
||||
|
||||
def HandleRightOfScreen(self, col):
|
||||
self.SetScrollTimer()
|
||||
if self.CanScroll():
|
||||
col = self.sx + self.sw
|
||||
col = min(col, self.CurrentLineLength())
|
||||
self.cx = col
|
||||
|
||||
##------------------------ mousing functions
|
||||
|
||||
def MouseToRow(self, mouseY):
|
||||
row = self.sy + (mouseY/ self.fh)
|
||||
if self.AboveScreen(row):
|
||||
self.HandleAboveScreen(row)
|
||||
elif self.BelowScreen(row):
|
||||
self.HandleBelowScreen(row)
|
||||
else:
|
||||
self.cy = min(row, self.LinesInFile() - 1)
|
||||
|
||||
def MouseToCol(self, mouseX):
|
||||
col = self.sx + (mouseX / self.fw)
|
||||
if self.LeftOfScreen(col):
|
||||
self.HandleLeftOfScreen(col)
|
||||
elif self.RightOfScreen(col):
|
||||
self.HandleRightOfScreen(col)
|
||||
else:
|
||||
self.cx = min(col, self.CurrentLineLength())
|
||||
|
||||
def MouseToCursor(self, event):
|
||||
self.MouseToRow(event.GetY())
|
||||
self.MouseToCol(event.GetX())
|
||||
|
||||
def OnMotion(self, event):
|
||||
if event.LeftIsDown() and self.HasCapture():
|
||||
self.Selecting = True
|
||||
self.MouseToCursor(event)
|
||||
self.SelectUpdate()
|
||||
|
||||
def OnLeftDown(self, event):
|
||||
self.MouseToCursor(event)
|
||||
self.SelectBegin = (self.cy, self.cx)
|
||||
self.SelectEnd = None
|
||||
self.UpdateView()
|
||||
self.CaptureMouse()
|
||||
|
||||
def OnLeftUp(self, event):
|
||||
if not self.HasCapture():
|
||||
return
|
||||
|
||||
if self.SelectEnd is None:
|
||||
self.OnClick()
|
||||
else:
|
||||
self.Selecting = False
|
||||
self.SelectNotify(False, self.SelectBegin, self.SelectEnd)
|
||||
|
||||
self.ReleaseMouse()
|
||||
self.scrollTimer.Stop()
|
||||
|
||||
|
||||
#------------------------- Scrolling
|
||||
|
||||
def HorizScroll(self, event, eventType):
|
||||
maxLineLen = self.CalcMaxLineLen()
|
||||
|
||||
if eventType == wxEVT_SCROLLWIN_LINEUP:
|
||||
self.sx -= 1
|
||||
elif eventType == wxEVT_SCROLLWIN_LINEDOWN:
|
||||
self.sx += 1
|
||||
elif eventType == wxEVT_SCROLLWIN_PAGEUP:
|
||||
self.sx -= self.sw
|
||||
elif eventType == wxEVT_SCROLLWIN_PAGEDOWN:
|
||||
self.sx += self.sw
|
||||
elif eventType == wxEVT_SCROLLWIN_TOP:
|
||||
self.sx = self.cx = 0
|
||||
elif eventType == wxEVT_SCROLLWIN_BOTTOM:
|
||||
self.sx = maxLineLen - self.sw
|
||||
self.cx = maxLineLen
|
||||
else:
|
||||
self.sx = event.GetPosition()
|
||||
|
||||
self.HorizBoundaries()
|
||||
|
||||
def VertScroll(self, event, eventType):
|
||||
if eventType == wxEVT_SCROLLWIN_LINEUP:
|
||||
self.sy -= 1
|
||||
elif eventType == wxEVT_SCROLLWIN_LINEDOWN:
|
||||
self.sy += 1
|
||||
elif eventType == wxEVT_SCROLLWIN_PAGEUP:
|
||||
self.sy -= self.sh
|
||||
elif eventType == wxEVT_SCROLLWIN_PAGEDOWN:
|
||||
self.sy += self.sh
|
||||
elif eventType == wxEVT_SCROLLWIN_TOP:
|
||||
self.sy = self.cy = 0
|
||||
elif eventType == wxEVT_SCROLLWIN_BOTTOM:
|
||||
self.sy = self.LinesInFile() - self.sh
|
||||
self.cy = self.LinesInFile()
|
||||
else:
|
||||
self.sy = event.GetPosition()
|
||||
|
||||
self.VertBoundaries()
|
||||
|
||||
def OnScroll(self, event):
|
||||
dir = event.GetOrientation()
|
||||
eventType = event.GetEventType()
|
||||
if dir == wxHORIZONTAL:
|
||||
self.HorizScroll(event, eventType)
|
||||
else:
|
||||
self.VertScroll(event, eventType)
|
||||
self.UpdateView()
|
||||
|
||||
|
||||
def AdjustScrollbars(self):
|
||||
for i in range(2):
|
||||
self.SetCharDimensions()
|
||||
self.scroller.SetScrollbars(
|
||||
self.fw, self.fh,
|
||||
self.CalcMaxLineLen()+3, max(self.LinesInFile()+1, self.sh),
|
||||
self.sx, self.sy)
|
||||
|
||||
#------------ backspace, delete, return
|
||||
|
||||
def BreakLine(self, event):
|
||||
if self.IsLine(self.cy):
|
||||
t = self.lines[self.cy]
|
||||
self.lines = self.lines[:self.cy] + [t[:self.cx],t[self.cx:]] + self.lines[self.cy+1:]
|
||||
self.cVert(1)
|
||||
self.cx = 0
|
||||
self.TouchBuffer()
|
||||
|
||||
def InsertChar(self,char):
|
||||
if self.IsLine(self.cy):
|
||||
t = self.lines[self.cy]
|
||||
t = t[:self.cx] + char + t[self.cx:]
|
||||
self.SetTextLine(self.cy, t)
|
||||
self.cHoriz(1)
|
||||
self.TouchBuffer()
|
||||
|
||||
def JoinLines(self):
|
||||
t1 = self.lines[self.cy]
|
||||
t2 = self.lines[self.cy+1]
|
||||
self.cx = len(t1)
|
||||
self.lines = self.lines[:self.cy] + [t1 + t2] + self.lines[self.cy+2:]
|
||||
self.TouchBuffer()
|
||||
|
||||
|
||||
def DeleteChar(self,x,y,oldtext):
|
||||
newtext = oldtext[:x] + oldtext[x+1:]
|
||||
self.SetTextLine(y, newtext)
|
||||
self.TouchBuffer()
|
||||
|
||||
|
||||
def BackSpace(self, event):
|
||||
t = self.GetTextLine(self.cy)
|
||||
if self.cx>0:
|
||||
self.DeleteChar(self.cx-1,self.cy,t)
|
||||
self.cHoriz(-1)
|
||||
self.TouchBuffer()
|
||||
elif self.cx == 0:
|
||||
if self.cy > 0:
|
||||
self.cy -= 1
|
||||
self.JoinLines()
|
||||
self.TouchBuffer()
|
||||
else:
|
||||
wxBell()
|
||||
|
||||
def Delete(self, event):
|
||||
t = self.GetTextLine(self.cy)
|
||||
if self.cx<len(t):
|
||||
self.DeleteChar(self.cx,self.cy,t)
|
||||
self.TouchBuffer()
|
||||
else:
|
||||
if self.cy < len(self.lines) - 1:
|
||||
self.JoinLines()
|
||||
self.TouchBuffer()
|
||||
|
||||
def Escape(self, event):
|
||||
self.SelectOff()
|
||||
|
||||
def TabKey(self, event):
|
||||
numSpaces = self.SpacesPerTab - (self.cx % self.SpacesPerTab)
|
||||
self.SingleLineInsert(' ' * numSpaces)
|
||||
|
||||
##----------- selection routines
|
||||
|
||||
def SelectUpdate(self):
|
||||
self.SelectEnd = (self.cy, self.cx)
|
||||
self.SelectNotify(self.Selecting, self.SelectBegin, self.SelectEnd)
|
||||
self.UpdateView()
|
||||
|
||||
def NormalizedSelect(self):
|
||||
(begin, end) = (self.SelectBegin, self.SelectEnd)
|
||||
(bRow, bCol) = begin
|
||||
(eRow, eCol) = end
|
||||
if (bRow < eRow):
|
||||
return (begin, end)
|
||||
elif (eRow < bRow):
|
||||
return (end, begin)
|
||||
else:
|
||||
if (bCol < eCol):
|
||||
return (begin, end)
|
||||
else:
|
||||
return (end, begin)
|
||||
|
||||
def FindSelection(self):
|
||||
if self.SelectEnd is None or self.SelectBegin is None:
|
||||
wxBell()
|
||||
return None
|
||||
(begin, end) = self.NormalizedSelect()
|
||||
(bRow, bCol) = begin
|
||||
(eRow, eCol) = end
|
||||
return (bRow, bCol, eRow, eCol)
|
||||
|
||||
def SelectOff(self):
|
||||
self.SelectBegin = None
|
||||
self.SelectEnd = None
|
||||
self.Selecting = False
|
||||
self.SelectNotify(False,None,None)
|
||||
|
||||
def CopySelection(self, event):
|
||||
selection = self.FindSelection()
|
||||
if selection is None:
|
||||
return
|
||||
(bRow, bCol, eRow, eCol) = selection
|
||||
|
||||
if bRow == eRow:
|
||||
self.SingleLineCopy(bRow, bCol, eCol)
|
||||
else:
|
||||
self.MultipleLineCopy(bRow, bCol, eRow, eCol)
|
||||
|
||||
def OnCopySelection(self, event):
|
||||
self.CopySelection(event)
|
||||
self.SelectOff()
|
||||
|
||||
def CopyToClipboard(self, linesOfText):
|
||||
do = wxTextDataObject()
|
||||
do.SetText(os.linesep.join(linesOfText))
|
||||
wxTheClipboard.Open()
|
||||
wxTheClipboard.SetData(do)
|
||||
wxTheClipboard.Close()
|
||||
|
||||
def SingleLineCopy(self, Row, bCol, eCol):
|
||||
Line = self.GetTextLine(Row)
|
||||
self.CopyToClipboard([Line[bCol:eCol]])
|
||||
|
||||
def MultipleLineCopy(self, bRow, bCol, eRow, eCol):
|
||||
bLine = self.GetTextLine(bRow)[bCol:]
|
||||
eLine = self.GetTextLine(eRow)[:eCol]
|
||||
self.CopyToClipboard([bLine] + [l for l in self.lines[bRow + 1:eRow]] + [eLine])
|
||||
|
||||
def OnDeleteSelection(self, event):
|
||||
selection = self.FindSelection()
|
||||
if selection is None:
|
||||
return
|
||||
(bRow, bCol, eRow, eCol) = selection
|
||||
|
||||
if bRow == eRow:
|
||||
self.SingleLineDelete(bRow, bCol, eCol)
|
||||
else:
|
||||
self.MultipleLineDelete(bRow, bCol, eRow, eCol)
|
||||
|
||||
self.TouchBuffer()
|
||||
|
||||
self.cy = bRow
|
||||
self.cx = bCol
|
||||
self.SelectOff()
|
||||
self.UpdateView()
|
||||
|
||||
|
||||
def SingleLineDelete(self, Row, bCol, eCol):
|
||||
ModLine = self.GetTextLine(Row)
|
||||
ModLine = ModLine[:bCol] + ModLine[eCol:]
|
||||
self.SetTextLine(Row,ModLine)
|
||||
|
||||
def MultipleLineDelete(self, bRow, bCol, eRow, eCol):
|
||||
bLine = self.GetTextLine(bRow)
|
||||
eLine = self.GetTextLine(eRow)
|
||||
ModLine = bLine[:bCol] + eLine[eCol:]
|
||||
self.lines[bRow:eRow + 1] = [ModLine]
|
||||
|
||||
def OnPaste(self, event):
|
||||
do = wxTextDataObject()
|
||||
wxTheClipboard.Open()
|
||||
success = wxTheClipboard.GetData(do)
|
||||
wxTheClipboard.Close()
|
||||
if success:
|
||||
pastedLines = LineSplitter(do.GetText())
|
||||
else:
|
||||
wxBell()
|
||||
return
|
||||
if len(pastedLines) == 0:
|
||||
wxBell()
|
||||
return
|
||||
elif len(pastedLines) == 1:
|
||||
self.SingleLineInsert(pastedLines[0])
|
||||
else:
|
||||
self.MultipleLinePaste(pastedLines)
|
||||
|
||||
def SingleLineInsert(self, newText):
|
||||
ModLine = self.GetTextLine(self.cy)
|
||||
ModLine = ModLine[:self.cx] + newText + ModLine[self.cx:]
|
||||
self.SetTextLine(self.cy, ModLine)
|
||||
self.cHoriz(len(newText))
|
||||
self.TouchBuffer()
|
||||
self.UpdateView()
|
||||
|
||||
def MultipleLinePaste(self, pastedLines):
|
||||
FirstLine = LastLine = self.GetTextLine(self.cy)
|
||||
FirstLine = FirstLine[:self.cx] + pastedLines[0]
|
||||
LastLine = pastedLines[-1] + LastLine[self.cx:]
|
||||
|
||||
NewSlice = [FirstLine]
|
||||
NewSlice += [l for l in pastedLines[1:-1]]
|
||||
NewSlice += [LastLine]
|
||||
self.lines[self.cy:self.cy + 1] = NewSlice
|
||||
|
||||
self.cy = self.cy + len(pastedLines)-1
|
||||
self.cx = len(pastedLines[-1])
|
||||
self.TouchBuffer()
|
||||
self.UpdateView()
|
||||
|
||||
def OnCutSelection(self,event):
|
||||
self.CopySelection(event)
|
||||
self.OnDeleteSelection(event)
|
||||
|
||||
#-------------- Keyboard movement implementations
|
||||
|
||||
def MoveDown(self, event):
|
||||
self.cVert(+1)
|
||||
|
||||
def MoveUp(self, event):
|
||||
self.cVert(-1)
|
||||
|
||||
def MoveLeft(self, event):
|
||||
if self.cx == 0:
|
||||
if self.cy == 0:
|
||||
wxBell()
|
||||
else:
|
||||
self.cVert(-1)
|
||||
self.cx = self.CurrentLineLength()
|
||||
else:
|
||||
self.cx -= 1
|
||||
|
||||
def MoveRight(self, event):
|
||||
linelen = self.CurrentLineLength()
|
||||
if self.cx == linelen:
|
||||
if self.cy == len(self.lines) - 1:
|
||||
wxBell()
|
||||
else:
|
||||
self.cx = 0
|
||||
self.cVert(1)
|
||||
else:
|
||||
self.cx += 1
|
||||
|
||||
|
||||
def MovePageDown(self, event):
|
||||
self.cVert(self.sh)
|
||||
|
||||
def MovePageUp(self, event):
|
||||
self.cVert(-self.sh)
|
||||
|
||||
def MoveHome(self, event):
|
||||
self.cx = 0
|
||||
|
||||
def MoveEnd(self, event):
|
||||
self.cx = self.CurrentLineLength()
|
||||
|
||||
def MoveStartOfFile(self, event):
|
||||
self.cy = 0
|
||||
self.cx = 0
|
||||
|
||||
def MoveEndOfFile(self, event):
|
||||
self.cy = len(self.lines) - 1
|
||||
self.cx = self.CurrentLineLength()
|
||||
|
||||
#-------------- Key handler mapping tables
|
||||
|
||||
def SetMoveSpecialFuncs(self, action):
|
||||
action[WXK_DOWN] = self.MoveDown
|
||||
action[WXK_UP] = self.MoveUp
|
||||
action[WXK_LEFT] = self.MoveLeft
|
||||
action[WXK_RIGHT] = self.MoveRight
|
||||
action[WXK_NEXT] = self.MovePageDown
|
||||
action[WXK_PRIOR] = self.MovePageUp
|
||||
action[WXK_HOME] = self.MoveHome
|
||||
action[WXK_END] = self.MoveEnd
|
||||
|
||||
def SetMoveSpecialControlFuncs(self, action):
|
||||
action[WXK_HOME] = self.MoveStartOfFile
|
||||
action[WXK_END] = self.MoveEndOfFile
|
||||
|
||||
def SetAltFuncs(self, action):
|
||||
# subclass implements
|
||||
pass
|
||||
|
||||
def SetControlFuncs(self, action):
|
||||
action['c'] = self.OnCopySelection
|
||||
action['d'] = self.OnDeleteSelection
|
||||
action['v'] = self.OnPaste
|
||||
action['x'] = self.OnCutSelection
|
||||
|
||||
def SetSpecialControlFuncs(self, action):
|
||||
action[WXK_INSERT] = self.OnCopySelection
|
||||
|
||||
def SetShiftFuncs(self, action):
|
||||
action[WXK_DELETE] = self.OnCutSelection
|
||||
action[WXK_INSERT] = self.OnPaste
|
||||
|
||||
def SetSpecialFuncs(self, action):
|
||||
action[WXK_BACK] = self.BackSpace
|
||||
action[WXK_DELETE] = self.Delete
|
||||
action[WXK_RETURN] = self.BreakLine
|
||||
action[WXK_ESCAPE] = self.Escape
|
||||
action[WXK_TAB] = self.TabKey
|
||||
|
||||
##-------------- Logic for key handlers
|
||||
|
||||
|
||||
def Move(self, keySettingFunction, key, event):
|
||||
action = {}
|
||||
keySettingFunction(action)
|
||||
|
||||
if not action.has_key(key):
|
||||
return False
|
||||
|
||||
if event.ShiftDown():
|
||||
if not self.Selecting:
|
||||
self.Selecting = True
|
||||
self.SelectBegin = (self.cy, self.cx)
|
||||
action[key](event)
|
||||
self.SelectEnd = (self.cy, self.cx)
|
||||
else:
|
||||
action[key](event)
|
||||
if self.Selecting:
|
||||
self.Selecting = False
|
||||
|
||||
self.SelectNotify(self.Selecting, self.SelectBegin, self.SelectEnd)
|
||||
self.UpdateView()
|
||||
return True
|
||||
|
||||
def MoveSpecialKey(self, event, key):
|
||||
return self.Move(self.SetMoveSpecialFuncs, key, event)
|
||||
|
||||
def MoveSpecialControlKey(self, event, key):
|
||||
if not event.ControlDown():
|
||||
return False
|
||||
return self.Move(self.SetMoveSpecialControlFuncs, key, event)
|
||||
|
||||
def Dispatch(self, keySettingFunction, key, event):
|
||||
action = {}
|
||||
keySettingFunction(action)
|
||||
if action.has_key(key):
|
||||
action[key](event)
|
||||
self.UpdateView()
|
||||
return True
|
||||
return False
|
||||
|
||||
def ModifierKey(self, key, event, modifierKeyDown, MappingFunc):
|
||||
if not modifierKeyDown:
|
||||
return False
|
||||
|
||||
key = self.UnixKeyHack(key)
|
||||
try:
|
||||
key = chr(key)
|
||||
except:
|
||||
return False
|
||||
if not self.Dispatch(MappingFunc, key, event):
|
||||
wxBell()
|
||||
return True
|
||||
|
||||
def ControlKey(self, event, key):
|
||||
return self.ModifierKey(key, event, event.ControlDown(), self.SetControlFuncs)
|
||||
|
||||
def AltKey(self, event, key):
|
||||
return self.ModifierKey(key, event, event.AltDown(), self.SetAltFuncs)
|
||||
|
||||
def SpecialControlKey(self, event, key):
|
||||
if not event.ControlDown():
|
||||
return False
|
||||
if not self.Dispatch(self.SetSpecialControlFuncs, key, event):
|
||||
wxBell()
|
||||
return True
|
||||
|
||||
def ShiftKey(self, event, key):
|
||||
if not event.ShiftDown():
|
||||
return False
|
||||
return self.Dispatch(self.SetShiftFuncs, key, event)
|
||||
|
||||
def NormalChar(self, event, key):
|
||||
self.SelectOff()
|
||||
|
||||
# regular ascii
|
||||
if not self.Dispatch(self.SetSpecialFuncs, key, event):
|
||||
if (key>31) and (key<256):
|
||||
self.InsertChar(chr(key))
|
||||
else:
|
||||
wxBell()
|
||||
return
|
||||
self.UpdateView()
|
||||
self.AdjustScrollbars()
|
||||
|
||||
def OnChar(self, event):
|
||||
key = event.KeyCode()
|
||||
filters = [self.AltKey,
|
||||
self.MoveSpecialControlKey,
|
||||
self.ControlKey,
|
||||
self.SpecialControlKey,
|
||||
self.MoveSpecialKey,
|
||||
self.ShiftKey,
|
||||
self.NormalChar]
|
||||
for filter in filters:
|
||||
if filter(event,key):
|
||||
break
|
||||
return 0
|
||||
|
||||
#----------------------- Eliminate memory leaks
|
||||
|
||||
def OnDestroy(self, event):
|
||||
self.mdc = None
|
||||
self.odc = None
|
||||
self.bgColor = None
|
||||
self.fgColor = None
|
||||
self.font = None
|
||||
self.selectColor = None
|
||||
self.scrollTimer = None
|
||||
self.eofMarker = None
|
||||
|
||||
#-------------------- Abstract methods for subclasses
|
||||
|
||||
def OnClick(self):
|
||||
pass
|
||||
|
||||
def SelectNotify(self, Selecting, SelectionBegin, SelectionEnd):
|
||||
pass
|
||||
__doc__ = wx.lib.editor.editor.__doc__
|
||||
|
||||
ForceBetween = wx.lib.editor.editor.ForceBetween
|
||||
LineSplitter = wx.lib.editor.editor.LineSplitter
|
||||
LineTrimmer = wx.lib.editor.editor.LineTrimmer
|
||||
Scroller = wx.lib.editor.editor.Scroller
|
||||
wxEditor = wx.lib.editor.editor.wxEditor
|
||||
|
||||
@@ -1,27 +1,10 @@
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
# images converted with wxPython's img2py.py tool
|
||||
|
||||
from wxPython.wx import wxImageFromStream, wxBitmapFromImage
|
||||
import cStringIO
|
||||
|
||||
##----------- Common Functions
|
||||
|
||||
def GetBitmap(ImageData):
|
||||
return wxBitmapFromImage(GetImage(ImageData))
|
||||
|
||||
def GetImage(ImageData):
|
||||
stream = cStringIO.StringIO(ImageData)
|
||||
return wxImageFromStream(stream)
|
||||
|
||||
##----------- Image Data
|
||||
|
||||
EofImageData = \
|
||||
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x07\x00\x00\x00\x07\x08\x06\
|
||||
\x00\x00\x00\xc4RW\xd3\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
|
||||
\x00:IDATx\x9c]\x8e\xc1\r\x000\x08\x02\xc5\tn\xff)\xdd\xa0}\xd1`\xf9(\x9c\t\
|
||||
\n(kf\x0e \xfbN\x90\xf3\xc1\x0c\xd2\xab\xaa\x16Huv\xa4\x00\xb5\x97\x1f\xac\
|
||||
\x87\x1c\xe4\xe1\x05`2\x15\x9e\xc54\xca\xb4\x00\x00\x00\x00IEND\xaeB`\x82'
|
||||
|
||||
|
||||
import wx.lib.editor.images
|
||||
|
||||
__doc__ = wx.lib.editor.images.__doc__
|
||||
|
||||
GetBitmap = wx.lib.editor.images.GetBitmap
|
||||
GetImage = wx.lib.editor.images.GetImage
|
||||
|
||||
@@ -1,42 +1,10 @@
|
||||
True = 1
|
||||
False = 0
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
def RestOfLine(sx, width, data, bool):
|
||||
if len(data) == 0 and sx == 0:
|
||||
return [('', bool)]
|
||||
if sx >= len(data):
|
||||
return []
|
||||
return [(data[sx:sx+width], bool)]
|
||||
|
||||
def Selection(SelectBegin,SelectEnd, sx, width, line, data):
|
||||
if SelectEnd is None or SelectBegin is None:
|
||||
return RestOfLine(sx, width, data, False)
|
||||
(bRow, bCol) = SelectBegin
|
||||
(eRow, eCol) = SelectEnd
|
||||
if (eRow < bRow):
|
||||
(bRow, bCol) = SelectEnd
|
||||
(eRow, eCol) = SelectBegin
|
||||
if (line < bRow or eRow < line):
|
||||
return RestOfLine(sx, width, data, False)
|
||||
if (bRow < line and line < eRow):
|
||||
return RestOfLine(sx, width, data, True)
|
||||
if (bRow == eRow) and (eCol < bCol):
|
||||
(bCol, eCol) = (eCol, bCol)
|
||||
# selection either starts or ends on this line
|
||||
end = min(sx+width, len(data))
|
||||
if (bRow < line):
|
||||
bCol = 0
|
||||
if (line < eRow):
|
||||
eCol = end
|
||||
pieces = []
|
||||
if (sx < bCol):
|
||||
if bCol <= end:
|
||||
pieces += [(data[sx:bCol], False)]
|
||||
else:
|
||||
return [(data[sx:end], False)]
|
||||
pieces += [(data[max(bCol,sx):min(eCol,end)], True)]
|
||||
if (eCol < end):
|
||||
pieces += [(data[eCol:end], False)]
|
||||
return pieces
|
||||
import wx.lib.editor.selection
|
||||
|
||||
__doc__ = wx.lib.editor.selection.__doc__
|
||||
|
||||
RestOfLine = wx.lib.editor.selection.RestOfLine
|
||||
Selection = wx.lib.editor.selection.Selection
|
||||
|
||||
@@ -1,518 +1,13 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: wxPython.lib.evtmgr
|
||||
# Purpose: An easier, more "Pythonic" and more OO method of registering
|
||||
# handlers for wxWindows events using the Publish/Subscribe
|
||||
# pattern.
|
||||
#
|
||||
# Author: Robb Shecter and Robin Dunn
|
||||
#
|
||||
# Created: 12-December-2002
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2003 by db-X Corporation
|
||||
# Licence: wxWindows license
|
||||
#---------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
"""
|
||||
A module that allows multiple handlers to respond to single wxWindows
|
||||
events. This allows true NxN Observer/Observable connections: One
|
||||
event can be received by multiple handlers, and one handler can
|
||||
receive multiple events.
|
||||
import wx.lib.evtmgr
|
||||
|
||||
There are two ways to register event handlers. The first way is
|
||||
similar to standard wxPython handler registration:
|
||||
__doc__ = wx.lib.evtmgr.__doc__
|
||||
|
||||
from wxPython.lib.evtmgr import eventManager
|
||||
eventManager.Register(handleEvents, EVT_BUTTON, win=frame, id=101)
|
||||
|
||||
There's also a new object-oriented way to register for events. This
|
||||
invocation is equivalent to the one above, but does not require the
|
||||
programmer to declare or track control ids or parent containers:
|
||||
|
||||
eventManager.Register(handleEvents, EVT_BUTTON, myButton)
|
||||
|
||||
This module is Python 2.1+ compatible.
|
||||
|
||||
"""
|
||||
from wxPython import wx
|
||||
import pubsub
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class EventManager:
|
||||
"""
|
||||
This is the main class in the module, and is the only class that
|
||||
the application programmer needs to use. There is a pre-created
|
||||
instance of this class called 'eventManager'. It should not be
|
||||
necessary to create other instances.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.eventAdapterDict = {}
|
||||
self.messageAdapterDict = {}
|
||||
self.windowTopicLookup = {}
|
||||
self.listenerTopicLookup = {}
|
||||
self.__publisher = pubsub.Publisher()
|
||||
self.EMPTY_LIST = []
|
||||
|
||||
|
||||
def Register(self, listener, event, source=None, win=None, id=None):
|
||||
"""
|
||||
Registers a listener function (or any callable object) to
|
||||
receive events of type event coming from the source window.
|
||||
For example:
|
||||
|
||||
eventManager.Register(self.OnButton, EVT_BUTTON, theButton)
|
||||
|
||||
Alternatively, the specific window where the event is
|
||||
delivered, and/or the ID of the event source can be specified.
|
||||
For example:
|
||||
|
||||
eventManager.Register(self.OnButton, EVT_BUTTON, win=self, id=ID_BUTTON)
|
||||
or
|
||||
eventManager.Register(self.OnButton, EVT_BUTTON, theButton, self)
|
||||
"""
|
||||
|
||||
# 1. Check if the 'event' is actually one of the multi-
|
||||
# event macros.
|
||||
if _macroInfo.isMultiEvent(event):
|
||||
raise 'Cannot register the macro, '+`event`+'. Register instead the individual events.'
|
||||
|
||||
# Support a more OO API. This allows the GUI widget itself to
|
||||
# be specified, and the id to be retrieved from the system,
|
||||
# instead of kept track of explicitly by the programmer.
|
||||
# (Being used to doing GUI work with Java, this seems to me to be
|
||||
# the natural way of doing things.)
|
||||
if source is not None:
|
||||
id = source.GetId()
|
||||
if win is None:
|
||||
# Some widgets do not function as their own windows.
|
||||
win = self._determineWindow(source)
|
||||
topic = (event, win, id)
|
||||
|
||||
# Create an adapter from the PS system back to wxEvents, and
|
||||
# possibly one from wxEvents:
|
||||
if not self.__haveMessageAdapter(listener, topic):
|
||||
messageAdapter = MessageAdapter(eventHandler=listener, topicPattern=topic)
|
||||
try:
|
||||
self.messageAdapterDict[topic][listener] = messageAdapter
|
||||
except KeyError:
|
||||
self.messageAdapterDict[topic] = {}
|
||||
self.messageAdapterDict[topic][listener] = messageAdapter
|
||||
|
||||
if not self.eventAdapterDict.has_key(topic):
|
||||
self.eventAdapterDict[topic] = EventAdapter(event, win, id)
|
||||
else:
|
||||
# Throwing away a duplicate request
|
||||
pass
|
||||
|
||||
# For time efficiency when deregistering by window:
|
||||
try:
|
||||
self.windowTopicLookup[win].append(topic)
|
||||
except KeyError:
|
||||
self.windowTopicLookup[win] = []
|
||||
self.windowTopicLookup[win].append(topic)
|
||||
|
||||
# For time efficiency when deregistering by listener:
|
||||
try:
|
||||
self.listenerTopicLookup[listener].append(topic)
|
||||
except KeyError:
|
||||
self.listenerTopicLookup[listener] = []
|
||||
self.listenerTopicLookup[listener].append(topic)
|
||||
|
||||
# See if the source understands the listeningFor protocol.
|
||||
# This is a bit of a test I'm working on - it allows classes
|
||||
# to know when their events are being listened to. I use
|
||||
# it to enable chaining events from contained windows only
|
||||
# when needed.
|
||||
if source is not None:
|
||||
try:
|
||||
# Let the source know that we're listening for this
|
||||
# event.
|
||||
source.listeningFor(event)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Some aliases for Register, just for kicks
|
||||
Bind = Register
|
||||
Subscribe = Register
|
||||
|
||||
|
||||
def DeregisterWindow(self, win):
|
||||
"""
|
||||
Deregister all events coming from the given window.
|
||||
"""
|
||||
win = self._determineWindow(win)
|
||||
topics = self.__getTopics(win)
|
||||
if topics:
|
||||
for aTopic in topics:
|
||||
self.__deregisterTopic(aTopic)
|
||||
del self.windowTopicLookup[win]
|
||||
|
||||
|
||||
def DeregisterListener(self, listener):
|
||||
"""
|
||||
Deregister all event notifications for the given listener.
|
||||
"""
|
||||
try:
|
||||
topicList = self.listenerTopicLookup[listener]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
for topic in topicList:
|
||||
topicDict = self.messageAdapterDict[topic]
|
||||
if topicDict.has_key(listener):
|
||||
topicDict[listener].Destroy()
|
||||
del topicDict[listener]
|
||||
if len(topicDict) == 0:
|
||||
self.eventAdapterDict[topic].Destroy()
|
||||
del self.eventAdapterDict[topic]
|
||||
del self.messageAdapterDict[topic]
|
||||
del self.listenerTopicLookup[listener]
|
||||
|
||||
|
||||
def GetStats(self):
|
||||
"""
|
||||
Return a dictionary with data about my state.
|
||||
"""
|
||||
stats = {}
|
||||
stats['Adapters: Message'] = reduce(lambda x,y: x+y, [0] + map(len, self.messageAdapterDict.values()))
|
||||
stats['Adapters: Event'] = len(self.eventAdapterDict)
|
||||
stats['Topics: Total'] = len(self.__getTopics())
|
||||
stats['Topics: Dead'] = len(self.GetDeadTopics())
|
||||
return stats
|
||||
|
||||
|
||||
def DeregisterDeadTopics(self):
|
||||
"""
|
||||
Deregister any entries relating to dead
|
||||
wxPython objects. Not sure if this is an
|
||||
important issue; 1) My app code always de-registers
|
||||
listeners it doesn't need. 2) I don't think
|
||||
that lingering references to these dead objects
|
||||
is a problem.
|
||||
"""
|
||||
for topic in self.GetDeadTopics():
|
||||
self.__deregisterTopic(topic)
|
||||
|
||||
|
||||
def GetDeadTopics(self):
|
||||
"""
|
||||
Return a list of topics relating to dead wxPython
|
||||
objects.
|
||||
"""
|
||||
return filter(self.__isDeadTopic, self.__getTopics())
|
||||
|
||||
|
||||
def __winString(self, aWin):
|
||||
"""
|
||||
A string rep of a window for debugging
|
||||
"""
|
||||
try:
|
||||
name = aWin.GetClassName()
|
||||
i = id(aWin)
|
||||
return '%s #%d' % (name, i)
|
||||
except wx.wxPyDeadObjectError:
|
||||
return '(dead wxObject)'
|
||||
|
||||
|
||||
def __topicString(self, aTopic):
|
||||
"""
|
||||
A string rep of a topic for debugging
|
||||
"""
|
||||
return '[%-26s %s]' % (aTopic[0].__name__, self.winString(aTopic[1]))
|
||||
|
||||
|
||||
def __listenerString(self, aListener):
|
||||
"""
|
||||
A string rep of a listener for debugging
|
||||
"""
|
||||
try:
|
||||
return aListener.im_class.__name__ + '.' + aListener.__name__
|
||||
except:
|
||||
return 'Function ' + aListener.__name__
|
||||
|
||||
|
||||
def __deregisterTopic(self, aTopic):
|
||||
try:
|
||||
messageAdapterList = self.messageAdapterDict[aTopic].values()
|
||||
except KeyError:
|
||||
# This topic isn't valid. Probably because it was deleted
|
||||
# by listener.
|
||||
return
|
||||
for messageAdapter in messageAdapterList:
|
||||
messageAdapter.Destroy()
|
||||
self.eventAdapterDict[aTopic].Destroy()
|
||||
del self.messageAdapterDict[aTopic]
|
||||
del self.eventAdapterDict[aTopic]
|
||||
|
||||
|
||||
def __getTopics(self, win=None):
|
||||
if win is None:
|
||||
return self.messageAdapterDict.keys()
|
||||
if win is not None:
|
||||
try:
|
||||
return self.windowTopicLookup[win]
|
||||
except KeyError:
|
||||
return self.EMPTY_LIST
|
||||
|
||||
|
||||
def __isDeadWxObject(self, anObject):
|
||||
return isinstance(anObject, wx._wxPyDeadObject)
|
||||
|
||||
|
||||
def __isDeadTopic(self, aTopic):
|
||||
return self.__isDeadWxObject(aTopic[1])
|
||||
|
||||
|
||||
def __haveMessageAdapter(self, eventHandler, topicPattern):
|
||||
"""
|
||||
Return True if there's already a message adapter
|
||||
with these specs.
|
||||
"""
|
||||
try:
|
||||
return self.messageAdapterDict[topicPattern].has_key(eventHandler)
|
||||
except KeyError:
|
||||
return 0
|
||||
|
||||
|
||||
def _determineWindow(self, aComponent):
|
||||
"""
|
||||
Return the window that corresponds to this component.
|
||||
A window is something that supports the Connect protocol.
|
||||
Most things registered with the event manager are a window,
|
||||
but there are apparently some exceptions. If more are
|
||||
discovered, the implementation can be changed to a dictionary
|
||||
lookup along the lines of class : function-to-get-window.
|
||||
"""
|
||||
if isinstance(aComponent, wx.wxMenuItem):
|
||||
return aComponent.GetMenu()
|
||||
else:
|
||||
return aComponent
|
||||
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# From here down is implementaion and support classes, although you may
|
||||
# find some of them useful in other contexts.
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class EventMacroInfo:
|
||||
"""
|
||||
A class that provides information about event macros.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.lookupTable = {}
|
||||
|
||||
|
||||
def getEventTypes(self, eventMacro):
|
||||
"""
|
||||
Return the list of event types that the given
|
||||
macro corresponds to.
|
||||
"""
|
||||
try:
|
||||
return self.lookupTable[eventMacro]
|
||||
except KeyError:
|
||||
win = FakeWindow()
|
||||
try:
|
||||
eventMacro(win, None, None)
|
||||
except TypeError:
|
||||
eventMacro(win, None)
|
||||
self.lookupTable[eventMacro] = win.eventTypes
|
||||
return win.eventTypes
|
||||
|
||||
|
||||
def eventIsA(self, event, macroList):
|
||||
"""
|
||||
Return True if the event is one of the given
|
||||
macros.
|
||||
"""
|
||||
eventType = event.GetEventType()
|
||||
for macro in macroList:
|
||||
if eventType in self.getEventTypes(macro):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def macroIsA(self, macro, macroList):
|
||||
"""
|
||||
Return True if the macro is in the macroList.
|
||||
The added value of this method is that it takes
|
||||
multi-events into account. The macroList parameter
|
||||
will be coerced into a sequence if needed.
|
||||
"""
|
||||
if callable(macroList):
|
||||
macroList = (macroList,)
|
||||
testList = self.getEventTypes(macro)
|
||||
eventList = []
|
||||
for m in macroList:
|
||||
eventList.extend(self.getEventTypes(m))
|
||||
# Return True if every element in testList is in eventList
|
||||
for element in testList:
|
||||
if element not in eventList:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
def isMultiEvent(self, macro):
|
||||
"""
|
||||
Return True if the given macro actually causes
|
||||
multiple events to be registered.
|
||||
"""
|
||||
return len(self.getEventTypes(macro)) > 1
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class FakeWindow:
|
||||
"""
|
||||
Used internally by the EventMacroInfo class. The FakeWindow is
|
||||
the most important component of the macro-info utility: it
|
||||
implements the Connect() protocol of wxWindow, but instead of
|
||||
registering for events, it keeps track of what parameters were
|
||||
passed to it.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.eventTypes = []
|
||||
|
||||
def Connect(self, id1, id2, eventType, handlerFunction):
|
||||
self.eventTypes.append(eventType)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class EventAdapter:
|
||||
"""
|
||||
A class that adapts incoming wxWindows events to
|
||||
Publish/Subscribe messages.
|
||||
|
||||
In other words, this is the object that's seen by the
|
||||
wxWindows system. Only one of these registers for any
|
||||
particular wxWindows event. It then relays it into the
|
||||
PS system, which lets many listeners respond.
|
||||
"""
|
||||
def __init__(self, func, win, id):
|
||||
"""
|
||||
Instantiate a new adapter. Pre-compute my Publish/Subscribe
|
||||
topic, which is constant, and register with wxWindows.
|
||||
"""
|
||||
self.publisher = pubsub.Publisher()
|
||||
self.topic = ((func, win, id),)
|
||||
self.id = id
|
||||
self.win = win
|
||||
self.eventType = _macroInfo.getEventTypes(func)[0]
|
||||
|
||||
# Register myself with the wxWindows event system
|
||||
try:
|
||||
func(win, id, self.handleEvent)
|
||||
self.callStyle = 3
|
||||
except TypeError:
|
||||
func(win, self.handleEvent)
|
||||
self.callStyle = 2
|
||||
|
||||
|
||||
def disconnect(self):
|
||||
if self.callStyle == 3:
|
||||
return self.win.Disconnect(self.id, -1, self.eventType)
|
||||
else:
|
||||
return self.win.Disconnect(-1, -1, self.eventType)
|
||||
|
||||
|
||||
def handleEvent(self, event):
|
||||
"""
|
||||
In response to a wxWindows event, send a PS message
|
||||
"""
|
||||
self.publisher.sendMessage(topic=self.topic, data=event)
|
||||
|
||||
|
||||
def Destroy(self):
|
||||
try:
|
||||
if not self.disconnect():
|
||||
print 'disconnect failed'
|
||||
except wx.wxPyDeadObjectError:
|
||||
print 'disconnect failed: dead object' ##????
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class MessageAdapter:
|
||||
"""
|
||||
A class that adapts incoming Publish/Subscribe messages
|
||||
to wxWindows event calls.
|
||||
|
||||
This class works opposite the EventAdapter, and
|
||||
retrieves the information an EventAdapter has sent in a message.
|
||||
Strictly speaking, this class is not required: Event listeners
|
||||
could pull the original wxEvent object out of the PS Message
|
||||
themselves.
|
||||
|
||||
However, by pairing an instance of this class with each wxEvent
|
||||
handler, the handlers can use the standard API: they receive an
|
||||
event as a parameter.
|
||||
"""
|
||||
def __init__(self, eventHandler, topicPattern):
|
||||
"""
|
||||
Instantiate a new MessageAdapter that send wxEvents to the
|
||||
given eventHandler.
|
||||
"""
|
||||
self.eventHandler = eventHandler
|
||||
pubsub.Publisher().subscribe(listener=self.deliverEvent, topic=(topicPattern,))
|
||||
|
||||
def deliverEvent(self, message):
|
||||
event = message.data # Extract the wxEvent
|
||||
self.eventHandler(event) # Perform the call as wxWindows would
|
||||
|
||||
def Destroy(self):
|
||||
pubsub.Publisher().unsubscribe(listener=self.deliverEvent)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Create globals
|
||||
|
||||
_macroInfo = EventMacroInfo()
|
||||
|
||||
# For now a singleton is not enforced. Should it be or can we trust
|
||||
# the programmers?
|
||||
eventManager = EventManager()
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# simple test code
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from wxPython.wx import wxPySimpleApp, wxFrame, wxToggleButton, wxBoxSizer, wxHORIZONTAL, EVT_MOTION, EVT_LEFT_DOWN, EVT_TOGGLEBUTTON, wxALL
|
||||
app = wxPySimpleApp()
|
||||
frame = wxFrame(None, -1, 'Event Test', size=(300,300))
|
||||
button = wxToggleButton(frame, -1, 'Listen for Mouse Events')
|
||||
sizer = wxBoxSizer(wxHORIZONTAL)
|
||||
sizer.Add(button, 0, 0 | wxALL, 10)
|
||||
frame.SetAutoLayout(1)
|
||||
frame.SetSizer(sizer)
|
||||
|
||||
#
|
||||
# Demonstrate 1) register/deregister, 2) Multiple listeners receiving
|
||||
# one event, and 3) Multiple events going to one listener.
|
||||
#
|
||||
|
||||
def printEvent(event):
|
||||
print 'Name:',event.GetClassName(),'Timestamp',event.GetTimestamp()
|
||||
|
||||
def enableFrameEvents(event):
|
||||
# Turn the output of mouse events on and off
|
||||
if event.IsChecked():
|
||||
print '\nEnabling mouse events...'
|
||||
eventManager.Register(printEvent, EVT_MOTION, frame)
|
||||
eventManager.Register(printEvent, EVT_LEFT_DOWN, frame)
|
||||
else:
|
||||
print '\nDisabling mouse events...'
|
||||
eventManager.DeregisterWindow(frame)
|
||||
|
||||
# Send togglebutton events to both the on/off code as well
|
||||
# as the function that prints to stdout.
|
||||
eventManager.Register(printEvent, EVT_TOGGLEBUTTON, button)
|
||||
eventManager.Register(enableFrameEvents, EVT_TOGGLEBUTTON, button)
|
||||
|
||||
frame.CenterOnScreen()
|
||||
frame.Show(1)
|
||||
app.MainLoop()
|
||||
EventAdapter = wx.lib.evtmgr.EventAdapter
|
||||
EventMacroInfo = wx.lib.evtmgr.EventMacroInfo
|
||||
EventManager = wx.lib.evtmgr.EventManager
|
||||
FakeWindow = wx.lib.evtmgr.FakeWindow
|
||||
MessageAdapter = wx.lib.evtmgr.MessageAdapter
|
||||
|
||||
@@ -1,410 +1,24 @@
|
||||
"""<font weight="bold" size="16">FancyText</font> -- <font style="italic" size="16">methods for rendering XML specified text</font>
|
||||
<font family="swiss" size="12">
|
||||
This module exports four main methods::
|
||||
<font family="fixed" style="slant">
|
||||
def GetExtent(str, dc=None, enclose=True)
|
||||
def GetFullExtent(str, dc=None, enclose=True)
|
||||
def RenderToBitmap(str, background=None, enclose=True)
|
||||
def RenderToDC(str, dc, x, y, enclose=True)
|
||||
</font>
|
||||
In all cases, 'str' is an XML string. Note that start and end tags
|
||||
are only required if *enclose* is set to False. In this case the
|
||||
text should be wrapped in FancyText tags.
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
In addition, the module exports one class::
|
||||
<font family="fixed" style="slant">
|
||||
class StaticFancyText(self, window, id, text, background, ...)
|
||||
</font>
|
||||
This class works similar to StaticText except it interprets its text
|
||||
as FancyText.
|
||||
|
||||
The text can support<sup>superscripts</sup> and <sub>subscripts</sub>, text
|
||||
in different <font size="20">sizes</font>, <font color="blue">colors</font>, <font style="italic">styles</font>, <font weight="bold">weights</font> and
|
||||
<font family="script">families</font>. It also supports a limited set of symbols,
|
||||
currently <times/>, <infinity/>, <angle/> as well as greek letters in both
|
||||
upper case (<Alpha/><Beta/>...<Omega/>) and lower case (<alpha/><beta/>...<omega/>).
|
||||
|
||||
We can use doctest/guitest to display this string in all its marked up glory.
|
||||
<font family="fixed">
|
||||
>>> frame = wx.Frame(wx.NULL, -1, "FancyText demo", wx.DefaultPosition)
|
||||
>>> sft = StaticFancyText(frame, -1, __doc__, wx.Brush("light grey", wx.SOLID))
|
||||
>>> frame.SetClientSize(sft.GetSize())
|
||||
>>> didit = frame.Show()
|
||||
>>> from guitest import PauseTests; PauseTests()
|
||||
|
||||
</font></font>
|
||||
The End"""
|
||||
# Copyright 2001-2003 Timothy Hochberg
|
||||
# Use as you see fit. No warantees, I cannot be help responsible, etc.
|
||||
import copy
|
||||
import math
|
||||
import sys
|
||||
import wx
|
||||
import xml.parsers.expat
|
||||
|
||||
__all__ = "GetExtent", "GetFullExtent", "RenderToBitmap", "RenderToDC", "StaticFancyText"
|
||||
|
||||
if sys.platform == "win32":
|
||||
_greekEncoding = str(wx.FONTENCODING_CP1253)
|
||||
else:
|
||||
_greekEncoding = str(wx.FONTENCODING_ISO8859_7)
|
||||
|
||||
_families = {"fixed" : wx.FIXED, "default" : wx.DEFAULT, "decorative" : wx.DECORATIVE, "roman" : wx.ROMAN,
|
||||
"script" : wx.SCRIPT, "swiss" : wx.SWISS, "modern" : wx.MODERN}
|
||||
_styles = {"normal" : wx.NORMAL, "slant" : wx.SLANT, "italic" : wx.ITALIC}
|
||||
_weights = {"normal" : wx.NORMAL, "light" : wx.LIGHT, "bold" : wx.BOLD}
|
||||
|
||||
# The next three classes: Renderer, SizeRenderer and DCRenderer are
|
||||
# what you will need to override to extend the XML language. All of
|
||||
# the font stuff as well as the subscript and superscript stuff are in
|
||||
# Renderer.
|
||||
|
||||
_greek_letters = ("alpha", "beta", "gamma", "delta", "epsilon", "zeta",
|
||||
"eta", "theta", "iota", "kappa", "lambda", "mu", "nu",
|
||||
"xi", "omnikron", "pi", "rho", "altsigma", "sigma", "tau", "upsilon",
|
||||
"phi", "chi", "psi", "omega")
|
||||
|
||||
def iround(number):
|
||||
return int(round(number))
|
||||
|
||||
def iceil(number):
|
||||
return int(math.ceil(number))
|
||||
|
||||
class Renderer:
|
||||
"""Class for rendering XML marked up text.
|
||||
|
||||
See the module docstring for a description of the markup.
|
||||
|
||||
This class must be subclassed and the methods the methods that do
|
||||
the drawing overridden for a particular output device.
|
||||
|
||||
"""
|
||||
defaultSize = wx.NORMAL_FONT.GetPointSize()
|
||||
defaultFamily = wx.DEFAULT
|
||||
defaultStyle = wx.NORMAL
|
||||
defaultWeight = wx.NORMAL
|
||||
defaultEncoding = wx.Font_GetDefaultEncoding()
|
||||
defaultColor = "black"
|
||||
|
||||
def __init__(self, dc=None, x=0, y=None):
|
||||
if dc == None:
|
||||
dc = wx.MemoryDC()
|
||||
self.dc = dc
|
||||
self.offsets = [0]
|
||||
self.fonts = [{}]
|
||||
self.width = self.height = 0
|
||||
self.x = x
|
||||
self.minY = self.maxY = self._y = y
|
||||
|
||||
def getY(self):
|
||||
if self._y is None:
|
||||
self.minY = self.maxY = self._y = self.dc.GetTextExtent("M")[1]
|
||||
return self._y
|
||||
def setY(self, value):
|
||||
self._y = y
|
||||
y = property(getY, setY)
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
method = "start_" + name
|
||||
if not hasattr(self, method):
|
||||
raise ValueError("XML tag '%s' not supported" % name)
|
||||
getattr(self, method)(attrs)
|
||||
|
||||
def endElement(self, name):
|
||||
methname = "end_" + name
|
||||
if hasattr(self, methname):
|
||||
getattr(self, methname)()
|
||||
elif hasattr(self, "start_" + name):
|
||||
pass
|
||||
else:
|
||||
raise ValueError("XML tag '%s' not supported" % methname)
|
||||
|
||||
def characterData(self, data):
|
||||
self.dc.SetFont(self.getCurrentFont())
|
||||
for i, chunk in enumerate(data.split('\n')):
|
||||
if i:
|
||||
self.x = 0
|
||||
self.y = self.mayY = self.maxY + self.dc.GetTextExtent("M")[1]
|
||||
if chunk:
|
||||
width, height, descent, extl = self.dc.GetFullTextExtent(chunk)
|
||||
self.renderCharacterData(data, iround(self.x), iround(self.y + self.offsets[-1] - height + descent))
|
||||
else:
|
||||
width = height = descent = extl = 0
|
||||
self.updateDims(width, height, descent, extl)
|
||||
|
||||
def updateDims(self, width, height, descent, externalLeading):
|
||||
self.x += width
|
||||
self.width = max(self.x, self.width)
|
||||
self.minY = min(self.minY, self.y+self.offsets[-1]-height+descent)
|
||||
self.maxY = max(self.maxY, self.y+self.offsets[-1]+descent)
|
||||
self.height = self.maxY - self.minY
|
||||
|
||||
def start_FancyText(self, attrs):
|
||||
pass
|
||||
start_wxFancyText = start_FancyText # For backward compatibility
|
||||
|
||||
def start_font(self, attrs):
|
||||
for key, value in attrs.items():
|
||||
if key == "size":
|
||||
value = int(value)
|
||||
elif key == "family":
|
||||
value = _families[value]
|
||||
elif key == "style":
|
||||
value = _styles[value]
|
||||
elif key == "weight":
|
||||
value = _weights[value]
|
||||
elif key == "encoding":
|
||||
value = int(value)
|
||||
elif key == "color":
|
||||
pass
|
||||
else:
|
||||
raise ValueError("unknown font attribute '%s'" % key)
|
||||
attrs[key] = value
|
||||
font = copy.copy(self.fonts[-1])
|
||||
font.update(attrs)
|
||||
self.fonts.append(font)
|
||||
|
||||
def end_font(self):
|
||||
self.fonts.pop()
|
||||
|
||||
def start_sub(self, attrs):
|
||||
if attrs.keys():
|
||||
raise ValueError("<sub> does not take attributes")
|
||||
font = self.getCurrentFont()
|
||||
self.offsets.append(self.offsets[-1] + self.dc.GetFullTextExtent("M", font)[1]*0.5)
|
||||
self.start_font({"size" : font.GetPointSize() * 0.8})
|
||||
|
||||
def end_sub(self):
|
||||
self.fonts.pop()
|
||||
self.offsets.pop()
|
||||
|
||||
def start_sup(self, attrs):
|
||||
if attrs.keys():
|
||||
raise ValueError("<sup> does not take attributes")
|
||||
font = self.getCurrentFont()
|
||||
self.offsets.append(self.offsets[-1] - self.dc.GetFullTextExtent("M", font)[1]*0.3)
|
||||
self.start_font({"size" : font.GetPointSize() * 0.8})
|
||||
|
||||
def end_sup(self):
|
||||
self.fonts.pop()
|
||||
self.offsets.pop()
|
||||
|
||||
def getCurrentFont(self):
|
||||
font = self.fonts[-1]
|
||||
return wx.TheFontList.FindOrCreateFont(font.get("size", self.defaultSize),
|
||||
font.get("family", self.defaultFamily),
|
||||
font.get("style", self.defaultStyle),
|
||||
font.get("weight", self.defaultWeight),
|
||||
encoding = font.get("encoding", self.defaultEncoding))
|
||||
|
||||
def getCurrentColor(self):
|
||||
font = self.fonts[-1]
|
||||
return wx.TheColourDatabase.FindColour(font.get("color", self.defaultColor))
|
||||
|
||||
def getCurrentPen(self):
|
||||
return wx.ThePenList.FindOrCreatePen(self.getCurrentColor(), 1, wx.SOLID)
|
||||
|
||||
def renderCharacterData(self, data, x, y):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _addGreek():
|
||||
alpha = 0xE1
|
||||
Alpha = 0xC1
|
||||
def end(self):
|
||||
pass
|
||||
for i, name in enumerate(_greek_letters):
|
||||
def start(self, attrs, code=chr(alpha+i)):
|
||||
self.start_font({"encoding" : _greekEncoding})
|
||||
self.characterData(code)
|
||||
self.end_font()
|
||||
setattr(Renderer, "start_%s" % name, start)
|
||||
setattr(Renderer, "end_%s" % name, end)
|
||||
if name == "altsigma":
|
||||
continue # There is no capital for altsigma
|
||||
def start(self, attrs, code=chr(Alpha+i)):
|
||||
self.start_font({"encoding" : _greekEncoding})
|
||||
self.characterData(code)
|
||||
self.end_font()
|
||||
setattr(Renderer, "start_%s" % name.capitalize(), start)
|
||||
setattr(Renderer, "end_%s" % name.capitalize(), end)
|
||||
_addGreek()
|
||||
|
||||
|
||||
|
||||
class SizeRenderer(Renderer):
|
||||
"""Processes text as if rendering it, but just computes the size."""
|
||||
|
||||
def __init__(self, dc=None):
|
||||
Renderer.__init__(self, dc, 0, 0)
|
||||
|
||||
def renderCharacterData(self, data, x, y):
|
||||
pass
|
||||
|
||||
def start_angle(self, attrs):
|
||||
self.characterData("M")
|
||||
|
||||
def start_infinity(self, attrs):
|
||||
width, height = self.dc.GetTextExtent("M")
|
||||
width = max(width, 10)
|
||||
height = max(height, width / 2)
|
||||
self.updateDims(width, height, 0, 0)
|
||||
|
||||
def start_times(self, attrs):
|
||||
self.characterData("M")
|
||||
|
||||
def start_in(self, attrs):
|
||||
self.characterData("M")
|
||||
|
||||
def start_times(self, attrs):
|
||||
self.characterData("M")
|
||||
|
||||
|
||||
class DCRenderer(Renderer):
|
||||
"""Renders text to a wxPython device context DC."""
|
||||
|
||||
def renderCharacterData(self, data, x, y):
|
||||
self.dc.SetTextForeground(self.getCurrentColor())
|
||||
self.dc.DrawText(data, x, y)
|
||||
|
||||
def start_angle(self, attrs):
|
||||
self.dc.SetFont(self.getCurrentFont())
|
||||
self.dc.SetPen(self.getCurrentPen())
|
||||
width, height, descent, leading = self.dc.GetFullTextExtent("M")
|
||||
y = self.y + self.offsets[-1]
|
||||
self.dc.DrawLine(iround(self.x), iround(y),iround( self.x+width), iround(y))
|
||||
self.dc.DrawLine(iround(self.x), iround(y), iround(self.x+width), iround(y-width))
|
||||
self.updateDims(width, height, descent, leading)
|
||||
|
||||
|
||||
def start_infinity(self, attrs):
|
||||
self.dc.SetFont(self.getCurrentFont())
|
||||
self.dc.SetPen(self.getCurrentPen())
|
||||
width, height, descent, leading = self.dc.GetFullTextExtent("M")
|
||||
width = max(width, 10)
|
||||
height = max(height, width / 2)
|
||||
self.dc.SetPen(wx.Pen(self.getCurrentColor(), max(1, width/10)))
|
||||
self.dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
||||
y = self.y + self.offsets[-1]
|
||||
r = iround( 0.95 * width / 4)
|
||||
xc = (2*self.x + width) / 2
|
||||
yc = iround(y-1.5*r)
|
||||
self.dc.DrawCircle(xc - r, yc, r)
|
||||
self.dc.DrawCircle(xc + r, yc, r)
|
||||
self.updateDims(width, height, 0, 0)
|
||||
|
||||
def start_times(self, attrs):
|
||||
self.dc.SetFont(self.getCurrentFont())
|
||||
self.dc.SetPen(self.getCurrentPen())
|
||||
width, height, descent, leading = self.dc.GetFullTextExtent("M")
|
||||
y = self.y + self.offsets[-1]
|
||||
width *= 0.8
|
||||
width = iround(width+.5)
|
||||
self.dc.SetPen(wx.Pen(self.getCurrentColor(), 1))
|
||||
self.dc.DrawLine(iround(self.x), iround(y-width), iround(self.x+width-1), iround(y-1))
|
||||
self.dc.DrawLine(iround(self.x), iround(y-2), iround(self.x+width-1), iround(y-width-1))
|
||||
self.updateDims(width, height, 0, 0)
|
||||
|
||||
|
||||
def RenderToRenderer(str, renderer, enclose=True):
|
||||
try:
|
||||
if enclose:
|
||||
str = '<?xml version="1.0"?><FancyText>%s</FancyText>' % str
|
||||
p = xml.parsers.expat.ParserCreate()
|
||||
p.returns_unicode = 0
|
||||
p.StartElementHandler = renderer.startElement
|
||||
p.EndElementHandler = renderer.endElement
|
||||
p.CharacterDataHandler = renderer.characterData
|
||||
p.Parse(str, 1)
|
||||
except xml.parsers.expat.error, err:
|
||||
raise ValueError('error parsing text text "%s": %s' % (str, err))
|
||||
|
||||
|
||||
# Public interface
|
||||
|
||||
|
||||
def GetExtent(str, dc=None, enclose=True):
|
||||
"Return the extent of str"
|
||||
renderer = SizeRenderer(dc)
|
||||
RenderToRenderer(str, renderer, enclose)
|
||||
return iceil(renderer.width), iceil(renderer.height) # XXX round up
|
||||
|
||||
|
||||
def GetFullExtent(str, dc=None, enclose=True):
|
||||
renderer = SizeRenderer(dc)
|
||||
RenderToRenderer(str, renderer, enclose)
|
||||
return iceil(renderer.width), iceil(renderer.height), -iceil(renderer.minY) # XXX round up
|
||||
|
||||
|
||||
def RenderToBitmap(str, background=None, enclose=1):
|
||||
"Return str rendered on a minumum size bitmap"
|
||||
dc = wx.MemoryDC()
|
||||
width, height, dy = GetFullExtent(str, dc, enclose)
|
||||
bmp = wx.EmptyBitmap(width, height)
|
||||
dc.SelectObject(bmp)
|
||||
if background is None:
|
||||
dc.SetBackground(wx.WHITE_BRUSH)
|
||||
else:
|
||||
dc.SetBackground(background)
|
||||
dc.Clear()
|
||||
renderer = DCRenderer(dc, y=dy)
|
||||
dc.BeginDrawing()
|
||||
RenderToRenderer(str, renderer, enclose)
|
||||
dc.EndDrawing()
|
||||
dc.SelectObject(wx.NullBitmap)
|
||||
if background is None:
|
||||
img = wx.ImageFromBitmap(bmp)
|
||||
bg = dc.GetBackground().GetColour()
|
||||
img.SetMaskColour(bg.Red(), bg.Green(), bg.Blue())
|
||||
bmp = img.ConvertToBitmap()
|
||||
return bmp
|
||||
|
||||
|
||||
def RenderToDC(str, dc, x, y, enclose=1):
|
||||
"Render str onto a wxDC at (x,y)"
|
||||
width, height, dy = GetFullExtent(str, dc)
|
||||
renderer = DCRenderer(dc, x, y+dy)
|
||||
RenderToRenderer(str, renderer, enclose)
|
||||
|
||||
|
||||
class StaticFancyText(wx.StaticBitmap):
|
||||
def __init__(self, window, id, text, *args, **kargs):
|
||||
args = list(args)
|
||||
kargs.setdefault('name', 'staticFancyText')
|
||||
if 'background' in kargs:
|
||||
background = kargs.pop('background')
|
||||
elif args:
|
||||
background = args.pop(0)
|
||||
else:
|
||||
background = wx.Brush(window.GetBackgroundColour(), wx.SOLID)
|
||||
|
||||
bmp = RenderToBitmap(text, background)
|
||||
wx.StaticBitmap.__init__(self, window, id, bmp, *args, **kargs)
|
||||
|
||||
|
||||
# Old names for backward compatibiliry
|
||||
getExtent = GetExtent
|
||||
renderToBitmap = RenderToBitmap
|
||||
renderToDC = RenderToDC
|
||||
|
||||
|
||||
# Test Driver
|
||||
|
||||
def test():
|
||||
app = wx.PyApp()
|
||||
box = wx.BoxSizer(wx.VERTICAL)
|
||||
frame = wx.Frame(wx.NULL, -1, "FancyText demo", wx.DefaultPosition)
|
||||
frame.SetBackgroundColour("light grey")
|
||||
sft = StaticFancyText(frame, -1, __doc__)
|
||||
box.Add(sft, 1, wx.EXPAND)
|
||||
frame.SetSizer(box)
|
||||
frame.SetAutoLayout(True)
|
||||
box.Fit(frame)
|
||||
box.SetSizeHints(frame)
|
||||
frame.Show()
|
||||
app.MainLoop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
||||
import wx.lib.fancytext
|
||||
|
||||
__doc__ = wx.lib.fancytext.__doc__
|
||||
|
||||
DCRenderer = wx.lib.fancytext.DCRenderer
|
||||
GetExtent = wx.lib.fancytext.GetExtent
|
||||
GetFullExtent = wx.lib.fancytext.GetFullExtent
|
||||
RenderToBitmap = wx.lib.fancytext.RenderToBitmap
|
||||
RenderToDC = wx.lib.fancytext.RenderToDC
|
||||
RenderToRenderer = wx.lib.fancytext.RenderToRenderer
|
||||
Renderer = wx.lib.fancytext.Renderer
|
||||
SizeRenderer = wx.lib.fancytext.SizeRenderer
|
||||
StaticFancyText = wx.lib.fancytext.StaticFancyText
|
||||
_addGreek = wx.lib.fancytext._addGreek
|
||||
getExtent = wx.lib.fancytext.getExtent
|
||||
iceil = wx.lib.fancytext.iceil
|
||||
iround = wx.lib.fancytext.iround
|
||||
renderToBitmap = wx.lib.fancytext.renderToBitmap
|
||||
renderToDC = wx.lib.fancytext.renderToDC
|
||||
test = wx.lib.fancytext.test
|
||||
|
||||
@@ -1,447 +1,11 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.filebrowsebutton
|
||||
# Purpose: Composite controls that provide a Browse button next to
|
||||
# either a wxTextCtrl or a wxComboBox. The Browse button
|
||||
# launches a wxFileDialog and loads the result into the
|
||||
# other control.
|
||||
#
|
||||
# Author: Mike Fletcher
|
||||
#
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2000 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
from wxPython.wx import *
|
||||
import os, types
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class FileBrowseButton(wxPanel):
|
||||
""" A control to allow the user to type in a filename
|
||||
or browse with the standard file dialog to select file
|
||||
|
||||
__init__ (
|
||||
parent, id, pos, size -- passed directly to wxPanel initialisation
|
||||
style = wxTAB_TRAVERSAL -- passed directly to wxPanel initialisation
|
||||
labelText -- Text for label to left of text field
|
||||
buttonText -- Text for button which launches the file dialog
|
||||
toolTip -- Help text
|
||||
dialogTitle -- Title used in file dialog
|
||||
startDirectory -- Default directory for file dialog startup
|
||||
fileMask -- File mask (glob pattern, such as *.*) to use in file dialog
|
||||
fileMode -- wxOPEN or wxSAVE, indicates type of file dialog to use
|
||||
changeCallback -- callback receives all > > changes in value of control
|
||||
)
|
||||
GetValue() -- retrieve current value of text control
|
||||
SetValue(string) -- set current value of text control
|
||||
label -- pointer to internal label widget
|
||||
textControl -- pointer to internal text control
|
||||
browseButton -- pointer to button
|
||||
"""
|
||||
def __init__ (self, parent, id= -1,
|
||||
pos = wxDefaultPosition, size = wxDefaultSize,
|
||||
style = wxTAB_TRAVERSAL,
|
||||
labelText= "File Entry:",
|
||||
buttonText= "Browse",
|
||||
toolTip= "Type filename or click browse to choose file",
|
||||
# following are the values for a file dialog box
|
||||
dialogTitle = "Choose a file",
|
||||
startDirectory = ".",
|
||||
initialValue = "",
|
||||
fileMask = "*.*",
|
||||
fileMode = wxOPEN,
|
||||
# callback for when value changes (optional)
|
||||
changeCallback= lambda x:x
|
||||
):
|
||||
# store variables
|
||||
self.labelText = labelText
|
||||
self.buttonText = buttonText
|
||||
self.toolTip = toolTip
|
||||
self.dialogTitle = dialogTitle
|
||||
self.startDirectory = startDirectory
|
||||
self.initialValue = initialValue
|
||||
self.fileMask = fileMask
|
||||
self.fileMode = fileMode
|
||||
self.changeCallback = changeCallback
|
||||
self.callCallback = True
|
||||
|
||||
|
||||
# get background to match it
|
||||
try:
|
||||
self._bc = parent.GetBackgroundColour()
|
||||
except:
|
||||
pass
|
||||
|
||||
# create the dialog
|
||||
self.createDialog(parent, id, pos, size, style )
|
||||
# Setting a value causes the changeCallback to be called.
|
||||
# In this case that would be before the return of the
|
||||
# constructor. Not good. So a default value on
|
||||
# SetValue is used to disable the callback
|
||||
self.SetValue( initialValue, 0)
|
||||
|
||||
|
||||
def createDialog( self, parent, id, pos, size, style ):
|
||||
"""Setup the graphic representation of the dialog"""
|
||||
wxPanel.__init__ (self, parent, id, pos, size, style)
|
||||
# try to set the background colour
|
||||
try:
|
||||
self.SetBackgroundColour(self._bc)
|
||||
except:
|
||||
pass
|
||||
box = wxBoxSizer(wxHORIZONTAL)
|
||||
|
||||
self.label = self.createLabel( )
|
||||
box.Add( self.label, 0, wxCENTER )
|
||||
|
||||
self.textControl = self.createTextControl()
|
||||
box.Add( self.textControl, 1, wxLEFT|wxCENTER, 5)
|
||||
|
||||
self.browseButton = self.createBrowseButton()
|
||||
box.Add( self.browseButton, 0, wxLEFT|wxCENTER, 5)
|
||||
|
||||
# add a border around the whole thing and resize the panel to fit
|
||||
outsidebox = wxBoxSizer(wxVERTICAL)
|
||||
outsidebox.Add(box, 1, wxEXPAND|wxALL, 3)
|
||||
outsidebox.Fit(self)
|
||||
|
||||
self.SetAutoLayout(True)
|
||||
self.SetSizer( outsidebox )
|
||||
self.Layout()
|
||||
if type( size ) == types.TupleType:
|
||||
size = apply( wxSize, size)
|
||||
self.SetDimensions(-1, -1, size.width, size.height, wxSIZE_USE_EXISTING)
|
||||
|
||||
# if size.width != -1 or size.height != -1:
|
||||
# self.SetSize(size)
|
||||
|
||||
def SetBackgroundColour(self,color):
|
||||
wxPanel.SetBackgroundColour(self,color)
|
||||
self.label.SetBackgroundColour(color)
|
||||
|
||||
def createLabel( self ):
|
||||
"""Create the label/caption"""
|
||||
label = wxStaticText(self, -1, self.labelText, style =wxALIGN_RIGHT )
|
||||
font = label.GetFont()
|
||||
w, h, d, e = self.GetFullTextExtent(self.labelText, font)
|
||||
label.SetSize(wxSize(w+5, h))
|
||||
return label
|
||||
|
||||
def createTextControl( self):
|
||||
"""Create the text control"""
|
||||
ID = wxNewId()
|
||||
textControl = wxTextCtrl(self, ID)
|
||||
textControl.SetToolTipString( self.toolTip )
|
||||
if self.changeCallback:
|
||||
EVT_TEXT(textControl, ID, self.OnChanged)
|
||||
EVT_COMBOBOX(textControl, ID, self.OnChanged)
|
||||
return textControl
|
||||
|
||||
def OnChanged(self, evt):
|
||||
if self.callCallback and self.changeCallback:
|
||||
self.changeCallback(evt)
|
||||
|
||||
def createBrowseButton( self):
|
||||
"""Create the browse-button control"""
|
||||
ID = wxNewId()
|
||||
button =wxButton(self, ID, self.buttonText)
|
||||
button.SetToolTipString( self.toolTip )
|
||||
EVT_BUTTON(button, ID, self.OnBrowse)
|
||||
return button
|
||||
|
||||
|
||||
def OnBrowse (self, event = None):
|
||||
""" Going to browse for file... """
|
||||
current = self.GetValue()
|
||||
directory = os.path.split(current)
|
||||
if os.path.isdir( current):
|
||||
directory = current
|
||||
current = ''
|
||||
elif directory and os.path.isdir( directory[0] ):
|
||||
current = directory[1]
|
||||
directory = directory [0]
|
||||
else:
|
||||
directory = self.startDirectory
|
||||
dlg = wxFileDialog(self, self.dialogTitle, directory, current, self.fileMask, self.fileMode)
|
||||
|
||||
if dlg.ShowModal() == wxID_OK:
|
||||
self.SetValue(dlg.GetPath())
|
||||
dlg.Destroy()
|
||||
|
||||
|
||||
def GetValue (self):
|
||||
""" Convenient access to text control value """
|
||||
return self.textControl.GetValue()
|
||||
|
||||
def SetValue (self, value, callBack=1):
|
||||
""" Convenient setting of text control value """
|
||||
save = self.callCallback
|
||||
self.callCallback = callBack
|
||||
self.textControl.SetValue(value)
|
||||
self.callCallback = save
|
||||
|
||||
|
||||
def Enable (self, value):
|
||||
""" Convenient enabling/disabling of entire control """
|
||||
self.label.Enable (value)
|
||||
self.textControl.Enable (value)
|
||||
return self.browseButton.Enable (value)
|
||||
|
||||
def GetLabel( self ):
|
||||
""" Retrieve the label's current text """
|
||||
return self.label.GetLabel()
|
||||
|
||||
def SetLabel( self, value ):
|
||||
""" Set the label's current text """
|
||||
rvalue = self.label.SetLabel( value )
|
||||
self.Refresh( True )
|
||||
return rvalue
|
||||
|
||||
|
||||
|
||||
|
||||
class FileBrowseButtonWithHistory( FileBrowseButton ):
|
||||
""" with following additions:
|
||||
__init__(..., history=None)
|
||||
|
||||
history -- optional list of paths for initial history drop-down
|
||||
(must be passed by name, not a positional argument)
|
||||
If history is callable it will must return a list used
|
||||
for the history drop-down
|
||||
changeCallback -- as for FileBrowseButton, but with a work-around
|
||||
for win32 systems which don't appear to create EVT_COMBOBOX
|
||||
events properly. There is a (slight) chance that this work-around
|
||||
will cause some systems to create two events for each Combobox
|
||||
selection. If you discover this condition, please report it!
|
||||
As for a FileBrowseButton.__init__ otherwise.
|
||||
GetHistoryControl()
|
||||
Return reference to the control which implements interfaces
|
||||
required for manipulating the history list. See GetHistoryControl
|
||||
documentation for description of what that interface is.
|
||||
GetHistory()
|
||||
Return current history list
|
||||
SetHistory( value=(), selectionIndex = None )
|
||||
Set current history list, if selectionIndex is not None, select that index
|
||||
"""
|
||||
def __init__( self, *arguments, **namedarguments):
|
||||
self.history = namedarguments.get( "history" )
|
||||
if self.history:
|
||||
del namedarguments["history"]
|
||||
|
||||
self.historyCallBack=None
|
||||
if callable(self.history):
|
||||
self.historyCallBack=self.history
|
||||
self.history=None
|
||||
apply( FileBrowseButton.__init__, ( self,)+arguments, namedarguments)
|
||||
|
||||
|
||||
def createTextControl( self):
|
||||
"""Create the text control"""
|
||||
ID = wxNewId()
|
||||
textControl = wxComboBox(self, ID, style = wxCB_DROPDOWN )
|
||||
textControl.SetToolTipString( self.toolTip )
|
||||
EVT_SET_FOCUS(textControl, self.OnSetFocus)
|
||||
if self.changeCallback:
|
||||
EVT_TEXT(textControl, ID, self.changeCallback)
|
||||
EVT_COMBOBOX(textControl, ID, self.changeCallback)
|
||||
if self.history:
|
||||
history=self.history
|
||||
self.history=None
|
||||
self.SetHistory( history, control=textControl)
|
||||
return textControl
|
||||
|
||||
|
||||
def GetHistoryControl( self ):
|
||||
"""Return a pointer to the control which provides (at least)
|
||||
the following methods for manipulating the history list.:
|
||||
Append( item ) -- add item
|
||||
Clear() -- clear all items
|
||||
Delete( index ) -- 0-based index to delete from list
|
||||
SetSelection( index ) -- 0-based index to select in list
|
||||
Semantics of the methods follow those for the wxComboBox control
|
||||
"""
|
||||
return self.textControl
|
||||
|
||||
|
||||
def SetHistory( self, value=(), selectionIndex = None, control=None ):
|
||||
"""Set the current history list"""
|
||||
if control is None:
|
||||
control = self.GetHistoryControl()
|
||||
if self.history == value:
|
||||
return
|
||||
self.history = value
|
||||
# Clear history values not the selected one.
|
||||
tempValue=control.GetValue()
|
||||
# clear previous values
|
||||
control.Clear()
|
||||
control.SetValue(tempValue)
|
||||
# walk through, appending new values
|
||||
for path in value:
|
||||
control.Append( path )
|
||||
if selectionIndex is not None:
|
||||
control.SetSelection( selectionIndex )
|
||||
|
||||
|
||||
def GetHistory( self ):
|
||||
"""Return the current history list"""
|
||||
if self.historyCallBack != None:
|
||||
return self.historyCallBack()
|
||||
else:
|
||||
return list( self.history )
|
||||
|
||||
|
||||
def OnSetFocus(self, event):
|
||||
"""When the history scroll is selected, update the history"""
|
||||
if self.historyCallBack != None:
|
||||
self.SetHistory( self.historyCallBack(), control=self.textControl)
|
||||
event.Skip()
|
||||
|
||||
|
||||
if wxPlatform == "__WXMSW__":
|
||||
def SetValue (self, value, callBack=1):
|
||||
""" Convenient setting of text control value, works
|
||||
around limitation of wxComboBox """
|
||||
save = self.callCallback
|
||||
self.callCallback = callBack
|
||||
self.textControl.SetValue(value)
|
||||
self.callCallback = save
|
||||
|
||||
# Hack to call an event handler
|
||||
class LocalEvent:
|
||||
def __init__(self, string):
|
||||
self._string=string
|
||||
def GetString(self):
|
||||
return self._string
|
||||
if callBack==1:
|
||||
# The callback wasn't being called when SetValue was used ??
|
||||
# So added this explicit call to it
|
||||
self.changeCallback(LocalEvent(value))
|
||||
|
||||
|
||||
class DirBrowseButton(FileBrowseButton):
|
||||
def __init__(self, parent, id = -1,
|
||||
pos = wxDefaultPosition, size = wxDefaultSize,
|
||||
style = wxTAB_TRAVERSAL,
|
||||
labelText = 'Select a directory:',
|
||||
buttonText = 'Browse',
|
||||
toolTip = 'Type directory name or browse to select',
|
||||
dialogTitle = '',
|
||||
startDirectory = '.',
|
||||
changeCallback = None,
|
||||
dialogClass = wxDirDialog):
|
||||
FileBrowseButton.__init__(self, parent, id, pos, size, style,
|
||||
labelText, buttonText, toolTip,
|
||||
dialogTitle, startDirectory,
|
||||
changeCallback = changeCallback)
|
||||
#
|
||||
self._dirDialog = dialogClass(self,
|
||||
message = dialogTitle,
|
||||
defaultPath = startDirectory)
|
||||
#
|
||||
def OnBrowse(self, ev = None):
|
||||
dialog = self._dirDialog
|
||||
if dialog.ShowModal() == wxID_OK:
|
||||
self.SetValue(dialog.GetPath())
|
||||
#
|
||||
def __del__(self):
|
||||
if self.__dict__.has_key('_dirDialog'):
|
||||
self._dirDialog.Destroy()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
#from skeletonbuilder import rulesfile
|
||||
class SimpleCallback:
|
||||
def __init__( self, tag ):
|
||||
self.tag = tag
|
||||
def __call__( self, event ):
|
||||
print self.tag, event.GetString()
|
||||
class DemoFrame( wxFrame ):
|
||||
def __init__(self, parent):
|
||||
wxFrame.__init__(self, parent, 2400, "File entry with browse", size=(500,260) )
|
||||
EVT_CLOSE(self, self.OnCloseWindow)
|
||||
panel = wxPanel (self,-1)
|
||||
innerbox = wxBoxSizer(wxVERTICAL)
|
||||
control = FileBrowseButton(
|
||||
panel,
|
||||
initialValue = "z:\\temp",
|
||||
)
|
||||
innerbox.Add( control, 0, wxEXPAND )
|
||||
middlecontrol = FileBrowseButtonWithHistory(
|
||||
panel,
|
||||
labelText = "With History",
|
||||
initialValue = "d:\\temp",
|
||||
history = ["c:\\temp", "c:\\tmp", "r:\\temp","z:\\temp"],
|
||||
changeCallback= SimpleCallback( "With History" ),
|
||||
)
|
||||
innerbox.Add( middlecontrol, 0, wxEXPAND )
|
||||
middlecontrol = FileBrowseButtonWithHistory(
|
||||
panel,
|
||||
labelText = "History callback",
|
||||
initialValue = "d:\\temp",
|
||||
history = self.historyCallBack,
|
||||
changeCallback= SimpleCallback( "History callback" ),
|
||||
)
|
||||
innerbox.Add( middlecontrol, 0, wxEXPAND )
|
||||
self.bottomcontrol = control = FileBrowseButton(
|
||||
panel,
|
||||
labelText = "With Callback",
|
||||
style = wxSUNKEN_BORDER|wxCLIP_CHILDREN ,
|
||||
changeCallback= SimpleCallback( "With Callback" ),
|
||||
)
|
||||
innerbox.Add( control, 0, wxEXPAND)
|
||||
self.bottommostcontrol = control = DirBrowseButton(
|
||||
panel,
|
||||
labelText = "Simple dir browse button",
|
||||
style = wxSUNKEN_BORDER|wxCLIP_CHILDREN)
|
||||
innerbox.Add( control, 0, wxEXPAND)
|
||||
ID = wxNewId()
|
||||
innerbox.Add( wxButton( panel, ID,"Change Label", ), 1, wxEXPAND)
|
||||
EVT_BUTTON( self, ID, self.OnChangeLabel )
|
||||
ID = wxNewId()
|
||||
innerbox.Add( wxButton( panel, ID,"Change Value", ), 1, wxEXPAND)
|
||||
EVT_BUTTON( self, ID, self.OnChangeValue )
|
||||
panel.SetAutoLayout(True)
|
||||
panel.SetSizer( innerbox )
|
||||
self.history={"c:\\temp":1, "c:\\tmp":1, "r:\\temp":1,"z:\\temp":1}
|
||||
|
||||
def historyCallBack(self):
|
||||
keys=self.history.keys()
|
||||
keys.sort()
|
||||
return keys
|
||||
|
||||
def OnFileNameChangedHistory (self, event):
|
||||
self.history[event.GetString ()]=1
|
||||
|
||||
def OnCloseMe(self, event):
|
||||
self.Close(True)
|
||||
def OnChangeLabel( self, event ):
|
||||
self.bottomcontrol.SetLabel( "Label Updated" )
|
||||
def OnChangeValue( self, event ):
|
||||
self.bottomcontrol.SetValue( "r:\\somewhere\\over\\the\\rainbow.htm" )
|
||||
|
||||
def OnCloseWindow(self, event):
|
||||
self.Destroy()
|
||||
|
||||
class DemoApp(wxApp):
|
||||
def OnInit(self):
|
||||
wxImage_AddHandler(wxJPEGHandler())
|
||||
wxImage_AddHandler(wxPNGHandler())
|
||||
wxImage_AddHandler(wxGIFHandler())
|
||||
frame = DemoFrame(NULL)
|
||||
#frame = RulesPanel(NULL )
|
||||
frame.Show(True)
|
||||
self.SetTopWindow(frame)
|
||||
return True
|
||||
|
||||
def test( ):
|
||||
app = DemoApp(0)
|
||||
app.MainLoop()
|
||||
print 'Creating dialog'
|
||||
test( )
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.filebrowsebutton
|
||||
|
||||
__doc__ = wx.lib.filebrowsebutton.__doc__
|
||||
|
||||
DirBrowseButton = wx.lib.filebrowsebutton.DirBrowseButton
|
||||
FileBrowseButton = wx.lib.filebrowsebutton.FileBrowseButton
|
||||
FileBrowseButtonWithHistory = wx.lib.filebrowsebutton.FileBrowseButtonWithHistory
|
||||
|
||||
@@ -1,283 +1,9 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: floatbar.py
|
||||
# Purpose: Contains floating toolbar class
|
||||
#
|
||||
# Author: Bryn Keller
|
||||
#
|
||||
# Created: 10/4/99
|
||||
#----------------------------------------------------------------------------
|
||||
"""
|
||||
NOTE: This module is *not* supported in any way. Use it however you
|
||||
wish, but be warned that dealing with any consequences is
|
||||
entirly up to you.
|
||||
--Robin
|
||||
"""
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
if wxPlatform == '__WXGTK__':
|
||||
#
|
||||
# For wxGTK all we have to do is set the wxTB_DOCKABLE flag
|
||||
#
|
||||
class wxFloatBar(wxToolBar):
|
||||
def __init__(self, parent, ID,
|
||||
pos = wxDefaultPosition,
|
||||
size = wxDefaultSize,
|
||||
style = 0,
|
||||
name = 'toolbar'):
|
||||
wxToolBar.__init__(self, parent, ID, pos, size,
|
||||
style|wxTB_DOCKABLE, name)
|
||||
|
||||
# these other methods just become no-ops
|
||||
def SetFloatable(self, float):
|
||||
pass
|
||||
|
||||
def IsFloating(self):
|
||||
return 1
|
||||
|
||||
def GetTitle(self):
|
||||
return ""
|
||||
|
||||
|
||||
def SetTitle(self, title):
|
||||
pass
|
||||
|
||||
else:
|
||||
_DOCKTHRESHOLD = 25
|
||||
|
||||
class wxFloatBar(wxToolBar):
|
||||
"""
|
||||
wxToolBar subclass which can be dragged off its frame and later
|
||||
replaced there. Drag on the toolbar to release it, close it like
|
||||
a normal window to make it return to its original
|
||||
position. Programmatically, call SetFloatable(True) and then
|
||||
Float(True) to float, Float(False) to dock.
|
||||
"""
|
||||
|
||||
def __init__(self,*_args,**_kwargs):
|
||||
"""
|
||||
In addition to the usual arguments, wxFloatBar accepts keyword
|
||||
args of: title(string): the title that should appear on the
|
||||
toolbar's frame when it is floating. floatable(bool): whether
|
||||
user actions (i.e., dragging) can float the toolbar or not.
|
||||
"""
|
||||
args = (self,) + _args
|
||||
apply(wxToolBar.__init__, args, _kwargs)
|
||||
if _kwargs.has_key('floatable'):
|
||||
self.floatable = _kwargs['floatable']
|
||||
assert type(self.floatable) == type(0)
|
||||
else:
|
||||
self.floatable = 0
|
||||
self.floating = 0
|
||||
if _kwargs.has_key('title'):
|
||||
self.title = _kwargs['title']
|
||||
assert type(self.title) == type("")
|
||||
else:
|
||||
self.title = ""
|
||||
EVT_MOUSE_EVENTS(self, self.OnMouse)
|
||||
self.parentframe = wxPyTypeCast(args[1], 'wxFrame')
|
||||
|
||||
|
||||
def IsFloatable(self):
|
||||
return self.floatable
|
||||
|
||||
|
||||
def SetFloatable(self, float):
|
||||
self.floatable = float
|
||||
#Find the size of a title bar.
|
||||
if not hasattr(self, 'titleheight'):
|
||||
test = wxMiniFrame(NULL, -1, "TEST")
|
||||
test.SetClientSize(wxSize(0,0))
|
||||
self.titleheight = test.GetSizeTuple()[1]
|
||||
test.Destroy()
|
||||
|
||||
|
||||
def IsFloating(self):
|
||||
return self.floating
|
||||
|
||||
|
||||
def Realize(self):
|
||||
wxToolBar.Realize(self)
|
||||
|
||||
|
||||
def GetTitle(self):
|
||||
return self.title
|
||||
|
||||
|
||||
def SetTitle(self, title):
|
||||
print 'SetTitle', title
|
||||
self.title = title
|
||||
if self.IsFloating():
|
||||
self.floatframe.SetTitle(self.title)
|
||||
|
||||
|
||||
## def GetHome(self):
|
||||
## """
|
||||
## Returns the frame which this toolbar will return to when
|
||||
## docked, or the parent if currently docked.
|
||||
## """
|
||||
## if hasattr(self, 'parentframe'):
|
||||
## return self.parentframe
|
||||
## else:
|
||||
## return wxPyTypeCast(self.GetParent(), 'wxFrame')
|
||||
|
||||
|
||||
## def SetHome(self, frame):
|
||||
## """
|
||||
## Called when docked, this will remove the toolbar from its
|
||||
## current frame and attach it to another. If called when
|
||||
## floating, it will dock to the frame specified when the toolbar
|
||||
## window is closed.
|
||||
## """
|
||||
## if self.IsFloating():
|
||||
## self.parentframe = frame
|
||||
## self.floatframe.Reparent(frame)
|
||||
## else:
|
||||
## parent = wxPyTypeCast(self.GetParent(), 'wxFrame')
|
||||
## self.Reparent(frame)
|
||||
## parent.SetToolBar(None)
|
||||
## size = parent.GetSize()
|
||||
## parent.SetSize(wxSize(0,0))
|
||||
## parent.SetSize(size)
|
||||
## frame.SetToolBar(self)
|
||||
## size = frame.GetSize()
|
||||
## frame.SetSize(wxSize(0,0))
|
||||
## frame.SetSize(size)
|
||||
|
||||
|
||||
def Float(self, bool):
|
||||
"Floats or docks the toolbar programmatically."
|
||||
if bool:
|
||||
self.parentframe = wxPyTypeCast(self.GetParent(), 'wxFrame')
|
||||
print self.title
|
||||
if self.title:
|
||||
useStyle = wxDEFAULT_FRAME_STYLE
|
||||
else:
|
||||
useStyle = wxTHICK_FRAME
|
||||
self.floatframe = wxMiniFrame(self.parentframe, -1, self.title,
|
||||
style = useStyle)
|
||||
|
||||
self.Reparent(self.floatframe)
|
||||
self.parentframe.SetToolBar(None)
|
||||
self.floating = 1
|
||||
psize = self.parentframe.GetSize()
|
||||
self.parentframe.SetSize(wxSize(0,0))
|
||||
self.parentframe.SetSize(psize)
|
||||
self.floatframe.SetToolBar(self)
|
||||
self.oldcolor = self.GetBackgroundColour()
|
||||
|
||||
w = psize.width
|
||||
h = self.GetSize().height
|
||||
if self.title:
|
||||
h = h + self.titleheight
|
||||
self.floatframe.SetSize(wxSize(w,h))
|
||||
self.floatframe.SetClientSize(self.GetSize())
|
||||
newpos = self.parentframe.GetPosition()
|
||||
newpos.y = newpos.y + _DOCKTHRESHOLD * 2
|
||||
self.floatframe.SetPosition(newpos)
|
||||
self.floatframe.Show(True)
|
||||
|
||||
EVT_CLOSE(self.floatframe, self.OnDock)
|
||||
#EVT_MOVE(self.floatframe, self.OnMove)
|
||||
|
||||
else:
|
||||
self.Reparent(self.parentframe)
|
||||
self.parentframe.SetToolBar(self)
|
||||
self.floating = 0
|
||||
self.floatframe.SetToolBar(None)
|
||||
self.floatframe.Destroy()
|
||||
size = self.parentframe.GetSize()
|
||||
self.parentframe.SetSize(wxSize(0,0))
|
||||
self.parentframe.SetSize(size)
|
||||
self.SetBackgroundColour(self.oldcolor)
|
||||
|
||||
|
||||
def OnDock(self, e):
|
||||
self.Float(0)
|
||||
if hasattr(self, 'oldpos'):
|
||||
del self.oldpos
|
||||
|
||||
|
||||
def OnMove(self, e):
|
||||
homepos = self.parentframe.ClientToScreen(wxPoint(0,0))
|
||||
floatpos = self.floatframe.GetPosition()
|
||||
if (abs(homepos.x - floatpos.x) < _DOCKTHRESHOLD and
|
||||
abs(homepos.y - floatpos.y) < _DOCKTHRESHOLD):
|
||||
self.Float(0)
|
||||
#homepos = self.parentframe.GetPositionTuple()
|
||||
#homepos = homepos[0], homepos[1] + self.titleheight
|
||||
#floatpos = self.floatframe.GetPositionTuple()
|
||||
#if abs(homepos[0] - floatpos[0]) < 35 and abs(homepos[1] - floatpos[1]) < 35:
|
||||
# self._SetFauxBarVisible(True)
|
||||
#else:
|
||||
# self._SetFauxBarVisible(False)
|
||||
|
||||
|
||||
def OnMouse(self, e):
|
||||
if not self.IsFloatable():
|
||||
e.Skip()
|
||||
return
|
||||
|
||||
if e.ButtonDClick(1) or e.ButtonDClick(2) or e.ButtonDClick(3) or e.ButtonDown() or e.ButtonUp():
|
||||
e.Skip()
|
||||
|
||||
if e.ButtonDown():
|
||||
self.CaptureMouse()
|
||||
self.oldpos = (e.GetX(), e.GetY())
|
||||
|
||||
if e.Entering():
|
||||
self.oldpos = (e.GetX(), e.GetY())
|
||||
|
||||
if e.ButtonUp():
|
||||
self.ReleaseMouse()
|
||||
if self.IsFloating():
|
||||
homepos = self.parentframe.ClientToScreen(wxPoint(0,0))
|
||||
floatpos = self.floatframe.GetPosition()
|
||||
if (abs(homepos.x - floatpos.x) < _DOCKTHRESHOLD and
|
||||
abs(homepos.y - floatpos.y) < _DOCKTHRESHOLD):
|
||||
self.Float(0)
|
||||
return
|
||||
|
||||
if e.Dragging():
|
||||
if not self.IsFloating():
|
||||
self.Float(True)
|
||||
self.oldpos = (e.GetX(), e.GetY())
|
||||
else:
|
||||
if hasattr(self, 'oldpos'):
|
||||
loc = self.floatframe.GetPosition()
|
||||
pt = wxPoint(loc.x - (self.oldpos[0]-e.GetX()), loc.y - (self.oldpos[1]-e.GetY()))
|
||||
self.floatframe.Move(pt)
|
||||
|
||||
|
||||
|
||||
def _SetFauxBarVisible(self, vis):
|
||||
return
|
||||
if vis:
|
||||
if self.parentframe.GetToolBar() == None:
|
||||
if not hasattr(self, 'nullbar'):
|
||||
self.nullbar = wxToolBar(self.parentframe, -1)
|
||||
print "Adding fauxbar."
|
||||
self.nullbar.Reparent(self.parentframe)
|
||||
print "Reparented."
|
||||
self.parentframe.SetToolBar(self.nullbar)
|
||||
print "Set toolbar"
|
||||
col = wxNamedColour("GREY")
|
||||
self.nullbar.SetBackgroundColour(col)
|
||||
print "Set color"
|
||||
size = self.parentframe.GetSize()
|
||||
self.parentframe.SetSize(wxSize(0,0))
|
||||
self.parentframe.SetSize(size)
|
||||
print "Set size"
|
||||
else:
|
||||
print self.parentframe.GetToolBar()
|
||||
else:
|
||||
if self.parentframe.GetToolBar() != None:
|
||||
print "Removing fauxbar"
|
||||
self.nullbar.Reparent(self.floatframe)
|
||||
self.parentframe.SetToolBar(None)
|
||||
size = self.parentframe.GetSize()
|
||||
self.parentframe.SetSize(wxSize(0,0))
|
||||
self.parentframe.SetSize(size)
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.floatbar
|
||||
|
||||
__doc__ = wx.lib.floatbar.__doc__
|
||||
|
||||
wxFloatBar = wx.lib.floatbar.wxFloatBar
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
10
wxPython/wxPython/lib/foldmenu.py
Normal file
10
wxPython/wxPython/lib/foldmenu.py
Normal file
@@ -0,0 +1,10 @@
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.foldmenu
|
||||
|
||||
__doc__ = wx.lib.foldmenu.__doc__
|
||||
|
||||
FoldOutMenu = wx.lib.foldmenu.FoldOutMenu
|
||||
FoldOutWindow = wx.lib.foldmenu.FoldOutWindow
|
||||
@@ -1,419 +1,18 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: GridColMover.py
|
||||
# Purpose: Grid Column Mover Extension
|
||||
#
|
||||
# Author: Gerrit van Dyk (email: gerritvd@decillion.net)
|
||||
#
|
||||
# Version 0.1
|
||||
# Date: Nov 19, 2002
|
||||
# RCS-ID: $Id$
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython.wx import *
|
||||
from wxPython.grid import wxGridPtr
|
||||
import wx.lib.gridmovers
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# event class and macors
|
||||
__doc__ = wx.lib.gridmovers.__doc__
|
||||
|
||||
|
||||
wxEVT_COMMAND_GRID_COL_MOVE = wxNewEventType()
|
||||
wxEVT_COMMAND_GRID_ROW_MOVE = wxNewEventType()
|
||||
|
||||
def EVT_GRID_COL_MOVE(win, id, func):
|
||||
win.Connect(id, -1, wxEVT_COMMAND_GRID_COL_MOVE, func)
|
||||
|
||||
def EVT_GRID_ROW_MOVE(win,id,func):
|
||||
win.Connect(id, -1, wxEVT_COMMAND_GRID_ROW_MOVE, func)
|
||||
|
||||
|
||||
class wxGridColMoveEvent(wxPyCommandEvent):
|
||||
def __init__(self, id, dCol, bCol):
|
||||
wxPyCommandEvent.__init__(self, id = id)
|
||||
self.SetEventType(wxEVT_COMMAND_GRID_COL_MOVE)
|
||||
self.moveColumn = dCol
|
||||
self.beforeColumn = bCol
|
||||
|
||||
def GetMoveColumn(self):
|
||||
return self.moveColumn
|
||||
|
||||
def GetBeforeColumn(self):
|
||||
return self.beforeColumn
|
||||
|
||||
|
||||
class wxGridRowMoveEvent(wxPyCommandEvent):
|
||||
def __init__(self, id, dRow, bRow):
|
||||
wxPyCommandEvent.__init__(self,id = id)
|
||||
self.SetEventType(wxEVT_COMMAND_GRID_ROW_MOVE)
|
||||
self.moveRow = dRow
|
||||
self.beforeRow = bRow
|
||||
|
||||
def GetMoveRow(self):
|
||||
return self.moveRow
|
||||
|
||||
def GetBeforeRow(self):
|
||||
return self.beforeRow
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# graft new methods into the wxGridPtr class
|
||||
|
||||
def _ColToRect(self,col):
|
||||
if self.GetNumberRows() > 0:
|
||||
rect = self.CellToRect(0,col)
|
||||
else:
|
||||
rect = wxRect()
|
||||
rect.height = self.GetColLabelSize()
|
||||
rect.width = self.GetColSize(col)
|
||||
for cCol in range(0,col):
|
||||
rect.x += self.GetColSize(cCol)
|
||||
rect.y = self.GetGridColLabelWindow().GetPosition()[1]
|
||||
return rect
|
||||
|
||||
wxGridPtr.ColToRect = _ColToRect
|
||||
|
||||
|
||||
def _RowToRect(self,row):
|
||||
if self.GetNumberCols() > 0:
|
||||
rect = self.CellToRect(row,0)
|
||||
else:
|
||||
rect = wxRect()
|
||||
rect.width = self.GetRowLabelSize()
|
||||
rect.height = self.GetRowSize(row)
|
||||
for cRow in range(0,row):
|
||||
rect.y += self.GetRowSize(cRow)
|
||||
rect.x = self.GetGridRowLabelWindow().GetPosition()[0]
|
||||
return rect
|
||||
|
||||
wxGridPtr.RowToRect = _RowToRect
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
class ColDragWindow(wxWindow):
|
||||
def __init__(self,parent,image,dragCol):
|
||||
wxWindow.__init__(self,parent,wxSIMPLE_BORDER)
|
||||
self.image = image
|
||||
self.SetSize((self.image.GetWidth(),self.image.GetHeight()))
|
||||
self.ux = parent.GetScrollPixelsPerUnit()[0]
|
||||
self.moveColumn = dragCol
|
||||
|
||||
EVT_PAINT(self,self.OnPaint)
|
||||
|
||||
def DisplayAt(self,pos,y):
|
||||
x = self.GetPositionTuple()[0]
|
||||
if x == pos:
|
||||
self.Refresh() # Need to display insertion point
|
||||
else:
|
||||
self.MoveXY(pos,y)
|
||||
|
||||
def GetMoveColumn(self):
|
||||
return self.moveColumn
|
||||
|
||||
def _GetInsertionInfo(self):
|
||||
parent = self.GetParent()
|
||||
sx = parent.GetViewStart()[0] * self.ux
|
||||
sx -= parent._rlSize
|
||||
x = self.GetPositionTuple()[0]
|
||||
w = self.GetSizeTuple()[0]
|
||||
sCol = parent.XToCol(x + sx)
|
||||
eCol = parent.XToCol(x + w + sx)
|
||||
iPos = xPos = xCol = 99999
|
||||
centerPos = x + sx + (w / 2)
|
||||
for col in range(sCol,eCol + 1):
|
||||
cx = parent.ColToRect(col)[0]
|
||||
if abs(cx - centerPos) < iPos:
|
||||
iPos = abs(cx - centerPos)
|
||||
xCol = col
|
||||
xPos = cx
|
||||
if xCol < 0 or xCol > parent.GetNumberCols():
|
||||
xCol = parent.GetNumberCols()
|
||||
return (xPos - sx - x,xCol)
|
||||
|
||||
def GetInsertionColumn(self):
|
||||
return self._GetInsertionInfo()[1]
|
||||
|
||||
def GetInsertionPos(self):
|
||||
return self._GetInsertionInfo()[0]
|
||||
|
||||
def OnPaint(self,evt):
|
||||
dc = wxPaintDC(self)
|
||||
w,h = self.GetSize()
|
||||
dc.DrawBitmap(self.image,0,0)
|
||||
dc.SetPen(wxPen(wxBLACK,1,wxSOLID))
|
||||
dc.SetBrush(wxTRANSPARENT_BRUSH)
|
||||
dc.DrawRectangle(0,0,w,h)
|
||||
iPos = self.GetInsertionPos()
|
||||
dc.DrawLine(iPos,h - 10,iPos,h)
|
||||
|
||||
|
||||
|
||||
|
||||
class RowDragWindow(wxWindow):
|
||||
def __init__(self,parent,image,dragRow):
|
||||
wxWindow.__init__(self,parent,wxSIMPLE_BORDER)
|
||||
self.image = image
|
||||
self.SetSize((self.image.GetWidth(),self.image.GetHeight()))
|
||||
self.uy = parent.GetScrollPixelsPerUnit()[1]
|
||||
self.moveRow = dragRow
|
||||
|
||||
EVT_PAINT(self,self.OnPaint)
|
||||
|
||||
def DisplayAt(self,x,pos):
|
||||
y = self.GetPositionTuple()[1]
|
||||
if y == pos:
|
||||
self.Refresh() # Need to display insertion point
|
||||
else:
|
||||
self.MoveXY(x,pos)
|
||||
|
||||
def GetMoveRow(self):
|
||||
return self.moveRow
|
||||
|
||||
def _GetInsertionInfo(self):
|
||||
parent = self.GetParent()
|
||||
sy = parent.GetViewStart()[1] * self.uy
|
||||
sy -= parent._clSize
|
||||
y = self.GetPositionTuple()[1]
|
||||
h = self.GetSizeTuple()[1]
|
||||
sRow = parent.YToRow(y + sy)
|
||||
eRow = parent.YToRow(y + h + sy)
|
||||
iPos = yPos = yRow = 99999
|
||||
centerPos = y + sy + (h / 2)
|
||||
for row in range(sRow,eRow + 1):
|
||||
cy = parent.RowToRect(row)[1]
|
||||
if abs(cy - centerPos) < iPos:
|
||||
iPos = abs(cy - centerPos)
|
||||
yRow = row
|
||||
yPos = cy
|
||||
if yRow < 0 or yRow > parent.GetNumberRows():
|
||||
yRow = parent.GetNumberRows()
|
||||
return (yPos - sy - y,yRow)
|
||||
|
||||
def GetInsertionRow(self):
|
||||
return self._GetInsertionInfo()[1]
|
||||
|
||||
def GetInsertionPos(self):
|
||||
return self._GetInsertionInfo()[0]
|
||||
|
||||
def OnPaint(self,evt):
|
||||
dc = wxPaintDC(self)
|
||||
w,h = self.GetSize()
|
||||
dc.DrawBitmap(self.image,0,0)
|
||||
dc.SetPen(wxPen(wxBLACK,1,wxSOLID))
|
||||
dc.SetBrush(wxTRANSPARENT_BRUSH)
|
||||
dc.DrawRectangle(0,0,w,h)
|
||||
iPos = self.GetInsertionPos()
|
||||
dc.DrawLine(w - 10,iPos,w,iPos)
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
class wxGridColMover(wxEvtHandler):
|
||||
def __init__(self,grid):
|
||||
wxEvtHandler.__init__(self)
|
||||
|
||||
self.grid = grid
|
||||
self.grid._rlSize = self.grid.GetRowLabelSize()
|
||||
self.lwin = grid.GetGridColLabelWindow()
|
||||
self.lwin.PushEventHandler(self)
|
||||
self.colWin = None
|
||||
self.ux = self.grid.GetScrollPixelsPerUnit()[0]
|
||||
self.startX = -10
|
||||
self.cellX = 0
|
||||
self.didMove = False
|
||||
self.isDragging = False
|
||||
|
||||
EVT_MOTION(self,self.OnMouseMove)
|
||||
EVT_LEFT_DOWN(self,self.OnPress)
|
||||
EVT_LEFT_UP(self,self.OnRelease)
|
||||
|
||||
def OnMouseMove(self,evt):
|
||||
if self.isDragging:
|
||||
if abs(self.startX - evt.m_x) >= 3:
|
||||
self.didMove = True
|
||||
sx,y = self.grid.GetViewStart()
|
||||
w,h = self.lwin.GetClientSizeTuple()
|
||||
x = sx * self.ux
|
||||
if (evt.m_x + x) < x:
|
||||
x = evt.m_x + x
|
||||
elif evt.m_x > w:
|
||||
x += evt.m_x - w
|
||||
if x < 1: x = 0
|
||||
else: x /= self.ux
|
||||
if x != sx:
|
||||
if wxPlatform == '__WXMSW__':
|
||||
self.colWin.Show(False)
|
||||
self.grid.Scroll(x,y)
|
||||
x,y = self.lwin.ClientToScreenXY(evt.m_x,0)
|
||||
x,y = self.grid.ScreenToClientXY(x,y)
|
||||
if not self.colWin.IsShown():
|
||||
self.colWin.Show(True)
|
||||
px = x - self.cellX
|
||||
if px < 0 + self.grid._rlSize: px = 0 + self.grid._rlSize
|
||||
if px > w - self.colWin.GetSizeTuple()[0] + self.grid._rlSize:
|
||||
px = w - self.colWin.GetSizeTuple()[0] + self.grid._rlSize
|
||||
self.colWin.DisplayAt(px,y)
|
||||
return
|
||||
evt.Skip()
|
||||
|
||||
def OnPress(self,evt):
|
||||
self.startX = evt.m_x
|
||||
sx = self.grid.GetViewStart()[0] * self.ux
|
||||
sx -= self.grid._rlSize
|
||||
px,py = self.lwin.ClientToScreenXY(evt.m_x,evt.m_y)
|
||||
px,py = self.grid.ScreenToClientXY(px,py)
|
||||
if self.grid.XToEdgeOfCol(px + sx) != wxNOT_FOUND:
|
||||
evt.Skip()
|
||||
return
|
||||
|
||||
self.isDragging = True
|
||||
self.didMove = False
|
||||
col = self.grid.XToCol(px + sx)
|
||||
rect = self.grid.ColToRect(col)
|
||||
self.cellX = px + sx - rect.x
|
||||
size = self.lwin.GetSizeTuple()
|
||||
rect.y = 0
|
||||
rect.x -= sx + self.grid._rlSize
|
||||
rect.height = size[1]
|
||||
colImg = self._CaptureImage(rect)
|
||||
self.colWin = ColDragWindow(self.grid,colImg,col)
|
||||
self.colWin.Show(False)
|
||||
self.lwin.CaptureMouse()
|
||||
|
||||
def OnRelease(self,evt):
|
||||
if self.isDragging:
|
||||
self.lwin.ReleaseMouse()
|
||||
self.colWin.Show(False)
|
||||
self.isDragging = False
|
||||
if not self.didMove:
|
||||
px = self.lwin.ClientToScreenXY(self.startX,0)[0]
|
||||
px = self.grid.ScreenToClientXY(px,0)[0]
|
||||
sx = self.grid.GetViewStart()[0] * self.ux
|
||||
sx -= self.grid._rlSize
|
||||
col = self.grid.XToCol(px+sx)
|
||||
if col != wxNOT_FOUND:
|
||||
self.grid.SelectCol(col,evt.m_controlDown)
|
||||
return
|
||||
else:
|
||||
bCol = self.colWin.GetInsertionColumn()
|
||||
dCol = self.colWin.GetMoveColumn()
|
||||
wxPostEvent(self,wxGridColMoveEvent(self.grid.GetId(),
|
||||
dCol,bCol))
|
||||
self.colWin.Destroy()
|
||||
evt.Skip()
|
||||
|
||||
def _CaptureImage(self,rect):
|
||||
bmp = wxEmptyBitmap(rect.width,rect.height)
|
||||
memdc = wxMemoryDC()
|
||||
memdc.SelectObject(bmp)
|
||||
dc = wxWindowDC(self.lwin)
|
||||
memdc.Blit(0,0,rect.width,rect.height,dc,rect.x,rect.y)
|
||||
memdc.SelectObject(wxNullBitmap)
|
||||
return bmp
|
||||
|
||||
|
||||
|
||||
class wxGridRowMover(wxEvtHandler):
|
||||
def __init__(self,grid):
|
||||
wxEvtHandler.__init__(self)
|
||||
|
||||
self.grid = grid
|
||||
self.grid._clSize = self.grid.GetColLabelSize()
|
||||
self.lwin = grid.GetGridRowLabelWindow()
|
||||
self.lwin.PushEventHandler(self)
|
||||
self.rowWin = None
|
||||
self.uy = self.grid.GetScrollPixelsPerUnit()[1]
|
||||
self.startY = -10
|
||||
self.cellY = 0
|
||||
self.didMove = False
|
||||
self.isDragging = False
|
||||
|
||||
EVT_MOTION(self,self.OnMouseMove)
|
||||
EVT_LEFT_DOWN(self,self.OnPress)
|
||||
EVT_LEFT_UP(self,self.OnRelease)
|
||||
|
||||
def OnMouseMove(self,evt):
|
||||
if self.isDragging:
|
||||
if abs(self.startY - evt.m_y) >= 3:
|
||||
self.didMove = True
|
||||
x,sy = self.grid.GetViewStart()
|
||||
w,h = self.lwin.GetClientSizeTuple()
|
||||
y = sy * self.uy
|
||||
if (evt.m_y + y) < y:
|
||||
y = evt.m_y + y
|
||||
elif evt.m_y > h:
|
||||
y += evt.m_y - h
|
||||
if y < 1: y = 0
|
||||
else: y /= self.uy
|
||||
if y != sy:
|
||||
if wxPlatform == '__WXMSW__':
|
||||
self.rowWin.Show(False)
|
||||
self.grid.Scroll(x,y)
|
||||
x,y = self.lwin.ClientToScreenXY(0,evt.m_y)
|
||||
x,y = self.grid.ScreenToClientXY(x,y)
|
||||
if not self.rowWin.IsShown():
|
||||
self.rowWin.Show(True)
|
||||
py = y - self.cellY
|
||||
if py < 0 + self.grid._clSize: py = 0 + self.grid._clSize
|
||||
if py > h - self.rowWin.GetSizeTuple()[1] + self.grid._clSize:
|
||||
py = h - self.rowWin.GetSizeTuple()[1] + self.grid._clSize
|
||||
self.rowWin.DisplayAt(x,py)
|
||||
return
|
||||
evt.Skip()
|
||||
|
||||
def OnPress(self,evt):
|
||||
self.startY = evt.m_y
|
||||
sy = self.grid.GetViewStart()[1] * self.uy
|
||||
sy -= self.grid._clSize
|
||||
px,py = self.lwin.ClientToScreenXY(evt.m_x,evt.m_y)
|
||||
px,py = self.grid.ScreenToClientXY(px,py)
|
||||
if self.grid.YToEdgeOfRow(py + sy) != wxNOT_FOUND:
|
||||
evt.Skip()
|
||||
return
|
||||
|
||||
self.isDragging = True
|
||||
self.didMove = False
|
||||
row = self.grid.YToRow(py + sy)
|
||||
rect = self.grid.RowToRect(row)
|
||||
self.cellY = py + sy - rect.y
|
||||
size = self.lwin.GetSizeTuple()
|
||||
rect.x = 0
|
||||
rect.y -= sy + self.grid._clSize
|
||||
rect.width = size[0]
|
||||
rowImg = self._CaptureImage(rect)
|
||||
self.rowWin = RowDragWindow(self.grid,rowImg,row)
|
||||
self.rowWin.Show(False)
|
||||
self.lwin.CaptureMouse()
|
||||
|
||||
def OnRelease(self,evt):
|
||||
if self.isDragging:
|
||||
self.lwin.ReleaseMouse()
|
||||
self.rowWin.Show(False)
|
||||
self.isDragging = False
|
||||
if not self.didMove:
|
||||
py = self.lwin.ClientToScreenXY(0,self.startY)[1]
|
||||
py = self.grid.ScreenToClientXY(0,py)[1]
|
||||
sy = self.grid.GetViewStart()[1] * self.uy
|
||||
sy -= self.grid._clSize
|
||||
row = self.grid.YToRow(py + sy)
|
||||
if row != wxNOT_FOUND:
|
||||
self.grid.SelectRow(row,evt.m_controlDown)
|
||||
return
|
||||
else:
|
||||
bRow = self.rowWin.GetInsertionRow()
|
||||
dRow = self.rowWin.GetMoveRow()
|
||||
wxPostEvent(self,wxGridRowMoveEvent(self.grid.GetId(),
|
||||
dRow,bRow))
|
||||
self.rowWin.Destroy()
|
||||
evt.Skip()
|
||||
|
||||
def _CaptureImage(self,rect):
|
||||
bmp = wxEmptyBitmap(rect.width,rect.height)
|
||||
memdc = wxMemoryDC()
|
||||
memdc.SelectObject(bmp)
|
||||
dc = wxWindowDC(self.lwin)
|
||||
memdc.Blit(0,0,rect.width,rect.height,dc,rect.x,rect.y)
|
||||
memdc.SelectObject(wxNullBitmap)
|
||||
return bmp
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
ColDragWindow = wx.lib.gridmovers.ColDragWindow
|
||||
EVT_GRID_COL_MOVE = wx.lib.gridmovers.EVT_GRID_COL_MOVE
|
||||
EVT_GRID_ROW_MOVE = wx.lib.gridmovers.EVT_GRID_ROW_MOVE
|
||||
RowDragWindow = wx.lib.gridmovers.RowDragWindow
|
||||
_ColToRect = wx.lib.gridmovers._ColToRect
|
||||
_RowToRect = wx.lib.gridmovers._RowToRect
|
||||
wxGridColMoveEvent = wx.lib.gridmovers.wxGridColMoveEvent
|
||||
wxGridColMover = wx.lib.gridmovers.wxGridColMover
|
||||
wxGridRowMoveEvent = wx.lib.gridmovers.wxGridRowMoveEvent
|
||||
wxGridRowMover = wx.lib.gridmovers.wxGridRowMover
|
||||
|
||||
@@ -1,267 +1,10 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.grids
|
||||
# Purpose: An example sizer derived from the C++ wxPySizer that
|
||||
# sizes items in a fixed or flexible grid.
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 21-Sept-1999
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 1999 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
In this module you will find wxGridSizer and wxFlexGridSizer. Please
|
||||
note that these sizers have since been ported to C++ and those
|
||||
versions are now exposed in the regular wxPython wrappers. However I
|
||||
am also leaving them here in the library so they can serve as an
|
||||
example of how to implement sizers in Python.
|
||||
|
||||
wxGridSizer: Sizes and positions items such that all rows are the same
|
||||
height and all columns are the same width. You can specify a gap in
|
||||
pixels to be used between the rows and/or the columns. When you
|
||||
create the sizer you specify the number of rows or the number of
|
||||
columns and then as you add items it figures out the other dimension
|
||||
automatically. Like other sizers, items can be set to fill their
|
||||
available space, or to be aligned on a side, in a corner, or in the
|
||||
center of the space. When the sizer is resized, all the items are
|
||||
resized the same amount so all rows and all columns remain the same
|
||||
size.
|
||||
|
||||
wxFlexGridSizer: Derives from wxGridSizer and adds the ability for
|
||||
particular rows and/or columns to be marked as growable. This means
|
||||
that when the sizer changes size, the growable rows and colums are the
|
||||
ones that stretch. The others remain at their initial size.
|
||||
|
||||
See the demo for a couple examples for how to use them.
|
||||
"""
|
||||
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
import operator
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class wxGridSizer(wxPySizer):
|
||||
def __init__(self, rows=0, cols=0, hgap=0, vgap=0):
|
||||
wxPySizer.__init__(self)
|
||||
if rows == 0 and cols == 0:
|
||||
raise ValueError, "rows and cols cannot both be zero"
|
||||
|
||||
self.rows = rows
|
||||
self.cols = cols
|
||||
self.hgap = hgap
|
||||
self.vgap = vgap
|
||||
|
||||
|
||||
def SetRows(self, rows):
|
||||
if rows == 0 and self.cols == 0:
|
||||
raise ValueError, "rows and cols cannot both be zero"
|
||||
self.rows = rows
|
||||
|
||||
def SetColumns(self, cols):
|
||||
if self.rows == 0 and cols == 0:
|
||||
raise ValueError, "rows and cols cannot both be zero"
|
||||
self.cols = cols
|
||||
|
||||
def GetRows(self):
|
||||
return self.rows
|
||||
|
||||
def GetColumns(self):
|
||||
return self.cols
|
||||
|
||||
def SetHgap(self, hgap):
|
||||
self.hgap = hgap
|
||||
|
||||
def SetVgap(self, vgap):
|
||||
self.vgap = vgap
|
||||
|
||||
def GetHgap(self, hgap):
|
||||
return self.hgap
|
||||
|
||||
def GetVgap(self, vgap):
|
||||
return self.vgap
|
||||
|
||||
#--------------------------------------------------
|
||||
def CalcMin(self):
|
||||
items = self.GetChildren()
|
||||
nitems = len(items)
|
||||
nrows = self.rows
|
||||
ncols = self.cols
|
||||
|
||||
if ncols > 0:
|
||||
nrows = (nitems + ncols-1) / ncols
|
||||
else:
|
||||
ncols = (nitems + nrows-1) / nrows
|
||||
|
||||
# Find the max width and height for any component.
|
||||
w = 0
|
||||
h = 0
|
||||
for item in items:
|
||||
size = item.CalcMin()
|
||||
w = max(w, size.width)
|
||||
h = max(h, size.height)
|
||||
|
||||
return wxSize(ncols * w + (ncols-1) * self.hgap,
|
||||
nrows * h + (nrows-1) * self.vgap)
|
||||
|
||||
|
||||
#--------------------------------------------------
|
||||
def RecalcSizes(self):
|
||||
items = self.GetChildren()
|
||||
if not items:
|
||||
return
|
||||
|
||||
nitems = len(items)
|
||||
nrows = self.rows
|
||||
ncols = self.cols
|
||||
|
||||
if ncols > 0:
|
||||
nrows = (nitems + ncols-1) / ncols
|
||||
else:
|
||||
ncols = (nitems + nrows-1) / nrows
|
||||
|
||||
|
||||
sz = self.GetSize()
|
||||
pt = self.GetPosition()
|
||||
w = (sz.width - (ncols - 1) * self.hgap) / ncols;
|
||||
h = (sz.height - (nrows - 1) * self.vgap) / nrows;
|
||||
|
||||
x = pt.x
|
||||
for c in range(ncols):
|
||||
y = pt.y
|
||||
for r in range(nrows):
|
||||
i = r * ncols + c
|
||||
if i < nitems:
|
||||
self.SetItemBounds(items[i], x, y, w, h)
|
||||
y = y + h + self.vgap
|
||||
x = x + w + self.hgap
|
||||
|
||||
|
||||
#--------------------------------------------------
|
||||
def SetItemBounds(self, item, x, y, w, h):
|
||||
# calculate the item's size and position within
|
||||
# its grid cell
|
||||
ipt = wxPoint(x, y)
|
||||
isz = item.CalcMin()
|
||||
flag = item.GetFlag()
|
||||
|
||||
if flag & wxEXPAND or flag & wxSHAPED:
|
||||
isz = wxSize(w, h)
|
||||
else:
|
||||
if flag & wxALIGN_CENTER_HORIZONTAL:
|
||||
ipt.x = x + (w - isz.width) / 2
|
||||
elif flag & wxALIGN_RIGHT:
|
||||
ipt.x = x + (w - isz.width)
|
||||
|
||||
if flag & wxALIGN_CENTER_VERTICAL:
|
||||
ipt.y = y + (h - isz.height) / 2
|
||||
elif flag & wxALIGN_BOTTOM:
|
||||
ipt.y = y + (h - isz.height)
|
||||
|
||||
item.SetDimension(ipt, isz)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
class wxFlexGridSizer(wxGridSizer):
|
||||
def __init__(self, rows=0, cols=0, hgap=0, vgap=0):
|
||||
wxGridSizer.__init__(self, rows, cols, hgap, vgap)
|
||||
self.rowHeights = []
|
||||
self.colWidths = []
|
||||
self.growableRows = []
|
||||
self.growableCols = []
|
||||
|
||||
def AddGrowableRow(self, idx):
|
||||
self.growableRows.append(idx)
|
||||
|
||||
def AddGrowableCol(self, idx):
|
||||
self.growableCols.append(idx)
|
||||
|
||||
#--------------------------------------------------
|
||||
def CalcMin(self):
|
||||
items = self.GetChildren()
|
||||
nitems = len(items)
|
||||
nrows = self.rows
|
||||
ncols = self.cols
|
||||
|
||||
if ncols > 0:
|
||||
nrows = (nitems + ncols-1) / ncols
|
||||
else:
|
||||
ncols = (nitems + nrows-1) / nrows
|
||||
|
||||
# Find the max width and height for any component.
|
||||
self.rowHeights = [0] * nrows
|
||||
self.colWidths = [0] * ncols
|
||||
for i in range(len(items)):
|
||||
size = items[i].CalcMin()
|
||||
row = i / ncols
|
||||
col = i % ncols
|
||||
self.rowHeights[row] = max(size.height, self.rowHeights[row])
|
||||
self.colWidths[col] = max(size.width, self.colWidths[col])
|
||||
|
||||
# Add up all the widths and heights
|
||||
cellsWidth = reduce(operator.__add__, self.colWidths)
|
||||
cellHeight = reduce(operator.__add__, self.rowHeights)
|
||||
|
||||
return wxSize(cellsWidth + (ncols-1) * self.hgap,
|
||||
cellHeight + (nrows-1) * self.vgap)
|
||||
|
||||
|
||||
#--------------------------------------------------
|
||||
def RecalcSizes(self):
|
||||
items = self.GetChildren()
|
||||
if not items:
|
||||
return
|
||||
|
||||
nitems = len(items)
|
||||
nrows = self.rows
|
||||
ncols = self.cols
|
||||
|
||||
if ncols > 0:
|
||||
nrows = (nitems + ncols-1) / ncols
|
||||
else:
|
||||
ncols = (nitems + nrows-1) / nrows
|
||||
|
||||
minsz = self.CalcMin()
|
||||
sz = self.GetSize()
|
||||
pt = self.GetPosition()
|
||||
|
||||
# Check for growables
|
||||
if self.growableRows and sz.height > minsz.height:
|
||||
delta = (sz.height - minsz.height) / len(self.growableRows)
|
||||
for idx in self.growableRows:
|
||||
self.rowHeights[idx] = self.rowHeights[idx] + delta
|
||||
|
||||
if self.growableCols and sz.width > minsz.width:
|
||||
delta = (sz.width - minsz.width) / len(self.growableCols)
|
||||
for idx in self.growableCols:
|
||||
self.colWidths[idx] = self.colWidths[idx] + delta
|
||||
|
||||
# bottom right corner
|
||||
sz = wxSize(pt.x + sz.width, pt.y + sz.height)
|
||||
|
||||
# Layout each cell
|
||||
x = pt.x
|
||||
for c in range(ncols):
|
||||
y = pt.y
|
||||
for r in range(nrows):
|
||||
i = r * ncols + c
|
||||
if i < nitems:
|
||||
w = max(0, min(self.colWidths[c], sz.width - x))
|
||||
h = max(0, min(self.rowHeights[r], sz.height - y))
|
||||
self.SetItemBounds(items[i], x, y, w, h)
|
||||
y = y + self.rowHeights[r] + self.vgap
|
||||
x = x + self.colWidths[c] + self.hgap
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.grids
|
||||
|
||||
__doc__ = wx.lib.grids.__doc__
|
||||
|
||||
wxFlexGridSizer = wx.lib.grids.wxFlexGridSizer
|
||||
wxGridSizer = wx.lib.grids.wxGridSizer
|
||||
|
||||
@@ -1,315 +1,14 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: BrowseImage.py
|
||||
# Purpose: Display and Select Image Files
|
||||
#
|
||||
# Author: Lorne White
|
||||
#
|
||||
# Version: 1.0
|
||||
# Date: January 29, 2002
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
# 1.0 Release
|
||||
# Create list of all available image file types
|
||||
# View "All Image" File Types as default filter
|
||||
# Sort the file list
|
||||
# Use newer "re" function for patterns
|
||||
import wx.lib.imagebrowser
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
__doc__ = wx.lib.imagebrowser.__doc__
|
||||
|
||||
import os, sys
|
||||
from wxPython.wx import *
|
||||
dir_path = os.getcwd()
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def ConvertBMP(file_nm):
|
||||
if file_nm is None:
|
||||
return None
|
||||
|
||||
fl_fld = os.path.splitext(file_nm)
|
||||
ext = fl_fld[1]
|
||||
ext = ext[1:].lower()
|
||||
if ext == 'bmp':
|
||||
image = wxImage(file_nm, wxBITMAP_TYPE_BMP)
|
||||
elif ext == 'gif':
|
||||
image = wxImage(file_nm, wxBITMAP_TYPE_GIF)
|
||||
elif ext == 'png':
|
||||
image = wxImage(file_nm, wxBITMAP_TYPE_PNG)
|
||||
elif ext == 'jpg':
|
||||
image = wxImage(file_nm, wxBITMAP_TYPE_JPEG)
|
||||
elif ext == 'pcx':
|
||||
image = wxImage(file_nm, wxBITMAP_TYPE_PCX)
|
||||
elif ext == 'tif':
|
||||
image = wxImage(file_nm, wxBITMAP_TYPE_TIF)
|
||||
elif ext == 'pnm':
|
||||
image = wxImage(file_nm, wxBITMAP_TYPE_PNM)
|
||||
else:
|
||||
image = wxImage(file_nm, wxBITMAP_TYPE_ANY)
|
||||
|
||||
return image
|
||||
|
||||
def GetSize(file_nm): # for scaling image values
|
||||
image = ConvertBMP(file_nm)
|
||||
bmp = image.ConvertToBitmap()
|
||||
size = bmp.GetWidth(), bmp.GetHeight()
|
||||
return size
|
||||
|
||||
class ImageView(wxWindow):
|
||||
def __init__(self, parent, id=-1, pos=wxDefaultPosition, size=wxDefaultSize):
|
||||
wxWindow.__init__(self, parent, id, pos, size)
|
||||
self.win = parent
|
||||
self.image = None
|
||||
self.back_color = 'WHITE'
|
||||
self.border_color = 'BLACK'
|
||||
|
||||
self.image_sizex = size.width
|
||||
self.image_sizey = size.height
|
||||
self.image_posx = pos.x
|
||||
self.image_posy = pos.y
|
||||
EVT_PAINT(self, self.OnPaint)
|
||||
|
||||
wxInitAllImageHandlers()
|
||||
|
||||
def OnPaint(self, event):
|
||||
dc = wxPaintDC(self)
|
||||
self.DrawImage(dc)
|
||||
|
||||
def DrawImage(self, dc):
|
||||
dc.BeginDrawing()
|
||||
self.DrawImage(dc)
|
||||
dc.EndDrawing()
|
||||
|
||||
def SetValue(self, file_nm): # display the selected file in the panel
|
||||
image = ConvertBMP(file_nm)
|
||||
self.image = image
|
||||
self.Refresh()
|
||||
|
||||
def DrawBorder(self, dc):
|
||||
brush = wxBrush(wxNamedColour(self.back_color), wxSOLID)
|
||||
dc.SetBrush(brush)
|
||||
dc.SetPen(wxPen(wxNamedColour(self.border_color), 1))
|
||||
dc.DrawRectangle(0, 0, self.image_sizex, self.image_sizey)
|
||||
|
||||
def DrawImage(self, dc):
|
||||
try:
|
||||
image = self.image
|
||||
except:
|
||||
return
|
||||
|
||||
self.DrawBorder(dc)
|
||||
if image is None:
|
||||
return
|
||||
|
||||
bmp = image.ConvertToBitmap()
|
||||
|
||||
iwidth = bmp.GetWidth() # dimensions of image file
|
||||
iheight = bmp.GetHeight()
|
||||
|
||||
diffx = (self.image_sizex - iwidth)/2 # center calc
|
||||
if iwidth >= self.image_sizex -10: # if image width fits in window adjust
|
||||
diffx = 5
|
||||
iwidth = self.image_sizex - 10
|
||||
|
||||
diffy = (self.image_sizey - iheight)/2 # center calc
|
||||
if iheight >= self.image_sizey - 10: # if image height fits in window adjust
|
||||
diffy = 5
|
||||
iheight = self.image_sizey - 10
|
||||
|
||||
image.Rescale(iwidth, iheight) # rescale to fit the window
|
||||
image.ConvertToBitmap()
|
||||
bmp = image.ConvertToBitmap()
|
||||
dc.DrawBitmap(bmp, diffx, diffy) # draw the image to window
|
||||
|
||||
|
||||
class ImageDialog(wxDialog):
|
||||
def __init__(self, parent, set_dir = None):
|
||||
wxDialog.__init__(self, parent, -1, "Image Browser", wxPyDefaultPosition, wxSize(400, 400))
|
||||
|
||||
self.x_pos = 30 # initial display positions
|
||||
self.y_pos = 20
|
||||
self.delta = 20
|
||||
|
||||
size = wxSize(80, 25)
|
||||
|
||||
self.set_dir = os.getcwd()
|
||||
self.set_file = None
|
||||
|
||||
if set_dir != None:
|
||||
if os.path.exists(set_dir): # set to working directory if nothing set
|
||||
self.set_dir = set_dir
|
||||
|
||||
self.dir_x = self.x_pos
|
||||
self.dir_y = self.y_pos
|
||||
self.DisplayDir() # display the directory value
|
||||
|
||||
self.y_pos = self.y_pos + self.delta
|
||||
|
||||
mID = wxNewId()
|
||||
wxButton(self, mID, ' Set Directory ', wxPoint(self.x_pos, self.y_pos), size).SetDefault()
|
||||
EVT_BUTTON(self, mID, self.SetDirect)
|
||||
|
||||
self.type_posy = self.y_pos # save the y position for the image type combo
|
||||
|
||||
self.fl_ext = '*.bmp' # initial setting for file filtering
|
||||
self.GetFiles() # get the file list
|
||||
|
||||
self.y_pos = self.y_pos + self.delta + 10
|
||||
|
||||
self.list_height = 150
|
||||
|
||||
# List of Labels
|
||||
mID = wxNewId()
|
||||
self.tb = tb = wxListBox(self, mID, wxPoint(self.x_pos, self.y_pos), wxSize(160, self.list_height), self.fl_list, wxLB_SINGLE)
|
||||
EVT_LISTBOX(self, mID, self.OnListClick)
|
||||
EVT_LISTBOX_DCLICK(self, mID, self.OnListDClick)
|
||||
|
||||
width, height = self.tb.GetSizeTuple()
|
||||
image_posx = self.x_pos + width + 20 # positions for setting the image window
|
||||
image_posy = self.y_pos
|
||||
image_sizex = 150
|
||||
image_sizey = self.list_height
|
||||
|
||||
self.fl_types = ["All Images", "Bmp", "Gif", "Png", "Jpg", "Ico", "Pnm", "Pcx", "Tif", "All Files"]
|
||||
self.fl_ext_types = { "All Images": "All", "Bmp": "*.bmp", "Gif": "*.gif", "Png": "*.png", "Jpg": "*.jpg",
|
||||
"Ico": "*.ico", "Pnm": "*.pnm", "Pcx": "*.pcx", "Tif": "*.tif", "All Files": "*.*" }
|
||||
|
||||
self.set_type = self.fl_types[0] # initial file filter setting
|
||||
self.fl_ext = self.fl_ext_types[self.set_type]
|
||||
|
||||
mID = wxNewId()
|
||||
self.sel_type = wxComboBox(self, mID, self.set_type, wxPoint(image_posx , self.type_posy), wxSize(150, -1), self.fl_types, wxCB_DROPDOWN)
|
||||
EVT_COMBOBOX(self, mID, self.OnSetType)
|
||||
|
||||
self.image_view = ImageView(self, pos=wxPoint(image_posx, image_posy), size=wxSize(image_sizex, image_sizey))
|
||||
|
||||
self.y_pos = self.y_pos + height + 20
|
||||
|
||||
mID = wxNewId()
|
||||
wxButton(self, mID, ' Select ', wxPoint(100, self.y_pos), size).SetDefault()
|
||||
EVT_BUTTON(self, mID, self.OnOk)
|
||||
|
||||
wxButton(self, wxID_CANCEL, 'Cancel', wxPoint(250, self.y_pos), size)
|
||||
|
||||
self.y_pos = self.y_pos + self.delta
|
||||
fsize = wxSize(400, self.y_pos + 50) # resize dialog for final vertical position
|
||||
self.SetSize(fsize)
|
||||
|
||||
self.ResetFiles()
|
||||
|
||||
def GetFiles(self): # get the file list using directory and extension values
|
||||
if self.fl_ext == "All":
|
||||
all_files = []
|
||||
for ftypes in self.fl_types[1:-1]: # get list of all available image types
|
||||
filter = self.fl_ext_types[ftypes]
|
||||
print "filter = ", filter
|
||||
self.fl_val = FindFiles(self, self.set_dir, filter)
|
||||
all_files = all_files + self.fl_val.files # add to list of files
|
||||
self.fl_list = all_files
|
||||
else:
|
||||
self.fl_val = FindFiles(self, self.set_dir, self.fl_ext)
|
||||
self.fl_list = self.fl_val.files
|
||||
|
||||
self.fl_list.sort() # sort the file list
|
||||
|
||||
def DisplayDir(self): # display the working directory
|
||||
wxStaticText(self, -1, self.set_dir, wxPoint(self.dir_x, self.dir_y), wxSize(250, -1))
|
||||
|
||||
def OnSetType(self, event):
|
||||
val = event.GetString() # get file type value
|
||||
self.fl_ext = self.fl_ext_types[val]
|
||||
self.ResetFiles()
|
||||
|
||||
def OnListDClick(self, event):
|
||||
self.OnOk(0)
|
||||
|
||||
def OnListClick(self, event):
|
||||
val = event.GetSelection()
|
||||
self.SetListValue(val)
|
||||
|
||||
def SetListValue(self, val):
|
||||
file_nm = self.fl_list[val]
|
||||
self.set_file = file_val = os.path.join(self.set_dir, file_nm)
|
||||
self.image_view.SetValue(file_val)
|
||||
|
||||
def SetDirect(self, event): # set the new directory
|
||||
dlg = wxDirDialog(self)
|
||||
dlg.SetPath(self.set_dir)
|
||||
if dlg.ShowModal() == wxID_OK:
|
||||
self.set_dir = dlg.GetPath()
|
||||
self.ResetFiles()
|
||||
dlg.Destroy()
|
||||
|
||||
def ResetFiles(self): # refresh the display with files and initial image
|
||||
self.DisplayDir()
|
||||
self.GetFiles()
|
||||
self.tb.Set(self.fl_list)
|
||||
try:
|
||||
self.tb.SetSelection(0)
|
||||
self.SetListValue(0)
|
||||
except:
|
||||
self.image_view.SetValue(None)
|
||||
|
||||
def GetFile(self):
|
||||
return self.set_file
|
||||
|
||||
def GetDirectory(self):
|
||||
return self.set_dir
|
||||
|
||||
def OnCancel(self, event):
|
||||
self.result = None
|
||||
self.EndModal(wxID_CANCEL)
|
||||
|
||||
def OnOk(self, event):
|
||||
self.result = self.set_file
|
||||
self.EndModal(wxID_OK)
|
||||
|
||||
|
||||
def OnFileDlg(self):
|
||||
dlg = wxFileDialog(self, "Choose an Image File", ".", "", "Bmp (*.bmp)|*.bmp|JPEG (*.jpg)|*.jpg", wxOPEN)
|
||||
if dlg.ShowModal() == wxID_OK:
|
||||
path = dlg.GetPath()
|
||||
else:
|
||||
path = None
|
||||
dlg.Destroy()
|
||||
return path
|
||||
|
||||
class FindFiles:
|
||||
def __init__(self, parent, dir, mask):
|
||||
filelist = []
|
||||
dirlist = [".."]
|
||||
self.dir = dir
|
||||
self.file = ""
|
||||
mask = mask.upper()
|
||||
pattern = self.MakeRegex(mask)
|
||||
for i in os.listdir(dir):
|
||||
if i == "." or i == "..":
|
||||
continue
|
||||
path = os.path.join(dir, i)
|
||||
path = path.upper()
|
||||
value = i.upper()
|
||||
|
||||
if pattern.match(value) != None:
|
||||
filelist.append(i)
|
||||
|
||||
self.files = filelist
|
||||
|
||||
def MakeRegex(self, pattern):
|
||||
import re
|
||||
f = "" # Set up a regex for file names
|
||||
for ch in pattern:
|
||||
if ch == "*":
|
||||
f = f + ".*"
|
||||
elif ch == ".":
|
||||
f = f + "\."
|
||||
elif ch == "?":
|
||||
f = f + "."
|
||||
else:
|
||||
f = f + ch
|
||||
return re.compile(f+'$')
|
||||
|
||||
def StripExt(self, file_nm):
|
||||
fl_fld = os.path.splitext(file_nm)
|
||||
fl_name = fl_fld[0]
|
||||
ext = fl_fld[1]
|
||||
return ext[1:]
|
||||
ConvertBMP = wx.lib.imagebrowser.ConvertBMP
|
||||
FindFiles = wx.lib.imagebrowser.FindFiles
|
||||
GetSize = wx.lib.imagebrowser.GetSize
|
||||
ImageDialog = wx.lib.imagebrowser.ImageDialog
|
||||
ImageView = wx.lib.imagebrowser.ImageView
|
||||
OnFileDlg = wx.lib.imagebrowser.OnFileDlg
|
||||
|
||||
@@ -1,45 +1,10 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.imageutils
|
||||
# Purpose: A collection of functions for simple image manipulations
|
||||
#
|
||||
# Author: Robb Shecter
|
||||
#
|
||||
# Created: 7-Nov-2002
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2002 by
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from __future__ import nested_scopes
|
||||
import wx.lib.imageutils
|
||||
|
||||
__doc__ = wx.lib.imageutils.__doc__
|
||||
|
||||
def grayOut(anImage):
|
||||
"""
|
||||
Convert the given image (in place) to a grayed-out
|
||||
version, appropriate for a 'disabled' appearance.
|
||||
"""
|
||||
factor = 0.7 # 0 < f < 1. Higher is grayer.
|
||||
if anImage.HasMask():
|
||||
maskColor = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue())
|
||||
else:
|
||||
maskColor = None
|
||||
data = map(ord, list(anImage.GetData()))
|
||||
|
||||
for i in range(0, len(data), 3):
|
||||
pixel = (data[i], data[i+1], data[i+2])
|
||||
pixel = makeGray(pixel, factor, maskColor)
|
||||
for x in range(3):
|
||||
data[i+x] = pixel[x]
|
||||
anImage.SetData(''.join(map(chr, data)))
|
||||
|
||||
|
||||
def makeGray((r,g,b), factor, maskColor):
|
||||
"""
|
||||
Make a pixel grayed-out. If the pixel
|
||||
matches the maskColor, it won't be
|
||||
changed.
|
||||
"""
|
||||
if (r,g,b) != maskColor:
|
||||
return map(lambda x: int((230 - x) * factor) + x, (r,g,b))
|
||||
else:
|
||||
return (r,g,b)
|
||||
grayOut = wx.lib.imageutils.grayOut
|
||||
makeGray = wx.lib.imageutils.makeGray
|
||||
|
||||
@@ -1,471 +1,11 @@
|
||||
"""
|
||||
infoframe.py
|
||||
Released under wxWindows license etc.
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
This is a fairly rudimentary, but slightly fancier tha
|
||||
wxPyOnDemandOutputWindow (on which it's based; thanks Robin), version
|
||||
of the same sort of thing: a file-like class called
|
||||
wxInformationalMessagesFrame. This window also has a status bar with a
|
||||
couple of buttons for controlling the echoing of all output to a file
|
||||
with a randomly-chosen filename...
|
||||
import wx.lib.infoframe
|
||||
|
||||
The class behaves similarly to wxPyOnDemandOutputWindow in that (at
|
||||
least by default) the frame does not appear until written to, but is
|
||||
somewhat different in that, either under programmatic (the
|
||||
DisableOutput method) or user (the frame's close button, it's status
|
||||
bar's "Dismiss" button, or a "Disable output" item of some menu,
|
||||
perhaps of some other frame), the frame will be destroyed, an
|
||||
associated file closed, and writing to it will then do nothing. This
|
||||
can be reversed: either under programmatic (the EnableOutput method)
|
||||
or user (an "Enable output" item of some menu), a new frame will be
|
||||
opened,And an associated file (with a "randomly"selected filename)
|
||||
opened for writing [to which all subsequent displayed messages will be
|
||||
echoed].
|
||||
|
||||
Please note that, like wxPyOnDemandOutputWindow, the instance is not
|
||||
itself a subclass of wxWindow: when the window is open (and ONLY
|
||||
then), it's "frame" attribute is the actual instance of wFrame...
|
||||
|
||||
Typical usage:
|
||||
from wxPython.lib.infoframe import *
|
||||
... # ... modify your wxApp as follows:
|
||||
class myApp(wxApp):
|
||||
outputWindowClass = wxPyInformationalMessagesFrame
|
||||
...
|
||||
If you're running on Linux, you'll also have to supply an argument 1 to your
|
||||
constructor of myApp to redirect stdout/stderr to this window (it's done
|
||||
automatically for you on Windows).
|
||||
|
||||
If you don't want to redirect stdout/stderr, but use the class directly: do
|
||||
it this way:
|
||||
|
||||
InformationalMessagesFrame = wxPyInformationalMessagesFrame\
|
||||
([options from progname (default ""),
|
||||
txt (default "informational
|
||||
messages"])
|
||||
#^^^^ early in the program
|
||||
...
|
||||
InformationalMessagesFrame([comma-separated list of items to
|
||||
display. Note that these will never
|
||||
be separated by spaces as they may
|
||||
be when used in the Python 'print'
|
||||
command])
|
||||
|
||||
The latter statement, of course, may be repeated arbitrarily often.
|
||||
The window will not appear until it is written to, and it may be
|
||||
manually closed by the user, after which it will reappear again until
|
||||
written to... Also note that all output is echoed to a file with a
|
||||
randomly-generated name [see the mktemp module in the standard
|
||||
library], in the directory given as the 'dir' keyword argument to the
|
||||
InformationalMessagesFrame constructor [which has a default value of
|
||||
'.'), or set via the method SetOutputDirectory... This file will be
|
||||
closed with the window--a new one will be created [by default] upon
|
||||
each subsequent reopening.
|
||||
|
||||
Please also note the methods EnableOutput and DisableOutput, and the
|
||||
possible arguments for the constructor in the code below... (* TO DO:
|
||||
explain this here...*) Neither of these methods need be used at all,
|
||||
and in this case the frame will only be displayed once it has been
|
||||
written to, like wxPyOnDemandOutputWindow.
|
||||
|
||||
The former, EnableOutput, displays the frame with an introductory
|
||||
message, opens a random file to which future displayed output also
|
||||
goes (unless the nofile attribute is present), and sets the __debug__
|
||||
variable of each module to 1 (unless the no __debug__ attribute is
|
||||
present]. This is so that you can say, in any module whatsoever,
|
||||
|
||||
if __debug__:
|
||||
InformationalMessagesFrame("... with lots of %<Character> constructs"
|
||||
% TUPLE)
|
||||
|
||||
without worrying about the overhead of evaluating the arguments, and
|
||||
calling the wxInformationalMessagesFrame instance, in the case where
|
||||
debugging is not turned on. (This won't happen if the instance has an
|
||||
attribute no__debug__; you can arrange this by sub-classing...)
|
||||
|
||||
"Debug mode" can also be turned on by selecting the item-"Enable
|
||||
output" from the "Debug" menu [the default--see the optional arguments
|
||||
to the SetOtherMenuBar method] of a frame which has been either passed
|
||||
appropriately to the constructor of the wxInformationalMessagesFrame
|
||||
(see the code), or set via the SetOtherMenuBar method thereof. This
|
||||
also writes an empty string to the instance, meaning that the frame
|
||||
will open (unless DisablOutput has been called) with an appropriate
|
||||
introductory message (which will vary according to whether the
|
||||
instance/class has the "no __debug__" attribute)^ I have found this to
|
||||
be an extremely useful tool, in lieu of a full wxPython debugger...
|
||||
|
||||
Following this, the menu item is also disabled, and an item "Disable
|
||||
output" (again, by default) is enabled. Note that these need not be
|
||||
done: e.g., you don't NEED to have a menu with appropriate items; in
|
||||
this case simply do not call the SetOtherMenuBar method or use the
|
||||
othermenubar keyword argument of the class instance constructor.
|
||||
|
||||
The DisableOutput method does the reverse of this; it closes the
|
||||
window (and associated file), and sets the __debug__ variable of each
|
||||
module whose name begins with a capital letter {this happens to be the
|
||||
author's personal practice; all my python module start with capital
|
||||
letters} to 0. It also enables/disabled the appropriate menu items,
|
||||
if this was done previously (or SetOtherMenuBar has been called...).
|
||||
Note too that after a call to DisableOutput, nothing further will be
|
||||
done upon subsequent write()'s, until the EnableOutput method is
|
||||
called, either explicitly or by the menu selection above...
|
||||
|
||||
Finally, note that the file-like method close() destroys the window
|
||||
(and closes any associated file) and there is a file-like method
|
||||
write() which displays it's argument.
|
||||
|
||||
All (well, most) of this is made clear by the example code at the end
|
||||
of this file, which is run if the file is run by itself; otherwise,
|
||||
see the appropriate "stub" file in the wxPython demo.
|
||||
|
||||
"""
|
||||
|
||||
from wxPython.wx import *
|
||||
import sys, tempfile, os
|
||||
|
||||
class _MyStatusBar(wxStatusBar):
|
||||
def __init__(self, parent,callbacks=None,useopenbutton=0):
|
||||
wxStatusBar.__init__(self, parent, -1, style=wxTAB_TRAVERSAL)
|
||||
self.SetFieldsCount(3)
|
||||
|
||||
self.SetStatusText("",0)
|
||||
|
||||
ID = wxNewId()
|
||||
self.button1 = wxButton(self,ID,"Dismiss",
|
||||
style=wxTAB_TRAVERSAL)
|
||||
EVT_BUTTON(self,ID,self.OnButton1)
|
||||
|
||||
ID = wxNewId()
|
||||
if not useopenbutton:
|
||||
self.button2 = wxButton(self,ID,"Close File",
|
||||
style=wxTAB_TRAVERSAL)
|
||||
else:
|
||||
self.button2 = wxButton(self,ID,"Open New File",
|
||||
style=wxTAB_TRAVERSAL)
|
||||
EVT_BUTTON(self,ID,self.OnButton2)
|
||||
self.useopenbutton = useopenbutton
|
||||
self.callbacks = callbacks
|
||||
|
||||
# figure out how tall to make the status bar
|
||||
dc = wxClientDC(self)
|
||||
dc.SetFont(self.GetFont())
|
||||
(w,h) = dc.GetTextExtent('X')
|
||||
h = int(h * 1.8)
|
||||
self.SetSize(wxSize(100, h))
|
||||
self.OnSize("dummy")
|
||||
EVT_SIZE(self,self.OnSize)
|
||||
|
||||
# reposition things...
|
||||
def OnSize(self, event):
|
||||
self.CalculateSizes()
|
||||
rect = self.GetFieldRect(1)
|
||||
self.button1.SetPosition(wxPoint(rect.x+5, rect.y+2))
|
||||
self.button1.SetSize(wxSize(rect.width-10, rect.height-4))
|
||||
rect = self.GetFieldRect(2)
|
||||
self.button2.SetPosition(wxPoint(rect.x+5, rect.y+2))
|
||||
self.button2.SetSize(wxSize(rect.width-10, rect.height-4))
|
||||
|
||||
# widths........
|
||||
def CalculateSizes(self):
|
||||
dc = wxClientDC(self.button1)
|
||||
dc.SetFont(self.button1.GetFont())
|
||||
(w1,h) = dc.GetTextExtent(self.button1.GetLabel())
|
||||
|
||||
dc = wxClientDC(self.button2)
|
||||
dc.SetFont(self.button2.GetFont())
|
||||
(w2,h) = dc.GetTextExtent(self.button2.GetLabel())
|
||||
|
||||
self.SetStatusWidths([-1,w1+15,w2+15])
|
||||
|
||||
def OnButton1(self,event):
|
||||
self.callbacks[0] ()
|
||||
|
||||
def OnButton2(self,event):
|
||||
if self.useopenbutton and self.callbacks[2] ():
|
||||
self.button2.SetLabel ("Close File")
|
||||
elif self.callbacks[1] ():
|
||||
self.button2.SetLabel ("Open New File")
|
||||
self.useopenbutton = 1 - self.useopenbutton
|
||||
self.OnSize("")
|
||||
self.button2.Refresh(True)
|
||||
self.Refresh()
|
||||
|
||||
|
||||
|
||||
class wxPyInformationalMessagesFrame:
|
||||
def __init__(self,
|
||||
progname="",
|
||||
text="informational messages",
|
||||
dir='.',
|
||||
menuname="Debug",
|
||||
enableitem="Enable output",
|
||||
disableitem="Disable output",
|
||||
othermenubar=None):
|
||||
|
||||
self.SetOtherMenuBar(othermenubar,
|
||||
menuname=menuname,
|
||||
enableitem=enableitem,
|
||||
disableitem=disableitem)
|
||||
|
||||
if hasattr(self,"othermenu") and self.othermenu is not None:
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.disableitem)
|
||||
self.othermenu.Enable(i,0)
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.enableitem)
|
||||
self.othermenu.Enable(i,1)
|
||||
|
||||
self.frame = None
|
||||
self.title = "%s %s" % (progname,text)
|
||||
self.parent = None # use the SetParent method if desired...
|
||||
self.softspace = 1 # of rather limited use
|
||||
if dir:
|
||||
self.SetOutputDirectory(dir)
|
||||
|
||||
|
||||
def SetParent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
|
||||
def SetOtherMenuBar(self,
|
||||
othermenu,
|
||||
menuname="Debug",
|
||||
enableitem="Enable output",
|
||||
disableitem="Disable output"):
|
||||
self.othermenu = othermenu
|
||||
self.menuname = menuname
|
||||
self.enableitem = enableitem
|
||||
self.disableitem = disableitem
|
||||
|
||||
|
||||
f = None
|
||||
|
||||
|
||||
def write(self,string):
|
||||
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()
|
||||
|
||||
if self.Enabled:
|
||||
if self.f:
|
||||
self.f.write(string)
|
||||
self.f.flush()
|
||||
|
||||
move = 1
|
||||
if (hasattr(self,"text")
|
||||
and self.text is not None
|
||||
and self.text.GetInsertionPoint() != self.text.GetLastPosition()):
|
||||
move = 0
|
||||
|
||||
if not self.frame:
|
||||
self.frame = wxFrame(self.parent, -1, self.title, size=(450, 300),
|
||||
style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
|
||||
self.text = wxTextCtrl(self.frame, -1, "",
|
||||
style = wxTE_MULTILINE|wxTE_READONLY|wxTE_RICH)
|
||||
|
||||
self.frame.sb = _MyStatusBar(self.frame,
|
||||
callbacks=[self.DisableOutput,
|
||||
self.CloseFile,
|
||||
self.OpenNewFile],
|
||||
useopenbutton=hasattr(self,
|
||||
"nofile"))
|
||||
self.frame.SetStatusBar(self.frame.sb)
|
||||
self.frame.Show(True)
|
||||
EVT_CLOSE(self.frame, self.OnCloseWindow)
|
||||
|
||||
if hasattr(self,"nofile"):
|
||||
self.text.AppendText(
|
||||
"Please close this window (or select the "
|
||||
"'Dismiss' button below) when desired. By "
|
||||
"default all messages written to this window "
|
||||
"will NOT be written to a file--you "
|
||||
"may change this by selecting 'Open New File' "
|
||||
"below, allowing you to select a "
|
||||
"new file...\n\n")
|
||||
else:
|
||||
tempfile.tempdir = self.dir
|
||||
filename = os.path.abspath(tempfile.mktemp ())
|
||||
self.text.AppendText(
|
||||
"Please close this window (or select the "
|
||||
"'Dismiss' button below) when desired. By "
|
||||
"default all messages written to this window "
|
||||
"will also be written to the file '%s'--you "
|
||||
"may close this file by selecting 'Close "
|
||||
"File' below, whereupon this button will be "
|
||||
"replaced with one allowing you to select a "
|
||||
"new file...\n\n" % filename)
|
||||
try:
|
||||
self.f = open(filename, 'w')
|
||||
self.frame.sb.SetStatusText("File '%s' opened..."
|
||||
% filename,
|
||||
0)
|
||||
except EnvironmentError:
|
||||
self.frame.sb.SetStatusText("File creation failed "
|
||||
"(filename '%s')..."
|
||||
% filename,
|
||||
0)
|
||||
self.text.AppendText(string)
|
||||
|
||||
if move:
|
||||
self.text.ShowPosition(self.text.GetLastPosition())
|
||||
|
||||
if not hasattr(self,"no__debug__"):
|
||||
for m in sys.modules.values():
|
||||
if m is not None:# and m.__dict__.has_key("__debug__"):
|
||||
m.__dict__["__debug__"] = 1
|
||||
|
||||
if hasattr(self,"othermenu") and self.othermenu is not None:
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.disableitem)
|
||||
self.othermenu.Enable(i,1)
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.enableitem)
|
||||
self.othermenu.Enable(i,0)
|
||||
|
||||
|
||||
Enabled = 1
|
||||
|
||||
def OnCloseWindow(self, event, exiting=0):
|
||||
if self.f:
|
||||
self.f.close()
|
||||
self.f = None
|
||||
|
||||
if (hasattr(self,"othermenu") and self.othermenu is not None
|
||||
and self.frame is not None
|
||||
and not exiting):
|
||||
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.disableitem)
|
||||
self.othermenu.Enable(i,0)
|
||||
i = self.othermenu.FindMenuItem(self.menuname,self.enableitem)
|
||||
self.othermenu.Enable(i,1)
|
||||
|
||||
if not hasattr(self,"no__debug__"):
|
||||
for m in sys.modules.values():
|
||||
if m is not None:# and m.__dict__.has_key("__debug__"):
|
||||
m.__dict__["__debug__"] = 0
|
||||
|
||||
if self.frame is not None: # typically True, but, e.g., allows
|
||||
# DisableOutput method (which calls this
|
||||
# one) to be called when the frame is not
|
||||
# actually open, so that it is always safe
|
||||
# to call this method...
|
||||
frame = self.frame
|
||||
self.frame = self.text = None
|
||||
frame.Destroy()
|
||||
self.Enabled = 1
|
||||
|
||||
|
||||
def EnableOutput(self,
|
||||
event=None,# event must be the first optional argument...
|
||||
othermenubar=None,
|
||||
menuname="Debug",
|
||||
enableitem="Enable output",
|
||||
disableitem="Disable output"):
|
||||
|
||||
if othermenubar is not None:
|
||||
self.SetOtherMenuBar(othermenubar,
|
||||
menuname=menuname,
|
||||
enableitem=enableitem,
|
||||
disableitem=disableitem)
|
||||
self.Enabled = 1
|
||||
if self.f:
|
||||
self.f.close()
|
||||
self.f = None
|
||||
self.write("")
|
||||
|
||||
|
||||
def CloseFile(self):
|
||||
if self.f:
|
||||
if self.frame:
|
||||
self.frame.sb.SetStatusText("File '%s' closed..."
|
||||
% os.path.abspath(self.f.name),
|
||||
0)
|
||||
self.f.close ()
|
||||
self.f = None
|
||||
else:
|
||||
if self.frame:
|
||||
self.frame.sb.SetStatusText("")
|
||||
if self.frame:
|
||||
self.frame.sb.Refresh()
|
||||
return 1
|
||||
|
||||
|
||||
def OpenNewFile(self):
|
||||
self.CloseFile()
|
||||
dlg = wxFileDialog(self.frame,
|
||||
"Choose a new log file", self.dir,"","*",
|
||||
wxSAVE | wxHIDE_READONLY | wxOVERWRITE_PROMPT)
|
||||
if dlg.ShowModal() == wxID_CANCEL:
|
||||
dlg.Destroy()
|
||||
return 0
|
||||
else:
|
||||
try:
|
||||
self.f = open(os.path.abspath(dlg.GetPath()),'w')
|
||||
except EnvironmentError:
|
||||
dlg.Destroy()
|
||||
return 0
|
||||
dlg.Destroy()
|
||||
if self.frame:
|
||||
self.frame.sb.SetStatusText("File '%s' opened..."
|
||||
% os.path.abspath(self.f.name),
|
||||
0)
|
||||
if hasattr(self,"nofile"):
|
||||
self.frame.sb = _MyStatusBar(self.frame,
|
||||
callbacks=[self.DisableOutput,
|
||||
self.CloseFile,
|
||||
self.OpenNewFile])
|
||||
self.frame.SetStatusBar(self.frame.sb)
|
||||
if hasattr(self,"nofile"):
|
||||
delattr(self,"nofile")
|
||||
return 1
|
||||
|
||||
|
||||
def DisableOutput(self,
|
||||
event=None,# event must be the first optional argument...
|
||||
exiting=0):
|
||||
self.write("<InformationalMessagesFrame>.DisableOutput()\n")
|
||||
if hasattr(self,"frame") \
|
||||
and self.frame is not None:
|
||||
self.OnCloseWindow("Dummy",exiting=exiting)
|
||||
self.Enabled = 0
|
||||
|
||||
|
||||
def close(self):
|
||||
self.DisableOutput()
|
||||
|
||||
|
||||
def flush(self):
|
||||
if self.text:
|
||||
self.text.SetInsertionPointEnd()
|
||||
wxYield()
|
||||
|
||||
|
||||
def __call__(self,* args):
|
||||
for s in args:
|
||||
self.write (str (s))
|
||||
|
||||
|
||||
def SetOutputDirectory(self,dir):
|
||||
self.dir = os.path.abspath(dir)
|
||||
## sys.__stderr__.write("Directory: os.path.abspath(%s) = %s\n"
|
||||
## % (dir,self.dir))
|
||||
|
||||
|
||||
|
||||
class Dummy_wxPyInformationalMessagesFrame:
|
||||
def __init__(self,progname=""):
|
||||
self.softspace = 1
|
||||
def __call__(self,*args):
|
||||
pass
|
||||
def write(self,s):
|
||||
pass
|
||||
def flush(self):
|
||||
pass
|
||||
def close(self):
|
||||
pass
|
||||
def EnableOutput(self):
|
||||
pass
|
||||
def __call__(self,* args):
|
||||
pass
|
||||
def DisableOutput(self,exiting=0):
|
||||
pass
|
||||
def SetParent(self,wX):
|
||||
pass
|
||||
__doc__ = wx.lib.infoframe.__doc__
|
||||
|
||||
Dummy_wxPyInformationalMessagesFrame = wx.lib.infoframe.Dummy_wxPyInformationalMessagesFrame
|
||||
_MyStatusBar = wx.lib.infoframe._MyStatusBar
|
||||
wxPyInformationalMessagesFrame = wx.lib.infoframe.wxPyInformationalMessagesFrame
|
||||
|
||||
@@ -1,894 +1,12 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: wxPython.lib.intctrl.py
|
||||
# Author: Will Sadkin
|
||||
# Created: 01/16/2003
|
||||
# Copyright: (c) 2003 by Will Sadkin
|
||||
# RCS-ID: $Id$
|
||||
# License: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
# NOTE:
|
||||
# This was written to provide a standard integer edit control for wxPython.
|
||||
#
|
||||
# wxIntCtrl permits integer (long) values to be retrieved or set via
|
||||
# .GetValue() and .SetValue(), and provides an EVT_INT() event function
|
||||
# for trapping changes to the control.
|
||||
#
|
||||
# It supports negative integers as well as the naturals, and does not
|
||||
# permit leading zeros or an empty control; attempting to delete the
|
||||
# contents of the control will result in a (selected) value of zero,
|
||||
# thus preserving a legitimate integer value, or an empty control
|
||||
# (if a value of None is allowed for the control.) Similarly, replacing the
|
||||
# contents of the control with '-' will result in a selected (absolute)
|
||||
# value of -1.
|
||||
#
|
||||
# wxIntCtrl also supports range limits, with the option of either
|
||||
# enforcing them or simply coloring the text of the control if the limits
|
||||
# are exceeded.
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython.wx import *
|
||||
import types, string
|
||||
from sys import maxint
|
||||
MAXINT = maxint # (constants should be in upper case)
|
||||
MININT = -maxint-1
|
||||
import wx.lib.intctrl
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
__doc__ = wx.lib.intctrl.__doc__
|
||||
|
||||
wxEVT_COMMAND_INT_UPDATED = wxNewEventType()
|
||||
|
||||
# wxWindows' wxTextCtrl translates Composite "control key"
|
||||
# events into single events before returning them to its OnChar
|
||||
# routine. The doc says that this results in 1 for Ctrl-A, 2 for
|
||||
# Ctrl-B, etc. However, there are no wxPython or wxWindows
|
||||
# symbols for them, so I'm defining codes for Ctrl-X (cut) and
|
||||
# Ctrl-V (paste) here for readability:
|
||||
WXK_CTRL_X = (ord('X')+1) - ord('A')
|
||||
WXK_CTRL_V = (ord('V')+1) - ord('A')
|
||||
|
||||
|
||||
def EVT_INT(win, id, func):
|
||||
"""Used to trap events indicating that the current
|
||||
integer value of the control has been changed."""
|
||||
win.Connect(id, -1, wxEVT_COMMAND_INT_UPDATED, func)
|
||||
|
||||
|
||||
class wxIntUpdatedEvent(wxPyCommandEvent):
|
||||
def __init__(self, id, value = 0, object=None):
|
||||
wxPyCommandEvent.__init__(self, wxEVT_COMMAND_INT_UPDATED, id)
|
||||
|
||||
self.__value = value
|
||||
self.SetEventObject(object)
|
||||
|
||||
def GetValue(self):
|
||||
"""Retrieve the value of the control at the time
|
||||
this event was generated."""
|
||||
return self.__value
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
class wxIntValidator( wxPyValidator ):
|
||||
"""
|
||||
Validator class used with wxIntCtrl; handles all validation of input
|
||||
prior to changing the value of the underlying wxTextCtrl.
|
||||
"""
|
||||
def __init__(self):
|
||||
wxPyValidator.__init__(self)
|
||||
EVT_CHAR(self, self.OnChar)
|
||||
|
||||
def Clone (self):
|
||||
return self.__class__()
|
||||
|
||||
def Validate(self, window): # window here is the *parent* of the ctrl
|
||||
"""
|
||||
Because each operation on the control is vetted as it's made,
|
||||
the value of the control is always valid.
|
||||
"""
|
||||
return 1
|
||||
|
||||
|
||||
def OnChar(self, event):
|
||||
"""
|
||||
Validates keystrokes to make sure the resulting value will a legal
|
||||
value. Erasing the value causes it to be set to 0, with the value
|
||||
selected, so it can be replaced. Similarly, replacing the value
|
||||
with a '-' sign causes the value to become -1, with the value
|
||||
selected. Leading zeros are removed if introduced by selection,
|
||||
and are prevented from being inserted.
|
||||
"""
|
||||
key = event.KeyCode()
|
||||
ctrl = event.GetEventObject()
|
||||
|
||||
|
||||
value = ctrl.GetValue()
|
||||
textval = wxTextCtrl.GetValue(ctrl)
|
||||
allow_none = ctrl.IsNoneAllowed()
|
||||
|
||||
pos = ctrl.GetInsertionPoint()
|
||||
sel_start, sel_to = ctrl.GetSelection()
|
||||
select_len = sel_to - sel_start
|
||||
|
||||
# (Uncomment for debugging:)
|
||||
## print 'keycode:', key
|
||||
## print 'pos:', pos
|
||||
## print 'sel_start, sel_to:', sel_start, sel_to
|
||||
## print 'select_len:', select_len
|
||||
## print 'textval:', textval
|
||||
|
||||
# set defaults for processing:
|
||||
allow_event = 1
|
||||
set_to_none = 0
|
||||
set_to_zero = 0
|
||||
set_to_minus_one = 0
|
||||
paste = 0
|
||||
internally_set = 0
|
||||
|
||||
new_value = value
|
||||
new_text = textval
|
||||
new_pos = pos
|
||||
|
||||
# Validate action, and predict resulting value, so we can
|
||||
# range check the result and validate that too.
|
||||
|
||||
if key in (WXK_DELETE, WXK_BACK, WXK_CTRL_X):
|
||||
if select_len:
|
||||
new_text = textval[:sel_start] + textval[sel_to:]
|
||||
elif key == WXK_DELETE and pos < len(textval):
|
||||
new_text = textval[:pos] + textval[pos+1:]
|
||||
elif key == WXK_BACK and pos > 0:
|
||||
new_text = textval[:pos-1] + textval[pos:]
|
||||
# (else value shouldn't change)
|
||||
|
||||
if new_text in ('', '-'):
|
||||
# Deletion of last significant digit:
|
||||
if allow_none and new_text == '':
|
||||
new_value = None
|
||||
set_to_none = 1
|
||||
else:
|
||||
new_value = 0
|
||||
set_to_zero = 1
|
||||
else:
|
||||
try:
|
||||
new_value = ctrl._fromGUI(new_text)
|
||||
except ValueError:
|
||||
allow_event = 0
|
||||
|
||||
|
||||
elif key == WXK_CTRL_V: # (see comments at top of file)
|
||||
# Only allow paste if number:
|
||||
paste_text = ctrl._getClipboardContents()
|
||||
new_text = textval[:sel_start] + paste_text + textval[sel_to:]
|
||||
if new_text == '' and allow_none:
|
||||
new_value = None
|
||||
set_to_none = 1
|
||||
else:
|
||||
try:
|
||||
# Convert the resulting strings, verifying they
|
||||
# are legal integers and will fit in proper
|
||||
# size if ctrl limited to int. (if not,
|
||||
# disallow event.)
|
||||
new_value = ctrl._fromGUI(new_text)
|
||||
if paste_text:
|
||||
paste_value = ctrl._fromGUI(paste_text)
|
||||
else:
|
||||
paste_value = 0
|
||||
new_pos = sel_start + len(str(paste_value))
|
||||
|
||||
# if resulting value is 0, truncate and highlight value:
|
||||
if new_value == 0 and len(new_text) > 1:
|
||||
set_to_zero = 1
|
||||
|
||||
elif paste_value == 0:
|
||||
# Disallow pasting a leading zero with nothing selected:
|
||||
if( select_len == 0
|
||||
and value is not None
|
||||
and ( (value >= 0 and pos == 0)
|
||||
or (value < 0 and pos in [0,1]) ) ):
|
||||
allow_event = 0
|
||||
paste = 1
|
||||
|
||||
except ValueError:
|
||||
allow_event = 0
|
||||
|
||||
|
||||
elif key < WXK_SPACE or key > 255:
|
||||
pass # event ok
|
||||
|
||||
|
||||
elif chr(key) == '-':
|
||||
# Allow '-' to result in -1 if replacing entire contents:
|
||||
if( value is None
|
||||
or (value == 0 and pos == 0)
|
||||
or (select_len >= len(str(abs(value)))) ):
|
||||
new_value = -1
|
||||
set_to_minus_one = 1
|
||||
|
||||
# else allow negative sign only at start, and only if
|
||||
# number isn't already zero or negative:
|
||||
elif pos != 0 or (value is not None and value < 0):
|
||||
allow_event = 0
|
||||
else:
|
||||
new_text = '-' + textval
|
||||
new_pos = 1
|
||||
try:
|
||||
new_value = ctrl._fromGUI(new_text)
|
||||
except ValueError:
|
||||
allow_event = 0
|
||||
|
||||
|
||||
elif chr(key) in string.digits:
|
||||
# disallow inserting a leading zero with nothing selected
|
||||
if( chr(key) == '0'
|
||||
and select_len == 0
|
||||
and value is not None
|
||||
and ( (value >= 0 and pos == 0)
|
||||
or (value < 0 and pos in [0,1]) ) ):
|
||||
allow_event = 0
|
||||
# disallow inserting digits before the minus sign:
|
||||
elif value is not None and value < 0 and pos == 0:
|
||||
allow_event = 0
|
||||
else:
|
||||
new_text = textval[:sel_start] + chr(key) + textval[sel_to:]
|
||||
try:
|
||||
new_value = ctrl._fromGUI(new_text)
|
||||
except ValueError:
|
||||
allow_event = 0
|
||||
|
||||
else:
|
||||
# not a legal char
|
||||
allow_event = 0
|
||||
|
||||
|
||||
if allow_event:
|
||||
# Do range checking for new candidate value:
|
||||
if ctrl.IsLimited() and not ctrl.IsInBounds(new_value):
|
||||
allow_event = 0
|
||||
elif new_value is not None:
|
||||
# ensure resulting text doesn't result in a leading 0:
|
||||
if not set_to_zero and not set_to_minus_one:
|
||||
if( (new_value > 0 and new_text[0] == '0')
|
||||
or (new_value < 0 and new_text[1] == '0')
|
||||
or (new_value == 0 and select_len > 1 ) ):
|
||||
|
||||
# Allow replacement of leading chars with
|
||||
# zero, but remove the leading zero, effectively
|
||||
# making this like "remove leading digits"
|
||||
|
||||
# Account for leading zero when positioning cursor:
|
||||
if( key == WXK_BACK
|
||||
or (paste and paste_value == 0 and new_pos > 0) ):
|
||||
new_pos = new_pos - 1
|
||||
|
||||
wxCallAfter(ctrl.SetValue, new_value)
|
||||
wxCallAfter(ctrl.SetInsertionPoint, new_pos)
|
||||
internally_set = 1
|
||||
|
||||
elif paste:
|
||||
# Always do paste numerically, to remove
|
||||
# leading/trailing spaces
|
||||
wxCallAfter(ctrl.SetValue, new_value)
|
||||
wxCallAfter(ctrl.SetInsertionPoint, new_pos)
|
||||
internally_set = 1
|
||||
|
||||
elif (new_value == 0 and len(new_text) > 1 ):
|
||||
allow_event = 0
|
||||
|
||||
if allow_event:
|
||||
ctrl._colorValue(new_value) # (one way or t'other)
|
||||
|
||||
# (Uncomment for debugging:)
|
||||
## if allow_event:
|
||||
## print 'new value:', new_value
|
||||
## if paste: print 'paste'
|
||||
## if set_to_none: print 'set_to_none'
|
||||
## if set_to_zero: print 'set_to_zero'
|
||||
## if set_to_minus_one: print 'set_to_minus_one'
|
||||
## if internally_set: print 'internally_set'
|
||||
## else:
|
||||
## print 'new text:', new_text
|
||||
## print 'disallowed'
|
||||
## print
|
||||
|
||||
if allow_event:
|
||||
if set_to_none:
|
||||
wxCallAfter(ctrl.SetValue, new_value)
|
||||
|
||||
elif set_to_zero:
|
||||
# select to "empty" numeric value
|
||||
wxCallAfter(ctrl.SetValue, new_value)
|
||||
wxCallAfter(ctrl.SetInsertionPoint, 0)
|
||||
wxCallAfter(ctrl.SetSelection, 0, 1)
|
||||
|
||||
elif set_to_minus_one:
|
||||
wxCallAfter(ctrl.SetValue, new_value)
|
||||
wxCallAfter(ctrl.SetInsertionPoint, 1)
|
||||
wxCallAfter(ctrl.SetSelection, 1, 2)
|
||||
|
||||
elif not internally_set:
|
||||
event.Skip() # allow base wxTextCtrl to finish processing
|
||||
|
||||
elif not wxValidator_IsSilent():
|
||||
wxBell()
|
||||
|
||||
|
||||
def TransferToWindow(self):
|
||||
""" Transfer data from validator to window.
|
||||
|
||||
The default implementation returns False, indicating that an error
|
||||
occurred. We simply return True, as we don't do any data transfer.
|
||||
"""
|
||||
return True # Prevent wxDialog from complaining.
|
||||
|
||||
|
||||
def TransferFromWindow(self):
|
||||
""" Transfer data from window to validator.
|
||||
|
||||
The default implementation returns False, indicating that an error
|
||||
occurred. We simply return True, as we don't do any data transfer.
|
||||
"""
|
||||
return True # Prevent wxDialog from complaining.
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
class wxIntCtrl(wxTextCtrl):
|
||||
"""
|
||||
This class provides a control that takes and returns integers as
|
||||
value, and provides bounds support and optional value limiting.
|
||||
|
||||
wxIntCtrl(
|
||||
parent, id = -1,
|
||||
value = 0,
|
||||
pos = wxDefaultPosition,
|
||||
size = wxDefaultSize,
|
||||
style = 0,
|
||||
validator = wxDefaultValidator,
|
||||
name = "integer",
|
||||
min = None,
|
||||
max = None,
|
||||
limited = False,
|
||||
allow_none = False,
|
||||
allow_long = False,
|
||||
default_color = wxBLACK,
|
||||
oob_color = wxRED )
|
||||
|
||||
value
|
||||
If no initial value is set, the default will be zero, or
|
||||
the minimum value, if specified. If an illegal string is specified,
|
||||
a ValueError will result. (You can always later set the initial
|
||||
value with SetValue() after instantiation of the control.)
|
||||
min
|
||||
The minimum value that the control should allow. This can be
|
||||
adjusted with SetMin(). If the control is not limited, any value
|
||||
below this bound will be colored with the current out-of-bounds color.
|
||||
If min < -sys.maxint-1 and the control is configured to not allow long
|
||||
values, the minimum bound will still be set to the long value, but
|
||||
the implicit bound will be -sys.maxint-1.
|
||||
max
|
||||
The maximum value that the control should allow. This can be
|
||||
adjusted with SetMax(). If the control is not limited, any value
|
||||
above this bound will be colored with the current out-of-bounds color.
|
||||
if max > sys.maxint and the control is configured to not allow long
|
||||
values, the maximum bound will still be set to the long value, but
|
||||
the implicit bound will be sys.maxint.
|
||||
|
||||
limited
|
||||
Boolean indicating whether the control prevents values from
|
||||
exceeding the currently set minimum and maximum values (bounds).
|
||||
If False and bounds are set, out-of-bounds values will
|
||||
be colored with the current out-of-bounds color.
|
||||
|
||||
allow_none
|
||||
Boolean indicating whether or not the control is allowed to be
|
||||
empty, representing a value of None for the control.
|
||||
|
||||
allow_long
|
||||
Boolean indicating whether or not the control is allowed to hold
|
||||
and return a long as well as an int.
|
||||
|
||||
default_color
|
||||
Color value used for in-bounds values of the control.
|
||||
|
||||
oob_color
|
||||
Color value used for out-of-bounds values of the control
|
||||
when the bounds are set but the control is not limited.
|
||||
|
||||
validator
|
||||
Normally None, wxIntCtrl uses its own validator to do value
|
||||
validation and input control. However, a validator derived
|
||||
from wxIntValidator can be supplied to override the data
|
||||
transfer methods for the wxIntValidator class.
|
||||
"""
|
||||
|
||||
def __init__ (
|
||||
self, parent, id=-1, value = 0,
|
||||
pos = wxDefaultPosition, size = wxDefaultSize,
|
||||
style = 0, validator = wxDefaultValidator,
|
||||
name = "integer",
|
||||
min=None, max=None,
|
||||
limited = 0, allow_none = 0, allow_long = 0,
|
||||
default_color = wxBLACK, oob_color = wxRED,
|
||||
):
|
||||
|
||||
# Establish attrs required for any operation on value:
|
||||
self.__min = None
|
||||
self.__max = None
|
||||
self.__limited = 0
|
||||
self.__default_color = wxBLACK
|
||||
self.__oob_color = wxRED
|
||||
self.__allow_none = 0
|
||||
self.__allow_long = 0
|
||||
self.__oldvalue = None
|
||||
|
||||
if validator == wxDefaultValidator:
|
||||
validator = wxIntValidator()
|
||||
|
||||
wxTextCtrl.__init__(
|
||||
self, parent, id, self._toGUI(0),
|
||||
pos, size, style, validator, name )
|
||||
|
||||
# The following lets us set out our "integer update" events:
|
||||
EVT_TEXT( self, self.GetId(), self.OnText )
|
||||
|
||||
# Establish parameters, with appropriate error checking
|
||||
|
||||
self.SetBounds(min, max)
|
||||
self.SetLimited(limited)
|
||||
self.SetColors(default_color, oob_color)
|
||||
self.SetNoneAllowed(allow_none)
|
||||
self.SetLongAllowed(allow_long)
|
||||
self.SetValue(value)
|
||||
self.__oldvalue = 0
|
||||
|
||||
|
||||
def OnText( self, event ):
|
||||
"""
|
||||
Handles an event indicating that the text control's value
|
||||
has changed, and issue EVT_INT event.
|
||||
NOTE: using wxTextCtrl.SetValue() to change the control's
|
||||
contents from within a EVT_CHAR handler can cause double
|
||||
text events. So we check for actual changes to the text
|
||||
before passing the events on.
|
||||
"""
|
||||
value = self.GetValue()
|
||||
if value != self.__oldvalue:
|
||||
try:
|
||||
self.GetEventHandler().ProcessEvent(
|
||||
wxIntUpdatedEvent( self.GetId(), self.GetValue(), self ) )
|
||||
except ValueError:
|
||||
return
|
||||
# let normal processing of the text continue
|
||||
event.Skip()
|
||||
self.__oldvalue = value # record for next event
|
||||
|
||||
|
||||
def GetValue(self):
|
||||
"""
|
||||
Returns the current integer (long) value of the control.
|
||||
"""
|
||||
return self._fromGUI( wxTextCtrl.GetValue(self) )
|
||||
|
||||
def SetValue(self, value):
|
||||
"""
|
||||
Sets the value of the control to the integer value specified.
|
||||
The resulting actual value of the control may be altered to
|
||||
conform with the bounds set on the control if limited,
|
||||
or colored if not limited but the value is out-of-bounds.
|
||||
A ValueError exception will be raised if an invalid value
|
||||
is specified.
|
||||
"""
|
||||
wxTextCtrl.SetValue( self, self._toGUI(value) )
|
||||
self._colorValue()
|
||||
|
||||
|
||||
def SetMin(self, min=None):
|
||||
"""
|
||||
Sets the minimum value of the control. If a value of None
|
||||
is provided, then the control will have no explicit minimum value.
|
||||
If the value specified is greater than the current maximum value,
|
||||
then the function returns 0 and the minimum will not change from
|
||||
its current setting. On success, the function returns 1.
|
||||
|
||||
If successful and the current value is lower than the new lower
|
||||
bound, if the control is limited, the value will be automatically
|
||||
adjusted to the new minimum value; if not limited, the value in the
|
||||
control will be colored with the current out-of-bounds color.
|
||||
|
||||
If min > -sys.maxint-1 and the control is configured to not allow longs,
|
||||
the function will return 0, and the min will not be set.
|
||||
"""
|
||||
if( self.__max is None
|
||||
or min is None
|
||||
or (self.__max is not None and self.__max >= min) ):
|
||||
self.__min = min
|
||||
|
||||
if self.IsLimited() and min is not None and self.GetValue() < min:
|
||||
self.SetValue(min)
|
||||
else:
|
||||
self._colorValue()
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def GetMin(self):
|
||||
"""
|
||||
Gets the minimum value of the control. It will return the current
|
||||
minimum integer, or None if not specified.
|
||||
"""
|
||||
return self.__min
|
||||
|
||||
|
||||
def SetMax(self, max=None):
|
||||
"""
|
||||
Sets the maximum value of the control. If a value of None
|
||||
is provided, then the control will have no explicit maximum value.
|
||||
If the value specified is less than the current minimum value, then
|
||||
the function returns 0 and the maximum will not change from its
|
||||
current setting. On success, the function returns 1.
|
||||
|
||||
If successful and the current value is greater than the new upper
|
||||
bound, if the control is limited the value will be automatically
|
||||
adjusted to this maximum value; if not limited, the value in the
|
||||
control will be colored with the current out-of-bounds color.
|
||||
|
||||
If max > sys.maxint and the control is configured to not allow longs,
|
||||
the function will return 0, and the max will not be set.
|
||||
"""
|
||||
if( self.__min is None
|
||||
or max is None
|
||||
or (self.__min is not None and self.__min <= max) ):
|
||||
self.__max = max
|
||||
|
||||
if self.IsLimited() and max is not None and self.GetValue() > max:
|
||||
self.SetValue(max)
|
||||
else:
|
||||
self._colorValue()
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def GetMax(self):
|
||||
"""
|
||||
Gets the maximum value of the control. It will return the current
|
||||
maximum integer, or None if not specified.
|
||||
"""
|
||||
return self.__max
|
||||
|
||||
|
||||
def SetBounds(self, min=None, max=None):
|
||||
"""
|
||||
This function is a convenience function for setting the min and max
|
||||
values at the same time. The function only applies the maximum bound
|
||||
if setting the minimum bound is successful, and returns True
|
||||
only if both operations succeed.
|
||||
NOTE: leaving out an argument will remove the corresponding bound.
|
||||
"""
|
||||
ret = self.SetMin(min)
|
||||
return ret and self.SetMax(max)
|
||||
|
||||
|
||||
def GetBounds(self):
|
||||
"""
|
||||
This function returns a two-tuple (min,max), indicating the
|
||||
current bounds of the control. Each value can be None if
|
||||
that bound is not set.
|
||||
"""
|
||||
return (self.__min, self.__max)
|
||||
|
||||
|
||||
def SetLimited(self, limited):
|
||||
"""
|
||||
If called with a value of True, this function will cause the control
|
||||
to limit the value to fall within the bounds currently specified.
|
||||
If the control's value currently exceeds the bounds, it will then
|
||||
be limited accordingly.
|
||||
|
||||
If called with a value of 0, this function will disable value
|
||||
limiting, but coloring of out-of-bounds values will still take
|
||||
place if bounds have been set for the control.
|
||||
"""
|
||||
self.__limited = limited
|
||||
if limited:
|
||||
min = self.GetMin()
|
||||
max = self.GetMax()
|
||||
if not min is None and self.GetValue() < min:
|
||||
self.SetValue(min)
|
||||
elif not max is None and self.GetValue() > max:
|
||||
self.SetValue(max)
|
||||
else:
|
||||
self._colorValue()
|
||||
|
||||
|
||||
def IsLimited(self):
|
||||
"""
|
||||
Returns True if the control is currently limiting the
|
||||
value to fall within the current bounds.
|
||||
"""
|
||||
return self.__limited
|
||||
|
||||
|
||||
def IsInBounds(self, value=None):
|
||||
"""
|
||||
Returns True if no value is specified and the current value
|
||||
of the control falls within the current bounds. This function can
|
||||
also be called with a value to see if that value would fall within
|
||||
the current bounds of the given control.
|
||||
"""
|
||||
if value is None:
|
||||
value = self.GetValue()
|
||||
|
||||
if( not (value is None and self.IsNoneAllowed())
|
||||
and type(value) not in (types.IntType, types.LongType) ):
|
||||
raise ValueError (
|
||||
'wxIntCtrl requires integer values, passed %s'% repr(value) )
|
||||
|
||||
min = self.GetMin()
|
||||
max = self.GetMax()
|
||||
if min is None: min = value
|
||||
if max is None: max = value
|
||||
|
||||
# if bounds set, and value is None, return False
|
||||
if value == None and (min is not None or max is not None):
|
||||
return 0
|
||||
else:
|
||||
return min <= value <= max
|
||||
|
||||
|
||||
def SetNoneAllowed(self, allow_none):
|
||||
"""
|
||||
Change the behavior of the validation code, allowing control
|
||||
to have a value of None or not, as appropriate. If the value
|
||||
of the control is currently None, and allow_none is 0, the
|
||||
value of the control will be set to the minimum value of the
|
||||
control, or 0 if no lower bound is set.
|
||||
"""
|
||||
self.__allow_none = allow_none
|
||||
if not allow_none and self.GetValue() is None:
|
||||
min = self.GetMin()
|
||||
if min is not None: self.SetValue(min)
|
||||
else: self.SetValue(0)
|
||||
|
||||
|
||||
def IsNoneAllowed(self):
|
||||
return self.__allow_none
|
||||
|
||||
|
||||
def SetLongAllowed(self, allow_long):
|
||||
"""
|
||||
Change the behavior of the validation code, allowing control
|
||||
to have a long value or not, as appropriate. If the value
|
||||
of the control is currently long, and allow_long is 0, the
|
||||
value of the control will be adjusted to fall within the
|
||||
size of an integer type, at either the sys.maxint or -sys.maxint-1,
|
||||
for positive and negative values, respectively.
|
||||
"""
|
||||
current_value = self.GetValue()
|
||||
if not allow_long and type(current_value) is types.LongType:
|
||||
if current_value > 0:
|
||||
self.SetValue(MAXINT)
|
||||
else:
|
||||
self.SetValue(MININT)
|
||||
self.__allow_long = allow_long
|
||||
|
||||
|
||||
def IsLongAllowed(self):
|
||||
return self.__allow_long
|
||||
|
||||
|
||||
|
||||
def SetColors(self, default_color=wxBLACK, oob_color=wxRED):
|
||||
"""
|
||||
Tells the control what colors to use for normal and out-of-bounds
|
||||
values. If the value currently exceeds the bounds, it will be
|
||||
recolored accordingly.
|
||||
"""
|
||||
self.__default_color = default_color
|
||||
self.__oob_color = oob_color
|
||||
self._colorValue()
|
||||
|
||||
|
||||
def GetColors(self):
|
||||
"""
|
||||
Returns a tuple of (default_color, oob_color), indicating
|
||||
the current color settings for the control.
|
||||
"""
|
||||
return self.__default_color, self.__oob_color
|
||||
|
||||
|
||||
def _colorValue(self, value=None):
|
||||
"""
|
||||
Colors text with oob_color if current value exceeds bounds
|
||||
set for control.
|
||||
"""
|
||||
if not self.IsInBounds(value):
|
||||
self.SetForegroundColour(self.__oob_color)
|
||||
else:
|
||||
self.SetForegroundColour(self.__default_color)
|
||||
self.Refresh()
|
||||
|
||||
|
||||
def _toGUI( self, value ):
|
||||
"""
|
||||
Conversion function used to set the value of the control; does
|
||||
type and bounds checking and raises ValueError if argument is
|
||||
not a valid value.
|
||||
"""
|
||||
if value is None and self.IsNoneAllowed():
|
||||
return ''
|
||||
elif type(value) == types.LongType and not self.IsLongAllowed():
|
||||
raise ValueError (
|
||||
'wxIntCtrl requires integer value, passed long' )
|
||||
elif type(value) not in (types.IntType, types.LongType):
|
||||
raise ValueError (
|
||||
'wxIntCtrl requires integer value, passed %s'% repr(value) )
|
||||
|
||||
elif self.IsLimited():
|
||||
min = self.GetMin()
|
||||
max = self.GetMax()
|
||||
if not min is None and value < min:
|
||||
raise ValueError (
|
||||
'value is below minimum value of control %d'% value )
|
||||
if not max is None and value > max:
|
||||
raise ValueError (
|
||||
'value exceeds value of control %d'% value )
|
||||
|
||||
return str(value)
|
||||
|
||||
|
||||
def _fromGUI( self, value ):
|
||||
"""
|
||||
Conversion function used in getting the value of the control.
|
||||
"""
|
||||
|
||||
# One or more of the underlying text control implementations
|
||||
# issue an intermediate EVT_TEXT when replacing the control's
|
||||
# value, where the intermediate value is an empty string.
|
||||
# So, to ensure consistency and to prevent spurious ValueErrors,
|
||||
# we make the following test, and react accordingly:
|
||||
#
|
||||
if value == '':
|
||||
if not self.IsNoneAllowed():
|
||||
return 0
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
try:
|
||||
return int( value )
|
||||
except ValueError:
|
||||
if self.IsLongAllowed():
|
||||
return long( value )
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def Cut( self ):
|
||||
"""
|
||||
Override the wxTextCtrl's .Cut function, with our own
|
||||
that does validation. Will result in a value of 0
|
||||
if entire contents of control are removed.
|
||||
"""
|
||||
sel_start, sel_to = self.GetSelection()
|
||||
select_len = sel_to - sel_start
|
||||
textval = wxTextCtrl.GetValue(self)
|
||||
|
||||
do = wxTextDataObject()
|
||||
do.SetText(textval[sel_start:sel_to])
|
||||
wxTheClipboard.Open()
|
||||
wxTheClipboard.SetData(do)
|
||||
wxTheClipboard.Close()
|
||||
if select_len == len(wxTextCtrl.GetValue(self)):
|
||||
if not self.IsNoneAllowed():
|
||||
self.SetValue(0)
|
||||
self.SetInsertionPoint(0)
|
||||
self.SetSelection(0,1)
|
||||
else:
|
||||
self.SetValue(None)
|
||||
else:
|
||||
new_value = self._fromGUI(textval[:sel_start] + textval[sel_to:])
|
||||
self.SetValue(new_value)
|
||||
|
||||
|
||||
def _getClipboardContents( self ):
|
||||
"""
|
||||
Subroutine for getting the current contents of the clipboard.
|
||||
"""
|
||||
do = wxTextDataObject()
|
||||
wxTheClipboard.Open()
|
||||
success = wxTheClipboard.GetData(do)
|
||||
wxTheClipboard.Close()
|
||||
|
||||
if not success:
|
||||
return None
|
||||
else:
|
||||
# Remove leading and trailing spaces before evaluating contents
|
||||
return do.GetText().strip()
|
||||
|
||||
|
||||
def Paste( self ):
|
||||
"""
|
||||
Override the wxTextCtrl's .Paste function, with our own
|
||||
that does validation. Will raise ValueError if not a
|
||||
valid integerizable value.
|
||||
"""
|
||||
paste_text = self._getClipboardContents()
|
||||
if paste_text:
|
||||
# (conversion will raise ValueError if paste isn't legal)
|
||||
sel_start, sel_to = self.GetSelection()
|
||||
text = wxTextCtrl.GetValue( self )
|
||||
new_text = text[:sel_start] + paste_text + text[sel_to:]
|
||||
if new_text == '' and self.IsNoneAllowed():
|
||||
self.SetValue(None)
|
||||
else:
|
||||
value = self._fromGUI(new_text)
|
||||
self.SetValue(value)
|
||||
new_pos = sel_start + len(paste_text)
|
||||
wxCallAfter(self.SetInsertionPoint, new_pos)
|
||||
|
||||
|
||||
|
||||
#===========================================================================
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
import traceback
|
||||
|
||||
class myDialog(wxDialog):
|
||||
def __init__(self, parent, id, title,
|
||||
pos = wxPyDefaultPosition, size = wxPyDefaultSize,
|
||||
style = wxDEFAULT_DIALOG_STYLE ):
|
||||
wxDialog.__init__(self, parent, id, title, pos, size, style)
|
||||
|
||||
self.int_ctrl = wxIntCtrl(self, wxNewId(), size=(55,20))
|
||||
self.OK = wxButton( self, wxID_OK, "OK")
|
||||
self.Cancel = wxButton( self, wxID_CANCEL, "Cancel")
|
||||
|
||||
vs = wxBoxSizer( wxVERTICAL )
|
||||
vs.AddWindow( self.int_ctrl, 0, wxALIGN_CENTRE|wxALL, 5 )
|
||||
hs = wxBoxSizer( wxHORIZONTAL )
|
||||
hs.AddWindow( self.OK, 0, wxALIGN_CENTRE|wxALL, 5 )
|
||||
hs.AddWindow( self.Cancel, 0, wxALIGN_CENTRE|wxALL, 5 )
|
||||
vs.AddSizer(hs, 0, wxALIGN_CENTRE|wxALL, 5 )
|
||||
|
||||
self.SetAutoLayout( True )
|
||||
self.SetSizer( vs )
|
||||
vs.Fit( self )
|
||||
vs.SetSizeHints( self )
|
||||
EVT_INT(self, self.int_ctrl.GetId(), self.OnInt)
|
||||
|
||||
def OnInt(self, event):
|
||||
print 'int now', event.GetValue()
|
||||
|
||||
class TestApp(wxApp):
|
||||
def OnInit(self):
|
||||
try:
|
||||
self.frame = wxFrame(NULL, -1, "Test",
|
||||
wxPoint(20,20), wxSize(120,100) )
|
||||
self.panel = wxPanel(self.frame, -1)
|
||||
button = wxButton(self.panel, 10, "Push Me",
|
||||
wxPoint(20, 20))
|
||||
EVT_BUTTON(self, 10, self.OnClick)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return False
|
||||
return True
|
||||
|
||||
def OnClick(self, event):
|
||||
dlg = myDialog(self.panel, -1, "test wxIntCtrl")
|
||||
dlg.int_ctrl.SetValue(501)
|
||||
dlg.int_ctrl.SetInsertionPoint(1)
|
||||
dlg.int_ctrl.SetSelection(1,2)
|
||||
rc = dlg.ShowModal()
|
||||
print 'final value', dlg.int_ctrl.GetValue()
|
||||
del dlg
|
||||
self.frame.Destroy()
|
||||
|
||||
def Show(self):
|
||||
self.frame.Show(True)
|
||||
|
||||
try:
|
||||
app = TestApp(0)
|
||||
app.Show()
|
||||
app.MainLoop()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
EVT_INT = wx.lib.intctrl.EVT_INT
|
||||
wxIntCtrl = wx.lib.intctrl.wxIntCtrl
|
||||
wxIntUpdatedEvent = wx.lib.intctrl.wxIntUpdatedEvent
|
||||
wxIntValidator = wx.lib.intctrl.wxIntValidator
|
||||
|
||||
@@ -1,263 +1,9 @@
|
||||
from wxPython.wx import wxLayoutConstraints,\
|
||||
wxTop, wxLeft, wxBottom, wxRight, \
|
||||
wxHeight, wxWidth, wxCentreX, wxCentreY
|
||||
import re
|
||||
|
||||
class Layoutf(wxLayoutConstraints):
|
||||
"""
|
||||
The class Layoutf(wxLayoutConstraints) presents a simplification
|
||||
of the wxLayoutConstraints syntax. The name Layoutf is choosen
|
||||
because of the similarity with C's printf function.
|
||||
|
||||
Quick Example:
|
||||
|
||||
lc = Layoutf('t=t#1;l=r10#2;r!100;h%h50#1', (self, self.panel))
|
||||
|
||||
is equivalent to
|
||||
|
||||
lc = wxLayoutContraints()
|
||||
lc.top.SameAs(self, wxTop)
|
||||
lc.left.SameAs(self.panel, wxRight, 10)
|
||||
lc.right.Absolute(100)
|
||||
lc.height.PercentOf(self, wxHeight, 50)
|
||||
|
||||
Usage:
|
||||
|
||||
You can give a constraint string to the Layoutf constructor,
|
||||
or use the 'pack' method. The following are equivalent:
|
||||
|
||||
lc = Layoutf('t=t#1;l=r#2;r!100;h%h50#1', (self, self.panel))
|
||||
|
||||
and
|
||||
|
||||
lc = Layoutf()
|
||||
lc.pack('t=t#1;l=r#2;r!100;h%h50#1', (self, self.panel))
|
||||
|
||||
Besides 'pack' there's also 'debug_pack' which does not set
|
||||
constraints, but prints traditional wxLayoutConstraint calls to
|
||||
stdout.
|
||||
|
||||
The calls to the Layoutf constructor and pack methods have
|
||||
the following argument list:
|
||||
|
||||
(constraint_string, objects_tuple)
|
||||
|
||||
Constraint String syntax:
|
||||
|
||||
Constraint directives are separated by semi-colons. You
|
||||
generally (always?) need four directives to completely describe a
|
||||
subwindow's location.
|
||||
|
||||
A single directive has either of the following forms:
|
||||
|
||||
1. <own attribute><compare operation>[numerical argument]
|
||||
for example r!100 -> lc.right.Absolute(100) )
|
||||
and w* -> lc.width.AsIs()
|
||||
|
||||
2. <own attribute><compare operation>[numerical argument]
|
||||
#<compare object nr.>
|
||||
for example t_10#2 (lc.top.Below(<second obj>, 10)
|
||||
|
||||
3. <own attribute><compare operation><compare attribute>
|
||||
[numerical argument]#<compare object nr.>
|
||||
for example w%h50#2 ( lc.width.PercentOf(<second obj>,
|
||||
wxHeight, 50) and t=b#1 ( lc.top.SameAs(<first obj>,
|
||||
wxBottom) )
|
||||
|
||||
Which one you need is defined by the <compare operation>
|
||||
type. The following take type 1 (no object to compare with):
|
||||
|
||||
'!': 'Absolute', '?': 'Unconstrained', '*': 'AsIs'
|
||||
|
||||
These take type 2 (need to be compared with another object)
|
||||
|
||||
'<': 'LeftOf', '>': 'RightOf', '^': 'Above', '_': 'Below'
|
||||
|
||||
These take type 3 (need to be compared to another object
|
||||
attribute)
|
||||
|
||||
'=': 'SameAs', '%': 'PercentOf'
|
||||
|
||||
For all types, the <own attribute> letter can be any of
|
||||
|
||||
't': 'top', 'l': 'left', 'b': 'bottom',
|
||||
'r': 'right', 'h': 'height', 'w': 'width',
|
||||
'x': 'centreX', 'y': 'centreY'
|
||||
|
||||
If the operation takes an (optional) numerical argument, place it
|
||||
in [numerical argument]. For type 3 directives, the <compare
|
||||
attribute> letter can be any of
|
||||
|
||||
't': 'wxTop', 'l': 'wxLeft', 'b': 'wxBottom'
|
||||
'r': 'wxRight', 'h': 'wxHeight', 'w': 'wxWidth',
|
||||
'x': 'wxCentreX', 'y': 'wxCentreY'
|
||||
|
||||
Note that these are the same letters as used for <own attribute>,
|
||||
so you'll only need to remember one set. Finally, the object
|
||||
whose attribute is refered to, is specified by #<compare object
|
||||
nr>, where <compare object nr> is the 1-based (stupid, I know,
|
||||
but I've gotten used to it) index of the object in the
|
||||
objects_tuple argument.
|
||||
|
||||
Bugs:
|
||||
|
||||
Not entirely happy about the logic in the order of arguments
|
||||
after the <compare operation> character.
|
||||
|
||||
Not all wxLayoutConstraint methods are included in the
|
||||
syntax. However, the type 3 directives are generally the most
|
||||
used. Further excuse: wxWindows layout constraints are at the
|
||||
time of this writing not documented.
|
||||
|
||||
"""
|
||||
|
||||
attr_d = { 't': 'top', 'l': 'left', 'b': 'bottom',
|
||||
'r': 'right', 'h': 'height', 'w': 'width',
|
||||
'x': 'centreX', 'y': 'centreY' }
|
||||
op_d = { '=': 'SameAs', '%': 'PercentOf', '<': 'LeftOf',
|
||||
'>': 'RightOf', '^': 'Above', '_': 'Below',
|
||||
'!': 'Absolute', '?': 'Unconstrained', '*': 'AsIs' }
|
||||
cmp_d = { 't': 'wxTop', 'l': 'wxLeft', 'b': 'wxBottom',
|
||||
'r': 'wxRight', 'h': 'wxHeight', 'w': 'wxWidth',
|
||||
'x': 'wxCentreX', 'y': 'wxCentreY' }
|
||||
|
||||
rexp1 = re.compile('^\s*([tlrbhwxy])\s*([!\?\*])\s*(\d*)\s*$')
|
||||
rexp2 = re.compile('^\s*([tlrbhwxy])\s*([=%<>^_])\s*([tlrbhwxy]?)\s*(\d*)\s*#(\d+)\s*$')
|
||||
|
||||
def __init__(self,pstr=None,winlist=None):
|
||||
wxLayoutConstraints.__init__(self)
|
||||
if pstr:
|
||||
self.pack(pstr,winlist)
|
||||
|
||||
def pack(self, pstr, winlist):
|
||||
pstr = pstr.lower()
|
||||
for item in pstr.split(';'):
|
||||
m = self.rexp1.match(item)
|
||||
if m:
|
||||
g = list(m.groups())
|
||||
attr = getattr(self, self.attr_d[g[0]])
|
||||
func = getattr(attr, self.op_d[g[1]])
|
||||
if g[1] == '!':
|
||||
func(int(g[2]))
|
||||
else:
|
||||
func()
|
||||
continue
|
||||
m = self.rexp2.match(item)
|
||||
if not m: raise ValueError
|
||||
g = list(m.groups())
|
||||
attr = getattr(self, self.attr_d[g[0]])
|
||||
func = getattr(attr, self.op_d[g[1]])
|
||||
if g[3]: g[3] = int(g[3])
|
||||
else: g[3] = None;
|
||||
g[4] = int(g[4]) - 1
|
||||
if g[1] in '<>^_':
|
||||
if g[3]: func(winlist[g[4]], g[3])
|
||||
else: func(winlist[g[4]])
|
||||
else:
|
||||
cmp = eval(self.cmp_d[g[2]])
|
||||
if g[3]: func(winlist[g[4]], cmp, g[3])
|
||||
else: func(winlist[g[4]], cmp)
|
||||
|
||||
def debug_pack(self, pstr, winlist):
|
||||
pstr = pstr.lower()
|
||||
for item in pstr.split(';'):
|
||||
m = self.rexp1.match(item)
|
||||
if m:
|
||||
g = list(m.groups())
|
||||
attr = getattr(self, self.attr_d[g[0]])
|
||||
func = getattr(attr, self.op_d[g[1]])
|
||||
if g[1] == '!':
|
||||
print "%s.%s.%s(%s)" % \
|
||||
('self',self.attr_d[g[0]],self.op_d[g[1]],g[2])
|
||||
else:
|
||||
print "%s.%s.%s()" % \
|
||||
('self',self.attr_d[g[0]],self.op_d[g[1]])
|
||||
continue
|
||||
m = self.rexp2.match(item)
|
||||
if not m: raise ValueError
|
||||
g = list(m.groups())
|
||||
if g[3]: g[3] = int(g[3])
|
||||
else: g[3] = 0;
|
||||
g[4] = int(g[4]) - 1
|
||||
if g[1] in '<>^_':
|
||||
if g[3]: print "%s.%s.%s(%s,%d)" % \
|
||||
('self',self.attr_d[g[0]],self.op_d[g[1]],winlist[g[4]],
|
||||
g[3])
|
||||
else: print "%s.%s.%s(%s)" % \
|
||||
('self',self.attr_d[g[0]],self.op_d[g[1]],winlist[g[4]])
|
||||
else:
|
||||
if g[3]: print "%s.%s.%s(%s,%s,%d)" % \
|
||||
('self',self.attr_d[g[0]],self.op_d[g[1]],winlist[g[4]],
|
||||
self.cmp_d[g[2]],g[3])
|
||||
else: print "%s.%s.%s(%s,%s)" % \
|
||||
('self',self.attr_d[g[0]],self.op_d[g[1]],winlist[g[4]],
|
||||
self.cmp_d[g[2]])
|
||||
|
||||
if __name__=='__main__':
|
||||
from wxPython.wx import *
|
||||
|
||||
class TestLayoutf(wxFrame):
|
||||
def __init__(self, parent):
|
||||
wxFrame.__init__(self, parent, -1, 'Test Layout Constraints',
|
||||
wxPyDefaultPosition, wxSize(500, 300))
|
||||
EVT_CLOSE(self, self.OnCloseWindow)
|
||||
|
||||
self.SetAutoLayout(True)
|
||||
EVT_BUTTON(self, 100, self.OnButton)
|
||||
EVT_BUTTON(self, 101, self.OnAbout)
|
||||
|
||||
self.panelA = wxWindow(self, -1, wxPyDefaultPosition, wxPyDefaultSize, wxSIMPLE_BORDER)
|
||||
self.panelA.SetBackgroundColour(wxBLUE)
|
||||
self.panelA.SetConstraints(Layoutf('t=t10#1;l=l10#1;b=b10#1;r%r50#1',(self,)))
|
||||
|
||||
self.panelB = wxWindow(self, -1, wxPyDefaultPosition, wxPyDefaultSize, wxSIMPLE_BORDER)
|
||||
self.panelB.SetBackgroundColour(wxRED)
|
||||
self.panelB.SetConstraints(Layoutf('t=t10#1;r=r10#1;b%b30#1;l>10#2', (self,self.panelA)))
|
||||
|
||||
self.panelC = wxWindow(self, -1, wxPyDefaultPosition, wxPyDefaultSize, wxSIMPLE_BORDER)
|
||||
self.panelC.SetBackgroundColour(wxWHITE)
|
||||
self.panelC.SetConstraints(Layoutf('t_10#3;r=r10#1;b=b10#1;l>10#2', (self,self.panelA,self.panelB)))
|
||||
|
||||
b = wxButton(self.panelA, 101, ' About: ')
|
||||
b.SetConstraints(Layoutf('X=X#1;Y=Y#1;h*;w%w50#1', (self.panelA,)))
|
||||
|
||||
b = wxButton(self.panelB, 100, ' Panel B ')
|
||||
b.SetConstraints(Layoutf('t=t2#1;r=r4#1;h*;w*', (self.panelB,)))
|
||||
|
||||
self.panelD = wxWindow(self.panelC, -1, wxPyDefaultPosition, wxPyDefaultSize, wxSIMPLE_BORDER)
|
||||
self.panelD.SetBackgroundColour(wxGREEN)
|
||||
self.panelD.SetConstraints(Layoutf('b%h50#1;r%w50#1;h=h#2;w=w#2', (self.panelC, b)))
|
||||
|
||||
b = wxButton(self.panelC, 100, ' Panel C ')
|
||||
b.SetConstraints(Layoutf('t_#1;l>#1;h*;w*', (self.panelD,)))
|
||||
|
||||
wxStaticText(self.panelD, -1, "Panel D", wxPoint(4, 4)).SetBackgroundColour(wxGREEN)
|
||||
|
||||
def OnButton(self, event):
|
||||
self.Close(True)
|
||||
|
||||
def OnAbout(self, event):
|
||||
try:
|
||||
from dialogs import wxScrolledMessageDialog
|
||||
msg = wxScrolledMessageDialog(self, Layoutf.__doc__, "about")
|
||||
msg.ShowModal()
|
||||
except:
|
||||
print msg
|
||||
|
||||
def OnCloseWindow(self, event):
|
||||
self.Destroy()
|
||||
|
||||
class TestApp(wxApp):
|
||||
def OnInit(self):
|
||||
frame = TestLayoutf(NULL)
|
||||
frame.Show(1)
|
||||
self.SetTopWindow(frame)
|
||||
return 1
|
||||
|
||||
app = TestApp(0)
|
||||
app.MainLoop()
|
||||
|
||||
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.layoutf
|
||||
|
||||
__doc__ = wx.lib.layoutf.__doc__
|
||||
|
||||
Layoutf = wx.lib.layoutf.Layoutf
|
||||
|
||||
@@ -1,101 +1,10 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: wxPython.lib.maskedctrl.py
|
||||
# Author: Will Sadkin
|
||||
# Created: 09/24/2003
|
||||
# Copyright: (c) 2003 by Will Sadkin
|
||||
# RCS-ID: $Id$
|
||||
# License: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
"""<html><body>
|
||||
<P>
|
||||
<B>wxMaskedCtrl</B> is actually a factory function for several types of
|
||||
masked edit controls:
|
||||
<P>
|
||||
<UL>
|
||||
<LI><b>wxMaskedTextCtrl</b> - standard masked edit text box</LI>
|
||||
<LI><b>wxMaskedComboBox</b> - adds combobox capabilities</LI>
|
||||
<LI><b>wxIpAddrCtrl</b> - adds logical input semantics for IP address entry</LI>
|
||||
<LI><b>wxTimeCtrl</b> - special subclass handling lots of time formats as values</LI>
|
||||
<LI><b>wxMaskedNumCtrl</b> - special subclass handling numeric values</LI>
|
||||
</UL>
|
||||
<P>
|
||||
<B>wxMaskedCtrl</B> works by looking for a special <b><i>controlType</i></b>
|
||||
parameter in the variable arguments of the control, to determine
|
||||
what kind of instance to return.
|
||||
controlType can be one of:
|
||||
<PRE><FONT SIZE=-1>
|
||||
controlTypes.MASKEDTEXT
|
||||
controlTypes.MASKEDCOMBO
|
||||
controlTypes.IPADDR
|
||||
controlTypes.TIME
|
||||
controlTypes.NUMBER
|
||||
</FONT></PRE>
|
||||
These constants are also available individually, ie, you can
|
||||
use either of the following:
|
||||
<PRE><FONT SIZE=-1>
|
||||
from wxPython.wx.lib.maskedctrl import wxMaskedCtrl, MASKEDCOMBO, MASKEDTEXT, NUMBER
|
||||
from wxPython.wx.lib.maskedctrl import wxMaskedCtrl, controlTypes
|
||||
</FONT></PRE>
|
||||
If not specified as a keyword argument, the default controlType is
|
||||
controlTypes.MASKEDTEXT.
|
||||
<P>
|
||||
Each of the above classes has its own unique arguments, but wxMaskedCtrl
|
||||
provides a single "unified" interface for masked controls. wxMaskedTextCtrl,
|
||||
wxMaskedComboBox and wxIpAddrCtrl are all documented below; the others have
|
||||
their own demo pages and interface descriptions.
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
from wxPython.lib.maskededit import wxMaskedTextCtrl, wxMaskedComboBox, wxIpAddrCtrl
|
||||
from wxPython.lib.maskednumctrl import wxMaskedNumCtrl
|
||||
from wxPython.lib.timectrl import wxTimeCtrl
|
||||
|
||||
|
||||
# "type" enumeration for class instance factory function
|
||||
MASKEDTEXT = 0
|
||||
MASKEDCOMBO = 1
|
||||
IPADDR = 2
|
||||
TIME = 3
|
||||
NUMBER = 4
|
||||
|
||||
# for ease of import
|
||||
class controlTypes:
|
||||
MASKEDTEXT = MASKEDTEXT
|
||||
MASKEDCOMBO = MASKEDCOMBO
|
||||
IPADDR = IPADDR
|
||||
TIME = TIME
|
||||
NUMBER = NUMBER
|
||||
|
||||
|
||||
def wxMaskedCtrl( *args, **kwargs):
|
||||
"""
|
||||
Actually a factory function providing a unifying
|
||||
interface for generating masked controls.
|
||||
"""
|
||||
if not kwargs.has_key('controlType'):
|
||||
controlType = MASKEDTEXT
|
||||
else:
|
||||
controlType = kwargs['controlType']
|
||||
del kwargs['controlType']
|
||||
|
||||
if controlType == MASKEDTEXT:
|
||||
return wxMaskedTextCtrl(*args, **kwargs)
|
||||
|
||||
elif controlType == MASKEDCOMBO:
|
||||
return wxMaskedComboBox(*args, **kwargs)
|
||||
|
||||
elif controlType == IPADDR:
|
||||
return wxIpAddrCtrl(*args, **kwargs)
|
||||
|
||||
elif controlType == TIME:
|
||||
return wxTimeCtrl(*args, **kwargs)
|
||||
|
||||
elif controlType == NUMBER:
|
||||
return wxMaskedNumCtrl(*args, **kwargs)
|
||||
|
||||
else:
|
||||
raise AttributeError(
|
||||
"invalid controlType specified: %s" % repr(controlType))
|
||||
import wx.lib.maskedctrl
|
||||
|
||||
__doc__ = wx.lib.maskedctrl.__doc__
|
||||
|
||||
controlTypes = wx.lib.maskedctrl.controlTypes
|
||||
wxMaskedCtrl = wx.lib.maskedctrl.wxMaskedCtrl
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.mixins
|
||||
# Purpose: A package for helpful wxPython mix-in classes
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 15-May-2001
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2001 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,43 +1,9 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: wxPython.lib.mixins.grid
|
||||
# Purpose: Helpful mix-in classes for wxGrid
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 5-June-2001
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2001 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython import wx, grid
|
||||
import wx.lib.mixins.grid
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class wxGridAutoEditMixin:
|
||||
"""A mix-in class that automatically enables the grid edit control when
|
||||
a cell is selected.
|
||||
|
||||
If your class hooks EVT_GRID_SELECT_CELL be sure to call event.Skip so
|
||||
this handler will be called too.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.__enableEdit = 0
|
||||
wx.EVT_IDLE(self, self.__OnIdle)
|
||||
grid.EVT_GRID_SELECT_CELL(self, self.__OnSelectCell)
|
||||
|
||||
|
||||
def __OnIdle(self, evt):
|
||||
if self.__enableEdit:
|
||||
if self.CanEnableCellControl():
|
||||
self.EnableCellEditControl()
|
||||
self.__enableEdit = 0
|
||||
evt.Skip()
|
||||
|
||||
|
||||
def __OnSelectCell(self, evt):
|
||||
self.__enableEdit = 1
|
||||
evt.Skip()
|
||||
__doc__ = wx.lib.mixins.grid.__doc__
|
||||
|
||||
wxGridAutoEditMixin = wx.lib.mixins.grid.wxGridAutoEditMixin
|
||||
|
||||
@@ -1,73 +1,9 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: wxPython.lib.mixins.listctrl
|
||||
# Purpose: Helpful mix-in classes for using a wxImageList
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 15-May-2001
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2001 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
class MagicImageList:
|
||||
'''
|
||||
Mix-in to provide "magic" growing image lists
|
||||
By Mike Fletcher
|
||||
'''
|
||||
|
||||
### LAZYTREE and LISTCONTROL Methods
|
||||
DEFAULTICONSIZE = 16
|
||||
|
||||
def SetupIcons(self, images=(), size=None):
|
||||
self.__size = size or self.DEFAULTICONSIZE
|
||||
self.__magicImageList = wxImageList (self.__size,self.__size)
|
||||
self.__magicImageListMapping = {}
|
||||
self.SetImageList (
|
||||
self.__magicImageList, {
|
||||
16:wxIMAGE_LIST_SMALL,
|
||||
32:wxIMAGE_LIST_NORMAL,
|
||||
}[self.__size]
|
||||
)
|
||||
for image in images:
|
||||
self.AddIcon (image)
|
||||
|
||||
def GetIcons (self, node):
|
||||
'''Get icon indexes for a given node, or None if no associated icon'''
|
||||
icon = self.GetIcon( node )
|
||||
if icon:
|
||||
index = self.AddIcon (icon)
|
||||
return index, index
|
||||
return None
|
||||
|
||||
|
||||
### Local methods...
|
||||
def AddIcon(self, icon, mask = wxNullBitmap):
|
||||
'''Add an icon to the image list, or get the index if already there'''
|
||||
index = self.__magicImageListMapping.get (id (icon))
|
||||
if index is None:
|
||||
if isinstance( icon, wxIconPtr ):
|
||||
index = self.__magicImageList.AddIcon( icon )
|
||||
elif isinstance( icon, wxBitmapPtr ):
|
||||
if isinstance( mask, wxColour ):
|
||||
index = self.__magicImageList.AddWithColourMask( icon, mask )
|
||||
else:
|
||||
index = self.__magicImageList.Add( icon, mask )
|
||||
else:
|
||||
raise ValueError("Unexpected icon object %s, "
|
||||
"expected wxIcon or wxBitmap" % (icon))
|
||||
self.__magicImageListMapping [id (icon)] = index
|
||||
return index
|
||||
|
||||
### Customisation point...
|
||||
def GetIcon( self, node ):
|
||||
'''Get the actual icon object for a node'''
|
||||
if hasattr (node,"DIAGRAMICON"):
|
||||
return node.DIAGRAMICON
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.mixins.imagelist
|
||||
|
||||
__doc__ = wx.lib.mixins.imagelist.__doc__
|
||||
|
||||
MagicImageList = wx.lib.mixins.imagelist.MagicImageList
|
||||
|
||||
@@ -1,322 +1,14 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: wxPython.lib.mixins.listctrl
|
||||
# Purpose: Helpful mix-in classes for wxListCtrl
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 15-May-2001
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2001 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython.wx import *
|
||||
import locale
|
||||
import wx.lib.mixins.listctrl
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
__doc__ = wx.lib.mixins.listctrl.__doc__
|
||||
|
||||
class wxColumnSorterMixin:
|
||||
"""
|
||||
A mixin class that handles sorting of a wxListCtrl in REPORT mode when
|
||||
the column header is clicked on.
|
||||
ListCtrlSelectionManagerMix = wx.lib.mixins.listctrl.ListCtrlSelectionManagerMix
|
||||
getListCtrlSelection = wx.lib.mixins.listctrl.getListCtrlSelection
|
||||
selectBeforePopup = wx.lib.mixins.listctrl.selectBeforePopup
|
||||
wxColumnSorterMixin = wx.lib.mixins.listctrl.wxColumnSorterMixin
|
||||
wxListCtrlAutoWidthMixin = wx.lib.mixins.listctrl.wxListCtrlAutoWidthMixin
|
||||
|
||||
There are a few requirments needed in order for this to work genericly:
|
||||
|
||||
1. The combined class must have a GetListCtrl method that
|
||||
returns the wxListCtrl to be sorted, and the list control
|
||||
must exist at the time the wxColumnSorterMixin.__init__
|
||||
method is called because it uses GetListCtrl.
|
||||
|
||||
2. Items in the list control must have a unique data value set
|
||||
with list.SetItemData.
|
||||
|
||||
3. The combined class must have an attribute named itemDataMap
|
||||
that is a dictionary mapping the data values to a sequence of
|
||||
objects representing the values in each column. These values
|
||||
are compared in the column sorter to determine sort order.
|
||||
|
||||
Interesting methods to override are GetColumnSorter,
|
||||
GetSecondarySortValues, and GetSortImages. See below for details.
|
||||
"""
|
||||
|
||||
def __init__(self, numColumns):
|
||||
self.SetColumnCount(numColumns)
|
||||
list = self.GetListCtrl()
|
||||
if not list:
|
||||
raise ValueError, "No wxListCtrl available"
|
||||
EVT_LIST_COL_CLICK(list, list.GetId(), self.__OnColClick)
|
||||
|
||||
|
||||
def SetColumnCount(self, newNumColumns):
|
||||
self._colSortFlag = [0] * newNumColumns
|
||||
self._col = -1
|
||||
|
||||
|
||||
def SortListItems(self, col=-1, ascending=1):
|
||||
"""Sort the list on demand. Can also be used to set the sort column and order."""
|
||||
oldCol = self._col
|
||||
if col != -1:
|
||||
self._col = col
|
||||
self._colSortFlag[col] = ascending
|
||||
self.GetListCtrl().SortItems(self.GetColumnSorter())
|
||||
self.__updateImages(oldCol)
|
||||
|
||||
|
||||
def GetColumnWidths(self):
|
||||
"""
|
||||
Returns a list of column widths. Can be used to help restore the current
|
||||
view later.
|
||||
"""
|
||||
list = self.GetListCtrl()
|
||||
rv = []
|
||||
for x in range(len(self._colSortFlag)):
|
||||
rv.append(list.GetColumnWidth(x))
|
||||
return rv
|
||||
|
||||
|
||||
def GetSortImages(self):
|
||||
"""
|
||||
Returns a tuple of image list indexesthe indexes in the image list for an image to be put on the column
|
||||
header when sorting in descending order.
|
||||
"""
|
||||
return (-1, -1) # (decending, ascending) image IDs
|
||||
|
||||
|
||||
def GetColumnSorter(self):
|
||||
"""Returns a callable object to be used for comparing column values when sorting."""
|
||||
return self.__ColumnSorter
|
||||
|
||||
|
||||
def GetSecondarySortValues(self, col, key1, key2):
|
||||
"""Returns a tuple of 2 values to use for secondary sort values when the
|
||||
items in the selected column match equal. The default just returns the
|
||||
item data values."""
|
||||
return (key1, key2)
|
||||
|
||||
|
||||
def __OnColClick(self, evt):
|
||||
oldCol = self._col
|
||||
self._col = col = evt.GetColumn()
|
||||
self._colSortFlag[col] = not self._colSortFlag[col]
|
||||
self.GetListCtrl().SortItems(self.GetColumnSorter())
|
||||
self.__updateImages(oldCol)
|
||||
evt.Skip()
|
||||
|
||||
|
||||
def __ColumnSorter(self, key1, key2):
|
||||
col = self._col
|
||||
ascending = self._colSortFlag[col]
|
||||
item1 = self.itemDataMap[key1][col]
|
||||
item2 = self.itemDataMap[key2][col]
|
||||
|
||||
#--- Internationalization of string sorting with locale module
|
||||
if type(item1) == type('') or type(item2) == type(''):
|
||||
cmpVal = locale.strcoll(str(item1), str(item2))
|
||||
else:
|
||||
cmpVal = cmp(item1, item2)
|
||||
#---
|
||||
|
||||
# If the items are equal then pick something else to make the sort value unique
|
||||
if cmpVal == 0:
|
||||
cmpVal = apply(cmp, self.GetSecondarySortValues(col, key1, key2))
|
||||
|
||||
if ascending:
|
||||
return cmpVal
|
||||
else:
|
||||
return -cmpVal
|
||||
|
||||
|
||||
def __updateImages(self, oldCol):
|
||||
sortImages = self.GetSortImages()
|
||||
if self._col != -1 and sortImages[0] != -1:
|
||||
img = sortImages[self._colSortFlag[self._col]]
|
||||
list = self.GetListCtrl()
|
||||
if oldCol != -1:
|
||||
list.ClearColumnImage(oldCol)
|
||||
list.SetColumnImage(self._col, img)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
class wxListCtrlAutoWidthMixin:
|
||||
""" A mix-in class that automatically resizes the last column to take up
|
||||
the remaining width of the wxListCtrl.
|
||||
|
||||
This causes the wxListCtrl to automatically take up the full width of
|
||||
the list, without either a horizontal scroll bar (unless absolutely
|
||||
necessary) or empty space to the right of the last column.
|
||||
|
||||
NOTE: This only works for report-style lists.
|
||||
|
||||
WARNING: If you override the EVT_SIZE event in your wxListCtrl, make
|
||||
sure you call event.Skip() to ensure that the mixin's
|
||||
_OnResize method is called.
|
||||
|
||||
This mix-in class was written by Erik Westra <ewestra@wave.co.nz>
|
||||
"""
|
||||
def __init__(self):
|
||||
""" Standard initialiser.
|
||||
"""
|
||||
self._lastColMinWidth = None
|
||||
|
||||
EVT_SIZE(self, self._onResize)
|
||||
EVT_LIST_COL_END_DRAG(self, self.GetId(), self._onResize)
|
||||
|
||||
|
||||
def resizeLastColumn(self, minWidth):
|
||||
""" Resize the last column appropriately.
|
||||
|
||||
If the list's columns are too wide to fit within the window, we use
|
||||
a horizontal scrollbar. Otherwise, we expand the right-most column
|
||||
to take up the remaining free space in the list.
|
||||
|
||||
This method is called automatically when the wxListCtrl is resized;
|
||||
you can also call it yourself whenever you want the last column to
|
||||
be resized appropriately (eg, when adding, removing or resizing
|
||||
columns).
|
||||
|
||||
'minWidth' is the preferred minimum width for the last column.
|
||||
"""
|
||||
self._lastColMinWidth = minWidth
|
||||
self._doResize()
|
||||
|
||||
# =====================
|
||||
# == Private Methods ==
|
||||
# =====================
|
||||
|
||||
def _onResize(self, event):
|
||||
""" Respond to the wxListCtrl being resized.
|
||||
|
||||
We automatically resize the last column in the list.
|
||||
"""
|
||||
wxCallAfter(self._doResize)
|
||||
event.Skip()
|
||||
|
||||
|
||||
def _doResize(self):
|
||||
""" Resize the last column as appropriate.
|
||||
|
||||
If the list's columns are too wide to fit within the window, we use
|
||||
a horizontal scrollbar. Otherwise, we expand the right-most column
|
||||
to take up the remaining free space in the list.
|
||||
|
||||
We remember the current size of the last column, before resizing,
|
||||
as the preferred minimum width if we haven't previously been given
|
||||
or calculated a minimum width. This ensure that repeated calls to
|
||||
_doResize() don't cause the last column to size itself too large.
|
||||
"""
|
||||
numCols = self.GetColumnCount()
|
||||
if numCols == 0: return # Nothing to resize.
|
||||
|
||||
if self._lastColMinWidth == None:
|
||||
self._lastColMinWidth = self.GetColumnWidth(numCols - 1)
|
||||
|
||||
# We're showing the vertical scrollbar -> allow for scrollbar width
|
||||
# NOTE: on GTK, the scrollbar is included in the client size, but on
|
||||
# Windows it is not included
|
||||
listWidth = self.GetClientSize().width
|
||||
if wxPlatform != '__WXMSW__':
|
||||
if self.GetItemCount() > self.GetCountPerPage():
|
||||
scrollWidth = wxSystemSettings_GetSystemMetric(wxSYS_VSCROLL_X)
|
||||
listWidth = listWidth - scrollWidth
|
||||
|
||||
totColWidth = 0 # Width of all columns except last one.
|
||||
for col in range(numCols-1):
|
||||
totColWidth = totColWidth + self.GetColumnWidth(col)
|
||||
|
||||
lastColWidth = self.GetColumnWidth(numCols - 1)
|
||||
|
||||
if totColWidth + self._lastColMinWidth > listWidth:
|
||||
# We haven't got the width to show the last column at its minimum
|
||||
# width -> set it to its minimum width and allow the horizontal
|
||||
# scrollbar to show.
|
||||
self.SetColumnWidth(numCols-1, self._lastColMinWidth)
|
||||
return
|
||||
|
||||
# Resize the last column to take up the remaining available space.
|
||||
|
||||
self.SetColumnWidth(numCols-1, listWidth - totColWidth)
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
SEL_FOC = wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED
|
||||
def selectBeforePopup(event):
|
||||
"""Ensures the item the mouse is pointing at is selected before a popup.
|
||||
|
||||
Works with both single-select and multi-select lists."""
|
||||
ctrl = event.GetEventObject()
|
||||
if isinstance(ctrl, wxListCtrl):
|
||||
n, flags = ctrl.HitTest(event.GetPosition())
|
||||
if n >= 0:
|
||||
if not ctrl.GetItemState(n, wxLIST_STATE_SELECTED):
|
||||
for i in range(ctrl.GetItemCount()):
|
||||
ctrl.SetItemState(i, 0, SEL_FOC)
|
||||
#for i in getListCtrlSelection(ctrl, SEL_FOC):
|
||||
# ctrl.SetItemState(i, 0, SEL_FOC)
|
||||
ctrl.SetItemState(n, SEL_FOC, SEL_FOC)
|
||||
|
||||
def getListCtrlSelection(listctrl, state=wxLIST_STATE_SELECTED):
|
||||
""" Returns list of item indexes of given state (selected by defaults) """
|
||||
res = []
|
||||
idx = -1
|
||||
while 1:
|
||||
idx = listctrl.GetNextItem(idx, wxLIST_NEXT_ALL, state)
|
||||
if idx == -1:
|
||||
break
|
||||
res.append(idx)
|
||||
return res
|
||||
|
||||
class ListCtrlSelectionManagerMix:
|
||||
"""Mixin that defines a platform independent selection policy
|
||||
|
||||
As selection single and multi-select list return the item index or a
|
||||
list of item indexes respectively.
|
||||
"""
|
||||
wxEVT_DOPOPUPMENU = wxNewId()
|
||||
_menu = None
|
||||
|
||||
def __init__(self):
|
||||
EVT_RIGHT_DOWN(self, self.OnLCSMRightDown)
|
||||
self.Connect(-1, -1, self.wxEVT_DOPOPUPMENU, self.OnLCSMDoPopup)
|
||||
|
||||
def getPopupMenu(self):
|
||||
""" Override to implement dynamic menus (create) """
|
||||
return self._menu
|
||||
|
||||
def setPopupMenu(self, menu):
|
||||
""" Must be set for default behaviour """
|
||||
self._menu = menu
|
||||
|
||||
def afterPopupMenu(self, menu):
|
||||
""" Override to implement dynamic menus (destroy) """
|
||||
pass
|
||||
|
||||
def getSelection(self):
|
||||
res = getListCtrlSelection(self)
|
||||
if self.GetWindowStyleFlag() & wxLC_SINGLE_SEL:
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
return -1
|
||||
else:
|
||||
return res
|
||||
|
||||
def OnLCSMRightDown(self, event):
|
||||
selectBeforePopup(event)
|
||||
event.Skip()
|
||||
menu = self.getPopupMenu()
|
||||
if menu:
|
||||
evt = wxPyEvent()
|
||||
evt.SetEventType(self.wxEVT_DOPOPUPMENU)
|
||||
evt.menu = menu
|
||||
evt.pos = event.GetPosition()
|
||||
wxPostEvent(self, evt)
|
||||
|
||||
def OnLCSMDoPopup(self, event):
|
||||
self.PopupMenu(event.menu, event.pos)
|
||||
self.afterPopupMenu(event.menu)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
@@ -1,396 +1,15 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: wxPython.lib.mixins.rubberband
|
||||
# Purpose: A mixin class for doing "RubberBand"-ing on a window.
|
||||
#
|
||||
# Author: Robb Shecter and members of wxPython-users
|
||||
#
|
||||
# Created: 11-September-2002
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2002 by db-X Corporation
|
||||
# Licence: wxWindows license
|
||||
#---------------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
"""
|
||||
A mixin class for doing "RubberBand"-ing on a window.
|
||||
"""
|
||||
import wx.lib.mixins.rubberband
|
||||
|
||||
from wxPython.wx import *
|
||||
__doc__ = wx.lib.mixins.rubberband.__doc__
|
||||
|
||||
|
||||
#
|
||||
# Some miscellaneous mathematical and geometrical functions
|
||||
#
|
||||
|
||||
def isNegative(aNumber):
|
||||
"""
|
||||
x < 0: 1
|
||||
else: 0
|
||||
"""
|
||||
return aNumber < 0
|
||||
|
||||
|
||||
def normalizeBox(box):
|
||||
"""
|
||||
Convert any negative measurements in the current
|
||||
box to positive, and adjust the origin.
|
||||
"""
|
||||
x, y, w, h = box
|
||||
if w < 0:
|
||||
x += (w+1)
|
||||
w *= -1
|
||||
if h < 0:
|
||||
y += (h+1)
|
||||
h *= -1
|
||||
return (x, y, w, h)
|
||||
|
||||
|
||||
def boxToExtent(box):
|
||||
"""
|
||||
Convert a box specification to an extent specification.
|
||||
I put this into a seperate function after I realized that
|
||||
I had been implementing it wrong in several places.
|
||||
"""
|
||||
b = normalizeBox(box)
|
||||
return (b[0], b[1], b[0]+b[2]-1, b[1]+b[3]-1)
|
||||
|
||||
|
||||
def pointInBox(x, y, box):
|
||||
"""
|
||||
Return True if the given point is contained in the box.
|
||||
"""
|
||||
e = boxToExtent(box)
|
||||
return x >= e[0] and x <= e[2] and y >= e[1] and y <= e[3]
|
||||
|
||||
|
||||
def pointOnBox(x, y, box, thickness=1):
|
||||
"""
|
||||
Return True if the point is on the outside edge
|
||||
of the box. The thickness defines how thick the
|
||||
edge should be. This is necessary for HCI reasons:
|
||||
For example, it's normally very difficult for a user
|
||||
to manuever the mouse onto a one pixel border.
|
||||
"""
|
||||
outerBox = box
|
||||
innerBox = (box[0]+thickness, box[1]+thickness, box[2]-(thickness*2), box[3]-(thickness*2))
|
||||
return pointInBox(x, y, outerBox) and not pointInBox(x, y, innerBox)
|
||||
|
||||
|
||||
def getCursorPosition(x, y, box, thickness=1):
|
||||
"""
|
||||
Return a position number in the range 0 .. 7 to indicate
|
||||
where on the box border the point is. The layout is:
|
||||
|
||||
0 1 2
|
||||
7 3
|
||||
6 5 4
|
||||
"""
|
||||
x0, y0, x1, y1 = boxToExtent(box)
|
||||
w, h = box[2], box[3]
|
||||
delta = thickness - 1
|
||||
p = None
|
||||
|
||||
if pointInBox(x, y, (x0, y0, thickness, thickness)):
|
||||
p = 0
|
||||
elif pointInBox(x, y, (x1-delta, y0, thickness, thickness)):
|
||||
p = 2
|
||||
elif pointInBox(x, y, (x1-delta, y1-delta, thickness, thickness)):
|
||||
p = 4
|
||||
elif pointInBox(x, y, (x0, y1-delta, thickness, thickness)):
|
||||
p = 6
|
||||
elif pointInBox(x, y, (x0+thickness, y0, w-(thickness*2), thickness)):
|
||||
p = 1
|
||||
elif pointInBox(x, y, (x1-delta, y0+thickness, thickness, h-(thickness*2))):
|
||||
p = 3
|
||||
elif pointInBox(x, y, (x0+thickness, y1-delta, w-(thickness*2), thickness)):
|
||||
p = 5
|
||||
elif pointInBox(x, y, (x0, y0+thickness, thickness, h-(thickness*2))):
|
||||
p = 7
|
||||
|
||||
return p
|
||||
|
||||
|
||||
|
||||
|
||||
class RubberBand:
|
||||
"""
|
||||
A stretchable border which is drawn on top of an
|
||||
image to define an area.
|
||||
"""
|
||||
def __init__(self, drawingSurface, aspectRatio=None):
|
||||
self.__THICKNESS = 5
|
||||
self.drawingSurface = drawingSurface
|
||||
self.aspectRatio = aspectRatio
|
||||
self.hasLetUp = 0
|
||||
self.currentlyMoving = None
|
||||
self.currentBox = None
|
||||
self.__enabled = 1
|
||||
self.__currentCursor = None
|
||||
EVT_MOUSE_EVENTS(drawingSurface, self.__handleMouseEvents)
|
||||
EVT_PAINT(drawingSurface, self.__handleOnPaint)
|
||||
|
||||
def __setEnabled(self, enabled):
|
||||
self.__enabled = enabled
|
||||
|
||||
def __isEnabled(self):
|
||||
return self.__enabled
|
||||
|
||||
def __handleOnPaint(self, event):
|
||||
#print 'paint'
|
||||
event.Skip()
|
||||
|
||||
def __isMovingCursor(self):
|
||||
"""
|
||||
Return True if the current cursor is one used to
|
||||
mean moving the rubberband.
|
||||
"""
|
||||
return self.__currentCursor == wxCURSOR_HAND
|
||||
|
||||
def __isSizingCursor(self):
|
||||
"""
|
||||
Return True if the current cursor is one of the ones
|
||||
I may use to signify sizing.
|
||||
"""
|
||||
sizingCursors = [wxCURSOR_SIZENESW,
|
||||
wxCURSOR_SIZENS,
|
||||
wxCURSOR_SIZENWSE,
|
||||
wxCURSOR_SIZEWE,
|
||||
wxCURSOR_SIZING,
|
||||
wxCURSOR_CROSS]
|
||||
try:
|
||||
sizingCursors.index(self.__currentCursor)
|
||||
return 1
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
|
||||
def __handleMouseEvents(self, event):
|
||||
"""
|
||||
React according to the new event. This is the main
|
||||
entry point into the class. This method contains the
|
||||
logic for the class's behavior.
|
||||
"""
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
x, y = event.GetPosition()
|
||||
|
||||
# First make sure we have started a box.
|
||||
if self.currentBox == None and not event.LeftDown():
|
||||
# No box started yet. Set cursor to the initial kind.
|
||||
self.__setCursor(wxCURSOR_CROSS)
|
||||
return
|
||||
|
||||
if event.LeftDown():
|
||||
if self.currentBox == None:
|
||||
# No RB Box, so start a new one.
|
||||
self.currentBox = (x, y, 0, 0)
|
||||
self.hasLetUp = 0
|
||||
elif self.__isSizingCursor():
|
||||
# Starting a sizing operation. Change the origin.
|
||||
position = getCursorPosition(x, y, self.currentBox, thickness=self.__THICKNESS)
|
||||
self.currentBox = self.__denormalizeBox(position, self.currentBox)
|
||||
|
||||
elif event.Dragging() and event.LeftIsDown():
|
||||
# Use the cursor type to determine operation
|
||||
if self.__isMovingCursor():
|
||||
if self.currentlyMoving or pointInBox(x, y, self.currentBox):
|
||||
if not self.currentlyMoving:
|
||||
self.currentlyMoving = (x - self.currentBox[0], y - self.currentBox[1])
|
||||
self.__moveTo(x - self.currentlyMoving[0], y - self.currentlyMoving[1])
|
||||
elif self.__isSizingCursor():
|
||||
self.__resizeBox(x, y)
|
||||
|
||||
elif event.LeftUp():
|
||||
self.hasLetUp = 1
|
||||
self.currentlyMoving = None
|
||||
self.__normalizeBox()
|
||||
|
||||
elif event.Moving() and not event.Dragging():
|
||||
# Simple mouse movement event
|
||||
self.__mouseMoved(x,y)
|
||||
|
||||
def __denormalizeBox(self, position, box):
|
||||
x, y, w, h = box
|
||||
b = box
|
||||
if position == 2 or position == 3:
|
||||
b = (x, y + (h-1), w, h * -1)
|
||||
elif position == 0 or position == 1 or position == 7:
|
||||
b = (x + (w-1), y + (h-1), w * -1, h * -1)
|
||||
elif position == 6:
|
||||
b = (x + (w-1), y, w * -1, h)
|
||||
return b
|
||||
|
||||
def __resizeBox(self, x, y):
|
||||
"""
|
||||
Resize and repaint the box based on the given mouse
|
||||
coordinates.
|
||||
"""
|
||||
# Implement the correct behavior for dragging a side
|
||||
# of the box: Only change one dimension.
|
||||
if not self.aspectRatio:
|
||||
if self.__currentCursor == wxCURSOR_SIZENS:
|
||||
x = None
|
||||
elif self.__currentCursor == wxCURSOR_SIZEWE:
|
||||
y = None
|
||||
|
||||
x0,y0,w0,h0 = self.currentBox
|
||||
currentExtent = boxToExtent(self.currentBox)
|
||||
if x == None:
|
||||
if w0 < 1:
|
||||
w0 += 1
|
||||
else:
|
||||
w0 -= 1
|
||||
x = x0 + w0
|
||||
if y == None:
|
||||
if h0 < 1:
|
||||
h0 += 1
|
||||
else:
|
||||
h0 -= 1
|
||||
y = y0 + h0
|
||||
x1,y1 = x, y
|
||||
w, h = abs(x1-x0)+1, abs(y1-y0)+1
|
||||
if self.aspectRatio:
|
||||
w = max(w, int(h * self.aspectRatio))
|
||||
h = int(w / self.aspectRatio)
|
||||
w *= [1,-1][isNegative(x1-x0)]
|
||||
h *= [1,-1][isNegative(y1-y0)]
|
||||
newbox = (x0, y0, w, h)
|
||||
self.__drawAndErase(boxToDraw=normalizeBox(newbox), boxToErase=normalizeBox(self.currentBox))
|
||||
self.currentBox = (x0, y0, w, h)
|
||||
|
||||
def __normalizeBox(self):
|
||||
"""
|
||||
Convert any negative measurements in the current
|
||||
box to positive, and adjust the origin.
|
||||
"""
|
||||
self.currentBox = normalizeBox(self.currentBox)
|
||||
|
||||
def __mouseMoved(self, x, y):
|
||||
"""
|
||||
Called when the mouse moved without any buttons pressed
|
||||
or dragging being done.
|
||||
"""
|
||||
# Are we on the bounding box?
|
||||
if pointOnBox(x, y, self.currentBox, thickness=self.__THICKNESS):
|
||||
position = getCursorPosition(x, y, self.currentBox, thickness=self.__THICKNESS)
|
||||
cursor = [
|
||||
wxCURSOR_SIZENWSE,
|
||||
wxCURSOR_SIZENS,
|
||||
wxCURSOR_SIZENESW,
|
||||
wxCURSOR_SIZEWE,
|
||||
wxCURSOR_SIZENWSE,
|
||||
wxCURSOR_SIZENS,
|
||||
wxCURSOR_SIZENESW,
|
||||
wxCURSOR_SIZEWE
|
||||
] [position]
|
||||
self.__setCursor(cursor)
|
||||
elif pointInBox(x, y, self.currentBox):
|
||||
self.__setCursor(wxCURSOR_HAND)
|
||||
else:
|
||||
self.__setCursor()
|
||||
|
||||
def __setCursor(self, id=None):
|
||||
"""
|
||||
Set the mouse cursor to the given id.
|
||||
"""
|
||||
if self.__currentCursor != id: # Avoid redundant calls
|
||||
if id:
|
||||
self.drawingSurface.SetCursor(wxStockCursor(id))
|
||||
else:
|
||||
self.drawingSurface.SetCursor(wxNullCursor)
|
||||
self.__currentCursor = id
|
||||
|
||||
def __moveCenterTo(self, x, y):
|
||||
"""
|
||||
Move the rubber band so that its center is at (x,y).
|
||||
"""
|
||||
x0, y0, w, h = self.currentBox
|
||||
x2, y2 = x - (w/2), y - (h/2)
|
||||
self.__moveTo(x2, y2)
|
||||
|
||||
def __moveTo(self, x, y):
|
||||
"""
|
||||
Move the rubber band so that its origin is at (x,y).
|
||||
"""
|
||||
newbox = (x, y, self.currentBox[2], self.currentBox[3])
|
||||
self.__drawAndErase(boxToDraw=newbox, boxToErase=self.currentBox)
|
||||
self.currentBox = newbox
|
||||
|
||||
def __drawAndErase(self, boxToDraw, boxToErase=None):
|
||||
"""
|
||||
Draw one box shape and possibly erase another.
|
||||
"""
|
||||
dc = wxClientDC(self.drawingSurface)
|
||||
dc.BeginDrawing()
|
||||
dc.SetPen(wxPen(wxWHITE, 1, wxDOT))
|
||||
dc.SetBrush(wxTRANSPARENT_BRUSH)
|
||||
dc.SetLogicalFunction(wxXOR)
|
||||
if boxToErase:
|
||||
dc.DrawRectangle(*boxToErase)
|
||||
dc.DrawRectangle(*boxToDraw)
|
||||
dc.EndDrawing()
|
||||
|
||||
def __dumpMouseEvent(self, event):
|
||||
print 'Moving: ',event.Moving()
|
||||
print 'Dragging: ',event.Dragging()
|
||||
print 'LeftDown: ',event.LeftDown()
|
||||
print 'LeftisDown: ',event.LeftIsDown()
|
||||
print 'LeftUp: ',event.LeftUp()
|
||||
print 'Position: ',event.GetPosition()
|
||||
print 'x,y: ',event.GetX(),event.GetY()
|
||||
print
|
||||
|
||||
|
||||
#
|
||||
# The public API:
|
||||
#
|
||||
|
||||
def reset(self, aspectRatio=None):
|
||||
"""
|
||||
Clear the existing rubberband
|
||||
"""
|
||||
self.currentBox = None
|
||||
self.aspectRatio = aspectRatio
|
||||
self.drawingSurface.Refresh()
|
||||
|
||||
def getCurrentExtent(self):
|
||||
"""
|
||||
Return (x0, y0, x1, y1) or None if
|
||||
no drawing has yet been done.
|
||||
"""
|
||||
if not self.currentBox:
|
||||
extent = None
|
||||
else:
|
||||
extent = boxToExtent(self.currentBox)
|
||||
return extent
|
||||
|
||||
enabled = property(__isEnabled, __setEnabled, None, 'True if I am responding to mouse events')
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = wxPySimpleApp()
|
||||
frame = wxFrame(None, -1, title='RubberBand Test', size=(300,300))
|
||||
|
||||
# Add a panel that the rubberband will work on.
|
||||
panel = wxPanel(frame, -1)
|
||||
panel.SetBackgroundColour(wxBLUE)
|
||||
|
||||
# Create the rubberband
|
||||
frame.rubberBand = RubberBand(drawingSurface=panel)
|
||||
frame.rubberBand.reset(aspectRatio=0.5)
|
||||
|
||||
# Add a button that creates a new rubberband
|
||||
def __newRubberBand(event):
|
||||
frame.rubberBand.reset()
|
||||
button = wxButton(frame, 100, 'Reset Rubberband')
|
||||
EVT_BUTTON(frame, 100, __newRubberBand)
|
||||
|
||||
# Layout the frame
|
||||
sizer = wxBoxSizer(wxVERTICAL)
|
||||
sizer.Add(panel, 1, wxEXPAND | wxALL, 5)
|
||||
sizer.Add(button, 0, wxALIGN_CENTER | wxALL, 5)
|
||||
frame.SetAutoLayout(1)
|
||||
frame.SetSizer(sizer)
|
||||
frame.Show(1)
|
||||
app.MainLoop()
|
||||
RubberBand = wx.lib.mixins.rubberband.RubberBand
|
||||
boxToExtent = wx.lib.mixins.rubberband.boxToExtent
|
||||
getCursorPosition = wx.lib.mixins.rubberband.getCursorPosition
|
||||
isNegative = wx.lib.mixins.rubberband.isNegative
|
||||
normalizeBox = wx.lib.mixins.rubberband.normalizeBox
|
||||
pointInBox = wx.lib.mixins.rubberband.pointInBox
|
||||
pointOnBox = wx.lib.mixins.rubberband.pointOnBox
|
||||
|
||||
@@ -1,726 +1,16 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: multisash
|
||||
# Purpose: Multi Sash control
|
||||
#
|
||||
# Author: Gerrit van Dyk
|
||||
#
|
||||
# Created: 2002/11/20
|
||||
# Version: 0.1
|
||||
# RCS-ID: $Id$
|
||||
# License: wxWindows licensie
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
MV_HOR = 0
|
||||
MV_VER = not MV_HOR
|
||||
|
||||
SH_SIZE = 5
|
||||
CR_SIZE = SH_SIZE * 3
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class wxMultiSash(wxWindow):
|
||||
def __init__(self, *_args,**_kwargs):
|
||||
apply(wxWindow.__init__,(self,) + _args,_kwargs)
|
||||
self._defChild = EmptyChild
|
||||
self.child = wxMultiSplit(self,self,wxPoint(0,0),self.GetSize())
|
||||
EVT_SIZE(self,self.OnMultiSize)
|
||||
|
||||
def SetDefaultChildClass(self,childCls):
|
||||
self._defChild = childCls
|
||||
self.child.DefaultChildChanged()
|
||||
|
||||
def OnMultiSize(self,evt):
|
||||
self.child.SetSize(self.GetSize())
|
||||
|
||||
def UnSelect(self):
|
||||
self.child.UnSelect()
|
||||
|
||||
def Clear(self):
|
||||
old = self.child
|
||||
self.child = wxMultiSplit(self,self,wxPoint(0,0),self.GetSize())
|
||||
old.Destroy()
|
||||
self.child.OnSize(None)
|
||||
|
||||
def GetSaveData(self):
|
||||
saveData = {}
|
||||
saveData['_defChild'] = str(self._defChild)
|
||||
saveData['child'] = self.child.GetSaveData()
|
||||
return saveData
|
||||
|
||||
def SetSaveData(self,data):
|
||||
dChild = data['_defChild']
|
||||
mod = dChild.split('.')[0]
|
||||
exec 'import %s' % mod
|
||||
self._defChild = eval(dChild)
|
||||
old = self.child
|
||||
self.child = wxMultiSplit(self,self,wxPoint(0,0),self.GetSize())
|
||||
self.child.SetSaveData(data['child'])
|
||||
old.Destroy()
|
||||
self.OnMultiSize(None)
|
||||
self.child.OnSize(None)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
class wxMultiSplit(wxWindow):
|
||||
def __init__(self,multiView,parent,pos,size,view1 = None):
|
||||
wxWindow.__init__(self,id = -1,parent = parent,pos = pos,size = size,
|
||||
style = wxCLIP_CHILDREN)
|
||||
self.multiView = multiView
|
||||
self.view2 = None
|
||||
if view1:
|
||||
self.view1 = view1
|
||||
self.view1.Reparent(self)
|
||||
self.view1.MoveXY(0,0)
|
||||
else:
|
||||
self.view1 = wxMultiViewLeaf(self.multiView,self,
|
||||
wxPoint(0,0),self.GetSize())
|
||||
self.direction = None
|
||||
|
||||
EVT_SIZE(self,self.OnSize)
|
||||
|
||||
def GetSaveData(self):
|
||||
saveData = {}
|
||||
if self.view1:
|
||||
saveData['view1'] = self.view1.GetSaveData()
|
||||
if isinstance(self.view1,wxMultiSplit):
|
||||
saveData['view1IsSplit'] = 1
|
||||
if self.view2:
|
||||
saveData['view2'] = self.view2.GetSaveData()
|
||||
if isinstance(self.view2,wxMultiSplit):
|
||||
saveData['view2IsSplit'] = 1
|
||||
saveData['direction'] = self.direction
|
||||
v1,v2 = self.GetPositionTuple()
|
||||
saveData['x'] = v1
|
||||
saveData['y'] = v2
|
||||
v1,v2 = self.GetSizeTuple()
|
||||
saveData['w'] = v1
|
||||
saveData['h'] = v2
|
||||
return saveData
|
||||
|
||||
def SetSaveData(self,data):
|
||||
self.direction = data['direction']
|
||||
self.SetDimensions(data['x'],data['y'],data['w'],data['h'])
|
||||
v1Data = data.get('view1',None)
|
||||
if v1Data:
|
||||
isSplit = data.get('view1IsSplit',None)
|
||||
old = self.view1
|
||||
if isSplit:
|
||||
self.view1 = wxMultiSplit(self.multiView,self,
|
||||
wxPoint(0,0),self.GetSize())
|
||||
else:
|
||||
self.view1 = wxMultiViewLeaf(self.multiView,self,
|
||||
wxPoint(0,0),self.GetSize())
|
||||
self.view1.SetSaveData(v1Data)
|
||||
if old:
|
||||
old.Destroy()
|
||||
v2Data = data.get('view2',None)
|
||||
if v2Data:
|
||||
isSplit = data.get('view2IsSplit',None)
|
||||
old = self.view2
|
||||
if isSplit:
|
||||
self.view2 = wxMultiSplit(self.multiView,self,
|
||||
wxPoint(0,0),self.GetSize())
|
||||
else:
|
||||
self.view2 = wxMultiViewLeaf(self.multiView,self,
|
||||
wxPoint(0,0),self.GetSize())
|
||||
self.view2.SetSaveData(v2Data)
|
||||
if old:
|
||||
old.Destroy()
|
||||
if self.view1:
|
||||
self.view1.OnSize(None)
|
||||
if self.view2:
|
||||
self.view2.OnSize(None)
|
||||
|
||||
def UnSelect(self):
|
||||
if self.view1:
|
||||
self.view1.UnSelect()
|
||||
if self.view2:
|
||||
self.view2.UnSelect()
|
||||
|
||||
def DefaultChildChanged(self):
|
||||
if not self.view2:
|
||||
self.view1.DefaultChildChanged()
|
||||
|
||||
def AddLeaf(self,direction,caller,pos):
|
||||
if self.view2:
|
||||
if caller == self.view1:
|
||||
self.view1 = wxMultiSplit(self.multiView,self,
|
||||
caller.GetPosition(),
|
||||
caller.GetSize(),
|
||||
caller)
|
||||
self.view1.AddLeaf(direction,caller,pos)
|
||||
else:
|
||||
self.view2 = wxMultiSplit(self.multiView,self,
|
||||
caller.GetPosition(),
|
||||
caller.GetSize(),
|
||||
caller)
|
||||
self.view2.AddLeaf(direction,caller,pos)
|
||||
else:
|
||||
self.direction = direction
|
||||
w,h = self.GetSizeTuple()
|
||||
if direction == MV_HOR:
|
||||
x,y = (pos,0)
|
||||
w1,h1 = (w-pos,h)
|
||||
w2,h2 = (pos,h)
|
||||
else:
|
||||
x,y = (0,pos)
|
||||
w1,h1 = (w,h-pos)
|
||||
w2,h2 = (w,pos)
|
||||
self.view2 = wxMultiViewLeaf(self.multiView,self,
|
||||
wxPoint(x,y),wxSize(w1,h1))
|
||||
self.view1.SetSize(wxSize(w2,h2))
|
||||
self.view2.OnSize(None)
|
||||
|
||||
def DestroyLeaf(self,caller):
|
||||
if not self.view2: # We will only have 2 windows if
|
||||
return # we need to destroy any
|
||||
parent = self.GetParent() # Another splitview
|
||||
if parent == self.multiView: # We'r at the root
|
||||
if caller == self.view1:
|
||||
old = self.view1
|
||||
self.view1 = self.view2
|
||||
self.view2 = None
|
||||
old.Destroy()
|
||||
else:
|
||||
self.view2.Destroy()
|
||||
self.view2 = None
|
||||
self.view1.SetSize(self.GetSize())
|
||||
self.view1.Move(self.GetPosition())
|
||||
else:
|
||||
w,h = self.GetSizeTuple()
|
||||
x,y = self.GetPositionTuple()
|
||||
if caller == self.view1:
|
||||
if self == parent.view1:
|
||||
parent.view1 = self.view2
|
||||
else:
|
||||
parent.view2 = self.view2
|
||||
self.view2.Reparent(parent)
|
||||
self.view2.SetDimensions(x,y,w,h)
|
||||
else:
|
||||
if self == parent.view1:
|
||||
parent.view1 = self.view1
|
||||
else:
|
||||
parent.view2 = self.view1
|
||||
self.view1.Reparent(parent)
|
||||
self.view1.SetDimensions(x,y,w,h)
|
||||
self.view1 = None
|
||||
self.view2 = None
|
||||
self.Destroy()
|
||||
|
||||
def CanSize(self,side,view):
|
||||
if self.SizeTarget(side,view):
|
||||
return True
|
||||
return False
|
||||
|
||||
def SizeTarget(self,side,view):
|
||||
if self.direction == side and self.view2 and view == self.view1:
|
||||
return self
|
||||
parent = self.GetParent()
|
||||
if parent != self.multiView:
|
||||
return parent.SizeTarget(side,self)
|
||||
return None
|
||||
|
||||
def SizeLeaf(self,leaf,pos,side):
|
||||
if self.direction != side:
|
||||
return
|
||||
if not (self.view1 and self.view2):
|
||||
return
|
||||
if pos < 10: return
|
||||
w,h = self.GetSizeTuple()
|
||||
if side == MV_HOR:
|
||||
if pos > w - 10: return
|
||||
else:
|
||||
if pos > h - 10: return
|
||||
if side == MV_HOR:
|
||||
self.view1.SetDimensions(0,0,pos,h)
|
||||
self.view2.SetDimensions(pos,0,w-pos,h)
|
||||
else:
|
||||
self.view1.SetDimensions(0,0,w,pos)
|
||||
self.view2.SetDimensions(0,pos,w,h-pos)
|
||||
|
||||
def OnSize(self,evt):
|
||||
if not self.view2:
|
||||
self.view1.SetSize(self.GetSize())
|
||||
self.view1.OnSize(None)
|
||||
return
|
||||
v1w,v1h = self.view1.GetSizeTuple()
|
||||
v2w,v2h = self.view2.GetSizeTuple()
|
||||
v1x,v1y = self.view1.GetPositionTuple()
|
||||
v2x,v2y = self.view2.GetPositionTuple()
|
||||
w,h = self.GetSizeTuple()
|
||||
|
||||
if v1x != v2x:
|
||||
ratio = float(w) / float((v1w + v2w))
|
||||
v1w *= ratio
|
||||
v2w = w - v1w
|
||||
v2x = v1w
|
||||
else:
|
||||
v1w = v2w = w
|
||||
|
||||
if v1y != v2y:
|
||||
ratio = float(h) / float((v1h + v2h))
|
||||
v1h *= ratio
|
||||
v2h = h - v1h
|
||||
v2y = v1h
|
||||
else:
|
||||
v1h = v2h = h
|
||||
|
||||
self.view1.SetDimensions(v1x,v1y,v1w,v1h)
|
||||
self.view2.SetDimensions(v2x,v2y,v2w,v2h)
|
||||
self.view1.OnSize(None)
|
||||
self.view2.OnSize(None)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
class wxMultiViewLeaf(wxWindow):
|
||||
def __init__(self,multiView,parent,pos,size):
|
||||
wxWindow.__init__(self,id = -1,parent = parent,pos = pos,size = size,
|
||||
style = wxCLIP_CHILDREN)
|
||||
self.multiView = multiView
|
||||
|
||||
self.sizerHor = MultiSizer(self,MV_HOR)
|
||||
self.sizerVer = MultiSizer(self,MV_VER)
|
||||
self.creatorHor = MultiCreator(self,MV_HOR)
|
||||
self.creatorVer = MultiCreator(self,MV_VER)
|
||||
self.detail = MultiClient(self,multiView._defChild)
|
||||
self.closer = MultiCloser(self)
|
||||
|
||||
EVT_SIZE(self,self.OnSize)
|
||||
|
||||
def GetSaveData(self):
|
||||
saveData = {}
|
||||
saveData['detailClass'] = str(self.detail.child.__class__)
|
||||
if hasattr(self.detail.child,'GetSaveData'):
|
||||
attr = getattr(self.detail.child,'GetSaveData')
|
||||
if callable(attr):
|
||||
dData = attr()
|
||||
if dData:
|
||||
saveData['detail'] = dData
|
||||
v1,v2 = self.GetPositionTuple()
|
||||
saveData['x'] = v1
|
||||
saveData['y'] = v2
|
||||
v1,v2 = self.GetSizeTuple()
|
||||
saveData['w'] = v1
|
||||
saveData['h'] = v2
|
||||
return saveData
|
||||
|
||||
def SetSaveData(self,data):
|
||||
dChild = data['detailClass']
|
||||
mod = dChild.split('.')[0]
|
||||
exec 'import %s' % mod
|
||||
detClass = eval(dChild)
|
||||
self.SetDimensions(data['x'],data['y'],data['w'],data['h'])
|
||||
old = self.detail
|
||||
self.detail = MultiClient(self,detClass)
|
||||
dData = data.get('detail',None)
|
||||
if dData:
|
||||
if hasattr(self.detail.child,'SetSaveData'):
|
||||
attr = getattr(self.detail.child,'SetSaveData')
|
||||
if callable(attr):
|
||||
attr(dData)
|
||||
old.Destroy()
|
||||
self.detail.OnSize(None)
|
||||
|
||||
def UnSelect(self):
|
||||
self.detail.UnSelect()
|
||||
|
||||
def DefaultChildChanged(self):
|
||||
self.detail.SetNewChildCls(self.multiView._defChild)
|
||||
|
||||
def AddLeaf(self,direction,pos):
|
||||
if pos < 10: return
|
||||
w,h = self.GetSizeTuple()
|
||||
if direction == MV_VER:
|
||||
if pos > h - 10: return
|
||||
else:
|
||||
if pos > w - 10: return
|
||||
self.GetParent().AddLeaf(direction,self,pos)
|
||||
|
||||
def DestroyLeaf(self):
|
||||
self.GetParent().DestroyLeaf(self)
|
||||
|
||||
def SizeTarget(self,side):
|
||||
return self.GetParent().SizeTarget(side,self)
|
||||
|
||||
def CanSize(self,side):
|
||||
return self.GetParent().CanSize(side,self)
|
||||
|
||||
def OnSize(self,evt):
|
||||
self.sizerHor.OnSize(evt)
|
||||
self.sizerVer.OnSize(evt)
|
||||
self.creatorHor.OnSize(evt)
|
||||
self.creatorVer.OnSize(evt)
|
||||
self.detail.OnSize(evt)
|
||||
self.closer.OnSize(evt)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
class MultiClient(wxWindow):
|
||||
def __init__(self,parent,childCls):
|
||||
w,h = self.CalcSize(parent)
|
||||
wxWindow.__init__(self,id = -1,parent = parent,
|
||||
pos = wxPoint(0,0),
|
||||
size = wxSize(w,h),
|
||||
style = wxCLIP_CHILDREN | wxSUNKEN_BORDER)
|
||||
self.child = childCls(self)
|
||||
self.child.MoveXY(2,2)
|
||||
self.normalColour = self.GetBackgroundColour()
|
||||
self.selected = False
|
||||
|
||||
EVT_SET_FOCUS(self,self.OnSetFocus)
|
||||
EVT_CHILD_FOCUS(self,self.OnChildFocus)
|
||||
|
||||
def UnSelect(self):
|
||||
if self.selected:
|
||||
self.selected = False
|
||||
self.SetBackgroundColour(self.normalColour)
|
||||
self.Refresh()
|
||||
|
||||
def Select(self):
|
||||
self.GetParent().multiView.UnSelect()
|
||||
self.selected = True
|
||||
self.SetBackgroundColour(wxColour(255,255,0)) # Yellow
|
||||
self.Refresh()
|
||||
|
||||
def CalcSize(self,parent):
|
||||
w,h = parent.GetSizeTuple()
|
||||
w -= SH_SIZE
|
||||
h -= SH_SIZE
|
||||
return (w,h)
|
||||
|
||||
def OnSize(self,evt):
|
||||
w,h = self.CalcSize(self.GetParent())
|
||||
self.SetDimensions(0,0,w,h)
|
||||
w,h = self.GetClientSizeTuple()
|
||||
self.child.SetSize(wxSize(w-4,h-4))
|
||||
|
||||
def SetNewChildCls(self,childCls):
|
||||
if self.child:
|
||||
self.child.Destroy()
|
||||
self.child = None
|
||||
self.child = childCls(self)
|
||||
self.child.MoveXY(2,2)
|
||||
|
||||
def OnSetFocus(self,evt):
|
||||
self.Select()
|
||||
|
||||
def OnChildFocus(self,evt):
|
||||
self.OnSetFocus(evt)
|
||||
## from Funcs import FindFocusedChild
|
||||
## child = FindFocusedChild(self)
|
||||
## EVT_KILL_FOCUS(child,self.OnChildKillFocus)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
class MultiSizer(wxWindow):
|
||||
def __init__(self,parent,side):
|
||||
self.side = side
|
||||
x,y,w,h = self.CalcSizePos(parent)
|
||||
wxWindow.__init__(self,id = -1,parent = parent,
|
||||
pos = wxPoint(x,y),
|
||||
size = wxSize(w,h),
|
||||
style = wxCLIP_CHILDREN)
|
||||
|
||||
self.px = None # Previous X
|
||||
self.py = None # Previous Y
|
||||
self.isDrag = False # In Dragging
|
||||
self.dragTarget = None # View being sized
|
||||
|
||||
EVT_LEAVE_WINDOW(self,self.OnLeave)
|
||||
EVT_ENTER_WINDOW(self,self.OnEnter)
|
||||
EVT_MOTION(self,self.OnMouseMove)
|
||||
EVT_LEFT_DOWN(self,self.OnPress)
|
||||
EVT_LEFT_UP(self,self.OnRelease)
|
||||
|
||||
def CalcSizePos(self,parent):
|
||||
pw,ph = parent.GetSizeTuple()
|
||||
if self.side == MV_HOR:
|
||||
x = CR_SIZE + 2
|
||||
y = ph - SH_SIZE
|
||||
w = pw - CR_SIZE - SH_SIZE - 2
|
||||
h = SH_SIZE
|
||||
else:
|
||||
x = pw - SH_SIZE
|
||||
y = CR_SIZE + 2 + SH_SIZE
|
||||
w = SH_SIZE
|
||||
h = ph - CR_SIZE - SH_SIZE - 4 - SH_SIZE # For Closer
|
||||
return (x,y,w,h)
|
||||
|
||||
def OnSize(self,evt):
|
||||
x,y,w,h = self.CalcSizePos(self.GetParent())
|
||||
self.SetDimensions(x,y,w,h)
|
||||
|
||||
def OnLeave(self,evt):
|
||||
self.SetCursor(wxStockCursor(wxCURSOR_ARROW))
|
||||
|
||||
def OnEnter(self,evt):
|
||||
if not self.GetParent().CanSize(not self.side):
|
||||
return
|
||||
if self.side == MV_HOR:
|
||||
self.SetCursor(wxStockCursor(wxCURSOR_SIZENS))
|
||||
else:
|
||||
self.SetCursor(wxStockCursor(wxCURSOR_SIZEWE))
|
||||
|
||||
def OnMouseMove(self,evt):
|
||||
if self.isDrag:
|
||||
DrawSash(self.dragTarget,self.px,self.py,self.side)
|
||||
self.px,self.py = self.ClientToScreenXY(evt.m_x,evt.m_y)
|
||||
self.px,self.py = self.dragTarget.ScreenToClientXY(self.px,self.py)
|
||||
DrawSash(self.dragTarget,self.px,self.py,self.side)
|
||||
else:
|
||||
evt.Skip()
|
||||
|
||||
def OnPress(self,evt):
|
||||
self.dragTarget = self.GetParent().SizeTarget(not self.side)
|
||||
if self.dragTarget:
|
||||
self.isDrag = True
|
||||
self.px,self.py = self.ClientToScreenXY(evt.m_x,evt.m_y)
|
||||
self.px,self.py = self.dragTarget.ScreenToClientXY(self.px,self.py)
|
||||
DrawSash(self.dragTarget,self.px,self.py,self.side)
|
||||
self.CaptureMouse()
|
||||
else:
|
||||
evt.Skip()
|
||||
|
||||
def OnRelease(self,evt):
|
||||
if self.isDrag:
|
||||
DrawSash(self.dragTarget,self.px,self.py,self.side)
|
||||
self.ReleaseMouse()
|
||||
self.isDrag = False
|
||||
if self.side == MV_HOR:
|
||||
self.dragTarget.SizeLeaf(self.GetParent(),
|
||||
self.py,not self.side)
|
||||
else:
|
||||
self.dragTarget.SizeLeaf(self.GetParent(),
|
||||
self.px,not self.side)
|
||||
self.dragTarget = None
|
||||
else:
|
||||
evt.Skip()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
class MultiCreator(wxWindow):
|
||||
def __init__(self,parent,side):
|
||||
self.side = side
|
||||
x,y,w,h = self.CalcSizePos(parent)
|
||||
wxWindow.__init__(self,id = -1,parent = parent,
|
||||
pos = wxPoint(x,y),
|
||||
size = wxSize(w,h),
|
||||
style = wxCLIP_CHILDREN)
|
||||
|
||||
self.px = None # Previous X
|
||||
self.py = None # Previous Y
|
||||
self.isDrag = False # In Dragging
|
||||
|
||||
EVT_LEAVE_WINDOW(self,self.OnLeave)
|
||||
EVT_ENTER_WINDOW(self,self.OnEnter)
|
||||
EVT_MOTION(self,self.OnMouseMove)
|
||||
EVT_LEFT_DOWN(self,self.OnPress)
|
||||
EVT_LEFT_UP(self,self.OnRelease)
|
||||
EVT_PAINT(self,self.OnPaint)
|
||||
|
||||
def CalcSizePos(self,parent):
|
||||
pw,ph = parent.GetSizeTuple()
|
||||
if self.side == MV_HOR:
|
||||
x = 2
|
||||
y = ph - SH_SIZE
|
||||
w = CR_SIZE
|
||||
h = SH_SIZE
|
||||
else:
|
||||
x = pw - SH_SIZE
|
||||
y = 4 + SH_SIZE # Make provision for closer
|
||||
w = SH_SIZE
|
||||
h = CR_SIZE
|
||||
return (x,y,w,h)
|
||||
|
||||
def OnSize(self,evt):
|
||||
x,y,w,h = self.CalcSizePos(self.GetParent())
|
||||
self.SetDimensions(x,y,w,h)
|
||||
|
||||
def OnLeave(self,evt):
|
||||
self.SetCursor(wxStockCursor(wxCURSOR_ARROW))
|
||||
|
||||
def OnEnter(self,evt):
|
||||
if self.side == MV_HOR:
|
||||
self.SetCursor(wxStockCursor(wxCURSOR_HAND))
|
||||
else:
|
||||
self.SetCursor(wxStockCursor(wxCURSOR_POINT_LEFT))
|
||||
|
||||
def OnMouseMove(self,evt):
|
||||
if self.isDrag:
|
||||
parent = self.GetParent()
|
||||
DrawSash(parent,self.px,self.py,self.side)
|
||||
self.px,self.py = self.ClientToScreenXY(evt.m_x,evt.m_y)
|
||||
self.px,self.py = parent.ScreenToClientXY(self.px,self.py)
|
||||
DrawSash(parent,self.px,self.py,self.side)
|
||||
else:
|
||||
evt.Skip()
|
||||
|
||||
def OnPress(self,evt):
|
||||
self.isDrag = True
|
||||
parent = self.GetParent()
|
||||
self.px,self.py = self.ClientToScreenXY(evt.m_x,evt.m_y)
|
||||
self.px,self.py = parent.ScreenToClientXY(self.px,self.py)
|
||||
DrawSash(parent,self.px,self.py,self.side)
|
||||
self.CaptureMouse()
|
||||
|
||||
def OnRelease(self,evt):
|
||||
if self.isDrag:
|
||||
parent = self.GetParent()
|
||||
DrawSash(parent,self.px,self.py,self.side)
|
||||
self.ReleaseMouse()
|
||||
self.isDrag = False
|
||||
|
||||
if self.side == MV_HOR:
|
||||
parent.AddLeaf(MV_VER,self.py)
|
||||
else:
|
||||
parent.AddLeaf(MV_HOR,self.px)
|
||||
else:
|
||||
evt.Skip()
|
||||
|
||||
def OnPaint(self,evt):
|
||||
dc = wxPaintDC(self)
|
||||
dc.SetBackground(wxBrush(self.GetBackgroundColour(),wxSOLID))
|
||||
dc.Clear()
|
||||
|
||||
highlight = wxPen(wxSystemSettings_GetSystemColour(
|
||||
wxSYS_COLOUR_BTNHIGHLIGHT),1,wxSOLID)
|
||||
shadow = wxPen(wxSystemSettings_GetSystemColour(
|
||||
wxSYS_COLOUR_BTNSHADOW),1,wxSOLID)
|
||||
black = wxPen(wxBLACK,1,wxSOLID)
|
||||
w,h = self.GetSizeTuple()
|
||||
w -= 1
|
||||
h -= 1
|
||||
|
||||
# Draw outline
|
||||
dc.SetPen(highlight)
|
||||
dc.DrawLine(0,0,0,h)
|
||||
dc.DrawLine(0,0,w,0)
|
||||
dc.SetPen(black)
|
||||
dc.DrawLine(0,h,w+1,h)
|
||||
dc.DrawLine(w,0,w,h)
|
||||
dc.SetPen(shadow)
|
||||
dc.DrawLine(w-1,2,w-1,h)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
class MultiCloser(wxWindow):
|
||||
def __init__(self,parent):
|
||||
x,y,w,h = self.CalcSizePos(parent)
|
||||
wxWindow.__init__(self,id = -1,parent = parent,
|
||||
pos = wxPoint(x,y),
|
||||
size = wxSize(w,h),
|
||||
style = wxCLIP_CHILDREN)
|
||||
|
||||
self.down = False
|
||||
self.entered = False
|
||||
|
||||
EVT_LEFT_DOWN(self,self.OnPress)
|
||||
EVT_LEFT_UP(self,self.OnRelease)
|
||||
EVT_PAINT(self,self.OnPaint)
|
||||
EVT_LEAVE_WINDOW(self,self.OnLeave)
|
||||
EVT_ENTER_WINDOW(self,self.OnEnter)
|
||||
|
||||
def OnLeave(self,evt):
|
||||
self.SetCursor(wxStockCursor(wxCURSOR_ARROW))
|
||||
self.entered = False
|
||||
|
||||
def OnEnter(self,evt):
|
||||
self.SetCursor(wxStockCursor(wxCURSOR_BULLSEYE))
|
||||
self.entered = True
|
||||
|
||||
def OnPress(self,evt):
|
||||
self.down = True
|
||||
evt.Skip()
|
||||
|
||||
def OnRelease(self,evt):
|
||||
if self.down and self.entered:
|
||||
self.GetParent().DestroyLeaf()
|
||||
else:
|
||||
evt.Skip()
|
||||
self.down = False
|
||||
|
||||
def OnPaint(self,evt):
|
||||
dc = wxPaintDC(self)
|
||||
dc.SetBackground(wxBrush(wxRED,wxSOLID))
|
||||
dc.Clear()
|
||||
|
||||
def CalcSizePos(self,parent):
|
||||
pw,ph = parent.GetSizeTuple()
|
||||
x = pw - SH_SIZE
|
||||
w = SH_SIZE
|
||||
h = SH_SIZE + 2
|
||||
y = 1
|
||||
return (x,y,w,h)
|
||||
|
||||
def OnSize(self,evt):
|
||||
x,y,w,h = self.CalcSizePos(self.GetParent())
|
||||
self.SetDimensions(x,y,w,h)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
class EmptyChild(wxWindow):
|
||||
def __init__(self,parent):
|
||||
wxWindow.__init__(self,parent,-1, style = wxCLIP_CHILDREN)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
def DrawSash(win,x,y,direction):
|
||||
dc = wxScreenDC()
|
||||
dc.StartDrawingOnTopWin(win)
|
||||
bmp = wxEmptyBitmap(8,8)
|
||||
bdc = wxMemoryDC()
|
||||
bdc.SelectObject(bmp)
|
||||
bdc.DrawRectangle(-1,-1,10,10)
|
||||
for i in range(8):
|
||||
for j in range(8):
|
||||
if ((i + j) & 1):
|
||||
bdc.DrawPoint(i,j)
|
||||
|
||||
brush = wxBrush(wxColour(0,0,0))
|
||||
brush.SetStipple(bmp)
|
||||
|
||||
dc.SetBrush(brush)
|
||||
dc.SetLogicalFunction(wxXOR)
|
||||
|
||||
body_w,body_h = win.GetClientSizeTuple()
|
||||
|
||||
if y < 0:
|
||||
y = 0
|
||||
if y > body_h:
|
||||
y = body_h
|
||||
if x < 0:
|
||||
x = 0
|
||||
if x > body_w:
|
||||
x = body_w
|
||||
|
||||
if direction == MV_HOR:
|
||||
x = 0
|
||||
else:
|
||||
y = 0
|
||||
|
||||
x,y = win.ClientToScreenXY(x,y)
|
||||
|
||||
w = body_w
|
||||
h = body_h
|
||||
|
||||
if direction == MV_HOR:
|
||||
dc.DrawRectangle(x,y-2,w,4)
|
||||
else:
|
||||
dc.DrawRectangle(x-2,y,4,h)
|
||||
|
||||
dc.EndDrawingOnTop()
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.multisash
|
||||
|
||||
__doc__ = wx.lib.multisash.__doc__
|
||||
|
||||
EmptyChild = wx.lib.multisash.EmptyChild
|
||||
MultiClient = wx.lib.multisash.MultiClient
|
||||
MultiCloser = wx.lib.multisash.MultiCloser
|
||||
MultiCreator = wx.lib.multisash.MultiCreator
|
||||
MultiSizer = wx.lib.multisash.MultiSizer
|
||||
wxMultiSash = wx.lib.multisash.wxMultiSash
|
||||
wxMultiSplit = wx.lib.multisash.wxMultiSplit
|
||||
wxMultiViewLeaf = wx.lib.multisash.wxMultiViewLeaf
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,122 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
"""Easy generation of new events classes and binder functions"""
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
__author__ = "Miki Tebeka <tebeka@cs.bgu.ac.il>"
|
||||
import wx.lib.newevent
|
||||
|
||||
import wx
|
||||
__doc__ = wx.lib.newevent.__doc__
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def NewEvent():
|
||||
"""Generate new (Event, Binder) tuple
|
||||
e.g. MooEvent, EVT_MOO = NewEvent()
|
||||
"""
|
||||
evttype = wx.NewEventType()
|
||||
|
||||
class _Event(wx.PyEvent):
|
||||
def __init__(self, **kw):
|
||||
wx.PyEvent.__init__(self)
|
||||
self.SetEventType(evttype)
|
||||
self.__dict__.update(kw)
|
||||
|
||||
def Binder(win, func):
|
||||
win.Connect(-1, -1, evttype, func)
|
||||
|
||||
return _Event, Binder
|
||||
|
||||
|
||||
|
||||
def NewCommandEvent():
|
||||
"""Generate new (CmdEvent, Binder) tuple
|
||||
e.g. MooCmdEvent, EVT_MOO = NewCommandEvent()
|
||||
"""
|
||||
evttype = wx.NewEventType()
|
||||
|
||||
class _Event(wx.PyCommandEvent):
|
||||
def __init__(self, id, **kw):
|
||||
wx.PyCommandEvent.__init__(self, evttype, id)
|
||||
self.__dict__.update(kw)
|
||||
|
||||
def Binder(win, id, func):
|
||||
win.Connect(id, -1, evttype, func)
|
||||
|
||||
return _Event, Binder
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def _test():
|
||||
"""A little smoke test"""
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
|
||||
MooEvent, EVT_MOO = NewEvent()
|
||||
GooEvent, EVT_GOO = NewCommandEvent()
|
||||
|
||||
DELAY = 0.7
|
||||
|
||||
def evt_thr(win):
|
||||
sleep(DELAY)
|
||||
wx.PostEvent(win, MooEvent(moo=1))
|
||||
|
||||
def cmd_thr(win, id):
|
||||
sleep(DELAY)
|
||||
wx.PostEvent(win, GooEvent(id, goo=id))
|
||||
|
||||
ID_CMD1 = wx.NewId()
|
||||
ID_CMD2 = wx.NewId()
|
||||
|
||||
class Frame(wx.Frame):
|
||||
def __init__(self):
|
||||
wx.Frame.__init__(self, None, -1, "MOO")
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
EVT_MOO(self, self.on_moo)
|
||||
b = wx.Button(self, -1, "Generate MOO")
|
||||
sizer.Add(b, 1, wx.EXPAND)
|
||||
wx.EVT_BUTTON(self, b.GetId(), self.on_evt_click)
|
||||
b = wx.Button(self, ID_CMD1, "Generate GOO with %d" % ID_CMD1)
|
||||
sizer.Add(b, 1, wx.EXPAND)
|
||||
wx.EVT_BUTTON(self, ID_CMD1, self.on_cmd_click)
|
||||
b = wx.Button(self, ID_CMD2, "Generate GOO with %d" % ID_CMD2)
|
||||
sizer.Add(b, 1, wx.EXPAND)
|
||||
wx.EVT_BUTTON(self, ID_CMD2, self.on_cmd_click)
|
||||
|
||||
EVT_GOO(self, ID_CMD1, self.on_cmd1)
|
||||
EVT_GOO(self, ID_CMD2, self.on_cmd2)
|
||||
|
||||
self.SetSizer(sizer)
|
||||
self.SetAutoLayout(True)
|
||||
sizer.Fit(self)
|
||||
|
||||
def on_evt_click(self, e):
|
||||
t = Thread(target=evt_thr, args=(self, ))
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
def on_cmd_click(self, e):
|
||||
t = Thread(target=cmd_thr, args=(self, e.GetId()))
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
def show(self, msg, title):
|
||||
dlg = wx.MessageDialog(self, msg, title, wx.OK)
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
|
||||
def on_moo(self, e):
|
||||
self.show("MOO = %s" % e.moo, "Got Moo")
|
||||
|
||||
def on_cmd1(self, e):
|
||||
self.show("goo = %s" % e.goo, "Got Goo (cmd1)")
|
||||
|
||||
def on_cmd2(self, e):
|
||||
self.show("goo = %s" % e.goo, "Got Goo (cmd2)")
|
||||
|
||||
|
||||
app = wx.PySimpleApp()
|
||||
f = Frame()
|
||||
f.Show(True)
|
||||
app.MainLoop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
_test()
|
||||
NewCommandEvent = wx.lib.newevent.NewCommandEvent
|
||||
NewEvent = wx.lib.newevent.NewEvent
|
||||
_test = wx.lib.newevent._test
|
||||
|
||||
@@ -1,249 +1,12 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: popup
|
||||
# Purpose: Generic popup control
|
||||
#
|
||||
# Author: Gerrit van Dyk
|
||||
#
|
||||
# Created: 2002/11/20
|
||||
# Version: 0.1
|
||||
# RCS-ID: $Id$
|
||||
# License: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython.wx import *
|
||||
from wxPython.lib.buttons import wxGenButtonEvent
|
||||
import wx.lib.popupctl
|
||||
|
||||
__doc__ = wx.lib.popupctl.__doc__
|
||||
|
||||
class PopButton(wxPyControl):
|
||||
def __init__(self,*_args,**_kwargs):
|
||||
apply(wxPyControl.__init__,(self,) + _args,_kwargs)
|
||||
|
||||
self.up = True
|
||||
self.didDown = False
|
||||
|
||||
self.InitColours()
|
||||
|
||||
EVT_LEFT_DOWN(self, self.OnLeftDown)
|
||||
EVT_LEFT_UP(self, self.OnLeftUp)
|
||||
EVT_MOTION(self, self.OnMotion)
|
||||
EVT_PAINT(self, self.OnPaint)
|
||||
|
||||
def InitColours(self):
|
||||
faceClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNFACE)
|
||||
self.faceDnClr = faceClr
|
||||
self.SetBackgroundColour(faceClr)
|
||||
|
||||
shadowClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNSHADOW)
|
||||
highlightClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNHIGHLIGHT)
|
||||
self.shadowPen = wxPen(shadowClr, 1, wxSOLID)
|
||||
self.highlightPen = wxPen(highlightClr, 1, wxSOLID)
|
||||
self.blackPen = wxPen(wxBLACK, 1, wxSOLID)
|
||||
|
||||
def Notify(self):
|
||||
evt = wxGenButtonEvent(wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
|
||||
evt.SetIsDown(not self.up)
|
||||
evt.SetButtonObj(self)
|
||||
evt.SetEventObject(self)
|
||||
self.GetEventHandler().ProcessEvent(evt)
|
||||
|
||||
def OnEraseBackground(self, event):
|
||||
pass
|
||||
|
||||
def OnLeftDown(self, event):
|
||||
if not self.IsEnabled():
|
||||
return
|
||||
self.didDown = True
|
||||
self.up = False
|
||||
self.CaptureMouse()
|
||||
self.GetParent().textCtrl.SetFocus()
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
|
||||
def OnLeftUp(self, event):
|
||||
if not self.IsEnabled():
|
||||
return
|
||||
if self.didDown:
|
||||
self.ReleaseMouse()
|
||||
if not self.up:
|
||||
self.Notify()
|
||||
self.up = True
|
||||
self.Refresh()
|
||||
self.didDown = False
|
||||
event.Skip()
|
||||
|
||||
def OnMotion(self, event):
|
||||
if not self.IsEnabled():
|
||||
return
|
||||
if event.LeftIsDown():
|
||||
if self.didDown:
|
||||
x,y = event.GetPositionTuple()
|
||||
w,h = self.GetClientSizeTuple()
|
||||
if self.up and x<w and x>=0 and y<h and y>=0:
|
||||
self.up = False
|
||||
self.Refresh()
|
||||
return
|
||||
if not self.up and (x<0 or y<0 or x>=w or y>=h):
|
||||
self.up = True
|
||||
self.Refresh()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
def DrawBezel(self, dc, x1, y1, x2, y2):
|
||||
# draw the upper left sides
|
||||
if self.up:
|
||||
dc.SetPen(self.highlightPen)
|
||||
else:
|
||||
dc.SetPen(self.shadowPen)
|
||||
for i in range(2):
|
||||
dc.DrawLine(x1+i, y1, x1+i, y2-i)
|
||||
dc.DrawLine(x1, y1+i, x2-i, y1+i)
|
||||
|
||||
# draw the lower right sides
|
||||
if self.up:
|
||||
dc.SetPen(self.shadowPen)
|
||||
else:
|
||||
dc.SetPen(self.highlightPen)
|
||||
for i in range(2):
|
||||
dc.DrawLine(x1+i, y2-i, x2+1, y2-i)
|
||||
dc.DrawLine(x2-i, y1+i, x2-i, y2)
|
||||
|
||||
def DrawArrow(self,dc):
|
||||
size = self.GetSize()
|
||||
mx = size.x / 2
|
||||
my = size.y / 2
|
||||
dc.SetPen(self.highlightPen)
|
||||
dc.DrawLine(mx-5,my-5,mx+5,my-5)
|
||||
dc.DrawLine(mx-5,my-5,mx,my+5)
|
||||
dc.SetPen(self.shadowPen)
|
||||
dc.DrawLine(mx+4,my-5,mx,my+5)
|
||||
dc.SetPen(self.blackPen)
|
||||
dc.DrawLine(mx+5,my-5,mx,my+5)
|
||||
|
||||
def OnPaint(self, event):
|
||||
width, height = self.GetClientSizeTuple()
|
||||
x1 = y1 = 0
|
||||
x2 = width - 1
|
||||
y2 = height - 1
|
||||
dc = wxBufferedPaintDC(self)
|
||||
if self.up:
|
||||
dc.SetBackground(wxBrush(self.GetBackgroundColour(), wxSOLID))
|
||||
else:
|
||||
dc.SetBackground(wxBrush(self.faceDnClr, wxSOLID))
|
||||
dc.Clear()
|
||||
self.DrawBezel(dc, x1, y1, x2, y2)
|
||||
self.DrawArrow(dc)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# Tried to use wxPopupWindow but the control misbehaves on MSW
|
||||
class wxPopupDialog(wxDialog):
|
||||
def __init__(self,parent,content = None):
|
||||
wxDialog.__init__(self,parent,-1,'', style = wxBORDER_SIMPLE|wxSTAY_ON_TOP)
|
||||
|
||||
self.ctrl = parent
|
||||
self.win = wxWindow(self,-1,pos = wxPoint(0,0),style = 0)
|
||||
|
||||
if content:
|
||||
self.SetContent(content)
|
||||
|
||||
def SetContent(self,content):
|
||||
self.content = content
|
||||
self.content.Reparent(self.win)
|
||||
self.content.Show(True)
|
||||
self.win.SetClientSize(self.content.GetSize())
|
||||
self.SetSize(self.win.GetSize())
|
||||
|
||||
def Display(self):
|
||||
pos = self.ctrl.ClientToScreen( (0,0) )
|
||||
dSize = wxGetDisplaySize()
|
||||
selfSize = self.GetSize()
|
||||
tcSize = self.ctrl.GetSize()
|
||||
|
||||
pos.x -= (selfSize.x - tcSize.x) / 2
|
||||
if pos.x + selfSize.x > dSize.x:
|
||||
pos.x = dSize.x - selfSize.x
|
||||
if pos.x < 0:
|
||||
pos.x = 0
|
||||
|
||||
pos.y += tcSize.height
|
||||
if pos.y + selfSize.y > dSize.y:
|
||||
pos.y = dSize.y - selfSize.y
|
||||
if pos.y < 0:
|
||||
pos.y = 0
|
||||
|
||||
self.MoveXY(pos.x,pos.y)
|
||||
|
||||
self.ctrl.FormatContent()
|
||||
|
||||
self.ShowModal()
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class wxPopupControl(wxPyControl):
|
||||
def __init__(self,*_args,**_kwargs):
|
||||
if _kwargs.has_key('value'):
|
||||
del _kwargs['value']
|
||||
apply(wxPyControl.__init__,(self,) + _args,_kwargs)
|
||||
|
||||
self.textCtrl = wxTextCtrl(self,-1,'',pos = wxPoint(0,0))
|
||||
self.bCtrl = PopButton(self,-1)
|
||||
self.pop = None
|
||||
self.content = None
|
||||
self.OnSize(None)
|
||||
|
||||
EVT_SIZE(self,self.OnSize)
|
||||
EVT_BUTTON(self.bCtrl,self.bCtrl.GetId(),self.OnButton)
|
||||
# embedded control should get focus on TAB keypress
|
||||
EVT_SET_FOCUS(self,self.OnFocus)
|
||||
|
||||
def OnFocus(self,evt):
|
||||
self.textCtrl.SetFocus()
|
||||
evt.Skip()
|
||||
|
||||
def OnSize(self,evt):
|
||||
w,h = self.GetClientSizeTuple()
|
||||
self.textCtrl.SetDimensions(0,0,w-17,h)
|
||||
self.bCtrl.SetDimensions(w-17,0,17,h)
|
||||
|
||||
def OnButton(self,evt):
|
||||
if not self.pop:
|
||||
if self.content:
|
||||
self.pop = wxPopupDialog(self,self.content)
|
||||
del self.content
|
||||
else:
|
||||
print 'No Content to pop'
|
||||
if self.pop:
|
||||
self.pop.Display()
|
||||
|
||||
def Enable(self,flag):
|
||||
wxPyControl.Enable(self,flag)
|
||||
self.textCtrl.Enable(flag)
|
||||
self.bCtrl.Enable(flag)
|
||||
|
||||
def SetPopupContent(self,content):
|
||||
if not self.pop:
|
||||
self.content = content
|
||||
self.content.Show(False)
|
||||
else:
|
||||
self.pop.SetContent(content)
|
||||
|
||||
def FormatContent(self):
|
||||
pass
|
||||
|
||||
def PopDown(self):
|
||||
if self.pop:
|
||||
self.pop.EndModal(1)
|
||||
|
||||
def SetValue(self,value):
|
||||
self.textCtrl.SetValue(value)
|
||||
|
||||
def GetValue(self):
|
||||
return self.textCtrl.GetValue()
|
||||
|
||||
|
||||
# an alias
|
||||
wxPopupCtrl = wxPopupControl
|
||||
PopButton = wx.lib.popupctl.PopButton
|
||||
wxPopupControl = wx.lib.popupctl.wxPopupControl
|
||||
wxPopupCtrl = wx.lib.popupctl.wxPopupCtrl
|
||||
wxPopupDialog = wx.lib.popupctl.wxPopupDialog
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,382 +1,10 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: wxPython.lib.pubsub
|
||||
# Purpose: The Publish/Subscribe framework used by evtmgr.EventManager
|
||||
#
|
||||
# Author: Robb Shecter and Robin Dunn
|
||||
#
|
||||
# Created: 12-December-2002
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2002 by db-X Corporation
|
||||
# Licence: wxWindows license
|
||||
#---------------------------------------------------------------------------
|
||||
"""
|
||||
This module has classes for implementing the Publish/Subscribe design
|
||||
pattern.
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
It's a very flexible PS implementation: The message topics are tuples
|
||||
of any length, containing any objects (that can be used as hash keys).
|
||||
A subscriber's topic matches any message topic for which it's a
|
||||
sublist.
|
||||
import wx.lib.pubsub
|
||||
|
||||
It also has many optimizations to favor time efficiency (ie., run-time
|
||||
speed). I did this because I use it to support extreme uses. For
|
||||
example, piping every wxWindows mouse event through to multiple
|
||||
listeners, and expecting the app to have no noticeable slowdown. This
|
||||
has made the code somewhat obfuscated, but I've done my best to
|
||||
document it.
|
||||
__doc__ = wx.lib.pubsub.__doc__
|
||||
|
||||
The Server and Message classes are the two that clients interact
|
||||
with..
|
||||
|
||||
This module is compatible with Python 2.1.
|
||||
|
||||
Author: Robb Shecter
|
||||
"""
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class Publisher:
|
||||
"""
|
||||
The publish/subscribe server. This class is a Singleton.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.topicDict = {}
|
||||
self.functionDict = {}
|
||||
self.subscribeAllList = []
|
||||
self.messageCount = 0
|
||||
self.deliveryCount = 0
|
||||
|
||||
|
||||
#
|
||||
# Public API
|
||||
#
|
||||
|
||||
def subscribe(self, topic, listener):
|
||||
"""
|
||||
Add the given subscription to the list. This will
|
||||
add an entry recording the fact that the listener wants
|
||||
to get messages for (at least) the given topic. This
|
||||
method may be called multiple times for one listener,
|
||||
registering it with many topics. It can also be invoked
|
||||
many times for a particular topic, each time with a
|
||||
different listener.
|
||||
|
||||
listener: expected to be either a method or function that
|
||||
takes zero or one parameters. (Not counting 'self' in the
|
||||
case of methods. If it accepts a parameter, it will be given
|
||||
a reference to a Message object.
|
||||
|
||||
topic: will be converted to a tuple if it isn't one.
|
||||
It's a pattern matches any topic that it's a sublist
|
||||
of. For example, this pattern:
|
||||
|
||||
('sports',)
|
||||
|
||||
would match these:
|
||||
|
||||
('sports',)
|
||||
('sports', 'baseball')
|
||||
('sports', 'baseball', 'highscores')
|
||||
|
||||
but not these:
|
||||
|
||||
()
|
||||
('news')
|
||||
(12345)
|
||||
"""
|
||||
if not callable(listener):
|
||||
raise TypeError('The P/S listener, '+`listener`+', is not callable.')
|
||||
aTopic = Topic(topic)
|
||||
|
||||
# Determine now (at registration time) how many parameters
|
||||
# the listener expects, and get a reference to a function which
|
||||
# calls it correctly at message-send time.
|
||||
callableVersion = self.__makeCallable(listener)
|
||||
|
||||
# Add this tuple to a list which is in a dict keyed by
|
||||
# the topic's first element.
|
||||
self.__addTopicToCorrectList(aTopic, listener, callableVersion)
|
||||
|
||||
# Add to a dict in order to speed-up unsubscribing.
|
||||
self.__addFunctionLookup(listener, aTopic)
|
||||
|
||||
|
||||
def unsubscribe(self, listener):
|
||||
"""
|
||||
Remove the given listener from the registry,
|
||||
for all topics that it's associated with.
|
||||
"""
|
||||
if not callable(listener):
|
||||
raise TypeError('The P/S listener, '+`listener`+', is not callable.')
|
||||
topicList = self.getAssociatedTopics(listener)
|
||||
for aTopic in topicList:
|
||||
subscriberList = self.__getTopicList(aTopic)
|
||||
listToKeep = []
|
||||
for subscriber in subscriberList:
|
||||
if subscriber[0] != listener:
|
||||
listToKeep.append(subscriber)
|
||||
self.__setTopicList(aTopic, listToKeep)
|
||||
self.__delFunctionLookup(listener)
|
||||
|
||||
|
||||
def getAssociatedTopics(self, listener):
|
||||
"""
|
||||
Return a list of topics the given listener is
|
||||
registered with.
|
||||
"""
|
||||
return self.functionDict.get(listener, [])
|
||||
|
||||
|
||||
def sendMessage(self, topic, data=None):
|
||||
"""
|
||||
Relay a message to registered listeners.
|
||||
"""
|
||||
aTopic = Topic(topic)
|
||||
message = Message(aTopic.items, data)
|
||||
topicList = self.__getTopicList(aTopic)
|
||||
|
||||
# Send to the matching topics
|
||||
for subscriber in topicList:
|
||||
if subscriber[1].matches(aTopic):
|
||||
subscriber[2](message)
|
||||
|
||||
# Send to any listeners registered for ALL
|
||||
for subscriber in self.subscribeAllList:
|
||||
subscriber[2](message)
|
||||
|
||||
|
||||
#
|
||||
# Private methods
|
||||
#
|
||||
|
||||
def __makeCallable(self, function):
|
||||
"""
|
||||
Return a function that is what the server
|
||||
will actually call.
|
||||
|
||||
This is a time optimization: this removes a test
|
||||
for the number of parameters from the inner loop
|
||||
of sendMessage().
|
||||
"""
|
||||
parameters = self.__parameterCount(function)
|
||||
if parameters == 0:
|
||||
# Return a function that calls the listener
|
||||
# with no arguments.
|
||||
return lambda m, f=function: f()
|
||||
elif parameters == 1:
|
||||
# Return a function that calls the listener
|
||||
# with one argument (which will be the message).
|
||||
return lambda m, f=function: f(m)
|
||||
else:
|
||||
raise TypeError('The publish/subscribe listener, '+`function`+', has wrong parameter count')
|
||||
|
||||
|
||||
def __parameterCount(self, callableObject):
|
||||
"""
|
||||
Return the effective number of parameters required
|
||||
by the callable object. In other words, the 'self'
|
||||
parameter of methods is not counted.
|
||||
"""
|
||||
try:
|
||||
# Try to handle this like a method
|
||||
return callableObject.im_func.func_code.co_argcount - 1
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
# Try to handle this like a function
|
||||
return callableObject.func_code.co_argcount
|
||||
except AttributeError:
|
||||
raise 'Cannot determine if this is a method or function: '+str(callableObject)
|
||||
|
||||
def __addFunctionLookup(self, aFunction, aTopic):
|
||||
try:
|
||||
aList = self.functionDict[aFunction]
|
||||
except KeyError:
|
||||
aList = []
|
||||
self.functionDict[aFunction] = aList
|
||||
aList.append(aTopic)
|
||||
|
||||
|
||||
def __delFunctionLookup(self, aFunction):
|
||||
try:
|
||||
del self.functionDict[aFunction]
|
||||
except KeyError:
|
||||
print 'Warning: listener not found. Logic error in PublishSubscribe?', aFunction
|
||||
|
||||
|
||||
def __addTopicToCorrectList(self, topic, listener, callableVersion):
|
||||
if len(topic.items) == 0:
|
||||
self.subscribeAllList.append((listener, topic, callableVersion))
|
||||
else:
|
||||
self.__getTopicList(topic).append((listener, topic, callableVersion))
|
||||
|
||||
|
||||
def __getTopicList(self, aTopic):
|
||||
"""
|
||||
Return the correct sublist of subscribers based on the
|
||||
given topic.
|
||||
"""
|
||||
try:
|
||||
elementZero = aTopic.items[0]
|
||||
except IndexError:
|
||||
return self.subscribeAllList
|
||||
|
||||
try:
|
||||
subList = self.topicDict[elementZero]
|
||||
except KeyError:
|
||||
subList = []
|
||||
self.topicDict[elementZero] = subList
|
||||
return subList
|
||||
|
||||
|
||||
def __setTopicList(self, aTopic, aSubscriberList):
|
||||
try:
|
||||
self.topicDict[aTopic.items[0]] = aSubscriberList
|
||||
except IndexError:
|
||||
self.subscribeAllList = aSubscriberList
|
||||
|
||||
|
||||
def __call__(self):
|
||||
return self
|
||||
|
||||
|
||||
# Create an instance with the same name as the class, effectivly
|
||||
# hiding the class object so it can't be instantiated any more. From
|
||||
# this point forward any calls to Publisher() will invoke the __call__
|
||||
# of this instance which just returns itself.
|
||||
#
|
||||
# The only flaw with this approach is that you can't derive a new
|
||||
# class from Publisher without jumping through hoops. If this ever
|
||||
# becomes an issue then a new Singleton implementaion will need to be
|
||||
# employed.
|
||||
Publisher = Publisher()
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class Message:
|
||||
"""
|
||||
A simple container object for the two components of
|
||||
a message; the topic and the data.
|
||||
"""
|
||||
def __init__(self, topic, data):
|
||||
self.topic = topic
|
||||
self.data = data
|
||||
|
||||
def __str__(self):
|
||||
return '[Topic: '+`self.topic`+', Data: '+`self.data`+']'
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class Topic:
|
||||
"""
|
||||
A class that represents a publish/subscribe topic.
|
||||
Currently, it's only used internally in the framework; the
|
||||
API expects and returns plain old tuples.
|
||||
|
||||
It currently exists mostly as a place to keep the matches()
|
||||
function. This function, though, could also correctly be
|
||||
seen as an attribute of the P/S server. Getting rid of this
|
||||
class would also mean one fewer object instantiation per
|
||||
message send.
|
||||
"""
|
||||
|
||||
listType = type([])
|
||||
tupleType = type(())
|
||||
|
||||
def __init__(self, items):
|
||||
# Make sure we have a tuple.
|
||||
if type(items) == self.__class__.listType:
|
||||
items = tuple(items)
|
||||
elif type(items) != self.__class__.tupleType:
|
||||
items = (items,)
|
||||
self.items = items
|
||||
self.length = len(items)
|
||||
|
||||
|
||||
def matches(self, aTopic):
|
||||
"""
|
||||
Consider myself to be a topic pattern,
|
||||
and return True if I match the given specific
|
||||
topic. For example,
|
||||
a = ('sports')
|
||||
b = ('sports','baseball')
|
||||
a.matches(b) --> 1
|
||||
b.matches(a) --> 0
|
||||
"""
|
||||
# The question this method answers is equivalent to;
|
||||
# is my list a sublist of aTopic's? So, my algorithm
|
||||
# is: 1) make a copy of the aTopic list which is
|
||||
# truncated to the pattern's length. 2) Test for
|
||||
# equality.
|
||||
#
|
||||
# This algorithm may be somewhat memory-intensive,
|
||||
# because it creates a temporary list on each
|
||||
# call to match. A possible to-do would be to
|
||||
# re-write this with a hand-coded loop.
|
||||
return (self.items == aTopic.items[:self.length])
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
import string
|
||||
return '<Topic>' + string.join(map(repr, self.items), ', ') + '</Topic>'
|
||||
|
||||
|
||||
def __eq__(self, aTopic):
|
||||
"""
|
||||
Return True if I equal the given topic. We're considered
|
||||
equal if our tuples are equal.
|
||||
"""
|
||||
if type(self) != type(aTopic):
|
||||
return 0
|
||||
else:
|
||||
return self.items == aTopic.items
|
||||
|
||||
|
||||
def __ne__(self, aTopic):
|
||||
"""
|
||||
Return False if I equal the given topic.
|
||||
"""
|
||||
return not self == aTopic
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
|
||||
#
|
||||
# Code for a simple command-line test
|
||||
#
|
||||
if __name__ == '__main__':
|
||||
|
||||
class SimpleListener:
|
||||
def __init__(self, number):
|
||||
self.number = number
|
||||
def notify(self, message):
|
||||
print '#'+str(self.number)+' got the message:', message
|
||||
|
||||
# Build a list of ten listeners.
|
||||
lList = []
|
||||
for x in range(10):
|
||||
lList.append(SimpleListener(x))
|
||||
|
||||
server = Publisher()
|
||||
|
||||
# Everyone's interested in politics...
|
||||
for x in lList:
|
||||
Publisher().subscribe(topic='politics', listener=x.notify) # also tests singleton
|
||||
|
||||
# But only the first four are interested in trivia.
|
||||
for x in lList[:4]:
|
||||
server.subscribe(topic='trivia', listener=x.notify)
|
||||
|
||||
# This one subscribes to everything.
|
||||
everythingListener = SimpleListener(999)
|
||||
server.subscribe(topic=(), listener=everythingListener.notify)
|
||||
|
||||
# Now send out two messages, testing topic matching.
|
||||
server.sendMessage(topic='trivia', data='What is the capitol of Oregon?')
|
||||
server.sendMessage(topic=('politics','germany'), data='The Greens have picked up another seat in the Bundestag.')
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
Message = wx.lib.pubsub.Message
|
||||
Topic = wx.lib.pubsub.Topic
|
||||
|
||||
@@ -1,329 +1,10 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.pyshell
|
||||
# Purpose: A Python Interactive Interpreter running in a wxStyledTextCtrl
|
||||
# window.
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 7-July-2000
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2000 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
"""
|
||||
PyShellWindow is a class that provides an Interactive Interpreter running
|
||||
inside a wxStyledTextCtrl, similar to the Python shell windows found in
|
||||
IDLE and PythonWin.
|
||||
|
||||
There is still much to be done to improve this class, such as line
|
||||
buffering/recall, autoindent, calltips, autocomplete, fixing the colourizer,
|
||||
etc... But it's a good start.
|
||||
|
||||
|
||||
8-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent
|
||||
PyCrust package instead.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from wxPython.wx import *
|
||||
from wxPython.stc import *
|
||||
|
||||
import sys, keyword
|
||||
from code import InteractiveInterpreter
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# default styles, etc. to use for the STC
|
||||
|
||||
if wxPlatform == '__WXMSW__':
|
||||
_defaultSize = 8
|
||||
else:
|
||||
_defaultSize = 10
|
||||
|
||||
|
||||
_default_properties = {
|
||||
'selMargin' : 0,
|
||||
'marginWidth' : 1,
|
||||
'ps1' : '>>> ',
|
||||
'stdout' : 'fore:#0000FF',
|
||||
'stderr' : 'fore:#007f00',
|
||||
'trace' : 'fore:#FF0000',
|
||||
|
||||
'default' : 'size:%d' % _defaultSize,
|
||||
'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
|
||||
'bracebad' : 'fore:#000000,back:#FF0000,bold',
|
||||
|
||||
# properties for the various Python lexer styles
|
||||
'comment' : 'fore:#007F00',
|
||||
'number' : 'fore:#007F7F',
|
||||
'string' : 'fore:#7F007F,italic',
|
||||
'char' : 'fore:#7F007F,italic',
|
||||
'keyword' : 'fore:#00007F,bold',
|
||||
'triple' : 'fore:#7F0000',
|
||||
'tripledouble': 'fore:#7F0000',
|
||||
'class' : 'fore:#0000FF,bold,underline',
|
||||
'def' : 'fore:#007F7F,bold',
|
||||
'operator' : 'bold',
|
||||
|
||||
}
|
||||
|
||||
|
||||
# new style numbers
|
||||
_stdout_style = 15
|
||||
_stderr_style = 16
|
||||
_trace_style = 17
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class PyShellWindow(wxStyledTextCtrl, InteractiveInterpreter):
|
||||
def __init__(self, parent, ID, pos=wxDefaultPosition,
|
||||
size=wxDefaultSize, style=0,
|
||||
locals=None, properties=None, banner=None):
|
||||
wxStyledTextCtrl.__init__(self, parent, ID, pos, size, style)
|
||||
InteractiveInterpreter.__init__(self, locals)
|
||||
|
||||
self.lastPromptPos = 0
|
||||
|
||||
# the line cache is used to cycle through previous commands
|
||||
self.lines = []
|
||||
self.lastUsedLine = self.curLine = 0
|
||||
|
||||
# set defaults and then deal with any user defined properties
|
||||
self.props = {}
|
||||
self.props.update(_default_properties)
|
||||
if properties:
|
||||
self.props.update(properties)
|
||||
self.UpdateProperties()
|
||||
|
||||
# copyright/banner message
|
||||
if banner is None:
|
||||
self.write("Python %s on %s\n" % #%s\n(%s)\n" %
|
||||
(sys.version, sys.platform,
|
||||
#sys.copyright, self.__class__.__name__
|
||||
))
|
||||
else:
|
||||
self.write("%s\n" % banner)
|
||||
|
||||
# write the initial prompt
|
||||
self.Prompt()
|
||||
|
||||
# Event handlers
|
||||
EVT_KEY_DOWN(self, self.OnKey)
|
||||
EVT_STC_UPDATEUI(self, ID, self.OnUpdateUI)
|
||||
#EVT_STC_STYLENEEDED(self, ID, self.OnStyle)
|
||||
|
||||
|
||||
def GetLocals(self): return self.locals
|
||||
def SetLocals(self, locals): self.locals = locals
|
||||
|
||||
def GetProperties(self): return self.props
|
||||
def SetProperties(self, properties):
|
||||
self.props.update(properties)
|
||||
self.UpdateProperties()
|
||||
|
||||
|
||||
def UpdateProperties(self):
|
||||
"""
|
||||
Reset the editor and other settings based on the contents of the
|
||||
current properties dictionary.
|
||||
"""
|
||||
p = self.props
|
||||
|
||||
#self.SetEdgeMode(wxSTC_EDGE_LINE)
|
||||
#self.SetEdgeColumn(80)
|
||||
|
||||
|
||||
# set the selection margin and window margin
|
||||
self.SetMarginWidth(1, p['selMargin'])
|
||||
self.SetMargins(p['marginWidth'], p['marginWidth'])
|
||||
|
||||
# styles
|
||||
self.StyleSetSpec(wxSTC_STYLE_DEFAULT, p['default'])
|
||||
self.StyleClearAll()
|
||||
self.StyleSetSpec(_stdout_style, p['stdout'])
|
||||
self.StyleSetSpec(_stderr_style, p['stderr'])
|
||||
self.StyleSetSpec(_trace_style, p['trace'])
|
||||
|
||||
self.StyleSetSpec(wxSTC_STYLE_BRACELIGHT, p['bracegood'])
|
||||
self.StyleSetSpec(wxSTC_STYLE_BRACEBAD, p['bracebad'])
|
||||
self.StyleSetSpec(wxSTC_P_COMMENTLINE, p['comment'])
|
||||
self.StyleSetSpec(wxSTC_P_NUMBER, p['number'])
|
||||
self.StyleSetSpec(wxSTC_P_STRING, p['string'])
|
||||
self.StyleSetSpec(wxSTC_P_CHARACTER, p['char'])
|
||||
self.StyleSetSpec(wxSTC_P_WORD, p['keyword'])
|
||||
self.StyleSetSpec(wxSTC_P_TRIPLE, p['triple'])
|
||||
self.StyleSetSpec(wxSTC_P_TRIPLEDOUBLE, p['tripledouble'])
|
||||
self.StyleSetSpec(wxSTC_P_CLASSNAME, p['class'])
|
||||
self.StyleSetSpec(wxSTC_P_DEFNAME, p['def'])
|
||||
self.StyleSetSpec(wxSTC_P_OPERATOR, p['operator'])
|
||||
self.StyleSetSpec(wxSTC_P_COMMENTBLOCK, p['comment'])
|
||||
|
||||
|
||||
# used for writing to stdout, etc.
|
||||
def _write(self, text, style=_stdout_style):
|
||||
self.lastPromptPos = 0
|
||||
pos = self.GetCurrentPos()
|
||||
self.AddText(text)
|
||||
self.StartStyling(pos, 0xFF)
|
||||
self.SetStyling(len(text), style)
|
||||
self.EnsureCaretVisible()
|
||||
wxYield()
|
||||
|
||||
write = _write
|
||||
|
||||
def writeTrace(self, text):
|
||||
self._write(text, _trace_style)
|
||||
|
||||
|
||||
def Prompt(self):
|
||||
# is the current line non-empty?
|
||||
text, pos = self.GetCurLine()
|
||||
if pos != 0:
|
||||
self.AddText('\n')
|
||||
self.AddText(self.props['ps1'])
|
||||
self.lastPromptPos = self.GetCurrentPos()
|
||||
self.EnsureCaretVisible()
|
||||
self.ScrollToColumn(0)
|
||||
|
||||
|
||||
def PushLine(self, text):
|
||||
# TODO: Add the text to the line cache, manage the cache so
|
||||
# it doesn't get too big.
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def OnKey(self, evt):
|
||||
key = evt.KeyCode()
|
||||
if key == WXK_RETURN:
|
||||
pos = self.GetCurrentPos()
|
||||
lastPos = self.GetTextLength()
|
||||
|
||||
# if not on the last line, duplicate the current line
|
||||
if self.GetLineCount()-1 != self.GetCurrentLine():
|
||||
text, col = self.GetCurLine()
|
||||
prompt = self.props['ps1']
|
||||
lp = len(prompt)
|
||||
if text[:lp] == prompt:
|
||||
text = text[lp:]
|
||||
|
||||
self.SetSelection(self.lastPromptPos, lastPos)
|
||||
self.ReplaceSelection(text[:-1])
|
||||
|
||||
else: # try to execute the text from the prompt to the end
|
||||
if lastPos == self.lastPromptPos:
|
||||
self.AddText('\n')
|
||||
self.Prompt()
|
||||
return
|
||||
|
||||
text = self.GetTextRange(self.lastPromptPos, lastPos)
|
||||
self.AddText('\n')
|
||||
|
||||
more = self.runsource(text)
|
||||
if not more:
|
||||
self.PushLine(text)
|
||||
self.Prompt()
|
||||
|
||||
# TODO: Add handlers for Alt-P and Alt-N to cycle through entries
|
||||
# in the line cache
|
||||
|
||||
else:
|
||||
evt.Skip()
|
||||
|
||||
|
||||
def OnStyle(self, evt):
|
||||
# Only style from the prompt pos to the end
|
||||
lastPos = self.GetTextLength()
|
||||
if self.lastPromptPos and self.lastPromptPos != lastPos:
|
||||
self.SetLexer(wxSTC_LEX_PYTHON)
|
||||
self.SetKeywords(0, ' '.join(keyword.kwlist))
|
||||
|
||||
self.Colourise(self.lastPromptPos, lastPos)
|
||||
|
||||
self.SetLexer(0)
|
||||
|
||||
|
||||
def OnUpdateUI(self, evt):
|
||||
# check for matching braces
|
||||
braceAtCaret = -1
|
||||
braceOpposite = -1
|
||||
charBefore = None
|
||||
caretPos = self.GetCurrentPos()
|
||||
if caretPos > 0:
|
||||
charBefore = self.GetCharAt(caretPos - 1)
|
||||
styleBefore = self.GetStyleAt(caretPos - 1)
|
||||
|
||||
# check before
|
||||
if charBefore and chr(charBefore) in "[]{}()" and styleBefore == wxSTC_P_OPERATOR:
|
||||
braceAtCaret = caretPos - 1
|
||||
|
||||
# check after
|
||||
if braceAtCaret < 0:
|
||||
charAfter = self.GetCharAt(caretPos)
|
||||
styleAfter = self.GetStyleAt(caretPos)
|
||||
if charAfter and chr(charAfter) in "[]{}()" and styleAfter == wxSTC_P_OPERATOR:
|
||||
braceAtCaret = caretPos
|
||||
|
||||
if braceAtCaret >= 0:
|
||||
braceOpposite = self.BraceMatch(braceAtCaret)
|
||||
|
||||
if braceAtCaret != -1 and braceOpposite == -1:
|
||||
self.BraceBadlight(braceAtCaret)
|
||||
else:
|
||||
self.BraceHighlight(braceAtCaret, braceOpposite)
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------
|
||||
# overloaded methods from InteractiveInterpreter
|
||||
def runsource(self, source):
|
||||
stdout, stderr = sys.stdout, sys.stderr
|
||||
sys.stdout = FauxFile(self, _stdout_style)
|
||||
sys.stderr = FauxFile(self, _stderr_style)
|
||||
|
||||
more = InteractiveInterpreter.runsource(self, source)
|
||||
|
||||
sys.stdout, sys.stderr = stdout, stderr
|
||||
return more
|
||||
|
||||
def showsyntaxerror(self, filename=None):
|
||||
self.write = self.writeTrace
|
||||
InteractiveInterpreter.showsyntaxerror(self, filename)
|
||||
self.write = self._write
|
||||
|
||||
def showtraceback(self):
|
||||
self.write = self.writeTrace
|
||||
InteractiveInterpreter.showtraceback(self)
|
||||
self.write = self._write
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class FauxFile:
|
||||
def __init__(self, psw, style):
|
||||
self.psw = psw
|
||||
self.style = style
|
||||
|
||||
def write(self, text):
|
||||
self.psw.write(text, self.style)
|
||||
|
||||
def writelines(self, lst):
|
||||
map(self.write, lst)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# test code
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = wxPyWidgetTester(size = (640, 480))
|
||||
app.SetWidget(PyShellWindow, -1)
|
||||
app.MainLoop()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
import wx.lib.pyshell
|
||||
|
||||
__doc__ = wx.lib.pyshell.__doc__
|
||||
|
||||
FauxFile = wx.lib.pyshell.FauxFile
|
||||
PyShellWindow = wx.lib.pyshell.PyShellWindow
|
||||
|
||||
@@ -1,203 +1,9 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.rcsizer
|
||||
# Purpose: RowColSizer:
|
||||
#
|
||||
# Author: Robin Dunn, adapted from code by Niki Spahiev
|
||||
#
|
||||
# Created: 26-Feb-2002
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2002 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
A pure-Python wxSizer that lays out items in a grid similar to
|
||||
wxFlexGridSizer but item position is not implicit but explicitly
|
||||
specified by row and col, and row/col spanning is supported.
|
||||
|
||||
Adapted from code by Niki Spahiev.
|
||||
|
||||
If anyone is interested, it would be nice to have this ported to C++.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
from wxPython.wx import *
|
||||
import operator
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class RowColSizer(wxPySizer):
|
||||
|
||||
# default sizes for cells with no item
|
||||
col_w = 10
|
||||
row_h = 22
|
||||
|
||||
def __init__(self):
|
||||
wxPySizer.__init__(self)
|
||||
self.growableRows = []
|
||||
self.growableCols = []
|
||||
|
||||
|
||||
def AddGrowableRow(self, idx):
|
||||
self.growableRows.append(idx)
|
||||
|
||||
def AddGrowableCol(self, idx):
|
||||
self.growableCols.append(idx)
|
||||
|
||||
|
||||
|
||||
#--------------------------------------------------
|
||||
def Add(self, item, option=0, flag=0, border=0,
|
||||
row=-1, col=-1, # row, col and spanning can be specified individually...
|
||||
rowspan=1, colspan=1,
|
||||
pos=None, size=None, # or as tuples (row,col) and (rowspan,colspan)
|
||||
):
|
||||
|
||||
if pos is not None:
|
||||
row, col = pos
|
||||
if size is not None:
|
||||
rowspan, colspan = size
|
||||
|
||||
assert row != -1, "Row must be specified"
|
||||
assert col != -1, "Column must be specified"
|
||||
|
||||
# Do I really want to do this? Probably not...
|
||||
#if rowspan > 1 or colspan > 1:
|
||||
# flag = flag | wxEXPAND
|
||||
|
||||
wxPySizer.Add(self, item, option, flag, border,
|
||||
userData=(row, col, row+rowspan, col+colspan))
|
||||
|
||||
#AddWindow = Add
|
||||
#AddSizer = Add
|
||||
|
||||
def AddSpacer(self, width, height, option=0, flag=0, border=0,
|
||||
row=-1, col=-1,
|
||||
rowspan=1, colspan=1,
|
||||
pos=None, size=None,
|
||||
):
|
||||
if pos is not None:
|
||||
row, col = pos
|
||||
if size is not None:
|
||||
rowspan, colspan = size
|
||||
|
||||
assert row != -1, "Row must be specified"
|
||||
assert col != -1, "Column must be specified"
|
||||
|
||||
wxPySizer.AddSpacer(self, width, height, option, flag, border,
|
||||
userData=(row, col, row+rowspan, col+colspan))
|
||||
|
||||
#--------------------------------------------------
|
||||
def _add( self, size, dim ):
|
||||
r, c, r2, c2 = dim # unpack coords and spanning
|
||||
|
||||
# are the widths and heights lists long enough?
|
||||
if r2 > len(self.rowHeights):
|
||||
x = [self.row_h] * (r2-len(self.rowHeights))
|
||||
self.rowHeights.extend( x )
|
||||
if c2 > len(self.colWidths):
|
||||
x = [self.col_w] * (c2-len(self.colWidths))
|
||||
self.colWidths.extend( x )
|
||||
|
||||
# set the widths and heights lists for this item
|
||||
scale = (r2 - r)
|
||||
for i in range(r, r2):
|
||||
self.rowHeights[i] = max( self.rowHeights[i], size.height / scale )
|
||||
scale = (c2 - c)
|
||||
for i in range(c, c2):
|
||||
self.colWidths[i] = max( self.colWidths[i], size.width / scale )
|
||||
|
||||
|
||||
#--------------------------------------------------
|
||||
def CalcMin( self ):
|
||||
self.rowHeights = []
|
||||
self.colWidths = []
|
||||
|
||||
items = self.GetChildren()
|
||||
if not items:
|
||||
return wxSize(10, 10)
|
||||
|
||||
for item in items:
|
||||
self._add( item.CalcMin(), item.GetUserData() )
|
||||
|
||||
size = wxSize( reduce( operator.add, self.colWidths),
|
||||
reduce( operator.add, self.rowHeights) )
|
||||
return size
|
||||
|
||||
|
||||
#--------------------------------------------------
|
||||
def RecalcSizes( self ):
|
||||
# save current dimensions, etc.
|
||||
curWidth = self.GetSize().width
|
||||
curHeight = self.GetSize().height
|
||||
px = self.GetPosition().x
|
||||
py = self.GetPosition().y
|
||||
minWidth = self.CalcMin().width
|
||||
minHeight = self.CalcMin().height
|
||||
|
||||
# Check for growables
|
||||
if self.growableRows and curHeight > minHeight:
|
||||
delta = (curHeight - minHeight) / len(self.growableRows)
|
||||
extra = (curHeight - minHeight) % len(self.growableRows)
|
||||
for idx in self.growableRows:
|
||||
self.rowHeights[idx] += delta
|
||||
self.rowHeights[self.growableRows[0]] += extra
|
||||
|
||||
if self.growableCols and curWidth > minWidth:
|
||||
delta = (curWidth - minWidth) / len(self.growableCols)
|
||||
extra = (curWidth - minWidth) % len(self.growableCols)
|
||||
for idx in self.growableCols:
|
||||
self.colWidths[idx] += delta
|
||||
self.colWidths[self.growableCols[0]] += extra
|
||||
|
||||
rpos = [0] * len(self.rowHeights)
|
||||
cpos = [0] * len(self.colWidths)
|
||||
|
||||
for i in range(len(self.rowHeights)):
|
||||
height = self.rowHeights[i]
|
||||
rpos[i] = py
|
||||
py += height
|
||||
|
||||
for i in range(len(self.colWidths)):
|
||||
width = self.colWidths[i]
|
||||
cpos[i] = px
|
||||
px += width
|
||||
|
||||
# iterate children and set dimensions...
|
||||
for item in self.GetChildren():
|
||||
r, c, r2, c2 = item.GetUserData()
|
||||
width = reduce( operator.add, self.colWidths[c:c2] )
|
||||
height = reduce( operator.add, self.rowHeights[r:r2] )
|
||||
self.SetItemBounds( item, cpos[c], rpos[r], width, height )
|
||||
|
||||
|
||||
#--------------------------------------------------
|
||||
def SetItemBounds(self, item, x, y, w, h):
|
||||
# calculate the item's actual size and position within
|
||||
# its grid cell
|
||||
ipt = wxPoint(x, y)
|
||||
isz = item.CalcMin()
|
||||
flag = item.GetFlag()
|
||||
|
||||
if flag & wxEXPAND or flag & wxSHAPED:
|
||||
isz = wxSize(w, h)
|
||||
else:
|
||||
if flag & wxALIGN_CENTER_HORIZONTAL:
|
||||
ipt.x = x + (w - isz.width) / 2
|
||||
elif flag & wxALIGN_RIGHT:
|
||||
ipt.x = x + (w - isz.width)
|
||||
|
||||
if flag & wxALIGN_CENTER_VERTICAL:
|
||||
ipt.y = y + (h - isz.height) / 2
|
||||
elif flag & wxALIGN_BOTTOM:
|
||||
ipt.y = y + (h - isz.height)
|
||||
|
||||
item.SetDimension(ipt, isz)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
import wx.lib.rcsizer
|
||||
|
||||
__doc__ = wx.lib.rcsizer.__doc__
|
||||
|
||||
RowColSizer = wx.lib.rcsizer.RowColSizer
|
||||
|
||||
@@ -1,82 +1,9 @@
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.rightalign
|
||||
# Purpose: A class derived from wxTextCtrl that aligns the text
|
||||
# on the right side of the control, (except when editing.)
|
||||
#
|
||||
# Author: Josu Oyanguren
|
||||
#
|
||||
# Created: 19-October-2001
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2001 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
"""
|
||||
Some time ago, I asked about how to right-align
|
||||
wxTextCtrls. Answer was that it is not supported. I forgot it.
|
||||
import wx.lib.rightalign
|
||||
|
||||
Just a week ago, one of my clients asked me to have numbers right
|
||||
aligned. (Indeed it was that numbers MUST be right aligned).
|
||||
|
||||
So the game begun. Hacking, hacking, ...
|
||||
|
||||
At last, i succeed. Here is some code that someone may find
|
||||
useful. ubRightTextCtrl is right-aligned when you are not editing, but
|
||||
left-aligned if it has focus.
|
||||
|
||||
Hope this can help someone, as much as this list helps me.
|
||||
|
||||
Josu Oyanguren
|
||||
Ubera Servicios Inform<72>ticos.
|
||||
|
||||
|
||||
P.S. This only works well on wxMSW.
|
||||
"""
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class wxRightTextCtrl(wxTextCtrl):
|
||||
def __init__(self, parent, id, *args, **kwargs):
|
||||
wxTextCtrl.__init__(self, parent, id, *args, **kwargs)
|
||||
EVT_KILL_FOCUS(self, self.OnKillFocus)
|
||||
EVT_PAINT(self, self.OnPaint)
|
||||
|
||||
def OnPaint(self, event):
|
||||
dc = wxPaintDC(self)
|
||||
dc.SetFont(self.GetFont())
|
||||
dc.Clear()
|
||||
text = self.GetValue()
|
||||
textwidth, textheight = dc.GetTextExtent(text)
|
||||
dcwidth, dcheight = self.GetClientSizeTuple()
|
||||
|
||||
y = (dcheight - textheight) / 2
|
||||
x = dcwidth - textwidth - 2
|
||||
|
||||
if self.IsEnabled():
|
||||
fclr = self.GetForegroundColour()
|
||||
else:
|
||||
fclr = wxSystemSettings_GetColour(wxSYS_COLOUR_GRAYTEXT)
|
||||
dc.SetTextForeground(fclr)
|
||||
|
||||
dc.SetClippingRegion(0, 0, dcwidth, dcheight)
|
||||
dc.DrawText(text, x, y)
|
||||
|
||||
if x < 0:
|
||||
toofat = '...'
|
||||
markwidth = dc.GetTextExtent(toofat)[0]
|
||||
dc.SetPen(wxPen(dc.GetBackground().GetColour(), 1, wxSOLID ))
|
||||
dc.DrawRectangle(0,0, markwidth, dcheight)
|
||||
dc.SetPen(wxPen(wxRED, 1, wxSOLID ))
|
||||
dc.SetBrush(wxTRANSPARENT_BRUSH)
|
||||
dc.DrawRectangle(1, 1, dcwidth-2, dcheight-2)
|
||||
dc.DrawText(toofat, 1, y)
|
||||
|
||||
|
||||
def OnKillFocus(self, event):
|
||||
if not self.GetParent(): return
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
__doc__ = wx.lib.rightalign.__doc__
|
||||
|
||||
wxRightTextCtrl = wx.lib.rightalign.wxRightTextCtrl
|
||||
|
||||
@@ -1,417 +0,0 @@
|
||||
#
|
||||
# This was modified from rpcMixin.py distributed with wxPython
|
||||
#
|
||||
#----------------------------------------------------------------------
|
||||
# Name: rpcMixin
|
||||
# Version: 0.2.0
|
||||
# Purpose: provides xmlrpc server functionality for wxPython
|
||||
# applications via a mixin class
|
||||
#
|
||||
# Requires: (1) Python with threading enabled.
|
||||
# (2) xmlrpclib from PythonWare
|
||||
# (http://www.pythonware.com/products/xmlrpc/)
|
||||
# the code was developed and tested using version 0.9.8
|
||||
#
|
||||
# Author: greg Landrum (Landrum@RationalDiscovery.com)
|
||||
#
|
||||
# Copyright: (c) 2000, 2001 by Greg Landrum and Rational Discovery LLC
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
"""provides xmlrpc server functionality for wxPython applications via a mixin class
|
||||
|
||||
**Some Notes:**
|
||||
|
||||
1) The xmlrpc server runs in a separate thread from the main GUI
|
||||
application, communication between the two threads using a custom
|
||||
event (see the Threads demo in the wxPython docs for more info).
|
||||
|
||||
2) Neither the server nor the client are particularly smart about
|
||||
checking method names. So it's easy to shoot yourself in the foot
|
||||
by calling improper methods. It would be pretty easy to add
|
||||
either a list of allowed methods or a list of forbidden methods.
|
||||
|
||||
3) Authentication of xmlrpc clients is *not* performed. I think it
|
||||
would be pretty easy to do this in a hacky way, but I haven't done
|
||||
it yet.
|
||||
|
||||
4) See the bottom of this file for an example of using the class.
|
||||
|
||||
**Obligatory disclaimer:**
|
||||
This is my first crack at both using xmlrpc and multi-threaded
|
||||
programming, so there could be huge horrible bugs or design
|
||||
flaws. If you see one, I'd love to hear about them.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
""" ChangeLog
|
||||
23 May 2001: Version bumped to 0.2.0
|
||||
Numerous code and design changes
|
||||
|
||||
21 Mar. 2001: Version bumped to 0.1.4
|
||||
Updated rpcMixin.OnExternal to support methods with further references
|
||||
(i.e. now you can do rpcClient.foo.bar() and have it work)
|
||||
This probably ain't super legal in xmlrpc land, but it works just fine here
|
||||
and we need it.
|
||||
|
||||
6 Mar. 2001: Version bumped to 0.1.3
|
||||
Documentation changes to make this compatible with happydoc
|
||||
|
||||
21 Jan. 2001: Version bumped to 0.1.2
|
||||
OnExternal() method in the mixin class now uses getattr() to check if
|
||||
a desired method is present. It should have been done this way in
|
||||
the first place.
|
||||
14 Dec. 2000: Version bumped to 0.1.1
|
||||
rearranged locking code and made other changes so that multiple
|
||||
servers in one application are possible.
|
||||
|
||||
"""
|
||||
|
||||
from wxPython.wx import *
|
||||
import xmlrpcserver,xmlrpclib
|
||||
import threading
|
||||
import SocketServer
|
||||
import new
|
||||
import sys
|
||||
|
||||
rpcPENDING = 0
|
||||
rpcDONE = 1
|
||||
rpcEXCEPT = 2
|
||||
|
||||
class RPCRequest:
|
||||
"""A wrapper to use for handling requests and their responses"""
|
||||
status = rpcPENDING
|
||||
result = None
|
||||
|
||||
# here's the ID for external events
|
||||
wxEVT_EXTERNAL_EVENT = 25015
|
||||
class ExternalEvent(wxPyEvent):
|
||||
"""The custom event class used to pass xmlrpc calls from
|
||||
the server thread into the GUI thread
|
||||
|
||||
"""
|
||||
def __init__(self,method,args):
|
||||
wxPyEvent.__init__(self)
|
||||
self.SetEventType(wxEVT_EXTERNAL_EVENT)
|
||||
self.method = method
|
||||
self.args = args
|
||||
self.rpcStatus = RPCRequest()
|
||||
self.rpcStatusLock = threading.Lock()
|
||||
self.rpcCondVar = threading.Condition()
|
||||
|
||||
def Destroy(self):
|
||||
self.method=None
|
||||
self.args=None
|
||||
self.rpcStatus = None
|
||||
self.rpcStatusLock = None
|
||||
self.rpcondVar = None
|
||||
|
||||
def EVT_EXTERNAL_EVENT(win,func):
|
||||
win.Connect(-1,-1,wxEVT_EXTERNAL_EVENT,func)
|
||||
|
||||
class Handler(xmlrpcserver.RequestHandler):
|
||||
"""The handler class that the xmlrpcserver actually calls
|
||||
when a request comes in.
|
||||
|
||||
"""
|
||||
def log_message(self,*args):
|
||||
""" causes the server to stop spewing messages every time a request comes in
|
||||
|
||||
"""
|
||||
pass
|
||||
def call(self,method,params):
|
||||
"""When an xmlrpc request comes in, this is the method that
|
||||
gets called.
|
||||
|
||||
**Arguments**
|
||||
|
||||
- method: name of the method to be called
|
||||
|
||||
- params: arguments to that method
|
||||
|
||||
"""
|
||||
if method == '_rpcPing':
|
||||
# we just acknowledge these without processing them
|
||||
return 'ack'
|
||||
|
||||
# construct the event
|
||||
evt = ExternalEvent(method,params)
|
||||
|
||||
# update the status variable
|
||||
evt.rpcStatusLock.acquire()
|
||||
evt.rpcStatus.status = rpcPENDING
|
||||
evt.rpcStatusLock.release()
|
||||
|
||||
evt.rpcCondVar.acquire()
|
||||
# dispatch the event to the GUI
|
||||
wxPostEvent(self._app,evt)
|
||||
|
||||
# wait for the GUI to finish
|
||||
while evt.rpcStatus.status == rpcPENDING:
|
||||
evt.rpcCondVar.wait()
|
||||
evt.rpcCondVar.release()
|
||||
evt.rpcStatusLock.acquire()
|
||||
if evt.rpcStatus.status == rpcEXCEPT:
|
||||
# The GUI threw an exception, release the status lock
|
||||
# and re-raise the exception
|
||||
evt.rpcStatusLock.release()
|
||||
raise evt.rpcStatus.result[0],evt.rpcStatus.result[1]
|
||||
else:
|
||||
# everything went through without problems
|
||||
s = evt.rpcStatus.result
|
||||
|
||||
evt.rpcStatusLock.release()
|
||||
evt.Destroy()
|
||||
self._app = None
|
||||
return s
|
||||
|
||||
# this global Event is used to let the server thread
|
||||
# know when it should quit
|
||||
stopEvent = threading.Event()
|
||||
stopEvent.clear()
|
||||
|
||||
class _ServerThread(threading.Thread):
|
||||
""" this is the Thread class which actually runs the server
|
||||
|
||||
"""
|
||||
def __init__(self,server,verbose=0):
|
||||
self._xmlServ = server
|
||||
threading.Thread.__init__(self,verbose=verbose)
|
||||
|
||||
def stop(self):
|
||||
stopEvent.set()
|
||||
|
||||
def shouldStop(self):
|
||||
return stopEvent.isSet()
|
||||
|
||||
def run(self):
|
||||
while not self.shouldStop():
|
||||
self._xmlServ.handle_request()
|
||||
self._xmlServ = None
|
||||
|
||||
class rpcMixin:
|
||||
"""A mixin class to provide xmlrpc server functionality to wxPython
|
||||
frames/windows
|
||||
|
||||
If you want to customize this, probably the best idea is to
|
||||
override the OnExternal method, which is what's invoked when an
|
||||
RPC is handled.
|
||||
|
||||
"""
|
||||
|
||||
# we'll try a range of ports for the server, this is the size of the
|
||||
# range to be scanned
|
||||
nPortsToTry=20
|
||||
if sys.platform == 'win32':
|
||||
defPort = 800
|
||||
else:
|
||||
defPort = 8023
|
||||
|
||||
def __init__(self,host='',port=-1,verbose=0,portScan=1):
|
||||
"""Constructor
|
||||
|
||||
**Arguments**
|
||||
|
||||
- host: (optional) the hostname for the server
|
||||
|
||||
- port: (optional) the port the server will use
|
||||
|
||||
- verbose: (optional) if set, the server thread will be launched
|
||||
in verbose mode
|
||||
|
||||
- portScan: (optional) if set, we'll scan across a number of ports
|
||||
to find one which is avaiable
|
||||
|
||||
"""
|
||||
if port == -1:
|
||||
port = self.defPort
|
||||
self.verbose=verbose
|
||||
EVT_EXTERNAL_EVENT(self,self.OnExternal)
|
||||
if hasattr(self,'OnClose'):
|
||||
self._origOnClose = self.OnClose
|
||||
self.Disconnect(-1,-1,wxEVT_CLOSE_WINDOW)
|
||||
else:
|
||||
self._origOnClose = None
|
||||
self.OnClose = self.RPCOnClose
|
||||
EVT_CLOSE(self,self.RPCOnClose)
|
||||
|
||||
tClass = new.classobj('Handler%d'%(port),(Handler,),{})
|
||||
tClass._app = self
|
||||
if portScan:
|
||||
self.rpcPort = -1
|
||||
for i in xrange(self.nPortsToTry):
|
||||
try:
|
||||
xmlServ = SocketServer.TCPServer((host,port+i),tClass)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
self.rpcPort = port+i
|
||||
else:
|
||||
self.rpcPort = port
|
||||
try:
|
||||
xmlServ = SocketServer.TCPServer((host,port),tClass)
|
||||
except:
|
||||
self.rpcPort = -1
|
||||
|
||||
if self.rpcPort == -1:
|
||||
raise 'RPCMixinError','Cannot initialize server'
|
||||
self.servThread = _ServerThread(xmlServ,verbose=self.verbose)
|
||||
self.servThread.setName('XML-RPC Server')
|
||||
self.servThread.start()
|
||||
|
||||
def RPCOnClose(self,event):
|
||||
""" callback for when the application is closed
|
||||
|
||||
be sure to shutdown the server and the server thread before
|
||||
leaving
|
||||
|
||||
"""
|
||||
# by setting the global stopEvent we inform the server thread
|
||||
# that it's time to shut down.
|
||||
stopEvent.set()
|
||||
if event is not None:
|
||||
# if we came in here from a user event (as opposed to an RPC event),
|
||||
# then we'll need to kick the server one last time in order
|
||||
# to get that thread to terminate. do so now
|
||||
s1 = xmlrpclib.Server('http://localhost:%d'%(self.rpcPort))
|
||||
try:
|
||||
s1._rpcPing()
|
||||
except:
|
||||
pass
|
||||
|
||||
if self._origOnClose is not None:
|
||||
self._origOnClose(event)
|
||||
|
||||
def RPCQuit(self):
|
||||
""" shuts down everything, including the rpc server
|
||||
|
||||
"""
|
||||
self.RPCOnClose(None)
|
||||
def OnExternal(self,event):
|
||||
""" this is the callback used to handle RPCs
|
||||
|
||||
**Arguments**
|
||||
|
||||
- event: an _ExternalEvent_ sent by the rpc server
|
||||
|
||||
Exceptions are caught and returned in the global _rpcStatus
|
||||
structure. This allows the xmlrpc server to report the
|
||||
exception to the client without mucking up any of the delicate
|
||||
thread stuff.
|
||||
|
||||
"""
|
||||
event.rpcStatusLock.acquire()
|
||||
doQuit = 0
|
||||
try:
|
||||
methsplit = event.method.split('.')
|
||||
meth = self
|
||||
for piece in methsplit:
|
||||
meth = getattr(meth,piece)
|
||||
except AttributeError,msg:
|
||||
event.rpcStatus.result = 'No Such Method',msg
|
||||
event.rpcStatus.status = rpcEXCEPT
|
||||
else:
|
||||
try:
|
||||
res = apply(meth,event.args)
|
||||
except:
|
||||
import traceback
|
||||
if self.verbose: traceback.print_exc()
|
||||
event.rpcStatus.result = sys.exc_info()[:2]
|
||||
event.rpcStatus.status = rpcEXCEPT
|
||||
else:
|
||||
if res is None:
|
||||
# returning None across the xmlrpc interface is problematic
|
||||
event.rpcStatus.result = []
|
||||
else:
|
||||
event.rpcStatus.result = res
|
||||
event.rpcStatus.status = rpcDONE
|
||||
|
||||
event.rpcStatusLock.release()
|
||||
|
||||
# broadcast (using the condition var) that we're done with the event
|
||||
event.rpcCondVar.acquire()
|
||||
event.rpcCondVar.notify()
|
||||
event.rpcCondVar.release()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import time
|
||||
if sys.platform == 'win32':
|
||||
port = 800
|
||||
else:
|
||||
port = 8023
|
||||
|
||||
class rpcFrame(wxFrame,rpcMixin):
|
||||
"""A simple wxFrame with the rpcMixin functionality added
|
||||
"""
|
||||
def __init__(self,*args,**kwargs):
|
||||
""" rpcHost or rpcPort keyword arguments will be passed along to
|
||||
the xmlrpc server.
|
||||
"""
|
||||
mixinArgs = {}
|
||||
if kwargs.has_key('rpcHost'):
|
||||
mixinArgs['host'] = kwargs['rpcHost']
|
||||
del kwargs['rpcHost']
|
||||
if kwargs.has_key('rpcPort'):
|
||||
mixinArgs['port'] = kwargs['rpcPort']
|
||||
del kwargs['rpcPort']
|
||||
if kwargs.has_key('rpcPortScan'):
|
||||
mixinArgs['portScan'] = kwargs['rpcPortScan']
|
||||
del kwargs['rpcPortScan']
|
||||
|
||||
apply(wxFrame.__init__,(self,)+args,kwargs)
|
||||
apply(rpcMixin.__init__,(self,),mixinArgs)
|
||||
|
||||
EVT_CHAR(self,self.OnChar)
|
||||
|
||||
def TestFunc(self,args):
|
||||
"""a demo method"""
|
||||
return args
|
||||
|
||||
def OnChar(self,event):
|
||||
key = event.GetKeyCode()
|
||||
if key == ord('q'):
|
||||
self.OnQuit(event)
|
||||
|
||||
def OnQuit(self,event):
|
||||
self.OnClose(event)
|
||||
|
||||
def OnClose(self,event):
|
||||
self.Destroy()
|
||||
|
||||
|
||||
|
||||
class MyApp(wxApp):
|
||||
def OnInit(self):
|
||||
self.frame = rpcFrame(NULL, -1, "wxPython RPCDemo", wxDefaultPosition,
|
||||
wxSize(300,300),
|
||||
rpcHost='localhost',rpcPort=port)
|
||||
self.frame.Show(True)
|
||||
return True
|
||||
|
||||
|
||||
def testcon(port):
|
||||
s1 = xmlrpclib.Server('http://localhost:%d'%(port))
|
||||
s1.SetTitle('Munged')
|
||||
s1._rpcPing()
|
||||
if doQuit:
|
||||
s1.RPCQuit()
|
||||
|
||||
doQuit = 1
|
||||
if len(sys.argv)>1 and sys.argv[1] == '-q':
|
||||
doQuit = 0
|
||||
nT = threading.activeCount()
|
||||
app = MyApp(0)
|
||||
activePort = app.frame.rpcPort
|
||||
t = threading.Thread(target=lambda x=activePort:testcon(x),verbose=0)
|
||||
t.start()
|
||||
|
||||
app.MainLoop()
|
||||
# give the threads time to shut down
|
||||
if threading.activeCount() > nT:
|
||||
print 'waiting for all threads to terminate'
|
||||
while threading.activeCount() > nT:
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
@@ -1,95 +1,9 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: wxScrolledPanel.py
|
||||
# Author: Will Sadkin
|
||||
# Created: 03/21/2003
|
||||
# Copyright: (c) 2003 by Will Sadkin
|
||||
# RCS-ID: $Id$
|
||||
# License: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
#
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython.wx import *
|
||||
import wx.lib.scrolledpanel
|
||||
|
||||
__doc__ = wx.lib.scrolledpanel.__doc__
|
||||
|
||||
class wxScrolledPanel( wxScrolledWindow ):
|
||||
"""
|
||||
wxScrolledPanel fills a "hole" in the implementation of wxScrolledWindow,
|
||||
providing automatic scrollbar and scrolling behavior and the tab traversal
|
||||
management that wxScrolledWindow lacks. This code was based on the original
|
||||
demo code showing how to do this, but is now available for general use
|
||||
as a proper class (and the demo is now converted to just use it.)
|
||||
"""
|
||||
def __init__(self, parent, id=-1,
|
||||
pos = wxDefaultPosition, size = wxDefaultSize,
|
||||
style = wxTAB_TRAVERSAL, name = "scrolledpanel"):
|
||||
|
||||
wxScrolledWindow.__init__(self, parent, -1,
|
||||
pos=pos, size=size,
|
||||
style=style, name=name)
|
||||
|
||||
EVT_CHILD_FOCUS(self, self.OnChildFocus)
|
||||
|
||||
|
||||
def SetupScrolling(self, scroll_x=True, scroll_y=True, rate_x=20, rate_y=20):
|
||||
"""
|
||||
This function sets up the event handling necessary to handle
|
||||
scrolling properly. It should be called within the __init__
|
||||
function of any class that is derived from wxScrolledPanel,
|
||||
once the controls on the panel have been constructed and
|
||||
thus the size of the scrolling area can be determined.
|
||||
|
||||
"""
|
||||
# The following is all that is needed to integrate the sizer and the
|
||||
# scrolled window.
|
||||
if not scroll_x: rate_x = 0
|
||||
if not scroll_y: rate_y = 0
|
||||
|
||||
# Round up the virtual size to be a multiple of the scroll rate
|
||||
sizer = self.GetSizer()
|
||||
if sizer:
|
||||
w, h = sizer.GetMinSize()
|
||||
if rate_x:
|
||||
w += rate_x - (w % rate_x)
|
||||
if rate_y:
|
||||
h += rate_y - (h % rate_y)
|
||||
self.SetVirtualSize( (w, h) )
|
||||
self.SetVirtualSizeHints( w, h )
|
||||
|
||||
self.SetScrollRate(rate_x, rate_y)
|
||||
wxCallAfter(self.Scroll, 0, 0) # scroll back to top after initial events
|
||||
|
||||
|
||||
def OnChildFocus(self, evt):
|
||||
# If the child window that gets the focus is not visible,
|
||||
# this handler will try to scroll enough to see it.
|
||||
evt.Skip()
|
||||
child = evt.GetWindow()
|
||||
|
||||
sppu_x, sppu_y = self.GetScrollPixelsPerUnit()
|
||||
vs_x, vs_y = self.GetViewStart()
|
||||
cpos = child.GetPosition()
|
||||
csz = child.GetSize()
|
||||
new_vs_x, new_vs_y = -1, -1
|
||||
|
||||
# is it before the left edge?
|
||||
if cpos.x < 0 and sppu_x > 0:
|
||||
new_vs_x = vs_x + (cpos.x / sppu_x)
|
||||
|
||||
# is it above the top?
|
||||
if cpos.y < 0 and sppu_y > 0:
|
||||
new_vs_y = vs_y + (cpos.y / sppu_y)
|
||||
|
||||
|
||||
# is it past the right edge ?
|
||||
if cpos.x + csz.width > self.GetClientSize().width and sppu_x > 0:
|
||||
diff = (cpos.x + csz.width - self.GetClientSize().width) / sppu_x
|
||||
new_vs_x = vs_x + diff + 1
|
||||
|
||||
# is it below the bottom ?
|
||||
if cpos.y + csz.height > self.GetClientSize().height and sppu_y > 0:
|
||||
diff = (cpos.y + csz.height - self.GetClientSize().height) / sppu_y
|
||||
new_vs_y = vs_y + diff + 1
|
||||
|
||||
# if we need to adjust
|
||||
if new_vs_x != -1 or new_vs_y != -1:
|
||||
self.Scroll(new_vs_x, new_vs_y)
|
||||
wxScrolledPanel = wx.lib.scrolledpanel.wxScrolledPanel
|
||||
|
||||
@@ -1,332 +1,11 @@
|
||||
# sheet.py
|
||||
# CSheet - A wxPython spreadsheet class.
|
||||
# This is free software. Feel free to adapt it as you like.
|
||||
# Author: Mark F. Russo (russomf@hotmail.com) 2002/01/31
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
from wxPython.wx import *
|
||||
from wxPython.grid import *
|
||||
import string
|
||||
import wx.lib.sheet
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
class CTextCellEditor(wxTextCtrl):
|
||||
""" Custom text control for cell editing """
|
||||
def __init__(self, parent, id, grid):
|
||||
wxTextCtrl.__init__(self, parent, id, "", style=wxNO_BORDER)
|
||||
self._grid = grid # Save grid reference
|
||||
EVT_CHAR(self, self.OnChar)
|
||||
__doc__ = wx.lib.sheet.__doc__
|
||||
|
||||
def OnChar(self, evt): # Hook OnChar for custom behavior
|
||||
"""Customizes char events """
|
||||
key = evt.GetKeyCode()
|
||||
if key == WXK_DOWN:
|
||||
self._grid.DisableCellEditControl() # Commit the edit
|
||||
self._grid.MoveCursorDown(False) # Change the current cell
|
||||
elif key == WXK_UP:
|
||||
self._grid.DisableCellEditControl() # Commit the edit
|
||||
self._grid.MoveCursorUp(False) # Change the current cell
|
||||
elif key == WXK_LEFT:
|
||||
self._grid.DisableCellEditControl() # Commit the edit
|
||||
self._grid.MoveCursorLeft(False) # Change the current cell
|
||||
elif key == WXK_RIGHT:
|
||||
self._grid.DisableCellEditControl() # Commit the edit
|
||||
self._grid.MoveCursorRight(False) # Change the current cell
|
||||
|
||||
evt.Skip() # Continue event
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
class CCellEditor(wxPyGridCellEditor):
|
||||
""" Custom cell editor """
|
||||
def __init__(self, grid):
|
||||
wxPyGridCellEditor.__init__(self)
|
||||
self._grid = grid # Save a reference to the grid
|
||||
|
||||
def Create(self, parent, id, evtHandler):
|
||||
""" Create the actual edit control. Must derive from wxControl.
|
||||
Must Override
|
||||
"""
|
||||
self._tc = CTextCellEditor(parent, id, self._grid)
|
||||
self._tc.SetInsertionPoint(0)
|
||||
self.SetControl(self._tc)
|
||||
if evtHandler:
|
||||
self._tc.PushEventHandler(evtHandler)
|
||||
|
||||
def SetSize(self, rect):
|
||||
""" Position/size the edit control within the cell rectangle. """
|
||||
# Size text control to exactly overlay in-cell editing
|
||||
self._tc.SetDimensions(rect.x+3, rect.y+3, rect.width-2, rect.height-2)
|
||||
|
||||
def Show(self, show, attr):
|
||||
""" Show or hide the edit control. Use the attr (if not None)
|
||||
to set colors or fonts for the control.
|
||||
"""
|
||||
self.base_Show(show, attr)
|
||||
|
||||
def PaintBackground(self, rect, attr):
|
||||
""" Draws the part of the cell not occupied by the edit control. The
|
||||
base class version just fills it with background colour from the
|
||||
attribute.
|
||||
"""
|
||||
# Call base class method.
|
||||
self.base_PaintBackground(self, rect, attr)
|
||||
|
||||
def BeginEdit(self, row, col, grid):
|
||||
""" Fetch the value from the table and prepare edit control to begin editing.
|
||||
Set the focus to the edit control. Must Override.
|
||||
"""
|
||||
self._startValue = grid.GetTable().GetValue(row, col)
|
||||
self._tc.SetValue(self._startValue)
|
||||
self._tc.SetFocus()
|
||||
|
||||
# Select the text when initiating an edit so that subsequent typing
|
||||
# replaces the contents.
|
||||
self._tc.SetSelection(0, self._tc.GetLastPosition())
|
||||
|
||||
def EndEdit(self, row, col, grid):
|
||||
""" Commit editing the current cell. Returns True if the value has changed.
|
||||
If necessary, the control may be destroyed. Must Override.
|
||||
"""
|
||||
changed = False # Assume value not changed
|
||||
val = self._tc.GetValue() # Get value in edit control
|
||||
if val != self._startValue: # Compare
|
||||
changed = True # If different then changed is True
|
||||
grid.GetTable().SetValue(row, col, val) # Update the table
|
||||
self._startValue = '' # Clear the class' start value
|
||||
self._tc.SetValue('') # Clear contents of the edit control
|
||||
|
||||
return changed
|
||||
|
||||
def Reset(self):
|
||||
""" Reset the value in the control back to its starting value. Must Override. """
|
||||
self._tc.SetValue(self._startValue)
|
||||
self._tc.SetInsertionPointEnd()
|
||||
|
||||
def IsAcceptedKey(self, evt):
|
||||
""" Return True to allow the given key to start editing. The base class
|
||||
version only checks that the event has no modifiers. F2 is special
|
||||
and will always start the editor.
|
||||
"""
|
||||
return (not (evt.ControlDown() or evt.AltDown())
|
||||
and evt.GetKeyCode() != WXK_SHIFT)
|
||||
|
||||
def StartingKey(self, evt):
|
||||
""" If the editor is enabled by pressing keys on the grid, this will be
|
||||
called to let the editor react to that first key.
|
||||
"""
|
||||
key = evt.GetKeyCode() # Get the key code
|
||||
ch = None # Handle num pad keys
|
||||
if key in [WXK_NUMPAD0, WXK_NUMPAD1, WXK_NUMPAD2, WXK_NUMPAD3, WXK_NUMPAD4,
|
||||
WXK_NUMPAD5, WXK_NUMPAD6, WXK_NUMPAD7, WXK_NUMPAD8, WXK_NUMPAD9]:
|
||||
ch = chr(ord('0') + key - WXK_NUMPAD0)
|
||||
|
||||
elif key == WXK_BACK: # Empty text control when init w/ back key
|
||||
ch = ""
|
||||
# Handle normal keys
|
||||
elif key < 256 and key >= 0 and chr(key) in string.printable:
|
||||
ch = chr(key)
|
||||
if not evt.ShiftDown():
|
||||
ch = ch.lower()
|
||||
|
||||
if ch is not None: # If are at this point with a key,
|
||||
self._tc.SetValue(ch) # replace the contents of the text control.
|
||||
self._tc.SetInsertionPointEnd() # Move to the end so that subsequent keys are appended
|
||||
else:
|
||||
evt.Skip()
|
||||
|
||||
def StartingClick(self):
|
||||
""" If the editor is enabled by clicking on the cell, this method will be
|
||||
called to allow the editor to simulate the click on the control.
|
||||
"""
|
||||
pass
|
||||
|
||||
def Destroy(self):
|
||||
""" Final cleanup """
|
||||
self.base_Destroy()
|
||||
|
||||
def Clone(self):
|
||||
""" Create a new object which is the copy of this one. Must Override. """
|
||||
return CCellEditor()
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
class CSheet(wxGrid):
|
||||
def __init__(self, parent):
|
||||
wxGrid.__init__(self, parent, -1)
|
||||
|
||||
# Init variables
|
||||
self._lastCol = -1 # Init last cell column clicked
|
||||
self._lastRow = -1 # Init last cell row clicked
|
||||
self._selected = None # Init range currently selected
|
||||
# Map string datatype to default renderer/editor
|
||||
self.RegisterDataType(wxGRID_VALUE_STRING,
|
||||
wxGridCellStringRenderer(),
|
||||
CCellEditor(self))
|
||||
|
||||
self.CreateGrid(4, 3) # By default start with a 4 x 3 grid
|
||||
self.SetColLabelSize(18) # Default sizes and alignment
|
||||
self.SetRowLabelSize(50)
|
||||
self.SetRowLabelAlignment(wxALIGN_RIGHT, wxALIGN_BOTTOM)
|
||||
self.SetColSize(0, 75) # Default column sizes
|
||||
self.SetColSize(1, 75)
|
||||
self.SetColSize(2, 75)
|
||||
|
||||
# Sink events
|
||||
EVT_GRID_CELL_LEFT_CLICK( self, self.OnLeftClick)
|
||||
EVT_GRID_CELL_RIGHT_CLICK( self, self.OnRightClick)
|
||||
EVT_GRID_CELL_LEFT_DCLICK( self, self.OnLeftDoubleClick)
|
||||
EVT_GRID_RANGE_SELECT( self, self.OnRangeSelect)
|
||||
EVT_GRID_ROW_SIZE( self, self.OnRowSize)
|
||||
EVT_GRID_COL_SIZE( self, self.OnColSize)
|
||||
EVT_GRID_CELL_CHANGE( self, self.OnCellChange)
|
||||
EVT_GRID_SELECT_CELL( self, self.OnGridSelectCell)
|
||||
|
||||
def OnGridSelectCell(self, event):
|
||||
""" Track cell selections """
|
||||
# Save the last cell coordinates
|
||||
self._lastRow, self._lastCol = event.GetRow(), event.GetCol()
|
||||
event.Skip()
|
||||
|
||||
def OnRowSize(self, event):
|
||||
event.Skip()
|
||||
|
||||
def OnColSize(self, event):
|
||||
event.Skip()
|
||||
|
||||
def OnCellChange(self, event):
|
||||
event.Skip()
|
||||
|
||||
def OnLeftClick(self, event):
|
||||
""" Override left-click behavior to prevent left-click edit initiation """
|
||||
# Save the cell clicked
|
||||
currCell = (event.GetRow(), event.GetCol())
|
||||
|
||||
# Suppress event if same cell clicked twice in a row.
|
||||
# This prevents a single-click from initiating an edit.
|
||||
if currCell != (self._lastRow, self._lastCol): event.Skip()
|
||||
|
||||
def OnRightClick(self, event):
|
||||
""" Move grid cursor when a cell is right-clicked """
|
||||
self.SetGridCursor( event.GetRow(), event.GetCol() )
|
||||
event.Skip()
|
||||
|
||||
def OnLeftDoubleClick(self, event):
|
||||
""" Initiate the cell editor on a double-click """
|
||||
# Move grid cursor to double-clicked cell
|
||||
if self.CanEnableCellControl():
|
||||
self.SetGridCursor( event.GetRow(), event.GetCol() )
|
||||
self.EnableCellEditControl(True) # Show the cell editor
|
||||
event.Skip()
|
||||
|
||||
def OnRangeSelect(self, event):
|
||||
""" Track which cells are selected so that copy/paste behavior can be implemented """
|
||||
# If a single cell is selected, then Selecting() returns False (0)
|
||||
# and range coords are entire grid. In this case cancel previous selection.
|
||||
# If more than one cell is selected, then Selecting() is True (1)
|
||||
# and range accurately reflects selected cells. Save them.
|
||||
# If more cells are added to a selection, selecting remains True (1)
|
||||
self._selected = None
|
||||
if event.Selecting():
|
||||
self._selected = ((event.GetTopRow(), event.GetLeftCol()),
|
||||
(event.GetBottomRow(), event.GetRightCol()))
|
||||
event.Skip()
|
||||
|
||||
def Copy(self):
|
||||
""" Copy the currently selected cells to the clipboard """
|
||||
# TODO: raise an error when there are no cells selected?
|
||||
if self._selected == None: return
|
||||
((r1, c1), (r2, c2)) = self._selected
|
||||
|
||||
# Build a string to put on the clipboard
|
||||
# (Is there a faster way to do this in Python?)
|
||||
crlf = chr(13) + chr(10)
|
||||
tab = chr(9)
|
||||
s = ""
|
||||
for row in range(r1, r2+1):
|
||||
for col in range(c1, c2):
|
||||
s += self.GetCellValue(row,col)
|
||||
s += tab
|
||||
s += self.GetCellValue(row, c2)
|
||||
s += crlf
|
||||
|
||||
# Put the string on the clipboard
|
||||
if wxTheClipboard.Open():
|
||||
wxTheClipboard.Clear()
|
||||
wxTheClipboard.SetData(wxTextDataObject(s))
|
||||
wxTheClipboard.Close()
|
||||
|
||||
def Paste(self):
|
||||
""" Paste the contents of the clipboard into the currently selected cells """
|
||||
# (Is there a better way to do this?)
|
||||
if wxTheClipboard.Open():
|
||||
td = wxTextDataObject()
|
||||
success = wxTheClipboard.GetData(td)
|
||||
wxTheClipboard.Close()
|
||||
if not success: return # Exit on failure
|
||||
s = td.GetText() # Get the text
|
||||
|
||||
crlf = chr(13) + chr(10) # CrLf characters
|
||||
tab = chr(9) # Tab character
|
||||
|
||||
rows = s.split(crlf) # split into rows
|
||||
rows = rows[0:-1] # leave out last element, which is always empty
|
||||
for i in range(0, len(rows)): # split rows into elements
|
||||
rows[i] = rows[i].split(tab)
|
||||
|
||||
# Get the starting and ending cell range to paste into
|
||||
if self._selected == None: # If no cells selected...
|
||||
r1 = self.GetGridCursorRow() # Start the paste at the current location
|
||||
c1 = self.GetGridCursorCol()
|
||||
r2 = self.GetNumberRows()-1 # Go to maximum row and col extents
|
||||
c2 = self.GetNumberCols()-1
|
||||
else: # If cells selected, only paste there
|
||||
((r1, c1), (r2, c2)) = self._selected
|
||||
|
||||
# Enter data into spreadsheet cells one at a time
|
||||
r = r1 # Init row and column counters
|
||||
c = c1
|
||||
for row in rows: # Loop over all rows
|
||||
for element in row: # Loop over all row elements
|
||||
self.SetCellValue(r, c, str(element)) # Set cell value
|
||||
c += 1 # Increment the column counter
|
||||
if c > c2: break # Do not exceed maximum column
|
||||
r += 1
|
||||
if r > r2: break # Do not exceed maximum row
|
||||
c = c1
|
||||
|
||||
def Clear(self):
|
||||
""" Clear the currently selected cells """
|
||||
if self._selected == None: # If no selection...
|
||||
r = self.GetGridCursorRow() # clear only current cell
|
||||
c = self.GetGridCursorCol()
|
||||
self.SetCellValue(r, c, "")
|
||||
else: # Otherwise clear selected cells
|
||||
((r1, c1), (r2, c2)) = self._selected
|
||||
for r in range(r1, r2+1):
|
||||
for c in range(c1, c2+1):
|
||||
self.SetCellValue(r, c, "")
|
||||
|
||||
def SetNumberRows(self, numRows=1):
|
||||
""" Set the number of rows in the sheet """
|
||||
# Check for non-negative number
|
||||
if numRows < 0: return False
|
||||
|
||||
# Adjust number of rows
|
||||
curRows = self.GetNumberRows()
|
||||
if curRows < numRows:
|
||||
self.AppendRows(numRows - curRows)
|
||||
elif curRows > numRows:
|
||||
self.DeleteRows(numRows, curRows - numRows)
|
||||
|
||||
return True
|
||||
|
||||
def SetNumberCols(self, numCols=1):
|
||||
""" Set the number of columns in the sheet """
|
||||
# Check for non-negative number
|
||||
if numCols < 0: return False
|
||||
|
||||
# Adjust number of rows
|
||||
curCols = self.GetNumberCols()
|
||||
if curCols < numCols:
|
||||
self.AppendCols(numCols - curCols)
|
||||
elif curCols > numCols:
|
||||
self.DeleteCols(numCols, curCols - numCols)
|
||||
|
||||
return True
|
||||
CCellEditor = wx.lib.sheet.CCellEditor
|
||||
CSheet = wx.lib.sheet.CSheet
|
||||
CTextCellEditor = wx.lib.sheet.CTextCellEditor
|
||||
|
||||
@@ -1,352 +1,11 @@
|
||||
# shell.py
|
||||
"""wxPython interactive shell
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
Copyright (c) 1999 SIA "ANK"
|
||||
import wx.lib.shell
|
||||
|
||||
this module is free software. it may be used under same terms as Python itself
|
||||
|
||||
Notes:
|
||||
i would like to use command completion (see rlcompleter library module),
|
||||
but i cannot load it because i don't have readline...
|
||||
|
||||
History:
|
||||
03-oct-1999 [als] created
|
||||
04-oct-1999 [als] PyShellOutput.intro moved from __init__ parameters
|
||||
to class attributes; html debug disabled
|
||||
04-oct-1999 [als] fixed bug with class attributes
|
||||
input prompts and output styles added to customized demo
|
||||
some html cleanups
|
||||
04-oct-1999 [rpd] Changed to use the new sizers
|
||||
05-oct-1999 [als] changes inspired by code.InteractiveInterpreter()
|
||||
from Python Library. if i knew about this class earlier,
|
||||
i would rather inherit from it.
|
||||
renamed to wxPyShell.py since i've renounced the 8.3 scheme
|
||||
|
||||
8-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent
|
||||
PyCrust package instead.
|
||||
|
||||
"""
|
||||
__version__ ="$Revision$"
|
||||
# $RCSfile$
|
||||
|
||||
import sys, code, traceback
|
||||
from wxPython.wx import *
|
||||
from wxPython.html import *
|
||||
|
||||
|
||||
class PyShellInput(wxPanel):
|
||||
"""PyShell input window
|
||||
|
||||
"""
|
||||
PS1 =" Enter Command:"
|
||||
PS2 ="... continue:"
|
||||
def __init__(self, parent, shell, id=-1):
|
||||
"""Create input window
|
||||
|
||||
shell must be a PyShell object.
|
||||
it is used for exception handling, eval() namespaces,
|
||||
and shell.output is used for output
|
||||
(print's go to overridden stdout)
|
||||
"""
|
||||
wxPanel.__init__(self, parent, id)
|
||||
self.shell =shell
|
||||
# make a private copy of class attrs
|
||||
self.PS1 =PyShellInput.PS1
|
||||
self.PS2 =PyShellInput.PS2
|
||||
# create controls
|
||||
self.label =wxStaticText(self, -1, self.PS1)
|
||||
tid =wxNewId()
|
||||
self.entry =wxTextCtrl(self, tid, style = wxTE_MULTILINE)
|
||||
EVT_CHAR(self.entry, self.OnChar)
|
||||
self.entry.SetFont(wxFont(9, wxMODERN, wxNORMAL, wxNORMAL, False))
|
||||
sizer =wxBoxSizer(wxVERTICAL)
|
||||
sizer.AddMany([(self.label, 0, wxEXPAND), (self.entry, 1, wxEXPAND)])
|
||||
self.SetSizer(sizer)
|
||||
self.SetAutoLayout(True)
|
||||
EVT_SET_FOCUS(self, self.OnSetFocus)
|
||||
# when in "continuation" mode,
|
||||
# two consecutive newlines are required
|
||||
# to avoid execution of unfinished block
|
||||
self.first_line =1
|
||||
|
||||
def OnSetFocus(self, event):
|
||||
self.entry.SetFocus()
|
||||
|
||||
|
||||
def Clear(self, event=None):
|
||||
"""reset input state"""
|
||||
self.label.SetLabel(self.PS1)
|
||||
self.label.Refresh()
|
||||
self.entry.SetSelection(0, self.entry.GetLastPosition())
|
||||
self.first_line =1
|
||||
# self.entry.SetFocus()
|
||||
|
||||
def OnChar(self, event):
|
||||
"""called on CHARevent. executes input on newline"""
|
||||
# print "On Char:", event.__dict__.keys()
|
||||
if event.KeyCode() !=WXK_RETURN:
|
||||
# not of our business
|
||||
event.Skip()
|
||||
return
|
||||
text =self.entry.GetValue()
|
||||
# weird CRLF thingy
|
||||
text = text.replace("\r\n", "\n")
|
||||
# see if we've finished
|
||||
if (not (self.first_line or text[-1] =="\n") # in continuation mode
|
||||
or (text[-1] =="\\") # escaped newline
|
||||
):
|
||||
# XXX should escaped newline put myself i "continuation" mode?
|
||||
event.Skip()
|
||||
return
|
||||
# ok, we can try to execute this
|
||||
rc =self.shell.TryExec(text)
|
||||
if rc:
|
||||
# code is incomplete; continue input
|
||||
if self.first_line:
|
||||
self.label.SetLabel(self.PS2)
|
||||
self.label.Refresh()
|
||||
self.first_line =0
|
||||
event.Skip()
|
||||
else:
|
||||
self.Clear()
|
||||
|
||||
class PyShellOutput(wxPanel):
|
||||
"""PyShell output window
|
||||
|
||||
for now, it is based on simple wxTextCtrl,
|
||||
but i'm looking at HTML classes to provide colorized output
|
||||
"""
|
||||
# attributes for for different (input, output, exception) display styles:
|
||||
# begin tag, end tag, newline
|
||||
in_style =(" <font color=\"#000080\"><tt>>>> ",
|
||||
"</tt></font><br>\n", "<br>\n... ")
|
||||
out_style =("<tt>", "</tt>\n", "<br>\n")
|
||||
exc_style =("<font color=\"#FF0000\"><tt>",
|
||||
"</tt></font>\n", "<br>\n")
|
||||
intro ="<H3>wxPython Interactive Shell</H3>\n"
|
||||
html_debug =0
|
||||
# entity references
|
||||
erefs =(("&", "&"), (">", ">"), ("<", "<"), (" ", " "))
|
||||
def __init__(self, parent, id=-1):
|
||||
wxPanel.__init__(self, parent, id)
|
||||
# make a private copy of class attrs
|
||||
self.in_style =PyShellOutput.in_style
|
||||
self.out_style =PyShellOutput.out_style
|
||||
self.exc_style =PyShellOutput.exc_style
|
||||
self.intro =PyShellOutput.intro
|
||||
self.html_debug =PyShellOutput.html_debug
|
||||
# create windows
|
||||
if self.html_debug:
|
||||
# this was used in html debugging,
|
||||
# but i don't want to delete it; it's funny
|
||||
splitter =wxSplitterWindow(self, -1)
|
||||
self.view =wxTextCtrl(splitter, -1,
|
||||
style = wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL)
|
||||
self.html =wxHtmlWindow(splitter)
|
||||
splitter.SplitVertically(self.view, self.html)
|
||||
splitter.SetSashPosition(40)
|
||||
splitter.SetMinimumPaneSize(3)
|
||||
self.client =splitter
|
||||
else:
|
||||
self.view =None
|
||||
self.html =wxHtmlWindow(self)
|
||||
self.client =self.html # used in OnSize()
|
||||
self.text =self.intro
|
||||
self.html.SetPage(self.text)
|
||||
self.html.SetAutoLayout(True)
|
||||
self.line_buffer =""
|
||||
# refreshes are annoying
|
||||
self.in_batch =0
|
||||
self.dirty =0
|
||||
EVT_SIZE(self, self.OnSize)
|
||||
EVT_IDLE(self, self.OnIdle)
|
||||
|
||||
def OnSize(self, event):
|
||||
self.client.SetSize(self.GetClientSize())
|
||||
|
||||
def OnIdle(self, event):
|
||||
"""when there's nothing to do, we can update display"""
|
||||
if self.in_batch and self.dirty: self.UpdWindow()
|
||||
|
||||
def BeginBatch(self):
|
||||
"""do not refresh display till EndBatch()"""
|
||||
self.in_batch =1
|
||||
|
||||
def EndBatch(self):
|
||||
"""end batch; start updating display immediately"""
|
||||
self.in_batch =0
|
||||
if self.dirty: self.UpdWindow()
|
||||
|
||||
def UpdWindow(self):
|
||||
"""sync display with text buffer"""
|
||||
html =self.html
|
||||
html.SetPage(self.text)
|
||||
self.dirty =0
|
||||
# scroll to the end
|
||||
(x,y) =html.GetVirtualSize()
|
||||
html.Scroll(0, y)
|
||||
|
||||
def AddText(self, text, style=None):
|
||||
"""write text to output window"""
|
||||
# a trick needed to defer default from compile-time to execute-time
|
||||
if style ==None: style =self.out_style
|
||||
if 0 and __debug__: sys.__stdout__.write(text)
|
||||
# handle entities
|
||||
for (symbol, eref) in self.erefs:
|
||||
text = text.replace(symbol, eref)
|
||||
# replace newlines
|
||||
text = text.replace("\n", style[2])
|
||||
# add to contents
|
||||
self.text =self.text +style[0] +text +style[1]
|
||||
if not self.in_batch: self.UpdWindow()
|
||||
else: self.dirty =1
|
||||
if self.html_debug:
|
||||
# html debug output needn't to be too large
|
||||
self.view.SetValue(self.text[-4096:])
|
||||
|
||||
def write(self, str, style=None):
|
||||
"""stdout-like interface"""
|
||||
if style ==None: style =self.out_style
|
||||
# do not process incomplete lines
|
||||
if len(str) <1:
|
||||
# hm... what was i supposed to do?
|
||||
return
|
||||
elif str[-1] !="\n":
|
||||
self.line_buffer =self.line_buffer +str
|
||||
else:
|
||||
self.AddText(self.line_buffer +str, style)
|
||||
self.line_buffer =""
|
||||
|
||||
def flush(self, style=None):
|
||||
"""write out all that was left in line buffer"""
|
||||
if style ==None: style =self.out_style
|
||||
self.AddText(self.line_buffer +"\n", style)
|
||||
|
||||
def write_in(self, str, style=None):
|
||||
"""write text in "input" style"""
|
||||
if style ==None: style =self.in_style
|
||||
self.AddText(str, style)
|
||||
|
||||
def write_exc(self, str, style=None):
|
||||
"""write text in "exception" style"""
|
||||
if style ==None: style =self.exc_style
|
||||
self.AddText(str, style)
|
||||
|
||||
class PyShell(wxPanel):
|
||||
"""interactive Python shell with wxPython interface
|
||||
|
||||
"""
|
||||
def __init__(self, parent, globals=globals(), locals={},
|
||||
id=-1, pos=wxDefaultPosition, size=wxDefaultSize,
|
||||
style=wxTAB_TRAVERSAL, name="shell"):
|
||||
"""create PyShell window"""
|
||||
wxPanel.__init__(self, parent, id, pos, size, style, name)
|
||||
self.globals =globals
|
||||
self.locals =locals
|
||||
splitter =wxSplitterWindow(self, -1)
|
||||
self.output =PyShellOutput(splitter)
|
||||
self.input =PyShellInput(splitter, self)
|
||||
self.input.SetFocus()
|
||||
splitter.SplitHorizontally(self.input, self.output)
|
||||
splitter.SetSashPosition(100)
|
||||
splitter.SetMinimumPaneSize(20)
|
||||
self.splitter =splitter
|
||||
EVT_SET_FOCUS(self, self.OnSetFocus)
|
||||
EVT_SIZE(self, self.OnSize)
|
||||
|
||||
def OnSetFocus(self, event):
|
||||
self.input.SetFocus()
|
||||
|
||||
def TryExec(self, source, symbol="single"):
|
||||
"""Compile and run some source in the interpreter.
|
||||
|
||||
borrowed from code.InteractiveInterpreter().runsource()
|
||||
as i said above, i would rather like to inherit from that class
|
||||
|
||||
returns 1 if more input is required, or 0, otherwise
|
||||
"""
|
||||
try:
|
||||
cc = code.compile_command(source, symbol=symbol)
|
||||
except (OverflowError, SyntaxError):
|
||||
# [als] hm... never seen anything of that kind
|
||||
self.ShowSyntaxError()
|
||||
return 0
|
||||
if cc is None:
|
||||
# source is incomplete
|
||||
return 1
|
||||
# source is sucessfully compiled
|
||||
out =self.output
|
||||
# redirect system stdout to the output window
|
||||
prev_out =sys.stdout
|
||||
sys.stdout =out
|
||||
# begin printout batch (html updates are deferred until EndBatch())
|
||||
out.BeginBatch()
|
||||
out.write_in(source)
|
||||
try:
|
||||
exec cc in self.globals, self.locals
|
||||
except SystemExit:
|
||||
# SystemExit is not handled and has to be re-raised
|
||||
raise
|
||||
except:
|
||||
# all other exceptions produce traceback output
|
||||
self.ShowException()
|
||||
# switch back to saved stdout
|
||||
sys.stdout =prev_out
|
||||
# commit printout
|
||||
out.flush()
|
||||
out.EndBatch()
|
||||
return 0
|
||||
|
||||
def ShowException(self):
|
||||
"""display the traceback for the latest exception"""
|
||||
(etype, value, tb) =sys.exc_info()
|
||||
# remove myself from traceback
|
||||
tblist =traceback.extract_tb(tb)[1:]
|
||||
msg = ' '.join(traceback.format_exception_only(etype, value)
|
||||
+traceback.format_list(tblist))
|
||||
self.output.write_exc(msg)
|
||||
|
||||
def ShowSyntaxError(self):
|
||||
"""display message about syntax error (no traceback here)"""
|
||||
(etype, value, tb) =sys.exc_info()
|
||||
msg = ' '.join(traceback.format_exception_only(etype, value))
|
||||
self.output.write_exc(msg)
|
||||
|
||||
def OnSize(self, event):
|
||||
self.splitter.SetSize(self.GetClientSize())
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
class MyFrame(wxFrame):
|
||||
"""Very standard Frame class. Nothing special here!"""
|
||||
def __init__(self, parent=NULL, id =-1,
|
||||
title="wxPython Interactive Shell"):
|
||||
wxFrame.__init__(self, parent, id, title)
|
||||
self.shell =PyShell(self)
|
||||
|
||||
class MyApp(wxApp):
|
||||
"""Demonstrates usage of both default and customized shells"""
|
||||
def OnInit(self):
|
||||
frame = MyFrame()
|
||||
frame.Show(True)
|
||||
self.SetTopWindow(frame)
|
||||
## PyShellInput.PS1 =" let's get some work done..."
|
||||
## PyShellInput.PS2 =" ok, what do you really mean?"
|
||||
## PyShellOutput.in_style =(
|
||||
## "<I><font color=\"#008000\"><tt>>>> ",
|
||||
## "</tt></font></I><br>\n", "<br>\n... ")
|
||||
## PyShellOutput.out_style =(
|
||||
## "<font color=\"#000080\"><tt>",
|
||||
## "</tt></font><br>\n", "<br>\n")
|
||||
## PyShellOutput.exc_style =("<B><font color=\"#FF0000\"><tt>",
|
||||
## "</tt></font></B>\n", "<br>\n")
|
||||
## PyShellOutput.intro ="<I><B>Customized wxPython Shell</B>" \
|
||||
## "<br><-- move this sash to see html debug output</I><br>\n"
|
||||
## PyShellOutput.html_debug =1
|
||||
## frame = MyFrame(title="Customized wxPython Shell")
|
||||
## frame.Show(True)
|
||||
return True
|
||||
|
||||
app = MyApp(0)
|
||||
app.MainLoop()
|
||||
__doc__ = wx.lib.shell.__doc__
|
||||
|
||||
PyShell = wx.lib.shell.PyShell
|
||||
PyShellInput = wx.lib.shell.PyShellInput
|
||||
PyShellOutput = wx.lib.shell.PyShellOutput
|
||||
|
||||
@@ -1,114 +1,9 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.splashscreen
|
||||
# Purpose: A simple frame that can display a bitmap and closes itself
|
||||
# after a specified timeout or a mouse click.
|
||||
#
|
||||
# Author: Mike Fletcher, Robin Dunn
|
||||
#
|
||||
# Created: 19-Nov-1999
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 1999 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
"""
|
||||
A Splash Screen implemented in Python.
|
||||
import wx.lib.splashscreen
|
||||
|
||||
NOTE: Now that wxWindows has a wxSplashScrren class and it is wrapped
|
||||
in wxPython this class is deprecated. See the docs for more details.
|
||||
"""
|
||||
__doc__ = wx.lib.splashscreen.__doc__
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class SplashScreen(wxFrame):
|
||||
def __init__(self, parent, ID=-1, title="SplashScreen",
|
||||
style=wxSIMPLE_BORDER|wxSTAY_ON_TOP,
|
||||
duration=1500, bitmapfile="bitmaps/splashscreen.bmp",
|
||||
callback = None):
|
||||
'''
|
||||
parent, ID, title, style -- see wxFrame
|
||||
duration -- milliseconds to display the splash screen
|
||||
bitmapfile -- absolute or relative pathname to image file
|
||||
callback -- if specified, is called when timer completes, callback is
|
||||
responsible for closing the splash screen
|
||||
'''
|
||||
### Loading bitmap
|
||||
self.bitmap = bmp = wxImage(bitmapfile, wxBITMAP_TYPE_ANY).ConvertToBitmap()
|
||||
|
||||
### Determine size of bitmap to size window...
|
||||
size = (bmp.GetWidth(), bmp.GetHeight())
|
||||
|
||||
# size of screen
|
||||
width = wxSystemSettings_GetSystemMetric(wxSYS_SCREEN_X)
|
||||
height = wxSystemSettings_GetSystemMetric(wxSYS_SCREEN_Y)
|
||||
pos = ((width-size[0])/2, (height-size[1])/2)
|
||||
|
||||
# check for overflow...
|
||||
if pos[0] < 0:
|
||||
size = (wxSystemSettings_GetSystemMetric(wxSYS_SCREEN_X), size[1])
|
||||
if pos[1] < 0:
|
||||
size = (size[0], wxSystemSettings_GetSystemMetric(wxSYS_SCREEN_Y))
|
||||
|
||||
wxFrame.__init__(self, parent, ID, title, pos, size, style)
|
||||
EVT_LEFT_DOWN(self, self.OnMouseClick)
|
||||
EVT_CLOSE(self, self.OnCloseWindow)
|
||||
EVT_PAINT(self, self.OnPaint)
|
||||
EVT_ERASE_BACKGROUND(self, self.OnEraseBG)
|
||||
|
||||
self.Show(True)
|
||||
|
||||
|
||||
class SplashTimer(wxTimer):
|
||||
def __init__(self, targetFunction):
|
||||
self.Notify = targetFunction
|
||||
wxTimer.__init__(self)
|
||||
|
||||
if callback is None:
|
||||
callback = self.OnSplashExitDefault
|
||||
|
||||
self.timer = SplashTimer(callback)
|
||||
self.timer.Start(duration, 1) # one-shot only
|
||||
|
||||
def OnPaint(self, event):
|
||||
dc = wxPaintDC(self)
|
||||
dc.DrawBitmap(self.bitmap, 0,0, False)
|
||||
|
||||
def OnEraseBG(self, event):
|
||||
pass
|
||||
|
||||
def OnSplashExitDefault(self, event=None):
|
||||
self.Close(True)
|
||||
|
||||
def OnCloseWindow(self, event=None):
|
||||
self.Show(False)
|
||||
self.timer.Stop()
|
||||
del self.timer
|
||||
self.Destroy()
|
||||
|
||||
def OnMouseClick(self, event):
|
||||
self.timer.Notify()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
class DemoApp(wxApp):
|
||||
def OnInit(self):
|
||||
wxImage_AddHandler(wxJPEGHandler())
|
||||
wxImage_AddHandler(wxPNGHandler())
|
||||
wxImage_AddHandler(wxGIFHandler())
|
||||
self.splash = SplashScreen(NULL, bitmapfile="splashscreen.jpg", callback=self.OnSplashExit)
|
||||
self.splash.Show(True)
|
||||
self.SetTopWindow(self.splash)
|
||||
return True
|
||||
def OnSplashExit(self, event=None):
|
||||
print "Yay! Application callback worked!"
|
||||
self.splash.Close(True)
|
||||
del self.splash
|
||||
### Build working windows here...
|
||||
def test(sceneGraph=None):
|
||||
app = DemoApp(0)
|
||||
app.MainLoop()
|
||||
test()
|
||||
SplashScreen = wx.lib.splashscreen.SplashScreen
|
||||
|
||||
@@ -1,144 +1,8 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.stattext
|
||||
# Purpose: A generic wxGenStaticText class. Using this should
|
||||
# eliminate some of the platform differences in wxStaticText,
|
||||
# such as background colours and mouse sensitivity.
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 8-July-2002
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 2002 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file reverse renames symbols in the wx package to give
|
||||
## them their wx prefix again, for backwards compatibility.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class wxGenStaticText(wxPyControl):
|
||||
labelDelta = 1
|
||||
|
||||
def __init__(self, parent, ID, label,
|
||||
pos = wxDefaultPosition, size = wxDefaultSize,
|
||||
style = 0,
|
||||
name = "genstattext"):
|
||||
wxPyControl.__init__(self, parent, ID, pos, size, style|wxNO_BORDER,
|
||||
wxDefaultValidator, name)
|
||||
|
||||
wxPyControl.SetLabel(self, label) # don't check wxST_NO_AUTORESIZE yet
|
||||
self.SetPosition(pos)
|
||||
font = parent.GetFont()
|
||||
if not font.Ok():
|
||||
font = wxSystemSettings_GetSystemFont(wxSYS_DEFAULT_GUI_FONT)
|
||||
wxPyControl.SetFont(self, font) # same here
|
||||
|
||||
self.defBackClr = parent.GetBackgroundColour()
|
||||
if not self.defBackClr.Ok():
|
||||
self.defBackClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_3DFACE)
|
||||
self.SetBackgroundColour(self.defBackClr)
|
||||
|
||||
clr = parent.GetForegroundColour()
|
||||
if not clr.Ok():
|
||||
clr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNTEXT)
|
||||
self.SetForegroundColour(clr)
|
||||
|
||||
rw, rh = size
|
||||
bw, bh = self.GetBestSize()
|
||||
if rw == -1: rw = bw
|
||||
if rh == -1: rh = bh
|
||||
self.SetSize(wxSize(rw, rh))
|
||||
|
||||
EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
|
||||
EVT_PAINT(self, self.OnPaint)
|
||||
|
||||
|
||||
def SetLabel(self, label):
|
||||
"""
|
||||
Sets the static text label and updates the control's size to exactly
|
||||
fit the label unless the control has wxST_NO_AUTORESIZE flag.
|
||||
"""
|
||||
wxPyControl.SetLabel(self, label)
|
||||
style = self.GetWindowStyleFlag()
|
||||
if not style & wxST_NO_AUTORESIZE:
|
||||
self.SetSize(self.GetBestSize())
|
||||
self.Refresh()
|
||||
|
||||
|
||||
def SetFont(self, font):
|
||||
"""
|
||||
Sets the static text font and updates the control's size to exactly
|
||||
fit the label unless the control has wxST_NO_AUTORESIZE flag.
|
||||
"""
|
||||
wxPyControl.SetFont(self, font)
|
||||
style = self.GetWindowStyleFlag()
|
||||
if not style & wxST_NO_AUTORESIZE:
|
||||
self.SetSize(self.GetBestSize())
|
||||
self.Refresh()
|
||||
|
||||
|
||||
def DoGetBestSize(self):
|
||||
"""Overridden base class virtual. Determines the best size of the
|
||||
button based on the label size."""
|
||||
label = self.GetLabel()
|
||||
maxWidth = totalHeight = 0
|
||||
for line in label.split('\n'):
|
||||
if line == '':
|
||||
w, h = self.GetTextExtent('W') # empty lines have height too
|
||||
else:
|
||||
w, h = self.GetTextExtent(line)
|
||||
totalHeight += h
|
||||
maxWidth = max(maxWidth, w)
|
||||
return wxSize(maxWidth, totalHeight)
|
||||
|
||||
|
||||
def AcceptsFocus(self):
|
||||
"""Overridden base class virtual."""
|
||||
return False
|
||||
|
||||
|
||||
def OnPaint(self, event):
|
||||
dc = wxBufferedPaintDC(self)
|
||||
#dc = wxPaintDC(self)
|
||||
width, height = self.GetClientSize()
|
||||
if not width or not height:
|
||||
return
|
||||
|
||||
clr = self.GetBackgroundColour()
|
||||
backBrush = wxBrush(clr, wxSOLID)
|
||||
if wxPlatform == "__WXMAC__" and clr == self.defBackClr:
|
||||
# if colour still the default the use the striped background on Mac
|
||||
backBrush.SetMacTheme(1) # 1 == kThemeBrushDialogBackgroundActive
|
||||
dc.SetBackground(backBrush)
|
||||
|
||||
dc.SetTextForeground(self.GetForegroundColour())
|
||||
dc.Clear()
|
||||
dc.SetFont(self.GetFont())
|
||||
label = self.GetLabel()
|
||||
style = self.GetWindowStyleFlag()
|
||||
x = y = 0
|
||||
for line in label.split('\n'):
|
||||
if line == '':
|
||||
w, h = self.GetTextExtent('W') # empty lines have height too
|
||||
else:
|
||||
w, h = self.GetTextExtent(line)
|
||||
if style & wxALIGN_RIGHT:
|
||||
x = width - w
|
||||
if style & wxALIGN_CENTER:
|
||||
x = (width - w)/2
|
||||
dc.DrawText(line, x, y)
|
||||
y += h
|
||||
|
||||
|
||||
def OnEraseBackground(self, event):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
import wx.lib.stattext
|
||||
|
||||
__doc__ = wx.lib.stattext.__doc__
|
||||
|
||||
wxGenStaticText = wx.lib.stattext.GenStaticText
|
||||
|
||||
@@ -1,245 +1,11 @@
|
||||
"""
|
||||
A throbber displays an animated image that can be
|
||||
started, stopped, reversed, etc. Useful for showing
|
||||
an ongoing process (like most web browsers use) or
|
||||
simply for adding eye-candy to an application.
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
Throbbers utilize a wxTimer so that normal processing
|
||||
can continue unencumbered.
|
||||
"""
|
||||
import wx.lib.throbber
|
||||
|
||||
#
|
||||
# throbber.py - Cliff Wells <clifford.wells@comcast.net>
|
||||
#
|
||||
# Thanks to Harald Massa <harald.massa@suedvers.de> for
|
||||
# suggestions and sample code.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
import os
|
||||
import wx
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
THROBBER_EVENT = wx.NewEventType()
|
||||
def EVT_UPDATE_THROBBER(win, func):
|
||||
win.Connect(-1, -1, THROBBER_EVENT, func)
|
||||
|
||||
class UpdateThrobberEvent(wx.PyEvent):
|
||||
def __init__(self):
|
||||
wx.PyEvent.__init__(self)
|
||||
self.SetEventType(THROBBER_EVENT)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
class Throbber(wx.Panel):
|
||||
"""
|
||||
The first argument is either the name of a file that will be split into frames
|
||||
(a composite image) or a list of strings of image names that will be treated
|
||||
as individual frames. If a single (composite) image is given, then additional
|
||||
information must be provided: the number of frames in the image and the width
|
||||
of each frame. The first frame is treated as the "at rest" frame (it is not
|
||||
shown during animation, but only when Throbber.Rest() is called.
|
||||
A second, single image may be optionally specified to overlay on top of the
|
||||
animation. A label may also be specified to show on top of the animation.
|
||||
"""
|
||||
def __init__(self, parent, id,
|
||||
bitmap, # single (composite) bitmap or list of bitmaps
|
||||
pos = wx.DefaultPosition,
|
||||
size = wx.DefaultSize,
|
||||
frameDelay = 0.1,# time between frames
|
||||
frames = 0, # number of frames (only necessary for composite image)
|
||||
frameWidth = 0, # width of each frame (only necessary for composite image)
|
||||
label = None, # optional text to be displayed
|
||||
overlay = None, # optional image to overlay on animation
|
||||
reverse = 0, # reverse direction at end of animation
|
||||
style = 0, # window style
|
||||
name = "throbber"):
|
||||
wx.Panel.__init__(self, parent, id, pos, size, style, name)
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.running = (1 != 1)
|
||||
_seqTypes = (type([]), type(()))
|
||||
|
||||
# set size, guessing if necessary
|
||||
width, height = size
|
||||
if width == -1:
|
||||
if type(bitmap) in _seqTypes:
|
||||
width = bitmap[0].GetWidth()
|
||||
else:
|
||||
if frameWidth:
|
||||
width = frameWidth
|
||||
if height == -1:
|
||||
if type(bitmap) in _seqTypes:
|
||||
height = bitmap[0].GetHeight()
|
||||
else:
|
||||
height = bitmap.GetHeight()
|
||||
self.width, self.height = width, height
|
||||
|
||||
# double check it
|
||||
assert width != -1 and height != -1, "Unable to guess size"
|
||||
|
||||
if label:
|
||||
extentX, extentY = self.GetTextExtent(label)
|
||||
self.labelX = (width - extentX)/2
|
||||
self.labelY = (height - extentY)/2
|
||||
self.frameDelay = frameDelay
|
||||
self.current = 0
|
||||
self.direction = 1
|
||||
self.autoReverse = reverse
|
||||
self.overlay = overlay
|
||||
if overlay is not None:
|
||||
self.overlay = overlay
|
||||
self.overlayX = (width - self.overlay.GetWidth()) / 2
|
||||
self.overlayY = (height - self.overlay.GetHeight()) / 2
|
||||
self.showOverlay = overlay is not None
|
||||
self.showLabel = label is not None
|
||||
|
||||
# do we have a sequence of images?
|
||||
if type(bitmap) in _seqTypes:
|
||||
self.submaps = bitmap
|
||||
self.frames = len(self.submaps)
|
||||
# or a composite image that needs to be split?
|
||||
else:
|
||||
self.frames = frames
|
||||
self.submaps = []
|
||||
for chunk in range(frames):
|
||||
rect = (chunk * frameWidth, 0, width, height)
|
||||
self.submaps.append(bitmap.GetSubBitmap(rect))
|
||||
|
||||
# self.sequence can be changed, but it's not recommended doing it
|
||||
# while the throbber is running. self.sequence[0] should always
|
||||
# refer to whatever frame is to be shown when 'resting' and be sure
|
||||
# that no item in self.sequence >= self.frames or < 0!!!
|
||||
self.sequence = range(self.frames)
|
||||
|
||||
self.SetClientSize((width, height))
|
||||
|
||||
timerID = wx.NewId()
|
||||
self.timer = wx.Timer(self, timerID)
|
||||
|
||||
EVT_UPDATE_THROBBER(self, self.Rotate)
|
||||
wx.EVT_PAINT(self, self.OnPaint)
|
||||
wx.EVT_TIMER(self, timerID, self.OnTimer)
|
||||
wx.EVT_WINDOW_DESTROY(self, self.OnDestroyWindow)
|
||||
|
||||
|
||||
def OnTimer(self, event):
|
||||
wx.PostEvent(self, UpdateThrobberEvent())
|
||||
|
||||
|
||||
def OnDestroyWindow(self, event):
|
||||
self.Stop()
|
||||
event.Skip()
|
||||
|
||||
|
||||
def Draw(self, dc):
|
||||
dc.DrawBitmap(self.submaps[self.sequence[self.current]], 0, 0, True)
|
||||
if self.overlay and self.showOverlay:
|
||||
dc.DrawBitmap(self.overlay, self.overlayX, self.overlayY, True)
|
||||
if self.label and self.showLabel:
|
||||
dc.DrawText(self.label, self.labelX, self.labelY)
|
||||
dc.SetTextForeground(wx.WHITE)
|
||||
dc.DrawText(self.label, self.labelX-1, self.labelY-1)
|
||||
|
||||
|
||||
def OnPaint(self, event):
|
||||
self.Draw(wx.PaintDC(self))
|
||||
event.Skip()
|
||||
|
||||
|
||||
def Rotate(self, event):
|
||||
self.current += self.direction
|
||||
if self.current >= len(self.sequence):
|
||||
if self.autoReverse:
|
||||
self.Reverse()
|
||||
self.current = len(self.sequence) - 1
|
||||
else:
|
||||
self.current = 1
|
||||
if self.current < 1:
|
||||
if self.autoReverse:
|
||||
self.Reverse()
|
||||
self.current = 1
|
||||
else:
|
||||
self.current = len(self.sequence) - 1
|
||||
self.Draw(wx.ClientDC(self))
|
||||
|
||||
|
||||
# --------- public methods ---------
|
||||
def SetFont(self, font):
|
||||
"""Set the font for the label"""
|
||||
wx.Panel.SetFont(self, font)
|
||||
self.SetLabel(self.label)
|
||||
self.Draw(wx.ClientDC(self))
|
||||
|
||||
|
||||
def Rest(self):
|
||||
"""Stop the animation and return to frame 0"""
|
||||
self.Stop()
|
||||
self.current = 0
|
||||
self.Draw(wx.ClientDC(self))
|
||||
|
||||
|
||||
def Reverse(self):
|
||||
"""Change the direction of the animation"""
|
||||
self.direction = -self.direction
|
||||
|
||||
|
||||
def Running(self):
|
||||
"""Returns True if the animation is running"""
|
||||
return self.running
|
||||
|
||||
|
||||
def Start(self):
|
||||
"""Start the animation"""
|
||||
if not self.running:
|
||||
self.running = not self.running
|
||||
self.timer.Start(self.frameDelay * 1000)
|
||||
|
||||
|
||||
def Stop(self):
|
||||
"""Stop the animation"""
|
||||
if self.running:
|
||||
self.timer.Stop()
|
||||
self.running = not self.running
|
||||
|
||||
|
||||
def SetFrameDelay(self, frameDelay = 0.05):
|
||||
"""Delay between each frame"""
|
||||
self.frameDelay = frameDelay
|
||||
if self.running:
|
||||
self.Stop()
|
||||
self.Start()
|
||||
|
||||
|
||||
def ToggleOverlay(self, state = None):
|
||||
"""Toggle the overlay image"""
|
||||
if state is None:
|
||||
self.showOverlay = not self.showOverlay
|
||||
else:
|
||||
self.showOverlay = state
|
||||
self.Draw(wx.ClientDC(self))
|
||||
|
||||
|
||||
def ToggleLabel(self, state = None):
|
||||
"""Toggle the label"""
|
||||
if state is None:
|
||||
self.showLabel = not self.showLabel
|
||||
else:
|
||||
self.showLabel = state
|
||||
self.Draw(wx.ClientDC(self))
|
||||
|
||||
|
||||
def SetLabel(self, label):
|
||||
"""Change the text of the label"""
|
||||
self.label = label
|
||||
if label:
|
||||
extentX, extentY = self.GetTextExtent(label)
|
||||
self.labelX = (self.width - extentX)/2
|
||||
self.labelY = (self.height - extentY)/2
|
||||
self.Draw(wx.ClientDC(self))
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
__doc__ = wx.lib.throbber.__doc__
|
||||
|
||||
EVT_UPDATE_THROBBER = wx.lib.throbber.EVT_UPDATE_THROBBER
|
||||
Throbber = wx.lib.throbber.Throbber
|
||||
UpdateThrobberEvent = wx.lib.throbber.UpdateThrobberEvent
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,569 +1,8 @@
|
||||
"""
|
||||
VTK is now including a package for using VTK with wxPython, so this
|
||||
module is now officially nothing but ancient history. If for some
|
||||
strange reason you really need this code (I don't know why, it didn't
|
||||
work all that well anyway,) then just remove the triple quotes below.
|
||||
I'm told that the module from Kitware is excellent and so you should
|
||||
really use it. See the URL below to get a copy from CVS.
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
http://public.kitware.com/cgi-bin/cvsweb.cgi/VTK/Wrapping/Python/vtk/wx/
|
||||
"""
|
||||
import wx.lib.vtk
|
||||
|
||||
print __doc__
|
||||
__doc__ = wx.lib.vtk.__doc__
|
||||
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.vtk
|
||||
# Purpose: Provides a wrapper around the vtkRenderWindow from the
|
||||
# VTK Visualization Toolkit. Requires the VTK Python
|
||||
# extensions from http://www.kitware.com/
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 16-Nov-1999
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 1999 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
# This class has been rewritten and improved by Prabhu Ramachandran
|
||||
# <prabhu@aero.iitm.ernet.in>. It has been tested under Win32 and
|
||||
# Linux. Many thanks to Eric Boix <frog@creatis.insa-lyon.fr> for
|
||||
# testing it under Windows and finding and fixing many errors.
|
||||
# Thanks also to Sebastien BARRE <sebastien@barre.nom.fr> for his
|
||||
# suggestions.
|
||||
|
||||
|
||||
try:
|
||||
from vtkpython import *
|
||||
except ImportError:
|
||||
raise ImportError, "VTK extension module not found"
|
||||
|
||||
from wxPython.wx import *
|
||||
import math
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
def debug(msg):
|
||||
if DEBUG:
|
||||
print msg
|
||||
|
||||
|
||||
class wxVTKRenderWindowBase(wxWindow):
|
||||
"""
|
||||
A base class that enables one to embed a vtkRenderWindow into
|
||||
a wxPython widget. This class embeds the RenderWindow correctly
|
||||
under different platforms. Provided are some empty methods that
|
||||
can be overloaded to provide a user defined interaction behaviour.
|
||||
The event handling functions have names that are similar to the
|
||||
ones in the vtkInteractorStyle class included with VTK.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, id, position=wxDefaultPosition,
|
||||
size=wxDefaultSize, style=0):
|
||||
wxWindow.__init__(self, parent, id, position, size, style | wxWANTS_CHARS)
|
||||
self._RenderWindow = vtkRenderWindow()
|
||||
|
||||
self.__InExpose = 0
|
||||
self.__Created = 0
|
||||
|
||||
if wxPlatform != '__WXMSW__':
|
||||
# We can't get the handle in wxGTK until after the widget
|
||||
# is created, the window create event happens later so we'll
|
||||
# catch it there
|
||||
EVT_WINDOW_CREATE(self, self.OnCreateWindow)
|
||||
EVT_PAINT (self, self.OnExpose)
|
||||
else:
|
||||
# but in MSW, the window create event happens durring the above
|
||||
# call to __init__ so we have to do it here.
|
||||
hdl = self.GetHandle()
|
||||
self._RenderWindow.SetWindowInfo(str(hdl))
|
||||
EVT_PAINT (self, self.OnExpose)
|
||||
self.__Created = 1
|
||||
|
||||
# common for all platforms
|
||||
EVT_SIZE (self, self.OnConfigure)
|
||||
|
||||
# setup the user defined events.
|
||||
self.SetupEvents()
|
||||
|
||||
|
||||
def SetupEvents(self):
|
||||
"Setup the user defined event bindings."
|
||||
# Remember to bind everything to self.box and NOT self
|
||||
EVT_LEFT_DOWN (self, self.OnLeftButtonDown)
|
||||
EVT_MIDDLE_DOWN (self, self.OnMiddleButtonDown)
|
||||
EVT_RIGHT_DOWN (self, self.OnRightButtonDown)
|
||||
EVT_LEFT_UP (self, self.OnLeftButtonUp)
|
||||
EVT_MIDDLE_UP (self, self.OnMiddleButtonUp)
|
||||
EVT_RIGHT_UP (self, self.OnRightButtonUp)
|
||||
EVT_MOTION (self, self.OnMouseMove)
|
||||
EVT_ENTER_WINDOW (self, self.OnEnter)
|
||||
EVT_LEAVE_WINDOW (self, self.OnLeave)
|
||||
EVT_CHAR (self, self.OnChar)
|
||||
# Add your bindings if you want them in the derived class.
|
||||
|
||||
|
||||
def GetRenderer(self):
|
||||
self._RenderWindow.GetRenderers().InitTraversal()
|
||||
return self._RenderWindow.GetRenderers().GetNextItem()
|
||||
|
||||
|
||||
def GetRenderWindow(self):
|
||||
return self._RenderWindow
|
||||
|
||||
|
||||
def Render(self):
|
||||
if self.__Created:
|
||||
# if block needed because calls to render before creation
|
||||
# will prevent the renderwindow from being embedded into a
|
||||
# wxPython widget.
|
||||
self._RenderWindow.Render()
|
||||
|
||||
|
||||
def OnExpose(self, event):
|
||||
# I need this for the MDIDemo. Somehow OnCreateWindow was
|
||||
# not getting called.
|
||||
if not self.__Created:
|
||||
self.OnCreateWindow(event)
|
||||
if (not self.__InExpose):
|
||||
self.__InExpose = 1
|
||||
dc = wxPaintDC(self)
|
||||
self._RenderWindow.Render()
|
||||
self.__InExpose = 0
|
||||
|
||||
|
||||
def OnCreateWindow(self, event):
|
||||
hdl = self.GetHandle()
|
||||
try:
|
||||
self._RenderWindow.SetParentInfo(str(hdl))
|
||||
except:
|
||||
self._RenderWindow.SetWindowInfo(str(hdl))
|
||||
msg = "Warning:\n "\
|
||||
"Unable to call vtkRenderWindow.SetParentInfo\n\n"\
|
||||
"Using the SetWindowInfo method instead. This\n"\
|
||||
"is likely to cause a lot of flicker when\n"\
|
||||
"rendering in the vtkRenderWindow. Please\n"\
|
||||
"use a recent Nightly VTK release (later than\n"\
|
||||
"March 10 2001) to eliminate this problem."
|
||||
dlg = wxMessageDialog(NULL, msg, "Warning!",
|
||||
wxOK |wxICON_INFORMATION)
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
self.__Created = 1
|
||||
|
||||
|
||||
def OnConfigure(self, event):
|
||||
sz = self.GetSize()
|
||||
self.SetSize(sz)
|
||||
# Ugly hack that according to Eric Boix is necessary under
|
||||
# Windows. If possible Try commenting this out and test under
|
||||
# Windows.
|
||||
#self._RenderWindow.GetSize()
|
||||
#
|
||||
self._RenderWindow.SetSize(sz.width, sz.height)
|
||||
|
||||
|
||||
def OnLeftButtonDown(self, event):
|
||||
"Left mouse button pressed."
|
||||
pass
|
||||
|
||||
|
||||
def OnMiddleButtonDown(self, event):
|
||||
"Middle mouse button pressed."
|
||||
pass
|
||||
|
||||
|
||||
def OnRightButtonDown(self, event):
|
||||
"Right mouse button pressed."
|
||||
pass
|
||||
|
||||
|
||||
def OnLeftButtonUp(self, event):
|
||||
"Left mouse button released."
|
||||
pass
|
||||
|
||||
|
||||
def OnMiddleButtonUp(self, event):
|
||||
"Middle mouse button released."
|
||||
pass
|
||||
|
||||
|
||||
def OnRightButtonUp(self, event):
|
||||
"Right mouse button released."
|
||||
pass
|
||||
|
||||
|
||||
def OnMouseMove(self, event):
|
||||
"Mouse has moved."
|
||||
pass
|
||||
|
||||
|
||||
def OnEnter(self, event):
|
||||
"Entering the vtkRenderWindow."
|
||||
pass
|
||||
|
||||
|
||||
def OnLeave(self, event):
|
||||
"Leaving the vtkRenderWindow."
|
||||
pass
|
||||
|
||||
|
||||
def OnChar(self, event):
|
||||
"Process Key events."
|
||||
pass
|
||||
|
||||
|
||||
def OnKeyDown(self, event):
|
||||
"Key pressed down."
|
||||
pass
|
||||
|
||||
|
||||
def OnKeyUp(self, event):
|
||||
"Key released."
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
class wxVTKRenderWindow(wxVTKRenderWindowBase):
|
||||
"""
|
||||
An example of a fully functional wxVTKRenderWindow that is
|
||||
based on the vtkRenderWidget.py provided with the VTK sources.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, id, position=wxDefaultPosition,
|
||||
size=wxDefaultSize, style=0):
|
||||
wxVTKRenderWindowBase.__init__(self, parent, id, position, size,
|
||||
style)
|
||||
|
||||
self._CurrentRenderer = None
|
||||
self._CurrentCamera = None
|
||||
self._CurrentZoom = 1.0
|
||||
self._CurrentLight = None
|
||||
|
||||
self._ViewportCenterX = 0
|
||||
self._ViewportCenterY = 0
|
||||
|
||||
self._Picker = vtkCellPicker()
|
||||
self._PickedAssembly = None
|
||||
self._PickedProperty = vtkProperty()
|
||||
self._PickedProperty.SetColor(1,0,0)
|
||||
self._PrePickedProperty = None
|
||||
|
||||
self._OldFocus = None
|
||||
|
||||
# these record the previous mouse position
|
||||
self._LastX = 0
|
||||
self._LastY = 0
|
||||
|
||||
|
||||
def OnLeftButtonDown(self, event):
|
||||
"Left mouse button pressed."
|
||||
self.StartMotion(event)
|
||||
|
||||
|
||||
def OnMiddleButtonDown(self, event):
|
||||
"Middle mouse button pressed."
|
||||
self.StartMotion(event)
|
||||
|
||||
|
||||
def OnRightButtonDown(self, event):
|
||||
"Right mouse button pressed."
|
||||
self.StartMotion(event)
|
||||
|
||||
|
||||
def OnLeftButtonUp(self, event):
|
||||
"Left mouse button released."
|
||||
self.EndMotion(event)
|
||||
|
||||
|
||||
def OnMiddleButtonUp(self, event):
|
||||
"Middle mouse button released."
|
||||
self.EndMotion(event)
|
||||
|
||||
|
||||
def OnRightButtonUp(self, event):
|
||||
"Right mouse button released."
|
||||
self.EndMotion(event)
|
||||
|
||||
|
||||
def OnMouseMove(self, event):
|
||||
event.x, event.y = event.GetPositionTuple()
|
||||
if event.LeftIsDown():
|
||||
if event.ShiftDown():
|
||||
self.Pan(event.x, event.y)
|
||||
else:
|
||||
self.Rotate(event.x, event.y)
|
||||
|
||||
elif event.MiddleIsDown():
|
||||
self.Pan(event.x, event.y)
|
||||
|
||||
elif event.RightIsDown():
|
||||
self.Zoom(event.x, event.y)
|
||||
|
||||
|
||||
def OnEnter(self, event):
|
||||
self.__OldFocus = wxWindow_FindFocus()
|
||||
self.SetFocus()
|
||||
x, y = event.GetPositionTuple()
|
||||
self.UpdateRenderer(x,y)
|
||||
|
||||
|
||||
def OnLeave(self, event):
|
||||
if (self._OldFocus != None):
|
||||
self.__OldFocus.SetFocus()
|
||||
|
||||
|
||||
def OnChar(self, event):
|
||||
key = event.KeyCode()
|
||||
if (key == ord('r')) or (key == ord('R')):
|
||||
self.Reset()
|
||||
elif (key == ord('w')) or (key == ord('W')):
|
||||
self.Wireframe()
|
||||
elif (key == ord('s')) or (key == ord('S')):
|
||||
self.Surface()
|
||||
elif (key == ord('p')) or (key == ord('P')):
|
||||
x, y = event.GetPositionTuple()
|
||||
self.PickActor(x, y)
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
|
||||
# Start of internal functions
|
||||
def GetZoomFactor(self):
|
||||
return self._CurrentZoom
|
||||
|
||||
|
||||
def SetZoomFactor(self, zf):
|
||||
self._CurrentZoom = zf
|
||||
|
||||
|
||||
def GetPicker(self):
|
||||
return self._Picker
|
||||
|
||||
|
||||
def Render(self):
|
||||
if (self._CurrentLight):
|
||||
light = self._CurrentLight
|
||||
light.SetPosition(self._CurrentCamera.GetPosition())
|
||||
light.SetFocalPoint(self._CurrentCamera.GetFocalPoint())
|
||||
|
||||
wxVTKRenderWindowBase.Render(self)
|
||||
|
||||
|
||||
def UpdateRenderer(self, x, y):
|
||||
"""
|
||||
UpdateRenderer will identify the renderer under the mouse and set
|
||||
up _CurrentRenderer, _CurrentCamera, and _CurrentLight.
|
||||
"""
|
||||
sz = self.GetSize()
|
||||
windowX = sz.width
|
||||
windowY = sz.height
|
||||
|
||||
renderers = self._RenderWindow.GetRenderers()
|
||||
numRenderers = renderers.GetNumberOfItems()
|
||||
|
||||
self._CurrentRenderer = None
|
||||
renderers.InitTraversal()
|
||||
for i in range(0,numRenderers):
|
||||
renderer = renderers.GetNextItem()
|
||||
vx,vy = (0,0)
|
||||
if (windowX > 1):
|
||||
vx = float (x)/(windowX-1)
|
||||
if (windowY > 1):
|
||||
vy = (windowY-float(y)-1)/(windowY-1)
|
||||
(vpxmin,vpymin,vpxmax,vpymax) = renderer.GetViewport()
|
||||
|
||||
if (vx >= vpxmin and vx <= vpxmax and
|
||||
vy >= vpymin and vy <= vpymax):
|
||||
self._CurrentRenderer = renderer
|
||||
self._ViewportCenterX = float(windowX)*(vpxmax-vpxmin)/2.0\
|
||||
+vpxmin
|
||||
self._ViewportCenterY = float(windowY)*(vpymax-vpymin)/2.0\
|
||||
+vpymin
|
||||
self._CurrentCamera = self._CurrentRenderer.GetActiveCamera()
|
||||
lights = self._CurrentRenderer.GetLights()
|
||||
lights.InitTraversal()
|
||||
self._CurrentLight = lights.GetNextItem()
|
||||
break
|
||||
|
||||
self._LastX = x
|
||||
self._LastY = y
|
||||
|
||||
|
||||
def GetCurrentRenderer(self):
|
||||
return self._CurrentRenderer
|
||||
|
||||
|
||||
def StartMotion(self, event):
|
||||
x, y = event.GetPositionTuple()
|
||||
self.UpdateRenderer(x,y)
|
||||
self.CaptureMouse()
|
||||
|
||||
|
||||
def EndMotion(self, event=None):
|
||||
if self._CurrentRenderer:
|
||||
self.Render()
|
||||
self.ReleaseMouse()
|
||||
|
||||
|
||||
def Rotate(self,x,y):
|
||||
if self._CurrentRenderer:
|
||||
|
||||
self._CurrentCamera.Azimuth(self._LastX - x)
|
||||
self._CurrentCamera.Elevation(y - self._LastY)
|
||||
self._CurrentCamera.OrthogonalizeViewUp()
|
||||
|
||||
self._LastX = x
|
||||
self._LastY = y
|
||||
|
||||
self._CurrentRenderer.ResetCameraClippingRange()
|
||||
self.Render()
|
||||
|
||||
|
||||
def PanAbsolute(self, x_vec, y_vec):
|
||||
if self._CurrentRenderer:
|
||||
|
||||
renderer = self._CurrentRenderer
|
||||
camera = self._CurrentCamera
|
||||
(pPoint0,pPoint1,pPoint2) = camera.GetPosition()
|
||||
(fPoint0,fPoint1,fPoint2) = camera.GetFocalPoint()
|
||||
|
||||
if (camera.GetParallelProjection()):
|
||||
renderer.SetWorldPoint(fPoint0,fPoint1,fPoint2,1.0)
|
||||
renderer.WorldToDisplay()
|
||||
fx,fy,fz = renderer.GetDisplayPoint()
|
||||
renderer.SetDisplayPoint(fx+x_vec,
|
||||
fy+y_vec,
|
||||
fz)
|
||||
renderer.DisplayToWorld()
|
||||
fx,fy,fz,fw = renderer.GetWorldPoint()
|
||||
camera.SetFocalPoint(fx,fy,fz)
|
||||
|
||||
renderer.SetWorldPoint(pPoint0,pPoint1,pPoint2,1.0)
|
||||
renderer.WorldToDisplay()
|
||||
fx,fy,fz = renderer.GetDisplayPoint()
|
||||
renderer.SetDisplayPoint(fx+x_vec,
|
||||
fy+y_vec,
|
||||
fz)
|
||||
renderer.DisplayToWorld()
|
||||
fx,fy,fz,fw = renderer.GetWorldPoint()
|
||||
camera.SetPosition(fx,fy,fz)
|
||||
|
||||
else:
|
||||
(fPoint0,fPoint1,fPoint2) = camera.GetFocalPoint()
|
||||
# Specify a point location in world coordinates
|
||||
renderer.SetWorldPoint(fPoint0,fPoint1,fPoint2,1.0)
|
||||
renderer.WorldToDisplay()
|
||||
# Convert world point coordinates to display coordinates
|
||||
dPoint = renderer.GetDisplayPoint()
|
||||
focalDepth = dPoint[2]
|
||||
|
||||
aPoint0 = self._ViewportCenterX + x_vec
|
||||
aPoint1 = self._ViewportCenterY + y_vec
|
||||
|
||||
renderer.SetDisplayPoint(aPoint0,aPoint1,focalDepth)
|
||||
renderer.DisplayToWorld()
|
||||
|
||||
(rPoint0,rPoint1,rPoint2,rPoint3) = renderer.GetWorldPoint()
|
||||
if (rPoint3 != 0.0):
|
||||
rPoint0 = rPoint0/rPoint3
|
||||
rPoint1 = rPoint1/rPoint3
|
||||
rPoint2 = rPoint2/rPoint3
|
||||
|
||||
camera.SetFocalPoint((fPoint0 - rPoint0) + fPoint0,
|
||||
(fPoint1 - rPoint1) + fPoint1,
|
||||
(fPoint2 - rPoint2) + fPoint2)
|
||||
|
||||
camera.SetPosition((fPoint0 - rPoint0) + pPoint0,
|
||||
(fPoint1 - rPoint1) + pPoint1,
|
||||
(fPoint2 - rPoint2) + pPoint2)
|
||||
|
||||
self.Render()
|
||||
|
||||
|
||||
def Pan(self, x, y):
|
||||
self.PanAbsolute(x - self._LastX, - y + self._LastY)
|
||||
self._LastX = x
|
||||
self._LastY = y
|
||||
|
||||
|
||||
def Zoom(self,x,y):
|
||||
if self._CurrentRenderer:
|
||||
|
||||
renderer = self._CurrentRenderer
|
||||
camera = self._CurrentCamera
|
||||
|
||||
zoomFactor = math.pow(1.02,(0.5*(self._LastY - y)))
|
||||
self._CurrentZoom = self._CurrentZoom * zoomFactor
|
||||
|
||||
if camera.GetParallelProjection():
|
||||
parallelScale = camera.GetParallelScale()/zoomFactor
|
||||
camera.SetParallelScale(parallelScale)
|
||||
else:
|
||||
camera.Dolly(zoomFactor)
|
||||
renderer.ResetCameraClippingRange()
|
||||
|
||||
self._LastX = x
|
||||
self._LastY = y
|
||||
|
||||
self.Render()
|
||||
|
||||
|
||||
def Reset(self):
|
||||
if self._CurrentRenderer:
|
||||
self._CurrentRenderer.ResetCamera()
|
||||
|
||||
self.Render()
|
||||
|
||||
|
||||
def Wireframe(self):
|
||||
actors = self._CurrentRenderer.GetActors()
|
||||
numActors = actors.GetNumberOfItems()
|
||||
actors.InitTraversal()
|
||||
for i in range(0,numActors):
|
||||
actor = actors.GetNextItem()
|
||||
actor.GetProperty().SetRepresentationToWireframe()
|
||||
|
||||
self.Render()
|
||||
|
||||
|
||||
def Surface(self):
|
||||
actors = self._CurrentRenderer.GetActors()
|
||||
numActors = actors.GetNumberOfItems()
|
||||
actors.InitTraversal()
|
||||
for i in range(0,numActors):
|
||||
actor = actors.GetNextItem()
|
||||
actor.GetProperty().SetRepresentationToSurface()
|
||||
|
||||
self.Render()
|
||||
|
||||
|
||||
def PickActor(self,x,y):
|
||||
if self._CurrentRenderer:
|
||||
renderer = self._CurrentRenderer
|
||||
picker = self._Picker
|
||||
|
||||
windowY = self.GetSize().height
|
||||
picker.Pick(x,(windowY - y - 1),0.0,renderer)
|
||||
assembly = picker.GetAssembly()
|
||||
|
||||
if (self._PickedAssembly != None and
|
||||
self._PrePickedProperty != None):
|
||||
self._PickedAssembly.SetProperty(self._PrePickedProperty)
|
||||
# release hold of the property
|
||||
self._PrePickedProperty.UnRegister(self._PrePickedProperty)
|
||||
self._PrePickedProperty = None
|
||||
|
||||
if (assembly != None):
|
||||
self._PickedAssembly = assembly
|
||||
self._PrePickedProperty = self._PickedAssembly.GetProperty()
|
||||
# hold onto the property
|
||||
self._PrePickedProperty.Register(self._PrePickedProperty)
|
||||
self._PickedAssembly.SetProperty(self._PickedProperty)
|
||||
|
||||
self.Render()
|
||||
'''
|
||||
|
||||
@@ -1,470 +0,0 @@
|
||||
"""
|
||||
This is a port of Konrad Hinsen's tkPlotCanvas.py plotting module.
|
||||
After thinking long and hard I came up with the name "wxPlotCanvas.py".
|
||||
|
||||
This file contains two parts; first the re-usable library stuff, then, after
|
||||
a "if __name__=='__main__'" test, a simple frame and a few default plots
|
||||
for testing.
|
||||
|
||||
Harm van der Heijden, feb 1999
|
||||
|
||||
Original comment follows below:
|
||||
# This module defines a plot widget for Tk user interfaces.
|
||||
# It supports only elementary line plots at the moment.
|
||||
# See the example at the end for documentation...
|
||||
#
|
||||
# Written by Konrad Hinsen <hinsen@cnrs-orleans.fr>
|
||||
# With contributions from RajGopal Srinivasan <raj@cherubino.med.jhmi.edu>
|
||||
# Last revision: 1998-7-28
|
||||
#
|
||||
"""
|
||||
|
||||
from wxPython import wx
|
||||
|
||||
# Not everybody will have Numeric, so let's be cool about it...
|
||||
try:
|
||||
import Numeric
|
||||
except:
|
||||
# bummer!
|
||||
msg = """This module requires the Numeric module, which could not be
|
||||
imported. It probably is not installed (it's not part of the standard
|
||||
Python distribution). See the Python site (http://www.python.org) for
|
||||
information on downloading source or binaries."""
|
||||
|
||||
if wx.wxPlatform == '__WXMSW__':
|
||||
d = wx.wxMessageDialog(wx.NULL, msg, "Numeric not found")
|
||||
if d.ShowModal() == wx.wxID_CANCEL:
|
||||
d = wx.wxMessageDialog(wx.NULL, "I kid you not! Pressing Cancel won't help you!", "Not a joke", wx.wxOK)
|
||||
d.ShowModal()
|
||||
else:
|
||||
print msg
|
||||
raise ImportError
|
||||
|
||||
#
|
||||
# Plotting classes...
|
||||
#
|
||||
class PolyPoints:
|
||||
|
||||
def __init__(self, points, attr):
|
||||
self.points = Numeric.array(points)
|
||||
self.scaled = self.points
|
||||
self.attributes = {}
|
||||
for name, value in self._attributes.items():
|
||||
try:
|
||||
value = attr[name]
|
||||
except KeyError: pass
|
||||
self.attributes[name] = value
|
||||
|
||||
def boundingBox(self):
|
||||
return Numeric.minimum.reduce(self.points), \
|
||||
Numeric.maximum.reduce(self.points)
|
||||
|
||||
def scaleAndShift(self, scale=1, shift=0):
|
||||
self.scaled = scale*self.points+shift
|
||||
|
||||
|
||||
class PolyLine(PolyPoints):
|
||||
|
||||
def __init__(self, points, **attr):
|
||||
PolyPoints.__init__(self, points, attr)
|
||||
|
||||
_attributes = {'color': 'black',
|
||||
'width': 1}
|
||||
|
||||
def draw(self, dc):
|
||||
color = self.attributes['color']
|
||||
width = self.attributes['width']
|
||||
arguments = []
|
||||
dc.SetPen(wx.wxPen(wx.wxNamedColour(color), width))
|
||||
dc.DrawLines(map(tuple,self.scaled))
|
||||
|
||||
|
||||
class PolyMarker(PolyPoints):
|
||||
|
||||
def __init__(self, points, **attr):
|
||||
|
||||
PolyPoints.__init__(self, points, attr)
|
||||
|
||||
_attributes = {'color': 'black',
|
||||
'width': 1,
|
||||
'fillcolor': None,
|
||||
'size': 2,
|
||||
'fillstyle': wx.wxSOLID,
|
||||
'outline': 'black',
|
||||
'marker': 'circle'}
|
||||
|
||||
def draw(self, dc):
|
||||
color = self.attributes['color']
|
||||
width = self.attributes['width']
|
||||
size = self.attributes['size']
|
||||
fillcolor = self.attributes['fillcolor']
|
||||
fillstyle = self.attributes['fillstyle']
|
||||
marker = self.attributes['marker']
|
||||
|
||||
dc.SetPen(wx.wxPen(wx.wxNamedColour(color),width))
|
||||
if fillcolor:
|
||||
dc.SetBrush(wx.wxBrush(wx.wxNamedColour(fillcolor),fillstyle))
|
||||
else:
|
||||
dc.SetBrush(wx.wxBrush(wx.wxNamedColour('black'), wx.wxTRANSPARENT))
|
||||
|
||||
self._drawmarkers(dc, self.scaled, marker, size)
|
||||
|
||||
def _drawmarkers(self, dc, coords, marker,size=1):
|
||||
f = eval('self._' +marker)
|
||||
for xc, yc in coords:
|
||||
f(dc, xc, yc, size)
|
||||
|
||||
def _circle(self, dc, xc, yc, size=1):
|
||||
dc.DrawEllipse(xc-2.5*size,yc-2.5*size,5.*size,5.*size)
|
||||
|
||||
def _dot(self, dc, xc, yc, size=1):
|
||||
dc.DrawPoint(xc,yc)
|
||||
|
||||
def _square(self, dc, xc, yc, size=1):
|
||||
dc.DrawRectangle(xc-2.5*size,yc-2.5*size,5.*size,5.*size)
|
||||
|
||||
def _triangle(self, dc, xc, yc, size=1):
|
||||
dc.DrawPolygon([(-0.5*size*5,0.2886751*size*5),
|
||||
(0.5*size*5,0.2886751*size*5),
|
||||
(0.0,-0.577350*size*5)],xc,yc)
|
||||
|
||||
def _triangle_down(self, dc, xc, yc, size=1):
|
||||
dc.DrawPolygon([(-0.5*size*5,-0.2886751*size*5),
|
||||
(0.5*size*5,-0.2886751*size*5),
|
||||
(0.0,0.577350*size*5)],xc,yc)
|
||||
|
||||
def _cross(self, dc, xc, yc, size=1):
|
||||
dc.DrawLine(xc-2.5*size,yc-2.5*size,xc+2.5*size,yc+2.5*size)
|
||||
dc.DrawLine(xc-2.5*size,yc+2.5*size,xc+2.5*size,yc-2.5*size)
|
||||
|
||||
def _plus(self, dc, xc, yc, size=1):
|
||||
dc.DrawLine(xc-2.5*size,yc,xc+2.5*size,yc)
|
||||
dc.DrawLine(xc,yc-2.5*size,xc,yc+2.5*size)
|
||||
|
||||
class PlotGraphics:
|
||||
|
||||
def __init__(self, objects):
|
||||
self.objects = objects
|
||||
|
||||
def boundingBox(self):
|
||||
p1, p2 = self.objects[0].boundingBox()
|
||||
for o in self.objects[1:]:
|
||||
p1o, p2o = o.boundingBox()
|
||||
p1 = Numeric.minimum(p1, p1o)
|
||||
p2 = Numeric.maximum(p2, p2o)
|
||||
return p1, p2
|
||||
|
||||
def scaleAndShift(self, scale=1, shift=0):
|
||||
for o in self.objects:
|
||||
o.scaleAndShift(scale, shift)
|
||||
|
||||
def draw(self, canvas):
|
||||
for o in self.objects:
|
||||
o.draw(canvas)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.objects)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.objects[item]
|
||||
|
||||
|
||||
class PlotCanvas(wx.wxWindow):
|
||||
|
||||
def __init__(self, parent, id=-1,
|
||||
pos = wx.wxDefaultPosition, size = wx.wxDefaultSize,
|
||||
style = 0, name = 'plotCanvas'):
|
||||
wx.wxWindow.__init__(self, parent, id, pos, size, style, name)
|
||||
self.border = (1,1)
|
||||
self.SetClientSizeWH(400,400)
|
||||
self.SetBackgroundColour(wx.wxNamedColour("white"))
|
||||
|
||||
wx.EVT_SIZE(self,self.reconfigure)
|
||||
wx.EVT_PAINT(self, self.OnPaint)
|
||||
self._setsize()
|
||||
self.last_draw = None
|
||||
# self.font = self._testFont(font)
|
||||
|
||||
def OnPaint(self, event):
|
||||
pdc = wx.wxPaintDC(self)
|
||||
if self.last_draw is not None:
|
||||
apply(self.draw, self.last_draw + (pdc,))
|
||||
|
||||
def reconfigure(self, event):
|
||||
(new_width,new_height) = self.GetClientSizeTuple()
|
||||
if new_width == self.width and new_height == self.height:
|
||||
return
|
||||
self._setsize()
|
||||
# self.redraw()
|
||||
|
||||
def _testFont(self, font):
|
||||
if font is not None:
|
||||
bg = self.canvas.cget('background')
|
||||
try:
|
||||
item = CanvasText(self.canvas, 0, 0, anchor=NW,
|
||||
text='0', fill=bg, font=font)
|
||||
self.canvas.delete(item)
|
||||
except TclError:
|
||||
font = None
|
||||
return font
|
||||
|
||||
def _setsize(self):
|
||||
(self.width,self.height) = self.GetClientSizeTuple();
|
||||
self.plotbox_size = 0.97*Numeric.array([self.width, -self.height])
|
||||
xo = 0.5*(self.width-self.plotbox_size[0])
|
||||
yo = self.height-0.5*(self.height+self.plotbox_size[1])
|
||||
self.plotbox_origin = Numeric.array([xo, yo])
|
||||
|
||||
def draw(self, graphics, xaxis = None, yaxis = None, dc = None):
|
||||
if dc == None: dc = wx.wxClientDC(self)
|
||||
dc.BeginDrawing()
|
||||
dc.Clear()
|
||||
self.last_draw = (graphics, xaxis, yaxis)
|
||||
p1, p2 = graphics.boundingBox()
|
||||
xaxis = self._axisInterval(xaxis, p1[0], p2[0])
|
||||
yaxis = self._axisInterval(yaxis, p1[1], p2[1])
|
||||
text_width = [0., 0.]
|
||||
text_height = [0., 0.]
|
||||
if xaxis is not None:
|
||||
p1[0] = xaxis[0]
|
||||
p2[0] = xaxis[1]
|
||||
xticks = self._ticks(xaxis[0], xaxis[1])
|
||||
bb = dc.GetTextExtent(xticks[0][1])
|
||||
text_height[1] = bb[1]
|
||||
text_width[0] = 0.5*bb[0]
|
||||
bb = dc.GetTextExtent(xticks[-1][1])
|
||||
text_width[1] = 0.5*bb[0]
|
||||
else:
|
||||
xticks = None
|
||||
if yaxis is not None:
|
||||
p1[1] = yaxis[0]
|
||||
p2[1] = yaxis[1]
|
||||
yticks = self._ticks(yaxis[0], yaxis[1])
|
||||
for y in yticks:
|
||||
bb = dc.GetTextExtent(y[1])
|
||||
text_width[0] = max(text_width[0],bb[0])
|
||||
h = 0.5*bb[1]
|
||||
text_height[0] = h
|
||||
text_height[1] = max(text_height[1], h)
|
||||
else:
|
||||
yticks = None
|
||||
text1 = Numeric.array([text_width[0], -text_height[1]])
|
||||
text2 = Numeric.array([text_width[1], -text_height[0]])
|
||||
scale = (self.plotbox_size-text1-text2) / (p2-p1)
|
||||
shift = -p1*scale + self.plotbox_origin + text1
|
||||
self._drawAxes(dc, xaxis, yaxis, p1, p2,
|
||||
scale, shift, xticks, yticks)
|
||||
graphics.scaleAndShift(scale, shift)
|
||||
graphics.draw(dc)
|
||||
dc.EndDrawing()
|
||||
|
||||
def _axisInterval(self, spec, lower, upper):
|
||||
if spec is None:
|
||||
return None
|
||||
if spec == 'minimal':
|
||||
if lower == upper:
|
||||
return lower-0.5, upper+0.5
|
||||
else:
|
||||
return lower, upper
|
||||
if spec == 'automatic':
|
||||
range = upper-lower
|
||||
if range == 0.:
|
||||
return lower-0.5, upper+0.5
|
||||
log = Numeric.log10(range)
|
||||
power = Numeric.floor(log)
|
||||
fraction = log-power
|
||||
if fraction <= 0.05:
|
||||
power = power-1
|
||||
grid = 10.**power
|
||||
lower = lower - lower % grid
|
||||
mod = upper % grid
|
||||
if mod != 0:
|
||||
upper = upper - mod + grid
|
||||
return lower, upper
|
||||
if type(spec) == type(()):
|
||||
lower, upper = spec
|
||||
if lower <= upper:
|
||||
return lower, upper
|
||||
else:
|
||||
return upper, lower
|
||||
raise ValueError, str(spec) + ': illegal axis specification'
|
||||
|
||||
def _drawAxes(self, dc, xaxis, yaxis,
|
||||
bb1, bb2, scale, shift, xticks, yticks):
|
||||
dc.SetPen(wx.wxPen(wx.wxNamedColour('BLACK'),1))
|
||||
if xaxis is not None:
|
||||
lower, upper = xaxis
|
||||
text = 1
|
||||
for y, d in [(bb1[1], -3), (bb2[1], 3)]:
|
||||
p1 = scale*Numeric.array([lower, y])+shift
|
||||
p2 = scale*Numeric.array([upper, y])+shift
|
||||
dc.DrawLine(p1[0],p1[1],p2[0],p2[1])
|
||||
for x, label in xticks:
|
||||
p = scale*Numeric.array([x, y])+shift
|
||||
dc.DrawLine(p[0],p[1],p[0],p[1]+d)
|
||||
if text:
|
||||
dc.DrawText(label,p[0],p[1])
|
||||
text = 0
|
||||
|
||||
if yaxis is not None:
|
||||
lower, upper = yaxis
|
||||
text = 1
|
||||
h = dc.GetCharHeight()
|
||||
for x, d in [(bb1[0], -3), (bb2[0], 3)]:
|
||||
p1 = scale*Numeric.array([x, lower])+shift
|
||||
p2 = scale*Numeric.array([x, upper])+shift
|
||||
dc.DrawLine(p1[0],p1[1],p2[0],p2[1])
|
||||
for y, label in yticks:
|
||||
p = scale*Numeric.array([x, y])+shift
|
||||
dc.DrawLine(p[0],p[1],p[0]-d,p[1])
|
||||
if text:
|
||||
dc.DrawText(label,p[0]-dc.GetTextExtent(label)[0],
|
||||
p[1]-0.5*h)
|
||||
text = 0
|
||||
|
||||
def _ticks(self, lower, upper):
|
||||
ideal = (upper-lower)/7.
|
||||
log = Numeric.log10(ideal)
|
||||
power = Numeric.floor(log)
|
||||
fraction = log-power
|
||||
factor = 1.
|
||||
error = fraction
|
||||
for f, lf in self._multiples:
|
||||
e = Numeric.fabs(fraction-lf)
|
||||
if e < error:
|
||||
error = e
|
||||
factor = f
|
||||
grid = factor * 10.**power
|
||||
if power > 3 or power < -3:
|
||||
format = '%+7.0e'
|
||||
elif power >= 0:
|
||||
digits = max(1, int(power))
|
||||
format = '%' + `digits`+'.0f'
|
||||
else:
|
||||
digits = -int(power)
|
||||
format = '%'+`digits+2`+'.'+`digits`+'f'
|
||||
ticks = []
|
||||
t = -grid*Numeric.floor(-lower/grid)
|
||||
while t <= upper:
|
||||
ticks.append( (t, format % (t,)) )
|
||||
t = t + grid
|
||||
return ticks
|
||||
|
||||
_multiples = [(2., Numeric.log10(2.)), (5., Numeric.log10(5.))]
|
||||
|
||||
def redraw(self,dc=None):
|
||||
if self.last_draw is not None:
|
||||
apply(self.draw, self.last_draw + (dc,))
|
||||
|
||||
def clear(self):
|
||||
self.canvas.delete('all')
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# if running standalone...
|
||||
#
|
||||
# ...a sample implementation using the above
|
||||
#
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def _InitObjects():
|
||||
# 100 points sin function, plotted as green circles
|
||||
data1 = 2.*Numeric.pi*Numeric.arange(200)/200.
|
||||
data1.shape = (100, 2)
|
||||
data1[:,1] = Numeric.sin(data1[:,0])
|
||||
markers1 = PolyMarker(data1, color='green', marker='circle',size=1)
|
||||
|
||||
# 50 points cos function, plotted as red line
|
||||
data1 = 2.*Numeric.pi*Numeric.arange(100)/100.
|
||||
data1.shape = (50,2)
|
||||
data1[:,1] = Numeric.cos(data1[:,0])
|
||||
lines = PolyLine(data1, color='red')
|
||||
|
||||
# A few more points...
|
||||
pi = Numeric.pi
|
||||
markers2 = PolyMarker([(0., 0.), (pi/4., 1.), (pi/2, 0.),
|
||||
(3.*pi/4., -1)], color='blue',
|
||||
fillcolor='green', marker='cross')
|
||||
|
||||
return PlotGraphics([markers1, lines, markers2])
|
||||
|
||||
|
||||
class AppFrame(wx.wxFrame):
|
||||
def __init__(self, parent, id, title):
|
||||
wx.wxFrame.__init__(self, parent, id, title,
|
||||
wx.wxPyDefaultPosition, wx.wxSize(400, 400))
|
||||
|
||||
# Now Create the menu bar and items
|
||||
self.mainmenu = wx.wxMenuBar()
|
||||
|
||||
menu = wx.wxMenu()
|
||||
menu.Append(200, '&Print...', 'Print the current plot')
|
||||
wx.EVT_MENU(self, 200, self.OnFilePrint)
|
||||
menu.Append(209, 'E&xit', 'Enough of this already!')
|
||||
wx.EVT_MENU(self, 209, self.OnFileExit)
|
||||
self.mainmenu.Append(menu, '&File')
|
||||
|
||||
menu = wx.wxMenu()
|
||||
menu.Append(210, '&Draw', 'Draw plots')
|
||||
wx.EVT_MENU(self,210,self.OnPlotDraw)
|
||||
menu.Append(211, '&Redraw', 'Redraw plots')
|
||||
wx.EVT_MENU(self,211,self.OnPlotRedraw)
|
||||
menu.Append(212, '&Clear', 'Clear canvas')
|
||||
wx.EVT_MENU(self,212,self.OnPlotClear)
|
||||
self.mainmenu.Append(menu, '&Plot')
|
||||
|
||||
menu = wx.wxMenu()
|
||||
menu.Append(220, '&About', 'About this thing...')
|
||||
wx.EVT_MENU(self, 220, self.OnHelpAbout)
|
||||
self.mainmenu.Append(menu, '&Help')
|
||||
|
||||
self.SetMenuBar(self.mainmenu)
|
||||
|
||||
# A status bar to tell people what's happening
|
||||
self.CreateStatusBar(1)
|
||||
|
||||
self.client = PlotCanvas(self)
|
||||
|
||||
def OnFilePrint(self, event):
|
||||
d = wx.wxMessageDialog(self,
|
||||
"""As of this writing, printing support in wxPython is shaky at best.
|
||||
Are you sure you want to do this?""", "Danger!", wx.wxYES_NO)
|
||||
if d.ShowModal() == wx.wxID_YES:
|
||||
psdc = wx.wxPostScriptDC("out.ps", wx.True, self)
|
||||
self.client.redraw(psdc)
|
||||
|
||||
def OnFileExit(self, event):
|
||||
self.Close()
|
||||
|
||||
def OnPlotDraw(self, event):
|
||||
self.client.draw(_InitObjects(),'automatic','automatic');
|
||||
|
||||
def OnPlotRedraw(self,event):
|
||||
self.client.redraw()
|
||||
|
||||
def OnPlotClear(self,event):
|
||||
self.client.last_draw = None
|
||||
dc = wx.wxClientDC(self.client)
|
||||
dc.Clear()
|
||||
|
||||
def OnHelpAbout(self, event):
|
||||
about = wx.wxMessageDialog(self, __doc__, "About...", wx.wxOK)
|
||||
about.ShowModal()
|
||||
|
||||
|
||||
|
||||
class MyApp(wx.wxApp):
|
||||
def OnInit(self):
|
||||
frame = AppFrame(wx.NULL, -1, "wxPlotCanvas")
|
||||
frame.Show(wx.True)
|
||||
self.SetTopWindow(frame)
|
||||
return wx.True
|
||||
|
||||
|
||||
app = MyApp(0)
|
||||
app.MainLoop()
|
||||
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
@@ -1,270 +1,12 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: wxPython.lib.wxpTag
|
||||
# Purpose: A wxHtmlTagHandler that knows how to build and place
|
||||
# wxPython widgets onto web pages.
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 13-Sept-1999
|
||||
# RCS-ID: $Id$
|
||||
# Copyright: (c) 1999 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
## This file imports items from the wx package into the wxPython package for
|
||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
||||
## that is how they used to be named in the old wxPython package.
|
||||
|
||||
'''
|
||||
wxPython.lib.wxpTag
|
||||
import wx.lib.wxpTag
|
||||
|
||||
This module contains a wxHtmlTagHandler that knows how to build
|
||||
and place wxPython widgets onto wxHtmlWindow web pages.
|
||||
__doc__ = wx.lib.wxpTag.__doc__
|
||||
|
||||
You don\'t need to use anything in this module directly, just
|
||||
importing it will create the tag handler and add it to any
|
||||
wxHtmlWinParsers created from that time forth.
|
||||
|
||||
Tags of the following form are recognised:
|
||||
|
||||
<WXP class="classname" [module="modulename"] [width="num"] [height="num"]>
|
||||
<PARAM name="parameterName" value="parameterValue>
|
||||
...
|
||||
</WXP>
|
||||
|
||||
where modulename is the name of a module (possibly in package
|
||||
notation) to import and classname is the name of a class in that
|
||||
module to create an instance of. If the module tag-attribute is not
|
||||
given or is an empty string, then wxPython.wx is used. The width and
|
||||
height attributes are expected to be integers and will be passed to
|
||||
the __init__ method of the class as a wxSize object named size.
|
||||
However, if the width attribute ends with the percent (%) symbol then
|
||||
the value will be used as a percentage of the available width and the
|
||||
wxHtmlWindow will manage the size.
|
||||
|
||||
The name-value pairs in all the nested PARAM tags are packaged up as
|
||||
strings into a python dictionary and passed to the __init__ method of
|
||||
the class as keyword arguments. This means that they are all
|
||||
accessible from the __init__ method as regular parameters, or you use
|
||||
the special Python **kw syntax in your __init__ method to get the
|
||||
dictionary directly.
|
||||
|
||||
Some parameter values are special and if they are present then they will
|
||||
be converted from strings to alternate datatypes. They are:
|
||||
|
||||
id If the value of id can be converted to an integer, it will
|
||||
be. Otherwise it is assumed to be the name of an integer
|
||||
variable in the module.
|
||||
|
||||
colours Any value of the form "#123ABC" will automatically be
|
||||
converted to a wxColour object.
|
||||
|
||||
Py Types Any value begining with "(", "[" or "{" are expected to
|
||||
be a Python tuple, list, or dictionary and eval()
|
||||
will be used to convert them to that type. If the
|
||||
eval() fails then the original string value will be
|
||||
preserved.
|
||||
|
||||
wx Types Any value begining with "wx" is expected to be an attempt
|
||||
to create a wxPython object, such as a wxSize, etc.
|
||||
The eval() will be used to try and construct the
|
||||
object and if it fails then the original string value
|
||||
will be used instead.
|
||||
|
||||
An example:
|
||||
|
||||
<wxp module="" class="wxButton">
|
||||
<param name="label" value="Click here">
|
||||
<param name="id" value="wxID_OK">
|
||||
</wxp>
|
||||
|
||||
Both the begining and ending WXP tags are required.
|
||||
|
||||
In the future support will be added for another tag that can be
|
||||
embedded between the two begining and ending WXP tags and will
|
||||
facilitate calling methods of the widget to help initialize it.
|
||||
Additionally, support may be added to fetch the module from a web
|
||||
server as is done with java applets.
|
||||
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
from wxPython.wx import *
|
||||
from wxPython.html import *
|
||||
import wxPython.wx
|
||||
|
||||
import types
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
WXPTAG = 'WXP'
|
||||
PARAMTAG = 'PARAM'
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class wxpTagHandler(wxHtmlWinTagHandler):
|
||||
def __init__(self):
|
||||
wxHtmlWinTagHandler.__init__(self)
|
||||
self.ctx = None
|
||||
|
||||
def GetSupportedTags(self):
|
||||
return WXPTAG+','+PARAMTAG
|
||||
|
||||
|
||||
def HandleTag(self, tag):
|
||||
name = tag.GetName()
|
||||
if name == WXPTAG:
|
||||
return self.HandleWxpTag(tag)
|
||||
elif name == PARAMTAG:
|
||||
return self.HandleParamTag(tag)
|
||||
else:
|
||||
raise ValueError, 'unknown tag: ' + name
|
||||
|
||||
|
||||
def HandleWxpTag(self, tag):
|
||||
# create a new context object
|
||||
self.ctx = _Context()
|
||||
|
||||
# find and import the module
|
||||
modName = ''
|
||||
if tag.HasParam('MODULE'):
|
||||
modName = tag.GetParam('MODULE')
|
||||
if modName:
|
||||
self.ctx.classMod = _my_import(modName)
|
||||
else:
|
||||
self.ctx.classMod = wxPython.wx
|
||||
|
||||
# find and verify the class
|
||||
if not tag.HasParam('CLASS'):
|
||||
raise AttributeError, "WXP tag requires a CLASS attribute"
|
||||
|
||||
className = tag.GetParam('CLASS')
|
||||
self.ctx.classObj = getattr(self.ctx.classMod, className)
|
||||
if type(self.ctx.classObj) != types.ClassType:
|
||||
raise TypeError, "WXP tag attribute CLASS must name a class"
|
||||
|
||||
# now look for width and height
|
||||
width = -1
|
||||
height = -1
|
||||
if tag.HasParam('WIDTH'):
|
||||
width = tag.GetParam('WIDTH')
|
||||
if width[-1] == '%':
|
||||
self.ctx.floatWidth = int(width[:-1], 0)
|
||||
width = self.ctx.floatWidth
|
||||
else:
|
||||
width = int(width)
|
||||
if tag.HasParam('HEIGHT'):
|
||||
height = int(tag.GetParam('HEIGHT'))
|
||||
self.ctx.kwargs['size'] = wxSize(width, height)
|
||||
|
||||
# parse up to the closing tag, and gather any nested Param tags.
|
||||
self.ParseInner(tag)
|
||||
|
||||
# create the object
|
||||
parent = self.GetParser().GetWindow()
|
||||
if parent:
|
||||
obj = apply(self.ctx.classObj,
|
||||
(parent,),
|
||||
self.ctx.kwargs)
|
||||
obj.Show(True)
|
||||
|
||||
# add it to the HtmlWindow
|
||||
self.GetParser().GetContainer().InsertCell(wxHtmlWidgetCell(obj, self.ctx.floatWidth))
|
||||
self.ctx = None
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def HandleParamTag(self, tag):
|
||||
if not tag.HasParam('NAME'):
|
||||
return False
|
||||
|
||||
name = tag.GetParam('NAME')
|
||||
value = ""
|
||||
if tag.HasParam('VALUE'):
|
||||
value = tag.GetParam('VALUE')
|
||||
|
||||
# check for a param named 'id'
|
||||
if name == 'id':
|
||||
theID = -1
|
||||
try:
|
||||
theID = int(value)
|
||||
except ValueError:
|
||||
theID = getattr(self.ctx.classMod, value)
|
||||
value = theID
|
||||
|
||||
|
||||
# check for something that should be evaluated
|
||||
elif value[0] in '[{(' or value[:2] == 'wx':
|
||||
saveVal = value
|
||||
try:
|
||||
value = eval(value, self.ctx.classMod.__dict__)
|
||||
except:
|
||||
value = saveVal
|
||||
|
||||
# convert to wxColour
|
||||
elif value[0] == '#':
|
||||
try:
|
||||
red = int('0x'+value[1:3], 16)
|
||||
green = int('0x'+value[3:5], 16)
|
||||
blue = int('0x'+value[5:], 16)
|
||||
value = wxColor(red, green, blue)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.ctx.kwargs[str(name)] = value
|
||||
return False
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# just a place to hold some values
|
||||
class _Context:
|
||||
def __init__(self):
|
||||
self.kwargs = {}
|
||||
self.width = -1
|
||||
self.height = -1
|
||||
self.classMod = None
|
||||
self.classObj = None
|
||||
self.floatWidth = 0
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Function to assist with importing packages
|
||||
def _my_import(name):
|
||||
mod = __import__(name)
|
||||
components = name.split('.')
|
||||
for comp in components[1:]:
|
||||
mod = getattr(mod, comp)
|
||||
return mod
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Function to parse a param string (of the form 'item=value item2="value etc"'
|
||||
# and creates a dictionary
|
||||
def _param2dict(param):
|
||||
i = 0; j = 0; s = len(param); d = {}
|
||||
while 1:
|
||||
while i<s and param[i] == " " : i = i+1
|
||||
if i>=s: break
|
||||
j = i
|
||||
while j<s and param[j] != "=": j=j+1
|
||||
if j+1>=s:
|
||||
break
|
||||
word = param[i:j]
|
||||
i=j+1
|
||||
if (param[i] == '"'):
|
||||
j=i+1
|
||||
while j<s and param[j] != '"' : j=j+1
|
||||
if j == s: break
|
||||
val = param[i+1:j]
|
||||
elif (param[i] != " "):
|
||||
j=i+1
|
||||
while j<s and param[j] != " " : j=j+1
|
||||
val = param[i:j]
|
||||
else:
|
||||
val = ""
|
||||
i=j+1
|
||||
d[word] = val
|
||||
return d
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
wxHtmlWinParser_AddTagHandler(wxpTagHandler)
|
||||
_Context = wx.lib.wxpTag._Context
|
||||
_my_import = wx.lib.wxpTag._my_import
|
||||
_param2dict = wx.lib.wxpTag._param2dict
|
||||
wxpTagHandler = wx.lib.wxpTag.wxpTagHandler
|
||||
|
||||
Reference in New Issue
Block a user