This commit is contained in:
rimach 2010-11-27 08:09:14 +01:00
commit bd173bd217
7 changed files with 139 additions and 66 deletions

View File

@ -48,7 +48,7 @@ Service Manger
-------------- --------------
The service manager contains the media items in your service file. This is the The service manager contains the media items in your service file. This is the
area from wich your media items go live, and you can also save, open, and edit area from which your media items go live, and you can also save, open, and edit
services files. services files.
.. image:: pics/servicemanager.png .. image:: pics/servicemanager.png

View File

@ -32,7 +32,7 @@ import logging
from PyQt4 import QtWebKit from PyQt4 import QtWebKit
from openlp.core.lib import expand_tags, build_lyrics_format_css, \ from openlp.core.lib import expand_tags, build_lyrics_format_css, \
build_lyrics_outline_css build_lyrics_outline_css, Receiver
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -92,13 +92,20 @@ class Renderer(object):
(build_lyrics_format_css(self._theme, self.page_width, (build_lyrics_format_css(self._theme, self.page_width,
self.page_height), build_lyrics_outline_css(self._theme)) self.page_height), build_lyrics_outline_css(self._theme))
def format_slide(self, words, line_break): def format_slide(self, words, line_break, force_page=False):
""" """
Figure out how much text can appear on a slide, using the current Figure out how much text can appear on a slide, using the current
theme settings. theme settings.
``words`` ``words``
The words to be fitted on the slide. The words to be fitted on the slide.
``line_break``
Add line endings after each line of text used for bibles.
``force_page``
Flag to tell message lines in page.
""" """
log.debug(u'format_slide - Start') log.debug(u'format_slide - Start')
line_end = u'' line_end = u''
@ -114,13 +121,19 @@ class Renderer(object):
formatted = [] formatted = []
html_text = u'' html_text = u''
styled_text = u'' styled_text = u''
line_count = 0
for line in text: for line in text:
if line_count != -1:
line_count += 1
styled_line = expand_tags(line) + line_end styled_line = expand_tags(line) + line_end
styled_text += styled_line styled_text += styled_line
html = self.page_shell + styled_text + u'</div></body></html>' html = self.page_shell + styled_text + u'</div></body></html>'
self.web.setHtml(html) self.web.setHtml(html)
# Text too long so go to next page # Text too long so go to next page
if self.web_frame.contentsSize().height() > self.page_height: if self.web_frame.contentsSize().height() > self.page_height:
if force_page and line_count > 0:
Receiver.send_message(u'theme_line_count', line_count)
line_count = -1
if html_text.endswith(u'<br>'): if html_text.endswith(u'<br>'):
html_text = html_text[:len(html_text)-4] html_text = html_text[:len(html_text)-4]
formatted.append(html_text) formatted.append(html_text)

View File

@ -67,8 +67,9 @@ class RenderManager(object):
self.service_theme = u'' self.service_theme = u''
self.theme_level = u'' self.theme_level = u''
self.override_background = None self.override_background = None
self.themedata = None self.theme_data = None
self.alertTab = None self.alertTab = None
self.force_page = False
def update_display(self): def update_display(self):
""" """
@ -80,7 +81,7 @@ class RenderManager(object):
self.display.imageManager = self.image_manager self.display.imageManager = self.image_manager
self.display.setup() self.display.setup()
self.renderer.bg_frame = None self.renderer.bg_frame = None
self.themedata = None self.theme_data = None
self.image_manager.update_display(self.width, self.height) self.image_manager.update_display(self.width, self.height)
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global): def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
@ -99,7 +100,7 @@ class RenderManager(object):
self.theme_level = theme_level self.theme_level = theme_level
self.global_theme_data = \ self.global_theme_data = \
self.theme_manager.getThemeData(self.global_theme) self.theme_manager.getThemeData(self.global_theme)
self.themedata = None self.theme_data = None
def set_service_theme(self, service_theme): def set_service_theme(self, service_theme):
""" """
@ -109,7 +110,7 @@ class RenderManager(object):
The service-level theme to be set. The service-level theme to be set.
""" """
self.service_theme = service_theme self.service_theme = service_theme
self.themedata = None self.theme_data = None
def set_override_theme(self, theme, overrideLevels=False): def set_override_theme(self, theme, overrideLevels=False):
""" """
@ -146,19 +147,19 @@ class RenderManager(object):
self.theme = self.service_theme self.theme = self.service_theme
else: else:
self.theme = self.global_theme self.theme = self.global_theme
if self.theme != self.renderer.theme_name or self.themedata is None \ if self.theme != self.renderer.theme_name or self.theme_data is None \
or overrideLevels: or overrideLevels:
log.debug(u'theme is now %s', self.theme) log.debug(u'theme is now %s', self.theme)
# Force the theme to be the one passed in. # Force the theme to be the one passed in.
if overrideLevels: if overrideLevels:
self.themedata = theme self.theme_data = theme
else: else:
self.themedata = self.theme_manager.getThemeData(self.theme) self.theme_data = self.theme_manager.getThemeData(self.theme)
self.calculate_default(self.screens.current[u'size']) self.calculate_default(self.screens.current[u'size'])
self.renderer.set_theme(self.themedata) self.renderer.set_theme(self.theme_data)
self.build_text_rectangle(self.themedata) self.build_text_rectangle(self.theme_data)
self.image_manager.add_image(self.themedata.theme_name, self.image_manager.add_image(self.theme_data.theme_name,
self.themedata.background_filename) self.theme_data.background_filename)
return self.renderer._rect, self.renderer._rect_footer return self.renderer._rect, self.renderer._rect_footer
def build_text_rectangle(self, theme): def build_text_rectangle(self, theme):
@ -187,14 +188,19 @@ class RenderManager(object):
theme.font_footer_height - 1) theme.font_footer_height - 1)
self.renderer.set_text_rectangle(main_rect, footer_rect) self.renderer.set_text_rectangle(main_rect, footer_rect)
def generate_preview(self, themedata): def generate_preview(self, theme_data, force_page=False):
""" """
Generate a preview of a theme. Generate a preview of a theme.
``themedata`` ``theme_data``
The theme to generated a preview for. The theme to generated a preview for.
``force_page``
Flag to tell message lines per page need to be generated.
""" """
log.debug(u'generate preview') log.debug(u'generate preview')
# save value for use in format_slide
self.force_page = force_page
# set the default image size for previews # set the default image size for previews
self.calculate_default(self.screens.preview[u'size']) self.calculate_default(self.screens.preview[u'size'])
verse = u'The Lord said to {r}Noah{/r}: \n' \ verse = u'The Lord said to {r}Noah{/r}: \n' \
@ -204,23 +210,27 @@ class RenderManager(object):
'Get those children out of the muddy, muddy \n' \ 'Get those children out of the muddy, muddy \n' \
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \ '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n' 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
# make big page for theme edit dialog to get line count
if self.force_page:
verse = verse + verse + verse
footer = [] footer = []
footer.append(u'Arky Arky (Unknown)' ) footer.append(u'Arky Arky (Unknown)' )
footer.append(u'Public Domain') footer.append(u'Public Domain')
footer.append(u'CCLI 123456') footer.append(u'CCLI 123456')
# build a service item to generate preview # build a service item to generate preview
serviceItem = ServiceItem() serviceItem = ServiceItem()
serviceItem.theme = themedata serviceItem.theme = theme_data
serviceItem.add_from_text(u'', verse, footer) serviceItem.add_from_text(u'', verse, footer)
serviceItem.render_manager = self serviceItem.render_manager = self
serviceItem.raw_footer = footer serviceItem.raw_footer = footer
serviceItem.render(True) serviceItem.render(True)
self.display.buildHtml(serviceItem) if not self.force_page:
raw_html = serviceItem.get_rendered_frame(0) self.display.buildHtml(serviceItem)
preview = self.display.text(raw_html) raw_html = serviceItem.get_rendered_frame(0)
# Reset the real screen size for subsequent render requests preview = self.display.text(raw_html)
self.calculate_default(self.screens.current[u'size']) # Reset the real screen size for subsequent render requests
return preview self.calculate_default(self.screens.current[u'size'])
return preview
def format_slide(self, words, line_break): def format_slide(self, words, line_break):
""" """
@ -228,9 +238,12 @@ class RenderManager(object):
``words`` ``words``
The words to go on the slides. The words to go on the slides.
``line_break``
Add line endings after each line of text used for bibles.
""" """
log.debug(u'format slide') log.debug(u'format slide')
return self.renderer.format_slide(words, line_break) return self.renderer.format_slide(words, line_break, self.force_page)
def calculate_default(self, screen): def calculate_default(self, screen):
""" """

View File

@ -29,7 +29,8 @@ import os
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate, BackgroundType, BackgroundGradientType from openlp.core.lib import translate, BackgroundType, BackgroundGradientType, \
Receiver
from openlp.core.utils import get_images_filter from openlp.core.utils import get_images_filter
from themewizard import Ui_ThemeWizard from themewizard import Ui_ThemeWizard
@ -96,10 +97,40 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
QtCore.QObject.connect(self, QtCore.QObject.connect(self,
QtCore.SIGNAL(u'currentIdChanged(int)'), QtCore.SIGNAL(u'currentIdChanged(int)'),
self.pageChanged) self.pageChanged)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'theme_line_count'),
self.updateLinesText)
QtCore.QObject.connect(self.mainSizeSpinBox,
QtCore.SIGNAL(u'valueChanged(int)'),
self.calculateLines)
QtCore.QObject.connect(self.mainSizeSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.calculateLines)
QtCore.QObject.connect(self.lineSpacingSpinBox,
QtCore.SIGNAL(u'valueChanged(int)'),
self.calculateLines)
QtCore.QObject.connect(self.lineSpacingSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.calculateLines)
QtCore.QObject.connect(self.outlineSizeSpinBox,
QtCore.SIGNAL(u'valueChanged(int)'),
self.calculateLines)
QtCore.QObject.connect(self.outlineSizeSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.calculateLines)
QtCore.QObject.connect(self.shadowSizeSpinBox,
QtCore.SIGNAL(u'valueChanged(int)'),
self.calculateLines)
QtCore.QObject.connect(self.shadowSizeSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.calculateLines)
QtCore.QObject.connect(self.mainFontComboBox,
QtCore.SIGNAL(u'activated(int)'),
self.calculateLines)
def pageChanged(self, pageId): def pageChanged(self, pageId):
""" """
Detects Page changes and updates. Detects Page changes and updates as approprate.
""" """
if pageId == 6: if pageId == 6:
self.updateTheme() self.updateTheme()
@ -184,6 +215,22 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.backgroundPage.registerField( self.backgroundPage.registerField(
u'name', self.themeNameEdit) u'name', self.themeNameEdit)
def calculateLines(self):
"""
Calculate the number of lines on a page by rendering text
"""
# Do not trigger on start up
if self.page != 0:
self.updateTheme()
frame = self.thememanager.generateImage(self.theme, True)
def updateLinesText(self, lines):
"""
Updates the lines on a page on the wizard
"""
self.mainLineCountLabel.setText(unicode(translate('OpenLP.ThemeForm', \
'(%d lines per slide)' % int(lines))))
def onOutlineCheckCheckBoxChanged(self, state): def onOutlineCheckCheckBoxChanged(self, state):
""" """
Change state as Outline check box changed Change state as Outline check box changed
@ -194,6 +241,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.theme.font_main_outline = False self.theme.font_main_outline = False
self.outlineColorPushButton.setEnabled(self.theme.font_main_outline) self.outlineColorPushButton.setEnabled(self.theme.font_main_outline)
self.outlineSizeSpinBox.setEnabled(self.theme.font_main_outline) self.outlineSizeSpinBox.setEnabled(self.theme.font_main_outline)
self.calculateLines()
def onShadowCheckCheckBoxChanged(self, state): def onShadowCheckCheckBoxChanged(self, state):
""" """
@ -205,6 +253,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.theme.font_main_shadow = False self.theme.font_main_shadow = False
self.shadowColorPushButton.setEnabled(self.theme.font_main_shadow) self.shadowColorPushButton.setEnabled(self.theme.font_main_shadow)
self.shadowSizeSpinBox.setEnabled(self.theme.font_main_shadow) self.shadowSizeSpinBox.setEnabled(self.theme.font_main_shadow)
self.calculateLines()
def onMainDefaultPositionCheckBox(self, value): def onMainDefaultPositionCheckBox(self, value):
""" """
@ -244,6 +293,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
Set up the pages for Initial run through dialog Set up the pages for Initial run through dialog
""" """
log.debug(u'initializePage %s' % id) log.debug(u'initializePage %s' % id)
self.page = id
if id == 1: if id == 1:
self.setBackgroundTabValues() self.setBackgroundTabValues()
elif id == 2: elif id == 2:
@ -578,15 +628,15 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
(QtGui.QMessageBox.Ok), (QtGui.QMessageBox.Ok),
QtGui.QMessageBox.Ok) QtGui.QMessageBox.Ok)
return return
save_from = None saveFrom = None
save_to = None saveTo = None
if self.theme.background_type == \ if self.theme.background_type == \
BackgroundType.to_string(BackgroundType.Image): BackgroundType.to_string(BackgroundType.Image):
filename = \ filename = \
os.path.split(unicode(self.theme.background_filename))[1] os.path.split(unicode(self.theme.background_filename))[1]
save_to = os.path.join(self.path, self.theme.theme_name, filename) saveTo = os.path.join(self.path, self.theme.theme_name, filename)
save_from = self.theme.background_filename saveFrom = self.theme.background_filename
if self.thememanager.saveTheme(self.theme, save_from, save_to): if self.thememanager.saveTheme(self.theme, saveFrom, saveTo):
return QtGui.QDialog.accept(self) return QtGui.QDialog.accept(self)
def _colorButton(self, field): def _colorButton(self, field):

View File

@ -181,7 +181,7 @@ class ThemeManager(QtGui.QWidget):
'%s (default)')) % newName '%s (default)')) % newName
self.themeListWidget.item(count).setText(name) self.themeListWidget.item(count).setText(name)
def changeGlobalFromScreen(self, index = -1): def changeGlobalFromScreen(self, index=-1):
""" """
Change the global theme when a theme is double clicked upon in the Change the global theme when a theme is double clicked upon in the
Theme Manager list Theme Manager list
@ -252,17 +252,14 @@ class ThemeManager(QtGui.QWidget):
Takes a theme and makes a new copy of it as well as saving it. Takes a theme and makes a new copy of it as well as saving it.
""" """
log.debug(u'cloneThemeData') log.debug(u'cloneThemeData')
themeData.new_document(newThemeName) saveTo = None
themeData.build_xml_from_attrs() saveFrom = None
save_to = None
save_from = None
if themeData.background_type == u'image': if themeData.background_type == u'image':
save_to = os.path.join(self.path, newThemeName, saveTo = os.path.join(self.path, newThemeName,
os.path.split(unicode(themeData.background_filename))[1]) os.path.split(unicode(themeData.background_filename))[1])
save_from = themeData.background_filename saveFrom = themeData.background_filename
theme = themeData.extract_xml() themeData.theme_name = newThemeName
pretty_theme = themeData.extract_formatted_xml() self.saveTheme(themeData, saveFrom, saveTo)
self.saveTheme(newThemeName, theme, pretty_theme, save_from, save_to)
def onEditTheme(self): def onEditTheme(self):
""" """
@ -462,17 +459,17 @@ class ThemeManager(QtGui.QWidget):
""" """
return self.themelist return self.themelist
def getThemeData(self, themename): def getThemeData(self, themeName):
""" """
Returns a theme object from an XML file Returns a theme object from an XML file
``themename`` ``themeName``
Name of the theme to load from file Name of the theme to load from file
""" """
log.debug(u'getthemedata for theme %s', themename) log.debug(u'getthemedata for theme %s', themeName)
xml_file = os.path.join(self.path, unicode(themename), xmlFile = os.path.join(self.path, unicode(themeName),
unicode(themename) + u'.xml') unicode(themeName) + u'.xml')
xml = get_text_file_string(xml_file) xml = get_text_file_string(xmlFile)
if not xml: if not xml:
return self.baseTheme() return self.baseTheme()
else: else:
@ -640,7 +637,7 @@ class ThemeManager(QtGui.QWidget):
newtheme.display_vertical_align = vAlignCorrection newtheme.display_vertical_align = vAlignCorrection
return newtheme.extract_xml() return newtheme.extract_xml()
def saveTheme(self, theme, image_from, image_to): def saveTheme(self, theme, imageFrom, imageTo):
""" """
Called by thememaintenance Dialog to save the theme Called by thememaintenance Dialog to save the theme
and to trigger the reload of the theme list and to trigger the reload of the theme list
@ -673,8 +670,8 @@ class ThemeManager(QtGui.QWidget):
self.deleteTheme(self.saveThemeName) self.deleteTheme(self.saveThemeName)
if result == QtGui.QMessageBox.Yes: if result == QtGui.QMessageBox.Yes:
# Save the theme, overwriting the existing theme if necessary. # Save the theme, overwriting the existing theme if necessary.
if image_to and self.oldBackgroundImage and \ if imageTo and self.oldBackgroundImage and \
image_to != self.oldBackgroundImage: imageTo != self.oldBackgroundImage:
try: try:
os.remove(self.oldBackgroundImage) os.remove(self.oldBackgroundImage)
except OSError: except OSError:
@ -688,12 +685,12 @@ class ThemeManager(QtGui.QWidget):
finally: finally:
if outfile: if outfile:
outfile.close() outfile.close()
if image_from and image_from != image_to: if imageFrom and imageFrom != imageTo:
try: try:
encoding = get_filesystem_encoding() encoding = get_filesystem_encoding()
shutil.copyfile( shutil.copyfile(
unicode(image_from).encode(encoding), unicode(imageFrom).encode(encoding),
unicode(image_to).encode(encoding)) unicode(imageTo).encode(encoding))
except IOError: except IOError:
log.exception(u'Failed to save theme image') log.exception(u'Failed to save theme image')
self.generateAndSaveImage(self.path, name, theme) self.generateAndSaveImage(self.path, name, theme)
@ -729,7 +726,6 @@ class ThemeManager(QtGui.QWidget):
def generateAndSaveImage(self, dir, name, theme): def generateAndSaveImage(self, dir, name, theme):
log.debug(u'generateAndSaveImage %s %s', dir, name) log.debug(u'generateAndSaveImage %s %s', dir, name)
#theme = self.createThemeFromXml(theme_xml, dir)
theme_xml = theme.extract_xml() theme_xml = theme.extract_xml()
frame = self.generateImage(theme) frame = self.generateImage(theme)
samplepathname = os.path.join(self.path, name + u'.png') samplepathname = os.path.join(self.path, name + u'.png')
@ -742,12 +738,18 @@ class ThemeManager(QtGui.QWidget):
pixmap.save(thumb, u'png') pixmap.save(thumb, u'png')
log.debug(u'Theme image written to %s', samplepathname) log.debug(u'Theme image written to %s', samplepathname)
def generateImage(self, themedata): def generateImage(self, themeData, forcePage=False):
""" """
Call the RenderManager to build a Sample Image Call the RenderManager to build a Sample Image
``themeData``
The theme to generated a preview for.
``forcePage``
Flag to tell message lines per page need to be generated.
""" """
log.debug(u'generateImage \n%s ', themedata) log.debug(u'generateImage \n%s ', themeData)
return self.parent.RenderManager.generate_preview(themedata) return self.parent.RenderManager.generate_preview(themeData, forcePage)
def getPreviewImage(self, theme): def getPreviewImage(self, theme):
""" """
@ -768,14 +770,14 @@ class ThemeManager(QtGui.QWidget):
newtheme = ThemeXML() newtheme = ThemeXML()
return newtheme return newtheme
def createThemeFromXml(self, theme_xml, path): def createThemeFromXml(self, themeXml, path):
""" """
Return a theme object using information parsed from XML Return a theme object using information parsed from XML
``theme_xml`` ``themeXml``
The XML data to load into the theme The XML data to load into the theme
""" """
theme = ThemeXML() theme = ThemeXML()
theme.parse(theme_xml) theme.parse(themeXml)
theme.extend_image_filename(path) theme.extend_image_filename(path)
return theme return theme

View File

@ -213,7 +213,7 @@ class BGExtract(object):
finally: finally:
if not page: if not page:
return None return None
cleaner = [(re.compile('&nbsp;|<br />'), lambda match: '')] cleaner = [(re.compile('&nbsp;|<br />|\'\+\''), lambda match: '')]
soup = None soup = None
try: try:
soup = BeautifulSoup(page, markupMassage=cleaner) soup = BeautifulSoup(page, markupMassage=cleaner)

View File

@ -415,17 +415,12 @@ class SongMediaItem(MediaManagerItem):
if search_results: if search_results:
for song in search_results: for song in search_results:
count = 0 count = 0
# temp debug to find why service items do not edit
log.debug(u'author list %s' % author_list)
for author in song.authors: for author in song.authors:
log.debug(u'author %s' % author.display_name)
if author.display_name in author_list: if author.display_name in author_list:
count += 1 count += 1
log.debug(u'found %s : %s' % (count, len(author_list)))
if count == len(author_list): if count == len(author_list):
editId = song.id editId = song.id
uuid = item._uuid uuid = item._uuid
if editId != 0: if editId != 0:
Receiver.send_message(u'service_item_update', Receiver.send_message(u'service_item_update',
u'%s:%s' %(editId, uuid)) u'%s:%s' %(editId, uuid))