diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py
index 5970efd4f..37d1de79c 100644
--- a/openlp/core/lib/imagemanager.py
+++ b/openlp/core/lib/imagemanager.py
@@ -112,17 +112,29 @@ class PriorityQueue(Queue.PriorityQueue):
"""
Customised ``Queue.PriorityQueue``.
"""
- def remove(self, item):
+ def modify_priority(self, image, new_priority):
"""
- Removes the given ``item`` from the queue.
+ Modifies the priority of the given ``image``.
- ``item``
- The item to remove. This should be a tuple::
+ ``image``
+ The image to remove. This should be an ``Image`` instance.
- ``(Priority, Image)``
+ ``new_priority``
+ The image's new priority.
"""
- if item in self.queue:
- self.queue.remove(item)
+ self.remove(image)
+ image.priority = new_priority
+ self.put((image.priority, image))
+
+ def remove(self, image):
+ """
+ Removes the given ``image`` from the queue.
+
+ ``image``
+ The image to remove. This should be an ``Image`` instance.
+ """
+ if (image.priority, image) in self.queue:
+ self.queue.remove((image.priority, image))
class ImageManager(QtCore.QObject):
@@ -168,12 +180,16 @@ class ImageManager(QtCore.QObject):
log.debug(u'get_image %s' % name)
image = self._cache[name]
if image.image is None:
- self._conversion_queue.remove((image.priority, image))
- image.priority = Priority.High
- self._conversion_queue.put((image.priority, image))
+ self._conversion_queue.modify_priority(image, Priority.High)
while image.image is None:
log.debug(u'get_image - waiting')
time.sleep(0.1)
+ elif image.image_bytes is None:
+ # Set the priority to Low, because the image was requested but the
+ # byte stream was not generated yet. However, we only need to do
+ # this, when the image was generated before it was requested
+ # (otherwise this is already taken care of).
+ self._conversion_queue.modify_priority(image, Priority.Low)
return image.image
def get_image_bytes(self, name):
@@ -184,9 +200,7 @@ class ImageManager(QtCore.QObject):
log.debug(u'get_image_bytes %s' % name)
image = self._cache[name]
if image.image_bytes is None:
- self._conversion_queue.remove((image.priority, image))
- image.priority = Priority.Urgent
- self._conversion_queue.put((image.priority, image))
+ self._conversion_queue.modify_priority(image, Priority.Urgent)
while image.image_bytes is None:
log.debug(u'get_image_bytes - waiting')
time.sleep(0.1)
@@ -198,8 +212,7 @@ class ImageManager(QtCore.QObject):
"""
log.debug(u'del_image %s' % name)
if name in self._cache:
- self._conversion_queue.remove(
- (self._cache[name].priority, self._cache[name]))
+ self._conversion_queue.remove(self._cache[name])
del self._cache[name]
def add_image(self, name, path):
@@ -238,18 +251,14 @@ class ImageManager(QtCore.QObject):
# Set the priority to Lowest and stop here as we need to process
# more important images first.
if image.priority == Priority.Normal:
- self._conversion_queue.remove((image.priority, image))
- image.priority = Priority.Lowest
- self._conversion_queue.put((image.priority, image))
+ self._conversion_queue.modify_priority(image, Priority.Lowest)
return
# For image with high priority we set the priority to Low, as the
# byte stream might be needed earlier the byte stream of image with
# Normal priority. We stop here as we need to process more important
# images first.
elif image.priority == Priority.High:
- self._conversion_queue.remove((image.priority, image))
- image.priority = Priority.Low
- self._conversion_queue.put((image.priority, image))
+ self._conversion_queue.modify_priority(image, Priority.Low)
return
# Generate the byte stream for the image.
if image.image_bytes is None:
diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py
index 03b094e82..28ceaad68 100644
--- a/openlp/plugins/bibles/lib/http.py
+++ b/openlp/plugins/bibles/lib/http.py
@@ -147,7 +147,10 @@ class BGExtract(object):
send_error_message(u'download')
return None
page_source = page.read()
- page_source = unicode(page_source, 'utf8')
+ try:
+ page_source = unicode(page_source, u'utf8')
+ except UnicodeDecodeError:
+ page_source = unicode(page_source, u'cp1251')
page_source_temp = re.search(u'
', page_source, re.DOTALL)
if page_source_temp:
diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py
index 308ff6aa1..e2996ff8f 100644
--- a/openlp/plugins/songs/lib/__init__.py
+++ b/openlp/plugins/songs/lib/__init__.py
@@ -267,6 +267,12 @@ def clean_song(manager, song):
``song``
The song object.
"""
+ if isinstance(song.title, buffer):
+ song.title = unicode(song.title)
+ if isinstance(song.alternate_title, buffer):
+ song.alternate_title = unicode(song.alternate_title)
+ if isinstance(song.lyrics, buffer):
+ song.lyrics = unicode(song.lyrics)
song.title = song.title.rstrip() if song.title else u''
if song.alternate_title is None:
song.alternate_title = u''
diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py
index 7304b5196..575295f37 100644
--- a/openlp/plugins/songusage/songusageplugin.py
+++ b/openlp/plugins/songusage/songusageplugin.py
@@ -48,8 +48,10 @@ class SongUsagePlugin(Plugin):
Plugin.__init__(self, u'SongUsage', plugin_helpers)
self.weight = -4
self.icon = build_icon(u':/plugins/plugin_songusage.png')
+ self.activeIcon = build_icon(u':/songusage/song_usage_active.png')
+ self.inactiveIcon = build_icon(u':/songusage/song_usage_inactive.png')
self.manager = None
- self.songusageActive = False
+ self.songUsageActive = False
def addToolsMenuItem(self, tools_menu):
"""
@@ -84,17 +86,29 @@ class SongUsagePlugin(Plugin):
self.songUsageStatus.setText(translate(
'SongUsagePlugin', 'Toggle Tracking'))
self.songUsageStatus.setStatusTip(translate('SongUsagePlugin',
- 'Toggle the tracking of song usage.'))
- #Add Menus together
+ 'Toggle the tracking of song usage.'))
+ # Add Menus together
self.toolsMenu.addAction(self.songUsageMenu.menuAction())
self.songUsageMenu.addAction(self.songUsageStatus)
self.songUsageMenu.addSeparator()
self.songUsageMenu.addAction(self.songUsageDelete)
self.songUsageMenu.addAction(self.songUsageReport)
+ self.songUsageActiveButton = QtGui.QToolButton(
+ self.formparent.statusBar)
+ self.songUsageActiveButton.setCheckable(True)
+ self.songUsageActiveButton.setStatusTip(translate('SongUsagePlugin',
+ 'Toggle the tracking of song usage.'))
+ self.songUsageActiveButton.setObjectName(u'songUsageActiveButton')
+ self.formparent.statusBar.insertPermanentWidget(1,
+ self.songUsageActiveButton)
+ self.songUsageActiveButton.hide()
# Signals and slots
QtCore.QObject.connect(self.songUsageStatus,
QtCore.SIGNAL(u'visibilityChanged(bool)'),
self.songUsageStatus.setChecked)
+ QtCore.QObject.connect(self.songUsageActiveButton,
+ QtCore.SIGNAL(u'toggled(bool)'),
+ self.toggleSongUsageState)
QtCore.QObject.connect(self.songUsageDelete,
QtCore.SIGNAL(u'triggered()'), self.onSongUsageDelete)
QtCore.QObject.connect(self.songUsageReport,
@@ -107,23 +121,25 @@ class SongUsagePlugin(Plugin):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_live_started'),
self.onReceiveSongUsage)
- self.SongUsageActive = QtCore.QSettings().value(
+ self.songUsageActive = QtCore.QSettings().value(
self.settingsSection + u'/active',
QtCore.QVariant(False)).toBool()
- self.songUsageStatus.setChecked(self.SongUsageActive)
+ # Set the button and checkbox state
+ self.setButtonState()
action_list = ActionList.get_instance()
+ action_list.add_action(self.songUsageStatus,
+ translate('SongUsagePlugin', 'Song Usage'))
action_list.add_action(self.songUsageDelete,
translate('SongUsagePlugin', 'Song Usage'))
action_list.add_action(self.songUsageReport,
translate('SongUsagePlugin', 'Song Usage'))
- action_list.add_action(self.songUsageStatus,
- translate('SongUsagePlugin', 'Song Usage'))
if self.manager is None:
self.manager = Manager(u'songusage', init_schema)
self.songUsageDeleteForm = SongUsageDeleteForm(self.manager,
self.formparent)
self.songUsageDetailForm = SongUsageDetailForm(self, self.formparent)
self.songUsageMenu.menuAction().setVisible(True)
+ self.songUsageActiveButton.show()
def finalise(self):
"""
@@ -134,26 +150,55 @@ class SongUsagePlugin(Plugin):
Plugin.finalise(self)
self.songUsageMenu.menuAction().setVisible(False)
action_list = ActionList.get_instance()
+ action_list.remove_action(self.songUsageStatus,
+ translate('SongUsagePlugin', 'Song Usage'))
action_list.remove_action(self.songUsageDelete,
translate('SongUsagePlugin', 'Song Usage'))
action_list.remove_action(self.songUsageReport,
translate('SongUsagePlugin', 'Song Usage'))
- action_list.remove_action(self.songUsageStatus,
- translate('SongUsagePlugin', 'Song Usage'))
- #stop any events being processed
- self.SongUsageActive = False
+ self.songUsageActiveButton.hide()
+ # stop any events being processed
+ self.songUsageActive = False
def toggleSongUsageState(self):
- self.SongUsageActive = not self.SongUsageActive
+ """
+ Manage the state of the audit collection and amend
+ the UI when necessary,
+ """
+ self.songUsageActive = not self.songUsageActive
QtCore.QSettings().setValue(self.settingsSection + u'/active',
- QtCore.QVariant(self.SongUsageActive))
+ QtCore.QVariant(self.songUsageActive))
+ self.setButtonState()
+
+ def setButtonState(self):
+ """
+ Keep buttons inline. Turn of signals to stop dead loop but we need the
+ button and check box set correctly.
+ """
+ self.songUsageActiveButton.blockSignals(True)
+ self.songUsageStatus.blockSignals(True)
+ if self.songUsageActive:
+ self.songUsageActiveButton.setIcon(self.activeIcon)
+ self.songUsageStatus.setChecked(True)
+ self.songUsageActiveButton.setChecked(True)
+ self.songUsageActiveButton.setToolTip(translate('SongUsagePlugin',
+ 'Song usage tracking is active.'))
+ else:
+ self.songUsageActiveButton.setIcon(self.inactiveIcon)
+ self.songUsageStatus.setChecked(False)
+ self.songUsageActiveButton.setChecked(False)
+ self.songUsageActiveButton.setToolTip(translate('SongUsagePlugin',
+ 'Song usage tracking is inactive.'))
+ self.songUsageActiveButton.blockSignals(False)
+ self.songUsageStatus.blockSignals(False)
+
def onReceiveSongUsage(self, item):
"""
Song Usage for live song from SlideController
"""
audit = item[0].audit
- if self.SongUsageActive and audit:
+ if self.songUsageActive and audit:
song_usage_item = SongUsageItem()
song_usage_item.usagedate = datetime.today()
song_usage_item.usagetime = datetime.now().time()
diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc
index 4dca475f4..fff1f75b8 100644
--- a/resources/images/openlp-2.qrc
+++ b/resources/images/openlp-2.qrc
@@ -138,6 +138,10 @@
messagebox_info.png
messagebox_warning.png
+
+ song_usage_active.png
+ song_usage_inactive.png
+
tools_add.png
tools_alert.png
diff --git a/resources/images/song_usage_active.png b/resources/images/song_usage_active.png
new file mode 100644
index 000000000..1221e1310
Binary files /dev/null and b/resources/images/song_usage_active.png differ
diff --git a/resources/images/song_usage_inactive.png b/resources/images/song_usage_inactive.png
new file mode 100644
index 000000000..cdcf944ee
Binary files /dev/null and b/resources/images/song_usage_inactive.png differ
diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py
new file mode 100755
index 000000000..4abd1504d
--- /dev/null
+++ b/scripts/check_dependencies.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2011 Raoul Snyman #
+# --------------------------------------------------------------------------- #
+# 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 #
+###############################################################################
+
+"""
+This script is used to check dependencies of OpenLP. It checks availability
+of required python modules and their version. To verify availability of Python
+modules, simply run this script::
+
+ @:~$ ./check_dependencies.py
+
+"""
+import os
+import sys
+
+is_win = sys.platform.startswith('win')
+
+VERS = {
+ 'Python': '2.6',
+ 'PyQt4': '4.6',
+ 'Qt4': '4.6',
+ 'sqlalchemy': '0.5',
+ # pyenchant 1.6 required on Windows
+ 'enchant': '1.6' if is_win else '1.3'
+ }
+
+# pywin32
+WIN32_MODULES = [
+ 'win32com',
+ 'win32ui',
+ 'pywintypes',
+ ]
+
+MODULES = [
+ 'PyQt4',
+ 'PyQt4.QtCore',
+ 'PyQt4.QtGui',
+ 'PyQt4.QtNetwork',
+ 'PyQt4.QtOpenGL',
+ 'PyQt4.QtSvg',
+ 'PyQt4.QtTest',
+ 'PyQt4.QtWebKit',
+ 'PyQt4.phonon',
+ 'sqlalchemy',
+ 'sqlite3',
+ 'lxml',
+ 'chardet',
+ 'enchant',
+ 'BeautifulSoup',
+ 'mako',
+ ]
+
+
+OPTIONAL_MODULES = [
+ ('sqlite', ' (SQLite 2 support)'),
+ ('MySQLdb', ' (MySQL support)'),
+ ('psycopg2', ' (PostgreSQL support)'),
+ ]
+
+w = sys.stdout.write
+
+
+def check_vers(version, required, text):
+ if type(version) is str:
+ version = version.split('.')
+ version = [int(x) for x in version]
+ if type(required) is str:
+ required = required.split('.')
+ required = [int(x) for x in required]
+ w(' %s >= %s ... ' % (text, '.'.join([str(x) for x in required])))
+ if version >= required:
+ w('.'.join([str(x) for x in version]) + os.linesep)
+ return True
+ else:
+ w('FAIL' + os.linesep)
+ return False
+
+
+def print_vers_fail(required, text):
+ print(' %s >= %s ... FAIL' % (text, required))
+
+
+def verify_python():
+ if not check_vers(list(sys.version_info), VERS['Python'], text='Python'):
+ exit(1)
+
+
+def verify_versions():
+ print('Verifying version of modules...')
+ try:
+ from PyQt4 import QtCore
+ check_vers(QtCore.PYQT_VERSION_STR, VERS['PyQt4'],
+ 'PyQt4')
+ check_vers(QtCore.qVersion(), VERS['Qt4'],
+ 'Qt4')
+ except ImportError:
+ print_vers_fail(VERS['PyQt4'], 'PyQt4')
+ print_vers_fail(VERS['Qt4'], 'Qt4')
+ try:
+ import sqlalchemy
+ check_vers(sqlalchemy.__version__, VERS['sqlalchemy'], 'sqlalchemy')
+ except ImportError:
+ print_vers_fail(VERS['sqlalchemy'], 'sqlalchemy')
+ try:
+ import enchant
+ check_vers(enchant.__version__, VERS['enchant'], 'enchant')
+ except ImportError:
+ print_vers_fail(VERS['enchant'], 'enchant')
+
+
+def check_module(mod, text='', indent=' '):
+ space = (30 - len(mod) - len(text)) * ' '
+ w(indent + '%s%s... ' % (mod, text) + space)
+ try:
+ __import__(mod)
+ w('OK')
+ except ImportError:
+ w('FAIL')
+ w(os.linesep)
+
+
+def verify_pyenchant():
+ w('Enchant (spell checker)... ')
+ try:
+ import enchant
+ w(os.linesep)
+ backends = ', '.join([x.name for x in enchant.Broker().describe()])
+ print(' available backends: %s' % backends)
+ langs = ', '.join(enchant.list_languages())
+ print(' available languages: %s' % langs)
+ except ImportError:
+ w('FAIL' + os.linesep)
+
+
+def verify_pyqt():
+ w('Qt4 image formats... ')
+ try:
+ from PyQt4 import QtGui
+ read_f = ', '.join([unicode(format).lower() \
+ for format in QtGui.QImageReader.supportedImageFormats()])
+ write_f= ', '.join([unicode(format).lower() \
+ for format in QtGui.QImageWriter.supportedImageFormats()])
+ w(os.linesep)
+ print(' read: %s' % read_f)
+ print(' write: %s' % write_f)
+ except ImportError:
+ w('FAIL' + os.linesep)
+
+
+def main():
+
+ verify_python()
+
+ print('Checking for modules...')
+ for m in MODULES:
+ check_module(m)
+
+ print('Checking for optional modules...')
+ for m in OPTIONAL_MODULES:
+ check_module(m[0], text=m[1])
+
+ if is_win:
+ print('Checking for Windows specific modules...')
+ for m in WIN32_MODULES:
+ check_module(m)
+
+ verify_versions()
+ verify_pyqt()
+ verify_pyenchant()
+
+
+if __name__ == u'__main__':
+ main()
diff --git a/scripts/windows-builder.py b/scripts/windows-builder.py
index d6f4d42e3..9c96fe251 100644
--- a/scripts/windows-builder.py
+++ b/scripts/windows-builder.py
@@ -96,7 +96,7 @@ psvince.dll
the install will fail. The dll can be obtained from here:
http://www.vincenzo.net/isxkb/index.php?title=PSVince)
-Mako
+Mako
Mako Templates for Python. This package is required for building the
remote plugin. It can be installed by going to your
python_directory\scripts\.. and running "easy_install Mako". If you do not
@@ -133,7 +133,14 @@ site_packages = os.path.join(os.path.split(python_exe)[0], u'Lib',
pyi_build = os.path.abspath(os.path.join(branch_path, u'..', u'..',
u'pyinstaller', u'pyinstaller.py'))
openlp_main_script = os.path.abspath(os.path.join(branch_path, 'openlp.pyw'))
-lrelease_exe = os.path.join(site_packages, u'PyQt4', u'bin', u'lrelease.exe')
+if os.path.exists(os.path.join(site_packages, u'PyQt4', u'bin')):
+ # Older versions of the PyQt4 Windows installer put their binaries in the
+ # "bin" directory
+ lrelease_exe = os.path.join(site_packages, u'PyQt4', u'bin', u'lrelease.exe')
+else:
+ # Newer versions of the PyQt4 Windows installer put their binaries in the
+ # base directory of the installation
+ lrelease_exe = os.path.join(site_packages, u'PyQt4', u'lrelease.exe')
i18n_utils = os.path.join(script_path, u'translation_utils.py')
win32_icon = os.path.join(branch_path, u'resources', u'images', 'OpenLP.ico')
@@ -145,7 +152,7 @@ helpfile_path = os.path.join(manual_build_path, u'htmlhelp')
i18n_path = os.path.join(branch_path, u'resources', u'i18n')
winres_path = os.path.join(branch_path, u'resources', u'windows')
build_path = os.path.join(branch_path, u'build')
-dist_path = os.path.join(build_path, u'dist', u'OpenLP')
+dist_path = os.path.join(branch_path, u'dist', u'OpenLP')
pptviewlib_path = os.path.join(source_path, u'plugins', u'presentations',
u'lib', u'pptviewlib')
@@ -172,7 +179,7 @@ def run_pyinstaller():
pyinstaller = Popen((python_exe, pyi_build,
u'--noconfirm',
u'--windowed',
- u'-o', build_path,
+ u'-o', branch_path,
u'-i', win32_icon,
u'-p', branch_path,
u'-n', 'OpenLP',
@@ -319,17 +326,19 @@ def main():
import sys
for arg in sys.argv:
if arg == u'-v' or arg == u'--verbose':
- print "Script path:", script_path
- print "Branch path:", branch_path
- print "Source path:", source_path
- print "\"dist\" path:", dist_path
- print "PyInstaller:", pyi_build
+ print "OpenLP main script: ......", openlp_main_script
+ print "Script path: .............", script_path
+ print "Branch path: .............", branch_path
+ print "Source path: .............", source_path
+ print "\"dist\" path: .............", dist_path
+ print "PyInstaller: .............", pyi_build
print "Documentation branch path:", doc_branch_path
- print "Help file build path;", helpfile_path
- print "Inno Setup path:", innosetup_exe
- print "Windows resources:", winres_path
- print "VCBuild path:", vcbuild_exe
- print "PPTVIEWLIB path:", pptviewlib_path
+ print "Help file build path: ....", helpfile_path
+ print "Inno Setup path: .........", innosetup_exe
+ print "Windows resources: .......", winres_path
+ print "VCBuild path: ............", vcbuild_exe
+ print "PPTVIEWLIB path: .........", pptviewlib_path
+ print ""
elif arg == u'--skip-update':
skip_update = True
elif arg == u'/?' or arg == u'-h' or arg == u'--help':