This commit is contained in:
Andreas Preikschat 2013-06-06 08:46:11 +02:00
commit a6422aa58b
118 changed files with 2660 additions and 2145 deletions

View File

@ -111,10 +111,10 @@ class OpenLP(QtGui.QApplication):
# Decide how many screens we have and their size # Decide how many screens we have and their size
screens = ScreenList.create(self.desktop()) screens = ScreenList.create(self.desktop())
# First time checks in settings # First time checks in settings
has_run_wizard = Settings().value(u'general/has run wizard') has_run_wizard = Settings().value(u'core/has run wizard')
if not has_run_wizard: if not has_run_wizard:
if FirstTimeForm(screens).exec_() == QtGui.QDialog.Accepted: if FirstTimeForm(screens).exec_() == QtGui.QDialog.Accepted:
Settings().setValue(u'general/has run wizard', True) Settings().setValue(u'core/has run wizard', True)
# Correct stylesheet bugs # Correct stylesheet bugs
application_stylesheet = u'' application_stylesheet = u''
if not Settings().value(u'advanced/alternate rows'): if not Settings().value(u'advanced/alternate rows'):
@ -126,7 +126,7 @@ class OpenLP(QtGui.QApplication):
application_stylesheet += NT_REPAIR_STYLESHEET application_stylesheet += NT_REPAIR_STYLESHEET
if application_stylesheet: if application_stylesheet:
self.setStyleSheet(application_stylesheet) self.setStyleSheet(application_stylesheet)
show_splash = Settings().value(u'general/show splash') show_splash = Settings().value(u'core/show splash')
if show_splash: if show_splash:
self.splash = SplashScreen() self.splash = SplashScreen()
self.splash.show() self.splash.show()
@ -147,7 +147,7 @@ class OpenLP(QtGui.QApplication):
self.processEvents() self.processEvents()
if not has_run_wizard: if not has_run_wizard:
self.main_window.first_time() self.main_window.first_time()
update_check = Settings().value(u'general/update check') update_check = Settings().value(u'core/update check')
if update_check: if update_check:
VersionThread(self.main_window).start() VersionThread(self.main_window).start()
self.main_window.is_display_blank() self.main_window.is_display_blank()
@ -305,8 +305,10 @@ def main(args=None):
# Instance check # Instance check
if application.is_already_running(): if application.is_already_running():
sys.exit() sys.exit()
# Remove/convert obsolete settings.
Settings().remove_obsolete_settings()
# First time checks in settings # First time checks in settings
if not Settings().value(u'general/has run wizard'): if not Settings().value(u'core/has run wizard'):
if not FirstTimeLanguageForm().exec_(): if not FirstTimeLanguageForm().exec_():
# if cancel then stop processing # if cancel then stop processing
sys.exit() sys.exit()

View File

@ -30,6 +30,7 @@
The :mod:`lib` module contains most of the components and libraries that make The :mod:`lib` module contains most of the components and libraries that make
OpenLP work. OpenLP work.
""" """
from __future__ import division
from distutils.version import LooseVersion from distutils.version import LooseVersion
import logging import logging
import os import os
@ -155,7 +156,7 @@ def build_icon(icon):
``icon`` ``icon``
The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file
location like ``/path/to/file.png``. location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
""" """
button_icon = QtGui.QIcon() button_icon = QtGui.QIcon()
if isinstance(icon, QtGui.QIcon): if isinstance(icon, QtGui.QIcon):
@ -202,12 +203,13 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
States if an icon should be build and returned from the thumb. Defaults to ``True``. States if an icon should be build and returned from the thumb. Defaults to ``True``.
``size`` ``size``
Allows to state a own size to use. Defaults to ``None``, which means that a default height of 88 is used. Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default height of 88
is used.
""" """
ext = os.path.splitext(thumb_path)[1].lower() ext = os.path.splitext(thumb_path)[1].lower()
reader = QtGui.QImageReader(image_path) reader = QtGui.QImageReader(image_path)
if size is None: if size is None:
ratio = float(reader.size().width()) / float(reader.size().height()) ratio = reader.size().width() / reader.size().height()
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88)) reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
else: else:
reader.setScaledSize(size) reader.setScaledSize(size)
@ -260,8 +262,8 @@ def resize_image(image_path, width, height, background=u'#000000'):
log.debug(u'resize_image - start') log.debug(u'resize_image - start')
reader = QtGui.QImageReader(image_path) reader = QtGui.QImageReader(image_path)
# The image's ratio. # The image's ratio.
image_ratio = float(reader.size().width()) / float(reader.size().height()) image_ratio = reader.size().width() / reader.size().height()
resize_ratio = float(width) / float(height) resize_ratio = width / height
# Figure out the size we want to resize the image to (keep aspect ratio). # Figure out the size we want to resize the image to (keep aspect ratio).
if image_ratio == resize_ratio: if image_ratio == resize_ratio:
size = QtCore.QSize(width, height) size = QtCore.QSize(width, height)
@ -282,7 +284,7 @@ def resize_image(image_path, width, height, background=u'#000000'):
new_image = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32_Premultiplied) new_image = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32_Premultiplied)
painter = QtGui.QPainter(new_image) painter = QtGui.QPainter(new_image)
painter.fillRect(new_image.rect(), QtGui.QColor(background)) painter.fillRect(new_image.rect(), QtGui.QColor(background))
painter.drawImage((width - real_width) / 2, (height - real_height) / 2, preview) painter.drawImage((width - real_width) // 2, (height - real_height) // 2, preview)
return new_image return new_image

View File

@ -30,6 +30,7 @@
""" """
Provide additional functionality required by OpenLP from the inherited QDockWidget. Provide additional functionality required by OpenLP from the inherited QDockWidget.
""" """
from __future__ import division
import logging import logging
from PyQt4 import QtGui from PyQt4 import QtGui
@ -55,7 +56,7 @@ class OpenLPDockWidget(QtGui.QDockWidget):
self.setWindowIcon(build_icon(icon)) self.setWindowIcon(build_icon(icon))
# Sort out the minimum width. # Sort out the minimum width.
screens = ScreenList() screens = ScreenList()
main_window_docbars = screens.current[u'size'].width() / 5 main_window_docbars = screens.current[u'size'].width() // 5
if main_window_docbars > 300: if main_window_docbars > 300:
self.setMinimumWidth(300) self.setMinimumWidth(300)
else: else:

View File

@ -26,7 +26,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from __future__ import division
import logging import logging
from PyQt4 import QtWebKit from PyQt4 import QtWebKit
@ -276,7 +276,7 @@ def build_background_css(item, width):
``item`` ``item``
Service Item containing theme and location information Service Item containing theme and location information
""" """
width = int(width) / 2 width = int(width) // 2
theme = item.themedata theme = item.themedata
background = u'background-color: black' background = u'background-color: black'
if theme: if theme:

View File

@ -102,7 +102,9 @@ class MediaManagerItem(QtGui.QWidget):
self.setupUi() self.setupUi()
self.retranslateUi() self.retranslateUi()
self.auto_select_id = -1 self.auto_select_id = -1
Registry().register_function(u'%s_service_load' % self.plugin.name, self.service_load) # Need to use event as called across threads and UI is updated
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_go_live' % self.plugin.name), self.go_live_remote)
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_add_to_service' % self.plugin.name), self.add_to_service_remote)
def required_icons(self): def required_icons(self):
""" """
@ -424,7 +426,7 @@ class MediaManagerItem(QtGui.QWidget):
""" """
raise NotImplementedError(u'MediaManagerItem.on_delete_click needs to be defined by the plugin') raise NotImplementedError(u'MediaManagerItem.on_delete_click needs to be defined by the plugin')
def onFocus(self): def on_focus(self):
""" """
Run when a tab in the media manager gains focus. This gives the media Run when a tab in the media manager gains focus. This gives the media
item a chance to focus any elements it wants to. item a chance to focus any elements it wants to.
@ -481,6 +483,15 @@ class MediaManagerItem(QtGui.QWidget):
else: else:
self.go_live() self.go_live()
def go_live_remote(self, message):
"""
Remote Call wrapper
``message``
The passed data item_id:Remote.
"""
self.go_live(message[0], remote=message[1])
def go_live(self, item_id=None, remote=False): def go_live(self, item_id=None, remote=False):
""" """
Make the currently selected item go live. Make the currently selected item go live.
@ -523,6 +534,15 @@ class MediaManagerItem(QtGui.QWidget):
for item in items: for item in items:
self.add_to_service(item) self.add_to_service(item)
def add_to_service_remote(self, message):
"""
Remote Call wrapper
``message``
The passed data item:Remote.
"""
self.add_to_service(message[0], remote=message[1])
def add_to_service(self, item=None, replace=None, remote=False): def add_to_service(self, item=None, replace=None, remote=False):
""" """
Add this item to the current service. Add this item to the current service.
@ -564,12 +584,15 @@ class MediaManagerItem(QtGui.QWidget):
else: else:
return None return None
def service_load(self, message): def service_load(self, item):
""" """
Method to add processing when a service has been loaded and individual service items need to be processed by the Method to add processing when a service has been loaded and individual service items need to be processed by the
plugins. plugins.
``item``
The item to be processed and returned.
""" """
pass return item
def check_search_result(self): def check_search_result(self):
""" """

View File

@ -103,7 +103,7 @@ class Plugin(QtCore.QObject):
``add_export_menu_Item(export_menu)`` ``add_export_menu_Item(export_menu)``
Add an item to the Export menu. Add an item to the Export menu.
``create_settings_Tab()`` ``create_settings_tab()``
Creates a new instance of SettingsTabItem to be used in the Settings Creates a new instance of SettingsTabItem to be used in the Settings
dialog. dialog.
@ -252,7 +252,7 @@ class Plugin(QtCore.QObject):
""" """
pass pass
def create_settings_Tab(self, parent): def create_settings_tab(self, parent):
""" """
Create a tab for the settings window to display the configurable options Create a tab for the settings window to display the configurable options
for this plugin to the user. for this plugin to the user.

View File

@ -153,7 +153,7 @@ class PluginManager(object):
""" """
for plugin in self.plugins: for plugin in self.plugins:
if plugin.status is not PluginStatus.Disabled: if plugin.status is not PluginStatus.Disabled:
plugin.create_settings_Tab(self.settings_form) plugin.create_settings_tab(self.settings_form)
def hook_import_menu(self): def hook_import_menu(self):
""" """

View File

@ -26,7 +26,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from __future__ import division
import logging import logging
from PyQt4 import QtGui, QtCore, QtWebKit from PyQt4 import QtGui, QtCore, QtWebKit
@ -327,7 +327,7 @@ class Renderer(object):
screen_size = self.screens.current[u'size'] screen_size = self.screens.current[u'size']
self.width = screen_size.width() self.width = screen_size.width()
self.height = screen_size.height() self.height = screen_size.height()
self.screen_ratio = float(self.height) / float(self.width) self.screen_ratio = self.height / self.width
log.debug(u'_calculate default %s, %f' % (screen_size, self.screen_ratio)) log.debug(u'_calculate default %s, %f' % (screen_size, self.screen_ratio))
# 90% is start of footer # 90% is start of footer
self.footer_start = int(self.height * 0.90) self.footer_start = int(self.height * 0.90)
@ -546,15 +546,15 @@ class Renderer(object):
""" """
smallest_index = 0 smallest_index = 0
highest_index = len(html_list) - 1 highest_index = len(html_list) - 1
index = int(highest_index / 2) index = highest_index // 2
while True: while True:
if not self._text_fits_on_slide(previous_html + separator.join(html_list[:index + 1]).strip()): if not self._text_fits_on_slide(previous_html + separator.join(html_list[:index + 1]).strip()):
# We know that it does not fit, so change/calculate the new index and highest_index accordingly. # We know that it does not fit, so change/calculate the new index and highest_index accordingly.
highest_index = index highest_index = index
index = int(index - (index - smallest_index) / 2) index = index - (index - smallest_index) // 2
else: else:
smallest_index = index smallest_index = index
index = int(index + (highest_index - index) / 2) index = index + (highest_index - index) // 2
# We found the number of words which will fit. # We found the number of words which will fit.
if smallest_index == index or highest_index == index: if smallest_index == index or highest_index == index:
index = smallest_index index = smallest_index
@ -582,7 +582,7 @@ class Renderer(object):
html_list[0] = html_tags + html_list[0] html_list[0] = html_tags + html_list[0]
smallest_index = 0 smallest_index = 0
highest_index = len(html_list) - 1 highest_index = len(html_list) - 1
index = int(highest_index / 2) index = highest_index // 2
return previous_html, previous_raw return previous_html, previous_raw
def _text_fits_on_slide(self, text): def _text_fits_on_slide(self, text):

View File

@ -30,6 +30,7 @@
The :mod:`screen` module provides management functionality for a machines' The :mod:`screen` module provides management functionality for a machines'
displays. displays.
""" """
from __future__ import division
import logging import logging
import copy import copy
@ -232,8 +233,8 @@ class ScreenList(object):
``window`` ``window``
A QWidget we are finding the location of. A QWidget we are finding the location of.
""" """
x = window.x() + (window.width() / 2) x = window.x() + (window.width() // 2)
y = window.y() + (window.height() / 2) y = window.y() + (window.height() // 2)
for screen in self.screen_list: for screen in self.screen_list:
size = screen[u'size'] size = screen[u'size']
if x >= size.x() and x <= (size.x() + size.width()) and y >= size.y() and y <= (size.y() + size.height()): if x >= size.x() and x <= (size.x() + size.width()) and y >= size.y() and y <= (size.y() + size.height()):
@ -247,15 +248,15 @@ class ScreenList(object):
# Add the screen settings to the settings dict. This has to be done here due to cyclic dependency. # Add the screen settings to the settings dict. This has to be done here due to cyclic dependency.
# Do not do this anywhere else. # Do not do this anywhere else.
screen_settings = { screen_settings = {
u'general/x position': self.current[u'size'].x(), u'core/x position': self.current[u'size'].x(),
u'general/y position': self.current[u'size'].y(), u'core/y position': self.current[u'size'].y(),
u'general/monitor': self.display_count - 1, u'core/monitor': self.display_count - 1,
u'general/height': self.current[u'size'].height(), u'core/height': self.current[u'size'].height(),
u'general/width': self.current[u'size'].width() u'core/width': self.current[u'size'].width()
} }
Settings.extend_default_settings(screen_settings) Settings.extend_default_settings(screen_settings)
settings = Settings() settings = Settings()
settings.beginGroup(u'general') settings.beginGroup(u'core')
monitor = settings.value(u'monitor') monitor = settings.value(u'monitor')
self.set_current_display(monitor) self.set_current_display(monitor)
self.display = settings.value(u'display on monitor') self.display = settings.value(u'display on monitor')

View File

@ -26,7 +26,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from __future__ import division
import logging import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -85,10 +85,10 @@ class SearchEdit(QtGui.QLineEdit):
size = self.clear_button.size() size = self.clear_button.size()
frame_width = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth) frame_width = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
self.clear_button.move(self.rect().right() - frame_width - size.width(), self.clear_button.move(self.rect().right() - frame_width - size.width(),
(self.rect().bottom() + 1 - size.height()) / 2) (self.rect().bottom() + 1 - size.height()) // 2)
if hasattr(self, u'menu_button'): if hasattr(self, u'menu_button'):
size = self.menu_button.size() size = self.menu_button.size()
self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) / 2) self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) // 2)
def current_search_type(self): def current_search_type(self):
""" """

View File

@ -58,23 +58,19 @@ class ItemCapabilities(object):
Provides an enumeration of a service item's capabilities Provides an enumeration of a service item's capabilities
``CanPreview`` ``CanPreview``
The capability to allow the ServiceManager to add to the preview The capability to allow the ServiceManager to add to the preview tab when making the previous item live.
tab when making the previous item live.
``CanEdit`` ``CanEdit``
The capability to allow the ServiceManager to allow the item to be The capability to allow the ServiceManager to allow the item to be edited
edited
``CanMaintain`` ``CanMaintain``
The capability to allow the ServiceManager to allow the item to be The capability to allow the ServiceManager to allow the item to be reordered.
reordered.
``RequiresMedia`` ``RequiresMedia``
Determines is the service_item needs a Media Player Determines is the service_item needs a Media Player
``CanLoop`` ``CanLoop``
The capability to allow the SlideController to allow the loop The capability to allow the SlideController to allow the loop processing.
processing.
``CanAppend`` ``CanAppend``
The capability to allow the ServiceManager to add leaves to the The capability to allow the ServiceManager to add leaves to the
@ -84,22 +80,19 @@ class ItemCapabilities(object):
The capability to remove lines breaks in the renderer The capability to remove lines breaks in the renderer
``OnLoadUpdate`` ``OnLoadUpdate``
The capability to update MediaManager when a service Item is The capability to update MediaManager when a service Item is loaded.
loaded.
``AddIfNewItem`` ``AddIfNewItem``
Not Used Not Used
``ProvidesOwnDisplay`` ``ProvidesOwnDisplay``
The capability to tell the SlideController the service Item has a The capability to tell the SlideController the service Item has a different display.
different display.
``HasDetailedTitleDisplay`` ``HasDetailedTitleDisplay``
ServiceItem provides a title Being Removed and decommissioned.
``HasVariableStartTime`` ``HasVariableStartTime``
The capability to tell the ServiceManager that a change to start The capability to tell the ServiceManager that a change to start time is possible.
time is possible.
``CanSoftBreak`` ``CanSoftBreak``
The capability to tell the renderer that Soft Break is allowed The capability to tell the renderer that Soft Break is allowed
@ -151,7 +144,7 @@ class ServiceItem(object):
if plugin: if plugin:
self.name = plugin.name self.name = plugin.name
self.title = u'' self.title = u''
self.shortname = u'' self.processor = None
self.audit = u'' self.audit = u''
self.items = [] self.items = []
self.iconic_representation = None self.iconic_representation = None
@ -355,7 +348,8 @@ class ServiceItem(object):
u'media_length': self.media_length, u'media_length': self.media_length,
u'background_audio': self.background_audio, u'background_audio': self.background_audio,
u'theme_overwritten': self.theme_overwritten, u'theme_overwritten': self.theme_overwritten,
u'will_auto_start': self.will_auto_start u'will_auto_start': self.will_auto_start,
u'processor': self.processor
} }
service_data = [] service_data = []
if self.service_item_type == ServiceItemType.Text: if self.service_item_type == ServiceItemType.Text:
@ -389,7 +383,6 @@ class ServiceItem(object):
self.title = header[u'title'] self.title = header[u'title']
self.name = header[u'name'] self.name = header[u'name']
self.service_item_type = header[u'type'] self.service_item_type = header[u'type']
self.shortname = header[u'plugin']
self.theme = header[u'theme'] self.theme = header[u'theme']
self.add_icon(header[u'icon']) self.add_icon(header[u'icon'])
self.raw_footer = header[u'footer'] self.raw_footer = header[u'footer']
@ -408,7 +401,13 @@ class ServiceItem(object):
self.auto_play_slides_loop = header.get(u'auto_play_slides_loop', False) self.auto_play_slides_loop = header.get(u'auto_play_slides_loop', False)
self.timed_slide_interval = header.get(u'timed_slide_interval', 0) self.timed_slide_interval = header.get(u'timed_slide_interval', 0)
self.will_auto_start = header.get(u'will_auto_start', False) self.will_auto_start = header.get(u'will_auto_start', False)
self.processor = header.get(u'processor', None)
self.has_original_files = True self.has_original_files = True
#TODO Remove me in 2,3 build phase
if self.is_capable(ItemCapabilities.HasDetailedTitleDisplay):
self.capabilities.remove(ItemCapabilities.HasDetailedTitleDisplay)
self.processor = self.title
self.title = None
if u'background_audio' in header: if u'background_audio' in header:
self.background_audio = [] self.background_audio = []
for filename in header[u'background_audio']: for filename in header[u'background_audio']:
@ -431,6 +430,8 @@ class ServiceItem(object):
self.add_from_image(text_image[u'path'], text_image[u'title'], background) self.add_from_image(text_image[u'path'], text_image[u'title'], background)
elif self.service_item_type == ServiceItemType.Command: elif self.service_item_type == ServiceItemType.Command:
for text_image in serviceitem[u'serviceitem'][u'data']: for text_image in serviceitem[u'serviceitem'][u'data']:
if not self.title:
self.title = text_image[u'title']
if path: if path:
self.has_original_files = False self.has_original_files = False
self.add_from_command(path, text_image[u'title'], text_image[u'image']) self.add_from_command(path, text_image[u'title'], text_image[u'image'])
@ -445,9 +446,7 @@ class ServiceItem(object):
if self.is_text(): if self.is_text():
return self.title return self.title
else: else:
if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities: if len(self._raw_frames) > 1:
return self._raw_frames[0][u'title']
elif len(self._raw_frames) > 1:
return self.title return self.title
else: else:
return self._raw_frames[0][u'title'] return self._raw_frames[0][u'title']

View File

@ -116,30 +116,29 @@ class Settings(QtCore.QSettings):
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'displayTags/html_tags': u'',
u'general/audio repeat list': False, u'core/audio repeat list': False,
u'general/auto open': False, u'core/auto open': False,
u'general/auto preview': False, u'core/auto preview': False,
u'general/audio start paused': True, u'core/audio start paused': True,
u'general/auto unblank': False, u'core/auto unblank': False,
u'general/blank warning': False, u'core/blank warning': False,
u'general/ccli number': u'', u'core/ccli number': u'',
u'general/has run wizard': False, u'core/has run wizard': False,
u'general/language': u'[en]', u'core/language': u'[en]',
# This defaults to yesterday in order to force the update check to run when you've never run it before. u'core/last version test': u'',
u'general/last version test': datetime.datetime.now().date() - datetime.timedelta(days=1), u'core/loop delay': 5,
u'general/loop delay': 5, u'core/recent files': [],
u'general/recent files': [], u'core/save prompt': False,
u'general/save prompt': False, u'core/screen blank': False,
u'general/screen blank': False, u'core/show splash': True,
u'general/show splash': True, u'core/songselect password': u'',
u'general/songselect password': u'', u'core/songselect username': u'',
u'general/songselect username': u'', u'core/update check': True,
u'general/update check': True, u'core/view mode': u'default',
u'general/view mode': u'default',
# The other display settings (display position and dimensions) are defined in the ScreenList class due to a # The other display settings (display position and dimensions) are defined in the ScreenList class due to a
# circular dependency. # circular dependency.
u'general/display on monitor': True, u'core/display on monitor': True,
u'general/override position': False, u'core/override position': False,
u'images/background color': u'#000000', u'images/background color': u'#000000',
u'media/players': u'webkit', u'media/players': u'webkit',
u'media/override player': QtCore.Qt.Unchecked, u'media/override player': QtCore.Qt.Unchecked,
@ -304,7 +303,7 @@ class Settings(QtCore.QSettings):
# Changed during 1.9.x development. # Changed during 1.9.x development.
(u'bibles/bookname language', u'bibles/book name language', []), (u'bibles/bookname language', u'bibles/book name language', []),
(u'general/enable slide loop', u'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]), (u'general/enable slide loop', u'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]),
(u'songs/ccli number', u'general/ccli number', []), (u'songs/ccli number', u'core/ccli number', []),
(u'media/use phonon', u'', []), (u'media/use phonon', u'', []),
# Changed during 2.1.x development. # Changed during 2.1.x development.
(u'advanced/stylesheet fix', u'', []), (u'advanced/stylesheet fix', u'', []),
@ -315,7 +314,34 @@ class Settings(QtCore.QSettings):
(u'songs/last directory 1', u'songs/last directory import', []), (u'songs/last directory 1', u'songs/last directory import', []),
(u'songusage/last directory 1', u'songusage/last directory export', []), (u'songusage/last directory 1', u'songusage/last directory export', []),
(u'user interface/mainwindow splitter geometry', u'user interface/main window splitter geometry', []), (u'user interface/mainwindow splitter geometry', u'user interface/main window splitter geometry', []),
(u'shortcuts/makeLive', u'shortcuts/make_live', []) (u'shortcuts/makeLive', u'shortcuts/make_live', []),
(u'general/audio repeat list', u'core/audio repeat list', []),
(u'general/auto open', u'core/auto open', []),
(u'general/auto preview', u'core/auto preview', []),
(u'general/audio start paused', u'core/audio start paused', []),
(u'general/auto unblank', u'core/auto unblank', []),
(u'general/blank warning', u'core/blank warning', []),
(u'general/ccli number', u'core/ccli number', []),
(u'general/has run wizard', u'core/has run wizard', []),
(u'general/language', u'core/language', []),
(u'general/last version test', u'core/last version test', []),
(u'general/loop delay', u'core/loop delay', []),
(u'general/recent files', u'core/recent files', []),
(u'general/save prompt', u'core/save prompt', []),
(u'general/screen blank', u'core/screen blank', []),
(u'general/show splash', u'core/show splash', []),
(u'general/songselect password', u'core/songselect password', []),
(u'general/songselect username', u'core/songselect username', []),
(u'general/update check', u'core/update check', []),
(u'general/view mode', u'core/view mode', []),
(u'general/display on monitor', u'core/display on monitor', []),
(u'general/override position', u'core/override position', []),
(u'general/x position', u'core/x position', []),
(u'general/y position', u'core/y position', []),
(u'general/monitor', u'core/monitor', []),
(u'general/height', u'core/height', []),
(u'general/monitor', u'core/monitor', []),
(u'general/width', u'core/width', [])
] ]
@staticmethod @staticmethod

View File

@ -30,6 +30,7 @@
The :mod:`~openlp.core.lib.settingstab` module contains the base SettingsTab class which plugins use for adding their The :mod:`~openlp.core.lib.settingstab` module contains the base SettingsTab class which plugins use for adding their
own tab to the settings dialog. own tab to the settings dialog.
""" """
from __future__ import division
from PyQt4 import QtGui from PyQt4 import QtGui
@ -90,7 +91,7 @@ class SettingsTab(QtGui.QWidget):
QtGui.QWidget.resizeEvent(self, event) QtGui.QWidget.resizeEvent(self, event)
width = self.width() - self.tab_layout.spacing() - \ width = self.width() - self.tab_layout.spacing() - \
self.tab_layout.contentsMargins().left() - self.tab_layout.contentsMargins().right() self.tab_layout.contentsMargins().left() - self.tab_layout.contentsMargins().right()
left_width = min(width - self.right_column.minimumSizeHint().width(), width / 2) left_width = min(width - self.right_column.minimumSizeHint().width(), width // 2)
left_width = max(left_width, self.left_column.minimumSizeHint().width()) left_width = max(left_width, self.left_column.minimumSizeHint().width())
self.left_column.setFixedWidth(left_width) self.left_column.setFixedWidth(left_width)

View File

@ -107,7 +107,6 @@ class UiStrings(object):
self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural') self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural')
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular') self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural') self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
self.OLPV1 = translate('OpenLP.Ui', 'openlp.org 1.x')
self.OLPV2 = translate('OpenLP.Ui', 'OpenLP 2') self.OLPV2 = translate('OpenLP.Ui', 'OpenLP 2')
self.OLPV2x = translate('OpenLP.Ui', 'OpenLP 2.1') self.OLPV2x = translate('OpenLP.Ui', 'OpenLP 2.1')
self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?') self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?')

View File

@ -35,7 +35,7 @@ import os
import platform import platform
import sqlalchemy import sqlalchemy
import BeautifulSoup 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
@ -59,11 +59,6 @@ try:
ENCHANT_VERSION = enchant.__version__ ENCHANT_VERSION = enchant.__version__
except ImportError: except ImportError:
ENCHANT_VERSION = u'-' ENCHANT_VERSION = u'-'
try:
import sqlite
SQLITE_VERSION = sqlite.version
except ImportError:
SQLITE_VERSION = u'-'
try: try:
import mako import mako
MAKO_VERSION = mako.__version__ MAKO_VERSION = mako.__version__
@ -72,11 +67,16 @@ except ImportError:
try: try:
import icu import icu
try: try:
ICU_VERSION = icu.VERSION ICU_VERSION = icu.VERSION
except AttributeError: except AttributeError:
ICU_VERSION = u'OK' ICU_VERSION = u'OK'
except ImportError: except ImportError:
ICU_VERSION = u'-' ICU_VERSION = u'-'
try:
import cherrypy
CHERRYPY_VERSION = cherrypy.__version__
except ImportError:
CHERRYPY_VERSION = u'-'
try: try:
import uno import uno
arg = uno.createUnoStruct(u'com.sun.star.beans.PropertyValue') arg = uno.createUnoStruct(u'com.sun.star.beans.PropertyValue')
@ -149,8 +149,8 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
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 + \
u'PySQLite: %s\n' % SQLITE_VERSION + \
u'Mako: %s\n' % MAKO_VERSION + \ u'Mako: %s\n' % MAKO_VERSION + \
u'CherryPy: %s\n' % CHERRYPY_VERSION + \
u'pyICU: %s\n' % ICU_VERSION + \ u'pyICU: %s\n' % ICU_VERSION + \
u'pyUNO bridge: %s\n' % UNO_VERSION + \ u'pyUNO bridge: %s\n' % UNO_VERSION + \
u'VLC: %s\n' % VLC_VERSION u'VLC: %s\n' % VLC_VERSION

View File

@ -118,7 +118,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
check_directory_exists(os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp')) check_directory_exists(os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp'))
self.noInternetFinishButton.setVisible(False) self.noInternetFinishButton.setVisible(False)
# Check if this is a re-run of the wizard. # Check if this is a re-run of the wizard.
self.hasRunWizard = Settings().value(u'general/has run wizard') self.hasRunWizard = Settings().value(u'core/has run wizard')
# Sort out internet access for downloads # Sort out internet access for downloads
if self.web_access: if self.web_access:
songs = self.config.get(u'songs', u'languages') songs = self.config.get(u'songs', u'languages')
@ -252,7 +252,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
self.application.set_busy_cursor() self.application.set_busy_cursor()
self._performWizard() self._performWizard()
self.application.set_normal_cursor() self.application.set_normal_cursor()
Settings().setValue(u'general/has run wizard', True) Settings().setValue(u'core/has run wizard', True)
self.close() self.close()
def urlGetFile(self, url, fpath): def urlGetFile(self, url, fpath):
@ -459,7 +459,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
self.urlGetFile(u'%s%s' % (self.web, theme), os.path.join(themes_destination, theme)) self.urlGetFile(u'%s%s' % (self.web, theme), os.path.join(themes_destination, theme))
# Set Default Display # Set Default Display
if self.displayComboBox.currentIndex() != -1: if self.displayComboBox.currentIndex() != -1:
Settings().setValue(u'General/monitor', self.displayComboBox.currentIndex()) Settings().setValue(u'core/monitor', self.displayComboBox.currentIndex())
self.screens.set_current_display(self.displayComboBox.currentIndex()) self.screens.set_current_display(self.displayComboBox.currentIndex())
# Set Global Theme # Set Global Theme
if self.themeComboBox.currentIndex() != -1: if self.themeComboBox.currentIndex() != -1:

View File

@ -39,39 +39,39 @@ class Ui_FirstTimeLanguageDialog(object):
""" """
The UI widgets of the language selection dialog. The UI widgets of the language selection dialog.
""" """
def setupUi(self, languageDialog): def setupUi(self, language_dialog):
""" """
Set up the UI. Set up the UI.
""" """
languageDialog.setObjectName(u'languageDialog') language_dialog.setObjectName(u'language_dialog')
languageDialog.resize(300, 50) language_dialog.resize(300, 50)
self.dialogLayout = QtGui.QVBoxLayout(languageDialog) self.dialog_layout = QtGui.QVBoxLayout(language_dialog)
self.dialogLayout.setContentsMargins(8, 8, 8, 8) self.dialog_layout.setContentsMargins(8, 8, 8, 8)
self.dialogLayout.setSpacing(8) self.dialog_layout.setSpacing(8)
self.dialogLayout.setObjectName(u'dialog_layout') self.dialog_layout.setObjectName(u'dialog_layout')
self.infoLabel = QtGui.QLabel(languageDialog) self.info_label = QtGui.QLabel(language_dialog)
self.infoLabel.setObjectName(u'infoLabel') self.info_label.setObjectName(u'info_label')
self.dialogLayout.addWidget(self.infoLabel) self.dialog_layout.addWidget(self.info_label)
self.languageLayout = QtGui.QHBoxLayout() self.language_layout = QtGui.QHBoxLayout()
self.languageLayout.setObjectName(u'languageLayout') self.language_layout.setObjectName(u'language_layout')
self.languageLabel = QtGui.QLabel(languageDialog) self.language_label = QtGui.QLabel(language_dialog)
self.languageLabel.setObjectName(u'languageLabel') self.language_label.setObjectName(u'language_label')
self.languageLayout.addWidget(self.languageLabel) self.language_layout.addWidget(self.language_label)
self.languageComboBox = QtGui.QComboBox(languageDialog) self.language_combo_box = QtGui.QComboBox(language_dialog)
self.languageComboBox.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.language_combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
self.languageComboBox.setObjectName("languageComboBox") self.language_combo_box.setObjectName("language_combo_box")
self.languageLayout.addWidget(self.languageComboBox) self.language_layout.addWidget(self.language_combo_box)
self.dialogLayout.addLayout(self.languageLayout) self.dialog_layout.addLayout(self.language_layout)
self.button_box = create_button_box(languageDialog, u'button_box', [u'cancel', u'ok']) self.button_box = create_button_box(language_dialog, u'button_box', [u'cancel', u'ok'])
self.dialogLayout.addWidget(self.button_box) self.dialog_layout.addWidget(self.button_box)
self.retranslateUi(languageDialog) self.retranslateUi(language_dialog)
self.setMaximumHeight(self.sizeHint().height()) self.setMaximumHeight(self.sizeHint().height())
def retranslateUi(self, languageDialog): def retranslateUi(self, language_dialog):
""" """
Translate the UI on the fly. Translate the UI on the fly.
""" """
self.setWindowTitle(translate('OpenLP.FirstTimeLanguageForm', 'Select Translation')) self.setWindowTitle(translate('OpenLP.FirstTimeLanguageForm', 'Select Translation'))
self.infoLabel.setText( self.info_label.setText(
translate('OpenLP.FirstTimeLanguageForm', 'Choose the translation you\'d like to use in OpenLP.')) translate('OpenLP.FirstTimeLanguageForm', 'Choose the translation you\'d like to use in OpenLP.'))
self.languageLabel.setText(translate('OpenLP.FirstTimeLanguageForm', 'Translation:')) self.language_label.setText(translate('OpenLP.FirstTimeLanguageForm', 'Translation:'))

View File

@ -47,8 +47,8 @@ class FirstTimeLanguageForm(QtGui.QDialog, Ui_FirstTimeLanguageDialog):
QtGui.QDialog.__init__(self, parent) QtGui.QDialog.__init__(self, parent)
self.setupUi(self) self.setupUi(self)
self.qmList = LanguageManager.get_qm_list() self.qmList = LanguageManager.get_qm_list()
self.languageComboBox.addItem(u'Autodetect') self.language_combo_box.addItem(u'Autodetect')
self.languageComboBox.addItems(sorted(self.qmList.keys())) self.language_combo_box.addItems(sorted(self.qmList.keys()))
def exec_(self): def exec_(self):
""" """
@ -61,12 +61,12 @@ class FirstTimeLanguageForm(QtGui.QDialog, Ui_FirstTimeLanguageDialog):
Run when the dialog is OKed. Run when the dialog is OKed.
""" """
# It's the first row so must be Automatic # It's the first row so must be Automatic
if self.languageComboBox.currentIndex() == 0: if self.language_combo_box.currentIndex() == 0:
LanguageManager.auto_language = True LanguageManager.auto_language = True
LanguageManager.set_language(False, False) LanguageManager.set_language(False, False)
else: else:
LanguageManager.auto_language = False LanguageManager.auto_language = False
action = create_action(None, self.languageComboBox.currentText()) action = create_action(None, self.language_combo_box.currentText())
LanguageManager.set_language(action, False) LanguageManager.set_language(action, False)
return QtGui.QDialog.accept(self) return QtGui.QDialog.accept(self)

View File

@ -49,7 +49,7 @@ class GeneralTab(SettingsTab):
self.screens = ScreenList() self.screens = ScreenList()
self.icon_path = u':/icon/openlp-logo-16x16.png' self.icon_path = u':/icon/openlp-logo-16x16.png'
general_translated = translate('OpenLP.GeneralTab', 'General') general_translated = translate('OpenLP.GeneralTab', 'General')
SettingsTab.__init__(self, parent, u'General', general_translated) SettingsTab.__init__(self, parent, u'Core', general_translated)
def setupUi(self): def setupUi(self):
""" """

View File

@ -35,6 +35,7 @@ Some of the code for this form is based on the examples at:
* `http://html5demos.com/two-videos`_ * `http://html5demos.com/two-videos`_
""" """
from __future__ import division
import cgi import cgi
import logging import logging
import sys import sys
@ -207,8 +208,8 @@ class MainDisplay(Display):
painter_image.begin(self.initial_fame) painter_image.begin(self.initial_fame)
painter_image.fillRect(self.initial_fame.rect(), background_color) painter_image.fillRect(self.initial_fame.rect(), background_color)
painter_image.drawImage( painter_image.drawImage(
(self.screen[u'size'].width() - splash_image.width()) / 2, (self.screen[u'size'].width() - splash_image.width()) // 2,
(self.screen[u'size'].height() - splash_image.height()) / 2, (self.screen[u'size'].height() - splash_image.height()) // 2,
splash_image) splash_image)
service_item = ServiceItem() service_item = ServiceItem()
service_item.bg_image_bytes = image_to_byte(self.initial_fame) service_item.bg_image_bytes = image_to_byte(self.initial_fame)
@ -268,7 +269,7 @@ class MainDisplay(Display):
self.resize(self.width(), alert_height) self.resize(self.width(), alert_height)
self.setVisible(True) self.setVisible(True)
if location == AlertLocation.Middle: if location == AlertLocation.Middle:
self.move(self.screen[u'size'].left(), (self.screen[u'size'].height() - alert_height) / 2) self.move(self.screen[u'size'].left(), (self.screen[u'size'].height() - alert_height) // 2)
elif location == AlertLocation.Bottom: elif location == AlertLocation.Bottom:
self.move(self.screen[u'size'].left(), self.screen[u'size'].height() - alert_height) self.move(self.screen[u'size'].left(), self.screen[u'size'].height() - alert_height)
else: else:
@ -357,7 +358,7 @@ class MainDisplay(Display):
# Single screen active # Single screen active
if self.screens.display_count == 1: if self.screens.display_count == 1:
# Only make visible if setting enabled. # Only make visible if setting enabled.
if Settings().value(u'general/display on monitor'): if Settings().value(u'core/display on monitor'):
self.setVisible(True) self.setVisible(True)
else: else:
self.setVisible(True) self.setVisible(True)
@ -405,7 +406,7 @@ class MainDisplay(Display):
self.footer(service_item.foot_text) self.footer(service_item.foot_text)
# if was hidden keep it hidden # if was hidden keep it hidden
if self.hide_mode and self.is_live and not service_item.is_media(): if self.hide_mode and self.is_live and not service_item.is_media():
if Settings().value(u'general/auto unblank'): if Settings().value(u'core/auto unblank'):
Registry().execute(u'slidecontroller_live_unblank') Registry().execute(u'slidecontroller_live_unblank')
else: else:
self.hide_display(self.hide_mode) self.hide_display(self.hide_mode)
@ -427,7 +428,7 @@ class MainDisplay(Display):
log.debug(u'hide_display mode = %d', mode) log.debug(u'hide_display mode = %d', mode)
if self.screens.display_count == 1: if self.screens.display_count == 1:
# Only make visible if setting enabled. # Only make visible if setting enabled.
if not Settings().value(u'general/display on monitor'): if not Settings().value(u'core/display on monitor'):
return return
if mode == HideMode.Screen: if mode == HideMode.Screen:
self.frame.evaluateJavaScript(u'show_blank("desktop");') self.frame.evaluateJavaScript(u'show_blank("desktop");')
@ -450,7 +451,7 @@ class MainDisplay(Display):
log.debug(u'show_display') log.debug(u'show_display')
if self.screens.display_count == 1: if self.screens.display_count == 1:
# Only make visible if setting enabled. # Only make visible if setting enabled.
if not Settings().value(u'general/display on monitor'): if not Settings().value(u'core/display on monitor'):
return return
self.frame.evaluateJavaScript('show_blank("show");') self.frame.evaluateJavaScript('show_blank("show");')
if self.isHidden(): if self.isHidden():

View File

@ -476,7 +476,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.arguments = self.application.args self.arguments = self.application.args
# Set up settings sections for the main application (not for use by plugins). # Set up settings sections for the main application (not for use by plugins).
self.ui_settings_section = u'user interface' self.ui_settings_section = u'user interface'
self.general_settings_section = u'general' self.general_settings_section = u'core'
self.advanced_settings_section = u'advanced' self.advanced_settings_section = u'advanced'
self.shortcuts_settings_section = u'shortcuts' self.shortcuts_settings_section = u'shortcuts'
self.service_manager_settings_section = u'servicemanager' self.service_manager_settings_section = u'servicemanager'
@ -491,7 +491,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.new_data_path = None self.new_data_path = None
self.copy_data = False self.copy_data = False
Settings().set_up_default_values() Settings().set_up_default_values()
Settings().remove_obsolete_settings()
self.service_not_saved = False self.service_not_saved = False
self.about_form = AboutForm(self) self.about_form = AboutForm(self)
self.media_controller = MediaController() self.media_controller = MediaController()
@ -560,7 +559,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
""" """
widget = self.media_tool_box.widget(index) widget = self.media_tool_box.widget(index)
if widget: if widget:
widget.onFocus() widget.on_focus()
def version_notice(self, version): def version_notice(self, version):
""" """

View File

@ -415,7 +415,7 @@ class MediaController(object):
elif not hidden or controller.media_info.is_background or service_item.will_auto_start: elif not hidden or controller.media_info.is_background or service_item.will_auto_start:
autoplay = True autoplay = True
# Unblank on load set # Unblank on load set
elif Settings().value(u'general/auto unblank'): elif Settings().value(u'core/auto unblank'):
autoplay = True autoplay = True
if autoplay: if autoplay:
if not self.media_play(controller): if not self.media_play(controller):
@ -466,8 +466,8 @@ class MediaController(object):
The ServiceItem containing the details to be played. The ServiceItem containing the details to be played.
""" """
used_players = get_media_players()[0] used_players = get_media_players()[0]
if service_item.title != UiStrings().Automatic: if service_item.processor != UiStrings().Automatic:
used_players = [service_item.title.lower()] used_players = [service_item.processor.lower()]
if controller.media_info.file_info.isFile(): if controller.media_info.file_info.isFile():
suffix = u'*.%s' % controller.media_info.file_info.suffix().lower() suffix = u'*.%s' % controller.media_info.file_info.suffix().lower()
for title in used_players: for title in used_players:

View File

@ -273,7 +273,6 @@ class ServiceManagerDialog(object):
Registry().register_function(u'config_screen_changed', self.regenerate_service_Items) Registry().register_function(u'config_screen_changed', self.regenerate_service_Items)
Registry().register_function(u'theme_update_global', self.theme_change) Registry().register_function(u'theme_update_global', self.theme_change)
Registry().register_function(u'mediaitem_suffix_reset', self.reset_supported_suffixes) Registry().register_function(u'mediaitem_suffix_reset', self.reset_supported_suffixes)
Registry().register_function(u'servicemanager_set_item', self.on_set_item)
def drag_enter_event(self, event): def drag_enter_event(self, event):
""" """
@ -296,8 +295,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
Sets up the service manager, toolbars, list view, et al. Sets up the service manager, toolbars, list view, et al.
""" """
QtGui.QWidget.__init__(self, parent) QtGui.QWidget.__init__(self, parent)
self.active = build_icon(QtGui.QImage(u':/media/auto-start_active.png')) self.active = build_icon(u':/media/auto-start_active.png')
self.inactive = build_icon(QtGui.QImage(u':/media/auto-start_inactive.png')) self.inactive = build_icon(u':/media/auto-start_inactive.png')
Registry().register(u'service_manager', self) Registry().register(u'service_manager', self)
self.service_items = [] self.service_items = []
self.suffixes = [] self.suffixes = []
@ -315,6 +314,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
self.layout.setSpacing(0) self.layout.setSpacing(0)
self.layout.setMargin(0) self.layout.setMargin(0)
self.setup_ui(self) self.setup_ui(self)
# Need to use event as called across threads and UI is updated
QtCore.QObject.connect(self, QtCore.SIGNAL(u'servicemanager_set_item'), self.on_set_item)
def set_modified(self, modified=True): def set_modified(self, modified=True):
""" """
@ -714,13 +715,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
else: else:
service_item.set_from_service(item, self.servicePath) service_item.set_from_service(item, self.servicePath)
service_item.validate_item(self.suffixes) service_item.validate_item(self.suffixes)
self.load_item_unique_identifier = 0
if service_item.is_capable(ItemCapabilities.OnLoadUpdate): if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
Registry().execute(u'%s_service_load' % service_item.name.lower(), service_item) new_item = Registry().get(service_item.name).service_load(service_item)
# if the item has been processed if new_item:
if service_item.unique_identifier == self.load_item_unique_identifier: service_item = new_item
service_item.edit_id = int(self.load_item_edit_id)
service_item.temporary_edit = self.load_item_temporary
self.add_service_item(service_item, repaint=False) self.add_service_item(service_item, repaint=False)
delete_file(p_file) delete_file(p_file)
self.main_window.add_recent_file(file_name) self.main_window.add_recent_file(file_name)
@ -993,7 +991,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
def on_set_item(self, message): def on_set_item(self, message):
""" """
Called by a signal to select a specific item. Called by a signal to select a specific item and make it live usually from remote.
""" """
self.set_item(int(message)) self.set_item(int(message))
@ -1259,14 +1257,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
self.repaint_service_list(-1, -1) self.repaint_service_list(-1, -1)
self.application.set_normal_cursor() self.application.set_normal_cursor()
def service_item_update(self, edit_id, unique_identifier, temporary=False):
"""
Triggered from plugins to update service items. Save the values as they will be used as part of the service load
"""
self.load_item_unique_identifier = unique_identifier
self.load_item_edit_id = int(edit_id)
self.load_item_temporary = str_to_bool(temporary)
def replace_service_item(self, newItem): def replace_service_item(self, newItem):
""" """
Using the service item passed replace the one with the same edit id if found. Using the service item passed replace the one with the same edit id if found.
@ -1393,7 +1383,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
item = self.find_service_item()[0] item = self.find_service_item()[0]
if self.service_items[item][u'service_item'].is_capable(ItemCapabilities.CanEdit): if self.service_items[item][u'service_item'].is_capable(ItemCapabilities.CanEdit):
new_item = Registry().get(self.service_items[item][u'service_item'].name). \ new_item = Registry().get(self.service_items[item][u'service_item'].name). \
onRemoteEdit(self.service_items[item][u'service_item'].edit_id) on_remote_edit(self.service_items[item][u'service_item'].edit_id)
if new_item: if new_item:
self.add_service_item(new_item, replace=True) self.add_service_item(new_item, replace=True)

View File

@ -96,6 +96,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
""" """
Process the form saving the settings Process the form saving the settings
""" """
log.debug(u'Processing settings exit')
for tabIndex in range(self.stacked_layout.count()): for tabIndex in range(self.stacked_layout.count()):
self.stacked_layout.widget(tabIndex).save() self.stacked_layout.widget(tabIndex).save()
# if the display of image background are changing we need to regenerate the image cache # if the display of image background are changing we need to regenerate the image cache

View File

@ -360,8 +360,9 @@ class SlideController(DisplayController):
# Signals # Signals
self.preview_list_widget.clicked.connect(self.onSlideSelected) self.preview_list_widget.clicked.connect(self.onSlideSelected)
if self.is_live: if self.is_live:
# Need to use event as called across threads and UI is updated
QtCore.QObject.connect(self, QtCore.SIGNAL(u'slidecontroller_toggle_display'), self.toggle_display)
Registry().register_function(u'slidecontroller_live_spin_delay', self.receive_spin_delay) Registry().register_function(u'slidecontroller_live_spin_delay', self.receive_spin_delay)
Registry().register_function(u'slidecontroller_toggle_display', self.toggle_display)
self.toolbar.set_widget_visible(self.loop_list, False) self.toolbar.set_widget_visible(self.loop_list, False)
self.toolbar.set_widget_visible(self.wide_menu, False) self.toolbar.set_widget_visible(self.wide_menu, False)
else: else:
@ -373,13 +374,16 @@ class SlideController(DisplayController):
else: else:
self.preview_list_widget.addActions([self.nextItem, self.previous_item]) self.preview_list_widget.addActions([self.nextItem, self.previous_item])
Registry().register_function(u'slidecontroller_%s_stop_loop' % self.type_prefix, self.on_stop_loop) Registry().register_function(u'slidecontroller_%s_stop_loop' % self.type_prefix, self.on_stop_loop)
Registry().register_function(u'slidecontroller_%s_next' % self.type_prefix, self.on_slide_selected_next)
Registry().register_function(u'slidecontroller_%s_previous' % self.type_prefix, self.on_slide_selected_previous)
Registry().register_function(u'slidecontroller_%s_change' % self.type_prefix, self.on_slide_change) Registry().register_function(u'slidecontroller_%s_change' % self.type_prefix, self.on_slide_change)
Registry().register_function(u'slidecontroller_%s_set' % self.type_prefix, self.on_slide_selected_index)
Registry().register_function(u'slidecontroller_%s_blank' % self.type_prefix, self.on_slide_blank) Registry().register_function(u'slidecontroller_%s_blank' % self.type_prefix, self.on_slide_blank)
Registry().register_function(u'slidecontroller_%s_unblank' % self.type_prefix, self.on_slide_unblank) Registry().register_function(u'slidecontroller_%s_unblank' % self.type_prefix, self.on_slide_unblank)
Registry().register_function(u'slidecontroller_update_slide_limits', self.update_slide_limits) Registry().register_function(u'slidecontroller_update_slide_limits', self.update_slide_limits)
QtCore.QObject.connect(self, QtCore.SIGNAL(u'slidecontroller_%s_set' % self.type_prefix),
self.on_slide_selected_index)
QtCore.QObject.connect(self, QtCore.SIGNAL(u'slidecontroller_%s_next' % self.type_prefix),
self.on_slide_selected_next)
QtCore.QObject.connect(self, QtCore.SIGNAL(u'slidecontroller_%s_previous' % self.type_prefix),
self.on_slide_selected_previous)
def _slideShortcutActivated(self): def _slideShortcutActivated(self):
""" """
@ -612,7 +616,7 @@ class SlideController(DisplayController):
""" """
Adjusts the value of the ``delay_spin_box`` to the given one. Adjusts the value of the ``delay_spin_box`` to the given one.
""" """
self.delay_spin_box.setValue(Settings().value(u'general/loop delay')) self.delay_spin_box.setValue(Settings().value(u'core/loop delay'))
def update_slide_limits(self): def update_slide_limits(self):
""" """
@ -1229,7 +1233,7 @@ class SlideController(DisplayController):
From the preview display requires the service Item to be editied From the preview display requires the service Item to be editied
""" """
self.song_edit = True self.song_edit = True
new_item = Registry().get(self.service_item.name).onRemoteEdit(self.service_item.edit_id, True) new_item = Registry().get(self.service_item.name).on_remote_edit(self.service_item.edit_id, True)
if new_item: if new_item:
self.add_service_item(new_item) self.add_service_item(new_item)

View File

@ -29,6 +29,8 @@
""" """
The Themes configuration tab The Themes configuration tab
""" """
from __future__ import division
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import Registry, Settings, SettingsTab, UiStrings, translate from openlp.core.lib import Registry, Settings, SettingsTab, UiStrings, translate
@ -90,7 +92,7 @@ class ThemesTab(SettingsTab):
self.global_level_label.setObjectName(u'global_level_label') self.global_level_label.setObjectName(u'global_level_label')
self.level_layout.addRow(self.global_level_radio_button, self.global_level_label) self.level_layout.addRow(self.global_level_radio_button, self.global_level_label)
label_top_margin = (self.song_level_radio_button.sizeHint().height() - label_top_margin = (self.song_level_radio_button.sizeHint().height() -
self.song_level_label.sizeHint().height()) / 2 self.song_level_label.sizeHint().height()) // 2
for label in [self.song_level_label, self.service_level_label, self.global_level_label]: for label in [self.song_level_label, self.service_level_label, self.global_level_label]:
rect = label.rect() rect = label.rect()
rect.setTop(rect.top() + label_top_margin) rect.setTop(rect.top() + label_top_margin)

View File

@ -58,8 +58,6 @@ class WizardStrings(object):
ImportingType = translate('OpenLP.Ui', 'Importing "%s"...') ImportingType = translate('OpenLP.Ui', 'Importing "%s"...')
ImportSelect = translate('OpenLP.Ui', 'Select Import Source') ImportSelect = translate('OpenLP.Ui', 'Select Import Source')
ImportSelectLong = translate('OpenLP.Ui', 'Select the import format and the location to import from.') ImportSelectLong = translate('OpenLP.Ui', 'Select the import format and the location to import from.')
NoSqlite = translate('OpenLP.Ui', 'The openlp.org 1.x importer has been disabled due to a missing Python module. '
'If you want to use this importer, you will need to install the "python-sqlite" module.')
OpenTypeFile = translate('OpenLP.Ui', 'Open %s File') OpenTypeFile = translate('OpenLP.Ui', 'Open %s File')
OpenTypeFolder = translate('OpenLP.Ui', 'Open %s Folder') OpenTypeFolder = translate('OpenLP.Ui', 'Open %s Folder')
PercentSymbolFormat = translate('OpenLP.Ui', '%p%') PercentSymbolFormat = translate('OpenLP.Ui', '%p%')

View File

@ -177,9 +177,9 @@ def check_latest_version(current_version):
version_string = current_version[u'full'] version_string = current_version[u'full']
# set to prod in the distribution config file. # set to prod in the distribution config file.
settings = Settings() settings = Settings()
settings.beginGroup(u'general') settings.beginGroup(u'core')
last_test = settings.value(u'last version test') last_test = settings.value(u'last version test')
this_test = datetime.now().date() this_test = unicode(datetime.now().date())
settings.setValue(u'last version test', this_test) settings.setValue(u'last version test', this_test)
settings.endGroup() settings.endGroup()
# Tell the main window whether there will ever be data to display # Tell the main window whether there will ever be data to display
@ -239,8 +239,7 @@ def get_images_filter():
global IMAGES_FILTER global IMAGES_FILTER
if not IMAGES_FILTER: if not IMAGES_FILTER:
log.debug(u'Generating images filter.') log.debug(u'Generating images filter.')
formats = [unicode(fmt) formats = map(unicode, QtGui.QImageReader.supportedImageFormats())
for fmt in QtGui.QImageReader.supportedImageFormats()]
visible_formats = u'(*.%s)' % u'; *.'.join(formats) visible_formats = u'(*.%s)' % u'; *.'.join(formats)
actual_formats = u'(*.%s)' % u' *.'.join(formats) actual_formats = u'(*.%s)' % u' *.'.join(formats)
IMAGES_FILTER = u'%s %s %s' % (translate('OpenLP', 'Image Files'), visible_formats, actual_formats) IMAGES_FILTER = u'%s %s %s' % (translate('OpenLP', 'Image Files'), visible_formats, actual_formats)

View File

@ -98,7 +98,7 @@ class LanguageManager(object):
""" """
Retrieve a saved language to use from settings Retrieve a saved language to use from settings
""" """
language = Settings().value(u'general/language') language = Settings().value(u'core/language')
language = str(language) language = str(language)
log.info(u'Language file: \'%s\' Loaded from conf file' % language) log.info(u'Language file: \'%s\' Loaded from conf file' % language)
if re.match(r'[[].*[]]', language): if re.match(r'[[].*[]]', language):
@ -128,7 +128,7 @@ class LanguageManager(object):
language = unicode(qm_list[action_name]) language = unicode(qm_list[action_name])
if LanguageManager.auto_language: if LanguageManager.auto_language:
language = u'[%s]' % language language = u'[%s]' % language
Settings().setValue(u'general/language', language) Settings().setValue(u'core/language', language)
log.info(u'Language file: \'%s\' written to conf file' % language) log.info(u'Language file: \'%s\' written to conf file' % language)
if message: if message:
QtGui.QMessageBox.information(None, QtGui.QMessageBox.information(None,

View File

@ -27,6 +27,5 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`alerts` module provides the Alerts plugin for producing impromptu The :mod:`alerts` module provides the Alerts plugin for producing impromptu on-screen announcements during a service.
on-screen announcements during a service.
""" """

View File

@ -115,13 +115,13 @@ HTML = """
""" """
__default_settings__ = { __default_settings__ = {
u'alerts/font face': QtGui.QFont().family(), u'alerts/font face': QtGui.QFont().family(),
u'alerts/font size': 40, u'alerts/font size': 40,
u'alerts/db type': u'sqlite', u'alerts/db type': u'sqlite',
u'alerts/location': AlertLocation.Bottom, u'alerts/location': AlertLocation.Bottom,
u'alerts/background color': u'#660000', u'alerts/background color': u'#660000',
u'alerts/font color': u'#ffffff', u'alerts/font color': u'#ffffff',
u'alerts/timeout': 5 u'alerts/timeout': 5
} }
@ -139,12 +139,10 @@ class AlertsPlugin(Plugin):
def add_tools_menu_item(self, tools_menu): def add_tools_menu_item(self, tools_menu):
""" """
Give the alerts plugin the opportunity to add items to the Give the alerts plugin the opportunity to add items to the **Tools** menu.
**Tools** menu.
``tools_menu`` ``tools_menu``
The actual **Tools** menu item, so that your actions can The actual **Tools** menu item, so that your actions can use it as their parent.
use it as their parent.
""" """
log.info(u'add tools menu') log.info(u'add tools menu')
self.tools_alert_item = create_action(tools_menu, u'toolsAlertItem', self.tools_alert_item = create_action(tools_menu, u'toolsAlertItem',

View File

@ -27,20 +27,16 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
Forms in OpenLP are made up of two classes. One class holds all the graphical Forms in OpenLP are made up of two classes. One class holds all the graphical elements, like buttons and lists, and the
elements, like buttons and lists, and the other class holds all the functional other class holds all the functional code, like slots and loading and saving.
code, like slots and loading and saving.
The first class, commonly known as the **Dialog** class, is typically named The first class, commonly known as the **Dialog** class, is typically named ``Ui_<name>Dialog``. It is a slightly
``Ui_<name>Dialog``. It is a slightly modified version of the class that the modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings.
converting most strings from "" to u'' and using OpenLP's ``translate()``
function for translating strings.
The second class, commonly known as the **Form** class, is typically named The second class, commonly known as the **Form** class, is typically named ``<name>Form``. This class is the one which
``<name>Form``. This class is the one which is instantiated and used. It uses is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned
dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class above, like so::
mentioned above, like so::
class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog): class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
@ -48,9 +44,8 @@ mentioned above, like so::
QtGui.QDialog.__init__(self, parent) QtGui.QDialog.__init__(self, parent)
self.setupUi(self) self.setupUi(self)
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping This allows OpenLP to use ``self.object`` for all the GUI elements while keeping them separate from the functionality,
them separate from the functionality, so that it is easier to recreate the GUI so that it is easier to recreate the GUI from the .ui files later if necessary.
from the .ui files later if necessary.
""" """
from alertform import AlertForm from alertform import AlertForm

View File

@ -71,13 +71,13 @@ class Ui_AlertDialog(object):
self.save_button.setObjectName(u'save_button') self.save_button.setObjectName(u'save_button')
self.manage_button_layout.addWidget(self.save_button) self.manage_button_layout.addWidget(self.save_button)
self.delete_button = create_button(alert_dialog, u'delete_button', role=u'delete', enabled=False, self.delete_button = create_button(alert_dialog, u'delete_button', role=u'delete', enabled=False,
click=alert_dialog.onDeleteButtonClicked) click=alert_dialog.on_delete_button_clicked)
self.manage_button_layout.addWidget(self.delete_button) self.manage_button_layout.addWidget(self.delete_button)
self.manage_button_layout.addStretch() self.manage_button_layout.addStretch()
self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1) self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1)
displayIcon = build_icon(u':/general/general_live.png') display_icon = build_icon(u':/general/general_live.png')
self.display_button = create_button(alert_dialog, u'display_button', icon=displayIcon, enabled=False) self.display_button = create_button(alert_dialog, u'display_button', icon=display_icon, enabled=False)
self.display_close_button = create_button(alert_dialog, u'display_close_button', icon=displayIcon, self.display_close_button = create_button(alert_dialog, u'display_close_button', icon=display_icon,
enabled=False) enabled=False)
self.button_box = create_button_box(alert_dialog, u'button_box', [u'close'], self.button_box = create_button_box(alert_dialog, u'button_box', [u'close'],
[self.display_button, self.display_close_button]) [self.display_button, self.display_close_button])

View File

@ -93,7 +93,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
if self.trigger_alert(self.alert_text_edit.text()): if self.trigger_alert(self.alert_text_edit.text()):
self.close() self.close()
def onDeleteButtonClicked(self): def on_delete_button_clicked(self):
""" """
Deletes the selected item. Deletes the selected item.
""" """
@ -160,8 +160,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
def on_single_click(self): def on_single_click(self):
""" """
List item has been single clicked to add it to the edit field so it can List item has been single clicked to add it to the edit field so it can be changed.
be changed.
""" """
item = self.alert_list_widget.selectedIndexes()[0] item = self.alert_list_widget.selectedIndexes()[0]
bitem = self.alert_list_widget.item(item.row()) bitem = self.alert_list_widget.item(item.row())
@ -186,7 +185,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
translate('AlertsPlugin.AlertForm', 'No Parameter Found'), translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
translate('AlertsPlugin.AlertForm', 'You have not entered a parameter to be replaced.\n' translate('AlertsPlugin.AlertForm', 'You have not entered a parameter to be replaced.\n'
'Do you want to continue anyway?'), 'Do you want to continue anyway?'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
self.parameter_edit.setFocus() self.parameter_edit.setFocus()
return False return False
# The ParameterEdit field is not empty, but we have not found '<>' # The ParameterEdit field is not empty, but we have not found '<>'
@ -195,7 +194,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
translate('AlertsPlugin.AlertForm', 'No Placeholder Found'), translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
translate('AlertsPlugin.AlertForm', 'The alert text does not contain \'<>\'.\n' translate('AlertsPlugin.AlertForm', 'The alert text does not contain \'<>\'.\n'
'Do you want to continue anyway?'), 'Do you want to continue anyway?'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
self.parameter_edit.setFocus() self.parameter_edit.setFocus()
return False return False
text = text.replace(u'<>', self.parameter_edit.text()) text = text.replace(u'<>', self.parameter_edit.text())
@ -204,8 +203,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
def on_current_row_changed(self, row): def on_current_row_changed(self, row):
""" """
Called when the *alert_list_widget*'s current row has been changed. This Called when the *alert_list_widget*'s current row has been changed. This enables or disables buttons which
enables or disables buttons which require an item to act on. require an item to act on.
``row`` ``row``
The row (int). If there is no current row, the value is -1. The row (int). If there is no current row, the value is -1.

View File

@ -27,8 +27,8 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of the plugin which manages storing and
the plugin which manages storing and displaying of alerts. displaying of alerts.
""" """
import logging import logging
@ -49,22 +49,23 @@ class AlertsManager(QtCore.QObject):
def __init__(self, parent): def __init__(self, parent):
QtCore.QObject.__init__(self, parent) QtCore.QObject.__init__(self, parent)
Registry().register(u'alerts_manager', self)
self.timer_id = 0 self.timer_id = 0
self.alert_list = [] self.alert_list = []
Registry().register_function(u'live_display_active', self.generate_alert) Registry().register_function(u'live_display_active', self.generate_alert)
Registry().register_function(u'alerts_text', self.alert_text) Registry().register_function(u'alerts_text', self.alert_text)
QtCore.QObject.connect(self, QtCore.SIGNAL(u'alerts_text'), self.alert_text)
def alert_text(self, message): def alert_text(self, message):
""" """
Called via a alerts_text event. Message is single element array Called via a alerts_text event. Message is single element array containing text.
containing text
""" """
if message: if message:
self.display_alert(message[0]) self.display_alert(message[0])
def display_alert(self, text=u''): def display_alert(self, text=u''):
""" """
Called from the Alert Tab to display an alert Called from the Alert Tab to display an alert.
``text`` ``text``
display text display text
@ -81,7 +82,7 @@ class AlertsManager(QtCore.QObject):
def generate_alert(self): def generate_alert(self):
""" """
Format and request the Alert and start the timer Format and request the Alert and start the timer.
""" """
log.debug(u'Generate Alert called') log.debug(u'Generate Alert called')
if not self.alert_list: if not self.alert_list:
@ -95,8 +96,7 @@ class AlertsManager(QtCore.QObject):
def timerEvent(self, event): def timerEvent(self, event):
""" """
Time has finished so if our time then request the next Alert Time has finished so if our time then request the next Alert if there is one and reset the timer.
if there is one and reset the timer.
``event`` ``event``
the QT event that has been triggered. the QT event that has been triggered.

View File

@ -27,8 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`db` module provides the database and schema that is the backend for The :mod:`db` module provides the database and schema that is the backend for the Alerts plugin.
the Alerts plugin
""" """
from sqlalchemy import Column, Table, types from sqlalchemy import Column, Table, types
@ -36,12 +35,14 @@ from sqlalchemy.orm import mapper
from openlp.core.lib.db import BaseModel, init_db from openlp.core.lib.db import BaseModel, init_db
class AlertItem(BaseModel): class AlertItem(BaseModel):
""" """
AlertItem model AlertItem model
""" """
pass pass
def init_schema(url): def init_schema(url):
""" """
Setup the alerts database connection and initialise the database schema Setup the alerts database connection and initialise the database schema

View File

@ -27,6 +27,5 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`bibles` module provides the Bible plugin to enable OpenLP to display The :mod:`bibles` module provides the Bible plugin to enable OpenLP to display scripture.
scripture.
""" """

View File

@ -43,25 +43,25 @@ log = logging.getLogger(__name__)
__default_settings__ = { __default_settings__ = {
u'bibles/db type': u'sqlite', u'bibles/db type': u'sqlite',
u'bibles/last search type': BibleSearch.Reference, u'bibles/last search type': BibleSearch.Reference,
u'bibles/verse layout style': LayoutStyle.VersePerSlide, u'bibles/verse layout style': LayoutStyle.VersePerSlide,
u'bibles/book name language': LanguageSelection.Bible, u'bibles/book name language': LanguageSelection.Bible,
u'bibles/display brackets': DisplayStyle.NoBrackets, u'bibles/display brackets': DisplayStyle.NoBrackets,
u'bibles/display new chapter': False, u'bibles/display new chapter': False,
u'bibles/second bibles': True, u'bibles/second bibles': True,
u'bibles/advanced bible': u'', u'bibles/advanced bible': u'',
u'bibles/quick bible': u'', u'bibles/quick bible': u'',
u'bibles/proxy name': u'', u'bibles/proxy name': u'',
u'bibles/proxy address': u'', u'bibles/proxy address': u'',
u'bibles/proxy username': u'', u'bibles/proxy username': u'',
u'bibles/proxy password': u'', u'bibles/proxy password': u'',
u'bibles/bible theme': u'', u'bibles/bible theme': u'',
u'bibles/verse separator': u'', u'bibles/verse separator': u'',
u'bibles/range separator': u'', u'bibles/range separator': u'',
u'bibles/list separator': u'', u'bibles/list separator': u'',
u'bibles/end separator': u'', u'bibles/end separator': u'',
u'bibles/last directory import': u'' u'bibles/last directory import': u''
} }
@ -124,18 +124,15 @@ class BiblePlugin(Plugin):
def add_export_menu_Item(self, export_menu): def add_export_menu_Item(self, export_menu):
self.export_bible_item = create_action(export_menu, u'exportBibleItem', self.export_bible_item = create_action(export_menu, u'exportBibleItem',
text=translate('BiblesPlugin', '&Bible'), text=translate('BiblesPlugin', '&Bible'), visible=False)
visible=False)
export_menu.addAction(self.export_bible_item) export_menu.addAction(self.export_bible_item)
def add_tools_menu_item(self, tools_menu): def add_tools_menu_item(self, tools_menu):
""" """
Give the bible plugin the opportunity to add items to the Give the bible plugin the opportunity to add items to the **Tools** menu.
**Tools** menu.
``tools_menu`` ``tools_menu``
The actual **Tools** menu item, so that your actions can The actual **Tools** menu item, so that your actions can use it as their parent.
use it as their parent.
""" """
log.debug(u'add tools menu') log.debug(u'add tools menu')
self.tools_upgrade_item = create_action(tools_menu, u'toolsUpgradeItem', self.tools_upgrade_item = create_action(tools_menu, u'toolsUpgradeItem',
@ -166,25 +163,23 @@ class BiblePlugin(Plugin):
def uses_theme(self, theme): def uses_theme(self, theme):
""" """
Called to find out if the bible plugin is currently using a theme. Called to find out if the bible plugin is currently using a theme. Returns ``True`` if the theme is being used,
Returns ``True`` if the theme is being used, otherwise returns otherwise returns ``False``.
``False``.
""" """
return unicode(self.settings_tab.bible_theme) == theme return unicode(self.settings_tab.bible_theme) == theme
def rename_theme(self, oldTheme, newTheme): def rename_theme(self, old_theme, new_theme):
""" """
Rename the theme the bible plugin is using making the plugin use the Rename the theme the bible plugin is using making the plugin use the
new name. new name.
``oldTheme`` ``old_theme``
The name of the theme the plugin should stop using. Unused for The name of the theme the plugin should stop using. Unused for this particular plugin.
this particular plugin.
``newTheme`` ``new_theme``
The new name the plugin should now use. The new name the plugin should now use.
""" """
self.settings_tab.bible_theme = newTheme self.settings_tab.bible_theme = new_theme
self.settings_tab.save() self.settings_tab.save()
def set_plugin_text_strings(self): def set_plugin_text_strings(self):

View File

@ -28,30 +28,25 @@
############################################################################### ###############################################################################
""" """
Forms in OpenLP are made up of two classes. One class holds all the graphical Forms in OpenLP are made up of two classes. One class holds all the graphical elements, like buttons and lists, and the
elements, like buttons and lists, and the other class holds all the functional other class holds all the functional code, like slots and loading and saving.
code, like slots and loading and saving.
The first class, commonly known as the **Dialog** class, is typically named The first class, commonly known as the **Dialog** class, is typically named ``Ui_<name>Dialog``. It is a slightly
``Ui_<name>Dialog``. It is a slightly modified version of the class that the modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings.
converting most strings from "" to u'' and using OpenLP's ``translate()``
function for translating strings.
The second class, commonly known as the **Form** class, is typically named The second class, commonly known as the **Form** class, is typically named ``<name>Form``. This class is the one which
``<name>Form``. This class is the one which is instantiated and used. It uses is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned
dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class above, like so::
mentioned above, like so::
class BibleImportForm(QtGui.QWizard, Ui_BibleImportWizard): class BibleImportForm(QtGui.QWizard, Ui_BibleImportWizard):
def __init__(self, parent, manager, bibleplugin): def __init__(self, parent, manager, bible_plugin):
QtGui.QWizard.__init__(self, parent) QtGui.QWizard.__init__(self, parent)
self.setupUi(self) self.setupUi(self)
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping This allows OpenLP to use ``self.object`` for all the GUI elements while keeping them separate from the functionality,
them separate from the functionality, so that it is easier to recreate the GUI so that it is easier to recreate the GUI from the .ui files later if necessary.
from the .ui files later if necessary.
""" """
from booknameform import BookNameForm from booknameform import BookNameForm
from languageform import LanguageForm from languageform import LanguageForm
@ -59,5 +54,4 @@ from bibleimportform import BibleImportForm
from bibleupgradeform import BibleUpgradeForm from bibleupgradeform import BibleUpgradeForm
from editbibleform import EditBibleForm from editbibleform import EditBibleForm
__all__ = [u'BookNameForm', u'LanguageForm', u'BibleImportForm', __all__ = [u'BookNameForm', u'LanguageForm', u'BibleImportForm', u'BibleUpgradeForm', u'EditBibleForm']
u'BibleUpgradeForm', u'EditBibleForm']

View File

@ -58,12 +58,12 @@ class WebDownload(object):
class BibleImportForm(OpenLPWizard): class BibleImportForm(OpenLPWizard):
""" """
This is the Bible Import Wizard, which allows easy importing of Bibles This is the Bible Import Wizard, which allows easy importing of Bibles into OpenLP from other formats like OSIS,
into OpenLP from other formats like OSIS, CSV and OpenSong. CSV and OpenSong.
""" """
log.info(u'BibleImportForm loaded') log.info(u'BibleImportForm loaded')
def __init__(self, parent, manager, bibleplugin): def __init__(self, parent, manager, bible_plugin):
""" """
Instantiate the wizard, and run any extra setup we need to. Instantiate the wizard, and run any extra setup we need to.
@ -73,12 +73,12 @@ class BibleImportForm(OpenLPWizard):
``manager`` ``manager``
The Bible manager. The Bible manager.
``bibleplugin`` ``bible_plugin``
The Bible plugin. The Bible plugin.
""" """
self.manager = manager self.manager = manager
self.web_bible_list = {} self.web_bible_list = {}
OpenLPWizard.__init__(self, parent, bibleplugin, u'bibleImportWizard', u':/wizards/wizard_importbible.bmp') OpenLPWizard.__init__(self, parent, bible_plugin, u'bibleImportWizard', u':/wizards/wizard_importbible.bmp')
def setupUi(self, image): def setupUi(self, image):
""" """
@ -94,19 +94,11 @@ class BibleImportForm(OpenLPWizard):
button. button.
""" """
self.selectStack.setCurrentIndex(index) self.selectStack.setCurrentIndex(index)
next_button = self.button(QtGui.QWizard.NextButton)
next_button.setEnabled(BibleFormat.get_availability(index))
def custom_init(self): def custom_init(self):
""" """
Perform any custom initialisation for bible importing. Perform any custom initialisation for bible importing.
""" """
if BibleFormat.get_availability(BibleFormat.OpenLP1):
self.openlp1DisabledLabel.hide()
else:
self.openlp1FileLabel.hide()
self.openlp1FileEdit.hide()
self.openlp1BrowseButton.hide()
self.manager.set_process_dialog(self) self.manager.set_process_dialog(self)
self.loadWebBibles() self.loadWebBibles()
self.restart() self.restart()
@ -121,7 +113,6 @@ class BibleImportForm(OpenLPWizard):
self.csvBooksButton.clicked.connect(self.onCsvBooksBrowseButtonClicked) self.csvBooksButton.clicked.connect(self.onCsvBooksBrowseButtonClicked)
self.csvVersesButton.clicked.connect(self.onCsvVersesBrowseButtonClicked) self.csvVersesButton.clicked.connect(self.onCsvVersesBrowseButtonClicked)
self.openSongBrowseButton.clicked.connect(self.onOpenSongBrowseButtonClicked) self.openSongBrowseButton.clicked.connect(self.onOpenSongBrowseButtonClicked)
self.openlp1BrowseButton.clicked.connect(self.onOpenlp1BrowseButtonClicked)
def add_custom_pages(self): def add_custom_pages(self):
""" """
@ -137,7 +128,7 @@ class BibleImportForm(OpenLPWizard):
self.formatLabel = QtGui.QLabel(self.selectPage) self.formatLabel = QtGui.QLabel(self.selectPage)
self.formatLabel.setObjectName(u'FormatLabel') self.formatLabel.setObjectName(u'FormatLabel')
self.formatComboBox = QtGui.QComboBox(self.selectPage) self.formatComboBox = QtGui.QComboBox(self.selectPage)
self.formatComboBox.addItems([u'', u'', u'', u'', u'']) self.formatComboBox.addItems([u'', u'', u'', u''])
self.formatComboBox.setObjectName(u'FormatComboBox') self.formatComboBox.setObjectName(u'FormatComboBox')
self.formatLayout.addRow(self.formatLabel, self.formatComboBox) self.formatLayout.addRow(self.formatLabel, self.formatComboBox)
self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
@ -259,29 +250,6 @@ class BibleImportForm(OpenLPWizard):
self.webProxyLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.webPasswordEdit) self.webProxyLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.webPasswordEdit)
self.webTabWidget.addTab(self.webProxyTab, u'') self.webTabWidget.addTab(self.webProxyTab, u'')
self.selectStack.addWidget(self.webTabWidget) self.selectStack.addWidget(self.webTabWidget)
self.openlp1Widget = QtGui.QWidget(self.selectPage)
self.openlp1Widget.setObjectName(u'Openlp1Widget')
self.openlp1Layout = QtGui.QFormLayout(self.openlp1Widget)
self.openlp1Layout.setMargin(0)
self.openlp1Layout.setObjectName(u'Openlp1Layout')
self.openlp1FileLabel = QtGui.QLabel(self.openlp1Widget)
self.openlp1FileLabel.setObjectName(u'Openlp1FileLabel')
self.openlp1FileLayout = QtGui.QHBoxLayout()
self.openlp1FileLayout.setObjectName(u'Openlp1FileLayout')
self.openlp1FileEdit = QtGui.QLineEdit(self.openlp1Widget)
self.openlp1FileEdit.setObjectName(u'Openlp1FileEdit')
self.openlp1FileLayout.addWidget(self.openlp1FileEdit)
self.openlp1BrowseButton = QtGui.QToolButton(self.openlp1Widget)
self.openlp1BrowseButton.setIcon(self.open_icon)
self.openlp1BrowseButton.setObjectName(u'Openlp1BrowseButton')
self.openlp1FileLayout.addWidget(self.openlp1BrowseButton)
self.openlp1Layout.addRow(self.openlp1FileLabel, self.openlp1FileLayout)
self.openlp1DisabledLabel = QtGui.QLabel(self.openlp1Widget)
self.openlp1DisabledLabel.setWordWrap(True)
self.openlp1DisabledLabel.setObjectName(u'Openlp1DisabledLabel')
self.openlp1Layout.addRow(self.openlp1DisabledLabel)
self.openlp1Layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer)
self.selectStack.addWidget(self.openlp1Widget)
self.selectPageLayout.addLayout(self.selectStack) self.selectPageLayout.addLayout(self.selectStack)
self.addPage(self.selectPage) self.addPage(self.selectPage)
# License Page # License Page
@ -330,8 +298,6 @@ class BibleImportForm(OpenLPWizard):
self.formatComboBox.setItemText(BibleFormat.OpenSong, WizardStrings.OS) self.formatComboBox.setItemText(BibleFormat.OpenSong, WizardStrings.OS)
self.formatComboBox.setItemText(BibleFormat.WebDownload, self.formatComboBox.setItemText(BibleFormat.WebDownload,
translate('BiblesPlugin.ImportWizardForm', 'Web Download')) translate('BiblesPlugin.ImportWizardForm', 'Web Download'))
self.formatComboBox.setItemText(BibleFormat.OpenLP1, UiStrings().OLPV1)
self.openlp1FileLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
self.osisFileLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.osisFileLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
self.csvBooksLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Books file:')) self.csvBooksLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Books file:'))
self.csvVersesLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Verses file:')) self.csvVersesLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Verses file:'))
@ -364,14 +330,12 @@ class BibleImportForm(OpenLPWizard):
'Please wait while your Bible is imported.')) 'Please wait while your Bible is imported.'))
self.progress_label.setText(WizardStrings.Ready) self.progress_label.setText(WizardStrings.Ready)
self.progress_bar.setFormat(u'%p%') self.progress_bar.setFormat(u'%p%')
self.openlp1DisabledLabel.setText(WizardStrings.NoSqlite)
# Align all QFormLayouts towards each other. # Align all QFormLayouts towards each other.
labelWidth = max(self.formatLabel.minimumSizeHint().width(), labelWidth = max(self.formatLabel.minimumSizeHint().width(),
self.osisFileLabel.minimumSizeHint().width(), self.osisFileLabel.minimumSizeHint().width(),
self.csvBooksLabel.minimumSizeHint().width(), self.csvBooksLabel.minimumSizeHint().width(),
self.csvVersesLabel.minimumSizeHint().width(), self.csvVersesLabel.minimumSizeHint().width(),
self.openSongFileLabel.minimumSizeHint().width(), self.openSongFileLabel.minimumSizeHint().width())
self.openlp1FileLabel.minimumSizeHint().width())
self.spacer.changeSize(labelWidth, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) self.spacer.changeSize(labelWidth, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
def validateCurrentPage(self): def validateCurrentPage(self):
@ -406,11 +370,6 @@ class BibleImportForm(OpenLPWizard):
elif self.field(u'source_format') == BibleFormat.WebDownload: elif self.field(u'source_format') == BibleFormat.WebDownload:
self.versionNameEdit.setText(self.webTranslationComboBox.currentText()) self.versionNameEdit.setText(self.webTranslationComboBox.currentText())
return True return True
elif self.field(u'source_format') == BibleFormat.OpenLP1:
if not self.field(u'openlp1_location'):
critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % UiStrings().OLPV1)
self.openlp1FileEdit.setFocus()
return False
return True return True
elif self.currentPage() == self.licenseDetailsPage: elif self.currentPage() == self.licenseDetailsPage:
license_version = self.field(u'license_version') license_version = self.field(u'license_version')
@ -484,13 +443,6 @@ class BibleImportForm(OpenLPWizard):
""" """
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.openSongFileEdit, u'last directory import') self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.openSongFileEdit, u'last directory import')
def onOpenlp1BrowseButtonClicked(self):
"""
Show the file open dialog for the openlp.org 1.x file.
"""
self.get_file_name(WizardStrings.OpenTypeFile % UiStrings().OLPV1, self.openlp1FileEdit, u'last directory import',
u'%s (*.bible)' % translate('BiblesPlugin.ImportWizardForm', 'openlp.org 1.x Bible Files'))
def register_fields(self): def register_fields(self):
""" """
Register the bible import wizard fields. Register the bible import wizard fields.
@ -505,7 +457,6 @@ class BibleImportForm(OpenLPWizard):
self.selectPage.registerField(u'proxy_server', self.webServerEdit) self.selectPage.registerField(u'proxy_server', self.webServerEdit)
self.selectPage.registerField(u'proxy_username', self.webUserEdit) self.selectPage.registerField(u'proxy_username', self.webUserEdit)
self.selectPage.registerField(u'proxy_password', self.webPasswordEdit) self.selectPage.registerField(u'proxy_password', self.webPasswordEdit)
self.selectPage.registerField(u'openlp1_location', self.openlp1FileEdit)
self.licenseDetailsPage.registerField(u'license_version', self.versionNameEdit) self.licenseDetailsPage.registerField(u'license_version', self.versionNameEdit)
self.licenseDetailsPage.registerField(u'license_copyright', self.copyrightEdit) self.licenseDetailsPage.registerField(u'license_copyright', self.copyrightEdit)
self.licenseDetailsPage.registerField(u'license_permissions', self.permissionsEdit) self.licenseDetailsPage.registerField(u'license_permissions', self.permissionsEdit)
@ -529,7 +480,6 @@ class BibleImportForm(OpenLPWizard):
self.setField(u'proxy_server', settings.value(u'proxy address')) self.setField(u'proxy_server', settings.value(u'proxy address'))
self.setField(u'proxy_username', settings.value(u'proxy username')) self.setField(u'proxy_username', settings.value(u'proxy username'))
self.setField(u'proxy_password', settings.value(u'proxy password')) self.setField(u'proxy_password', settings.value(u'proxy password'))
self.setField(u'openlp1_location', '')
self.setField(u'license_version', self.versionNameEdit.text()) self.setField(u'license_version', self.versionNameEdit.text())
self.setField(u'license_copyright', self.copyrightEdit.text()) self.setField(u'license_copyright', self.copyrightEdit.text())
self.setField(u'license_permissions', self.permissionsEdit.text()) self.setField(u'license_permissions', self.permissionsEdit.text())
@ -561,7 +511,7 @@ class BibleImportForm(OpenLPWizard):
name = bible[u'abbreviation'] name = bible[u'abbreviation']
self.web_bible_list[download_type][version] = name.strip() self.web_bible_list[download_type][version] = name.strip()
def preWizard(self): def pre_wizard(self):
""" """
Prepare the UI for the import. Prepare the UI for the import.
""" """
@ -615,12 +565,6 @@ class BibleImportForm(OpenLPWizard):
proxy_username=self.field(u'proxy_username'), proxy_username=self.field(u'proxy_username'),
proxy_password=self.field(u'proxy_password') proxy_password=self.field(u'proxy_password')
) )
elif bible_type == BibleFormat.OpenLP1:
# Import an openlp.org 1.x bible.
importer = self.manager.import_bible(BibleFormat.OpenLP1,
name=license_version,
filename=self.field(u'openlp1_location')
)
if importer.do_import(license_version): if importer.do_import(license_version):
self.manager.save_meta_data(license_version, license_version, self.manager.save_meta_data(license_version, license_version,
license_copyright, license_permissions) license_copyright, license_permissions)

View File

@ -48,8 +48,8 @@ log = logging.getLogger(__name__)
class BibleUpgradeForm(OpenLPWizard): class BibleUpgradeForm(OpenLPWizard):
""" """
This is the Bible Upgrade Wizard, which allows easy importing of Bibles This is the Bible Upgrade Wizard, which allows easy importing of Bibles into OpenLP from older OpenLP2 database
into OpenLP from older OpenLP2 database versions. versions.
""" """
log.info(u'BibleUpgradeForm loaded') log.info(u'BibleUpgradeForm loaded')
@ -63,7 +63,7 @@ class BibleUpgradeForm(OpenLPWizard):
``manager`` ``manager``
The Bible manager. The Bible manager.
``bibleplugin`` ``bible_plugin``
The Bible plugin. The Bible plugin.
""" """
self.manager = manager self.manager = manager
@ -74,7 +74,7 @@ class BibleUpgradeForm(OpenLPWizard):
self.temp_dir = os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp') self.temp_dir = os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp')
self.files = self.manager.old_bible_databases self.files = self.manager.old_bible_databases
self.success = {} self.success = {}
self.newbibles = {} self.new_bibles = {}
OpenLPWizard.__init__(self, parent, bible_plugin, u'bibleUpgradeWizard', u':/wizards/wizard_importbible.bmp') OpenLPWizard.__init__(self, parent, bible_plugin, u'bibleUpgradeWizard', u':/wizards/wizard_importbible.bmp')
def setupUi(self, image): def setupUi(self, image):
@ -105,7 +105,7 @@ class BibleUpgradeForm(OpenLPWizard):
Perform necessary functions depending on which wizard page is active. Perform necessary functions depending on which wizard page is active.
""" """
if self.page(pageId) == self.progress_page: if self.page(pageId) == self.progress_page:
self.preWizard() self.pre_wizard()
self.performWizard() self.performWizard()
self.post_wizard() self.post_wizard()
elif self.page(pageId) == self.selectPage and not self.files: elif self.page(pageId) == self.selectPage and not self.files:
@ -159,41 +159,41 @@ class BibleUpgradeForm(OpenLPWizard):
Add the bible import specific wizard pages. Add the bible import specific wizard pages.
""" """
# Backup Page # Backup Page
self.backupPage = QtGui.QWizardPage() self.backup_page = QtGui.QWizardPage()
self.backupPage.setObjectName(u'BackupPage') self.backup_page.setObjectName(u'BackupPage')
self.backupLayout = QtGui.QVBoxLayout(self.backupPage) self.backupLayout = QtGui.QVBoxLayout(self.backup_page)
self.backupLayout.setObjectName(u'BackupLayout') self.backupLayout.setObjectName(u'BackupLayout')
self.backupInfoLabel = QtGui.QLabel(self.backupPage) self.backupInfoLabel = QtGui.QLabel(self.backup_page)
self.backupInfoLabel.setOpenExternalLinks(True) self.backupInfoLabel.setOpenExternalLinks(True)
self.backupInfoLabel.setTextFormat(QtCore.Qt.RichText) self.backupInfoLabel.setTextFormat(QtCore.Qt.RichText)
self.backupInfoLabel.setWordWrap(True) self.backupInfoLabel.setWordWrap(True)
self.backupInfoLabel.setObjectName(u'backupInfoLabel') self.backupInfoLabel.setObjectName(u'backupInfoLabel')
self.backupLayout.addWidget(self.backupInfoLabel) self.backupLayout.addWidget(self.backupInfoLabel)
self.selectLabel = QtGui.QLabel(self.backupPage) self.selectLabel = QtGui.QLabel(self.backup_page)
self.selectLabel.setObjectName(u'select_label') self.selectLabel.setObjectName(u'select_label')
self.backupLayout.addWidget(self.selectLabel) self.backupLayout.addWidget(self.selectLabel)
self.formLayout = QtGui.QFormLayout() self.formLayout = QtGui.QFormLayout()
self.formLayout.setMargin(0) self.formLayout.setMargin(0)
self.formLayout.setObjectName(u'FormLayout') self.formLayout.setObjectName(u'FormLayout')
self.backupDirectoryLabel = QtGui.QLabel(self.backupPage) self.backupDirectoryLabel = QtGui.QLabel(self.backup_page)
self.backupDirectoryLabel.setObjectName(u'backupDirectoryLabel') self.backupDirectoryLabel.setObjectName(u'backupDirectoryLabel')
self.backupDirectoryLayout = QtGui.QHBoxLayout() self.backupDirectoryLayout = QtGui.QHBoxLayout()
self.backupDirectoryLayout.setObjectName(u'BackupDirectoryLayout') self.backupDirectoryLayout.setObjectName(u'BackupDirectoryLayout')
self.backupDirectoryEdit = QtGui.QLineEdit(self.backupPage) self.backupDirectoryEdit = QtGui.QLineEdit(self.backup_page)
self.backupDirectoryEdit.setObjectName(u'BackupFolderEdit') self.backupDirectoryEdit.setObjectName(u'BackupFolderEdit')
self.backupDirectoryLayout.addWidget(self.backupDirectoryEdit) self.backupDirectoryLayout.addWidget(self.backupDirectoryEdit)
self.backupBrowseButton = QtGui.QToolButton(self.backupPage) self.backupBrowseButton = QtGui.QToolButton(self.backup_page)
self.backupBrowseButton.setIcon(self.open_icon) self.backupBrowseButton.setIcon(self.open_icon)
self.backupBrowseButton.setObjectName(u'BackupBrowseButton') self.backupBrowseButton.setObjectName(u'BackupBrowseButton')
self.backupDirectoryLayout.addWidget(self.backupBrowseButton) self.backupDirectoryLayout.addWidget(self.backupBrowseButton)
self.formLayout.addRow(self.backupDirectoryLabel, self.backupDirectoryLayout) self.formLayout.addRow(self.backupDirectoryLabel, self.backupDirectoryLayout)
self.backupLayout.addLayout(self.formLayout) self.backupLayout.addLayout(self.formLayout)
self.noBackupCheckBox = QtGui.QCheckBox(self.backupPage) self.noBackupCheckBox = QtGui.QCheckBox(self.backup_page)
self.noBackupCheckBox.setObjectName('NoBackupCheckBox') self.noBackupCheckBox.setObjectName('NoBackupCheckBox')
self.backupLayout.addWidget(self.noBackupCheckBox) self.backupLayout.addWidget(self.noBackupCheckBox)
self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
self.backupLayout.addItem(self.spacer) self.backupLayout.addItem(self.spacer)
self.addPage(self.backupPage) self.addPage(self.backup_page)
# Select Page # Select Page
self.selectPage = QtGui.QWizardPage() self.selectPage = QtGui.QWizardPage()
self.selectPage.setObjectName(u'SelectPage') self.selectPage.setObjectName(u'SelectPage')
@ -247,8 +247,8 @@ class BibleUpgradeForm(OpenLPWizard):
self.information_label.setText(translate('BiblesPlugin.UpgradeWizardForm', self.information_label.setText(translate('BiblesPlugin.UpgradeWizardForm',
'This wizard will help you to upgrade your existing Bibles from a prior version of OpenLP 2. ' 'This wizard will help you to upgrade your existing Bibles from a prior version of OpenLP 2. '
'Click the next button below to start the upgrade process.')) 'Click the next button below to start the upgrade process.'))
self.backupPage.setTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Select Backup Directory')) self.backup_page.setTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Select Backup Directory'))
self.backupPage.setSubTitle(translate('BiblesPlugin.UpgradeWizardForm', self.backup_page.setSubTitle(translate('BiblesPlugin.UpgradeWizardForm',
'Please select a backup directory for your Bibles')) 'Please select a backup directory for your Bibles'))
self.backupInfoLabel.setText(translate('BiblesPlugin.UpgradeWizardForm', self.backupInfoLabel.setText(translate('BiblesPlugin.UpgradeWizardForm',
'Previous releases of OpenLP 2.0 are unable to use upgraded Bibles.' 'Previous releases of OpenLP 2.0 are unable to use upgraded Bibles.'
@ -277,7 +277,7 @@ class BibleUpgradeForm(OpenLPWizard):
""" """
if self.currentPage() == self.welcome_page: if self.currentPage() == self.welcome_page:
return True return True
elif self.currentPage() == self.backupPage: elif self.currentPage() == self.backup_page:
if not self.noBackupCheckBox.checkState() == QtCore.Qt.Checked: if not self.noBackupCheckBox.checkState() == QtCore.Qt.Checked:
backup_path = self.backupDirectoryEdit.text() backup_path = self.backupDirectoryEdit.text()
if not backup_path: if not backup_path:
@ -316,7 +316,7 @@ class BibleUpgradeForm(OpenLPWizard):
settings.beginGroup(self.plugin.settings_section) settings.beginGroup(self.plugin.settings_section)
self.stop_import_flag = False self.stop_import_flag = False
self.success.clear() self.success.clear()
self.newbibles.clear() self.new_bibles.clear()
self.clearScrollArea() self.clearScrollArea()
self.files = self.manager.old_bible_databases self.files = self.manager.old_bible_databases
self.addScrollArea() self.addScrollArea()
@ -329,7 +329,7 @@ class BibleUpgradeForm(OpenLPWizard):
self.cancel_button.setVisible(True) self.cancel_button.setVisible(True)
settings.endGroup() settings.endGroup()
def preWizard(self): def pre_wizard(self):
""" """
Prepare the UI for the upgrade. Prepare the UI for the upgrade.
""" """
@ -372,8 +372,8 @@ class BibleUpgradeForm(OpenLPWizard):
name = filename[1] name = filename[1]
self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm', self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nUpgrading ...') % (number + 1, max_bibles, name)) 'Upgrading Bible %s of %s: "%s"\nUpgrading ...') % (number + 1, max_bibles, name))
self.newbibles[number] = BibleDB(self.media_item, path=self.path, name=name, file=filename[0]) self.new_bibles[number] = BibleDB(self.media_item, path=self.path, name=name, file=filename[0])
self.newbibles[number].register(self.plugin.upgrade_wizard) self.new_bibles[number].register(self.plugin.upgrade_wizard)
metadata = old_bible.get_metadata() metadata = old_bible.get_metadata()
web_bible = False web_bible = False
meta_data = {} meta_data = {}
@ -387,7 +387,7 @@ class BibleUpgradeForm(OpenLPWizard):
# Copy the metadata # Copy the metadata
meta_data[meta[u'key']] = meta[u'value'] meta_data[meta[u'key']] = meta[u'value']
if meta[u'key'] != u'name' and meta[u'key'] != u'dbversion': if meta[u'key'] != u'name' and meta[u'key'] != u'dbversion':
self.newbibles[number].save_meta(meta[u'key'], meta[u'value']) self.new_bibles[number].save_meta(meta[u'key'], meta[u'value'])
if meta[u'key'] == u'download_source': if meta[u'key'] == u'download_source':
web_bible = True web_bible = True
self.includeWebBible = True self.includeWebBible = True
@ -403,8 +403,8 @@ class BibleUpgradeForm(OpenLPWizard):
if not books: if not books:
log.error(u'Upgrading books from %s - download name: "%s" failed' % ( log.error(u'Upgrading books from %s - download name: "%s" failed' % (
meta_data[u'download_source'], meta_data[u'download_name'])) meta_data[u'download_source'], meta_data[u'download_name']))
self.newbibles[number].session.close() self.new_bibles[number].session.close()
del self.newbibles[number] del self.new_bibles[number]
critical_error_message_box( critical_error_message_box(
translate('BiblesPlugin.UpgradeWizardForm', 'Download Error'), translate('BiblesPlugin.UpgradeWizardForm', 'Download Error'),
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
@ -419,14 +419,14 @@ class BibleUpgradeForm(OpenLPWizard):
meta_data[u'download_source'].lower()) meta_data[u'download_source'].lower())
if bible and bible[u'language_id']: if bible and bible[u'language_id']:
language_id = bible[u'language_id'] language_id = bible[u'language_id']
self.newbibles[number].save_meta(u'language_id', self.new_bibles[number].save_meta(u'language_id',
language_id) language_id)
else: else:
language_id = self.newbibles[number].get_language(name) language_id = self.new_bibles[number].get_language(name)
if not language_id: if not language_id:
log.warn(u'Upgrading from "%s" failed' % filename[0]) log.warn(u'Upgrading from "%s" failed' % filename[0])
self.newbibles[number].session.close() self.new_bibles[number].session.close()
del self.newbibles[number] del self.new_bibles[number]
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name),
self.progress_bar.maximum() - self.progress_bar.value()) self.progress_bar.maximum() - self.progress_bar.value())
@ -439,17 +439,17 @@ class BibleUpgradeForm(OpenLPWizard):
break break
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % (number + 1, max_bibles, name, book)) 'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % (number + 1, max_bibles, name, book))
book_ref_id = self.newbibles[number].\ book_ref_id = self.new_bibles[number].\
get_book_ref_id_by_name(book, len(books), language_id) get_book_ref_id_by_name(book, len(books), language_id)
if not book_ref_id: if not book_ref_id:
log.warn(u'Upgrading books from %s - download name: "%s" aborted by user' % ( log.warn(u'Upgrading books from %s - download name: "%s" aborted by user' % (
meta_data[u'download_source'], meta_data[u'download_name'])) meta_data[u'download_source'], meta_data[u'download_name']))
self.newbibles[number].session.close() self.new_bibles[number].session.close()
del self.newbibles[number] del self.new_bibles[number]
self.success[number] = False self.success[number] = False
break break
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.newbibles[number].create_book(book, db_book = self.new_bibles[number].create_book(book,
book_ref_id, book_details[u'testament_id']) book_ref_id, book_details[u'testament_id'])
# Try to import already downloaded verses. # Try to import already downloaded verses.
oldbook = old_bible.get_book(book) oldbook = old_bible.get_book(book)
@ -462,19 +462,19 @@ class BibleUpgradeForm(OpenLPWizard):
if self.stop_import_flag: if self.stop_import_flag:
self.success[number] = False self.success[number] = False
break break
self.newbibles[number].create_verse(db_book.id, self.new_bibles[number].create_verse(db_book.id,
int(verse[u'chapter']), int(verse[u'chapter']),
int(verse[u'verse']), unicode(verse[u'text'])) int(verse[u'verse']), unicode(verse[u'text']))
self.application.process_events() self.application.process_events()
self.newbibles[number].session.commit() self.new_bibles[number].session.commit()
else: else:
language_id = self.newbibles[number].get_object(BibleMeta, u'language_id') language_id = self.new_bibles[number].get_object(BibleMeta, u'language_id')
if not language_id: if not language_id:
language_id = self.newbibles[number].get_language(name) language_id = self.new_bibles[number].get_language(name)
if not language_id: if not language_id:
log.warn(u'Upgrading books from "%s" failed' % name) log.warn(u'Upgrading books from "%s" failed' % name)
self.newbibles[number].session.close() self.new_bibles[number].session.close()
del self.newbibles[number] del self.new_bibles[number]
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name),
self.progress_bar.maximum() - self.progress_bar.value()) self.progress_bar.maximum() - self.progress_bar.value())
@ -489,41 +489,41 @@ class BibleUpgradeForm(OpenLPWizard):
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % 'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') %
(number + 1, max_bibles, name, book[u'name'])) (number + 1, max_bibles, name, book[u'name']))
book_ref_id = self.newbibles[number].get_book_ref_id_by_name(book[u'name'], len(books), language_id) book_ref_id = self.new_bibles[number].get_book_ref_id_by_name(book[u'name'], len(books), language_id)
if not book_ref_id: if not book_ref_id:
log.warn(u'Upgrading books from %s " failed - aborted by user' % name) log.warn(u'Upgrading books from %s " failed - aborted by user' % name)
self.newbibles[number].session.close() self.new_bibles[number].session.close()
del self.newbibles[number] del self.new_bibles[number]
self.success[number] = False self.success[number] = False
break break
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.newbibles[number].create_book(book[u'name'], db_book = self.new_bibles[number].create_book(book[u'name'],
book_ref_id, book_details[u'testament_id']) book_ref_id, book_details[u'testament_id'])
verses = old_bible.get_verses(book[u'id']) verses = old_bible.get_verses(book[u'id'])
if not verses: if not verses:
log.warn(u'No verses found to import for book "%s"', book[u'name']) log.warn(u'No verses found to import for book "%s"', book[u'name'])
self.newbibles[number].delete_book(db_book) self.new_bibles[number].delete_book(db_book)
continue continue
for verse in verses: for verse in verses:
if self.stop_import_flag: if self.stop_import_flag:
self.success[number] = False self.success[number] = False
break break
self.newbibles[number].create_verse(db_book.id, self.new_bibles[number].create_verse(db_book.id,
int(verse[u'chapter']), int(verse[u'chapter']),
int(verse[u'verse']), unicode(verse[u'text'])) int(verse[u'verse']), unicode(verse[u'text']))
self.application.process_events() self.application.process_events()
self.newbibles[number].session.commit() self.new_bibles[number].session.commit()
if not self.success.get(number, True): if not self.success.get(number, True):
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name),
self.progress_bar.maximum() - self.progress_bar.value()) self.progress_bar.maximum() - self.progress_bar.value())
else: else:
self.success[number] = True self.success[number] = True
self.newbibles[number].save_meta(u'name', name) self.new_bibles[number].save_meta(u'name', name)
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nComplete') % (number + 1, max_bibles, name)) 'Upgrading Bible %s of %s: "%s"\nComplete') % (number + 1, max_bibles, name))
if number in self.newbibles: if number in self.new_bibles:
self.newbibles[number].session.close() self.new_bibles[number].session.close()
# Close the last bible's connection if possible. # Close the last bible's connection if possible.
if old_bible is not None: if old_bible is not None:
old_bible.close_connection() old_bible.close_connection()

View File

@ -33,66 +33,66 @@ from openlp.core.lib import translate
from openlp.core.lib.ui import create_button_box from openlp.core.lib.ui import create_button_box
class Ui_BookNameDialog(object): class Ui_BookNameDialog(object):
def setupUi(self, bookNameDialog): def setupUi(self, book_name_dialog):
bookNameDialog.setObjectName(u'bookNameDialog') book_name_dialog.setObjectName(u'book_name_dialog')
bookNameDialog.resize(400, 271) book_name_dialog.resize(400, 271)
self.bookNameLayout = QtGui.QVBoxLayout(bookNameDialog) self.book_name_layout = QtGui.QVBoxLayout(book_name_dialog)
self.bookNameLayout.setSpacing(8) self.book_name_layout.setSpacing(8)
self.bookNameLayout.setMargin(8) self.book_name_layout.setMargin(8)
self.bookNameLayout.setObjectName(u'bookNameLayout') self.book_name_layout.setObjectName(u'book_name_layout')
self.infoLabel = QtGui.QLabel(bookNameDialog) self.info_label = QtGui.QLabel(book_name_dialog)
self.infoLabel.setWordWrap(True) self.info_label.setWordWrap(True)
self.infoLabel.setObjectName(u'infoLabel') self.info_label.setObjectName(u'info_label')
self.bookNameLayout.addWidget(self.infoLabel) self.book_name_layout.addWidget(self.info_label)
self.correspondingLayout = QtGui.QGridLayout() self.corresponding_layout = QtGui.QGridLayout()
self.correspondingLayout.setColumnStretch(1, 1) self.corresponding_layout.setColumnStretch(1, 1)
self.correspondingLayout.setSpacing(8) self.corresponding_layout.setSpacing(8)
self.correspondingLayout.setObjectName(u'correspondingLayout') self.corresponding_layout.setObjectName(u'corresponding_layout')
self.currentLabel = QtGui.QLabel(bookNameDialog) self.currentLabel = QtGui.QLabel(book_name_dialog)
self.currentLabel.setObjectName(u'currentLabel') self.currentLabel.setObjectName(u'currentLabel')
self.correspondingLayout.addWidget(self.currentLabel, 0, 0, 1, 1) self.corresponding_layout.addWidget(self.currentLabel, 0, 0, 1, 1)
self.currentBookLabel = QtGui.QLabel(bookNameDialog) self.current_book_label = QtGui.QLabel(book_name_dialog)
self.currentBookLabel.setObjectName(u'currentBookLabel') self.current_book_label.setObjectName(u'current_book_label')
self.correspondingLayout.addWidget(self.currentBookLabel, 0, 1, 1, 1) self.corresponding_layout.addWidget(self.current_book_label, 0, 1, 1, 1)
self.correspondingLabel = QtGui.QLabel(bookNameDialog) self.correspondingLabel = QtGui.QLabel(book_name_dialog)
self.correspondingLabel.setObjectName(u'correspondingLabel') self.correspondingLabel.setObjectName(u'correspondingLabel')
self.correspondingLayout.addWidget(self.correspondingLabel, 1, 0, 1, 1) self.corresponding_layout.addWidget(self.correspondingLabel, 1, 0, 1, 1)
self.correspondingComboBox = QtGui.QComboBox(bookNameDialog) self.corresponding_combo_box = QtGui.QComboBox(book_name_dialog)
self.correspondingComboBox.setObjectName(u'correspondingComboBox') self.corresponding_combo_box.setObjectName(u'corresponding_combo_box')
self.correspondingLayout.addWidget(self.correspondingComboBox, 1, 1, 1, 1) self.corresponding_layout.addWidget(self.corresponding_combo_box, 1, 1, 1, 1)
self.bookNameLayout.addLayout(self.correspondingLayout) self.book_name_layout.addLayout(self.corresponding_layout)
self.optionsGroupBox = QtGui.QGroupBox(bookNameDialog) self.options_group_box = QtGui.QGroupBox(book_name_dialog)
self.optionsGroupBox.setObjectName(u'optionsGroupBox') self.options_group_box.setObjectName(u'options_group_box')
self.optionsLayout = QtGui.QVBoxLayout(self.optionsGroupBox) self.options_layout = QtGui.QVBoxLayout(self.options_group_box)
self.optionsLayout.setSpacing(8) self.options_layout.setSpacing(8)
self.optionsLayout.setMargin(8) self.options_layout.setMargin(8)
self.optionsLayout.setObjectName(u'optionsLayout') self.options_layout.setObjectName(u'options_layout')
self.oldTestamentCheckBox = QtGui.QCheckBox(self.optionsGroupBox) self.old_testament_check_box = QtGui.QCheckBox(self.options_group_box)
self.oldTestamentCheckBox.setObjectName(u'oldTestamentCheckBox') self.old_testament_check_box.setObjectName(u'old_testament_check_box')
self.oldTestamentCheckBox.setCheckState(QtCore.Qt.Checked) self.old_testament_check_box.setCheckState(QtCore.Qt.Checked)
self.optionsLayout.addWidget(self.oldTestamentCheckBox) self.options_layout.addWidget(self.old_testament_check_box)
self.newTestamentCheckBox = QtGui.QCheckBox(self.optionsGroupBox) self.new_testament_check_box = QtGui.QCheckBox(self.options_group_box)
self.newTestamentCheckBox.setObjectName(u'newTestamentCheckBox') self.new_testament_check_box.setObjectName(u'new_testament_check_box')
self.newTestamentCheckBox.setCheckState(QtCore.Qt.Checked) self.new_testament_check_box.setCheckState(QtCore.Qt.Checked)
self.optionsLayout.addWidget(self.newTestamentCheckBox) self.options_layout.addWidget(self.new_testament_check_box)
self.apocryphaCheckBox = QtGui.QCheckBox(self.optionsGroupBox) self.apocrypha_check_box = QtGui.QCheckBox(self.options_group_box)
self.apocryphaCheckBox.setObjectName(u'apocryphaCheckBox') self.apocrypha_check_box.setObjectName(u'apocrypha_check_box')
self.apocryphaCheckBox.setCheckState(QtCore.Qt.Checked) self.apocrypha_check_box.setCheckState(QtCore.Qt.Checked)
self.optionsLayout.addWidget(self.apocryphaCheckBox) self.options_layout.addWidget(self.apocrypha_check_box)
self.bookNameLayout.addWidget(self.optionsGroupBox) self.book_name_layout.addWidget(self.options_group_box)
self.button_box = create_button_box(bookNameDialog, u'button_box', [u'cancel', u'ok']) self.button_box = create_button_box(book_name_dialog, u'button_box', [u'cancel', u'ok'])
self.bookNameLayout.addWidget(self.button_box) self.book_name_layout.addWidget(self.button_box)
self.retranslateUi(bookNameDialog) self.retranslateUi(book_name_dialog)
def retranslateUi(self, bookNameDialog): def retranslateUi(self, book_name_dialog):
bookNameDialog.setWindowTitle(translate('BiblesPlugin.BookNameDialog', 'Select Book Name')) book_name_dialog.setWindowTitle(translate('BiblesPlugin.BookNameDialog', 'Select Book Name'))
self.infoLabel.setText(translate('BiblesPlugin.BookNameDialog', self.info_label.setText(translate('BiblesPlugin.BookNameDialog',
'The following book name cannot be matched up internally. ' 'The following book name cannot be matched up internally. '
'Please select the corresponding name from the list.')) 'Please select the corresponding name from the list.'))
self.currentLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Current name:')) self.currentLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Current name:'))
self.correspondingLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Corresponding name:')) self.correspondingLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Corresponding name:'))
self.optionsGroupBox.setTitle(translate('BiblesPlugin.BookNameDialog', 'Show Books From')) self.options_group_box.setTitle(translate('BiblesPlugin.BookNameDialog', 'Show Books From'))
self.oldTestamentCheckBox.setText(translate('BiblesPlugin.BookNameDialog', 'Old Testament')) self.old_testament_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'Old Testament'))
self.newTestamentCheckBox.setText(translate('BiblesPlugin.BookNameDialog', 'New Testament')) self.new_testament_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'New Testament'))
self.apocryphaCheckBox.setText(translate('BiblesPlugin.BookNameDialog', 'Apocrypha')) self.apocrypha_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'Apocrypha'))

View File

@ -66,61 +66,61 @@ class BookNameForm(QDialog, Ui_BookNameDialog):
""" """
Set up the signals used in the booknameform. Set up the signals used in the booknameform.
""" """
self.oldTestamentCheckBox.stateChanged.connect(self.onCheckBoxIndexChanged) self.old_testament_check_box.stateChanged.connect(self.onCheckBoxIndexChanged)
self.newTestamentCheckBox.stateChanged.connect(self.onCheckBoxIndexChanged) self.new_testament_check_box.stateChanged.connect(self.onCheckBoxIndexChanged)
self.apocryphaCheckBox.stateChanged.connect(self.onCheckBoxIndexChanged) self.apocrypha_check_box.stateChanged.connect(self.onCheckBoxIndexChanged)
def onCheckBoxIndexChanged(self, index): def onCheckBoxIndexChanged(self, index):
""" """
Reload Combobox if CheckBox state has changed Reload Combobox if CheckBox state has changed
""" """
self.reloadComboBox() self.reload_combo_box()
def reloadComboBox(self): def reload_combo_box(self):
""" """
Reload the Combobox items Reload the Combobox items
""" """
self.correspondingComboBox.clear() self.corresponding_combo_box.clear()
items = BiblesResourcesDB.get_books() items = BiblesResourcesDB.get_books()
for item in items: for item in items:
addBook = True add_book = True
for book in self.books: for book in self.books:
if book.book_reference_id == item[u'id']: if book.book_reference_id == item[u'id']:
addBook = False add_book = False
break break
if self.oldTestamentCheckBox.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 1: if self.old_testament_check_box.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 1:
addBook = False add_book = False
elif self.newTestamentCheckBox.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 2: elif self.new_testament_check_box.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 2:
addBook = False add_book = False
elif self.apocryphaCheckBox.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 3: elif self.apocrypha_check_box.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 3:
addBook = False add_book = False
if addBook: if add_book:
self.correspondingComboBox.addItem(self.book_names[item[u'abbreviation']]) self.corresponding_combo_box.addItem(self.book_names[item[u'abbreviation']])
def exec_(self, name, books, maxbooks): def exec_(self, name, books, max_books):
self.books = books self.books = books
log.debug(maxbooks) log.debug(max_books)
if maxbooks <= 27: if max_books <= 27:
self.oldTestamentCheckBox.setCheckState(QtCore.Qt.Unchecked) self.old_testament_check_box.setCheckState(QtCore.Qt.Unchecked)
self.apocryphaCheckBox.setCheckState(QtCore.Qt.Unchecked) self.apocrypha_check_box.setCheckState(QtCore.Qt.Unchecked)
elif maxbooks <= 66: elif max_books <= 66:
self.apocryphaCheckBox.setCheckState(QtCore.Qt.Unchecked) self.apocrypha_check_box.setCheckState(QtCore.Qt.Unchecked)
self.reloadComboBox() self.reload_combo_box()
self.currentBookLabel.setText(unicode(name)) self.current_book_label.setText(unicode(name))
self.correspondingComboBox.setFocus() self.corresponding_combo_box.setFocus()
return QDialog.exec_(self) return QDialog.exec_(self)
def accept(self): def accept(self):
if self.correspondingComboBox.currentText() == u'': if not self.corresponding_combo_box.currentText():
critical_error_message_box(message=translate('BiblesPlugin.BookNameForm', 'You need to select a book.')) critical_error_message_box(message=translate('BiblesPlugin.BookNameForm', 'You need to select a book.'))
self.correspondingComboBox.setFocus() self.corresponding_combo_box.setFocus()
return False return False
else: else:
cor_book = self.correspondingComboBox.currentText() cor_book = self.corresponding_combo_box.currentText()
for character in u'\\.^$*+?{}[]()': for character in u'\\.^$*+?{}[]()':
cor_book = cor_book.replace(character, u'\\' + character) cor_book = cor_book.replace(character, u'\\' + character)
books = filter(lambda key: books = filter(
re.match(cor_book, unicode(self.book_names[key]), re.UNICODE), self.book_names.keys()) lambda key: re.match(cor_book, unicode(self.book_names[key]), re.UNICODE), self.book_names.keys())
books = filter(None, map(BiblesResourcesDB.get_book, books)) books = filter(None, map(BiblesResourcesDB.get_book, books))
if books: if books:
self.book_id = books[0][u'id'] self.book_id = books[0][u'id']

View File

@ -36,118 +36,118 @@ from openlp.plugins.bibles.lib.db import BiblesResourcesDB
class Ui_EditBibleDialog(object): class Ui_EditBibleDialog(object):
def setupUi(self, editBibleDialog): def setupUi(self, edit_bible_dialog):
editBibleDialog.setObjectName(u'editBibleDialog') edit_bible_dialog.setObjectName(u'edit_bible_dialog')
editBibleDialog.resize(520, 400) edit_bible_dialog.resize(520, 400)
editBibleDialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png')) edit_bible_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png'))
editBibleDialog.setModal(True) edit_bible_dialog.setModal(True)
self.dialogLayout = QtGui.QVBoxLayout(editBibleDialog) self.dialog_layout = QtGui.QVBoxLayout(edit_bible_dialog)
self.dialogLayout.setSpacing(8) self.dialog_layout.setSpacing(8)
self.dialogLayout.setContentsMargins(8, 8, 8, 8) self.dialog_layout.setContentsMargins(8, 8, 8, 8)
self.dialogLayout.setObjectName(u'dialog_layout') self.dialog_layout.setObjectName(u'dialog_layout')
self.bibleTabWidget = QtGui.QTabWidget(editBibleDialog) self.bible_tab_widget = QtGui.QTabWidget(edit_bible_dialog)
self.bibleTabWidget.setObjectName(u'BibleTabWidget') self.bible_tab_widget.setObjectName(u'BibleTabWidget')
# Meta tab # Meta tab
self.metaTab = QtGui.QWidget() self.meta_tab = QtGui.QWidget()
self.metaTab.setObjectName(u'metaTab') self.meta_tab.setObjectName(u'meta_tab')
self.metaTabLayout = QtGui.QVBoxLayout(self.metaTab) self.meta_tab_layout = QtGui.QVBoxLayout(self.meta_tab)
self.metaTabLayout.setObjectName(u'metaTabLayout') self.meta_tab_layout.setObjectName(u'meta_tab_layout')
self.licenseDetailsGroupBox = QtGui.QGroupBox(self.metaTab) self.license_details_group_box = QtGui.QGroupBox(self.meta_tab)
self.licenseDetailsGroupBox.setObjectName(u'licenseDetailsGroupBox') self.license_details_group_box.setObjectName(u'license_details_group_box')
self.licenseDetailsLayout = QtGui.QFormLayout(self.licenseDetailsGroupBox) self.license_details_layout = QtGui.QFormLayout(self.license_details_group_box)
self.licenseDetailsLayout.setObjectName(u'licenseDetailsLayout') self.license_details_layout.setObjectName(u'license_details_layout')
self.versionNameLabel = QtGui.QLabel(self.licenseDetailsGroupBox) self.version_name_label = QtGui.QLabel(self.license_details_group_box)
self.versionNameLabel.setObjectName(u'versionNameLabel') self.version_name_label.setObjectName(u'version_name_label')
self.versionNameEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox) self.version_name_edit = QtGui.QLineEdit(self.license_details_group_box)
self.versionNameEdit.setObjectName(u'versionNameEdit') self.version_name_edit.setObjectName(u'version_name_edit')
self.versionNameLabel.setBuddy(self.versionNameEdit) self.version_name_label.setBuddy(self.version_name_edit)
self.licenseDetailsLayout.addRow(self.versionNameLabel, self.versionNameEdit) self.license_details_layout.addRow(self.version_name_label, self.version_name_edit)
self.copyrightLabel = QtGui.QLabel(self.licenseDetailsGroupBox) self.copyright_label = QtGui.QLabel(self.license_details_group_box)
self.copyrightLabel.setObjectName(u'copyrightLabel') self.copyright_label.setObjectName(u'copyright_label')
self.copyrightEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox) self.copyright_edit = QtGui.QLineEdit(self.license_details_group_box)
self.copyrightEdit.setObjectName(u'copyright_edit') self.copyright_edit.setObjectName(u'copyright_edit')
self.copyrightLabel.setBuddy(self.copyrightEdit) self.copyright_label.setBuddy(self.copyright_edit)
self.licenseDetailsLayout.addRow(self.copyrightLabel, self.copyrightEdit) self.license_details_layout.addRow(self.copyright_label, self.copyright_edit)
self.permissionsLabel = QtGui.QLabel(self.licenseDetailsGroupBox) self.permissions_label = QtGui.QLabel(self.license_details_group_box)
self.permissionsLabel.setObjectName(u'permissionsLabel') self.permissions_label.setObjectName(u'permissions_label')
self.permissionsEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox) self.permissions_edit = QtGui.QLineEdit(self.license_details_group_box)
self.permissionsEdit.setObjectName(u'permissionsEdit') self.permissions_edit.setObjectName(u'permissions_edit')
self.permissionsLabel.setBuddy(self.permissionsEdit) self.permissions_label.setBuddy(self.permissions_edit)
self.licenseDetailsLayout.addRow(self.permissionsLabel, self.permissionsEdit) self.license_details_layout.addRow(self.permissions_label, self.permissions_edit)
self.metaTabLayout.addWidget(self.licenseDetailsGroupBox) self.meta_tab_layout.addWidget(self.license_details_group_box)
self.languageSelectionGroupBox = QtGui.QGroupBox(self.metaTab) self.language_selection_group_box = QtGui.QGroupBox(self.meta_tab)
self.languageSelectionGroupBox.setObjectName(u'languageSelectionGroupBox') self.language_selection_group_box.setObjectName(u'language_selection_group_box')
self.languageSelectionLayout = QtGui.QVBoxLayout(self.languageSelectionGroupBox) self.language_selection_layout = QtGui.QVBoxLayout(self.language_selection_group_box)
self.languageSelectionLabel = QtGui.QLabel(self.languageSelectionGroupBox) self.language_selection_label = QtGui.QLabel(self.language_selection_group_box)
self.languageSelectionLabel.setObjectName(u'languageSelectionLabel') self.language_selection_label.setObjectName(u'language_selection_label')
self.languageSelectionComboBox = QtGui.QComboBox(self.languageSelectionGroupBox) self.language_selection_combo_box = QtGui.QComboBox(self.language_selection_group_box)
self.languageSelectionComboBox.setObjectName(u'languageSelectionComboBox') self.language_selection_combo_box.setObjectName(u'language_selection_combo_box')
self.languageSelectionComboBox.addItems([u'', u'', u'', u'']) self.language_selection_combo_box.addItems([u'', u'', u'', u''])
self.languageSelectionLayout.addWidget(self.languageSelectionLabel) self.language_selection_layout.addWidget(self.language_selection_label)
self.languageSelectionLayout.addWidget(self.languageSelectionComboBox) self.language_selection_layout.addWidget(self.language_selection_combo_box)
self.metaTabLayout.addWidget(self.languageSelectionGroupBox) self.meta_tab_layout.addWidget(self.language_selection_group_box)
self.metaTabLayout.addStretch() self.meta_tab_layout.addStretch()
self.bibleTabWidget.addTab(self.metaTab, u'') self.bible_tab_widget.addTab(self.meta_tab, u'')
# Book name tab # Book name tab
self.bookNameTab = QtGui.QWidget() self.book_name_tab = QtGui.QWidget()
self.bookNameTab.setObjectName(u'bookNameTab') self.book_name_tab.setObjectName(u'book_name_tab')
self.bookNameTabLayout = QtGui.QVBoxLayout(self.bookNameTab) self.book_name_tab_layout = QtGui.QVBoxLayout(self.book_name_tab)
self.bookNameTabLayout.setObjectName(u'bookNameTabLayout') self.book_name_tab_layout.setObjectName(u'book_name_tab_layout')
self.bookNameNotice = QtGui.QLabel(self.bookNameTab) self.book_name_notice = QtGui.QLabel(self.book_name_tab)
self.bookNameNotice.setObjectName(u'bookNameNotice') self.book_name_notice.setObjectName(u'book_name_notice')
self.bookNameNotice.setWordWrap(True) self.book_name_notice.setWordWrap(True)
self.bookNameTabLayout.addWidget(self.bookNameNotice) self.book_name_tab_layout.addWidget(self.book_name_notice)
self.scrollArea = QtGui.QScrollArea(self.bookNameTab) self.scroll_area = QtGui.QScrollArea(self.book_name_tab)
self.scrollArea.setWidgetResizable(True) self.scroll_area.setWidgetResizable(True)
self.scrollArea.setObjectName(u'scrollArea') self.scroll_area.setObjectName(u'scroll_area')
self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.bookNameWidget = QtGui.QWidget(self.scrollArea) self.book_name_widget = QtGui.QWidget(self.scroll_area)
self.bookNameWidget.setObjectName(u'bookNameWidget') self.book_name_widget.setObjectName(u'book_name_widget')
self.bookNameWidgetLayout = QtGui.QFormLayout(self.bookNameWidget) self.book_name_widget_layout = QtGui.QFormLayout(self.book_name_widget)
self.bookNameWidgetLayout.setObjectName(u'bookNameWidgetLayout') self.book_name_widget_layout.setObjectName(u'book_name_widget_layout')
self.bookNameLabel = {} self.book_name_label = {}
self.bookNameEdit= {} self.book_name_edit= {}
for book in BiblesResourcesDB.get_books(): for book in BiblesResourcesDB.get_books():
self.bookNameLabel[book[u'abbreviation']] = QtGui.QLabel(self.bookNameWidget) self.book_name_label[book[u'abbreviation']] = QtGui.QLabel(self.book_name_widget)
self.bookNameLabel[book[u'abbreviation']].setObjectName(u'bookNameLabel[%s]' % book[u'abbreviation']) self.book_name_label[book[u'abbreviation']].setObjectName(u'book_name_label[%s]' % book[u'abbreviation'])
self.bookNameEdit[book[u'abbreviation']] = QtGui.QLineEdit(self.bookNameWidget) self.book_name_edit[book[u'abbreviation']] = QtGui.QLineEdit(self.book_name_widget)
self.bookNameEdit[book[u'abbreviation']].setObjectName(u'bookNameEdit[%s]' % book[u'abbreviation']) self.book_name_edit[book[u'abbreviation']].setObjectName(u'book_name_edit[%s]' % book[u'abbreviation'])
self.bookNameWidgetLayout.addRow( self.book_name_widget_layout.addRow(
self.bookNameLabel[book[u'abbreviation']], self.book_name_label[book[u'abbreviation']],
self.bookNameEdit[book[u'abbreviation']]) self.book_name_edit[book[u'abbreviation']])
self.scrollArea.setWidget(self.bookNameWidget) self.scroll_area.setWidget(self.book_name_widget)
self.bookNameTabLayout.addWidget(self.scrollArea) self.book_name_tab_layout.addWidget(self.scroll_area)
self.bookNameTabLayout.addStretch() self.book_name_tab_layout.addStretch()
self.bibleTabWidget.addTab(self.bookNameTab, u'') self.bible_tab_widget.addTab(self.book_name_tab, u'')
# Last few bits # Last few bits
self.dialogLayout.addWidget(self.bibleTabWidget) self.dialog_layout.addWidget(self.bible_tab_widget)
self.button_box = create_button_box(editBibleDialog, u'button_box', [u'cancel', u'save']) self.button_box = create_button_box(edit_bible_dialog, u'button_box', [u'cancel', u'save'])
self.dialogLayout.addWidget(self.button_box) self.dialog_layout.addWidget(self.button_box)
self.retranslateUi(editBibleDialog) self.retranslateUi(edit_bible_dialog)
QtCore.QMetaObject.connectSlotsByName(editBibleDialog) QtCore.QMetaObject.connectSlotsByName(edit_bible_dialog)
def retranslateUi(self, editBibleDialog): def retranslateUi(self, edit_bible_dialog):
self.book_names = BibleStrings().BookNames self.book_names = BibleStrings().BookNames
editBibleDialog.setWindowTitle(translate('BiblesPlugin.EditBibleForm', 'Bible Editor')) edit_bible_dialog.setWindowTitle(translate('BiblesPlugin.EditBibleForm', 'Bible Editor'))
# Meta tab # Meta tab
self.bibleTabWidget.setTabText( self.bibleTabWidget.indexOf(self.metaTab), self.bible_tab_widget.setTabText( self.bible_tab_widget.indexOf(self.meta_tab),
translate('SongsPlugin.EditBibleForm', 'Meta Data')) translate('SongsPlugin.EditBibleForm', 'Meta Data'))
self.licenseDetailsGroupBox.setTitle(translate('BiblesPlugin.EditBibleForm', 'License Details')) self.license_details_group_box.setTitle(translate('BiblesPlugin.EditBibleForm', 'License Details'))
self.versionNameLabel.setText(translate('BiblesPlugin.EditBibleForm', 'Version name:')) self.version_name_label.setText(translate('BiblesPlugin.EditBibleForm', 'Version name:'))
self.copyrightLabel.setText(translate('BiblesPlugin.EditBibleForm', 'Copyright:')) self.copyright_label.setText(translate('BiblesPlugin.EditBibleForm', 'Copyright:'))
self.permissionsLabel.setText(translate('BiblesPlugin.EditBibleForm', 'Permissions:')) self.permissions_label.setText(translate('BiblesPlugin.EditBibleForm', 'Permissions:'))
self.languageSelectionGroupBox.setTitle(translate('BiblesPlugin.EditBibleForm', 'Default Bible Language')) self.language_selection_group_box.setTitle(translate('BiblesPlugin.EditBibleForm', 'Default Bible Language'))
self.languageSelectionLabel.setText(translate('BiblesPlugin.EditBibleForm', self.language_selection_label.setText(translate('BiblesPlugin.EditBibleForm',
'Book name language in search field, search results and on display:')) 'Book name language in search field, search results and on display:'))
self.languageSelectionComboBox.setItemText(0, translate('BiblesPlugin.EditBibleForm', 'Global Settings')) self.language_selection_combo_box.setItemText(0, translate('BiblesPlugin.EditBibleForm', 'Global Settings'))
self.languageSelectionComboBox.setItemText(LanguageSelection.Bible + 1, self.language_selection_combo_box.setItemText(LanguageSelection.Bible + 1,
translate('BiblesPlugin.EditBibleForm', 'Bible Language')) translate('BiblesPlugin.EditBibleForm', 'Bible Language'))
self.languageSelectionComboBox.setItemText(LanguageSelection.Application + 1, self.language_selection_combo_box.setItemText(LanguageSelection.Application + 1,
translate('BiblesPlugin.EditBibleForm', 'Application Language')) translate('BiblesPlugin.EditBibleForm', 'Application Language'))
self.languageSelectionComboBox.setItemText(LanguageSelection.English + 1, self.language_selection_combo_box.setItemText(LanguageSelection.English + 1,
translate('BiblesPlugin.EditBibleForm', 'English')) translate('BiblesPlugin.EditBibleForm', 'English'))
# Book name tab # Book name tab
self.bibleTabWidget.setTabText(self.bibleTabWidget.indexOf(self.bookNameTab), self.bible_tab_widget.setTabText(self.bible_tab_widget.indexOf(self.book_name_tab),
translate('SongsPlugin.EditBibleForm', 'Custom Book Names')) translate('SongsPlugin.EditBibleForm', 'Custom Book Names'))
for book in BiblesResourcesDB.get_books(): for book in BiblesResourcesDB.get_books():
self.bookNameLabel[book[u'abbreviation']].setText(u'%s:' % unicode(self.book_names[book[u'abbreviation']])) self.book_name_label[book[u'abbreviation']].setText(u'%s:' % unicode(self.book_names[book[u'abbreviation']]))

View File

@ -65,33 +65,33 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
""" """
log.debug(u'Load Bible') log.debug(u'Load Bible')
self.bible = bible self.bible = bible
self.versionNameEdit.setText(self.manager.get_meta_data(self.bible, u'name').value) self.version_name_edit.setText(self.manager.get_meta_data(self.bible, u'name').value)
self.copyrightEdit.setText(self.manager.get_meta_data(self.bible, u'copyright').value) self.copyright_edit.setText(self.manager.get_meta_data(self.bible, u'copyright').value)
self.permissionsEdit.setText(self.manager.get_meta_data(self.bible, u'permissions').value) self.permissions_edit.setText(self.manager.get_meta_data(self.bible, u'permissions').value)
book_name_language = self.manager.get_meta_data(self.bible, u'book_name_language') book_name_language = self.manager.get_meta_data(self.bible, u'book_name_language')
if book_name_language and book_name_language.value != u'None': if book_name_language and book_name_language.value != u'None':
self.languageSelectionComboBox.setCurrentIndex(int(book_name_language.value) + 1) self.language_selection_combo_box.setCurrentIndex(int(book_name_language.value) + 1)
self.books = {} self.books = {}
self.webbible = self.manager.get_meta_data(self.bible, u'download_source') self.webbible = self.manager.get_meta_data(self.bible, u'download_source')
if self.webbible: if self.webbible:
self.bookNameNotice.setText(translate('BiblesPlugin.EditBibleForm', self.book_name_notice.setText(translate('BiblesPlugin.EditBibleForm',
'This is a Web Download Bible.\nIt is not possible to customize the Book Names.')) 'This is a Web Download Bible.\nIt is not possible to customize the Book Names.'))
self.scrollArea.hide() self.scroll_area.hide()
else: else:
self.bookNameNotice.setText(translate('BiblesPlugin.EditBibleForm', self.book_name_notice.setText(translate('BiblesPlugin.EditBibleForm',
'To use the customized book names, "Bible language" must be selected on the Meta Data tab or, ' 'To use the customized book names, "Bible language" must be selected on the Meta Data tab or, '
'if "Global settings" is selected, on the Bible page in Configure OpenLP.')) 'if "Global settings" is selected, on the Bible page in Configure OpenLP.'))
for book in BiblesResourcesDB.get_books(): for book in BiblesResourcesDB.get_books():
self.books[book[u'abbreviation']] = self.manager.get_book_by_id(self.bible, book[u'id']) self.books[book[u'abbreviation']] = self.manager.get_book_by_id(self.bible, book[u'id'])
if self.books[book[u'abbreviation']] and not self.webbible: if self.books[book[u'abbreviation']] and not self.webbible:
self.bookNameEdit[book[u'abbreviation']].setText(self.books[book[u'abbreviation']].name) self.book_name_edit[book[u'abbreviation']].setText(self.books[book[u'abbreviation']].name)
else: else:
# It is necessary to remove the Widget otherwise there still # It is necessary to remove the Widget otherwise there still
# exists the vertical spacing in QFormLayout # exists the vertical spacing in QFormLayout
self.bookNameWidgetLayout.removeWidget(self.bookNameLabel[book[u'abbreviation']]) self.book_name_widget_layout.removeWidget(self.book_name_label[book[u'abbreviation']])
self.bookNameLabel[book[u'abbreviation']].hide() self.book_name_label[book[u'abbreviation']].hide()
self.bookNameWidgetLayout.removeWidget(self.bookNameEdit[book[u'abbreviation']]) self.book_name_widget_layout.removeWidget(self.book_name_edit[book[u'abbreviation']])
self.bookNameEdit[book[u'abbreviation']].hide() self.book_name_edit[book[u'abbreviation']].hide()
def reject(self): def reject(self):
""" """
@ -106,10 +106,10 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
Exit Dialog and save data Exit Dialog and save data
""" """
log.debug(u'BibleEditForm.accept') log.debug(u'BibleEditForm.accept')
version = self.versionNameEdit.text() version = self.version_name_edit.text()
copyright = self.copyrightEdit.text() copyright = self.copyright_edit.text()
permissions = self.permissionsEdit.text() permissions = self.permissions_edit.text()
book_name_language = self.languageSelectionComboBox.currentIndex() - 1 book_name_language = self.language_selection_combo_box.currentIndex() - 1
if book_name_language == -1: if book_name_language == -1:
book_name_language = None book_name_language = None
if not self.validateMeta(version, copyright): if not self.validateMeta(version, copyright):
@ -118,7 +118,7 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
custom_names = {} custom_names = {}
for abbr, book in self.books.iteritems(): for abbr, book in self.books.iteritems():
if book: if book:
custom_names[abbr] = self.bookNameEdit[abbr].text() custom_names[abbr] = self.book_name_edit[abbr].text()
if book.name != custom_names[abbr]: if book.name != custom_names[abbr]:
if not self.validateBook(custom_names[abbr], abbr): if not self.validateBook(custom_names[abbr], abbr):
return return
@ -139,19 +139,19 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
Validate the Meta before saving. Validate the Meta before saving.
""" """
if not name: if not name:
self.versionNameEdit.setFocus() self.version_name_edit.setFocus()
critical_error_message_box(UiStrings().EmptyField, critical_error_message_box(UiStrings().EmptyField,
translate('BiblesPlugin.BibleEditForm', 'You need to specify a version name for your Bible.')) translate('BiblesPlugin.BibleEditForm', 'You need to specify a version name for your Bible.'))
return False return False
elif not copyright: elif not copyright:
self.copyrightEdit.setFocus() self.copyright_edit.setFocus()
critical_error_message_box(UiStrings().EmptyField, critical_error_message_box(UiStrings().EmptyField,
translate('BiblesPlugin.BibleEditForm', translate('BiblesPlugin.BibleEditForm',
'You need to set a copyright for your Bible. Bibles in the Public Domain need to be marked as such.')) 'You need to set a copyright for your Bible. Bibles in the Public Domain need to be marked as such.'))
return False return False
elif self.manager.exists(name) and self.manager.get_meta_data(self.bible, u'name').value != \ elif self.manager.exists(name) and self.manager.get_meta_data(self.bible, u'name').value != \
name: name:
self.versionNameEdit.setFocus() self.version_name_edit.setFocus()
critical_error_message_box(translate('BiblesPlugin.BibleEditForm', 'Bible Exists'), critical_error_message_box(translate('BiblesPlugin.BibleEditForm', 'Bible Exists'),
translate('BiblesPlugin.BibleEditForm', 'This Bible already exists. Please import ' translate('BiblesPlugin.BibleEditForm', 'This Bible already exists. Please import '
'a different Bible or first delete the existing one.')) 'a different Bible or first delete the existing one.'))
@ -164,13 +164,13 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
""" """
book_regex = re.compile(u'[\d]*[^\d]+$') book_regex = re.compile(u'[\d]*[^\d]+$')
if not new_book_name: if not new_book_name:
self.bookNameEdit[abbreviation].setFocus() self.book_name_edit[abbreviation].setFocus()
critical_error_message_box(UiStrings().EmptyField, critical_error_message_box(UiStrings().EmptyField,
translate('BiblesPlugin.BibleEditForm', 'You need to specify a book name for "%s".') % translate('BiblesPlugin.BibleEditForm', 'You need to specify a book name for "%s".') %
self.book_names[abbreviation]) self.book_names[abbreviation])
return False return False
elif not book_regex.match(new_book_name): elif not book_regex.match(new_book_name):
self.bookNameEdit[abbreviation].setFocus() self.book_name_edit[abbreviation].setFocus()
critical_error_message_box(UiStrings().EmptyField, critical_error_message_box(UiStrings().EmptyField,
translate('BiblesPlugin.BibleEditForm', translate('BiblesPlugin.BibleEditForm',
'The book name "%s" is not correct.\nNumbers can only be used at the beginning and must\nbe ' 'The book name "%s" is not correct.\nNumbers can only be used at the beginning and must\nbe '
@ -180,8 +180,8 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
if book: if book:
if abbr == abbreviation: if abbr == abbreviation:
continue continue
if self.bookNameEdit[abbr].text() == new_book_name: if self.book_name_edit[abbr].text() == new_book_name:
self.bookNameEdit[abbreviation].setFocus() self.book_name_edit[abbreviation].setFocus()
critical_error_message_box( critical_error_message_box(
translate('BiblesPlugin.BibleEditForm', 'Duplicate Book Name'), translate('BiblesPlugin.BibleEditForm', 'Duplicate Book Name'),
translate('BiblesPlugin.BibleEditForm', 'The Book Name "%s" has been entered more than once.') translate('BiblesPlugin.BibleEditForm', 'The Book Name "%s" has been entered more than once.')

View File

@ -33,44 +33,44 @@ from openlp.core.lib import translate
from openlp.core.lib.ui import create_button_box from openlp.core.lib.ui import create_button_box
class Ui_LanguageDialog(object): class Ui_LanguageDialog(object):
def setupUi(self, languageDialog): def setupUi(self, language_dialog):
languageDialog.setObjectName(u'languageDialog') language_dialog.setObjectName(u'language_dialog')
languageDialog.resize(400, 165) language_dialog.resize(400, 165)
self.languageLayout = QtGui.QVBoxLayout(languageDialog) self.language_layout = QtGui.QVBoxLayout(language_dialog)
self.languageLayout.setSpacing(8) self.language_layout.setSpacing(8)
self.languageLayout.setMargin(8) self.language_layout.setMargin(8)
self.languageLayout.setObjectName(u'languageLayout') self.language_layout.setObjectName(u'language_layout')
self.bibleLabel = QtGui.QLabel(languageDialog) self.bible_label = QtGui.QLabel(language_dialog)
self.bibleLabel.setObjectName(u'bibleLabel') self.bible_label.setObjectName(u'bible_label')
self.languageLayout.addWidget(self.bibleLabel) self.language_layout.addWidget(self.bible_label)
self.infoLabel = QtGui.QLabel(languageDialog) self.info_label = QtGui.QLabel(language_dialog)
self.infoLabel.setWordWrap(True) self.info_label.setWordWrap(True)
self.infoLabel.setObjectName(u'infoLabel') self.info_label.setObjectName(u'info_label')
self.languageLayout.addWidget(self.infoLabel) self.language_layout.addWidget(self.info_label)
self.languageHBoxLayout = QtGui.QHBoxLayout() self.language_h_box_layout = QtGui.QHBoxLayout()
self.languageHBoxLayout.setSpacing(8) self.language_h_box_layout.setSpacing(8)
self.languageHBoxLayout.setObjectName(u'languageHBoxLayout') self.language_h_box_layout.setObjectName(u'language_h_box_layout')
self.languageLabel = QtGui.QLabel(languageDialog) self.language_label = QtGui.QLabel(language_dialog)
self.languageLabel.setObjectName(u'languageLabel') self.language_label.setObjectName(u'language_label')
self.languageHBoxLayout.addWidget(self.languageLabel) self.language_h_box_layout.addWidget(self.language_label)
self.languageComboBox = QtGui.QComboBox(languageDialog) self.language_combo_box = QtGui.QComboBox(language_dialog)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.languageComboBox.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.language_combo_box.sizePolicy().hasHeightForWidth())
self.languageComboBox.setSizePolicy(sizePolicy) self.language_combo_box.setSizePolicy(sizePolicy)
self.languageComboBox.setObjectName(u'languageComboBox') self.language_combo_box.setObjectName(u'language_combo_box')
self.languageHBoxLayout.addWidget(self.languageComboBox) self.language_h_box_layout.addWidget(self.language_combo_box)
self.languageLayout.addLayout(self.languageHBoxLayout) self.language_layout.addLayout(self.language_h_box_layout)
self.button_box = create_button_box(languageDialog, u'button_box', [u'cancel', u'ok']) self.button_box = create_button_box(language_dialog, u'button_box', [u'cancel', u'ok'])
self.languageLayout.addWidget(self.button_box) self.language_layout.addWidget(self.button_box)
self.retranslateUi(languageDialog) self.retranslateUi(language_dialog)
def retranslateUi(self, languageDialog): def retranslateUi(self, language_dialog):
languageDialog.setWindowTitle(translate('BiblesPlugin.LanguageDialog', 'Select Language')) language_dialog.setWindowTitle(translate('BiblesPlugin.LanguageDialog', 'Select Language'))
self.bibleLabel.setText(translate('BiblesPlugin.LanguageDialog', '')) self.bible_label.setText(translate('BiblesPlugin.LanguageDialog', ''))
self.infoLabel.setText(translate('BiblesPlugin.LanguageDialog', self.info_label.setText(translate('BiblesPlugin.LanguageDialog',
'OpenLP is unable to determine the language of this translation of the Bible. Please select the language ' 'OpenLP is unable to determine the language of this translation of the Bible. Please select the language '
'from the list below.')) 'from the list below.'))
self.languageLabel.setText(translate('BiblesPlugin.LanguageDialog', 'Language:')) self.language_label.setText(translate('BiblesPlugin.LanguageDialog', 'Language:'))

View File

@ -40,8 +40,10 @@ from openlp.plugins.bibles.forms.languagedialog import \
Ui_LanguageDialog Ui_LanguageDialog
from openlp.plugins.bibles.lib.db import BiblesResourcesDB from openlp.plugins.bibles.lib.db import BiblesResourcesDB
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class LanguageForm(QDialog, Ui_LanguageDialog): class LanguageForm(QDialog, Ui_LanguageDialog):
""" """
Class to manage a dialog which ask the user for a language. Class to manage a dialog which ask the user for a language.
@ -56,19 +58,17 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
self.setupUi(self) self.setupUi(self)
def exec_(self, bible_name): def exec_(self, bible_name):
self.languageComboBox.addItem(u'') self.language_combo_box.addItem(u'')
if bible_name: if bible_name:
self.bibleLabel.setText(unicode(bible_name)) self.bible_label.setText(unicode(bible_name))
items = BiblesResourcesDB.get_languages() items = BiblesResourcesDB.get_languages()
self.languageComboBox.addItems([item[u'name'] for item in items]) self.language_combo_box.addItems([item[u'name'] for item in items])
return QDialog.exec_(self) return QDialog.exec_(self)
def accept(self): def accept(self):
if not self.languageComboBox.currentText(): if not self.language_combo_box.currentText():
critical_error_message_box( critical_error_message_box(message=translate('BiblesPlugin.LanguageForm', 'You need to choose a language.'))
message=translate('BiblesPlugin.LanguageForm', self.language_combo_box.setFocus()
'You need to choose a language.'))
self.languageComboBox.setFocus()
return False return False
else: else:
return QDialog.accept(self) return QDialog.accept(self)

View File

@ -38,9 +38,11 @@ from openlp.core.lib import Settings, translate
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
REFERENCE_MATCHES = {} REFERENCE_MATCHES = {}
REFERENCE_SEPARATORS = {} REFERENCE_SEPARATORS = {}
class LayoutStyle(object): class LayoutStyle(object):
""" """
An enumeration for bible screen layout styles. An enumeration for bible screen layout styles.
@ -62,8 +64,7 @@ class DisplayStyle(object):
class LanguageSelection(object): class LanguageSelection(object):
""" """
An enumeration for bible bookname language. An enumeration for bible bookname language. And standard strings for use throughout the bibles plugin.
And standard strings for use throughout the bibles plugin.
""" """
Bible = 0 Bible = 0
Application = 1 Application = 1
@ -178,8 +179,7 @@ class BibleStrings(object):
def update_reference_separators(): def update_reference_separators():
""" """
Updates separators and matches for parsing and formating scripture Updates separators and matches for parsing and formating scripture references.
references.
""" """
default_separators = translate('BiblesPlugin', default_separators = translate('BiblesPlugin',
':|v|V|verse|verses;;-|to;;,|and;;end Double-semicolon delimited separators for parsing references. ' ':|v|V|verse|verses;;-|to;;,|and;;end Double-semicolon delimited separators for parsing references. '
@ -245,9 +245,8 @@ def get_reference_match(match_type):
def parse_reference(reference, bible, language_selection, book_ref_id=False): def parse_reference(reference, bible, language_selection, book_ref_id=False):
""" """
This is the next generation über-awesome function that takes a person's This is the next generation über-awesome function that takes a person's typed in string and converts it to a list
typed in string and converts it to a list of references to be queried from of references to be queried from the Bible database files.
the Bible database files.
``reference`` ``reference``
A string. The Bible reference to parse. A string. The Bible reference to parse.
@ -256,16 +255,14 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
A object. The Bible database object. A object. The Bible database object.
``language_selection`` ``language_selection``
An int. The language selection the user has choosen in settings An int. The language selection the user has choosen in settings section.
section.
``book_ref_id`` ``book_ref_id``
A string. The book reference id. A string. The book reference id.
Returns ``None`` or a reference list. Returns ``None`` or a reference list.
The reference list is a list of tuples, with each tuple structured like The reference list is a list of tuples, with each tuple structured like this::
this::
(book, chapter, from_verse, to_verse) (book, chapter, from_verse, to_verse)
@ -275,8 +272,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
**Reference string details:** **Reference string details:**
Each reference starts with the book name and a chapter number. These are Each reference starts with the book name and a chapter number. These are both mandatory.
both mandatory.
* ``John 3`` refers to Gospel of John chapter 3 * ``John 3`` refers to Gospel of John chapter 3
@ -289,38 +285,31 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
* ``John 3:16`` refers to John chapter 3 verse 16 * ``John 3:16`` refers to John chapter 3 verse 16
* ``John 3:16-4:3`` refers to John chapter 3 verse 16 to chapter 4 verse 3 * ``John 3:16-4:3`` refers to John chapter 3 verse 16 to chapter 4 verse 3
After a verse reference all further single values are treat as verse in After a verse reference all further single values are treat as verse in the last selected chapter.
the last selected chapter.
* ``John 3:16-18`` refers to John chapter 3 verses 16 to 18 * ``John 3:16-18`` refers to John chapter 3 verses 16 to 18
After a list separator it is possible to refer to additional verses. They After a list separator it is possible to refer to additional verses. They are build analog to the first ones. This
are build analog to the first ones. This way it is possible to define each way it is possible to define each number of verse references. It is not possible to refer to verses in additional
number of verse references. It is not possible to refer to verses in books.
additional books.
* ``John 3:16,18`` refers to John chapter 3 verses 16 and 18 * ``John 3:16,18`` refers to John chapter 3 verses 16 and 18
* ``John 3:16-18,20`` refers to John chapter 3 verses 16 to 18 and 20 * ``John 3:16-18,20`` refers to John chapter 3 verses 16 to 18 and 20
* ``John 3:16-18,4:1`` refers to John chapter 3 verses 16 to 18 and * ``John 3:16-18,4:1`` refers to John chapter 3 verses 16 to 18 and chapter 4 verse 1
chapter 4 verse 1
If there is a range separator without further verse declaration the last If there is a range separator without further verse declaration the last refered chapter is addressed until the end.
refered chapter is addressed until the end.
``range_regex`` is a regular expression which matches for verse range ``range_regex`` is a regular expression which matches for verse range declarations:
declarations:
``(?:(?P<from_chapter>[0-9]+)%(sep_v)s)?`` ``(?:(?P<from_chapter>[0-9]+)%(sep_v)s)?``
It starts with a optional chapter reference ``from_chapter`` followed by It starts with a optional chapter reference ``from_chapter`` followed by a verse separator.
a verse separator.
``(?P<from_verse>[0-9]+)`` ``(?P<from_verse>[0-9]+)``
The verse reference ``from_verse`` is manditory The verse reference ``from_verse`` is manditory
``(?P<range_to>%(sep_r)s(?:`` ... ``|%(sep_e)s)?)?`` ``(?P<range_to>%(sep_r)s(?:`` ... ``|%(sep_e)s)?)?``
A ``range_to`` declaration is optional. It starts with a range separator A ``range_to`` declaration is optional. It starts with a range separator and contains optional a chapter and
and contains optional a chapter and verse declaration or a end verse declaration or a end separator.
separator.
``(?:(?P<to_chapter>[0-9]+)%(sep_v)s)?`` ``(?:(?P<to_chapter>[0-9]+)%(sep_v)s)?``
The ``to_chapter`` reference with separator is equivalent to group 1. The ``to_chapter`` reference with separator is equivalent to group 1.
@ -328,17 +317,15 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
``(?P<to_verse>[0-9]+)`` ``(?P<to_verse>[0-9]+)``
The ``to_verse`` reference is equivalent to group 2. The ``to_verse`` reference is equivalent to group 2.
The full reference is matched against get_reference_match(u'full'). This The full reference is matched against get_reference_match(u'full'). This regular expression looks like this:
regular expression looks like this:
``^\s*(?!\s)(?P<book>[\d]*[^\d]+)(?<!\s)\s*`` ``^\s*(?!\s)(?P<book>[\d]*[^\d]+)(?<!\s)\s*``
The ``book`` group starts with the first non-whitespace character. There The ``book`` group starts with the first non-whitespace character. There are optional leading digits followed by
are optional leading digits followed by non-digits. The group ends non-digits. The group ends before the whitspace in front of the next digit.
before the whitspace in front of the next digit.
``(?P<ranges>(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$`` ``(?P<ranges>(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$``
The second group contains all ``ranges``. This can be multiple The second group contains all ``ranges``. This can be multiple declarations of range_regex separated by a list
declarations of range_regex separated by a list separator. separator.
""" """
log.debug(u'parse_reference("%s")', reference) log.debug(u'parse_reference("%s")', reference)

View File

@ -27,8 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`cvsbible` modules provides a facility to import bibles from a set of The :mod:`cvsbible` modules provides a facility to import bibles from a set of CSV files.
CSV files.
The module expects two mandatory files containing the books and the verses. The module expects two mandatory files containing the books and the verses.
@ -55,8 +54,7 @@ There are two acceptable formats of the verses file. They are:
or or
"Genesis",1,2,"And the earth was without form, and void; and...." "Genesis",1,2,"And the earth was without form, and void; and...."
All CSV files are expected to use a comma (',') as the delimiter and double All CSV files are expected to use a comma (',') as the delimiter and double quotes ('"') as the quote symbol.
quotes ('"') as the quote symbol.
""" """
import logging import logging
import chardet import chardet
@ -65,8 +63,10 @@ import csv
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class CSVBible(BibleDB): class CSVBible(BibleDB):
""" """
This class provides a specialisation for importing of CSV Bibles. This class provides a specialisation for importing of CSV Bibles.
@ -75,9 +75,8 @@ class CSVBible(BibleDB):
def __init__(self, parent, **kwargs): def __init__(self, parent, **kwargs):
""" """
Loads a Bible from a set of CSV files. Loads a Bible from a set of CSV files. This class assumes the files contain all the information and a clean
This class assumes the files contain all the information and bible is being loaded.
a clean bible is being loaded.
""" """
log.info(self.__class__.__name__) log.info(self.__class__.__name__)
BibleDB.__init__(self, parent, **kwargs) BibleDB.__init__(self, parent, **kwargs)

View File

@ -511,7 +511,7 @@ class BibleDB(QtCore.QObject, Manager):
language = None language = None
language_form = LanguageForm(self.wizard) language_form = LanguageForm(self.wizard)
if language_form.exec_(bible_name): if language_form.exec_(bible_name):
language = unicode(language_form.languageComboBox.currentText()) language = unicode(language_form.language_combo_box.currentText())
if not language: if not language:
return False return False
language = BiblesResourcesDB.get_language(language) language = BiblesResourcesDB.get_language(language)

View File

@ -27,8 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`http` module enables OpenLP to retrieve scripture from bible The :mod:`http` module enables OpenLP to retrieve scripture from bible websites.
websites.
""" """
import logging import logging
import re import re
@ -36,7 +35,7 @@ import socket
import urllib import urllib
from HTMLParser import HTMLParseError from HTMLParser import HTMLParseError
from BeautifulSoup import BeautifulSoup, NavigableString, Tag from bs4 import BeautifulSoup, NavigableString, Tag
from openlp.core.lib import Registry, translate from openlp.core.lib import Registry, translate
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
@ -44,6 +43,9 @@ from openlp.core.utils import get_web_page
from openlp.plugins.bibles.lib import SearchResults from openlp.plugins.bibles.lib import SearchResults
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB, Book from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB, Book
CLEANER_REGEX = re.compile(r'&nbsp;|<br />|\'\+\'')
FIX_PUNKCTUATION_REGEX = re.compile(r'[ ]+([.,;])')
REDUCE_SPACES_REGEX = re.compile(r'[ ]{2,}')
UGLY_CHARS = { UGLY_CHARS = {
u'\u2014': u' - ', u'\u2014': u' - ',
u'\u2018': u'\'', u'\u2018': u'\'',
@ -52,9 +54,12 @@ UGLY_CHARS = {
u'\u201d': u'"', u'\u201d': u'"',
u'&nbsp;': u' ' u'&nbsp;': u' '
} }
VERSE_NUMBER_REGEX = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse.*')
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class BGExtract(object): class BGExtract(object):
""" """
Extract verses from BibleGateway Extract verses from BibleGateway
@ -78,9 +83,9 @@ class BGExtract(object):
An HTML class attribute for further qualification. An HTML class attribute for further qualification.
""" """
if class_: if class_:
all_tags = parent.findAll(tag, class_) all_tags = parent.find_all(tag, class_)
else: else:
all_tags = parent.findAll(tag) all_tags = parent.find_all(tag)
for element in all_tags: for element in all_tags:
element.extract() element.extract()
@ -93,14 +98,15 @@ class BGExtract(object):
""" """
if isinstance(tag, NavigableString): if isinstance(tag, NavigableString):
return None, unicode(tag) return None, unicode(tag)
elif tag.get('class') == 'versenum' or tag.get('class') == 'versenum mid-line': elif tag.get('class')[0] == "versenum" or tag.get('class')[0] == 'versenum mid-line':
verse = unicode(tag.string).replace('[', '').replace(']', '').strip() verse = unicode(tag.string).replace('[', '').replace(']', '').strip()
return verse, None return verse, None
elif tag.get('class') == 'chapternum': elif tag.get('class')[0] == 'chapternum':
verse = '1' verse = '1'
return verse, None return verse, None
else: else:
verse, text = None, '' verse = None
text = ''
for child in tag.contents: for child in tag.contents:
c_verse, c_text = self._extract_verse(child) c_verse, c_text = self._extract_verse(child)
if c_verse: if c_verse:
@ -137,7 +143,8 @@ class BGExtract(object):
tags = tags[::-1] tags = tags[::-1]
current_text = '' current_text = ''
for tag in tags: for tag in tags:
verse, text = None, '' verse = None
text = ''
for child in tag.contents: for child in tag.contents:
c_verse, c_text = self._extract_verse(child) c_verse, c_text = self._extract_verse(child)
if c_verse: if c_verse:
@ -173,8 +180,8 @@ class BGExtract(object):
def _extract_verses_old(self, div): def _extract_verses_old(self, div):
""" """
Use the old style of parsing for those Bibles on BG who mysteriously Use the old style of parsing for those Bibles on BG who mysteriously have not been migrated to the new (still
have not been migrated to the new (still broken) HTML. broken) HTML.
``div`` ``div``
The parent div. The parent div.
@ -185,13 +192,12 @@ class BGExtract(object):
if first_verse and first_verse.contents: if first_verse and first_verse.contents:
verse_list[1] = unicode(first_verse.contents[0]) verse_list[1] = unicode(first_verse.contents[0])
for verse in div(u'sup', u'versenum'): for verse in div(u'sup', u'versenum'):
raw_verse_num = verse.next raw_verse_num = verse.next_element
clean_verse_num = 0 clean_verse_num = 0
# Not all verses exist in all translations and may or may not be # Not all verses exist in all translations and may or may not be represented by a verse number. If they are
# represented by a verse number. If they are not fine, if they are # not fine, if they are it will probably be in a format that breaks int(). We will then have no idea what
# it will probably be in a format that breaks int(). We will then # garbage may be sucked in to the verse text so if we do not get a clean int() then ignore the verse
# have no idea what garbage may be sucked in to the verse text so # completely.
# if we do not get a clean int() then ignore the verse completely.
try: try:
clean_verse_num = int(str(raw_verse_num)) clean_verse_num = int(str(raw_verse_num))
except ValueError: except ValueError:
@ -201,16 +207,16 @@ class BGExtract(object):
except TypeError: except TypeError:
log.warn(u'Illegal verse number: %s', unicode(raw_verse_num)) log.warn(u'Illegal verse number: %s', unicode(raw_verse_num))
if clean_verse_num: if clean_verse_num:
verse_text = raw_verse_num.next verse_text = raw_verse_num.next_element
part = raw_verse_num.next.next part = raw_verse_num.next_element.next_element
while not (isinstance(part, Tag) and part.get(u'class') == u'versenum'): while not (isinstance(part, Tag) and part.get(u'class')[0] == u'versenum'):
# While we are still in the same verse grab all the text. # While we are still in the same verse grab all the text.
if isinstance(part, NavigableString): if isinstance(part, NavigableString):
verse_text += part verse_text += part
if isinstance(part.next, Tag) and part.next.name == u'div': if isinstance(part.next_element, Tag) and part.next_element.name == u'div':
# Run out of verses so stop. # Run out of verses so stop.
break break
part = part.next part = part.next_element
verse_list[clean_verse_num] = unicode(verse_text) verse_list[clean_verse_num] = unicode(verse_text)
return verse_list return verse_list
@ -230,15 +236,14 @@ class BGExtract(object):
log.debug(u'BGExtract.get_bible_chapter("%s", "%s", "%s")', version, book_name, chapter) log.debug(u'BGExtract.get_bible_chapter("%s", "%s", "%s")', version, book_name, chapter)
url_book_name = urllib.quote(book_name.encode("utf-8")) url_book_name = urllib.quote(book_name.encode("utf-8"))
url_params = u'search=%s+%s&version=%s' % (url_book_name, chapter, version) url_params = u'search=%s+%s&version=%s' % (url_book_name, chapter, version)
cleaner = [(re.compile('&nbsp;|<br />|\'\+\''), lambda match: '')]
soup = get_soup_for_bible_ref( soup = get_soup_for_bible_ref(
u'http://www.biblegateway.com/passage/?%s' % url_params, u'http://www.biblegateway.com/passage/?%s' % url_params,
pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='', cleaner=cleaner) pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='')
if not soup: if not soup:
return None return None
div = soup.find('div', 'result-text-style-normal') div = soup.find('div', 'result-text-style-normal')
self._clean_soup(div) self._clean_soup(div)
span_list = div.findAll('span', 'text') span_list = div.find_all('span', 'text')
log.debug('Span list: %s', span_list) log.debug('Span list: %s', span_list)
if not span_list: if not span_list:
# If we don't get any spans then we must have the old HTML format # If we don't get any spans then we must have the old HTML format
@ -282,7 +287,7 @@ class BGExtract(object):
self.application.process_events() self.application.process_events()
content = soup.find(u'table', u'infotable') content = soup.find(u'table', u'infotable')
if content: if content:
content = content.findAll(u'tr') content = content.find_all(u'tr')
if not content: if not content:
log.error(u'No books found in the Biblegateway response.') log.error(u'No books found in the Biblegateway response.')
send_error_message(u'parse') send_error_message(u'parse')
@ -341,19 +346,17 @@ class BSExtract(object):
log.error(u'No verses found in the Bibleserver response.') log.error(u'No verses found in the Bibleserver response.')
send_error_message(u'parse') send_error_message(u'parse')
return None return None
content = content.find(u'div').findAll(u'div') content = content.find(u'div').find_all(u'div')
verse_number = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse.*')
verses = {} verses = {}
for verse in content: for verse in content:
self.application.process_events() self.application.process_events()
versenumber = int(verse_number.sub(r'\3', verse[u'class'])) versenumber = int(VERSE_NUMBER_REGEX.sub(r'\3', u' '.join(verse[u'class'])))
verses[versenumber] = verse.contents[1].rstrip(u'\n') verses[versenumber] = verse.contents[1].rstrip(u'\n')
return SearchResults(book_name, chapter, verses) return SearchResults(book_name, chapter, verses)
def get_books_from_http(self, version): def get_books_from_http(self, version):
""" """
Load a list of all books a Bible contains from Bibleserver mobile Load a list of all books a Bible contains from Bibleserver mobile website.
website.
``version`` ``version``
The version of the Bible like NIV for New International Version The version of the Bible like NIV for New International Version
@ -369,9 +372,19 @@ class BSExtract(object):
log.error(u'No books found in the Bibleserver response.') log.error(u'No books found in the Bibleserver response.')
send_error_message(u'parse') send_error_message(u'parse')
return None return None
content = content.findAll(u'li') content = content.find_all(u'li')
return [book.contents[0].contents[0] for book in content] return [book.contents[0].contents[0] for book in content]
def _get_application(self):
"""
Adds the openlp to the class dynamically
"""
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
application = property(_get_application)
class CWExtract(object): class CWExtract(object):
""" """
@ -404,14 +417,12 @@ class CWExtract(object):
if not soup: if not soup:
return None return None
self.application.process_events() self.application.process_events()
html_verses = soup.findAll(u'span', u'versetext') html_verses = soup.find_all(u'span', u'versetext')
if not html_verses: if not html_verses:
log.error(u'No verses found in the CrossWalk response.') log.error(u'No verses found in the CrossWalk response.')
send_error_message(u'parse') send_error_message(u'parse')
return None return None
verses = {} verses = {}
reduce_spaces = re.compile(r'[ ]{2,}')
fix_punctuation = re.compile(r'[ ]+([.,;])')
for verse in html_verses: for verse in html_verses:
self.application.process_events() self.application.process_events()
verse_number = int(verse.contents[0].contents[0]) verse_number = int(verse.contents[0].contents[0])
@ -432,11 +443,10 @@ class CWExtract(object):
if isinstance(subsub, NavigableString): if isinstance(subsub, NavigableString):
verse_text += subsub verse_text += subsub
self.application.process_events() self.application.process_events()
# Fix up leading and trailing spaces, multiple spaces, and spaces # Fix up leading and trailing spaces, multiple spaces, and spaces between text and , and .
# between text and , and .
verse_text = verse_text.strip(u'\n\r\t ') verse_text = verse_text.strip(u'\n\r\t ')
verse_text = reduce_spaces.sub(u' ', verse_text) verse_text = REDUCE_SPACES_REGEX.sub(u' ', verse_text)
verse_text = fix_punctuation.sub(r'\1', verse_text) verse_text = FIX_PUNKCTUATION_REGEX.sub(r'\1', verse_text)
verses[verse_number] = verse_text verses[verse_number] = verse_text
return SearchResults(book_name, chapter, verses) return SearchResults(book_name, chapter, verses)
@ -458,7 +468,7 @@ class CWExtract(object):
log.error(u'No books found in the Crosswalk response.') log.error(u'No books found in the Crosswalk response.')
send_error_message(u'parse') send_error_message(u'parse')
return None return None
content = content.findAll(u'li') content = content.find_all(u'li')
books = [] books = []
for book in content: for book in content:
book = book.find(u'a') book = book.find(u'a')
@ -481,9 +491,8 @@ class HTTPBible(BibleDB):
def __init__(self, parent, **kwargs): def __init__(self, parent, **kwargs):
""" """
Finds all the bibles defined for the system Finds all the bibles defined for the system. Creates an Interface Object for each bible containing connection
Creates an Interface Object for each bible containing connection information.
information
Throws Exception if no Bibles are found. Throws Exception if no Bibles are found.
@ -492,8 +501,7 @@ class HTTPBible(BibleDB):
BibleDB.__init__(self, parent, **kwargs) BibleDB.__init__(self, parent, **kwargs)
self.download_source = kwargs[u'download_source'] self.download_source = kwargs[u'download_source']
self.download_name = kwargs[u'download_name'] self.download_name = kwargs[u'download_name']
# TODO: Clean up proxy stuff. We probably want one global proxy per # TODO: Clean up proxy stuff. We probably want one global proxy per connection type (HTTP and HTTPS) at most.
# connection type (HTTP and HTTPS) at most.
self.proxy_server = None self.proxy_server = None
self.proxy_username = None self.proxy_username = None
self.proxy_password = None self.proxy_password = None
@ -508,8 +516,8 @@ class HTTPBible(BibleDB):
def do_import(self, bible_name=None): def do_import(self, bible_name=None):
""" """
Run the import. This method overrides the parent class method. Returns Run the import. This method overrides the parent class method. Returns ``True`` on success, ``False`` on
``True`` on success, ``False`` on failure. failure.
""" """
self.wizard.progress_bar.setMaximum(68) self.wizard.progress_bar.setMaximum(68)
self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Bible and loading books...')) self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Bible and loading books...'))
@ -549,8 +557,7 @@ class HTTPBible(BibleDB):
if self.stop_import_flag: if self.stop_import_flag:
break break
self.wizard.increment_progress_bar(translate( self.wizard.increment_progress_bar(translate(
'BiblesPlugin.HTTPBible', 'Importing %s...', 'BiblesPlugin.HTTPBible', 'Importing %s...', 'Importing <book name>...') % book)
'Importing <book name>...') % book)
book_ref_id = self.get_book_ref_id_by_name(book, len(books), language_id) book_ref_id = self.get_book_ref_id_by_name(book, len(books), language_id)
if not book_ref_id: if not book_ref_id:
log.exception(u'Importing books from %s - download name: "%s" '\ log.exception(u'Importing books from %s - download name: "%s" '\
@ -567,22 +574,19 @@ class HTTPBible(BibleDB):
def get_verses(self, reference_list, show_error=True): def get_verses(self, reference_list, show_error=True):
""" """
A reimplementation of the ``BibleDB.get_verses`` method, this one is A reimplementation of the ``BibleDB.get_verses`` method, this one is specifically for web Bibles. It first
specifically for web Bibles. It first checks to see if the particular checks to see if the particular chapter exists in the DB, and if not it pulls it from the web. If the chapter
chapter exists in the DB, and if not it pulls it from the web. If the DOES exist, it simply pulls the verses from the DB using the ancestor method.
chapter DOES exist, it simply pulls the verses from the DB using the
ancestor method.
``reference_list`` ``reference_list``
This is the list of references the media manager item wants. It is This is the list of references the media manager item wants. It is a list of tuples, with the following
a list of tuples, with the following format:: format::
(book_reference_id, chapter, start_verse, end_verse) (book_reference_id, chapter, start_verse, end_verse)
Therefore, when you are looking for multiple items, simply break Therefore, when you are looking for multiple items, simply break them up into references like this, bundle
them up into references like this, bundle them into a list. This them into a list. This function then runs through the list, and returns an amalgamated list of ``Verse``
function then runs through the list, and returns an amalgamated objects. For example::
list of ``Verse`` objects. For example::
[(u'35', 1, 1, 1), (u'35', 2, 2, 3)] [(u'35', 1, 1, 1), (u'35', 2, 2, 3)]
""" """
@ -643,7 +647,7 @@ class HTTPBible(BibleDB):
Return the number of chapters in a particular book. Return the number of chapters in a particular book.
``book`` ``book``
The book object to get the chapter count for. The book object to get the chapter count for.
""" """
log.debug(u'HTTPBible.get_chapter_count("%s")', book.name) log.debug(u'HTTPBible.get_chapter_count("%s")', book.name)
return BiblesResourcesDB.get_chapter_count(book.book_reference_id) return BiblesResourcesDB.get_chapter_count(book.book_reference_id)
@ -671,8 +675,7 @@ class HTTPBible(BibleDB):
application = property(_get_application) application = property(_get_application)
def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre_parse_substitute=None):
pre_parse_substitute=None, cleaner=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.
@ -683,14 +686,11 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None,
An optional HTTP header to pass to the bible web server. An optional HTTP header to pass to the bible web server.
``pre_parse_regex`` ``pre_parse_regex``
A regular expression to run on the webpage. Allows manipulation of the A regular expression to run on the webpage. Allows manipulation of the webpage before passing to BeautifulSoup
webpage before passing to BeautifulSoup for parsing. for parsing.
``pre_parse_substitute`` ``pre_parse_substitute``
The text to replace any matches to the regular expression with. The text to replace any matches to the regular expression with.
``cleaner``
An optional regex to use during webpage parsing.
""" """
if not reference_url: if not reference_url:
return None return None
@ -703,10 +703,8 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None,
page_source = re.sub(pre_parse_regex, pre_parse_substitute, page_source) page_source = re.sub(pre_parse_regex, pre_parse_substitute, page_source)
soup = None soup = None
try: try:
if cleaner: soup = BeautifulSoup(page_source)
soup = BeautifulSoup(page_source, markupMassage=cleaner) CLEANER_REGEX.sub(u'', unicode(soup))
else:
soup = BeautifulSoup(page_source)
except HTMLParseError: except HTMLParseError:
log.exception(u'BeautifulSoup could not parse the bible page.') log.exception(u'BeautifulSoup could not parse the bible page.')
if not soup: if not soup:
@ -715,6 +713,7 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None,
Registry().get(u'application').process_events() Registry().get(u'application').process_events()
return soup return soup
def send_error_message(error_type): def send_error_message(error_type):
""" """
Send a standard error message informing the user of an issue. Send a standard error message informing the user of an issue.

View File

@ -38,28 +38,21 @@ from csvbible import CSVBible
from http import HTTPBible from http import HTTPBible
from opensong import OpenSongBible from opensong import OpenSongBible
from osis import OSISBible from osis import OSISBible
# Imports that might fail.
try:
from openlp1 import OpenLP1Bible
HAS_OPENLP1 = True
except ImportError:
HAS_OPENLP1 = False
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class BibleFormat(object): class BibleFormat(object):
""" """
This is a special enumeration class that holds the various types of Bibles, This is a special enumeration class that holds the various types of Bibles.
plus a few helper functions to facilitate generic handling of Bible types
for importing.
""" """
_format_availability = {}
Unknown = -1 Unknown = -1
OSIS = 0 OSIS = 0
CSV = 1 CSV = 1
OpenSong = 2 OpenSong = 2
WebDownload = 3 WebDownload = 3
OpenLP1 = 4
@staticmethod @staticmethod
def get_class(format): def get_class(format):
@ -77,8 +70,6 @@ class BibleFormat(object):
return OpenSongBible return OpenSongBible
elif format == BibleFormat.WebDownload: elif format == BibleFormat.WebDownload:
return HTTPBible return HTTPBible
elif format == BibleFormat.OpenLP1:
return OpenLP1Bible
else: else:
return None return None
@ -92,17 +83,8 @@ class BibleFormat(object):
BibleFormat.CSV, BibleFormat.CSV,
BibleFormat.OpenSong, BibleFormat.OpenSong,
BibleFormat.WebDownload, BibleFormat.WebDownload,
BibleFormat.OpenLP1
] ]
@staticmethod
def set_availability(format, available):
BibleFormat._format_availability[format] = available
@staticmethod
def get_availability(format):
return BibleFormat._format_availability.get(format, True)
class BibleManager(object): class BibleManager(object):
""" """
@ -463,6 +445,5 @@ class BibleManager(object):
main_window = property(_get_main_window) main_window = property(_get_main_window)
BibleFormat.set_availability(BibleFormat.OpenLP1, HAS_OPENLP1)
__all__ = [u'BibleFormat'] __all__ = [u'BibleFormat']

View File

@ -61,8 +61,8 @@ class BibleMediaItem(MediaManagerItem):
def __init__(self, parent, plugin): def __init__(self, parent, plugin):
self.icon_path = u'songs/song' self.icon_path = u'songs/song'
self.lockIcon = build_icon(u':/bibles/bibles_search_lock.png') self.lock_icon = build_icon(u':/bibles/bibles_search_lock.png')
self.unlockIcon = build_icon(u':/bibles/bibles_search_unlock.png') self.unlock_icon = build_icon(u':/bibles/bibles_search_unlock.png')
MediaManagerItem.__init__(self, parent, plugin) MediaManagerItem.__init__(self, parent, plugin)
# Place to store the search results for both bibles. # Place to store the search results for both bibles.
self.settings = self.plugin.settings_tab self.settings = self.plugin.settings_tab
@ -73,7 +73,7 @@ class BibleMediaItem(MediaManagerItem):
self.check_search_result() self.check_search_result()
Registry().register_function(u'bibles_load_list', self.reload_bibles) Registry().register_function(u'bibles_load_list', self.reload_bibles)
def __checkSecondBible(self, bible, second_bible): def __check_second_bible(self, bible, second_bible):
""" """
Check if the first item is a second bible item or not. Check if the first item is a second bible item or not.
""" """
@ -84,7 +84,7 @@ class BibleMediaItem(MediaManagerItem):
self.displayResults(bible, second_bible) self.displayResults(bible, second_bible)
return return
else: else:
item_second_bible = self._decodeQtObject(bitem, 'second_bible') item_second_bible = self._decode_qt_object(bitem, 'second_bible')
if item_second_bible and second_bible or not item_second_bible and not second_bible: if item_second_bible and second_bible or not item_second_bible and not second_bible:
self.displayResults(bible, second_bible) self.displayResults(bible, second_bible)
elif critical_error_message_box( elif critical_error_message_box(
@ -95,7 +95,7 @@ class BibleMediaItem(MediaManagerItem):
self.list_view.clear() self.list_view.clear()
self.displayResults(bible, second_bible) self.displayResults(bible, second_bible)
def _decodeQtObject(self, bitem, key): def _decode_qt_object(self, bitem, key):
reference = bitem.data(QtCore.Qt.UserRole) reference = bitem.data(QtCore.Qt.UserRole)
obj = reference[unicode(key)] obj = reference[unicode(key)]
return unicode(obj).strip() return unicode(obj).strip()
@ -159,7 +159,7 @@ class BibleMediaItem(MediaManagerItem):
search_button_layout.setObjectName(prefix + u'search_button_layout') search_button_layout.setObjectName(prefix + u'search_button_layout')
search_button_layout.addStretch() search_button_layout.addStretch()
lockButton = QtGui.QToolButton(tab) lockButton = QtGui.QToolButton(tab)
lockButton.setIcon(self.unlockIcon) lockButton.setIcon(self.unlock_icon)
lockButton.setCheckable(True) lockButton.setCheckable(True)
lockButton.setObjectName(prefix + u'LockButton') lockButton.setObjectName(prefix + u'LockButton')
search_button_layout.addWidget(lockButton) search_button_layout.addWidget(lockButton)
@ -250,7 +250,7 @@ class BibleMediaItem(MediaManagerItem):
self.quickSearchEdit.returnPressed.connect(self.onQuickSearchButton) self.quickSearchEdit.returnPressed.connect(self.onQuickSearchButton)
self.searchTabBar.currentChanged.connect(self.onSearchTabBarCurrentChanged) self.searchTabBar.currentChanged.connect(self.onSearchTabBarCurrentChanged)
def onFocus(self): def on_focus(self):
if self.quickTab.isVisible(): if self.quickTab.isVisible():
self.quickSearchEdit.setFocus() self.quickSearchEdit.setFocus()
else: else:
@ -509,9 +509,9 @@ class BibleMediaItem(MediaManagerItem):
def onLockButtonToggled(self, checked): def onLockButtonToggled(self, checked):
if checked: if checked:
self.sender().setIcon(self.lockIcon) self.sender().setIcon(self.lock_icon)
else: else:
self.sender().setIcon(self.unlockIcon) self.sender().setIcon(self.unlock_icon)
def onQuickStyleComboBoxChanged(self): def onQuickStyleComboBoxChanged(self):
self.settings.layout_style = self.quickStyleComboBox.currentIndex() self.settings.layout_style = self.quickStyleComboBox.currentIndex()
@ -633,7 +633,7 @@ class BibleMediaItem(MediaManagerItem):
if not self.advancedLockButton.isChecked(): if not self.advancedLockButton.isChecked():
self.list_view.clear() self.list_view.clear()
if self.list_view.count() != 0: if self.list_view.count() != 0:
self.__checkSecondBible(bible, second_bible) self.__check_second_bible(bible, second_bible)
elif self.search_results: elif self.search_results:
self.displayResults(bible, second_bible) self.displayResults(bible, second_bible)
self.advancedSearchButton.setEnabled(True) self.advancedSearchButton.setEnabled(True)
@ -689,7 +689,7 @@ class BibleMediaItem(MediaManagerItem):
if not self.quickLockButton.isChecked(): if not self.quickLockButton.isChecked():
self.list_view.clear() self.list_view.clear()
if self.list_view.count() != 0 and self.search_results: if self.list_view.count() != 0 and self.search_results:
self.__checkSecondBible(bible, second_bible) self.__check_second_bible(bible, second_bible)
elif self.search_results: elif self.search_results:
self.displayResults(bible, second_bible) self.displayResults(bible, second_bible)
self.quickSearchButton.setEnabled(True) self.quickSearchButton.setEnabled(True)
@ -787,19 +787,19 @@ class BibleMediaItem(MediaManagerItem):
raw_title = [] raw_title = []
verses = VerseReferenceList() verses = VerseReferenceList()
for bitem in items: for bitem in items:
book = self._decodeQtObject(bitem, 'book') book = self._decode_qt_object(bitem, 'book')
chapter = int(self._decodeQtObject(bitem, 'chapter')) chapter = int(self._decode_qt_object(bitem, 'chapter'))
verse = int(self._decodeQtObject(bitem, 'verse')) verse = int(self._decode_qt_object(bitem, 'verse'))
bible = self._decodeQtObject(bitem, 'bible') bible = self._decode_qt_object(bitem, 'bible')
version = self._decodeQtObject(bitem, 'version') version = self._decode_qt_object(bitem, 'version')
copyright = self._decodeQtObject(bitem, 'copyright') copyright = self._decode_qt_object(bitem, 'copyright')
permissions = self._decodeQtObject(bitem, 'permissions') permissions = self._decode_qt_object(bitem, 'permissions')
text = self._decodeQtObject(bitem, 'text') text = self._decode_qt_object(bitem, 'text')
second_bible = self._decodeQtObject(bitem, 'second_bible') second_bible = self._decode_qt_object(bitem, 'second_bible')
second_version = self._decodeQtObject(bitem, 'second_version') second_version = self._decode_qt_object(bitem, 'second_version')
second_copyright = self._decodeQtObject(bitem, 'second_copyright') second_copyright = self._decode_qt_object(bitem, 'second_copyright')
second_permissions = self._decodeQtObject(bitem, 'second_permissions') second_permissions = self._decode_qt_object(bitem, 'second_permissions')
second_text = self._decodeQtObject(bitem, 'second_text') second_text = self._decode_qt_object(bitem, 'second_text')
verses.add(book, chapter, verse, version, copyright, permissions) verses.add(book, chapter, verse, version, copyright, permissions)
verse_text = self.formatVerse(old_chapter, chapter, verse) verse_text = self.formatVerse(old_chapter, chapter, verse)
if second_bible: if second_bible:
@ -867,13 +867,13 @@ class BibleMediaItem(MediaManagerItem):
""" """
verse_separator = get_reference_separator(u'sep_v_display') verse_separator = get_reference_separator(u'sep_v_display')
range_separator = get_reference_separator(u'sep_r_display') range_separator = get_reference_separator(u'sep_r_display')
old_chapter = self._decodeQtObject(old_bitem, 'chapter') old_chapter = self._decode_qt_object(old_bitem, 'chapter')
old_verse = self._decodeQtObject(old_bitem, 'verse') old_verse = self._decode_qt_object(old_bitem, 'verse')
start_book = self._decodeQtObject(start_bitem, 'book') start_book = self._decode_qt_object(start_bitem, 'book')
start_chapter = self._decodeQtObject(start_bitem, 'chapter') start_chapter = self._decode_qt_object(start_bitem, 'chapter')
start_verse = self._decodeQtObject(start_bitem, 'verse') start_verse = self._decode_qt_object(start_bitem, 'verse')
start_bible = self._decodeQtObject(start_bitem, 'bible') start_bible = self._decode_qt_object(start_bitem, 'bible')
start_second_bible = self._decodeQtObject(start_bitem, 'second_bible') start_second_bible = self._decode_qt_object(start_bitem, 'second_bible')
if start_second_bible: if start_second_bible:
bibles = u'%s, %s' % (start_bible, start_second_bible) bibles = u'%s, %s' % (start_bible, start_second_bible)
else: else:
@ -901,16 +901,16 @@ class BibleMediaItem(MediaManagerItem):
The item we were previously dealing with. The item we were previously dealing with.
""" """
# Get all the necessary meta data. # Get all the necessary meta data.
book = self._decodeQtObject(bitem, 'book') book = self._decode_qt_object(bitem, 'book')
chapter = int(self._decodeQtObject(bitem, 'chapter')) chapter = int(self._decode_qt_object(bitem, 'chapter'))
verse = int(self._decodeQtObject(bitem, 'verse')) verse = int(self._decode_qt_object(bitem, 'verse'))
bible = self._decodeQtObject(bitem, 'bible') bible = self._decode_qt_object(bitem, 'bible')
second_bible = self._decodeQtObject(bitem, 'second_bible') second_bible = self._decode_qt_object(bitem, 'second_bible')
old_book = self._decodeQtObject(old_bitem, 'book') old_book = self._decode_qt_object(old_bitem, 'book')
old_chapter = int(self._decodeQtObject(old_bitem, 'chapter')) old_chapter = int(self._decode_qt_object(old_bitem, 'chapter'))
old_verse = int(self._decodeQtObject(old_bitem, 'verse')) old_verse = int(self._decode_qt_object(old_bitem, 'verse'))
old_bible = self._decodeQtObject(old_bitem, 'bible') old_bible = self._decode_qt_object(old_bitem, 'bible')
old_second_bible = self._decodeQtObject(old_bitem, 'second_bible') old_second_bible = self._decode_qt_object(old_bitem, 'second_bible')
if old_bible != bible or old_second_bible != second_bible or old_book != book: if old_bible != bible or old_second_bible != second_bible or old_book != book:
# The bible, second bible or book has changed. # The bible, second bible or book has changed.
return True return True

View File

@ -1,113 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 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 #
###############################################################################
import logging
import sqlite
import sys
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
log = logging.getLogger(__name__)
class OpenLP1Bible(BibleDB):
"""
This class provides the OpenLPv1 bible importer.
"""
def __init__(self, parent, **kwargs):
"""
Constructor.
"""
log.debug(self.__class__.__name__)
BibleDB.__init__(self, parent, **kwargs)
self.filename = kwargs[u'filename']
def do_import(self, bible_name=None):
"""
Imports an openlp.org v1 bible.
"""
connection = None
cursor = None
try:
connection = sqlite.connect(self.filename.encode(sys.getfilesystemencoding()))
cursor = connection.cursor()
except sqlite.DatabaseError:
log.exception(u'File "%s" is encrypted or not a sqlite database, '
'therefore not an openlp.org 1.x database either' % self.filename)
# Please add an user error here!
# This file is not an openlp.org 1.x bible database.
return False
#Create the bible language
language_id = self.get_language(bible_name)
if not language_id:
log.exception(u'Importing books from "%s" failed' % self.filename)
return False
# Create all books.
try:
cursor.execute(u'SELECT id, testament_id, name, abbreviation FROM book')
except sqlite.DatabaseError as error:
log.exception(u'DatabaseError: %s' % error)
# Please add an user error here!
# This file is not an openlp.org 1.x bible database.
return False
books = cursor.fetchall()
self.wizard.progress_bar.setMaximum(len(books) + 1)
for book in books:
if self.stop_import_flag:
connection.close()
return False
book_id = int(book[0])
testament_id = int(book[1])
name = unicode(book[2], u'cp1252')
abbreviation = unicode(book[3], u'cp1252')
book_ref_id = self.get_book_ref_id_by_name(name, len(books),
language_id)
if not book_ref_id:
log.exception(u'Importing books from "%s" failed' % self.filename)
return False
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.create_book(name, book_ref_id, book_details[u'testament_id'])
# Update the progess bar.
self.wizard.increment_progress_bar(WizardStrings.ImportingType % name)
# Import the verses for this book.
cursor.execute(u'SELECT chapter, verse, text || \'\' AS text FROM '
'verse WHERE book_id=%s' % book_id)
verses = cursor.fetchall()
for verse in verses:
if self.stop_import_flag:
connection.close()
return False
chapter = int(verse[0])
verse_number = int(verse[1])
text = unicode(verse[2], u'cp1252')
self.create_verse(db_book.id, chapter, verse_number, text)
self.application.process_events()
self.session.commit()
connection.close()
return True

View File

@ -34,16 +34,18 @@ from openlp.core.lib import translate
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class OpenSongBible(BibleDB): class OpenSongBible(BibleDB):
""" """
OpenSong Bible format importer class. OpenSong Bible format importer class.
""" """
def __init__(self, parent, **kwargs): def __init__(self, parent, **kwargs):
""" """
Constructor to create and set up an instance of the OpenSongBible Constructor to create and set up an instance of the OpenSongBible class. This class is used to import Bibles
class. This class is used to import Bibles from OpenSong's XML format. from OpenSong's XML format.
""" """
log.debug(self.__class__.__name__) log.debug(self.__class__.__name__)
BibleDB.__init__(self, parent, **kwargs) BibleDB.__init__(self, parent, **kwargs)
@ -75,9 +77,8 @@ class OpenSongBible(BibleDB):
file = None file = None
success = True success = True
try: try:
# NOTE: We don't need to do any of the normal encoding detection # NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding
# here, because lxml does it's own encoding detection, and the two # detection, and the two mechanisms together interfere with each other.
# mechanisms together interfere with each other.
file = open(self.filename, u'r') file = open(self.filename, u'r')
opensong = objectify.parse(file) opensong = objectify.parse(file)
bible = opensong.getroot() bible = opensong.getroot()
@ -116,16 +117,11 @@ class OpenSongBible(BibleDB):
if len(verse_parts) > 1: if len(verse_parts) > 1:
number = int(verse_parts[0]) number = int(verse_parts[0])
except TypeError: except TypeError:
log.warn(u'Illegal verse number: %s', log.warn(u'Illegal verse number: %s', unicode(verse.attrib[u'n']))
unicode(verse.attrib[u'n']))
verse_number = number verse_number = number
else: else:
verse_number += 1 verse_number += 1
self.create_verse( self.create_verse(db_book.id, chapter_number, verse_number, self.get_text(verse))
db_book.id,
chapter_number,
verse_number,
self.get_text(verse))
self.wizard.increment_progress_bar(translate('BiblesPlugin.Opensong', 'Importing %s %s...', self.wizard.increment_progress_bar(translate('BiblesPlugin.Opensong', 'Importing %s %s...',
'Importing <book name> <chapter>...')) % (db_book.name, chapter_number) 'Importing <book name> <chapter>...')) % (db_book.name, chapter_number)
self.session.commit() self.session.commit()

View File

@ -39,9 +39,11 @@ from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def replacement(match): def replacement(match):
return match.group(2).upper() return match.group(2).upper()
class OSISBible(BibleDB): class OSISBible(BibleDB):
""" """
`OSIS <http://www.bibletechnologies.net/>`_ Bible format importer class. `OSIS <http://www.bibletechnologies.net/>`_ Bible format importer class.
@ -53,8 +55,7 @@ class OSISBible(BibleDB):
BibleDB.__init__(self, parent, **kwargs) BibleDB.__init__(self, parent, **kwargs)
self.filename = kwargs[u'filename'] self.filename = kwargs[u'filename']
self.language_regex = re.compile(r'<language.*>(.*?)</language>') self.language_regex = re.compile(r'<language.*>(.*?)</language>')
self.verse_regex = re.compile( self.verse_regex = re.compile(r'<verse osisID="([a-zA-Z0-9 ]*).([0-9]*).([0-9]*)">(.*?)</verse>')
r'<verse osisID="([a-zA-Z0-9 ]*).([0-9]*).([0-9]*)">(.*?)</verse>')
self.note_regex = re.compile(r'<note(.*?)>(.*?)</note>') self.note_regex = re.compile(r'<note(.*?)>(.*?)</note>')
self.title_regex = re.compile(r'<title(.*?)>(.*?)</title>') self.title_regex = re.compile(r'<title(.*?)>(.*?)</title>')
self.milestone_regex = re.compile(r'<milestone(.*?)/>') self.milestone_regex = re.compile(r'<milestone(.*?)/>')
@ -68,8 +69,7 @@ class OSISBible(BibleDB):
self.q1_regex = re.compile(r'<q(.*?)level="1"(.*?)>') self.q1_regex = re.compile(r'<q(.*?)level="1"(.*?)>')
self.q2_regex = re.compile(r'<q(.*?)level="2"(.*?)>') self.q2_regex = re.compile(r'<q(.*?)level="2"(.*?)>')
self.trans_regex = re.compile(r'<transChange(.*?)>(.*?)</transChange>') self.trans_regex = re.compile(r'<transChange(.*?)>(.*?)</transChange>')
self.divine_name_regex = re.compile( self.divine_name_regex = re.compile(r'<divineName(.*?)>(.*?)</divineName>')
r'<divineName(.*?)>(.*?)</divineName>')
self.spaces_regex = re.compile(r'([ ]{2,})') self.spaces_regex = re.compile(r'([ ]{2,})')
filepath = os.path.join( filepath = os.path.join(
AppLocation.get_directory(AppLocation.PluginsDir), u'bibles', u'resources', u'osisbooks.csv') AppLocation.get_directory(AppLocation.PluginsDir), u'bibles', u'resources', u'osisbooks.csv')
@ -158,10 +158,8 @@ class OSISBible(BibleDB):
self.wizard.increment_progress_bar(translate('BiblesPlugin.OsisImport', 'Importing %s %s...', self.wizard.increment_progress_bar(translate('BiblesPlugin.OsisImport', 'Importing %s %s...',
'Importing <book name> <chapter>...') % (book_details[u'name'], chapter)) 'Importing <book name> <chapter>...') % (book_details[u'name'], chapter))
last_chapter = chapter last_chapter = chapter
# All of this rigmarol below is because the mod2osis # All of this rigmarol below is because the mod2osis tool from the Sword library embeds XML in the
# tool from the Sword library embeds XML in the OSIS # OSIS but neglects to enclose the verse text (with XML) in <[CDATA[ ]]> tags.
# but neglects to enclose the verse text (with XML) in
# <[CDATA[ ]]> tags.
verse_text = self.note_regex.sub(u'', verse_text) verse_text = self.note_regex.sub(u'', verse_text)
verse_text = self.title_regex.sub(u'', verse_text) verse_text = self.title_regex.sub(u'', verse_text)
verse_text = self.milestone_regex.sub(u'', verse_text) verse_text = self.milestone_regex.sub(u'', verse_text)

View File

@ -27,8 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`upgrade` module provides a way for the database and schema that is the The :mod:`upgrade` module provides a way for the database and schema that is the backend for the Bibles plugin.
backend for the Bibles plugin
""" """
import logging import logging

View File

@ -29,8 +29,8 @@
class VerseReferenceList(object): class VerseReferenceList(object):
""" """
The VerseReferenceList class encapsulates a list of verse references, but The VerseReferenceList class encapsulates a list of verse references, but maintains the order in which they were
maintains the order in which they were added. added.
""" """
def __init__(self): def __init__(self):

View File

@ -257,6 +257,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
# We must have at least one slide. # We must have at least one slide.
if self.slide_list_view.count() == 0: if self.slide_list_view.count() == 0:
critical_error_message_box(message=translate('CustomPlugin.EditCustomForm', critical_error_message_box(message=translate('CustomPlugin.EditCustomForm',
'You need to add at least one slide')) 'You need to add at least one slide.'))
return False return False
return True return True

View File

@ -40,6 +40,7 @@ from openlp.plugins.custom.lib.db import CustomSlide
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class CustomSearch(object): class CustomSearch(object):
""" """
An enumeration for custom search methods. An enumeration for custom search methods.
@ -63,14 +64,14 @@ class CustomMediaItem(MediaManagerItem):
self.has_search = True self.has_search = True
# Holds information about whether the edit is remotely triggered and # Holds information about whether the edit is remotely triggered and
# which Custom is required. # which Custom is required.
self.remoteCustom = -1 self.remote_custom = -1
self.manager = plugin.manager self.manager = plugin.manager
def add_end_header_bar(self): def add_end_header_bar(self):
self.toolbar.addSeparator() self.toolbar.addSeparator()
self.add_search_to_toolbar() self.add_search_to_toolbar()
# Signals and slots # Signals and slots
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'cleared()'), self.onClearTextButtonClick) QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'cleared()'), self.on_clear_text_button_click)
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'searchTypeChanged(int)'), QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'searchTypeChanged(int)'),
self.on_search_text_button_clicked) self.on_search_text_button_clicked)
Registry().register_function(u'custom_load_list', self.load_list) Registry().register_function(u'custom_load_list', self.load_list)
@ -119,14 +120,13 @@ class CustomMediaItem(MediaManagerItem):
def on_new_click(self): def on_new_click(self):
self.edit_custom_form.load_custom(0) self.edit_custom_form.load_custom(0)
self.edit_custom_form.exec_() self.edit_custom_form.exec_()
self.onClearTextButtonClick() self.on_clear_text_button_click()
self.on_selection_change() self.on_selection_change()
def onRemoteEdit(self, custom_id, preview=False): def on_remote_edit(self, custom_id, preview=False):
""" """
Called by ServiceManager or SlideController by event passing Called by ServiceManager or SlideController by event passing the custom Id in the payload along with an
the custom Id in the payload along with an indicator to say which indicator to say which type of display is required.
type of display is required.
""" """
custom_id = int(custom_id) custom_id = int(custom_id)
valid = self.manager.get_object(CustomSlide, custom_id) valid = self.manager.get_object(CustomSlide, custom_id)
@ -134,12 +134,12 @@ class CustomMediaItem(MediaManagerItem):
self.edit_custom_form.load_custom(custom_id, preview) self.edit_custom_form.load_custom(custom_id, preview)
if self.edit_custom_form.exec_() == QtGui.QDialog.Accepted: if self.edit_custom_form.exec_() == QtGui.QDialog.Accepted:
self.remote_triggered = True self.remote_triggered = True
self.remoteCustom = custom_id self.remote_custom = custom_id
self.auto_select_id = -1 self.auto_select_id = -1
self.on_search_text_button_clicked() self.on_search_text_button_clicked()
item = self.build_service_item(remote=True) item = self.build_service_item(remote=True)
self.remote_triggered = None self.remote_triggered = None
self.remoteCustom = 1 self.remote_custom = 1
if item: if item:
return item return item
return None return None
@ -163,22 +163,21 @@ class CustomMediaItem(MediaManagerItem):
if check_item_selected(self.list_view, UiStrings().SelectDelete): if check_item_selected(self.list_view, UiStrings().SelectDelete):
items = self.list_view.selectedIndexes() items = self.list_view.selectedIndexes()
if QtGui.QMessageBox.question(self, if QtGui.QMessageBox.question(self,
UiStrings().ConfirmDelete, UiStrings().ConfirmDelete,
translate('CustomPlugin.MediaItem', translate('CustomPlugin.MediaItem',
'Are you sure you want to delete the %n selected custom slide(s)?', '', 'Are you sure you want to delete the %n selected custom slide(s)?', '',
QtCore.QCoreApplication.CodecForTr, len(items)), QtCore.QCoreApplication.CodecForTr, len(items)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No: QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
return return
row_list = [item.row() for item in self.list_view.selectedIndexes()] row_list = [item.row() for item in self.list_view.selectedIndexes()]
row_list.sort(reverse=True) row_list.sort(reverse=True)
id_list = [(item.data(QtCore.Qt.UserRole)) id_list = [(item.data(QtCore.Qt.UserRole)) for item in self.list_view.selectedIndexes()]
for item in self.list_view.selectedIndexes()]
for id in id_list: for id in id_list:
self.plugin.manager.delete_object(CustomSlide, id) self.plugin.manager.delete_object(CustomSlide, id)
self.on_search_text_button_clicked() self.on_search_text_button_clicked()
def onFocus(self): def on_focus(self):
self.search_text_edit.setFocus() self.search_text_edit.setFocus()
def generate_slide_data(self, service_item, item=None, xmlVersion=False, def generate_slide_data(self, service_item, item=None, xmlVersion=False,
@ -186,20 +185,20 @@ class CustomMediaItem(MediaManagerItem):
""" """
Generate the slide data. Needs to be implemented by the plugin. Generate the slide data. Needs to be implemented by the plugin.
""" """
item_id = self._get_id_of_item_to_generate(item, self.remoteCustom) item_id = self._get_id_of_item_to_generate(item, self.remote_custom)
service_item.add_capability(ItemCapabilities.CanEdit) service_item.add_capability(ItemCapabilities.CanEdit)
service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.CanLoop) service_item.add_capability(ItemCapabilities.CanLoop)
service_item.add_capability(ItemCapabilities.CanSoftBreak) service_item.add_capability(ItemCapabilities.CanSoftBreak)
service_item.add_capability(ItemCapabilities.OnLoadUpdate) service_item.add_capability(ItemCapabilities.OnLoadUpdate)
customSlide = self.plugin.manager.get_object(CustomSlide, item_id) custom_slide = self.plugin.manager.get_object(CustomSlide, item_id)
title = customSlide.title title = custom_slide.title
credit = customSlide.credits credit = custom_slide.credits
service_item.edit_id = item_id service_item.edit_id = item_id
theme = customSlide.theme_name theme = custom_slide.theme_name
if theme: if theme:
service_item.theme = theme service_item.theme = theme
custom_xml = CustomXMLParser(customSlide.text) custom_xml = CustomXMLParser(custom_slide.text)
verse_list = custom_xml.get_verses() verse_list = custom_xml.get_verses()
raw_slides = [verse[1] for verse in verse_list] raw_slides = [verse[1] for verse in verse_list]
service_item.title = title service_item.title = title
@ -216,7 +215,6 @@ class CustomMediaItem(MediaManagerItem):
Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type()) Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
# Reload the list considering the new search type. # Reload the list considering the new search type.
search_keywords = self.search_text_edit.displayText() search_keywords = self.search_text_edit.displayText()
search_results = []
search_type = self.search_text_edit.current_search_type() search_type = self.search_text_edit.current_search_type()
if search_type == CustomSearch.Titles: if search_type == CustomSearch.Titles:
log.debug(u'Titles Search') log.debug(u'Titles Search')
@ -234,15 +232,14 @@ class CustomMediaItem(MediaManagerItem):
def on_search_text_edit_changed(self, text): def on_search_text_edit_changed(self, text):
""" """
If search as type enabled invoke the search on each key press. If search as type enabled invoke the search on each key press. If the Title is being searched do not start until
If the Title is being searched do not start until 2 characters 2 characters have been entered.
have been entered.
""" """
search_length = 2 search_length = 2
if len(text) > search_length: if len(text) > search_length:
self.on_search_text_button_clicked() self.on_search_text_button_clicked()
elif not text: elif not text:
self.onClearTextButtonClick() self.on_clear_text_button_click()
def service_load(self, item): def service_load(self, item):
""" """
@ -255,7 +252,8 @@ class CustomMediaItem(MediaManagerItem):
and_(CustomSlide.title == item.title, CustomSlide.theme_name == item.theme, and_(CustomSlide.title == item.title, CustomSlide.theme_name == item.theme,
CustomSlide.credits == item.raw_footer[0][len(item.title) + 1:])) CustomSlide.credits == item.raw_footer[0][len(item.title) + 1:]))
if custom: if custom:
self.service_manager.service_item_update(custom.id, item.unique_identifier) item.edit_id = custom.id
return item
else: else:
if self.add_custom_from_service: if self.add_custom_from_service:
self.create_from_service_item(item) self.create_from_service_item(item)
@ -284,10 +282,8 @@ class CustomMediaItem(MediaManagerItem):
custom.text = unicode(custom_xml.extract_xml(), u'utf-8') custom.text = unicode(custom_xml.extract_xml(), u'utf-8')
self.plugin.manager.save_object(custom) self.plugin.manager.save_object(custom)
self.on_search_text_button_clicked() self.on_search_text_button_clicked()
if item.name.lower() == u'custom':
Registry().execute(u'service_item_update', u'%s:%s:%s' % (custom.id, item.unique_identifier, False))
def onClearTextButtonClick(self): def on_clear_text_button_click(self):
""" """
Clear the search text. Clear the search text.
""" """

View File

@ -27,6 +27,6 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`images` module provides the Images plugin. The Images plugin The :mod:`images` module provides the Images plugin. The Images plugin provides the facility to display images from
provides the facility to display images from OpenLP. OpenLP.
""" """

View File

@ -27,20 +27,16 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
Forms in OpenLP are made up of two classes. One class holds all the graphical Forms in OpenLP are made up of two classes. One class holds all the graphical elements, like buttons and lists, and the
elements, like buttons and lists, and the other class holds all the functional other class holds all the functional code, like slots and loading and saving.
code, like slots and loading and saving.
The first class, commonly known as the **Dialog** class, is typically named The first class, commonly known as the **Dialog** class, is typically named ``Ui_<name>Dialog``. It is a slightly
``Ui_<name>Dialog``. It is a slightly modified version of the class that the modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings.
converting most strings from "" to u'' and using OpenLP's ``translate()``
function for translating strings.
The second class, commonly known as the **Form** class, is typically named The second class, commonly known as the **Form** class, is typically named ``<name>Form``. This class is the one which
``<name>Form``. This class is the one which is instantiated and used. It uses is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned
dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class above, like so::
mentioned above, like so::
class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog): class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
@ -48,9 +44,8 @@ mentioned above, like so::
QtGui.QDialog.__init__(self, parent) QtGui.QDialog.__init__(self, parent)
self.setupUi(self) self.setupUi(self)
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping This allows OpenLP to use ``self.object`` for all the GUI elements while keeping them separate from the functionality,
them separate from the functionality, so that it is easier to recreate the GUI so that it is easier to recreate the GUI from the .ui files later if necessary.
from the .ui files later if necessary.
""" """
from addgroupform import AddGroupForm from addgroupform import AddGroupForm

View File

@ -47,16 +47,16 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog):
def exec_(self, clear=True, show_top_level_group=False, selected_group=None): def exec_(self, clear=True, show_top_level_group=False, selected_group=None):
""" """
Show the form Show the form.
``clear`` ``clear``
Set to False if the text input box should not be cleared when showing the dialog (default: True) Set to False if the text input box should not be cleared when showing the dialog (default: True).
``show_top_level_group`` ``show_top_level_group``
Set to True when "-- Top level group --" should be showed as first item (default: False) Set to True when "-- Top level group --" should be showed as first item (default: False).
``selected_group`` ``selected_group``
The ID of the group that should be selected by default when showing the dialog The ID of the group that should be selected by default when showing the dialog.
""" """
if clear: if clear:
self.name_edit.clear() self.name_edit.clear()
@ -72,7 +72,7 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog):
def accept(self): def accept(self):
""" """
Override the accept() method from QDialog to make sure something is entered in the text input box Override the accept() method from QDialog to make sure something is entered in the text input box.
""" """
if not self.name_edit.text(): if not self.name_edit.text():
critical_error_message_box(message=translate('ImagePlugin.AddGroupForm', critical_error_message_box(message=translate('ImagePlugin.AddGroupForm',

View File

@ -48,10 +48,11 @@ class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog):
Show the form Show the form
``selected_group`` ``selected_group``
The ID of the group that should be selected by default when showing the dialog The ID of the group that should be selected by default when showing the dialog.
""" """
self.new_group_edit.clear()
if selected_group is not None: if selected_group is not None:
for i in range(self.group_combobox.count()): for index in range(self.group_combobox.count()):
if self.group_combobox.itemData(i) == selected_group: if self.group_combobox.itemData(index) == selected_group:
self.group_combobox.setCurrentIndex(i) self.group_combobox.setCurrentIndex(index)
return QtGui.QDialog.exec_(self) return QtGui.QDialog.exec_(self)

View File

@ -70,10 +70,10 @@ class ImagePlugin(Plugin):
def app_startup(self): def app_startup(self):
""" """
Perform tasks on application startup Perform tasks on application startup.
""" """
Plugin.app_startup(self) Plugin.app_startup(self)
# Convert old settings-based image list to the database # Convert old settings-based image list to the database.
files_from_config = Settings().get_files_from_config(self) files_from_config = Settings().get_files_from_config(self)
if files_from_config: if files_from_config:
log.debug(u'Importing images list from old config: %s' % files_from_config) log.debug(u'Importing images list from old config: %s' % files_from_config)
@ -93,7 +93,7 @@ class ImagePlugin(Plugin):
def set_plugin_text_strings(self): def set_plugin_text_strings(self):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin.
""" """
## Name PluginList ## ## Name PluginList ##
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
@ -117,8 +117,8 @@ class ImagePlugin(Plugin):
def config_update(self): def config_update(self):
""" """
Triggered by saving and changing the image border. Sets the images in image manager to require updates. Triggered by saving and changing the image border. Sets the images in image manager to require updates. Actual
Actual update is triggered by the last part of saving the config. update is triggered by the last part of saving the config.
""" """
log.info(u'Images config_update') log.info(u'Images config_update')
background = QtGui.QColor(Settings().value(self.settings_section + u'/background color')) background = QtGui.QColor(Settings().value(self.settings_section + u'/background color'))

View File

@ -27,7 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`db` module provides the database and schema that is the backend for the Images plugin The :mod:`db` module provides the database and schema that is the backend for the Images plugin.
""" """
from sqlalchemy import Column, ForeignKey, Table, types from sqlalchemy import Column, ForeignKey, Table, types
@ -38,14 +38,14 @@ from openlp.core.lib.db import BaseModel, init_db
class ImageGroups(BaseModel): class ImageGroups(BaseModel):
""" """
ImageGroups model ImageGroups model.
""" """
pass pass
class ImageFilenames(BaseModel): class ImageFilenames(BaseModel):
""" """
ImageFilenames model ImageFilenames model.
""" """
pass pass

View File

@ -32,7 +32,6 @@ from PyQt4 import QtGui
from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate
class ImageTab(SettingsTab): class ImageTab(SettingsTab):
""" """
ImageTab is the images settings tab in the settings dialog. ImageTab is the images settings tab in the settings dialog.

View File

@ -40,6 +40,7 @@ from openlp.core.utils import AppLocation, delete_file, get_locale_key, get_imag
from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm
from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -60,24 +61,23 @@ class ImageMediaItem(MediaManagerItem):
self.fill_groups_combobox(self.choose_group_form.group_combobox) self.fill_groups_combobox(self.choose_group_form.group_combobox)
self.fill_groups_combobox(self.add_group_form.parent_group_combobox) self.fill_groups_combobox(self.add_group_form.parent_group_combobox)
Registry().register_function(u'live_theme_changed', self.live_theme_changed) Registry().register_function(u'live_theme_changed', self.live_theme_changed)
# Allow DnD from the desktop # Allow DnD from the desktop.
self.list_view.activateDnD() self.list_view.activateDnD()
def retranslateUi(self): def retranslateUi(self):
self.on_new_prompt = translate('ImagePlugin.MediaItem', self.on_new_prompt = translate('ImagePlugin.MediaItem', 'Select Image(s)')
'Select Image(s)')
file_formats = get_images_filter() file_formats = get_images_filter()
self.on_new_file_masks = u'%s;;%s (*.*) (*)' % (file_formats, UiStrings().AllFiles) self.on_new_file_masks = u'%s;;%s (*.*) (*)' % (file_formats, UiStrings().AllFiles)
self.addGroupAction.setText(UiStrings().AddGroup) self.addGroupAction.setText(UiStrings().AddGroup)
self.addGroupAction.setToolTip(UiStrings().AddGroup) self.addGroupAction.setToolTip(UiStrings().AddGroup)
self.replaceAction.setText(UiStrings().ReplaceBG) self.replace_action.setText(UiStrings().ReplaceBG)
self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG) self.replace_action.setToolTip(UiStrings().ReplaceLiveBG)
self.resetAction.setText(UiStrings().ResetBG) self.reset_action.setText(UiStrings().ResetBG)
self.resetAction.setToolTip(UiStrings().ResetLiveBG) self.reset_action.setToolTip(UiStrings().ResetLiveBG)
def required_icons(self): def required_icons(self):
""" """
Set which icons the media manager tab should show Set which icons the media manager tab should show.
""" """
MediaManagerItem.required_icons(self) MediaManagerItem.required_icons(self)
self.has_file_icon = True self.has_file_icon = True
@ -94,13 +94,13 @@ class ImageMediaItem(MediaManagerItem):
self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settings_section), u'thumbnails') self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settings_section), u'thumbnails')
check_directory_exists(self.servicePath) check_directory_exists(self.servicePath)
# Load images from the database # Load images from the database
self.loadFullList( self.load_full_list(
self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=True) self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=True)
def add_list_view_to_toolbar(self): def add_list_view_to_toolbar(self):
""" """
Creates the main widget for listing items the media item is tracking. Creates the main widget for listing items the media item is tracking. This method overloads
This method overloads MediaManagerItem.add_list_view_to_toolbar MediaManagerItem.add_list_view_to_toolbar.
""" """
# Add the List widget # Add the List widget
self.list_view = TreeWidgetWithDnD(self, self.plugin.name) self.list_view = TreeWidgetWithDnD(self, self.plugin.name)
@ -155,44 +155,41 @@ class ImageMediaItem(MediaManagerItem):
self.list_view.doubleClicked.connect(self.on_double_clicked) self.list_view.doubleClicked.connect(self.on_double_clicked)
self.list_view.itemSelectionChanged.connect(self.on_selection_change) self.list_view.itemSelectionChanged.connect(self.on_selection_change)
self.list_view.customContextMenuRequested.connect(self.context_menu) self.list_view.customContextMenuRequested.connect(self.context_menu)
self.list_view.addAction(self.replaceAction) self.list_view.addAction(self.replace_action)
def add_custom_context_actions(self): def add_custom_context_actions(self):
""" """
Add custom actions to the context menu Add custom actions to the context menu.
""" """
create_widget_action(self.list_view, separator=True) create_widget_action(self.list_view, separator=True)
create_widget_action(self.list_view, create_widget_action(self.list_view,
text=UiStrings().AddGroup, text=UiStrings().AddGroup, icon=u':/images/image_new_group.png', triggers=self.on_add_group_click)
icon=u':/images/image_new_group.png',
triggers=self.onAddGroupClick)
create_widget_action(self.list_view, create_widget_action(self.list_view,
text=self.plugin.get_string(StringContent.Load)[u'tooltip'], text=self.plugin.get_string(StringContent.Load)[u'tooltip'],
icon=u':/general/general_open.png', icon=u':/general/general_open.png', triggers=self.on_file_click)
triggers=self.on_file_click)
def add_start_header_bar(self): def add_start_header_bar(self):
""" """
Add custom buttons to the start of the toolbar Add custom buttons to the start of the toolbar.
""" """
self.addGroupAction = self.toolbar.add_toolbar_action(u'addGroupAction', self.addGroupAction = self.toolbar.add_toolbar_action(u'addGroupAction',
icon=u':/images/image_new_group.png', triggers=self.onAddGroupClick) icon=u':/images/image_new_group.png', triggers=self.on_add_group_click)
def add_end_header_bar(self): def add_end_header_bar(self):
""" """
Add custom buttons to the end of the toolbar Add custom buttons to the end of the toolbar
""" """
self.replaceAction = self.toolbar.add_toolbar_action(u'replaceAction', self.replace_action = self.toolbar.add_toolbar_action(u'replace_action',
icon=u':/slides/slide_blank.png', triggers=self.onReplaceClick) icon=u':/slides/slide_blank.png', triggers=self.on_replace_click)
self.resetAction = self.toolbar.add_toolbar_action(u'resetAction', self.reset_action = self.toolbar.add_toolbar_action(u'reset_action',
icon=u':/system/system_close.png', visible=False, triggers=self.onResetClick) icon=u':/system/system_close.png', visible=False, triggers=self.on_reset_click)
def recursively_delete_group(self, image_group): def recursively_delete_group(self, image_group):
""" """
Recursively deletes a group and all groups and images in it Recursively deletes a group and all groups and images in it.
``image_group`` ``image_group``
The ImageGroups instance of the group that will be deleted The ImageGroups instance of the group that will be deleted.
""" """
images = self.manager.get_all_objects(ImageFilenames, ImageFilenames.group_id == image_group.id) images = self.manager.get_all_objects(ImageFilenames, ImageFilenames.group_id == image_group.id)
for image in images: for image in images:
@ -205,7 +202,7 @@ class ImageMediaItem(MediaManagerItem):
def on_delete_click(self): def on_delete_click(self):
""" """
Remove an image item from the list Remove an image item from the list.
""" """
# Turn off auto preview triggers. # Turn off auto preview triggers.
self.list_view.blockSignals(True) self.list_view.blockSignals(True)
@ -226,11 +223,11 @@ class ImageMediaItem(MediaManagerItem):
self.manager.delete_object(ImageFilenames, row_item.data(0, QtCore.Qt.UserRole).id) self.manager.delete_object(ImageFilenames, row_item.data(0, QtCore.Qt.UserRole).id)
elif isinstance(item_data, ImageGroups): elif isinstance(item_data, ImageGroups):
if QtGui.QMessageBox.question(self.list_view.parent(), if QtGui.QMessageBox.question(self.list_view.parent(),
translate('ImagePlugin.MediaItem', 'Remove group'), translate('ImagePlugin.MediaItem', 'Remove group'),
translate('ImagePlugin.MediaItem', translate('ImagePlugin.MediaItem',
'Are you sure you want to remove "%s" and everything in it?') % item_data.group_name, 'Are you sure you want to remove "%s" and everything in it?') % item_data.group_name,
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No)) == QtGui.QMessageBox.Yes: QtGui.QMessageBox.No)) == QtGui.QMessageBox.Yes:
self.recursively_delete_group(item_data) self.recursively_delete_group(item_data)
self.manager.delete_object(ImageGroups, row_item.data(0, QtCore.Qt.UserRole).id) self.manager.delete_object(ImageGroups, row_item.data(0, QtCore.Qt.UserRole).id)
if item_data.parent_id == 0: if item_data.parent_id == 0:
@ -246,13 +243,13 @@ class ImageMediaItem(MediaManagerItem):
def add_sub_groups(self, group_list, parent_group_id): def add_sub_groups(self, group_list, parent_group_id):
""" """
Recursively add subgroups to the given parent group in a QTreeWidget Recursively add subgroups to the given parent group in a QTreeWidget.
``group_list`` ``group_list``
The List object that contains all QTreeWidgetItems The List object that contains all QTreeWidgetItems.
``parent_group_id`` ``parent_group_id``
The ID of the group that will be added recursively The ID of the group that will be added recursively.
""" """
image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parent_group_id) image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parent_group_id)
image_groups.sort(key=lambda group_object: get_locale_key(group_object.group_name)) image_groups.sort(key=lambda group_object: get_locale_key(group_object.group_name))
@ -271,16 +268,16 @@ class ImageMediaItem(MediaManagerItem):
def fill_groups_combobox(self, combobox, parent_group_id=0, prefix=''): def fill_groups_combobox(self, combobox, parent_group_id=0, prefix=''):
""" """
Recursively add groups to the combobox in the 'Add group' dialog Recursively add groups to the combobox in the 'Add group' dialog.
``combobox`` ``combobox``
The QComboBox to add the options to The QComboBox to add the options to.
``parent_group_id`` ``parent_group_id``
The ID of the group that will be added The ID of the group that will be added.
``prefix`` ``prefix``
A string containing the prefix that will be added in front of the groupname for each level of the tree A string containing the prefix that will be added in front of the groupname for each level of the tree.
""" """
if parent_group_id == 0: if parent_group_id == 0:
combobox.clear() combobox.clear()
@ -293,13 +290,13 @@ class ImageMediaItem(MediaManagerItem):
def expand_group(self, group_id, root_item=None): def expand_group(self, group_id, root_item=None):
""" """
Expand groups in the widget recursively Expand groups in the widget recursively.
``group_id`` ``group_id``
The ID of the group that will be expanded The ID of the group that will be expanded.
``root_item`` ``root_item``
This option is only used for recursion purposes This option is only used for recursion purposes.
""" """
return_value = False return_value = False
if root_item is None: if root_item is None:
@ -314,29 +311,29 @@ class ImageMediaItem(MediaManagerItem):
return True return True
return return_value return return_value
def loadFullList(self, images, initial_load=False, open_group=None): def load_full_list(self, images, initial_load=False, open_group=None):
""" """
Replace the list of images and groups in the interface. Replace the list of images and groups in the interface.
``images`` ``images``
A List of ImageFilenames objects that will be used to reload the mediamanager list A List of ImageFilenames objects that will be used to reload the mediamanager list.
``initial_load`` ``initial_load``
When set to False, the busy cursor and progressbar will be shown while loading images When set to False, the busy cursor and progressbar will be shown while loading images.
``open_group`` ``open_group``
ImageGroups object of the group that must be expanded after reloading the list in the interface ImageGroups object of the group that must be expanded after reloading the list in the interface.
""" """
if not initial_load: if not initial_load:
self.application.set_busy_cursor() self.application.set_busy_cursor()
self.main_window.display_progress_bar(len(images)) self.main_window.display_progress_bar(len(images))
self.list_view.clear() self.list_view.clear()
# Load the list of groups and add them to the treeView # Load the list of groups and add them to the treeView.
group_items = {} group_items = {}
self.add_sub_groups(group_items, parent_group_id=0) self.add_sub_groups(group_items, parent_group_id=0)
if open_group is not None: if open_group is not None:
self.expand_group(open_group.id) self.expand_group(open_group.id)
# Sort the images by its filename considering language specific # Sort the images by its filename considering language specific.
# characters. # characters.
images.sort(key=lambda image_object: get_locale_key(os.path.split(unicode(image_object.filename))[1])) images.sort(key=lambda image_object: get_locale_key(os.path.split(unicode(image_object.filename))[1]))
for imageFile in images: for imageFile in images:
@ -394,6 +391,7 @@ class ImageMediaItem(MediaManagerItem):
``initial_load`` ``initial_load``
When set to False, the busy cursor and progressbar will be shown while loading images When set to False, the busy cursor and progressbar will be shown while loading images
""" """
parent_group = None
if target_group is None: if target_group is None:
# Find out if a group must be pre-selected # Find out if a group must be pre-selected
preselect_group = None preselect_group = None
@ -439,6 +437,8 @@ class ImageMediaItem(MediaManagerItem):
parent_group.parent_id = 0 parent_group.parent_id = 0
parent_group.group_name = self.choose_group_form.new_group_edit.text() parent_group.group_name = self.choose_group_form.new_group_edit.text()
self.manager.save_object(parent_group) self.manager.save_object(parent_group)
self.fill_groups_combobox(self.choose_group_form.group_combobox)
self.fill_groups_combobox(self.add_group_form.parent_group_combobox)
else: else:
parent_group = target_group.data(0, QtCore.Qt.UserRole) parent_group = target_group.data(0, QtCore.Qt.UserRole)
if isinstance(parent_group, ImageFilenames): if isinstance(parent_group, ImageFilenames):
@ -455,7 +455,7 @@ class ImageMediaItem(MediaManagerItem):
self.main_window.display_progress_bar(len(images)) self.main_window.display_progress_bar(len(images))
# Save the new images in the database # Save the new images in the database
self.save_new_images_list(images, group_id=parent_group.id, reload_list=False) self.save_new_images_list(images, group_id=parent_group.id, reload_list=False)
self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), self.load_full_list(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename),
initial_load=initial_load, open_group=parent_group) initial_load=initial_load, open_group=parent_group)
self.application.set_normal_cursor() self.application.set_normal_cursor()
@ -482,7 +482,7 @@ class ImageMediaItem(MediaManagerItem):
self.manager.save_object(imageFile) self.manager.save_object(imageFile)
self.main_window.increment_progress_bar() self.main_window.increment_progress_bar()
if reload_list and images_list: if reload_list and images_list:
self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename)) self.load_full_list(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename))
def dnd_move_internal(self, target): def dnd_move_internal(self, target):
""" """
@ -530,7 +530,7 @@ class ImageMediaItem(MediaManagerItem):
image_items.sort(key=lambda item: get_locale_key(item.text(0))) image_items.sort(key=lambda item: get_locale_key(item.text(0)))
target_group.addChildren(image_items) target_group.addChildren(image_items)
def generate_slide_data(self, service_item, item=None, xmlVersion=False, def generate_slide_data(self, service_item, item=None, xml_version=False,
remote=False, context=ServiceItemContext.Service): remote=False, context=ServiceItemContext.Service):
""" """
Generate the slide data. Needs to be implemented by the plugin. Generate the slide data. Needs to be implemented by the plugin.
@ -553,28 +553,25 @@ class ImageMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.CanAppend) service_item.add_capability(ItemCapabilities.CanAppend)
# force a nonexistent theme # force a nonexistent theme
service_item.theme = -1 service_item.theme = -1
missing_items = []
missing_items_filenames = [] missing_items_filenames = []
images_filenames = []
# Expand groups to images # Expand groups to images
for bitem in items: for bitem in items:
if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None: if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None:
for index in range(0, bitem.childCount()): for index in range(0, bitem.childCount()):
if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames): if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames):
items.append(bitem.child(index)) images_filenames.append(bitem.child(index).data(0, QtCore.Qt.UserRole).filename)
items.remove(bitem) elif isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames):
images_filenames.append(bitem.data(0, QtCore.Qt.UserRole).filename)
# Don't try to display empty groups # Don't try to display empty groups
if not items: if not images_filenames:
return False return False
# Find missing files # Find missing files
for bitem in items: for filename in images_filenames:
filename = bitem.data(0, QtCore.Qt.UserRole).filename
if not os.path.exists(filename): if not os.path.exists(filename):
missing_items.append(bitem)
missing_items_filenames.append(filename) missing_items_filenames.append(filename)
for item in missing_items:
items.remove(item)
# We cannot continue, as all images do not exist. # We cannot continue, as all images do not exist.
if not items: if not images_filenames:
if not remote: if not remote:
critical_error_message_box( critical_error_message_box(
translate('ImagePlugin.MediaItem', 'Missing Image(s)'), translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
@ -582,15 +579,14 @@ class ImageMediaItem(MediaManagerItem):
u'\n'.join(missing_items_filenames)) u'\n'.join(missing_items_filenames))
return False return False
# We have missing as well as existing images. We ask what to do. # We have missing as well as existing images. We ask what to do.
elif missing_items and QtGui.QMessageBox.question(self, elif missing_items_filenames and QtGui.QMessageBox.question(self,
translate('ImagePlugin.MediaItem', 'Missing Image(s)'), translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: %s\n' translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: %s\n'
'Do you want to add the other images anyway?') % u'\n'.join(missing_items_filenames), 'Do you want to add the other images anyway?') % u'\n'.join(missing_items_filenames),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
return False return False
# Continue with the existing images. # Continue with the existing images.
for bitem in items: for filename in images_filenames:
filename = bitem.data(0, QtCore.Qt.UserRole).filename
name = os.path.split(filename)[1] name = os.path.split(filename)[1]
service_item.add_from_image(filename, name, background) service_item.add_from_image(filename, name, background)
return True return True
@ -608,7 +604,7 @@ class ImageMediaItem(MediaManagerItem):
else: else:
return False return False
def onAddGroupClick(self): def on_add_group_click(self):
""" """
Called to add a new group Called to add a new group
""" """
@ -629,7 +625,7 @@ class ImageMediaItem(MediaManagerItem):
group_name=self.add_group_form.name_edit.text()) group_name=self.add_group_form.name_edit.text())
if not self.check_group_exists(new_group): if not self.check_group_exists(new_group):
if self.manager.save_object(new_group): if self.manager.save_object(new_group):
self.loadFullList(self.manager.get_all_objects(ImageFilenames, self.load_full_list(self.manager.get_all_objects(ImageFilenames,
order_by_ref=ImageFilenames.filename)) order_by_ref=ImageFilenames.filename))
self.expand_group(new_group.id) self.expand_group(new_group.id)
self.fill_groups_combobox(self.choose_group_form.group_combobox) self.fill_groups_combobox(self.choose_group_form.group_combobox)
@ -638,23 +634,22 @@ class ImageMediaItem(MediaManagerItem):
critical_error_message_box( critical_error_message_box(
message=translate('ImagePlugin.AddGroupForm', 'Could not add the new group.')) message=translate('ImagePlugin.AddGroupForm', 'Could not add the new group.'))
else: else:
critical_error_message_box( critical_error_message_box(message=translate('ImagePlugin.AddGroupForm', 'This group already exists.'))
message=translate('ImagePlugin.AddGroupForm', 'This group already exists.'))
def onResetClick(self): def on_reset_click(self):
""" """
Called to reset the Live background with the image selected, Called to reset the Live background with the image selected.
""" """
self.resetAction.setVisible(False) self.reset_action.setVisible(False)
self.live_controller.display.reset_image() self.live_controller.display.reset_image()
def live_theme_changed(self): def live_theme_changed(self):
""" """
Triggered by the change of theme in the slide controller Triggered by the change of theme in the slide controller.
""" """
self.resetAction.setVisible(False) self.reset_action.setVisible(False)
def onReplaceClick(self): def on_replace_click(self):
""" """
Called to replace Live backgound with the image selected. Called to replace Live backgound with the image selected.
""" """
@ -663,12 +658,12 @@ class ImageMediaItem(MediaManagerItem):
background = QtGui.QColor(Settings().value(self.settings_section + u'/background color')) background = QtGui.QColor(Settings().value(self.settings_section + u'/background color'))
bitem = self.list_view.selectedItems()[0] bitem = self.list_view.selectedItems()[0]
if not isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames): if not isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames):
# Only continue when an image is selected # Only continue when an image is selected.
return return
filename = bitem.data(0, QtCore.Qt.UserRole).filename filename = bitem.data(0, QtCore.Qt.UserRole).filename
if os.path.exists(filename): if os.path.exists(filename):
if self.live_controller.display.direct_image(filename, background): if self.live_controller.display.direct_image(filename, background):
self.resetAction.setVisible(True) self.reset_action.setVisible(True)
else: else:
critical_error_message_box(UiStrings().LiveBGError, critical_error_message_box(UiStrings().LiveBGError,
translate('ImagePlugin.MediaItem', 'There was no display item to amend.')) translate('ImagePlugin.MediaItem', 'There was no display item to amend.'))

View File

@ -27,8 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`media` module provides the Media plugin which allows OpenLP to The :mod:`media` module provides the Media plugin which allows OpenLP to display videos. The media supported depends not
display videos. The media supported depends not only on the Python support only on the Python support but also extensively on the codecs installed on the underlying operating system being picked
but also extensively on the codecs installed on the underlying operating system up and usable by Python.
being picked up and usable by Python.
""" """

View File

@ -39,13 +39,16 @@ from openlp.core.ui import DisplayController, Display, DisplayControllerType
from openlp.core.ui.media import get_media_players, set_media_players from openlp.core.ui.media import get_media_players, set_media_players
from openlp.core.utils import AppLocation, get_locale_key from openlp.core.utils import AppLocation, get_locale_key
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
CLAPPERBOARD = u':/media/slidecontroller_multimedia.png' CLAPPERBOARD = u':/media/slidecontroller_multimedia.png'
VIDEO = build_icon(QtGui.QImage(u':/media/media_video.png')) VIDEO_ICON = build_icon(u':/media/media_video.png')
AUDIO = build_icon(QtGui.QImage(u':/media/media_audio.png')) AUDIO_ICON = build_icon(u':/media/media_audio.png')
DVDICON = build_icon(QtGui.QImage(u':/media/media_video.png')) DVD_ICON = build_icon(u':/media/media_video.png')
ERROR = build_icon(QtGui.QImage(u':/general/general_delete.png')) ERROR_ICON = build_icon(u':/general/general_delete.png')
class MediaMediaItem(MediaManagerItem): class MediaMediaItem(MediaManagerItem):
""" """
@ -79,12 +82,12 @@ class MediaMediaItem(MediaManagerItem):
def retranslateUi(self): def retranslateUi(self):
self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media') self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media')
self.replaceAction.setText(UiStrings().ReplaceBG) self.replace_action.setText(UiStrings().ReplaceBG)
self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG) self.replace_action.setToolTip(UiStrings().ReplaceLiveBG)
self.resetAction.setText(UiStrings().ResetBG) self.reset_action.setText(UiStrings().ResetBG)
self.resetAction.setToolTip(UiStrings().ResetLiveBG) self.reset_action.setToolTip(UiStrings().ResetLiveBG)
self.automatic = UiStrings().Automatic self.automatic = UiStrings().Automatic
self.displayTypeLabel.setText(translate('MediaPlugin.MediaItem', 'Use Player:')) self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
self.rebuild_players() self.rebuild_players()
def required_icons(self): def required_icons(self):
@ -98,27 +101,28 @@ class MediaMediaItem(MediaManagerItem):
def add_list_view_to_toolbar(self): def add_list_view_to_toolbar(self):
MediaManagerItem.add_list_view_to_toolbar(self) MediaManagerItem.add_list_view_to_toolbar(self)
self.list_view.addAction(self.replaceAction) self.list_view.addAction(self.replace_action)
def add_end_header_bar(self): def add_end_header_bar(self):
# Replace backgrounds do not work at present so remove functionality. # Replace backgrounds do not work at present so remove functionality.
self.replaceAction = self.toolbar.add_toolbar_action(u'replaceAction', icon=u':/slides/slide_blank.png', self.replace_action = self.toolbar.add_toolbar_action(u'replace_action', icon=u':/slides/slide_blank.png',
triggers=self.onReplaceClick) triggers=self.onReplaceClick)
self.resetAction = self.toolbar.add_toolbar_action(u'resetAction', icon=u':/system/system_close.png', self.reset_action = self.toolbar.add_toolbar_action(u'reset_action', icon=u':/system/system_close.png',
visible=False, triggers=self.onResetClick) visible=False, triggers=self.onResetClick)
self.mediaWidget = QtGui.QWidget(self) self.media_widget = QtGui.QWidget(self)
self.mediaWidget.setObjectName(u'mediaWidget') self.media_widget.setObjectName(u'media_widget')
self.displayLayout = QtGui.QFormLayout(self.mediaWidget) self.display_layout = QtGui.QFormLayout(self.media_widget)
self.displayLayout.setMargin(self.displayLayout.spacing()) self.display_layout.setMargin(self.display_layout.spacing())
self.displayLayout.setObjectName(u'displayLayout') self.display_layout.setObjectName(u'display_layout')
self.displayTypeLabel = QtGui.QLabel(self.mediaWidget) self.display_type_label = QtGui.QLabel(self.media_widget)
self.displayTypeLabel.setObjectName(u'displayTypeLabel') self.display_type_label.setObjectName(u'display_type_label')
self.displayTypeComboBox = create_horizontal_adjusting_combo_box(self.mediaWidget, u'displayTypeComboBox') self.display_type_combo_box = create_horizontal_adjusting_combo_box(
self.displayTypeLabel.setBuddy(self.displayTypeComboBox) self.media_widget, u'display_type_combo_box')
self.displayLayout.addRow(self.displayTypeLabel, self.displayTypeComboBox) self.display_type_label.setBuddy(self.display_type_combo_box)
# Add the Media widget to the page layout self.display_layout.addRow(self.display_type_label, self.display_type_combo_box)
self.page_layout.addWidget(self.mediaWidget) # Add the Media widget to the page layout.
self.displayTypeComboBox.currentIndexChanged.connect(self.overridePlayerChanged) self.page_layout.addWidget(self.media_widget)
self.display_type_combo_box.currentIndexChanged.connect(self.overridePlayerChanged)
def overridePlayerChanged(self, index): def overridePlayerChanged(self, index):
player = get_media_players()[0] player = get_media_players()[0]
@ -132,13 +136,13 @@ class MediaMediaItem(MediaManagerItem):
Called to reset the Live background with the media selected, Called to reset the Live background with the media selected,
""" """
self.media_controller.media_reset(self.live_controller) self.media_controller.media_reset(self.live_controller)
self.resetAction.setVisible(False) self.reset_action.setVisible(False)
def video_background_replaced(self): def video_background_replaced(self):
""" """
Triggered by main display on change of serviceitem. Triggered by main display on change of serviceitem.
""" """
self.resetAction.setVisible(False) self.reset_action.setVisible(False)
def onReplaceClick(self): def onReplaceClick(self):
""" """
@ -151,11 +155,11 @@ class MediaMediaItem(MediaManagerItem):
if os.path.exists(filename): if os.path.exists(filename):
service_item = ServiceItem() service_item = ServiceItem()
service_item.title = u'webkit' service_item.title = u'webkit'
service_item.shortname = service_item.title service_item.processor = u'webkit'
(path, name) = os.path.split(filename) (path, name) = os.path.split(filename)
service_item.add_from_command(path, name,CLAPPERBOARD) service_item.add_from_command(path, name,CLAPPERBOARD)
if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True): if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True):
self.resetAction.setVisible(True) self.reset_action.setVisible(True)
else: else:
critical_error_message_box(UiStrings().LiveBGError, critical_error_message_box(UiStrings().LiveBGError,
translate('MediaPlugin.MediaItem', 'There was no display item to amend.')) translate('MediaPlugin.MediaItem', 'There was no display item to amend.'))
@ -164,7 +168,7 @@ class MediaMediaItem(MediaManagerItem):
translate('MediaPlugin.MediaItem', translate('MediaPlugin.MediaItem',
'There was a problem replacing your background, the media file "%s" no longer exists.') % filename) 'There was a problem replacing your background, the media file "%s" no longer exists.') % filename)
def generate_slide_data(self, service_item, item=None, xmlVersion=False, remote=False, def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
context=ServiceItemContext.Live): context=ServiceItemContext.Live):
""" """
Generate the slide data. Needs to be implemented by the plugin. Generate the slide data. Needs to be implemented by the plugin.
@ -181,9 +185,9 @@ class MediaMediaItem(MediaManagerItem):
translate('MediaPlugin.MediaItem', 'Missing Media File'), translate('MediaPlugin.MediaItem', 'Missing Media File'),
translate('MediaPlugin.MediaItem', 'The file %s no longer exists.') % filename) translate('MediaPlugin.MediaItem', 'The file %s no longer exists.') % filename)
return False return False
service_item.title = self.displayTypeComboBox.currentText()
service_item.shortname = service_item.title
(path, name) = os.path.split(filename) (path, name) = os.path.split(filename)
service_item.title = name
service_item.processor = self.display_type_combo_box.currentText()
service_item.add_from_command(path, name, CLAPPERBOARD) service_item.add_from_command(path, name, CLAPPERBOARD)
# Only get start and end times if going to a service # Only get start and end times if going to a service
if context == ServiceItemContext.Service: if context == ServiceItemContext.Service:
@ -192,7 +196,6 @@ class MediaMediaItem(MediaManagerItem):
return False return False
service_item.add_capability(ItemCapabilities.CanAutoStartForLive) service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
service_item.add_capability(ItemCapabilities.RequiresMedia) service_item.add_capability(ItemCapabilities.RequiresMedia)
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
if Settings().value(self.settings_section + u'/media auto start') == QtCore.Qt.Checked: if Settings().value(self.settings_section + u'/media auto start') == QtCore.Qt.Checked:
service_item.will_auto_start = True service_item.will_auto_start = True
# force a non-existent theme # force a non-existent theme
@ -209,8 +212,7 @@ class MediaMediaItem(MediaManagerItem):
def rebuild_players(self): def rebuild_players(self):
""" """
Rebuild the tab in the media manager when changes are made in Rebuild the tab in the media manager when changes are made in the settings.
the settings
""" """
self.populateDisplayTypes() self.populateDisplayTypes()
self.on_new_file_masks = translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)') % ( self.on_new_file_masks = translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)') % (
@ -222,29 +224,27 @@ class MediaMediaItem(MediaManagerItem):
def populateDisplayTypes(self): def populateDisplayTypes(self):
""" """
Load the combobox with the enabled media players, Load the combobox with the enabled media players, allowing user to select a specific player if settings allow.
allowing user to select a specific player if settings allow
""" """
# block signals to avoid unnecessary overridePlayerChanged Signals # block signals to avoid unnecessary overridePlayerChanged Signals while combo box creation
# while combo box creation self.display_type_combo_box.blockSignals(True)
self.displayTypeComboBox.blockSignals(True) self.display_type_combo_box.clear()
self.displayTypeComboBox.clear()
usedPlayers, overridePlayer = get_media_players() usedPlayers, overridePlayer = get_media_players()
media_players = self.media_controller.media_players media_players = self.media_controller.media_players
currentIndex = 0 currentIndex = 0
for player in usedPlayers: for player in usedPlayers:
# load the drop down selection # load the drop down selection
self.displayTypeComboBox.addItem(media_players[player].original_name) self.display_type_combo_box.addItem(media_players[player].original_name)
if overridePlayer == player: if overridePlayer == player:
currentIndex = len(self.displayTypeComboBox) currentIndex = len(self.display_type_combo_box)
if self.displayTypeComboBox.count() > 1: if self.display_type_combo_box.count() > 1:
self.displayTypeComboBox.insertItem(0, self.automatic) self.display_type_combo_box.insertItem(0, self.automatic)
self.displayTypeComboBox.setCurrentIndex(currentIndex) self.display_type_combo_box.setCurrentIndex(currentIndex)
if overridePlayer: if overridePlayer:
self.mediaWidget.show() self.media_widget.show()
else: else:
self.mediaWidget.hide() self.media_widget.hide()
self.displayTypeComboBox.blockSignals(False) self.display_type_combo_box.blockSignals(False)
def on_delete_click(self): def on_delete_click(self):
""" """
@ -259,42 +259,41 @@ class MediaMediaItem(MediaManagerItem):
Settings().setValue(self.settings_section + u'/media files', self.get_file_list()) Settings().setValue(self.settings_section + u'/media files', self.get_file_list())
def load_list(self, media, target_group=None): def load_list(self, media, target_group=None):
# Sort the media by its filename considering language specific # Sort the media by its filename considering language specific characters.
# characters.
media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1])) media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1]))
for track in media: for track in media:
track_info = QtCore.QFileInfo(track) track_info = QtCore.QFileInfo(track)
if not os.path.exists(track): if not os.path.exists(track):
filename = os.path.split(unicode(track))[1] filename = os.path.split(unicode(track))[1]
item_name = QtGui.QListWidgetItem(filename) item_name = QtGui.QListWidgetItem(filename)
item_name.setIcon(ERROR) item_name.setIcon(ERROR_ICON)
item_name.setData(QtCore.Qt.UserRole, track) item_name.setData(QtCore.Qt.UserRole, track)
elif track_info.isFile(): elif track_info.isFile():
filename = os.path.split(unicode(track))[1] filename = os.path.split(unicode(track))[1]
item_name = QtGui.QListWidgetItem(filename) item_name = QtGui.QListWidgetItem(filename)
if u'*.%s' % (filename.split(u'.')[-1].lower()) in self.media_controller.audio_extensions_list: if u'*.%s' % (filename.split(u'.')[-1].lower()) in self.media_controller.audio_extensions_list:
item_name.setIcon(AUDIO) item_name.setIcon(AUDIO_ICON)
else: else:
item_name.setIcon(VIDEO) item_name.setIcon(VIDEO_ICON)
item_name.setData(QtCore.Qt.UserRole, track) item_name.setData(QtCore.Qt.UserRole, track)
else: else:
filename = os.path.split(unicode(track))[1] filename = os.path.split(unicode(track))[1]
item_name = QtGui.QListWidgetItem(filename) item_name = QtGui.QListWidgetItem(filename)
item_name.setIcon(build_icon(DVDICON)) item_name.setIcon(build_icon(DVD_ICON))
item_name.setData(QtCore.Qt.UserRole, track) item_name.setData(QtCore.Qt.UserRole, track)
item_name.setToolTip(track) item_name.setToolTip(track)
self.list_view.addItem(item_name) self.list_view.addItem(item_name)
def getList(self, type=MediaType.Audio): def get_list(self, type=MediaType.Audio):
media = Settings().value(self.settings_section + u'/media files') media = Settings().value(self.settings_section + u'/media files')
media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1])) media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1]))
ext = [] extension = []
if type == MediaType.Audio: if type == MediaType.Audio:
ext = self.media_controller.audio_extensions_list extension = self.media_controller.audio_extensions_list
else: else:
ext = self.media_controller.video_extensions_list extension = self.media_controller.video_extensions_list
ext = map(lambda x: x[1:], ext) extension = map(lambda x: x[1:], extension)
media = filter(lambda x: os.path.splitext(x)[1] in ext, media) media = filter(lambda x: os.path.splitext(x)[1] in extension, media)
return media return media
def search(self, string, showError): def search(self, string, showError):

View File

@ -34,12 +34,14 @@ from PyQt4 import QtCore
from openlp.core.lib import Plugin, Registry, StringContent, Settings, build_icon, translate from openlp.core.lib import Plugin, Registry, StringContent, Settings, build_icon, translate
from openlp.plugins.media.lib import MediaMediaItem, MediaTab from openlp.plugins.media.lib import MediaMediaItem, MediaTab
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# Some settings starting with "media" are in core, because they are needed for core functionality. # Some settings starting with "media" are in core, because they are needed for core functionality.
__default_settings__ = { __default_settings__ = {
u'media/media auto start': QtCore.Qt.Unchecked, u'media/media auto start': QtCore.Qt.Unchecked,
u'media/media files': [] u'media/media files': []
} }
@ -54,7 +56,7 @@ class MediaPlugin(Plugin):
# passed with drag and drop messages # passed with drag and drop messages
self.dnd_id = u'Media' self.dnd_id = u'Media'
def create_settings_Tab(self, parent): def create_settings_tab(self, parent):
""" """
Create the settings Tab Create the settings Tab
""" """
@ -94,7 +96,7 @@ class MediaPlugin(Plugin):
def finalise(self): def finalise(self):
""" """
Time to tidy up on exit Time to tidy up on exit.
""" """
log.info(u'Media Finalising') log.info(u'Media Finalising')
self.media_controller.finalise() self.media_controller.finalise()
@ -102,19 +104,19 @@ class MediaPlugin(Plugin):
def get_display_css(self): def get_display_css(self):
""" """
Add css style sheets to htmlbuilder Add css style sheets to htmlbuilder.
""" """
return self.media_controller.get_media_display_css() return self.media_controller.get_media_display_css()
def get_display_javascript(self): def get_display_javascript(self):
""" """
Add javascript functions to htmlbuilder Add javascript functions to htmlbuilder.
""" """
return self.media_controller.get_media_display_javascript() return self.media_controller.get_media_display_javascript()
def get_display_html(self): def get_display_html(self):
""" """
Add html code to htmlbuilder Add html code to htmlbuilder.
""" """
return self.media_controller.get_media_display_html() return self.media_controller.get_media_display_html()

View File

@ -27,6 +27,6 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`presentations` module provides the Presentations plugin which allows The :mod:`presentations` module provides the Presentations plugin which allows OpenLP to show presentations from most
OpenLP to show presentations from most popular presentation packages. popular presentation packages.
""" """

View File

@ -62,13 +62,14 @@ from openlp.core.lib import ScreenList
from openlp.core.utils import delete_file, get_uno_command, get_uno_instance from openlp.core.utils import delete_file, get_uno_command, get_uno_instance
from presentationcontroller import PresentationController, PresentationDocument from presentationcontroller import PresentationController, PresentationDocument
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class ImpressController(PresentationController): class ImpressController(PresentationController):
""" """
Class to control interactions with Impress presentations. Class to control interactions with Impress presentations. It creates the runtime environment, loads and closes the
It creates the runtime environment, loads and closes the presentation as presentation as well as triggering the correct activities based on the users input.
well as triggering the correct activities based on the users input
""" """
log.info(u'ImpressController loaded') log.info(u'ImpressController loaded')
@ -79,14 +80,14 @@ class ImpressController(PresentationController):
log.debug(u'Initialising') log.debug(u'Initialising')
PresentationController.__init__(self, plugin, u'Impress', ImpressDocument) PresentationController.__init__(self, plugin, u'Impress', ImpressDocument)
self.supports = [u'odp'] self.supports = [u'odp']
self.alsosupports = [u'ppt', u'pps', u'pptx', u'ppsx'] self.also_supports = [u'ppt', u'pps', u'pptx', u'ppsx']
self.process = None self.process = None
self.desktop = None self.desktop = None
self.manager = None self.manager = None
def check_available(self): def check_available(self):
""" """
Impress is able to run on this machine Impress is able to run on this machine.
""" """
log.debug(u'check_available') log.debug(u'check_available')
if os.name == u'nt': if os.name == u'nt':
@ -96,9 +97,8 @@ class ImpressController(PresentationController):
def start_process(self): def start_process(self):
""" """
Loads a running version of OpenOffice in the background. Loads a running version of OpenOffice in the background. It is not displayed to the user but is available to the
It is not displayed to the user but is available to the UNO interface UNO interface when required.
when required.
""" """
log.debug(u'start process Openoffice') log.debug(u'start process Openoffice')
if os.name == u'nt': if os.name == u'nt':
@ -113,8 +113,7 @@ class ImpressController(PresentationController):
def get_uno_desktop(self): def get_uno_desktop(self):
""" """
On non-Windows platforms, use Uno. Get the OpenOffice desktop On non-Windows platforms, use Uno. Get the OpenOffice desktop which will be used to manage impress.
which will be used to manage impress
""" """
log.debug(u'get UNO Desktop Openoffice') log.debug(u'get UNO Desktop Openoffice')
uno_instance = None uno_instance = None
@ -132,8 +131,7 @@ class ImpressController(PresentationController):
loop += 1 loop += 1
try: try:
self.manager = uno_instance.ServiceManager self.manager = uno_instance.ServiceManager
log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext' log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
u' - Desktop')
desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance) desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
return desktop return desktop
except: except:
@ -142,8 +140,7 @@ class ImpressController(PresentationController):
def get_com_desktop(self): def get_com_desktop(self):
""" """
On Windows platforms, use COM. Return the desktop object which On Windows platforms, use COM. Return the desktop object which will be used to manage Impress.
will be used to manage Impress
""" """
log.debug(u'get COM Desktop OpenOffice') log.debug(u'get COM Desktop OpenOffice')
if not self.manager: if not self.manager:
@ -157,7 +154,7 @@ class ImpressController(PresentationController):
def get_com_servicemanager(self): def get_com_servicemanager(self):
""" """
Return the OOo service manager for windows Return the OOo service manager for windows.
""" """
log.debug(u'get_com_servicemanager openoffice') log.debug(u'get_com_servicemanager openoffice')
try: try:
@ -168,7 +165,7 @@ class ImpressController(PresentationController):
def kill(self): def kill(self):
""" """
Called at system exit to clean up any running presentations Called at system exit to clean up any running presentations.
""" """
log.debug(u'Kill OpenOffice') log.debug(u'Kill OpenOffice')
while self.docs: while self.docs:
@ -203,12 +200,12 @@ class ImpressController(PresentationController):
class ImpressDocument(PresentationDocument): class ImpressDocument(PresentationDocument):
""" """
Class which holds information and controls a single presentation Class which holds information and controls a single presentation.
""" """
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
""" """
Constructor, store information about the file and initialise Constructor, store information about the file and initialise.
""" """
log.debug(u'Init Presentation OpenOffice') log.debug(u'Init Presentation OpenOffice')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
@ -218,11 +215,9 @@ class ImpressDocument(PresentationDocument):
def load_presentation(self): def load_presentation(self):
""" """
Called when a presentation is added to the SlideController. Called when a presentation is added to the SlideController. It builds the environment, starts communcations with
It builds the environment, starts communcations with the background the background OpenOffice task started earlier. If OpenOffice is not present is is started. Once the environment
OpenOffice task started earlier. If OpenOffice is not present is is is available the presentation is loaded and started.
started. Once the environment is available the presentation is loaded
and started.
""" """
log.debug(u'Load Presentation OpenOffice') log.debug(u'Load Presentation OpenOffice')
if os.name == u'nt': if os.name == u'nt':
@ -239,13 +234,12 @@ class ImpressDocument(PresentationDocument):
self.desktop = desktop self.desktop = desktop
properties = [] properties = []
if os.name != u'nt': if os.name != u'nt':
# Recent versions of Impress on Windows won't start the presentation # Recent versions of Impress on Windows won't start the presentation if it starts as minimized. It seems OK
# if it starts as minimized. It seems OK on Linux though. # on Linux though.
properties.append(self.create_property(u'Minimized', True)) properties.append(self.create_property(u'Minimized', True))
properties = tuple(properties) properties = tuple(properties)
try: try:
self.document = desktop.loadComponentFromURL(url, u'_blank', self.document = desktop.loadComponentFromURL(url, u'_blank', 0, properties)
0, properties)
except: except:
log.warn(u'Failed to load presentation %s' % url) log.warn(u'Failed to load presentation %s' % url)
return False return False
@ -262,33 +256,33 @@ class ImpressDocument(PresentationDocument):
def create_thumbnails(self): def create_thumbnails(self):
""" """
Create thumbnail images for presentation Create thumbnail images for presentation.
""" """
log.debug(u'create thumbnails OpenOffice') log.debug(u'create thumbnails OpenOffice')
if self.check_thumbnails(): if self.check_thumbnails():
return return
if os.name == u'nt': if os.name == u'nt':
thumbdirurl = u'file:///' + self.get_temp_folder().replace(u'\\', u'/') \ thumb_dir_url = u'file:///' + self.get_temp_folder().replace(u'\\', u'/') \
.replace(u':', u'|').replace(u' ', u'%20') .replace(u':', u'|').replace(u' ', u'%20')
else: else:
thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder()) thumb_dir_url = uno.systemPathToFileUrl(self.get_temp_folder())
props = [] properties = []
props.append(self.create_property(u'FilterName', u'impress_png_Export')) properties.append(self.create_property(u'FilterName', u'impress_png_Export'))
props = tuple(props) properties = tuple(properties)
doc = self.document doc = self.document
pages = doc.getDrawPages() pages = doc.getDrawPages()
if not pages: if not pages:
return return
if not os.path.isdir(self.get_temp_folder()): if not os.path.isdir(self.get_temp_folder()):
os.makedirs(self.get_temp_folder()) os.makedirs(self.get_temp_folder())
for idx in range(pages.getCount()): for index in range(pages.getCount()):
page = pages.getByIndex(idx) page = pages.getByIndex(index)
doc.getCurrentController().setCurrentPage(page) doc.getCurrentController().setCurrentPage(page)
urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1)) url_path = u'%s/%s.png' % (thumb_dir_url, unicode(index + 1))
path = os.path.join(self.get_temp_folder(), unicode(idx + 1) + u'.png') path = os.path.join(self.get_temp_folder(), unicode(index + 1) + u'.png')
try: try:
doc.storeToURL(urlpath, props) doc.storeToURL(url_path, properties)
self.convert_thumbnail(path, idx + 1) self.convert_thumbnail(path, index + 1)
delete_file(path) delete_file(path)
except ErrorCodeIOException, exception: except ErrorCodeIOException, exception:
log.exception(u'ERROR! ErrorCodeIOException %d' % exception.ErrCode) log.exception(u'ERROR! ErrorCodeIOException %d' % exception.ErrCode)
@ -297,23 +291,21 @@ class ImpressDocument(PresentationDocument):
def create_property(self, name, value): def create_property(self, name, value):
""" """
Create an OOo style property object which are passed into some Create an OOo style property object which are passed into some Uno methods.
Uno methods
""" """
log.debug(u'create property OpenOffice') log.debug(u'create property OpenOffice')
if os.name == u'nt': if os.name == u'nt':
prop = self.controller.manager.Bridge_GetStruct(u'com.sun.star.beans.PropertyValue') property_object = self.controller.manager.Bridge_GetStruct(u'com.sun.star.beans.PropertyValue')
else: else:
prop = PropertyValue() property_object = PropertyValue()
prop.Name = name property_object.Name = name
prop.Value = value property_object.Value = value
return prop return property_object
def close_presentation(self): def close_presentation(self):
""" """
Close presentation and clean up objects Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
Triggered by new object being added to SlideController or OpenLP shutdown.
being shutdown
""" """
log.debug(u'close Presentation OpenOffice') log.debug(u'close Presentation OpenOffice')
if self.document: if self.document:
@ -329,7 +321,7 @@ class ImpressDocument(PresentationDocument):
def is_loaded(self): def is_loaded(self):
""" """
Returns true if a presentation is loaded Returns true if a presentation is loaded.
""" """
log.debug(u'is loaded OpenOffice') log.debug(u'is loaded OpenOffice')
if self.presentation is None or self.document is None: if self.presentation is None or self.document is None:
@ -346,7 +338,7 @@ class ImpressDocument(PresentationDocument):
def is_active(self): def is_active(self):
""" """
Returns true if a presentation is active and running Returns true if a presentation is active and running.
""" """
log.debug(u'is active OpenOffice') log.debug(u'is active OpenOffice')
if not self.is_loaded(): if not self.is_loaded():
@ -355,21 +347,21 @@ class ImpressDocument(PresentationDocument):
def unblank_screen(self): def unblank_screen(self):
""" """
Unblanks the screen Unblanks the screen.
""" """
log.debug(u'unblank screen OpenOffice') log.debug(u'unblank screen OpenOffice')
return self.control.resume() return self.control.resume()
def blank_screen(self): def blank_screen(self):
""" """
Blanks the screen Blanks the screen.
""" """
log.debug(u'blank screen OpenOffice') log.debug(u'blank screen OpenOffice')
self.control.blankScreen(0) self.control.blankScreen(0)
def is_blank(self): def is_blank(self):
""" """
Returns true if screen is blank Returns true if screen is blank.
""" """
log.debug(u'is blank OpenOffice') log.debug(u'is blank OpenOffice')
if self.control and self.control.isRunning(): if self.control and self.control.isRunning():
@ -379,7 +371,7 @@ class ImpressDocument(PresentationDocument):
def stop_presentation(self): def stop_presentation(self):
""" """
Stop the presentation, remove from screen Stop the presentation, remove from screen.
""" """
log.debug(u'stop presentation OpenOffice') log.debug(u'stop presentation OpenOffice')
# deactivate should hide the screen according to docs, but doesn't # deactivate should hide the screen according to docs, but doesn't
@ -389,18 +381,17 @@ class ImpressDocument(PresentationDocument):
def start_presentation(self): def start_presentation(self):
""" """
Start the presentation from the beginning Start the presentation from the beginning.
""" """
log.debug(u'start presentation OpenOffice') log.debug(u'start presentation OpenOffice')
if self.control is None or not self.control.isRunning(): if self.control is None or not self.control.isRunning():
self.presentation.start() self.presentation.start()
self.control = self.presentation.getController() self.control = self.presentation.getController()
# start() returns before the Component is ready. # start() returns before the Component is ready. Try for 15 seconds.
# Try for 15 seconds sleep_count = 1
i = 1 while not self.control and sleep_count < 150:
while not self.control and i < 150:
time.sleep(0.1) time.sleep(0.1)
i += 1 sleep_count += 1
self.control = self.presentation.getController() self.control = self.presentation.getController()
else: else:
self.control.activate() self.control.activate()
@ -408,25 +399,25 @@ class ImpressDocument(PresentationDocument):
def get_slide_number(self): def get_slide_number(self):
""" """
Return the current slide number on the screen, from 1 Return the current slide number on the screen, from 1.
""" """
return self.control.getCurrentSlideIndex() + 1 return self.control.getCurrentSlideIndex() + 1
def get_slide_count(self): def get_slide_count(self):
""" """
Return the total number of slides Return the total number of slides.
""" """
return self.document.getDrawPages().getCount() return self.document.getDrawPages().getCount()
def goto_slide(self, slideno): def goto_slide(self, slideno):
""" """
Go to a specific slide (from 1) Go to a specific slide (from 1).
""" """
self.control.gotoSlideIndex(slideno-1) self.control.gotoSlideIndex(slideno-1)
def next_step(self): def next_step(self):
""" """
Triggers the next effect of slide on the running presentation Triggers the next effect of slide on the running presentation.
""" """
is_paused = self.control.isPaused() is_paused = self.control.isPaused()
self.control.gotoNextEffect() self.control.gotoNextEffect()
@ -436,7 +427,7 @@ class ImpressDocument(PresentationDocument):
def previous_step(self): def previous_step(self):
""" """
Triggers the previous slide on the running presentation Triggers the previous slide on the running presentation.
""" """
self.control.gotoPreviousSlide() self.control.gotoPreviousSlide()
@ -470,8 +461,8 @@ class ImpressDocument(PresentationDocument):
page = pages.getByIndex(slide_no - 1) page = pages.getByIndex(slide_no - 1)
if notes: if notes:
page = page.getNotesPage() page = page.getNotesPage()
for idx in range(page.getCount()): for index in range(page.getCount()):
shape = page.getByIndex(idx) shape = page.getByIndex(index)
if shape.supportsService("com.sun.star.drawing.Text"): if shape.supportsService("com.sun.star.drawing.Text"):
text += shape.getString() + '\n' text += shape.getString() + '\n'
return text return text

View File

@ -38,14 +38,17 @@ from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adj
from openlp.core.utils import get_locale_key from openlp.core.utils import get_locale_key
from openlp.plugins.presentations.lib import MessageListener from openlp.plugins.presentations.lib import MessageListener
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
ERROR = QtGui.QImage(u':/general/general_delete.png')
ERROR_IMAGE = QtGui.QImage(u':/general/general_delete.png')
class PresentationMediaItem(MediaManagerItem): class PresentationMediaItem(MediaManagerItem):
""" """
This is the Presentation media manager item for Presentation Items. This is the Presentation media manager item for Presentation Items. It can present files using Openoffice and
It can present files using Openoffice and Powerpoint Powerpoint
""" """
log.info(u'Presentations Media Item loaded') log.info(u'Presentations Media Item loaded')
@ -71,25 +74,25 @@ class PresentationMediaItem(MediaManagerItem):
""" """
self.on_new_prompt = translate('PresentationPlugin.MediaItem', 'Select Presentation(s)') self.on_new_prompt = translate('PresentationPlugin.MediaItem', 'Select Presentation(s)')
self.Automatic = translate('PresentationPlugin.MediaItem', 'Automatic') self.Automatic = translate('PresentationPlugin.MediaItem', 'Automatic')
self.displayTypeLabel.setText(translate('PresentationPlugin.MediaItem', 'Present using:')) self.display_type_label.setText(translate('PresentationPlugin.MediaItem', 'Present using:'))
def build_file_mask_string(self): def build_file_mask_string(self):
""" """
Build the list of file extensions to be used in the Open file dialog Build the list of file extensions to be used in the Open file dialog.
""" """
fileType = u'' file_type = u''
for controller in self.controllers: for controller in self.controllers:
if self.controllers[controller].enabled(): if self.controllers[controller].enabled():
types = self.controllers[controller].supports + self.controllers[controller].alsosupports file_types = self.controllers[controller].supports + self.controllers[controller].also_supports
for type in types: for file_type in file_types:
if fileType.find(type) == -1: if file_type.find(file_type) == -1:
fileType += u'*.%s ' % type file_type += u'*.%s ' % file_type
self.service_manager.supported_suffixes(type) self.service_manager.supported_suffixes(file_type)
self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % fileType self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % file_type
def required_icons(self): def required_icons(self):
""" """
Set which icons the media manager tab should show Set which icons the media manager tab should show.
""" """
MediaManagerItem.required_icons(self) MediaManagerItem.required_icons(self)
self.has_file_icon = True self.has_file_icon = True
@ -98,21 +101,21 @@ class PresentationMediaItem(MediaManagerItem):
def add_end_header_bar(self): def add_end_header_bar(self):
""" """
Display custom media manager items for presentations Display custom media manager items for presentations.
""" """
self.presentationWidget = QtGui.QWidget(self) self.presentation_widget = QtGui.QWidget(self)
self.presentationWidget.setObjectName(u'presentationWidget') self.presentation_widget.setObjectName(u'presentation_widget')
self.displayLayout = QtGui.QFormLayout(self.presentationWidget) self.display_layout = QtGui.QFormLayout(self.presentation_widget)
self.displayLayout.setMargin(self.displayLayout.spacing()) self.display_layout.setMargin(self.display_layout.spacing())
self.displayLayout.setObjectName(u'displayLayout') self.display_layout.setObjectName(u'display_layout')
self.displayTypeLabel = QtGui.QLabel(self.presentationWidget) self.display_type_label = QtGui.QLabel(self.presentation_widget)
self.displayTypeLabel.setObjectName(u'displayTypeLabel') self.display_type_label.setObjectName(u'display_type_label')
self.displayTypeComboBox = create_horizontal_adjusting_combo_box(self.presentationWidget, self.display_type_combo_box = create_horizontal_adjusting_combo_box(self.presentation_widget,
u'displayTypeComboBox') u'display_type_combo_box')
self.displayTypeLabel.setBuddy(self.displayTypeComboBox) self.display_type_label.setBuddy(self.display_type_combo_box)
self.displayLayout.addRow(self.displayTypeLabel, self.displayTypeComboBox) self.display_layout.addRow(self.display_type_label, self.display_type_combo_box)
# Add the Presentation widget to the page layout # Add the Presentation widget to the page layout.
self.page_layout.addWidget(self.presentationWidget) self.page_layout.addWidget(self.presentation_widget)
def initialise(self): def initialise(self):
""" """
@ -120,55 +123,54 @@ class PresentationMediaItem(MediaManagerItem):
""" """
self.list_view.setIconSize(QtCore.QSize(88, 50)) self.list_view.setIconSize(QtCore.QSize(88, 50))
files = Settings().value(self.settings_section + u'/presentations files') files = Settings().value(self.settings_section + u'/presentations files')
self.load_list(files, initialLoad=True) self.load_list(files, initial_load=True)
self.populate_display_types() self.populate_display_types()
def populate_display_types(self): def populate_display_types(self):
""" """
Load the combobox with the enabled presentation controllers, Load the combobox with the enabled presentation controllers, allowing user to select a specific app if settings
allowing user to select a specific app if settings allow allow.
""" """
self.displayTypeComboBox.clear() self.display_type_combo_box.clear()
for item in self.controllers: for item in self.controllers:
# load the drop down selection # load the drop down selection
if self.controllers[item].enabled(): if self.controllers[item].enabled():
self.displayTypeComboBox.addItem(item) self.display_type_combo_box.addItem(item)
if self.displayTypeComboBox.count() > 1: if self.display_type_combo_box.count() > 1:
self.displayTypeComboBox.insertItem(0, self.Automatic) self.display_type_combo_box.insertItem(0, self.Automatic)
self.displayTypeComboBox.setCurrentIndex(0) self.display_type_combo_box.setCurrentIndex(0)
if Settings().value(self.settings_section + u'/override app') == QtCore.Qt.Checked: if Settings().value(self.settings_section + u'/override app') == QtCore.Qt.Checked:
self.presentationWidget.show() self.presentation_widget.show()
else: else:
self.presentationWidget.hide() self.presentation_widget.hide()
def load_list(self, files, target_group=None, initialLoad=False): def load_list(self, files, target_group=None, initial_load=False):
""" """
Add presentations into the media manager Add presentations into the media manager. This is called both on initial load of the plugin to populate with
This is called both on initial load of the plugin to populate with existing files, and when the user adds new files via the media manager.
existing files, and when the user adds new files via the media manager
""" """
currlist = self.get_file_list() current_list = self.get_file_list()
titles = [os.path.split(file)[1] for file in currlist] titles = [os.path.split(file)[1] for file in current_list]
self.application.set_busy_cursor() self.application.set_busy_cursor()
if not initialLoad: if not initial_load:
self.main_window.display_progress_bar(len(files)) self.main_window.display_progress_bar(len(files))
# Sort the presentations by its filename considering language specific characters. # Sort the presentations by its filename considering language specific characters.
files.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1])) files.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1]))
for file in files: for file in files:
if not initialLoad: if not initial_load:
self.main_window.increment_progress_bar() self.main_window.increment_progress_bar()
if currlist.count(file) > 0: if current_list.count(file) > 0:
continue continue
filename = os.path.split(unicode(file))[1] filename = os.path.split(unicode(file))[1]
if not os.path.exists(file): if not os.path.exists(file):
item_name = QtGui.QListWidgetItem(filename) item_name = QtGui.QListWidgetItem(filename)
item_name.setIcon(build_icon(ERROR)) item_name.setIcon(build_icon(ERROR_IMAGE))
item_name.setData(QtCore.Qt.UserRole, file) item_name.setData(QtCore.Qt.UserRole, file)
item_name.setToolTip(file) item_name.setToolTip(file)
self.list_view.addItem(item_name) self.list_view.addItem(item_name)
else: else:
if titles.count(filename) > 0: if titles.count(filename) > 0:
if not initialLoad: if not initial_load:
critical_error_message_box(translate('PresentationPlugin.MediaItem', 'File Exists'), critical_error_message_box(translate('PresentationPlugin.MediaItem', 'File Exists'),
translate('PresentationPlugin.MediaItem', translate('PresentationPlugin.MediaItem',
'A presentation with that filename already exists.') 'A presentation with that filename already exists.')
@ -180,7 +182,7 @@ class PresentationMediaItem(MediaManagerItem):
doc = controller.add_document(unicode(file)) doc = controller.add_document(unicode(file))
thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png') thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png')
preview = doc.get_thumbnail_path(1, True) preview = doc.get_thumbnail_path(1, True)
if not preview and not initialLoad: if not preview and not initial_load:
doc.load_presentation() doc.load_presentation()
preview = doc.get_thumbnail_path(1, True) preview = doc.get_thumbnail_path(1, True)
doc.close_presentation() doc.close_presentation()
@ -192,7 +194,7 @@ class PresentationMediaItem(MediaManagerItem):
else: else:
icon = create_thumb(preview, thumb) icon = create_thumb(preview, thumb)
else: else:
if initialLoad: if initial_load:
icon = build_icon(u':/general/general_delete.png') icon = build_icon(u':/general/general_delete.png')
else: else:
critical_error_message_box(UiStrings().UnsupportedFile, critical_error_message_box(UiStrings().UnsupportedFile,
@ -203,13 +205,13 @@ class PresentationMediaItem(MediaManagerItem):
item_name.setIcon(icon) item_name.setIcon(icon)
item_name.setToolTip(file) item_name.setToolTip(file)
self.list_view.addItem(item_name) self.list_view.addItem(item_name)
if not initialLoad: if not initial_load:
self.main_window.finished_progress_bar() self.main_window.finished_progress_bar()
self.application.set_normal_cursor() self.application.set_normal_cursor()
def on_delete_click(self): def on_delete_click(self):
""" """
Remove a presentation item from the list Remove a presentation item from the list.
""" """
if check_item_selected(self.list_view, UiStrings().SelectDelete): if check_item_selected(self.list_view, UiStrings().SelectDelete):
items = self.list_view.selectedIndexes() items = self.list_view.selectedIndexes()
@ -230,12 +232,11 @@ class PresentationMediaItem(MediaManagerItem):
self.list_view.takeItem(row) self.list_view.takeItem(row)
Settings().setValue(self.settings_section + u'/presentations files', self.get_file_list()) Settings().setValue(self.settings_section + u'/presentations files', self.get_file_list())
def generate_slide_data(self, service_item, item=None, xmlVersion=False, def generate_slide_data(self, service_item, item=None, xml_version=False,
remote=False, context=ServiceItemContext.Service): remote=False, context=ServiceItemContext.Service):
""" """
Load the relevant information for displaying the presentation Load the relevant information for displaying the presentation in the slidecontroller. In the case of
in the slidecontroller. In the case of powerpoints, an image powerpoints, an image for each slide.
for each slide
""" """
if item: if item:
items = [item] items = [item]
@ -243,22 +244,20 @@ class PresentationMediaItem(MediaManagerItem):
items = self.list_view.selectedItems() items = self.list_view.selectedItems()
if len(items) > 1: if len(items) > 1:
return False return False
service_item.title = self.displayTypeComboBox.currentText() service_item.processor = self.display_type_combo_box.currentText()
service_item.shortname = self.displayTypeComboBox.currentText()
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay) if not self.display_type_combo_box.currentText():
shortname = service_item.shortname
if not shortname:
return False return False
for bitem in items: for bitem in items:
filename = bitem.data(QtCore.Qt.UserRole) filename = bitem.data(QtCore.Qt.UserRole)
(path, name) = os.path.split(filename)
service_item.title = name
if os.path.exists(filename): if os.path.exists(filename):
if shortname == self.Automatic: if service_item.processor == self.Automatic:
service_item.shortname = self.findControllerByType(filename) service_item.processor = self.findControllerByType(filename)
if not service_item.shortname: if not service_item.processor:
return False return False
controller = self.controllers[service_item.shortname] controller = self.controllers[service_item.processor]
(path, name) = os.path.split(filename)
doc = controller.add_document(filename) doc = controller.add_document(filename)
if doc.get_thumbnail_path(1, True) is None: if doc.get_thumbnail_path(1, True) is None:
doc.load_presentation() doc.load_presentation()
@ -287,26 +286,24 @@ class PresentationMediaItem(MediaManagerItem):
def findControllerByType(self, filename): def findControllerByType(self, filename):
""" """
Determine the default application controller to use for the selected Determine the default application controller to use for the selected file type. This is used if "Automatic" is
file type. This is used if "Automatic" is set as the preferred set as the preferred controller. Find the first (alphabetic) enabled controller which "supports" the extension.
controller. Find the first (alphabetic) enabled controller which If none found, then look for a controller which "also supports" it instead.
"supports" the extension. If none found, then look for a controller
which "also supports" it instead.
""" """
filetype = os.path.splitext(filename)[1][1:] file_type = os.path.splitext(filename)[1][1:]
if not filetype: if not file_type:
return None return None
for controller in self.controllers: for controller in self.controllers:
if self.controllers[controller].enabled(): if self.controllers[controller].enabled():
if filetype in self.controllers[controller].supports: if file_type in self.controllers[controller].supports:
return controller return controller
for controller in self.controllers: for controller in self.controllers:
if self.controllers[controller].enabled(): if self.controllers[controller].enabled():
if filetype in self.controllers[controller].alsosupports: if file_type in self.controllers[controller].also_supports:
return controller return controller
return None return None
def search(self, string, showError): def search(self, string, show_error):
files = Settings().value(self.settings_section + u'/presentations files') files = Settings().value(self.settings_section + u'/presentations files')
results = [] results = []
string = string.lower() string = string.lower()

View File

@ -36,10 +36,11 @@ from openlp.core.ui import HideMode
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Controller(object): class Controller(object):
""" """
This is the Presentation listener who acts on events from the slide This is the Presentation listener who acts on events from the slide controller and passes the messages on the the
controller and passes the messages on the the correct presentation handlers correct presentation handlers.
""" """
log.info(u'Controller loaded') log.info(u'Controller loaded')
@ -54,9 +55,8 @@ class Controller(object):
def add_handler(self, controller, file, hide_mode, slide_no): def add_handler(self, controller, file, hide_mode, slide_no):
""" """
Add a handler, which is an instance of a presentation and Add a handler, which is an instance of a presentation and slidecontroller combination. If the slidecontroller
slidecontroller combination. If the slidecontroller has a display has a display then load the presentation.
then load the presentation.
""" """
log.debug(u'Live = %s, add_handler %s' % (self.is_live, file)) log.debug(u'Live = %s, add_handler %s' % (self.is_live, file))
self.controller = controller self.controller = controller
@ -86,8 +86,7 @@ class Controller(object):
def activate(self): def activate(self):
""" """
Active the presentation, and show it on the screen. Active the presentation, and show it on the screen. Use the last slide number.
Use the last slide number.
""" """
log.debug(u'Live = %s, activate' % self.is_live) log.debug(u'Live = %s, activate' % self.is_live)
if not self.doc: if not self.doc:
@ -130,7 +129,7 @@ class Controller(object):
def first(self): def first(self):
""" """
Based on the handler passed at startup triggers the first slide Based on the handler passed at startup triggers the first slide.
""" """
log.debug(u'Live = %s, first' % self.is_live) log.debug(u'Live = %s, first' % self.is_live)
if not self.doc: if not self.doc:
@ -148,7 +147,7 @@ class Controller(object):
def last(self): def last(self):
""" """
Based on the handler passed at startup triggers the last slide Based on the handler passed at startup triggers the last slide.
""" """
log.debug(u'Live = %s, last' % self.is_live) log.debug(u'Live = %s, last' % self.is_live)
if not self.doc: if not self.doc:
@ -166,7 +165,7 @@ class Controller(object):
def next(self): def next(self):
""" """
Based on the handler passed at startup triggers the next slide event Based on the handler passed at startup triggers the next slide event.
""" """
log.debug(u'Live = %s, next' % self.is_live) log.debug(u'Live = %s, next' % self.is_live)
if not self.doc: if not self.doc:
@ -182,9 +181,8 @@ class Controller(object):
return return
if not self.activate(): if not self.activate():
return return
# The "End of slideshow" screen is after the last slide # The "End of slideshow" screen is after the last slide. Note, we can't just stop on the last slide, since it
# Note, we can't just stop on the last slide, since it may # may contain animations that need to be stepped through.
# contain animations that need to be stepped through.
if self.doc.slidenumber > self.doc.get_slide_count(): if self.doc.slidenumber > self.doc.get_slide_count():
return return
self.doc.next_step() self.doc.next_step()
@ -192,7 +190,7 @@ class Controller(object):
def previous(self): def previous(self):
""" """
Based on the handler passed at startup triggers the previous slide event Based on the handler passed at startup triggers the previous slide event.
""" """
log.debug(u'Live = %s, previous' % self.is_live) log.debug(u'Live = %s, previous' % self.is_live)
if not self.doc: if not self.doc:
@ -213,7 +211,7 @@ class Controller(object):
def shutdown(self): def shutdown(self):
""" """
Based on the handler passed at startup triggers slide show to shut down Based on the handler passed at startup triggers slide show to shut down.
""" """
log.debug(u'Live = %s, shutdown' % self.is_live) log.debug(u'Live = %s, shutdown' % self.is_live)
if not self.doc: if not self.doc:
@ -223,7 +221,7 @@ class Controller(object):
def blank(self, hide_mode): def blank(self, hide_mode):
""" """
Instruct the controller to blank the presentation Instruct the controller to blank the presentation.
""" """
log.debug(u'Live = %s, blank' % self.is_live) log.debug(u'Live = %s, blank' % self.is_live)
self.hide_mode = hide_mode self.hide_mode = hide_mode
@ -244,7 +242,7 @@ class Controller(object):
def stop(self): def stop(self):
""" """
Instruct the controller to stop and hide the presentation Instruct the controller to stop and hide the presentation.
""" """
log.debug(u'Live = %s, stop' % self.is_live) log.debug(u'Live = %s, stop' % self.is_live)
self.hide_mode = HideMode.Screen self.hide_mode = HideMode.Screen
@ -260,7 +258,7 @@ class Controller(object):
def unblank(self): def unblank(self):
""" """
Instruct the controller to unblank the presentation Instruct the controller to unblank the presentation.
""" """
log.debug(u'Live = %s, unblank' % self.is_live) log.debug(u'Live = %s, unblank' % self.is_live)
self.hide_mode = None self.hide_mode = None
@ -283,8 +281,8 @@ class Controller(object):
class MessageListener(object): class MessageListener(object):
""" """
This is the Presentation listener who acts on events from the slide This is the Presentation listener who acts on events from the slide controller and passes the messages on the the
controller and passes the messages on the the correct presentation handlers correct presentation handlers
""" """
log.info(u'Message Listener loaded') log.info(u'Message Listener loaded')
@ -310,15 +308,14 @@ class MessageListener(object):
def startup(self, message): def startup(self, message):
""" """
Start of new presentation Start of new presentation. Save the handler as any new presentations start here
Save the handler as any new presentations start here
""" """
log.debug(u'Startup called with message %s' % message)
is_live = message[1] is_live = message[1]
item = message[0] item = message[0]
log.debug(u'Startup called with message %s' % message)
hide_mode = message[2] hide_mode = message[2]
file = item.get_frame_path() file = item.get_frame_path()
self.handler = item.title self.handler = item.processor
if self.handler == self.media_item.Automatic: if self.handler == self.media_item.Automatic:
self.handler = self.media_item.findControllerByType(file) self.handler = self.media_item.findControllerByType(file)
if not self.handler: if not self.handler:
@ -331,7 +328,7 @@ class MessageListener(object):
def slide(self, message): def slide(self, message):
""" """
React to the message to move to a specific slide React to the message to move to a specific slide.
""" """
is_live = message[1] is_live = message[1]
slide = message[2] slide = message[2]
@ -342,7 +339,7 @@ class MessageListener(object):
def first(self, message): def first(self, message):
""" """
React to the message to move to the first slide React to the message to move to the first slide.
""" """
is_live = message[1] is_live = message[1]
if is_live: if is_live:
@ -352,7 +349,7 @@ class MessageListener(object):
def last(self, message): def last(self, message):
""" """
React to the message to move to the last slide React to the message to move to the last slide.
""" """
is_live = message[1] is_live = message[1]
if is_live: if is_live:
@ -362,7 +359,7 @@ class MessageListener(object):
def next(self, message): def next(self, message):
""" """
React to the message to move to the next animation/slide React to the message to move to the next animation/slide.
""" """
is_live = message[1] is_live = message[1]
if is_live: if is_live:
@ -372,7 +369,7 @@ class MessageListener(object):
def previous(self, message): def previous(self, message):
""" """
React to the message to move to the previous animation/slide React to the message to move to the previous animation/slide.
""" """
is_live = message[1] is_live = message[1]
if is_live: if is_live:
@ -382,8 +379,7 @@ class MessageListener(object):
def shutdown(self, message): def shutdown(self, message):
""" """
React to message to shutdown the presentation. I.e. end the show React to message to shutdown the presentation. I.e. end the show and close the file.
and close the file
""" """
is_live = message[1] is_live = message[1]
if is_live: if is_live:
@ -393,7 +389,7 @@ class MessageListener(object):
def hide(self, message): def hide(self, message):
""" """
React to the message to show the desktop React to the message to show the desktop.
""" """
is_live = message[1] is_live = message[1]
if is_live: if is_live:
@ -401,7 +397,7 @@ class MessageListener(object):
def blank(self, message): def blank(self, message):
""" """
React to the message to blank the display React to the message to blank the display.
""" """
is_live = message[1] is_live = message[1]
hide_mode = message[2] hide_mode = message[2]
@ -410,7 +406,7 @@ class MessageListener(object):
def unblank(self, message): def unblank(self, message):
""" """
React to the message to unblank the display React to the message to unblank the display.
""" """
is_live = message[1] is_live = message[1]
if is_live: if is_live:
@ -418,9 +414,7 @@ class MessageListener(object):
def timeout(self): def timeout(self):
""" """
The presentation may be timed or might be controlled by the The presentation may be timed or might be controlled by the application directly, rather than through OpenLP.
application directly, rather than through OpenLP. Poll occasionally Poll occasionally to check which slide is currently displayed so the slidecontroller view can be updated.
to check which slide is currently displayed so the slidecontroller
view can be updated
""" """
self.live_handler.poll() self.live_handler.poll()

View File

@ -26,7 +26,10 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
"""
This modul is for controlling powerpiont. PPT API documentation:
`http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_
"""
import os import os
import logging import logging
@ -39,16 +42,14 @@ if os.name == u'nt':
from openlp.core.lib import ScreenList from openlp.core.lib import ScreenList
from presentationcontroller import PresentationController, PresentationDocument from presentationcontroller import PresentationController, PresentationDocument
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# PPT API documentation:
# http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx
class PowerpointController(PresentationController): class PowerpointController(PresentationController):
""" """
Class to control interactions with PowerPoint Presentations Class to control interactions with PowerPoint Presentations. It creates the runtime Environment , Loads the and
It creates the runtime Environment , Loads the and Closes the Presentation Closes the Presentation. As well as triggering the correct activities based on the users input.
As well as triggering the correct activities based on the users input
""" """
log.info(u'PowerpointController loaded') log.info(u'PowerpointController loaded')
@ -63,7 +64,7 @@ class PowerpointController(PresentationController):
def check_available(self): def check_available(self):
""" """
PowerPoint is able to run on this machine PowerPoint is able to run on this machine.
""" """
log.debug(u'check_available') log.debug(u'check_available')
if os.name == u'nt': if os.name == u'nt':
@ -77,7 +78,7 @@ class PowerpointController(PresentationController):
if os.name == u'nt': if os.name == u'nt':
def start_process(self): def start_process(self):
""" """
Loads PowerPoint process Loads PowerPoint process.
""" """
log.debug(u'start_process') log.debug(u'start_process')
if not self.process: if not self.process:
@ -87,7 +88,7 @@ class PowerpointController(PresentationController):
def kill(self): def kill(self):
""" """
Called at system exit to clean up any running presentations Called at system exit to clean up any running presentations.
""" """
log.debug(u'Kill powerpoint') log.debug(u'Kill powerpoint')
while self.docs: while self.docs:
@ -105,12 +106,12 @@ class PowerpointController(PresentationController):
class PowerpointDocument(PresentationDocument): class PowerpointDocument(PresentationDocument):
""" """
Class which holds information and controls a single presentation Class which holds information and controls a single presentation.
""" """
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
""" """
Constructor, store information about the file and initialise Constructor, store information about the file and initialise.
""" """
log.debug(u'Init Presentation Powerpoint') log.debug(u'Init Presentation Powerpoint')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
@ -118,8 +119,8 @@ class PowerpointDocument(PresentationDocument):
def load_presentation(self): def load_presentation(self):
""" """
Called when a presentation is added to the SlideController. Called when a presentation is added to the SlideController. Opens the PowerPoint file using the process created
Opens the PowerPoint file using the process created earlier. earlier.
""" """
log.debug(u'load_presentation') log.debug(u'load_presentation')
if not self.controller.process or not self.controller.process.Visible: if not self.controller.process or not self.controller.process.Visible:
@ -142,20 +143,19 @@ class PowerpointDocument(PresentationDocument):
self.presentation.Slides[n].Copy() self.presentation.Slides[n].Copy()
thumbnail = QApplication.clipboard.image() thumbnail = QApplication.clipboard.image()
However, for the moment, we want a physical file since it makes life However, for the moment, we want a physical file since it makes life easier elsewhere.
easier elsewhere.
""" """
log.debug(u'create_thumbnails') log.debug(u'create_thumbnails')
if self.check_thumbnails(): if self.check_thumbnails():
return return
for num in range(self.presentation.Slides.Count): for num in range(self.presentation.Slides.Count):
self.presentation.Slides(num + 1).Export(os.path.join( self.presentation.Slides(num + 1).Export(
self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), 'png', 320, 240) os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), 'png', 320, 240)
def close_presentation(self): def close_presentation(self):
""" """
Close presentation and clean up objects. This is triggered by a new Close presentation and clean up objects. This is triggered by a new object being added to SlideController or
object being added to SlideController or OpenLP being shut down. OpenLP being shut down.
""" """
log.debug(u'ClosePresentation') log.debug(u'ClosePresentation')
if self.presentation: if self.presentation:
@ -182,7 +182,6 @@ class PowerpointDocument(PresentationDocument):
return False return False
return True return True
def is_active(self): def is_active(self):
""" """
Returns ``True`` if a presentation is currently active. Returns ``True`` if a presentation is currently active.
@ -253,15 +252,14 @@ class PowerpointDocument(PresentationDocument):
dpi = win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88) dpi = win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88)
except win32ui.error: except win32ui.error:
dpi = 96 dpi = 96
rect = ScreenList().current[u'size'] size = ScreenList().current[u'size']
ppt_window = self.presentation.SlideShowSettings.Run() ppt_window = self.presentation.SlideShowSettings.Run()
if not ppt_window: if not ppt_window:
return return
ppt_window.Top = rect.y() * 72 / dpi ppt_window.Top = size.y() * 72 / dpi
ppt_window.Height = rect.height() * 72 / dpi ppt_window.Height = size.height() * 72 / dpi
ppt_window.Left = rect.x() * 72 / dpi ppt_window.Left = size.x() * 72 / dpi
ppt_window.Width = rect.width() * 72 / dpi ppt_window.Width = size.width() * 72 / dpi
def get_slide_number(self): def get_slide_number(self):
""" """
@ -318,6 +316,7 @@ class PowerpointDocument(PresentationDocument):
""" """
return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes) return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes)
def _get_text_from_shapes(shapes): def _get_text_from_shapes(shapes):
""" """
Returns any text extracted from the shapes on a presentation slide. Returns any text extracted from the shapes on a presentation slide.
@ -326,8 +325,8 @@ def _get_text_from_shapes(shapes):
A set of shapes to search for text. A set of shapes to search for text.
""" """
text = '' text = ''
for idx in range(shapes.Count): for index in range(shapes.Count):
shape = shapes(idx + 1) shape = shapes(index + 1)
if shape.HasTextFrame: if shape.HasTextFrame:
text += shape.TextFrame.TextRange.Text + '\n' text += shape.TextFrame.TextRange.Text + '\n'
return text return text

View File

@ -37,13 +37,14 @@ if os.name == u'nt':
from openlp.core.lib import ScreenList from openlp.core.lib import ScreenList
from presentationcontroller import PresentationController, PresentationDocument from presentationcontroller import PresentationController, PresentationDocument
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class PptviewController(PresentationController): class PptviewController(PresentationController):
""" """
Class to control interactions with PowerPoint Viewer Presentations Class to control interactions with PowerPoint Viewer Presentations. It creates the runtime Environment , Loads the
It creates the runtime Environment , Loads the and Closes the Presentation and Closes the Presentation. As well as triggering the correct activities based on the users input
As well as triggering the correct activities based on the users input
""" """
log.info(u'PPTViewController loaded') log.info(u'PPTViewController loaded')
@ -58,7 +59,7 @@ class PptviewController(PresentationController):
def check_available(self): def check_available(self):
""" """
PPT Viewer is able to run on this machine PPT Viewer is able to run on this machine.
""" """
log.debug(u'check_available') log.debug(u'check_available')
if os.name != u'nt': if os.name != u'nt':
@ -68,7 +69,7 @@ class PptviewController(PresentationController):
if os.name == u'nt': if os.name == u'nt':
def check_installed(self): def check_installed(self):
""" """
Check the viewer is installed Check the viewer is installed.
""" """
log.debug(u'Check installed') log.debug(u'Check installed')
try: try:
@ -79,14 +80,14 @@ class PptviewController(PresentationController):
def start_process(self): def start_process(self):
""" """
Loads the PPTVIEWLIB library Loads the PPTVIEWLIB library.
""" """
if self.process: if self.process:
return return
log.debug(u'start PPTView') log.debug(u'start PPTView')
dllpath = os.path.join(self.plugin_manager.base_path, u'presentations', u'lib', u'pptviewlib', dll_path = os.path.join(
u'pptviewlib.dll') self.plugin_manager.base_path, u'presentations', u'lib', u'pptviewlib', u'pptviewlib.dll')
self.process = cdll.LoadLibrary(dllpath) self.process = cdll.LoadLibrary(dll_path)
if log.isEnabledFor(logging.DEBUG): if log.isEnabledFor(logging.DEBUG):
self.process.SetDebug(1) self.process.SetDebug(1)
@ -101,33 +102,32 @@ class PptviewController(PresentationController):
class PptviewDocument(PresentationDocument): class PptviewDocument(PresentationDocument):
""" """
Class which holds information and controls a single presentation Class which holds information and controls a single presentation.
""" """
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
""" """
Constructor, store information about the file and initialise Constructor, store information about the file and initialise.
""" """
log.debug(u'Init Presentation PowerPoint') log.debug(u'Init Presentation PowerPoint')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
self.presentation = None self.presentation = None
self.pptid = None self.ppt_id = None
self.blanked = False self.blanked = False
self.hidden = False self.hidden = False
def load_presentation(self): def load_presentation(self):
""" """
Called when a presentation is added to the SlideController. Called when a presentation is added to the SlideController. It builds the environment, starts communication with
It builds the environment, starts communication with the background the background PptView task started earlier.
PptView task started earlier.
""" """
log.debug(u'LoadPresentation') log.debug(u'LoadPresentation')
rect = ScreenList().current[u'size'] size = ScreenList().current[u'size']
rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom()) rect = RECT(size.x(), size.y(), size.right(), size.bottom())
filepath = str(self.filepath.replace(u'/', u'\\')) filepath = str(self.filepath.replace(u'/', u'\\'))
if not os.path.isdir(self.get_temp_folder()): if not os.path.isdir(self.get_temp_folder()):
os.makedirs(self.get_temp_folder()) os.makedirs(self.get_temp_folder())
self.pptid = self.controller.process.OpenPPT(filepath, None, rect, str(self.get_temp_folder()) + '\\slide') self.ppt_id = self.controller.process.OpenPPT(filepath, None, rect, str(self.get_temp_folder()) + '\\slide')
if self.pptid >= 0: if self.ppt_id >= 0:
self.create_thumbnails() self.create_thumbnails()
self.stop_presentation() self.stop_presentation()
return True return True
@ -136,8 +136,7 @@ class PptviewDocument(PresentationDocument):
def create_thumbnails(self): def create_thumbnails(self):
""" """
PPTviewLib creates large BMP's, but we want small PNG's for consistency. PPTviewLib creates large BMP's, but we want small PNG's for consistency. Convert them here.
Convert them here.
""" """
log.debug(u'create_thumbnails') log.debug(u'create_thumbnails')
if self.check_thumbnails(): if self.check_thumbnails():
@ -149,21 +148,20 @@ class PptviewDocument(PresentationDocument):
def close_presentation(self): def close_presentation(self):
""" """
Close presentation and clean up objects Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
Triggered by new object being added to SlideController orOpenLP shut down.
being shut down
""" """
log.debug(u'ClosePresentation') log.debug(u'ClosePresentation')
if self.controller.process: if self.controller.process:
self.controller.process.ClosePPT(self.pptid) self.controller.process.ClosePPT(self.ppt_id)
self.pptid = -1 self.ppt_id = -1
self.controller.remove_doc(self) self.controller.remove_doc(self)
def is_loaded(self): def is_loaded(self):
""" """
Returns true if a presentation is loaded Returns true if a presentation is loaded.
""" """
if self.pptid < 0: if self.ppt_id < 0:
return False return False
if self.get_slide_count() < 0: if self.get_slide_count() < 0:
return False return False
@ -171,74 +169,74 @@ class PptviewDocument(PresentationDocument):
def is_active(self): def is_active(self):
""" """
Returns true if a presentation is currently active Returns true if a presentation is currently active.
""" """
return self.is_loaded() and not self.hidden return self.is_loaded() and not self.hidden
def blank_screen(self): def blank_screen(self):
""" """
Blanks the screen Blanks the screen.
""" """
self.controller.process.Blank(self.pptid) self.controller.process.Blank(self.ppt_id)
self.blanked = True self.blanked = True
def unblank_screen(self): def unblank_screen(self):
""" """
Unblanks (restores) the presentation Unblanks (restores) the presentation.
""" """
self.controller.process.Unblank(self.pptid) self.controller.process.Unblank(self.ppt_id)
self.blanked = False self.blanked = False
def is_blank(self): def is_blank(self):
""" """
Returns true if screen is blank Returns true if screen is blank.
""" """
log.debug(u'is blank OpenOffice') log.debug(u'is blank OpenOffice')
return self.blanked return self.blanked
def stop_presentation(self): def stop_presentation(self):
""" """
Stops the current presentation and hides the output Stops the current presentation and hides the output.
""" """
self.hidden = True self.hidden = True
self.controller.process.Stop(self.pptid) self.controller.process.Stop(self.ppt_id)
def start_presentation(self): def start_presentation(self):
""" """
Starts a presentation from the beginning Starts a presentation from the beginning.
""" """
if self.hidden: if self.hidden:
self.hidden = False self.hidden = False
self.controller.process.Resume(self.pptid) self.controller.process.Resume(self.ppt_id)
else: else:
self.controller.process.RestartShow(self.pptid) self.controller.process.RestartShow(self.ppt_id)
def get_slide_number(self): def get_slide_number(self):
""" """
Returns the current slide number Returns the current slide number.
""" """
return self.controller.process.GetCurrentSlide(self.pptid) return self.controller.process.GetCurrentSlide(self.ppt_id)
def get_slide_count(self): def get_slide_count(self):
""" """
Returns total number of slides Returns total number of slides.
""" """
return self.controller.process.GetSlideCount(self.pptid) return self.controller.process.GetSlideCount(self.ppt_id)
def goto_slide(self, slideno): def goto_slide(self, slideno):
""" """
Moves to a specific slide in the presentation Moves to a specific slide in the presentation.
""" """
self.controller.process.GotoSlide(self.pptid, slideno) self.controller.process.GotoSlide(self.ppt_id, slideno)
def next_step(self): def next_step(self):
""" """
Triggers the next effect of slide on the running presentation Triggers the next effect of slide on the running presentation.
""" """
self.controller.process.NextStep(self.pptid) self.controller.process.NextStep(self.ppt_id)
def previous_step(self): def previous_step(self):
""" """
Triggers the previous slide on the running presentation Triggers the previous slide on the running presentation.
""" """
self.controller.process.PrevStep(self.pptid) self.controller.process.PrevStep(self.ppt_id)

View File

@ -38,11 +38,11 @@ from openlp.core.utils import AppLocation
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class PresentationDocument(object): class PresentationDocument(object):
""" """
Base class for presentation documents to inherit from. Base class for presentation documents to inherit from. Loads and closes the presentation as well as triggering the
Loads and closes the presentation as well as triggering the correct correct activities based on the users input
activities based on the users input
**Hook Functions** **Hook Functions**
@ -131,20 +131,17 @@ class PresentationDocument(object):
""" """
The location where thumbnail images will be stored The location where thumbnail images will be stored
""" """
return os.path.join( return os.path.join(self.controller.thumbnail_folder, self.get_file_name())
self.controller.thumbnail_folder, self.get_file_name())
def get_temp_folder(self): def get_temp_folder(self):
""" """
The location where thumbnail images will be stored The location where thumbnail images will be stored
""" """
return os.path.join( return os.path.join(self.controller.temp_folder, self.get_file_name())
self.controller.temp_folder, self.get_file_name())
def check_thumbnails(self): def check_thumbnails(self):
""" """
Returns ``True`` if the thumbnail images exist and are more recent than Returns ``True`` if the thumbnail images exist and are more recent than the powerpoint file.
the powerpoint file.
""" """
lastimage = self.get_thumbnail_path(self.get_slide_count(), True) lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
if not (lastimage and os.path.isfile(lastimage)): if not (lastimage and os.path.isfile(lastimage)):
@ -153,8 +150,7 @@ class PresentationDocument(object):
def close_presentation(self): def close_presentation(self):
""" """
Close presentation and clean up objects Close presentation and clean up objects. Triggered by new object being added to SlideController
Triggered by new object being added to SlideController
""" """
self.controller.close_presentation() self.controller.close_presentation()
@ -223,8 +219,8 @@ class PresentationDocument(object):
def next_step(self): def next_step(self):
""" """
Triggers the next effect of slide on the running presentation Triggers the next effect of slide on the running presentation. This might be the next animation on the current
This might be the next animation on the current slide, or the next slide slide, or the next slide
""" """
pass pass
@ -236,8 +232,7 @@ class PresentationDocument(object):
def convert_thumbnail(self, file, idx): def convert_thumbnail(self, file, idx):
""" """
Convert the slide image the application made to a standard 320x240 Convert the slide image the application made to a standard 320x240 .png image.
.png image.
""" """
if self.check_thumbnails(): if self.check_thumbnails():
return return
@ -281,7 +276,7 @@ class PresentationDocument(object):
Returns the text on the slide Returns the text on the slide
``slide_no`` ``slide_no``
The slide the text is required for, starting at 1 The slide the text is required for, starting at 1
""" """
return '' return ''
@ -290,24 +285,21 @@ class PresentationDocument(object):
Returns the text on the slide Returns the text on the slide
``slide_no`` ``slide_no``
The slide the notes are required for, starting at 1 The slide the notes are required for, starting at 1
""" """
return '' return ''
class PresentationController(object): class PresentationController(object):
""" """
This class is used to control interactions with presentation applications This class is used to control interactions with presentation applications by creating a runtime environment. This is
by creating a runtime environment. This is a base class for presentation a base class for presentation controllers to inherit from.
controllers to inherit from.
To create a new controller, take a copy of this file and name it so it ends To create a new controller, take a copy of this file and name it so it ends with ``controller.py``, i.e.
with ``controller.py``, i.e. ``foobarcontroller.py``. Make sure it inherits ``foobarcontroller.py``. Make sure it inherits
:class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`, :class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`, and then fill in the
and then fill in the blanks. If possible try to make sure it loads on all blanks. If possible try to make sure it loads on all platforms, usually by using :mod:``os.name`` checks, although
platforms, usually by using :mod:``os.name`` checks, although ``__init__``, ``check_available`` and ``presentation_deleted`` should always be implemented.
``__init__``, ``check_available`` and ``presentation_deleted`` should
always be implemented.
See :class:`~openlp.plugins.presentations.lib.impresscontroller.ImpressController`, See :class:`~openlp.plugins.presentations.lib.impresscontroller.ImpressController`,
:class:`~openlp.plugins.presentations.lib.powerpointcontroller.PowerpointController` or :class:`~openlp.plugins.presentations.lib.powerpointcontroller.PowerpointController` or
@ -317,36 +309,34 @@ class PresentationController(object):
**Basic Attributes** **Basic Attributes**
``name`` ``name``
The name that appears in the options and the media manager The name that appears in the options and the media manager.
``enabled`` ``enabled``
The controller is enabled The controller is enabled.
``available`` ``available``
The controller is available on this machine. Set by init via The controller is available on this machine. Set by init via call to check_available.
call to check_available
``plugin`` ``plugin``
The presentationplugin object The presentationplugin object.
``supports`` ``supports``
The primary native file types this application supports The primary native file types this application supports.
``alsosupports`` ``also_supports``
Other file types the application can import, although not necessarily Other file types the application can import, although not necessarily the first choice due to potential
the first choice due to potential incompatibilities incompatibilities.
**Hook Functions** **Hook Functions**
``kill()`` ``kill()``
Called at system exit to clean up any running presentations Called at system exit to clean up any running presentations.
``check_available()`` ``check_available()``
Returns True if presentation application is installed/can run on this Returns True if presentation application is installed/can run on this machine.
machine
``presentation_deleted()`` ``presentation_deleted()``
Deletes presentation specific files, e.g. thumbnails Deletes presentation specific files, e.g. thumbnails.
""" """
log.info(u'PresentationController loaded') log.info(u'PresentationController loaded')
@ -354,9 +344,8 @@ class PresentationController(object):
def __init__(self, plugin=None, name=u'PresentationController', def __init__(self, plugin=None, name=u'PresentationController',
document_class=PresentationDocument): document_class=PresentationDocument):
""" """
This is the constructor for the presentationcontroller object. This This is the constructor for the presentationcontroller object. This provides an easy way for descendent plugins
provides an easy way for descendent plugins to populate common data. to populate common data. This method *must* be overridden, like so::
This method *must* be overridden, like so::
class MyPresentationController(PresentationController): class MyPresentationController(PresentationController):
def __init__(self, plugin): def __init__(self, plugin):
@ -370,7 +359,7 @@ class PresentationController(object):
Name of the application, to appear in the application Name of the application, to appear in the application
""" """
self.supports = [] self.supports = []
self.alsosupports = [] self.also_supports = []
self.docs = [] self.docs = []
self.plugin = plugin self.plugin = plugin
self.name = name self.name = name
@ -399,28 +388,26 @@ class PresentationController(object):
def check_available(self): def check_available(self):
""" """
Presentation app is able to run on this machine Presentation app is able to run on this machine.
""" """
return False return False
def start_process(self): def start_process(self):
""" """
Loads a running version of the presentation application in the Loads a running version of the presentation application in the background.
background.
""" """
pass pass
def kill(self): def kill(self):
""" """
Called at system exit to clean up any running presentations and Called at system exit to clean up any running presentations and close the application.
close the application
""" """
log.debug(u'Kill') log.debug(u'Kill')
self.close_presentation() self.close_presentation()
def add_document(self, name): def add_document(self, name):
""" """
Called when a new presentation document is opened Called when a new presentation document is opened.
""" """
document = self.document_class(self, name) document = self.document_class(self, name)
self.docs.append(document) self.docs.append(document)
@ -428,7 +415,7 @@ class PresentationController(object):
def remove_doc(self, doc=None): def remove_doc(self, doc=None):
""" """
Called to remove an open document from the collection Called to remove an open document from the collection.
""" """
log.debug(u'remove_doc Presentation') log.debug(u'remove_doc Presentation')
if doc is None: if doc is None:

View File

@ -91,8 +91,7 @@ class PresentationTab(SettingsTab):
if checkbox.isEnabled(): if checkbox.isEnabled():
checkbox.setText(controller.name) checkbox.setText(controller.name)
else: else:
checkbox.setText( checkbox.setText(translate('PresentationPlugin.PresentationTab', '%s (unavailable)') % controller.name)
translate('PresentationPlugin.PresentationTab', '%s (unavailable)') % controller.name)
def load(self): def load(self):
""" """
@ -106,8 +105,8 @@ class PresentationTab(SettingsTab):
def save(self): def save(self):
""" """
Save the settings. If the tab hasn't been made visible to the user then there is nothing to do, Save the settings. If the tab hasn't been made visible to the user then there is nothing to do, so exit. This
so exit. This removes the need to start presentation applications unnecessarily. removes the need to start presentation applications unnecessarily.
""" """
if not self.activated: if not self.activated:
return return

View File

@ -27,8 +27,8 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`presentationplugin` module provides the ability for OpenLP to display The :mod:`presentationplugin` module provides the ability for OpenLP to display presentations from a variety of document
presentations from a variety of document formats. formats.
""" """
import os import os
import logging import logging
@ -39,22 +39,23 @@ from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation
from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
__default_settings__ = { __default_settings__ = {
u'presentations/override app': QtCore.Qt.Unchecked, u'presentations/override app': QtCore.Qt.Unchecked,
u'presentations/Impress': QtCore.Qt.Checked, u'presentations/Impress': QtCore.Qt.Checked,
u'presentations/Powerpoint': QtCore.Qt.Checked, u'presentations/Powerpoint': QtCore.Qt.Checked,
u'presentations/Powerpoint Viewer': QtCore.Qt.Checked, u'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
u'presentations/presentations files': [] u'presentations/presentations files': []
} }
class PresentationPlugin(Plugin): class PresentationPlugin(Plugin):
""" """
This plugin allowed a Presentation to be opened, controlled and displayed This plugin allowed a Presentation to be opened, controlled and displayed on the output display. The plugin controls
on the output display. The plugin controls third party applications such third party applications such as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer.
as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer
""" """
log = logging.getLogger(u'PresentationPlugin') log = logging.getLogger(u'PresentationPlugin')
@ -69,18 +70,16 @@ class PresentationPlugin(Plugin):
self.icon_path = u':/plugins/plugin_presentations.png' self.icon_path = u':/plugins/plugin_presentations.png'
self.icon = build_icon(self.icon_path) self.icon = build_icon(self.icon_path)
def create_settings_Tab(self, parent): def create_settings_tab(self, parent):
""" """
Create the settings Tab Create the settings Tab.
""" """
visible_name = self.get_string(StringContent.VisibleName) visible_name = self.get_string(StringContent.VisibleName)
self.settings_tab = PresentationTab(parent, self.name, visible_name[u'title'], self.controllers, self.settings_tab = PresentationTab(parent, self.name, visible_name[u'title'], self.controllers, self.icon_path)
self.icon_path)
def initialise(self): def initialise(self):
""" """
Initialise the plugin. Determine which controllers are enabled Initialise the plugin. Determine which controllers are enabled are start their processes.
are start their processes.
""" """
log.info(u'Presentations Initialising') log.info(u'Presentations Initialising')
Plugin.initialise(self) Plugin.initialise(self)
@ -95,8 +94,8 @@ class PresentationPlugin(Plugin):
def finalise(self): def finalise(self):
""" """
Finalise the plugin. Ask all the enabled presentation applications Finalise the plugin. Ask all the enabled presentation applications to close down their applications and release
to close down their applications and release resources. resources.
""" """
log.info(u'Plugin Finalise') log.info(u'Plugin Finalise')
# Ask each controller to tidy up. # Ask each controller to tidy up.
@ -108,26 +107,23 @@ class PresentationPlugin(Plugin):
def create_media_manager_item(self): def create_media_manager_item(self):
""" """
Create the Media Manager List Create the Media Manager List.
""" """
self.media_item = PresentationMediaItem( self.media_item = PresentationMediaItem(
self.main_window.media_dock_manager.media_dock, self, self.icon, self.controllers) self.main_window.media_dock_manager.media_dock, self, self.icon, self.controllers)
def register_controllers(self, controller): def register_controllers(self, controller):
""" """
Register each presentation controller (Impress, PPT etc) and store for later use Register each presentation controller (Impress, PPT etc) and store for later use.
""" """
self.controllers[controller.name] = controller self.controllers[controller.name] = controller
def check_pre_conditions(self): def check_pre_conditions(self):
""" """
Check to see if we have any presentation software available Check to see if we have any presentation software available. If not do not install the plugin.
If Not do not install the plugin.
""" """
log.debug(u'check_pre_conditions') log.debug(u'check_pre_conditions')
controller_dir = os.path.join( controller_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'presentations', u'lib')
AppLocation.get_directory(AppLocation.PluginsDir),
u'presentations', u'lib')
for filename in os.listdir(controller_dir): for filename in os.listdir(controller_dir):
if filename.endswith(u'controller.py') and not filename == 'presentationcontroller.py': if filename.endswith(u'controller.py') and not filename == 'presentationcontroller.py':
path = os.path.join(controller_dir, filename) path = os.path.join(controller_dir, filename)
@ -146,7 +142,7 @@ class PresentationPlugin(Plugin):
def about(self): def about(self):
""" """
Return information about this plugin Return information about this plugin.
""" """
about_text = translate('PresentationPlugin', '<strong>Presentation ' about_text = translate('PresentationPlugin', '<strong>Presentation '
'Plugin</strong><br />The presentation plugin provides the ' 'Plugin</strong><br />The presentation plugin provides the '
@ -157,7 +153,7 @@ class PresentationPlugin(Plugin):
def set_plugin_text_strings(self): def set_plugin_text_strings(self):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin.
""" """
## Name PluginList ## ## Name PluginList ##
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {

View File

@ -147,7 +147,7 @@ window.OpenLP = {
}, },
pollServer: function () { pollServer: function () {
$.getJSON( $.getJSON(
"/api/poll", "/stage/poll",
function (data, status) { function (data, status) {
var prevItem = OpenLP.currentItem; var prevItem = OpenLP.currentItem;
OpenLP.currentSlide = data.results.slide; OpenLP.currentSlide = data.results.slide;

View File

@ -26,7 +26,7 @@
window.OpenLP = { window.OpenLP = {
loadService: function (event) { loadService: function (event) {
$.getJSON( $.getJSON(
"/api/service/list", "/stage/service/list",
function (data, status) { function (data, status) {
OpenLP.nextSong = ""; OpenLP.nextSong = "";
$("#notes").html(""); $("#notes").html("");
@ -46,7 +46,7 @@ window.OpenLP = {
}, },
loadSlides: function (event) { loadSlides: function (event) {
$.getJSON( $.getJSON(
"/api/controller/live/text", "/stage/controller/live/text",
function (data, status) { function (data, status) {
OpenLP.currentSlides = data.results.slides; OpenLP.currentSlides = data.results.slides;
OpenLP.currentSlide = 0; OpenLP.currentSlide = 0;
@ -137,7 +137,7 @@ window.OpenLP = {
}, },
pollServer: function () { pollServer: function () {
$.getJSON( $.getJSON(
"/api/poll", "/stage/poll",
function (data, status) { function (data, status) {
OpenLP.updateClock(data); OpenLP.updateClock(data);
if (OpenLP.currentItem != data.results.item || if (OpenLP.currentItem != data.results.item ||

View File

@ -43,7 +43,7 @@ the remotes.
``/files/{filename}`` ``/files/{filename}``
Serve a static file. Serve a static file.
``/api/poll`` ``/stage/api/poll``
Poll to see if there are any changes. Returns a JSON-encoded dict of Poll to see if there are any changes. Returns a JSON-encoded dict of
any changes that occurred:: any changes that occurred::
@ -119,122 +119,198 @@ import os
import re import re
import urllib import urllib
import urlparse import urlparse
import cherrypy
from PyQt4 import QtCore, QtNetwork
from mako.template import Template from mako.template import Template
from PyQt4 import QtCore
from openlp.core.lib import Registry, Settings, PluginStatus, StringContent from openlp.core.lib import Registry, Settings, PluginStatus, StringContent
from openlp.core.utils import AppLocation, translate from openlp.core.utils import AppLocation, translate
from cherrypy._cpcompat import sha, ntob
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class HttpResponse(object): def make_sha_hash(password):
""" """
A simple object to encapsulate a pseudo-http response. Create an encrypted password for the given password.
""" """
code = '200 OK' return sha(ntob(password)).hexdigest()
content = ''
headers = {
'Content-Type': 'text/html; charset="utf-8"\r\n'
}
def __init__(self, content='', headers=None, code=None):
if headers is None: def fetch_password(username):
headers = {} """
self.content = content Fetch the password for a provided user.
for key, value in headers.iteritems(): """
self.headers[key] = value if username != Settings().value(u'remotes/user id'):
if code: return None
self.code = code return make_sha_hash(Settings().value(u'remotes/password'))
class HttpServer(object): class HttpServer(object):
""" """
Ability to control OpenLP via a web browser. Ability to control OpenLP via a web browser.
This class controls the Cherrypy server and configuration.
""" """
def __init__(self, plugin): _cp_config = {
'tools.sessions.on': True,
'tools.auth.on': True
}
def __init__(self):
""" """
Initialise the httpserver, and start the server. Initialise the http server, and start the server.
""" """
log.debug(u'Initialise httpserver') log.debug(u'Initialise httpserver')
self.plugin = plugin self.settings_section = u'remotes'
self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html') self.router = HttpRouter()
self.connections = []
self.start_tcp()
def start_tcp(self): def start_server(self):
""" """
Start the http server, use the port in the settings default to 4316. Start the http server based on configuration.
Listen out for slide and song changes so they can be broadcast to
clients. Listen out for socket connections.
""" """
log.debug(u'Start TCP server') log.debug(u'Start CherryPy server')
port = Settings().value(self.plugin.settings_section + u'/port') # Define to security levels and inject the router code
address = Settings().value(self.plugin.settings_section + u'/ip address') self.root = self.Public()
self.server = QtNetwork.QTcpServer() self.root.files = self.Files()
self.server.listen(QtNetwork.QHostAddress(address), port) self.root.stage = self.Stage()
self.server.newConnection.connect(self.new_connection) self.root.router = self.router
log.debug(u'TCP listening on port %d' % port) self.root.files.router = self.router
self.root.stage.router = self.router
cherrypy.tree.mount(self.root, '/', config=self.define_config())
# Turn off the flood of access messages cause by poll
cherrypy.log.access_log.propagate = False
cherrypy.engine.start()
def new_connection(self): def define_config(self):
""" """
A new http connection has been made. Create a client object to handle Define the configuration of the server.
communication.
""" """
log.debug(u'new http connection') if Settings().value(self.settings_section + u'/https enabled'):
socket = self.server.nextPendingConnection() port = Settings().value(self.settings_section + u'/https port')
if socket: address = Settings().value(self.settings_section + u'/ip address')
self.connections.append(HttpConnection(self, socket)) local_data = AppLocation.get_directory(AppLocation.DataDir)
cherrypy.config.update({u'server.socket_host': str(address),
u'server.socket_port': port,
u'server.ssl_certificate': os.path.join(local_data, u'remotes', u'openlp.crt'),
u'server.ssl_private_key': os.path.join(local_data, u'remotes', u'openlp.key')})
else:
port = Settings().value(self.settings_section + u'/port')
address = Settings().value(self.settings_section + u'/ip address')
cherrypy.config.update({u'server.socket_host': str(address)})
cherrypy.config.update({u'server.socket_port': port})
cherrypy.config.update({u'environment': u'embedded'})
cherrypy.config.update({u'engine.autoreload_on': False})
directory_config = {u'/': {u'tools.staticdir.on': True,
u'tools.staticdir.dir': self.router.html_dir,
u'tools.basic_auth.on': Settings().value(u'remotes/authentication enabled'),
u'tools.basic_auth.realm': u'OpenLP Remote Login',
u'tools.basic_auth.users': fetch_password,
u'tools.basic_auth.encrypt': make_sha_hash},
u'/files': {u'tools.staticdir.on': True,
u'tools.staticdir.dir': self.router.html_dir,
u'tools.basic_auth.on': False},
u'/stage': {u'tools.staticdir.on': True,
u'tools.staticdir.dir': self.router.html_dir,
u'tools.basic_auth.on': False}}
return directory_config
def close_connection(self, connection): class Public(object):
""" """
The connection has been closed. Clean up Main access class with may have security enabled on it.
""" """
log.debug(u'close http connection') @cherrypy.expose
if connection in self.connections: def default(self, *args, **kwargs):
self.connections.remove(connection) self.router.request_data = None
if isinstance(kwargs, dict):
self.router.request_data = kwargs.get(u'data', None)
url = urlparse.urlparse(cherrypy.url())
return self.router.process_http_request(url.path, *args)
class Files(object):
"""
Provides access to files and has no security available. These are read only accesses
"""
@cherrypy.expose
def default(self, *args, **kwargs):
url = urlparse.urlparse(cherrypy.url())
return self.router.process_http_request(url.path, *args)
class Stage(object):
"""
Stageview is read only so security is not relevant and would reduce it's usability
"""
@cherrypy.expose
def default(self, *args, **kwargs):
url = urlparse.urlparse(cherrypy.url())
return self.router.process_http_request(url.path, *args)
def close(self): def close(self):
""" """
Close down the http server. Close down the http server.
""" """
log.debug(u'close http server') log.debug(u'close http server')
self.server.close() cherrypy.engine.exit()
class HttpConnection(object): class HttpRouter(object):
""" """
A single connection, this handles communication between the server This code is called by the HttpServer upon a request and it processes it based on the routing table.
and the client.
""" """
def __init__(self, parent, socket): def __init__(self):
""" """
Initialise the http connection. Listen out for socket signals. Initialise the router
""" """
log.debug(u'Initialise HttpConnection: %s' % socket.peerAddress())
self.socket = socket
self.parent = parent
self.routes = [ self.routes = [
(u'^/$', self.serve_file), (u'^/$', self.serve_file),
(u'^/(stage)$', self.serve_file), (u'^/(stage)$', self.serve_file),
(r'^/files/(.*)$', self.serve_file), (r'^/files/(.*)$', self.serve_file),
(r'^/api/poll$', self.poll), (r'^/api/poll$', self.poll),
(r'^/stage/poll$', self.poll),
(r'^/api/controller/(live|preview)/(.*)$', self.controller), (r'^/api/controller/(live|preview)/(.*)$', self.controller),
(r'^/stage/controller/(live|preview)/(.*)$', self.controller),
(r'^/api/service/(.*)$', self.service), (r'^/api/service/(.*)$', self.service),
(r'^/stage/service/(.*)$', self.service),
(r'^/api/display/(hide|show|blank|theme|desktop)$', self.display), (r'^/api/display/(hide|show|blank|theme|desktop)$', self.display),
(r'^/api/alert$', self.alert), (r'^/api/alert$', self.alert),
(r'^/api/plugin/(search)$', self.pluginInfo), (r'^/api/plugin/(search)$', self.plugin_info),
(r'^/api/(.*)/search$', self.search), (r'^/api/(.*)/search$', self.search),
(r'^/api/(.*)/live$', self.go_live), (r'^/api/(.*)/live$', self.go_live),
(r'^/api/(.*)/add$', self.add_to_service) (r'^/api/(.*)/add$', self.add_to_service)
] ]
self.socket.readyRead.connect(self.ready_read)
self.socket.disconnected.connect(self.disconnected)
self.translate() self.translate()
self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html')
def process_http_request(self, url_path, *args):
"""
Common function to process HTTP requests
``url_path``
The requested URL.
``*args``
Any passed data.
"""
response = None
for route, func in self.routes:
match = re.match(route, url_path)
if match:
log.debug('Route "%s" matched "%s"', route, url_path)
args = []
for param in match.groups():
args.append(param)
response = func(*args)
break
if response:
return response
else:
return self._http_not_found()
def _get_service_items(self): def _get_service_items(self):
"""
Read the service item in use and return the data as a json object
"""
service_items = [] service_items = []
if self.live_controller.service_item: if self.live_controller.service_item:
current_unique_identifier = self.live_controller.service_item.unique_identifier current_unique_identifier = self.live_controller.service_item.unique_identifier
@ -281,40 +357,6 @@ class HttpConnection(object):
'slides': translate('RemotePlugin.Mobile', 'Slides') 'slides': translate('RemotePlugin.Mobile', 'Slides')
} }
def ready_read(self):
"""
Data has been sent from the client. Respond to it
"""
log.debug(u'ready to read socket')
if self.socket.canReadLine():
data = str(self.socket.readLine())
try:
log.debug(u'received: ' + data)
except UnicodeDecodeError:
# Malicious request containing non-ASCII characters.
self.close()
return
words = data.split(' ')
response = None
if words[0] == u'GET':
url = urlparse.urlparse(words[1])
self.url_params = urlparse.parse_qs(url.query)
# Loop through the routes we set up earlier and execute them
for route, func in self.routes:
match = re.match(route, url.path)
if match:
log.debug('Route "%s" matched "%s"', route, url.path)
args = []
for param in match.groups():
args.append(param)
response = func(*args)
break
if response:
self.send_response(response)
else:
self.send_response(HttpResponse(code='404 Not Found'))
self.close()
def serve_file(self, filename=None): def serve_file(self, filename=None):
""" """
Send a file to the socket. For now, just a subset of file types Send a file to the socket. For now, just a subset of file types
@ -329,9 +371,9 @@ class HttpConnection(object):
filename = u'index.html' filename = u'index.html'
elif filename == u'stage': elif filename == u'stage':
filename = u'stage.html' filename = u'stage.html'
path = os.path.normpath(os.path.join(self.parent.html_dir, filename)) path = os.path.normpath(os.path.join(self.html_dir, filename))
if not path.startswith(self.parent.html_dir): if not path.startswith(self.html_dir):
return HttpResponse(code=u'404 Not Found') return self._http_not_found()
ext = os.path.splitext(filename)[1] ext = os.path.splitext(filename)[1]
html = None html = None
if ext == u'.html': if ext == u'.html':
@ -360,11 +402,12 @@ class HttpConnection(object):
content = file_handle.read() content = file_handle.read()
except IOError: except IOError:
log.exception(u'Failed to open %s' % path) log.exception(u'Failed to open %s' % path)
return HttpResponse(code=u'404 Not Found') return self._http_not_found()
finally: finally:
if file_handle: if file_handle:
file_handle.close() file_handle.close()
return HttpResponse(content, {u'Content-Type': mimetype}) cherrypy.response.headers['Content-Type'] = mimetype
return content
def poll(self): def poll(self):
""" """
@ -379,18 +422,20 @@ class HttpConnection(object):
u'theme': self.live_controller.theme_screen.isChecked(), u'theme': self.live_controller.theme_screen.isChecked(),
u'display': self.live_controller.desktop_screen.isChecked() u'display': self.live_controller.desktop_screen.isChecked()
} }
return HttpResponse(json.dumps({u'results': result}), {u'Content-Type': u'application/json'}) cherrypy.response.headers['Content-Type'] = u'application/json'
return json.dumps({u'results': result})
def display(self, action): def display(self, action):
""" """
Hide or show the display screen. Hide or show the display screen.
This is a cross Thread call and UI is updated so Events need to be used.
``action`` ``action``
This is the action, either ``hide`` or ``show``. This is the action, either ``hide`` or ``show``.
""" """
Registry().execute(u'slidecontroller_toggle_display', action) self.live_controller.emit(QtCore.SIGNAL(u'slidecontroller_toggle_display'), action)
return HttpResponse(json.dumps({u'results': {u'success': True}}), cherrypy.response.headers['Content-Type'] = u'application/json'
{u'Content-Type': u'application/json'}) return json.dumps({u'results': {u'success': True}})
def alert(self): def alert(self):
""" """
@ -399,16 +444,16 @@ class HttpConnection(object):
plugin = self.plugin_manager.get_plugin_by_name("alerts") plugin = self.plugin_manager.get_plugin_by_name("alerts")
if plugin.status == PluginStatus.Active: if plugin.status == PluginStatus.Active:
try: try:
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text'] text = json.loads(self.request_data)[u'request'][u'text']
except KeyError, ValueError: except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request') return self._http_bad_request()
text = urllib.unquote(text) text = urllib.unquote(text)
Registry().execute(u'alerts_text', [text]) self.alerts_manager.emit(QtCore.SIGNAL(u'alerts_text'), [text])
success = True success = True
else: else:
success = False success = False
return HttpResponse(json.dumps({u'results': {u'success': success}}), cherrypy.response.headers['Content-Type'] = u'application/json'
{u'Content-Type': u'application/json'}) return json.dumps({u'results': {u'success': success}})
def controller(self, display_type, action): def controller(self, display_type, action):
""" """
@ -444,44 +489,44 @@ class HttpConnection(object):
if current_item: if current_item:
json_data[u'results'][u'item'] = self.live_controller.service_item.unique_identifier json_data[u'results'][u'item'] = self.live_controller.service_item.unique_identifier
else: else:
if self.url_params and self.url_params.get(u'data'): if self.request_data:
try: try:
data = json.loads(self.url_params[u'data'][0]) data = json.loads(self.request_data)[u'request'][u'id']
except KeyError, ValueError: except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request') return self._http_bad_request()
log.info(data) log.info(data)
# This slot expects an int within a list. # This slot expects an int within a list.
id = data[u'request'][u'id'] self.live_controller.emit(QtCore.SIGNAL(event), [data])
Registry().execute(event, [id])
else: else:
Registry().execute(event) self.live_controller.emit(QtCore.SIGNAL(event))
json_data = {u'results': {u'success': True}} json_data = {u'results': {u'success': True}}
return HttpResponse(json.dumps(json_data), {u'Content-Type': u'application/json'}) cherrypy.response.headers['Content-Type'] = u'application/json'
return json.dumps(json_data)
def service(self, action): def service(self, action):
""" """
Handles requests for service items Handles requests for service items in the service manager
``action`` ``action``
The action to perform. The action to perform.
""" """
event = u'servicemanager_%s' % action event = u'servicemanager_%s' % action
if action == u'list': if action == u'list':
return HttpResponse(json.dumps({u'results': {u'items': self._get_service_items()}}), cherrypy.response.headers['Content-Type'] = u'application/json'
{u'Content-Type': u'application/json'}) return json.dumps({u'results': {u'items': self._get_service_items()}})
else: event += u'_item'
event += u'_item' if self.request_data:
if self.url_params and self.url_params.get(u'data'):
try: try:
data = json.loads(self.url_params[u'data'][0]) data = json.loads(self.request_data)[u'request'][u'id']
except KeyError, ValueError: except KeyError:
return HttpResponse(code=u'400 Bad Request') return self._http_bad_request()
Registry().execute(event, data[u'request'][u'id']) self.service_manager.emit(QtCore.SIGNAL(event), data)
else: else:
Registry().execute(event) Registry().execute(event)
return HttpResponse(json.dumps({u'results': {u'success': True}}), {u'Content-Type': u'application/json'}) cherrypy.response.headers['Content-Type'] = u'application/json'
return json.dumps({u'results': {u'success': True}})
def pluginInfo(self, action): def plugin_info(self, action):
""" """
Return plugin related information, based on the action. Return plugin related information, based on the action.
@ -493,8 +538,9 @@ class HttpConnection(object):
searches = [] searches = []
for plugin in self.plugin_manager.plugins: for plugin in self.plugin_manager.plugins:
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search: if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
searches.append([plugin.name, unicode(plugin.textStrings[StringContent.Name][u'plural'])]) searches.append([plugin.name, unicode(plugin.text_strings[StringContent.Name][u'plural'])])
return HttpResponse(json.dumps({u'results': {u'items': searches}}), {u'Content-Type': u'application/json'}) cherrypy.response.headers['Content-Type'] = u'application/json'
return json.dumps({u'results': {u'items': searches}})
def search(self, plugin_name): def search(self, plugin_name):
""" """
@ -504,69 +550,63 @@ class HttpConnection(object):
The plugin name to search in. The plugin name to search in.
""" """
try: try:
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text'] text = json.loads(self.request_data)[u'request'][u'text']
except KeyError, ValueError: except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request') return self._http_bad_request()
text = urllib.unquote(text) text = urllib.unquote(text)
plugin = self.plugin_manager.get_plugin_by_name(plugin_name) plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search: if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
results = plugin.media_item.search(text, False) results = plugin.media_item.search(text, False)
else: else:
results = [] results = []
return HttpResponse(json.dumps({u'results': {u'items': results}}), {u'Content-Type': u'application/json'}) cherrypy.response.headers['Content-Type'] = u'application/json'
return json.dumps({u'results': {u'items': results}})
def go_live(self, plugin_name): def go_live(self, plugin_name):
""" """
Go live on an item of type ``plugin``. Go live on an item of type ``plugin``.
""" """
try: try:
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id'] id = json.loads(self.request_data)[u'request'][u'id']
except KeyError, ValueError: except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request') return self._http_bad_request()
plugin = self.plugin_manager.get_plugin_by_name(plugin_name) plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
if plugin.status == PluginStatus.Active and plugin.media_item: if plugin.status == PluginStatus.Active and plugin.media_item:
plugin.media_item.go_live(id, remote=True) plugin.media_item.emit(QtCore.SIGNAL(u'%s_go_live' % plugin_name), [id, True])
return HttpResponse(code=u'200 OK') return self._http_success()
def add_to_service(self, plugin_name): def add_to_service(self, plugin_name):
""" """
Add item of type ``plugin_name`` to the end of the service. Add item of type ``plugin_name`` to the end of the service.
""" """
try: try:
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id'] id = json.loads(self.request_data)[u'request'][u'id']
except KeyError, ValueError: except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request') return self._http_bad_request()
plugin = self.plugin_manager.get_plugin_by_name(plugin_name) plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
if plugin.status == PluginStatus.Active and plugin.media_item: if plugin.status == PluginStatus.Active and plugin.media_item:
item_id = plugin.media_item.createItemFromId(id) item_id = plugin.media_item.create_item_from_id(id)
plugin.media_item.add_to_service(item_id, remote=True) plugin.media_item.emit(QtCore.SIGNAL(u'%s_add_to_service' % plugin_name), [item_id, True])
return HttpResponse(code=u'200 OK') self._http_success()
def send_response(self, response): def _http_success(self):
http = u'HTTP/1.1 %s\r\n' % response.code """
for header, value in response.headers.iteritems(): Set the HTTP success return code.
http += '%s: %s\r\n' % (header, value) """
http += '\r\n' cherrypy.response.status = 200
self.socket.write(http)
self.socket.write(response.content)
def disconnected(self): def _http_bad_request(self):
""" """
The client has disconnected. Tidy up Set the HTTP bad response return code.
""" """
log.debug(u'socket disconnected') cherrypy.response.status = 400
self.close()
def close(self): def _http_not_found(self):
""" """
The server has closed the connection. Tidy up Set the HTTP not found return code.
""" """
if not self.socket: cherrypy.response.status = 404
return cherrypy.response.body = ["<html><body>Sorry, an error occurred </body></html>"]
log.debug(u'close socket')
self.socket.close()
self.socket = None
self.parent.close_connection(self)
def _get_service_manager(self): def _get_service_manager(self):
""" """
@ -597,3 +637,13 @@ class HttpConnection(object):
return self._plugin_manager return self._plugin_manager
plugin_manager = property(_get_plugin_manager) plugin_manager = property(_get_plugin_manager)
def _get_alerts_manager(self):
"""
Adds the alerts manager to the class dynamically
"""
if not hasattr(self, u'_alerts_manager'):
self._alerts_manager = Registry().get(u'alerts_manager')
return self._alerts_manager
alerts_manager = property(_get_alerts_manager)

View File

@ -27,9 +27,12 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
import os.path
from PyQt4 import QtCore, QtGui, QtNetwork from PyQt4 import QtCore, QtGui, QtNetwork
from openlp.core.lib import Registry, Settings, SettingsTab, translate from openlp.core.lib import Settings, SettingsTab, translate
from openlp.core.utils import AppLocation
ZERO_URL = u'0.0.0.0' ZERO_URL = u'0.0.0.0'
@ -53,32 +56,84 @@ class RemoteTab(SettingsTab):
self.address_label.setObjectName(u'address_label') self.address_label.setObjectName(u'address_label')
self.address_edit = QtGui.QLineEdit(self.server_settings_group_box) self.address_edit = QtGui.QLineEdit(self.server_settings_group_box)
self.address_edit.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.address_edit.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
self.address_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp( self.address_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'),
u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'), self)) self))
self.address_edit.setObjectName(u'address_edit') self.address_edit.setObjectName(u'address_edit')
self.server_settings_layout.addRow(self.address_label, self.address_edit) self.server_settings_layout.addRow(self.address_label, self.address_edit)
self.twelve_hour_check_box = QtGui.QCheckBox(self.server_settings_group_box) self.twelve_hour_check_box = QtGui.QCheckBox(self.server_settings_group_box)
self.twelve_hour_check_box.setObjectName(u'twelve_hour_check_box') self.twelve_hour_check_box.setObjectName(u'twelve_hour_check_box')
self.server_settings_layout.addRow(self.twelve_hour_check_box) self.server_settings_layout.addRow(self.twelve_hour_check_box)
self.port_label = QtGui.QLabel(self.server_settings_group_box) self.left_layout.addWidget(self.server_settings_group_box)
self.http_settings_group_box = QtGui.QGroupBox(self.left_column)
self.http_settings_group_box.setObjectName(u'http_settings_group_box')
self.http_setting_layout = QtGui.QFormLayout(self.http_settings_group_box)
self.http_setting_layout.setObjectName(u'http_setting_layout')
self.port_label = QtGui.QLabel(self.http_settings_group_box)
self.port_label.setObjectName(u'port_label') self.port_label.setObjectName(u'port_label')
self.port_spin_box = QtGui.QSpinBox(self.server_settings_group_box) self.port_spin_box = QtGui.QSpinBox(self.http_settings_group_box)
self.port_spin_box.setMaximum(32767) self.port_spin_box.setMaximum(32767)
self.port_spin_box.setObjectName(u'port_spin_box') self.port_spin_box.setObjectName(u'port_spin_box')
self.server_settings_layout.addRow(self.port_label, self.port_spin_box) self.http_setting_layout.addRow(self.port_label, self.port_spin_box)
self.remote_url_label = QtGui.QLabel(self.server_settings_group_box) self.remote_url_label = QtGui.QLabel(self.http_settings_group_box)
self.remote_url_label.setObjectName(u'remote_url_label') self.remote_url_label.setObjectName(u'remote_url_label')
self.remote_url = QtGui.QLabel(self.server_settings_group_box) self.remote_url = QtGui.QLabel(self.http_settings_group_box)
self.remote_url.setObjectName(u'remote_url') self.remote_url.setObjectName(u'remote_url')
self.remote_url.setOpenExternalLinks(True) self.remote_url.setOpenExternalLinks(True)
self.server_settings_layout.addRow(self.remote_url_label, self.remote_url) self.http_setting_layout.addRow(self.remote_url_label, self.remote_url)
self.stage_url_label = QtGui.QLabel(self.server_settings_group_box) self.stage_url_label = QtGui.QLabel(self.http_settings_group_box)
self.stage_url_label.setObjectName(u'stage_url_label') self.stage_url_label.setObjectName(u'stage_url_label')
self.stage_url = QtGui.QLabel(self.server_settings_group_box) self.stage_url = QtGui.QLabel(self.http_settings_group_box)
self.stage_url.setObjectName(u'stage_url') self.stage_url.setObjectName(u'stage_url')
self.stage_url.setOpenExternalLinks(True) self.stage_url.setOpenExternalLinks(True)
self.server_settings_layout.addRow(self.stage_url_label, self.stage_url) self.http_setting_layout.addRow(self.stage_url_label, self.stage_url)
self.left_layout.addWidget(self.server_settings_group_box) self.left_layout.addWidget(self.http_settings_group_box)
self.https_settings_group_box = QtGui.QGroupBox(self.left_column)
self.https_settings_group_box.setCheckable(True)
self.https_settings_group_box.setChecked(False)
self.https_settings_group_box.setObjectName(u'https_settings_group_box')
self.https_settings_layout = QtGui.QFormLayout(self.https_settings_group_box)
self.https_settings_layout.setObjectName(u'https_settings_layout')
self.https_error_label = QtGui.QLabel(self.https_settings_group_box)
self.https_error_label.setVisible(False)
self.https_error_label.setWordWrap(True)
self.https_error_label.setObjectName(u'https_error_label')
self.https_settings_layout.addRow(self.https_error_label)
self.https_port_label = QtGui.QLabel(self.https_settings_group_box)
self.https_port_label.setObjectName(u'https_port_label')
self.https_port_spin_box = QtGui.QSpinBox(self.https_settings_group_box)
self.https_port_spin_box.setMaximum(32767)
self.https_port_spin_box.setObjectName(u'https_port_spin_box')
self.https_settings_layout.addRow(self.https_port_label, self.https_port_spin_box)
self.remote_https_url = QtGui.QLabel(self.https_settings_group_box)
self.remote_https_url.setObjectName(u'remote_http_url')
self.remote_https_url.setOpenExternalLinks(True)
self.remote_https_url_label = QtGui.QLabel(self.https_settings_group_box)
self.remote_https_url_label.setObjectName(u'remote_http_url_label')
self.https_settings_layout.addRow(self.remote_https_url_label, self.remote_https_url)
self.stage_https_url_label = QtGui.QLabel(self.http_settings_group_box)
self.stage_https_url_label.setObjectName(u'stage_https_url_label')
self.stage_https_url = QtGui.QLabel(self.https_settings_group_box)
self.stage_https_url.setObjectName(u'stage_https_url')
self.stage_https_url.setOpenExternalLinks(True)
self.https_settings_layout.addRow(self.stage_https_url_label, self.stage_https_url)
self.left_layout.addWidget(self.https_settings_group_box)
self.user_login_group_box = QtGui.QGroupBox(self.left_column)
self.user_login_group_box.setCheckable(True)
self.user_login_group_box.setChecked(False)
self.user_login_group_box.setObjectName(u'user_login_group_box')
self.user_login_layout = QtGui.QFormLayout(self.user_login_group_box)
self.user_login_layout.setObjectName(u'user_login_layout')
self.user_id_label = QtGui.QLabel(self.user_login_group_box)
self.user_id_label.setObjectName(u'user_id_label')
self.user_id = QtGui.QLineEdit(self.user_login_group_box)
self.user_id.setObjectName(u'user_id')
self.user_login_layout.addRow(self.user_id_label, self.user_id)
self.password_label = QtGui.QLabel(self.user_login_group_box)
self.password_label.setObjectName(u'password_label')
self.password = QtGui.QLineEdit(self.user_login_group_box)
self.password.setObjectName(u'password')
self.user_login_layout.addRow(self.password_label, self.password)
self.left_layout.addWidget(self.user_login_group_box)
self.android_app_group_box = QtGui.QGroupBox(self.right_column) self.android_app_group_box = QtGui.QGroupBox(self.right_column)
self.android_app_group_box.setObjectName(u'android_app_group_box') self.android_app_group_box.setObjectName(u'android_app_group_box')
self.right_layout.addWidget(self.android_app_group_box) self.right_layout.addWidget(self.android_app_group_box)
@ -96,9 +151,11 @@ class RemoteTab(SettingsTab):
self.qr_layout.addWidget(self.qr_description_label) self.qr_layout.addWidget(self.qr_description_label)
self.left_layout.addStretch() self.left_layout.addStretch()
self.right_layout.addStretch() self.right_layout.addStretch()
self.twelve_hour_check_box.stateChanged.connect(self.onTwelveHourCheckBoxChanged) self.twelve_hour_check_box.stateChanged.connect(self.on_twelve_hour_check_box_changed)
self.address_edit.textChanged.connect(self.set_urls) self.address_edit.textChanged.connect(self.set_urls)
self.port_spin_box.valueChanged.connect(self.set_urls) self.port_spin_box.valueChanged.connect(self.set_urls)
self.https_port_spin_box.valueChanged.connect(self.set_urls)
self.https_settings_group_box.clicked.connect(self.https_changed)
def retranslateUi(self): def retranslateUi(self):
self.server_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Server Settings')) self.server_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Server Settings'))
@ -112,8 +169,21 @@ class RemoteTab(SettingsTab):
'Scan the QR code or click <a href="https://play.google.com/store/' 'Scan the QR code or click <a href="https://play.google.com/store/'
'apps/details?id=org.openlp.android">download</a> to install the ' 'apps/details?id=org.openlp.android">download</a> to install the '
'Android app from Google Play.')) 'Android app from Google Play.'))
self.https_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'HTTPS Server'))
self.https_error_label.setText(translate('RemotePlugin.RemoteTab',
'Could not find an SSL certificate. The HTTPS server will not be available unless an SSL certificate '
'is found. Please see the manual for more information.'))
self.https_port_label.setText(self.port_label.text())
self.remote_https_url_label.setText(self.remote_url_label.text())
self.stage_https_url_label.setText(self.stage_url_label.text())
self.user_login_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'User Authentication'))
self.user_id_label.setText(translate('RemotePlugin.RemoteTab', 'User id:'))
self.password_label.setText(translate('RemotePlugin.RemoteTab', 'Password:'))
def set_urls(self): def set_urls(self):
"""
Update the display based on the data input on the screen
"""
ip_address = u'localhost' ip_address = u'localhost'
if self.address_edit.text() == ZERO_URL: if self.address_edit.text() == ZERO_URL:
interfaces = QtNetwork.QNetworkInterface.allInterfaces() interfaces = QtNetwork.QNetworkInterface.allInterfaces()
@ -129,31 +199,73 @@ class RemoteTab(SettingsTab):
break break
else: else:
ip_address = self.address_edit.text() ip_address = self.address_edit.text()
url = u'http://%s:%s/' % (ip_address, self.port_spin_box.value()) http_url = u'http://%s:%s/' % (ip_address, self.port_spin_box.value())
self.remote_url.setText(u'<a href="%s">%s</a>' % (url, url)) https_url = u'https://%s:%s/' % (ip_address, self.https_port_spin_box.value())
url += u'stage' self.remote_url.setText(u'<a href="%s">%s</a>' % (http_url, http_url))
self.stage_url.setText(u'<a href="%s">%s</a>' % (url, url)) self.remote_https_url.setText(u'<a href="%s">%s</a>' % (https_url, https_url))
http_url += u'stage'
https_url += u'stage'
self.stage_url.setText(u'<a href="%s">%s</a>' % (http_url, http_url))
self.stage_https_url.setText(u'<a href="%s">%s</a>' % (https_url, https_url))
def load(self): def load(self):
"""
Load the configuration and update the server configuration if necessary
"""
self.port_spin_box.setValue(Settings().value(self.settings_section + u'/port')) self.port_spin_box.setValue(Settings().value(self.settings_section + u'/port'))
self.https_port_spin_box.setValue(Settings().value(self.settings_section + u'/https port'))
self.address_edit.setText(Settings().value(self.settings_section + u'/ip address')) self.address_edit.setText(Settings().value(self.settings_section + u'/ip address'))
self.twelve_hour = Settings().value(self.settings_section + u'/twelve hour') self.twelve_hour = Settings().value(self.settings_section + u'/twelve hour')
self.twelve_hour_check_box.setChecked(self.twelve_hour) self.twelve_hour_check_box.setChecked(self.twelve_hour)
local_data = AppLocation.get_directory(AppLocation.DataDir)
if not os.path.exists(os.path.join(local_data, u'remotes', u'openlp.crt')) or \
not os.path.exists(os.path.join(local_data, u'remotes', u'openlp.key')):
self.https_settings_group_box.setChecked(False)
self.https_settings_group_box.setEnabled(False)
self.https_error_label.setVisible(True)
else:
self.https_settings_group_box.setChecked(Settings().value(self.settings_section + u'/https enabled'))
self.https_settings_group_box.setEnabled(True)
self.https_error_label.setVisible(False)
self.user_login_group_box.setChecked(Settings().value(self.settings_section + u'/authentication enabled'))
self.user_id.setText(Settings().value(self.settings_section + u'/user id'))
self.password.setText(Settings().value(self.settings_section + u'/password'))
self.set_urls() self.set_urls()
self.https_changed()
def save(self): def save(self):
changed = False """
Save the configuration and update the server configuration if necessary
"""
if Settings().value(self.settings_section + u'/ip address') != self.address_edit.text() or \ if Settings().value(self.settings_section + u'/ip address') != self.address_edit.text() or \
Settings().value(self.settings_section + u'/port') != self.port_spin_box.value(): Settings().value(self.settings_section + u'/port') != self.port_spin_box.value() or \
changed = True Settings().value(self.settings_section + u'/https port') != self.https_port_spin_box.value() or \
Settings().value(self.settings_section + u'/https enabled') != \
self.https_settings_group_box.isChecked() or \
Settings().value(self.settings_section + u'/authentication enabled') != \
self.user_login_group_box.isChecked():
self.settings_form.register_post_process(u'remotes_config_updated')
Settings().setValue(self.settings_section + u'/port', self.port_spin_box.value()) Settings().setValue(self.settings_section + u'/port', self.port_spin_box.value())
Settings().setValue(self.settings_section + u'/https port', self.https_port_spin_box.value())
Settings().setValue(self.settings_section + u'/https enabled', self.https_settings_group_box.isChecked())
Settings().setValue(self.settings_section + u'/ip address', self.address_edit.text()) Settings().setValue(self.settings_section + u'/ip address', self.address_edit.text())
Settings().setValue(self.settings_section + u'/twelve hour', self.twelve_hour) Settings().setValue(self.settings_section + u'/twelve hour', self.twelve_hour)
if changed: Settings().setValue(self.settings_section + u'/authentication enabled', self.user_login_group_box.isChecked())
Registry().execute(u'remotes_config_updated') Settings().setValue(self.settings_section + u'/user id', self.user_id.text())
Settings().setValue(self.settings_section + u'/password', self.password.text())
def onTwelveHourCheckBoxChanged(self, check_state): def on_twelve_hour_check_box_changed(self, check_state):
"""
Toggle the 12 hour check box.
"""
self.twelve_hour = False self.twelve_hour = False
# we have a set value convert to True/False # we have a set value convert to True/False
if check_state == QtCore.Qt.Checked: if check_state == QtCore.Qt.Checked:
self.twelve_hour = True self.twelve_hour = True
def https_changed(self):
"""
Invert the HTTP group box based on Https group settings
"""
self.http_settings_group_box.setEnabled(not self.https_settings_group_box.isChecked())

View File

@ -29,6 +29,8 @@
import logging import logging
from PyQt4 import QtGui
from openlp.core.lib import Plugin, StringContent, translate, build_icon from openlp.core.lib import Plugin, StringContent, translate, build_icon
from openlp.plugins.remotes.lib import RemoteTab, HttpServer from openlp.plugins.remotes.lib import RemoteTab, HttpServer
@ -37,6 +39,11 @@ log = logging.getLogger(__name__)
__default_settings__ = { __default_settings__ = {
u'remotes/twelve hour': True, u'remotes/twelve hour': True,
u'remotes/port': 4316, u'remotes/port': 4316,
u'remotes/https port': 4317,
u'remotes/https enabled': False,
u'remotes/user id': u'openlp',
u'remotes/password': u'password',
u'remotes/authentication enabled': False,
u'remotes/ip address': u'0.0.0.0' u'remotes/ip address': u'0.0.0.0'
} }
@ -60,7 +67,8 @@ class RemotesPlugin(Plugin):
""" """
log.debug(u'initialise') log.debug(u'initialise')
Plugin.initialise(self) Plugin.initialise(self)
self.server = HttpServer(self) self.server = HttpServer()
self.server.start_server()
def finalise(self): def finalise(self):
""" """
@ -70,6 +78,7 @@ class RemotesPlugin(Plugin):
Plugin.finalise(self) Plugin.finalise(self)
if self.server: if self.server:
self.server.close() self.server.close()
self.server = None
def about(self): def about(self):
""" """
@ -99,5 +108,6 @@ class RemotesPlugin(Plugin):
""" """
Called when Config is changed to restart the server on new address or port Called when Config is changed to restart the server on new address or port
""" """
self.finalise() log.debug(u'remote config changed')
self.initialise() self.main_window.information_message(translate('RemotePlugin', 'Configuration Change'),
translate('RemotePlugin', 'OpenLP will need to be restarted for the Remote changes to become active.'))

View File

@ -52,3 +52,4 @@ This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
them separate from the functionality, so that it is easier to recreate the GUI them separate from the functionality, so that it is easier to recreate the GUI
from the .ui files later if necessary. from the .ui files later if necessary.
""" """
from editsongform import EditSongForm

View File

@ -320,7 +320,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
for plugin in self.plugin_manager.plugins: for plugin in self.plugin_manager.plugins:
if plugin.name == u'media' and plugin.status == PluginStatus.Active: if plugin.name == u'media' and plugin.status == PluginStatus.Active:
self.from_media_button.setVisible(True) self.from_media_button.setVisible(True)
self.media_form.populateFiles(plugin.media_item.getList(MediaType.Audio)) self.media_form.populateFiles(plugin.media_item.get_list(MediaType.Audio))
break break
def new_song(self): def new_song(self):
@ -714,7 +714,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
text = self.song_book_combo_box.currentText() text = self.song_book_combo_box.currentText()
if item == 0 and text: if item == 0 and text:
temp_song_book = text temp_song_book = text
self.media_item.songMaintenanceForm.exec_(True) self.media_item.song_maintenance_form.exec_(True)
self.load_authors() self.load_authors()
self.load_books() self.load_books()
self.load_topics() self.load_topics()

View File

@ -235,7 +235,7 @@ class SongExportForm(OpenLPWizard):
self.availableListWidget.addItem(item) self.availableListWidget.addItem(item)
self.application.set_normal_cursor() self.application.set_normal_cursor()
def preWizard(self): def pre_wizard(self):
""" """
Perform pre export tasks. Perform pre export tasks.
""" """

View File

@ -325,7 +325,7 @@ class SongImportForm(OpenLPWizard):
self.error_copy_to_button.setHidden(True) self.error_copy_to_button.setHidden(True)
self.error_save_to_button.setHidden(True) self.error_save_to_button.setHidden(True)
def preWizard(self): def pre_wizard(self):
""" """
Perform pre import tasks Perform pre import tasks
""" """

View File

@ -50,13 +50,11 @@ from sundayplusimport import SundayPlusImport
from foilpresenterimport import FoilPresenterImport from foilpresenterimport import FoilPresenterImport
from zionworximport import ZionWorxImport from zionworximport import ZionWorxImport
# Imports that might fail # Imports that might fail
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
try:
from olp1import import OpenLP1SongImport
HAS_OPENLP1 = True
except ImportError:
log.exception('Error importing %s', 'OpenLP1SongImport')
HAS_OPENLP1 = False
try: try:
from sofimport import SofImport from sofimport import SofImport
HAS_SOF = True HAS_SOF = True
@ -144,23 +142,22 @@ class SongFormat(object):
Unknown = -1 Unknown = -1
OpenLyrics = 0 OpenLyrics = 0
OpenLP2 = 1 OpenLP2 = 1
OpenLP1 = 2 Generic = 2
Generic = 3 CCLI = 3
CCLI = 4 DreamBeam = 4
DreamBeam = 5 EasySlides = 5
EasySlides = 6 EasyWorship = 6
EasyWorship = 7 FoilPresenter = 7
FoilPresenter = 8 MediaShout = 8
MediaShout = 9 OpenSong = 9
OpenSong = 10 PowerSong = 10
PowerSong = 11 SongBeamer = 11
SongBeamer = 12 SongPro = 12
SongPro = 13 SongShowPlus = 13
SongShowPlus = 14 SongsOfFellowship = 14
SongsOfFellowship = 15 SundayPlus = 15
SundayPlus = 16 WordsOfWorship = 16
WordsOfWorship = 17 ZionWorx = 17
ZionWorx = 18
# Set optional attribute defaults # Set optional attribute defaults
__defaults__ = { __defaults__ = {
@ -191,14 +188,6 @@ class SongFormat(object):
u'selectMode': SongFormatSelect.SingleFile, u'selectMode': SongFormatSelect.SingleFile,
u'filter': u'%s (*.sqlite)' % (translate('SongsPlugin.ImportWizardForm', 'OpenLP 2.0 Databases')) u'filter': u'%s (*.sqlite)' % (translate('SongsPlugin.ImportWizardForm', 'OpenLP 2.0 Databases'))
}, },
OpenLP1: {
u'name': UiStrings().OLPV1,
u'prefix': u'openLP1',
u'canDisable': True,
u'selectMode': SongFormatSelect.SingleFile,
u'filter': u'%s (*.olp)' % translate('SongsPlugin.ImportWizardForm', 'openlp.org v1.x Databases'),
u'disabledLabelText': WizardStrings.NoSqlite
},
Generic: { Generic: {
u'name': translate('SongsPlugin.ImportWizardForm', 'Generic Document/Presentation'), u'name': translate('SongsPlugin.ImportWizardForm', 'Generic Document/Presentation'),
u'prefix': u'generic', u'prefix': u'generic',
@ -332,7 +321,6 @@ class SongFormat(object):
return [ return [
SongFormat.OpenLyrics, SongFormat.OpenLyrics,
SongFormat.OpenLP2, SongFormat.OpenLP2,
SongFormat.OpenLP1,
SongFormat.Generic, SongFormat.Generic,
SongFormat.CCLI, SongFormat.CCLI,
SongFormat.DreamBeam, SongFormat.DreamBeam,
@ -387,9 +375,7 @@ class SongFormat(object):
""" """
SongFormat.__attributes__[format][attribute] = value SongFormat.__attributes__[format][attribute] = value
SongFormat.set(SongFormat.OpenLP1, u'availability', HAS_OPENLP1)
if HAS_OPENLP1:
SongFormat.set(SongFormat.OpenLP1, u'class', OpenLP1SongImport)
SongFormat.set(SongFormat.SongsOfFellowship, u'availability', HAS_SOF) SongFormat.set(SongFormat.SongsOfFellowship, u'availability', HAS_SOF)
if HAS_SOF: if HAS_SOF:
SongFormat.set(SongFormat.SongsOfFellowship, u'class', SofImport) SongFormat.set(SongFormat.SongsOfFellowship, u'class', SofImport)

View File

@ -72,27 +72,21 @@ class SongMediaItem(MediaManagerItem):
def __init__(self, parent, plugin): def __init__(self, parent, plugin):
self.icon_path = u'songs/song' self.icon_path = u'songs/song'
MediaManagerItem.__init__(self, parent, plugin) MediaManagerItem.__init__(self, parent, plugin)
self.editSongForm = EditSongForm(self, self.main_window, self.plugin.manager)
self.openLyrics = OpenLyrics(self.plugin.manager)
self.single_service_item = False self.single_service_item = False
self.songMaintenanceForm = SongMaintenanceForm(self.plugin.manager, self) # Holds information about whether the edit is remotely triggered and which Song is required.
# Holds information about whether the edit is remotely triggered and self.remote_song = -1
# which Song is required. self.edit_item = None
self.remoteSong = -1
self.editItem = None
self.quick_preview_allowed = True self.quick_preview_allowed = True
self.has_search = True self.has_search = True
def _updateBackgroundAudio(self, song, item): def _update_background_audio(self, song, item):
song.media_files = [] song.media_files = []
for i, bga in enumerate(item.background_audio): for i, bga in enumerate(item.background_audio):
dest_file = os.path.join( dest_file = os.path.join(
AppLocation.get_section_data_path(self.plugin.name), u'audio', str(song.id), os.path.split(bga)[1]) AppLocation.get_section_data_path(self.plugin.name), u'audio', str(song.id), os.path.split(bga)[1])
check_directory_exists(os.path.split(dest_file)[0]) check_directory_exists(os.path.split(dest_file)[0])
shutil.copyfile(os.path.join(AppLocation.get_section_data_path(u'servicemanager'), bga), shutil.copyfile(os.path.join(AppLocation.get_section_data_path(u'servicemanager'), bga), dest_file)
dest_file) song.media_files.append(MediaFile.populate(weight=i, file_name=dest_file))
song.media_files.append(MediaFile.populate(
weight=i, file_name=dest_file))
self.plugin.manager.save_object(song, True) self.plugin.manager.save_object(song, True)
def add_end_header_bar(self): def add_end_header_bar(self):
@ -100,12 +94,12 @@ class SongMediaItem(MediaManagerItem):
## Song Maintenance Button ## ## Song Maintenance Button ##
self.maintenanceAction = self.toolbar.add_toolbar_action('maintenanceAction', self.maintenanceAction = self.toolbar.add_toolbar_action('maintenanceAction',
icon=':/songs/song_maintenance.png', icon=':/songs/song_maintenance.png',
triggers=self.onSongMaintenanceClick) triggers=self.on_song_maintenance_click)
self.add_search_to_toolbar() self.add_search_to_toolbar()
# Signals and slots # Signals and slots
Registry().register_function(u'songs_load_list', self.on_song_list_load) Registry().register_function(u'songs_load_list', self.on_song_list_load)
Registry().register_function(u'songs_preview', self.on_preview_click) Registry().register_function(u'songs_preview', self.on_preview_click)
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'cleared()'), self.onClearTextButtonClick) QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'cleared()'), self.on_clear_text_button_click)
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'searchTypeChanged(int)'), QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'searchTypeChanged(int)'),
self.on_search_text_button_clicked) self.on_search_text_button_clicked)
@ -113,17 +107,17 @@ class SongMediaItem(MediaManagerItem):
create_widget_action(self.list_view, separator=True) create_widget_action(self.list_view, separator=True)
create_widget_action(self.list_view, create_widget_action(self.list_view,
text=translate('OpenLP.MediaManagerItem', '&Clone'), icon=u':/general/general_clone.png', text=translate('OpenLP.MediaManagerItem', '&Clone'), icon=u':/general/general_clone.png',
triggers=self.onCloneClick) triggers=self.on_clone_click)
def onFocus(self): def on_focus(self):
self.search_text_edit.setFocus() self.search_text_edit.setFocus()
def config_update(self): def config_update(self):
""" """
IS triggered when the songs config is updated Is triggered when the songs config is updated
""" """
log.debug(u'config_updated') log.debug(u'config_updated')
self.searchAsYouType = Settings().value(self.settings_section + u'/search as type') self.search_as_you_type = Settings().value(self.settings_section + u'/search as type')
self.updateServiceOnEdit = Settings().value(self.settings_section + u'/update service on edit') self.updateServiceOnEdit = Settings().value(self.settings_section + u'/update service on edit')
self.addSongFromService = Settings().value(self.settings_section + u'/add song from service',) self.addSongFromService = Settings().value(self.settings_section + u'/add song from service',)
@ -135,6 +129,12 @@ class SongMediaItem(MediaManagerItem):
'Maintain the lists of authors, topics and books.')) 'Maintain the lists of authors, topics and books.'))
def initialise(self): def initialise(self):
"""
Initialise variables when they cannot be initialised in the constructor.
"""
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
self.openLyrics = OpenLyrics(self.plugin.manager)
self.search_text_edit.set_search_types([ self.search_text_edit.set_search_types([
(SongSearch.Entire, u':/songs/song_search_all.png', (SongSearch.Entire, u':/songs/song_search_all.png',
translate('SongsPlugin.MediaItem', 'Entire Song'), translate('SongsPlugin.MediaItem', 'Entire Song'),
@ -160,27 +160,26 @@ class SongMediaItem(MediaManagerItem):
Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type()) Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
# Reload the list considering the new search type. # Reload the list considering the new search type.
search_keywords = unicode(self.search_text_edit.displayText()) search_keywords = unicode(self.search_text_edit.displayText())
search_results = []
search_type = self.search_text_edit.current_search_type() search_type = self.search_text_edit.current_search_type()
if search_type == SongSearch.Entire: if search_type == SongSearch.Entire:
log.debug(u'Entire Song Search') log.debug(u'Entire Song Search')
search_results = self.searchEntire(search_keywords) search_results = self.search_entire(search_keywords)
self.displayResultsSong(search_results) self.display_results_song(search_results)
elif search_type == SongSearch.Titles: elif search_type == SongSearch.Titles:
log.debug(u'Titles Search') log.debug(u'Titles Search')
search_results = self.plugin.manager.get_all_objects(Song, search_results = self.plugin.manager.get_all_objects(Song,
Song.search_title.like(u'%' + clean_string(search_keywords) + u'%')) Song.search_title.like(u'%' + clean_string(search_keywords) + u'%'))
self.displayResultsSong(search_results) self.display_results_song(search_results)
elif search_type == SongSearch.Lyrics: elif search_type == SongSearch.Lyrics:
log.debug(u'Lyrics Search') log.debug(u'Lyrics Search')
search_results = self.plugin.manager.get_all_objects(Song, search_results = self.plugin.manager.get_all_objects(Song,
Song.search_lyrics.like(u'%' + clean_string(search_keywords) + u'%')) Song.search_lyrics.like(u'%' + clean_string(search_keywords) + u'%'))
self.displayResultsSong(search_results) self.display_results_song(search_results)
elif search_type == SongSearch.Authors: elif search_type == SongSearch.Authors:
log.debug(u'Authors Search') log.debug(u'Authors Search')
search_results = self.plugin.manager.get_all_objects(Author, search_results = self.plugin.manager.get_all_objects(Author,
Author.display_name.like(u'%' + search_keywords + u'%'), Author.display_name.asc()) Author.display_name.like(u'%' + search_keywords + u'%'), Author.display_name.asc())
self.displayResultsAuthor(search_results) self.display_results_author(search_results)
elif search_type == SongSearch.Books: elif search_type == SongSearch.Books:
log.debug(u'Books Search') log.debug(u'Books Search')
search_results = self.plugin.manager.get_all_objects(Book, search_results = self.plugin.manager.get_all_objects(Book,
@ -191,15 +190,15 @@ class SongMediaItem(MediaManagerItem):
search_results = self.plugin.manager.get_all_objects(Book, search_results = self.plugin.manager.get_all_objects(Book,
Book.name.like(u'%' + search_keywords[0] + u'%'), Book.name.asc()) Book.name.like(u'%' + search_keywords[0] + u'%'), Book.name.asc())
song_number = re.sub(r'[^0-9]', u'', search_keywords[2]) song_number = re.sub(r'[^0-9]', u'', search_keywords[2])
self.displayResultsBook(search_results, song_number) self.display_results_book(search_results, song_number)
elif search_type == SongSearch.Themes: elif search_type == SongSearch.Themes:
log.debug(u'Theme Search') log.debug(u'Theme Search')
search_results = self.plugin.manager.get_all_objects(Song, search_results = self.plugin.manager.get_all_objects(Song,
Song.theme_name.like(u'%' + search_keywords + u'%')) Song.theme_name.like(u'%' + search_keywords + u'%'))
self.displayResultsSong(search_results) self.display_results_song(search_results)
self.check_search_result() self.check_search_result()
def searchEntire(self, search_keywords): def search_entire(self, search_keywords):
return self.plugin.manager.get_all_objects(Song, return self.plugin.manager.get_all_objects(Song,
or_(Song.search_title.like(u'%' + clean_string(search_keywords) + u'%'), or_(Song.search_title.like(u'%' + clean_string(search_keywords) + u'%'),
Song.search_lyrics.like(u'%' + clean_string(search_keywords) + u'%'), Song.search_lyrics.like(u'%' + clean_string(search_keywords) + u'%'),
@ -211,17 +210,16 @@ class SongMediaItem(MediaManagerItem):
of songs of songs
""" """
log.debug(u'on_song_list_load - start') log.debug(u'on_song_list_load - start')
# Called to redisplay the song list screen edit from a search # Called to redisplay the song list screen edit from a search or from the exit of the Song edit dialog. If
# or from the exit of the Song edit dialog. If remote editing is active # remote editing is active Trigger it and clean up so it will not update again. Push edits to the service
# Trigger it and clean up so it will not update again. # manager to update items
# Push edits to the service manager to update items if self.edit_item and self.updateServiceOnEdit and not self.remote_triggered:
if self.editItem and self.updateServiceOnEdit and not self.remote_triggered: item = self.build_service_item(self.edit_item)
item = self.build_service_item(self.editItem)
self.service_manager.replace_service_item(item) self.service_manager.replace_service_item(item)
self.on_search_text_button_clicked() self.on_search_text_button_clicked()
log.debug(u'on_song_list_load - finished') log.debug(u'on_song_list_load - finished')
def displayResultsSong(self, searchresults): def display_results_song(self, searchresults):
log.debug(u'display results Song') log.debug(u'display results Song')
self.save_auto_select_id() self.save_auto_select_id()
self.list_view.clear() self.list_view.clear()
@ -241,7 +239,7 @@ class SongMediaItem(MediaManagerItem):
self.list_view.setCurrentItem(song_name) self.list_view.setCurrentItem(song_name)
self.auto_select_id = -1 self.auto_select_id = -1
def displayResultsAuthor(self, searchresults): def display_results_author(self, searchresults):
log.debug(u'display results Author') log.debug(u'display results Author')
self.list_view.clear() self.list_view.clear()
for author in searchresults: for author in searchresults:
@ -254,7 +252,7 @@ class SongMediaItem(MediaManagerItem):
song_name.setData(QtCore.Qt.UserRole, song.id) song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name) self.list_view.addItem(song_name)
def displayResultsBook(self, searchresults, song_number=False): def display_results_book(self, searchresults, song_number=False):
log.debug(u'display results Book') log.debug(u'display results Book')
self.list_view.clear() self.list_view.clear()
for book in searchresults: for book in searchresults:
@ -271,7 +269,7 @@ class SongMediaItem(MediaManagerItem):
song_name.setData(QtCore.Qt.UserRole, song.id) song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name) self.list_view.addItem(song_name)
def onClearTextButtonClick(self): def on_clear_text_button_click(self):
""" """
Clear the search text. Clear the search text.
""" """
@ -280,11 +278,10 @@ class SongMediaItem(MediaManagerItem):
def on_search_text_edit_changed(self, text): def on_search_text_edit_changed(self, text):
""" """
If search as type enabled invoke the search on each key press. If search as type enabled invoke the search on each key press. If the Lyrics are being searched do not start
If the Lyrics are being searched do not start till 7 characters till 7 characters have been entered.
have been entered.
""" """
if self.searchAsYouType: if self.search_as_you_type:
search_length = 1 search_length = 1
if self.search_text_edit.current_search_type() == SongSearch.Entire: if self.search_text_edit.current_search_type() == SongSearch.Entire:
search_length = 4 search_length = 4
@ -293,7 +290,7 @@ class SongMediaItem(MediaManagerItem):
if len(text) > search_length: if len(text) > search_length:
self.on_search_text_button_clicked() self.on_search_text_button_clicked()
elif not text: elif not text:
self.onClearTextButtonClick() self.on_clear_text_button_click()
def on_import_click(self): def on_import_click(self):
if not hasattr(self, u'import_wizard'): if not hasattr(self, u'import_wizard'):
@ -309,33 +306,32 @@ class SongMediaItem(MediaManagerItem):
def on_new_click(self): def on_new_click(self):
log.debug(u'on_new_click') log.debug(u'on_new_click')
self.editSongForm.new_song() self.edit_song_form.new_song()
self.editSongForm.exec_() self.edit_song_form.exec_()
self.onClearTextButtonClick() self.on_clear_text_button_click()
self.on_selection_change() self.on_selection_change()
self.auto_select_id = -1 self.auto_select_id = -1
def onSongMaintenanceClick(self): def on_song_maintenance_click(self):
self.songMaintenanceForm.exec_() self.song_maintenance_form.exec_()
def onRemoteEdit(self, song_id, preview=False): def on_remote_edit(self, song_id, preview=False):
""" """
Called by ServiceManager or SlideController by event passing Called by ServiceManager or SlideController by event passing the Song Id in the payload along with an indicator
the Song Id in the payload along with an indicator to say which to say which type of display is required.
type of display is required.
""" """
log.debug(u'onRemoteEdit for song %s' % song_id) log.debug(u'on_remote_edit for song %s' % song_id)
song_id = int(song_id) song_id = int(song_id)
valid = self.plugin.manager.get_object(Song, song_id) valid = self.plugin.manager.get_object(Song, song_id)
if valid: if valid:
self.editSongForm.load_song(song_id, preview) self.edit_song_form.load_song(song_id, preview)
if self.editSongForm.exec_() == QtGui.QDialog.Accepted: if self.edit_song_form.exec_() == QtGui.QDialog.Accepted:
self.auto_select_id = -1 self.auto_select_id = -1
self.on_song_list_load() self.on_song_list_load()
self.remoteSong = song_id self.remote_song = song_id
self.remote_triggered = True self.remote_triggered = True
item = self.build_service_item(remote=True) item = self.build_service_item(remote=True)
self.remoteSong = -1 self.remote_song = -1
self.remote_triggered = None self.remote_triggered = None
if item: if item:
return item return item
@ -347,13 +343,13 @@ class SongMediaItem(MediaManagerItem):
""" """
log.debug(u'on_edit_click') log.debug(u'on_edit_click')
if check_item_selected(self.list_view, UiStrings().SelectEdit): if check_item_selected(self.list_view, UiStrings().SelectEdit):
self.editItem = self.list_view.currentItem() self.edit_item = self.list_view.currentItem()
item_id = self.editItem.data(QtCore.Qt.UserRole) item_id = self.edit_item.data(QtCore.Qt.UserRole)
self.editSongForm.load_song(item_id, False) self.edit_song_form.load_song(item_id, False)
self.editSongForm.exec_() self.edit_song_form.exec_()
self.auto_select_id = -1 self.auto_select_id = -1
self.on_song_list_load() self.on_song_list_load()
self.editItem = None self.edit_item = None
def on_delete_click(self): def on_delete_click(self):
""" """
@ -362,11 +358,11 @@ class SongMediaItem(MediaManagerItem):
if check_item_selected(self.list_view, UiStrings().SelectDelete): if check_item_selected(self.list_view, UiStrings().SelectDelete):
items = self.list_view.selectedIndexes() items = self.list_view.selectedIndexes()
if QtGui.QMessageBox.question(self, if QtGui.QMessageBox.question(self,
UiStrings().ConfirmDelete, UiStrings().ConfirmDelete,
translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the %n selected song(s)?', '', translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the %n selected song(s)?', '',
QtCore.QCoreApplication.CodecForTr, len(items)), QtCore.QCoreApplication.CodecForTr, len(items)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No: QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
return return
self.application.set_busy_cursor() self.application.set_busy_cursor()
self.main_window.display_progress_bar(len(items)) self.main_window.display_progress_bar(len(items))
@ -390,14 +386,14 @@ class SongMediaItem(MediaManagerItem):
self.application.set_normal_cursor() self.application.set_normal_cursor()
self.on_search_text_button_clicked() self.on_search_text_button_clicked()
def onCloneClick(self): def on_clone_click(self):
""" """
Clone a Song Clone a Song
""" """
log.debug(u'onCloneClick') log.debug(u'on_clone_click')
if check_item_selected(self.list_view, UiStrings().SelectEdit): if check_item_selected(self.list_view, UiStrings().SelectEdit):
self.editItem = self.list_view.currentItem() self.edit_item = self.list_view.currentItem()
item_id = self.editItem.data(QtCore.Qt.UserRole) item_id = self.edit_item.data(QtCore.Qt.UserRole)
old_song = self.plugin.manager.get_object(Song, item_id) old_song = self.plugin.manager.get_object(Song, item_id)
song_xml = self.openLyrics.song_to_xml(old_song) song_xml = self.openLyrics.song_to_xml(old_song)
new_song = self.openLyrics.xml_to_song(song_xml) new_song = self.openLyrics.xml_to_song(song_xml)
@ -411,8 +407,8 @@ class SongMediaItem(MediaManagerItem):
""" """
Generate the slide data. Needs to be implemented by the plugin. Generate the slide data. Needs to be implemented by the plugin.
""" """
log.debug(u'generate_slide_data: %s, %s, %s' % (service_item, item, self.remoteSong)) log.debug(u'generate_slide_data: %s, %s, %s' % (service_item, item, self.remote_song))
item_id = self._get_id_of_item_to_generate(item, self.remoteSong) item_id = self._get_id_of_item_to_generate(item, self.remote_song)
service_item.add_capability(ItemCapabilities.CanEdit) service_item.add_capability(ItemCapabilities.CanEdit)
service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.CanLoop) service_item.add_capability(ItemCapabilities.CanLoop)
@ -430,8 +426,8 @@ class SongMediaItem(MediaManagerItem):
verse_tags_translated = True verse_tags_translated = True
if not song.verse_order.strip(): if not song.verse_order.strip():
for verse in verse_list: for verse in verse_list:
# We cannot use from_loose_input() here, because database # We cannot use from_loose_input() here, because database is supposed to contain English lowercase
# is supposed to contain English lowercase singlechar tags. # singlechar tags.
verse_tag = verse[0][u'type'] verse_tag = verse[0][u'type']
verse_index = None verse_index = None
if len(verse_tag) > 1: if len(verse_tag) > 1:
@ -463,16 +459,7 @@ class SongMediaItem(MediaManagerItem):
for slide in verses: for slide in verses:
service_item.add_from_text(unicode(slide)) service_item.add_from_text(unicode(slide))
service_item.title = song.title service_item.title = song.title
author_list = [unicode(author.display_name) for author in song.authors] author_list = self.generate_footer(service_item, song)
service_item.raw_footer.append(song.title)
service_item.raw_footer.append(create_separated_list(author_list))
service_item.raw_footer.append(song.copyright)
if Settings().value(u'general/ccli number'):
service_item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
Settings().value(u'general/ccli number'))
service_item.audit = [
song.title, author_list, song.copyright, unicode(song.ccli_number)
]
service_item.data_string = {u'title': song.search_title, u'authors': u', '.join(author_list)} service_item.data_string = {u'title': song.search_title, u'authors': u', '.join(author_list)}
service_item.xml_version = self.openLyrics.song_to_xml(song) service_item.xml_version = self.openLyrics.song_to_xml(song)
# Add the audio file to the service item. # Add the audio file to the service item.
@ -481,6 +468,30 @@ class SongMediaItem(MediaManagerItem):
service_item.background_audio = [m.file_name for m in song.media_files] service_item.background_audio = [m.file_name for m in song.media_files]
return True return True
def generate_footer(self, item, song):
"""
Generates the song footer based on a song and adds details to a service item.
author_list is only required for initial song generation.
``item``
The service item to be amended
``song``
The song to be used to generate the footer
"""
author_list = [unicode(author.display_name) for author in song.authors]
item.audit = [
song.title, author_list, song.copyright, unicode(song.ccli_number)
]
item.raw_footer = []
item.raw_footer.append(song.title)
item.raw_footer.append(create_separated_list(author_list))
item.raw_footer.append(song.copyright)
if Settings().value(u'core/ccli number'):
item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
Settings().value(u'core/ccli number'))
return author_list
def service_load(self, item): def service_load(self, item):
""" """
Triggered by a song being loaded by the service manager. Triggered by a song being loaded by the service manager.
@ -489,9 +500,8 @@ class SongMediaItem(MediaManagerItem):
if self.plugin.status != PluginStatus.Active or not item.data_string: if self.plugin.status != PluginStatus.Active or not item.data_string:
return return
if item.data_string[u'title'].find(u'@') == -1: if item.data_string[u'title'].find(u'@') == -1:
# This file seems to be an old one (prior to 1.9.5), which means, # FIXME: This file seems to be an old one (prior to 1.9.5), which means, that the search title
# that the search title (data_string[u'title']) is probably wrong. # (data_string[u'title']) is probably wrong. We add "@" to search title and hope that we do not add any
# We add "@" to search title and hope that we do not add any
# duplicate. This should work for songs without alternate title. # duplicate. This should work for songs without alternate title.
search_results = self.plugin.manager.get_all_objects(Song, search_results = self.plugin.manager.get_all_objects(Song,
Song.search_title == (re.compile(r'\W+', re.UNICODE).sub(u' ', Song.search_title == (re.compile(r'\W+', re.UNICODE).sub(u' ',
@ -499,9 +509,8 @@ class SongMediaItem(MediaManagerItem):
else: else:
search_results = self.plugin.manager.get_all_objects(Song, search_results = self.plugin.manager.get_all_objects(Song,
Song.search_title == item.data_string[u'title'], Song.search_title.asc()) Song.search_title == item.data_string[u'title'], Song.search_title.asc())
editId = 0 edit_id = 0
add_song = True add_song = True
temporary = False
if search_results: if search_results:
for song in search_results: for song in search_results:
author_list = item.data_string[u'authors'] author_list = item.data_string[u'authors']
@ -514,16 +523,16 @@ class SongMediaItem(MediaManagerItem):
break break
if same_authors and author_list.strip(u', ') == u'': if same_authors and author_list.strip(u', ') == u'':
add_song = False add_song = False
editId = song.id edit_id = song.id
break break
# If there's any backing tracks, copy them over. # If there's any backing tracks, copy them over.
if item.background_audio: if item.background_audio:
self._updateBackgroundAudio(song, item) self._update_background_audio(song, item)
if add_song and self.addSongFromService: if add_song and self.addSongFromService:
song = self.openLyrics.xml_to_song(item.xml_version) song = self.openLyrics.xml_to_song(item.xml_version)
# If there's any backing tracks, copy them over. # If there's any backing tracks, copy them over.
if item.background_audio: if item.background_audio:
self._updateBackgroundAudio(song, item) self._update_background_audio(song, item)
editId = song.id editId = song.id
self.on_search_text_button_clicked() self.on_search_text_button_clicked()
elif add_song and not self.addSongFromService: elif add_song and not self.addSongFromService:
@ -531,16 +540,16 @@ class SongMediaItem(MediaManagerItem):
song = self.openLyrics.xml_to_song(item.xml_version, True) song = self.openLyrics.xml_to_song(item.xml_version, True)
# If there's any backing tracks, copy them over. # If there's any backing tracks, copy them over.
if item.background_audio: if item.background_audio:
self._updateBackgroundAudio(song, item) self._update_background_audio(song, item)
editId = song.id edit_id = song.id
temporary = True # Update service with correct song id and return it to caller.
# Update service with correct song id. item.edit_id = edit_id
if editId: self.generate_footer(item, song)
self.service_manager.service_item_update(editId, item.unique_identifier, temporary) return item
def search(self, string, showError): def search(self, string, showError):
""" """
Search for some songs Search for some songs
""" """
search_results = self.searchEntire(string) search_results = self.search_entire(string)
return [[song.id, song.title] for song in search_results] return [[song.id, song.title] for song in search_results]

View File

@ -1,227 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 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 #
###############################################################################
"""
The :mod:`olp1import` module provides the functionality for importing
openlp.org 1.x song databases into the current installation database.
"""
import logging
from chardet.universaldetector import UniversalDetector
import sqlite
import sys
import os
from openlp.core.lib import Registry, translate
from openlp.plugins.songs.lib import retrieve_windows_encoding
from songimport import SongImport
log = logging.getLogger(__name__)
class OpenLP1SongImport(SongImport):
"""
The :class:`OpenLP1SongImport` class provides OpenLP with the ability to
import song databases from installations of openlp.org 1.x.
"""
lastEncoding = u'windows-1252'
def __init__(self, manager, **kwargs):
"""
Initialise the import.
``manager``
The song manager for the running OpenLP installation.
``filename``
The database providing the data to import.
"""
SongImport.__init__(self, manager, **kwargs)
def doImport(self):
"""
Run the import for an openlp.org 1.x song database.
"""
if not self.import_source.endswith(u'.olp'):
self.logError(self.import_source,
translate('SongsPlugin.OpenLP1SongImport', 'Not a valid openlp.org 1.x song database.'))
return
encoding = self.getEncoding()
if not encoding:
return
# Connect to the database.
connection = sqlite.connect(self.import_source, mode=0444, encoding=(encoding, 'replace'))
cursor = connection.cursor()
# Determine if the db supports linking audio to songs.
cursor.execute(u'SELECT name FROM sqlite_master '
u'WHERE type = \'table\' AND name = \'tracks\'')
db_has_tracks = len(cursor.fetchall()) > 0
# Determine if the db contains theme information.
cursor.execute(u'SELECT name FROM sqlite_master '
u'WHERE type = \'table\' AND name = \'settings\'')
db_has_themes = len(cursor.fetchall()) > 0
# "cache" our list of authors.
cursor.execute(u'-- types int, unicode')
cursor.execute(u'SELECT authorid, authorname FROM authors')
authors = cursor.fetchall()
if db_has_tracks:
# "cache" our list of tracks.
cursor.execute(u'-- types int, unicode')
cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
tracks = cursor.fetchall()
if db_has_themes:
# "cache" our list of themes.
themes = {}
cursor.execute(u'-- types int, unicode')
cursor.execute(u'SELECT settingsid, settingsname FROM settings')
for theme_id, theme_name in cursor.fetchall():
if theme_name in self.theme_manager.get_themes():
themes[theme_id] = theme_name
# Import the songs.
cursor.execute(u'-- types int, unicode, unicode, unicode')
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS ' \
u'lyrics, copyrightinfo FROM songs')
songs = cursor.fetchall()
self.import_wizard.progress_bar.setMaximum(len(songs))
for song in songs:
self.setDefaults()
if self.stop_import_flag:
break
song_id = song[0]
self.title = song[1]
lyrics = song[2].replace(u'\r\n', u'\n')
self.addCopyright(song[3])
if db_has_themes:
cursor.execute(u'-- types int')
cursor.execute(
u'SELECT settingsid FROM songs WHERE songid = %s' % song_id)
theme_id = cursor.fetchone()[0]
self.themeName = themes.get(theme_id, u'')
verses = lyrics.split(u'\n\n')
for verse in verses:
if verse.strip():
self.addVerse(verse.strip())
cursor.execute(u'-- types int')
cursor.execute(u'SELECT authorid FROM songauthors '
u'WHERE songid = %s' % song_id)
author_ids = cursor.fetchall()
for author_id in author_ids:
if self.stop_import_flag:
break
for author in authors:
if author[0] == author_id[0]:
self.parse_author(author[1])
break
if self.stop_import_flag:
break
if db_has_tracks:
cursor.execute(u'-- types int, int')
cursor.execute(u'SELECT trackid, listindex '
u'FROM songtracks '
u'WHERE songid = %s ORDER BY listindex' % song_id)
track_ids = cursor.fetchall()
for track_id, listindex in track_ids:
if self.stop_import_flag:
break
for track in tracks:
if track[0] == track_id:
media_file = self.expandMediaFile(track[1])
self.addMediaFile(media_file, listindex)
break
if self.stop_import_flag:
break
if not self.finish():
self.logError(self.import_source)
def getEncoding(self):
"""
Detect character encoding of an openlp.org 1.x song database.
"""
# Connect to the database.
connection = sqlite.connect(self.import_source.encode(
sys.getfilesystemencoding()), mode=0444)
cursor = connection.cursor()
detector = UniversalDetector()
# Detect charset by authors.
cursor.execute(u'SELECT authorname FROM authors')
authors = cursor.fetchall()
for author in authors:
detector.feed(author[0])
if detector.done:
detector.close()
return detector.result[u'encoding']
# Detect charset by songs.
cursor.execute(u'SELECT songtitle, copyrightinfo, '
u'lyrics || \'\' AS lyrics FROM songs')
songs = cursor.fetchall()
for index in [0, 1, 2]:
for song in songs:
detector.feed(song[index])
if detector.done:
detector.close()
return detector.result[u'encoding']
# Detect charset by songs.
cursor.execute(u'SELECT name FROM sqlite_master '
u'WHERE type = \'table\' AND name = \'tracks\'')
if cursor.fetchall():
cursor.execute(u'SELECT fulltrackname FROM tracks')
tracks = cursor.fetchall()
for track in tracks:
detector.feed(track[0])
if detector.done:
detector.close()
return detector.result[u'encoding']
detector.close()
return retrieve_windows_encoding(detector.result[u'encoding'])
def expandMediaFile(self, filename):
"""
When you're on Windows, this function expands the file name to include
the path to OpenLP's application data directory. If you are not on
Windows, it returns the original file name.
``filename``
The filename to expand.
"""
if sys.platform != u'win32' and not os.environ.get(u'ALLUSERSPROFILE') and not os.environ.get(u'APPDATA'):
return filename
common_app_data = os.path.join(os.environ[u'ALLUSERSPROFILE'],
os.path.split(os.environ[u'APPDATA'])[1])
if not common_app_data:
return filename
return os.path.join(common_app_data, u'openlp.org', 'Audio', filename)
def _get_theme_manager(self):
"""
Adds the theme manager to the class dynamically
"""
if not hasattr(self, u'_theme_manager'):
self._theme_manager = Registry().get(u'theme_manager')
return self._theme_manager
theme_manager = property(_get_theme_manager)

View File

@ -235,8 +235,7 @@ class SongsPlugin(Plugin):
u'delete': translate('SongsPlugin', 'Delete the selected song.'), u'delete': translate('SongsPlugin', 'Delete the selected song.'),
u'preview': translate('SongsPlugin', 'Preview the selected song.'), u'preview': translate('SongsPlugin', 'Preview the selected song.'),
u'live': translate('SongsPlugin', 'Send the selected song live.'), u'live': translate('SongsPlugin', 'Send the selected song live.'),
u'service': translate('SongsPlugin', u'service': translate('SongsPlugin', 'Add the selected song to the service.')
'Add the selected song to the service.')
} }
self.set_plugin_ui_text_strings(tooltips) self.set_plugin_ui_text_strings(tooltips)

View File

@ -27,7 +27,6 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`songusage` module contains the Song Usage plugin. The Song Usage The :mod:`songusage` module contains the Song Usage plugin. The Song Usage plugin provides auditing capabilities for
plugin provides auditing capabilities for reporting the songs you are using to reporting the songs you are using to copyright license organisations.
copyright license organisations.
""" """

View File

@ -55,7 +55,8 @@ class Ui_SongUsageDeleteDialog(object):
self.retranslateUi(song_usage_delete_dialog) self.retranslateUi(song_usage_delete_dialog)
def retranslateUi(self, song_usage_delete_dialog): def retranslateUi(self, song_usage_delete_dialog):
song_usage_delete_dialog.setWindowTitle(translate('SongUsagePlugin.SongUsageDeleteForm', 'Delete Song Usage Data')) song_usage_delete_dialog.setWindowTitle(
translate('SongUsagePlugin.SongUsageDeleteForm', 'Delete Song Usage Data'))
self.delete_label.setText( self.delete_label.setText(
translate('SongUsagePlugin.SongUsageDeleteForm', 'Select the date up to which the song usage data ' translate('SongUsagePlugin.SongUsageDeleteForm', 'Select the date up to which the song usage data '
'should be deleted. All data recorded before this date will be permanently deleted.')) 'should be deleted. All data recorded before this date will be permanently deleted.'))

View File

@ -81,7 +81,8 @@ class Ui_SongUsageDetailDialog(object):
self.save_file_push_button.clicked.connect(song_usage_detail_dialog.define_output_location) self.save_file_push_button.clicked.connect(song_usage_detail_dialog.define_output_location)
def retranslateUi(self, song_usage_detail_dialog): def retranslateUi(self, song_usage_detail_dialog):
song_usage_detail_dialog.setWindowTitle(translate('SongUsagePlugin.SongUsageDetailForm', 'Song Usage Extraction')) song_usage_detail_dialog.setWindowTitle(
translate('SongUsagePlugin.SongUsageDetailForm', 'Song Usage Extraction'))
self.date_range_group_box.setTitle(translate('SongUsagePlugin.SongUsageDetailForm', 'Select Date Range')) self.date_range_group_box.setTitle(translate('SongUsagePlugin.SongUsageDetailForm', 'Select Date Range'))
self.to_label.setText(translate('SongUsagePlugin.SongUsageDetailForm', 'to')) self.to_label.setText(translate('SongUsagePlugin.SongUsageDetailForm', 'to'))
self.file_group_box.setTitle(translate('SongUsagePlugin.SongUsageDetailForm', 'Report Location')) self.file_group_box.setTitle(translate('SongUsagePlugin.SongUsageDetailForm', 'Report Location'))

View File

@ -36,12 +36,14 @@ from sqlalchemy.orm import mapper
from openlp.core.lib.db import BaseModel, init_db from openlp.core.lib.db import BaseModel, init_db
class SongUsageItem(BaseModel): class SongUsageItem(BaseModel):
""" """
SongUsageItem model SongUsageItem model
""" """
pass pass
def init_schema(url): def init_schema(url):
""" """
Setup the songusage database connection and initialise the database schema Setup the songusage database connection and initialise the database schema

View File

@ -48,11 +48,11 @@ if QtCore.QDate().currentDate().month() < 9:
__default_settings__ = { __default_settings__ = {
u'songusage/db type': u'sqlite', u'songusage/db type': u'sqlite',
u'songusage/active': False, u'songusage/active': False,
u'songusage/to date': QtCore.QDate(YEAR, 8, 31), u'songusage/to date': QtCore.QDate(YEAR, 8, 31),
u'songusage/from date': QtCore.QDate(YEAR - 1, 9, 1), u'songusage/from date': QtCore.QDate(YEAR - 1, 9, 1),
u'songusage/last directory export': u'' u'songusage/last directory export': u''
} }
@ -76,12 +76,10 @@ class SongUsagePlugin(Plugin):
def add_tools_menu_item(self, tools_menu): def add_tools_menu_item(self, tools_menu):
""" """
Give the SongUsage plugin the opportunity to add items to the Give the SongUsage plugin the opportunity to add items to the **Tools** menu.
**Tools** menu.
``tools_menu`` ``tools_menu``
The actual **Tools** menu item, so that your actions can The actual **Tools** menu item, so that your actions can use it as their parent.
use it as their parent.
""" """
log.info(u'add tools menu') log.info(u'add tools menu')
self.toolsMenu = tools_menu self.toolsMenu = tools_menu
@ -218,8 +216,8 @@ class SongUsagePlugin(Plugin):
self.song_usage_detail_form.exec_() self.song_usage_detail_form.exec_()
def about(self): def about(self):
about_text = translate('SongUsagePlugin', '<strong>SongUsage Plugin' about_text = translate('SongUsagePlugin',
'</strong><br />This plugin tracks the usage of songs in services.') '<strong>SongUsage Plugin</strong><br />This plugin tracks the usage of songs in services.')
return about_text return about_text
def set_plugin_text_strings(self): def set_plugin_text_strings(self):

View File

@ -79,8 +79,9 @@ MODULES = [
'lxml', 'lxml',
'chardet', 'chardet',
'enchant', 'enchant',
'BeautifulSoup', 'bs4',
'mako', 'mako',
'cherrypy',
'migrate', 'migrate',
'uno', 'uno',
'icu', 'icu',
@ -88,7 +89,6 @@ MODULES = [
OPTIONAL_MODULES = [ OPTIONAL_MODULES = [
('sqlite', ' (SQLite 2 support)'),
('MySQLdb', ' (MySQL support)'), ('MySQLdb', ' (MySQL support)'),
('psycopg2', ' (PostgreSQL support)'), ('psycopg2', ' (PostgreSQL support)'),
('nose', ' (testing framework)'), ('nose', ' (testing framework)'),

View File

@ -1,13 +1,20 @@
""" """
Package to test the openlp.core.lib package. Package to test the openlp.core.lib package.
""" """
import os
from unittest import TestCase from unittest import TestCase
from datetime import datetime, timedelta from datetime import datetime, timedelta
from mock import MagicMock, patch from mock import MagicMock, patch
from PyQt4 import QtCore, QtGui
from openlp.core.lib import str_to_bool, create_thumb, translate, check_directory_exists, get_text_file_string, \
build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
from openlp.core.lib import str_to_bool, translate, check_directory_exists, get_text_file_string, build_icon, \
image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags
class TestLib(TestCase): class TestLib(TestCase):
@ -15,7 +22,7 @@ class TestLib(TestCase):
""" """
Test the str_to_bool function with boolean input Test the str_to_bool function with boolean input
""" """
#GIVEN: A boolean value set to true # GIVEN: A boolean value set to true
true_boolean = True true_boolean = True
# WHEN: We "convert" it to a bool # WHEN: We "convert" it to a bool
@ -25,7 +32,7 @@ class TestLib(TestCase):
assert isinstance(true_result, bool), u'The result should be a boolean' assert isinstance(true_result, bool), u'The result should be a boolean'
assert true_result is True, u'The result should be True' assert true_result is True, u'The result should be True'
#GIVEN: A boolean value set to false # GIVEN: A boolean value set to false
false_boolean = False false_boolean = False
# WHEN: We "convert" it to a bool # WHEN: We "convert" it to a bool
@ -125,7 +132,7 @@ class TestLib(TestCase):
Test the check_directory_exists() function Test the check_directory_exists() function
""" """
with patch(u'openlp.core.lib.os.path.exists') as mocked_exists, \ with patch(u'openlp.core.lib.os.path.exists') as mocked_exists, \
patch(u'openlp.core.lib.os.makedirs') as mocked_makedirs: patch(u'openlp.core.lib.os.makedirs') as mocked_makedirs:
# GIVEN: A directory to check and a mocked out os.makedirs and os.path.exists # GIVEN: A directory to check and a mocked out os.makedirs and os.path.exists
directory_to_check = u'existing/directory' directory_to_check = u'existing/directory'
@ -219,7 +226,7 @@ class TestLib(TestCase):
Test the build_icon() function with a resource URI Test the build_icon() function with a resource URI
""" """
with patch(u'openlp.core.lib.QtGui') as MockedQtGui, \ with patch(u'openlp.core.lib.QtGui') as MockedQtGui, \
patch(u'openlp.core.lib.QtGui.QPixmap') as MockedQPixmap: patch(u'openlp.core.lib.QtGui.QPixmap') as MockedQPixmap:
# GIVEN: A mocked QIcon and a mocked QPixmap # GIVEN: A mocked QIcon and a mocked QPixmap
MockedQtGui.QIcon = MagicMock MockedQtGui.QIcon = MagicMock
MockedQtGui.QIcon.Normal = 1 MockedQtGui.QIcon.Normal = 1
@ -261,9 +268,43 @@ class TestLib(TestCase):
mocked_byte_array.toBase64.assert_called_with() mocked_byte_array.toBase64.assert_called_with()
assert result == u'base64mock', u'The result should be the return value of the mocked out base64 method' assert result == u'base64mock', u'The result should be the return value of the mocked out base64 method'
def create_thumb_with_size_test(self):
"""
Test the create_thumb() function
"""
# GIVEN: An image to create a thumb of.
image_path = os.path.join(TEST_PATH, u'church.jpg')
thumb_path = os.path.join(TEST_PATH, u'church_thumb.jpg')
thumb_size = QtCore.QSize(10, 20)
# Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
# last test.
try:
os.remove(thumb_path)
except:
pass
# Only continue when the thumb does not exist.
assert not os.path.exists(thumb_path), u'Test was not ran, because the thumb already exists.'
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created.
assert os.path.exists(thumb_path), u'Test was not ran, because the thumb already exists.'
assert isinstance(icon, QtGui.QIcon), u'The icon should be a QIcon.'
assert not icon.isNull(), u'The icon should not be null.'
assert QtGui.QImageReader(thumb_path).size() == thumb_size, u'The thumb should have the given size.'
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
os.remove(thumb_path)
except:
pass
def check_item_selected_true_test(self): def check_item_selected_true_test(self):
""" """
Test that the check_item_selected() function returns True when there are selected indexes. Test that the check_item_selected() function returns True when there are selected indexes
""" """
# GIVEN: A mocked out QtGui module and a list widget with selected indexes # GIVEN: A mocked out QtGui module and a list widget with selected indexes
MockedQtGui = patch(u'openlp.core.lib.QtGui') MockedQtGui = patch(u'openlp.core.lib.QtGui')
@ -423,7 +464,7 @@ class TestLib(TestCase):
def create_separated_list_qlocate_test(self): def create_separated_list_qlocate_test(self):
""" """
Test the create_separated_list function using the Qt provided method. Test the create_separated_list function using the Qt provided method
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt, \ with patch(u'openlp.core.lib.Qt') as mocked_qt, \
patch(u'openlp.core.lib.QtCore.QLocale.createSeparatedList') as mocked_createSeparatedList: patch(u'openlp.core.lib.QtCore.QLocale.createSeparatedList') as mocked_createSeparatedList:
@ -442,7 +483,7 @@ class TestLib(TestCase):
def create_separated_list_empty_list_test(self): def create_separated_list_empty_list_test(self):
""" """
Test the create_separated_list function with an empty list. Test the create_separated_list function with an empty list
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt: with patch(u'openlp.core.lib.Qt') as mocked_qt:
# GIVEN: An empty list and the mocked Qt module. # GIVEN: An empty list and the mocked Qt module.
@ -458,7 +499,7 @@ class TestLib(TestCase):
def create_separated_list_with_one_item_test(self): def create_separated_list_with_one_item_test(self):
""" """
Test the create_separated_list function with a list consisting of only one entry. Test the create_separated_list function with a list consisting of only one entry
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt: with patch(u'openlp.core.lib.Qt') as mocked_qt:
# GIVEN: A list with a string and the mocked Qt module. # GIVEN: A list with a string and the mocked Qt module.
@ -474,7 +515,7 @@ class TestLib(TestCase):
def create_separated_list_with_two_items_test(self): def create_separated_list_with_two_items_test(self):
""" """
Test the create_separated_list function with a list of two entries. Test the create_separated_list function with a list of two entries
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate: with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate:
# GIVEN: A list of strings and the mocked Qt module. # GIVEN: A list of strings and the mocked Qt module.
@ -491,7 +532,7 @@ class TestLib(TestCase):
def create_separated_list_with_three_items_test(self): def create_separated_list_with_three_items_test(self):
""" """
Test the create_separated_list function with a list of three items. Test the create_separated_list function with a list of three items
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate: with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate:
# GIVEN: A list with a string and the mocked Qt module. # GIVEN: A list with a string and the mocked Qt module.

Some files were not shown because too many files have changed in this diff Show More