Reworked some renderer code.

This improves the performance a bit. And improves code readability/clearness (at least that what I think). :-D

bzr-revno: 1982
This commit is contained in:
Andreas Preikschat 2012-06-09 10:33:16 +01:00 committed by Tim Bentley
commit 5c5a1ab68f
7 changed files with 186 additions and 155 deletions

View File

@ -55,29 +55,32 @@ class Renderer(object):
"""
log.info(u'Renderer Loaded')
def __init__(self, imageManager, themeManager):
def __init__(self, image_manager, theme_manager):
"""
Initialise the renderer.
``imageManager``
A imageManager instance which takes care of e. g. caching and resizing
images.
``image_manager``
A image_manager instance which takes care of e. g. caching and
resizing images.
``themeManager``
The themeManager instance, used to get the current theme details.
``theme_manager``
The theme_manager instance, used to get the current theme details.
"""
log.debug(u'Initialisation started')
self.themeManager = themeManager
self.imageManager = imageManager
self.theme_manager = theme_manager
self.image_manager = image_manager
self.screens = ScreenList()
self.service_theme = u''
self.theme_level = u''
self.override_background = None
self.theme_data = None
self.bg_frame = None
self.theme_level = ThemeLevel.Global
self.global_theme_name = u''
self.service_theme_name = u''
self.item_theme_name = u''
self.force_page = False
self.display = MainDisplay(None, self.imageManager, False, self)
self.display = MainDisplay(None, self.image_manager, False, self)
self.display.setup()
self._theme_dimensions = {}
self._calculate_default()
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'theme_update_global'), self.set_global_theme)
def update_display(self):
"""
@ -87,100 +90,132 @@ class Renderer(object):
self._calculate_default()
if self.display:
self.display.close()
self.display = MainDisplay(None, self.imageManager, False, self)
self.display = MainDisplay(None, self.image_manager, False, self)
self.display.setup()
self.bg_frame = None
self.theme_data = None
self._theme_dimensions = {}
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
def update_theme(self, theme_name, old_theme_name=None, only_delete=False):
"""
Set the global-level theme and the theme level.
This method updates the theme in ``_theme_dimensions`` when a theme
has been edited or renamed.
``global_theme``
The global-level theme to be set.
``theme_name``
The current theme name.
``old_theme_name``
The old theme name. Has only to be passed, when the theme has been
renamed. Defaults to *None*.
``only_delete``
Only remove the given ``theme_name`` from the ``_theme_dimensions``
list. This can be used when a theme is permanently deleted.
"""
if old_theme_name is not None and \
old_theme_name in self._theme_dimensions:
del self._theme_dimensions[old_theme_name]
if theme_name in self._theme_dimensions:
del self._theme_dimensions[theme_name]
if not only_delete:
self._set_theme(theme_name)
def _set_theme(self, theme_name):
"""
Helper method to save theme names and theme data.
``theme_name``
The theme name.
"""
if theme_name not in self._theme_dimensions:
theme_data = self.theme_manager.getThemeData(theme_name)
main_rect = self.get_main_rectangle(theme_data)
footer_rect = self.get_footer_rectangle(theme_data)
self._theme_dimensions[theme_name] = \
[theme_data, main_rect, footer_rect]
else:
theme_data, main_rect, footer_rect = \
self._theme_dimensions[theme_name]
# if No file do not update cache
if theme_data.background_filename:
self.image_manager.addImage(theme_data.theme_name,
theme_data.background_filename, u'theme',
QtGui.QColor(theme_data.background_border_color))
def pre_render(self, override_theme_data=None):
"""
Set up the theme to be used before rendering an item.
``override_theme_data``
The theme data should be passed, when we want to use our own theme
data, regardless of the theme level. This should for example be used
in the theme manager. **Note**, this is **not** to be mixed up with
the ``set_item_theme`` method.
"""
# Just assume we use the global theme.
theme_to_use = self.global_theme_name
# The theme level is either set to Service or Item. Use the service
# theme if one is set. We also have to use the service theme, even when
# the theme level is set to Item, because the item does not necessarily
# have to have a theme.
if self.theme_level != ThemeLevel.Global:
# When the theme level is at Service and we actually have a service
# theme then use it.
if self.service_theme_name:
theme_to_use = self.service_theme_name
# If we have Item level and have an item theme then use it.
if self.theme_level == ThemeLevel.Song and self.item_theme_name:
theme_to_use = self.item_theme_name
if override_theme_data is None:
if theme_to_use not in self._theme_dimensions:
self._set_theme(theme_to_use)
theme_data, main_rect, footer_rect = \
self._theme_dimensions[theme_to_use]
else:
# Ignore everything and use own theme data.
theme_data = override_theme_data
main_rect = self.get_main_rectangle(override_theme_data)
footer_rect = self.get_footer_rectangle(override_theme_data)
self._set_text_rectangle(theme_data, main_rect, footer_rect)
return theme_data, self._rect, self._rect_footer
def set_theme_level(self, theme_level):
"""
Sets the theme level.
``theme_level``
Defaults to ``ThemeLevel.Global``. The theme level, can be
``ThemeLevel.Global``, ``ThemeLevel.Service`` or
``ThemeLevel.Song``.
The theme level to be used.
"""
self.global_theme = global_theme
self.theme_level = theme_level
self.global_theme_data = \
self.themeManager.getThemeData(self.global_theme)
self.theme_data = None
self._cache_background_image(self.global_theme_data)
def set_service_theme(self, service_theme):
def set_global_theme(self, global_theme_name):
"""
Set the global-level theme name.
``global_theme_name``
The global-level theme's name.
"""
self._set_theme(global_theme_name)
self.global_theme_name = global_theme_name
def set_service_theme(self, service_theme_name):
"""
Set the service-level theme.
``service_theme``
The service-level theme to be set.
``service_theme_name``
The service level theme's name.
"""
self.service_theme = service_theme
self.theme_data = None
self._cache_background_image(self.themeManager.getThemeData
(service_theme))
self._set_theme(service_theme_name)
self.service_theme_name = service_theme_name
def _cache_background_image(self, temp_theme):
def set_item_theme(self, item_theme_name):
"""
Adds a background image to the image cache if necessary.
Set the item-level theme. **Note**, this has to be done for each item we
are rendering.
``temp_theme``
The theme object containing the theme data.
``item_theme_name``
The item theme's name.
"""
# if No file do not update cache
if temp_theme.background_filename:
self.imageManager.addImage(temp_theme.theme_name,
temp_theme.background_filename, u'theme',
QtGui.QColor(temp_theme.background_border_color))
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
``override_theme``
The name of the song-level theme. None means the service
item wants to use the given value.
``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.themeManager.getThemeData(theme)
self._calculate_default()
self._build_text_rectangle(self.theme_data)
self._cache_background_image(self.theme_data)
return self._rect, self._rect_footer
self._set_theme(item_theme_name)
self.item_theme_name = item_theme_name
def generate_preview(self, theme_data, force_page=False):
"""
@ -195,27 +230,31 @@ class Renderer(object):
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()
# 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_FOR_LINE_COUNT)
else:
self.imageManager.deleteImage(theme_data.theme_name)
self.image_manager.deleteImage(theme_data.theme_name)
serviceItem.add_from_text(u'', VERSE)
serviceItem.renderer = self
serviceItem.raw_footer = FOOTER
# if No file do not update cache
if theme_data.background_filename:
self.image_manager.addImage(theme_data.theme_name,
theme_data.background_filename, u'theme',
QtGui.QColor(theme_data.background_border_color))
theme_data, main, footer = self.pre_render(theme_data)
serviceItem.themedata = theme_data
serviceItem.main = main
serviceItem.footer = footer
serviceItem.render(True)
if not self.force_page:
self.display.buildHtml(serviceItem)
raw_html = serviceItem.get_rendered_frame(0)
self.display.text(raw_html)
preview = self.display.preview()
# Reset the real screen size for subsequent render requests
self._calculate_default()
return preview
self.force_page = False
@ -315,52 +354,41 @@ class Renderer(object):
# 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 = self.get_main_rectangle(theme)
footer_rect = self.get_footer_rectangle(theme)
self._set_text_rectangle(main_rect, footer_rect)
def get_main_rectangle(self, theme):
def get_main_rectangle(self, theme_data):
"""
Calculates the placement and size of the main rectangle.
``theme``
``theme_data``
The theme information
"""
if not theme.font_main_override:
return QtCore.QRect(10, 0, self.width - 20, self.footer_start)
if not theme_data.font_main_override:
return QtCore.QRect(10, 0, self.width, self.footer_start)
else:
return QtCore.QRect(theme.font_main_x, theme.font_main_y,
theme.font_main_width - 1, theme.font_main_height - 1)
return QtCore.QRect(theme_data.font_main_x, theme_data.font_main_y,
theme_data.font_main_width - 1, theme_data.font_main_height - 1)
def get_footer_rectangle(self, theme):
def get_footer_rectangle(self, theme_data):
"""
Calculates the placement and size of the footer rectangle.
``theme``
The theme information
``theme_data``
The theme data.
"""
if not theme.font_footer_override:
if not theme_data.font_footer_override:
return QtCore.QRect(10, self.footer_start, self.width - 20,
self.height - self.footer_start)
else:
return QtCore.QRect(theme.font_footer_x,
theme.font_footer_y, theme.font_footer_width - 1,
theme.font_footer_height - 1)
return QtCore.QRect(theme_data.font_footer_x,
theme_data.font_footer_y, theme_data.font_footer_width - 1,
theme_data.font_footer_height - 1)
def _set_text_rectangle(self, rect_main, rect_footer):
def _set_text_rectangle(self, theme_data, rect_main, rect_footer):
"""
Sets the rectangle within which text should be rendered.
``theme_data``
The theme data.
``rect_main``
The main text block.
@ -372,9 +400,9 @@ class Renderer(object):
self._rect_footer = rect_footer
self.page_width = self._rect.width()
self.page_height = self._rect.height()
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)
if theme_data.font_main_shadow:
self.page_width -= int(theme_data.font_main_shadow_size)
self.page_height -= int(theme_data.font_main_shadow_size)
self.web = QtWebKit.QWebView()
self.web.setVisible(False)
self.web.resize(self.page_width, self.page_height)
@ -392,8 +420,8 @@ class Renderer(object):
</script><style>*{margin: 0; padding: 0; border: 0;}
#main {position: absolute; top: 0px; %s %s}</style></head><body>
<div id="main"></div></body></html>""" % \
(build_lyrics_format_css(self.theme_data, self.page_width,
self.page_height), build_lyrics_outline_css(self.theme_data))
(build_lyrics_format_css(theme_data, self.page_width,
self.page_height), build_lyrics_outline_css(theme_data))
self.web.setHtml(html)
self.empty_height = self.web_frame.contentsSize().height()

View File

@ -158,19 +158,24 @@ class ServiceItem(object):
self.icon = icon
self.iconic_representation = build_icon(icon)
def render(self, use_override=False):
def render(self, provides_own_theme_data=False):
"""
The render method is what generates the frames for the screen and
obtains the display information from the renderer. At this point all
slides are built for the given display size.
``provides_own_theme_data``
This switch disables the usage of the item's theme. However, this is
disabled by default. If this is used, it has to be taken care, that
the renderer knows the correct theme data. However, this is needed
for the theme manager.
"""
log.debug(u'Render called')
self._display_frames = []
self.bg_image_bytes = None
theme = self.theme if self.theme else None
self.main, self.footer = \
self.renderer.set_override_theme(theme, use_override)
self.themedata = self.renderer.theme_data
if not provides_own_theme_data:
self.renderer.set_item_theme(self.theme)
self.themedata, self.main, self.footer = self.renderer.pre_render()
if self.service_item_type == ServiceItemType.Text:
log.debug(u'Formatting slides')
for slide in self._raw_frames:
@ -211,7 +216,7 @@ class ServiceItem(object):
self.image_border = background
self.service_item_type = ServiceItemType.Image
self._raw_frames.append({u'title': title, u'path': path})
self.renderer.imageManager.addImage(title, path, u'image',
self.renderer.image_manager.addImage(title, path, u'image',
self.image_border)
self._new_item()

View File

@ -863,7 +863,7 @@ class SlideController(Controller):
image = self.imageManager.getImage(frame[u'title'])
label.setPixmap(QtGui.QPixmap.fromImage(image))
self.previewListWidget.setCellWidget(framenumber, 0, label)
slideHeight = width * self.parent().renderer.screen_ratio
slideHeight = width * (1 / self.ratio)
row += 1
self.slideList[unicode(row)] = row - 1
text.append(unicode(row))

View File

@ -247,8 +247,7 @@ class ThemeManager(QtGui.QWidget):
QtCore.QSettings().setValue(
self.settingsSection + u'/global theme',
QtCore.QVariant(self.global_theme))
Receiver.send_message(u'theme_update_global',
self.global_theme)
Receiver.send_message(u'theme_update_global', self.global_theme)
self._pushThemes()
def onAddTheme(self):
@ -285,6 +284,8 @@ class ThemeManager(QtGui.QWidget):
if plugin.usesTheme(old_theme_name):
plugin.renameTheme(old_theme_name, new_theme_name)
self.loadThemes()
self.mainwindow.renderer.update_theme(
new_theme_name, old_theme_name)
def onCopyTheme(self):
"""
@ -321,9 +322,8 @@ class ThemeManager(QtGui.QWidget):
Loads the settings for the theme that is to be edited and launches the
theme editing form so the user can make their changes.
"""
if check_item_selected(self.themeListWidget,
translate('OpenLP.ThemeManager',
'You must select a theme to edit.')):
if check_item_selected(self.themeListWidget, translate(
'OpenLP.ThemeManager', 'You must select a theme to edit.')):
item = self.themeListWidget.currentItem()
theme = self.getThemeData(
unicode(item.data(QtCore.Qt.UserRole).toString()))
@ -332,6 +332,7 @@ class ThemeManager(QtGui.QWidget):
self.themeForm.theme = theme
self.themeForm.exec_(True)
self.old_background_image = None
self.mainwindow.renderer.update_theme(theme.theme_name)
def onDeleteTheme(self):
"""
@ -349,6 +350,7 @@ class ThemeManager(QtGui.QWidget):
# As we do not reload the themes, push out the change. Reload the
# list as the internal lists and events need to be triggered.
self._pushThemes()
self.mainwindow.renderer.update_theme(theme, only_delete=True)
def deleteTheme(self, theme):
"""

View File

@ -151,8 +151,8 @@ class ThemesTab(SettingsTab):
settings.setValue(u'theme level', QtCore.QVariant(self.theme_level))
settings.setValue(u'global theme', QtCore.QVariant(self.global_theme))
settings.endGroup()
self.mainwindow.renderer.set_global_theme(
self.global_theme, self.theme_level)
self.mainwindow.renderer.set_global_theme(self.global_theme)
self.mainwindow.renderer.set_theme_level(self.theme_level)
Receiver.send_message(u'theme_update_global', self.global_theme)
def postSetUp(self):
@ -169,8 +169,8 @@ class ThemesTab(SettingsTab):
def onDefaultComboBoxChanged(self, value):
self.global_theme = unicode(self.DefaultComboBox.currentText())
self.mainwindow.renderer.set_global_theme(
self.global_theme, self.theme_level)
self.mainwindow.renderer.set_global_theme(self.global_theme)
self.mainwindow.renderer.set_theme_level(self.theme_level)
self.__previewGlobalTheme()
def updateThemeList(self, theme_list):
@ -189,8 +189,8 @@ class ThemesTab(SettingsTab):
self.DefaultComboBox.clear()
self.DefaultComboBox.addItems(theme_list)
find_and_set_in_combo_box(self.DefaultComboBox, self.global_theme)
self.mainwindow.renderer.set_global_theme(
self.global_theme, self.theme_level)
self.mainwindow.renderer.set_global_theme(self.global_theme)
self.mainwindow.renderer.set_theme_level(self.theme_level)
if self.global_theme is not u'':
self.__previewGlobalTheme()

View File

@ -197,9 +197,6 @@ class CustomMediaItem(MediaManagerItem):
def generateSlideData(self, service_item, item=None, xmlVersion=False,
remote=False):
raw_footer = []
slide = None
theme = None
item_id = self._getIdOfItemToGenerate(item, self.remoteCustom)
service_item.add_capability(ItemCapabilities.CanEdit)
service_item.add_capability(ItemCapabilities.CanPreview)
@ -220,10 +217,9 @@ class CustomMediaItem(MediaManagerItem):
service_item.add_from_text(slide[:30], slide)
if QtCore.QSettings().value(self.settingsSection + u'/display footer',
QtCore.QVariant(True)).toBool() or credit:
raw_footer.append(title + u' ' + credit)
service_item.raw_footer.append(u' '.join([title, credit]))
else:
raw_footer.append(u'')
service_item.raw_footer = raw_footer
service_item.raw_footer.append(u'')
return True
def onSearchTextButtonClicked(self):

View File

@ -97,7 +97,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.onVerseOrderTextChanged)
QtCore.QObject.connect(self.themeAddButton,
QtCore.SIGNAL(u'clicked()'),
self.mediaitem.plugin.renderer.themeManager.onAddTheme)
self.mediaitem.plugin.renderer.theme_manager.onAddTheme)
QtCore.QObject.connect(self.maintenanceButton,
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
QtCore.QObject.connect(self.audioAddFromFileButton,