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): class StyledText(object):
"""Helper class to enable character based text formatting. """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. @ivar string: The clear text part.
@type string: str @type string: str
@ -50,26 +63,27 @@ class StyledText(object):
an instance. an instance.
@type POS_TAGS: int @type POS_TAGS: int
@attention: The POS_<x> class variables reflect the serialized object, they @attention: The POS_<x> class variables reflect the serialized object,
have to be updated in case the data structure or the L{serialize} method they have to be updated in case the data structure or the L{serialize}
changes! 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) (POS_TEXT, POS_TAGS) = range(2)
def __init__(self, text="", tags=None): def __init__(self, text="", tags=None):
"""Setup initial instance variable values.""" """Setup initial instance variable values."""
self._string = text self._string = text
# TODO we might want to make simple sanity check first
if tags: if tags:
self._tags = tags self._tags = tags
else: else:
@ -81,29 +95,123 @@ class StyledText(object):
def __repr__(self): return self._string.__repr__() def __repr__(self): return self._string.__repr__()
def __add__(self, other): 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): if isinstance(other, StyledText):
# FIXME merging tags missing # need to join strings and merge tags
return self.__class__("".join([self._string, other.string])) 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): elif isinstance(other, basestring):
# in this case tags remain the same, only text becomes longer # tags remain the same, only text becomes longer
return self.__class__("".join([self._string, other])) return self.__class__("".join([self._string, other]), self._tags)
else: 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: # string methods in alphabetical order:
def join(self, seq): def join(self, seq):
# FIXME handling tags missing """Emulate __builtin__.str.join method.
return self.__class__(self._string.join(seq))
def replace(self, old, new, maxsplit=-1): @param seq: list of strings to join
# FIXME handling tags missing @type seq: basestring or StyledText
return self.__class__(self._string.replace(old, new, maxsplit)) @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): 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) 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 # other public methods
@ -133,9 +241,9 @@ class StyledText(object):
# I really wonder why this doesn't work... it does for all other types # I really wonder why this doesn't work... it does for all other types
#self._tags = [StyledTextTag().unserialize(tag) for tag in the_tags] #self._tags = [StyledTextTag().unserialize(tag) for tag in the_tags]
for tag in the_tags: for tag in the_tags:
gtt = StyledTextTag() stt = StyledTextTag()
gtt.unserialize(tag) stt.unserialize(tag)
self._tags.append(gtt) self._tags.append(stt)
def get_tags(self): def get_tags(self):
"""Return the list of formatting tags. """Return the list of formatting tags.
@ -146,17 +254,22 @@ class StyledText(object):
""" """
return self._tags 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__': if __name__ == '__main__':
GT = StyledText("asbcde") from styledtexttagtype import StyledTextTagType
print GT 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