2009-04-22 19:46:10 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
2009-07-14 13:51:27 +00:00
|
|
|
|
2009-09-08 19:58:05 +00:00
|
|
|
###############################################################################
|
|
|
|
# OpenLP - Open Source Lyrics Projection #
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
# Copyright (c) 2008-2009 Raoul Snyman #
|
|
|
|
# Portions copyright (c) 2008-2009 Martin Thompson, Tim Bentley, Carsten #
|
|
|
|
# Tinggaard, Jon Tibble, Jonathan Corwin, Maikel Stuivenberg, Scott Guerrieri #
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
# 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 #
|
|
|
|
###############################################################################
|
2009-07-14 13:51:27 +00:00
|
|
|
|
2009-04-22 19:46:10 +00:00
|
|
|
import logging
|
2009-07-10 13:16:15 +00:00
|
|
|
import os
|
2009-04-22 19:46:10 +00:00
|
|
|
import sys
|
2009-07-10 13:16:15 +00:00
|
|
|
import linecache
|
2009-05-11 05:09:43 +00:00
|
|
|
|
2009-06-19 18:41:38 +00:00
|
|
|
from PyQt4 import QtGui, QtCore
|
2009-04-22 19:46:10 +00:00
|
|
|
|
2009-07-10 13:16:15 +00:00
|
|
|
from renderer import Renderer
|
2009-05-11 05:09:43 +00:00
|
|
|
|
2009-07-10 13:16:15 +00:00
|
|
|
class RenderManager(object):
|
2009-04-22 19:46:10 +00:00
|
|
|
"""
|
2009-07-10 13:16:15 +00:00
|
|
|
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.
|
2009-09-04 22:50:19 +00:00
|
|
|
|
|
|
|
``theme_manager``
|
|
|
|
The ThemeManager instance, used to get the current theme details.
|
|
|
|
|
|
|
|
``screen_list``
|
|
|
|
The list of screens available.
|
|
|
|
|
|
|
|
``screen_number``
|
|
|
|
Defaults to *0*. The index of the output/display screen.
|
2009-04-22 19:46:10 +00:00
|
|
|
"""
|
|
|
|
global log
|
2009-07-14 13:51:27 +00:00
|
|
|
log = logging.getLogger(u'RenderManager')
|
2009-04-22 19:46:10 +00:00
|
|
|
log.info(u'RenderManager Loaded')
|
|
|
|
|
2009-06-05 18:53:50 +00:00
|
|
|
def __init__(self, theme_manager, screen_list, screen_number=0):
|
2009-07-10 13:16:15 +00:00
|
|
|
"""
|
|
|
|
Initialise the render manager.
|
|
|
|
"""
|
2009-04-22 19:46:10 +00:00
|
|
|
log.debug(u'Initilisation started')
|
|
|
|
self.screen_list = screen_list
|
2009-04-25 06:11:15 +00:00
|
|
|
self.theme_manager = theme_manager
|
2009-04-22 19:46:10 +00:00
|
|
|
self.displays = len(screen_list)
|
2009-07-23 20:20:49 +00:00
|
|
|
if (screen_number + 1) > len(screen_list):
|
|
|
|
self.current_display = 0
|
|
|
|
else:
|
|
|
|
self.current_display = screen_number
|
2009-05-16 10:24:03 +00:00
|
|
|
self.renderer = Renderer()
|
2009-09-16 19:00:17 +00:00
|
|
|
self.calculate_default(self.screen_list[self.current_display][u'size'])
|
2009-05-18 19:04:25 +00:00
|
|
|
self.theme = u''
|
2009-05-20 20:17:20 +00:00
|
|
|
self.service_theme = u''
|
2009-06-08 05:04:06 +00:00
|
|
|
self.global_style = u''
|
2009-05-18 19:04:25 +00:00
|
|
|
|
2009-06-05 18:53:50 +00:00
|
|
|
def update_display(self, screen_number):
|
|
|
|
"""
|
|
|
|
Updates the render manager's information about the current screen.
|
2009-07-10 13:16:15 +00:00
|
|
|
|
|
|
|
``screen_number``
|
|
|
|
The updated index of the output/display screen.
|
2009-06-05 18:53:50 +00:00
|
|
|
"""
|
2009-06-10 15:54:46 +00:00
|
|
|
log.debug(u'Update Display')
|
2009-06-05 18:53:50 +00:00
|
|
|
if self.current_display != screen_number:
|
|
|
|
self.current_display = screen_number
|
2009-06-17 05:11:16 +00:00
|
|
|
self.calculate_default(self.screen_list[self.current_display][u'size'])
|
2009-06-05 18:53:50 +00:00
|
|
|
|
2009-07-10 13:16:15 +00:00
|
|
|
def set_global_theme(self, global_theme, global_style=u'Global'):
|
|
|
|
"""
|
|
|
|
Set the global-level theme and the theme level.
|
|
|
|
|
|
|
|
``global_theme``
|
|
|
|
The global-level theme to be set.
|
|
|
|
|
|
|
|
``global_style``
|
|
|
|
Defaults to *"Global"*. The theme level, can be "Global",
|
|
|
|
"Service" or "Song".
|
|
|
|
"""
|
2009-05-18 19:04:25 +00:00
|
|
|
self.global_theme = global_theme
|
|
|
|
self.global_style = global_style
|
|
|
|
|
|
|
|
def set_service_theme(self, service_theme):
|
2009-07-10 13:16:15 +00:00
|
|
|
"""
|
|
|
|
Set the service-level theme.
|
|
|
|
|
|
|
|
``service_theme``
|
|
|
|
The service-level theme to be set.
|
|
|
|
"""
|
2009-05-18 19:04:25 +00:00
|
|
|
self.service_theme = service_theme
|
2009-04-25 06:11:15 +00:00
|
|
|
|
2009-04-29 19:07:13 +00:00
|
|
|
def set_override_theme(self, theme):
|
2009-07-10 13:16:15 +00:00
|
|
|
"""
|
|
|
|
Set the appropriate theme depending on the theme level.
|
|
|
|
|
|
|
|
``theme``
|
|
|
|
The name of the song-level theme.
|
|
|
|
"""
|
|
|
|
log.debug(u'set override theme to %s', theme)
|
2009-05-18 19:04:25 +00:00
|
|
|
if self.global_style == u'Global':
|
|
|
|
self.theme = self.global_theme
|
|
|
|
elif self.global_style == u'Service':
|
|
|
|
if self.service_theme == u'':
|
|
|
|
self.theme = self.global_theme
|
|
|
|
else:
|
|
|
|
self.theme = self.service_theme
|
2009-04-29 19:07:13 +00:00
|
|
|
else:
|
2009-05-18 19:04:25 +00:00
|
|
|
if theme is not None:
|
|
|
|
self.theme = theme
|
2009-05-22 05:14:55 +00:00
|
|
|
elif self.global_style == u'Song' or self.global_style == u'Service':
|
2009-05-18 19:04:25 +00:00
|
|
|
if self.service_theme == u'':
|
|
|
|
self.theme = self.global_theme
|
|
|
|
else:
|
|
|
|
self.theme = self.service_theme
|
2009-05-22 05:14:55 +00:00
|
|
|
else:
|
|
|
|
self.theme = self.global_theme
|
2009-07-10 13:16:15 +00:00
|
|
|
if self.theme != self.renderer.theme_name:
|
2009-05-16 10:24:03 +00:00
|
|
|
log.debug(u'theme is now %s', self.theme)
|
|
|
|
self.themedata = self.theme_manager.getThemeData(self.theme)
|
2009-06-05 18:53:50 +00:00
|
|
|
self.calculate_default(self.screen_list[self.current_display][u'size'])
|
2009-05-16 10:24:03 +00:00
|
|
|
self.renderer.set_theme(self.themedata)
|
|
|
|
self.build_text_rectangle(self.themedata)
|
2009-04-30 21:02:28 +00:00
|
|
|
|
|
|
|
def build_text_rectangle(self, theme):
|
2009-07-10 13:16:15 +00:00
|
|
|
"""
|
|
|
|
Builds a text block using the settings in ``theme``.
|
2009-08-31 06:53:55 +00:00
|
|
|
One is needed per slide
|
2009-07-10 13:16:15 +00:00
|
|
|
|
|
|
|
``theme``
|
|
|
|
The theme to build a text block for.
|
|
|
|
"""
|
|
|
|
log.debug(u'build_text_rectangle')
|
2009-04-30 21:02:28 +00:00
|
|
|
main_rect = None
|
|
|
|
footer_rect = None
|
|
|
|
if theme.font_main_override == False:
|
2009-09-02 01:44:09 +00:00
|
|
|
main_rect = QtCore.QRect(10, 0, self.width - 1,
|
|
|
|
self.footer_start - 20)
|
2009-04-30 21:02:28 +00:00
|
|
|
else:
|
2009-09-02 01:44:09 +00:00
|
|
|
main_rect = QtCore.QRect(int(theme.font_main_x),
|
|
|
|
int(theme.font_main_y), int(theme.font_main_width)-1,
|
|
|
|
int(theme.font_main_height) - 1)
|
2009-04-30 21:02:28 +00:00
|
|
|
if theme.font_footer_override == False:
|
2009-09-02 01:44:09 +00:00
|
|
|
footer_rect = QtCore.QRect(10,self.footer_start, self.width - 1,
|
|
|
|
self.height-self.footer_start)
|
2009-04-30 21:02:28 +00:00
|
|
|
else:
|
2009-09-02 01:44:09 +00:00
|
|
|
footer_rect = QtCore.QRect(int(theme.font_footer_x),
|
|
|
|
int(theme.font_footer_y), int(theme.font_footer_width)-1,
|
|
|
|
int(theme.font_footer_height) - 1)
|
2009-08-31 06:53:55 +00:00
|
|
|
self.renderer.set_text_rectangle(main_rect, footer_rect)
|
2009-04-22 19:46:10 +00:00
|
|
|
|
2009-04-29 19:07:13 +00:00
|
|
|
def generate_preview(self, themedata):
|
2009-07-10 13:16:15 +00:00
|
|
|
"""
|
|
|
|
Generate a preview of a theme.
|
|
|
|
|
|
|
|
``themedata``
|
|
|
|
The theme to generated a preview for.
|
|
|
|
"""
|
2009-05-11 05:09:43 +00:00
|
|
|
log.debug(u'generate preview')
|
2009-05-22 18:30:25 +00:00
|
|
|
self.calculate_default(QtCore.QSize(1024, 768))
|
2009-04-29 19:07:13 +00:00
|
|
|
self.renderer.set_theme(themedata)
|
2009-04-30 21:02:28 +00:00
|
|
|
self.build_text_rectangle(themedata)
|
2009-05-16 10:24:03 +00:00
|
|
|
self.renderer.set_frame_dest(self.width, self.height, True)
|
2009-07-10 13:16:15 +00:00
|
|
|
verse = []
|
|
|
|
verse.append(u'Amazing Grace!')
|
|
|
|
verse.append(u'How sweet the sound')
|
|
|
|
verse.append(u'To save a wretch like me;')
|
|
|
|
verse.append(u'I once was lost but now am found,')
|
|
|
|
verse.append(u'Was blind, but now I see.')
|
|
|
|
footer = []
|
|
|
|
footer.append(u'Amazing Grace (John Newton)' )
|
|
|
|
footer.append(u'Public Domain')
|
|
|
|
footer.append(u'CCLI xxx')
|
|
|
|
return self.renderer.generate_frame_from_lines(verse, footer)
|
2009-05-16 10:24:03 +00:00
|
|
|
|
2009-06-01 17:50:37 +00:00
|
|
|
def format_slide(self, words):
|
2009-07-10 13:16:15 +00:00
|
|
|
"""
|
2009-08-31 06:53:55 +00:00
|
|
|
Calculate how much text can fit on a slide.
|
2009-07-10 13:16:15 +00:00
|
|
|
|
|
|
|
``words``
|
|
|
|
The words to go on the slides.
|
|
|
|
"""
|
2009-05-04 13:48:12 +00:00
|
|
|
log.debug(u'format slide')
|
2009-05-17 08:13:22 +00:00
|
|
|
self.build_text_rectangle(self.themedata)
|
2009-05-17 15:24:02 +00:00
|
|
|
return self.renderer.format_slide(words, False)
|
2009-04-25 06:11:15 +00:00
|
|
|
|
2009-07-10 13:16:15 +00:00
|
|
|
def generate_slide(self, main_text, footer_text):
|
|
|
|
"""
|
|
|
|
Generate the actual slide image.
|
|
|
|
|
|
|
|
``main_text``
|
|
|
|
The text for the main area of the slide.
|
|
|
|
|
|
|
|
``footer_text``
|
|
|
|
The text for the slide footer.
|
|
|
|
"""
|
2009-05-04 13:48:12 +00:00
|
|
|
log.debug(u'generate slide')
|
2009-05-17 08:13:22 +00:00
|
|
|
self.build_text_rectangle(self.themedata)
|
2009-05-16 10:24:03 +00:00
|
|
|
self.renderer.set_frame_dest(self.width, self.height)
|
2009-06-01 17:50:37 +00:00
|
|
|
return self.renderer.generate_frame_from_lines(main_text, footer_text)
|
2009-04-22 19:46:10 +00:00
|
|
|
|
2009-09-03 04:41:30 +00:00
|
|
|
def resize_image(self, image, width = 0, height = 0):
|
2009-07-10 13:16:15 +00:00
|
|
|
"""
|
|
|
|
Resize an image to fit on the current screen.
|
|
|
|
|
|
|
|
``image``
|
|
|
|
The image to resize.
|
|
|
|
"""
|
2009-06-20 10:44:12 +00:00
|
|
|
preview = QtGui.QImage(image)
|
2009-09-03 04:41:30 +00:00
|
|
|
if width == 0:
|
|
|
|
w = self.width
|
|
|
|
h = self.height
|
|
|
|
else:
|
|
|
|
w = width
|
|
|
|
h = height
|
2009-09-02 01:44:09 +00:00
|
|
|
preview = preview.scaled(w, h, QtCore.Qt.KeepAspectRatio,
|
|
|
|
QtCore.Qt.SmoothTransformation)
|
2009-06-20 10:44:12 +00:00
|
|
|
realw = preview.width();
|
|
|
|
realh = preview.height()
|
|
|
|
# and move it to the centre of the preview space
|
|
|
|
newImage = QtGui.QImage(w, h, QtGui.QImage.Format_ARGB32_Premultiplied)
|
2009-09-17 17:05:42 +00:00
|
|
|
newImage.fill(QtCore.Qt.black)
|
2009-06-20 10:44:12 +00:00
|
|
|
painter = QtGui.QPainter(newImage)
|
2009-09-02 01:44:09 +00:00
|
|
|
painter.drawImage((w-realw) / 2, (h-realh) / 2, preview)
|
2009-06-20 10:44:12 +00:00
|
|
|
return newImage
|
|
|
|
|
2009-04-22 19:46:10 +00:00
|
|
|
def calculate_default(self, screen):
|
2009-07-10 13:16:15 +00:00
|
|
|
"""
|
|
|
|
Calculate the default dimentions of the screen.
|
|
|
|
|
|
|
|
``screen``
|
|
|
|
The QWidget instance of the screen.
|
|
|
|
"""
|
|
|
|
log.debug(u'calculate default %s', screen)
|
2009-08-31 06:53:55 +00:00
|
|
|
#size fixed so reflects the preview size.
|
2009-07-02 19:04:50 +00:00
|
|
|
if self.current_display == 0:
|
|
|
|
self.width = 1024
|
|
|
|
self.height = 768
|
|
|
|
else:
|
|
|
|
self.width = screen.width()
|
|
|
|
self.height = screen.height()
|
2009-09-20 07:12:47 +00:00
|
|
|
self.screen_ratio = float(self.height) / float(self.width)
|
|
|
|
log.debug(u'calculate default %d, %d, %f', self.width, self.height, self.screen_ratio )
|
2009-06-17 05:11:16 +00:00
|
|
|
# 90% is start of footer
|
2009-06-20 11:23:34 +00:00
|
|
|
self.footer_start = int(self.height * 0.90)
|