Welcome, guest | Sign In | My Account | Store | Cart

A tree data type holding text data together with styling information.

Python, 138 lines
  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').

Created by Chris Ecker on Thu, 8 Dec 2011 (MIT)
Python recipes (4591)
Chris Ecker's recipes (2)

Required Modules

  • (none specified)

Other Information and Tasks