diff --git a/wxPython/wxPython/lib/fancytext.py b/wxPython/wxPython/lib/fancytext.py
index 0767c986e7..c91c49ec4f 100644
--- a/wxPython/wxPython/lib/fancytext.py
+++ b/wxPython/wxPython/lib/fancytext.py
@@ -1,62 +1,109 @@
-"""wxFancyText -- methods for rendering XML specified text
+"""FancyText -- methods for rendering XML specified text
+
+This module exports four main methods::
+
+ 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)
+
+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 module has four main methods:
+In addition, the module exports one class::
+
+ class StaticFancyText(self, window, id, text, background, ...)
+
+This class works similar to StaticText except it interprets its text
+as FancyText.
-def getExtent(str, dc=None, enclose=1):
-def renderToBitmap(str, background=None, enclose=1)
-def renderToDC(str, dc, x, y, enclose=1)
+The text can supportsuperscripts and subscripts, text
+in different sizes, colors, styles, weights and
+families. It also supports a limited set of symbols,
+currently , , as well as greek letters in both
+upper case (...) and lower case (...).
-In all cases, 'str' is an XML string. The tags in the string can
-currently specify the font, subscripts, superscripts, and the angle
-sign. The allowable properties of font are size, family, style, weght,
-encoding, and color. See the example on the bottom for a better idea
-of how this works.
+We can use doctest/guitest to display this string in all its marked up glory.
+
+>>> 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()
-Note that start and end tags for the string are provided if enclose is
-True, so for instance, renderToBitmap("X1") will work.
+
+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
-"""
-# Copyright 2001 Timothy Hochberg
-# Use as you see fit. No warantees, I cannot be held responsible, etc.
+__all__ = "GetExtent", "GetFullExtent", "RenderToBitmap", "RenderToDC", "StaticFancyText"
+if sys.platform == "win32":
+ _greekEncoding = str(wx.FONTENCODING_CP1253)
+else:
+ _greekEncoding = str(wx.FONTENCODING_ISO8859_7)
-
-# TODO: Make a wxFancyTextCtrl class that derives from wxControl.
-# Add support for line breaks
-# etc.
-# - Robin
-
-
-
-from wxPython.wx import *
-import xml.parsers.expat, copy
-
-_families = {"default" : wxDEFAULT, "decorative" : wxDECORATIVE, "roman" : wxROMAN,
- "swiss" : wxSWISS, "modern" : wxMODERN}
-_styles = {"normal" : wxNORMAL, "slant" : wxSLANT, "italic" : wxITALIC}
-_weights = {"normal" : wxNORMAL, "light" : wxLIGHT, "bold" : wxBOLD}
+_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
+# the font stuff as well as the subscript and superscript stuff are in
# Renderer.
-class 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")
- defaultSize = wxNORMAL_FONT.GetPointSize()
- defaultFamily = wxDEFAULT
- defaultStyle = wxNORMAL
- defaultWeight = wxNORMAL
- defaultEncoding = wxFont_GetDefaultEncoding()
+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):
+ def __init__(self, dc=None, x=0, y=None):
if dc == None:
- dc = wxMemoryDC()
+ 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):
@@ -64,16 +111,37 @@ class Renderer:
getattr(self, method)(attrs)
def endElement(self, name):
- method = "end_" + name
- if not hasattr(self, method):
- raise ValueError("XML tag '%s' not supported" % name)
- getattr(self, method)()
+ 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 start_wxFancyString(self, attrs):
- pass
+ 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 end_wxFancyString(self):
+ def start_FancyText(self, attrs):
pass
+ start_wxFancyText = start_FancyText # For backward compatibility
def start_font(self, attrs):
for key, value in attrs.items():
@@ -86,7 +154,7 @@ class Renderer:
elif key == "weight":
value = _weights[value]
elif key == "encoding":
- pass
+ value = int(value)
elif key == "color":
pass
else:
@@ -119,11 +187,11 @@ class Renderer:
def end_sup(self):
self.fonts.pop()
- self.offsets.pop()
+ self.offsets.pop()
def getCurrentFont(self):
font = self.fonts[-1]
- return wxFont(font.get("size", self.defaultSize),
+ return wx.TheFontList.FindOrCreateFont(font.get("size", self.defaultSize),
font.get("family", self.defaultFamily),
font.get("style", self.defaultStyle),
font.get("weight", self.defaultWeight),
@@ -131,119 +199,212 @@ class Renderer:
def getCurrentColor(self):
font = self.fonts[-1]
- return wxNamedColour(font.get("color", self.defaultColor))
+ 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)
- self.width = self.height = 0
- self.minY = self.maxY = 0
-
- def characterData(self, data):
- self.dc.SetFont(self.getCurrentFont())
- width, height = self.dc.GetTextExtent(data)
- self.width = self.width + width
- self.minY = min(self.minY, self.offsets[-1])
- self.maxY = max(self.maxY, self.offsets[-1] + height)
- self.height = self.maxY - self.minY
-
+ Renderer.__init__(self, dc, 0, 0)
+
+ def renderCharacterData(self, data, x, y):
+ pass
+
def start_angle(self, attrs):
self.characterData("M")
- def end_angle(self):
- pass
+ 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 __init__(self, dc=None, x=0, y=0):
- Renderer.__init__(self, dc)
- self.x = x
- self.y = y
-
- def characterData(self, data):
- self.dc.SetFont(self.getCurrentFont())
+ def renderCharacterData(self, data, x, y):
self.dc.SetTextForeground(self.getCurrentColor())
- width, height = self.dc.GetTextExtent(data)
- self.dc.DrawText(data, self.x, self.y + self.offsets[-1])
- self.x = self.x + width
+ self.dc.DrawText(data, x, y)
def start_angle(self, attrs):
self.dc.SetFont(self.getCurrentFont())
- self.dc.SetPen(wxPen(self.getCurrentColor(), 1))
+ self.dc.SetPen(self.getCurrentPen())
width, height, descent, leading = self.dc.GetFullTextExtent("M")
- y = self.y + self.offsets[-1] + height - descent
- self.dc.DrawLine(self.x, y, self.x+width, y)
- self.dc.DrawLine(self.x, y, self.x+width, y-width)
- self.x = self.x + width
+ 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 end_angle(self):
- pass
+ 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)
-# This is a rendering function that is primarily used internally,
-# although it could be used externally if one had overridden the
-# Renderer classes.
-
-def renderToRenderer(str, renderer, enclose=1):
- if enclose:
- str = '%s' % 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)
+ 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 getExtent(str, dc=None, enclose=1):
+def RenderToRenderer(str, renderer, enclose=True):
+ try:
+ if enclose:
+ str = '%s' % 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 wxSize(renderer.width, renderer.height)
+ RenderToRenderer(str, renderer, enclose)
+ return iceil(renderer.width), iceil(renderer.height) # XXX round up
-# This should probably only be used internally....
-def getFullExtent(str, dc=None, enclose=1):
+def GetFullExtent(str, dc=None, enclose=True):
renderer = SizeRenderer(dc)
- renderToRenderer(str, renderer, enclose)
- return renderer.width, renderer.height, -renderer.minY
+ RenderToRenderer(str, renderer, enclose)
+ return iceil(renderer.width), iceil(renderer.height), -iceil(renderer.minY) # XXX round up
-def renderToBitmap(str, background=None, enclose=1):
+
+def RenderToBitmap(str, background=None, enclose=1):
"Return str rendered on a minumum size bitmap"
- dc = wxMemoryDC()
- width, height, dy = getFullExtent(str, dc)
- bmp = wxEmptyBitmap(width, height)
+ dc = wx.MemoryDC()
+ width, height, dy = GetFullExtent(str, dc, enclose)
+ bmp = wx.EmptyBitmap(width, height)
dc.SelectObject(bmp)
- if background is not None:
- dc.SetBackground(background)
+ 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)
+ RenderToRenderer(str, renderer, enclose)
dc.EndDrawing()
- dc.SelectObject(wxNullBitmap)
+ 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):
+
+def RenderToDC(str, dc, x, y, enclose=1):
"Render str onto a wxDC at (x,y)"
- width, height, dy = getFullExtent(str, dc)
+ width, height, dy = GetFullExtent(str, dc)
renderer = DCRenderer(dc, x, y+dy)
- renderToRenderer(str, renderer, enclose)
+ 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)
-if __name__ == "__main__":
- str = ('some |23 textwith subscript some other text'
- 'big green text')
- ID_EXIT = 102
- class myApp(wxApp):
- def OnInit(self):
- return 1
- app = myApp()
- frame = wxFrame(NULL, -1, "wxFancyText demo", wxDefaultPosition)
- frame.SetClientSize(getExtent(str))
- bmp = renderToBitmap(str, wxCYAN_BRUSH)
- sb = wxStaticBitmap(frame, -1, bmp)
- EVT_MENU(frame, ID_EXIT, frame.Destroy)
- frame.Show(1)
+# 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()
+
+
diff --git a/wxPython/wxPython/lib/foldmenu.py b/wxPython/wxPython/lib/foldmenu.py
index 3b732a9f2b..46178226b2 100644
--- a/wxPython/wxPython/lib/foldmenu.py
+++ b/wxPython/wxPython/lib/foldmenu.py
@@ -1,10 +1,79 @@
-## 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
+import wx
+from wx.lib.evtmgr import eventManager
-__doc__ = wx.lib.foldmenu.__doc__
+class FoldOutWindow(wx.PopupWindow):
+ def __init__(self,parent,style=0):
+ wx.PopupWindow.__init__(self,parent,style)
+ self.SetAutoLayout(True)
+ self.sizer=wx.BoxSizer(wx.HORIZONTAL)
+ self.SetSizer(self.sizer, deleteOld=False)
+ self.handlers={}
+ self.InitColors()
+ self.inWindow=False
+ wx.EVT_ENTER_WINDOW(self,self.evEnter)
+ wx.EVT_LEAVE_WINDOW(self,self.evLeave)
+
+ def InitColors(self):
+ faceClr = wx.SystemSettings_GetSystemColour(wx.SYS_COLOUR_WINDOW)
+ self.SetBackgroundColour(faceClr)
-FoldOutMenu = wx.lib.foldmenu.FoldOutMenu
-FoldOutWindow = wx.lib.foldmenu.FoldOutWindow
+ def AddButton(self,bitmap,handler=None):
+ id=wx.NewId()
+ btn=wx.BitmapButton(self,id,bitmap)
+ self.sizer.Add(btn, 1, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 2)
+ wx.EVT_BUTTON(self,id,self.OnBtnClick)
+ self.sizer.Fit(self)
+ self.Layout()
+ if handler:
+ self.handlers[id]=handler
+ return id
+
+ def Popup(self):
+ if not self.IsShown():
+ self.Show()
+
+ def OnBtnClick(self,event):
+ id=event.GetEventObject().GetId()
+ if self.handlers.has_key(id):
+ self.handlers[id](event)
+ self.Hide()
+ self.inWindow=False
+ event.Skip()
+
+ def evEnter(self,event):
+ self.inWindow=True
+ self.rect=self.GetRect()
+ event.Skip()
+
+ def evLeave(self,event):
+ if self.inWindow:
+ if not self.rect.Inside(self.ClientToScreen(event.GetPosition())):
+ self.Hide()
+ event.Skip()
+
+
+
+
+
+class FoldOutMenu(wx.BitmapButton):
+ def __init__(self,parent,id,bitmap,pos = wx.DefaultPosition,
+ size = wx.DefaultSize, style = wx.BU_AUTODRAW,
+ validator = wx.DefaultValidator, name = "button"):
+ wx.BitmapButton.__init__(self,parent,id,bitmap,pos = wx.DefaultPosition,
+ size = wx.DefaultSize, style = wx.BU_AUTODRAW,
+ validator = wx.DefaultValidator, name = "button")
+ self.parent=parent
+ wx.EVT_BUTTON(self.parent, self.GetId(), self.click)
+ self.popwin=FoldOutWindow(self.parent)
+
+ def AddButton(self,bitmap,handler=None):
+ return self.popwin.AddButton(bitmap,handler=handler)
+
+ def click(self,event):
+ pos=self.GetPosition()
+ sz=self.GetSize()
+ pos.x=pos.x+sz.width
+ pos.y=pos.y+sz.height/2
+ self.popwin.Position(pos,sz)
+ self.popwin.Popup()
diff --git a/wxPython/wxPython/lib/mixins/listctrl.py b/wxPython/wxPython/lib/mixins/listctrl.py
index c422c17cad..029994a8c9 100644
--- a/wxPython/wxPython/lib/mixins/listctrl.py
+++ b/wxPython/wxPython/lib/mixins/listctrl.py
@@ -40,15 +40,18 @@ class wxColumnSorterMixin:
"""
def __init__(self, numColumns):
- self._colSortFlag = [0] * numColumns
- self._col = -1
-
+ 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