forked from openlp/openlp
Merged from trunk
This commit is contained in:
commit
6c83e98fd5
@ -34,7 +34,8 @@ from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Receiver
|
||||
from openlp.core.resources import qInitResources
|
||||
from openlp.core.ui import MainWindow, SplashScreen, ScreenList
|
||||
from openlp.core.ui.mainwindow import MainWindow
|
||||
from openlp.core.ui import SplashScreen, ScreenList
|
||||
from openlp.core.utils import AppLocation, LanguageManager, VersionThread
|
||||
|
||||
log = logging.getLogger()
|
||||
|
@ -35,6 +35,67 @@ from PyQt4 import QtCore, QtGui
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# TODO make external and configurable in alpha 4 via a settings dialog
|
||||
html_expands = []
|
||||
|
||||
html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \
|
||||
u'start html':u'<font color=red>', \
|
||||
u'end tag':u'{/r}', u'end html':u'</font>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \
|
||||
u'start html':u'<font color=black>', \
|
||||
u'end tag':u'{/b}', u'end html':u'</font>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \
|
||||
u'start html':u'<font color=blue>', \
|
||||
u'end tag':u'{/bl}', u'end html':u'</font>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \
|
||||
u'start html':u'<font color=yellow>', \
|
||||
u'end tag':u'{/y}', u'end html':u'</font>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \
|
||||
u'start html':u'<font color=green>', \
|
||||
u'end tag':u'{/g}', u'end html':u'</font>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \
|
||||
u'start html':u'<font color=#CC33CC>', \
|
||||
u'end tag':u'{/pk}', u'end html':u'</font>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \
|
||||
u'start html':u'<font color=#CC0033>', \
|
||||
u'end tag':u'{/o}', u'end html':u'</font>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \
|
||||
u'start html':u'<font color=#9900FF>', \
|
||||
u'end tag':u'{/pp}', u'end html':u'</font>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'White', u'start tag':u'{w}', \
|
||||
u'start html':u'<font color=white>', \
|
||||
u'end tag':u'{/w}', u'end html':u'</font>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \
|
||||
u'start html':u'<sup>', \
|
||||
u'end tag':u'{/su}', u'end html':u'</sup>', \
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}', \
|
||||
u'start html':u'<sub>', \
|
||||
u'end tag':u'{/sb}', u'end html':u'</sub>', \
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}', \
|
||||
u'start html':u'<p>', \
|
||||
u'end tag':u'{/p}', u'end html':u'</p>', \
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Bold', u'start tag':u'{st}', \
|
||||
u'start html':u'<strong>', \
|
||||
u'end tag':u'{/st}', \
|
||||
u'end html':u'</strong>', \
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Italics', u'start tag':u'{it}', \
|
||||
u'start html':u'<em>', \
|
||||
u'end tag':u'{/it}', u'end html':u'</em>', \
|
||||
u'protected':True})
|
||||
|
||||
def translate(context, text, comment=None):
|
||||
"""
|
||||
A special shortcut method to wrap around the Qt4 translation functions.
|
||||
@ -166,15 +227,46 @@ def context_menu_separator(base):
|
||||
action.setSeparator(True)
|
||||
return action
|
||||
|
||||
def resize_image(image, width, height):
|
||||
def image_to_byte(image):
|
||||
"""
|
||||
Resize an image to fit on the current screen for the web and returns
|
||||
it as a byte stream.
|
||||
|
||||
``image``
|
||||
The image to converted.
|
||||
"""
|
||||
byte_array = QtCore.QByteArray()
|
||||
# use buffer to store pixmap into byteArray
|
||||
buffie = QtCore.QBuffer(byte_array)
|
||||
buffie.open(QtCore.QIODevice.WriteOnly)
|
||||
if isinstance(image, QtGui.QImage):
|
||||
pixmap = QtGui.QPixmap.fromImage(image)
|
||||
else:
|
||||
pixmap = QtGui.QPixmap(image)
|
||||
pixmap.save(buffie, "PNG")
|
||||
# convert to base64 encoding so does not get missed!
|
||||
return byte_array.toBase64()
|
||||
|
||||
def resize_image(image, width, height, background=QtCore.Qt.black):
|
||||
"""
|
||||
Resize an image to fit on the current screen.
|
||||
|
||||
``image``
|
||||
The image to resize.
|
||||
|
||||
``width``
|
||||
The new image width.
|
||||
|
||||
``height``
|
||||
The new image height.
|
||||
|
||||
``background``
|
||||
The background colour defaults to black.
|
||||
|
||||
"""
|
||||
preview = QtGui.QImage(image)
|
||||
if not preview.isNull():
|
||||
# Only resize if different size
|
||||
if preview.width() == width and preview.height == height:
|
||||
return preview
|
||||
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
|
||||
@ -184,7 +276,7 @@ def resize_image(image, width, height):
|
||||
# and move it to the centre of the preview space
|
||||
new_image = QtGui.QImage(width, height,
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
new_image.fill(QtCore.Qt.black)
|
||||
new_image.fill(background)
|
||||
painter = QtGui.QPainter(new_image)
|
||||
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
|
||||
return new_image
|
||||
@ -205,6 +297,25 @@ def check_item_selected(list_widget, message):
|
||||
return False
|
||||
return True
|
||||
|
||||
def clean_tags(text):
|
||||
"""
|
||||
Remove Tags from text for display
|
||||
"""
|
||||
text = text.replace(u'<br>', u'\n')
|
||||
for tag in html_expands:
|
||||
text = text.replace(tag[u'start tag'], u'')
|
||||
text = text.replace(tag[u'end tag'], u'')
|
||||
return text
|
||||
|
||||
def expand_tags(text):
|
||||
"""
|
||||
Expand tags HTML for display
|
||||
"""
|
||||
for tag in html_expands:
|
||||
text = text.replace(tag[u'start tag'], tag[u'start html'])
|
||||
text = text.replace(tag[u'end tag'], tag[u'end html'])
|
||||
return text
|
||||
|
||||
from eventreceiver import Receiver
|
||||
from settingsmanager import SettingsManager
|
||||
from plugin import PluginStatus, Plugin
|
||||
@ -213,6 +324,7 @@ from settingstab import SettingsTab
|
||||
from serviceitem import ServiceItem
|
||||
from serviceitem import ServiceItemType
|
||||
from serviceitem import ItemCapabilities
|
||||
from htmlbuilder import build_html
|
||||
from toolbar import OpenLPToolbar
|
||||
from dockwidget import OpenLPDockWidget
|
||||
from theme import ThemeLevel, ThemeXML
|
||||
|
445
openlp/core/lib/htmlbuilder.py
Normal file
445
openlp/core/lib/htmlbuilder.py
Normal 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, islive):
|
||||
"""
|
||||
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 and islive \
|
||||
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: %spt; 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: %spt;
|
||||
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: %spt;
|
||||
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
|
@ -131,7 +131,6 @@ class Plugin(QtCore.QObject):
|
||||
self.serviceManager = plugin_helpers[u'service']
|
||||
self.settingsForm = plugin_helpers[u'settings form']
|
||||
self.mediadock = plugin_helpers[u'toolbox']
|
||||
self.displayManager = plugin_helpers[u'displaymanager']
|
||||
self.pluginManager = plugin_helpers[u'pluginmanager']
|
||||
self.formparent = plugin_helpers[u'formparent']
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
|
@ -31,7 +31,7 @@ import logging
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
from openlp.core.lib import resize_image
|
||||
from openlp.core.lib import resize_image, expand_tags
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -80,7 +80,6 @@ class Renderer(object):
|
||||
self.bg_image = None
|
||||
self._bg_image_filename = None
|
||||
self.theme_name = theme.theme_name
|
||||
self._set_theme_font()
|
||||
if theme.background_type == u'image':
|
||||
if theme.background_filename:
|
||||
self.set_bg_image(theme.background_filename)
|
||||
@ -99,6 +98,20 @@ class Renderer(object):
|
||||
self.frame.width(),
|
||||
self.frame.height())
|
||||
|
||||
def set_text_rectangle(self, rect_main, rect_footer):
|
||||
"""
|
||||
Sets the rectangle within which text should be rendered.
|
||||
|
||||
``rect_main``
|
||||
The main text block.
|
||||
|
||||
``rect_footer``
|
||||
The footer text block.
|
||||
"""
|
||||
log.debug(u'set_text_rectangle %s , %s' % (rect_main, rect_footer))
|
||||
self._rect = rect_main
|
||||
self._rect_footer = rect_footer
|
||||
|
||||
def set_frame_dest(self, frame_width, frame_height, preview=False):
|
||||
"""
|
||||
Set the size of the slide.
|
||||
@ -118,26 +131,24 @@ class Renderer(object):
|
||||
frame_height)
|
||||
self.frame = QtGui.QImage(frame_width, frame_height,
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
self.frame_opaque = QtGui.QImage(frame_width, frame_height,
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
if self._bg_image_filename and not self.bg_image:
|
||||
self.bg_image = resize_image(self._bg_image_filename,
|
||||
self.frame.width(), self.frame.height())
|
||||
if self.bg_frame is None:
|
||||
self._generate_background_frame()
|
||||
|
||||
def format_slide(self, words, footer):
|
||||
def format_slide(self, words, line_break):
|
||||
"""
|
||||
Figure out how much text can appear on a slide, using the current
|
||||
theme settings.
|
||||
|
||||
``words``
|
||||
The words to be fitted on the slide.
|
||||
|
||||
``footer``
|
||||
The footer of the slide.
|
||||
"""
|
||||
log.debug(u'format_slide - Start')
|
||||
line_end = u''
|
||||
if line_break:
|
||||
line_end = u'<br>'
|
||||
words = words.replace(u'\r\n', u'\n')
|
||||
verses_text = words.split(u'\n')
|
||||
text = []
|
||||
@ -145,124 +156,43 @@ class Renderer(object):
|
||||
lines = verse.split(u'\n')
|
||||
for line in lines:
|
||||
text.append(line)
|
||||
split_text = self.pre_render_text(text)
|
||||
log.debug(u'format_slide - End')
|
||||
return split_text
|
||||
|
||||
def pre_render_text(self, text):
|
||||
metrics = QtGui.QFontMetrics(self.main_font)
|
||||
#work out line width
|
||||
line_width = self._rect.width()
|
||||
#number of lines on a page - adjust for rounding up.
|
||||
line_height = metrics.height()
|
||||
if self._theme.display_shadow:
|
||||
line_height += int(self._theme.display_shadow_size)
|
||||
if self._theme.display_outline:
|
||||
# pixels top/bottom
|
||||
line_height += 2 * int(self._theme.display_outline_size)
|
||||
page_length = int(self._rect.height() / line_height )
|
||||
#Average number of characters in line
|
||||
ave_line_width = line_width / metrics.averageCharWidth()
|
||||
#Maximum size of a character
|
||||
max_char_width = metrics.maxWidth()
|
||||
#Max characters pre line based on min size of a character
|
||||
char_per_line = line_width / metrics.width(u'i')
|
||||
log.debug(u'Page Length area height %s , metrics %s , lines %s' %
|
||||
(int(self._rect.height()), metrics.height(), page_length ))
|
||||
split_pages = []
|
||||
page = []
|
||||
split_lines = []
|
||||
count = 0
|
||||
doc = QtGui.QTextDocument()
|
||||
doc.setPageSize(QtCore.QSizeF(self._rect.width(), self._rect.height()))
|
||||
df = doc.defaultFont()
|
||||
df.setPixelSize(self._theme.font_main_proportion)
|
||||
df.setFamily(self._theme.font_main_name)
|
||||
main_weight = 50
|
||||
if self._theme.font_main_weight == u'Bold':
|
||||
main_weight = 75
|
||||
df.setWeight(main_weight)
|
||||
doc.setDefaultFont(df)
|
||||
layout = doc.documentLayout()
|
||||
formatted = []
|
||||
if self._theme.font_main_weight == u'Bold' and \
|
||||
self._theme.font_main_italics:
|
||||
shell = u'{p}{st}{it}%s{/it}{/st}{/p}'
|
||||
elif self._theme.font_main_weight == u'Bold' and \
|
||||
not self._theme.font_main_italics:
|
||||
shell = u'{p}{st}%s{/st}{/p}'
|
||||
elif self._theme.font_main_italics:
|
||||
shell = u'{p}{it}%s{/it}{/p}'
|
||||
else:
|
||||
shell = u'{p}%s{/p}'
|
||||
temp_text = u''
|
||||
old_html_text = u''
|
||||
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:
|
||||
return {u'main':self.frame, u'trans':None}
|
||||
# mark line ends
|
||||
temp_text = temp_text + line + line_end
|
||||
html_text = shell % expand_tags(temp_text)
|
||||
doc.setHtml(html_text)
|
||||
# Text too long so gone to next mage
|
||||
if layout.pageCount() != 1:
|
||||
formatted.append(shell % old_html_text)
|
||||
temp_text = line
|
||||
old_html_text = temp_text
|
||||
formatted.append(shell % old_html_text)
|
||||
log.debug(u'format_slide - End')
|
||||
return formatted
|
||||
|
||||
def _generate_background_frame(self):
|
||||
"""
|
||||
@ -270,19 +200,11 @@ class Renderer(object):
|
||||
Results are cached for performance reasons.
|
||||
"""
|
||||
assert(self._theme)
|
||||
if self._theme.background_mode == u'transparent':
|
||||
self.bg_frame = \
|
||||
QtGui.QPixmap(self.frame.width(), self.frame.height())
|
||||
self.bg_frame.fill(QtCore.Qt.transparent)
|
||||
else:
|
||||
self.bg_frame = QtGui.QImage(self.frame.width(),
|
||||
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
log.debug(u'render background %s start', self._theme.background_type)
|
||||
painter = QtGui.QPainter()
|
||||
painter.begin(self.bg_frame)
|
||||
if self._theme.background_mode == u'transparent':
|
||||
painter.fillRect(self.frame.rect(), QtCore.Qt.transparent)
|
||||
else:
|
||||
if self._theme.background_type == u'solid':
|
||||
painter.fillRect(self.frame.rect(),
|
||||
QtGui.QColor(self._theme.background_color))
|
||||
@ -292,13 +214,11 @@ class Renderer(object):
|
||||
if self._theme.background_direction == u'horizontal':
|
||||
w = int(self.frame.width()) / 2
|
||||
# vertical
|
||||
gradient = QtGui.QLinearGradient(w, 0, w,
|
||||
self.frame.height())
|
||||
gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
|
||||
elif self._theme.background_direction == u'vertical':
|
||||
h = int(self.frame.height()) / 2
|
||||
# Horizontal
|
||||
gradient = QtGui.QLinearGradient(0, h, self.frame.width(),
|
||||
h)
|
||||
gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
|
||||
else:
|
||||
w = int(self.frame.width()) / 2
|
||||
h = int(self.frame.height()) / 2
|
||||
@ -324,273 +244,3 @@ class Renderer(object):
|
||||
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:
|
||||
pen = QtGui.QColor(color)
|
||||
x, y = tlcorner
|
||||
rowpos = y + metrics.ascent()
|
||||
if self._theme.display_outline and outline_size != 0 and not footer:
|
||||
path = QtGui.QPainterPath()
|
||||
path.addText(QtCore.QPointF(x, rowpos), font, line)
|
||||
self.painter.setBrush(self.painter.pen().brush())
|
||||
self.painter.setPen(QtGui.QPen(QtGui.QColor(
|
||||
self._theme.display_outline_color), outline_size))
|
||||
self.painter.drawPath(path)
|
||||
self.painter.setPen(pen)
|
||||
self.painter.drawText(x, rowpos, line)
|
||||
if self._theme.display_slideTransition:
|
||||
# Print 2nd image with 70% weight
|
||||
if self._theme.display_outline and outline_size != 0 and \
|
||||
not footer:
|
||||
path = QtGui.QPainterPath()
|
||||
path.addText(QtCore.QPointF(x, rowpos), font, line)
|
||||
self.painter2.setBrush(self.painter2.pen().brush())
|
||||
self.painter2.setPen(QtGui.QPen(
|
||||
QtGui.QColor(self._theme.display_outline_color),
|
||||
outline_size))
|
||||
self.painter2.drawPath(path)
|
||||
self.painter2.setFont(font)
|
||||
self.painter2.setPen(pen)
|
||||
self.painter2.drawText(x, rowpos, line)
|
||||
return (w, h)
|
||||
|
||||
def snoop_image(self, image, image2=None):
|
||||
"""
|
||||
Debugging method to allow images to be viewed.
|
||||
|
||||
``image``
|
||||
An image to save to disk.
|
||||
|
||||
``image2``
|
||||
Defaults to *None*. Another image to save to disk.
|
||||
"""
|
||||
image.save(u'renderer.png', u'png')
|
||||
if image2:
|
||||
image2.save(u'renderer2.png', u'png')
|
||||
|
@ -28,7 +28,8 @@ import logging
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Renderer, ThemeLevel
|
||||
from openlp.core.lib import Renderer, ThemeLevel, ServiceItem
|
||||
from openlp.core.ui import MainDisplay
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -55,6 +56,8 @@ class RenderManager(object):
|
||||
"""
|
||||
log.debug(u'Initilisation started')
|
||||
self.screens = screens
|
||||
self.display = MainDisplay(self, screens, False)
|
||||
self.display.setup()
|
||||
self.theme_manager = theme_manager
|
||||
self.renderer = Renderer()
|
||||
self.calculate_default(self.screens.current[u'size'])
|
||||
@ -63,6 +66,7 @@ class RenderManager(object):
|
||||
self.theme_level = u''
|
||||
self.override_background = None
|
||||
self.themedata = None
|
||||
self.alertTab = None
|
||||
|
||||
def update_display(self):
|
||||
"""
|
||||
@ -70,7 +74,10 @@ class RenderManager(object):
|
||||
"""
|
||||
log.debug(u'Update Display')
|
||||
self.calculate_default(self.screens.current[u'size'])
|
||||
self.display = MainDisplay(self, self.screens, False)
|
||||
self.display.setup()
|
||||
self.renderer.bg_frame = None
|
||||
self.themedata = None
|
||||
|
||||
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
|
||||
"""
|
||||
@ -96,17 +103,22 @@ class RenderManager(object):
|
||||
"""
|
||||
self.service_theme = service_theme
|
||||
|
||||
def set_override_theme(self, theme):
|
||||
def set_override_theme(self, theme, overrideLevels=False):
|
||||
"""
|
||||
Set the appropriate theme depending on the theme level.
|
||||
Called by the service item when building a display frame
|
||||
|
||||
``theme``
|
||||
The name of the song-level theme.
|
||||
The name of the song-level theme. None means the service
|
||||
item wants to use the given value.
|
||||
"""
|
||||
log.debug(u'set override theme to %s', theme)
|
||||
if self.theme_level == ThemeLevel.Global:
|
||||
theme_level = self.theme_level
|
||||
if overrideLevels:
|
||||
theme_level = ThemeLevel.Song
|
||||
if theme_level == ThemeLevel.Global:
|
||||
self.theme = self.global_theme
|
||||
elif self.theme_level == ThemeLevel.Service:
|
||||
elif theme_level == ThemeLevel.Service:
|
||||
if self.service_theme == u'':
|
||||
self.theme = self.global_theme
|
||||
else:
|
||||
@ -114,20 +126,26 @@ class RenderManager(object):
|
||||
else:
|
||||
if theme:
|
||||
self.theme = theme
|
||||
elif self.theme_level == ThemeLevel.Song or \
|
||||
self.theme_level == ThemeLevel.Service:
|
||||
elif theme_level == ThemeLevel.Song or \
|
||||
theme_level == ThemeLevel.Service:
|
||||
if self.service_theme == u'':
|
||||
self.theme = self.global_theme
|
||||
else:
|
||||
self.theme = self.service_theme
|
||||
else:
|
||||
self.theme = self.global_theme
|
||||
if self.theme != self.renderer.theme_name or self.themedata is None:
|
||||
if self.theme != self.renderer.theme_name or self.themedata is None \
|
||||
or overrideLevels:
|
||||
log.debug(u'theme is now %s', self.theme)
|
||||
if overrideLevels:
|
||||
self.themedata = theme
|
||||
else:
|
||||
self.themedata = self.theme_manager.getThemeData(self.theme)
|
||||
self.calculate_default(self.screens.current[u'size'])
|
||||
self.renderer.set_theme(self.themedata)
|
||||
self.build_text_rectangle(self.themedata)
|
||||
self.renderer.set_frame_dest(self.width, self.height)
|
||||
return self.renderer._rect, self.renderer._rect_footer
|
||||
|
||||
def build_text_rectangle(self, theme):
|
||||
"""
|
||||
@ -163,13 +181,8 @@ class RenderManager(object):
|
||||
The theme to generated a preview for.
|
||||
"""
|
||||
log.debug(u'generate preview')
|
||||
#set the default image size for previews
|
||||
# set the default image size for previews
|
||||
self.calculate_default(self.screens.preview[u'size'])
|
||||
self.renderer.set_theme(themedata)
|
||||
self.build_text_rectangle(themedata)
|
||||
self.renderer.set_frame_dest(self.width, self.height, True)
|
||||
#Reset the real screen size for subsequent render requests
|
||||
self.calculate_default(self.screens.current[u'size'])
|
||||
verse = u'Amazing Grace!\n'\
|
||||
'How sweet the sound\n'\
|
||||
'To save a wretch like me;\n'\
|
||||
@ -179,12 +192,21 @@ class RenderManager(object):
|
||||
footer.append(u'Amazing Grace (John Newton)' )
|
||||
footer.append(u'Public Domain')
|
||||
footer.append(u'CCLI 123456')
|
||||
formatted = self.renderer.format_slide(verse, False)
|
||||
#Only Render the first slide page returned
|
||||
return self.renderer.generate_frame_from_lines(formatted[0],
|
||||
footer)[u'main']
|
||||
# build a service item to generate preview
|
||||
serviceItem = ServiceItem()
|
||||
serviceItem.theme = themedata
|
||||
serviceItem.add_from_text(u'', verse, footer)
|
||||
serviceItem.render_manager = self
|
||||
serviceItem.raw_footer = footer
|
||||
serviceItem.render(True)
|
||||
self.display.buildHtml(serviceItem)
|
||||
raw_html = serviceItem.get_rendered_frame(0)[1]
|
||||
preview = self.display.text(raw_html)
|
||||
# Reset the real screen size for subsequent render requests
|
||||
self.calculate_default(self.screens.current[u'size'])
|
||||
return preview
|
||||
|
||||
def format_slide(self, words):
|
||||
def format_slide(self, words, line_break):
|
||||
"""
|
||||
Calculate how much text can fit on a slide.
|
||||
|
||||
@ -193,22 +215,7 @@ class RenderManager(object):
|
||||
"""
|
||||
log.debug(u'format slide')
|
||||
self.build_text_rectangle(self.themedata)
|
||||
return self.renderer.format_slide(words, False)
|
||||
|
||||
def generate_slide(self, main_text, footer_text):
|
||||
"""
|
||||
Generate the actual slide image.
|
||||
|
||||
``main_text``
|
||||
The text for the main area of the slide.
|
||||
|
||||
``footer_text``
|
||||
The text for the slide footer.
|
||||
"""
|
||||
log.debug(u'generate slide')
|
||||
self.build_text_rectangle(self.themedata)
|
||||
self.renderer.set_frame_dest(self.width, self.height)
|
||||
return self.renderer.generate_frame_from_lines(main_text, footer_text)
|
||||
return self.renderer.format_slide(words, line_break)
|
||||
|
||||
def calculate_default(self, screen):
|
||||
"""
|
||||
|
@ -35,7 +35,7 @@ import uuid
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import build_icon, resize_image
|
||||
from openlp.core.lib import build_icon, resize_image, clean_tags, expand_tags
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -57,6 +57,7 @@ class ItemCapabilities(object):
|
||||
RequiresMedia = 4
|
||||
AllowsLoop = 5
|
||||
AllowsAdditions = 6
|
||||
NoLineBreaks = 7
|
||||
|
||||
class ServiceItem(object):
|
||||
"""
|
||||
@ -82,6 +83,7 @@ class ServiceItem(object):
|
||||
self.items = []
|
||||
self.iconic_representation = None
|
||||
self.raw_footer = None
|
||||
self.foot_text = None
|
||||
self.theme = None
|
||||
self.service_item_type = None
|
||||
self._raw_frames = []
|
||||
@ -91,10 +93,18 @@ class ServiceItem(object):
|
||||
self.from_plugin = False
|
||||
self.capabilities = []
|
||||
self.is_valid = True
|
||||
self.cache = {}
|
||||
self.icon = None
|
||||
self.themedata = None
|
||||
self.main = None
|
||||
self.footer = None
|
||||
self.bg_frame = None
|
||||
|
||||
def _new_item(self):
|
||||
"""
|
||||
Method to set the internal id of the item
|
||||
This is used to compare service items to see if they are
|
||||
the same
|
||||
"""
|
||||
self._uuid = unicode(uuid.uuid1())
|
||||
|
||||
def add_capability(self, capability):
|
||||
@ -126,34 +136,38 @@ class ServiceItem(object):
|
||||
self.icon = icon
|
||||
self.iconic_representation = build_icon(icon)
|
||||
|
||||
def render(self):
|
||||
def render(self, useOverride=False):
|
||||
"""
|
||||
The render method is what generates the frames for the screen.
|
||||
The render method is what generates the frames for the screen and
|
||||
obtains the display information from the renderemanager.
|
||||
At this point all the slides are build for the given
|
||||
display size.
|
||||
"""
|
||||
log.debug(u'Render called')
|
||||
self._display_frames = []
|
||||
self.clear_cache()
|
||||
self.bg_frame = None
|
||||
line_break = True
|
||||
if self.is_capable(ItemCapabilities.NoLineBreaks):
|
||||
line_break = False
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
log.debug(u'Formatting slides')
|
||||
if self.theme is None:
|
||||
self.render_manager.set_override_theme(None)
|
||||
else:
|
||||
self.render_manager.set_override_theme(self.theme)
|
||||
theme = None
|
||||
if self.theme:
|
||||
theme = self.theme
|
||||
self.main, self.footer = \
|
||||
self.render_manager.set_override_theme(theme, useOverride)
|
||||
self.bg_frame = self.render_manager.renderer.bg_frame
|
||||
self.themedata = self.render_manager.renderer._theme
|
||||
for slide in self._raw_frames:
|
||||
before = time.time()
|
||||
formated = self.render_manager.format_slide(slide[u'raw_slide'])
|
||||
for format in formated:
|
||||
lines = u''
|
||||
title = u''
|
||||
for line in format:
|
||||
if title == u'':
|
||||
title = line
|
||||
lines += line + u'\n'
|
||||
self._display_frames.append({u'title': title,
|
||||
u'text': lines.rstrip(),
|
||||
formated = self.render_manager \
|
||||
.format_slide(slide[u'raw_slide'], line_break)
|
||||
for page in formated:
|
||||
self._display_frames.append(
|
||||
{u'title': clean_tags(page),
|
||||
u'text': clean_tags(page.rstrip()),
|
||||
u'html': expand_tags(page.rstrip()),
|
||||
u'verseTag': slide[u'verseTag'] })
|
||||
if len(self._display_frames) in self.cache.keys():
|
||||
del self.cache[len(self._display_frames)]
|
||||
log.log(15, u'Formatting took %4s' % (time.time() - before))
|
||||
elif self.service_item_type == ServiceItemType.Image:
|
||||
for slide in self._raw_frames:
|
||||
@ -163,29 +177,14 @@ class ServiceItem(object):
|
||||
pass
|
||||
else:
|
||||
log.error(u'Invalid value renderer :%s' % self.service_item_type)
|
||||
|
||||
def render_individual(self, row):
|
||||
"""
|
||||
Takes an array of text and generates an Image from the
|
||||
theme. It assumes the text will fit on the screen as it
|
||||
has generated by the render method above.
|
||||
"""
|
||||
log.debug(u'render individual')
|
||||
if self.theme is None:
|
||||
self.render_manager.set_override_theme(None)
|
||||
self.title = clean_tags(self.title)
|
||||
self.foot_text = None
|
||||
if self.raw_footer:
|
||||
for foot in self.raw_footer:
|
||||
if not self.foot_text:
|
||||
self.foot_text = foot
|
||||
else:
|
||||
self.render_manager.set_override_theme(self.theme)
|
||||
format = self._display_frames[row][u'text'].split(u'\n')
|
||||
if self.cache.get(row):
|
||||
frame = self.cache[row]
|
||||
else:
|
||||
if format[0]:
|
||||
frame = self.render_manager.generate_slide(format,
|
||||
self.raw_footer)
|
||||
else:
|
||||
frame = self.render_manager.generate_slide(format, u'')
|
||||
self.cache[row] = frame
|
||||
return frame
|
||||
self.foot_text = u'%s<br>%s' % (self.foot_text, foot)
|
||||
|
||||
def add_from_image(self, path, title, image):
|
||||
"""
|
||||
@ -375,9 +374,9 @@ class ServiceItem(object):
|
||||
renders it if required.
|
||||
"""
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
return self.render_individual(row)
|
||||
return None, self._display_frames[row][u'html'].split(u'\n')[0]
|
||||
else:
|
||||
return {u'main':self._raw_frames[row][u'image'], u'trans':None}
|
||||
return self._raw_frames[row][u'image'], u''
|
||||
|
||||
def get_frame_title(self, row=0):
|
||||
"""
|
||||
@ -390,9 +389,3 @@ class ServiceItem(object):
|
||||
Returns the title of the raw frame
|
||||
"""
|
||||
return self._raw_frames[row][u'path']
|
||||
|
||||
def clear_cache(self):
|
||||
"""
|
||||
Clear's the service item's cache.
|
||||
"""
|
||||
self.cache = {}
|
||||
|
@ -55,7 +55,6 @@ BLANK_THEME_XML = \
|
||||
<proportion>30</proportion>
|
||||
<weight>Normal</weight>
|
||||
<italics>False</italics>
|
||||
<indentation>0</indentation>
|
||||
<line_adjustment>0</line_adjustment>
|
||||
<location override="False" x="10" y="10" width="1004" height="730"/>
|
||||
</font>
|
||||
@ -65,7 +64,6 @@ BLANK_THEME_XML = \
|
||||
<proportion>12</proportion>
|
||||
<weight>Normal</weight>
|
||||
<italics>False</italics>
|
||||
<indentation>0</indentation>
|
||||
<line_adjustment>0</line_adjustment>
|
||||
<location override="False" x="10" y="730" width="1004" height="38"/>
|
||||
</font>
|
||||
@ -184,7 +182,7 @@ class ThemeXML(object):
|
||||
self.child_element(background, u'filename', filename)
|
||||
|
||||
def add_font(self, name, color, proportion, override, fonttype=u'main',
|
||||
weight=u'Normal', italics=u'False', indentation=0, line_adjustment=0,
|
||||
weight=u'Normal', italics=u'False', line_adjustment=0,
|
||||
xpos=0, ypos=0, width=0, height=0):
|
||||
"""
|
||||
Add a Font.
|
||||
@ -210,9 +208,6 @@ class ThemeXML(object):
|
||||
``italics``
|
||||
Does the font render to italics Defaults to 0 Normal
|
||||
|
||||
``indentation``
|
||||
Number of characters the wrap line is indented
|
||||
|
||||
``xpos``
|
||||
The X position of the text block.
|
||||
|
||||
@ -239,8 +234,6 @@ class ThemeXML(object):
|
||||
#Create italics name element
|
||||
self.child_element(background, u'italics', italics)
|
||||
#Create indentation name element
|
||||
self.child_element(background, u'indentation', unicode(indentation))
|
||||
#Create indentation name element
|
||||
self.child_element(
|
||||
background, u'line_adjustment', unicode(line_adjustment))
|
||||
|
||||
|
@ -204,7 +204,7 @@ class Theme(object):
|
||||
val = element_text
|
||||
if (element.tag.find(u'Color') > 0 or
|
||||
(element.tag.find(u'BackgroundParameter') == 0 and
|
||||
isinstance(int, val))):
|
||||
isinstance(val, int))):
|
||||
# convert to a wx.Colour
|
||||
if not delphi_color_change:
|
||||
val = QtGui.QColor(
|
||||
|
@ -27,6 +27,141 @@
|
||||
The :mod:`ui` module provides the core user interface for OpenLP
|
||||
"""
|
||||
|
||||
# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
|
||||
|
||||
import re
|
||||
import sys
|
||||
try:
|
||||
import enchant
|
||||
enchant_available = True
|
||||
except ImportError:
|
||||
enchant_available = False
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from openlp.core.lib import html_expands, translate, context_menu_action
|
||||
|
||||
class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||
def __init__(self, *args):
|
||||
QtGui.QPlainTextEdit.__init__(self, *args)
|
||||
# Default dictionary based on the current locale.
|
||||
if enchant_available:
|
||||
self.dict = enchant.Dict()
|
||||
self.highlighter = Highlighter(self.document())
|
||||
self.highlighter.setDict(self.dict)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == QtCore.Qt.RightButton:
|
||||
# Rewrite the mouse event to a left button event so the cursor is
|
||||
# moved to the location of the pointer.
|
||||
event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
|
||||
event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
|
||||
QtCore.Qt.NoModifier)
|
||||
QtGui.QPlainTextEdit.mousePressEvent(self, event)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
popup_menu = self.createStandardContextMenu()
|
||||
# Select the word under the cursor.
|
||||
cursor = self.textCursor()
|
||||
cursor.select(QtGui.QTextCursor.WordUnderCursor)
|
||||
self.setTextCursor(cursor)
|
||||
# Check if the selected word is misspelled and offer spelling
|
||||
# suggestions if it is.
|
||||
if enchant_available and self.textCursor().hasSelection():
|
||||
text = unicode(self.textCursor().selectedText())
|
||||
if not self.dict.check(text):
|
||||
spell_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
|
||||
'Spelling Suggestions'))
|
||||
for word in self.dict.suggest(text):
|
||||
action = SpellAction(word, spell_menu)
|
||||
action.correct.connect(self.correctWord)
|
||||
spell_menu.addAction(action)
|
||||
# Only add the spelling suggests to the menu if there are
|
||||
# suggestions.
|
||||
if len(spell_menu.actions()) != 0:
|
||||
popup_menu.insertSeparator(popup_menu.actions()[0])
|
||||
popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
|
||||
tag_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
|
||||
'Formatting Tags'))
|
||||
for html in html_expands:
|
||||
action = SpellAction( html[u'desc'], tag_menu)
|
||||
action.correct.connect(self.htmlTag)
|
||||
tag_menu.addAction(action)
|
||||
popup_menu.insertSeparator(popup_menu.actions()[0])
|
||||
popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
|
||||
|
||||
popup_menu.exec_(event.globalPos())
|
||||
|
||||
def correctWord(self, word):
|
||||
"""
|
||||
Replaces the selected text with word.
|
||||
"""
|
||||
cursor = self.textCursor()
|
||||
cursor.beginEditBlock()
|
||||
|
||||
cursor.removeSelectedText()
|
||||
cursor.insertText(word)
|
||||
|
||||
cursor.endEditBlock()
|
||||
|
||||
def htmlTag(self, tag):
|
||||
"""
|
||||
Replaces the selected text with word.
|
||||
"""
|
||||
for html in html_expands:
|
||||
if tag == html[u'desc']:
|
||||
cursor = self.textCursor()
|
||||
if self.textCursor().hasSelection():
|
||||
text = cursor.selectedText()
|
||||
cursor.beginEditBlock()
|
||||
cursor.removeSelectedText()
|
||||
cursor.insertText(html[u'start tag'])
|
||||
cursor.insertText(text)
|
||||
cursor.insertText(html[u'end tag'])
|
||||
cursor.endEditBlock()
|
||||
else:
|
||||
cursor = self.textCursor()
|
||||
cursor.insertText(html[u'start tag'])
|
||||
cursor.insertText(html[u'end tag'])
|
||||
|
||||
class Highlighter(QtGui.QSyntaxHighlighter):
|
||||
|
||||
WORDS = u'(?iu)[\w\']+'
|
||||
|
||||
def __init__(self, *args):
|
||||
QtGui.QSyntaxHighlighter.__init__(self, *args)
|
||||
|
||||
self.dict = None
|
||||
|
||||
def setDict(self, dict):
|
||||
self.dict = dict
|
||||
|
||||
def highlightBlock(self, text):
|
||||
if not self.dict:
|
||||
return
|
||||
|
||||
text = unicode(text)
|
||||
|
||||
format = QtGui.QTextCharFormat()
|
||||
format.setUnderlineColor(QtCore.Qt.red)
|
||||
format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
|
||||
|
||||
for word_object in re.finditer(self.WORDS, text):
|
||||
if not self.dict.check(word_object.group()):
|
||||
self.setFormat(word_object.start(),
|
||||
word_object.end() - word_object.start(), format)
|
||||
|
||||
class SpellAction(QtGui.QAction):
|
||||
"""
|
||||
A special QAction that returns the text in a signal.
|
||||
"""
|
||||
correct = QtCore.pyqtSignal(unicode)
|
||||
|
||||
def __init__(self, *args):
|
||||
QtGui.QAction.__init__(self, *args)
|
||||
|
||||
self.triggered.connect(lambda x: self.correct.emit(
|
||||
unicode(self.text())))
|
||||
|
||||
class HideMode(object):
|
||||
"""
|
||||
This is basically an enumeration class which specifies the mode of a Bible.
|
||||
@ -37,13 +172,11 @@ class HideMode(object):
|
||||
Theme = 2
|
||||
Screen = 3
|
||||
|
||||
from maindisplay import MainDisplay
|
||||
from slidecontroller import HideMode
|
||||
from servicenoteform import ServiceNoteForm
|
||||
from serviceitemeditform import ServiceItemEditForm
|
||||
from screen import ScreenList
|
||||
from maindisplay import MainDisplay
|
||||
from maindisplay import VideoDisplay
|
||||
from maindisplay import DisplayManager
|
||||
from amendthemeform import AmendThemeForm
|
||||
from slidecontroller import SlideController
|
||||
from splashscreen import SplashScreen
|
||||
@ -56,8 +189,7 @@ from settingsform import SettingsForm
|
||||
from mediadockmanager import MediaDockManager
|
||||
from servicemanager import ServiceManager
|
||||
from thememanager import ThemeManager
|
||||
from mainwindow import MainWindow
|
||||
|
||||
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainWindow',
|
||||
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm',
|
||||
'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager',
|
||||
'AmendThemeForm', 'MediaDockManager', 'ServiceItemEditForm']
|
||||
|
@ -68,17 +68,6 @@ class Ui_AmendThemeDialog(object):
|
||||
self.backgroundLayout.setMargin(8)
|
||||
self.backgroundLayout.setSpacing(8)
|
||||
self.backgroundLayout.setObjectName(u'backgroundLayout')
|
||||
self.backgroundLabel = QtGui.QLabel(self.backgroundTab)
|
||||
self.backgroundLabel.setObjectName(u'backgroundLabel')
|
||||
self.backgroundLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
|
||||
self.backgroundLabel)
|
||||
self.backgroundComboBox = QtGui.QComboBox(self.backgroundTab)
|
||||
self.backgroundComboBox.setObjectName(u'backgroundComboBox')
|
||||
self.backgroundLabel.setBuddy(self.backgroundComboBox)
|
||||
self.backgroundComboBox.addItem(QtCore.QString())
|
||||
self.backgroundComboBox.addItem(QtCore.QString())
|
||||
self.backgroundLayout.setWidget(0, QtGui.QFormLayout.FieldRole,
|
||||
self.backgroundComboBox)
|
||||
self.backgroundTypeLabel = QtGui.QLabel(self.backgroundTab)
|
||||
self.backgroundTypeLabel.setObjectName(u'backgroundTypeLabel')
|
||||
self.backgroundLayout.setWidget(1, QtGui.QFormLayout.LabelRole,
|
||||
@ -216,17 +205,6 @@ class Ui_AmendThemeDialog(object):
|
||||
self.fontMainLineAdjustmentSpinBox.setMinimum(-99)
|
||||
self.mainFontLayout.setWidget(4, QtGui.QFormLayout.FieldRole,
|
||||
self.fontMainLineAdjustmentSpinBox)
|
||||
self.fontMainWrapIndentationLabel = QtGui.QLabel(self.fontMainGroupBox)
|
||||
self.fontMainWrapIndentationLabel.setObjectName(
|
||||
u'fontMainWrapIndentationLabel')
|
||||
self.mainFontLayout.setWidget(5, QtGui.QFormLayout.LabelRole,
|
||||
self.fontMainWrapIndentationLabel)
|
||||
self.fontMainLineSpacingSpinBox = QtGui.QSpinBox(self.fontMainGroupBox)
|
||||
self.fontMainLineSpacingSpinBox.setObjectName(
|
||||
u'fontMainLineSpacingSpinBox')
|
||||
self.fontMainLineSpacingSpinBox.setMaximum(10)
|
||||
self.mainFontLayout.setWidget(5, QtGui.QFormLayout.FieldRole,
|
||||
self.fontMainLineSpacingSpinBox)
|
||||
self.fontMainLinesPageLabel = QtGui.QLabel(self.fontMainGroupBox)
|
||||
self.fontMainLinesPageLabel.setObjectName(u'fontMainLinesPageLabel')
|
||||
self.mainFontLayout.addRow(self.fontMainLinesPageLabel)
|
||||
@ -661,12 +639,6 @@ class Ui_AmendThemeDialog(object):
|
||||
translate('OpenLP.AmendThemeForm', 'Theme Maintenance'))
|
||||
self.themeNameLabel.setText(
|
||||
translate('OpenLP.AmendThemeForm', 'Theme &name:'))
|
||||
self.backgroundLabel.setText(
|
||||
translate('OpenLP.AmendThemeForm', '&Visibility:'))
|
||||
self.backgroundComboBox.setItemText(0,
|
||||
translate('OpenLP.AmendThemeForm', 'Opaque'))
|
||||
self.backgroundComboBox.setItemText(1,
|
||||
translate('OpenLP.AmendThemeForm', 'Transparent'))
|
||||
self.backgroundTypeLabel.setText(
|
||||
translate('OpenLP.AmendThemeForm', 'Type:'))
|
||||
self.backgroundTypeComboBox.setItemText(0,
|
||||
@ -700,8 +672,6 @@ class Ui_AmendThemeDialog(object):
|
||||
translate('OpenLP.AmendThemeForm', 'Size:'))
|
||||
self.fontMainSizeSpinBox.setSuffix(
|
||||
translate('OpenLP.AmendThemeForm', 'pt'))
|
||||
self.fontMainWrapIndentationLabel.setText(
|
||||
translate('OpenLP.AmendThemeForm', 'Wrap indentation:'))
|
||||
self.fontMainWrapLineAdjustmentLabel.setText(
|
||||
translate('OpenLP.AmendThemeForm', 'Adjust line spacing:'))
|
||||
self.fontMainWeightComboBox.setItemText(0,
|
||||
|
@ -50,7 +50,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
self.path = None
|
||||
self.theme = ThemeXML()
|
||||
self.setupUi(self)
|
||||
# define signals
|
||||
# Buttons
|
||||
QtCore.QObject.connect(self.color1PushButton,
|
||||
QtCore.SIGNAL(u'pressed()'), self.onColor1PushButtonClicked)
|
||||
@ -68,8 +67,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
QtCore.QObject.connect(self.imageToolButton,
|
||||
QtCore.SIGNAL(u'clicked()'), self.onImageToolButtonClicked)
|
||||
# Combo boxes
|
||||
QtCore.QObject.connect(self.backgroundComboBox,
|
||||
QtCore.SIGNAL(u'activated(int)'), self.onBackgroundComboBoxSelected)
|
||||
QtCore.QObject.connect(self.backgroundTypeComboBox,
|
||||
QtCore.SIGNAL(u'activated(int)'),
|
||||
self.onBackgroundTypeComboBoxSelected)
|
||||
@ -109,9 +106,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
QtCore.QObject.connect(self.fontMainLineAdjustmentSpinBox,
|
||||
QtCore.SIGNAL(u'editingFinished()'),
|
||||
self.onFontMainLineAdjustmentSpinBoxChanged)
|
||||
QtCore.QObject.connect(self.fontMainLineSpacingSpinBox,
|
||||
QtCore.SIGNAL(u'editingFinished()'),
|
||||
self.onFontMainLineSpacingSpinBoxChanged)
|
||||
QtCore.QObject.connect(self.fontFooterXSpinBox,
|
||||
QtCore.SIGNAL(u'editingFinished()'),
|
||||
self.onFontFooterXSpinBoxChanged)
|
||||
@ -151,9 +145,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
new_theme.new_document(theme_name)
|
||||
save_from = None
|
||||
save_to = None
|
||||
if self.theme.background_mode == u'transparent':
|
||||
new_theme.add_background_transparent()
|
||||
else:
|
||||
if self.theme.background_type == u'solid':
|
||||
new_theme.add_background_solid(
|
||||
unicode(self.theme.background_color))
|
||||
@ -174,7 +165,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
unicode(self.theme.font_main_override), u'main',
|
||||
unicode(self.theme.font_main_weight),
|
||||
unicode(self.theme.font_main_italics),
|
||||
unicode(self.theme.font_main_indentation),
|
||||
unicode(self.theme.font_main_line_adjustment),
|
||||
unicode(self.theme.font_main_x),
|
||||
unicode(self.theme.font_main_y),
|
||||
@ -186,7 +176,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
unicode(self.theme.font_footer_override), u'footer',
|
||||
unicode(self.theme.font_footer_weight),
|
||||
unicode(self.theme.font_footer_italics),
|
||||
0, # indentation
|
||||
0, # line adjustment
|
||||
unicode(self.theme.font_footer_x),
|
||||
unicode(self.theme.font_footer_y),
|
||||
@ -230,7 +219,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
self.previewTheme()
|
||||
|
||||
#
|
||||
#Main Font Tab
|
||||
# Main Font Tab
|
||||
#
|
||||
def onFontMainComboBoxSelected(self):
|
||||
self.theme.font_main_name = self.fontMainComboBox.currentFont().family()
|
||||
@ -283,8 +272,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
self.fontMainHeightSpinBox.setValue(self.theme.font_main_height)
|
||||
self.fontMainLineAdjustmentSpinBox.setValue(
|
||||
self.theme.font_main_line_adjustment)
|
||||
self.fontMainLineSpacingSpinBox.setValue(
|
||||
self.theme.font_main_indentation)
|
||||
self.stateChanging(self.theme)
|
||||
self.previewTheme()
|
||||
|
||||
@ -310,20 +297,13 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
self.fontMainLineAdjustmentSpinBox.value()
|
||||
self.previewTheme()
|
||||
|
||||
def onFontMainLineSpacingSpinBoxChanged(self):
|
||||
if self.theme.font_main_indentation != \
|
||||
self.fontMainLineSpacingSpinBox.value():
|
||||
self.theme.font_main_indentation = \
|
||||
self.fontMainLineSpacingSpinBox.value()
|
||||
self.previewTheme()
|
||||
|
||||
def onFontMainHeightSpinBoxChanged(self):
|
||||
if self.theme.font_main_height != self.fontMainHeightSpinBox.value():
|
||||
self.theme.font_main_height = self.fontMainHeightSpinBox.value()
|
||||
self.previewTheme()
|
||||
|
||||
#
|
||||
#Footer Font Tab
|
||||
# Footer Font Tab
|
||||
#
|
||||
def onFontFooterComboBoxSelected(self):
|
||||
self.theme.font_footer_name = \
|
||||
@ -404,20 +384,12 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
self.previewTheme()
|
||||
|
||||
#
|
||||
#Background Tab
|
||||
# Background Tab
|
||||
#
|
||||
def onGradientComboBoxSelected(self, currentIndex):
|
||||
self.setBackground(self.backgroundTypeComboBox.currentIndex(),
|
||||
currentIndex)
|
||||
|
||||
def onBackgroundComboBoxSelected(self, currentIndex):
|
||||
if currentIndex == 0: # Opaque
|
||||
self.theme.background_mode = u'opaque'
|
||||
else:
|
||||
self.theme.background_mode = u'transparent'
|
||||
self.stateChanging(self.theme)
|
||||
self.previewTheme()
|
||||
|
||||
def onBackgroundTypeComboBoxSelected(self, currentIndex):
|
||||
self.setBackground(currentIndex, self.gradientComboBox.currentIndex())
|
||||
|
||||
@ -472,7 +444,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
self.previewTheme()
|
||||
|
||||
#
|
||||
#Other Tab
|
||||
# Other Tab
|
||||
#
|
||||
def onOutlineCheckBoxChanged(self, value):
|
||||
if value == 2: # checked
|
||||
@ -537,16 +509,12 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
self.previewTheme()
|
||||
|
||||
#
|
||||
#Local Methods
|
||||
# Local Methods
|
||||
#
|
||||
def paintUi(self, theme):
|
||||
self.stateChanging(theme)
|
||||
self.themeNameEdit.setText(self.theme.theme_name)
|
||||
# Background Tab
|
||||
if self.theme.background_mode == u'opaque':
|
||||
self.backgroundComboBox.setCurrentIndex(0)
|
||||
else:
|
||||
self.backgroundComboBox.setCurrentIndex(1)
|
||||
self.imageLineEdit.setText(u'')
|
||||
if theme.background_type == u'solid':
|
||||
self.backgroundTypeComboBox.setCurrentIndex(0)
|
||||
@ -576,8 +544,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
self.fontMainWeightComboBox.setCurrentIndex(2)
|
||||
else:
|
||||
self.fontMainWeightComboBox.setCurrentIndex(3)
|
||||
self.fontMainLineSpacingSpinBox.setValue(
|
||||
self.theme.font_main_indentation)
|
||||
self.fontMainXSpinBox.setValue(self.theme.font_main_x)
|
||||
self.fontMainYSpinBox.setValue(self.theme.font_main_y)
|
||||
self.fontMainWidthSpinBox.setValue(self.theme.font_main_width)
|
||||
@ -641,19 +607,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
|
||||
self.verticalComboBox.setCurrentIndex(self.theme.display_verticalAlign)
|
||||
|
||||
def stateChanging(self, theme):
|
||||
if theme.background_mode == u'transparent':
|
||||
self.color1Label.setVisible(False)
|
||||
self.color1PushButton.setVisible(False)
|
||||
self.color2Label.setVisible(False)
|
||||
self.color2PushButton.setVisible(False)
|
||||
self.imageLabel.setVisible(False)
|
||||
self.imageLineEdit.setVisible(False)
|
||||
self.imageFilenameWidget.setVisible(False)
|
||||
self.gradientLabel.setVisible(False)
|
||||
self.gradientComboBox.setVisible(False)
|
||||
self.backgroundTypeComboBox.setVisible(False)
|
||||
self.backgroundTypeLabel.setVisible(False)
|
||||
else:
|
||||
self.backgroundTypeComboBox.setVisible(True)
|
||||
self.backgroundTypeLabel.setVisible(True)
|
||||
if theme.background_type == u'solid':
|
||||
|
@ -390,7 +390,6 @@ class GeneralTab(SettingsTab):
|
||||
unicode(self.screens.current[u'size'].width()))
|
||||
self.overrideCheckBox.setChecked(settings.value(u'override position',
|
||||
QtCore.QVariant(False)).toBool())
|
||||
if self.overrideCheckBox.isChecked():
|
||||
self.customXValueEdit.setText(settings.value(u'x position',
|
||||
QtCore.QVariant(self.screens.current[u'size'].x())).toString())
|
||||
self.customYValueEdit.setText(settings.value(u'y position',
|
||||
@ -401,15 +400,6 @@ class GeneralTab(SettingsTab):
|
||||
self.customWidthValueEdit.setText(
|
||||
settings.value(u'width', QtCore.QVariant(
|
||||
self.screens.current[u'size'].width())).toString())
|
||||
else:
|
||||
self.customXValueEdit.setText(
|
||||
unicode(self.screens.current[u'size'].x()))
|
||||
self.customYValueEdit.setText(
|
||||
unicode(self.screens.current[u'size'].y()))
|
||||
self.customHeightValueEdit.setText(
|
||||
unicode(self.screens.current[u'size'].height()))
|
||||
self.customWidthValueEdit.setText(
|
||||
unicode(self.screens.current[u'size'].width()))
|
||||
settings.endGroup()
|
||||
self.customXValueEdit.setEnabled(self.overrideCheckBox.isChecked())
|
||||
self.customYValueEdit.setEnabled(self.overrideCheckBox.isChecked())
|
||||
@ -438,8 +428,6 @@ class GeneralTab(SettingsTab):
|
||||
QtCore.QVariant(self.autoPreviewCheckBox.isChecked()))
|
||||
settings.setValue(u'loop delay',
|
||||
QtCore.QVariant(self.timeoutSpinBox.value()))
|
||||
Receiver.send_message(u'slidecontroller_live_spin_delay',
|
||||
self.timeoutSpinBox.value())
|
||||
settings.setValue(u'ccli number',
|
||||
QtCore.QVariant(self.numberEdit.displayText()))
|
||||
settings.setValue(u'songselect username',
|
||||
@ -459,17 +447,18 @@ class GeneralTab(SettingsTab):
|
||||
settings.endGroup()
|
||||
self.screens.display = self.displayOnMonitorCheck.isChecked()
|
||||
# Monitor Number has changed.
|
||||
postUpdate = False
|
||||
if self.screens.monitor_number != self.monitorNumber:
|
||||
self.screens.monitor_number = self.monitorNumber
|
||||
self.screens.set_current_display(self.monitorNumber)
|
||||
Receiver.send_message(u'config_screen_changed')
|
||||
Receiver.send_message(u'config_updated')
|
||||
postUpdate = True
|
||||
# On save update the screens as well
|
||||
self.postSetUp()
|
||||
self.postSetUp(postUpdate)
|
||||
|
||||
def postSetUp(self):
|
||||
def postSetUp(self, postUpdate=False):
|
||||
"""
|
||||
Apply settings after settings tab has loaded
|
||||
Apply settings after settings tab has loaded and most of the
|
||||
system so must be delayed
|
||||
"""
|
||||
Receiver.send_message(u'slidecontroller_live_spin_delay',
|
||||
self.timeoutSpinBox.value())
|
||||
@ -482,10 +471,13 @@ class GeneralTab(SettingsTab):
|
||||
int(self.customHeightValueEdit.text()))
|
||||
if self.overrideCheckBox.isChecked():
|
||||
self.screens.set_override_display()
|
||||
Receiver.send_message(u'config_screen_changed')
|
||||
else:
|
||||
self.screens.reset_current_display()
|
||||
# Order is important so be careful if you change
|
||||
if self.overrideChanged or postUpdate:
|
||||
Receiver.send_message(u'config_screen_changed')
|
||||
Receiver.send_message(u'config_updated')
|
||||
self.overrideChanged = False
|
||||
|
||||
def onOverrideCheckBoxToggled(self, checked):
|
||||
"""
|
||||
|
@ -26,149 +26,46 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
from PyQt4 import QtCore, QtGui, QtWebKit
|
||||
from PyQt4.phonon import Phonon
|
||||
|
||||
from openlp.core.lib import Receiver, resize_image
|
||||
from openlp.core.lib import Receiver, resize_image, build_html, ServiceItem, \
|
||||
image_to_byte
|
||||
from openlp.core.ui import HideMode
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
#http://www.steveheffernan.com/html5-video-player/demo-video-player.html
|
||||
HTMLVIDEO = u"""<html>
|
||||
<head>
|
||||
<style>
|
||||
*{
|
||||
margin: 0;
|
||||
padding:0
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
var video;
|
||||
var bodyLoaded = function(){
|
||||
video = document.getElementById("video");
|
||||
video.volume = 0;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body id="body" onload="bodyLoaded();">
|
||||
<video id="video" src="%s" autoplay="autoplay" loop="loop"
|
||||
width="%s" height="%s" autobuffer="autobuffer" preload="preload" />
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
class DisplayManager(QtGui.QWidget):
|
||||
"""
|
||||
Wrapper class to hold the display widgets.
|
||||
I will provide API's in future to access the screens allow for
|
||||
extra displays to be added.
|
||||
RenderManager is poked in by MainWindow
|
||||
"""
|
||||
def __init__(self, screens):
|
||||
QtGui.QWidget.__init__(self)
|
||||
self.screens = screens
|
||||
self.videoDisplay = VideoDisplay(self, screens)
|
||||
self.audioPlayer = AudioPlayer(self)
|
||||
self.mainDisplay = MainDisplay(self, screens)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'videodisplay_start'), self.onStartVideo)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'videodisplay_stop'), self.onStopVideo)
|
||||
|
||||
def setup(self):
|
||||
self.videoDisplay.setup()
|
||||
self.mainDisplay.setup()
|
||||
|
||||
def hideDisplay(self, message):
|
||||
"""
|
||||
Hide the output displays
|
||||
"""
|
||||
self.videoDisplay.mediaHide(message)
|
||||
self.mainDisplay.hideDisplay(message)
|
||||
|
||||
def showDisplay(self):
|
||||
"""
|
||||
Hide the output displays
|
||||
"""
|
||||
self.videoDisplay.mediaShow()
|
||||
self.mainDisplay.showDisplay()
|
||||
|
||||
def addAlert(self, alertMessage, location):
|
||||
"""
|
||||
Handles the addition of an Alert Message to the Displays
|
||||
"""
|
||||
self.mainDisplay.addAlert(alertMessage, location)
|
||||
|
||||
def displayImageWithText(self, frame):
|
||||
"""
|
||||
Handles the addition of a background Image to the displays
|
||||
"""
|
||||
self.mainDisplay.addImageWithText(frame)
|
||||
|
||||
def displayImage(self, frame):
|
||||
"""
|
||||
Handles the addition of a background Image to the displays
|
||||
"""
|
||||
self.mainDisplay.displayImage(frame)
|
||||
|
||||
def displayVideo(self, path):
|
||||
"""
|
||||
Handles the addition of a background Video to the displays
|
||||
"""
|
||||
self.mainDisplay.displayVideo(path)
|
||||
|
||||
def onStartVideo(self, item):
|
||||
"""
|
||||
Handles the Starting of a Video and Display Management
|
||||
"""
|
||||
self.videoDisplay.setVisible(True)
|
||||
self.mainDisplay.setVisible(False)
|
||||
self.videoDisplay.onMediaQueue(item)
|
||||
|
||||
def onStopVideo(self):
|
||||
"""
|
||||
Handles the Stopping of a Video and Display Management
|
||||
"""
|
||||
self.mainDisplay.setVisible(True)
|
||||
self.videoDisplay.setVisible(False)
|
||||
self.videoDisplay.onMediaStop()
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Handles the closure of the displays
|
||||
"""
|
||||
self.videoDisplay.close()
|
||||
self.audioPlayer.close()
|
||||
self.mainDisplay.close()
|
||||
|
||||
|
||||
class DisplayWidget(QtGui.QGraphicsView):
|
||||
"""
|
||||
Customised version of QTableWidget which can respond to keyboard
|
||||
events.
|
||||
"""
|
||||
log.info(u'MainDisplay loaded')
|
||||
log.info(u'Display Widget loaded')
|
||||
|
||||
def __init__(self, parent=None, name=None, primary=False):
|
||||
QtGui.QWidget.__init__(self, None)
|
||||
def __init__(self, live, parent=None):
|
||||
QtGui.QGraphicsView.__init__(self)
|
||||
self.parent = parent
|
||||
self.primary = primary
|
||||
self.live = live
|
||||
self.hotkey_map = {
|
||||
QtCore.Qt.Key_Return: 'servicemanager_next_item',
|
||||
QtCore.Qt.Key_Space: 'slidecontroller_live_next_noloop',
|
||||
QtCore.Qt.Key_Enter: 'slidecontroller_live_next_noloop',
|
||||
QtCore.Qt.Key_0: 'servicemanager_next_item',
|
||||
QtCore.Qt.Key_Backspace: 'slidecontroller_live_previous_noloop'}
|
||||
self.setStyleSheet(u'border: none;')
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""
|
||||
Handle key events from display screen
|
||||
"""
|
||||
# Key events only needed for live
|
||||
if not self.live:
|
||||
return
|
||||
if isinstance(event, QtGui.QKeyEvent):
|
||||
#here accept the event and do something
|
||||
# Here accept the event and do something
|
||||
if event.key() == QtCore.Qt.Key_Up:
|
||||
Receiver.send_message(u'slidecontroller_live_previous')
|
||||
event.accept()
|
||||
@ -185,157 +82,271 @@ class DisplayWidget(QtGui.QGraphicsView):
|
||||
Receiver.send_message(self.hotkey_map[event.key()])
|
||||
event.accept()
|
||||
elif event.key() == QtCore.Qt.Key_Escape:
|
||||
self.resetDisplay()
|
||||
self.setVisible(False)
|
||||
self.videoStop()
|
||||
event.accept()
|
||||
event.ignore()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def resetDisplay(self):
|
||||
log.debug(u'resetDisplay')
|
||||
Receiver.send_message(u'slidecontroller_live_stop_loop')
|
||||
if self.primary:
|
||||
self.setVisible(False)
|
||||
else:
|
||||
self.setVisible(True)
|
||||
|
||||
class MainDisplay(DisplayWidget):
|
||||
"""
|
||||
This is the form that is used to display things on the projector.
|
||||
"""
|
||||
log.info(u'MainDisplay Loaded')
|
||||
|
||||
def __init__(self, parent, screens):
|
||||
"""
|
||||
The constructor for the display form.
|
||||
|
||||
``parent``
|
||||
The parent widget.
|
||||
|
||||
``screens``
|
||||
The list of screens.
|
||||
"""
|
||||
log.debug(u'Initialisation started')
|
||||
DisplayWidget.__init__(self, parent, primary=True)
|
||||
self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint)
|
||||
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
# WA_TranslucentBackground is not available in QT4.4
|
||||
try:
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
except AttributeError:
|
||||
pass
|
||||
def __init__(self, parent, screens, live):
|
||||
DisplayWidget.__init__(self, live, parent=None)
|
||||
self.parent = parent
|
||||
self.screens = screens
|
||||
self.setupScene()
|
||||
self.setupVideo()
|
||||
self.setupImage()
|
||||
self.setupText()
|
||||
self.setupAlert()
|
||||
self.setupBlank()
|
||||
self.blankFrame = None
|
||||
self.frame = None
|
||||
#Hide desktop for now until we know where to put it
|
||||
#and what size it should be.
|
||||
self.setVisible(False)
|
||||
self.isLive = live
|
||||
self.alertTab = None
|
||||
self.setWindowTitle(u'OpenLP Display')
|
||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
|
||||
QtCore.Qt.WindowStaysOnTopHint)
|
||||
if self.isLive:
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay)
|
||||
|
||||
def setup(self):
|
||||
"""
|
||||
Sets up the screen on a particular screen.
|
||||
Set up and build the output screen
|
||||
"""
|
||||
log.debug(u'Setup %s for %s ' % (
|
||||
self.screens, self.screens.monitor_number))
|
||||
self.setVisible(False)
|
||||
log.debug(u'Setup live = %s for %s ' % (self.isLive,
|
||||
self.screens.monitor_number))
|
||||
self.screen = self.screens.current
|
||||
#Sort out screen locations and sizes
|
||||
self.setVisible(False)
|
||||
self.setGeometry(self.screen[u'size'])
|
||||
self.scene.setSceneRect(0, 0, self.size().width(),
|
||||
self.size().height())
|
||||
self.webView.setGeometry(0, 0, self.size().width(),
|
||||
self.size().height())
|
||||
self.alertText.setTextWidth(self.size().width())
|
||||
#Build a custom splash screen
|
||||
self.initialFrame = QtGui.QImage(
|
||||
self.screen[u'size'].width(),
|
||||
self.screen[u'size'].height(),
|
||||
self.webView = QtWebKit.QWebView(self)
|
||||
self.webView.setGeometry(0, 0, self.screen[u'size'].width(), \
|
||||
self.screen[u'size'].height())
|
||||
self.page = self.webView.page()
|
||||
self.frame = self.page.mainFrame()
|
||||
QtCore.QObject.connect(self.webView,
|
||||
QtCore.SIGNAL(u'loadFinished(bool)'), self.isLoaded)
|
||||
self.frame.setScrollBarPolicy(QtCore.Qt.Vertical,
|
||||
QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
|
||||
QtCore.Qt.ScrollBarAlwaysOff)
|
||||
if self.isLive:
|
||||
# Build the initial frame.
|
||||
self.black = QtGui.QImage(
|
||||
self.screens.current[u'size'].width(),
|
||||
self.screens.current[u'size'].height(),
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
painter_image = QtGui.QPainter()
|
||||
painter_image.begin(self.black)
|
||||
painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
|
||||
#Build the initial frame.
|
||||
initialFrame = QtGui.QImage(
|
||||
self.screens.current[u'size'].width(),
|
||||
self.screens.current[u'size'].height(),
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png')
|
||||
painter_image = QtGui.QPainter()
|
||||
painter_image.begin(self.initialFrame)
|
||||
painter_image.fillRect(self.initialFrame.rect(), QtCore.Qt.white)
|
||||
painter_image.begin(initialFrame)
|
||||
painter_image.fillRect(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,
|
||||
(self.screens.current[u'size'].width() \
|
||||
- splash_image.width()) / 2,
|
||||
(self.screens.current[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)
|
||||
serviceItem = ServiceItem()
|
||||
serviceItem.bg_frame = initialFrame
|
||||
self.webView.setHtml(build_html(serviceItem, self.screen,
|
||||
self.parent.alertTab, self.isLive))
|
||||
self.initialFrame = True
|
||||
# To display or not to display?
|
||||
if not self.screen[u'primary']:
|
||||
self.setVisible(True)
|
||||
self.show()
|
||||
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 text(self, slide):
|
||||
"""
|
||||
Add the slide text from slideController
|
||||
|
||||
def setupVideo(self):
|
||||
self.webView = QtWebKit.QWebView()
|
||||
self.page = self.webView.page()
|
||||
self.videoDisplay = self.page.mainFrame()
|
||||
self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Vertical,
|
||||
QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Horizontal,
|
||||
QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.proxy = QtGui.QGraphicsProxyWidget()
|
||||
self.proxy.setWidget(self.webView)
|
||||
self.proxy.setWindowFlags(QtCore.Qt.Window |
|
||||
QtCore.Qt.FramelessWindowHint)
|
||||
self.proxy.setZValue(1)
|
||||
self.scene.addItem(self.proxy)
|
||||
`slide`
|
||||
The slide text to be displayed
|
||||
"""
|
||||
log.debug(u'text')
|
||||
self.frame.evaluateJavaScript(u'show_text("%s")' % \
|
||||
slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
||||
return self.preview()
|
||||
|
||||
def setupImage(self):
|
||||
self.imageDisplay = QtGui.QGraphicsPixmapItem()
|
||||
self.imageDisplay.setZValue(2)
|
||||
self.scene.addItem(self.imageDisplay)
|
||||
def alert(self, text):
|
||||
"""
|
||||
Add the alert text
|
||||
|
||||
def setupText(self):
|
||||
#self.displayText = QtGui.QGraphicsTextItem()
|
||||
self.displayText = QtGui.QGraphicsPixmapItem()
|
||||
#self.displayText.setPos(0,0)
|
||||
#self.displayText.setTextWidth(self.size().width())
|
||||
self.displayText.setZValue(4)
|
||||
self.scene.addItem(self.displayText)
|
||||
`slide`
|
||||
The slide text to be displayed
|
||||
"""
|
||||
log.debug(u'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 setupAlert(self):
|
||||
self.alertText = QtGui.QGraphicsTextItem()
|
||||
self.alertText.setZValue(8)
|
||||
self.scene.addItem(self.alertText)
|
||||
def image(self, image):
|
||||
"""
|
||||
Add an image as the background. The image is converted to a
|
||||
bytestream on route.
|
||||
|
||||
def setupBlank(self):
|
||||
self.displayBlank = QtGui.QGraphicsPixmapItem()
|
||||
self.displayBlank.setZValue(10)
|
||||
self.scene.addItem(self.displayBlank)
|
||||
`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 hideDisplayForVideo(self):
|
||||
# """
|
||||
# Hides the main display if for the video to be played
|
||||
# """
|
||||
# self.hideDisplay(HideMode.Screen)
|
||||
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)
|
||||
if 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.isLive)
|
||||
self.webView.setHtml(html)
|
||||
if serviceItem.foot_text and serviceItem.foot_text:
|
||||
self.footer(serviceItem.foot_text)
|
||||
|
||||
def footer(self, text):
|
||||
"""
|
||||
Display the Footer
|
||||
"""
|
||||
log.debug(u'footer')
|
||||
js = "show_footer('" + \
|
||||
text.replace("\\", "\\\\").replace("\'", "\\\'") + "')"
|
||||
self.frame.evaluateJavaScript(js)
|
||||
|
||||
def hideDisplay(self, mode=HideMode.Screen):
|
||||
"""
|
||||
@ -343,20 +354,13 @@ class MainDisplay(DisplayWidget):
|
||||
Store the images so they can be replaced when required
|
||||
"""
|
||||
log.debug(u'hideDisplay mode = %d', mode)
|
||||
#self.displayText.setPixmap(self.transparent)
|
||||
if mode == HideMode.Screen:
|
||||
#self.display_image.setPixmap(self.transparent)
|
||||
self.frame.evaluateJavaScript(u'show_blank("desktop");')
|
||||
self.setVisible(False)
|
||||
elif mode == HideMode.Blank:
|
||||
self.displayBlank.setPixmap(
|
||||
QtGui.QPixmap.fromImage(self.blankFrame))
|
||||
elif mode == HideMode.Blank or self.initialFrame:
|
||||
self.frame.evaluateJavaScript(u'show_blank("black");')
|
||||
else:
|
||||
if self.parent.renderManager.renderer.bg_frame:
|
||||
self.displayBlank.setPixmap(QtGui.QPixmap.fromImage(
|
||||
self.parent.renderManager.renderer.bg_frame))
|
||||
else:
|
||||
self.displayBlank.setPixmap(
|
||||
QtGui.QPixmap.fromImage(self.blankFrame))
|
||||
self.frame.evaluateJavaScript(u'show_blank("theme");')
|
||||
if mode != HideMode.Screen and self.isHidden():
|
||||
self.setVisible(True)
|
||||
|
||||
@ -367,275 +371,16 @@ class MainDisplay(DisplayWidget):
|
||||
Make the stored images None to release memory.
|
||||
"""
|
||||
log.debug(u'showDisplay')
|
||||
self.displayBlank.setPixmap(self.transparent)
|
||||
self.frame.evaluateJavaScript('show_blank("show");')
|
||||
if self.isHidden():
|
||||
self.setVisible(True)
|
||||
#Trigger actions when display is active again
|
||||
# Trigger actions when display is active again
|
||||
Receiver.send_message(u'maindisplay_active')
|
||||
|
||||
def addImageWithText(self, frame):
|
||||
log.debug(u'addImageWithText')
|
||||
frame = resize_image(
|
||||
frame, self.screen[u'size'].width(), self.screen[u'size'].height())
|
||||
self.imageDisplay.setPixmap(QtGui.QPixmap.fromImage(frame))
|
||||
self.videoDisplay.setHtml(u'<html></html>')
|
||||
|
||||
def addAlert(self, message, location):
|
||||
"""
|
||||
Places the Alert text on the display at the correct location
|
||||
``message``
|
||||
Text to be displayed
|
||||
``location``
|
||||
Where on the screen the text should be. From the AlertTab
|
||||
Combo box.
|
||||
"""
|
||||
log.debug(u'addAlertImage')
|
||||
if location == 0:
|
||||
self.alertText.setPos(0, 0)
|
||||
elif location == 1:
|
||||
self.alertText.setPos(0, self.size().height() / 2)
|
||||
else:
|
||||
self.alertText.setPos(0, self.size().height() - 76)
|
||||
self.alertText.setHtml(message)
|
||||
|
||||
def displayImage(self, frame):
|
||||
"""
|
||||
Places the Image passed on the display screen
|
||||
``frame``
|
||||
The image to be displayed
|
||||
"""
|
||||
log.debug(u'adddisplayImage')
|
||||
if isinstance(frame, QtGui.QImage):
|
||||
self.imageDisplay.setPixmap(QtGui.QPixmap.fromImage(frame))
|
||||
else:
|
||||
self.imageDisplay.setPixmap(frame)
|
||||
self.frameView(self.transparent)
|
||||
self.videoDisplay.setHtml(u'<html></html>')
|
||||
|
||||
def displayVideo(self, path):
|
||||
"""
|
||||
Places the Video passed on the display screen
|
||||
``path``
|
||||
The path to the image to be displayed
|
||||
"""
|
||||
log.debug(u'adddisplayVideo')
|
||||
self.displayImage(self.transparent)
|
||||
self.videoDisplay.setHtml(HTMLVIDEO %
|
||||
(path, self.screen[u'size'].width(),
|
||||
self.screen[u'size'].height()))
|
||||
|
||||
def frameView(self, frame, transition=False):
|
||||
"""
|
||||
Called from a slide controller to display a frame
|
||||
if the alert is in progress the alert is added on top
|
||||
``frame``
|
||||
Image frame to be rendered
|
||||
``transition``
|
||||
Are transitions required.
|
||||
"""
|
||||
log.debug(u'frameView')
|
||||
if transition:
|
||||
if self.frame is not None:
|
||||
self.displayText.setPixmap(
|
||||
QtGui.QPixmap.fromImage(self.frame))
|
||||
self.repaint()
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
time.sleep(0.1)
|
||||
self.frame = None
|
||||
if frame[u'trans'] is not None:
|
||||
self.displayText.setPixmap(
|
||||
QtGui.QPixmap.fromImage(frame[u'trans']))
|
||||
self.repaint()
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
time.sleep(0.1)
|
||||
self.frame = frame[u'trans']
|
||||
self.displayText.setPixmap(
|
||||
QtGui.QPixmap.fromImage(frame[u'main']))
|
||||
else:
|
||||
if isinstance(frame, QtGui.QPixmap):
|
||||
self.displayText.setPixmap(frame)
|
||||
else:
|
||||
self.displayText.setPixmap(QtGui.QPixmap.fromImage(frame))
|
||||
if not self.isVisible() and self.screens.current['primary']:
|
||||
self.setVisible(True)
|
||||
|
||||
class VideoDisplay(Phonon.VideoWidget):
|
||||
"""
|
||||
This is the form that is used to display videos on the projector.
|
||||
"""
|
||||
log.info(u'VideoDisplay Loaded')
|
||||
|
||||
def __init__(self, parent, screens,
|
||||
aspect=Phonon.VideoWidget.AspectRatioWidget):
|
||||
"""
|
||||
The constructor for the display form.
|
||||
|
||||
``parent``
|
||||
The parent widget.
|
||||
|
||||
``screens``
|
||||
The list of screens.
|
||||
"""
|
||||
log.debug(u'VideoDisplay Initialisation started')
|
||||
Phonon.VideoWidget.__init__(self)
|
||||
self.setWindowTitle(u'OpenLP Video Display')
|
||||
self.parent = parent
|
||||
self.screens = screens
|
||||
self.hidden = False
|
||||
self.message = None
|
||||
self.mediaActive = False
|
||||
self.mediaObject = Phonon.MediaObject()
|
||||
self.setAspectRatio(aspect)
|
||||
self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory)
|
||||
Phonon.createPath(self.mediaObject, self)
|
||||
Phonon.createPath(self.mediaObject, self.audioObject)
|
||||
flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Dialog
|
||||
## # WindowsStaysOnBottomHint is not available in QT4.4
|
||||
# try:
|
||||
# flags = flags | QtCore.Qt.WindowStaysOnBottomHint
|
||||
# except AttributeError:
|
||||
# pass
|
||||
self.setWindowFlags(flags)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'videodisplay_play'), self.onMediaPlay)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'videodisplay_pause'), self.onMediaPause)
|
||||
# QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
# QtCore.SIGNAL(u'videodisplay_background'), self.onMediaBackground)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'config_updated'), self.setup)
|
||||
QtCore.QObject.connect(self.mediaObject,
|
||||
QtCore.SIGNAL(u'finished()'), self.onMediaStop)
|
||||
self.setVisible(False)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if isinstance(event, QtGui.QKeyEvent):
|
||||
#here accept the event and do something
|
||||
if event.key() == QtCore.Qt.Key_Escape:
|
||||
self.onMediaStop()
|
||||
event.accept()
|
||||
event.ignore()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def setup(self):
|
||||
"""
|
||||
Sets up the screen on a particular screen.
|
||||
"""
|
||||
log.debug(u'VideoDisplay Setup %s for %s ' % (self.screens,
|
||||
self.screens.monitor_number))
|
||||
self.screen = self.screens.current
|
||||
#Sort out screen locations and sizes
|
||||
self.setGeometry(self.screen[u'size'])
|
||||
# To display or not to display?
|
||||
if not self.screen[u'primary']: # and self.isVisible():
|
||||
#self.showFullScreen()
|
||||
self.setVisible(False)
|
||||
self.primary = False
|
||||
else:
|
||||
self.setVisible(False)
|
||||
self.primary = True
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""
|
||||
Shutting down so clean up connections
|
||||
"""
|
||||
self.onMediaStop()
|
||||
for path in self.outputPaths():
|
||||
path.disconnect()
|
||||
|
||||
# def onMediaBackground(self, message=None):
|
||||
# """
|
||||
# Play a video triggered from the video plugin with the
|
||||
# file name passed in on the event.
|
||||
# Also triggered from the Finish event so the video will loop
|
||||
# if it is triggered from the plugin
|
||||
# """
|
||||
# log.debug(u'VideoDisplay Queue new media message %s' % message)
|
||||
# #If not file take the stored one
|
||||
# if not message:
|
||||
# message = self.message
|
||||
# # still no file name then stop as it was a normal video stopping
|
||||
# if message:
|
||||
# self.mediaObject.setCurrentSource(Phonon.MediaSource(message))
|
||||
# self.message = message
|
||||
# self._play()
|
||||
|
||||
def onMediaQueue(self, message):
|
||||
"""
|
||||
Set up a video to play from the serviceitem.
|
||||
"""
|
||||
log.debug(u'VideoDisplay Queue new media message %s' % message)
|
||||
file = os.path.join(message.get_frame_path(),
|
||||
message.get_frame_title())
|
||||
self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
|
||||
self.mediaActive = True
|
||||
self._play()
|
||||
|
||||
def onMediaPlay(self):
|
||||
"""
|
||||
Respond to the Play button on the slide controller unless the display
|
||||
has been hidden by the slidecontroller
|
||||
"""
|
||||
if not self.hidden:
|
||||
log.debug(u'VideoDisplay Play the new media, Live ')
|
||||
self._play()
|
||||
|
||||
def _play(self):
|
||||
"""
|
||||
We want to play the video so start it and display the screen
|
||||
"""
|
||||
log.debug(u'VideoDisplay _play called')
|
||||
self.mediaObject.play()
|
||||
self.setVisible(True)
|
||||
|
||||
def onMediaPause(self):
|
||||
"""
|
||||
Pause the video and refresh the screen
|
||||
"""
|
||||
log.debug(u'VideoDisplay Media paused by user')
|
||||
self.mediaObject.pause()
|
||||
self.show()
|
||||
|
||||
def onMediaStop(self):
|
||||
"""
|
||||
Stop the video and clean up
|
||||
"""
|
||||
log.debug(u'VideoDisplay Media stopped by user')
|
||||
self.message = None
|
||||
self.mediaActive = False
|
||||
self.mediaObject.stop()
|
||||
self.onMediaFinish()
|
||||
|
||||
def onMediaFinish(self):
|
||||
"""
|
||||
Clean up the Object queue
|
||||
"""
|
||||
log.debug(u'VideoDisplay Reached end of media playlist')
|
||||
self.mediaObject.clearQueue()
|
||||
self.setVisible(False)
|
||||
|
||||
def mediaHide(self, message=u''):
|
||||
"""
|
||||
Hide the video display
|
||||
"""
|
||||
self.mediaObject.pause()
|
||||
self.hidden = True
|
||||
self.setVisible(False)
|
||||
|
||||
def mediaShow(self):
|
||||
"""
|
||||
Show the video display if it was already hidden
|
||||
"""
|
||||
if self.hidden:
|
||||
self.hidden = False
|
||||
if self.mediaActive:
|
||||
self._play()
|
||||
|
||||
class AudioPlayer(QtCore.QObject):
|
||||
"""
|
||||
This Class will play audio only allowing components to work with a
|
||||
soundtrack which does not take over the user interface.
|
||||
soundtrack independent of the user interface.
|
||||
"""
|
||||
log.info(u'AudioPlayer Loaded')
|
||||
|
||||
@ -675,9 +420,9 @@ class AudioPlayer(QtCore.QObject):
|
||||
Set up a video to play from the serviceitem.
|
||||
"""
|
||||
log.debug(u'AudioPlayer Queue new media message %s' % message)
|
||||
file = os.path.join(message[0].get_frame_path(),
|
||||
mfile = os.path.join(message[0].get_frame_path(),
|
||||
message[0].get_frame_title())
|
||||
self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
|
||||
self.mediaObject.setCurrentSource(Phonon.MediaSource(mfile))
|
||||
self.onMediaPlay()
|
||||
|
||||
def onMediaPlay(self):
|
||||
|
@ -29,7 +29,7 @@ import logging
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \
|
||||
ThemeManager, SlideController, PluginForm, MediaDockManager, DisplayManager
|
||||
ThemeManager, SlideController, PluginForm, MediaDockManager
|
||||
from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \
|
||||
SettingsManager, PluginManager, Receiver, translate
|
||||
from openlp.core.utils import AppLocation, add_actions, LanguageManager
|
||||
@ -94,8 +94,10 @@ class Ui_MainWindow(object):
|
||||
self.ControlSplitter.setObjectName(u'ControlSplitter')
|
||||
self.MainContentLayout.addWidget(self.ControlSplitter)
|
||||
# Create slide controllers
|
||||
self.PreviewController = SlideController(self, self.settingsmanager)
|
||||
self.LiveController = SlideController(self, self.settingsmanager, True)
|
||||
self.PreviewController = SlideController(self, self.settingsmanager,
|
||||
self.screens)
|
||||
self.LiveController = SlideController(self, self.settingsmanager,
|
||||
self.screens, True)
|
||||
# Create menu
|
||||
self.MenuBar = QtGui.QMenuBar(MainWindow)
|
||||
self.MenuBar.setGeometry(QtCore.QRect(0, 0, 1087, 27))
|
||||
@ -509,7 +511,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.songsSettingsSection = u'songs'
|
||||
self.serviceNotSaved = False
|
||||
self.settingsmanager = SettingsManager(screens)
|
||||
self.displayManager = DisplayManager(screens)
|
||||
self.aboutForm = AboutForm(self, applicationVersion)
|
||||
self.settingsForm = SettingsForm(self.screens, self, self)
|
||||
self.recentFiles = QtCore.QStringList()
|
||||
@ -594,7 +595,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
#ThemeManager needs to call RenderManager
|
||||
self.RenderManager = RenderManager(
|
||||
self.ThemeManagerContents, self.screens)
|
||||
self.displayManager.renderManager = self.RenderManager
|
||||
#Define the media Dock Manager
|
||||
self.mediaDockManager = MediaDockManager(self.MediaToolBox)
|
||||
log.info(u'Load Plugins')
|
||||
@ -605,7 +605,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.plugin_helpers[u'service'] = self.ServiceManagerContents
|
||||
self.plugin_helpers[u'settings form'] = self.settingsForm
|
||||
self.plugin_helpers[u'toolbox'] = self.mediaDockManager
|
||||
self.plugin_helpers[u'displaymanager'] = self.displayManager
|
||||
self.plugin_helpers[u'pluginmanager'] = self.plugin_manager
|
||||
self.plugin_helpers[u'formparent'] = self
|
||||
self.plugin_manager.find_plugins(pluginpath, self.plugin_helpers)
|
||||
@ -652,8 +651,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
version_text = unicode(translate('OpenLP.MainWindow',
|
||||
'Version %s of OpenLP is now available for download (you are '
|
||||
'currently running version %s). \n\nYou can download the latest '
|
||||
'version from '
|
||||
'<a href="http://openlp.org/">http://openlp.org/</a>.'))
|
||||
'version from http://openlp.org/.'))
|
||||
QtGui.QMessageBox.question(self,
|
||||
translate('OpenLP.MainWindow', 'OpenLP Version Updated'),
|
||||
version_text % (version, self.applicationVersion[u'full']))
|
||||
@ -663,9 +661,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
Show the main form, as well as the display form
|
||||
"""
|
||||
QtGui.QWidget.show(self)
|
||||
self.displayManager.setup()
|
||||
if self.displayManager.mainDisplay.isVisible():
|
||||
self.displayManager.mainDisplay.setFocus()
|
||||
self.LiveController.display.setup()
|
||||
self.PreviewController.display.setup()
|
||||
if self.LiveController.display.isVisible():
|
||||
self.LiveController.display.setFocus()
|
||||
self.activateWindow()
|
||||
if QtCore.QSettings().value(
|
||||
self.generalSettingsSection + u'/auto open',
|
||||
@ -745,8 +744,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
The screen has changed to so tell the displays to update_display
|
||||
their locations
|
||||
"""
|
||||
log.debug(u'screenChanged')
|
||||
self.RenderManager.update_display()
|
||||
self.displayManager.setup()
|
||||
self.setFocus()
|
||||
self.activateWindow()
|
||||
|
||||
@ -792,8 +791,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.plugin_manager.finalise_plugins()
|
||||
# Save settings
|
||||
self.saveSettings()
|
||||
#Close down the displays
|
||||
self.displayManager.close()
|
||||
#Close down the display
|
||||
self.LiveController.display.close()
|
||||
|
||||
def serviceChanged(self, reset=False, serviceName=None):
|
||||
"""
|
||||
|
@ -44,9 +44,9 @@ class ScreenList(object):
|
||||
self.override = None
|
||||
self.screen_list = []
|
||||
self.display_count = 0
|
||||
#actual display number
|
||||
# actual display number
|
||||
self.current_display = 0
|
||||
#save config display number
|
||||
# save config display number
|
||||
self.monitor_number = 0
|
||||
|
||||
def add_screen(self, screen):
|
||||
|
@ -317,9 +317,8 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.serviceItemEditForm.setServiceItem(
|
||||
self.serviceItems[item][u'service_item'])
|
||||
if self.serviceItemEditForm.exec_():
|
||||
self.serviceItems[item][u'service_item'] = \
|
||||
self.serviceItemEditForm.getServiceItem()
|
||||
self.repaintServiceList(item, 0)
|
||||
self.addServiceItem(self.serviceItemEditForm.getServiceItem(),
|
||||
replace=True)
|
||||
|
||||
def nextItem(self):
|
||||
"""
|
||||
@ -780,7 +779,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
Rebuild the service list as things have changed and a
|
||||
repaint is the easiest way to do this.
|
||||
"""
|
||||
#force reset of renderer as theme data has changed
|
||||
# force reset of renderer as theme data has changed
|
||||
self.parent.RenderManager.themedata = None
|
||||
if self.serviceItems:
|
||||
tempServiceItems = self.serviceItems
|
||||
@ -790,8 +789,8 @@ class ServiceManager(QtGui.QWidget):
|
||||
for item in tempServiceItems:
|
||||
self.addServiceItem(
|
||||
item[u'service_item'], False, item[u'expanded'])
|
||||
#Set to False as items may have changed rendering
|
||||
#does not impact the saved song so True may also be valid
|
||||
# Set to False as items may have changed rendering
|
||||
# does not impact the saved song so True may also be valid
|
||||
self.parent.serviceChanged(False, self.serviceName)
|
||||
|
||||
def addServiceItem(self, item, rebuild=False, expand=True, replace=False):
|
||||
@ -873,6 +872,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
ItemCapabilities.AllowsPreview):
|
||||
self.parent.PreviewController.addServiceManagerItem(
|
||||
self.serviceItems[item][u'service_item'], 0)
|
||||
self.parent.LiveController.PreviewListWidget.setFocus()
|
||||
else:
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('OpenLP.ServiceManager', 'Missing Display Handler'),
|
||||
|
@ -25,36 +25,17 @@
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import time
|
||||
import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4.phonon import Phonon
|
||||
|
||||
from openlp.core.ui import HideMode
|
||||
from openlp.core.ui import HideMode, MainDisplay
|
||||
from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \
|
||||
ItemCapabilities, translate
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class SlideThread(QtCore.QThread):
|
||||
"""
|
||||
A special Qt thread class to speed up the display of text based frames.
|
||||
This is threaded so it loads the frames in background
|
||||
"""
|
||||
def __init__(self, parent, prefix, count):
|
||||
QtCore.QThread.__init__(self, parent)
|
||||
self.prefix = prefix
|
||||
self.count = count
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Run the thread.
|
||||
"""
|
||||
time.sleep(1)
|
||||
for i in range(0, self.count):
|
||||
Receiver.send_message(u'%s_slide_cache' % self.prefix, i)
|
||||
|
||||
class SlideList(QtGui.QTableWidget):
|
||||
"""
|
||||
Customised version of QTableWidget which can respond to keyboard
|
||||
@ -97,7 +78,7 @@ class SlideController(QtGui.QWidget):
|
||||
SlideController is the slide controller widget. This widget is what the
|
||||
user uses to control the displaying of verses/slides/etc on the screen.
|
||||
"""
|
||||
def __init__(self, parent, settingsmanager, isLive=False):
|
||||
def __init__(self, parent, settingsmanager, screens, isLive=False):
|
||||
"""
|
||||
Set up the Slide Controller.
|
||||
"""
|
||||
@ -105,8 +86,10 @@ class SlideController(QtGui.QWidget):
|
||||
self.settingsmanager = settingsmanager
|
||||
self.isLive = isLive
|
||||
self.parent = parent
|
||||
self.mainDisplay = self.parent.displayManager.mainDisplay
|
||||
self.displayManager = self.parent.displayManager
|
||||
self.screens = screens
|
||||
self.ratio = float(self.screens.current[u'size'].width()) / \
|
||||
float(self.screens.current[u'size'].height())
|
||||
self.display = MainDisplay(self, screens, isLive)
|
||||
self.loopList = [
|
||||
u'Start Loop',
|
||||
u'Loop Separator',
|
||||
@ -115,13 +98,14 @@ class SlideController(QtGui.QWidget):
|
||||
self.songEditList = [
|
||||
u'Edit Song',
|
||||
]
|
||||
self.volume = 10
|
||||
self.timer_id = 0
|
||||
self.songEdit = False
|
||||
self.selectedRow = 0
|
||||
self.serviceItem = None
|
||||
self.alertTab = None
|
||||
self.Panel = QtGui.QWidget(parent.ControlSplitter)
|
||||
self.slideList = {}
|
||||
self.canDisplay = True
|
||||
# Layout for holding panel
|
||||
self.PanelLayout = QtGui.QVBoxLayout(self.Panel)
|
||||
self.PanelLayout.setSpacing(0)
|
||||
@ -133,7 +117,8 @@ class SlideController(QtGui.QWidget):
|
||||
self.split = 1
|
||||
self.typePrefix = u'live'
|
||||
else:
|
||||
self.TypeLabel.setText(translate('OpenLP.SlideController', 'Preview'))
|
||||
self.TypeLabel.setText(translate('OpenLP.SlideController',
|
||||
'Preview'))
|
||||
self.split = 0
|
||||
self.typePrefix = u'preview'
|
||||
self.TypeLabel.setStyleSheet(u'font-weight: bold; font-size: 12pt;')
|
||||
@ -177,11 +162,11 @@ class SlideController(QtGui.QWidget):
|
||||
sizeToolbarPolicy.setHeightForWidth(
|
||||
self.Toolbar.sizePolicy().hasHeightForWidth())
|
||||
self.Toolbar.setSizePolicy(sizeToolbarPolicy)
|
||||
if self.isLive:
|
||||
self.Toolbar.addToolbarButton(
|
||||
u'First Slide', u':/slides/slide_first.png',
|
||||
translate('OpenLP.SlideController', 'Move to first'),
|
||||
self.onSlideSelectedFirst)
|
||||
# if self.isLive:
|
||||
# self.Toolbar.addToolbarButton(
|
||||
# u'First Slide', u':/slides/slide_first.png',
|
||||
# translate('OpenLP.SlideController', 'Move to first'),
|
||||
# self.onSlideSelectedFirst)
|
||||
self.Toolbar.addToolbarButton(
|
||||
u'Previous Slide', u':/slides/slide_previous.png',
|
||||
translate('OpenLP.SlideController', 'Move to previous'),
|
||||
@ -190,11 +175,11 @@ class SlideController(QtGui.QWidget):
|
||||
u'Next Slide', u':/slides/slide_next.png',
|
||||
translate('OpenLP.SlideController', 'Move to next'),
|
||||
self.onSlideSelectedNext)
|
||||
if self.isLive:
|
||||
self.Toolbar.addToolbarButton(
|
||||
u'Last Slide', u':/slides/slide_last.png',
|
||||
translate('OpenLP.SlideController', 'Move to last'),
|
||||
self.onSlideSelectedLast)
|
||||
# if self.isLive:
|
||||
# self.Toolbar.addToolbarButton(
|
||||
# u'Last Slide', u':/slides/slide_last.png',
|
||||
# translate('OpenLP.SlideController', 'Move to last'),
|
||||
# self.onSlideSelectedLast)
|
||||
if self.isLive:
|
||||
self.Toolbar.addToolbarSeparator(u'Close Separator')
|
||||
self.HideMenu = QtGui.QToolButton(self.Toolbar)
|
||||
@ -213,20 +198,24 @@ class SlideController(QtGui.QWidget):
|
||||
self.ThemeScreen.setCheckable(True)
|
||||
QtCore.QObject.connect(self.ThemeScreen,
|
||||
QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay)
|
||||
if self.screens.display_count > 1:
|
||||
self.DesktopScreen = QtGui.QAction(QtGui.QIcon(
|
||||
u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu)
|
||||
u':/slides/slide_desktop.png'), u'Show Desktop',
|
||||
self.HideMenu)
|
||||
self.DesktopScreen.setCheckable(True)
|
||||
QtCore.QObject.connect(self.DesktopScreen,
|
||||
QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay)
|
||||
self.HideMenu.setDefaultAction(self.BlankScreen)
|
||||
self.HideMenu.menu().addAction(self.BlankScreen)
|
||||
self.HideMenu.menu().addAction(self.ThemeScreen)
|
||||
if self.screens.display_count > 1:
|
||||
self.HideMenu.menu().addAction(self.DesktopScreen)
|
||||
if not self.isLive:
|
||||
self.Toolbar.addToolbarSeparator(u'Close Separator')
|
||||
self.Toolbar.addToolbarButton(
|
||||
u'Go Live', u':/general/general_live.png',
|
||||
translate('OpenLP.SlideController', 'Move to live'), self.onGoLive)
|
||||
translate('OpenLP.SlideController', 'Move to live'),
|
||||
self.onGoLive)
|
||||
self.Toolbar.addToolbarSeparator(u'Close Separator')
|
||||
self.Toolbar.addToolbarButton(
|
||||
u'Edit Song', u':/general/general_edit.png',
|
||||
@ -247,11 +236,12 @@ class SlideController(QtGui.QWidget):
|
||||
self.DelaySpinBox.setMaximum(180)
|
||||
self.Toolbar.addToolbarWidget(
|
||||
u'Image SpinBox', self.DelaySpinBox)
|
||||
self.DelaySpinBox.setSuffix(translate('OpenLP.SlideController', 's'))
|
||||
self.DelaySpinBox.setSuffix(translate('OpenLP.SlideController',
|
||||
's'))
|
||||
self.DelaySpinBox.setToolTip(translate('OpenLP.SlideController',
|
||||
'Delay between slides in seconds'))
|
||||
self.ControllerLayout.addWidget(self.Toolbar)
|
||||
#Build a Media ToolBar
|
||||
# Build a Media ToolBar
|
||||
self.Mediabar = OpenLPToolbar(self)
|
||||
self.Mediabar.addToolbarButton(
|
||||
u'Media Start', u':/slides/media_playback_start.png',
|
||||
@ -275,15 +265,26 @@ class SlideController(QtGui.QWidget):
|
||||
self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
|
||||
self.volumeSlider.setObjectName(u'volumeSlider')
|
||||
self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
|
||||
else:
|
||||
self.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
|
||||
self.volumeSlider.setTickInterval(1)
|
||||
self.volumeSlider.setTickPosition(QtGui.QSlider.TicksAbove)
|
||||
self.volumeSlider.setMinimum(0)
|
||||
self.volumeSlider.setMaximum(10)
|
||||
self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
|
||||
self.volumeSlider.setObjectName(u'volumeSlider')
|
||||
self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
|
||||
self.ControllerLayout.addWidget(self.Mediabar)
|
||||
# Build the Song Toolbar
|
||||
if isLive:
|
||||
self.SongMenu = QtGui.QToolButton(self.Toolbar)
|
||||
self.SongMenu.setText(translate('OpenLP.SlideController', 'Go to Verse'))
|
||||
self.SongMenu.setText(translate('OpenLP.SlideController',
|
||||
'Go to Verse'))
|
||||
self.SongMenu.setPopupMode(QtGui.QToolButton.InstantPopup)
|
||||
self.Toolbar.addToolbarWidget(u'Song Menu', self.SongMenu)
|
||||
self.SongMenu.setMenu(QtGui.QMenu(
|
||||
translate('OpenLP.SlideController', 'Go to Verse'), self.Toolbar))
|
||||
translate('OpenLP.SlideController', 'Go to Verse'),
|
||||
self.Toolbar))
|
||||
self.Toolbar.makeWidgetsInvisible([u'Song Menu'])
|
||||
# Screen preview area
|
||||
self.PreviewFrame = QtGui.QFrame(self.Splitter)
|
||||
@ -322,7 +323,7 @@ class SlideController(QtGui.QWidget):
|
||||
self.SlidePreview.setSizePolicy(sizePolicy)
|
||||
self.SlidePreview.setFixedSize(
|
||||
QtCore.QSize(self.settingsmanager.slidecontroller_image,
|
||||
self.settingsmanager.slidecontroller_image / 1.3 ))
|
||||
self.settingsmanager.slidecontroller_image / self.ratio))
|
||||
self.SlidePreview.setFrameShape(QtGui.QFrame.Box)
|
||||
self.SlidePreview.setFrameShadow(QtGui.QFrame.Plain)
|
||||
self.SlidePreview.setLineWidth(1)
|
||||
@ -390,17 +391,37 @@ class SlideController(QtGui.QWidget):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'config_updated'), self.refreshServiceItem)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'%s_slide_cache' % self.typePrefix), self.slideCache)
|
||||
QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged)
|
||||
if self.isLive:
|
||||
QtCore.QObject.connect(self.volumeSlider,
|
||||
QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
|
||||
|
||||
def screenSizeChanged(self):
|
||||
"""
|
||||
Settings dialog has changed the screen size of adjust output and
|
||||
screen previews
|
||||
"""
|
||||
log.debug(u'screenSizeChanged live = %s' % self.isLive)
|
||||
# rebuild display as screen size changed
|
||||
self.display = MainDisplay(self, self.screens, self.isLive)
|
||||
self.display.alertTab = self.alertTab
|
||||
self.ratio = float(self.screens.current[u'size'].width()) / \
|
||||
float(self.screens.current[u'size'].height())
|
||||
self.display.setup()
|
||||
self.SlidePreview.setFixedSize(
|
||||
QtCore.QSize(self.settingsmanager.slidecontroller_image,
|
||||
self.settingsmanager.slidecontroller_image / self.ratio))
|
||||
|
||||
def widthChanged(self):
|
||||
"""
|
||||
Handle changes of width from the splitter between the live and preview
|
||||
controller. Event only issues when changes have finished
|
||||
"""
|
||||
log.debug(u'widthChanged live = %s' % self.isLive)
|
||||
width = self.parent.ControlSplitter.sizes()[self.split]
|
||||
height = width * self.parent.RenderManager.screen_ratio
|
||||
self.PreviewListWidget.setColumnWidth(0, width)
|
||||
#Sort out image heights (Songs, bibles excluded)
|
||||
# Sort out image heights (Songs, bibles excluded)
|
||||
if self.serviceItem and not self.serviceItem.is_text():
|
||||
for framenumber in range(len(self.serviceItem.get_frames())):
|
||||
self.PreviewListWidget.setRowHeight(framenumber, height)
|
||||
@ -453,8 +474,6 @@ class SlideController(QtGui.QWidget):
|
||||
if item.is_media():
|
||||
self.Toolbar.setVisible(False)
|
||||
self.Mediabar.setVisible(True)
|
||||
#self.volumeSlider.setAudioOutput(
|
||||
# self.mainDisplay.videoDisplay.audio)
|
||||
|
||||
def enablePreviewToolBar(self, item):
|
||||
"""
|
||||
@ -474,22 +493,20 @@ class SlideController(QtGui.QWidget):
|
||||
"""
|
||||
Method to update the service item if the screen has changed
|
||||
"""
|
||||
log.debug(u'refreshServiceItem')
|
||||
log.debug(u'refreshServiceItem live = %s' % self.isLive)
|
||||
if self.serviceItem:
|
||||
if self.serviceItem.is_text() or self.serviceItem.is_image():
|
||||
item = self.serviceItem
|
||||
item.render()
|
||||
self.addServiceManagerItem(item, self.selectedRow)
|
||||
self._processItem(item, self.selectedRow)
|
||||
|
||||
def addServiceItem(self, item):
|
||||
"""
|
||||
Method to install the service item into the controller
|
||||
Called by plugins
|
||||
"""
|
||||
log.debug(u'addServiceItem')
|
||||
before = time.time()
|
||||
log.debug(u'addServiceItem live = %s' % self.isLive)
|
||||
item.render()
|
||||
log.log(15, u'Rendering took %4s' % (time.time() - before))
|
||||
slideno = 0
|
||||
if self.songEdit:
|
||||
slideno = self.selectedRow
|
||||
@ -509,8 +526,8 @@ class SlideController(QtGui.QWidget):
|
||||
request the correct toolbar for the plugin.
|
||||
Called by ServiceManager
|
||||
"""
|
||||
log.debug(u'addServiceManagerItem')
|
||||
#If service item is the same as the current on only change slide
|
||||
log.debug(u'addServiceManagerItem live = %s' % self.isLive)
|
||||
# If service item is the same as the current on only change slide
|
||||
if item.__eq__(self.serviceItem):
|
||||
self.PreviewListWidget.selectRow(slideno)
|
||||
self.onSlideSelected()
|
||||
@ -522,17 +539,15 @@ class SlideController(QtGui.QWidget):
|
||||
Loads a ServiceItem into the system from ServiceManager
|
||||
Display the slide number passed
|
||||
"""
|
||||
log.debug(u'processManagerItem')
|
||||
log.debug(u'processManagerItem live = %s' % self.isLive)
|
||||
self.onStopLoop()
|
||||
#If old item was a command tell it to stop
|
||||
# If old item was a command tell it to stop
|
||||
if self.serviceItem:
|
||||
if self.serviceItem.is_command():
|
||||
Receiver.send_message(u'%s_stop' %
|
||||
self.serviceItem.name.lower(), [serviceItem, self.isLive])
|
||||
if self.serviceItem.is_media():
|
||||
self.onMediaStop()
|
||||
if serviceItem.is_media():
|
||||
self.onMediaStart(serviceItem)
|
||||
if self.isLive:
|
||||
blanked = self.BlankScreen.isChecked()
|
||||
else:
|
||||
@ -541,12 +556,8 @@ class SlideController(QtGui.QWidget):
|
||||
[serviceItem, self.isLive, blanked, slideno])
|
||||
self.slideList = {}
|
||||
width = self.parent.ControlSplitter.sizes()[self.split]
|
||||
#Set pointing cursor when we have somthing to point at
|
||||
# Set pointing cursor when we have somthing to point at
|
||||
self.PreviewListWidget.setCursor(QtCore.Qt.PointingHandCursor)
|
||||
before = time.time()
|
||||
#Clear the old serviceItem cache to release memory
|
||||
if self.serviceItem and self.serviceItem is not serviceItem:
|
||||
self.serviceItem.clear_cache()
|
||||
self.serviceItem = serviceItem
|
||||
self.PreviewListWidget.clear()
|
||||
self.PreviewListWidget.setRowCount(0)
|
||||
@ -560,20 +571,19 @@ class SlideController(QtGui.QWidget):
|
||||
self.PreviewListWidget.rowCount() + 1)
|
||||
item = QtGui.QTableWidgetItem()
|
||||
slideHeight = 0
|
||||
#It is a based Text Render
|
||||
if self.serviceItem.is_text():
|
||||
if frame[u'verseTag']:
|
||||
bits = frame[u'verseTag'].split(u':')
|
||||
tag = u'%s\n%s' % (bits[0][0], bits[1][0:] )
|
||||
tag1 = u'%s%s' % (bits[0][0], bits[1][0:] )
|
||||
row = tag
|
||||
else:
|
||||
row += 1
|
||||
if self.isLive and frame[u'verseTag'] is not None:
|
||||
if self.isLive:
|
||||
if tag1 not in self.slideList:
|
||||
self.slideList[tag1] = framenumber
|
||||
self.SongMenu.menu().addAction(tag1,
|
||||
self.onSongBarHandler)
|
||||
else:
|
||||
row += 1
|
||||
item.setText(frame[u'text'])
|
||||
else:
|
||||
label = QtGui.QLabel()
|
||||
@ -600,15 +610,14 @@ class SlideController(QtGui.QWidget):
|
||||
else:
|
||||
self.PreviewListWidget.selectRow(slideno)
|
||||
self.enableToolBar(serviceItem)
|
||||
# Pass to display for viewing
|
||||
self.display.buildHtml(self.serviceItem)
|
||||
if serviceItem.is_media():
|
||||
self.onMediaStart(serviceItem)
|
||||
self.onSlideSelected()
|
||||
self.PreviewListWidget.setFocus()
|
||||
Receiver.send_message(u'slidecontroller_%s_started' % self.typePrefix,
|
||||
[serviceItem])
|
||||
if self.serviceItem.is_text():
|
||||
st = SlideThread(
|
||||
self, self.typePrefix, len(self.serviceItem.get_frames()))
|
||||
st.start()
|
||||
log.log(15, u'Display Rendering took %4s' % (time.time() - before))
|
||||
|
||||
def onTextRequest(self):
|
||||
"""
|
||||
@ -620,7 +629,7 @@ class SlideController(QtGui.QWidget):
|
||||
dataItem = {}
|
||||
if self.serviceItem.is_text():
|
||||
dataItem[u'tag'] = unicode(frame[u'verseTag'])
|
||||
dataItem[u'text'] = unicode(frame[u'text'])
|
||||
dataItem[u'text'] = unicode(frame[u'html'])
|
||||
else:
|
||||
dataItem[u'tag'] = unicode(framenumber)
|
||||
dataItem[u'text'] = u''
|
||||
@ -630,7 +639,7 @@ class SlideController(QtGui.QWidget):
|
||||
Receiver.send_message(u'slidecontroller_%s_text_response'
|
||||
% self.typePrefix, data)
|
||||
|
||||
#Screen event methods
|
||||
# Screen event methods
|
||||
def onSlideSelectedFirst(self):
|
||||
"""
|
||||
Go to the first slide.
|
||||
@ -664,9 +673,9 @@ class SlideController(QtGui.QWidget):
|
||||
"""
|
||||
Allow the main display to blank the main display at startup time
|
||||
"""
|
||||
log.debug(u'mainDisplaySetBackground')
|
||||
if not self.mainDisplay.primary:
|
||||
self.onBlankDisplay(True)
|
||||
log.debug(u'mainDisplaySetBackground live = %s' % self.isLive)
|
||||
if not self.display.primary:
|
||||
self.onHideDisplay(True)
|
||||
|
||||
def onSlideBlank(self):
|
||||
"""
|
||||
@ -688,6 +697,7 @@ class SlideController(QtGui.QWidget):
|
||||
self.HideMenu.setDefaultAction(self.BlankScreen)
|
||||
self.BlankScreen.setChecked(checked)
|
||||
self.ThemeScreen.setChecked(False)
|
||||
if self.screens.display_count > 1:
|
||||
self.DesktopScreen.setChecked(False)
|
||||
QtCore.QSettings().setValue(
|
||||
self.parent.generalSettingsSection + u'/screen blank',
|
||||
@ -706,6 +716,7 @@ class SlideController(QtGui.QWidget):
|
||||
self.HideMenu.setDefaultAction(self.ThemeScreen)
|
||||
self.BlankScreen.setChecked(False)
|
||||
self.ThemeScreen.setChecked(checked)
|
||||
if self.screens.display_count > 1:
|
||||
self.DesktopScreen.setChecked(False)
|
||||
if checked:
|
||||
Receiver.send_message(u'maindisplay_hide', HideMode.Theme)
|
||||
@ -721,6 +732,7 @@ class SlideController(QtGui.QWidget):
|
||||
self.HideMenu.setDefaultAction(self.DesktopScreen)
|
||||
self.BlankScreen.setChecked(False)
|
||||
self.ThemeScreen.setChecked(False)
|
||||
if self.screens.display_count > 1:
|
||||
self.DesktopScreen.setChecked(checked)
|
||||
if checked:
|
||||
Receiver.send_message(u'maindisplay_hide', HideMode.Screen)
|
||||
@ -758,13 +770,6 @@ class SlideController(QtGui.QWidget):
|
||||
% self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive])
|
||||
|
||||
def slideCache(self, slide):
|
||||
"""
|
||||
Generate a slide cache item rendered and ready for use
|
||||
in the background.
|
||||
"""
|
||||
self.serviceItem.get_rendered_frame(int(slide))
|
||||
|
||||
def onSlideSelected(self):
|
||||
"""
|
||||
Generate the preview when you click on a slide.
|
||||
@ -778,24 +783,15 @@ class SlideController(QtGui.QWidget):
|
||||
if self.serviceItem.is_command() and self.isLive:
|
||||
self.updatePreview()
|
||||
else:
|
||||
before = time.time()
|
||||
frame = self.serviceItem.get_rendered_frame(row)
|
||||
frame, raw_html = self.serviceItem.get_rendered_frame(row)
|
||||
if self.serviceItem.is_text():
|
||||
frame = self.display.text(raw_html)
|
||||
else:
|
||||
self.display.image(frame)
|
||||
if isinstance(frame, QtGui.QImage):
|
||||
self.SlidePreview.setPixmap(QtGui.QPixmap.fromImage(frame))
|
||||
else:
|
||||
if isinstance(frame[u'main'], basestring):
|
||||
self.SlidePreview.setPixmap(
|
||||
QtGui.QPixmap(frame[u'main']))
|
||||
else:
|
||||
self.SlidePreview.setPixmap(
|
||||
QtGui.QPixmap.fromImage(frame[u'main']))
|
||||
log.log(
|
||||
15, u'Slide Rendering took %4s' % (time.time() - before))
|
||||
if self.isLive:
|
||||
if self.serviceItem.is_text():
|
||||
self.mainDisplay.frameView(frame, True)
|
||||
else:
|
||||
self.displayManager.displayImage(frame[u'main'])
|
||||
self.SlidePreview.setPixmap(QtGui.QPixmap(frame))
|
||||
self.selectedRow = row
|
||||
Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
|
||||
row)
|
||||
@ -810,8 +806,7 @@ class SlideController(QtGui.QWidget):
|
||||
row)
|
||||
|
||||
def updatePreview(self):
|
||||
rm = self.parent.RenderManager
|
||||
if not rm.screens.current[u'primary']:
|
||||
if not self.screens.current[u'primary']:
|
||||
# Grab now, but try again in a couple of seconds if slide change
|
||||
# is slow
|
||||
QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
|
||||
@ -819,12 +814,12 @@ class SlideController(QtGui.QWidget):
|
||||
else:
|
||||
label = self.PreviewListWidget.cellWidget(
|
||||
self.PreviewListWidget.currentRow(), 1)
|
||||
if label:
|
||||
self.SlidePreview.setPixmap(label.pixmap())
|
||||
|
||||
def grabMainDisplay(self):
|
||||
rm = self.parent.RenderManager
|
||||
winid = QtGui.QApplication.desktop().winId()
|
||||
rect = rm.screens.current[u'size']
|
||||
rect = self.screens.current[u'size']
|
||||
winimg = QtGui.QPixmap.grabWindow(winid, rect.x(),
|
||||
rect.y(), rect.width(), rect.height())
|
||||
self.SlidePreview.setPixmap(winimg)
|
||||
@ -941,7 +936,9 @@ class SlideController(QtGui.QWidget):
|
||||
"""
|
||||
log.debug(u'SlideController onMediaStart')
|
||||
if self.isLive:
|
||||
Receiver.send_message(u'videodisplay_start', item)
|
||||
file = os.path.join(item.get_frame_path(), item.get_frame_title())
|
||||
self.display.video(file, self.volume)
|
||||
self.volumeSlider.setValue(self.volume)
|
||||
else:
|
||||
self.mediaObject.stop()
|
||||
self.mediaObject.clearQueue()
|
||||
@ -951,13 +948,21 @@ class SlideController(QtGui.QWidget):
|
||||
self.seekSlider.show()
|
||||
self.onMediaPlay()
|
||||
|
||||
def mediaVolume(self):
|
||||
"""
|
||||
Respond to the release of Volume Slider
|
||||
"""
|
||||
log.debug(u'SlideController mediaVolume')
|
||||
self.volume = self.volumeSlider.value()
|
||||
self.display.videoVolume(self.volume)
|
||||
|
||||
def onMediaPause(self):
|
||||
"""
|
||||
Respond to the Pause from the media Toolbar
|
||||
"""
|
||||
log.debug(u'SlideController onMediaPause')
|
||||
if self.isLive:
|
||||
Receiver.send_message(u'videodisplay_pause')
|
||||
self.display.videoPause()
|
||||
else:
|
||||
self.mediaObject.pause()
|
||||
|
||||
@ -967,7 +972,7 @@ class SlideController(QtGui.QWidget):
|
||||
"""
|
||||
log.debug(u'SlideController onMediaPlay')
|
||||
if self.isLive:
|
||||
Receiver.send_message(u'videodisplay_play')
|
||||
self.display.videoPlay()
|
||||
else:
|
||||
self.SlidePreview.hide()
|
||||
self.video.show()
|
||||
@ -979,7 +984,7 @@ class SlideController(QtGui.QWidget):
|
||||
"""
|
||||
log.debug(u'SlideController onMediaStop')
|
||||
if self.isLive:
|
||||
Receiver.send_message(u'videodisplay_stop')
|
||||
self.display.videoStop()
|
||||
else:
|
||||
self.mediaObject.stop()
|
||||
self.video.hide()
|
||||
|
@ -139,13 +139,13 @@ class ThemeManager(QtGui.QWidget):
|
||||
"""
|
||||
log.debug(u'changeGlobalFromTab %s', themeName)
|
||||
for count in range (0, self.themeListWidget.count()):
|
||||
#reset the old name
|
||||
# reset the old name
|
||||
item = self.themeListWidget.item(count)
|
||||
oldName = item.text()
|
||||
newName = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
if oldName != newName:
|
||||
self.themeListWidget.item(count).setText(newName)
|
||||
#Set the new name
|
||||
# Set the new name
|
||||
if themeName == newName:
|
||||
name = unicode(translate('OpenLP.ThemeManager',
|
||||
'%s (default)')) % newName
|
||||
@ -161,11 +161,11 @@ class ThemeManager(QtGui.QWidget):
|
||||
for count in range (0, self.themeListWidget.count()):
|
||||
item = self.themeListWidget.item(count)
|
||||
oldName = item.text()
|
||||
#reset the old name
|
||||
# reset the old name
|
||||
if oldName != unicode(item.data(QtCore.Qt.UserRole).toString()):
|
||||
self.themeListWidget.item(count).setText(
|
||||
unicode(item.data(QtCore.Qt.UserRole).toString()))
|
||||
#Set the new name
|
||||
# Set the new name
|
||||
if count == selected_row:
|
||||
self.global_theme = unicode(
|
||||
self.themeListWidget.item(count).text())
|
||||
@ -295,7 +295,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
path = unicode(path)
|
||||
if path:
|
||||
SettingsManager.set_last_dir(self.settingsSection, path, 1)
|
||||
themePath = os.path.join(path, theme + u'.theme')
|
||||
themePath = os.path.join(path, theme + u'.otz')
|
||||
zip = None
|
||||
try:
|
||||
zip = zipfile.ZipFile(themePath, u'w')
|
||||
@ -346,11 +346,10 @@ class ThemeManager(QtGui.QWidget):
|
||||
log.debug(u'Load themes from dir')
|
||||
self.themelist = []
|
||||
self.themeListWidget.clear()
|
||||
#root, dirs, files = os.walk(self.path)
|
||||
dirList = os.listdir(self.path)
|
||||
for name in dirList:
|
||||
if name.endswith(u'.png'):
|
||||
#check to see file is in theme root directory
|
||||
# check to see file is in theme root directory
|
||||
theme = os.path.join(self.path, name)
|
||||
if os.path.exists(theme):
|
||||
textName = os.path.splitext(name)[0]
|
||||
@ -660,9 +659,8 @@ class ThemeManager(QtGui.QWidget):
|
||||
"""
|
||||
Call the RenderManager to build a Sample Image
|
||||
"""
|
||||
log.debug(u'generateImage %s ', themedata)
|
||||
frame = self.parent.RenderManager.generate_preview(themedata)
|
||||
return frame
|
||||
log.debug(u'generateImage \n%s ', themedata)
|
||||
return self.parent.RenderManager.generate_preview(themedata)
|
||||
|
||||
def getPreviewImage(self, theme):
|
||||
"""
|
||||
@ -732,8 +730,6 @@ class ThemeManager(QtGui.QWidget):
|
||||
theme.display_slideTransition = theme.display_slideTransition
|
||||
theme.font_footer_color = theme.font_footer_color.strip()
|
||||
theme.font_footer_height = int(theme.font_footer_height.strip())
|
||||
theme.font_footer_indentation = \
|
||||
int(theme.font_footer_indentation.strip())
|
||||
theme.font_footer_italics = str_to_bool(theme.font_footer_italics)
|
||||
theme.font_footer_name = theme.font_footer_name.strip()
|
||||
#theme.font_footer_override
|
||||
@ -746,7 +742,6 @@ class ThemeManager(QtGui.QWidget):
|
||||
theme.font_main_color = theme.font_main_color.strip()
|
||||
theme.font_main_height = int(theme.font_main_height.strip())
|
||||
theme.font_main_italics = str_to_bool(theme.font_main_italics)
|
||||
theme.font_main_indentation = int(theme.font_main_indentation)
|
||||
theme.font_main_name = theme.font_main_name.strip()
|
||||
#theme.font_main_override
|
||||
theme.font_main_proportion = int(theme.font_main_proportion.strip())
|
||||
@ -757,3 +752,8 @@ class ThemeManager(QtGui.QWidget):
|
||||
#theme.theme_mode
|
||||
theme.theme_name = theme.theme_name.strip()
|
||||
#theme.theme_version
|
||||
# Remove the Transparent settings as they are not relevent
|
||||
if theme.background_mode == u'transparent':
|
||||
theme.background_mode = u'opaque'
|
||||
theme.background_type = u'solid'
|
||||
theme.background_startColor = u'#000000'
|
||||
|
@ -70,6 +70,8 @@ class VersionThread(QtCore.QThread):
|
||||
remote_version[u'release'] = int(match.group(3))
|
||||
if len(match.groups()) > 3 and match.group(4):
|
||||
remote_version[u'revision'] = int(match.group(4))
|
||||
else:
|
||||
return
|
||||
match = self.version_splitter.match(self.app_version[u'full'])
|
||||
if match:
|
||||
local_version[u'major'] = int(match.group(1))
|
||||
@ -77,6 +79,8 @@ class VersionThread(QtCore.QThread):
|
||||
local_version[u'release'] = int(match.group(3))
|
||||
if len(match.groups()) > 3 and match.group(4):
|
||||
local_version[u'revision'] = int(match.group(4))
|
||||
else:
|
||||
return
|
||||
if remote_version[u'major'] > local_version[u'major'] or \
|
||||
remote_version[u'minor'] > local_version[u'minor'] or \
|
||||
remote_version[u'release'] > local_version[u'release']:
|
||||
@ -147,10 +151,10 @@ class AppLocation(object):
|
||||
return plugin_path
|
||||
elif dir_type == AppLocation.VersionDir:
|
||||
if hasattr(sys, u'frozen') and sys.frozen == 1:
|
||||
plugin_path = os.path.abspath(os.path.split(sys.argv[0])[0])
|
||||
version_path = os.path.abspath(os.path.split(sys.argv[0])[0])
|
||||
else:
|
||||
plugin_path = os.path.split(openlp.__file__)[0]
|
||||
return plugin_path
|
||||
version_path = os.path.split(openlp.__file__)[0]
|
||||
return version_path
|
||||
elif dir_type == AppLocation.CacheDir:
|
||||
if sys.platform == u'win32':
|
||||
path = os.path.join(os.getenv(u'APPDATA'), u'openlp')
|
||||
@ -206,11 +210,14 @@ def check_latest_version(current_version):
|
||||
else:
|
||||
req = urllib2.Request(u'http://www.openlp.org/files/version.txt')
|
||||
req.add_header(u'User-Agent', u'OpenLP/%s' % current_version[u'full'])
|
||||
remote_version = None
|
||||
try:
|
||||
version_string = unicode(urllib2.urlopen(req, None).read()).strip()
|
||||
remote_version = unicode(urllib2.urlopen(req, None).read()).strip()
|
||||
except IOError, e:
|
||||
if hasattr(e, u'reason'):
|
||||
log.exception(u'Reason for failure: %s', e.reason)
|
||||
if remote_version:
|
||||
version_string = remote_version
|
||||
return version_string
|
||||
|
||||
def add_actions(target, actions):
|
||||
|
@ -80,9 +80,10 @@ class AlertsPlugin(Plugin):
|
||||
log.info(u'Alerts Initialising')
|
||||
Plugin.initialise(self)
|
||||
self.toolsAlertItem.setVisible(True)
|
||||
self.liveController.alertTab = self.alertsTab
|
||||
|
||||
def finalise(self):
|
||||
log.info(u'Plugin Finalise')
|
||||
log.info(u'Alerts Finalising')
|
||||
Plugin.finalise(self)
|
||||
self.toolsAlertItem.setVisible(False)
|
||||
|
||||
|
@ -32,18 +32,9 @@ from openlp.core.lib import Receiver, translate
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
HTMLCODE = u"""
|
||||
<p style=\"color:%s;
|
||||
background-color:%s;
|
||||
font-family:%s;
|
||||
font-size: %spt; \">
|
||||
%s
|
||||
</p>
|
||||
"""
|
||||
|
||||
class AlertsManager(QtCore.QObject):
|
||||
"""
|
||||
AlertsTab is the Alerts settings tab in the settings dialog.
|
||||
AlertsManager manages the settings of Alerts.
|
||||
"""
|
||||
log.info(u'Alert Manager loaded')
|
||||
|
||||
@ -94,10 +85,7 @@ class AlertsManager(QtCore.QObject):
|
||||
return
|
||||
text = self.alertList.pop(0)
|
||||
alertTab = self.parent.alertsTab
|
||||
text = HTMLCODE % (alertTab.font_color, alertTab.bg_color,
|
||||
alertTab.font_face, alertTab.font_size, text)
|
||||
self.parent.previewController.parent.displayManager.addAlert(text,
|
||||
alertTab.location)
|
||||
self.parent.liveController.display.alert(text)
|
||||
# check to see if we have a timer running
|
||||
if self.timer_id == 0:
|
||||
self.timer_id = self.startTimer(int(alertTab.timeout) * 1000)
|
||||
@ -111,10 +99,8 @@ class AlertsManager(QtCore.QObject):
|
||||
|
||||
"""
|
||||
log.debug(u'timer event')
|
||||
alertTab = self.parent.alertsTab
|
||||
if event.timerId() == self.timer_id:
|
||||
self.parent.previewController.parent.displayManager.addAlert(u'',
|
||||
alertTab.location)
|
||||
self.parent.liveController.display.alert(u'')
|
||||
self.killTimer(self.timer_id)
|
||||
self.timer_id = 0
|
||||
self.generateAlert()
|
||||
|
@ -261,7 +261,7 @@ class AlertsTab(SettingsTab):
|
||||
self.font_face = unicode(settings.value(
|
||||
u'font face', QtCore.QVariant(QtGui.QFont().family())).toString())
|
||||
self.location = settings.value(
|
||||
u'location', QtCore.QVariant(0)).toInt()[0]
|
||||
u'location', QtCore.QVariant(1)).toInt()[0]
|
||||
settings.endGroup()
|
||||
self.FontSizeSpinBox.setValue(self.font_size)
|
||||
self.TimeoutSpinBox.setValue(self.timeout)
|
||||
@ -296,3 +296,4 @@ class AlertsTab(SettingsTab):
|
||||
self.FontPreview.setFont(font)
|
||||
self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' %
|
||||
(self.bg_color, self.font_color))
|
||||
|
||||
|
@ -465,9 +465,9 @@ class BibleMediaItem(MediaManagerItem):
|
||||
self.displayResults(bible, dual_bible)
|
||||
|
||||
def generateSlideData(self, service_item, item=None):
|
||||
'''
|
||||
"""
|
||||
Generates and formats the slides for the service item.
|
||||
'''
|
||||
"""
|
||||
log.debug(u'generating slide data')
|
||||
items = self.listView.selectedIndexes()
|
||||
if len(items) == 0:
|
||||
@ -504,16 +504,16 @@ class BibleMediaItem(MediaManagerItem):
|
||||
dual_text = self._decodeQtObject(reference, 'dual_text')
|
||||
if self.parent.settings_tab.display_style == 1:
|
||||
verse_text = self.formatVerse(old_chapter, chapter, verse,
|
||||
u'(', u')')
|
||||
u'{su}(', u'){/su}')
|
||||
elif self.parent.settings_tab.display_style == 2:
|
||||
verse_text = self.formatVerse(old_chapter, chapter, verse,
|
||||
u'{', u'}')
|
||||
u'{su}{', u'}{/su}')
|
||||
elif self.parent.settings_tab.display_style == 3:
|
||||
verse_text = self.formatVerse(old_chapter, chapter, verse,
|
||||
u'[', u']')
|
||||
u'{su}[', u']{/su}')
|
||||
else:
|
||||
verse_text = self.formatVerse(old_chapter, chapter, verse,
|
||||
u'', u'')
|
||||
u'{su}', u'{/su}')
|
||||
old_chapter = chapter
|
||||
footer = u'%s (%s %s)' % (book, version, copyright)
|
||||
# If not found add to footer
|
||||
@ -532,7 +532,11 @@ class BibleMediaItem(MediaManagerItem):
|
||||
else:
|
||||
# If we are 'Verse Per Line' then force a new line.
|
||||
if self.parent.settings_tab.layout_style == 1:
|
||||
text = text + u'\n\n'
|
||||
text = text + u'\n'
|
||||
else:
|
||||
# split the line but do not replace line breaks in renderer
|
||||
service_item.add_capability(ItemCapabilities.NoLineBreaks)
|
||||
text = text + u'\n'
|
||||
bible_text = u'%s %s %s' % (bible_text, verse_text, text)
|
||||
# If we are 'Verse Per Slide' then create a new slide.
|
||||
if self.parent.settings_tab.layout_style == 0:
|
||||
@ -547,7 +551,8 @@ class BibleMediaItem(MediaManagerItem):
|
||||
if isinstance(reference, QtCore.QVariant):
|
||||
reference = reference.toPyObject()
|
||||
bible_new = self._decodeQtObject(reference, 'bible')
|
||||
dual_bible_new = self._decodeQtObject(reference, 'dual_bible')
|
||||
dual_bible_new = self._decodeQtObject(reference,
|
||||
'dual_bible')
|
||||
if dual_bible_new:
|
||||
raw_slides.append(bible_text)
|
||||
bible_text = u''
|
||||
@ -635,13 +640,13 @@ class BibleMediaItem(MediaManagerItem):
|
||||
combo.addItem(unicode(i))
|
||||
|
||||
def displayResults(self, bible, dual_bible=None):
|
||||
'''
|
||||
Displays the search results in the media manager. All data needed for further
|
||||
action is saved for/in each row.
|
||||
'''
|
||||
"""
|
||||
Displays the search results in the media manager. All data needed for
|
||||
further action is saved for/in each row.
|
||||
"""
|
||||
version = self.parent.manager.get_meta_data(bible, u'Version')
|
||||
copyright = self.parent.manager.get_meta_data(bible, u'Copyright')
|
||||
permission = self.parent.manager.get_meta_data(bible, u'Permissions')
|
||||
#permission = self.parent.manager.get_meta_data(bible, u'Permissions')
|
||||
if dual_bible:
|
||||
dual_version = self.parent.manager.get_meta_data(dual_bible,
|
||||
u'Version')
|
||||
|
@ -27,6 +27,7 @@
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import build_icon, translate
|
||||
from openlp.core.ui import SpellTextEdit
|
||||
|
||||
class Ui_CustomEditDialog(object):
|
||||
def setupUi(self, customEditDialog):
|
||||
@ -73,7 +74,7 @@ class Ui_CustomEditDialog(object):
|
||||
self.editLayout3.setSpacing(8)
|
||||
self.editLayout3.setMargin(0)
|
||||
self.editLayout3.setObjectName(u'editLayout3')
|
||||
self.verseTextEdit = QtGui.QTextEdit(self.editWidget)
|
||||
self.verseTextEdit = SpellTextEdit(self)
|
||||
self.verseTextEdit.setObjectName(u'verseTextEdit')
|
||||
self.editLayout3.addWidget(self.verseTextEdit)
|
||||
self.buttonWidget = QtGui.QWidget(self.editWidget)
|
||||
@ -142,7 +143,7 @@ class Ui_CustomEditDialog(object):
|
||||
customEditDialog.setWindowTitle(
|
||||
translate('CustomPlugin.EditCustomForm', 'Edit Custom Slides'))
|
||||
self.upButton.setToolTip(
|
||||
translate('CustomPlugin.EditCustomForm', 'Move slide up once '
|
||||
translate('CustomPlugin.EditCustomForm', 'Move slide up one '
|
||||
'position.'))
|
||||
self.downButton.setToolTip(
|
||||
translate('CustomPlugin.EditCustomForm', 'Move slide down one '
|
||||
|
@ -110,8 +110,14 @@ class ImageMediaItem(MediaManagerItem):
|
||||
u':/slides/slide_blank.png',
|
||||
translate('ImagePlugin.MediaItem', 'Replace Live Background'),
|
||||
self.onReplaceClick, False)
|
||||
self.resetButton = self.toolbar.addToolbarButton(
|
||||
translate('ImagePlugin.MediaItem', u'Reset Background'),
|
||||
u':/system/system_close.png',
|
||||
translate('ImagePlugin.MediaItem', 'Reset Live Background'),
|
||||
self.onResetClick, False)
|
||||
# Add the song widget to the page layout
|
||||
self.pageLayout.addWidget(self.ImageWidget)
|
||||
self.resetButton.setVisible(False)
|
||||
|
||||
def onDeleteClick(self):
|
||||
"""
|
||||
@ -169,6 +175,10 @@ class ImageMediaItem(MediaManagerItem):
|
||||
else:
|
||||
return False
|
||||
|
||||
def onResetClick(self):
|
||||
self.resetButton.setVisible(False)
|
||||
self.parent.liveController.display.resetImage()
|
||||
|
||||
def onReplaceClick(self):
|
||||
if check_item_selected(self.listView,
|
||||
translate('ImagePlugin.MediaItem',
|
||||
@ -178,7 +188,8 @@ class ImageMediaItem(MediaManagerItem):
|
||||
bitem = self.listView.item(item.row())
|
||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||
frame = QtGui.QImage(unicode(filename))
|
||||
self.parent.displayManager.displayImageWithText(frame)
|
||||
self.parent.liveController.display.image(frame)
|
||||
self.resetButton.setVisible(True)
|
||||
|
||||
def onPreviewClick(self):
|
||||
MediaManagerItem.onPreviewClick(self)
|
||||
|
@ -97,8 +97,17 @@ class MediaMediaItem(MediaManagerItem):
|
||||
u':/slides/slide_blank.png',
|
||||
translate('MediaPlugin.MediaItem', 'Replace Live Background'),
|
||||
self.onReplaceClick, False)
|
||||
self.resetButton = self.toolbar.addToolbarButton(
|
||||
u'Reset Background', u':/system/system_close.png',
|
||||
translate('ImagePlugin.MediaItem', 'Reset Live Background'),
|
||||
self.onResetClick, False)
|
||||
# Add the song widget to the page layout
|
||||
self.pageLayout.addWidget(self.ImageWidget)
|
||||
self.resetButton.setVisible(False)
|
||||
|
||||
def onResetClick(self):
|
||||
self.resetButton.setVisible(False)
|
||||
self.parent.liveController.display.resetVideo()
|
||||
|
||||
def onReplaceClick(self):
|
||||
if check_item_selected(self.listView,
|
||||
@ -106,7 +115,8 @@ class MediaMediaItem(MediaManagerItem):
|
||||
'You must select a media file to replace the background with.')):
|
||||
item = self.listView.currentItem()
|
||||
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
self.parent.displayManager.displayVideo(filename)
|
||||
self.parent.liveController.display.video(filename, 0)
|
||||
self.resetButton.setVisible(True)
|
||||
|
||||
def generateSlideData(self, service_item, item=None):
|
||||
if item is None:
|
||||
|
@ -263,8 +263,8 @@ class Ui_EditSongDialog(object):
|
||||
self.SongbookLayout.setSpacing(8)
|
||||
self.SongbookLayout.setObjectName(u'SongbookLayout')
|
||||
self.SongbookCombo = QtGui.QComboBox(self.SongBookGroup)
|
||||
sizePolicy = QtGui.QSizePolicy(
|
||||
QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding,
|
||||
QtGui.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
@ -273,6 +273,12 @@ class Ui_EditSongDialog(object):
|
||||
self.SongbookCombo.setSizePolicy(sizePolicy)
|
||||
self.SongbookCombo.setObjectName(u'SongbookCombo')
|
||||
self.SongbookLayout.addWidget(self.SongbookCombo, 0, 0, 1, 1)
|
||||
self.songBookNumberLabel = QtGui.QLabel(self.SongBookGroup)
|
||||
self.SongbookLayout.addWidget(self.songBookNumberLabel, 0, 1, 1, 1)
|
||||
self.songBookNumberEdit = QtGui.QLineEdit(self.SongBookGroup)
|
||||
self.songBookNumberLabel.setBuddy(self.songBookNumberEdit)
|
||||
self.songBookNumberEdit.setMaximumWidth(35)
|
||||
self.SongbookLayout.addWidget(self.songBookNumberEdit, 0, 2, 1, 1)
|
||||
self.TopicBookLayout.addWidget(self.SongBookGroup)
|
||||
self.AuthorsTabLayout.addWidget(self.TopicBookWidget)
|
||||
self.SongTabWidget.addTab(self.AuthorsTab, u'')
|
||||
@ -440,6 +446,8 @@ class Ui_EditSongDialog(object):
|
||||
translate('SongsPlugin.EditSongForm', 'R&emove'))
|
||||
self.SongBookGroup.setTitle(
|
||||
translate('SongsPlugin.EditSongForm', 'Song Book'))
|
||||
self.songBookNumberLabel.setText(translate('SongsPlugin.EditSongForm',
|
||||
'Song No.:'))
|
||||
self.SongTabWidget.setTabText(
|
||||
self.SongTabWidget.indexOf(self.AuthorsTab),
|
||||
translate('SongsPlugin.EditSongForm',
|
||||
|
@ -49,6 +49,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
"""
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.song = None
|
||||
# can this be automated?
|
||||
self.width = 400
|
||||
self.setupUi(self)
|
||||
@ -83,10 +84,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
QtCore.QObject.connect(self.VerseListWidget,
|
||||
QtCore.SIGNAL(u'itemClicked(QTableWidgetItem*)'),
|
||||
self.onVerseListViewPressed)
|
||||
QtCore.QObject.connect(self.SongbookCombo,
|
||||
QtCore.SIGNAL(u'activated(int)'), self.onSongBookComboChanged)
|
||||
QtCore.QObject.connect(self.ThemeSelectionComboItem,
|
||||
QtCore.SIGNAL(u'activated(int)'), self.onThemeComboChanged)
|
||||
QtCore.QObject.connect(self.ThemeAddButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.parent.parent.renderManager.theme_manager.onAddTheme)
|
||||
@ -157,7 +154,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
def newSong(self):
|
||||
log.debug(u'New Song')
|
||||
self.SongTabWidget.setCurrentIndex(0)
|
||||
self.song = Song()
|
||||
self.TitleEditItem.setText(u'')
|
||||
self.AlternativeEdit.setText(u'')
|
||||
self.CopyrightEditItem.setText(u'')
|
||||
@ -298,8 +294,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
else:
|
||||
author = Author.populate(first_name=text.rsplit(u' ', 1)[0],
|
||||
last_name=text.rsplit(u' ', 1)[1], display_name=text)
|
||||
self.songmanager.save_object(author, False)
|
||||
self.song.authors.append(author)
|
||||
self.songmanager.save_object(author)
|
||||
author_item = QtGui.QListWidgetItem(
|
||||
unicode(author.display_name))
|
||||
author_item.setData(QtCore.Qt.UserRole,
|
||||
@ -312,13 +307,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
elif item > 0:
|
||||
item_id = (self.AuthorsSelectionComboItem.itemData(item)).toInt()[0]
|
||||
author = self.songmanager.get_object(Author, item_id)
|
||||
if author in self.song.authors:
|
||||
if self.AuthorsListView.findItems(unicode(author.display_name),
|
||||
QtCore.Qt.MatchExactly):
|
||||
QtGui.QMessageBox.warning(self,
|
||||
translate('SongsPlugin.EditSongForm', 'Error'),
|
||||
translate('SongsPlugin.EditSongForm', 'This author is '
|
||||
'already in the list.'))
|
||||
else:
|
||||
self.song.authors.append(author)
|
||||
author_item = QtGui.QListWidgetItem(unicode(
|
||||
author.display_name))
|
||||
author_item.setData(QtCore.Qt.UserRole,
|
||||
@ -340,9 +335,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
def onAuthorRemoveButtonClicked(self):
|
||||
self.AuthorRemoveButton.setEnabled(False)
|
||||
item = self.AuthorsListView.currentItem()
|
||||
author_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||
author = self.songmanager.get_object(Author, author_id)
|
||||
self.song.authors.remove(author)
|
||||
row = self.AuthorsListView.row(item)
|
||||
self.AuthorsListView.takeItem(row)
|
||||
|
||||
@ -357,8 +349,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
|
||||
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes:
|
||||
topic = Topic.populate(name=text)
|
||||
self.songmanager.save_object(topic, False)
|
||||
self.song.topics.append(topic)
|
||||
self.songmanager.save_object(topic)
|
||||
topic_item = QtGui.QListWidgetItem(unicode(topic.name))
|
||||
topic_item.setData(QtCore.Qt.UserRole,
|
||||
QtCore.QVariant(topic.id))
|
||||
@ -370,13 +361,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
elif item > 0:
|
||||
item_id = (self.SongTopicCombo.itemData(item)).toInt()[0]
|
||||
topic = self.songmanager.get_object(Topic, item_id)
|
||||
if topic in self.song.topics:
|
||||
if self.TopicsListView.findItems(unicode(topic.name),
|
||||
QtCore.Qt.MatchExactly):
|
||||
QtGui.QMessageBox.warning(self,
|
||||
translate('SongsPlugin.EditSongForm', 'Error'),
|
||||
translate('SongsPlugin.EditSongForm', 'This topic is '
|
||||
'already in the list.'))
|
||||
else:
|
||||
self.song.topics.append(topic)
|
||||
topic_item = QtGui.QListWidgetItem(unicode(topic.name))
|
||||
topic_item.setData(QtCore.Qt.UserRole,
|
||||
QtCore.QVariant(topic.id))
|
||||
@ -396,33 +387,17 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
def onTopicRemoveButtonClicked(self):
|
||||
self.TopicRemoveButton.setEnabled(False)
|
||||
item = self.TopicsListView.currentItem()
|
||||
topic_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||
topic = self.songmanager.get_object(Topic, topic_id)
|
||||
self.song.topics.remove(topic)
|
||||
row = self.TopicsListView.row(item)
|
||||
self.TopicsListView.takeItem(row)
|
||||
|
||||
def onSongBookComboChanged(self, item):
|
||||
if item >= 1:
|
||||
self.song.song_book_id = \
|
||||
(self.SongbookCombo.itemData(item)).toInt()[0]
|
||||
else:
|
||||
self.song.song_book_id = 0
|
||||
|
||||
def onThemeComboChanged(self, item):
|
||||
if item == 0:
|
||||
# None means no Theme
|
||||
self.song.theme_name = None
|
||||
else:
|
||||
them_name = unicode(self.ThemeSelectionComboItem.itemText(item))
|
||||
self.song.theme_name = them_name
|
||||
|
||||
def onVerseListViewPressed(self):
|
||||
self.VerseEditButton.setEnabled(True)
|
||||
self.VerseDeleteButton.setEnabled(True)
|
||||
|
||||
def onVerseAddButtonClicked(self):
|
||||
self.verse_form.setVerse(u'', True)
|
||||
# Allow insert button as you do not know if multiple verses will
|
||||
# be entered.
|
||||
self.verse_form.setVerse(u'')
|
||||
if self.verse_form.exec_():
|
||||
afterText, verse, subVerse = self.verse_form.getVerse()
|
||||
data = u'%s:%s' % (verse, subVerse)
|
||||
@ -600,10 +575,17 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.CopyrightEditItem.setCursorPosition(pos + 1)
|
||||
|
||||
def onMaintenanceButtonClicked(self):
|
||||
temp_song_book = None
|
||||
item = int(self.SongbookCombo.currentIndex())
|
||||
text = unicode(self.SongbookCombo.currentText())
|
||||
if item == 0 and text:
|
||||
temp_song_book = text
|
||||
self.parent.song_maintenance_form.exec_()
|
||||
self.loadAuthors()
|
||||
self.loadBooks()
|
||||
self.loadTopics()
|
||||
if temp_song_book:
|
||||
self.SongbookCombo.setEditText(temp_song_book)
|
||||
|
||||
def onPreview(self, button):
|
||||
"""
|
||||
@ -620,6 +602,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
|
||||
def accept(self):
|
||||
log.debug(u'accept')
|
||||
if not self.song:
|
||||
self.song = Song()
|
||||
item = int(self.SongbookCombo.currentIndex())
|
||||
text = unicode(self.SongbookCombo.currentText())
|
||||
if item == 0 and text:
|
||||
@ -632,7 +616,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
book = Book.populate(name=text, publisher=u'')
|
||||
self.songmanager.save_object(book)
|
||||
self.song.book = book
|
||||
self.loadBooks()
|
||||
else:
|
||||
return
|
||||
if self.saveSong():
|
||||
@ -648,9 +631,22 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.song.comments = unicode(self.CommentsEdit.toPlainText())
|
||||
self.song.verse_order = unicode(self.VerseOrderEdit.text())
|
||||
self.song.ccli_number = unicode(self.CCLNumberEdit.text())
|
||||
self.song.song_number = unicode(self.songBookNumberEdit.text())
|
||||
if self._validate_song():
|
||||
self.processLyrics()
|
||||
self.processTitle()
|
||||
self.song.authors = []
|
||||
for row in range(self.AuthorsListView.count()):
|
||||
item = self.AuthorsListView.item(row)
|
||||
authorId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||
self.song.authors.append(self.songmanager.get_object(Author,
|
||||
authorId))
|
||||
self.song.topics = []
|
||||
for row in range(self.TopicsListView.count()):
|
||||
item = self.TopicsListView.item(row)
|
||||
topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||
self.song.topics.append(self.songmanager.get_object(Topic,
|
||||
topicId))
|
||||
self.songmanager.save_object(self.song)
|
||||
return True
|
||||
return False
|
||||
@ -682,5 +678,5 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
|
||||
def processTitle(self):
|
||||
log.debug(u'processTitle')
|
||||
self.song.search_title = \
|
||||
re.sub(r'[\'"`,;:(){}?]+', u'', unicode(self.song.search_title))
|
||||
self.song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'',
|
||||
unicode(self.song.search_title))
|
||||
|
@ -27,80 +27,82 @@
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import build_icon, translate
|
||||
from openlp.core.ui import SpellTextEdit
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
|
||||
class Ui_EditVerseDialog(object):
|
||||
def setupUi(self, EditVerseDialog):
|
||||
EditVerseDialog.setObjectName(u'EditVerseDialog')
|
||||
EditVerseDialog.resize(474, 442)
|
||||
EditVerseDialog.setModal(True)
|
||||
self.EditVerseLayout = QtGui.QVBoxLayout(EditVerseDialog)
|
||||
self.EditVerseLayout.setSpacing(8)
|
||||
self.EditVerseLayout.setMargin(8)
|
||||
self.EditVerseLayout.setObjectName(u'EditVerseLayout')
|
||||
self.VerseTextEdit = QtGui.QPlainTextEdit(EditVerseDialog)
|
||||
self.VerseTextEdit.setObjectName(u'VerseTextEdit')
|
||||
self.EditVerseLayout.addWidget(self.VerseTextEdit)
|
||||
self.VerseTypeLayout = QtGui.QHBoxLayout()
|
||||
self.VerseTypeLayout.setSpacing(8)
|
||||
self.VerseTypeLayout.setObjectName(u'VerseTypeLayout')
|
||||
self.VerseTypeLabel = QtGui.QLabel(EditVerseDialog)
|
||||
self.VerseTypeLabel.setObjectName(u'VerseTypeLabel')
|
||||
self.VerseTypeLayout.addWidget(self.VerseTypeLabel)
|
||||
self.VerseTypeComboBox = QtGui.QComboBox(EditVerseDialog)
|
||||
self.VerseTypeComboBox.setObjectName(u'VerseTypeComboBox')
|
||||
self.VerseTypeComboBox.addItem(u'')
|
||||
self.VerseTypeComboBox.addItem(u'')
|
||||
self.VerseTypeComboBox.addItem(u'')
|
||||
self.VerseTypeComboBox.addItem(u'')
|
||||
self.VerseTypeComboBox.addItem(u'')
|
||||
self.VerseTypeComboBox.addItem(u'')
|
||||
self.VerseTypeComboBox.addItem(u'')
|
||||
self.VerseTypeLayout.addWidget(self.VerseTypeComboBox)
|
||||
self.VerseNumberBox = QtGui.QSpinBox(EditVerseDialog)
|
||||
self.VerseNumberBox.setMinimum(1)
|
||||
self.VerseNumberBox.setObjectName(u'VerseNumberBox')
|
||||
self.VerseTypeLayout.addWidget(self.VerseNumberBox)
|
||||
self.InsertButton = QtGui.QPushButton(EditVerseDialog)
|
||||
self.InsertButton.setIcon(build_icon(u':/general/general_add.png'))
|
||||
self.InsertButton.setObjectName(u'InsertButton')
|
||||
self.VerseTypeLayout.addWidget(self.InsertButton)
|
||||
self.VerseTypeSpacer = QtGui.QSpacerItem(40, 20,
|
||||
def setupUi(self, editVerseDialog):
|
||||
editVerseDialog.setObjectName(u'editVerseDialog')
|
||||
editVerseDialog.resize(474, 442)
|
||||
editVerseDialog.setModal(True)
|
||||
self.editVerseLayout = QtGui.QVBoxLayout(editVerseDialog)
|
||||
self.editVerseLayout.setSpacing(8)
|
||||
self.editVerseLayout.setMargin(8)
|
||||
self.editVerseLayout.setObjectName(u'editVerseLayout')
|
||||
self.verseTextEdit = SpellTextEdit(editVerseDialog)
|
||||
self.verseTextEdit.setObjectName(u'verseTextEdit')
|
||||
self.editVerseLayout.addWidget(self.verseTextEdit)
|
||||
self.verseTypeLayout = QtGui.QHBoxLayout()
|
||||
self.verseTypeLayout.setSpacing(8)
|
||||
self.verseTypeLayout.setObjectName(u'verseTypeLayout')
|
||||
self.verseTypeLabel = QtGui.QLabel(editVerseDialog)
|
||||
self.verseTypeLabel.setObjectName(u'verseTypeLabel')
|
||||
self.verseTypeLayout.addWidget(self.verseTypeLabel)
|
||||
self.verseTypeComboBox = QtGui.QComboBox(editVerseDialog)
|
||||
self.verseTypeComboBox.setObjectName(u'verseTypeComboBox')
|
||||
self.verseTypeLabel.setBuddy(self.verseTypeComboBox)
|
||||
self.verseTypeComboBox.addItem(u'')
|
||||
self.verseTypeComboBox.addItem(u'')
|
||||
self.verseTypeComboBox.addItem(u'')
|
||||
self.verseTypeComboBox.addItem(u'')
|
||||
self.verseTypeComboBox.addItem(u'')
|
||||
self.verseTypeComboBox.addItem(u'')
|
||||
self.verseTypeComboBox.addItem(u'')
|
||||
self.verseTypeLayout.addWidget(self.verseTypeComboBox)
|
||||
self.verseNumberBox = QtGui.QSpinBox(editVerseDialog)
|
||||
self.verseNumberBox.setMinimum(1)
|
||||
self.verseNumberBox.setObjectName(u'verseNumberBox')
|
||||
self.verseTypeLayout.addWidget(self.verseNumberBox)
|
||||
self.insertButton = QtGui.QPushButton(editVerseDialog)
|
||||
self.insertButton.setIcon(build_icon(u':/general/general_add.png'))
|
||||
self.insertButton.setObjectName(u'insertButton')
|
||||
self.verseTypeLayout.addWidget(self.insertButton)
|
||||
self.verseTypeSpacer = QtGui.QSpacerItem(40, 20,
|
||||
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.VerseTypeLayout.addItem(self.VerseTypeSpacer)
|
||||
self.EditVerseLayout.addLayout(self.VerseTypeLayout)
|
||||
self.EditButtonBox = QtGui.QDialogButtonBox(EditVerseDialog)
|
||||
self.EditButtonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.EditButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel |
|
||||
self.verseTypeLayout.addItem(self.verseTypeSpacer)
|
||||
self.editVerseLayout.addLayout(self.verseTypeLayout)
|
||||
self.editButtonBox = QtGui.QDialogButtonBox(editVerseDialog)
|
||||
self.editButtonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.editButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel |
|
||||
QtGui.QDialogButtonBox.Save)
|
||||
self.EditButtonBox.setObjectName(u'EditButtonBox')
|
||||
self.EditVerseLayout.addWidget(self.EditButtonBox)
|
||||
self.editButtonBox.setObjectName(u'editButtonBox')
|
||||
self.editVerseLayout.addWidget(self.editButtonBox)
|
||||
|
||||
self.retranslateUi(EditVerseDialog)
|
||||
QtCore.QObject.connect(self.EditButtonBox, QtCore.SIGNAL(u'accepted()'),
|
||||
EditVerseDialog.accept)
|
||||
QtCore.QObject.connect(self.EditButtonBox, QtCore.SIGNAL(u'rejected()'),
|
||||
EditVerseDialog.reject)
|
||||
QtCore.QMetaObject.connectSlotsByName(EditVerseDialog)
|
||||
self.retranslateUi(editVerseDialog)
|
||||
QtCore.QObject.connect(self.editButtonBox, QtCore.SIGNAL(u'accepted()'),
|
||||
editVerseDialog.accept)
|
||||
QtCore.QObject.connect(self.editButtonBox, QtCore.SIGNAL(u'rejected()'),
|
||||
editVerseDialog.reject)
|
||||
QtCore.QMetaObject.connectSlotsByName(editVerseDialog)
|
||||
|
||||
def retranslateUi(self, EditVerseDialog):
|
||||
EditVerseDialog.setWindowTitle(
|
||||
def retranslateUi(self, editVerseDialog):
|
||||
editVerseDialog.setWindowTitle(
|
||||
translate('SongsPlugin.EditVerseForm', 'Edit Verse'))
|
||||
self.VerseTypeLabel.setText(
|
||||
self.verseTypeLabel.setText(
|
||||
translate('SongsPlugin.EditVerseForm', '&Verse type:'))
|
||||
self.VerseTypeComboBox.setItemText(0,
|
||||
self.verseTypeComboBox.setItemText(0,
|
||||
VerseType.to_string(VerseType.Verse))
|
||||
self.VerseTypeComboBox.setItemText(1,
|
||||
self.verseTypeComboBox.setItemText(1,
|
||||
VerseType.to_string(VerseType.Chorus))
|
||||
self.VerseTypeComboBox.setItemText(2,
|
||||
self.verseTypeComboBox.setItemText(2,
|
||||
VerseType.to_string(VerseType.Bridge))
|
||||
self.VerseTypeComboBox.setItemText(3,
|
||||
self.verseTypeComboBox.setItemText(3,
|
||||
VerseType.to_string(VerseType.PreChorus))
|
||||
self.VerseTypeComboBox.setItemText(4,
|
||||
self.verseTypeComboBox.setItemText(4,
|
||||
VerseType.to_string(VerseType.Intro))
|
||||
self.VerseTypeComboBox.setItemText(5,
|
||||
self.verseTypeComboBox.setItemText(5,
|
||||
VerseType.to_string(VerseType.Ending))
|
||||
self.VerseTypeComboBox.setItemText(6,
|
||||
self.verseTypeComboBox.setItemText(6,
|
||||
VerseType.to_string(VerseType.Other))
|
||||
self.InsertButton.setText(
|
||||
self.insertButton.setText(
|
||||
translate('SongsPlugin.EditVerseForm', '&Insert'))
|
||||
|
@ -45,34 +45,36 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||
"""
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
QtCore.QObject.connect(
|
||||
self.InsertButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onInsertButtonClicked
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.VerseTextEdit,
|
||||
QtCore.QObject.connect(self.verseTextEdit,
|
||||
QtCore.SIGNAL('customContextMenuRequested(QPoint)'),
|
||||
self.contextMenu)
|
||||
QtCore.QObject.connect(self.insertButton, QtCore.SIGNAL(u'clicked()'),
|
||||
self.onInsertButtonClicked)
|
||||
QtCore.QObject.connect(self.verseTextEdit,
|
||||
QtCore.SIGNAL(u'cursorPositionChanged()'),
|
||||
self.onCursorPositionChanged
|
||||
)
|
||||
self.onCursorPositionChanged)
|
||||
self.verse_regex = re.compile(r'---\[([-\w]+):([\d]+)\]---')
|
||||
|
||||
def contextMenu(self, point):
|
||||
item = self.serviceManagerList.itemAt(point)
|
||||
print item
|
||||
|
||||
def insertVerse(self, title, num=1):
|
||||
if self.VerseTextEdit.textCursor().columnNumber() != 0:
|
||||
self.VerseTextEdit.insertPlainText(u'\n')
|
||||
self.VerseTextEdit.insertPlainText(u'---[%s:%s]---\n' % (title, num))
|
||||
self.VerseTextEdit.setFocus()
|
||||
if self.verseTextEdit.textCursor().columnNumber() != 0:
|
||||
self.verseTextEdit.insertPlainText(u'\n')
|
||||
self.verseTextEdit.insertPlainText(u'---[%s:%s]---\n' % (title, num))
|
||||
self.verseTextEdit.setFocus()
|
||||
|
||||
def onInsertButtonClicked(self):
|
||||
if self.VerseTextEdit.textCursor().columnNumber() != 0:
|
||||
self.VerseTextEdit.insertPlainText(u'\n')
|
||||
verse_type = self.VerseTypeComboBox.currentIndex()
|
||||
if self.verseTextEdit.textCursor().columnNumber() != 0:
|
||||
self.verseTextEdit.insertPlainText(u'\n')
|
||||
verse_type = self.verseTypeComboBox.currentIndex()
|
||||
if verse_type == VerseType.Verse:
|
||||
self.insertVerse(VerseType.to_string(VerseType.Verse),
|
||||
self.VerseNumberBox.value())
|
||||
self.verseNumberBox.value())
|
||||
elif verse_type == VerseType.Chorus:
|
||||
self.insertVerse(VerseType.to_string(VerseType.Chorus),
|
||||
self.VerseNumberBox.value())
|
||||
self.verseNumberBox.value())
|
||||
elif verse_type == VerseType.Bridge:
|
||||
self.insertVerse(VerseType.to_string(VerseType.Bridge))
|
||||
elif verse_type == VerseType.PreChorus:
|
||||
@ -85,8 +87,8 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||
self.insertVerse(VerseType.to_string(VerseType.Other))
|
||||
|
||||
def onCursorPositionChanged(self):
|
||||
position = self.VerseTextEdit.textCursor().position()
|
||||
text = unicode(self.VerseTextEdit.toPlainText())
|
||||
position = self.verseTextEdit.textCursor().position()
|
||||
text = unicode(self.verseTextEdit.toPlainText())
|
||||
if not text:
|
||||
return
|
||||
if text.rfind(u'[', 0, position) > text.rfind(u']', 0, position) and \
|
||||
@ -106,8 +108,8 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||
verse_number = int(match.group(2))
|
||||
verse_type_index = VerseType.from_string(verse_type)
|
||||
if verse_type_index is not None:
|
||||
self.VerseTypeComboBox.setCurrentIndex(verse_type_index)
|
||||
self.VerseNumberBox.setValue(verse_number)
|
||||
self.verseTypeComboBox.setCurrentIndex(verse_type_index)
|
||||
self.verseNumberBox.setValue(verse_number)
|
||||
|
||||
def setVerse(self, text, single=False,
|
||||
tag=u'%s:1' % VerseType.to_string(VerseType.Verse)):
|
||||
@ -115,26 +117,26 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||
verse_type, verse_number = tag.split(u':')
|
||||
verse_type_index = VerseType.from_string(verse_type)
|
||||
if verse_type_index is not None:
|
||||
self.VerseTypeComboBox.setCurrentIndex(verse_type_index)
|
||||
self.VerseNumberBox.setValue(int(verse_number))
|
||||
self.InsertButton.setVisible(False)
|
||||
self.verseTypeComboBox.setCurrentIndex(verse_type_index)
|
||||
self.verseNumberBox.setValue(int(verse_number))
|
||||
self.insertButton.setVisible(False)
|
||||
else:
|
||||
if not text:
|
||||
text = u'---[%s:1]---\n' % VerseType.to_string(VerseType.Verse)
|
||||
self.VerseTypeComboBox.setCurrentIndex(0)
|
||||
self.VerseNumberBox.setValue(1)
|
||||
self.InsertButton.setVisible(True)
|
||||
self.VerseTextEdit.setPlainText(text)
|
||||
self.VerseTextEdit.setFocus(QtCore.Qt.OtherFocusReason)
|
||||
self.VerseTextEdit.moveCursor(QtGui.QTextCursor.End)
|
||||
self.verseTypeComboBox.setCurrentIndex(0)
|
||||
self.verseNumberBox.setValue(1)
|
||||
self.insertButton.setVisible(True)
|
||||
self.verseTextEdit.setPlainText(text)
|
||||
self.verseTextEdit.setFocus(QtCore.Qt.OtherFocusReason)
|
||||
self.verseTextEdit.moveCursor(QtGui.QTextCursor.End)
|
||||
|
||||
def getVerse(self):
|
||||
return self.VerseTextEdit.toPlainText(), \
|
||||
VerseType.to_string(self.VerseTypeComboBox.currentIndex()), \
|
||||
unicode(self.VerseNumberBox.value())
|
||||
return self.verseTextEdit.toPlainText(), \
|
||||
VerseType.to_string(self.verseTypeComboBox.currentIndex()), \
|
||||
unicode(self.verseNumberBox.value())
|
||||
|
||||
def getVerseAll(self):
|
||||
text = self.VerseTextEdit.toPlainText()
|
||||
text = self.verseTextEdit.toPlainText()
|
||||
if not text.startsWith(u'---['):
|
||||
text = u'---[%s:1]---\n%s' % (VerseType.to_string(VerseType.Verse),
|
||||
text)
|
||||
|
@ -302,8 +302,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
Stop the import on pressing the cancel button.
|
||||
"""
|
||||
log.debug('Cancel button pressed!')
|
||||
if self.currentId() == 3:
|
||||
Receiver.send_message(u'song_stop_import')
|
||||
if self.currentId() == 2:
|
||||
Receiver.send_message(u'songs_stop_import')
|
||||
|
||||
def onCurrentIdChanged(self, id):
|
||||
if id == 2:
|
||||
|
@ -215,25 +215,32 @@ class Ui_SongImportWizard(object):
|
||||
self.wordsOfWorshipLayout.setSpacing(8)
|
||||
self.wordsOfWorshipLayout.setMargin(0)
|
||||
self.wordsOfWorshipLayout.setObjectName(u'wordsOfWorshipLayout')
|
||||
self.wordsOfWorshipFileListWidget = QtGui.QListWidget(self.wordsOfWorshipPage)
|
||||
self.wordsOfWorshipFileListWidget = QtGui.QListWidget(
|
||||
self.wordsOfWorshipPage)
|
||||
self.wordsOfWorshipFileListWidget.setSelectionMode(
|
||||
QtGui.QAbstractItemView.ExtendedSelection)
|
||||
self.wordsOfWorshipFileListWidget.setObjectName(u'wordsOfWorshipFileListWidget')
|
||||
self.wordsOfWorshipFileListWidget.setObjectName(
|
||||
u'wordsOfWorshipFileListWidget')
|
||||
self.wordsOfWorshipLayout.addWidget(self.wordsOfWorshipFileListWidget)
|
||||
self.wordsOfWorshipButtonLayout = QtGui.QHBoxLayout()
|
||||
self.wordsOfWorshipButtonLayout.setSpacing(8)
|
||||
self.wordsOfWorshipButtonLayout.setObjectName(u'wordsOfWorshipButtonLayout')
|
||||
self.wordsOfWorshipAddButton = QtGui.QPushButton(self.wordsOfWorshipPage)
|
||||
self.wordsOfWorshipButtonLayout.setObjectName(
|
||||
u'wordsOfWorshipButtonLayout')
|
||||
self.wordsOfWorshipAddButton = QtGui.QPushButton(
|
||||
self.wordsOfWorshipPage)
|
||||
self.wordsOfWorshipAddButton.setIcon(openIcon)
|
||||
self.wordsOfWorshipAddButton.setObjectName(u'wordsOfWorshipAddButton')
|
||||
self.wordsOfWorshipButtonLayout.addWidget(self.wordsOfWorshipAddButton)
|
||||
self.wordsOfWorshipButtonSpacer = QtGui.QSpacerItem(40, 20,
|
||||
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.wordsOfWorshipButtonLayout.addItem(self.wordsOfWorshipButtonSpacer)
|
||||
self.wordsOfWorshipRemoveButton = QtGui.QPushButton(self.wordsOfWorshipPage)
|
||||
self.wordsOfWorshipRemoveButton = QtGui.QPushButton(
|
||||
self.wordsOfWorshipPage)
|
||||
self.wordsOfWorshipRemoveButton.setIcon(deleteIcon)
|
||||
self.wordsOfWorshipRemoveButton.setObjectName(u'wordsOfWorshipRemoveButton')
|
||||
self.wordsOfWorshipButtonLayout.addWidget(self.wordsOfWorshipRemoveButton)
|
||||
self.wordsOfWorshipRemoveButton.setObjectName(
|
||||
u'wordsOfWorshipRemoveButton')
|
||||
self.wordsOfWorshipButtonLayout.addWidget(
|
||||
self.wordsOfWorshipRemoveButton)
|
||||
self.wordsOfWorshipLayout.addLayout(self.wordsOfWorshipButtonLayout)
|
||||
self.formatStackedWidget.addWidget(self.wordsOfWorshipPage)
|
||||
# CCLI File import
|
||||
@ -267,30 +274,43 @@ class Ui_SongImportWizard(object):
|
||||
# Songs of Fellowship
|
||||
self.songsOfFellowshipPage = QtGui.QWidget()
|
||||
self.songsOfFellowshipPage.setObjectName(u'songsOfFellowshipPage')
|
||||
self.songsOfFellowshipLayout = QtGui.QVBoxLayout(self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipLayout = QtGui.QVBoxLayout(
|
||||
self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipLayout.setMargin(0)
|
||||
self.songsOfFellowshipLayout.setSpacing(8)
|
||||
self.songsOfFellowshipLayout.setObjectName(u'songsOfFellowshipLayout')
|
||||
self.songsOfFellowshipFileListWidget = QtGui.QListWidget(self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipFileListWidget = QtGui.QListWidget(
|
||||
self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipFileListWidget.setSelectionMode(
|
||||
QtGui.QAbstractItemView.ExtendedSelection)
|
||||
self.songsOfFellowshipFileListWidget.setObjectName(u'songsOfFellowshipFileListWidget')
|
||||
self.songsOfFellowshipLayout.addWidget(self.songsOfFellowshipFileListWidget)
|
||||
self.songsOfFellowshipFileListWidget.setObjectName(
|
||||
u'songsOfFellowshipFileListWidget')
|
||||
self.songsOfFellowshipLayout.addWidget(
|
||||
self.songsOfFellowshipFileListWidget)
|
||||
self.songsOfFellowshipButtonLayout = QtGui.QHBoxLayout()
|
||||
self.songsOfFellowshipButtonLayout.setSpacing(8)
|
||||
self.songsOfFellowshipButtonLayout.setObjectName(u'songsOfFellowshipButtonLayout')
|
||||
self.songsOfFellowshipAddButton = QtGui.QPushButton(self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipButtonLayout.setObjectName(
|
||||
u'songsOfFellowshipButtonLayout')
|
||||
self.songsOfFellowshipAddButton = QtGui.QPushButton(
|
||||
self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipAddButton.setIcon(openIcon)
|
||||
self.songsOfFellowshipAddButton.setObjectName(u'songsOfFellowshipAddButton')
|
||||
self.songsOfFellowshipButtonLayout.addWidget(self.songsOfFellowshipAddButton)
|
||||
self.songsOfFellowshipAddButton.setObjectName(
|
||||
u'songsOfFellowshipAddButton')
|
||||
self.songsOfFellowshipButtonLayout.addWidget(
|
||||
self.songsOfFellowshipAddButton)
|
||||
self.songsOfFellowshipButtonSpacer = QtGui.QSpacerItem(40, 20,
|
||||
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.songsOfFellowshipButtonLayout.addItem(self.songsOfFellowshipButtonSpacer)
|
||||
self.songsOfFellowshipRemoveButton = QtGui.QPushButton(self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipButtonLayout.addItem(
|
||||
self.songsOfFellowshipButtonSpacer)
|
||||
self.songsOfFellowshipRemoveButton = QtGui.QPushButton(
|
||||
self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipRemoveButton.setIcon(deleteIcon)
|
||||
self.songsOfFellowshipRemoveButton.setObjectName(u'songsOfFellowshipRemoveButton')
|
||||
self.songsOfFellowshipButtonLayout.addWidget(self.songsOfFellowshipRemoveButton)
|
||||
self.songsOfFellowshipLayout.addLayout(self.songsOfFellowshipButtonLayout)
|
||||
self.songsOfFellowshipRemoveButton.setObjectName(
|
||||
u'songsOfFellowshipRemoveButton')
|
||||
self.songsOfFellowshipButtonLayout.addWidget(
|
||||
self.songsOfFellowshipRemoveButton)
|
||||
self.songsOfFellowshipLayout.addLayout(
|
||||
self.songsOfFellowshipButtonLayout)
|
||||
self.formatStackedWidget.addWidget(self.songsOfFellowshipPage)
|
||||
# Generic Document/Presentation import
|
||||
self.genericPage = QtGui.QWidget()
|
||||
|
@ -88,9 +88,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
|
||||
item = self.songmanager.get_object(item_class, item_id)
|
||||
if item and len(item.songs) == 0:
|
||||
if QtGui.QMessageBox.warning(self, dlg_title, del_text,
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)
|
||||
) == QtGui.QMessageBox.Yes:
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
|
||||
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
|
||||
self.songmanager.delete_object(item_class, item.id)
|
||||
reset_func()
|
||||
else:
|
||||
@ -405,12 +404,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
|
||||
book.publisher = temp_publisher
|
||||
|
||||
def mergeAuthors(self, old_author):
|
||||
'''
|
||||
"""
|
||||
Merges two authors into one author.
|
||||
|
||||
``old_author``
|
||||
The author which will be deleted afterwards.
|
||||
'''
|
||||
"""
|
||||
existing_author = self.songmanager.get_object_filtered(Author,
|
||||
and_(Author.first_name == old_author.first_name,
|
||||
Author.last_name == old_author.last_name,
|
||||
@ -427,12 +426,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
|
||||
self.songmanager.delete_object(Author, old_author.id)
|
||||
|
||||
def mergeTopics(self, old_topic):
|
||||
'''
|
||||
"""
|
||||
Merges two topics into one topic.
|
||||
|
||||
``old_topic``
|
||||
The topic which will be deleted afterwards.
|
||||
'''
|
||||
"""
|
||||
existing_topic = self.songmanager.get_object_filtered(Topic,
|
||||
Topic.name == old_topic.name)
|
||||
songs = self.songmanager.get_all_objects(Song,
|
||||
@ -447,12 +446,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
|
||||
self.songmanager.delete_object(Topic, old_topic.id)
|
||||
|
||||
def mergeBooks(self, old_book):
|
||||
'''
|
||||
"""
|
||||
Merges two books into one book.
|
||||
|
||||
``old_book``
|
||||
The book which will be deleted afterwards.
|
||||
'''
|
||||
"""
|
||||
existing_book = self.songmanager.get_object_filtered(Book,
|
||||
and_(Book.name == old_book.name,
|
||||
Book.publisher == old_book.publisher))
|
||||
|
@ -72,7 +72,7 @@ def init_schema(url):
|
||||
``url``
|
||||
The database to setup
|
||||
"""
|
||||
session, metadata = init_db(url, auto_flush=False)
|
||||
session, metadata = init_db(url)
|
||||
|
||||
# Definition of the "authors" table
|
||||
authors_table = Table(u'authors', metadata,
|
||||
|
@ -75,18 +75,17 @@ class OpenLPSongImport(SongImport):
|
||||
The :class:`OpenLPSongImport` class provides OpenLP with the ability to
|
||||
import song databases from other installations of OpenLP.
|
||||
"""
|
||||
def __init__(self, master_manager, **kwargs):
|
||||
def __init__(self, manager, **kwargs):
|
||||
"""
|
||||
Initialise the import.
|
||||
|
||||
``master_manager``
|
||||
``manager``
|
||||
The song manager for the running OpenLP installation.
|
||||
|
||||
``source_db``
|
||||
The database providing the data to import.
|
||||
"""
|
||||
SongImport.__init__(self, master_manager)
|
||||
self.master_manager = master_manager
|
||||
SongImport.__init__(self, manager)
|
||||
self.import_source = u'sqlite:///%s' % kwargs[u'filename']
|
||||
log.debug(self.import_source)
|
||||
self.source_session = None
|
||||
@ -145,7 +144,12 @@ class OpenLPSongImport(SongImport):
|
||||
mapper(OldTopic, source_topics_table)
|
||||
|
||||
source_songs = self.source_session.query(OldSong).all()
|
||||
song_total = len(source_songs)
|
||||
self.import_wizard.importProgressBar.setMaximum(song_total)
|
||||
song_count = 1
|
||||
for song in source_songs:
|
||||
self.import_wizard.incrementProgressBar(
|
||||
u'Importing song %s of %s' % (song_count, song_total))
|
||||
new_song = Song()
|
||||
new_song.title = song.title
|
||||
if has_media_files:
|
||||
@ -167,7 +171,7 @@ class OpenLPSongImport(SongImport):
|
||||
new_song.ccli_number = song.ccli_number
|
||||
if song.authors:
|
||||
for author in song.authors:
|
||||
existing_author = self.master_manager.get_object_filtered(
|
||||
existing_author = self.manager.get_object_filtered(
|
||||
Author, Author.display_name == author.display_name)
|
||||
if existing_author:
|
||||
new_song.authors.append(existing_author)
|
||||
@ -177,7 +181,7 @@ class OpenLPSongImport(SongImport):
|
||||
last_name=author.last_name,
|
||||
display_name=author.display_name))
|
||||
else:
|
||||
au = self.master_manager.get_object_filtered(Author,
|
||||
au = self.manager.get_object_filtered(Author,
|
||||
Author.display_name == u'Author Unknown')
|
||||
if au:
|
||||
new_song.authors.append(au)
|
||||
@ -185,7 +189,7 @@ class OpenLPSongImport(SongImport):
|
||||
new_song.authors.append(Author.populate(
|
||||
display_name=u'Author Unknown'))
|
||||
if song.book:
|
||||
existing_song_book = self.master_manager.get_object_filtered(
|
||||
existing_song_book = self.manager.get_object_filtered(
|
||||
Book, Book.name == song.book.name)
|
||||
if existing_song_book:
|
||||
new_song.book = existing_song_book
|
||||
@ -194,7 +198,7 @@ class OpenLPSongImport(SongImport):
|
||||
publisher=song.book.publisher)
|
||||
if song.topics:
|
||||
for topic in song.topics:
|
||||
existing_topic = self.master_manager.get_object_filtered(
|
||||
existing_topic = self.manager.get_object_filtered(
|
||||
Topic, Topic.name == topic.name)
|
||||
if existing_topic:
|
||||
new_song.topics.append(existing_topic)
|
||||
@ -204,12 +208,16 @@ class OpenLPSongImport(SongImport):
|
||||
# if song.media_files:
|
||||
# for media_file in song.media_files:
|
||||
# existing_media_file = \
|
||||
# self.master_manager.get_object_filtered(MediaFile,
|
||||
# self.manager.get_object_filtered(MediaFile,
|
||||
# MediaFile.file_name == media_file.file_name)
|
||||
# if existing_media_file:
|
||||
# new_song.media_files.append(existing_media_file)
|
||||
# else:
|
||||
# new_song.media_files.append(MediaFile.populate(
|
||||
# file_name=media_file.file_name))
|
||||
self.master_manager.save_object(new_song)
|
||||
self.manager.save_object(new_song)
|
||||
song_count += 1
|
||||
if self.stop_import_flag:
|
||||
return False
|
||||
engine.dispose()
|
||||
return True
|
||||
|
@ -24,14 +24,18 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import re
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.lib import Receiver, translate
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book
|
||||
from openlp.plugins.songs.lib.xml import SongXMLBuilder
|
||||
|
||||
class SongImport(object):
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class SongImport(QtCore.QObject):
|
||||
"""
|
||||
Helper class for import a song from a third party source into OpenLP
|
||||
|
||||
@ -39,7 +43,6 @@ class SongImport(object):
|
||||
whether the authors etc already exist and add them or refer to them
|
||||
as necessary
|
||||
"""
|
||||
|
||||
def __init__(self, manager):
|
||||
"""
|
||||
Initialise and create defaults for properties
|
||||
@ -48,6 +51,7 @@ class SongImport(object):
|
||||
database access is performed
|
||||
"""
|
||||
self.manager = manager
|
||||
self.stop_import_flag = False
|
||||
self.title = u''
|
||||
self.song_number = u''
|
||||
self.alternate_title = u''
|
||||
@ -67,6 +71,15 @@ class SongImport(object):
|
||||
'SongsPlugin.SongImport', 'copyright'))
|
||||
self.copyright_symbol = unicode(translate(
|
||||
'SongsPlugin.SongImport', '\xa9'))
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'songs_stop_import'), self.stop_import)
|
||||
|
||||
def stop_import(self):
|
||||
"""
|
||||
Sets the flag for importers to stop their import
|
||||
"""
|
||||
log.debug(u'Stopping songs import')
|
||||
self.stop_import_flag = True
|
||||
|
||||
def register(self, import_wizard):
|
||||
self.import_wizard = import_wizard
|
||||
|
91
resources/Fedora/191/OpenLP.spec
Normal file
91
resources/Fedora/191/OpenLP.spec
Normal 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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user