Merge lp:~trb143/openlp/ThemeManager into lp:openlp
- ThemeManager
- Merge into trunk
Status: | Merged |
---|---|
Merge reported by: | Tim Bentley |
Merged at revision: | not available |
Proposed branch: | lp:~trb143/openlp/ThemeManager |
Merge into: | lp:openlp |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~trb143/openlp/ThemeManager |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Bentley | Approve | ||
Michael Gorven (community) | Approve | ||
Raoul Snyman | Approve | ||
Review via email:
|
Commit message
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Raoul Snyman (raoul-snyman) wrote : | # |
In general this looks fine. Once again, a couple of minor points (mostly just style):
Why is there a ThemeXMLBuilder and a ThemeXMLParser... Parser is so small, is there a reason why you didn't combine the two into a ThemeXML class?
Just remember that we're using ' for strings, not " (seen in a few places)
No spaces between param, "=", and default value (see "footer" param):
def _get_extent_
In the function above, "dodraw" - surely that can be changed to simply "draw" ?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michael Gorven (mgorven) wrote : | # |
+ log.debug(r.x(), r.y(), r.width(
+ log.debug(
Needs a format string.
+# log.debug(u" "Just split where you can"
+# log.debug(u" "Getting the words split right"
+# log.debug(u" "This is how they split", lines
+# log.debug(u" "Now render them"
+# log.debug(u" "Line %2d: Render '%s' at (%d, %d) wh=(%d,%d)"%(
linenum, line, x, y,w,h)
I know these are commented out, but if it's code the syntax should still be
correct.
+ print bbox
+ print "AA"
+ print tree.find(
+ print "AAA"
Should use log.debug().
review approve
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) : | # |
Preview Diff
1 | === modified file 'openlp/core/lib/__init__.py' |
2 | --- openlp/core/lib/__init__.py 2009-03-23 20:18:06 +0000 |
3 | +++ openlp/core/lib/__init__.py 2009-03-28 20:12:22 +0000 |
4 | @@ -31,7 +31,9 @@ |
5 | from toolbar import OpenLPToolbar |
6 | from songxmlhandler import SongXMLBuilder |
7 | from songxmlhandler import SongXMLParser |
8 | +from themexmlhandler import ThemeXMLBuilder |
9 | +from themexmlhandler import ThemeXMLParser |
10 | |
11 | __all__ = ['PluginConfig', 'Plugin', 'SettingsTab', 'MediaManagerItem', 'Event', 'EventType' |
12 | - 'XmlRootClass', 'ServiceItem', 'Receiver', 'OpenLPToolbar', 'SongXMLBuilder', |
13 | - 'SongXMLParser', 'EventManager'] |
14 | + 'XmlRootClass', 'ServiceItem', 'Receiver', 'OpenLPToolbar', 'SongXMLBuilder', |
15 | + 'SongXMLParser', 'EventManager', 'ThemeXMLBuilder', 'ThemeXMLParser'] |
16 | |
17 | === added file 'openlp/core/lib/themexmlhandler.py' |
18 | --- openlp/core/lib/themexmlhandler.py 1970-01-01 00:00:00 +0000 |
19 | +++ openlp/core/lib/themexmlhandler.py 2009-03-28 20:12:22 +0000 |
20 | @@ -0,0 +1,118 @@ |
21 | +from xml.dom.minidom import Document |
22 | +from xml.etree.ElementTree import ElementTree, XML, dump |
23 | +""" |
24 | +<?xml version="1.0" encoding="UTF-8"?> |
25 | +<song version="1.0"> |
26 | + <lyrics language="en"> |
27 | + <verse type="chorus" label="1"> |
28 | + <![CDATA[ ... ]]> |
29 | + </verse> |
30 | + </lyrics> |
31 | +</song> |
32 | + |
33 | +""" |
34 | +class ThemeXMLBuilder(): |
35 | + def __init__(self): |
36 | + # Create the minidom document |
37 | + self.theme_xml = Document() |
38 | + |
39 | + def new_document(self, name): |
40 | + # Create the <song> base element |
41 | + self.theme = self.theme_xml.createElement(u'Theme') |
42 | + self.theme_xml.appendChild(self.theme) |
43 | + self.theme.setAttribute(u'version', u'1.0') |
44 | + |
45 | + self.name = self.theme_xml.createElement(u'Name') |
46 | + ctn = self.theme_xml.createTextNode(name) |
47 | + self.name.appendChild(ctn) |
48 | + self.theme.appendChild(self.name) |
49 | + |
50 | + def add_background_transparent(self): |
51 | + # Create the main <lyrics> element |
52 | + background = self.theme_xml.createElement(u'Background') |
53 | + background.setAttribute(u'mode', u'transparent') |
54 | + self.theme.appendChild(background) |
55 | + |
56 | + def add_background_solid(self, bkcolor): |
57 | + background = self.theme_xml.createElement(u'Background') |
58 | + background.setAttribute(u'mode', u'opaque') |
59 | + background.setAttribute(u'type', u'solid') |
60 | + self.theme.appendChild(background) |
61 | + |
62 | + color = self.theme_xml.createElement(u'color') |
63 | + bkc = self.theme_xml.createTextNode(bkcolor) |
64 | + color.appendChild(bkc) |
65 | + background.appendChild(color) |
66 | + |
67 | + def add_background_gradient(self, startcolor, endcolor): |
68 | + background = self.theme_xml.createElement(u'Background') |
69 | + background.setAttribute(u'mode', u'opaque') |
70 | + background.setAttribute(u'type', u'gradient') |
71 | + self.theme.appendChild(background) |
72 | + |
73 | + color = self.theme_xml.createElement(u'startcolor') |
74 | + bkc = self.theme_xml.createTextNode(startcolor) |
75 | + color.appendChild(bkc) |
76 | + background.appendChild(color) |
77 | + |
78 | + color = self.theme_xml.createElement(u'endcolor') |
79 | + bkc = self.theme_xml.createTextNode(endcolor) |
80 | + color.appendChild(bkc) |
81 | + background.appendChild(color) |
82 | + |
83 | + def add_background_image(self, filename, bordercolor): |
84 | + background = self.theme_xml.createElement(u'Background') |
85 | + background.setAttribute(u'mode', u'opaque') |
86 | + background.setAttribute(u'type', u'image') |
87 | + self.theme.appendChild(background) |
88 | + |
89 | + color = self.theme_xml.createElement(u'filename') |
90 | + bkc = self.theme_xml.createCDATASection(filename) |
91 | + color.appendChild(bkc) |
92 | + background.appendChild(color) |
93 | + |
94 | + color = self.theme_xml.createElement(u'bordercolor') |
95 | + bkc = self.theme_xml.createTextNode(bordercolor) |
96 | + color.appendChild(bkc) |
97 | + background.appendChild(color) |
98 | + |
99 | + |
100 | + def add_verse_to_lyrics(self, type, number, content): |
101 | + """ |
102 | + type - type of verse (Chorus, Verse , Bridge, Custom etc |
103 | + number - number of item eg verse 1 |
104 | + content - the text to be stored |
105 | + """ |
106 | + verse = self.theme_xml.createElement(u'verse') |
107 | + verse.setAttribute(u'type', type) |
108 | + verse.setAttribute(u'label', number) |
109 | + self.lyrics.appendChild(verse) |
110 | + |
111 | + # add data as a CDATA section |
112 | + cds = self.theme_xml.createCDATASection(content) |
113 | + verse.appendChild(cds) |
114 | + |
115 | + def dump_xml(self): |
116 | + # Debugging aid to see what we have |
117 | + print self.theme_xml.toprettyxml(indent=" ") |
118 | + |
119 | + def extract_xml(self): |
120 | + # Print our newly created XML |
121 | + return self.theme_xml.toxml() |
122 | + |
123 | +class ThemeXMLParser(): |
124 | + def __init__(self, xml): |
125 | + self.theme_xml = ElementTree(element=XML(xml)) |
126 | + |
127 | + def get_verses(self): |
128 | + #return a list of verse's and attributes |
129 | + iter=self.theme_xml.getiterator() |
130 | + verse_list = [] |
131 | + for element in iter: |
132 | + if element.tag == u'verse': |
133 | + verse_list.append([element.attrib, element.text]) |
134 | + return verse_list |
135 | + |
136 | + def dump_xml(self): |
137 | + # Debugging aid to see what we have |
138 | + print dump(self.theme_xml) |
139 | |
140 | === modified file 'openlp/core/render.py' |
141 | --- openlp/core/render.py 2009-03-22 07:13:34 +0000 |
142 | +++ openlp/core/render.py 2009-03-29 16:51:42 +0000 |
143 | @@ -17,13 +17,19 @@ |
144 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
145 | Place, Suite 330, Boston, MA 02111-1307 USA |
146 | """ |
147 | +import logging |
148 | |
149 | import sys |
150 | from PyQt4 import QtGui, QtCore, Qt |
151 | |
152 | from copy import copy |
153 | from interpolate import interpolate |
154 | + |
155 | class Renderer: |
156 | + |
157 | + global log |
158 | + log=logging.getLogger(u'Renderer') |
159 | + log.info(u'Renderer Loaded') |
160 | """All the functions for rendering a set of words onto a Device Context |
161 | |
162 | How to use: |
163 | @@ -44,21 +50,21 @@ |
164 | self._theme=None |
165 | self._bg_image_filename=None |
166 | self._paint=None |
167 | - |
168 | + |
169 | def set_debug(self, debug): |
170 | self._debug=debug |
171 | - |
172 | + |
173 | def set_theme(self, theme): |
174 | self._theme=theme |
175 | if theme.BackgroundType == 2: |
176 | self.set_bg_image(theme.BackgroundParameter1) |
177 | |
178 | def set_bg_image(self, filename): |
179 | - print "set bg image", filename |
180 | + log.debug(u"set bg image %s", filename) |
181 | self._bg_image_filename=filename |
182 | if self._paint is not None: |
183 | self.scale_bg_image() |
184 | - |
185 | + |
186 | def scale_bg_image(self): |
187 | assert self._paint |
188 | i=QtGui.QImage(self._bg_image_filename) |
189 | @@ -67,7 +73,7 @@ |
190 | dcw=self._paint.width()+1;dch=self._paint.height() |
191 | imratio=imw/float(imh) |
192 | dcratio=dcw/float(dch) |
193 | - print "Image scaling params", imw, imh, imratio, dcw, dch, dcratio |
194 | + log.debug(u"Image scaling params %s %s %s %s %s %s", imw, imh, imratio, dcw, dch, dcratio) |
195 | if imratio > dcratio: |
196 | scale=dcw/float(imw) |
197 | elif imratio < dcratio: |
198 | @@ -84,9 +90,9 @@ |
199 | self._paint=p |
200 | if self._bg_image_filename is not None: |
201 | self.scale_bg_image() |
202 | - |
203 | + |
204 | def set_words_openlp(self, words): |
205 | -# print "set words openlp", words |
206 | +# log.debug(u" "set words openlp", words |
207 | verses=[] |
208 | words=words.replace("\r\n", "\n") |
209 | verses_text=words.split('\n\n') |
210 | @@ -99,9 +105,9 @@ |
211 | verses_text.append('\n'.join(v).lstrip()) # remove first \n |
212 | |
213 | return verses_text |
214 | - |
215 | + |
216 | def render_screen(self, screennum): |
217 | - print "render screen\n", screennum, self.words[screennum] |
218 | + log.debug(u"render screen\n %s %s ", screennum, self.words[screennum]) |
219 | import time |
220 | t=0.0 |
221 | words=self.words[screennum] |
222 | @@ -111,51 +117,45 @@ |
223 | def set_text_rectangle(self, rect): |
224 | """ Sets the rectangle within which text should be rendered""" |
225 | self._rect=rect |
226 | - |
227 | + |
228 | def _render_background(self): |
229 | - # xxx may have to prerender to a memdc when set theme is called for use on slow machines |
230 | - # takes 26ms on mijiti's machine! |
231 | assert(self._theme) |
232 | assert(self._paint) |
233 | - print "render background", self._theme.BackgroundType |
234 | + log.debug(u"render background %s %s", self._theme.BackgroundType) |
235 | p=QtGui.QPainter() |
236 | p.begin(self._paint) |
237 | if self._theme.BackgroundType == 0: |
238 | p.fillRect(self._paint.rect(), self._theme.BackgroundParameter1) |
239 | elif self._theme.BackgroundType == 1: # gradient |
240 | - # xxx Use a QGradient Brush!!! |
241 | - # get colours as tuples |
242 | - c1=self._theme.BackgroundParameter1.getRgb() |
243 | - c2=self._theme.BackgroundParameter2.getRgb() |
244 | - dir=self._theme.BackgroundParameter3 |
245 | - w=self._paint.width();h=self._paint.height() |
246 | - lines=[] |
247 | - pens=[] |
248 | - if dir == 0: # vertical |
249 | - for y in range (h): |
250 | - c=interpolate(c1, c2, y/float(h)) |
251 | - lines.append((0,y,w,y)) |
252 | - pens.append(QtGui.QPen(QtGui.QColor(c[0],c[1],c[2]))) # bleagh |
253 | - else: |
254 | - for x in range (w): |
255 | - c=interpolate(c1, c2, x/float(w)) |
256 | - lines.append((x,0,x,h)) |
257 | - pens.append(QtGui.QPen(QtGui.QColor(c[0],c[1],c[2]))) # bleagh |
258 | - for i in range(len(pens)): |
259 | - p.setPen(pens[i]) |
260 | - l=lines[i] |
261 | - p.drawLine(l[0],l[1],l[2],l[3]) # must be a better way! |
262 | + #TODO Add Theme code and fix direction |
263 | + |
264 | + gradient = QtGui.QLinearGradient(0, 0, self._paint.width(), self._paint.height()) |
265 | + gradient.setColorAt(0, QtGui.QColor(255, 0, 0)) |
266 | + gradient.setColorAt(0.5, QtGui.QColor(0, 255, 0)) |
267 | + gradient.setColorAt(1, QtGui.QColor(0, 0, 255)) |
268 | + p.setBrush(QtGui.QBrush(gradient)) |
269 | + rectPath = QtGui.QPainterPath() |
270 | + |
271 | + MAX_X = self._paint.width() |
272 | + MAX_Y = self._paint.height() |
273 | + |
274 | + rectPath.moveTo(0, 0) |
275 | + rectPath.lineTo(0, MAX_Y) |
276 | + rectPath.lineTo(MAX_X, MAX_Y) |
277 | + rectPath.lineTo(MAX_X, 0) |
278 | + rectPath.closeSubpath() |
279 | + p.drawPath(rectPath) |
280 | |
281 | elif self._theme.BackgroundType == 2: # image |
282 | r=self._paint.rect() |
283 | - print r.x(), r.y(), r.width(),r.height() |
284 | - print self._theme.BackgroundParameter2 |
285 | + log.debug(r.x(), r.y(), r.width(),r.height()) |
286 | + log.debug(self._theme.BackgroundParameter2) |
287 | if self._theme.BackgroundParameter2 is not None: |
288 | p.fillRect(self._paint.rect(), self._theme.BackgroundParameter2) |
289 | p.drawPixmap(self.background_offsetx,self.background_offsety, self.img) |
290 | p.end() |
291 | - print "render background done" |
292 | - |
293 | + log.debug(u"render background done") |
294 | + |
295 | def split_set_of_lines(self, lines): |
296 | |
297 | """Given a list of lines, decide how to split them best if they don't all fit on the screen |
298 | @@ -166,7 +166,7 @@ |
299 | |
300 | Returns a list of [lists of lines], one set for each screenful |
301 | """ |
302 | -# print "Split set of lines" |
303 | +# log.debug(u" "Split set of lines" |
304 | # Probably ought to save the rendering results to a pseudoDC for redrawing efficiency. But let's not optimse prematurely! |
305 | |
306 | bboxes = [] |
307 | @@ -202,7 +202,7 @@ |
308 | retval.append(thislines) |
309 | thislines=[] |
310 | else: |
311 | -# print "Just split where you can" |
312 | +# log.debug(u" "Just split where you can" |
313 | retval=[] |
314 | startline=0 |
315 | endline=startline+1 |
316 | @@ -221,9 +221,10 @@ |
317 | |
318 | def _render_lines(self, lines): |
319 | """render a set of lines according to the theme, return bounding box""" |
320 | - print "_render_lines", lines |
321 | + log.debug(u"_render_lines %s", lines) |
322 | |
323 | bbox=self._render_lines_unaligned(lines) |
324 | + print bbox |
325 | |
326 | t=self._theme |
327 | x=self._rect.left() |
328 | @@ -237,10 +238,10 @@ |
329 | assert(0, "Invalid value for theme.VerticalAlign:%d" % t.VerticalAlign) |
330 | self._render_background() |
331 | bbox=self._render_lines_unaligned(lines, (x,y)) |
332 | - print "render lines DONE" |
333 | + log.debug(u"render lines DONE") |
334 | |
335 | return bbox |
336 | - |
337 | + |
338 | def _render_lines_unaligned(self, lines, tlcorner=(0,0)): |
339 | |
340 | """Given a list of lines to render, render each one in turn |
341 | @@ -249,7 +250,7 @@ |
342 | than a screenful (eg. by using split_set_of_lines) |
343 | |
344 | Returns the bounding box of the text as QRect""" |
345 | - print "render unaligned", lines |
346 | + log.debug(u"render unaligned %s", lines) |
347 | x,y=tlcorner |
348 | brx=x |
349 | bry=y |
350 | @@ -268,7 +269,7 @@ |
351 | p.setPen(QtGui.QPen(QtGui.QColor(0,0,255))) |
352 | p.drawRect(retval) |
353 | p.end() |
354 | - print "render unaligned DONE" |
355 | + log.debug(u"render unaligned DONE") |
356 | |
357 | return retval |
358 | |
359 | @@ -282,14 +283,14 @@ |
360 | |
361 | Returns the bottom-right corner (of what was rendered) as a tuple(x,y). |
362 | """ |
363 | - print "Render single line '%s' @ %s "%( line, tlcorner) |
364 | + log.debug(u"Render single line '%s' @ %s "%( line, tlcorner)) |
365 | x,y=tlcorner |
366 | # We draw the text to see how big it is and then iterate to make it fit |
367 | # when we line wrap we do in in the "lyrics" style, so the second line is |
368 | # right aligned with a "hanging indent" |
369 | |
370 | # get the words |
371 | -# print "Getting the words split right" |
372 | +# log.debug(u" "Getting the words split right" |
373 | words=line.split(" ") |
374 | thisline=' '.join(words) |
375 | lastword=len(words) |
376 | @@ -307,8 +308,8 @@ |
377 | lastword-=1 |
378 | thisline=' '.join(words[:lastword]) |
379 | |
380 | -# print "This is how they split", lines |
381 | -# print "Now render them" |
382 | +# log.debug(u" "This is how they split", lines |
383 | +# log.debug(u" "Now render them" |
384 | startx=x |
385 | starty=y |
386 | rightextent=None |
387 | @@ -356,7 +357,7 @@ |
388 | self._get_extent_and_render(line, (x-self._outline_offset,y-self._outline_offset), dodraw=True, color = t.OutlineColor) |
389 | |
390 | self._get_extent_and_render(line, tlcorner=(x,y), dodraw=True) |
391 | -# print "Line %2d: Render '%s' at (%d, %d) wh=(%d,%d)"%( linenum, line, x, y,w,h) |
392 | +# log.debug(u" "Line %2d: Render '%s' at (%d, %d) wh=(%d,%d)"%( linenum, line, x, y,w,h) |
393 | y += h |
394 | if linenum == 0: |
395 | self._first_line_right_extent=rightextent |
396 | @@ -372,28 +373,36 @@ |
397 | return brcorner |
398 | |
399 | # xxx this is what to override for an SDL version |
400 | - def _get_extent_and_render(self, line, tlcorner=(0,0), dodraw=False, color=None): |
401 | + def _get_extent_and_render(self, line, tlcorner=(0,0), dodraw=False, color=None, footer = False): |
402 | """Find bounding box of text - as render_single_line. |
403 | If dodraw is set, actually draw the text to the current DC as well |
404 | |
405 | return width and height of text as a tuple (w,h)""" |
406 | # setup defaults |
407 | - print "_get_extent_and_render", [line], tlcorner, dodraw |
408 | + log.debug(u"_get_extent_and_render %s %s %s ", [line], tlcorner, dodraw) |
409 | p=QtGui.QPainter() |
410 | p.begin(self._paint) |
411 | # 'twould be more efficient to set this once when theme changes |
412 | # or p changes |
413 | - font=QtGui.QFont(self._theme.FontName, |
414 | - self._theme.FontProportion, # size |
415 | - QtGui.QFont.Normal, # weight |
416 | - 0)# italic |
417 | + if footer : |
418 | + font=QtGui.QFont(self._theme.FontName, |
419 | + 12, # size |
420 | + QtGui.QFont.Normal, # weight |
421 | + 0)# italic |
422 | + else: |
423 | + font=QtGui.QFont(self._theme.FontName, |
424 | + self._theme.FontProportion, # size |
425 | + QtGui.QFont.Normal, # weight |
426 | + 0)# italic |
427 | # to make the unit tests monitor independent, we have to be able to |
428 | # specify whether a font proportion is in pixels or points |
429 | if self._theme.FontUnits.lower() == "pixels": |
430 | - print "pixels" |
431 | - font.setPixelSize(self._theme.FontProportion) |
432 | - print self._theme.FontName, self._theme.FontProportion |
433 | - print font.family(), font.pointSize() |
434 | + log.debug(u"pixels") |
435 | + if footer: |
436 | + font.setPixelSize(12) |
437 | + else: |
438 | + font.setPixelSize(self._theme.FontProportion) |
439 | + log.debug(u'Font details %s %s %s %s', self._theme.FontName, self._theme.FontProportion, font.family(), font.pointSize()) |
440 | p.setFont(font) |
441 | if color == None: |
442 | p.setPen(self._theme.FontColor) |
443 | |
444 | === modified file 'openlp/core/test/test_render.py' |
445 | --- openlp/core/test/test_render.py 2009-03-12 20:19:24 +0000 |
446 | +++ openlp/core/test/test_render.py 2009-03-29 14:38:23 +0000 |
447 | @@ -81,6 +81,7 @@ |
448 | def teardown_class(self): |
449 | print "class quit", self, self.app |
450 | self.app.quit() |
451 | + |
452 | def setup_method(self, method): |
453 | print "SSsetup", method |
454 | if not hasattr(self, "app"): |
455 | @@ -157,6 +158,7 @@ |
456 | expected_answer=["Verse 1: Line 1\nLine 2","Verse 2: Line 1\nLine 2","Verse 3: Line 1\nLine 2\nLine 3"] |
457 | answer=self.r.set_words_openlp(words) |
458 | assert (answer==expected_answer) |
459 | + |
460 | def test_render_screens(self): |
461 | words=""" |
462 | Verse 1: Line 1 |
463 | @@ -177,6 +179,7 @@ |
464 | answer=self.r.render_screen(v) |
465 | # print v, answer.x(), answer.y(), answer.width(), answer.height() |
466 | assert(answer==expected_answer[v]) |
467 | + |
468 | def split_test(self, number, answer, expected_answers): |
469 | lines=[] |
470 | print "Split test", number, answer |
471 | |
472 | === modified file 'openlp/core/theme/theme.py' |
473 | --- openlp/core/theme/theme.py 2009-03-22 07:13:34 +0000 |
474 | +++ openlp/core/theme/theme.py 2009-03-29 14:38:23 +0000 |
475 | @@ -8,6 +8,8 @@ |
476 | from PyQt4 import QtGui |
477 | |
478 | DelphiColors={"clRed":0xFF0000, |
479 | + "clBlue":0x0000FF, |
480 | + "clYellow":0x0FFFF00, |
481 | "clBlack":0x000000, |
482 | "clWhite":0xFFFFFF} |
483 | |
484 | @@ -16,7 +18,7 @@ |
485 | <Theme> |
486 | <Name>BlankStyle</Name> |
487 | <BackgroundMode>1</BackgroundMode> |
488 | - <BackgroundType>0</BackgroundType> |
489 | + <BackgroundType>0</BackgroundType> |
490 | <BackgroundParameter1>$000000</BackgroundParameter1> |
491 | <BackgroundParameter2/> |
492 | <BackgroundParameter3/> |
493 | @@ -37,10 +39,10 @@ |
494 | """ stores the info about a theme |
495 | attributes: |
496 | name : theme name |
497 | - |
498 | + |
499 | BackgroundMode : 1 - Transparent |
500 | 1 - Opaque |
501 | - |
502 | + |
503 | BackgroundType : 0 - solid color |
504 | 1 - gradient color |
505 | 2 - image |
506 | @@ -53,7 +55,7 @@ |
507 | for solid - N/A |
508 | BackgroundParameter3 : for image - N/A |
509 | for gradient - 0 -> vertical, 1 -> horizontal |
510 | - |
511 | + |
512 | FontName : name of font to use |
513 | FontColor : color for main font |
514 | FontProportion : size of font |
515 | @@ -108,7 +110,7 @@ |
516 | # print "nope", |
517 | pass |
518 | elif DelphiColors.has_key(t): |
519 | -# print "colour", |
520 | +# print "colour", |
521 | val=DelphiColors[t] |
522 | else: |
523 | try: |
524 | @@ -121,7 +123,7 @@ |
525 | val= QtGui.QColor((val>>16) & 0xFF, (val>>8)&0xFF, val&0xFF) |
526 | # print [val] |
527 | setattr(self,element.tag, val) |
528 | - |
529 | + |
530 | |
531 | def __str__(self): |
532 | s="" |
533 | |
534 | === modified file 'openlp/core/ui/thememanager.py' |
535 | --- openlp/core/ui/thememanager.py 2009-03-23 20:18:06 +0000 |
536 | +++ openlp/core/ui/thememanager.py 2009-03-28 20:12:22 +0000 |
537 | @@ -17,17 +17,22 @@ |
538 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
539 | Place, Suite 330, Boston, MA 02111-1307 USA |
540 | """ |
541 | -import os |
542 | +import os,os.path |
543 | +import sys |
544 | +import zipfile |
545 | |
546 | from time import sleep |
547 | from copy import deepcopy |
548 | +from xml.etree.ElementTree import ElementTree, XML |
549 | from PyQt4 import * |
550 | from PyQt4 import QtCore, QtGui |
551 | from PyQt4.QtCore import * |
552 | from PyQt4.QtGui import * |
553 | # from openlp.core.resources import * |
554 | # from openlp.core.ui import AboutForm, AlertForm, SettingsForm, SlideController |
555 | +from openlp.core import translate |
556 | from openlp.core.lib import OpenLPToolbar |
557 | +from openlp.core.utils import ConfigHelper |
558 | #from openlp.core.lib import ThemeItem |
559 | |
560 | # from openlp.core import PluginManager |
561 | @@ -44,53 +49,74 @@ |
562 | def __init__(self): |
563 | QAbstractItemModel.__init__(self) |
564 | self.items=[] |
565 | + self.rowheight=50 |
566 | + self.maximagewidth=self.rowheight*16/9.0; |
567 | log.info("Starting") |
568 | + |
569 | + def clearItems(self): |
570 | + self.items=[] |
571 | + |
572 | def columnCount(self, parent): |
573 | return 1; # always only a single column (for now) |
574 | + |
575 | def rowCount(self, parent): |
576 | return len(self.items) |
577 | - def insertRow(self, row, Theme_item): |
578 | -# self.beginInsertRows(QModelIndex(),row,row) |
579 | - log.info("insert row %d:%s"%(row,Theme_item)) |
580 | - self.items.insert(row, Theme_item) |
581 | + |
582 | + def insertRow(self, row, filename): |
583 | + self.beginInsertRows(QModelIndex(),row,row) |
584 | + log.info("insert row %d:%s"%(row,filename)) |
585 | + (prefix, shortfilename) = os.path.split(str(filename)) |
586 | + log.info("shortfilename=%s"%(shortfilename)) |
587 | + # create a preview image |
588 | + if os.path.exists(filename): |
589 | + preview = QPixmap(str(filename)) |
590 | + w=self.maximagewidth;h=self.rowheight |
591 | + preview = preview.scaled(w,h, Qt.KeepAspectRatio) |
592 | + realw=preview.width(); realh=preview.height() |
593 | + # and move it to the centre of the preview space |
594 | + p=QPixmap(w,h) |
595 | + p.fill(Qt.transparent) |
596 | + painter=QPainter(p) |
597 | + painter.drawPixmap((w-realw)/2,(h-realh)/2,preview) |
598 | + else: |
599 | + w=self.maximagewidth;h=self.rowheight |
600 | + p=QPixmap(w,h) |
601 | + p.fill(Qt.transparent) |
602 | + # finally create the row |
603 | + self.items.insert(row,(filename, p, shortfilename)) |
604 | log.info("Items: %s" % self.items) |
605 | -# self.endInsertRows() |
606 | + self.endInsertRows() |
607 | + |
608 | def removeRow(self, row): |
609 | self.beginRemoveRows(QModelIndex(), row,row) |
610 | self.items.pop(row) |
611 | self.endRemoveRows() |
612 | + |
613 | def addRow(self, item): |
614 | self.insertRow(len(self.items), item) |
615 | - |
616 | + |
617 | def index(self, row, col, parent = QModelIndex()): |
618 | return self.createIndex(row,col) |
619 | |
620 | def parent(self, index=QModelIndex()): |
621 | return QModelIndex() # no children as yet |
622 | + |
623 | def data(self, index, role): |
624 | - """ |
625 | - Called by the Theme manager to draw us in the Theme window |
626 | - """ |
627 | row=index.row() |
628 | if row > len(self.items): # if the last row is selected and deleted, we then get called with an empty row! |
629 | return QVariant() |
630 | - item=self.items[row] |
631 | if role==Qt.DisplayRole: |
632 | - retval= item.pluginname + ":" + item.shortname |
633 | + retval= self.items[row][2] |
634 | elif role == Qt.DecorationRole: |
635 | - retval = item.iconic_representation |
636 | - elif role == Qt.ToolTipRole: |
637 | - retval= None |
638 | + retval= self.items[row][1] |
639 | else: |
640 | - retval= None |
641 | - if retval == None: |
642 | - retval=QVariant() |
643 | + retval= QVariant() |
644 | # log.info("Returning"+ str(retval)) |
645 | if type(retval) is not type(QVariant): |
646 | return QVariant(retval) |
647 | else: |
648 | return retval |
649 | - |
650 | + |
651 | def __iter__(self): |
652 | for i in self.items: |
653 | yield i |
654 | @@ -99,14 +125,9 @@ |
655 | log.info("Get Item:%d -> %s" %(row, str(self.items))) |
656 | return self.items[row] |
657 | |
658 | - |
659 | class ThemeManager(QWidget): |
660 | - |
661 | - """Manages the orders of Theme. Currently this involves taking |
662 | - text strings from plugins and adding them to an OOS file. In |
663 | - future, it will also handle zipping up all the resources used into |
664 | - one lump. |
665 | - Also handles the UI tasks of moving things up and down etc. |
666 | + """ |
667 | + Manages the orders of Theme. C |
668 | """ |
669 | global log |
670 | log=logging.getLogger(u'ThemeManager') |
671 | @@ -122,19 +143,26 @@ |
672 | self.Toolbar.addToolbarButton("Edit Theme", ":/themes/theme_edit.png") |
673 | self.Toolbar.addToolbarButton("Delete Theme", ":/themes/theme_delete.png") |
674 | self.Toolbar.addSeparator() |
675 | - self.Toolbar.addToolbarButton("Import Theme", ":/themes/theme_import.png") |
676 | - self.Toolbar.addToolbarButton("Export Theme", ":/themes/theme_export.png") |
677 | + self.Toolbar.addToolbarButton("Import Theme", ":/themes/theme_import.png", |
678 | + u'Allows Themes to be imported', self.onImportTheme) |
679 | + self.Toolbar.addToolbarButton("Export Theme", ":/themes/theme_export.png") |
680 | self.ThemeWidget = QtGui.QWidgetAction(self.Toolbar) |
681 | self.Toolbar.addAction(self.ThemeWidget) |
682 | |
683 | self.Layout.addWidget(self.Toolbar) |
684 | |
685 | - self.TreeView = QtGui.QTreeView(self) |
686 | + self.ThemeListView = QtGui.QListView(self) |
687 | self.Theme_data=ThemeData() |
688 | - self.TreeView.setModel(self.Theme_data) |
689 | - self.Layout.addWidget(self.TreeView) |
690 | + self.ThemeListView.setModel(self.Theme_data) |
691 | + self.ThemeListView.setAlternatingRowColors(True) |
692 | + self.Layout.addWidget(self.ThemeListView) |
693 | + self.ThemeListView.setAlternatingRowColors(True) |
694 | + |
695 | self.themelist= [] |
696 | - |
697 | + self.path = os.path.join(ConfigHelper.get_data_path(), u'themes') |
698 | + self.checkThemesExists(self.path) |
699 | + self.loadThemes() # load the themes |
700 | + |
701 | # def addThemeItem(self, item): |
702 | # """Adds Theme item""" |
703 | # log.info("addThemeItem") |
704 | @@ -158,7 +186,7 @@ |
705 | # self.Theme_data.addRow(item) |
706 | # else: |
707 | # self.Theme_data.insertRow(row+1, item) |
708 | -# |
709 | +# |
710 | # def removeThemeItem(self): |
711 | # """Remove currently selected item""" |
712 | # pass |
713 | @@ -182,10 +210,55 @@ |
714 | # oosfile.write(self.oos_as_text) |
715 | # oosfile.write("# END OOS\n") |
716 | # oosfile.close() |
717 | - |
718 | - def load(self): |
719 | - log.debug(u'Load') |
720 | - self.themelist = [u'African Sunset', u'Snowy Mountains', u'Wilderness', u'Wet and Windy London'] |
721 | - |
722 | + |
723 | + def onImportTheme(self): |
724 | + files = QtGui.QFileDialog.getOpenFileNames(None, |
725 | + translate('ThemeManager', u'Select Import File'), |
726 | + self.path, |
727 | + u'Theme (*.theme)') |
728 | + log.info(u'New Themes) %s', str(files)) |
729 | + if len(files) > 0: |
730 | + for file in files: |
731 | + self.unzipTheme(file, self.path) |
732 | + self.Theme_data.clearItems() |
733 | + self.loadThemes() |
734 | + |
735 | + def loadThemes(self): |
736 | + log.debug(u'Load themes from dir') |
737 | +# self.themelist = [u'African Sunset', u'Snowy Mountains', u'Wilderness', u'Wet and Windy London'] |
738 | + for root, dirs, files in os.walk(self.path): |
739 | + for name in files: |
740 | + if name.endswith(u'.bmp'): |
741 | + self.Theme_data.addRow(os.path.join(self.path, name)) |
742 | + |
743 | def getThemes(self): |
744 | return self.themelist |
745 | + |
746 | + def checkThemesExists(self, dir): |
747 | + log.debug(u'check themes') |
748 | + if os.path.exists(dir) == False: |
749 | + os.mkdir(dir) |
750 | + |
751 | + def unzipTheme(self, filename, dir): |
752 | + log.debug(u'Unzipping theme %s', filename) |
753 | + zip = zipfile.ZipFile(str(filename)) |
754 | + for file in zip.namelist(): |
755 | + if file.endswith('/'): |
756 | + theme_dir = os.path.join(dir, file) |
757 | + if os.path.exists(theme_dir) == False: |
758 | + os.mkdir(os.path.join(dir, file)) |
759 | + else: |
760 | + fullpath = os.path.join(dir, file) |
761 | + if file.endswith(u'.xml'): |
762 | + self.checkVersion1(fullpath) |
763 | + outfile = open(fullpath, 'w') |
764 | + outfile.write(zip.read(file)) |
765 | + outfile.close() |
766 | + |
767 | + def checkVersion1(self, xmlfile): |
768 | + file=open(xmlfile) |
769 | + t=''.join(file.readlines()) # read the file and change list to a string |
770 | + tree = ElementTree(element=XML(t)).getroot() |
771 | + print "AA" |
772 | + print tree.find('BackgroundType') |
773 | + print "AAA" |
Initial version of Theme Manager with Import Functionality.
Updated Renderer to use Qgradient for gradients.
Improvements will continue once new Theme xml schema is confirmed.