This commit is contained in:
Raoul Snyman 2013-07-02 22:28:06 +02:00
commit 88e1932097
32 changed files with 336 additions and 362 deletions

View File

@ -29,7 +29,7 @@
""" """
Provide HTML Tag management and Formatting Tag access class Provide HTML Tag management and Formatting Tag access class
""" """
import cPickle import json
from openlp.core.lib import Settings, translate from openlp.core.lib import Settings, translate
@ -66,7 +66,7 @@ class FormattingTags(object):
if isinstance(tag[element], unicode): if isinstance(tag[element], unicode):
tag[element] = tag[element].encode('utf8') tag[element] = tag[element].encode('utf8')
# Formatting Tags were also known as display tags. # Formatting Tags were also known as display tags.
Settings().setValue(u'displayTags/html_tags', cPickle.dumps(tags) if tags else u'') Settings().setValue(u'formattingTags/html_tags', json.dumps(tags) if tags else u'')
@staticmethod @staticmethod
def load_tags(): def load_tags():
@ -156,13 +156,10 @@ class FormattingTags(object):
u'end html': u'', u'protected': True, u'temporary': False}) u'end html': u'', u'protected': True, u'temporary': False})
FormattingTags.add_html_tags(base_tags) FormattingTags.add_html_tags(base_tags)
FormattingTags.add_html_tags(temporary_tags) FormattingTags.add_html_tags(temporary_tags)
# Formatting Tags were also known as display tags. # Formatting Tags were also known as display tags.
user_expands = Settings().value(u'displayTags/html_tags') user_expands_string = str(Settings().value(u'formattingTags/html_tags'))
# cPickle only accepts str not unicode strings
user_expands_string = str(user_expands)
if user_expands_string: if user_expands_string:
user_tags = cPickle.loads(user_expands_string) user_tags = json.loads(user_expands_string)
for tag in user_tags: for tag in user_tags:
for element in tag: for element in tag:
if isinstance(tag[element], str): if isinstance(tag[element], str):

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4

View File

@ -728,10 +728,14 @@ class MediaManagerItem(QtGui.QWidget):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -30,6 +30,7 @@
Provide the generic plugin functionality for OpenLP plugins. Provide the generic plugin functionality for OpenLP plugins.
""" """
import logging import logging
import os
from PyQt4 import QtCore from PyQt4 import QtCore
@ -424,8 +425,11 @@ class Plugin(QtCore.QObject):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -103,9 +103,6 @@ class Registry(object):
``key`` ``key``
The service to be deleted. The service to be deleted.
""" """
if self.running_under_test is False:
log.error(u'Invalid Method call for key %s' % key)
raise KeyError(u'Invalid Method call for key %s' % key)
if key in self.service_list: if key in self.service_list:
del self.service_list[key] del self.service_list[key]

View File

@ -485,6 +485,12 @@ class ServiceItem(object):
""" """
return self.unique_identifier != other.unique_identifier return self.unique_identifier != other.unique_identifier
def __hash__(self):
"""
Return the hash for the service item.
"""
return self.unique_identifier
def is_media(self): def is_media(self):
""" """
Confirms if the ServiceItem is media Confirms if the ServiceItem is media

View File

@ -115,7 +115,7 @@ class Settings(QtCore.QSettings):
u'advanced/single click preview': False, u'advanced/single click preview': False,
u'advanced/x11 bypass wm': X11_BYPASS_DEFAULT, u'advanced/x11 bypass wm': X11_BYPASS_DEFAULT,
u'crashreport/last directory': u'', u'crashreport/last directory': u'',
u'displayTags/html_tags': u'', u'formattingTags/html_tags': u'',
u'core/audio repeat list': False, u'core/audio repeat list': False,
u'core/auto open': False, u'core/auto open': False,
u'core/auto preview': False, u'core/auto preview': False,

View File

@ -34,8 +34,8 @@ import re
import os import os
import platform import platform
import bs4
import sqlalchemy import sqlalchemy
from bs4 import BeautifulSoup
from lxml import etree from lxml import etree
from PyQt4 import Qt, QtCore, QtGui, QtWebKit from PyQt4 import Qt, QtCore, QtGui, QtWebKit
@ -145,7 +145,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
u'QtWebkit: %s\n' % WEBKIT_VERSION + \ u'QtWebkit: %s\n' % WEBKIT_VERSION + \
u'SQLAlchemy: %s\n' % sqlalchemy.__version__ + \ u'SQLAlchemy: %s\n' % sqlalchemy.__version__ + \
u'SQLAlchemy Migrate: %s\n' % MIGRATE_VERSION + \ u'SQLAlchemy Migrate: %s\n' % MIGRATE_VERSION + \
u'BeautifulSoup: %s\n' % BeautifulSoup.__version__ + \ u'BeautifulSoup: %s\n' % bs4.__version__ + \
u'lxml: %s\n' % etree.__version__ + \ u'lxml: %s\n' % etree.__version__ + \
u'Chardet: %s\n' % CHARDET_VERSION + \ u'Chardet: %s\n' % CHARDET_VERSION + \
u'PyEnchant: %s\n' % ENCHANT_VERSION + \ u'PyEnchant: %s\n' % ENCHANT_VERSION + \

View File

@ -484,10 +484,14 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -38,6 +38,7 @@ Some of the code for this form is based on the examples at:
from __future__ import division from __future__ import division
import cgi import cgi
import logging import logging
import os
import sys import sys
from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
@ -494,11 +495,15 @@ class MainDisplay(Display):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -1064,6 +1064,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
if self.live_controller.display: if self.live_controller.display:
self.live_controller.display.close() self.live_controller.display.close()
self.live_controller.display = None self.live_controller.display = None
if os.name == u'nt':
# Needed for Windows to stop crashes on exit
Registry().remove(u'application')
def service_changed(self, reset=False, serviceName=None): def service_changed(self, reset=False, serviceName=None):
""" """
@ -1374,10 +1377,14 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -29,6 +29,8 @@
""" """
The :mod:`~openlp.core.ui.media.mediaplayer` module contains the MediaPlayer class. The :mod:`~openlp.core.ui.media.mediaplayer` module contains the MediaPlayer class.
""" """
import os
from openlp.core.lib import Registry from openlp.core.lib import Registry
from openlp.core.ui.media import MediaState from openlp.core.ui.media import MediaState
@ -153,10 +155,14 @@ class MediaPlayer(object):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -44,113 +44,57 @@ VIDEO_CSS = u"""
z-index:3; z-index:3;
background-color: %(bgcolor)s; background-color: %(bgcolor)s;
} }
#video1 { #video {
background-color: %(bgcolor)s;
z-index:4;
}
#video2 {
background-color: %(bgcolor)s; background-color: %(bgcolor)s;
z-index:4; z-index:4;
} }
""" """
VIDEO_JS = u""" VIDEO_JS = u"""
var video_timer = null; function show_video(state, path, volume, loop, variable_value){
var current_video = '1'; // Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent!
function show_video(state, path, volume, loop, varVal){ var video = document.getElementById('video');
// Note, the preferred method for looping would be to use the
// video tag loop attribute.
// But QtWebKit doesn't support this. Neither does it support the
// onended event, hence the setInterval()
// In addition, setting the currentTime attribute to zero to restart
// the video raises an INDEX_SIZE_ERROR: DOM Exception 1
// To complicate it further, sometimes vid.currentTime stops
// slightly short of vid.duration and vid.ended is intermittent!
//
// Note, currently the background may go black between loops. Not
// desirable. Need to investigate using two <video>'s, and hiding/
// preloading one, and toggle between the two when looping.
if(current_video=='1'){
var vid = document.getElementById('video1');
var vid2 = document.getElementById('video2');
} else {
var vid = document.getElementById('video2');
var vid2 = document.getElementById('video1');
}
if(volume != null){ if(volume != null){
vid.volume = volume; video.volume = volume;
vid2.volume = volume;
} }
switch(state){ switch(state){
case 'init':
vid.src = 'file:///' + path;
vid2.src = 'file:///' + path;
if(loop == null) loop = false;
vid.looping = loop;
vid2.looping = loop;
vid.load();
break;
case 'load': case 'load':
vid2.style.visibility = 'hidden'; video.src = 'file:///' + path;
vid2.load(); if(loop == true) {
video.loop = true;
}
video.load();
break; break;
case 'play': case 'play':
vid.play(); video.play();
if(vid.looping){
video_timer = setInterval(
function() {
show_video('poll');
}, 200);
}
break; break;
case 'pause': case 'pause':
if(video_timer!=null){ video.pause();
clearInterval(video_timer);
video_timer = null;
}
vid.pause();
break; break;
case 'stop': case 'stop':
show_video('pause'); show_video('pause');
vid.currentTime = 0; video.currentTime = 0;
break;
case 'poll':
if(vid.ended||vid.currentTime+0.2>vid.duration)
show_video('swap');
break;
case 'swap':
show_video('pause');
if(current_video=='1')
current_video = '2';
else
current_video = '1';
show_video('load');
show_video('play');
show_video('setVisible',null,null,null,'visible');
break; break;
case 'close': case 'close':
show_video('stop'); show_video('stop');
vid.src = ''; video.src = '';
vid2.src = '';
break; break;
case 'length': case 'length':
return vid.duration; return video.duration;
case 'currentTime': case 'current_time':
return vid.currentTime; return video.currentTime;
case 'seek': case 'seek':
// doesnt work currently video.currentTime = variable_value;
vid.currentTime = varVal;
break; break;
case 'isEnded': case 'isEnded':
return vid.ended; return video.ended;
case 'setVisible': case 'setVisible':
vid.style.visibility = varVal; video.style.visibility = variable_value;
break; break;
case 'setBackBoard': case 'setBackBoard':
var back = document.getElementById('videobackboard'); var back = document.getElementById('videobackboard');
back.style.visibility = varVal; back.style.visibility = variable_value;
break; break;
} }
} }
@ -158,10 +102,7 @@ VIDEO_JS = u"""
VIDEO_HTML = u""" VIDEO_HTML = u"""
<div id="videobackboard" class="size" style="visibility:hidden"></div> <div id="videobackboard" class="size" style="visibility:hidden"></div>
<video id="video1" class="size" style="visibility:hidden" autobuffer preload> <video id="video" class="size" style="visibility:hidden" autobuffer preload></video>
</video>
<video id="video2" class="size" style="visibility:hidden" autobuffer preload>
</video>
""" """
FLASH_CSS = u""" FLASH_CSS = u"""
@ -173,25 +114,21 @@ FLASH_CSS = u"""
FLASH_JS = u""" FLASH_JS = u"""
function getFlashMovieObject(movieName) function getFlashMovieObject(movieName)
{ {
if (window.document[movieName]) if (window.document[movieName]){
{
return window.document[movieName]; return window.document[movieName];
} }
if (document.embeds && document.embeds[movieName]) if (document.embeds && document.embeds[movieName]){
return document.embeds[movieName]; return document.embeds[movieName];
}
} }
function show_flash(state, path, volume, varVal){ function show_flash(state, path, volume, variable_value){
var text = document.getElementById('flash'); var text = document.getElementById('flash');
var flashMovie = getFlashMovieObject("OpenLPFlashMovie"); var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
var src = "src = 'file:///" + path + "'"; var src = "src = 'file:///" + path + "'";
var view_parm = " wmode='opaque'" + var view_parm = " wmode='opaque'" + " width='100%%'" + " height='100%%'";
" width='100%%'" + var swf_parm = " name='OpenLPFlashMovie'" + " autostart='true' loop='false' play='true'" +
" height='100%%'"; " hidden='false' swliveconnect='true' allowscriptaccess='always'" + " volume='" + volume + "'";
var swf_parm = " name='OpenLPFlashMovie'" +
" autostart='true' loop='false' play='true'" +
" hidden='false' swliveconnect='true' allowscriptaccess='always'" +
" volume='" + volume + "'";
switch(state){ switch(state){
case 'load': case 'load':
@ -217,15 +154,16 @@ FLASH_JS = u"""
break; break;
case 'length': case 'length':
return flashMovie.TotalFrames(); return flashMovie.TotalFrames();
case 'currentTime': case 'current_time':
return flashMovie.CurrentFrame(); return flashMovie.CurrentFrame();
case 'seek': case 'seek':
// flashMovie.GotoFrame(varVal); // flashMovie.GotoFrame(variable_value);
break; break;
case 'isEnded': case 'isEnded':
return false;//TODO check flash end //TODO check flash end
return false;
case 'setVisible': case 'setVisible':
text.style.visibility = varVal; text.style.visibility = variable_value;
break; break;
} }
} }
@ -338,7 +276,7 @@ class WebkitPlayer(MediaPlayer):
controller.media_info.is_flash = True controller.media_info.is_flash = True
js = u'show_flash("load","%s");' % (path.replace(u'\\', u'\\\\')) js = u'show_flash("load","%s");' % (path.replace(u'\\', u'\\\\'))
else: else:
js = u'show_video("init", "%s", %s, %s);' % (path.replace(u'\\', u'\\\\'), str(vol), loop) js = u'show_video("load", "%s", %s, %s);' % (path.replace(u'\\', u'\\\\'), str(vol), loop)
display.frame.evaluateJavaScript(js) display.frame.evaluateJavaScript(js)
return True return True
@ -447,25 +385,25 @@ class WebkitPlayer(MediaPlayer):
""" """
controller = display.controller controller = display.controller
if controller.media_info.is_flash: if controller.media_info.is_flash:
currentTime = display.frame.evaluateJavaScript(u'show_flash("currentTime");') current_time = display.frame.evaluateJavaScript(u'show_flash("current_time");')
length = display.frame.evaluateJavaScript(u'show_flash("length");') length = display.frame.evaluateJavaScript(u'show_flash("length");')
else: else:
if display.frame.evaluateJavaScript(u'show_video("isEnded");'): if display.frame.evaluateJavaScript(u'show_video("isEnded");'):
self.stop(display) self.stop(display)
currentTime = display.frame.evaluateJavaScript(u'show_video("currentTime");') current_time = display.frame.evaluateJavaScript(u'show_video("current_time");')
# check if conversion was ok and value is not 'NaN' # check if conversion was ok and value is not 'NaN'
if currentTime and currentTime != float('inf'): if current_time and current_time != float('inf'):
currentTime = int(currentTime * 1000) current_time = int(current_time * 1000)
length = display.frame.evaluateJavaScript(u'show_video("length");') length = display.frame.evaluateJavaScript(u'show_video("length");')
# check if conversion was ok and value is not 'NaN' # check if conversion was ok and value is not 'NaN'
if length and length != float('inf'): if length and length != float('inf'):
length = int(length * 1000) length = int(length * 1000)
if currentTime > 0: if current_time:
controller.media_info.length = length controller.media_info.length = length
controller.seek_slider.setMaximum(length) controller.seek_slider.setMaximum(length)
if not controller.seek_slider.isSliderDown(): if not controller.seek_slider.isSliderDown():
controller.seek_slider.blockSignals(True) controller.seek_slider.blockSignals(True)
controller.seek_slider.setSliderPosition(currentTime) controller.seek_slider.setSliderPosition(current_time)
controller.seek_slider.blockSignals(False) controller.seek_slider.blockSignals(False)
def get_info(self): def get_info(self):

View File

@ -30,6 +30,7 @@
The actual plugin view form The actual plugin view form
""" """
import logging import logging
import os
from PyQt4 import QtGui from PyQt4 import QtGui
@ -166,10 +167,14 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -1588,10 +1588,14 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -836,10 +836,14 @@ class ThemeManager(QtGui.QWidget):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -320,10 +320,14 @@ class OpenLPWizard(QtGui.QWizard):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -28,6 +28,7 @@
############################################################################### ###############################################################################
import logging import logging
import os
import re import re
from PyQt4 import QtGui from PyQt4 import QtGui
@ -191,10 +192,14 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -544,11 +544,15 @@ class BibleDB(QtCore.QObject, Manager):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -29,6 +29,7 @@
""" """
The :mod:`http` module enables OpenLP to retrieve scripture from bible websites. The :mod:`http` module enables OpenLP to retrieve scripture from bible websites.
""" """
import os
import logging import logging
import re import re
import socket import socket
@ -301,11 +302,15 @@ class BGExtract(object):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)
@ -362,8 +367,8 @@ class BSExtract(object):
The version of the Bible like NIV for New International Version The version of the Bible like NIV for New International Version
""" """
log.debug(u'BSExtract.get_books_from_http("%s")', version) log.debug(u'BSExtract.get_books_from_http("%s")', version)
urlversion = urllib.quote(version.encode("utf-8")) url_version = urllib.quote(version.encode("utf-8"))
chapter_url = u'http://m.bibleserver.com/overlay/selectBook?translation=%s' % (urlversion) chapter_url = u'http://m.bibleserver.com/overlay/selectBook?translation=%s' % (url_version)
soup = get_soup_for_bible_ref(chapter_url) soup = get_soup_for_bible_ref(chapter_url)
if not soup: if not soup:
return None return None
@ -377,11 +382,15 @@ class BSExtract(object):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)
@ -477,11 +486,15 @@ class CWExtract(object):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)
@ -598,9 +611,8 @@ class HTTPBible(BibleDB):
if show_error: if show_error:
critical_error_message_box( critical_error_message_box(
translate('BiblesPlugin', 'No Book Found'), translate('BiblesPlugin', 'No Book Found'),
translate('BiblesPlugin', 'No matching ' translate('BiblesPlugin', 'No matching book could be found in this Bible. Check that you have '
'book could be found in this Bible. Check that you ' 'spelled the name of the book correctly.'))
'have spelled the name of the book correctly.'))
return [] return []
book = db_book.name book = db_book.name
if BibleDB.get_verse_count(self, book_id, reference[1]) == 0: if BibleDB.get_verse_count(self, book_id, reference[1]) == 0:
@ -667,14 +679,19 @@ class HTTPBible(BibleDB):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)
def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre_parse_substitute=None): def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre_parse_substitute=None):
""" """
Gets a webpage and returns a parsed and optionally cleaned soup or None. Gets a webpage and returns a parsed and optionally cleaned soup or None.
@ -724,13 +741,10 @@ def send_error_message(error_type):
if error_type == u'download': if error_type == u'download':
critical_error_message_box( critical_error_message_box(
translate('BiblesPlugin.HTTPBible', 'Download Error'), translate('BiblesPlugin.HTTPBible', 'Download Error'),
translate('BiblesPlugin.HTTPBible', 'There was a ' translate('BiblesPlugin.HTTPBible', 'There was a problem downloading your verse selection. Please check '
'problem downloading your verse selection. Please check your ' 'your Internet connection, and if this error continues to occur please consider reporting a bug.'))
'Internet connection, and if this error continues to occur '
'please consider reporting a bug.'))
elif error_type == u'parse': elif error_type == u'parse':
critical_error_message_box( critical_error_message_box(
translate('BiblesPlugin.HTTPBible', 'Parse Error'), translate('BiblesPlugin.HTTPBible', 'Parse Error'),
translate('BiblesPlugin.HTTPBible', 'There was a ' translate('BiblesPlugin.HTTPBible', 'There was a problem extracting your verse selection. If this error '
'problem extracting your verse selection. If this error continues ' 'continues to occur please consider reporting a bug.'))
'to occur please consider reporting a bug.'))

View File

@ -100,8 +100,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
self.credit_edit.setText(self.custom_slide.credits) self.credit_edit.setText(self.custom_slide.credits)
custom_XML = CustomXMLParser(self.custom_slide.text) custom_XML = CustomXMLParser(self.custom_slide.text)
slide_list = custom_XML.get_verses() slide_list = custom_XML.get_verses()
for slide in slide_list: self.slide_list_view.addItems([slide[1] for slide in slide_list])
self.slide_list_view.addItem(slide[1])
theme = self.custom_slide.theme_name theme = self.custom_slide.theme_name
find_and_set_in_combo_box(self.theme_combo_box, theme) find_and_set_in_combo_box(self.theme_combo_box, theme)
self.title_edit.setFocus() self.title_edit.setFocus()

View File

@ -41,14 +41,19 @@ class CustomSlide(BaseModel):
""" """
CustomSlide model CustomSlide model
""" """
# By default sort the customs by its title considering language specific # By default sort the customs by its title considering language specific characters.
# characters.
def __lt__(self, other): def __lt__(self, other):
return get_locale_key(self.title) < get_locale_key(other.title) return get_locale_key(self.title) < get_locale_key(other.title)
def __eq__(self, other): def __eq__(self, other):
return get_locale_key(self.title) == get_locale_key(other.title) return get_locale_key(self.title) == get_locale_key(other.title)
def __hash__(self):
"""
Return the hash for a custom slide.
"""
return self.id
def init_schema(url): def init_schema(url):
""" """

View File

@ -174,13 +174,13 @@ class PPTViewer(QtGui.QWidget):
int(self.widthEdit.text()), int(self.heightEdit.text())) int(self.widthEdit.text()), int(self.heightEdit.text()))
filename = str(self.pptEdit.text().replace(u'/', u'\\')) filename = str(self.pptEdit.text().replace(u'/', u'\\'))
folder = str(self.folderEdit.text().replace(u'/', u'\\')) folder = str(self.folderEdit.text().replace(u'/', u'\\'))
print filename, folder print(filename, folder)
self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder) self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder)
print u'id: ' + unicode(self.pptid) print(u'id: ' + unicode(self.pptid))
if oldid >= 0: if oldid >= 0:
self.pptdll.ClosePPT(oldid); self.pptdll.ClosePPT(oldid);
slides = self.pptdll.GetSlideCount(self.pptid) slides = self.pptdll.GetSlideCount(self.pptid)
print u'slidecount: ' + unicode(slides) print(u'slidecount: ' + unicode(slides))
self.total.setNum(self.pptdll.GetSlideCount(self.pptid)) self.total.setNum(self.pptdll.GetSlideCount(self.pptid))
self.updateCurrSlide() self.updateCurrSlide()
@ -188,14 +188,14 @@ class PPTViewer(QtGui.QWidget):
if self.pptid < 0: if self.pptid < 0:
return return
slide = unicode(self.pptdll.GetCurrentSlide(self.pptid)) slide = unicode(self.pptdll.GetCurrentSlide(self.pptid))
print u'currslide: ' + slide print(u'currslide: ' + slide)
self.slideEdit.setText(slide) self.slideEdit.setText(slide)
app.processEvents() app.processEvents()
def gotoClick(self): def gotoClick(self):
if self.pptid < 0: if self.pptid < 0:
return return
print self.slideEdit.text() print(self.slideEdit.text())
self.pptdll.GotoSlide(self.pptid, int(self.slideEdit.text())) self.pptdll.GotoSlide(self.pptid, int(self.slideEdit.text()))
self.updateCurrSlide() self.updateCurrSlide()
app.processEvents() app.processEvents()
@ -207,7 +207,7 @@ class PPTViewer(QtGui.QWidget):
if __name__ == '__main__': if __name__ == '__main__':
pptdll = cdll.LoadLibrary(r'pptviewlib.dll') pptdll = cdll.LoadLibrary(r'pptviewlib.dll')
pptdll.SetDebug(1) pptdll.SetDebug(1)
print u'Begin...' print(u'Begin...')
app = QtGui.QApplication(sys.argv) app = QtGui.QApplication(sys.argv)
window = PPTViewer() window = PPTViewer()
window.pptdll = pptdll window.pptdll = pptdll

View File

@ -349,10 +349,14 @@ class DuplicateSongRemovalForm(OpenLPWizard):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -275,7 +275,6 @@ class Ui_EditSongDialog(object):
self.bottom_layout.setObjectName(u'bottom_layout') self.bottom_layout.setObjectName(u'bottom_layout')
self.warning_label = QtGui.QLabel(edit_song_dialog) self.warning_label = QtGui.QLabel(edit_song_dialog)
self.warning_label.setObjectName(u'warning_label') self.warning_label.setObjectName(u'warning_label')
self.warning_label.setVisible(False)
self.bottom_layout.addWidget(self.warning_label) self.bottom_layout.addWidget(self.warning_label)
self.button_box = create_button_box(edit_song_dialog, u'button_box', [u'cancel', u'save']) self.button_box = create_button_box(edit_song_dialog, u'button_box', [u'cancel', u'save'])
self.bottom_layout.addWidget(self.button_box) self.bottom_layout.addWidget(self.button_box)
@ -323,8 +322,10 @@ class Ui_EditSongDialog(object):
self.from_media_button.setText(translate('SongsPlugin.EditSongForm', 'Add &Media')) self.from_media_button.setText(translate('SongsPlugin.EditSongForm', 'Add &Media'))
self.audio_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove')) self.audio_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
self.audio_remove_all_button.setText(translate('SongsPlugin.EditSongForm', 'Remove &All')) self.audio_remove_all_button.setText(translate('SongsPlugin.EditSongForm', 'Remove &All'))
self.warning_label.setText( self.not_all_verses_used_warning = \
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> Not all of the verses are in use.')) translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> Not all of the verses are in use.')
self.no_verse_order_entered_warning = \
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> You have not entered a verse order.')
def create_combo_box(parent, name): def create_combo_box(parent, name):

View File

@ -456,6 +456,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.title_edit.setFocus() self.title_edit.setFocus()
# Hide or show the preview button. # Hide or show the preview button.
self.preview_button.setVisible(preview) self.preview_button.setVisible(preview)
# Check if all verse tags are used.
self.on_verse_order_text_changed(self.verse_order_edit.text())
def tag_rows(self): def tag_rows(self):
""" """
@ -683,21 +685,33 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.verse_edit_button.setEnabled(False) self.verse_edit_button.setEnabled(False)
self.verse_delete_button.setEnabled(False) self.verse_delete_button.setEnabled(False)
def on_verse_order_text_changed(self, text): def on_verse_order_text_changed(self, text):
verses = [] """
verse_names = [] Checks if the verse order is complete or missing. Shows a error message according to the state of the verse
order = self._extract_verse_order(text) order.
``text``
The text of the verse order edit (ignored).
"""
# Extract all verses which were used in the order.
verses_in_order = self._extract_verse_order(self.verse_order_edit.text())
# Find the verses which were not used in the order.
verses_not_used = []
for index in range(self.verse_list_widget.rowCount()): for index in range(self.verse_list_widget.rowCount()):
verse = self.verse_list_widget.item(index, 0) verse = self.verse_list_widget.item(index, 0)
verse = verse.data(QtCore.Qt.UserRole) verse = verse.data(QtCore.Qt.UserRole)
if verse not in verse_names: if verse not in verses_in_order:
verses.append(verse)
verse_names.append(u'%s%s' % (VerseType.translated_tag(verse[0]), verse[1:]))
verses_not_used = []
for verse in verses:
if not verse in order:
verses_not_used.append(verse) verses_not_used.append(verse)
self.warning_label.setVisible(len(verses_not_used) > 0) # Set the label text.
label_text = u''
# No verse order was entered.
if not verses_in_order:
label_text = self.no_verse_order_entered_warning
# The verse order does not contain all verses.
elif verses_not_used:
label_text = self.not_all_verses_used_warning
self.warning_label.setText(label_text)
def on_copyright_insert_button_triggered(self): def on_copyright_insert_button_triggered(self):
text = self.copyright_edit.text() text = self.copyright_edit.text()

View File

@ -27,6 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
import logging import logging
import os
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
from sqlalchemy.sql import and_ from sqlalchemy.sql import and_
@ -525,10 +526,14 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def _get_application(self): def _get_application(self):
""" """
Adds the application to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -84,10 +84,14 @@ class OpenLyricsExport(object):
def _get_application(self): def _get_application(self):
""" """
Adds the openlp to the class dynamically Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
""" """
if not hasattr(self, u'_application'): if os.name == u'nt':
self._application = Registry().get(u'application') return Registry().get(u'application')
return self._application else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application) application = property(_get_application)

View File

@ -1,131 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2013 Raoul Snyman #
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# 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.plugins.songs.lib.opensongimport import OpenSongImport
from openlp.core.lib.db import Manager
from openlp.plugins.songs.lib.db import init_schema
import logging
LOG_FILENAME = 'test.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO)
# Stubs to replace the UI functions for raw testing
class wizard_stub:
def __init__(self):
self.progressBar=progbar_stub()
def incrementProgressBar(self, str):
pass
class progbar_stub:
def __init__(self):
pass
def setMaximum(self, arg):
pass
def test():
manager = Manager(u'songs', init_schema)
o = OpenSongImport(manager, filenames=[u'test.opensong'])
o.import_wizard = wizard_stub()
o.commit = False
o.do_import()
o.print_song()
assert o.copyright == u'2010 Martin Thompson'
assert o.authors == [u'MartiÑ Thómpson', u'Martin2 Thómpson']
assert o.title == u'Martins Test'
assert o.alternate_title == u''
assert o.song_number == u'1'
assert [u'C1', u'Chorus 1'] in o.verses
assert [u'C2', u'Chorus 2'] in o.verses
assert not [u'C3', u'Chorus 3'] in o.verses
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
assert [u'V3A', u'V3 Line 1\nV3 Line 2'] in o.verses
assert [u'RAP1', u'Rap 1 Line 1\nRap 1 Line 2'] in o.verses
assert [u'RAP2', u'Rap 2 Line 1\nRap 2 Line 2'] in o.verses
assert [u'RAP3', u'Rap 3 Line 1\nRap 3 Line 2'] in o.verses
assert [u'X1', u'Unreferenced verse line 1'] in o.verses
assert o.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3A', u'B1', u'V1', u'T1', u'RAP1', u'RAP2', u'RAP3']
assert o.ccli_number == u'Blah'
assert o.topics == [u'TestTheme', u'TestAltTheme']
o.filenames = [u'test.opensong.zip']
o.set_defaults()
o.do_import()
o.print_song()
assert o.copyright == u'2010 Martin Thompson'
assert o.authors == [u'MartiÑ Thómpson']
assert o.title == u'Martins Test'
assert o.alternate_title == u''
assert o.song_number == u'1'
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
assert [u'C1', u'Chorus 1'] in o.verses
assert [u'C2', u'Chorus 2'] in o.verses
assert not [u'C3', u'Chorus 3'] in o.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
print o.verse_order_list
assert o.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
o.filenames = [u'test2.opensong']
o.set_defaults()
o.do_import()
o.print_song()
assert o.copyright == u'2010 Martin Thompson'
assert o.authors == [u'Martin Thompson']
assert o.title == u'Martins 2nd Test'
assert o.alternate_title == u''
assert o.song_number == u'2'
print o.verses
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
assert [u'C1', u'Chorus 1'] in o.verses
assert [u'C2', u'Chorus 2'] in o.verses
assert not [u'C3', u'Chorus 3'] in o.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
print o.verse_order_list
assert o.verse_order_list == [u'V1', u'V2', u'B1', u'C1', u'C2']
o.filenames = [u'test3.opensong']
o.set_defaults()
o.do_import()
o.print_song()
assert o.copyright == u'2010'
assert o.authors == [u'Martin Thompson']
assert o.title == u'Test single verse'
assert o.alternate_title == u''
assert o.ccli_number == u'123456'
assert o.verse_order_list == [u'V1']
assert o.topics == [u'Worship: Declaration']
print o.verses[0]
assert [u'V1', u'Line 1\nLine 2'] in o.verses
print "Tests passed"
if __name__ == "__main__":
test()

View File

@ -85,6 +85,7 @@ MODULES = [
'migrate', 'migrate',
'uno', 'uno',
'icu', 'icu',
'bs4',
] ]

View File

@ -33,11 +33,11 @@ class TestFormattingTags(TestCase):
""" """
with patch(u'openlp.core.lib.translate') as mocked_translate, \ with patch(u'openlp.core.lib.translate') as mocked_translate, \
patch(u'openlp.core.lib.settings') as mocked_settings, \ patch(u'openlp.core.lib.settings') as mocked_settings, \
patch(u'openlp.core.lib.formattingtags.cPickle') as mocked_cPickle: patch(u'openlp.core.lib.formattingtags.json') as mocked_json:
# GIVEN: Our mocked modules and functions. # GIVEN: Our mocked modules and functions.
mocked_translate.side_effect = lambda module, string_to_translate, comment: string_to_translate mocked_translate.side_effect = lambda module, string_to_translate, comment: string_to_translate
mocked_settings.value.return_value = u'' mocked_settings.value.return_value = u''
mocked_cPickle.load.return_value = [] mocked_json.load.return_value = []
# WHEN: Get the display tags. # WHEN: Get the display tags.
FormattingTags.load_tags() FormattingTags.load_tags()
@ -54,11 +54,11 @@ class TestFormattingTags(TestCase):
""" """
with patch(u'openlp.core.lib.translate') as mocked_translate, \ with patch(u'openlp.core.lib.translate') as mocked_translate, \
patch(u'openlp.core.lib.settings') as mocked_settings, \ patch(u'openlp.core.lib.settings') as mocked_settings, \
patch(u'openlp.core.lib.formattingtags.cPickle') as mocked_cPickle: patch(u'openlp.core.lib.formattingtags.json') as mocked_json:
# GIVEN: Our mocked modules and functions. # GIVEN: Our mocked modules and functions.
mocked_translate.side_effect = lambda module, string_to_translate: string_to_translate mocked_translate.side_effect = lambda module, string_to_translate: string_to_translate
mocked_settings.value.return_value = u'' mocked_settings.value.return_value = u''
mocked_cPickle.loads.side_effect = [[], [TAG]] mocked_json.loads.side_effect = [[], [TAG]]
# WHEN: Get the display tags. # WHEN: Get the display tags.
FormattingTags.load_tags() FormattingTags.load_tags()

View File

@ -45,3 +45,66 @@ class TestEditSongForm(TestCase):
def is_verse_edit_form_executed_test(self): def is_verse_edit_form_executed_test(self):
pass pass
def verse_order_no_warning_test(self):
"""
Test if the verse order warning is not shown
"""
# GIVEN: Mocked methods.
given_verse_order = u'V1 V2'
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
# Mock out the verse.
first_verse = MagicMock()
first_verse.data = MagicMock(return_value=u'V1')
second_verse = MagicMock()
second_verse.data = MagicMock(return_value= u'V2')
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
self.form._extract_verse_order = MagicMock(return_value=given_verse_order.split())
# WHEN: Call the method.
self.form.on_verse_order_text_changed(given_verse_order)
# THEN: No text should be shown.
assert self.form.warning_label.text() == u'', u'There should be no warning.'
def verse_order_incomplete_warning_test(self):
"""
Test if the verse-order-incomple warning is shown
"""
# GIVEN: Mocked methods.
given_verse_order = u'V1'
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
# Mock out the verse.
first_verse = MagicMock()
first_verse.data = MagicMock(return_value=u'V1')
second_verse = MagicMock()
second_verse.data = MagicMock(return_value= u'V2')
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
self.form._extract_verse_order = MagicMock(return_value=[given_verse_order])
# WHEN: Call the method.
self.form.on_verse_order_text_changed(given_verse_order)
# THEN: The verse-order-incomplete text should be shown.
assert self.form.warning_label.text() == self.form.not_all_verses_used_warning, \
u'The verse-order-incomplete warning should be shown.'
def bug_1170435_test(self):
"""
Regression test for bug 1170435 (test if "no verse order" message is shown)
"""
# GIVEN: Mocked methods.
given_verse_order = u''
self.form.verse_list_widget.rowCount = MagicMock(return_value=1)
# Mock out the verse. (We want a verse type to be returned).
mocked_verse = MagicMock()
mocked_verse.data = MagicMock(return_value=u'V1')
self.form.verse_list_widget.item = MagicMock(return_value=mocked_verse)
self.form._extract_verse_order = MagicMock(return_value=[])
self.form.verse_order_edit.text = MagicMock(return_value=given_verse_order)
# WHEN: Call the method.
self.form.on_verse_order_text_changed(given_verse_order)
# THEN: The no-verse-order message should be shown.
assert self.form.warning_label.text() == self.form.no_verse_order_entered_warning, \
u'The no-verse-order message should be shown.'