git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@19793 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
250 lines
8.1 KiB
Python
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()
|