Merge lp:~trb143/openlp/ThemeManager into lp:openlp

Proposed by Tim Bentley
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
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Michael Gorven (community) Approve
Raoul Snyman Approve
Review via email: mp+5014@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) wrote :

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.

Revision history for this message
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_and_render(self, line, tlcorner=(0,0), dodraw=False, color=None, footer = False):

In the function above, "dodraw" - surely that can be changed to simply "draw" ?

review: Approve
Revision history for this message
Michael Gorven (mgorven) wrote :

+ log.debug(r.x(), r.y(), r.width(),r.height())
+ log.debug(self._theme.BackgroundParameter2)
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('BackgroundType')
+ print "AAA"
Should use log.debug().

 review approve

review: Approve
Revision history for this message
Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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"