Update Render to use Qgradient instead of home built code.

Colors hard coded for now.
This commit is contained in:
Tim Bentley 2009-03-29 15:38:23 +01:00
parent 8aa67b5a8b
commit 4ac2ed4fd6
3 changed files with 67 additions and 61 deletions

View File

@ -17,13 +17,19 @@ You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA Place, Suite 330, Boston, MA 02111-1307 USA
""" """
import logging
import sys import sys
from PyQt4 import QtGui, QtCore, Qt from PyQt4 import QtGui, QtCore, Qt
from copy import copy from copy import copy
from interpolate import interpolate from interpolate import interpolate
class Renderer: class Renderer:
global log
log=logging.getLogger(u'Renderer')
log.info(u'Renderer Loaded')
"""All the functions for rendering a set of words onto a Device Context """All the functions for rendering a set of words onto a Device Context
How to use: How to use:
@ -44,21 +50,21 @@ class Renderer:
self._theme=None self._theme=None
self._bg_image_filename=None self._bg_image_filename=None
self._paint=None self._paint=None
def set_debug(self, debug): def set_debug(self, debug):
self._debug=debug self._debug=debug
def set_theme(self, theme): def set_theme(self, theme):
self._theme=theme self._theme=theme
if theme.BackgroundType == 2: if theme.BackgroundType == 2:
self.set_bg_image(theme.BackgroundParameter1) self.set_bg_image(theme.BackgroundParameter1)
def set_bg_image(self, filename): def set_bg_image(self, filename):
print "set bg image", filename log.debug(u"set bg image %s", filename)
self._bg_image_filename=filename self._bg_image_filename=filename
if self._paint is not None: if self._paint is not None:
self.scale_bg_image() self.scale_bg_image()
def scale_bg_image(self): def scale_bg_image(self):
assert self._paint assert self._paint
i=QtGui.QImage(self._bg_image_filename) i=QtGui.QImage(self._bg_image_filename)
@ -67,7 +73,7 @@ class Renderer:
dcw=self._paint.width()+1;dch=self._paint.height() dcw=self._paint.width()+1;dch=self._paint.height()
imratio=imw/float(imh) imratio=imw/float(imh)
dcratio=dcw/float(dch) dcratio=dcw/float(dch)
print "Image scaling params", imw, imh, imratio, dcw, dch, dcratio log.debug(u"Image scaling params %s %s %s %s %s %s", imw, imh, imratio, dcw, dch, dcratio)
if imratio > dcratio: if imratio > dcratio:
scale=dcw/float(imw) scale=dcw/float(imw)
elif imratio < dcratio: elif imratio < dcratio:
@ -84,9 +90,9 @@ class Renderer:
self._paint=p self._paint=p
if self._bg_image_filename is not None: if self._bg_image_filename is not None:
self.scale_bg_image() self.scale_bg_image()
def set_words_openlp(self, words): def set_words_openlp(self, words):
# print "set words openlp", words # log.debug(u" "set words openlp", words
verses=[] verses=[]
words=words.replace("\r\n", "\n") words=words.replace("\r\n", "\n")
verses_text=words.split('\n\n') verses_text=words.split('\n\n')
@ -99,9 +105,9 @@ class Renderer:
verses_text.append('\n'.join(v).lstrip()) # remove first \n verses_text.append('\n'.join(v).lstrip()) # remove first \n
return verses_text return verses_text
def render_screen(self, screennum): def render_screen(self, screennum):
print "render screen\n", screennum, self.words[screennum] log.debug(u"render screen\n %s %s ", screennum, self.words[screennum])
import time import time
t=0.0 t=0.0
words=self.words[screennum] words=self.words[screennum]
@ -111,51 +117,45 @@ class Renderer:
def set_text_rectangle(self, rect): def set_text_rectangle(self, rect):
""" Sets the rectangle within which text should be rendered""" """ Sets the rectangle within which text should be rendered"""
self._rect=rect self._rect=rect
def _render_background(self): def _render_background(self):
# xxx may have to prerender to a memdc when set theme is called for use on slow machines
# takes 26ms on mijiti's machine!
assert(self._theme) assert(self._theme)
assert(self._paint) assert(self._paint)
print "render background", self._theme.BackgroundType log.debug(u"render background %s %s", self._theme.BackgroundType)
p=QtGui.QPainter() p=QtGui.QPainter()
p.begin(self._paint) p.begin(self._paint)
if self._theme.BackgroundType == 0: if self._theme.BackgroundType == 0:
p.fillRect(self._paint.rect(), self._theme.BackgroundParameter1) p.fillRect(self._paint.rect(), self._theme.BackgroundParameter1)
elif self._theme.BackgroundType == 1: # gradient elif self._theme.BackgroundType == 1: # gradient
# xxx Use a QGradient Brush!!! #TODO Add Theme code and fix direction
# get colours as tuples
c1=self._theme.BackgroundParameter1.getRgb() gradient = QtGui.QLinearGradient(0, 0, self._paint.width(), self._paint.height())
c2=self._theme.BackgroundParameter2.getRgb() gradient.setColorAt(0, QtGui.QColor(255, 0, 0))
dir=self._theme.BackgroundParameter3 gradient.setColorAt(0.5, QtGui.QColor(0, 255, 0))
w=self._paint.width();h=self._paint.height() gradient.setColorAt(1, QtGui.QColor(0, 0, 255))
lines=[] p.setBrush(QtGui.QBrush(gradient))
pens=[] rectPath = QtGui.QPainterPath()
if dir == 0: # vertical
for y in range (h): MAX_X = self._paint.width()
c=interpolate(c1, c2, y/float(h)) MAX_Y = self._paint.height()
lines.append((0,y,w,y))
pens.append(QtGui.QPen(QtGui.QColor(c[0],c[1],c[2]))) # bleagh rectPath.moveTo(0, 0)
else: rectPath.lineTo(0, MAX_Y)
for x in range (w): rectPath.lineTo(MAX_X, MAX_Y)
c=interpolate(c1, c2, x/float(w)) rectPath.lineTo(MAX_X, 0)
lines.append((x,0,x,h)) rectPath.closeSubpath()
pens.append(QtGui.QPen(QtGui.QColor(c[0],c[1],c[2]))) # bleagh p.drawPath(rectPath)
for i in range(len(pens)):
p.setPen(pens[i])
l=lines[i]
p.drawLine(l[0],l[1],l[2],l[3]) # must be a better way!
elif self._theme.BackgroundType == 2: # image elif self._theme.BackgroundType == 2: # image
r=self._paint.rect() r=self._paint.rect()
print r.x(), r.y(), r.width(),r.height() log.debug(r.x(), r.y(), r.width(),r.height())
print self._theme.BackgroundParameter2 log.debug(self._theme.BackgroundParameter2)
if self._theme.BackgroundParameter2 is not None: if self._theme.BackgroundParameter2 is not None:
p.fillRect(self._paint.rect(), self._theme.BackgroundParameter2) p.fillRect(self._paint.rect(), self._theme.BackgroundParameter2)
p.drawPixmap(self.background_offsetx,self.background_offsety, self.img) p.drawPixmap(self.background_offsetx,self.background_offsety, self.img)
p.end() p.end()
print "render background done" log.debug(u"render background done")
def split_set_of_lines(self, lines): def split_set_of_lines(self, lines):
"""Given a list of lines, decide how to split them best if they don't all fit on the screen """Given a list of lines, decide how to split them best if they don't all fit on the screen
@ -166,7 +166,7 @@ class Renderer:
Returns a list of [lists of lines], one set for each screenful Returns a list of [lists of lines], one set for each screenful
""" """
# print "Split set of lines" # log.debug(u" "Split set of lines"
# Probably ought to save the rendering results to a pseudoDC for redrawing efficiency. But let's not optimse prematurely! # Probably ought to save the rendering results to a pseudoDC for redrawing efficiency. But let's not optimse prematurely!
bboxes = [] bboxes = []
@ -202,7 +202,7 @@ class Renderer:
retval.append(thislines) retval.append(thislines)
thislines=[] thislines=[]
else: else:
# print "Just split where you can" # log.debug(u" "Just split where you can"
retval=[] retval=[]
startline=0 startline=0
endline=startline+1 endline=startline+1
@ -221,9 +221,10 @@ class Renderer:
def _render_lines(self, lines): def _render_lines(self, lines):
"""render a set of lines according to the theme, return bounding box""" """render a set of lines according to the theme, return bounding box"""
print "_render_lines", lines log.debug(u"_render_lines %s", lines)
bbox=self._render_lines_unaligned(lines) bbox=self._render_lines_unaligned(lines)
print bbox
t=self._theme t=self._theme
x=self._rect.left() x=self._rect.left()
@ -237,10 +238,10 @@ class Renderer:
assert(0, "Invalid value for theme.VerticalAlign:%d" % t.VerticalAlign) assert(0, "Invalid value for theme.VerticalAlign:%d" % t.VerticalAlign)
self._render_background() self._render_background()
bbox=self._render_lines_unaligned(lines, (x,y)) bbox=self._render_lines_unaligned(lines, (x,y))
print "render lines DONE" log.debug(u"render lines DONE")
return bbox return bbox
def _render_lines_unaligned(self, lines, tlcorner=(0,0)): def _render_lines_unaligned(self, lines, tlcorner=(0,0)):
"""Given a list of lines to render, render each one in turn """Given a list of lines to render, render each one in turn
@ -249,7 +250,7 @@ class Renderer:
than a screenful (eg. by using split_set_of_lines) than a screenful (eg. by using split_set_of_lines)
Returns the bounding box of the text as QRect""" Returns the bounding box of the text as QRect"""
print "render unaligned", lines log.debug(u"render unaligned %s", lines)
x,y=tlcorner x,y=tlcorner
brx=x brx=x
bry=y bry=y
@ -268,7 +269,7 @@ class Renderer:
p.setPen(QtGui.QPen(QtGui.QColor(0,0,255))) p.setPen(QtGui.QPen(QtGui.QColor(0,0,255)))
p.drawRect(retval) p.drawRect(retval)
p.end() p.end()
print "render unaligned DONE" log.debug(u"render unaligned DONE")
return retval return retval
@ -282,14 +283,14 @@ class Renderer:
Returns the bottom-right corner (of what was rendered) as a tuple(x,y). Returns the bottom-right corner (of what was rendered) as a tuple(x,y).
""" """
print "Render single line '%s' @ %s "%( line, tlcorner) log.debug(u"Render single line '%s' @ %s "%( line, tlcorner))
x,y=tlcorner x,y=tlcorner
# We draw the text to see how big it is and then iterate to make it fit # We draw the text to see how big it is and then iterate to make it fit
# when we line wrap we do in in the "lyrics" style, so the second line is # when we line wrap we do in in the "lyrics" style, so the second line is
# right aligned with a "hanging indent" # right aligned with a "hanging indent"
# get the words # get the words
# print "Getting the words split right" # log.debug(u" "Getting the words split right"
words=line.split(" ") words=line.split(" ")
thisline=' '.join(words) thisline=' '.join(words)
lastword=len(words) lastword=len(words)
@ -307,8 +308,8 @@ class Renderer:
lastword-=1 lastword-=1
thisline=' '.join(words[:lastword]) thisline=' '.join(words[:lastword])
# print "This is how they split", lines # log.debug(u" "This is how they split", lines
# print "Now render them" # log.debug(u" "Now render them"
startx=x startx=x
starty=y starty=y
rightextent=None rightextent=None
@ -356,7 +357,7 @@ class Renderer:
self._get_extent_and_render(line, (x-self._outline_offset,y-self._outline_offset), dodraw=True, color = t.OutlineColor) self._get_extent_and_render(line, (x-self._outline_offset,y-self._outline_offset), dodraw=True, color = t.OutlineColor)
self._get_extent_and_render(line, tlcorner=(x,y), dodraw=True) self._get_extent_and_render(line, tlcorner=(x,y), dodraw=True)
# print "Line %2d: Render '%s' at (%d, %d) wh=(%d,%d)"%( linenum, line, x, y,w,h) # log.debug(u" "Line %2d: Render '%s' at (%d, %d) wh=(%d,%d)"%( linenum, line, x, y,w,h)
y += h y += h
if linenum == 0: if linenum == 0:
self._first_line_right_extent=rightextent self._first_line_right_extent=rightextent
@ -378,7 +379,7 @@ class Renderer:
return width and height of text as a tuple (w,h)""" return width and height of text as a tuple (w,h)"""
# setup defaults # setup defaults
print "_get_extent_and_render", [line], tlcorner, dodraw log.debug(u"_get_extent_and_render %s %s %s ", [line], tlcorner, dodraw)
p=QtGui.QPainter() p=QtGui.QPainter()
p.begin(self._paint) p.begin(self._paint)
# 'twould be more efficient to set this once when theme changes # 'twould be more efficient to set this once when theme changes
@ -390,10 +391,10 @@ class Renderer:
# to make the unit tests monitor independent, we have to be able to # to make the unit tests monitor independent, we have to be able to
# specify whether a font proportion is in pixels or points # specify whether a font proportion is in pixels or points
if self._theme.FontUnits.lower() == "pixels": if self._theme.FontUnits.lower() == "pixels":
print "pixels" log.debug(u"pixels")
font.setPixelSize(self._theme.FontProportion) font.setPixelSize(self._theme.FontProportion)
print self._theme.FontName, self._theme.FontProportion log.debug(self._theme.FontName, self._theme.FontProportion)
print font.family(), font.pointSize() log.debug(font.family(), font.pointSize())
p.setFont(font) p.setFont(font)
if color == None: if color == None:
p.setPen(self._theme.FontColor) p.setPen(self._theme.FontColor)

View File

@ -81,6 +81,7 @@ class TestRender_base:
def teardown_class(self): def teardown_class(self):
print "class quit", self, self.app print "class quit", self, self.app
self.app.quit() self.app.quit()
def setup_method(self, method): def setup_method(self, method):
print "SSsetup", method print "SSsetup", method
if not hasattr(self, "app"): if not hasattr(self, "app"):
@ -157,6 +158,7 @@ Line 3"""
expected_answer=["Verse 1: Line 1\nLine 2","Verse 2: Line 1\nLine 2","Verse 3: Line 1\nLine 2\nLine 3"] expected_answer=["Verse 1: Line 1\nLine 2","Verse 2: Line 1\nLine 2","Verse 3: Line 1\nLine 2\nLine 3"]
answer=self.r.set_words_openlp(words) answer=self.r.set_words_openlp(words)
assert (answer==expected_answer) assert (answer==expected_answer)
def test_render_screens(self): def test_render_screens(self):
words=""" words="""
Verse 1: Line 1 Verse 1: Line 1
@ -177,6 +179,7 @@ Line 3"""
answer=self.r.render_screen(v) answer=self.r.render_screen(v)
# print v, answer.x(), answer.y(), answer.width(), answer.height() # print v, answer.x(), answer.y(), answer.width(), answer.height()
assert(answer==expected_answer[v]) assert(answer==expected_answer[v])
def split_test(self, number, answer, expected_answers): def split_test(self, number, answer, expected_answers):
lines=[] lines=[]
print "Split test", number, answer print "Split test", number, answer

View File

@ -8,6 +8,8 @@ else:
from PyQt4 import QtGui from PyQt4 import QtGui
DelphiColors={"clRed":0xFF0000, DelphiColors={"clRed":0xFF0000,
"clBlue":0x0000FF,
"clYellow":0x0FFFF00,
"clBlack":0x000000, "clBlack":0x000000,
"clWhite":0xFFFFFF} "clWhite":0xFFFFFF}
@ -16,7 +18,7 @@ blankstylexml=\
<Theme> <Theme>
<Name>BlankStyle</Name> <Name>BlankStyle</Name>
<BackgroundMode>1</BackgroundMode> <BackgroundMode>1</BackgroundMode>
<BackgroundType>0</BackgroundType> <BackgroundType>0</BackgroundType>
<BackgroundParameter1>$000000</BackgroundParameter1> <BackgroundParameter1>$000000</BackgroundParameter1>
<BackgroundParameter2/> <BackgroundParameter2/>
<BackgroundParameter3/> <BackgroundParameter3/>
@ -37,10 +39,10 @@ class Theme:
""" stores the info about a theme """ stores the info about a theme
attributes: attributes:
name : theme name name : theme name
BackgroundMode : 1 - Transparent BackgroundMode : 1 - Transparent
1 - Opaque 1 - Opaque
BackgroundType : 0 - solid color BackgroundType : 0 - solid color
1 - gradient color 1 - gradient color
2 - image 2 - image
@ -53,7 +55,7 @@ class Theme:
for solid - N/A for solid - N/A
BackgroundParameter3 : for image - N/A BackgroundParameter3 : for image - N/A
for gradient - 0 -> vertical, 1 -> horizontal for gradient - 0 -> vertical, 1 -> horizontal
FontName : name of font to use FontName : name of font to use
FontColor : color for main font FontColor : color for main font
FontProportion : size of font FontProportion : size of font
@ -108,7 +110,7 @@ class Theme:
# print "nope", # print "nope",
pass pass
elif DelphiColors.has_key(t): elif DelphiColors.has_key(t):
# print "colour", # print "colour",
val=DelphiColors[t] val=DelphiColors[t]
else: else:
try: try:
@ -121,7 +123,7 @@ class Theme:
val= QtGui.QColor((val>>16) & 0xFF, (val>>8)&0xFF, val&0xFF) val= QtGui.QColor((val>>16) & 0xFF, (val>>8)&0xFF, val&0xFF)
# print [val] # print [val]
setattr(self,element.tag, val) setattr(self,element.tag, val)
def __str__(self): def __str__(self):
s="" s=""