A tree data type holding text data together with styling information.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | def hash_style(style):
return tuple(sorted(style.items()))
style_pool = {}
defaultstyle = {}
def create_style(**kwds):
style = defaultstyle.copy()
style.update(kwds)
key = hash_style(style)
try:
return style_pool[key]
except KeyError:
style_pool[key] = style
return style
defaultstyle = create_style(textcolor='black', fontsize=10)
def updated_style(style, properties):
new = style.copy()
new.update(properties)
return create_style(**new)
class Characters:
style = None
def __init__(self, text, style=defaultstyle):
self.data = text
self.style = style
def __len__(self):
return len(self.data)
def get_style(self, i):
return self.style
def __repr__(self):
return "C(%s)" % repr(self.data)
def __len(self):
return len(self.data)
def split(self, i):
if i<0 or i>len(self):
raise IndexError(i)
l = Characters(self.data[:i], self.style)
r = Characters(self.data[i:], self.style)
return l, r
def set_properties(self, i1, i2, properties):
i1 = max(0, i1)
i2 = min(len(self), i2)
tmp, r = self.split(i2)
l, tmp = tmp.split(i1)
style = updated_style(self.style, properties)
c = Characters(tmp.data, style)
return Group([l, c, r])
def insert(self, i, texel):
if isinstance(texel, Characters) and texel.style is self.style:
text = self.data[:i]+texel.data+self.data[i:]
return Characters(text, self.style)
a, b = self.split(i)
return Group([a, texel, b])
class Group:
def __init__(self, content):
self.data = list(content)
length = 0
for texel in content:
length += len(texel)
self._length = length
def __len__(self):
return self._length
def __repr__(self):
return "G(%s)" % repr(self.data)
def get_style(self, i):
for texel in self.data:
n = len(texel)
if n>i:
return texel.get_style(i)
i -= n
def set_properties(self, i1, i2, properties):
r = []
i = 0
for texel in self.data:
n = len(texel)
if i1<n and i2>0:
r.append(texel.set_properties(i1, i2, properties))
else:
r.append(texel)
i1 -= n
i2 -= n
return Group(r)
def split(self, i):
if i<0 or i>len(self):
raise IndexError(i)
l = []
r = []
for texel in self.data:
n = len(texel)
if i<=0:
r.append(texel)
elif i>=n:
l.append(texel)
elif n>i:
a, b = texel.split(i)
l.append(a)
r.append(b)
i -= n
return Group(l), Group(r)
def insert(self, i, texel):
if i == len(self):
return Group(self.data+[texel])
data = []
for elem in self.data:
n = len(elem)
if i<0:
data.append(elem)
elif i>=n:
data.append(elem)
else:
data.append(elem.insert(i, texel))
i -= n
return Group(data)
C = Characters
G = Group
|
Working with simple text in python is very easy. What is quite difficult however, is handling text which has styling information such as font or color. This recipe shows a simple and efficient structure which allows to store and manipulate styled text information.
Inserting text:
>>> t = G([C("Hello!", create_style(fontsize=10))])
>>> t2 = t.insert(5, C(" world", create_style(fontsize=12)))
>>> t2
G([G([C('Hello'), C(' world'), C('!')])])
Getting the style information at a certain index:
>>> t2.get_style(0)
{'textcolor': 'black', 'fontsize': 10}
>>> t2.get_style(5)
{'textcolor': 'black', 'fontsize': 12}
Modifying style:
>>> t3 = t2.set_properties(0, len(t2), {'fontsize':8})
>>> t3.get_style(2)
{'textcolor': 'black', 'fontsize': 8}
Copying text from index 1 to index 10:
>>> t2.split(10)[0].split(1)[1]
G([G([C('ello'), C(' Worl')])])
To keep this recipe short, I did not include simplification code. For example G([G([C('Hello')])]) could be simplified to C('Hello').