add configurethemes.png

This commit is contained in:
Wesley Stout 2011-04-30 06:35:06 -05:00
commit 7857b60c66
30 changed files with 666 additions and 440 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

@ -46,7 +46,7 @@ from openlp.core.ui.firsttimeform import FirstTimeForm
from openlp.core.ui.exceptionform import ExceptionForm
from openlp.core.ui import SplashScreen, ScreenList
from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \
get_application_version
get_application_version, DelayStartThread
log = logging.getLogger()
@ -130,6 +130,7 @@ class OpenLP(QtGui.QApplication):
u'general/update check', QtCore.QVariant(True)).toBool()
if update_check:
VersionThread(self.mainWindow).start()
DelayStartThread(self.mainWindow).start()
return self.exec_()
def isAlreadyRunning(self):
@ -250,4 +251,4 @@ if __name__ == u'__main__':
"""
Instantiate and run the application.
"""
main()
main()

View File

@ -289,6 +289,5 @@ from htmlbuilder import build_html, build_lyrics_format_css, \
from toolbar import OpenLPToolbar
from dockwidget import OpenLPDockWidget
from renderer import Renderer
from rendermanager import RenderManager
from mediamanageritem import MediaManagerItem
from openlp.core.utils.actions import ActionList

View File

@ -554,4 +554,4 @@ class MediaManagerItem(QtGui.QWidget):
item_id = remoteItem
else:
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
return item_id
return item_id

View File

@ -161,7 +161,7 @@ class Plugin(QtCore.QObject):
self.log = logging.getLogger(self.name)
self.previewController = plugin_helpers[u'preview']
self.liveController = plugin_helpers[u'live']
self.renderManager = plugin_helpers[u'render']
self.renderer = plugin_helpers[u'renderer']
self.serviceManager = plugin_helpers[u'service']
self.settingsForm = plugin_helpers[u'settings form']
self.mediadock = plugin_helpers[u'toolbox']
@ -359,4 +359,4 @@ class Plugin(QtCore.QObject):
use of the singular name of the plugin object so must only be called
after this has been set.
"""
self.textStrings[name] = {u'title': title, u'tooltip': tooltip}
self.textStrings[name] = {u'title': title, u'tooltip': tooltip}

View File

@ -23,46 +23,260 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`renderer` module enables OpenLP to take the input from plugins and
format it for the output display.
"""
import logging
from PyQt4 import QtWebKit
from PyQt4 import QtCore, QtWebKit
from openlp.core.lib import expand_tags, build_lyrics_format_css, \
build_lyrics_outline_css, Receiver
from openlp.core.lib import ServiceItem, ImageManager, expand_tags, \
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
ItemCapabilities
from openlp.core.lib.theme import ThemeLevel
from openlp.core.ui import MainDisplay
log = logging.getLogger(__name__)
VERSE = u'The Lord said to {r}Noah{/r}: \n' \
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
'The Lord said to {g}Noah{/g}:\n' \
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\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{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
HTML_END = u'</div></body></html>'
class Renderer(object):
"""
Genarates a pixmap image of a array of text. The Text is formatted to
make sure it fits on the screen and if not extra frames are generated.
Class to pull all Renderer interactions into one place. The plugins will
call helper methods to do the rendering but this class will provide
display defense code.
``theme_manager``
The ThemeManager instance, used to get the current theme details.
``screens``
Contains information about the Screens.
``screen_number``
Defaults to *0*. The index of the output/display screen.
"""
log.info(u'Renderer Loaded')
def __init__(self):
def __init__(self, theme_manager, screens):
"""
Initialise the renderer.
Initialise the render manager.
"""
self._rect = None
self.theme_name = None
self._theme = None
log.debug(u'Initilisation started')
self.screens = screens
self.image_manager = ImageManager()
self.display = MainDisplay(self, screens, False)
self.display.imageManager = self.image_manager
self.theme_manager = theme_manager
self.service_theme = u''
self.theme_level = u''
self.override_background = None
self.theme_data = None
self.force_page = False
def set_theme(self, theme):
def update_display(self):
"""
Set the theme to be used.
Updates the render manager's information about the current screen.
"""
log.debug(u'Update Display')
self._calculate_default(self.screens.current[u'size'])
self.display = MainDisplay(self, self.screens, False)
self.display.imageManager = self.image_manager
self.display.setup()
self.bg_frame = None
self.theme_data = None
self.image_manager.update_display(self.width, self.height)
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
"""
Set the global-level theme and the theme level.
``global_theme``
The global-level theme to be set.
``theme_level``
Defaults to *``ThemeLevel.Global``*. The theme level, can be
``ThemeLevel.Global``, ``ThemeLevel.Service`` or
``ThemeLevel.Song``.
"""
self.global_theme = global_theme
self.theme_level = theme_level
self.global_theme_data = \
self.theme_manager.getThemeData(self.global_theme)
self.theme_data = None
def set_service_theme(self, service_theme):
"""
Set the service-level theme.
``service_theme``
The service-level theme to be set.
"""
self.service_theme = service_theme
self.theme_data = None
def set_override_theme(self, override_theme, override_levels=False):
"""
Set the appropriate theme depending on the theme level.
Called by the service item when building a display frame
``theme``
The theme to be used.
"""
log.debug(u'set theme')
self._theme = theme
self.theme_name = theme.theme_name
The name of the song-level theme. None means the service
item wants to use the given value.
def set_text_rectangle(self, rect_main, rect_footer):
``override_levels``
Used to force the theme data passed in to be used.
"""
log.debug(u'set override theme to %s', override_theme)
theme_level = self.theme_level
if override_levels:
theme_level = ThemeLevel.Song
if theme_level == ThemeLevel.Global:
theme = self.global_theme
elif theme_level == ThemeLevel.Service:
if self.service_theme == u'':
theme = self.global_theme
else:
theme = self.service_theme
else:
# Images have a theme of -1
if override_theme and override_theme != -1:
theme = override_theme
elif theme_level == ThemeLevel.Song or \
theme_level == ThemeLevel.Service:
if self.service_theme == u'':
theme = self.global_theme
else:
theme = self.service_theme
else:
theme = self.global_theme
log.debug(u'theme is now %s', theme)
# Force the theme to be the one passed in.
if override_levels:
self.theme_data = override_theme
else:
self.theme_data = self.theme_manager.getThemeData(theme)
self._calculate_default(self.screens.current[u'size'])
self._build_text_rectangle(self.theme_data)
self.image_manager.add_image(self.theme_data.theme_name,
self.theme_data.background_filename)
return self._rect, self._rect_footer
def generate_preview(self, theme_data, force_page=False):
"""
Generate a preview of a theme.
``theme_data``
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')
# save value for use in format_slide
self.force_page = force_page
# set the default image size for previews
self._calculate_default(self.screens.preview[u'size'])
# build a service item to generate preview
serviceItem = ServiceItem()
serviceItem.theme = theme_data
if self.force_page:
# make big page for theme edit dialog to get line count
serviceItem.add_from_text(u'', VERSE + VERSE + VERSE, FOOTER)
else:
self.image_manager.del_image(theme_data.theme_name)
serviceItem.add_from_text(u'', VERSE, FOOTER)
serviceItem.renderer = self
serviceItem.raw_footer = FOOTER
serviceItem.render(True)
if not self.force_page:
self.display.buildHtml(serviceItem)
raw_html = serviceItem.get_rendered_frame(0)
preview = self.display.text(raw_html)
# Reset the real screen size for subsequent render requests
self._calculate_default(self.screens.current[u'size'])
return preview
def format_slide(self, text, line_break, item):
"""
Calculate how much text can fit on a slide.
``text``
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')
# clean up line endings
lines = self._lines_split(text)
pages = self._paginate_slide(lines, line_break, self.force_page)
if len(pages) > 1:
# Songs and Custom
if item.is_capable(ItemCapabilities.AllowsVirtualSplit):
# Do not forget the line breaks !
slides = text.split(u'[---]')
pages = []
for slide in slides:
lines = slide.strip(u'\n').split(u'\n')
new_pages = self._paginate_slide(lines, line_break,
self.force_page)
pages.extend(new_pages)
# Bibles
elif item.is_capable(ItemCapabilities.AllowsWordSplit):
pages = self._paginate_slide_words(text, line_break)
return pages
def _calculate_default(self, screen):
"""
Calculate the default dimentions of the screen.
``screen``
The QSize of the screen.
"""
log.debug(u'calculate default %s', screen)
self.width = screen.width()
self.height = screen.height()
self.screen_ratio = float(self.height) / float(self.width)
log.debug(u'calculate default %d, %d, %f',
self.width, self.height, self.screen_ratio)
# 90% is start of footer
self.footer_start = int(self.height * 0.90)
def _build_text_rectangle(self, theme):
"""
Builds a text block using the settings in ``theme``
and the size of the display screen.height.
Note the system has a 10 pixel border round the screen
``theme``
The theme to build a text block for.
"""
log.debug(u'_build_text_rectangle')
main_rect = None
footer_rect = None
if not theme.font_main_override:
main_rect = QtCore.QRect(10, 0, self.width - 20, self.footer_start)
else:
main_rect = QtCore.QRect(theme.font_main_x, theme.font_main_y,
theme.font_main_width - 1, theme.font_main_height - 1)
if not theme.font_footer_override:
footer_rect = QtCore.QRect(10, self.footer_start, self.width - 20,
self.height - self.footer_start)
else:
footer_rect = QtCore.QRect(theme.font_footer_x,
theme.font_footer_y, theme.font_footer_width - 1,
theme.font_footer_height - 1)
self._set_text_rectangle(main_rect, footer_rect)
def _set_text_rectangle(self, rect_main, rect_footer):
"""
Sets the rectangle within which text should be rendered.
@ -77,9 +291,9 @@ class Renderer(object):
self._rect_footer = rect_footer
self.page_width = self._rect.width()
self.page_height = self._rect.height()
if self._theme.font_main_shadow:
self.page_width -= int(self._theme.font_main_shadow_size)
self.page_height -= int(self._theme.font_main_shadow_size)
if self.theme_data.font_main_shadow:
self.page_width -= int(self.theme_data.font_main_shadow_size)
self.page_height -= int(self.theme_data.font_main_shadow_size)
self.web = QtWebKit.QWebView()
self.web.setVisible(False)
self.web.resize(self.page_width, self.page_height)
@ -89,59 +303,146 @@ class Renderer(object):
u'*{margin: 0; padding: 0; border: 0;} '\
u'#main {position:absolute; top:0px; %s %s}</style><body>' \
u'<div id="main">' % \
(build_lyrics_format_css(self._theme, self.page_width,
self.page_height), build_lyrics_outline_css(self._theme))
(build_lyrics_format_css(self.theme_data, self.page_width,
self.page_height), build_lyrics_outline_css(self.theme_data))
def format_slide(self, words, line_break, force_page=False):
def _paginate_slide(self, lines, line_break, force_page=False):
"""
Figure out how much text can appear on a slide, using the current
theme settings.
``words``
The words to be fitted on the slide.
``lines``
The words to be fitted on the slide split into lines.
``line_break``
Add line endings after each line of text used for bibles.
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'_paginate_slide - Start')
line_end = u''
if line_break:
line_end = u'<br>'
words = words.replace(u'\r\n', u'\n')
verses_text = words.split(u'\n')
text = []
for verse in verses_text:
lines = verse.split(u'\n')
for line in lines:
text.append(line)
formatted = []
html_text = u''
styled_text = u''
line_count = 0
for line in text:
for line in lines:
if line_count != -1:
line_count += 1
styled_line = expand_tags(line) + line_end
styled_text += styled_line
html = self.page_shell + styled_text + u'</div></body></html>'
html = self.page_shell + styled_text + HTML_END
self.web.setHtml(html)
# Text too long so go to next page
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>'):
html_text = html_text[:len(html_text)-4]
while html_text.endswith(u'<br>'):
html_text = html_text[:-4]
formatted.append(html_text)
html_text = u''
styled_text = styled_line
html_text += line + line_end
if html_text.endswith(u'<br>'):
html_text = html_text[:len(html_text)-4]
while html_text.endswith(u'<br>'):
html_text = html_text[:-4]
formatted.append(html_text)
log.debug(u'format_slide - End')
log.debug(u'_paginate_slide - End')
return formatted
def _paginate_slide_words(self, text, line_break):
"""
Figure out how much text can appear on a slide, using the current
theme settings. This version is to handle text which needs to be split
into words to get it to fit.
``text``
The words to be fitted on the slide split into lines.
``line_break``
Add line endings after each line of text used for bibles.
"""
log.debug(u'_paginate_slide_words - Start')
line_end = u''
if line_break:
line_end = u'<br>'
formatted = []
previous_html = u''
previous_raw = u''
lines = text.split(u'\n')
for line in lines:
styled_line = expand_tags(line)
html = self.page_shell + previous_html + styled_line + HTML_END
self.web.setHtml(html)
# Text too long so go to next page
if self.web_frame.contentsSize().height() > self.page_height:
# Check if there was a verse before the current one and append
# it, when it fits on the page.
if previous_html:
html = self.page_shell + previous_html + HTML_END
self.web.setHtml(html)
if self.web_frame.contentsSize().height() <= \
self.page_height:
while previous_raw.endswith(u'<br>'):
previous_raw = previous_raw[:-4]
formatted.append(previous_raw)
previous_html = u''
previous_raw = u''
html = self.page_shell + styled_line + HTML_END
self.web.setHtml(html)
# Now check if the current verse will fit, if it does
# not we have to start to process the verse word by
# word.
if self.web_frame.contentsSize().height() <= \
self.page_height:
previous_html = styled_line + line_end
previous_raw = line + line_end
continue
words = self._words_split(line)
for word in words:
styled_word = expand_tags(word)
html = self.page_shell + previous_html + styled_word + \
HTML_END
self.web.setHtml(html)
# Text too long so go to next page
if self.web_frame.contentsSize().height() > \
self.page_height:
while previous_raw.endswith(u'<br>'):
previous_raw = previous_raw[:-4]
formatted.append(previous_raw)
previous_html = u''
previous_raw = u''
previous_html += styled_word
previous_raw += word
previous_html += line_end
previous_raw += line_end
else:
previous_html += styled_line + line_end
previous_raw += line + line_end
while previous_raw.endswith(u'<br>'):
previous_raw = previous_raw[:-4]
formatted.append(previous_raw)
log.debug(u'_paginate_slide_words - End')
return formatted
def _words_split(self, line):
"""
Split the slide up by word so can wrap better
"""
# this parse we are to be wordy
line = line.replace(u'\n', u' ')
words = line.split(u' ')
return [word + u' ' for word in words]
def _lines_split(self, text):
"""
Split the slide up by physical line
"""
# this parse we do not want to use this so remove it
text = text.replace(u'\n[---]', u'')
lines = text.split(u'\n')
return [line.replace(u'[---]', u'') for line in lines]

View File

@ -1,261 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# 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 Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
from PyQt4 import QtCore
from openlp.core.lib import Renderer, ServiceItem, ImageManager
from openlp.core.lib.theme import ThemeLevel
from openlp.core.ui import MainDisplay
log = logging.getLogger(__name__)
VERSE = u'The Lord said to {r}Noah{/r}: \n' \
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
'The Lord said to {g}Noah{/g}:\n' \
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\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{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
class RenderManager(object):
"""
Class to pull all Renderer interactions into one place. The plugins will
call helper methods to do the rendering but this class will provide
display defense code.
``theme_manager``
The ThemeManager instance, used to get the current theme details.
``screens``
Contains information about the Screens.
``screen_number``
Defaults to *0*. The index of the output/display screen.
"""
log.info(u'RenderManager Loaded')
def __init__(self, theme_manager, screens):
"""
Initialise the render manager.
"""
log.debug(u'Initilisation started')
self.screens = screens
self.image_manager = ImageManager()
self.display = MainDisplay(self, screens, False)
self.display.imageManager = self.image_manager
self.theme_manager = theme_manager
self.renderer = Renderer()
self.calculate_default(self.screens.current[u'size'])
self.theme = u''
self.service_theme = u''
self.theme_level = u''
self.override_background = None
self.theme_data = None
self.force_page = False
def update_display(self):
"""
Updates the render manager's information about the current screen.
"""
log.debug(u'Update Display')
self.calculate_default(self.screens.current[u'size'])
self.display = MainDisplay(self, self.screens, False)
self.display.imageManager = self.image_manager
self.display.setup()
self.renderer.bg_frame = None
self.theme_data = None
self.image_manager.update_display(self.width, self.height)
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
"""
Set the global-level theme and the theme level.
``global_theme``
The global-level theme to be set.
``theme_level``
Defaults to *``ThemeLevel.Global``*. The theme level, can be
``ThemeLevel.Global``, ``ThemeLevel.Service`` or
``ThemeLevel.Song``.
"""
self.global_theme = global_theme
self.theme_level = theme_level
self.global_theme_data = \
self.theme_manager.getThemeData(self.global_theme)
self.theme_data = None
def set_service_theme(self, service_theme):
"""
Set the service-level theme.
``service_theme``
The service-level theme to be set.
"""
self.service_theme = service_theme
self.theme_data = None
def set_override_theme(self, theme, overrideLevels=False):
"""
Set the appropriate theme depending on the theme level.
Called by the service item when building a display frame
``theme``
The name of the song-level theme. None means the service
item wants to use the given value.
``overrideLevels``
Used to force the theme data passed in to be used.
"""
log.debug(u'set override theme to %s', theme)
theme_level = self.theme_level
if overrideLevels:
theme_level = ThemeLevel.Song
if theme_level == ThemeLevel.Global:
self.theme = self.global_theme
elif theme_level == ThemeLevel.Service:
if self.service_theme == u'':
self.theme = self.global_theme
else:
self.theme = self.service_theme
else:
# Images have a theme of -1
if theme and theme != -1:
self.theme = theme
elif theme_level == ThemeLevel.Song or \
theme_level == ThemeLevel.Service:
if self.service_theme == u'':
self.theme = self.global_theme
else:
self.theme = self.service_theme
else:
self.theme = self.global_theme
if self.theme != self.renderer.theme_name or self.theme_data is None \
or overrideLevels:
log.debug(u'theme is now %s', self.theme)
# Force the theme to be the one passed in.
if overrideLevels:
self.theme_data = theme
else:
self.theme_data = self.theme_manager.getThemeData(self.theme)
self.calculate_default(self.screens.current[u'size'])
self.renderer.set_theme(self.theme_data)
self.build_text_rectangle(self.theme_data)
self.image_manager.add_image(self.theme_data.theme_name,
self.theme_data.background_filename)
return self.renderer._rect, self.renderer._rect_footer
def build_text_rectangle(self, theme):
"""
Builds a text block using the settings in ``theme``
and the size of the display screen.height.
``theme``
The theme to build a text block for.
"""
log.debug(u'build_text_rectangle')
main_rect = None
footer_rect = None
if not theme.font_main_override:
main_rect = QtCore.QRect(10, 0, self.width - 20, self.footer_start)
else:
main_rect = QtCore.QRect(theme.font_main_x, theme.font_main_y,
theme.font_main_width - 1, theme.font_main_height - 1)
if not theme.font_footer_override:
footer_rect = QtCore.QRect(10, self.footer_start, self.width - 20,
self.height - self.footer_start)
else:
footer_rect = QtCore.QRect(theme.font_footer_x,
theme.font_footer_y, theme.font_footer_width - 1,
theme.font_footer_height - 1)
self.renderer.set_text_rectangle(main_rect, footer_rect)
def generate_preview(self, theme_data, force_page=False):
"""
Generate a preview of a theme.
``theme_data``
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')
# save value for use in format_slide
self.force_page = force_page
# set the default image size for previews
self.calculate_default(self.screens.preview[u'size'])
# build a service item to generate preview
serviceItem = ServiceItem()
serviceItem.theme = theme_data
if self.force_page:
# make big page for theme edit dialog to get line count
serviceItem.add_from_text(u'', VERSE + VERSE + VERSE, FOOTER)
else:
self.image_manager.del_image(theme_data.theme_name)
serviceItem.add_from_text(u'', VERSE, FOOTER)
serviceItem.render_manager = self
serviceItem.raw_footer = FOOTER
serviceItem.render(True)
if not self.force_page:
self.display.buildHtml(serviceItem)
raw_html = serviceItem.get_rendered_frame(0)
preview = self.display.text(raw_html)
# Reset the real screen size for subsequent render requests
self.calculate_default(self.screens.current[u'size'])
return preview
def format_slide(self, words, line_break):
"""
Calculate how much text can fit on a slide.
``words``
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')
return self.renderer.format_slide(words, line_break, self.force_page)
def calculate_default(self, screen):
"""
Calculate the default dimentions of the screen.
``screen``
The QSize of the screen.
"""
log.debug(u'calculate default %s', screen)
self.width = screen.width()
self.height = screen.height()
self.screen_ratio = float(self.height) / float(self.width)
log.debug(u'calculate default %d, %d, %f',
self.width, self.height, self.screen_ratio)
# 90% is start of footer
self.footer_start = int(self.height * 0.90)

View File

@ -63,6 +63,8 @@ class ItemCapabilities(object):
ProvidesOwnDisplay = 10
AllowsDetailedTitleDisplay = 11
AllowsVariableStartTime = 12
AllowsVirtualSplit = 13
AllowsWordSplit = 14
class ServiceItem(object):
@ -81,7 +83,7 @@ class ServiceItem(object):
The plugin that this service item belongs to.
"""
if plugin:
self.render_manager = plugin.renderManager
self.renderer = plugin.renderer
self.name = plugin.name
self.title = u''
self.shortname = u''
@ -151,7 +153,7 @@ class ServiceItem(object):
self.icon = icon
self.iconic_representation = build_icon(icon)
def render(self, useOverride=False):
def render(self, use_override=False):
"""
The render method is what generates the frames for the screen and
obtains the display information from the renderemanager.
@ -161,24 +163,23 @@ class ServiceItem(object):
log.debug(u'Render called')
self._display_frames = []
self.bg_image_bytes = None
line_break = True
if self.is_capable(ItemCapabilities.NoLineBreaks):
line_break = False
line_break = not self.is_capable(ItemCapabilities.NoLineBreaks)
theme = self.theme if self.theme else None
self.main, self.footer = \
self.render_manager.set_override_theme(theme, useOverride)
self.themedata = self.render_manager.renderer._theme
self.renderer.set_override_theme(theme, use_override)
self.themedata = self.renderer.theme_data
if self.service_item_type == ServiceItemType.Text:
log.debug(u'Formatting slides')
for slide in self._raw_frames:
formatted = self.render_manager \
.format_slide(slide[u'raw_slide'], line_break)
formatted = self.renderer \
.format_slide(slide[u'raw_slide'], line_break, self)
for page in formatted:
self._display_frames.append(
{u'title': clean_tags(page),
self._display_frames.append({
u'title': clean_tags(page),
u'text': clean_tags(page.rstrip()),
u'html': expand_tags(page.rstrip()),
u'verseTag': slide[u'verseTag'] })
u'verseTag': slide[u'verseTag']
})
elif self.service_item_type == ServiceItemType.Image or \
self.service_item_type == ServiceItemType.Command:
pass
@ -205,7 +206,7 @@ class ServiceItem(object):
"""
self.service_item_type = ServiceItemType.Image
self._raw_frames.append({u'title': title, u'path': path})
self.render_manager.image_manager.add_image(title, path)
self.renderer.image_manager.add_image(title, path)
self._new_item()
def add_from_text(self, title, raw_slide, verse_tag=None):
@ -453,4 +454,5 @@ class ServiceItem(object):
elif not start and end:
return end
else:
return u'%s : %s' % (start, end)
return u'%s : %s' % (start, end)

View File

@ -70,6 +70,8 @@ class MainDisplay(DisplayWidget):
self.videoHide = False
self.override = {}
self.retranslateUi()
self.mediaObject = None
self.firstTime = True
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool |
QtCore.Qt.WindowStaysOnTopHint)
@ -78,6 +80,9 @@ class MainDisplay(DisplayWidget):
QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_phonon_creation'),
self.createMediaObject)
def retranslateUi(self):
"""
@ -102,19 +107,9 @@ class MainDisplay(DisplayWidget):
self.videoWidget.setGeometry(QtCore.QRect(0, 0,
self.screen[u'size'].width(), self.screen[u'size'].height()))
log.debug(u'Setup Phonon for monitor %s' % self.screens.monitor_number)
self.mediaObject = Phonon.MediaObject(self)
self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
Phonon.createPath(self.mediaObject, self.videoWidget)
Phonon.createPath(self.mediaObject, self.audio)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'),
self.videoState)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'finished()'),
self.videoFinished)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'tick(qint64)'),
self.videoTick)
if self.isLive:
if not self.firstTime:
self.createMediaObject()
log.debug(u'Setup webView for monitor %s' % self.screens.monitor_number)
self.webView = QtWebKit.QWebView(self)
self.webView.setGeometry(0, 0,
@ -175,6 +170,24 @@ class MainDisplay(DisplayWidget):
log.debug(
u'Finished setup for monitor %s' % self.screens.monitor_number)
def createMediaObject(self):
self.firstTime = False
log.debug(u'Creating Phonon objects - Start for %s', self.isLive)
self.mediaObject = Phonon.MediaObject(self)
self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
Phonon.createPath(self.mediaObject, self.videoWidget)
Phonon.createPath(self.mediaObject, self.audio)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'),
self.videoState)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'finished()'),
self.videoFinished)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'tick(qint64)'),
self.videoTick)
log.debug(u'Creating Phonon objects - Finished for %s', self.isLive)
def text(self, slide):
"""
Add the slide text from slideController
@ -348,6 +361,8 @@ class MainDisplay(DisplayWidget):
"""
Loads and starts a video to run with the option of sound
"""
if not self.mediaObject:
self.createMediaObject()
log.debug(u'video')
self.webLoaded = True
self.setGeometry(self.screen[u'size'])

View File

@ -30,7 +30,7 @@ from tempfile import gettempdir
from PyQt4 import QtCore, QtGui
from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \
from openlp.core.lib import Renderer, build_icon, OpenLPDockWidget, \
SettingsManager, PluginManager, Receiver, translate
from openlp.core.lib.ui import UiStrings, base_action, checkable_action, \
icon_action, shortcut_action
@ -122,12 +122,17 @@ class Ui_MainWindow(object):
self.HelpMenu = QtGui.QMenu(self.MenuBar)
self.HelpMenu.setObjectName(u'HelpMenu')
mainWindow.setMenuBar(self.MenuBar)
self.StatusBar = QtGui.QStatusBar(mainWindow)
self.StatusBar.setObjectName(u'StatusBar')
mainWindow.setStatusBar(self.StatusBar)
self.DefaultThemeLabel = QtGui.QLabel(self.StatusBar)
self.DefaultThemeLabel.setObjectName(u'DefaultThemeLabel')
self.StatusBar.addPermanentWidget(self.DefaultThemeLabel)
self.statusBar = QtGui.QStatusBar(mainWindow)
self.statusBar.setObjectName(u'statusBar')
mainWindow.setStatusBar(self.statusBar)
self.loadProgressBar = QtGui.QProgressBar(self.statusBar)
self.loadProgressBar.setObjectName(u'loadProgressBar')
self.statusBar.addPermanentWidget(self.loadProgressBar)
self.loadProgressBar.hide()
self.loadProgressBar.setValue(0)
self.defaultThemeLabel = QtGui.QLabel(self.statusBar)
self.defaultThemeLabel.setObjectName(u'defaultThemeLabel')
self.statusBar.addPermanentWidget(self.defaultThemeLabel)
# Create the MediaManager
self.mediaManagerDock = OpenLPDockWidget(mainWindow,
u'mediaManagerDock', u':/system/system_mediamanager.png')
@ -545,9 +550,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
QtCore.SIGNAL(u'openlp_information_message'),
self.onInformationMessage)
# warning cyclic dependency
# RenderManager needs to call ThemeManager and
# ThemeManager needs to call RenderManager
self.renderManager = RenderManager(
# renderer needs to call ThemeManager and
# ThemeManager needs to call Renderer
self.renderer = Renderer(
self.themeManagerContents, self.screens)
# Define the media Dock Manager
self.mediaDockManager = MediaDockManager(self.MediaToolBox)
@ -555,7 +560,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
# make the controllers available to the plugins
self.pluginHelpers[u'preview'] = self.previewController
self.pluginHelpers[u'live'] = self.liveController
self.pluginHelpers[u'render'] = self.renderManager
self.pluginHelpers[u'renderer'] = self.renderer
self.pluginHelpers[u'service'] = self.ServiceManagerContents
self.pluginHelpers[u'settings form'] = self.settingsForm
self.pluginHelpers[u'toolbox'] = self.mediaDockManager
@ -781,7 +786,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
their locations
"""
log.debug(u'screenChanged')
self.renderManager.update_display()
self.renderer.update_display()
self.setFocus()
self.activateWindow()
@ -880,10 +885,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.setWindowTitle(title)
def showStatusMessage(self, message):
self.StatusBar.showMessage(message)
self.statusBar.showMessage(message)
def defaultThemeChanged(self, theme):
self.DefaultThemeLabel.setText(
self.defaultThemeLabel.setText(
unicode(translate('OpenLP.MainWindow', 'Default Theme: %s')) %
theme)
@ -979,7 +984,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
for fileId, filename in enumerate(recentFilesToDisplay):
log.debug('Recent file name: %s', filename)
action = base_action(self, u'')
action.setText(u'&%d %s' %
action.setText(u'&%d %s' %
(fileId + 1, QtCore.QFileInfo(filename).fileName()))
action.setData(QtCore.QVariant(filename))
self.connect(action, QtCore.SIGNAL(u'triggered()'),
@ -1008,3 +1013,34 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
while self.recentFiles.count() > maxRecentFiles:
# Don't care what API says takeLast works, removeLast doesn't!
self.recentFiles.takeLast()
def displayProgressBar(self, size):
"""
Make Progress bar visible and set size
"""
self.loadProgressBar.show()
self.loadProgressBar.setMaximum(size)
self.loadProgressBar.setValue(0)
Receiver.send_message(u'openlp_process_events')
def incrementProgressBar(self):
"""
Increase the Progress Bar value by 1
"""
self.loadProgressBar.setValue(self.loadProgressBar.value() + 1)
Receiver.send_message(u'openlp_process_events')
def finishedProgressBar(self):
"""
Trigger it's removal after 2.5 second
"""
self.timer_id = self.startTimer(2500)
def timerEvent(self, event):
"""
Remove the Progress bar from view.
"""
if event.timerId() == self.timer_id:
self.timer_id = 0
self.loadProgressBar.hide()
Receiver.send_message(u'openlp_process_events')

View File

@ -460,7 +460,11 @@ class ServiceManager(QtGui.QWidget):
service = []
write_list = []
total_size = 0
Receiver.send_message(u'cursor_busy')
# Number of items + 1 to zip it
self.mainwindow.displayProgressBar(len(self.serviceItems) + 1)
for item in self.serviceItems:
self.mainwindow.incrementProgressBar()
service.append({u'serviceitem':
item[u'service_item'].get_service_repr()})
if not item[u'service_item'].uses_file():
@ -501,6 +505,7 @@ class ServiceManager(QtGui.QWidget):
log.debug(u'ServiceManager.saveFile - allowZip64 is %s' % allow_zip_64)
zip = None
success = True
self.mainwindow.incrementProgressBar()
try:
zip = zipfile.ZipFile(path_file_name, 'w', zipfile.ZIP_STORED,
allow_zip_64)
@ -516,6 +521,8 @@ class ServiceManager(QtGui.QWidget):
finally:
if zip:
zip.close()
self.mainwindow.finishedProgressBar()
Receiver.send_message(u'cursor_normal')
if success:
self.mainwindow.addRecentFile(path_file_name)
self.setModified(False)
@ -576,13 +583,15 @@ class ServiceManager(QtGui.QWidget):
items = cPickle.load(fileTo)
fileTo.close()
self.newFile()
self.mainwindow.displayProgressBar(len(items))
for item in items:
self.mainwindow.incrementProgressBar()
serviceItem = ServiceItem()
serviceItem.from_service = True
serviceItem.render_manager = self.mainwindow.renderManager
serviceItem.renderer = self.mainwindow.renderer
serviceItem.set_from_service(item, self.servicePath)
self.validateItem(serviceItem)
self.addServiceItem(serviceItem)
self.addServiceItem(serviceItem, repaint=False)
if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate):
Receiver.send_message(u'%s_service_load' %
serviceItem.name.lower(), serviceItem)
@ -592,7 +601,6 @@ class ServiceManager(QtGui.QWidget):
self.setModified(False)
QtCore.QSettings().setValue(
'service/last file', QtCore.QVariant(fileName))
Receiver.send_message(u'cursor_normal')
else:
critical_error_message_box(
message=translate('OpenLP.ServiceManager',
@ -623,6 +631,9 @@ class ServiceManager(QtGui.QWidget):
fileTo.close()
if zip:
zip.close()
self.mainwindow.finishedProgressBar()
Receiver.send_message(u'cursor_normal')
self.repaintServiceList(-1, -1)
def loadLastFile(self):
"""
@ -989,7 +1000,7 @@ class ServiceManager(QtGui.QWidget):
"""
log.debug(u'onThemeComboBoxSelected')
self.service_theme = unicode(self.themeComboBox.currentText())
self.mainwindow.renderManager.set_service_theme(self.service_theme)
self.mainwindow.renderer.set_service_theme(self.service_theme)
QtCore.QSettings().setValue(
self.mainwindow.serviceSettingsSection + u'/service theme',
QtCore.QVariant(self.service_theme))
@ -1001,7 +1012,7 @@ class ServiceManager(QtGui.QWidget):
sure the theme combo box is in the correct state.
"""
log.debug(u'themeChange')
if self.mainwindow.renderManager.theme_level == ThemeLevel.Global:
if self.mainwindow.renderer.theme_level == ThemeLevel.Global:
self.toolbar.actions[u'ThemeLabel'].setVisible(False)
self.toolbar.actions[u'ThemeWidget'].setVisible(False)
else:
@ -1016,7 +1027,7 @@ class ServiceManager(QtGui.QWidget):
Receiver.send_message(u'cursor_busy')
log.debug(u'regenerateServiceItems')
# force reset of renderer as theme data has changed
self.mainwindow.renderManager.themedata = None
self.mainwindow.renderer.themedata = None
if self.serviceItems:
tempServiceItems = self.serviceItems
self.serviceManagerList.clear()
@ -1056,7 +1067,8 @@ class ServiceManager(QtGui.QWidget):
newItem)
self.setModified()
def addServiceItem(self, item, rebuild=False, expand=None, replace=False):
def addServiceItem(self, item, rebuild=False, expand=None, replace=False,
repaint=True):
"""
Add a Service item to the list
@ -1089,7 +1101,8 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems.append({u'service_item': item,
u'order': len(self.serviceItems) + 1,
u'expanded': expand})
self.repaintServiceList(len(self.serviceItems) - 1, -1)
if repaint:
self.repaintServiceList(len(self.serviceItems) - 1, -1)
else:
self.serviceItems.insert(self.dropPosition,
{u'service_item': item, u'order': self.dropPosition,
@ -1278,7 +1291,7 @@ class ServiceManager(QtGui.QWidget):
self.onThemeChangeAction, context=QtCore.Qt.WidgetShortcut)
self.themeMenu.addAction(action)
find_and_set_in_combo_box(self.themeComboBox, self.service_theme)
self.mainwindow.renderManager.set_service_theme(self.service_theme)
self.mainwindow.renderer.set_service_theme(self.service_theme)
self.regenerateServiceItems()
def onThemeChangeAction(self):

View File

@ -180,16 +180,25 @@ class SlideController(QtGui.QWidget):
self.hideMenu.menu().addAction(self.themeScreen)
self.hideMenu.menu().addAction(self.desktopScreen)
self.toolbar.addToolbarSeparator(u'Loop Separator')
self.toolbar.addToolbarButton(
startLoop = self.toolbar.addToolbarButton(
# Does not need translating - control string.
u'Start Loop', u':/media/media_time.png',
translate('OpenLP.SlideController', 'Start continuous loop'),
self.onStartLoop)
self.toolbar.addToolbarButton(
action_list = ActionList.get_instance()
action_list.add_action(startLoop, UiStrings().LiveToolbar)
stopLoop = self.toolbar.addToolbarButton(
# Does not need translating - control string.
u'Stop Loop', u':/media/media_stop.png',
translate('OpenLP.SlideController', 'Stop continuous loop'),
self.onStopLoop)
action_list.add_action(stopLoop, UiStrings().LiveToolbar)
self.toogleLoop = shortcut_action(self, u'toogleLoop',
[QtGui.QKeySequence(u'L')], self.onToggleLoop,
category=UiStrings().LiveToolbar)
self.toogleLoop.setText(translate('OpenLP.SlideController',
'Start/Stop continuous loop'))
self.addAction(self.toogleLoop)
self.delaySpinBox = QtGui.QSpinBox()
self.delaySpinBox.setMinimum(1)
self.delaySpinBox.setMaximum(180)
@ -268,15 +277,16 @@ class SlideController(QtGui.QWidget):
self.slideLayout.setSpacing(0)
self.slideLayout.setMargin(0)
self.slideLayout.setObjectName(u'SlideLayout')
self.mediaObject = Phonon.MediaObject(self)
self.video = Phonon.VideoWidget()
self.video.setVisible(False)
self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
Phonon.createPath(self.mediaObject, self.video)
Phonon.createPath(self.mediaObject, self.audio)
if not self.isLive:
self.mediaObject = Phonon.MediaObject(self)
self.video = Phonon.VideoWidget()
self.video.setVisible(False)
self.audio = Phonon.AudioOutput(Phonon.VideoCategory,
self.mediaObject)
Phonon.createPath(self.mediaObject, self.video)
Phonon.createPath(self.mediaObject, self.audio)
self.video.setGeometry(QtCore.QRect(0, 0, 300, 225))
self.slideLayout.insertWidget(0, self.video)
self.slideLayout.insertWidget(0, self.video)
# Actual preview screen
self.slidePreview = QtGui.QLabel(self)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,
@ -380,18 +390,21 @@ class SlideController(QtGui.QWidget):
action_list.add_action(self.previousItem)
action_list.add_action(self.nextItem)
self.previousService = shortcut_action(parent, u'previousService',
[QtCore.Qt.Key_Left], self.servicePrevious, UiStrings().LiveToolbar)
self.previousService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
[QtCore.Qt.Key_Left], self.servicePrevious,
category=UiStrings().LiveToolbar,
context=QtCore.Qt.WidgetWithChildrenShortcut)
self.previousService.setText(
translate('OpenLP.SlideController', 'Previous Service'))
self.nextService = shortcut_action(parent, 'nextService',
[QtCore.Qt.Key_Right], self.serviceNext, UiStrings().LiveToolbar)
self.nextService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
[QtCore.Qt.Key_Right], self.serviceNext,
category=UiStrings().LiveToolbar,
context=QtCore.Qt.WidgetWithChildrenShortcut)
self.nextService.setText(
translate('OpenLP.SlideController', 'Next Service'))
self.escapeItem = shortcut_action(parent, 'escapeItem',
[QtCore.Qt.Key_Escape], self.liveEscape, UiStrings().LiveToolbar)
self.escapeItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
[QtCore.Qt.Key_Escape], self.liveEscape,
category=UiStrings().LiveToolbar,
context=QtCore.Qt.WidgetWithChildrenShortcut)
self.escapeItem.setText(
translate('OpenLP.SlideController', 'Escape Item'))
@ -412,7 +425,7 @@ class SlideController(QtGui.QWidget):
"""
# rebuild display as screen size changed
self.display = MainDisplay(self, self.screens, self.isLive)
self.display.imageManager = self.parent.renderManager.image_manager
self.display.imageManager = self.parent.renderer.image_manager
self.display.alertTab = self.alertTab
self.display.setup()
if self.isLive:
@ -491,6 +504,9 @@ class SlideController(QtGui.QWidget):
self.mediabar.setVisible(False)
self.toolbar.makeWidgetsInvisible([u'Song Menu'])
self.toolbar.makeWidgetsInvisible(self.loopList)
self.toogleLoop.setEnabled(False)
self.toolbar.actions[u'Start Loop'].setEnabled(False)
self.toolbar.actions[u'Stop Loop'].setEnabled(False)
self.toolbar.actions[u'Stop Loop'].setVisible(False)
if item.is_text():
if QtCore.QSettings().value(
@ -500,6 +516,9 @@ class SlideController(QtGui.QWidget):
if item.is_capable(ItemCapabilities.AllowsLoop) and \
len(item.get_frames()) > 1:
self.toolbar.makeWidgetsVisible(self.loopList)
self.toogleLoop.setEnabled(True)
self.toolbar.actions[u'Start Loop'].setEnabled(True)
self.toolbar.actions[u'Stop Loop'].setEnabled(True)
if item.is_media():
self.toolbar.setVisible(False)
self.mediabar.setVisible(True)
@ -614,19 +633,19 @@ class SlideController(QtGui.QWidget):
label.setScaledContents(True)
if self.serviceItem.is_command():
image = resize_image(frame[u'image'],
self.parent.renderManager.width,
self.parent.renderManager.height)
self.parent.renderer.width,
self.parent.renderer.height)
else:
# If current slide set background to image
if framenumber == slideno:
self.serviceItem.bg_image_bytes = \
self.parent.renderManager.image_manager. \
self.parent.renderer.image_manager. \
get_image_bytes(frame[u'title'])
image = self.parent.renderManager.image_manager. \
image = self.parent.renderer.image_manager. \
get_image(frame[u'title'])
label.setPixmap(QtGui.QPixmap.fromImage(image))
self.previewListWidget.setCellWidget(framenumber, 0, label)
slideHeight = width * self.parent.renderManager.screen_ratio
slideHeight = width * self.parent.renderer.screen_ratio
row += 1
text.append(unicode(row))
self.previewListWidget.setItem(framenumber, 0, item)
@ -992,6 +1011,15 @@ class SlideController(QtGui.QWidget):
self.previewListWidget.rowCount() - 1)
self.slideSelected()
def onToggleLoop(self, toggled):
"""
Toggles the loop state.
"""
if self.toolbar.actions[u'Start Loop'].isVisible():
self.onStartLoop()
else:
self.onStopLoop()
def onStartLoop(self):
"""
Start the timer loop running and store the timer id
@ -1150,4 +1178,4 @@ class SlideController(QtGui.QWidget):
elif self.desktopScreen.isChecked():
return HideMode.Screen
else:
return None
return None

View File

@ -660,7 +660,7 @@ class ThemeManager(QtGui.QWidget):
def generateImage(self, themeData, forcePage=False):
"""
Call the RenderManager to build a Sample Image
Call the renderer to build a Sample Image
``themeData``
The theme to generated a preview for.
@ -669,7 +669,7 @@ class ThemeManager(QtGui.QWidget):
Flag to tell message lines per page need to be generated.
"""
log.debug(u'generateImage \n%s ', themeData)
return self.mainwindow.renderManager.generate_preview(
return self.mainwindow.renderer.generate_preview(
themeData, forcePage)
def getPreviewImage(self, theme):

View File

@ -149,7 +149,7 @@ class ThemesTab(SettingsTab):
settings.setValue(u'global theme',
QtCore.QVariant(self.global_theme))
settings.endGroup()
self.mainwindow.renderManager.set_global_theme(
self.mainwindow.renderer.set_global_theme(
self.global_theme, self.theme_level)
Receiver.send_message(u'theme_update_global', self.global_theme)
@ -167,7 +167,7 @@ class ThemesTab(SettingsTab):
def onDefaultComboBoxChanged(self, value):
self.global_theme = unicode(self.DefaultComboBox.currentText())
self.mainwindow.renderManager.set_global_theme(
self.mainwindow.renderer.set_global_theme(
self.global_theme, self.theme_level)
self.__previewGlobalTheme()
@ -188,7 +188,7 @@ class ThemesTab(SettingsTab):
for theme in theme_list:
self.DefaultComboBox.addItem(theme)
find_and_set_in_combo_box(self.DefaultComboBox, self.global_theme)
self.mainwindow.renderManager.set_global_theme(
self.mainwindow.renderer.set_global_theme(
self.global_theme, self.theme_level)
if self.global_theme is not u'':
self.__previewGlobalTheme()
@ -203,4 +203,4 @@ class ThemesTab(SettingsTab):
if not preview.isNull():
preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation)
self.DefaultListView.setPixmap(preview)
self.DefaultListView.setPixmap(preview)

View File

@ -101,6 +101,20 @@ class VersionThread(QtCore.QThread):
Receiver.send_message(u'openlp_version_check', u'%s' % version)
class DelayStartThread(QtCore.QThread):
"""
A special Qt thread class to build things after OpenLP has started
"""
def __init__(self, parent):
QtCore.QThread.__init__(self, parent)
def run(self):
"""
Run the thread.
"""
Receiver.send_message(u'openlp_phonon_creation')
class AppLocation(object):
"""
The :class:`AppLocation` class is a static class which retrieves a

View File

@ -723,6 +723,7 @@ class BibleMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.NoLineBreaks)
service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop)
service_item.add_capability(ItemCapabilities.AllowsWordSplit)
# Service Item: Title
service_item.title = u', '.join(raw_title)
# Service Item: Theme
@ -849,4 +850,4 @@ class BibleMediaItem(MediaManagerItem):
self.settings.layout_style)
QtCore.QSettings().setValue(
self.settingsSection + u'/verse layout style',
QtCore.QVariant(self.settings.layout_style))
QtCore.QVariant(self.settings.layout_style))

View File

@ -37,6 +37,9 @@ from openlp.plugins.bibles.lib.db import BibleDB
log = logging.getLogger(__name__)
def replacement(match):
return match.group(2).upper()
class OSISBible(BibleDB):
"""
`OSIS <http://www.bibletechnologies.net/>`_ Bible format importer class.
@ -60,6 +63,7 @@ class OSISBible(BibleDB):
self.lg_regex = re.compile(r'<lg(.*?)>')
self.l_regex = re.compile(r'<l (.*?)>')
self.w_regex = re.compile(r'<w (.*?)>')
self.q_regex = re.compile(r'<q(.*?)>')
self.q1_regex = re.compile(r'<q(.*?)level="1"(.*?)>')
self.q2_regex = re.compile(r'<q(.*?)level="2"(.*?)>')
self.trans_regex = re.compile(r'<transChange(.*?)>(.*?)</transChange>')
@ -106,6 +110,7 @@ class OSISBible(BibleDB):
detect_file.close()
try:
osis = codecs.open(self.filename, u'r', details['encoding'])
repl = replacement
for file_record in osis:
if self.stop_import_flag:
break
@ -148,12 +153,13 @@ class OSISBible(BibleDB):
verse_text = self.rf_regex.sub(u'', verse_text)
verse_text = self.lb_regex.sub(u' ', verse_text)
verse_text = self.lg_regex.sub(u'', verse_text)
verse_text = self.l_regex.sub(u'', verse_text)
verse_text = self.l_regex.sub(u' ', verse_text)
verse_text = self.w_regex.sub(u'', verse_text)
verse_text = self.q1_regex.sub(u'"', verse_text)
verse_text = self.q2_regex.sub(u'\'', verse_text)
verse_text = self.q_regex.sub(u'', verse_text)
verse_text = self.divine_name_regex.sub(repl, verse_text)
verse_text = self.trans_regex.sub(u'', verse_text)
verse_text = self.divine_name_regex.sub(u'', verse_text)
verse_text = verse_text.replace(u'</lb>', u'')\
.replace(u'</l>', u'').replace(u'<lg>', u'')\
.replace(u'</lg>', u'').replace(u'</q>', u'')\

View File

@ -169,7 +169,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
item = self.slideListView.item(row)
slide_list += item.text()
if row != self.slideListView.count() - 1:
slide_list += u'\n[---]\n'
slide_list += u'\n[===]\n'
self.editSlideForm.setText(slide_list)
if self.editSlideForm.exec_():
self.updateSlideList(self.editSlideForm.getText(), True)

View File

@ -63,7 +63,7 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
"""
Returns a list with all slides.
"""
return self.slideTextEdit.toPlainText().split(u'\n[---]\n')
return self.slideTextEdit.toPlainText().split(u'\n[===]\n')
def onSplitButtonPressed(self):
"""
@ -71,5 +71,5 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
"""
if self.slideTextEdit.textCursor().columnNumber() != 0:
self.slideTextEdit.insertPlainText(u'\n')
self.slideTextEdit.insertPlainText(u'[---]\n')
self.slideTextEdit.insertPlainText(u'[===]\n')
self.slideTextEdit.setFocus()

View File

@ -140,6 +140,7 @@ class CustomMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.AllowsEdit)
service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop)
service_item.add_capability(ItemCapabilities.AllowsVirtualSplit)
customSlide = self.parent.manager.get_object(CustomSlide, item_id)
title = customSlide.title
credit = customSlide.credits

View File

@ -77,7 +77,7 @@ class ImageMediaItem(MediaManagerItem):
u'thumbnails')
check_directory_exists(self.servicePath)
self.loadList(SettingsManager.load_list(
self.settingsSection, self.settingsSection))
self.settingsSection, self.settingsSection), True)
def addListViewToToolBar(self):
MediaManagerItem.addListViewToToolBar(self)
@ -107,8 +107,13 @@ class ImageMediaItem(MediaManagerItem):
SettingsManager.set_list(self.settingsSection,
self.settingsSection, self.getFileList())
def loadList(self, list):
def loadList(self, list, initialLoad=False):
Receiver.send_message(u'cursor_busy')
if not initialLoad:
self.parent.formparent.displayProgressBar(len(list))
for imageFile in list:
if not initialLoad:
self.parent.formparent.incrementProgressBar()
filename = os.path.split(unicode(imageFile))[1]
thumb = os.path.join(self.servicePath, filename)
if os.path.exists(thumb):
@ -122,6 +127,9 @@ class ImageMediaItem(MediaManagerItem):
item_name.setIcon(icon)
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(imageFile))
self.listView.addItem(item_name)
Receiver.send_message(u'cursor_normal')
if not initialLoad:
self.parent.formparent.finishedProgressBar()
def generateSlideData(self, service_item, item=None, xmlVersion=False):
items = self.listView.selectedIndexes()
@ -201,4 +209,4 @@ class ImageMediaItem(MediaManagerItem):
critical_error_message_box(UiStrings().LiveBGError,
unicode(translate('ImagePlugin.MediaItem',
'There was a problem replacing your background, '
'the image file "%s" no longer exists.')) % filename)
'the image file "%s" no longer exists.')) % filename)

View File

@ -50,10 +50,13 @@ class MediaMediaItem(MediaManagerItem):
u':/media/media_video.png').toImage()
MediaManagerItem.__init__(self, parent, self, icon)
self.singleServiceItem = False
self.mediaObject = Phonon.MediaObject(self)
self.mediaObject = None
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'video_background_replaced'),
self.videobackgroundReplaced)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_phonon_creation'),
self.createPhonon)
def retranslateUi(self):
self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media')
@ -209,4 +212,9 @@ class MediaMediaItem(MediaManagerItem):
img = QtGui.QPixmap(u':/media/media_video.png').toImage()
item_name.setIcon(build_icon(img))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
self.listView.addItem(item_name)
self.listView.addItem(item_name)
def createPhonon(self):
log.debug(u'CreatePhonon')
if not self.mediaObject:
self.mediaObject = Phonon.MediaObject(self)

View File

@ -219,7 +219,6 @@ class ImpressDocument(PresentationDocument):
The file name of the presentatios to the run.
"""
log.debug(u'Load Presentation OpenOffice')
#print "s.dsk1 ", self.desktop
if os.name == u'nt':
desktop = self.controller.get_com_desktop()
if desktop is None:
@ -234,7 +233,10 @@ class ImpressDocument(PresentationDocument):
return False
self.desktop = desktop
properties = []
properties.append(self.create_property(u'Minimized', True))
if os.name != u'nt':
# Recent versions of Impress on Windows won't start the presentation
# if it starts as minimized. It seems OK on Linux though.
properties.append(self.create_property(u'Minimized', True))
properties = tuple(properties)
try:
self.document = desktop.loadComponentFromURL(url, u'_blank',
@ -242,9 +244,15 @@ class ImpressDocument(PresentationDocument):
except:
log.exception(u'Failed to load presentation %s' % url)
return False
if os.name == u'nt':
# As we can't start minimized the Impress window gets in the way.
# Either window.setPosSize(0, 0, 200, 400, 12) or .setVisible(False)
window = self.document.getCurrentController().getFrame() \
.getContainerWindow()
window.setVisible(False)
self.presentation = self.document.getPresentation()
self.presentation.Display = \
self.controller.plugin.renderManager.screens.current_display + 1
self.controller.plugin.renderer.screens.current_display + 1
self.control = None
self.create_thumbnails()
return True
@ -387,14 +395,14 @@ class ImpressDocument(PresentationDocument):
log.debug(u'start presentation OpenOffice')
if self.control is None or not self.control.isRunning():
self.presentation.start()
# start() returns before the getCurrentComponent is ready.
# Try for 5 seconds
self.control = self.presentation.getController()
# start() returns before the Component is ready.
# Try for 15 seconds
i = 1
while self.desktop.getCurrentComponent() is None and i < 50:
while not self.control and i < 150:
time.sleep(0.1)
i = i + 1
self.control = \
self.desktop.getCurrentComponent().Presentation.getController()
self.control = self.presentation.getController()
else:
self.control.activate()
self.goto_slide(1)

View File

@ -158,7 +158,12 @@ class PresentationMediaItem(MediaManagerItem):
titles = []
for file in currlist:
titles.append(os.path.split(file)[1])
Receiver.send_message(u'cursor_busy')
if not initialLoad:
self.parent.formparent.displayProgressBar(len(list))
for file in list:
if not initialLoad:
self.parent.formparent.incrementProgressBar()
if currlist.count(file) > 0:
continue
filename = os.path.split(unicode(file))[1]
@ -198,6 +203,9 @@ class PresentationMediaItem(MediaManagerItem):
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
item_name.setIcon(icon)
self.listView.addItem(item_name)
Receiver.send_message(u'cursor_normal')
if not initialLoad:
self.parent.formparent.finishedProgressBar()
def onDeleteClick(self):
"""
@ -285,7 +293,7 @@ class PresentationMediaItem(MediaManagerItem):
"supports" the extension. If none found, then look for a controller
which "also supports" it instead.
"""
filetype = filename.split(u'.')[1]
filetype = os.path.splitext(filename)[1]
if not filetype:
return None
for controller in self.controllers:
@ -296,4 +304,4 @@ class PresentationMediaItem(MediaManagerItem):
if self.controllers[controller].enabled():
if filetype in self.controllers[controller].alsosupports:
return controller
return None
return None

View File

@ -251,14 +251,15 @@ class PowerpointDocument(PresentationDocument):
win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88)
except win32ui.error:
dpi = 96
rendermanager = self.controller.plugin.renderManager
rect = rendermanager.screens.current[u'size']
renderer = self.controller.plugin.renderer
rect = renderer.screens.current[u'size']
ppt_window = self.presentation.SlideShowSettings.Run()
ppt_window.Top = rect.y() * 72 / dpi
ppt_window.Height = rect.height() * 72 / dpi
ppt_window.Left = rect.x() * 72 / dpi
ppt_window.Width = rect.width() * 72 / dpi
def get_slide_number(self):
"""
Returns the current slide number.

View File

@ -121,8 +121,8 @@ class PptviewDocument(PresentationDocument):
The file name of the presentations to run.
"""
log.debug(u'LoadPresentation')
rendermanager = self.controller.plugin.renderManager
rect = rendermanager.screens.current[u'size']
renderer = self.controller.plugin.renderer
rect = renderer.screens.current[u'size']
rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom())
filepath = str(self.filepath.replace(u'/', u'\\'))
if not os.path.isdir(self.get_temp_folder()):
@ -244,4 +244,4 @@ class PptviewDocument(PresentationDocument):
"""
Triggers the previous slide on the running presentation
"""
self.controller.process.PrevStep(self.pptid)
self.controller.process.PrevStep(self.pptid)

View File

@ -89,7 +89,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.onVerseListViewPressed)
QtCore.QObject.connect(self.themeAddButton,
QtCore.SIGNAL(u'clicked()'),
self.parent.parent.renderManager.theme_manager.onAddTheme)
self.parent.parent.renderer.theme_manager.onAddTheme)
QtCore.QObject.connect(self.maintenanceButton,
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
QtCore.QObject.connect(Receiver.get_receiver(),
@ -576,11 +576,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if verse_index is not None:
order.append(VerseType.Tags[verse_index] + u'1')
else:
order.append(u'') # it matches no verses anyway
# it matches no verses anyway
order.append(u'')
else:
verse_index = VerseType.from_translated_tag(item[0])
if verse_index is None:
order.append(u'') # same as above
# it matches no verses anyway
order.append(u'')
else:
verse_tag = VerseType.Tags[verse_index]
verse_num = item[1:].lower()
@ -779,4 +781,4 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.song.verse_order)
except:
log.exception(u'Problem processing song Lyrics \n%s',
sxml.dump_xml())
sxml.dump_xml())

View File

@ -257,7 +257,7 @@ def clean_song(manager, song):
``song``
The song object.
"""
song.title = song.title.strip() if song.title else u''
song.title = song.title.rstrip() if song.title else u''
if song.alternate_title is None:
song.alternate_title = u''
song.alternate_title = song.alternate_title.strip()
@ -293,7 +293,10 @@ def clean_song(manager, song):
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
# Rebuild the verse order, to convert translated verse tags, which might
# have been added prior to 1.9.5.
order = song.verse_order.strip().split()
if song.verse_order:
order = song.verse_order.strip().split()
else:
order = []
new_order = []
for verse_def in order:
verse_type = VerseType.Tags[VerseType.from_loose_input(verse_def[0])]

View File

@ -347,6 +347,7 @@ class SongMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.AllowsLoop)
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
service_item.add_capability(ItemCapabilities.AddIfNewItem)
service_item.add_capability(ItemCapabilities.AllowsVirtualSplit)
song = self.parent.manager.get_object(Song, item_id)
service_item.theme = song.theme_name
service_item.edit_id = item_id
@ -472,4 +473,4 @@ class SongMediaItem(MediaManagerItem):
Locale aware collation of song titles
"""
return locale.strcoll(unicode(song_1.title.lower()),
unicode(song_2.title.lower()))
unicode(song_2.title.lower()))

View File

@ -32,7 +32,7 @@ The basic XML for storing the lyrics in the song database looks like this::
<song version="1.0">
<lyrics>
<verse type="c" label="1" lang="en">
<![CDATA[ ... ]]>
<![CDATA[Chorus virtual slide 1[---]Chorus virtual slide 2]]>
</verse>
</lyrics>
</song>
@ -129,18 +129,31 @@ class SongXML(object):
The returned list has the following format::
[[{'lang': 'en', 'type': 'v', 'label': '1'}, u"English verse"],
[[{'type': 'v', 'label': '1'},
u"virtual slide 1[---]virtual slide 2"],
[{'lang': 'en', 'type': 'c', 'label': '1'}, u"English chorus"]]
"""
self.song_xml = None
if xml[:5] == u'<?xml':
verse_list = []
if not xml.startswith(u'<?xml') and not xml.startswith(u'<song'):
# This is an old style song, without XML. Let's handle it correctly
# by iterating through the verses, and then recreating the internal
# xml object as well.
self.song_xml = objectify.fromstring(u'<song version="1.0" />')
self.lyrics = etree.SubElement(self.song_xml, u'lyrics')
verses = xml.split(u'\n\n')
for count, verse in enumerate(verses):
verse_list.append([{u'type': u'v', u'label': unicode(count)},
unicode(verse)])
self.add_verse_to_lyrics(u'v', unicode(count), verse)
return verse_list
elif xml.startswith(u'<?xml'):
xml = xml[38:]
try:
self.song_xml = objectify.fromstring(xml)
except etree.XMLSyntaxError:
log.exception(u'Invalid xml %s', xml)
xml_iter = self.song_xml.getiterator()
verse_list = []
for element in xml_iter:
if element.tag == u'verse':
if element.text is None:
@ -226,7 +239,6 @@ class OpenLyrics(object):
Convert the song to OpenLyrics Format.
"""
sxml = SongXML()
verse_list = sxml.get_verses(song.lyrics)
song_xml = objectify.fromstring(u'<song/>')
# Append the necessary meta data to the song.
song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song')
@ -268,17 +280,26 @@ class OpenLyrics(object):
themes = etree.SubElement(properties, u'themes')
for topic in song.topics:
self._add_text_to_element(u'theme', themes, topic.name)
# Process the song's lyrics.
lyrics = etree.SubElement(song_xml, u'lyrics')
verse_list = sxml.get_verses(song.lyrics)
for verse in verse_list:
verse_tag = u'%s%s' % (
verse[0][u'type'][0].lower(), verse[0][u'label'])
element = \
self._add_text_to_element(u'verse', lyrics, None, verse_tag)
if verse[0].has_key(u'lang'):
element.set(u'lang', verse[0][u'lang'])
element = self._add_text_to_element(u'lines', element)
for line in unicode(verse[1]).split(u'\n'):
self._add_text_to_element(u'line', element, line)
verse_tag = verse[0][u'type'][0].lower()
verse_number = verse[0][u'label']
# Create a list with all "virtual" verses.
virtual_verses = verse[1].split(u'[---]')
for index, virtual_verse in enumerate(virtual_verses):
verse_def = verse_tag + verse_number
# We need "v1a" because we have more than one virtual verse.
if len(virtual_verses) > 1:
verse_def += list(u'abcdefghijklmnopqrstuvwxyz')[index]
element = \
self._add_text_to_element(u'verse', lyrics, None, verse_def)
if verse[0].has_key(u'lang'):
element.set(u'lang', verse[0][u'lang'])
element = self._add_text_to_element(u'lines', element)
for line in virtual_verse.strip(u'\n').split(u'\n'):
self._add_text_to_element(u'line', element, line)
return self._extract_xml(song_xml)
def xml_to_song(self, xml):
@ -446,6 +467,8 @@ class OpenLyrics(object):
The song object.
"""
sxml = SongXML()
verses = {}
verse_def_list = []
for verse in lyrics.verse:
text = u''
for lines in verse.lines:
@ -465,7 +488,15 @@ class OpenLyrics(object):
lang = None
if self._get(verse, u'lang'):
lang = self._get(verse, u'lang')
sxml.add_verse_to_lyrics(verse_tag, verse_number, text, lang)
if verses.has_key((verse_tag, verse_number, lang)):
verses[(verse_tag, verse_number, lang)] += u'\n[---]\n' + text
else:
verses[(verse_tag, verse_number, lang)] = text
verse_def_list.append((verse_tag, verse_number, lang))
# We have to use a list to keep the order, as dicts are not sorted.
for verse in verse_def_list:
sxml.add_verse_to_lyrics(
verse[0], verse[1], verses[verse], verse[2])
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
# Process verse order
if hasattr(properties, u'verseOrder'):