This commit is contained in:
Tim Bentley 2011-07-11 17:20:28 +01:00
commit 96537cb0d4
9 changed files with 318 additions and 50 deletions

View File

@ -112,17 +112,29 @@ class PriorityQueue(Queue.PriorityQueue):
""" """
Customised ``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`` ``image``
The item to remove. This should be a tuple:: 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.remove(image)
self.queue.remove(item) 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): class ImageManager(QtCore.QObject):
@ -168,12 +180,16 @@ class ImageManager(QtCore.QObject):
log.debug(u'get_image %s' % name) log.debug(u'get_image %s' % name)
image = self._cache[name] image = self._cache[name]
if image.image is None: if image.image is None:
self._conversion_queue.remove((image.priority, image)) self._conversion_queue.modify_priority(image, Priority.High)
image.priority = Priority.High
self._conversion_queue.put((image.priority, image))
while image.image is None: while image.image is None:
log.debug(u'get_image - waiting') log.debug(u'get_image - waiting')
time.sleep(0.1) 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 return image.image
def get_image_bytes(self, name): def get_image_bytes(self, name):
@ -184,9 +200,7 @@ class ImageManager(QtCore.QObject):
log.debug(u'get_image_bytes %s' % name) log.debug(u'get_image_bytes %s' % name)
image = self._cache[name] image = self._cache[name]
if image.image_bytes is None: if image.image_bytes is None:
self._conversion_queue.remove((image.priority, image)) self._conversion_queue.modify_priority(image, Priority.Urgent)
image.priority = Priority.Urgent
self._conversion_queue.put((image.priority, image))
while image.image_bytes is None: while image.image_bytes is None:
log.debug(u'get_image_bytes - waiting') log.debug(u'get_image_bytes - waiting')
time.sleep(0.1) time.sleep(0.1)
@ -198,8 +212,7 @@ class ImageManager(QtCore.QObject):
""" """
log.debug(u'del_image %s' % name) log.debug(u'del_image %s' % name)
if name in self._cache: if name in self._cache:
self._conversion_queue.remove( self._conversion_queue.remove(self._cache[name])
(self._cache[name].priority, self._cache[name]))
del self._cache[name] del self._cache[name]
def add_image(self, name, path): 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 # Set the priority to Lowest and stop here as we need to process
# more important images first. # more important images first.
if image.priority == Priority.Normal: if image.priority == Priority.Normal:
self._conversion_queue.remove((image.priority, image)) self._conversion_queue.modify_priority(image, Priority.Lowest)
image.priority = Priority.Lowest
self._conversion_queue.put((image.priority, image))
return return
# For image with high priority we set the priority to Low, as the # 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 # byte stream might be needed earlier the byte stream of image with
# Normal priority. We stop here as we need to process more important # Normal priority. We stop here as we need to process more important
# images first. # images first.
elif image.priority == Priority.High: elif image.priority == Priority.High:
self._conversion_queue.remove((image.priority, image)) self._conversion_queue.modify_priority(image, Priority.Low)
image.priority = Priority.Low
self._conversion_queue.put((image.priority, image))
return return
# Generate the byte stream for the image. # Generate the byte stream for the image.
if image.image_bytes is None: if image.image_bytes is None:

View File

@ -147,7 +147,10 @@ class BGExtract(object):
send_error_message(u'download') send_error_message(u'download')
return None return None
page_source = page.read() 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'<table .*?class="infotable".*?>.*?'\ page_source_temp = re.search(u'<table .*?class="infotable".*?>.*?'\
u'</table>', page_source, re.DOTALL) u'</table>', page_source, re.DOTALL)
if page_source_temp: if page_source_temp:

View File

@ -267,6 +267,12 @@ def clean_song(manager, song):
``song`` ``song``
The song object. 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'' song.title = song.title.rstrip() if song.title else u''
if song.alternate_title is None: if song.alternate_title is None:
song.alternate_title = u'' song.alternate_title = u''

View File

@ -48,8 +48,10 @@ class SongUsagePlugin(Plugin):
Plugin.__init__(self, u'SongUsage', plugin_helpers) Plugin.__init__(self, u'SongUsage', plugin_helpers)
self.weight = -4 self.weight = -4
self.icon = build_icon(u':/plugins/plugin_songusage.png') 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.manager = None
self.songusageActive = False self.songUsageActive = False
def addToolsMenuItem(self, tools_menu): def addToolsMenuItem(self, tools_menu):
""" """
@ -84,17 +86,29 @@ class SongUsagePlugin(Plugin):
self.songUsageStatus.setText(translate( self.songUsageStatus.setText(translate(
'SongUsagePlugin', 'Toggle Tracking')) 'SongUsagePlugin', 'Toggle Tracking'))
self.songUsageStatus.setStatusTip(translate('SongUsagePlugin', self.songUsageStatus.setStatusTip(translate('SongUsagePlugin',
'Toggle the tracking of song usage.')) 'Toggle the tracking of song usage.'))
#Add Menus together # Add Menus together
self.toolsMenu.addAction(self.songUsageMenu.menuAction()) self.toolsMenu.addAction(self.songUsageMenu.menuAction())
self.songUsageMenu.addAction(self.songUsageStatus) self.songUsageMenu.addAction(self.songUsageStatus)
self.songUsageMenu.addSeparator() self.songUsageMenu.addSeparator()
self.songUsageMenu.addAction(self.songUsageDelete) self.songUsageMenu.addAction(self.songUsageDelete)
self.songUsageMenu.addAction(self.songUsageReport) 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 # Signals and slots
QtCore.QObject.connect(self.songUsageStatus, QtCore.QObject.connect(self.songUsageStatus,
QtCore.SIGNAL(u'visibilityChanged(bool)'), QtCore.SIGNAL(u'visibilityChanged(bool)'),
self.songUsageStatus.setChecked) self.songUsageStatus.setChecked)
QtCore.QObject.connect(self.songUsageActiveButton,
QtCore.SIGNAL(u'toggled(bool)'),
self.toggleSongUsageState)
QtCore.QObject.connect(self.songUsageDelete, QtCore.QObject.connect(self.songUsageDelete,
QtCore.SIGNAL(u'triggered()'), self.onSongUsageDelete) QtCore.SIGNAL(u'triggered()'), self.onSongUsageDelete)
QtCore.QObject.connect(self.songUsageReport, QtCore.QObject.connect(self.songUsageReport,
@ -107,23 +121,25 @@ class SongUsagePlugin(Plugin):
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_live_started'), QtCore.SIGNAL(u'slidecontroller_live_started'),
self.onReceiveSongUsage) self.onReceiveSongUsage)
self.SongUsageActive = QtCore.QSettings().value( self.songUsageActive = QtCore.QSettings().value(
self.settingsSection + u'/active', self.settingsSection + u'/active',
QtCore.QVariant(False)).toBool() QtCore.QVariant(False)).toBool()
self.songUsageStatus.setChecked(self.SongUsageActive) # Set the button and checkbox state
self.setButtonState()
action_list = ActionList.get_instance() action_list = ActionList.get_instance()
action_list.add_action(self.songUsageStatus,
translate('SongUsagePlugin', 'Song Usage'))
action_list.add_action(self.songUsageDelete, action_list.add_action(self.songUsageDelete,
translate('SongUsagePlugin', 'Song Usage')) translate('SongUsagePlugin', 'Song Usage'))
action_list.add_action(self.songUsageReport, action_list.add_action(self.songUsageReport,
translate('SongUsagePlugin', 'Song Usage')) translate('SongUsagePlugin', 'Song Usage'))
action_list.add_action(self.songUsageStatus,
translate('SongUsagePlugin', 'Song Usage'))
if self.manager is None: if self.manager is None:
self.manager = Manager(u'songusage', init_schema) self.manager = Manager(u'songusage', init_schema)
self.songUsageDeleteForm = SongUsageDeleteForm(self.manager, self.songUsageDeleteForm = SongUsageDeleteForm(self.manager,
self.formparent) self.formparent)
self.songUsageDetailForm = SongUsageDetailForm(self, self.formparent) self.songUsageDetailForm = SongUsageDetailForm(self, self.formparent)
self.songUsageMenu.menuAction().setVisible(True) self.songUsageMenu.menuAction().setVisible(True)
self.songUsageActiveButton.show()
def finalise(self): def finalise(self):
""" """
@ -134,26 +150,55 @@ class SongUsagePlugin(Plugin):
Plugin.finalise(self) Plugin.finalise(self)
self.songUsageMenu.menuAction().setVisible(False) self.songUsageMenu.menuAction().setVisible(False)
action_list = ActionList.get_instance() action_list = ActionList.get_instance()
action_list.remove_action(self.songUsageStatus,
translate('SongUsagePlugin', 'Song Usage'))
action_list.remove_action(self.songUsageDelete, action_list.remove_action(self.songUsageDelete,
translate('SongUsagePlugin', 'Song Usage')) translate('SongUsagePlugin', 'Song Usage'))
action_list.remove_action(self.songUsageReport, action_list.remove_action(self.songUsageReport,
translate('SongUsagePlugin', 'Song Usage')) translate('SongUsagePlugin', 'Song Usage'))
action_list.remove_action(self.songUsageStatus, self.songUsageActiveButton.hide()
translate('SongUsagePlugin', 'Song Usage')) # stop any events being processed
#stop any events being processed self.songUsageActive = False
self.SongUsageActive = False
def toggleSongUsageState(self): 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.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): def onReceiveSongUsage(self, item):
""" """
Song Usage for live song from SlideController Song Usage for live song from SlideController
""" """
audit = item[0].audit audit = item[0].audit
if self.SongUsageActive and audit: if self.songUsageActive and audit:
song_usage_item = SongUsageItem() song_usage_item = SongUsageItem()
song_usage_item.usagedate = datetime.today() song_usage_item.usagedate = datetime.today()
song_usage_item.usagetime = datetime.now().time() song_usage_item.usagetime = datetime.now().time()

View File

@ -138,6 +138,10 @@
<file>messagebox_info.png</file> <file>messagebox_info.png</file>
<file>messagebox_warning.png</file> <file>messagebox_warning.png</file>
</qresource> </qresource>
<qresource prefix="songusage">
<file>song_usage_active.png</file>
<file>song_usage_inactive.png</file>
</qresource>
<qresource prefix="tools"> <qresource prefix="tools">
<file>tools_add.png</file> <file>tools_add.png</file>
<file>tools_alert.png</file> <file>tools_alert.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

192
scripts/check_dependencies.py Executable file
View File

@ -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()

View File

@ -96,7 +96,7 @@ psvince.dll
the install will fail. The dll can be obtained from here: the install will fail. The dll can be obtained from here:
http://www.vincenzo.net/isxkb/index.php?title=PSVince) http://www.vincenzo.net/isxkb/index.php?title=PSVince)
Mako Mako
Mako Templates for Python. This package is required for building the Mako Templates for Python. This package is required for building the
remote plugin. It can be installed by going to your remote plugin. It can be installed by going to your
python_directory\scripts\.. and running "easy_install Mako". If you do not 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'..', pyi_build = os.path.abspath(os.path.join(branch_path, u'..', u'..',
u'pyinstaller', u'pyinstaller.py')) u'pyinstaller', u'pyinstaller.py'))
openlp_main_script = os.path.abspath(os.path.join(branch_path, 'openlp.pyw')) 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') i18n_utils = os.path.join(script_path, u'translation_utils.py')
win32_icon = os.path.join(branch_path, u'resources', u'images', 'OpenLP.ico') 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') i18n_path = os.path.join(branch_path, u'resources', u'i18n')
winres_path = os.path.join(branch_path, u'resources', u'windows') winres_path = os.path.join(branch_path, u'resources', u'windows')
build_path = os.path.join(branch_path, u'build') 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', pptviewlib_path = os.path.join(source_path, u'plugins', u'presentations',
u'lib', u'pptviewlib') u'lib', u'pptviewlib')
@ -172,7 +179,7 @@ def run_pyinstaller():
pyinstaller = Popen((python_exe, pyi_build, pyinstaller = Popen((python_exe, pyi_build,
u'--noconfirm', u'--noconfirm',
u'--windowed', u'--windowed',
u'-o', build_path, u'-o', branch_path,
u'-i', win32_icon, u'-i', win32_icon,
u'-p', branch_path, u'-p', branch_path,
u'-n', 'OpenLP', u'-n', 'OpenLP',
@ -319,17 +326,19 @@ def main():
import sys import sys
for arg in sys.argv: for arg in sys.argv:
if arg == u'-v' or arg == u'--verbose': if arg == u'-v' or arg == u'--verbose':
print "Script path:", script_path print "OpenLP main script: ......", openlp_main_script
print "Branch path:", branch_path print "Script path: .............", script_path
print "Source path:", source_path print "Branch path: .............", branch_path
print "\"dist\" path:", dist_path print "Source path: .............", source_path
print "PyInstaller:", pyi_build print "\"dist\" path: .............", dist_path
print "PyInstaller: .............", pyi_build
print "Documentation branch path:", doc_branch_path print "Documentation branch path:", doc_branch_path
print "Help file build path;", helpfile_path print "Help file build path: ....", helpfile_path
print "Inno Setup path:", innosetup_exe print "Inno Setup path: .........", innosetup_exe
print "Windows resources:", winres_path print "Windows resources: .......", winres_path
print "VCBuild path:", vcbuild_exe print "VCBuild path: ............", vcbuild_exe
print "PPTVIEWLIB path:", pptviewlib_path print "PPTVIEWLIB path: .........", pptviewlib_path
print ""
elif arg == u'--skip-update': elif arg == u'--skip-update':
skip_update = True skip_update = True
elif arg == u'/?' or arg == u'-h' or arg == u'--help': elif arg == u'/?' or arg == u'-h' or arg == u'--help':