Fri, 24 May 2019 17:37:10 +0200
added proper handling of syntax errors
8 | 1 | #!/usr/bin/env python3 |
4 | 2 | class Colour: |
8 | 3 | ''' |
4 | Colour interface. Supports LDConfig colours and direct colours. | |
5 | For LDConfig colours to work, load_colours must be called first. | |
6 | ''' | |
3
1dc58f44d556
Can now write dat files, added direct color handling
Santeri Piippo
parents:
2
diff
changeset
|
7 | def __init__(self, index): |
8 | 8 | if not isinstance(index, int): |
9 | index = int(index, 0) | |
10 | self.index = index | |
3
1dc58f44d556
Can now write dat files, added direct color handling
Santeri Piippo
parents:
2
diff
changeset
|
11 | def __str__(self): |
8 | 12 | if self.is_direct_colour: |
4 | 13 | # write direct colours with hex codes |
3
1dc58f44d556
Can now write dat files, added direct color handling
Santeri Piippo
parents:
2
diff
changeset
|
14 | return '0x%07X' % self.index |
1dc58f44d556
Can now write dat files, added direct color handling
Santeri Piippo
parents:
2
diff
changeset
|
15 | else: |
1dc58f44d556
Can now write dat files, added direct color handling
Santeri Piippo
parents:
2
diff
changeset
|
16 | return str(self.index) |
8 | 17 | def __repr__(self): |
18 | try: | |
19 | return 'colours.' + colours_inverse_dict[self.index] | |
20 | except KeyError: | |
21 | return str.format('Colour({!r})', self.index) | |
22 | @property | |
4 | 23 | def is_direct_colour(self): |
3
1dc58f44d556
Can now write dat files, added direct color handling
Santeri Piippo
parents:
2
diff
changeset
|
24 | return self.index >= 0x2000000 |
8 | 25 | @property |
26 | def is_ldconfig_colour(self): | |
27 | return self.index in ldconfig_colour_data | |
28 | @property | |
29 | def name(self): | |
30 | if self.is_ldconfig_colour: | |
31 | return ldconfig_colour_data[self.index]['name'] | |
32 | else: | |
33 | return str(self) | |
34 | @property | |
35 | def face_colour(self): | |
36 | if self.is_ldconfig_colour: | |
37 | return ldconfig_colour_data[self.index]['value'] | |
38 | elif self.is_direct_colour: | |
39 | return '#%06X' % (self.index & 0xffffff) | |
40 | else: | |
41 | return '#000000' | |
42 | @property | |
43 | def is_valid(self): | |
44 | return self.is_ldconfig_colour or self.is_direct_colour | |
45 | def __eq__(self, other): | |
46 | return self.index == other.index | |
47 | def __lt__(self, other): | |
48 | return self.index < other.index | |
49 | def __le__(self, other): | |
50 | return self.index <= other.index | |
51 | def __gt__(self, other): | |
52 | return self.index > other.index | |
53 | def __ge__(self, other): | |
54 | return self.index >= other.index | |
55 | ||
56 | def parse_ldconfig_ldr_line(line): | |
57 | ''' | |
58 | Parses a single LDConfig.ldr line. | |
59 | ||
60 | Returns: | |
61 | · a dict for a successful parsed colour. | |
62 | · None for empty lines and comments. | |
63 | ||
64 | Raises an error for invalid lines. | |
65 | ''' | |
66 | line = line.strip() | |
67 | import re | |
68 | def parse_tag(tag): | |
69 | match = re.search(tag + r'\s+([^ ]+)', line) | |
70 | if match: | |
71 | return match.group(1) | |
72 | else: | |
73 | raise KeyError(str.format('Tag {} not found', tag)) | |
74 | # parse 0 !COLOUR and get the name | |
75 | match = re.search(r'^0\s+!COLOUR\s([^ ]+)', line) | |
76 | if not match: | |
11 | 77 | raise ValueError('Bad LDConfig.ldr line: ' + line) |
8 | 78 | # replace underscores for readability |
79 | name = match.group(1).replace('_', ' ') | |
80 | # parse tags | |
81 | code = int(parse_tag('CODE')) | |
82 | value = parse_tag('VALUE') | |
83 | edge = parse_tag('EDGE') | |
84 | return { | |
85 | 'name': name, | |
86 | 'code': code, | |
87 | 'value': value, | |
88 | 'edge': edge, | |
89 | } | |
90 | ||
91 | def parse_ldconfig_ldr(ldconfig_ldr): | |
92 | ''' | |
93 | Parses LDConfig.ldr and returns pairs | |
94 | ''' | |
95 | for line in ldconfig_ldr: | |
11 | 96 | line = line.strip() |
97 | if line.startswith('0 !COLOUR'): | |
98 | colour = parse_ldconfig_ldr_line(line) | |
8 | 99 | yield (colour['code'], colour) |
100 | ||
101 | class colours: | |
102 | ''' | |
103 | LDConfig colour namespace, exists for interactive mode and for | |
104 | Colour.__repr__ to return something pretty. | |
105 | ''' | |
106 | pass | |
107 | ||
108 | # LDConfig lookup tables | |
109 | colours_inverse_dict = {} | |
110 | ldconfig_colour_data = {} | |
111 | ||
112 | def load_colours(ldconfig_ldr): | |
113 | ''' | |
114 | Loads colours. Expects a file pointer to LDConfig.ldr as the parameter. | |
115 | ''' | |
116 | global ldconfig_colour_data | |
117 | ldconfig_colour_data = dict(parse_ldconfig_ldr(ldconfig_ldr)) | |
118 | for index, colour in ldconfig_colour_data.items(): | |
119 | identifier = colour['name'].replace(' ', '_').lower() | |
120 | setattr(colours, identifier, Colour(index)) | |
121 | colours_inverse_dict[index] = identifier | |
122 | ||
123 | # Interactive mode support (pass LDConfig.ldr path as a command-line argument) | |
124 | if __name__ == '__main__': | |
125 | from sys import argv | |
126 | with open(argv[1]) as ldconfig_ldr: | |
11 | 127 | load_colours(ldconfig_ldr) |