Convert htmlbuilder.py to use Template() strings for css

- Changed strings to use Template() instead of format() for css building
- Fix htmlbuilder strings tests
- Cleanup template variables and fix tests to match
- bug 1590386 - string format error in editbibledialog.py
- bug 1590475 - string format error in mediaitem.py
- Function call error in firsttimeform.py (missed label text variable)

--------------------------------
lp:~alisonken1/openlp/strings-templates2 (revision 2679)
[SUCCESS] ...

bzr-revno: 2675
Fixes: https://launchpad.net/bugs/1590386, https://launchpad.net/bugs/1590475
This commit is contained in:
Ken Roberts 2016-06-14 22:05:51 +01:00 committed by Tim Bentley
commit f20499b1af
5 changed files with 400 additions and 389 deletions

View File

@ -390,66 +390,65 @@ is the function which has to be called from outside. The generated and returned
import logging import logging
from PyQt5 import QtWebKit from PyQt5 import QtWebKit
from string import Template
from openlp.core.common import Settings from openlp.core.common import Settings
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, VerticalType, HorizontalType from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, VerticalType, HorizontalType
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# TODO: Verify where this is used before converting to python3 HTML_SRC = Template("""
HTMLSRC = """ <!DOCTYPE html>
<!DOCTYPE html> <html>
<html> <head>
<head> <title>OpenLP Display</title>
<title>OpenLP Display</title> <style>
<style> *{
*{
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;
overflow: hidden; overflow: hidden;
-webkit-user-select: none; -webkit-user-select: none;
} }
body { body {
%s; ${bg_css};
} }
.size { .size {
position: absolute; position: absolute;
left: 0px; left: 0px;
top: 0px; top: 0px;
width: 100%%; width: 100%;
height: 100%%; height: 100%;
} }
#black { #black {
z-index: 8; z-index: 8;
background-color: black; background-color: black;
display: none; display: none;
} }
#bgimage { #bgimage {
z-index: 1; z-index: 1;
} }
#image { #image {
z-index: 2; z-index: 2;
} }
%s ${css_additions}
#footer { #footer {
position: absolute; position: absolute;
z-index: 6; z-index: 6;
%s ${footer_css}
} }
/* lyric css */ /* lyric css */${lyrics_css}
%s sup {
sup {
font-size: 0.6em; font-size: 0.6em;
vertical-align: top; vertical-align: top;
position: relative; position: relative;
top: -0.3em; top: -0.3em;
} }
</style> </style>
<script> <script>
var timer = null; var timer = null;
var transition = %s; var transition = ${transitions};
%s ${js_additions}
function show_image(src){ function show_image(src){
var img = document.getElementById('image'); var img = document.getElementById('image');
@ -535,18 +534,63 @@ sup {
function show_text_completed(){ function show_text_completed(){
return (timer == null); return (timer == null);
} }
</script> </script>
</head> </head>
<body> <body>
<img id="bgimage" class="size" %s /> <img id="bgimage" class="size" ${bg_image} />
<img id="image" class="size" %s /> <img id="image" class="size" ${image} />
%s ${html_additions}
<div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div> <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
<div id="footer" class="footer"></div> <div id="footer" class="footer"></div>
<div id="black" class="size"></div> <div id="black" class="size"></div>
</body> </body>
</html> </html>
""" """)
LYRICS_SRC = Template("""
.lyricstable {
z-index: 5;
position: absolute;
display: table;
${stable}
}
.lyricscell {
display: table-cell;
word-wrap: break-word;
-webkit-transition: opacity 0.4s ease;
${lyrics}
}
.lyricsmain {
${main}
}
""")
FOOTER_SRC = Template("""
left: ${left}px;
bottom: ${bottom}px;
width: ${width}px;
font-family: ${family};
font-size: ${size}pt;
color: ${color};
text-align: left;
white-space: ${space};
""")
LYRICS_FORMAT_SRC = Template("""
${justify}word-wrap: break-word;
text-align: ${align};
vertical-align: ${valign};
font-family: ${font};
font-size: ${size}pt;
color: ${color};
line-height: ${line}%;
margin: 0;
padding: 0;
padding-bottom: ${bottom};
padding-left: ${left}px;
width: ${width}px;
height: ${height}px;${font_style}${font_weight}
""")
def build_html(item, screen, is_live, background, image=None, plugins=None): def build_html(item, screen, is_live, background, image=None, plugins=None):
@ -582,18 +626,17 @@ def build_html(item, screen, is_live, background, image=None, plugins=None):
css_additions += plugin.get_display_css() css_additions += plugin.get_display_css()
js_additions += plugin.get_display_javascript() js_additions += plugin.get_display_javascript()
html_additions += plugin.get_display_html() html_additions += plugin.get_display_html()
html = HTMLSRC % ( return HTML_SRC.substitute(bg_css=build_background_css(item, width),
build_background_css(item, width), css_additions=css_additions,
css_additions, footer_css=build_footer_css(item, height),
build_footer_css(item, height), lyrics_css=build_lyrics_css(item),
build_lyrics_css(item), transitions='true' if (theme_data and
'true' if theme_data and theme_data.display_slide_transition and is_live else 'false', theme_data.display_slide_transition and
js_additions, is_live) else 'false',
bgimage_src, js_additions=js_additions,
image_src, bg_image=bgimage_src,
html_additions image=image_src,
) html_additions=html_additions)
return html
def webkit_version(): def webkit_version():
@ -650,24 +693,6 @@ def build_lyrics_css(item):
:param item: Service Item containing theme and location information :param item: Service Item containing theme and location information
""" """
# TODO: Verify this before converting to python3
style = """
.lyricstable {
z-index: 5;
position: absolute;
display: table;
%s
}
.lyricscell {
display: table-cell;
word-wrap: break-word;
-webkit-transition: opacity 0.4s ease;
%s
}
.lyricsmain {
%s
}
"""
theme_data = item.theme_data theme_data = item.theme_data
lyricstable = '' lyricstable = ''
lyrics = '' lyrics = ''
@ -680,8 +705,7 @@ def build_lyrics_css(item):
lyricsmain += ' text-shadow: {theme} {shadow}px ' \ lyricsmain += ' text-shadow: {theme} {shadow}px ' \
'{shadow}px;'.format(theme=theme_data.font_main_shadow_color, '{shadow}px;'.format(theme=theme_data.font_main_shadow_color,
shadow=theme_data.font_main_shadow_size) shadow=theme_data.font_main_shadow_size)
lyrics_css = style % (lyricstable, lyrics, lyricsmain) return LYRICS_SRC.substitute(stable=lyricstable, lyrics=lyrics, main=lyricsmain)
return lyrics_css
def build_lyrics_outline_css(theme_data): def build_lyrics_outline_css(theme_data):
@ -710,38 +734,23 @@ def build_lyrics_format_css(theme_data, width, height):
""" """
align = HorizontalType.Names[theme_data.display_horizontal_align] align = HorizontalType.Names[theme_data.display_horizontal_align]
valign = VerticalType.Names[theme_data.display_vertical_align] valign = VerticalType.Names[theme_data.display_vertical_align]
if theme_data.font_main_outline: left_margin = (int(theme_data.font_main_outline_size) * 2) if theme_data.font_main_outline else 0
left_margin = int(theme_data.font_main_outline_size) * 2
else:
left_margin = 0
justify = 'white-space:pre-wrap;'
# fix tag incompatibilities # fix tag incompatibilities
if theme_data.display_horizontal_align == HorizontalType.Justify: justify = '' if (theme_data.display_horizontal_align == HorizontalType.Justify) else ' white-space: pre-wrap;\n'
justify = '' padding_bottom = '0.5em' if (theme_data.display_vertical_align == VerticalType.Bottom) else '0'
if theme_data.display_vertical_align == VerticalType.Bottom: return LYRICS_FORMAT_SRC.substitute(justify=justify,
padding_bottom = '0.5em'
else:
padding_bottom = '0'
lyrics = '{justify} word-wrap: break-word; ' \
'text-align: {align}; vertical-align: {valign}; font-family: {font}; ' \
'font-size: {size}pt; color: {color}; line-height: {line:d}%; margin: 0;' \
'padding: 0; padding-bottom: {bottom}; padding-left: {left}px; width: {width}px; ' \
'height: {height}px; '.format(justify=justify,
align=align, align=align,
valign=valign, valign=valign,
font=theme_data.font_main_name, font=theme_data.font_main_name,
size=theme_data.font_main_size, size=theme_data.font_main_size,
color=theme_data.font_main_color, color=theme_data.font_main_color,
line=100 + int(theme_data.font_main_line_adjustment), line='{line:d}'.format(line=100 + int(theme_data.font_main_line_adjustment)),
bottom=padding_bottom, bottom=padding_bottom,
left=left_margin, left=left_margin,
width=width, width=width,
height=height) height=height,
if theme_data.font_main_italics: font_style='\n font-style: italic;' if theme_data.font_main_italics else '',
lyrics += 'font-style:italic; ' font_weight='\n font-weight: bold;' if theme_data.font_main_bold else '')
if theme_data.font_main_bold:
lyrics += 'font-weight:bold; '
return lyrics
def build_footer_css(item, height): def build_footer_css(item, height):
@ -751,22 +760,11 @@ def build_footer_css(item, height):
:param item: Service Item to be processed. :param item: Service Item to be processed.
:param height: :param height:
""" """
style = """
left: {left}px;
bottom: {bottom}px;
width: {width}px;
font-family: {family};
font-size: {size}pt;
color: {color};
text-align: left;
white-space: {space};
"""
theme = item.theme_data theme = item.theme_data
if not theme or not item.footer: if not theme or not item.footer:
return '' return ''
bottom = height - int(item.footer.y()) - int(item.footer.height()) bottom = height - int(item.footer.y()) - int(item.footer.height())
whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap' whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
lyrics_html = style.format(left=item.footer.x(), bottom=bottom, width=item.footer.width(), return FOOTER_SRC.substitute(left=item.footer.x(), bottom=bottom, width=item.footer.width(),
family=theme.font_footer_name, size=theme.font_footer_size, family=theme.font_footer_name, size=theme.font_footer_size,
color=theme.font_footer_color, space=whitespace) color=theme.font_footer_color, space=whitespace)
return lyrics_html

View File

@ -565,13 +565,13 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
if self.has_run_wizard: if self.has_run_wizard:
text = translate('OpenLP.FirstTimeWizard', text = translate('OpenLP.FirstTimeWizard',
'Download complete. Click the {button} button to return to OpenLP.' 'Download complete. Click the {button} button to return to OpenLP.'
).format(text=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
self.progress_label.setText(text) self.progress_label.setText(text)
else: else:
text = translate('OpenLP.FirstTimeWizard', text = translate('OpenLP.FirstTimeWizard',
'Download complete. Click the {button} button to start OpenLP.' 'Download complete. Click the {button} button to start OpenLP.'
).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
self.progress_label.setText() self.progress_label.setText(text)
else: else:
if self.has_run_wizard: if self.has_run_wizard:
text = translate('OpenLP.FirstTimeWizard', text = translate('OpenLP.FirstTimeWizard',
@ -582,7 +582,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
text = translate('OpenLP.FirstTimeWizard', text = translate('OpenLP.FirstTimeWizard',
'Click the {button} button to start OpenLP.' 'Click the {button} button to start OpenLP.'
).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
self.progress_label.setText() self.progress_label.setText(text)
self.finish_button.setVisible(True) self.finish_button.setVisible(True)
self.finish_button.setEnabled(True) self.finish_button.setEnabled(True)
self.cancel_button.setVisible(False) self.cancel_button.setVisible(False)

View File

@ -104,7 +104,7 @@ class Ui_EditBibleDialog(object):
for book in BiblesResourcesDB.get_books(): for book in BiblesResourcesDB.get_books():
self.book_name_label[book['abbreviation']] = QtWidgets.QLabel(self.book_name_widget) self.book_name_label[book['abbreviation']] = QtWidgets.QLabel(self.book_name_widget)
self.book_name_label[book['abbreviation']].setObjectName( self.book_name_label[book['abbreviation']].setObjectName(
'book_name_label[{name}]'.format(book=book['abbreviation'])) 'book_name_label[{book}]'.format(book=book['abbreviation']))
self.book_name_edit[book['abbreviation']] = QtWidgets.QLineEdit(self.book_name_widget) self.book_name_edit[book['abbreviation']] = QtWidgets.QLineEdit(self.book_name_widget)
self.book_name_edit[book['abbreviation']].setObjectName( self.book_name_edit[book['abbreviation']].setObjectName(
'book_name_edit[{name}]'.format(name=book['abbreviation'])) 'book_name_edit[{name}]'.format(name=book['abbreviation']))

View File

@ -603,7 +603,7 @@ class SongMediaItem(MediaManagerItem):
else: else:
verse_index = VerseType.from_tag(verse[0]['type']) verse_index = VerseType.from_tag(verse[0]['type'])
verse_tag = VerseType.translated_tags[verse_index] verse_tag = VerseType.translated_tags[verse_index]
verse_def = '{tag}{label}'.format(tzg=verse_tag, text=verse[0]['label']) verse_def = '{tag}{text}'.format(tag=verse_tag, text=verse[0]['label'])
service_item.add_from_text(verse[1], verse_def) service_item.add_from_text(verse[1], verse_def)
service_item.title = song.title service_item.title = song.title
author_list = self.generate_footer(service_item, song) author_list = self.generate_footer(service_item, song)

View File

@ -14,55 +14,54 @@ from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
HTML = """ HTML = """
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>OpenLP Display</title> <title>OpenLP Display</title>
<style> <style>
*{ *{
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;
overflow: hidden; overflow: hidden;
-webkit-user-select: none; -webkit-user-select: none;
} }
body { body {
; ;
} }
.size { .size {
position: absolute; position: absolute;
left: 0px; left: 0px;
top: 0px; top: 0px;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
#black { #black {
z-index: 8; z-index: 8;
background-color: black; background-color: black;
display: none; display: none;
} }
#bgimage { #bgimage {
z-index: 1; z-index: 1;
} }
#image { #image {
z-index: 2; z-index: 2;
} }
plugin CSS plugin CSS
#footer { #footer {
position: absolute; position: absolute;
z-index: 6; z-index: 6;
dummy: dummy; dummy: dummy;
} }
/* lyric css */ /* lyric css */
sup {
sup {
font-size: 0.6em; font-size: 0.6em;
vertical-align: top; vertical-align: top;
position: relative; position: relative;
top: -0.3em; top: -0.3em;
} }
</style> </style>
<script> <script>
var timer = null; var timer = null;
var transition = false; var transition = false;
plugin JS plugin JS
@ -151,40 +150,54 @@ sup {
function show_text_completed(){ function show_text_completed(){
return (timer == null); return (timer == null);
} }
</script> </script>
</head> </head>
<body> <body>
<img id="bgimage" class="size" style="display:none;" /> <img id="bgimage" class="size" style="display:none;" />
<img id="image" class="size" style="display:none;" /> <img id="image" class="size" style="display:none;" />
plugin HTML plugin HTML
<div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div> <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
<div id="footer" class="footer"></div> <div id="footer" class="footer"></div>
<div id="black" class="size"></div> <div id="black" class="size"></div>
</body> </body>
</html> </html>
""" """
BACKGROUND_CSS_RADIAL = 'background: -webkit-gradient(radial, 5 50%, 100, 5 50%, 5, from(#000000), to(#FFFFFF)) fixed' BACKGROUND_CSS_RADIAL = 'background: -webkit-gradient(radial, 5 50%, 100, 5 50%, 5, from(#000000), to(#FFFFFF)) fixed'
LYRICS_CSS = """ LYRICS_CSS = """
.lyricstable { .lyricstable {
z-index: 5; z-index: 5;
position: absolute; position: absolute;
display: table; display: table;
left: 10px; top: 20px; left: 10px; top: 20px;
} }
.lyricscell { .lyricscell {
display: table-cell; display: table-cell;
word-wrap: break-word; word-wrap: break-word;
-webkit-transition: opacity 0.4s ease; -webkit-transition: opacity 0.4s ease;
lyrics_format_css lyrics_format_css
} }
.lyricsmain { .lyricsmain {
text-shadow: #000000 5px 5px; text-shadow: #000000 5px 5px;
} }
""" """
LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; ' LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; '
LYRICS_FORMAT_CSS = ' word-wrap: break-word; text-align: justify; vertical-align: bottom; ' + \ LYRICS_FORMAT_CSS = """
'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 108%; margin: 0;padding: 0; ' + \ word-wrap: break-word;
'padding-bottom: 0.5em; padding-left: 2px; width: 1580px; height: 810px; font-style:italic; font-weight:bold; ' text-align: justify;
vertical-align: bottom;
font-family: Arial;
font-size: 40pt;
color: #FFFFFF;
line-height: 108%;
margin: 0;
padding: 0;
padding-bottom: 0.5em;
padding-left: 2px;
width: 1580px;
height: 810px;
font-style: italic;
font-weight: bold;
"""
FOOTER_CSS_BASE = """ FOOTER_CSS_BASE = """
left: 10px; left: 10px;
bottom: 0px; bottom: 0px;