Implement a few string methods in StyledText, and documentation.

svn: r10445
This commit is contained in:
Zsolt Foldvari 2008-04-01 16:57:55 +00:00
parent 42e91346ed
commit 0087d69fab

View File

@ -37,6 +37,19 @@ from gen.lib.styledtexttag import StyledTextTag
class StyledText(object):
"""Helper class to enable character based text formatting.
StyledText is a wrapper class binding the clear text string and it's
formatting tags together.
StyledText provides several string methods in order to manipulate
formatted strings, such as L{join}, L{replace}, L{split}, and also
supports the '+' operation (L{__add__}).
To get the clear text of the StyledText use the built-in str() function.
To get the list of formatting tags use the L{get_tags} method.
StyledText supports the I{creation} of formatted texts too. This feature
is intended to replace (or extend) the current report interface.
To be continued... FIXME
@ivar string: The clear text part.
@type string: str
@ -50,26 +63,27 @@ class StyledText(object):
an instance.
@type POS_TAGS: int
@attention: The POS_<x> class variables reflect the serialized object, they
have to be updated in case the data structure or the L{serialize} method
changes!
@attention: The POS_<x> class variables reflect the serialized object,
they have to be updated in case the data structure or the L{serialize}
method changes!
@note:
1. There is no sanity check of tags in L{__init__}, because when a
StyledText is displayed it is passed to a StyledTextBuffer, which
in turn will 'eat' all invalid tags (including out-of-range tags too).
2. After string methods the tags can become fragmented. That means the
same tag may appear more than once in the tag list with different ranges.
There could be a 'merge_tags' functionality in L{__init__}, however
StyledTextBuffer will merge them automatically if the text is displayed.
"""
##StyledText provides interface
##Provide interface for:
##- tag manipulation for editor access:
##. get_tags
##. set_tags
##- explicit formatting for reports; at the moment:
##. start_bold() - end_bold()
##. start_superscript() - end_superscript()
(POS_TEXT, POS_TAGS) = range(2)
def __init__(self, text="", tags=None):
"""Setup initial instance variable values."""
self._string = text
# TODO we might want to make simple sanity check first
if tags:
self._tags = tags
else:
@ -81,29 +95,123 @@ class StyledText(object):
def __repr__(self): return self._string.__repr__()
def __add__(self, other):
"""Implement '+' operation on the class.
@param other: string to concatenate to self
@type other: basestring or StyledText
@returns: concatenated strings
@returntype: StyledText
"""
offset = len(self._string)
if isinstance(other, StyledText):
# FIXME merging tags missing
return self.__class__("".join([self._string, other.string]))
# need to join strings and merge tags
for tag in other._tags:
tag.ranges = [(start + offset, end + offset)
for (start, end) in tag.ranges]
return self.__class__("".join([self._string, other._string]),
self._tags + other._tags)
elif isinstance(other, basestring):
# in this case tags remain the same, only text becomes longer
return self.__class__("".join([self._string, other]))
# tags remain the same, only text becomes longer
return self.__class__("".join([self._string, other]), self._tags)
else:
return self.__class__("".join([self._string, str(other)]))
return self.__class__("".join([self._string, str(other)]),
self._tags)
# private methods
# string methods in alphabetical order:
def join(self, seq):
# FIXME handling tags missing
return self.__class__(self._string.join(seq))
"""Emulate __builtin__.str.join method.
def replace(self, old, new, maxsplit=-1):
# FIXME handling tags missing
return self.__class__(self._string.replace(old, new, maxsplit))
@param seq: list of strings to join
@type seq: basestring or StyledText
@returns: joined strings
@returntype: StyledText
"""
new_string = self._string.join([str(string) for string in seq])
offset = 0
new_tags = []
self_len = len(self._string)
for text in seq:
if isinstance(text, StyledText):
for tag in text._tags:
tag.ranges = [(start + offset, end + offset)
for (start, end) in tag.ranges]
new_tags += [tag]
offset = offset + len(str(text)) + self_len
return self.__class__(new_string, new_tags)
def replace(self, old, new, count=-1):
"""Emulate __builtin__.str.replace method.
@param old: substring to be replaced
@type old: basestring or StyledText
@param new: substring to replace by
@type new: StyledText
@param count: if given, only the first count occurrences are replaced
@type count: int
@returns: copy of the string with replaced substring(s)
@returntype: StyledText
@attention: by the correct implementation parameter I{new}
should be StyledText or basestring, however only StyledText
is currently supported.
"""
# quick and dirty solution: works only if new.__class__ == StyledText
return new.join(self.split(old, count))
def split(self, sep=None, maxsplit=-1):
# FIXME handling tags missing
"""Emulate __builtin__.str.split method.
@param sep: the delimiter string
@type seq: basestring or StyledText
@param maxsplit: if given, at most maxsplit splits are done
@type maxsplit: int
@returns: a list of the words in the string
@returntype: list of StyledText
"""
# split the clear text first
if sep is not None:
sep = str(sep)
string_list = self._string.split(sep, maxsplit)
return [self.__class__(string) for string in string_list]
# then split the tags too
end_string = 0
styledtext_list = []
for string in string_list:
start_string = self._string.find(string, end_string)
end_string = start_string + len(string)
new_tags = []
for tag in self._tags:
new_tag = StyledTextTag(int(tag.name), tag.value)
for (start_tag, end_tag) in tag.ranges:
start = max(start_string, start_tag)
end = min(end_string, end_tag)
if start < end:
new_tag.ranges.append((start - start_string,
end - start_string))
if new_tag.ranges:
new_tags.append(new_tag)
styledtext_list.append(self.__class__(string, new_tags))
return styledtext_list
# other public methods
@ -133,9 +241,9 @@ class StyledText(object):
# I really wonder why this doesn't work... it does for all other types
#self._tags = [StyledTextTag().unserialize(tag) for tag in the_tags]
for tag in the_tags:
gtt = StyledTextTag()
gtt.unserialize(tag)
self._tags.append(gtt)
stt = StyledTextTag()
stt.unserialize(tag)
self._tags.append(stt)
def get_tags(self):
"""Return the list of formatting tags.
@ -146,17 +254,22 @@ class StyledText(object):
"""
return self._tags
##def set_tags(self, tags):
##"""Set all the formatting tags at once.
##@param tags: The formatting tags to be applied on the text.
##@type tags: list of 0 or more StyledTextTag instances.
##"""
### TODO we might want to make simple sanity check first
##self._tags = tags
if __name__ == '__main__':
GT = StyledText("asbcde")
print GT
from styledtexttagtype import StyledTextTagType
T1 = StyledTextTag(StyledTextTagType(1), 'v1', [(0, 2), (2, 4), (4, 6)])
T2 = StyledTextTag(StyledTextTagType(2), 'v2', [(1, 3), (3, 5), (0, 7)])
A = StyledText('123X456', [T1])
B = StyledText("abcXdef", [T2])
C = StyledText('\n')
S = 'cleartext'
C = C.join([A, S, B])
L = C.split()
C = C.replace('X', StyledText('_'))
A = A + B
print A