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
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()
|