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.lib import Receiver
from openlp.core.resources import qInitResources 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 from openlp.core.utils import AppLocation, LanguageManager, VersionThread
log = logging.getLogger() log = logging.getLogger()

View File

@ -35,6 +35,67 @@ from PyQt4 import QtCore, QtGui
log = logging.getLogger(__name__) 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): def translate(context, text, comment=None):
""" """
A special shortcut method to wrap around the Qt4 translation functions. A special shortcut method to wrap around the Qt4 translation functions.
@ -166,15 +227,46 @@ def context_menu_separator(base):
action.setSeparator(True) action.setSeparator(True)
return action 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. Resize an image to fit on the current screen.
``image`` ``image``
The image to resize. 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) preview = QtGui.QImage(image)
if not preview.isNull(): if not preview.isNull():
# Only resize if different size
if preview.width() == width and preview.height == height: if preview.width() == width and preview.height == height:
return preview return preview
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio, 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 # and move it to the centre of the preview space
new_image = QtGui.QImage(width, height, new_image = QtGui.QImage(width, height,
QtGui.QImage.Format_ARGB32_Premultiplied) QtGui.QImage.Format_ARGB32_Premultiplied)
new_image.fill(QtCore.Qt.black) new_image.fill(background)
painter = QtGui.QPainter(new_image) painter = QtGui.QPainter(new_image)
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview) painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
return new_image return new_image
@ -205,6 +297,25 @@ def check_item_selected(list_widget, message):
return False return False
return True 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 eventreceiver import Receiver
from settingsmanager import SettingsManager from settingsmanager import SettingsManager
from plugin import PluginStatus, Plugin from plugin import PluginStatus, Plugin
@ -213,6 +324,7 @@ from settingstab import SettingsTab
from serviceitem import ServiceItem from serviceitem import ServiceItem
from serviceitem import ServiceItemType from serviceitem import ServiceItemType
from serviceitem import ItemCapabilities from serviceitem import ItemCapabilities
from htmlbuilder import build_html
from toolbar import OpenLPToolbar from toolbar import OpenLPToolbar
from dockwidget import OpenLPDockWidget from dockwidget import OpenLPDockWidget
from theme import ThemeLevel, ThemeXML 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.serviceManager = plugin_helpers[u'service']
self.settingsForm = plugin_helpers[u'settings form'] self.settingsForm = plugin_helpers[u'settings form']
self.mediadock = plugin_helpers[u'toolbox'] self.mediadock = plugin_helpers[u'toolbox']
self.displayManager = plugin_helpers[u'displaymanager']
self.pluginManager = plugin_helpers[u'pluginmanager'] self.pluginManager = plugin_helpers[u'pluginmanager']
self.formparent = plugin_helpers[u'formparent'] self.formparent = plugin_helpers[u'formparent']
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),

View File

@ -31,7 +31,7 @@ import logging
from PyQt4 import QtGui, QtCore 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__) log = logging.getLogger(__name__)
@ -80,7 +80,6 @@ class Renderer(object):
self.bg_image = None self.bg_image = None
self._bg_image_filename = None self._bg_image_filename = None
self.theme_name = theme.theme_name self.theme_name = theme.theme_name
self._set_theme_font()
if theme.background_type == u'image': if theme.background_type == u'image':
if theme.background_filename: if theme.background_filename:
self.set_bg_image(theme.background_filename) self.set_bg_image(theme.background_filename)
@ -99,6 +98,20 @@ class Renderer(object):
self.frame.width(), self.frame.width(),
self.frame.height()) 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): def set_frame_dest(self, frame_width, frame_height, preview=False):
""" """
Set the size of the slide. Set the size of the slide.
@ -118,26 +131,24 @@ class Renderer(object):
frame_height) frame_height)
self.frame = QtGui.QImage(frame_width, frame_height, self.frame = QtGui.QImage(frame_width, frame_height,
QtGui.QImage.Format_ARGB32_Premultiplied) 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: if self._bg_image_filename and not self.bg_image:
self.bg_image = resize_image(self._bg_image_filename, self.bg_image = resize_image(self._bg_image_filename,
self.frame.width(), self.frame.height()) self.frame.width(), self.frame.height())
if self.bg_frame is None: if self.bg_frame is None:
self._generate_background_frame() 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 Figure out how much text can appear on a slide, using the current
theme settings. theme settings.
``words`` ``words``
The words to be fitted on the slide. The words to be fitted on the slide.
``footer``
The footer of the slide.
""" """
log.debug(u'format_slide - Start') log.debug(u'format_slide - Start')
line_end = u''
if line_break:
line_end = u'<br>'
words = words.replace(u'\r\n', u'\n') words = words.replace(u'\r\n', u'\n')
verses_text = words.split(u'\n') verses_text = words.split(u'\n')
text = [] text = []
@ -145,124 +156,43 @@ class Renderer(object):
lines = verse.split(u'\n') lines = verse.split(u'\n')
for line in lines: for line in lines:
text.append(line) text.append(line)
split_text = self.pre_render_text(text) doc = QtGui.QTextDocument()
log.debug(u'format_slide - End') doc.setPageSize(QtCore.QSizeF(self._rect.width(), self._rect.height()))
return split_text df = doc.defaultFont()
df.setPixelSize(self._theme.font_main_proportion)
def pre_render_text(self, text): df.setFamily(self._theme.font_main_name)
metrics = QtGui.QFontMetrics(self.main_font) main_weight = 50
#work out line width if self._theme.font_main_weight == u'Bold':
line_width = self._rect.width() main_weight = 75
#number of lines on a page - adjust for rounding up. df.setWeight(main_weight)
line_height = metrics.height() doc.setDefaultFont(df)
if self._theme.display_shadow: layout = doc.documentLayout()
line_height += int(self._theme.display_shadow_size) formatted = []
if self._theme.display_outline: if self._theme.font_main_weight == u'Bold' and \
# pixels top/bottom self._theme.font_main_italics:
line_height += 2 * int(self._theme.display_outline_size) shell = u'{p}{st}{it}%s{/it}{/st}{/p}'
page_length = int(self._rect.height() / line_height ) elif self._theme.font_main_weight == u'Bold' and \
#Average number of characters in line not self._theme.font_main_italics:
ave_line_width = line_width / metrics.averageCharWidth() shell = u'{p}{st}%s{/st}{/p}'
#Maximum size of a character elif self._theme.font_main_italics:
max_char_width = metrics.maxWidth() shell = u'{p}{it}%s{/it}{/p}'
#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}
else: 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): def _generate_background_frame(self):
""" """
@ -270,327 +200,47 @@ class Renderer(object):
Results are cached for performance reasons. Results are cached for performance reasons.
""" """
assert(self._theme) assert(self._theme)
if self._theme.background_mode == u'transparent': self.bg_frame = QtGui.QImage(self.frame.width(),
self.bg_frame = \ self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
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)
log.debug(u'render background %s start', self._theme.background_type) log.debug(u'render background %s start', self._theme.background_type)
painter = QtGui.QPainter() painter = QtGui.QPainter()
painter.begin(self.bg_frame) painter.begin(self.bg_frame)
if self._theme.background_mode == u'transparent': if self._theme.background_type == u'solid':
painter.fillRect(self.frame.rect(), QtCore.Qt.transparent) painter.fillRect(self.frame.rect(),
else: QtGui.QColor(self._theme.background_color))
if self._theme.background_type == u'solid': elif self._theme.background_type == u'gradient':
painter.fillRect(self.frame.rect(), # gradient
QtGui.QColor(self._theme.background_color)) gradient = None
elif self._theme.background_type == u'gradient': if self._theme.background_direction == u'horizontal':
# gradient w = int(self.frame.width()) / 2
gradient = None # vertical
if self._theme.background_direction == u'horizontal': gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
w = int(self.frame.width()) / 2 elif self._theme.background_direction == u'vertical':
# vertical h = int(self.frame.height()) / 2
gradient = QtGui.QLinearGradient(w, 0, w, # Horizontal
self.frame.height()) gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
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)
else: else:
pen = QtGui.QColor(color) w = int(self.frame.width()) / 2
x, y = tlcorner h = int(self.frame.height()) / 2
rowpos = y + metrics.ascent() # Circular
if self._theme.display_outline and outline_size != 0 and not footer: gradient = QtGui.QRadialGradient(w, h, w)
path = QtGui.QPainterPath() gradient.setColorAt(0,
path.addText(QtCore.QPointF(x, rowpos), font, line) QtGui.QColor(self._theme.background_startColor))
self.painter.setBrush(self.painter.pen().brush()) gradient.setColorAt(1,
self.painter.setPen(QtGui.QPen(QtGui.QColor( QtGui.QColor(self._theme.background_endColor))
self._theme.display_outline_color), outline_size)) painter.setBrush(QtGui.QBrush(gradient))
self.painter.drawPath(path) rect_path = QtGui.QPainterPath()
self.painter.setPen(pen) max_x = self.frame.width()
self.painter.drawText(x, rowpos, line) max_y = self.frame.height()
if self._theme.display_slideTransition: rect_path.moveTo(0, 0)
# Print 2nd image with 70% weight rect_path.lineTo(0, max_y)
if self._theme.display_outline and outline_size != 0 and \ rect_path.lineTo(max_x, max_y)
not footer: rect_path.lineTo(max_x, 0)
path = QtGui.QPainterPath() rect_path.closeSubpath()
path.addText(QtCore.QPointF(x, rowpos), font, line) painter.drawPath(rect_path)
self.painter2.setBrush(self.painter2.pen().brush()) elif self._theme.background_type == u'image':
self.painter2.setPen(QtGui.QPen( # image
QtGui.QColor(self._theme.display_outline_color), painter.fillRect(self.frame.rect(), QtCore.Qt.black)
outline_size)) if self.bg_image:
self.painter2.drawPath(path) painter.drawImage(0, 0, self.bg_image)
self.painter2.setFont(font) painter.end()
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')

View File

@ -28,7 +28,8 @@ import logging
from PyQt4 import QtCore 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__) log = logging.getLogger(__name__)
@ -55,6 +56,8 @@ class RenderManager(object):
""" """
log.debug(u'Initilisation started') log.debug(u'Initilisation started')
self.screens = screens self.screens = screens
self.display = MainDisplay(self, screens, False)
self.display.setup()
self.theme_manager = theme_manager self.theme_manager = theme_manager
self.renderer = Renderer() self.renderer = Renderer()
self.calculate_default(self.screens.current[u'size']) self.calculate_default(self.screens.current[u'size'])
@ -63,6 +66,7 @@ class RenderManager(object):
self.theme_level = u'' self.theme_level = u''
self.override_background = None self.override_background = None
self.themedata = None self.themedata = None
self.alertTab = None
def update_display(self): def update_display(self):
""" """
@ -70,7 +74,10 @@ class RenderManager(object):
""" """
log.debug(u'Update Display') log.debug(u'Update Display')
self.calculate_default(self.screens.current[u'size']) self.calculate_default(self.screens.current[u'size'])
self.display = MainDisplay(self, self.screens, False)
self.display.setup()
self.renderer.bg_frame = None self.renderer.bg_frame = None
self.themedata = None
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global): def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
""" """
@ -96,17 +103,22 @@ class RenderManager(object):
""" """
self.service_theme = service_theme 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. Set the appropriate theme depending on the theme level.
Called by the service item when building a display frame
``theme`` ``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) 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 self.theme = self.global_theme
elif self.theme_level == ThemeLevel.Service: elif theme_level == ThemeLevel.Service:
if self.service_theme == u'': if self.service_theme == u'':
self.theme = self.global_theme self.theme = self.global_theme
else: else:
@ -114,20 +126,26 @@ class RenderManager(object):
else: else:
if theme: if theme:
self.theme = theme self.theme = theme
elif self.theme_level == ThemeLevel.Song or \ elif theme_level == ThemeLevel.Song or \
self.theme_level == ThemeLevel.Service: theme_level == ThemeLevel.Service:
if self.service_theme == u'': if self.service_theme == u'':
self.theme = self.global_theme self.theme = self.global_theme
else: else:
self.theme = self.service_theme self.theme = self.service_theme
else: else:
self.theme = self.global_theme self.theme = self.global_theme
if self.theme != self.renderer.theme_name or self.themedata is None: if self.theme != self.renderer.theme_name or self.themedata is None \
or overrideLevels:
log.debug(u'theme is now %s', self.theme) 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.calculate_default(self.screens.current[u'size'])
self.renderer.set_theme(self.themedata) self.renderer.set_theme(self.themedata)
self.build_text_rectangle(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): def build_text_rectangle(self, theme):
""" """
@ -163,13 +181,8 @@ class RenderManager(object):
The theme to generated a preview for. The theme to generated a preview for.
""" """
log.debug(u'generate preview') 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.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'\ verse = u'Amazing Grace!\n'\
'How sweet the sound\n'\ 'How sweet the sound\n'\
'To save a wretch like me;\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'Amazing Grace (John Newton)' )
footer.append(u'Public Domain') footer.append(u'Public Domain')
footer.append(u'CCLI 123456') footer.append(u'CCLI 123456')
formatted = self.renderer.format_slide(verse, False) # build a service item to generate preview
#Only Render the first slide page returned serviceItem = ServiceItem()
return self.renderer.generate_frame_from_lines(formatted[0], serviceItem.theme = themedata
footer)[u'main'] 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. Calculate how much text can fit on a slide.
@ -193,22 +215,7 @@ class RenderManager(object):
""" """
log.debug(u'format slide') log.debug(u'format slide')
self.build_text_rectangle(self.themedata) self.build_text_rectangle(self.themedata)
return self.renderer.format_slide(words, False) return self.renderer.format_slide(words, line_break)
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)
def calculate_default(self, screen): def calculate_default(self, screen):
""" """

View File

@ -35,7 +35,7 @@ import uuid
from PyQt4 import QtGui 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__) log = logging.getLogger(__name__)
@ -57,6 +57,7 @@ class ItemCapabilities(object):
RequiresMedia = 4 RequiresMedia = 4
AllowsLoop = 5 AllowsLoop = 5
AllowsAdditions = 6 AllowsAdditions = 6
NoLineBreaks = 7
class ServiceItem(object): class ServiceItem(object):
""" """
@ -82,6 +83,7 @@ class ServiceItem(object):
self.items = [] self.items = []
self.iconic_representation = None self.iconic_representation = None
self.raw_footer = None self.raw_footer = None
self.foot_text = None
self.theme = None self.theme = None
self.service_item_type = None self.service_item_type = None
self._raw_frames = [] self._raw_frames = []
@ -91,10 +93,18 @@ class ServiceItem(object):
self.from_plugin = False self.from_plugin = False
self.capabilities = [] self.capabilities = []
self.is_valid = True self.is_valid = True
self.cache = {}
self.icon = None self.icon = None
self.themedata = None
self.main = None
self.footer = None
self.bg_frame = None
def _new_item(self): 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()) self._uuid = unicode(uuid.uuid1())
def add_capability(self, capability): def add_capability(self, capability):
@ -126,34 +136,38 @@ class ServiceItem(object):
self.icon = icon self.icon = icon
self.iconic_representation = build_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') log.debug(u'Render called')
self._display_frames = [] 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: if self.service_item_type == ServiceItemType.Text:
log.debug(u'Formatting slides') log.debug(u'Formatting slides')
if self.theme is None: theme = None
self.render_manager.set_override_theme(None) if self.theme:
else: theme = self.theme
self.render_manager.set_override_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: for slide in self._raw_frames:
before = time.time() before = time.time()
formated = self.render_manager.format_slide(slide[u'raw_slide']) formated = self.render_manager \
for format in formated: .format_slide(slide[u'raw_slide'], line_break)
lines = u'' for page in formated:
title = u'' self._display_frames.append(
for line in format: {u'title': clean_tags(page),
if title == u'': u'text': clean_tags(page.rstrip()),
title = line u'html': expand_tags(page.rstrip()),
lines += line + u'\n'
self._display_frames.append({u'title': title,
u'text': lines.rstrip(),
u'verseTag': slide[u'verseTag'] }) 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)) log.log(15, u'Formatting took %4s' % (time.time() - before))
elif self.service_item_type == ServiceItemType.Image: elif self.service_item_type == ServiceItemType.Image:
for slide in self._raw_frames: for slide in self._raw_frames:
@ -163,29 +177,14 @@ class ServiceItem(object):
pass pass
else: else:
log.error(u'Invalid value renderer :%s' % self.service_item_type) log.error(u'Invalid value renderer :%s' % self.service_item_type)
self.title = clean_tags(self.title)
def render_individual(self, row): self.foot_text = None
""" if self.raw_footer:
Takes an array of text and generates an Image from the for foot in self.raw_footer:
theme. It assumes the text will fit on the screen as it if not self.foot_text:
has generated by the render method above. self.foot_text = foot
""" else:
log.debug(u'render individual') self.foot_text = u'%s<br>%s' % (self.foot_text, foot)
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
def add_from_image(self, path, title, image): def add_from_image(self, path, title, image):
""" """
@ -375,9 +374,9 @@ class ServiceItem(object):
renders it if required. renders it if required.
""" """
if self.service_item_type == ServiceItemType.Text: 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: 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): def get_frame_title(self, row=0):
""" """
@ -390,9 +389,3 @@ class ServiceItem(object):
Returns the title of the raw frame Returns the title of the raw frame
""" """
return self._raw_frames[row][u'path'] 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> <proportion>30</proportion>
<weight>Normal</weight> <weight>Normal</weight>
<italics>False</italics> <italics>False</italics>
<indentation>0</indentation>
<line_adjustment>0</line_adjustment> <line_adjustment>0</line_adjustment>
<location override="False" x="10" y="10" width="1004" height="730"/> <location override="False" x="10" y="10" width="1004" height="730"/>
</font> </font>
@ -65,7 +64,6 @@ BLANK_THEME_XML = \
<proportion>12</proportion> <proportion>12</proportion>
<weight>Normal</weight> <weight>Normal</weight>
<italics>False</italics> <italics>False</italics>
<indentation>0</indentation>
<line_adjustment>0</line_adjustment> <line_adjustment>0</line_adjustment>
<location override="False" x="10" y="730" width="1004" height="38"/> <location override="False" x="10" y="730" width="1004" height="38"/>
</font> </font>
@ -184,7 +182,7 @@ class ThemeXML(object):
self.child_element(background, u'filename', filename) self.child_element(background, u'filename', filename)
def add_font(self, name, color, proportion, override, fonttype=u'main', 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): xpos=0, ypos=0, width=0, height=0):
""" """
Add a Font. Add a Font.
@ -210,9 +208,6 @@ class ThemeXML(object):
``italics`` ``italics``
Does the font render to italics Defaults to 0 Normal Does the font render to italics Defaults to 0 Normal
``indentation``
Number of characters the wrap line is indented
``xpos`` ``xpos``
The X position of the text block. The X position of the text block.
@ -239,8 +234,6 @@ class ThemeXML(object):
#Create italics name element #Create italics name element
self.child_element(background, u'italics', italics) self.child_element(background, u'italics', italics)
#Create indentation name element #Create indentation name element
self.child_element(background, u'indentation', unicode(indentation))
#Create indentation name element
self.child_element( self.child_element(
background, u'line_adjustment', unicode(line_adjustment)) background, u'line_adjustment', unicode(line_adjustment))

View File

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

View File

@ -27,6 +27,143 @@
The :mod:`ui` module provides the core user interface for OpenLP 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): class HideMode(object):
""" """
This is basically an enumeration class which specifies the mode of a Bible. This is basically an enumeration class which specifies the mode of a Bible.
@ -37,13 +174,11 @@ class HideMode(object):
Theme = 2 Theme = 2
Screen = 3 Screen = 3
from maindisplay import MainDisplay
from slidecontroller import HideMode from slidecontroller import HideMode
from servicenoteform import ServiceNoteForm from servicenoteform import ServiceNoteForm
from serviceitemeditform import ServiceItemEditForm from serviceitemeditform import ServiceItemEditForm
from screen import ScreenList from screen import ScreenList
from maindisplay import MainDisplay
from maindisplay import VideoDisplay
from maindisplay import DisplayManager
from amendthemeform import AmendThemeForm from amendthemeform import AmendThemeForm
from slidecontroller import SlideController from slidecontroller import SlideController
from splashscreen import SplashScreen from splashscreen import SplashScreen
@ -56,8 +191,7 @@ from settingsform import SettingsForm
from mediadockmanager import MediaDockManager from mediadockmanager import MediaDockManager
from servicemanager import ServiceManager from servicemanager import ServiceManager
from thememanager import ThemeManager from thememanager import ThemeManager
from mainwindow import MainWindow
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainWindow', __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm',
'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager',
'AmendThemeForm', 'MediaDockManager', 'ServiceItemEditForm'] 'AmendThemeForm', 'MediaDockManager', 'ServiceItemEditForm']

View File

@ -68,17 +68,6 @@ class Ui_AmendThemeDialog(object):
self.backgroundLayout.setMargin(8) self.backgroundLayout.setMargin(8)
self.backgroundLayout.setSpacing(8) self.backgroundLayout.setSpacing(8)
self.backgroundLayout.setObjectName(u'backgroundLayout') 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 = QtGui.QLabel(self.backgroundTab)
self.backgroundTypeLabel.setObjectName(u'backgroundTypeLabel') self.backgroundTypeLabel.setObjectName(u'backgroundTypeLabel')
self.backgroundLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.backgroundLayout.setWidget(1, QtGui.QFormLayout.LabelRole,
@ -216,17 +205,6 @@ class Ui_AmendThemeDialog(object):
self.fontMainLineAdjustmentSpinBox.setMinimum(-99) self.fontMainLineAdjustmentSpinBox.setMinimum(-99)
self.mainFontLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.mainFontLayout.setWidget(4, QtGui.QFormLayout.FieldRole,
self.fontMainLineAdjustmentSpinBox) 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 = QtGui.QLabel(self.fontMainGroupBox)
self.fontMainLinesPageLabel.setObjectName(u'fontMainLinesPageLabel') self.fontMainLinesPageLabel.setObjectName(u'fontMainLinesPageLabel')
self.mainFontLayout.addRow(self.fontMainLinesPageLabel) self.mainFontLayout.addRow(self.fontMainLinesPageLabel)
@ -661,12 +639,6 @@ class Ui_AmendThemeDialog(object):
translate('OpenLP.AmendThemeForm', 'Theme Maintenance')) translate('OpenLP.AmendThemeForm', 'Theme Maintenance'))
self.themeNameLabel.setText( self.themeNameLabel.setText(
translate('OpenLP.AmendThemeForm', 'Theme &name:')) 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( self.backgroundTypeLabel.setText(
translate('OpenLP.AmendThemeForm', 'Type:')) translate('OpenLP.AmendThemeForm', 'Type:'))
self.backgroundTypeComboBox.setItemText(0, self.backgroundTypeComboBox.setItemText(0,
@ -700,8 +672,6 @@ class Ui_AmendThemeDialog(object):
translate('OpenLP.AmendThemeForm', 'Size:')) translate('OpenLP.AmendThemeForm', 'Size:'))
self.fontMainSizeSpinBox.setSuffix( self.fontMainSizeSpinBox.setSuffix(
translate('OpenLP.AmendThemeForm', 'pt')) translate('OpenLP.AmendThemeForm', 'pt'))
self.fontMainWrapIndentationLabel.setText(
translate('OpenLP.AmendThemeForm', 'Wrap indentation:'))
self.fontMainWrapLineAdjustmentLabel.setText( self.fontMainWrapLineAdjustmentLabel.setText(
translate('OpenLP.AmendThemeForm', 'Adjust line spacing:')) translate('OpenLP.AmendThemeForm', 'Adjust line spacing:'))
self.fontMainWeightComboBox.setItemText(0, self.fontMainWeightComboBox.setItemText(0,

View File

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

View File

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

View File

@ -26,149 +26,46 @@
import logging import logging
import os import os
import time
from PyQt4 import QtCore, QtGui, QtWebKit from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.phonon import Phonon 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 from openlp.core.ui import HideMode
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
#http://www.steveheffernan.com/html5-video-player/demo-video-player.html #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): class DisplayWidget(QtGui.QGraphicsView):
""" """
Customised version of QTableWidget which can respond to keyboard Customised version of QTableWidget which can respond to keyboard
events. events.
""" """
log.info(u'MainDisplay loaded') log.info(u'Display Widget loaded')
def __init__(self, parent=None, name=None, primary=False): def __init__(self, live, parent=None):
QtGui.QWidget.__init__(self, None) QtGui.QGraphicsView.__init__(self)
self.parent = parent self.parent = parent
self.primary = primary self.live = live
self.hotkey_map = { self.hotkey_map = {
QtCore.Qt.Key_Return: 'servicemanager_next_item', QtCore.Qt.Key_Return: 'servicemanager_next_item',
QtCore.Qt.Key_Space: 'slidecontroller_live_next_noloop', QtCore.Qt.Key_Space: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_Enter: 'slidecontroller_live_next_noloop', QtCore.Qt.Key_Enter: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_0: 'servicemanager_next_item', QtCore.Qt.Key_0: 'servicemanager_next_item',
QtCore.Qt.Key_Backspace: 'slidecontroller_live_previous_noloop'} QtCore.Qt.Key_Backspace: 'slidecontroller_live_previous_noloop'}
self.setStyleSheet(u'border: none;')
def keyPressEvent(self, event): 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): 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: if event.key() == QtCore.Qt.Key_Up:
Receiver.send_message(u'slidecontroller_live_previous') Receiver.send_message(u'slidecontroller_live_previous')
event.accept() event.accept()
@ -185,157 +82,269 @@ class DisplayWidget(QtGui.QGraphicsView):
Receiver.send_message(self.hotkey_map[event.key()]) Receiver.send_message(self.hotkey_map[event.key()])
event.accept() event.accept()
elif event.key() == QtCore.Qt.Key_Escape: elif event.key() == QtCore.Qt.Key_Escape:
self.resetDisplay() self.setVisible(False)
self.videoStop()
event.accept() event.accept()
event.ignore() event.ignore()
else: else:
event.ignore() 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): 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): def __init__(self, parent, screens, live):
""" DisplayWidget.__init__(self, live, parent=None)
The constructor for the display form. self.parent = parent
``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
self.screens = screens self.screens = screens
self.setupScene() self.isLive = live
self.setupVideo() self.alertTab = None
self.setupImage() self.setWindowTitle(u'OpenLP Display')
self.setupText() self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
self.setupAlert() QtCore.Qt.WindowStaysOnTopHint)
self.setupBlank() if self.isLive:
self.blankFrame = None QtCore.QObject.connect(Receiver.get_receiver(),
self.frame = None QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay)
#Hide desktop for now until we know where to put it QtCore.QObject.connect(Receiver.get_receiver(),
#and what size it should be. QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay)
self.setVisible(False)
def setup(self): 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 ' % ( log.debug(u'Setup live = %s for %s ' % (self.isLive,
self.screens, self.screens.monitor_number)) self.screens.monitor_number))
self.setVisible(False)
self.screen = self.screens.current self.screen = self.screens.current
#Sort out screen locations and sizes self.setVisible(False)
self.setGeometry(self.screen[u'size']) self.setGeometry(self.screen[u'size'])
self.scene.setSceneRect(0, 0, self.size().width(), self.webView = QtWebKit.QWebView(self)
self.size().height()) self.webView.setGeometry(0, 0, self.screen[u'size'].width(), \
self.webView.setGeometry(0, 0, self.size().width(), self.screen[u'size'].height())
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.page = self.webView.page() self.page = self.webView.page()
self.videoDisplay = self.page.mainFrame() self.frame = self.page.mainFrame()
self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Vertical, QtCore.QObject.connect(self.webView,
QtCore.SIGNAL(u'loadFinished(bool)'), self.isLoaded)
self.frame.setScrollBarPolicy(QtCore.Qt.Vertical,
QtCore.Qt.ScrollBarAlwaysOff) QtCore.Qt.ScrollBarAlwaysOff)
self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Horizontal, self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
QtCore.Qt.ScrollBarAlwaysOff) QtCore.Qt.ScrollBarAlwaysOff)
self.proxy = QtGui.QGraphicsProxyWidget() if self.isLive:
self.proxy.setWidget(self.webView) # Build the initial frame.
self.proxy.setWindowFlags(QtCore.Qt.Window | self.black = QtGui.QImage(
QtCore.Qt.FramelessWindowHint) self.screens.current[u'size'].width(),
self.proxy.setZValue(1) self.screens.current[u'size'].height(),
self.scene.addItem(self.proxy) 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): def text(self, slide):
self.imageDisplay = QtGui.QGraphicsPixmapItem() """
self.imageDisplay.setZValue(2) Add the slide text from slideController
self.scene.addItem(self.imageDisplay)
def setupText(self): `slide`
#self.displayText = QtGui.QGraphicsTextItem() The slide text to be displayed
self.displayText = QtGui.QGraphicsPixmapItem() """
#self.displayText.setPos(0,0) log.debug(u'text')
#self.displayText.setTextWidth(self.size().width()) self.frame.evaluateJavaScript(u'show_text("%s")' % \
self.displayText.setZValue(4) slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
self.scene.addItem(self.displayText) return self.preview()
def setupAlert(self): def alert(self, text):
self.alertText = QtGui.QGraphicsTextItem() """
self.alertText.setZValue(8) Add the alert text
self.scene.addItem(self.alertText)
def setupBlank(self): `slide`
self.displayBlank = QtGui.QGraphicsPixmapItem() The slide text to be displayed
self.displayBlank.setZValue(10) """
self.scene.addItem(self.displayBlank) 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): def image(self, image):
# """ """
# Hides the main display if for the video to be played Add an image as the background. The image is converted to a
# """ bytestream on route.
# self.hideDisplay(HideMode.Screen)
`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): def hideDisplay(self, mode=HideMode.Screen):
""" """
@ -343,20 +352,13 @@ class MainDisplay(DisplayWidget):
Store the images so they can be replaced when required Store the images so they can be replaced when required
""" """
log.debug(u'hideDisplay mode = %d', mode) log.debug(u'hideDisplay mode = %d', mode)
#self.displayText.setPixmap(self.transparent)
if mode == HideMode.Screen: if mode == HideMode.Screen:
#self.display_image.setPixmap(self.transparent) self.frame.evaluateJavaScript(u'show_blank("desktop");')
self.setVisible(False) self.setVisible(False)
elif mode == HideMode.Blank: elif mode == HideMode.Blank or self.initialFrame:
self.displayBlank.setPixmap( self.frame.evaluateJavaScript(u'show_blank("black");')
QtGui.QPixmap.fromImage(self.blankFrame))
else: else:
if self.parent.renderManager.renderer.bg_frame: self.frame.evaluateJavaScript(u'show_blank("theme");')
self.displayBlank.setPixmap(QtGui.QPixmap.fromImage(
self.parent.renderManager.renderer.bg_frame))
else:
self.displayBlank.setPixmap(
QtGui.QPixmap.fromImage(self.blankFrame))
if mode != HideMode.Screen and self.isHidden(): if mode != HideMode.Screen and self.isHidden():
self.setVisible(True) self.setVisible(True)
@ -367,275 +369,16 @@ class MainDisplay(DisplayWidget):
Make the stored images None to release memory. Make the stored images None to release memory.
""" """
log.debug(u'showDisplay') log.debug(u'showDisplay')
self.displayBlank.setPixmap(self.transparent) self.frame.evaluateJavaScript('show_blank("show");')
if self.isHidden(): if self.isHidden():
self.setVisible(True) self.setVisible(True)
#Trigger actions when display is active again # Trigger actions when display is active again
Receiver.send_message(u'maindisplay_active') 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): class AudioPlayer(QtCore.QObject):
""" """
This Class will play audio only allowing components to work with a 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') log.info(u'AudioPlayer Loaded')
@ -675,9 +418,9 @@ class AudioPlayer(QtCore.QObject):
Set up a video to play from the serviceitem. Set up a video to play from the serviceitem.
""" """
log.debug(u'AudioPlayer Queue new media message %s' % message) 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()) message[0].get_frame_title())
self.mediaObject.setCurrentSource(Phonon.MediaSource(file)) self.mediaObject.setCurrentSource(Phonon.MediaSource(mfile))
self.onMediaPlay() self.onMediaPlay()
def onMediaPlay(self): def onMediaPlay(self):

View File

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

View File

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

View File

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

View File

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

View File

@ -139,13 +139,13 @@ class ThemeManager(QtGui.QWidget):
""" """
log.debug(u'changeGlobalFromTab %s', themeName) log.debug(u'changeGlobalFromTab %s', themeName)
for count in range (0, self.themeListWidget.count()): for count in range (0, self.themeListWidget.count()):
#reset the old name # reset the old name
item = self.themeListWidget.item(count) item = self.themeListWidget.item(count)
oldName = item.text() oldName = item.text()
newName = unicode(item.data(QtCore.Qt.UserRole).toString()) newName = unicode(item.data(QtCore.Qt.UserRole).toString())
if oldName != newName: if oldName != newName:
self.themeListWidget.item(count).setText(newName) self.themeListWidget.item(count).setText(newName)
#Set the new name # Set the new name
if themeName == newName: if themeName == newName:
name = unicode(translate('OpenLP.ThemeManager', name = unicode(translate('OpenLP.ThemeManager',
'%s (default)')) % newName '%s (default)')) % newName
@ -161,11 +161,11 @@ class ThemeManager(QtGui.QWidget):
for count in range (0, self.themeListWidget.count()): for count in range (0, self.themeListWidget.count()):
item = self.themeListWidget.item(count) item = self.themeListWidget.item(count)
oldName = item.text() oldName = item.text()
#reset the old name # reset the old name
if oldName != unicode(item.data(QtCore.Qt.UserRole).toString()): if oldName != unicode(item.data(QtCore.Qt.UserRole).toString()):
self.themeListWidget.item(count).setText( self.themeListWidget.item(count).setText(
unicode(item.data(QtCore.Qt.UserRole).toString())) unicode(item.data(QtCore.Qt.UserRole).toString()))
#Set the new name # Set the new name
if count == selected_row: if count == selected_row:
self.global_theme = unicode( self.global_theme = unicode(
self.themeListWidget.item(count).text()) self.themeListWidget.item(count).text())
@ -295,7 +295,7 @@ class ThemeManager(QtGui.QWidget):
path = unicode(path) path = unicode(path)
if path: if path:
SettingsManager.set_last_dir(self.settingsSection, path, 1) 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 zip = None
try: try:
zip = zipfile.ZipFile(themePath, u'w') zip = zipfile.ZipFile(themePath, u'w')
@ -346,11 +346,10 @@ class ThemeManager(QtGui.QWidget):
log.debug(u'Load themes from dir') log.debug(u'Load themes from dir')
self.themelist = [] self.themelist = []
self.themeListWidget.clear() self.themeListWidget.clear()
#root, dirs, files = os.walk(self.path)
dirList = os.listdir(self.path) dirList = os.listdir(self.path)
for name in dirList: for name in dirList:
if name.endswith(u'.png'): 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) theme = os.path.join(self.path, name)
if os.path.exists(theme): if os.path.exists(theme):
textName = os.path.splitext(name)[0] textName = os.path.splitext(name)[0]
@ -660,9 +659,8 @@ class ThemeManager(QtGui.QWidget):
""" """
Call the RenderManager to build a Sample Image Call the RenderManager to build a Sample Image
""" """
log.debug(u'generateImage %s ', themedata) log.debug(u'generateImage \n%s ', themedata)
frame = self.parent.RenderManager.generate_preview(themedata) return self.parent.RenderManager.generate_preview(themedata)
return frame
def getPreviewImage(self, theme): def getPreviewImage(self, theme):
""" """
@ -732,8 +730,6 @@ class ThemeManager(QtGui.QWidget):
theme.display_slideTransition = theme.display_slideTransition theme.display_slideTransition = theme.display_slideTransition
theme.font_footer_color = theme.font_footer_color.strip() theme.font_footer_color = theme.font_footer_color.strip()
theme.font_footer_height = int(theme.font_footer_height.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_italics = str_to_bool(theme.font_footer_italics)
theme.font_footer_name = theme.font_footer_name.strip() theme.font_footer_name = theme.font_footer_name.strip()
#theme.font_footer_override #theme.font_footer_override
@ -746,7 +742,6 @@ class ThemeManager(QtGui.QWidget):
theme.font_main_color = theme.font_main_color.strip() theme.font_main_color = theme.font_main_color.strip()
theme.font_main_height = int(theme.font_main_height.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_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_name = theme.font_main_name.strip()
#theme.font_main_override #theme.font_main_override
theme.font_main_proportion = int(theme.font_main_proportion.strip()) theme.font_main_proportion = int(theme.font_main_proportion.strip())
@ -757,3 +752,8 @@ class ThemeManager(QtGui.QWidget):
#theme.theme_mode #theme.theme_mode
theme.theme_name = theme.theme_name.strip() theme.theme_name = theme.theme_name.strip()
#theme.theme_version #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') log.info(u'Alerts Initialising')
Plugin.initialise(self) Plugin.initialise(self)
self.toolsAlertItem.setVisible(True) self.toolsAlertItem.setVisible(True)
self.liveController.alertTab = self.alertsTab
def finalise(self): def finalise(self):
log.info(u'Plugin Finalise') log.info(u'Alerts Finalising')
Plugin.finalise(self) Plugin.finalise(self)
self.toolsAlertItem.setVisible(False) self.toolsAlertItem.setVisible(False)

View File

@ -32,18 +32,9 @@ from openlp.core.lib import Receiver, translate
log = logging.getLogger(__name__) 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): 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') log.info(u'Alert Manager loaded')
@ -94,10 +85,7 @@ class AlertsManager(QtCore.QObject):
return return
text = self.alertList.pop(0) text = self.alertList.pop(0)
alertTab = self.parent.alertsTab alertTab = self.parent.alertsTab
text = HTMLCODE % (alertTab.font_color, alertTab.bg_color, self.parent.liveController.display.alert(text)
alertTab.font_face, alertTab.font_size, text)
self.parent.previewController.parent.displayManager.addAlert(text,
alertTab.location)
# check to see if we have a timer running # check to see if we have a timer running
if self.timer_id == 0: if self.timer_id == 0:
self.timer_id = self.startTimer(int(alertTab.timeout) * 1000) self.timer_id = self.startTimer(int(alertTab.timeout) * 1000)
@ -111,10 +99,8 @@ class AlertsManager(QtCore.QObject):
""" """
log.debug(u'timer event') log.debug(u'timer event')
alertTab = self.parent.alertsTab
if event.timerId() == self.timer_id: if event.timerId() == self.timer_id:
self.parent.previewController.parent.displayManager.addAlert(u'', self.parent.liveController.display.alert(u'')
alertTab.location)
self.killTimer(self.timer_id) self.killTimer(self.timer_id)
self.timer_id = 0 self.timer_id = 0
self.generateAlert() self.generateAlert()

View File

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

View File

@ -504,16 +504,16 @@ class BibleMediaItem(MediaManagerItem):
dual_text = self._decodeQtObject(reference, 'dual_text') dual_text = self._decodeQtObject(reference, 'dual_text')
if self.parent.settings_tab.display_style == 1: if self.parent.settings_tab.display_style == 1:
verse_text = self.formatVerse(old_chapter, chapter, verse, verse_text = self.formatVerse(old_chapter, chapter, verse,
u'(', u')') u'{su}(', u'){/su}')
elif self.parent.settings_tab.display_style == 2: elif self.parent.settings_tab.display_style == 2:
verse_text = self.formatVerse(old_chapter, chapter, verse, verse_text = self.formatVerse(old_chapter, chapter, verse,
u'{', u'}') u'{su}{', u'}{/su}')
elif self.parent.settings_tab.display_style == 3: elif self.parent.settings_tab.display_style == 3:
verse_text = self.formatVerse(old_chapter, chapter, verse, verse_text = self.formatVerse(old_chapter, chapter, verse,
u'[', u']') u'{su}[', u']{/su}')
else: else:
verse_text = self.formatVerse(old_chapter, chapter, verse, verse_text = self.formatVerse(old_chapter, chapter, verse,
u'', u'') u'{su}', u'{/su}')
old_chapter = chapter old_chapter = chapter
footer = u'%s (%s %s)' % (book, version, copyright) footer = u'%s (%s %s)' % (book, version, copyright)
# If not found add to footer # If not found add to footer
@ -532,7 +532,11 @@ class BibleMediaItem(MediaManagerItem):
else: else:
# If we are 'Verse Per Line' then force a new line. # If we are 'Verse Per Line' then force a new line.
if self.parent.settings_tab.layout_style == 1: 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) bible_text = u'%s %s %s' % (bible_text, verse_text, text)
# If we are 'Verse Per Slide' then create a new slide. # If we are 'Verse Per Slide' then create a new slide.
if self.parent.settings_tab.layout_style == 0: if self.parent.settings_tab.layout_style == 0:

View File

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

View File

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

View File

@ -97,8 +97,17 @@ class MediaMediaItem(MediaManagerItem):
u':/slides/slide_blank.png', u':/slides/slide_blank.png',
translate('MediaPlugin.MediaItem', 'Replace Live Background'), translate('MediaPlugin.MediaItem', 'Replace Live Background'),
self.onReplaceClick, False) 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 # Add the song widget to the page layout
self.pageLayout.addWidget(self.ImageWidget) self.pageLayout.addWidget(self.ImageWidget)
self.resetButton.setVisible(False)
def onResetClick(self):
self.resetButton.setVisible(False)
self.parent.liveController.display.resetVideo()
def onReplaceClick(self): def onReplaceClick(self):
if check_item_selected(self.listView, if check_item_selected(self.listView,
@ -106,7 +115,8 @@ class MediaMediaItem(MediaManagerItem):
'You must select a media file to replace the background with.')): 'You must select a media file to replace the background with.')):
item = self.listView.currentItem() item = self.listView.currentItem()
filename = unicode(item.data(QtCore.Qt.UserRole).toString()) 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): def generateSlideData(self, service_item, item=None):
if item is 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) author = self.songmanager.get_object(Author, item_id)
if author in self.song.authors: if author in self.song.authors:
QtGui.QMessageBox.warning(self, QtGui.QMessageBox.warning(self,
translate('SongsPlugin.EditSongForm', 'Error'), translate('SongsPlugin.EditSongForm', 'Error'),
translate('SongsPlugin.EditSongForm', 'This author is ' translate('SongsPlugin.EditSongForm', 'This author is '
'already in the list.')) 'already in the list.'))
else: else:
@ -422,7 +422,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.VerseDeleteButton.setEnabled(True) self.VerseDeleteButton.setEnabled(True)
def onVerseAddButtonClicked(self): 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_(): if self.verse_form.exec_():
afterText, verse, subVerse = self.verse_form.getVerse() afterText, verse, subVerse = self.verse_form.getVerse()
data = u'%s:%s' % (verse, subVerse) data = u'%s:%s' % (verse, subVerse)

View File

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

View File

@ -45,6 +45,9 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
""" """
QtGui.QDialog.__init__(self, parent) QtGui.QDialog.__init__(self, parent)
self.setupUi(self) self.setupUi(self)
QtCore.QObject.connect(self.VerseTextEdit,
QtCore.SIGNAL('customContextMenuRequested(QPoint)'),
self.contextMenu)
QtCore.QObject.connect( QtCore.QObject.connect(
self.InsertButton, self.InsertButton,
QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
@ -57,6 +60,10 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
) )
self.verse_regex = re.compile(r'---\[([-\w]+):([\d]+)\]---') 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): def insertVerse(self, title, num=1):
if self.VerseTextEdit.textCursor().columnNumber() != 0: if self.VerseTextEdit.textCursor().columnNumber() != 0:
self.VerseTextEdit.insertPlainText(u'\n') 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. Stop the import on pressing the cancel button.
""" """
log.debug('Cancel button pressed!') log.debug('Cancel button pressed!')
if self.currentId() == 3: if self.currentId() == 2:
Receiver.send_message(u'song_stop_import') Receiver.send_message(u'songs_stop_import')
def onCurrentIdChanged(self, id): def onCurrentIdChanged(self, id):
if id == 2: if id == 2:

View File

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

View File

@ -24,14 +24,18 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
import logging
import re 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 import VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book from openlp.plugins.songs.lib.db import Song, Author, Topic, Book
from openlp.plugins.songs.lib.xml import SongXMLBuilder 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 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 whether the authors etc already exist and add them or refer to them
as necessary as necessary
""" """
def __init__(self, manager): def __init__(self, manager):
""" """
Initialise and create defaults for properties Initialise and create defaults for properties
@ -48,6 +51,7 @@ class SongImport(object):
database access is performed database access is performed
""" """
self.manager = manager self.manager = manager
self.stop_import_flag = False
self.title = u'' self.title = u''
self.song_number = u'' self.song_number = u''
self.alternate_title = u'' self.alternate_title = u''
@ -67,6 +71,15 @@ class SongImport(object):
'SongsPlugin.SongImport', 'copyright')) 'SongsPlugin.SongImport', 'copyright'))
self.copyright_symbol = unicode(translate( self.copyright_symbol = unicode(translate(
'SongsPlugin.SongImport', '\xa9')) '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): def register(self, import_wizard):
self.import_wizard = 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