This commit is contained in:
Raoul Snyman 2010-08-28 21:49:12 +02:00
commit 7fcd2512ce
33 changed files with 1549 additions and 1425 deletions

View File

@ -34,7 +34,8 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver
from openlp.core.resources import qInitResources
from openlp.core.ui import MainWindow, SplashScreen, ScreenList
from openlp.core.ui.mainwindow import MainWindow
from openlp.core.ui import SplashScreen, ScreenList
from openlp.core.utils import AppLocation, LanguageManager, VersionThread
log = logging.getLogger()

View File

@ -35,6 +35,67 @@ from PyQt4 import QtCore, QtGui
log = logging.getLogger(__name__)
# TODO make external and configurable in alpha 4 via a settings dialog
html_expands = []
html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \
u'start html':u'<font color=red>', \
u'end tag':u'{/r}', u'end html':u'</font>', \
u'protected':False})
html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \
u'start html':u'<font color=black>', \
u'end tag':u'{/b}', u'end html':u'</font>', \
u'protected':False})
html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \
u'start html':u'<font color=blue>', \
u'end tag':u'{/bl}', u'end html':u'</font>', \
u'protected':False})
html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \
u'start html':u'<font color=yellow>', \
u'end tag':u'{/y}', u'end html':u'</font>', \
u'protected':False})
html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \
u'start html':u'<font color=green>', \
u'end tag':u'{/g}', u'end html':u'</font>', \
u'protected':False})
html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \
u'start html':u'<font color=#CC33CC>', \
u'end tag':u'{/pk}', u'end html':u'</font>', \
u'protected':False})
html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \
u'start html':u'<font color=#CC0033>', \
u'end tag':u'{/o}', u'end html':u'</font>', \
u'protected':False})
html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \
u'start html':u'<font color=#9900FF>', \
u'end tag':u'{/pp}', u'end html':u'</font>', \
u'protected':False})
html_expands.append({u'desc':u'White', u'start tag':u'{w}', \
u'start html':u'<font color=white>', \
u'end tag':u'{/w}', u'end html':u'</font>', \
u'protected':False})
html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \
u'start html':u'<sup>', \
u'end tag':u'{/su}', u'end html':u'</sup>', \
u'protected':True})
html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}', \
u'start html':u'<sub>', \
u'end tag':u'{/sb}', u'end html':u'</sub>', \
u'protected':True})
html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}', \
u'start html':u'<p>', \
u'end tag':u'{/p}', u'end html':u'</p>', \
u'protected':True})
html_expands.append({u'desc':u'Bold', u'start tag':u'{st}', \
u'start html':u'<strong>', \
u'end tag':u'{/st}', \
u'end html':u'</strong>', \
u'protected':True})
html_expands.append({u'desc':u'Italics', u'start tag':u'{it}', \
u'start html':u'<em>', \
u'end tag':u'{/it}', u'end html':u'</em>', \
u'protected':True})
def translate(context, text, comment=None):
"""
A special shortcut method to wrap around the Qt4 translation functions.
@ -166,15 +227,46 @@ def context_menu_separator(base):
action.setSeparator(True)
return action
def resize_image(image, width, height):
def image_to_byte(image):
"""
Resize an image to fit on the current screen for the web and returns
it as a byte stream.
``image``
The image to converted.
"""
byte_array = QtCore.QByteArray()
# use buffer to store pixmap into byteArray
buffie = QtCore.QBuffer(byte_array)
buffie.open(QtCore.QIODevice.WriteOnly)
if isinstance(image, QtGui.QImage):
pixmap = QtGui.QPixmap.fromImage(image)
else:
pixmap = QtGui.QPixmap(image)
pixmap.save(buffie, "PNG")
# convert to base64 encoding so does not get missed!
return byte_array.toBase64()
def resize_image(image, width, height, background=QtCore.Qt.black):
"""
Resize an image to fit on the current screen.
``image``
The image to resize.
``width``
The new image width.
``height``
The new image height.
``background``
The background colour defaults to black.
"""
preview = QtGui.QImage(image)
if not preview.isNull():
# Only resize if different size
if preview.width() == width and preview.height == height:
return preview
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
@ -184,7 +276,7 @@ def resize_image(image, width, height):
# and move it to the centre of the preview space
new_image = QtGui.QImage(width, height,
QtGui.QImage.Format_ARGB32_Premultiplied)
new_image.fill(QtCore.Qt.black)
new_image.fill(background)
painter = QtGui.QPainter(new_image)
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
return new_image
@ -205,6 +297,25 @@ def check_item_selected(list_widget, message):
return False
return True
def clean_tags(text):
"""
Remove Tags from text for display
"""
text = text.replace(u'<br>', u'\n')
for tag in html_expands:
text = text.replace(tag[u'start tag'], u'')
text = text.replace(tag[u'end tag'], u'')
return text
def expand_tags(text):
"""
Expand tags HTML for display
"""
for tag in html_expands:
text = text.replace(tag[u'start tag'], tag[u'start html'])
text = text.replace(tag[u'end tag'], tag[u'end html'])
return text
from eventreceiver import Receiver
from settingsmanager import SettingsManager
from plugin import PluginStatus, Plugin
@ -213,6 +324,7 @@ from settingstab import SettingsTab
from serviceitem import ServiceItem
from serviceitem import ServiceItemType
from serviceitem import ItemCapabilities
from htmlbuilder import build_html
from toolbar import OpenLPToolbar
from dockwidget import OpenLPDockWidget
from theme import ThemeLevel, ThemeXML

View File

@ -0,0 +1,445 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, 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 #
###############################################################################
from openlp.core.lib import image_to_byte
HTMLSRC = u"""
<html>
<head>
<title>OpenLP Display</title>
<style>
*{
margin: 0;
padding: 0;
border: 0;
}
body {
background-color: black;
}
.dim {
position: absolute;
left: 0px;
top: 0px;
width: %spx;
height: %spx;
}
#black {
z-index:8;
background-color: black;
display: none;
}
#video {
z-index:2;
}
#alert {
position: absolute;
left: 0px;
top: 0px;
z-index:10;
%s
}
#footer {
position: absolute;
z-index:5;
%s
}
/* lyric css */
%s
</style>
<script language="javascript">
var timer = null;
var transition = %s;
function show_video(state, path, volume, loop){
var vid = document.getElementById('video');
if(path != null)
vid.src = path;
if(loop != null){
if(loop)
vid.loop = 'loop';
else
vid.loop = '';
}
if(volume != null){
vid.volume = volume;
}
switch(state){
case 'play':
vid.play();
vid.style.display = 'block';
break;
case 'pause':
vid.pause();
vid.style.display = 'block';
break;
case 'stop':
vid.pause();
vid.style.display = 'none';
break;
case 'close':
vid.pause();
vid.style.display = 'none';
vid.src = '';
break;
}
}
function show_image(src){
var img = document.getElementById('image');
img.src = src;
if(src == '')
img.style.display = 'none';
else
img.style.display = 'block';
}
function show_blank(state){
var black = 'none';
var lyrics = '';
var pause = false;
switch(state){
case 'theme':
lyrics = 'hidden';
pause = true;
break;
case 'black':
black = 'block';
pause = true;
break;
case 'desktop':
pause = true;
break;
}
document.getElementById('black').style.display = black;
document.getElementById('lyricsmain').style.visibility = lyrics;
document.getElementById('lyricsoutline').style.visibility = lyrics;
document.getElementById('lyricsshadow').style.visibility = lyrics;
document.getElementById('footer').style.visibility = lyrics;
var vid = document.getElementById('video');
if(vid.src != ''){
if(pause)
vid.pause();
else
vid.play();
}
}
function show_alert(alerttext, position){
var text = document.getElementById('alert');
text.innerHTML = alerttext;
if(alerttext == '') {
text.style.visibility = 'hidden';
return 0;
}
if(position == ''){
position = window.getComputedStyle(text, '').verticalAlign;
}
switch(position)
{
case 'top':
text.style.top = '0px';
break;
case 'middle':
text.style.top = ((window.innerHeight - text.clientHeight) / 2)
+ 'px';
break;
case 'bottom':
text.style.top = (window.innerHeight - text.clientHeight)
+ 'px';
break;
}
text.style.visibility = 'visible';
return text.clientHeight;
}
function show_footer(footertext){
document.getElementById('footer').innerHTML = footertext;
}
function show_text(newtext){
var text1 = document.getElementById('lyricsmain');
var texto1 = document.getElementById('lyricsoutline');
var texts1 = document.getElementById('lyricsshadow');
if(!transition){
text1.innerHTML = newtext;
texto1.innerHTML = newtext;
texts1.innerHTML = newtext;
return;
}
var text2 = document.getElementById('lyricsmain2');
var texto2 = document.getElementById('lyricsoutline2');
var texts2 = document.getElementById('lyricsshadow2');
if((text2.style.opacity == '')||(parseFloat(text2.style.opacity) < 0.5))
{
text2.innerHTML = text1.innerHTML;
text2.style.opacity = text1.style.opacity;
texto2.innerHTML = text1.innerHTML;
texto2.style.opacity = text1.style.opacity;
texts2.innerHTML = text1.innerHTML;
texts2.style.opacity = text1.style.opacity;
}
text1.style.opacity = 0;
text1.innerHTML = newtext;
texto1.style.opacity = 0;
texto1.innerHTML = newtext;
texts1.style.opacity = 0;
texts1.innerHTML = newtext;
// For performance reasons, we'll not animate the shadow for now
texts2.style.opacity = 0;
if(timer != null)
clearTimeout(timer);
timer = setTimeout('text_fade()', 50);
}
function text_fade(){
var text1 = document.getElementById('lyricsmain');
var texto1 = document.getElementById('lyricsoutline');
var texts1 = document.getElementById('lyricsshadow');
var text2 = document.getElementById('lyricsmain2');
var texto2 = document.getElementById('lyricsoutline2');
var texts2 = document.getElementById('lyricsshadow2');
if(parseFloat(text1.style.opacity) < 1){
text1.style.opacity = parseFloat(text1.style.opacity) + 0.1;
texto1.style.opacity = parseFloat(texto1.style.opacity) + 0.1;
// Don't animate shadow (performance)
//texts1.style.opacity = parseFloat(texts1.style.opacity) + 0.1;
}
if(parseFloat(text2.style.opacity) > 0){
text2.style.opacity = parseFloat(text2.style.opacity) - 0.1;
texto2.style.opacity = parseFloat(texto2.style.opacity) - 0.1;
// Don't animate shadow (performance)
//texts2.style.opacity = parseFloat(texts2.style.opacity) - 0.1;
}
if((parseFloat(text1.style.opacity) < 1) ||
(parseFloat(text2.style.opacity) > 0)){
t = setTimeout('text_fade()', 50);
} else {
text1.style.opacity = 1;
texto1.style.opacity = 1;
texts1.style.opacity = 1;
text2.style.opacity = 0;
texto2.style.opacity = 0;
texts2.style.opacity = 0;
}
}
function show_text_complete(){
return (document.getElementById('lyricsmain').style.opacity == 1);
}
</script>
</head>
<body>
<!--
Using tables, rather than div's to make use of the vertical-align style that
doesn't work on div's. This avoids the need to do positioning manually which
could get messy when changing verses esp. with transitions
Would prefer to use a single table and make use of -webkit-text-fill-color
-webkit-text-stroke and text-shadow styles, but they have problems working/
co-operating in qwebkit. https://bugs.webkit.org/show_bug.cgi?id=43187
Therefore one table for text, one for outline and one for shadow.
-->
<table class="lyricstable lyricscommon">
<tr><td id="lyricsmain" class="lyrics"></td></tr>
</table>
<table class="lyricsoutlinetable lyricscommon">
<tr><td id="lyricsoutline" class="lyricsoutline lyrics"></td></tr>
</table>
<table class="lyricsshadowtable lyricscommon">
<tr><td id="lyricsshadow" class="lyricsshadow lyrics"></td></tr>
</table>
<table class="lyricstable lyricscommon">
<tr><td id="lyricsmain2" class="lyrics"></td></tr>
</table>
<table class="lyricsoutlinetable lyricscommon">
<tr><td id="lyricsoutline2" class="lyricsoutline lyrics"></td></tr>
</table>
<table class="lyricsshadowtable lyricscommon">
<tr><td id="lyricsshadow2" class="lyricsshadow lyrics"></td></tr>
</table>
<div id="alert" style="visibility:hidden;"></div>
<div id="footer" class="footer"></div>
<video class="dim" id="video"></video>
<div class="dim" id="black"></div>
<img class="dim" id="image" src="%s" />
</body>
</html>
"""
def build_html(item, screen, alert):
"""
Build the full web paged structure for display
`item`
Service Item to be displayed
`screen`
Current display information
`alert`
Alert display display information
"""
width = screen[u'size'].width()
height = screen[u'size'].height()
theme = item.themedata
if item.bg_frame:
image = u'data:image/png;base64,%s' % image_to_byte(item.bg_frame)
else:
image = u''
html = HTMLSRC % (width, height,
build_alert(alert, width),
build_footer(item),
build_lyrics(item),
u'true' if theme and theme.display_slideTransition \
else u'false',
image)
return html
def build_lyrics(item):
"""
Build the video display div
`item`
Service Item containing theme and location information
"""
style = """
.lyricscommon { position: absolute; %s }
.lyricstable { z-index:4; %s }
.lyricsoutlinetable { z-index:3; %s }
.lyricsshadowtable { z-index:2; %s }
.lyrics { %s }
.lyricsoutline { %s }
.lyricsshadow { %s }
"""
theme = item.themedata
lyricscommon = u''
lyricstable = u''
outlinetable = u''
shadowtable = u''
lyrics = u''
outline = u'display: none;'
shadow = u'display: none;'
if theme:
lyricscommon = u'width: %spx; height: %spx; word-wrap: break-word; ' \
u'font-family: %s; font-size: %spx; color: %s; line-height: %d%%;' % \
(item.main.width(), item.main.height(),
theme.font_main_name, theme.font_main_proportion,
theme.font_main_color, 100 + int(theme.font_main_line_adjustment))
lyricstable = u'left: %spx; top: %spx;' % \
(item.main.x(), item.main.y())
outlinetable = u'left: %spx; top: %spx;' % \
(item.main.x(), item.main.y())
shadowtable = u'left: %spx; top: %spx;' % \
(item.main.x() + float(theme.display_shadow_size),
item.main.y() + float(theme.display_shadow_size))
align = u''
if theme.display_horizontalAlign == 2:
align = u'text-align:center;'
elif theme.display_horizontalAlign == 1:
align = u'text-align:right;'
else:
align = u'text-align:left;'
if theme.display_verticalAlign == 2:
valign = u'vertical-align:bottom;'
elif theme.display_verticalAlign == 1:
valign = u'vertical-align:middle;'
else:
valign = u'vertical-align:top;'
lyrics = u'%s %s' % (align, valign)
if theme.display_outline:
lyricscommon += u' letter-spacing: 1px;'
outline = u'-webkit-text-stroke: %sem %s; ' % \
(float(theme.display_outline_size) / 16,
theme.display_outline_color)
if theme.display_shadow:
shadow = u'-webkit-text-stroke: %sem %s; ' \
u'-webkit-text-fill-color: %s; '% \
(float(theme.display_outline_size) / 16,
theme.display_shadow_color, theme.display_shadow_color)
else:
if theme.display_shadow:
shadow = u'color: %s;' % (theme.display_shadow_color)
lyrics_html = style % (lyricscommon, lyricstable, outlinetable,
shadowtable, lyrics, outline, shadow)
return lyrics_html
def build_footer(item):
"""
Build the display of the item footer
`item`
Service Item to be processed.
"""
style = """
left: %spx;
top: %spx;
width: %spx;
height: %spx;
font-family: %s;
font-size: %spx;
color: %s;
text-align: %s;
"""
theme = item.themedata
if not theme:
return u''
if theme.display_horizontalAlign == 2:
align = u'center'
elif theme.display_horizontalAlign == 1:
align = u'right'
else:
align = u'left'
lyrics_html = style % (item.footer.x(), item.footer.y(),
item.footer.width(), item.footer.height(), theme.font_footer_name,
theme.font_footer_proportion, theme.font_footer_color, align)
return lyrics_html
def build_alert(alertTab, width):
"""
Build the display of the footer
`alertTab`
Details from the Alert tab for fonts etc
"""
style = """
width: %s;
vertical-align: %s;
font-family: %s;
font-size: %spx;
color: %s;
background-color: %s;
"""
if not alertTab:
return u''
align = u''
if alertTab.location == 2:
align = u'bottom'
elif alertTab.location == 1:
align = u'middle'
else:
align = u'top'
alert = style % (width, align, alertTab.font_face, alertTab.font_size,
alertTab.font_color, alertTab.bg_color)
return alert

View File

@ -131,7 +131,6 @@ class Plugin(QtCore.QObject):
self.serviceManager = plugin_helpers[u'service']
self.settingsForm = plugin_helpers[u'settings form']
self.mediadock = plugin_helpers[u'toolbox']
self.displayManager = plugin_helpers[u'displaymanager']
self.pluginManager = plugin_helpers[u'pluginmanager']
self.formparent = plugin_helpers[u'formparent']
QtCore.QObject.connect(Receiver.get_receiver(),

View File

@ -31,7 +31,7 @@ import logging
from PyQt4 import QtGui, QtCore
from openlp.core.lib import resize_image
from openlp.core.lib import resize_image, expand_tags
log = logging.getLogger(__name__)
@ -80,7 +80,6 @@ class Renderer(object):
self.bg_image = None
self._bg_image_filename = None
self.theme_name = theme.theme_name
self._set_theme_font()
if theme.background_type == u'image':
if theme.background_filename:
self.set_bg_image(theme.background_filename)
@ -99,6 +98,20 @@ class Renderer(object):
self.frame.width(),
self.frame.height())
def set_text_rectangle(self, rect_main, rect_footer):
"""
Sets the rectangle within which text should be rendered.
``rect_main``
The main text block.
``rect_footer``
The footer text block.
"""
log.debug(u'set_text_rectangle %s , %s' % (rect_main, rect_footer))
self._rect = rect_main
self._rect_footer = rect_footer
def set_frame_dest(self, frame_width, frame_height, preview=False):
"""
Set the size of the slide.
@ -118,26 +131,24 @@ class Renderer(object):
frame_height)
self.frame = QtGui.QImage(frame_width, frame_height,
QtGui.QImage.Format_ARGB32_Premultiplied)
self.frame_opaque = QtGui.QImage(frame_width, frame_height,
QtGui.QImage.Format_ARGB32_Premultiplied)
if self._bg_image_filename and not self.bg_image:
self.bg_image = resize_image(self._bg_image_filename,
self.frame.width(), self.frame.height())
if self.bg_frame is None:
self._generate_background_frame()
def format_slide(self, words, footer):
def format_slide(self, words, line_break):
"""
Figure out how much text can appear on a slide, using the current
theme settings.
``words``
The words to be fitted on the slide.
``footer``
The footer of the slide.
"""
log.debug(u'format_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 = []
@ -145,124 +156,43 @@ class Renderer(object):
lines = verse.split(u'\n')
for line in lines:
text.append(line)
split_text = self.pre_render_text(text)
log.debug(u'format_slide - End')
return split_text
def pre_render_text(self, text):
metrics = QtGui.QFontMetrics(self.main_font)
#work out line width
line_width = self._rect.width()
#number of lines on a page - adjust for rounding up.
line_height = metrics.height()
if self._theme.display_shadow:
line_height += int(self._theme.display_shadow_size)
if self._theme.display_outline:
# pixels top/bottom
line_height += 2 * int(self._theme.display_outline_size)
page_length = int(self._rect.height() / line_height )
#Average number of characters in line
ave_line_width = line_width / metrics.averageCharWidth()
#Maximum size of a character
max_char_width = metrics.maxWidth()
#Max characters pre line based on min size of a character
char_per_line = line_width / metrics.width(u'i')
log.debug(u'Page Length area height %s , metrics %s , lines %s' %
(int(self._rect.height()), metrics.height(), page_length ))
split_pages = []
page = []
split_lines = []
count = 0
for line in text:
#Must be a blank line so keep it.
if len(line) == 0:
line = u' '
while line:
pos = char_per_line
split_text = line[:pos]
#line needs splitting
if metrics.width(split_text, -1) > line_width:
#We have no spaces
if split_text.find(u' ') == -1:
#Move back 1 char at a time till it fits
while metrics.width(split_text, -1) > line_width:
split_text = split_text[:-1]
pos = len(split_text)
else:
#We have spaces so split at previous one
while metrics.width(split_text, -1) > line_width:
pos = split_text.rfind(u' ')
#no more spaces and we are still too long
if pos == -1:
while \
metrics.width(split_text, -1) > line_width:
split_text = split_text[:-1]
pos = len(split_text)
else:
split_text = line[:pos]
split_lines.append(split_text)
line = line[pos:].lstrip()
#if we have more text add up to 10 spaces on the front.
if line and self._theme.font_main_indentation > 0:
line = u'%s%s' % \
(u' '[:int(self._theme.font_main_indentation)],
line)
#Text fits in a line now
for count, line in enumerate(split_lines):
page.append(line)
#last but one line and only 2 lines to go or end of page
if (len(page) == page_length - 1 and
len(split_lines) - 3 == count) or \
len(page) == page_length:
split_pages.append(page)
page = []
if page and page != u' ':
split_pages.append(page)
return split_pages
def set_text_rectangle(self, rect_main, rect_footer):
"""
Sets the rectangle within which text should be rendered.
``rect_main``
The main text block.
``rect_footer``
The footer text block.
"""
log.debug(u'set_text_rectangle %s , %s' % (rect_main, rect_footer))
self._rect = rect_main
self._rect_footer = rect_footer
def generate_frame_from_lines(self, lines, footer_lines=None):
"""
Render a set of lines according to the theme, and return the block
dimensions.
``lines``
The lines to be rendered.
``footer_lines``
Defaults to *None*. The footer to render.
"""
log.debug(u'generate_frame_from_lines - Start')
bbox = self._render_lines_unaligned(lines, False)
if footer_lines:
bbox1 = self._render_lines_unaligned(footer_lines, True)
# reset the frame. first time do not worry about what you paint on.
self.frame = QtGui.QImage(self.bg_frame)
if self._theme.display_slideTransition:
self.frame_opaque = QtGui.QImage(self.bg_frame)
x, y = self._correct_alignment(self._rect, bbox)
bbox = self._render_lines_unaligned(lines, False, (x, y), True)
if footer_lines:
bbox = self._render_lines_unaligned(footer_lines, True,
(self._rect_footer.left(), self._rect_footer.top()), True)
log.debug(u'generate_frame_from_lines - Finish')
if self._theme.display_slideTransition:
return {u'main':self.frame, u'trans':self.frame_opaque}
doc = QtGui.QTextDocument()
doc.setPageSize(QtCore.QSizeF(self._rect.width(), self._rect.height()))
df = doc.defaultFont()
df.setPixelSize(self._theme.font_main_proportion)
df.setFamily(self._theme.font_main_name)
main_weight = 50
if self._theme.font_main_weight == u'Bold':
main_weight = 75
df.setWeight(main_weight)
doc.setDefaultFont(df)
layout = doc.documentLayout()
formatted = []
if self._theme.font_main_weight == u'Bold' and \
self._theme.font_main_italics:
shell = u'{p}{st}{it}%s{/it}{/st}{/p}'
elif self._theme.font_main_weight == u'Bold' and \
not self._theme.font_main_italics:
shell = u'{p}{st}%s{/st}{/p}'
elif self._theme.font_main_italics:
shell = u'{p}{it}%s{/it}{/p}'
else:
return {u'main':self.frame, u'trans':None}
shell = u'{p}%s{/p}'
temp_text = u''
old_html_text = u''
for line in text:
# mark line ends
temp_text = temp_text + line + line_end
html_text = shell % expand_tags(temp_text)
doc.setHtml(html_text)
# Text too long so gone to next mage
if layout.pageCount() != 1:
formatted.append(shell % old_html_text)
temp_text = line
old_html_text = temp_text
formatted.append(shell % old_html_text)
log.debug(u'format_slide - End')
return formatted
def _generate_background_frame(self):
"""
@ -270,327 +200,47 @@ class Renderer(object):
Results are cached for performance reasons.
"""
assert(self._theme)
if self._theme.background_mode == u'transparent':
self.bg_frame = \
QtGui.QPixmap(self.frame.width(), self.frame.height())
self.bg_frame.fill(QtCore.Qt.transparent)
else:
self.bg_frame = QtGui.QImage(self.frame.width(),
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
self.bg_frame = QtGui.QImage(self.frame.width(),
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
log.debug(u'render background %s start', self._theme.background_type)
painter = QtGui.QPainter()
painter.begin(self.bg_frame)
if self._theme.background_mode == u'transparent':
painter.fillRect(self.frame.rect(), QtCore.Qt.transparent)
else:
if self._theme.background_type == u'solid':
painter.fillRect(self.frame.rect(),
QtGui.QColor(self._theme.background_color))
elif self._theme.background_type == u'gradient':
# gradient
gradient = None
if self._theme.background_direction == u'horizontal':
w = int(self.frame.width()) / 2
# vertical
gradient = QtGui.QLinearGradient(w, 0, w,
self.frame.height())
elif self._theme.background_direction == u'vertical':
h = int(self.frame.height()) / 2
# Horizontal
gradient = QtGui.QLinearGradient(0, h, self.frame.width(),
h)
else:
w = int(self.frame.width()) / 2
h = int(self.frame.height()) / 2
# Circular
gradient = QtGui.QRadialGradient(w, h, w)
gradient.setColorAt(0,
QtGui.QColor(self._theme.background_startColor))
gradient.setColorAt(1,
QtGui.QColor(self._theme.background_endColor))
painter.setBrush(QtGui.QBrush(gradient))
rect_path = QtGui.QPainterPath()
max_x = self.frame.width()
max_y = self.frame.height()
rect_path.moveTo(0, 0)
rect_path.lineTo(0, max_y)
rect_path.lineTo(max_x, max_y)
rect_path.lineTo(max_x, 0)
rect_path.closeSubpath()
painter.drawPath(rect_path)
elif self._theme.background_type == u'image':
# image
painter.fillRect(self.frame.rect(), QtCore.Qt.black)
if self.bg_image:
painter.drawImage(0, 0, self.bg_image)
painter.end()
log.debug(u'render background End')
def _correct_alignment(self, rect, bbox):
"""
Corrects the vertical alignment of text.
``rect``
The block dimentions.
``bbox``
Footer dimensions?
"""
x = rect.left()
if self._theme.display_verticalAlign == 0:
# top align
y = rect.top()
elif self._theme.display_verticalAlign == 2:
# bottom align
y = rect.bottom() - bbox.height()
elif self._theme.display_verticalAlign == 1:
# centre align
y = rect.top() + (rect.height() - bbox.height()) / 2
else:
log.error(u'Invalid value for theme.VerticalAlign:%s',
self._theme.display_verticalAlign)
return x, y
def _render_lines_unaligned(self, lines, footer, tlcorner=(0, 0),
live=False):
"""
Given a list of lines to render, render each one in turn (using the
``_render_single_line`` fn - which may result in going off the bottom).
They are expected to be pre-arranged to less than a screenful (eg. by
using split_set_of_lines).
Returns the bounding box of the text as QRect.
``lines``
The lines of text to render.
``footer``
The slide footer.
``tlcorner``
Defaults to *``(0, 0)``*. Co-ordinates of the top left corner.
``live``
Defaults to *False*. Whether or not this is a live screen.
"""
x, y = tlcorner
brx = x
bry = y
for line in lines:
# render after current bottom, but at original left edge
# keep track of right edge to see which is biggest
(thisx, bry) = self._render_and_wrap_single_line(line, footer,
(x, bry), live)
if (thisx > brx):
brx = thisx
retval = QtCore.QRect(x, y, brx - x, bry - y)
if self._debug:
painter = QtGui.QPainter()
painter.begin(self.frame)
painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 255)))
painter.drawRect(retval)
painter.end()
return retval
def _render_and_wrap_single_line(self, line, footer, tlcorner=(0, 0),
live=False):
"""
Render a single line of words onto the DC, top left corner specified.
If the line is too wide for the context, it wraps, but right-aligns
the surplus words in the manner of song lyrics.
Returns the bottom-right corner (of what was rendered) as a tuple(x, y).
``line``
Line of text to be rendered.
``footer``
The footer of the slide.
``tlcorner``
Defaults to *``(0, 0)``*. The top left corner.
``live``
Defaults to *False*. Whether or not this is a live screen.
"""
x, y = tlcorner
maxx = self._rect.width()
maxy = self._rect.height()
lines = []
lines.append(line)
startx = x
starty = y
rightextent = None
self.painter = QtGui.QPainter()
self.painter.begin(self.frame)
self.painter.setRenderHint(QtGui.QPainter.Antialiasing)
if self._theme.display_slideTransition:
self.painter2 = QtGui.QPainter()
self.painter2.begin(self.frame_opaque)
self.painter2.setRenderHint(QtGui.QPainter.Antialiasing)
self.painter2.setOpacity(0.7)
# dont allow alignment messing with footers
if footer:
align = 0
display_shadow_size = self._display_shadow_size_footer
display_outline_size = self._display_outline_size_footer
else:
align = self._theme.display_horizontalAlign
display_shadow_size = int(self._theme.display_shadow_size)
display_outline_size = int(self._theme.display_outline_size)
for linenum in range(len(lines)):
line = lines[linenum]
#find out how wide line is
w, h = self._get_extent_and_render(line, footer, tlcorner=(x, y),
draw=False)
if self._theme.display_shadow:
w += display_shadow_size
h += display_shadow_size
if self._theme.display_outline:
# pixels either side
w += 2 * display_outline_size
# pixels top/bottom
h += 2 * display_outline_size
if align == 0: # left align
rightextent = x + w
# shift right from last line's rh edge
if self._theme.display_wrapStyle == 1 and linenum != 0:
rightextent = self._first_line_right_extent
if rightextent > maxx:
rightextent = maxx
x = rightextent - w
# right align
elif align == 1:
rightextent = maxx
x = maxx - w
# centre
elif align == 2:
x = (maxx - w) / 2
rightextent = x + w
if live:
# now draw the text, and any outlines/shadows
if self._theme.display_shadow:
self._get_extent_and_render(line, footer,
tlcorner=(x + display_shadow_size,
y + display_shadow_size),
draw=True, color=self._theme.display_shadow_color)
self._get_extent_and_render(line, footer, tlcorner=(x, y),
draw=True, outline_size=display_outline_size)
y += h
if linenum == 0:
self._first_line_right_extent = rightextent
# draw a box around the text - debug only
if self._debug:
self.painter.setPen(QtGui.QPen(QtGui.QColor(0, 255, 0)))
self.painter.drawRect(startx, starty, rightextent-startx, y-starty)
brcorner = (rightextent, y)
self.painter.end()
if self._theme.display_slideTransition:
self.painter2.end()
return brcorner
def _set_theme_font(self):
"""
Set the fonts from the current theme settings.
"""
footer_weight = 50
if self._theme.font_footer_weight == u'Bold':
footer_weight = 75
#TODO Add myfont.setPixelSize((screen_height / 100) * font_size)
self.footer_font = QtGui.QFont(self._theme.font_footer_name,
self._theme.font_footer_proportion, # size
footer_weight, # weight
self._theme.font_footer_italics) # italic
self.footer_font.setPixelSize(self._theme.font_footer_proportion)
main_weight = 50
if self._theme.font_main_weight == u'Bold':
main_weight = 75
self.main_font = QtGui.QFont(self._theme.font_main_name,
self._theme.font_main_proportion, # size
main_weight, # weight
self._theme.font_main_italics)# italic
self.main_font.setPixelSize(self._theme.font_main_proportion)
def _get_extent_and_render(self, line, footer, tlcorner=(0, 0), draw=False,
color=None, outline_size=0):
"""
Find bounding box of text - as render_single_line. If draw is set,
actually draw the text to the current DC as well return width and
height of text as a tuple (w, h).
``line``
The line of text to render.
``footer``
The footer text.
``tlcorner``
Defaults to *``(0, 0)``*. The top left corner co-ordinates.
``draw``
Defaults to *False*. Draw the text to the current surface.
``color``
Defaults to *None*. The colour to draw with.
"""
# setup defaults
if footer:
font = self.footer_font
else:
font = self.main_font
metrics = QtGui.QFontMetrics(font)
w = metrics.width(line)
if footer:
h = metrics.height()
else:
h = metrics.height() + int(self._theme.font_main_line_adjustment)
if draw:
self.painter.setFont(font)
if color is None:
if footer:
pen = QtGui.QColor(self._theme.font_footer_color)
else:
pen = QtGui.QColor(self._theme.font_main_color)
if self._theme.background_type == u'solid':
painter.fillRect(self.frame.rect(),
QtGui.QColor(self._theme.background_color))
elif self._theme.background_type == u'gradient':
# gradient
gradient = None
if self._theme.background_direction == u'horizontal':
w = int(self.frame.width()) / 2
# vertical
gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
elif self._theme.background_direction == u'vertical':
h = int(self.frame.height()) / 2
# Horizontal
gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
else:
pen = QtGui.QColor(color)
x, y = tlcorner
rowpos = y + metrics.ascent()
if self._theme.display_outline and outline_size != 0 and not footer:
path = QtGui.QPainterPath()
path.addText(QtCore.QPointF(x, rowpos), font, line)
self.painter.setBrush(self.painter.pen().brush())
self.painter.setPen(QtGui.QPen(QtGui.QColor(
self._theme.display_outline_color), outline_size))
self.painter.drawPath(path)
self.painter.setPen(pen)
self.painter.drawText(x, rowpos, line)
if self._theme.display_slideTransition:
# Print 2nd image with 70% weight
if self._theme.display_outline and outline_size != 0 and \
not footer:
path = QtGui.QPainterPath()
path.addText(QtCore.QPointF(x, rowpos), font, line)
self.painter2.setBrush(self.painter2.pen().brush())
self.painter2.setPen(QtGui.QPen(
QtGui.QColor(self._theme.display_outline_color),
outline_size))
self.painter2.drawPath(path)
self.painter2.setFont(font)
self.painter2.setPen(pen)
self.painter2.drawText(x, rowpos, line)
return (w, h)
def snoop_image(self, image, image2=None):
"""
Debugging method to allow images to be viewed.
``image``
An image to save to disk.
``image2``
Defaults to *None*. Another image to save to disk.
"""
image.save(u'renderer.png', u'png')
if image2:
image2.save(u'renderer2.png', u'png')
w = int(self.frame.width()) / 2
h = int(self.frame.height()) / 2
# Circular
gradient = QtGui.QRadialGradient(w, h, w)
gradient.setColorAt(0,
QtGui.QColor(self._theme.background_startColor))
gradient.setColorAt(1,
QtGui.QColor(self._theme.background_endColor))
painter.setBrush(QtGui.QBrush(gradient))
rect_path = QtGui.QPainterPath()
max_x = self.frame.width()
max_y = self.frame.height()
rect_path.moveTo(0, 0)
rect_path.lineTo(0, max_y)
rect_path.lineTo(max_x, max_y)
rect_path.lineTo(max_x, 0)
rect_path.closeSubpath()
painter.drawPath(rect_path)
elif self._theme.background_type == u'image':
# image
painter.fillRect(self.frame.rect(), QtCore.Qt.black)
if self.bg_image:
painter.drawImage(0, 0, self.bg_image)
painter.end()

View File

@ -28,7 +28,8 @@ import logging
from PyQt4 import QtCore
from openlp.core.lib import Renderer, ThemeLevel
from openlp.core.lib import Renderer, ThemeLevel, ServiceItem
from openlp.core.ui import MainDisplay
log = logging.getLogger(__name__)
@ -55,6 +56,8 @@ class RenderManager(object):
"""
log.debug(u'Initilisation started')
self.screens = screens
self.display = MainDisplay(self, screens, False)
self.display.setup()
self.theme_manager = theme_manager
self.renderer = Renderer()
self.calculate_default(self.screens.current[u'size'])
@ -63,6 +66,7 @@ class RenderManager(object):
self.theme_level = u''
self.override_background = None
self.themedata = None
self.alertTab = None
def update_display(self):
"""
@ -70,7 +74,10 @@ class RenderManager(object):
"""
log.debug(u'Update Display')
self.calculate_default(self.screens.current[u'size'])
self.display = MainDisplay(self, self.screens, False)
self.display.setup()
self.renderer.bg_frame = None
self.themedata = None
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
"""
@ -96,17 +103,22 @@ class RenderManager(object):
"""
self.service_theme = service_theme
def set_override_theme(self, theme):
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.
The name of the song-level theme. None means the service
item wants to use the given value.
"""
log.debug(u'set override theme to %s', theme)
if self.theme_level == ThemeLevel.Global:
theme_level = self.theme_level
if overrideLevels:
theme_level = ThemeLevel.Song
if theme_level == ThemeLevel.Global:
self.theme = self.global_theme
elif self.theme_level == ThemeLevel.Service:
elif theme_level == ThemeLevel.Service:
if self.service_theme == u'':
self.theme = self.global_theme
else:
@ -114,20 +126,26 @@ class RenderManager(object):
else:
if theme:
self.theme = theme
elif self.theme_level == ThemeLevel.Song or \
self.theme_level == ThemeLevel.Service:
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.themedata is None:
if self.theme != self.renderer.theme_name or self.themedata is None \
or overrideLevels:
log.debug(u'theme is now %s', self.theme)
self.themedata = self.theme_manager.getThemeData(self.theme)
if overrideLevels:
self.themedata = theme
else:
self.themedata = self.theme_manager.getThemeData(self.theme)
self.calculate_default(self.screens.current[u'size'])
self.renderer.set_theme(self.themedata)
self.build_text_rectangle(self.themedata)
self.renderer.set_frame_dest(self.width, self.height)
return self.renderer._rect, self.renderer._rect_footer
def build_text_rectangle(self, theme):
"""
@ -163,13 +181,8 @@ class RenderManager(object):
The theme to generated a preview for.
"""
log.debug(u'generate preview')
#set the default image size for previews
# set the default image size for previews
self.calculate_default(self.screens.preview[u'size'])
self.renderer.set_theme(themedata)
self.build_text_rectangle(themedata)
self.renderer.set_frame_dest(self.width, self.height, True)
#Reset the real screen size for subsequent render requests
self.calculate_default(self.screens.current[u'size'])
verse = u'Amazing Grace!\n'\
'How sweet the sound\n'\
'To save a wretch like me;\n'\
@ -179,12 +192,21 @@ class RenderManager(object):
footer.append(u'Amazing Grace (John Newton)' )
footer.append(u'Public Domain')
footer.append(u'CCLI 123456')
formatted = self.renderer.format_slide(verse, False)
#Only Render the first slide page returned
return self.renderer.generate_frame_from_lines(formatted[0],
footer)[u'main']
# build a service item to generate preview
serviceItem = ServiceItem()
serviceItem.theme = themedata
serviceItem.add_from_text(u'', verse, footer)
serviceItem.render_manager = self
serviceItem.raw_footer = footer
serviceItem.render(True)
self.display.buildHtml(serviceItem)
frame, 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):
def format_slide(self, words, line_break):
"""
Calculate how much text can fit on a slide.
@ -193,22 +215,7 @@ class RenderManager(object):
"""
log.debug(u'format slide')
self.build_text_rectangle(self.themedata)
return self.renderer.format_slide(words, False)
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.
"""
log.debug(u'generate slide')
self.build_text_rectangle(self.themedata)
self.renderer.set_frame_dest(self.width, self.height)
return self.renderer.generate_frame_from_lines(main_text, footer_text)
return self.renderer.format_slide(words, line_break)
def calculate_default(self, screen):
"""

View File

@ -35,7 +35,7 @@ import uuid
from PyQt4 import QtGui
from openlp.core.lib import build_icon, resize_image
from openlp.core.lib import build_icon, resize_image, clean_tags, expand_tags
log = logging.getLogger(__name__)
@ -57,6 +57,7 @@ class ItemCapabilities(object):
RequiresMedia = 4
AllowsLoop = 5
AllowsAdditions = 6
NoLineBreaks = 7
class ServiceItem(object):
"""
@ -82,6 +83,7 @@ class ServiceItem(object):
self.items = []
self.iconic_representation = None
self.raw_footer = None
self.foot_text = None
self.theme = None
self.service_item_type = None
self._raw_frames = []
@ -91,10 +93,18 @@ class ServiceItem(object):
self.from_plugin = False
self.capabilities = []
self.is_valid = True
self.cache = {}
self.icon = None
self.themedata = None
self.main = None
self.footer = None
self.bg_frame = None
def _new_item(self):
"""
Method to set the internal id of the item
This is used to compare service items to see if they are
the same
"""
self._uuid = unicode(uuid.uuid1())
def add_capability(self, capability):
@ -126,34 +136,38 @@ class ServiceItem(object):
self.icon = icon
self.iconic_representation = build_icon(icon)
def render(self):
def render(self, useOverride=False):
"""
The render method is what generates the frames for the screen.
The render method is what generates the frames for the screen and
obtains the display information from the renderemanager.
At this point all the slides are build for the given
display size.
"""
log.debug(u'Render called')
self._display_frames = []
self.clear_cache()
self.bg_frame = None
line_break = True
if self.is_capable(ItemCapabilities.NoLineBreaks):
line_break = False
if self.service_item_type == ServiceItemType.Text:
log.debug(u'Formatting slides')
if self.theme is None:
self.render_manager.set_override_theme(None)
else:
self.render_manager.set_override_theme(self.theme)
theme = None
if self.theme:
theme = self.theme
self.main, self.footer = \
self.render_manager.set_override_theme(theme, useOverride)
self.bg_frame = self.render_manager.renderer.bg_frame
self.themedata = self.render_manager.renderer._theme
for slide in self._raw_frames:
before = time.time()
formated = self.render_manager.format_slide(slide[u'raw_slide'])
for format in formated:
lines = u''
title = u''
for line in format:
if title == u'':
title = line
lines += line + u'\n'
self._display_frames.append({u'title': title,
u'text': lines.rstrip(),
formated = self.render_manager \
.format_slide(slide[u'raw_slide'], line_break)
for page in formated:
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'] })
if len(self._display_frames) in self.cache.keys():
del self.cache[len(self._display_frames)]
log.log(15, u'Formatting took %4s' % (time.time() - before))
elif self.service_item_type == ServiceItemType.Image:
for slide in self._raw_frames:
@ -163,29 +177,14 @@ class ServiceItem(object):
pass
else:
log.error(u'Invalid value renderer :%s' % self.service_item_type)
def render_individual(self, row):
"""
Takes an array of text and generates an Image from the
theme. It assumes the text will fit on the screen as it
has generated by the render method above.
"""
log.debug(u'render individual')
if self.theme is None:
self.render_manager.set_override_theme(None)
else:
self.render_manager.set_override_theme(self.theme)
format = self._display_frames[row][u'text'].split(u'\n')
if self.cache.get(row):
frame = self.cache[row]
else:
if format[0]:
frame = self.render_manager.generate_slide(format,
self.raw_footer)
else:
frame = self.render_manager.generate_slide(format, u'')
self.cache[row] = frame
return frame
self.title = clean_tags(self.title)
self.foot_text = None
if self.raw_footer:
for foot in self.raw_footer:
if not self.foot_text:
self.foot_text = foot
else:
self.foot_text = u'%s<br>%s' % (self.foot_text, foot)
def add_from_image(self, path, title, image):
"""
@ -375,9 +374,9 @@ class ServiceItem(object):
renders it if required.
"""
if self.service_item_type == ServiceItemType.Text:
return self.render_individual(row)
return None, self._display_frames[row][u'html'].split(u'\n')[0]
else:
return {u'main':self._raw_frames[row][u'image'], u'trans':None}
return self._raw_frames[row][u'image'], u''
def get_frame_title(self, row=0):
"""
@ -390,9 +389,3 @@ class ServiceItem(object):
Returns the title of the raw frame
"""
return self._raw_frames[row][u'path']
def clear_cache(self):
"""
Clear's the service item's cache.
"""
self.cache = {}

View File

@ -55,7 +55,6 @@ BLANK_THEME_XML = \
<proportion>30</proportion>
<weight>Normal</weight>
<italics>False</italics>
<indentation>0</indentation>
<line_adjustment>0</line_adjustment>
<location override="False" x="10" y="10" width="1004" height="730"/>
</font>
@ -65,7 +64,6 @@ BLANK_THEME_XML = \
<proportion>12</proportion>
<weight>Normal</weight>
<italics>False</italics>
<indentation>0</indentation>
<line_adjustment>0</line_adjustment>
<location override="False" x="10" y="730" width="1004" height="38"/>
</font>
@ -184,7 +182,7 @@ class ThemeXML(object):
self.child_element(background, u'filename', filename)
def add_font(self, name, color, proportion, override, fonttype=u'main',
weight=u'Normal', italics=u'False', indentation=0, line_adjustment=0,
weight=u'Normal', italics=u'False', line_adjustment=0,
xpos=0, ypos=0, width=0, height=0):
"""
Add a Font.
@ -210,9 +208,6 @@ class ThemeXML(object):
``italics``
Does the font render to italics Defaults to 0 Normal
``indentation``
Number of characters the wrap line is indented
``xpos``
The X position of the text block.
@ -239,8 +234,6 @@ class ThemeXML(object):
#Create italics name element
self.child_element(background, u'italics', italics)
#Create indentation name element
self.child_element(background, u'indentation', unicode(indentation))
#Create indentation name element
self.child_element(
background, u'line_adjustment', unicode(line_adjustment))

View File

@ -204,7 +204,7 @@ class Theme(object):
val = element_text
if (element.tag.find(u'Color') > 0 or
(element.tag.find(u'BackgroundParameter') == 0 and
isinstance(int, val))):
isinstance(val, int))):
# convert to a wx.Colour
if not delphi_color_change:
val = QtGui.QColor(

View File

@ -27,6 +27,143 @@
The :mod:`ui` module provides the core user interface for OpenLP
"""
# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
import re
import sys
try:
import enchant
enchant_available = True
except ImportError:
enchant_available = False
from PyQt4 import QtCore, QtGui
from openlp.core.lib import html_expands, translate, context_menu_action
class SpellTextEdit(QtGui.QPlainTextEdit):
def __init__(self, *args):
QtGui.QPlainTextEdit.__init__(self, *args)
# Default dictionary based on the current locale.
if enchant_available:
self.dict = enchant.Dict()
self.highlighter = Highlighter(self.document())
self.highlighter.setDict(self.dict)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.RightButton:
# Rewrite the mouse event to a left button event so the cursor is
# moved to the location of the pointer.
event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, event.pos(),
QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier)
QtGui.QPlainTextEdit.mousePressEvent(self, event)
def contextMenuEvent(self, event):
popup_menu = self.createStandardContextMenu()
# Select the word under the cursor.
cursor = self.textCursor()
cursor.select(QtGui.QTextCursor.WordUnderCursor)
self.setTextCursor(cursor)
# Check if the selected word is misspelled and offer spelling
# suggestions if it is.
if enchant_available and self.textCursor().hasSelection():
text = unicode(self.textCursor().selectedText())
if not self.dict.check(text):
spell_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
'Spelling Suggestions'))
for word in self.dict.suggest(text):
action = SpellAction(word, spell_menu)
action.correct.connect(self.correctWord)
spell_menu.addAction(action)
# Only add the spelling suggests to the menu if there are
# suggestions.
if len(spell_menu.actions()) != 0:
popup_menu.insertSeparator(popup_menu.actions()[0])
popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
tag_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
'Formatting Tags'))
for html in html_expands:
action = SpellAction( html[u'desc'], tag_menu)
action.correct.connect(self.htmlTag)
tag_menu.addAction(action)
popup_menu.insertSeparator(popup_menu.actions()[0])
popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
popup_menu.exec_(event.globalPos())
def correctWord(self, word):
'''
Replaces the selected text with word.
'''
cursor = self.textCursor()
cursor.beginEditBlock()
cursor.removeSelectedText()
cursor.insertText(word)
cursor.endEditBlock()
def htmlTag(self, tag):
'''
Replaces the selected text with word.
'''
for html in html_expands:
if tag == html[u'desc']:
cursor = self.textCursor()
if self.textCursor().hasSelection():
text = cursor.selectedText()
cursor.beginEditBlock()
cursor.removeSelectedText()
cursor.insertText(html[u'start tag'])
cursor.insertText(text)
cursor.insertText(html[u'end tag'])
cursor.endEditBlock()
else:
cursor = self.textCursor()
cursor.insertText(html[u'start tag'])
cursor.insertText(html[u'end tag'])
class Highlighter(QtGui.QSyntaxHighlighter):
WORDS = u'(?iu)[\w\']+'
def __init__(self, *args):
QtGui.QSyntaxHighlighter.__init__(self, *args)
self.dict = None
def setDict(self, dict):
self.dict = dict
def highlightBlock(self, text):
if not self.dict:
return
text = unicode(text)
format = QtGui.QTextCharFormat()
format.setUnderlineColor(QtCore.Qt.red)
format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
for word_object in re.finditer(self.WORDS, text):
if not self.dict.check(word_object.group()):
self.setFormat(word_object.start(),
word_object.end() - word_object.start(), format)
class SpellAction(QtGui.QAction):
'''
A special QAction that returns the text in a signal.
'''
correct = QtCore.pyqtSignal(unicode)
def __init__(self, *args):
QtGui.QAction.__init__(self, *args)
self.triggered.connect(lambda x: self.correct.emit(
unicode(self.text())))
class HideMode(object):
"""
This is basically an enumeration class which specifies the mode of a Bible.
@ -37,13 +174,11 @@ class HideMode(object):
Theme = 2
Screen = 3
from maindisplay import MainDisplay
from slidecontroller import HideMode
from servicenoteform import ServiceNoteForm
from serviceitemeditform import ServiceItemEditForm
from screen import ScreenList
from maindisplay import MainDisplay
from maindisplay import VideoDisplay
from maindisplay import DisplayManager
from amendthemeform import AmendThemeForm
from slidecontroller import SlideController
from splashscreen import SplashScreen
@ -56,8 +191,7 @@ from settingsform import SettingsForm
from mediadockmanager import MediaDockManager
from servicemanager import ServiceManager
from thememanager import ThemeManager
from mainwindow import MainWindow
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainWindow',
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm',
'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager',
'AmendThemeForm', 'MediaDockManager', 'ServiceItemEditForm']

View File

@ -68,17 +68,6 @@ class Ui_AmendThemeDialog(object):
self.backgroundLayout.setMargin(8)
self.backgroundLayout.setSpacing(8)
self.backgroundLayout.setObjectName(u'backgroundLayout')
self.backgroundLabel = QtGui.QLabel(self.backgroundTab)
self.backgroundLabel.setObjectName(u'backgroundLabel')
self.backgroundLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
self.backgroundLabel)
self.backgroundComboBox = QtGui.QComboBox(self.backgroundTab)
self.backgroundComboBox.setObjectName(u'backgroundComboBox')
self.backgroundLabel.setBuddy(self.backgroundComboBox)
self.backgroundComboBox.addItem(QtCore.QString())
self.backgroundComboBox.addItem(QtCore.QString())
self.backgroundLayout.setWidget(0, QtGui.QFormLayout.FieldRole,
self.backgroundComboBox)
self.backgroundTypeLabel = QtGui.QLabel(self.backgroundTab)
self.backgroundTypeLabel.setObjectName(u'backgroundTypeLabel')
self.backgroundLayout.setWidget(1, QtGui.QFormLayout.LabelRole,
@ -216,17 +205,6 @@ class Ui_AmendThemeDialog(object):
self.fontMainLineAdjustmentSpinBox.setMinimum(-99)
self.mainFontLayout.setWidget(4, QtGui.QFormLayout.FieldRole,
self.fontMainLineAdjustmentSpinBox)
self.fontMainWrapIndentationLabel = QtGui.QLabel(self.fontMainGroupBox)
self.fontMainWrapIndentationLabel.setObjectName(
u'fontMainWrapIndentationLabel')
self.mainFontLayout.setWidget(5, QtGui.QFormLayout.LabelRole,
self.fontMainWrapIndentationLabel)
self.fontMainLineSpacingSpinBox = QtGui.QSpinBox(self.fontMainGroupBox)
self.fontMainLineSpacingSpinBox.setObjectName(
u'fontMainLineSpacingSpinBox')
self.fontMainLineSpacingSpinBox.setMaximum(10)
self.mainFontLayout.setWidget(5, QtGui.QFormLayout.FieldRole,
self.fontMainLineSpacingSpinBox)
self.fontMainLinesPageLabel = QtGui.QLabel(self.fontMainGroupBox)
self.fontMainLinesPageLabel.setObjectName(u'fontMainLinesPageLabel')
self.mainFontLayout.addRow(self.fontMainLinesPageLabel)
@ -661,12 +639,6 @@ class Ui_AmendThemeDialog(object):
translate('OpenLP.AmendThemeForm', 'Theme Maintenance'))
self.themeNameLabel.setText(
translate('OpenLP.AmendThemeForm', 'Theme &name:'))
self.backgroundLabel.setText(
translate('OpenLP.AmendThemeForm', '&Visibility:'))
self.backgroundComboBox.setItemText(0,
translate('OpenLP.AmendThemeForm', 'Opaque'))
self.backgroundComboBox.setItemText(1,
translate('OpenLP.AmendThemeForm', 'Transparent'))
self.backgroundTypeLabel.setText(
translate('OpenLP.AmendThemeForm', 'Type:'))
self.backgroundTypeComboBox.setItemText(0,
@ -700,8 +672,6 @@ class Ui_AmendThemeDialog(object):
translate('OpenLP.AmendThemeForm', 'Size:'))
self.fontMainSizeSpinBox.setSuffix(
translate('OpenLP.AmendThemeForm', 'pt'))
self.fontMainWrapIndentationLabel.setText(
translate('OpenLP.AmendThemeForm', 'Wrap indentation:'))
self.fontMainWrapLineAdjustmentLabel.setText(
translate('OpenLP.AmendThemeForm', 'Adjust line spacing:'))
self.fontMainWeightComboBox.setItemText(0,

View File

@ -50,7 +50,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.path = None
self.theme = ThemeXML()
self.setupUi(self)
# define signals
# Buttons
QtCore.QObject.connect(self.color1PushButton,
QtCore.SIGNAL(u'pressed()'), self.onColor1PushButtonClicked)
@ -68,8 +67,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.QObject.connect(self.imageToolButton,
QtCore.SIGNAL(u'clicked()'), self.onImageToolButtonClicked)
# Combo boxes
QtCore.QObject.connect(self.backgroundComboBox,
QtCore.SIGNAL(u'activated(int)'), self.onBackgroundComboBoxSelected)
QtCore.QObject.connect(self.backgroundTypeComboBox,
QtCore.SIGNAL(u'activated(int)'),
self.onBackgroundTypeComboBoxSelected)
@ -109,9 +106,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.QObject.connect(self.fontMainLineAdjustmentSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onFontMainLineAdjustmentSpinBoxChanged)
QtCore.QObject.connect(self.fontMainLineSpacingSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onFontMainLineSpacingSpinBoxChanged)
QtCore.QObject.connect(self.fontFooterXSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onFontFooterXSpinBoxChanged)
@ -151,30 +145,26 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
new_theme.new_document(theme_name)
save_from = None
save_to = None
if self.theme.background_mode == u'transparent':
new_theme.add_background_transparent()
if self.theme.background_type == u'solid':
new_theme.add_background_solid(
unicode(self.theme.background_color))
elif self.theme.background_type == u'gradient':
new_theme.add_background_gradient(
unicode(self.theme.background_startColor),
unicode(self.theme.background_endColor),
self.theme.background_direction)
else:
if self.theme.background_type == u'solid':
new_theme.add_background_solid(
unicode(self.theme.background_color))
elif self.theme.background_type == u'gradient':
new_theme.add_background_gradient(
unicode(self.theme.background_startColor),
unicode(self.theme.background_endColor),
self.theme.background_direction)
else:
filename = \
os.path.split(unicode(self.theme.background_filename))[1]
new_theme.add_background_image(filename)
save_to = os.path.join(self.path, theme_name, filename)
save_from = self.theme.background_filename
filename = \
os.path.split(unicode(self.theme.background_filename))[1]
new_theme.add_background_image(filename)
save_to = os.path.join(self.path, theme_name, filename)
save_from = self.theme.background_filename
new_theme.add_font(unicode(self.theme.font_main_name),
unicode(self.theme.font_main_color),
unicode(self.theme.font_main_proportion),
unicode(self.theme.font_main_override), u'main',
unicode(self.theme.font_main_weight),
unicode(self.theme.font_main_italics),
unicode(self.theme.font_main_indentation),
unicode(self.theme.font_main_line_adjustment),
unicode(self.theme.font_main_x),
unicode(self.theme.font_main_y),
@ -186,7 +176,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
unicode(self.theme.font_footer_override), u'footer',
unicode(self.theme.font_footer_weight),
unicode(self.theme.font_footer_italics),
0, # indentation
0, # line adjustment
unicode(self.theme.font_footer_x),
unicode(self.theme.font_footer_y),
@ -230,7 +219,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.previewTheme()
#
#Main Font Tab
# Main Font Tab
#
def onFontMainComboBoxSelected(self):
self.theme.font_main_name = self.fontMainComboBox.currentFont().family()
@ -283,8 +272,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.fontMainHeightSpinBox.setValue(self.theme.font_main_height)
self.fontMainLineAdjustmentSpinBox.setValue(
self.theme.font_main_line_adjustment)
self.fontMainLineSpacingSpinBox.setValue(
self.theme.font_main_indentation)
self.stateChanging(self.theme)
self.previewTheme()
@ -310,20 +297,13 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.fontMainLineAdjustmentSpinBox.value()
self.previewTheme()
def onFontMainLineSpacingSpinBoxChanged(self):
if self.theme.font_main_indentation != \
self.fontMainLineSpacingSpinBox.value():
self.theme.font_main_indentation = \
self.fontMainLineSpacingSpinBox.value()
self.previewTheme()
def onFontMainHeightSpinBoxChanged(self):
if self.theme.font_main_height != self.fontMainHeightSpinBox.value():
self.theme.font_main_height = self.fontMainHeightSpinBox.value()
self.previewTheme()
#
#Footer Font Tab
# Footer Font Tab
#
def onFontFooterComboBoxSelected(self):
self.theme.font_footer_name = \
@ -404,20 +384,12 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.previewTheme()
#
#Background Tab
# Background Tab
#
def onGradientComboBoxSelected(self, currentIndex):
self.setBackground(self.backgroundTypeComboBox.currentIndex(),
currentIndex)
def onBackgroundComboBoxSelected(self, currentIndex):
if currentIndex == 0: # Opaque
self.theme.background_mode = u'opaque'
else:
self.theme.background_mode = u'transparent'
self.stateChanging(self.theme)
self.previewTheme()
def onBackgroundTypeComboBoxSelected(self, currentIndex):
self.setBackground(currentIndex, self.gradientComboBox.currentIndex())
@ -472,7 +444,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.previewTheme()
#
#Other Tab
# Other Tab
#
def onOutlineCheckBoxChanged(self, value):
if value == 2: # checked
@ -537,16 +509,12 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.previewTheme()
#
#Local Methods
# Local Methods
#
def paintUi(self, theme):
self.stateChanging(theme)
self.themeNameEdit.setText(self.theme.theme_name)
# Background Tab
if self.theme.background_mode == u'opaque':
self.backgroundComboBox.setCurrentIndex(0)
else:
self.backgroundComboBox.setCurrentIndex(1)
self.imageLineEdit.setText(u'')
if theme.background_type == u'solid':
self.backgroundTypeComboBox.setCurrentIndex(0)
@ -576,8 +544,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.fontMainWeightComboBox.setCurrentIndex(2)
else:
self.fontMainWeightComboBox.setCurrentIndex(3)
self.fontMainLineSpacingSpinBox.setValue(
self.theme.font_main_indentation)
self.fontMainXSpinBox.setValue(self.theme.font_main_x)
self.fontMainYSpinBox.setValue(self.theme.font_main_y)
self.fontMainWidthSpinBox.setValue(self.theme.font_main_width)
@ -641,9 +607,15 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.verticalComboBox.setCurrentIndex(self.theme.display_verticalAlign)
def stateChanging(self, theme):
if theme.background_mode == u'transparent':
self.color1Label.setVisible(False)
self.color1PushButton.setVisible(False)
self.backgroundTypeComboBox.setVisible(True)
self.backgroundTypeLabel.setVisible(True)
if theme.background_type == u'solid':
self.color1PushButton.setStyleSheet(
u'background-color: %s' % unicode(theme.background_color))
self.color1Label.setText(
translate('OpenLP.AmendThemeForm', 'Color:'))
self.color1Label.setVisible(True)
self.color1PushButton.setVisible(True)
self.color2Label.setVisible(False)
self.color2PushButton.setVisible(False)
self.imageLabel.setVisible(False)
@ -651,53 +623,34 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.imageFilenameWidget.setVisible(False)
self.gradientLabel.setVisible(False)
self.gradientComboBox.setVisible(False)
self.backgroundTypeComboBox.setVisible(False)
self.backgroundTypeLabel.setVisible(False)
else:
self.backgroundTypeComboBox.setVisible(True)
self.backgroundTypeLabel.setVisible(True)
if theme.background_type == u'solid':
self.color1PushButton.setStyleSheet(
u'background-color: %s' % unicode(theme.background_color))
self.color1Label.setText(
translate('OpenLP.AmendThemeForm', 'Color:'))
self.color1Label.setVisible(True)
self.color1PushButton.setVisible(True)
self.color2Label.setVisible(False)
self.color2PushButton.setVisible(False)
self.imageLabel.setVisible(False)
self.imageLineEdit.setVisible(False)
self.imageFilenameWidget.setVisible(False)
self.gradientLabel.setVisible(False)
self.gradientComboBox.setVisible(False)
elif theme.background_type == u'gradient':
self.color1PushButton.setStyleSheet(u'background-color: %s' \
% unicode(theme.background_startColor))
self.color2PushButton.setStyleSheet(u'background-color: %s' \
% unicode(theme.background_endColor))
self.color1Label.setText(
translate('OpenLP.AmendThemeForm', 'First color:'))
self.color2Label.setText(
translate('OpenLP.AmendThemeForm', 'Second color:'))
self.color1Label.setVisible(True)
self.color1PushButton.setVisible(True)
self.color2Label.setVisible(True)
self.color2PushButton.setVisible(True)
self.imageLabel.setVisible(False)
self.imageLineEdit.setVisible(False)
self.imageFilenameWidget.setVisible(False)
self.gradientLabel.setVisible(True)
self.gradientComboBox.setVisible(True)
else: # must be image
self.color1Label.setVisible(False)
self.color1PushButton.setVisible(False)
self.color2Label.setVisible(False)
self.color2PushButton.setVisible(False)
self.imageLabel.setVisible(True)
self.imageLineEdit.setVisible(True)
self.imageFilenameWidget.setVisible(True)
self.gradientLabel.setVisible(False)
self.gradientComboBox.setVisible(False)
elif theme.background_type == u'gradient':
self.color1PushButton.setStyleSheet(u'background-color: %s' \
% unicode(theme.background_startColor))
self.color2PushButton.setStyleSheet(u'background-color: %s' \
% unicode(theme.background_endColor))
self.color1Label.setText(
translate('OpenLP.AmendThemeForm', 'First color:'))
self.color2Label.setText(
translate('OpenLP.AmendThemeForm', 'Second color:'))
self.color1Label.setVisible(True)
self.color1PushButton.setVisible(True)
self.color2Label.setVisible(True)
self.color2PushButton.setVisible(True)
self.imageLabel.setVisible(False)
self.imageLineEdit.setVisible(False)
self.imageFilenameWidget.setVisible(False)
self.gradientLabel.setVisible(True)
self.gradientComboBox.setVisible(True)
else: # must be image
self.color1Label.setVisible(False)
self.color1PushButton.setVisible(False)
self.color2Label.setVisible(False)
self.color2PushButton.setVisible(False)
self.imageLabel.setVisible(True)
self.imageLineEdit.setVisible(True)
self.imageFilenameWidget.setVisible(True)
self.gradientLabel.setVisible(False)
self.gradientComboBox.setVisible(False)
if not theme.font_main_override:
self.fontMainXSpinBox.setEnabled(False)
self.fontMainYSpinBox.setEnabled(False)

View File

@ -390,26 +390,16 @@ class GeneralTab(SettingsTab):
unicode(self.screens.current[u'size'].width()))
self.overrideCheckBox.setChecked(settings.value(u'override position',
QtCore.QVariant(False)).toBool())
if self.overrideCheckBox.isChecked():
self.customXValueEdit.setText(settings.value(u'x position',
QtCore.QVariant(self.screens.current[u'size'].x())).toString())
self.customYValueEdit.setText(settings.value(u'y position',
QtCore.QVariant(self.screens.current[u'size'].y())).toString())
self.customHeightValueEdit.setText(
settings.value(u'height', QtCore.QVariant(
self.screens.current[u'size'].height())).toString())
self.customWidthValueEdit.setText(
settings.value(u'width', QtCore.QVariant(
self.screens.current[u'size'].width())).toString())
else:
self.customXValueEdit.setText(
unicode(self.screens.current[u'size'].x()))
self.customYValueEdit.setText(
unicode(self.screens.current[u'size'].y()))
self.customHeightValueEdit.setText(
unicode(self.screens.current[u'size'].height()))
self.customWidthValueEdit.setText(
unicode(self.screens.current[u'size'].width()))
self.customXValueEdit.setText(settings.value(u'x position',
QtCore.QVariant(self.screens.current[u'size'].x())).toString())
self.customYValueEdit.setText(settings.value(u'y position',
QtCore.QVariant(self.screens.current[u'size'].y())).toString())
self.customHeightValueEdit.setText(
settings.value(u'height', QtCore.QVariant(
self.screens.current[u'size'].height())).toString())
self.customWidthValueEdit.setText(
settings.value(u'width', QtCore.QVariant(
self.screens.current[u'size'].width())).toString())
settings.endGroup()
self.customXValueEdit.setEnabled(self.overrideCheckBox.isChecked())
self.customYValueEdit.setEnabled(self.overrideCheckBox.isChecked())
@ -436,10 +426,8 @@ class GeneralTab(SettingsTab):
QtCore.QVariant(self.saveCheckServiceCheckBox.isChecked()))
settings.setValue(u'auto preview',
QtCore.QVariant(self.autoPreviewCheckBox.isChecked()))
settings.setValue(u'loop delay',
settings.setValue(u'loop delay',
QtCore.QVariant(self.timeoutSpinBox.value()))
Receiver.send_message(u'slidecontroller_live_spin_delay',
self.timeoutSpinBox.value())
settings.setValue(u'ccli number',
QtCore.QVariant(self.numberEdit.displayText()))
settings.setValue(u'songselect username',
@ -459,17 +447,18 @@ class GeneralTab(SettingsTab):
settings.endGroup()
self.screens.display = self.displayOnMonitorCheck.isChecked()
# Monitor Number has changed.
postUpdate = False
if self.screens.monitor_number != self.monitorNumber:
self.screens.monitor_number = self.monitorNumber
self.screens.set_current_display(self.monitorNumber)
Receiver.send_message(u'config_screen_changed')
Receiver.send_message(u'config_updated')
postUpdate = True
# On save update the screens as well
self.postSetUp()
self.postSetUp(postUpdate)
def postSetUp(self):
def postSetUp(self, postUpdate=False):
"""
Apply settings after settings tab has loaded
Apply settings after settings tab has loaded and most of the
system so must be delayed
"""
Receiver.send_message(u'slidecontroller_live_spin_delay',
self.timeoutSpinBox.value())
@ -480,12 +469,15 @@ class GeneralTab(SettingsTab):
int(self.customYValueEdit.text()),
int(self.customWidthValueEdit.text()),
int(self.customHeightValueEdit.text()))
if self.overrideCheckBox.isChecked():
self.screens.set_override_display()
Receiver.send_message(u'config_screen_changed')
else:
self.screens.reset_current_display()
Receiver.send_message(u'config_screen_changed')
if self.overrideCheckBox.isChecked():
self.screens.set_override_display()
else:
self.screens.reset_current_display()
# Order is important so be careful if you change
if self.overrideChanged or postUpdate:
Receiver.send_message(u'config_screen_changed')
Receiver.send_message(u'config_updated')
self.overrideChanged = False
def onOverrideCheckBoxToggled(self, checked):
"""

View File

@ -26,149 +26,46 @@
import logging
import os
import time
from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.phonon import Phonon
from openlp.core.lib import Receiver, resize_image
from openlp.core.lib import Receiver, resize_image, build_html, ServiceItem, \
image_to_byte
from openlp.core.ui import HideMode
log = logging.getLogger(__name__)
#http://www.steveheffernan.com/html5-video-player/demo-video-player.html
HTMLVIDEO = u"""<html>
<head>
<style>
*{
margin: 0;
padding:0
}
</style>
<script type="text/javascript" charset="utf-8">
var video;
var bodyLoaded = function(){
video = document.getElementById("video");
video.volume = 0;
}
</script>
</head>
<body id="body" onload="bodyLoaded();">
<video id="video" src="%s" autoplay="autoplay" loop="loop"
width="%s" height="%s" autobuffer="autobuffer" preload="preload" />
</body></html>
"""
class DisplayManager(QtGui.QWidget):
"""
Wrapper class to hold the display widgets.
I will provide API's in future to access the screens allow for
extra displays to be added.
RenderManager is poked in by MainWindow
"""
def __init__(self, screens):
QtGui.QWidget.__init__(self)
self.screens = screens
self.videoDisplay = VideoDisplay(self, screens)
self.audioPlayer = AudioPlayer(self)
self.mainDisplay = MainDisplay(self, screens)
QtCore.QObject.connect(Receiver.get_receiver(),
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'videodisplay_start'), self.onStartVideo)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'videodisplay_stop'), self.onStopVideo)
def setup(self):
self.videoDisplay.setup()
self.mainDisplay.setup()
def hideDisplay(self, message):
"""
Hide the output displays
"""
self.videoDisplay.mediaHide(message)
self.mainDisplay.hideDisplay(message)
def showDisplay(self):
"""
Hide the output displays
"""
self.videoDisplay.mediaShow()
self.mainDisplay.showDisplay()
def addAlert(self, alertMessage, location):
"""
Handles the addition of an Alert Message to the Displays
"""
self.mainDisplay.addAlert(alertMessage, location)
def displayImageWithText(self, frame):
"""
Handles the addition of a background Image to the displays
"""
self.mainDisplay.addImageWithText(frame)
def displayImage(self, frame):
"""
Handles the addition of a background Image to the displays
"""
self.mainDisplay.displayImage(frame)
def displayVideo(self, path):
"""
Handles the addition of a background Video to the displays
"""
self.mainDisplay.displayVideo(path)
def onStartVideo(self, item):
"""
Handles the Starting of a Video and Display Management
"""
self.videoDisplay.setVisible(True)
self.mainDisplay.setVisible(False)
self.videoDisplay.onMediaQueue(item)
def onStopVideo(self):
"""
Handles the Stopping of a Video and Display Management
"""
self.mainDisplay.setVisible(True)
self.videoDisplay.setVisible(False)
self.videoDisplay.onMediaStop()
def close(self):
"""
Handles the closure of the displays
"""
self.videoDisplay.close()
self.audioPlayer.close()
self.mainDisplay.close()
class DisplayWidget(QtGui.QGraphicsView):
"""
Customised version of QTableWidget which can respond to keyboard
events.
"""
log.info(u'MainDisplay loaded')
log.info(u'Display Widget loaded')
def __init__(self, parent=None, name=None, primary=False):
QtGui.QWidget.__init__(self, None)
def __init__(self, live, parent=None):
QtGui.QGraphicsView.__init__(self)
self.parent = parent
self.primary = primary
self.live = live
self.hotkey_map = {
QtCore.Qt.Key_Return: 'servicemanager_next_item',
QtCore.Qt.Key_Space: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_Enter: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_0: 'servicemanager_next_item',
QtCore.Qt.Key_Backspace: 'slidecontroller_live_previous_noloop'}
self.setStyleSheet(u'border: none;')
def keyPressEvent(self, event):
"""
Handle key events from display screen
"""
# Key events only needed for live
if not self.live:
return
if isinstance(event, QtGui.QKeyEvent):
#here accept the event and do something
# Here accept the event and do something
if event.key() == QtCore.Qt.Key_Up:
Receiver.send_message(u'slidecontroller_live_previous')
event.accept()
@ -185,157 +82,269 @@ class DisplayWidget(QtGui.QGraphicsView):
Receiver.send_message(self.hotkey_map[event.key()])
event.accept()
elif event.key() == QtCore.Qt.Key_Escape:
self.resetDisplay()
self.setVisible(False)
self.videoStop()
event.accept()
event.ignore()
else:
event.ignore()
def resetDisplay(self):
log.debug(u'resetDisplay')
Receiver.send_message(u'slidecontroller_live_stop_loop')
if self.primary:
self.setVisible(False)
else:
self.setVisible(True)
class MainDisplay(DisplayWidget):
"""
This is the form that is used to display things on the projector.
"""
log.info(u'MainDisplay Loaded')
def __init__(self, parent, screens):
"""
The constructor for the display form.
``parent``
The parent widget.
``screens``
The list of screens.
"""
log.debug(u'Initialisation started')
DisplayWidget.__init__(self, parent, primary=True)
self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
# WA_TranslucentBackground is not available in QT4.4
try:
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
except AttributeError:
pass
def __init__(self, parent, screens, live):
DisplayWidget.__init__(self, live, parent=None)
self.parent = parent
self.screens = screens
self.setupScene()
self.setupVideo()
self.setupImage()
self.setupText()
self.setupAlert()
self.setupBlank()
self.blankFrame = None
self.frame = None
#Hide desktop for now until we know where to put it
#and what size it should be.
self.setVisible(False)
self.isLive = live
self.alertTab = None
self.setWindowTitle(u'OpenLP Display')
self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
QtCore.Qt.WindowStaysOnTopHint)
if self.isLive:
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay)
def setup(self):
"""
Sets up the screen on a particular screen.
Set up and build the output screen
"""
log.debug(u'Setup %s for %s ' % (
self.screens, self.screens.monitor_number))
self.setVisible(False)
log.debug(u'Setup live = %s for %s ' % (self.isLive,
self.screens.monitor_number))
self.screen = self.screens.current
#Sort out screen locations and sizes
self.setVisible(False)
self.setGeometry(self.screen[u'size'])
self.scene.setSceneRect(0, 0, self.size().width(),
self.size().height())
self.webView.setGeometry(0, 0, self.size().width(),
self.size().height())
self.alertText.setTextWidth(self.size().width())
#Build a custom splash screen
self.initialFrame = QtGui.QImage(
self.screen[u'size'].width(),
self.screen[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png')
painter_image = QtGui.QPainter()
painter_image.begin(self.initialFrame)
painter_image.fillRect(self.initialFrame.rect(), QtCore.Qt.white)
painter_image.drawImage(
(self.screen[u'size'].width() - splash_image.width()) / 2,
(self.screen[u'size'].height() - splash_image.height()) / 2,
splash_image)
#build a blank transparent image
self.transparent = QtGui.QPixmap(
self.screen[u'size'].width(), self.screen[u'size'].height())
self.transparent.fill(QtCore.Qt.transparent)
self.displayImage(self.initialFrame)
self.repaint()
#Build a Black screen
painter = QtGui.QPainter()
self.blankFrame = QtGui.QImage(
self.screen[u'size'].width(),
self.screen[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
painter.begin(self.blankFrame)
painter.fillRect(self.blankFrame.rect(), QtCore.Qt.black)
# To display or not to display?
if not self.screen[u'primary']:
self.setVisible(True)
self.primary = False
else:
self.setVisible(False)
self.primary = True
def setupScene(self):
self.scene = QtGui.QGraphicsScene(self)
self.scene.setSceneRect(0, 0, self.size().width(), self.size().height())
self.setScene(self.scene)
def setupVideo(self):
self.webView = QtWebKit.QWebView()
self.webView = QtWebKit.QWebView(self)
self.webView.setGeometry(0, 0, self.screen[u'size'].width(), \
self.screen[u'size'].height())
self.page = self.webView.page()
self.videoDisplay = self.page.mainFrame()
self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Vertical,
self.frame = self.page.mainFrame()
QtCore.QObject.connect(self.webView,
QtCore.SIGNAL(u'loadFinished(bool)'), self.isLoaded)
self.frame.setScrollBarPolicy(QtCore.Qt.Vertical,
QtCore.Qt.ScrollBarAlwaysOff)
self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Horizontal,
self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
QtCore.Qt.ScrollBarAlwaysOff)
self.proxy = QtGui.QGraphicsProxyWidget()
self.proxy.setWidget(self.webView)
self.proxy.setWindowFlags(QtCore.Qt.Window |
QtCore.Qt.FramelessWindowHint)
self.proxy.setZValue(1)
self.scene.addItem(self.proxy)
if self.isLive:
# Build the initial frame.
self.black = QtGui.QImage(
self.screens.current[u'size'].width(),
self.screens.current[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
painter_image = QtGui.QPainter()
painter_image.begin(self.black)
painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
#Build the initial frame.
initialFrame = QtGui.QImage(
self.screens.current[u'size'].width(),
self.screens.current[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png')
painter_image = QtGui.QPainter()
painter_image.begin(initialFrame)
painter_image.fillRect(initialFrame.rect(), QtCore.Qt.white)
painter_image.drawImage(
(self.screens.current[u'size'].width() \
- splash_image.width()) / 2,
(self.screens.current[u'size'].height() \
- splash_image.height()) / 2,
splash_image)
serviceItem = ServiceItem()
serviceItem.bg_frame = initialFrame
self.webView.setHtml(build_html(serviceItem, self.screen, \
self.parent.alertTab))
self.initialFrame = True
self.show()
# To display or not to display?
if not self.screen[u'primary']:
self.primary = False
else:
self.primary = True
def setupImage(self):
self.imageDisplay = QtGui.QGraphicsPixmapItem()
self.imageDisplay.setZValue(2)
self.scene.addItem(self.imageDisplay)
def text(self, slide):
"""
Add the slide text from slideController
def setupText(self):
#self.displayText = QtGui.QGraphicsTextItem()
self.displayText = QtGui.QGraphicsPixmapItem()
#self.displayText.setPos(0,0)
#self.displayText.setTextWidth(self.size().width())
self.displayText.setZValue(4)
self.scene.addItem(self.displayText)
`slide`
The slide text to be displayed
"""
log.debug(u'text')
self.frame.evaluateJavaScript(u'show_text("%s")' % \
slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
return self.preview()
def setupAlert(self):
self.alertText = QtGui.QGraphicsTextItem()
self.alertText.setZValue(8)
self.scene.addItem(self.alertText)
def alert(self, text):
"""
Add the alert text
def setupBlank(self):
self.displayBlank = QtGui.QGraphicsPixmapItem()
self.displayBlank.setZValue(10)
self.scene.addItem(self.displayBlank)
`slide`
The slide text to be displayed
"""
log.debug(u'alert')
if self.height() != self.screen[u'size'].height() \
or not self.isVisible():
shrink = True
else:
shrink = False
js = u'show_alert("%s", "%s")' % (
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'),
u'top' if shrink else u'')
height = self.frame.evaluateJavaScript(js)
if shrink:
if text:
self.resize(self.width(), int(height.toString()))
self.setVisible(True)
else:
self.setGeometry(self.screen[u'size'])
self.setVisible(False)
# def hideDisplayForVideo(self):
# """
# Hides the main display if for the video to be played
# """
# self.hideDisplay(HideMode.Screen)
def image(self, image):
"""
Add an image as the background. The image is converted to a
bytestream on route.
`Image`
The Image to be displayed can be QImage or QPixmap
"""
log.debug(u'image')
image = resize_image(image, self.screen[u'size'].width(),
self.screen[u'size'].height())
self.resetVideo()
self.displayImage(image)
# show screen
if self.isLive:
self.setVisible(True)
def displayImage(self, image):
"""
Display an image, as is.
"""
if image:
js = u'show_image("data:image/png;base64,%s");' % \
image_to_byte(image)
else:
js = u'show_image("");'
self.frame.evaluateJavaScript(js)
def resetImage(self):
"""
Reset the backgound image to the service item image.
Used after Image plugin has changed the background
"""
log.debug(u'resetImage')
self.displayImage(self.serviceItem.bg_frame)
def resetVideo(self):
"""
Used after Video plugin has changed the background
"""
log.debug(u'resetVideo')
self.frame.evaluateJavaScript(u'show_video("close");')
def videoPlay(self):
"""
Responds to the request to play a loaded video
"""
log.debug(u'videoPlay')
self.frame.evaluateJavaScript(u'show_video("play");')
# show screen
if self.isLive:
self.setVisible(True)
def videoPause(self):
"""
Responds to the request to pause a loaded video
"""
log.debug(u'videoPause')
self.frame.evaluateJavaScript(u'show_video("pause");')
def videoStop(self):
"""
Responds to the request to stop a loaded video
"""
log.debug(u'videoStop')
self.frame.evaluateJavaScript(u'show_video("stop");')
def videoVolume(self, volume):
"""
Changes the volume of a running video
"""
log.debug(u'videoVolume %d' % volume)
self.frame.evaluateJavaScript(u'show_video(null, null, %s);' %
str(float(volume)/float(10)))
def video(self, videoPath, volume):
"""
Loads and starts a video to run with the option of sound
"""
log.debug(u'video')
self.loaded = True
js = u'show_video("play", "%s", %s, true);' % \
(videoPath.replace(u'\\', u'\\\\'), str(float(volume)/float(10)))
self.frame.evaluateJavaScript(js)
return self.preview()
def isLoaded(self):
"""
Called by webView event to show display is fully loaded
"""
log.debug(u'loaded')
self.loaded = True
def preview(self):
"""
Generates a preview of the image displayed.
"""
log.debug(u'preview for %s', self.isLive)
# Wait for the fade to finish before geting the preview.
# Important otherwise preview will have incorrect text if at all !
if self.serviceItem.themedata and \
self.serviceItem.themedata.display_slideTransition:
while self.frame.evaluateJavaScript(u'show_text_complete()') \
.toString() == u'false':
Receiver.send_message(u'openlp_process_events')
# Wait for the webview to update before geting the preview.
# Important otherwise first preview will miss the background !
while not self.loaded:
Receiver.send_message(u'openlp_process_events')
preview = QtGui.QImage(self.screen[u'size'].width(),
self.screen[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
painter = QtGui.QPainter(preview)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
self.frame.render(painter)
painter.end()
# Make display show up if in single screen mode
if self.isLive:
self.setVisible(True)
# save preview for debugging
if log.isEnabledFor(logging.DEBUG):
preview.save(u'temp.png', u'png')
return preview
def buildHtml(self, serviceItem):
"""
Store the serviceItem and build the new HTML from it. Add the
HTML to the display
"""
log.debug(u'buildHtml')
self.loaded = False
self.initialFrame = False
self.serviceItem = serviceItem
html = build_html(self.serviceItem, self.screen, self.parent.alertTab)
self.webView.setHtml(html)
if serviceItem.foot_text and serviceItem.foot_text:
self.footer(serviceItem.foot_text)
def footer(self, text):
"""
Display the Footer
"""
log.debug(u'footer')
js = "show_footer('" + \
text.replace("\\", "\\\\").replace("\'", "\\\'") + "')"
self.frame.evaluateJavaScript(js)
def hideDisplay(self, mode=HideMode.Screen):
"""
@ -343,20 +352,13 @@ class MainDisplay(DisplayWidget):
Store the images so they can be replaced when required
"""
log.debug(u'hideDisplay mode = %d', mode)
#self.displayText.setPixmap(self.transparent)
if mode == HideMode.Screen:
#self.display_image.setPixmap(self.transparent)
self.frame.evaluateJavaScript(u'show_blank("desktop");')
self.setVisible(False)
elif mode == HideMode.Blank:
self.displayBlank.setPixmap(
QtGui.QPixmap.fromImage(self.blankFrame))
elif mode == HideMode.Blank or self.initialFrame:
self.frame.evaluateJavaScript(u'show_blank("black");')
else:
if self.parent.renderManager.renderer.bg_frame:
self.displayBlank.setPixmap(QtGui.QPixmap.fromImage(
self.parent.renderManager.renderer.bg_frame))
else:
self.displayBlank.setPixmap(
QtGui.QPixmap.fromImage(self.blankFrame))
self.frame.evaluateJavaScript(u'show_blank("theme");')
if mode != HideMode.Screen and self.isHidden():
self.setVisible(True)
@ -367,275 +369,16 @@ class MainDisplay(DisplayWidget):
Make the stored images None to release memory.
"""
log.debug(u'showDisplay')
self.displayBlank.setPixmap(self.transparent)
self.frame.evaluateJavaScript('show_blank("show");')
if self.isHidden():
self.setVisible(True)
#Trigger actions when display is active again
# Trigger actions when display is active again
Receiver.send_message(u'maindisplay_active')
def addImageWithText(self, frame):
log.debug(u'addImageWithText')
frame = resize_image(
frame, self.screen[u'size'].width(), self.screen[u'size'].height())
self.imageDisplay.setPixmap(QtGui.QPixmap.fromImage(frame))
self.videoDisplay.setHtml(u'<html></html>')
def addAlert(self, message, location):
"""
Places the Alert text on the display at the correct location
``message``
Text to be displayed
``location``
Where on the screen the text should be. From the AlertTab
Combo box.
"""
log.debug(u'addAlertImage')
if location == 0:
self.alertText.setPos(0, 0)
elif location == 1:
self.alertText.setPos(0, self.size().height() / 2)
else:
self.alertText.setPos(0, self.size().height() - 76)
self.alertText.setHtml(message)
def displayImage(self, frame):
"""
Places the Image passed on the display screen
``frame``
The image to be displayed
"""
log.debug(u'adddisplayImage')
if isinstance(frame, QtGui.QImage):
self.imageDisplay.setPixmap(QtGui.QPixmap.fromImage(frame))
else:
self.imageDisplay.setPixmap(frame)
self.frameView(self.transparent)
self.videoDisplay.setHtml(u'<html></html>')
def displayVideo(self, path):
"""
Places the Video passed on the display screen
``path``
The path to the image to be displayed
"""
log.debug(u'adddisplayVideo')
self.displayImage(self.transparent)
self.videoDisplay.setHtml(HTMLVIDEO %
(path, self.screen[u'size'].width(),
self.screen[u'size'].height()))
def frameView(self, frame, transition=False):
"""
Called from a slide controller to display a frame
if the alert is in progress the alert is added on top
``frame``
Image frame to be rendered
``transition``
Are transitions required.
"""
log.debug(u'frameView')
if transition:
if self.frame is not None:
self.displayText.setPixmap(
QtGui.QPixmap.fromImage(self.frame))
self.repaint()
Receiver.send_message(u'openlp_process_events')
time.sleep(0.1)
self.frame = None
if frame[u'trans'] is not None:
self.displayText.setPixmap(
QtGui.QPixmap.fromImage(frame[u'trans']))
self.repaint()
Receiver.send_message(u'openlp_process_events')
time.sleep(0.1)
self.frame = frame[u'trans']
self.displayText.setPixmap(
QtGui.QPixmap.fromImage(frame[u'main']))
else:
if isinstance(frame, QtGui.QPixmap):
self.displayText.setPixmap(frame)
else:
self.displayText.setPixmap(QtGui.QPixmap.fromImage(frame))
if not self.isVisible() and self.screens.current['primary']:
self.setVisible(True)
class VideoDisplay(Phonon.VideoWidget):
"""
This is the form that is used to display videos on the projector.
"""
log.info(u'VideoDisplay Loaded')
def __init__(self, parent, screens,
aspect=Phonon.VideoWidget.AspectRatioWidget):
"""
The constructor for the display form.
``parent``
The parent widget.
``screens``
The list of screens.
"""
log.debug(u'VideoDisplay Initialisation started')
Phonon.VideoWidget.__init__(self)
self.setWindowTitle(u'OpenLP Video Display')
self.parent = parent
self.screens = screens
self.hidden = False
self.message = None
self.mediaActive = False
self.mediaObject = Phonon.MediaObject()
self.setAspectRatio(aspect)
self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory)
Phonon.createPath(self.mediaObject, self)
Phonon.createPath(self.mediaObject, self.audioObject)
flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Dialog
## # WindowsStaysOnBottomHint is not available in QT4.4
# try:
# flags = flags | QtCore.Qt.WindowStaysOnBottomHint
# except AttributeError:
# pass
self.setWindowFlags(flags)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'videodisplay_play'), self.onMediaPlay)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'videodisplay_pause'), self.onMediaPause)
# QtCore.QObject.connect(Receiver.get_receiver(),
# QtCore.SIGNAL(u'videodisplay_background'), self.onMediaBackground)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.setup)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'finished()'), self.onMediaStop)
self.setVisible(False)
def keyPressEvent(self, event):
if isinstance(event, QtGui.QKeyEvent):
#here accept the event and do something
if event.key() == QtCore.Qt.Key_Escape:
self.onMediaStop()
event.accept()
event.ignore()
else:
event.ignore()
def setup(self):
"""
Sets up the screen on a particular screen.
"""
log.debug(u'VideoDisplay Setup %s for %s ' % (self.screens,
self.screens.monitor_number))
self.screen = self.screens.current
#Sort out screen locations and sizes
self.setGeometry(self.screen[u'size'])
# To display or not to display?
if not self.screen[u'primary']: # and self.isVisible():
#self.showFullScreen()
self.setVisible(False)
self.primary = False
else:
self.setVisible(False)
self.primary = True
def closeEvent(self, event):
"""
Shutting down so clean up connections
"""
self.onMediaStop()
for path in self.outputPaths():
path.disconnect()
# def onMediaBackground(self, message=None):
# """
# Play a video triggered from the video plugin with the
# file name passed in on the event.
# Also triggered from the Finish event so the video will loop
# if it is triggered from the plugin
# """
# log.debug(u'VideoDisplay Queue new media message %s' % message)
# #If not file take the stored one
# if not message:
# message = self.message
# # still no file name then stop as it was a normal video stopping
# if message:
# self.mediaObject.setCurrentSource(Phonon.MediaSource(message))
# self.message = message
# self._play()
def onMediaQueue(self, message):
"""
Set up a video to play from the serviceitem.
"""
log.debug(u'VideoDisplay Queue new media message %s' % message)
file = os.path.join(message.get_frame_path(),
message.get_frame_title())
self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
self.mediaActive = True
self._play()
def onMediaPlay(self):
"""
Respond to the Play button on the slide controller unless the display
has been hidden by the slidecontroller
"""
if not self.hidden:
log.debug(u'VideoDisplay Play the new media, Live ')
self._play()
def _play(self):
"""
We want to play the video so start it and display the screen
"""
log.debug(u'VideoDisplay _play called')
self.mediaObject.play()
self.setVisible(True)
def onMediaPause(self):
"""
Pause the video and refresh the screen
"""
log.debug(u'VideoDisplay Media paused by user')
self.mediaObject.pause()
self.show()
def onMediaStop(self):
"""
Stop the video and clean up
"""
log.debug(u'VideoDisplay Media stopped by user')
self.message = None
self.mediaActive = False
self.mediaObject.stop()
self.onMediaFinish()
def onMediaFinish(self):
"""
Clean up the Object queue
"""
log.debug(u'VideoDisplay Reached end of media playlist')
self.mediaObject.clearQueue()
self.setVisible(False)
def mediaHide(self, message=u''):
"""
Hide the video display
"""
self.mediaObject.pause()
self.hidden = True
self.setVisible(False)
def mediaShow(self):
"""
Show the video display if it was already hidden
"""
if self.hidden:
self.hidden = False
if self.mediaActive:
self._play()
class AudioPlayer(QtCore.QObject):
"""
This Class will play audio only allowing components to work with a
soundtrack which does not take over the user interface.
soundtrack independent of the user interface.
"""
log.info(u'AudioPlayer Loaded')
@ -675,9 +418,9 @@ class AudioPlayer(QtCore.QObject):
Set up a video to play from the serviceitem.
"""
log.debug(u'AudioPlayer Queue new media message %s' % message)
file = os.path.join(message[0].get_frame_path(),
mfile = os.path.join(message[0].get_frame_path(),
message[0].get_frame_title())
self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
self.mediaObject.setCurrentSource(Phonon.MediaSource(mfile))
self.onMediaPlay()
def onMediaPlay(self):

View File

@ -29,7 +29,7 @@ import logging
from PyQt4 import QtCore, QtGui
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \
ThemeManager, SlideController, PluginForm, MediaDockManager, DisplayManager
ThemeManager, SlideController, PluginForm, MediaDockManager
from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \
SettingsManager, PluginManager, Receiver, translate
from openlp.core.utils import AppLocation, add_actions, LanguageManager
@ -94,8 +94,8 @@ class Ui_MainWindow(object):
self.ControlSplitter.setObjectName(u'ControlSplitter')
self.MainContentLayout.addWidget(self.ControlSplitter)
# Create slide controllers
self.PreviewController = SlideController(self, self.settingsmanager)
self.LiveController = SlideController(self, self.settingsmanager, True)
self.PreviewController = SlideController(self, self.settingsmanager, self.screens)
self.LiveController = SlideController(self, self.settingsmanager, self.screens, True)
# Create menu
self.MenuBar = QtGui.QMenuBar(MainWindow)
self.MenuBar.setGeometry(QtCore.QRect(0, 0, 1087, 27))
@ -509,7 +509,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.songsSettingsSection = u'songs'
self.serviceNotSaved = False
self.settingsmanager = SettingsManager(screens)
self.displayManager = DisplayManager(screens)
self.aboutForm = AboutForm(self, applicationVersion)
self.settingsForm = SettingsForm(self.screens, self, self)
self.recentFiles = QtCore.QStringList()
@ -594,7 +593,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
#ThemeManager needs to call RenderManager
self.RenderManager = RenderManager(
self.ThemeManagerContents, self.screens)
self.displayManager.renderManager = self.RenderManager
#Define the media Dock Manager
self.mediaDockManager = MediaDockManager(self.MediaToolBox)
log.info(u'Load Plugins')
@ -605,7 +603,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.plugin_helpers[u'service'] = self.ServiceManagerContents
self.plugin_helpers[u'settings form'] = self.settingsForm
self.plugin_helpers[u'toolbox'] = self.mediaDockManager
self.plugin_helpers[u'displaymanager'] = self.displayManager
self.plugin_helpers[u'pluginmanager'] = self.plugin_manager
self.plugin_helpers[u'formparent'] = self
self.plugin_manager.find_plugins(pluginpath, self.plugin_helpers)
@ -652,8 +649,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
version_text = unicode(translate('OpenLP.MainWindow',
'Version %s of OpenLP is now available for download (you are '
'currently running version %s). \n\nYou can download the latest '
'version from '
'<a href="http://openlp.org/">http://openlp.org/</a>.'))
'version from http://openlp.org/.'))
QtGui.QMessageBox.question(self,
translate('OpenLP.MainWindow', 'OpenLP Version Updated'),
version_text % (version, self.applicationVersion[u'full']))
@ -663,9 +659,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
Show the main form, as well as the display form
"""
QtGui.QWidget.show(self)
self.displayManager.setup()
if self.displayManager.mainDisplay.isVisible():
self.displayManager.mainDisplay.setFocus()
self.LiveController.display.setup()
self.PreviewController.display.setup()
if self.LiveController.display.isVisible():
self.LiveController.display.setFocus()
self.activateWindow()
if QtCore.QSettings().value(
self.generalSettingsSection + u'/auto open',
@ -745,8 +742,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
The screen has changed to so tell the displays to update_display
their locations
"""
log.debug(u'screenChanged')
self.RenderManager.update_display()
self.displayManager.setup()
self.setFocus()
self.activateWindow()
@ -792,8 +789,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.plugin_manager.finalise_plugins()
# Save settings
self.saveSettings()
#Close down the displays
self.displayManager.close()
#Close down the display
self.LiveController.display.close()
def serviceChanged(self, reset=False, serviceName=None):
"""

View File

@ -44,9 +44,9 @@ class ScreenList(object):
self.override = None
self.screen_list = []
self.display_count = 0
#actual display number
# actual display number
self.current_display = 0
#save config display number
# save config display number
self.monitor_number = 0
def add_screen(self, screen):

View File

@ -317,9 +317,8 @@ class ServiceManager(QtGui.QWidget):
self.serviceItemEditForm.setServiceItem(
self.serviceItems[item][u'service_item'])
if self.serviceItemEditForm.exec_():
self.serviceItems[item][u'service_item'] = \
self.serviceItemEditForm.getServiceItem()
self.repaintServiceList(item, 0)
self.addServiceItem(self.serviceItemEditForm.getServiceItem(),
replace=True)
def nextItem(self):
"""
@ -780,7 +779,7 @@ class ServiceManager(QtGui.QWidget):
Rebuild the service list as things have changed and a
repaint is the easiest way to do this.
"""
#force reset of renderer as theme data has changed
# force reset of renderer as theme data has changed
self.parent.RenderManager.themedata = None
if self.serviceItems:
tempServiceItems = self.serviceItems
@ -790,8 +789,8 @@ class ServiceManager(QtGui.QWidget):
for item in tempServiceItems:
self.addServiceItem(
item[u'service_item'], False, item[u'expanded'])
#Set to False as items may have changed rendering
#does not impact the saved song so True may also be valid
# Set to False as items may have changed rendering
# does not impact the saved song so True may also be valid
self.parent.serviceChanged(False, self.serviceName)
def addServiceItem(self, item, rebuild=False, expand=True, replace=False):
@ -873,6 +872,7 @@ class ServiceManager(QtGui.QWidget):
ItemCapabilities.AllowsPreview):
self.parent.PreviewController.addServiceManagerItem(
self.serviceItems[item][u'service_item'], 0)
self.parent.LiveController.PreviewListWidget.setFocus()
else:
QtGui.QMessageBox.critical(self,
translate('OpenLP.ServiceManager', 'Missing Display Handler'),

View File

@ -25,36 +25,17 @@
###############################################################################
import logging
import time
import os
from PyQt4 import QtCore, QtGui
from PyQt4.phonon import Phonon
from openlp.core.ui import HideMode
from openlp.core.ui import HideMode, MainDisplay
from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \
ItemCapabilities, translate
log = logging.getLogger(__name__)
class SlideThread(QtCore.QThread):
"""
A special Qt thread class to speed up the display of text based frames.
This is threaded so it loads the frames in background
"""
def __init__(self, parent, prefix, count):
QtCore.QThread.__init__(self, parent)
self.prefix = prefix
self.count = count
def run(self):
"""
Run the thread.
"""
time.sleep(1)
for i in range(0, self.count):
Receiver.send_message(u'%s_slide_cache' % self.prefix, i)
class SlideList(QtGui.QTableWidget):
"""
Customised version of QTableWidget which can respond to keyboard
@ -97,7 +78,7 @@ class SlideController(QtGui.QWidget):
SlideController is the slide controller widget. This widget is what the
user uses to control the displaying of verses/slides/etc on the screen.
"""
def __init__(self, parent, settingsmanager, isLive=False):
def __init__(self, parent, settingsmanager, screens, isLive=False):
"""
Set up the Slide Controller.
"""
@ -105,8 +86,10 @@ class SlideController(QtGui.QWidget):
self.settingsmanager = settingsmanager
self.isLive = isLive
self.parent = parent
self.mainDisplay = self.parent.displayManager.mainDisplay
self.displayManager = self.parent.displayManager
self.screens = screens
self.ratio = float(self.screens.current[u'size'].width()) / \
float(self.screens.current[u'size'].height())
self.display = MainDisplay(self, screens, isLive)
self.loopList = [
u'Start Loop',
u'Loop Separator',
@ -115,13 +98,14 @@ class SlideController(QtGui.QWidget):
self.songEditList = [
u'Edit Song',
]
self.volume = 10
self.timer_id = 0
self.songEdit = False
self.selectedRow = 0
self.serviceItem = None
self.alertTab = None
self.Panel = QtGui.QWidget(parent.ControlSplitter)
self.slideList = {}
self.canDisplay = True
# Layout for holding panel
self.PanelLayout = QtGui.QVBoxLayout(self.Panel)
self.PanelLayout.setSpacing(0)
@ -177,11 +161,11 @@ class SlideController(QtGui.QWidget):
sizeToolbarPolicy.setHeightForWidth(
self.Toolbar.sizePolicy().hasHeightForWidth())
self.Toolbar.setSizePolicy(sizeToolbarPolicy)
if self.isLive:
self.Toolbar.addToolbarButton(
u'First Slide', u':/slides/slide_first.png',
translate('OpenLP.SlideController', 'Move to first'),
self.onSlideSelectedFirst)
# if self.isLive:
# self.Toolbar.addToolbarButton(
# u'First Slide', u':/slides/slide_first.png',
# translate('OpenLP.SlideController', 'Move to first'),
# self.onSlideSelectedFirst)
self.Toolbar.addToolbarButton(
u'Previous Slide', u':/slides/slide_previous.png',
translate('OpenLP.SlideController', 'Move to previous'),
@ -190,11 +174,11 @@ class SlideController(QtGui.QWidget):
u'Next Slide', u':/slides/slide_next.png',
translate('OpenLP.SlideController', 'Move to next'),
self.onSlideSelectedNext)
if self.isLive:
self.Toolbar.addToolbarButton(
u'Last Slide', u':/slides/slide_last.png',
translate('OpenLP.SlideController', 'Move to last'),
self.onSlideSelectedLast)
# if self.isLive:
# self.Toolbar.addToolbarButton(
# u'Last Slide', u':/slides/slide_last.png',
# translate('OpenLP.SlideController', 'Move to last'),
# self.onSlideSelectedLast)
if self.isLive:
self.Toolbar.addToolbarSeparator(u'Close Separator')
self.HideMenu = QtGui.QToolButton(self.Toolbar)
@ -213,15 +197,17 @@ class SlideController(QtGui.QWidget):
self.ThemeScreen.setCheckable(True)
QtCore.QObject.connect(self.ThemeScreen,
QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay)
self.DesktopScreen = QtGui.QAction(QtGui.QIcon(
u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu)
self.DesktopScreen.setCheckable(True)
QtCore.QObject.connect(self.DesktopScreen,
QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay)
if self.screens.display_count > 1:
self.DesktopScreen = QtGui.QAction(QtGui.QIcon(
u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu)
self.DesktopScreen.setCheckable(True)
QtCore.QObject.connect(self.DesktopScreen,
QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay)
self.HideMenu.setDefaultAction(self.BlankScreen)
self.HideMenu.menu().addAction(self.BlankScreen)
self.HideMenu.menu().addAction(self.ThemeScreen)
self.HideMenu.menu().addAction(self.DesktopScreen)
if self.screens.display_count > 1:
self.HideMenu.menu().addAction(self.DesktopScreen)
if not self.isLive:
self.Toolbar.addToolbarSeparator(u'Close Separator')
self.Toolbar.addToolbarButton(
@ -251,7 +237,7 @@ class SlideController(QtGui.QWidget):
self.DelaySpinBox.setToolTip(translate('OpenLP.SlideController',
'Delay between slides in seconds'))
self.ControllerLayout.addWidget(self.Toolbar)
#Build a Media ToolBar
# Build a Media ToolBar
self.Mediabar = OpenLPToolbar(self)
self.Mediabar.addToolbarButton(
u'Media Start', u':/slides/media_playback_start.png',
@ -271,10 +257,19 @@ class SlideController(QtGui.QWidget):
self.seekSlider.setObjectName(u'seekSlider')
self.Mediabar.addToolbarWidget(
u'Seek Slider', self.seekSlider)
self.volumeSlider = Phonon.VolumeSlider()
self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
self.volumeSlider.setObjectName(u'volumeSlider')
self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
self.volumeSlider = Phonon.VolumeSlider()
self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
self.volumeSlider.setObjectName(u'volumeSlider')
self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
else:
self.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.volumeSlider.setTickInterval(1)
self.volumeSlider.setTickPosition(QtGui.QSlider.TicksAbove)
self.volumeSlider.setMinimum(0)
self.volumeSlider.setMaximum(10)
self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
self.volumeSlider.setObjectName(u'volumeSlider')
self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
self.ControllerLayout.addWidget(self.Mediabar)
# Build the Song Toolbar
if isLive:
@ -322,7 +317,7 @@ class SlideController(QtGui.QWidget):
self.SlidePreview.setSizePolicy(sizePolicy)
self.SlidePreview.setFixedSize(
QtCore.QSize(self.settingsmanager.slidecontroller_image,
self.settingsmanager.slidecontroller_image / 1.3 ))
self.settingsmanager.slidecontroller_image / self.ratio))
self.SlidePreview.setFrameShape(QtGui.QFrame.Box)
self.SlidePreview.setFrameShadow(QtGui.QFrame.Plain)
self.SlidePreview.setLineWidth(1)
@ -390,17 +385,37 @@ class SlideController(QtGui.QWidget):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.refreshServiceItem)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_slide_cache' % self.typePrefix), self.slideCache)
QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged)
if self.isLive:
QtCore.QObject.connect(self.volumeSlider,
QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
def screenSizeChanged(self):
"""
Settings dialog has changed the screen size of adjust output and
screen previews
"""
log.debug(u'screenSizeChanged live = %s' % self.isLive)
# rebuild display as screen size changed
self.display = MainDisplay(self, self.screens, self.isLive)
self.display.alertTab = self.alertTab
self.ratio = float(self.screens.current[u'size'].width()) / \
float(self.screens.current[u'size'].height())
self.display.setup()
self.SlidePreview.setFixedSize(
QtCore.QSize(self.settingsmanager.slidecontroller_image,
self.settingsmanager.slidecontroller_image / self.ratio))
def widthChanged(self):
"""
Handle changes of width from the splitter between the live and preview
controller. Event only issues when changes have finished
"""
log.debug(u'widthChanged live = %s' % self.isLive)
width = self.parent.ControlSplitter.sizes()[self.split]
height = width * self.parent.RenderManager.screen_ratio
self.PreviewListWidget.setColumnWidth(0, width)
#Sort out image heights (Songs, bibles excluded)
# Sort out image heights (Songs, bibles excluded)
if self.serviceItem and not self.serviceItem.is_text():
for framenumber in range(len(self.serviceItem.get_frames())):
self.PreviewListWidget.setRowHeight(framenumber, height)
@ -453,8 +468,6 @@ class SlideController(QtGui.QWidget):
if item.is_media():
self.Toolbar.setVisible(False)
self.Mediabar.setVisible(True)
#self.volumeSlider.setAudioOutput(
# self.mainDisplay.videoDisplay.audio)
def enablePreviewToolBar(self, item):
"""
@ -474,22 +487,20 @@ class SlideController(QtGui.QWidget):
"""
Method to update the service item if the screen has changed
"""
log.debug(u'refreshServiceItem')
log.debug(u'refreshServiceItem live = %s' % self.isLive)
if self.serviceItem:
if self.serviceItem.is_text() or self.serviceItem.is_image():
item = self.serviceItem
item.render()
self.addServiceManagerItem(item, self.selectedRow)
self._processItem(item, self.selectedRow)
def addServiceItem(self, item):
"""
Method to install the service item into the controller
Called by plugins
"""
log.debug(u'addServiceItem')
before = time.time()
log.debug(u'addServiceItem live = %s' % self.isLive)
item.render()
log.log(15, u'Rendering took %4s' % (time.time() - before))
slideno = 0
if self.songEdit:
slideno = self.selectedRow
@ -509,8 +520,8 @@ class SlideController(QtGui.QWidget):
request the correct toolbar for the plugin.
Called by ServiceManager
"""
log.debug(u'addServiceManagerItem')
#If service item is the same as the current on only change slide
log.debug(u'addServiceManagerItem live = %s' % self.isLive)
# If service item is the same as the current on only change slide
if item.__eq__(self.serviceItem):
self.PreviewListWidget.selectRow(slideno)
self.onSlideSelected()
@ -522,17 +533,15 @@ class SlideController(QtGui.QWidget):
Loads a ServiceItem into the system from ServiceManager
Display the slide number passed
"""
log.debug(u'processManagerItem')
log.debug(u'processManagerItem live = %s' % self.isLive)
self.onStopLoop()
#If old item was a command tell it to stop
# If old item was a command tell it to stop
if self.serviceItem:
if self.serviceItem.is_command():
Receiver.send_message(u'%s_stop' %
self.serviceItem.name.lower(), [serviceItem, self.isLive])
if self.serviceItem.is_media():
self.onMediaStop()
if serviceItem.is_media():
self.onMediaStart(serviceItem)
if self.isLive:
blanked = self.BlankScreen.isChecked()
else:
@ -541,12 +550,8 @@ class SlideController(QtGui.QWidget):
[serviceItem, self.isLive, blanked, slideno])
self.slideList = {}
width = self.parent.ControlSplitter.sizes()[self.split]
#Set pointing cursor when we have somthing to point at
# Set pointing cursor when we have somthing to point at
self.PreviewListWidget.setCursor(QtCore.Qt.PointingHandCursor)
before = time.time()
#Clear the old serviceItem cache to release memory
if self.serviceItem and self.serviceItem is not serviceItem:
self.serviceItem.clear_cache()
self.serviceItem = serviceItem
self.PreviewListWidget.clear()
self.PreviewListWidget.setRowCount(0)
@ -560,20 +565,19 @@ class SlideController(QtGui.QWidget):
self.PreviewListWidget.rowCount() + 1)
item = QtGui.QTableWidgetItem()
slideHeight = 0
#It is a based Text Render
if self.serviceItem.is_text():
if frame[u'verseTag']:
bits = frame[u'verseTag'].split(u':')
tag = u'%s\n%s' % (bits[0][0], bits[1][0:] )
tag1 = u'%s%s' % (bits[0][0], bits[1][0:] )
row = tag
if self.isLive:
if tag1 not in self.slideList:
self.slideList[tag1] = framenumber
self.SongMenu.menu().addAction(tag1,
self.onSongBarHandler)
else:
row += 1
if self.isLive and frame[u'verseTag'] is not None:
if tag1 not in self.slideList:
self.slideList[tag1] = framenumber
self.SongMenu.menu().addAction(tag1,
self.onSongBarHandler)
item.setText(frame[u'text'])
else:
label = QtGui.QLabel()
@ -600,15 +604,14 @@ class SlideController(QtGui.QWidget):
else:
self.PreviewListWidget.selectRow(slideno)
self.enableToolBar(serviceItem)
# Pass to display for viewing
self.display.buildHtml(self.serviceItem)
if serviceItem.is_media():
self.onMediaStart(serviceItem)
self.onSlideSelected()
self.PreviewListWidget.setFocus()
Receiver.send_message(u'slidecontroller_%s_started' % self.typePrefix,
[serviceItem])
if self.serviceItem.is_text():
st = SlideThread(
self, self.typePrefix, len(self.serviceItem.get_frames()))
st.start()
log.log(15, u'Display Rendering took %4s' % (time.time() - before))
def onTextRequest(self):
"""
@ -620,7 +623,7 @@ class SlideController(QtGui.QWidget):
dataItem = {}
if self.serviceItem.is_text():
dataItem[u'tag'] = unicode(frame[u'verseTag'])
dataItem[u'text'] = unicode(frame[u'text'])
dataItem[u'text'] = unicode(frame[u'html'])
else:
dataItem[u'tag'] = unicode(framenumber)
dataItem[u'text'] = u''
@ -630,7 +633,7 @@ class SlideController(QtGui.QWidget):
Receiver.send_message(u'slidecontroller_%s_text_response'
% self.typePrefix, data)
#Screen event methods
# Screen event methods
def onSlideSelectedFirst(self):
"""
Go to the first slide.
@ -664,9 +667,9 @@ class SlideController(QtGui.QWidget):
"""
Allow the main display to blank the main display at startup time
"""
log.debug(u'mainDisplaySetBackground')
if not self.mainDisplay.primary:
self.onBlankDisplay(True)
log.debug(u'mainDisplaySetBackground live = %s' % self.isLive)
if not self.display.primary:
self.onHideDisplay(True)
def onSlideBlank(self):
"""
@ -688,7 +691,8 @@ class SlideController(QtGui.QWidget):
self.HideMenu.setDefaultAction(self.BlankScreen)
self.BlankScreen.setChecked(checked)
self.ThemeScreen.setChecked(False)
self.DesktopScreen.setChecked(False)
if self.screens.display_count > 1:
self.DesktopScreen.setChecked(False)
QtCore.QSettings().setValue(
self.parent.generalSettingsSection + u'/screen blank',
QtCore.QVariant(checked))
@ -706,7 +710,8 @@ class SlideController(QtGui.QWidget):
self.HideMenu.setDefaultAction(self.ThemeScreen)
self.BlankScreen.setChecked(False)
self.ThemeScreen.setChecked(checked)
self.DesktopScreen.setChecked(False)
if self.screens.display_count > 1:
self.DesktopScreen.setChecked(False)
if checked:
Receiver.send_message(u'maindisplay_hide', HideMode.Theme)
else:
@ -721,7 +726,8 @@ class SlideController(QtGui.QWidget):
self.HideMenu.setDefaultAction(self.DesktopScreen)
self.BlankScreen.setChecked(False)
self.ThemeScreen.setChecked(False)
self.DesktopScreen.setChecked(checked)
if self.screens.display_count > 1:
self.DesktopScreen.setChecked(checked)
if checked:
Receiver.send_message(u'maindisplay_hide', HideMode.Screen)
else:
@ -758,13 +764,6 @@ class SlideController(QtGui.QWidget):
% self.serviceItem.name.lower(),
[self.serviceItem, self.isLive])
def slideCache(self, slide):
"""
Generate a slide cache item rendered and ready for use
in the background.
"""
self.serviceItem.get_rendered_frame(int(slide))
def onSlideSelected(self):
"""
Generate the preview when you click on a slide.
@ -778,24 +777,15 @@ class SlideController(QtGui.QWidget):
if self.serviceItem.is_command() and self.isLive:
self.updatePreview()
else:
before = time.time()
frame = self.serviceItem.get_rendered_frame(row)
frame, raw_html = self.serviceItem.get_rendered_frame(row)
if self.serviceItem.is_text():
frame = self.display.text(raw_html)
else:
self.display.image(frame)
if isinstance(frame, QtGui.QImage):
self.SlidePreview.setPixmap(QtGui.QPixmap.fromImage(frame))
else:
if isinstance(frame[u'main'], basestring):
self.SlidePreview.setPixmap(
QtGui.QPixmap(frame[u'main']))
else:
self.SlidePreview.setPixmap(
QtGui.QPixmap.fromImage(frame[u'main']))
log.log(
15, u'Slide Rendering took %4s' % (time.time() - before))
if self.isLive:
if self.serviceItem.is_text():
self.mainDisplay.frameView(frame, True)
else:
self.displayManager.displayImage(frame[u'main'])
self.SlidePreview.setPixmap(QtGui.QPixmap(frame))
self.selectedRow = row
Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
row)
@ -810,8 +800,7 @@ class SlideController(QtGui.QWidget):
row)
def updatePreview(self):
rm = self.parent.RenderManager
if not rm.screens.current[u'primary']:
if not self.screens.current[u'primary']:
# Grab now, but try again in a couple of seconds if slide change
# is slow
QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
@ -819,12 +808,12 @@ class SlideController(QtGui.QWidget):
else:
label = self.PreviewListWidget.cellWidget(
self.PreviewListWidget.currentRow(), 1)
self.SlidePreview.setPixmap(label.pixmap())
if label:
self.SlidePreview.setPixmap(label.pixmap())
def grabMainDisplay(self):
rm = self.parent.RenderManager
winid = QtGui.QApplication.desktop().winId()
rect = rm.screens.current[u'size']
rect = self.screens.current[u'size']
winimg = QtGui.QPixmap.grabWindow(winid, rect.x(),
rect.y(), rect.width(), rect.height())
self.SlidePreview.setPixmap(winimg)
@ -941,7 +930,9 @@ class SlideController(QtGui.QWidget):
"""
log.debug(u'SlideController onMediaStart')
if self.isLive:
Receiver.send_message(u'videodisplay_start', item)
file = os.path.join(item.get_frame_path(), item.get_frame_title())
self.display.video(file, self.volume)
self.volumeSlider.setValue(self.volume)
else:
self.mediaObject.stop()
self.mediaObject.clearQueue()
@ -951,13 +942,21 @@ class SlideController(QtGui.QWidget):
self.seekSlider.show()
self.onMediaPlay()
def mediaVolume(self):
"""
Respond to the release of Volume Slider
"""
log.debug(u'SlideController mediaVolume')
self.volume = self.volumeSlider.value()
self.display.videoVolume(self.volume)
def onMediaPause(self):
"""
Respond to the Pause from the media Toolbar
"""
log.debug(u'SlideController onMediaPause')
if self.isLive:
Receiver.send_message(u'videodisplay_pause')
self.display.videoPause()
else:
self.mediaObject.pause()
@ -967,7 +966,7 @@ class SlideController(QtGui.QWidget):
"""
log.debug(u'SlideController onMediaPlay')
if self.isLive:
Receiver.send_message(u'videodisplay_play')
self.display.videoPlay()
else:
self.SlidePreview.hide()
self.video.show()
@ -979,7 +978,7 @@ class SlideController(QtGui.QWidget):
"""
log.debug(u'SlideController onMediaStop')
if self.isLive:
Receiver.send_message(u'videodisplay_stop')
self.display.videoStop()
else:
self.mediaObject.stop()
self.video.hide()

View File

@ -139,13 +139,13 @@ class ThemeManager(QtGui.QWidget):
"""
log.debug(u'changeGlobalFromTab %s', themeName)
for count in range (0, self.themeListWidget.count()):
#reset the old name
# reset the old name
item = self.themeListWidget.item(count)
oldName = item.text()
newName = unicode(item.data(QtCore.Qt.UserRole).toString())
if oldName != newName:
self.themeListWidget.item(count).setText(newName)
#Set the new name
# Set the new name
if themeName == newName:
name = unicode(translate('OpenLP.ThemeManager',
'%s (default)')) % newName
@ -161,11 +161,11 @@ class ThemeManager(QtGui.QWidget):
for count in range (0, self.themeListWidget.count()):
item = self.themeListWidget.item(count)
oldName = item.text()
#reset the old name
# reset the old name
if oldName != unicode(item.data(QtCore.Qt.UserRole).toString()):
self.themeListWidget.item(count).setText(
unicode(item.data(QtCore.Qt.UserRole).toString()))
#Set the new name
# Set the new name
if count == selected_row:
self.global_theme = unicode(
self.themeListWidget.item(count).text())
@ -295,7 +295,7 @@ class ThemeManager(QtGui.QWidget):
path = unicode(path)
if path:
SettingsManager.set_last_dir(self.settingsSection, path, 1)
themePath = os.path.join(path, theme + u'.theme')
themePath = os.path.join(path, theme + u'.thz')
zip = None
try:
zip = zipfile.ZipFile(themePath, u'w')
@ -346,11 +346,10 @@ class ThemeManager(QtGui.QWidget):
log.debug(u'Load themes from dir')
self.themelist = []
self.themeListWidget.clear()
#root, dirs, files = os.walk(self.path)
dirList = os.listdir(self.path)
for name in dirList:
if name.endswith(u'.png'):
#check to see file is in theme root directory
# check to see file is in theme root directory
theme = os.path.join(self.path, name)
if os.path.exists(theme):
textName = os.path.splitext(name)[0]
@ -660,9 +659,8 @@ class ThemeManager(QtGui.QWidget):
"""
Call the RenderManager to build a Sample Image
"""
log.debug(u'generateImage %s ', themedata)
frame = self.parent.RenderManager.generate_preview(themedata)
return frame
log.debug(u'generateImage \n%s ', themedata)
return self.parent.RenderManager.generate_preview(themedata)
def getPreviewImage(self, theme):
"""
@ -732,8 +730,6 @@ class ThemeManager(QtGui.QWidget):
theme.display_slideTransition = theme.display_slideTransition
theme.font_footer_color = theme.font_footer_color.strip()
theme.font_footer_height = int(theme.font_footer_height.strip())
theme.font_footer_indentation = \
int(theme.font_footer_indentation.strip())
theme.font_footer_italics = str_to_bool(theme.font_footer_italics)
theme.font_footer_name = theme.font_footer_name.strip()
#theme.font_footer_override
@ -746,7 +742,6 @@ class ThemeManager(QtGui.QWidget):
theme.font_main_color = theme.font_main_color.strip()
theme.font_main_height = int(theme.font_main_height.strip())
theme.font_main_italics = str_to_bool(theme.font_main_italics)
theme.font_main_indentation = int(theme.font_main_indentation)
theme.font_main_name = theme.font_main_name.strip()
#theme.font_main_override
theme.font_main_proportion = int(theme.font_main_proportion.strip())
@ -757,3 +752,8 @@ class ThemeManager(QtGui.QWidget):
#theme.theme_mode
theme.theme_name = theme.theme_name.strip()
#theme.theme_version
# Remove the Transparent settings as they are not relevent
if theme.background_mode == u'transparent':
theme.background_mode = u'opaque'
theme.background_type = u'solid'
theme.background_startColor = u'#000000'

View File

@ -80,9 +80,10 @@ class AlertsPlugin(Plugin):
log.info(u'Alerts Initialising')
Plugin.initialise(self)
self.toolsAlertItem.setVisible(True)
self.liveController.alertTab = self.alertsTab
def finalise(self):
log.info(u'Plugin Finalise')
log.info(u'Alerts Finalising')
Plugin.finalise(self)
self.toolsAlertItem.setVisible(False)

View File

@ -32,18 +32,9 @@ from openlp.core.lib import Receiver, translate
log = logging.getLogger(__name__)
HTMLCODE = u"""
<p style=\"color:%s;
background-color:%s;
font-family:%s;
font-size: %spt; \">
%s
</p>
"""
class AlertsManager(QtCore.QObject):
"""
AlertsTab is the Alerts settings tab in the settings dialog.
AlertsManager manages the settings of Alerts.
"""
log.info(u'Alert Manager loaded')
@ -94,10 +85,7 @@ class AlertsManager(QtCore.QObject):
return
text = self.alertList.pop(0)
alertTab = self.parent.alertsTab
text = HTMLCODE % (alertTab.font_color, alertTab.bg_color,
alertTab.font_face, alertTab.font_size, text)
self.parent.previewController.parent.displayManager.addAlert(text,
alertTab.location)
self.parent.liveController.display.alert(text)
# check to see if we have a timer running
if self.timer_id == 0:
self.timer_id = self.startTimer(int(alertTab.timeout) * 1000)
@ -111,10 +99,8 @@ class AlertsManager(QtCore.QObject):
"""
log.debug(u'timer event')
alertTab = self.parent.alertsTab
if event.timerId() == self.timer_id:
self.parent.previewController.parent.displayManager.addAlert(u'',
alertTab.location)
self.parent.liveController.display.alert(u'')
self.killTimer(self.timer_id)
self.timer_id = 0
self.generateAlert()

View File

@ -261,7 +261,7 @@ class AlertsTab(SettingsTab):
self.font_face = unicode(settings.value(
u'font face', QtCore.QVariant(QtGui.QFont().family())).toString())
self.location = settings.value(
u'location', QtCore.QVariant(0)).toInt()[0]
u'location', QtCore.QVariant(1)).toInt()[0]
settings.endGroup()
self.FontSizeSpinBox.setValue(self.font_size)
self.TimeoutSpinBox.setValue(self.timeout)
@ -296,3 +296,4 @@ class AlertsTab(SettingsTab):
self.FontPreview.setFont(font)
self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' %
(self.bg_color, self.font_color))

View File

@ -504,16 +504,16 @@ class BibleMediaItem(MediaManagerItem):
dual_text = self._decodeQtObject(reference, 'dual_text')
if self.parent.settings_tab.display_style == 1:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'(', u')')
u'{su}(', u'){/su}')
elif self.parent.settings_tab.display_style == 2:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'{', u'}')
u'{su}{', u'}{/su}')
elif self.parent.settings_tab.display_style == 3:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'[', u']')
u'{su}[', u']{/su}')
else:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'', u'')
u'{su}', u'{/su}')
old_chapter = chapter
footer = u'%s (%s %s)' % (book, version, copyright)
# If not found add to footer
@ -532,7 +532,11 @@ class BibleMediaItem(MediaManagerItem):
else:
# If we are 'Verse Per Line' then force a new line.
if self.parent.settings_tab.layout_style == 1:
text = text + u'\n\n'
text = text + u'\n'
else:
# split the line but do not replace line breaks in renderer
service_item.add_capability(ItemCapabilities.NoLineBreaks)
text = text + u'\n'
bible_text = u'%s %s %s' % (bible_text, verse_text, text)
# If we are 'Verse Per Slide' then create a new slide.
if self.parent.settings_tab.layout_style == 0:

View File

@ -27,6 +27,7 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, translate
from openlp.core.ui import SpellTextEdit
class Ui_CustomEditDialog(object):
def setupUi(self, customEditDialog):
@ -73,7 +74,7 @@ class Ui_CustomEditDialog(object):
self.editLayout3.setSpacing(8)
self.editLayout3.setMargin(0)
self.editLayout3.setObjectName(u'editLayout3')
self.verseTextEdit = QtGui.QTextEdit(self.editWidget)
self.verseTextEdit = SpellTextEdit(self)
self.verseTextEdit.setObjectName(u'verseTextEdit')
self.editLayout3.addWidget(self.verseTextEdit)
self.buttonWidget = QtGui.QWidget(self.editWidget)

View File

@ -110,8 +110,14 @@ class ImageMediaItem(MediaManagerItem):
u':/slides/slide_blank.png',
translate('ImagePlugin.MediaItem', 'Replace Live Background'),
self.onReplaceClick, False)
self.resetButton = self.toolbar.addToolbarButton(
translate('ImagePlugin.MediaItem', u'Reset Background'),
u':/system/system_close.png',
translate('ImagePlugin.MediaItem', 'Reset Live Background'),
self.onResetClick, False)
# Add the song widget to the page layout
self.pageLayout.addWidget(self.ImageWidget)
self.resetButton.setVisible(False)
def onDeleteClick(self):
"""
@ -169,6 +175,10 @@ class ImageMediaItem(MediaManagerItem):
else:
return False
def onResetClick(self):
self.resetButton.setVisible(False)
self.parent.liveController.display.resetImage()
def onReplaceClick(self):
if check_item_selected(self.listView,
translate('ImagePlugin.MediaItem',
@ -178,7 +188,8 @@ class ImageMediaItem(MediaManagerItem):
bitem = self.listView.item(item.row())
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
frame = QtGui.QImage(unicode(filename))
self.parent.displayManager.displayImageWithText(frame)
self.parent.liveController.display.image(frame)
self.resetButton.setVisible(True)
def onPreviewClick(self):
MediaManagerItem.onPreviewClick(self)

View File

@ -97,8 +97,17 @@ class MediaMediaItem(MediaManagerItem):
u':/slides/slide_blank.png',
translate('MediaPlugin.MediaItem', 'Replace Live Background'),
self.onReplaceClick, False)
self.resetButton = self.toolbar.addToolbarButton(
u'Reset Background', u':/system/system_close.png',
translate('ImagePlugin.MediaItem', 'Reset Live Background'),
self.onResetClick, False)
# Add the song widget to the page layout
self.pageLayout.addWidget(self.ImageWidget)
self.resetButton.setVisible(False)
def onResetClick(self):
self.resetButton.setVisible(False)
self.parent.liveController.display.resetVideo()
def onReplaceClick(self):
if check_item_selected(self.listView,
@ -106,7 +115,8 @@ class MediaMediaItem(MediaManagerItem):
'You must select a media file to replace the background with.')):
item = self.listView.currentItem()
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
self.parent.displayManager.displayVideo(filename)
self.parent.liveController.display.video(filename, 0)
self.resetButton.setVisible(True)
def generateSlideData(self, service_item, item=None):
if item is None:

View File

@ -314,7 +314,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
author = self.songmanager.get_object(Author, item_id)
if author in self.song.authors:
QtGui.QMessageBox.warning(self,
translate('SongsPlugin.EditSongForm', 'Error'),
translate('SongsPlugin.EditSongForm', 'Error'),
translate('SongsPlugin.EditSongForm', 'This author is '
'already in the list.'))
else:
@ -422,7 +422,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.VerseDeleteButton.setEnabled(True)
def onVerseAddButtonClicked(self):
self.verse_form.setVerse(u'', True)
# Allow insert button as you do not know if multiple verses will
# be entered.
self.verse_form.setVerse(u'')
if self.verse_form.exec_():
afterText, verse, subVerse = self.verse_form.getVerse()
data = u'%s:%s' % (verse, subVerse)

View File

@ -27,6 +27,7 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, translate
from openlp.core.ui import SpellTextEdit
from openlp.plugins.songs.lib import VerseType
class Ui_EditVerseDialog(object):
@ -38,7 +39,7 @@ class Ui_EditVerseDialog(object):
self.EditVerseLayout.setSpacing(8)
self.EditVerseLayout.setMargin(8)
self.EditVerseLayout.setObjectName(u'EditVerseLayout')
self.VerseTextEdit = QtGui.QPlainTextEdit(EditVerseDialog)
self.VerseTextEdit = SpellTextEdit(EditVerseDialog)
self.VerseTextEdit.setObjectName(u'VerseTextEdit')
self.EditVerseLayout.addWidget(self.VerseTextEdit)
self.VerseTypeLayout = QtGui.QHBoxLayout()

View File

@ -45,6 +45,9 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
"""
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
QtCore.QObject.connect(self.VerseTextEdit,
QtCore.SIGNAL('customContextMenuRequested(QPoint)'),
self.contextMenu)
QtCore.QObject.connect(
self.InsertButton,
QtCore.SIGNAL(u'clicked()'),
@ -57,6 +60,10 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
)
self.verse_regex = re.compile(r'---\[([-\w]+):([\d]+)\]---')
def contextMenu(self, point):
item = self.serviceManagerList.itemAt(point)
print item
def insertVerse(self, title, num=1):
if self.VerseTextEdit.textCursor().columnNumber() != 0:
self.VerseTextEdit.insertPlainText(u'\n')

View File

@ -302,8 +302,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
Stop the import on pressing the cancel button.
"""
log.debug('Cancel button pressed!')
if self.currentId() == 3:
Receiver.send_message(u'song_stop_import')
if self.currentId() == 2:
Receiver.send_message(u'songs_stop_import')
def onCurrentIdChanged(self, id):
if id == 2:

View File

@ -86,7 +86,6 @@ class OpenLPSongImport(SongImport):
The database providing the data to import.
"""
SongImport.__init__(self, manager)
#self.master_manager = master_manager
self.import_source = u'sqlite:///%s' % kwargs[u'filename']
log.debug(self.import_source)
self.source_session = None
@ -145,7 +144,12 @@ class OpenLPSongImport(SongImport):
mapper(OldTopic, source_topics_table)
source_songs = self.source_session.query(OldSong).all()
song_total = len(source_songs)
self.import_wizard.importProgressBar.setMaximum(song_total)
song_count = 1
for song in source_songs:
self.import_wizard.incrementProgressBar(
u'Importing song %s of %s' % (song_count, song_total))
new_song = Song()
new_song.title = song.title
if has_media_files:
@ -212,4 +216,8 @@ class OpenLPSongImport(SongImport):
# new_song.media_files.append(MediaFile.populate(
# file_name=media_file.file_name))
self.manager.save_object(new_song)
song_count += 1
if self.stop_import_flag:
return False
engine.dispose()
return True

View File

@ -24,14 +24,18 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
import re
from PyQt4 import QtCore
from openlp.core.lib import translate
from openlp.core.lib import Receiver, translate
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book
from openlp.plugins.songs.lib.xml import SongXMLBuilder
class SongImport(object):
log = logging.getLogger(__name__)
class SongImport(QtCore.QObject):
"""
Helper class for import a song from a third party source into OpenLP
@ -39,7 +43,6 @@ class SongImport(object):
whether the authors etc already exist and add them or refer to them
as necessary
"""
def __init__(self, manager):
"""
Initialise and create defaults for properties
@ -48,6 +51,7 @@ class SongImport(object):
database access is performed
"""
self.manager = manager
self.stop_import_flag = False
self.title = u''
self.song_number = u''
self.alternate_title = u''
@ -67,6 +71,15 @@ class SongImport(object):
'SongsPlugin.SongImport', 'copyright'))
self.copyright_symbol = unicode(translate(
'SongsPlugin.SongImport', '\xa9'))
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'songs_stop_import'), self.stop_import)
def stop_import(self):
"""
Sets the flag for importers to stop their import
"""
log.debug(u'Stopping songs import')
self.stop_import_flag = True
def register(self, import_wizard):
self.import_wizard = import_wizard

View File

@ -0,0 +1,91 @@
%{!?python_sitelib:%global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
Summary: Open source Church presentation and lyrics projection application
Name: OpenLP
Version: 1.9.1.1
Release: 1%{?dist}
Source0: http://downloads.sourceforge.net/openlp/openlp/%{version}/%{name}-%{version}.tar.gz
License: GPLv2
Group: Applications/Multimedia
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
URL: http://openlp.org/
BuildRequires: desktop-file-utils
BuildRequires: python2-devel
BuildRequires: python-setuptools
Requires: PyQt4
Requires: phonon
Requires: python-BeautifulSoup
Requires: python-chardet
Requires: python-lxml
Requires: python-sqlalchemy
Requires: hicolor-icon-theme
%description
OpenLP is a church presentation software, for lyrics projection software,
used to display slides of Songs, Bible verses, videos, images, and
presentations (if OpenOffice.org is installed) using a computer and projector.
%prep
%setup -q
%build
python setup.py build
%install
rm -rf %{buildroot}
python setup.py install --skip-build -O1 --root %{buildroot}
install -m644 -p -D resources/images/openlp-logo-16x16.png \
%{buildroot}%{_datadir}/icons/hicolor/16x16/apps/openlp.png
install -m644 -p -D resources/images/openlp-logo-32x32.png \
%{buildroot}%{_datadir}/icons/hicolor/32x32/apps/openlp.png
install -m644 -p -D resources/images/openlp-logo-48x48.png \
%{buildroot}%{_datadir}/icons/hicolor/48x48/apps/openlp.png
install -m644 -p -D resources/images/openlp-logo.svg \
%{buildroot}%{_datadir}/icons/hicolor/scalable/apps/openlp.svg
desktop-file-install \
--dir %{buildroot}/%{_datadir}/applications \
resources/openlp.desktop
mv %{buildroot}%{_bindir}/bible-1to2-converter.py \
%{buildroot}%{_bindir}/bible-1to2-converter
mv %{buildroot}%{_bindir}/openlp-1to2-converter.py \
%{buildroot}%{_bindir}/openlp-1to2-converter
mv %{buildroot}%{_bindir}/openlp-remoteclient.py \
%{buildroot}%{_bindir}/openlp-remoteclient
mv %{buildroot}%{_bindir}/openlp.pyw %{buildroot}%{_bindir}/openlp
%post
touch --no-create %{_datadir}/icons/hicolor ||:
gtk-update-icon-cache -q %{_datadir}/icons/hicolor 2> /dev/null ||:
%postun
touch --no-create %{_datadir}/icons/hicolor ||:
gtk-update-icon-cache -q %{_datadir}/icons/hicolor 2> /dev/null ||:
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root)
%doc copyright.txt LICENSE
%{_bindir}/bible-1to2-converter
%{_bindir}/openlp-1to2-converter
%{_bindir}/openlp-remoteclient
%{_bindir}/openlp
%{_datadir}/applications/openlp.desktop
%{_datadir}/icons/hicolor/*/apps/openlp.*
%{python_sitelib}/openlp/
%{python_sitelib}/OpenLP-%{version}*.egg-info
%doc documentation/*.txt
%changelog
* Sun Mar 28 2010 Tim Bentley <timbentley@openlp.org> 1.9.1.1
- Initial build version - Alpha 1 Release