Files
wxWidgets/wxPython/wxPython/lib/fancytext.py
Robin Dunn 64dfb023eb Switched to using True/False in the wxPython lib and demo instead of
true/false or TRUE/FALSE to prepare for the new boolean type and
constants being added to Python.  Added code to wx.py to test for the
existence of the new constants and to create suitable values if not
present.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_4_BRANCH@19335 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2003-02-26 18:38:37 +00:00

250 lines
8.1 KiB
Python

"""wxFancyText -- methods for rendering XML specified text
This module has four main methods:
def getExtent(str, dc=None, enclose=1):
def renderToBitmap(str, background=None, enclose=1)
def renderToDC(str, dc, x, y, enclose=1)
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.
Note that start and end tags for the string are provided if enclose is
True, so for instance, renderToBitmap("X<sub>1</sub>") will work.
"""
# Copyright 2001 Timothy Hochberg
# Use as you see fit. No warantees, I cannot be held responsible, etc.
# 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}
# 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.
class Renderer:
defaultSize = wxNORMAL_FONT.GetPointSize()
defaultFamily = wxDEFAULT
defaultStyle = wxNORMAL
defaultWeight = wxNORMAL
defaultEncoding = wxFont_GetDefaultEncoding()
defaultColor = "black"
def __init__(self, dc=None):
if dc == None:
dc = wxMemoryDC()
self.dc = dc
self.offsets = [0]
self.fonts = [{}]
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):
method = "end_" + name
if not hasattr(self, method):
raise ValueError("XML tag '%s' not supported" % name)
getattr(self, method)()
def start_wxFancyString(self, attrs):
pass
def end_wxFancyString(self):
pass
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":
pass
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 wxFont(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 wxNamedColour(font.get("color", self.defaultColor))
class SizeRenderer(Renderer):
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
def start_angle(self, attrs):
self.characterData("M")
def end_angle(self):
pass
class DCRenderer(Renderer):
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())
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
def start_angle(self, attrs):
self.dc.SetFont(self.getCurrentFont())
self.dc.SetPen(wxPen(self.getCurrentColor(), 1))
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
def end_angle(self):
pass
# 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 = '<?xml version="1.0"?><wxFancyString>%s</wxFancyString>' % 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 getExtent(str, dc=None, enclose=1):
"Return the extent of str"
renderer = SizeRenderer(dc)
renderToRenderer(str, renderer, enclose)
return wxSize(renderer.width, renderer.height)
# This should probably only be used internally....
def getFullExtent(str, dc=None, enclose=1):
renderer = SizeRenderer(dc)
renderToRenderer(str, renderer, enclose)
return renderer.width, renderer.height, -renderer.minY
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.SelectObject(bmp)
if background is not None:
dc.SetBackground(background)
dc.Clear()
renderer = DCRenderer(dc, y=dy)
dc.BeginDrawing()
renderToRenderer(str, renderer, enclose)
dc.EndDrawing()
dc.SelectObject(wxNullBitmap)
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)
if __name__ == "__main__":
str = ('<font style="italic" family="swiss" color="red" weight="bold" >some |<sup>23</sup> <angle/>text<sub>with <angle/> subscript</sub> </font> some other text'
'<font family="swiss" color="green" size="40">big green text</font>')
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)
app.MainLoop()