diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index 95a8122d6..23986ffc4 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -111,10 +111,10 @@ class OpenLP(QtGui.QApplication): # Decide how many screens we have and their size screens = ScreenList.create(self.desktop()) # 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 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 application_stylesheet = u'' if not Settings().value(u'advanced/alternate rows'): @@ -126,7 +126,7 @@ class OpenLP(QtGui.QApplication): application_stylesheet += NT_REPAIR_STYLESHEET if 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: self.splash = SplashScreen() self.splash.show() @@ -147,7 +147,7 @@ class OpenLP(QtGui.QApplication): self.processEvents() if not has_run_wizard: self.main_window.first_time() - update_check = Settings().value(u'general/update check') + update_check = Settings().value(u'core/update check') if update_check: VersionThread(self.main_window).start() self.main_window.is_display_blank() @@ -305,8 +305,10 @@ def main(args=None): # Instance check if application.is_already_running(): sys.exit() + # Remove/convert obsolete settings. + Settings().remove_obsolete_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 cancel then stop processing sys.exit() diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 155c10141..d6c338271 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -30,6 +30,7 @@ The :mod:`lib` module contains most of the components and libraries that make OpenLP work. """ +from __future__ import division from distutils.version import LooseVersion import logging import os @@ -155,7 +156,7 @@ def build_icon(icon): ``icon`` 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() 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``. ``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() reader = QtGui.QImageReader(image_path) 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)) else: reader.setScaledSize(size) @@ -260,8 +262,8 @@ def resize_image(image_path, width, height, background=u'#000000'): log.debug(u'resize_image - start') reader = QtGui.QImageReader(image_path) # The image's ratio. - image_ratio = float(reader.size().width()) / float(reader.size().height()) - resize_ratio = float(width) / float(height) + image_ratio = reader.size().width() / reader.size().height() + resize_ratio = width / height # Figure out the size we want to resize the image to (keep aspect ratio). if image_ratio == resize_ratio: 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) painter = QtGui.QPainter(new_image) 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 diff --git a/openlp/core/lib/dockwidget.py b/openlp/core/lib/dockwidget.py index 30182c901..15c116e0f 100644 --- a/openlp/core/lib/dockwidget.py +++ b/openlp/core/lib/dockwidget.py @@ -30,6 +30,7 @@ """ Provide additional functionality required by OpenLP from the inherited QDockWidget. """ +from __future__ import division import logging from PyQt4 import QtGui @@ -55,7 +56,7 @@ class OpenLPDockWidget(QtGui.QDockWidget): self.setWindowIcon(build_icon(icon)) # Sort out the minimum width. 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: self.setMinimumWidth(300) else: diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 9c8b04076..d4e22b0dd 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -26,7 +26,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - +from __future__ import division import logging from PyQt4 import QtWebKit @@ -276,7 +276,7 @@ def build_background_css(item, width): ``item`` Service Item containing theme and location information """ - width = int(width) / 2 + width = int(width) // 2 theme = item.themedata background = u'background-color: black' if theme: diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index f6c95aa9e..c3e1fa366 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -102,7 +102,9 @@ class MediaManagerItem(QtGui.QWidget): self.setupUi() self.retranslateUi() 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): """ @@ -424,7 +426,7 @@ class MediaManagerItem(QtGui.QWidget): """ 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 item a chance to focus any elements it wants to. @@ -481,6 +483,15 @@ class MediaManagerItem(QtGui.QWidget): else: 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): """ Make the currently selected item go live. @@ -523,6 +534,15 @@ class MediaManagerItem(QtGui.QWidget): for item in items: 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): """ Add this item to the current service. @@ -564,12 +584,15 @@ class MediaManagerItem(QtGui.QWidget): else: 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 plugins. + + ``item`` + The item to be processed and returned. """ - pass + return item def check_search_result(self): """ diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index dd9843930..b4f851b24 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -103,7 +103,7 @@ class Plugin(QtCore.QObject): ``add_export_menu_Item(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 dialog. @@ -252,7 +252,7 @@ class Plugin(QtCore.QObject): """ 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 for this plugin to the user. diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 8fc294ea6..db96e3fa7 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -153,7 +153,7 @@ class PluginManager(object): """ for plugin in self.plugins: 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): """ diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index c426e8871..5161246c0 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -26,7 +26,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - +from __future__ import division import logging from PyQt4 import QtGui, QtCore, QtWebKit @@ -327,7 +327,7 @@ class Renderer(object): screen_size = self.screens.current[u'size'] self.width = screen_size.width() 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)) # 90% is start of footer self.footer_start = int(self.height * 0.90) @@ -546,15 +546,15 @@ class Renderer(object): """ smallest_index = 0 highest_index = len(html_list) - 1 - index = int(highest_index / 2) + index = highest_index // 2 while True: 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. highest_index = index - index = int(index - (index - smallest_index) / 2) + index = index - (index - smallest_index) // 2 else: 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. if smallest_index == index or highest_index == index: index = smallest_index @@ -582,7 +582,7 @@ class Renderer(object): html_list[0] = html_tags + html_list[0] smallest_index = 0 highest_index = len(html_list) - 1 - index = int(highest_index / 2) + index = highest_index // 2 return previous_html, previous_raw def _text_fits_on_slide(self, text): diff --git a/openlp/core/lib/screen.py b/openlp/core/lib/screen.py index 368035e17..146b492db 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/lib/screen.py @@ -30,6 +30,7 @@ The :mod:`screen` module provides management functionality for a machines' displays. """ +from __future__ import division import logging import copy @@ -232,8 +233,8 @@ class ScreenList(object): ``window`` A QWidget we are finding the location of. """ - x = window.x() + (window.width() / 2) - y = window.y() + (window.height() / 2) + x = window.x() + (window.width() // 2) + y = window.y() + (window.height() // 2) for screen in self.screen_list: size = screen[u'size'] 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. # Do not do this anywhere else. screen_settings = { - u'general/x position': self.current[u'size'].x(), - u'general/y position': self.current[u'size'].y(), - u'general/monitor': self.display_count - 1, - u'general/height': self.current[u'size'].height(), - u'general/width': self.current[u'size'].width() + u'core/x position': self.current[u'size'].x(), + u'core/y position': self.current[u'size'].y(), + u'core/monitor': self.display_count - 1, + u'core/height': self.current[u'size'].height(), + u'core/width': self.current[u'size'].width() } Settings.extend_default_settings(screen_settings) settings = Settings() - settings.beginGroup(u'general') + settings.beginGroup(u'core') monitor = settings.value(u'monitor') self.set_current_display(monitor) self.display = settings.value(u'display on monitor') diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index 9623b0f53..a0c51cb74 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -26,7 +26,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - +from __future__ import division import logging from PyQt4 import QtCore, QtGui @@ -85,10 +85,10 @@ class SearchEdit(QtGui.QLineEdit): size = self.clear_button.size() frame_width = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth) 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'): 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): """ diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index f57243818..b32e1aaf0 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -58,23 +58,19 @@ class ItemCapabilities(object): Provides an enumeration of a service item's capabilities ``CanPreview`` - The capability to allow the ServiceManager to add to the preview - tab when making the previous item live. + The capability to allow the ServiceManager to add to the preview tab when making the previous item live. ``CanEdit`` - The capability to allow the ServiceManager to allow the item to be - edited + The capability to allow the ServiceManager to allow the item to be edited ``CanMaintain`` - The capability to allow the ServiceManager to allow the item to be - reordered. + The capability to allow the ServiceManager to allow the item to be reordered. ``RequiresMedia`` Determines is the service_item needs a Media Player ``CanLoop`` - The capability to allow the SlideController to allow the loop - processing. + The capability to allow the SlideController to allow the loop processing. ``CanAppend`` 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 ``OnLoadUpdate`` - The capability to update MediaManager when a service Item is - loaded. + The capability to update MediaManager when a service Item is loaded. ``AddIfNewItem`` Not Used ``ProvidesOwnDisplay`` - The capability to tell the SlideController the service Item has a - different display. + The capability to tell the SlideController the service Item has a different display. ``HasDetailedTitleDisplay`` - ServiceItem provides a title + Being Removed and decommissioned. ``HasVariableStartTime`` - The capability to tell the ServiceManager that a change to start - time is possible. + The capability to tell the ServiceManager that a change to start time is possible. ``CanSoftBreak`` The capability to tell the renderer that Soft Break is allowed @@ -151,7 +144,7 @@ class ServiceItem(object): if plugin: self.name = plugin.name self.title = u'' - self.shortname = u'' + self.processor = None self.audit = u'' self.items = [] self.iconic_representation = None @@ -355,7 +348,8 @@ class ServiceItem(object): u'media_length': self.media_length, u'background_audio': self.background_audio, 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 = [] if self.service_item_type == ServiceItemType.Text: @@ -389,7 +383,6 @@ class ServiceItem(object): self.title = header[u'title'] self.name = header[u'name'] self.service_item_type = header[u'type'] - self.shortname = header[u'plugin'] self.theme = header[u'theme'] self.add_icon(header[u'icon']) 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.timed_slide_interval = header.get(u'timed_slide_interval', 0) self.will_auto_start = header.get(u'will_auto_start', False) + self.processor = header.get(u'processor', None) 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: self.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) elif self.service_item_type == ServiceItemType.Command: for text_image in serviceitem[u'serviceitem'][u'data']: + if not self.title: + self.title = text_image[u'title'] if path: self.has_original_files = False self.add_from_command(path, text_image[u'title'], text_image[u'image']) @@ -445,9 +446,7 @@ class ServiceItem(object): if self.is_text(): return self.title else: - if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities: - return self._raw_frames[0][u'title'] - elif len(self._raw_frames) > 1: + if len(self._raw_frames) > 1: return self.title else: return self._raw_frames[0][u'title'] diff --git a/openlp/core/lib/settings.py b/openlp/core/lib/settings.py index 30a8b25d8..49cd8f6d5 100644 --- a/openlp/core/lib/settings.py +++ b/openlp/core/lib/settings.py @@ -116,30 +116,29 @@ class Settings(QtCore.QSettings): u'advanced/x11 bypass wm': X11_BYPASS_DEFAULT, u'crashreport/last directory': u'', u'displayTags/html_tags': u'', - u'general/audio repeat list': False, - u'general/auto open': False, - u'general/auto preview': False, - u'general/audio start paused': True, - u'general/auto unblank': False, - u'general/blank warning': False, - u'general/ccli number': u'', - u'general/has run wizard': False, - u'general/language': u'[en]', - # This defaults to yesterday in order to force the update check to run when you've never run it before. - u'general/last version test': datetime.datetime.now().date() - datetime.timedelta(days=1), - u'general/loop delay': 5, - u'general/recent files': [], - u'general/save prompt': False, - u'general/screen blank': False, - u'general/show splash': True, - u'general/songselect password': u'', - u'general/songselect username': u'', - u'general/update check': True, - u'general/view mode': u'default', + u'core/audio repeat list': False, + u'core/auto open': False, + u'core/auto preview': False, + u'core/audio start paused': True, + u'core/auto unblank': False, + u'core/blank warning': False, + u'core/ccli number': u'', + u'core/has run wizard': False, + u'core/language': u'[en]', + u'core/last version test': u'', + u'core/loop delay': 5, + u'core/recent files': [], + u'core/save prompt': False, + u'core/screen blank': False, + u'core/show splash': True, + u'core/songselect password': u'', + u'core/songselect username': u'', + u'core/update check': True, + u'core/view mode': u'default', # The other display settings (display position and dimensions) are defined in the ScreenList class due to a # circular dependency. - u'general/display on monitor': True, - u'general/override position': False, + u'core/display on monitor': True, + u'core/override position': False, u'images/background color': u'#000000', u'media/players': u'webkit', u'media/override player': QtCore.Qt.Unchecked, @@ -304,7 +303,7 @@ class Settings(QtCore.QSettings): # Changed during 1.9.x development. (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'songs/ccli number', u'general/ccli number', []), + (u'songs/ccli number', u'core/ccli number', []), (u'media/use phonon', u'', []), # Changed during 2.1.x development. (u'advanced/stylesheet fix', u'', []), @@ -315,7 +314,34 @@ class Settings(QtCore.QSettings): (u'songs/last directory 1', u'songs/last directory import', []), (u'songusage/last directory 1', u'songusage/last directory export', []), (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 diff --git a/openlp/core/lib/settingstab.py b/openlp/core/lib/settingstab.py index d63f9c678..ab775599a 100644 --- a/openlp/core/lib/settingstab.py +++ b/openlp/core/lib/settingstab.py @@ -30,6 +30,7 @@ The :mod:`~openlp.core.lib.settingstab` module contains the base SettingsTab class which plugins use for adding their own tab to the settings dialog. """ +from __future__ import division from PyQt4 import QtGui @@ -90,7 +91,7 @@ class SettingsTab(QtGui.QWidget): QtGui.QWidget.resizeEvent(self, event) width = self.width() - self.tab_layout.spacing() - \ 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()) self.left_column.setFixedWidth(left_width) diff --git a/openlp/core/lib/uistrings.py b/openlp/core/lib/uistrings.py index a5a45961a..6d4b4d250 100644 --- a/openlp/core/lib/uistrings.py +++ b/openlp/core/lib/uistrings.py @@ -107,7 +107,6 @@ class UiStrings(object): self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural') self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular') 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.OLPV2x = translate('OpenLP.Ui', 'OpenLP 2.1') self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?') diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 6b77a8c6f..af1744424 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -35,7 +35,7 @@ import os import platform import sqlalchemy -import BeautifulSoup +from bs4 import BeautifulSoup from lxml import etree from PyQt4 import Qt, QtCore, QtGui, QtWebKit @@ -59,11 +59,6 @@ try: ENCHANT_VERSION = enchant.__version__ except ImportError: ENCHANT_VERSION = u'-' -try: - import sqlite - SQLITE_VERSION = sqlite.version -except ImportError: - SQLITE_VERSION = u'-' try: import mako MAKO_VERSION = mako.__version__ @@ -72,11 +67,16 @@ except ImportError: try: import icu try: - ICU_VERSION = icu.VERSION + ICU_VERSION = icu.VERSION except AttributeError: - ICU_VERSION = u'OK' + ICU_VERSION = u'OK' except ImportError: ICU_VERSION = u'-' +try: + import cherrypy + CHERRYPY_VERSION = cherrypy.__version__ +except ImportError: + CHERRYPY_VERSION = u'-' try: import uno 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'Chardet: %s\n' % CHARDET_VERSION + \ u'PyEnchant: %s\n' % ENCHANT_VERSION + \ - u'PySQLite: %s\n' % SQLITE_VERSION + \ u'Mako: %s\n' % MAKO_VERSION + \ + u'CherryPy: %s\n' % CHERRYPY_VERSION + \ u'pyICU: %s\n' % ICU_VERSION + \ u'pyUNO bridge: %s\n' % UNO_VERSION + \ u'VLC: %s\n' % VLC_VERSION diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index e828db597..0f3f3cc18 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -118,7 +118,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): check_directory_exists(os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp')) self.noInternetFinishButton.setVisible(False) # 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 if self.web_access: songs = self.config.get(u'songs', u'languages') @@ -252,7 +252,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): self.application.set_busy_cursor() self._performWizard() self.application.set_normal_cursor() - Settings().setValue(u'general/has run wizard', True) + Settings().setValue(u'core/has run wizard', True) self.close() 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)) # Set Default Display 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()) # Set Global Theme if self.themeComboBox.currentIndex() != -1: diff --git a/openlp/core/ui/firsttimelanguagedialog.py b/openlp/core/ui/firsttimelanguagedialog.py index 21f4dabcd..55fce4399 100644 --- a/openlp/core/ui/firsttimelanguagedialog.py +++ b/openlp/core/ui/firsttimelanguagedialog.py @@ -39,39 +39,39 @@ class Ui_FirstTimeLanguageDialog(object): """ The UI widgets of the language selection dialog. """ - def setupUi(self, languageDialog): + def setupUi(self, language_dialog): """ Set up the UI. """ - languageDialog.setObjectName(u'languageDialog') - languageDialog.resize(300, 50) - self.dialogLayout = QtGui.QVBoxLayout(languageDialog) - self.dialogLayout.setContentsMargins(8, 8, 8, 8) - self.dialogLayout.setSpacing(8) - self.dialogLayout.setObjectName(u'dialog_layout') - self.infoLabel = QtGui.QLabel(languageDialog) - self.infoLabel.setObjectName(u'infoLabel') - self.dialogLayout.addWidget(self.infoLabel) - self.languageLayout = QtGui.QHBoxLayout() - self.languageLayout.setObjectName(u'languageLayout') - self.languageLabel = QtGui.QLabel(languageDialog) - self.languageLabel.setObjectName(u'languageLabel') - self.languageLayout.addWidget(self.languageLabel) - self.languageComboBox = QtGui.QComboBox(languageDialog) - self.languageComboBox.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) - self.languageComboBox.setObjectName("languageComboBox") - self.languageLayout.addWidget(self.languageComboBox) - self.dialogLayout.addLayout(self.languageLayout) - self.button_box = create_button_box(languageDialog, u'button_box', [u'cancel', u'ok']) - self.dialogLayout.addWidget(self.button_box) - self.retranslateUi(languageDialog) + language_dialog.setObjectName(u'language_dialog') + language_dialog.resize(300, 50) + self.dialog_layout = QtGui.QVBoxLayout(language_dialog) + self.dialog_layout.setContentsMargins(8, 8, 8, 8) + self.dialog_layout.setSpacing(8) + self.dialog_layout.setObjectName(u'dialog_layout') + self.info_label = QtGui.QLabel(language_dialog) + self.info_label.setObjectName(u'info_label') + self.dialog_layout.addWidget(self.info_label) + self.language_layout = QtGui.QHBoxLayout() + self.language_layout.setObjectName(u'language_layout') + self.language_label = QtGui.QLabel(language_dialog) + self.language_label.setObjectName(u'language_label') + self.language_layout.addWidget(self.language_label) + self.language_combo_box = QtGui.QComboBox(language_dialog) + self.language_combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) + self.language_combo_box.setObjectName("language_combo_box") + self.language_layout.addWidget(self.language_combo_box) + self.dialog_layout.addLayout(self.language_layout) + self.button_box = create_button_box(language_dialog, u'button_box', [u'cancel', u'ok']) + self.dialog_layout.addWidget(self.button_box) + self.retranslateUi(language_dialog) self.setMaximumHeight(self.sizeHint().height()) - def retranslateUi(self, languageDialog): + def retranslateUi(self, language_dialog): """ Translate the UI on the fly. """ 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.')) - self.languageLabel.setText(translate('OpenLP.FirstTimeLanguageForm', 'Translation:')) + self.language_label.setText(translate('OpenLP.FirstTimeLanguageForm', 'Translation:')) diff --git a/openlp/core/ui/firsttimelanguageform.py b/openlp/core/ui/firsttimelanguageform.py index 85e98e114..14b9dadc3 100644 --- a/openlp/core/ui/firsttimelanguageform.py +++ b/openlp/core/ui/firsttimelanguageform.py @@ -47,8 +47,8 @@ class FirstTimeLanguageForm(QtGui.QDialog, Ui_FirstTimeLanguageDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) self.qmList = LanguageManager.get_qm_list() - self.languageComboBox.addItem(u'Autodetect') - self.languageComboBox.addItems(sorted(self.qmList.keys())) + self.language_combo_box.addItem(u'Autodetect') + self.language_combo_box.addItems(sorted(self.qmList.keys())) def exec_(self): """ @@ -61,12 +61,12 @@ class FirstTimeLanguageForm(QtGui.QDialog, Ui_FirstTimeLanguageDialog): Run when the dialog is OKed. """ # 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.set_language(False, False) else: 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) return QtGui.QDialog.accept(self) diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index 58752a4a9..49497a10e 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -49,7 +49,7 @@ class GeneralTab(SettingsTab): self.screens = ScreenList() self.icon_path = u':/icon/openlp-logo-16x16.png' 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): """ diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 9b4037875..02ae469d6 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -35,6 +35,7 @@ Some of the code for this form is based on the examples at: * `http://html5demos.com/two-videos`_ """ +from __future__ import division import cgi import logging import sys @@ -207,8 +208,8 @@ class MainDisplay(Display): painter_image.begin(self.initial_fame) painter_image.fillRect(self.initial_fame.rect(), background_color) painter_image.drawImage( - (self.screen[u'size'].width() - splash_image.width()) / 2, - (self.screen[u'size'].height() - splash_image.height()) / 2, + (self.screen[u'size'].width() - splash_image.width()) // 2, + (self.screen[u'size'].height() - splash_image.height()) // 2, splash_image) service_item = ServiceItem() 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.setVisible(True) 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: self.move(self.screen[u'size'].left(), self.screen[u'size'].height() - alert_height) else: @@ -357,7 +358,7 @@ class MainDisplay(Display): # Single screen active if self.screens.display_count == 1: # 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) else: self.setVisible(True) @@ -405,7 +406,7 @@ class MainDisplay(Display): self.footer(service_item.foot_text) # if was hidden keep it hidden 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') else: self.hide_display(self.hide_mode) @@ -427,7 +428,7 @@ class MainDisplay(Display): log.debug(u'hide_display mode = %d', mode) if self.screens.display_count == 1: # 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 if mode == HideMode.Screen: self.frame.evaluateJavaScript(u'show_blank("desktop");') @@ -450,7 +451,7 @@ class MainDisplay(Display): log.debug(u'show_display') if self.screens.display_count == 1: # 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 self.frame.evaluateJavaScript('show_blank("show");') if self.isHidden(): diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 69439c1b1..2afbb4eb0 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -476,7 +476,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.arguments = self.application.args # Set up settings sections for the main application (not for use by plugins). 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.shortcuts_settings_section = u'shortcuts' self.service_manager_settings_section = u'servicemanager' @@ -491,7 +491,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.new_data_path = None self.copy_data = False Settings().set_up_default_values() - Settings().remove_obsolete_settings() self.service_not_saved = False self.about_form = AboutForm(self) self.media_controller = MediaController() @@ -560,7 +559,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): """ widget = self.media_tool_box.widget(index) if widget: - widget.onFocus() + widget.on_focus() def version_notice(self, version): """ diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 51a8f3517..71f2b4b10 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -415,7 +415,7 @@ class MediaController(object): elif not hidden or controller.media_info.is_background or service_item.will_auto_start: autoplay = True # Unblank on load set - elif Settings().value(u'general/auto unblank'): + elif Settings().value(u'core/auto unblank'): autoplay = True if autoplay: if not self.media_play(controller): @@ -466,8 +466,8 @@ class MediaController(object): The ServiceItem containing the details to be played. """ used_players = get_media_players()[0] - if service_item.title != UiStrings().Automatic: - used_players = [service_item.title.lower()] + if service_item.processor != UiStrings().Automatic: + used_players = [service_item.processor.lower()] if controller.media_info.file_info.isFile(): suffix = u'*.%s' % controller.media_info.file_info.suffix().lower() for title in used_players: diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 6e0f4ad95..8ddd4135e 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -273,7 +273,6 @@ class ServiceManagerDialog(object): 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'mediaitem_suffix_reset', self.reset_supported_suffixes) - Registry().register_function(u'servicemanager_set_item', self.on_set_item) 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. """ QtGui.QWidget.__init__(self, parent) - self.active = build_icon(QtGui.QImage(u':/media/auto-start_active.png')) - self.inactive = build_icon(QtGui.QImage(u':/media/auto-start_inactive.png')) + self.active = build_icon(u':/media/auto-start_active.png') + self.inactive = build_icon(u':/media/auto-start_inactive.png') Registry().register(u'service_manager', self) self.service_items = [] self.suffixes = [] @@ -315,6 +314,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): self.layout.setSpacing(0) self.layout.setMargin(0) 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): """ @@ -714,13 +715,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): else: service_item.set_from_service(item, self.servicePath) service_item.validate_item(self.suffixes) - self.load_item_unique_identifier = 0 if service_item.is_capable(ItemCapabilities.OnLoadUpdate): - Registry().execute(u'%s_service_load' % service_item.name.lower(), service_item) - # if the item has been processed - if service_item.unique_identifier == self.load_item_unique_identifier: - service_item.edit_id = int(self.load_item_edit_id) - service_item.temporary_edit = self.load_item_temporary + new_item = Registry().get(service_item.name).service_load(service_item) + if new_item: + service_item = new_item self.add_service_item(service_item, repaint=False) delete_file(p_file) self.main_window.add_recent_file(file_name) @@ -993,7 +991,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): 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)) @@ -1259,14 +1257,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): self.repaint_service_list(-1, -1) 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): """ 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] if self.service_items[item][u'service_item'].is_capable(ItemCapabilities.CanEdit): 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: self.add_service_item(new_item, replace=True) diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index eeb85fa66..bc40539cf 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -96,6 +96,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): """ Process the form saving the settings """ + log.debug(u'Processing settings exit') for tabIndex in range(self.stacked_layout.count()): self.stacked_layout.widget(tabIndex).save() # if the display of image background are changing we need to regenerate the image cache diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index eaeebfba8..86b114e1e 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -360,8 +360,9 @@ class SlideController(DisplayController): # Signals self.preview_list_widget.clicked.connect(self.onSlideSelected) 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_toggle_display', self.toggle_display) self.toolbar.set_widget_visible(self.loop_list, False) self.toolbar.set_widget_visible(self.wide_menu, False) else: @@ -373,13 +374,16 @@ class SlideController(DisplayController): else: 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_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_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_unblank' % self.type_prefix, self.on_slide_unblank) 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): """ @@ -612,7 +616,7 @@ class SlideController(DisplayController): """ 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): """ @@ -1229,7 +1233,7 @@ class SlideController(DisplayController): From the preview display requires the service Item to be editied """ 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: self.add_service_item(new_item) diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index f0f821494..d6dba2880 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -29,6 +29,8 @@ """ The Themes configuration tab """ +from __future__ import division + from PyQt4 import QtCore, QtGui 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.level_layout.addRow(self.global_level_radio_button, self.global_level_label) 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]: rect = label.rect() rect.setTop(rect.top() + label_top_margin) diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index f8b32a9c0..783b310b3 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -58,8 +58,6 @@ class WizardStrings(object): ImportingType = translate('OpenLP.Ui', 'Importing "%s"...') ImportSelect = translate('OpenLP.Ui', 'Select Import Source') 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') OpenTypeFolder = translate('OpenLP.Ui', 'Open %s Folder') PercentSymbolFormat = translate('OpenLP.Ui', '%p%') diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 5c4a4466c..0a594b5cf 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -177,9 +177,9 @@ def check_latest_version(current_version): version_string = current_version[u'full'] # set to prod in the distribution config file. settings = Settings() - settings.beginGroup(u'general') + settings.beginGroup(u'core') 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.endGroup() # Tell the main window whether there will ever be data to display @@ -239,8 +239,7 @@ def get_images_filter(): global IMAGES_FILTER if not IMAGES_FILTER: log.debug(u'Generating images filter.') - formats = [unicode(fmt) - for fmt in QtGui.QImageReader.supportedImageFormats()] + formats = map(unicode, QtGui.QImageReader.supportedImageFormats()) visible_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) diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index 00a0d0079..6dc18c1ad 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -98,7 +98,7 @@ class LanguageManager(object): """ Retrieve a saved language to use from settings """ - language = Settings().value(u'general/language') + language = Settings().value(u'core/language') language = str(language) log.info(u'Language file: \'%s\' Loaded from conf file' % language) if re.match(r'[[].*[]]', language): @@ -128,7 +128,7 @@ class LanguageManager(object): language = unicode(qm_list[action_name]) if LanguageManager.auto_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) if message: QtGui.QMessageBox.information(None, diff --git a/openlp/plugins/alerts/__init__.py b/openlp/plugins/alerts/__init__.py index 3e4a63444..bb4c2835d 100644 --- a/openlp/plugins/alerts/__init__.py +++ b/openlp/plugins/alerts/__init__.py @@ -27,6 +27,5 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`alerts` module provides the Alerts plugin for producing impromptu -on-screen announcements during a service. +The :mod:`alerts` module provides the Alerts plugin for producing impromptu on-screen announcements during a service. """ diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index 8d8530918..a2d77c42b 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -115,13 +115,13 @@ HTML = """ """ __default_settings__ = { - u'alerts/font face': QtGui.QFont().family(), - u'alerts/font size': 40, - u'alerts/db type': u'sqlite', - u'alerts/location': AlertLocation.Bottom, - u'alerts/background color': u'#660000', - u'alerts/font color': u'#ffffff', - u'alerts/timeout': 5 + u'alerts/font face': QtGui.QFont().family(), + u'alerts/font size': 40, + u'alerts/db type': u'sqlite', + u'alerts/location': AlertLocation.Bottom, + u'alerts/background color': u'#660000', + u'alerts/font color': u'#ffffff', + u'alerts/timeout': 5 } @@ -139,12 +139,10 @@ class AlertsPlugin(Plugin): def add_tools_menu_item(self, tools_menu): """ - Give the alerts plugin the opportunity to add items to the - **Tools** menu. + Give the alerts plugin the opportunity to add items to the **Tools** menu. ``tools_menu`` - The actual **Tools** menu item, so that your actions can - use it as their parent. + The actual **Tools** menu item, so that your actions can use it as their parent. """ log.info(u'add tools menu') self.tools_alert_item = create_action(tools_menu, u'toolsAlertItem', diff --git a/openlp/plugins/alerts/forms/__init__.py b/openlp/plugins/alerts/forms/__init__.py index e97fdfed3..2a3037f97 100644 --- a/openlp/plugins/alerts/forms/__init__.py +++ b/openlp/plugins/alerts/forms/__init__.py @@ -27,20 +27,16 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -Forms in OpenLP are made up of two classes. One class holds all the graphical -elements, like buttons and lists, and the other class holds all the functional -code, like slots and loading and saving. +Forms in OpenLP are made up of two classes. One class holds all the graphical elements, like buttons and lists, and the +other class holds all the functional code, like slots and loading and saving. -The first class, commonly known as the **Dialog** class, is typically named -``Ui_Dialog``. It is a slightly modified version of the class that the -``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. +The first class, commonly known as the **Dialog** class, is typically named ``Ui_Dialog``. It is a slightly +modified version of the class that the ``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. -The second class, commonly known as the **Form** class, is typically named -``Form``. This class is the one which is instantiated and used. It uses -dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class -mentioned above, like so:: +The second class, commonly known as the **Form** class, is typically named ``Form``. This class is the one which +is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned +above, like so:: class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog): @@ -48,9 +44,8 @@ mentioned above, like so:: QtGui.QDialog.__init__(self, parent) self.setupUi(self) -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 -from the .ui files later if necessary. +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 from the .ui files later if necessary. """ from alertform import AlertForm diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py index 1985fdcd1..db2579298 100644 --- a/openlp/plugins/alerts/forms/alertdialog.py +++ b/openlp/plugins/alerts/forms/alertdialog.py @@ -71,13 +71,13 @@ class Ui_AlertDialog(object): self.save_button.setObjectName(u'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, - click=alert_dialog.onDeleteButtonClicked) + click=alert_dialog.on_delete_button_clicked) self.manage_button_layout.addWidget(self.delete_button) self.manage_button_layout.addStretch() self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1) - displayIcon = build_icon(u':/general/general_live.png') - self.display_button = create_button(alert_dialog, u'display_button', icon=displayIcon, enabled=False) - self.display_close_button = create_button(alert_dialog, u'display_close_button', icon=displayIcon, + display_icon = build_icon(u':/general/general_live.png') + 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=display_icon, enabled=False) self.button_box = create_button_box(alert_dialog, u'button_box', [u'close'], [self.display_button, self.display_close_button]) diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index 64aca1e26..d1f5361d0 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -93,7 +93,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): if self.trigger_alert(self.alert_text_edit.text()): self.close() - def onDeleteButtonClicked(self): + def on_delete_button_clicked(self): """ Deletes the selected item. """ @@ -160,8 +160,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): def on_single_click(self): """ - List item has been single clicked to add it to the edit field so it can - be changed. + List item has been single clicked to add it to the edit field so it can be changed. """ item = self.alert_list_widget.selectedIndexes()[0] 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', 'You have not entered a parameter to be replaced.\n' '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() return False # 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', 'The alert text does not contain \'<>\'.\n' '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() return False 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): """ - Called when the *alert_list_widget*'s current row has been changed. This - enables or disables buttons which require an item to act on. + Called when the *alert_list_widget*'s current row has been changed. This enables or disables buttons which + require an item to act on. ``row`` The row (int). If there is no current row, the value is -1. diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index 042999a11..a319f5c74 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -27,8 +27,8 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of -the plugin which manages storing and displaying of alerts. +The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of the plugin which manages storing and +displaying of alerts. """ import logging @@ -49,22 +49,23 @@ class AlertsManager(QtCore.QObject): def __init__(self, parent): QtCore.QObject.__init__(self, parent) + Registry().register(u'alerts_manager', self) self.timer_id = 0 self.alert_list = [] Registry().register_function(u'live_display_active', self.generate_alert) 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): """ - Called via a alerts_text event. Message is single element array - containing text + Called via a alerts_text event. Message is single element array containing text. """ if message: self.display_alert(message[0]) 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`` display text @@ -81,7 +82,7 @@ class AlertsManager(QtCore.QObject): 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') if not self.alert_list: @@ -95,8 +96,7 @@ class AlertsManager(QtCore.QObject): def timerEvent(self, event): """ - Time has finished so if our time then request the next Alert - if there is one and reset the timer. + Time has finished so if our time then request the next Alert if there is one and reset the timer. ``event`` the QT event that has been triggered. diff --git a/openlp/plugins/alerts/lib/db.py b/openlp/plugins/alerts/lib/db.py index 58ee47076..499e63906 100644 --- a/openlp/plugins/alerts/lib/db.py +++ b/openlp/plugins/alerts/lib/db.py @@ -27,8 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`db` module provides the database and schema that is the backend for -the Alerts plugin +The :mod:`db` module provides the database and schema that is the backend for the Alerts plugin. """ from sqlalchemy import Column, Table, types @@ -36,12 +35,14 @@ from sqlalchemy.orm import mapper from openlp.core.lib.db import BaseModel, init_db + class AlertItem(BaseModel): """ AlertItem model """ pass + def init_schema(url): """ Setup the alerts database connection and initialise the database schema diff --git a/openlp/plugins/bibles/__init__.py b/openlp/plugins/bibles/__init__.py index 1c1510e97..62e6c5f90 100644 --- a/openlp/plugins/bibles/__init__.py +++ b/openlp/plugins/bibles/__init__.py @@ -27,6 +27,5 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`bibles` module provides the Bible plugin to enable OpenLP to display -scripture. +The :mod:`bibles` module provides the Bible plugin to enable OpenLP to display scripture. """ diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 1f17ae39a..e2888d67a 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -43,25 +43,25 @@ log = logging.getLogger(__name__) __default_settings__ = { - u'bibles/db type': u'sqlite', - u'bibles/last search type': BibleSearch.Reference, - u'bibles/verse layout style': LayoutStyle.VersePerSlide, - u'bibles/book name language': LanguageSelection.Bible, - u'bibles/display brackets': DisplayStyle.NoBrackets, - u'bibles/display new chapter': False, - u'bibles/second bibles': True, - u'bibles/advanced bible': u'', - u'bibles/quick bible': u'', - u'bibles/proxy name': u'', - u'bibles/proxy address': u'', - u'bibles/proxy username': u'', - u'bibles/proxy password': u'', - u'bibles/bible theme': u'', - u'bibles/verse separator': u'', - u'bibles/range separator': u'', - u'bibles/list separator': u'', - u'bibles/end separator': u'', - u'bibles/last directory import': u'' + u'bibles/db type': u'sqlite', + u'bibles/last search type': BibleSearch.Reference, + u'bibles/verse layout style': LayoutStyle.VersePerSlide, + u'bibles/book name language': LanguageSelection.Bible, + u'bibles/display brackets': DisplayStyle.NoBrackets, + u'bibles/display new chapter': False, + u'bibles/second bibles': True, + u'bibles/advanced bible': u'', + u'bibles/quick bible': u'', + u'bibles/proxy name': u'', + u'bibles/proxy address': u'', + u'bibles/proxy username': u'', + u'bibles/proxy password': u'', + u'bibles/bible theme': u'', + u'bibles/verse separator': u'', + u'bibles/range separator': u'', + u'bibles/list separator': u'', + u'bibles/end separator': u'', + u'bibles/last directory import': u'' } @@ -124,18 +124,15 @@ class BiblePlugin(Plugin): def add_export_menu_Item(self, export_menu): self.export_bible_item = create_action(export_menu, u'exportBibleItem', - text=translate('BiblesPlugin', '&Bible'), - visible=False) + text=translate('BiblesPlugin', '&Bible'), visible=False) export_menu.addAction(self.export_bible_item) def add_tools_menu_item(self, tools_menu): """ - Give the bible plugin the opportunity to add items to the - **Tools** menu. + Give the bible plugin the opportunity to add items to the **Tools** menu. ``tools_menu`` - The actual **Tools** menu item, so that your actions can - use it as their parent. + The actual **Tools** menu item, so that your actions can use it as their parent. """ log.debug(u'add tools menu') self.tools_upgrade_item = create_action(tools_menu, u'toolsUpgradeItem', @@ -166,25 +163,23 @@ class BiblePlugin(Plugin): def uses_theme(self, theme): """ - Called to find out if the bible plugin is currently using a theme. - Returns ``True`` if the theme is being used, otherwise returns - ``False``. + Called to find out if the bible plugin is currently using a theme. Returns ``True`` if the theme is being used, + otherwise returns ``False``. """ 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 new name. - ``oldTheme`` - The name of the theme the plugin should stop using. Unused for - this particular plugin. + ``old_theme`` + The name of the theme the plugin should stop using. Unused for this particular plugin. - ``newTheme`` + ``new_theme`` 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() def set_plugin_text_strings(self): diff --git a/openlp/plugins/bibles/forms/__init__.py b/openlp/plugins/bibles/forms/__init__.py index ee0922695..abef5c85c 100644 --- a/openlp/plugins/bibles/forms/__init__.py +++ b/openlp/plugins/bibles/forms/__init__.py @@ -28,30 +28,25 @@ ############################################################################### """ -Forms in OpenLP are made up of two classes. One class holds all the graphical -elements, like buttons and lists, and the other class holds all the functional -code, like slots and loading and saving. +Forms in OpenLP are made up of two classes. One class holds all the graphical elements, like buttons and lists, and the +other class holds all the functional code, like slots and loading and saving. -The first class, commonly known as the **Dialog** class, is typically named -``Ui_Dialog``. It is a slightly modified version of the class that the -``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. +The first class, commonly known as the **Dialog** class, is typically named ``Ui_Dialog``. It is a slightly +modified version of the class that the ``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. -The second class, commonly known as the **Form** class, is typically named -``Form``. This class is the one which is instantiated and used. It uses -dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class -mentioned above, like so:: +The second class, commonly known as the **Form** class, is typically named ``Form``. This class is the one which +is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned +above, like so:: class BibleImportForm(QtGui.QWizard, Ui_BibleImportWizard): - def __init__(self, parent, manager, bibleplugin): + def __init__(self, parent, manager, bible_plugin): QtGui.QWizard.__init__(self, parent) self.setupUi(self) -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 -from the .ui files later if necessary. +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 from the .ui files later if necessary. """ from booknameform import BookNameForm from languageform import LanguageForm @@ -59,5 +54,4 @@ from bibleimportform import BibleImportForm from bibleupgradeform import BibleUpgradeForm from editbibleform import EditBibleForm -__all__ = [u'BookNameForm', u'LanguageForm', u'BibleImportForm', - u'BibleUpgradeForm', u'EditBibleForm'] +__all__ = [u'BookNameForm', u'LanguageForm', u'BibleImportForm', u'BibleUpgradeForm', u'EditBibleForm'] diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index f8d771e77..0662f78db 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -58,12 +58,12 @@ class WebDownload(object): class BibleImportForm(OpenLPWizard): """ - This is the Bible Import Wizard, which allows easy importing of Bibles - into OpenLP from other formats like OSIS, CSV and OpenSong. + This is the Bible Import Wizard, which allows easy importing of Bibles into OpenLP from other formats like OSIS, + CSV and OpenSong. """ 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. @@ -73,12 +73,12 @@ class BibleImportForm(OpenLPWizard): ``manager`` The Bible manager. - ``bibleplugin`` + ``bible_plugin`` The Bible plugin. """ self.manager = manager 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): """ @@ -94,19 +94,11 @@ class BibleImportForm(OpenLPWizard): button. """ self.selectStack.setCurrentIndex(index) - next_button = self.button(QtGui.QWizard.NextButton) - next_button.setEnabled(BibleFormat.get_availability(index)) def custom_init(self): """ 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.loadWebBibles() self.restart() @@ -121,7 +113,6 @@ class BibleImportForm(OpenLPWizard): self.csvBooksButton.clicked.connect(self.onCsvBooksBrowseButtonClicked) self.csvVersesButton.clicked.connect(self.onCsvVersesBrowseButtonClicked) self.openSongBrowseButton.clicked.connect(self.onOpenSongBrowseButtonClicked) - self.openlp1BrowseButton.clicked.connect(self.onOpenlp1BrowseButtonClicked) def add_custom_pages(self): """ @@ -137,7 +128,7 @@ class BibleImportForm(OpenLPWizard): self.formatLabel = QtGui.QLabel(self.selectPage) self.formatLabel.setObjectName(u'FormatLabel') 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.formatLayout.addRow(self.formatLabel, self.formatComboBox) 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.webTabWidget.addTab(self.webProxyTab, u'') 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.addPage(self.selectPage) # License Page @@ -330,8 +298,6 @@ class BibleImportForm(OpenLPWizard): self.formatComboBox.setItemText(BibleFormat.OpenSong, WizardStrings.OS) self.formatComboBox.setItemText(BibleFormat.WebDownload, 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.csvBooksLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Books file:')) self.csvVersesLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Verses file:')) @@ -364,14 +330,12 @@ class BibleImportForm(OpenLPWizard): 'Please wait while your Bible is imported.')) self.progress_label.setText(WizardStrings.Ready) self.progress_bar.setFormat(u'%p%') - self.openlp1DisabledLabel.setText(WizardStrings.NoSqlite) # Align all QFormLayouts towards each other. labelWidth = max(self.formatLabel.minimumSizeHint().width(), self.osisFileLabel.minimumSizeHint().width(), self.csvBooksLabel.minimumSizeHint().width(), self.csvVersesLabel.minimumSizeHint().width(), - self.openSongFileLabel.minimumSizeHint().width(), - self.openlp1FileLabel.minimumSizeHint().width()) + self.openSongFileLabel.minimumSizeHint().width()) self.spacer.changeSize(labelWidth, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) def validateCurrentPage(self): @@ -406,11 +370,6 @@ class BibleImportForm(OpenLPWizard): elif self.field(u'source_format') == BibleFormat.WebDownload: self.versionNameEdit.setText(self.webTranslationComboBox.currentText()) 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 elif self.currentPage() == self.licenseDetailsPage: 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') - 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): """ 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_username', self.webUserEdit) 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_copyright', self.copyrightEdit) 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_username', settings.value(u'proxy username')) 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_copyright', self.copyrightEdit.text()) self.setField(u'license_permissions', self.permissionsEdit.text()) @@ -561,7 +511,7 @@ class BibleImportForm(OpenLPWizard): name = bible[u'abbreviation'] self.web_bible_list[download_type][version] = name.strip() - def preWizard(self): + def pre_wizard(self): """ Prepare the UI for the import. """ @@ -615,12 +565,6 @@ class BibleImportForm(OpenLPWizard): proxy_username=self.field(u'proxy_username'), 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): self.manager.save_meta_data(license_version, license_version, license_copyright, license_permissions) diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index 0127d19be..caba4b30f 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -48,8 +48,8 @@ log = logging.getLogger(__name__) class BibleUpgradeForm(OpenLPWizard): """ - This is the Bible Upgrade Wizard, which allows easy importing of Bibles - into OpenLP from older OpenLP2 database versions. + This is the Bible Upgrade Wizard, which allows easy importing of Bibles into OpenLP from older OpenLP2 database + versions. """ log.info(u'BibleUpgradeForm loaded') @@ -63,7 +63,7 @@ class BibleUpgradeForm(OpenLPWizard): ``manager`` The Bible manager. - ``bibleplugin`` + ``bible_plugin`` The Bible plugin. """ self.manager = manager @@ -74,7 +74,7 @@ class BibleUpgradeForm(OpenLPWizard): self.temp_dir = os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp') self.files = self.manager.old_bible_databases self.success = {} - self.newbibles = {} + self.new_bibles = {} OpenLPWizard.__init__(self, parent, bible_plugin, u'bibleUpgradeWizard', u':/wizards/wizard_importbible.bmp') def setupUi(self, image): @@ -105,7 +105,7 @@ class BibleUpgradeForm(OpenLPWizard): Perform necessary functions depending on which wizard page is active. """ if self.page(pageId) == self.progress_page: - self.preWizard() + self.pre_wizard() self.performWizard() self.post_wizard() elif self.page(pageId) == self.selectPage and not self.files: @@ -159,41 +159,41 @@ class BibleUpgradeForm(OpenLPWizard): Add the bible import specific wizard pages. """ # Backup Page - self.backupPage = QtGui.QWizardPage() - self.backupPage.setObjectName(u'BackupPage') - self.backupLayout = QtGui.QVBoxLayout(self.backupPage) + self.backup_page = QtGui.QWizardPage() + self.backup_page.setObjectName(u'BackupPage') + self.backupLayout = QtGui.QVBoxLayout(self.backup_page) self.backupLayout.setObjectName(u'BackupLayout') - self.backupInfoLabel = QtGui.QLabel(self.backupPage) + self.backupInfoLabel = QtGui.QLabel(self.backup_page) self.backupInfoLabel.setOpenExternalLinks(True) self.backupInfoLabel.setTextFormat(QtCore.Qt.RichText) self.backupInfoLabel.setWordWrap(True) self.backupInfoLabel.setObjectName(u'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.backupLayout.addWidget(self.selectLabel) self.formLayout = QtGui.QFormLayout() self.formLayout.setMargin(0) self.formLayout.setObjectName(u'FormLayout') - self.backupDirectoryLabel = QtGui.QLabel(self.backupPage) + self.backupDirectoryLabel = QtGui.QLabel(self.backup_page) self.backupDirectoryLabel.setObjectName(u'backupDirectoryLabel') self.backupDirectoryLayout = QtGui.QHBoxLayout() self.backupDirectoryLayout.setObjectName(u'BackupDirectoryLayout') - self.backupDirectoryEdit = QtGui.QLineEdit(self.backupPage) + self.backupDirectoryEdit = QtGui.QLineEdit(self.backup_page) self.backupDirectoryEdit.setObjectName(u'BackupFolderEdit') 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.setObjectName(u'BackupBrowseButton') self.backupDirectoryLayout.addWidget(self.backupBrowseButton) self.formLayout.addRow(self.backupDirectoryLabel, self.backupDirectoryLayout) self.backupLayout.addLayout(self.formLayout) - self.noBackupCheckBox = QtGui.QCheckBox(self.backupPage) + self.noBackupCheckBox = QtGui.QCheckBox(self.backup_page) self.noBackupCheckBox.setObjectName('NoBackupCheckBox') self.backupLayout.addWidget(self.noBackupCheckBox) self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) self.backupLayout.addItem(self.spacer) - self.addPage(self.backupPage) + self.addPage(self.backup_page) # Select Page self.selectPage = QtGui.QWizardPage() self.selectPage.setObjectName(u'SelectPage') @@ -247,8 +247,8 @@ class BibleUpgradeForm(OpenLPWizard): self.information_label.setText(translate('BiblesPlugin.UpgradeWizardForm', '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.')) - self.backupPage.setTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Select Backup Directory')) - self.backupPage.setSubTitle(translate('BiblesPlugin.UpgradeWizardForm', + self.backup_page.setTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Select Backup Directory')) + self.backup_page.setSubTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Please select a backup directory for your Bibles')) self.backupInfoLabel.setText(translate('BiblesPlugin.UpgradeWizardForm', '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: return True - elif self.currentPage() == self.backupPage: + elif self.currentPage() == self.backup_page: if not self.noBackupCheckBox.checkState() == QtCore.Qt.Checked: backup_path = self.backupDirectoryEdit.text() if not backup_path: @@ -316,7 +316,7 @@ class BibleUpgradeForm(OpenLPWizard): settings.beginGroup(self.plugin.settings_section) self.stop_import_flag = False self.success.clear() - self.newbibles.clear() + self.new_bibles.clear() self.clearScrollArea() self.files = self.manager.old_bible_databases self.addScrollArea() @@ -329,7 +329,7 @@ class BibleUpgradeForm(OpenLPWizard): self.cancel_button.setVisible(True) settings.endGroup() - def preWizard(self): + def pre_wizard(self): """ Prepare the UI for the upgrade. """ @@ -372,8 +372,8 @@ class BibleUpgradeForm(OpenLPWizard): name = filename[1] self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm', '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.newbibles[number].register(self.plugin.upgrade_wizard) + self.new_bibles[number] = BibleDB(self.media_item, path=self.path, name=name, file=filename[0]) + self.new_bibles[number].register(self.plugin.upgrade_wizard) metadata = old_bible.get_metadata() web_bible = False meta_data = {} @@ -387,7 +387,7 @@ class BibleUpgradeForm(OpenLPWizard): # Copy the metadata meta_data[meta[u'key']] = meta[u'value'] 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': web_bible = True self.includeWebBible = True @@ -403,8 +403,8 @@ class BibleUpgradeForm(OpenLPWizard): if not books: log.error(u'Upgrading books from %s - download name: "%s" failed' % ( meta_data[u'download_source'], meta_data[u'download_name'])) - self.newbibles[number].session.close() - del self.newbibles[number] + self.new_bibles[number].session.close() + del self.new_bibles[number] critical_error_message_box( translate('BiblesPlugin.UpgradeWizardForm', 'Download Error'), translate('BiblesPlugin.UpgradeWizardForm', @@ -419,14 +419,14 @@ class BibleUpgradeForm(OpenLPWizard): meta_data[u'download_source'].lower()) if bible and 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) else: - language_id = self.newbibles[number].get_language(name) + language_id = self.new_bibles[number].get_language(name) if not language_id: log.warn(u'Upgrading from "%s" failed' % filename[0]) - self.newbibles[number].session.close() - del self.newbibles[number] + self.new_bibles[number].session.close() + del self.new_bibles[number] self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), self.progress_bar.maximum() - self.progress_bar.value()) @@ -439,17 +439,17 @@ class BibleUpgradeForm(OpenLPWizard): break self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', '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) if not book_ref_id: log.warn(u'Upgrading books from %s - download name: "%s" aborted by user' % ( meta_data[u'download_source'], meta_data[u'download_name'])) - self.newbibles[number].session.close() - del self.newbibles[number] + self.new_bibles[number].session.close() + del self.new_bibles[number] self.success[number] = False break 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']) # Try to import already downloaded verses. oldbook = old_bible.get_book(book) @@ -462,19 +462,19 @@ class BibleUpgradeForm(OpenLPWizard): if self.stop_import_flag: self.success[number] = False 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'verse']), unicode(verse[u'text'])) self.application.process_events() - self.newbibles[number].session.commit() + self.new_bibles[number].session.commit() 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: - language_id = self.newbibles[number].get_language(name) + language_id = self.new_bibles[number].get_language(name) if not language_id: log.warn(u'Upgrading books from "%s" failed' % name) - self.newbibles[number].session.close() - del self.newbibles[number] + self.new_bibles[number].session.close() + del self.new_bibles[number] self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), self.progress_bar.maximum() - self.progress_bar.value()) @@ -489,41 +489,41 @@ class BibleUpgradeForm(OpenLPWizard): self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % (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: log.warn(u'Upgrading books from %s " failed - aborted by user' % name) - self.newbibles[number].session.close() - del self.newbibles[number] + self.new_bibles[number].session.close() + del self.new_bibles[number] self.success[number] = False break 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']) verses = old_bible.get_verses(book[u'id']) if not verses: 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 for verse in verses: if self.stop_import_flag: self.success[number] = False 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'verse']), unicode(verse[u'text'])) self.application.process_events() - self.newbibles[number].session.commit() + self.new_bibles[number].session.commit() if not self.success.get(number, True): self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), self.progress_bar.maximum() - self.progress_bar.value()) else: 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', 'Upgrading Bible %s of %s: "%s"\nComplete') % (number + 1, max_bibles, name)) - if number in self.newbibles: - self.newbibles[number].session.close() + if number in self.new_bibles: + self.new_bibles[number].session.close() # Close the last bible's connection if possible. if old_bible is not None: old_bible.close_connection() diff --git a/openlp/plugins/bibles/forms/booknamedialog.py b/openlp/plugins/bibles/forms/booknamedialog.py index df86b4380..285e9cdcc 100644 --- a/openlp/plugins/bibles/forms/booknamedialog.py +++ b/openlp/plugins/bibles/forms/booknamedialog.py @@ -33,66 +33,66 @@ from openlp.core.lib import translate from openlp.core.lib.ui import create_button_box class Ui_BookNameDialog(object): - def setupUi(self, bookNameDialog): - bookNameDialog.setObjectName(u'bookNameDialog') - bookNameDialog.resize(400, 271) - self.bookNameLayout = QtGui.QVBoxLayout(bookNameDialog) - self.bookNameLayout.setSpacing(8) - self.bookNameLayout.setMargin(8) - self.bookNameLayout.setObjectName(u'bookNameLayout') - self.infoLabel = QtGui.QLabel(bookNameDialog) - self.infoLabel.setWordWrap(True) - self.infoLabel.setObjectName(u'infoLabel') - self.bookNameLayout.addWidget(self.infoLabel) - self.correspondingLayout = QtGui.QGridLayout() - self.correspondingLayout.setColumnStretch(1, 1) - self.correspondingLayout.setSpacing(8) - self.correspondingLayout.setObjectName(u'correspondingLayout') - self.currentLabel = QtGui.QLabel(bookNameDialog) + def setupUi(self, book_name_dialog): + book_name_dialog.setObjectName(u'book_name_dialog') + book_name_dialog.resize(400, 271) + self.book_name_layout = QtGui.QVBoxLayout(book_name_dialog) + self.book_name_layout.setSpacing(8) + self.book_name_layout.setMargin(8) + self.book_name_layout.setObjectName(u'book_name_layout') + self.info_label = QtGui.QLabel(book_name_dialog) + self.info_label.setWordWrap(True) + self.info_label.setObjectName(u'info_label') + self.book_name_layout.addWidget(self.info_label) + self.corresponding_layout = QtGui.QGridLayout() + self.corresponding_layout.setColumnStretch(1, 1) + self.corresponding_layout.setSpacing(8) + self.corresponding_layout.setObjectName(u'corresponding_layout') + self.currentLabel = QtGui.QLabel(book_name_dialog) self.currentLabel.setObjectName(u'currentLabel') - self.correspondingLayout.addWidget(self.currentLabel, 0, 0, 1, 1) - self.currentBookLabel = QtGui.QLabel(bookNameDialog) - self.currentBookLabel.setObjectName(u'currentBookLabel') - self.correspondingLayout.addWidget(self.currentBookLabel, 0, 1, 1, 1) - self.correspondingLabel = QtGui.QLabel(bookNameDialog) + self.corresponding_layout.addWidget(self.currentLabel, 0, 0, 1, 1) + self.current_book_label = QtGui.QLabel(book_name_dialog) + self.current_book_label.setObjectName(u'current_book_label') + self.corresponding_layout.addWidget(self.current_book_label, 0, 1, 1, 1) + self.correspondingLabel = QtGui.QLabel(book_name_dialog) self.correspondingLabel.setObjectName(u'correspondingLabel') - self.correspondingLayout.addWidget(self.correspondingLabel, 1, 0, 1, 1) - self.correspondingComboBox = QtGui.QComboBox(bookNameDialog) - self.correspondingComboBox.setObjectName(u'correspondingComboBox') - self.correspondingLayout.addWidget(self.correspondingComboBox, 1, 1, 1, 1) - self.bookNameLayout.addLayout(self.correspondingLayout) - self.optionsGroupBox = QtGui.QGroupBox(bookNameDialog) - self.optionsGroupBox.setObjectName(u'optionsGroupBox') - self.optionsLayout = QtGui.QVBoxLayout(self.optionsGroupBox) - self.optionsLayout.setSpacing(8) - self.optionsLayout.setMargin(8) - self.optionsLayout.setObjectName(u'optionsLayout') - self.oldTestamentCheckBox = QtGui.QCheckBox(self.optionsGroupBox) - self.oldTestamentCheckBox.setObjectName(u'oldTestamentCheckBox') - self.oldTestamentCheckBox.setCheckState(QtCore.Qt.Checked) - self.optionsLayout.addWidget(self.oldTestamentCheckBox) - self.newTestamentCheckBox = QtGui.QCheckBox(self.optionsGroupBox) - self.newTestamentCheckBox.setObjectName(u'newTestamentCheckBox') - self.newTestamentCheckBox.setCheckState(QtCore.Qt.Checked) - self.optionsLayout.addWidget(self.newTestamentCheckBox) - self.apocryphaCheckBox = QtGui.QCheckBox(self.optionsGroupBox) - self.apocryphaCheckBox.setObjectName(u'apocryphaCheckBox') - self.apocryphaCheckBox.setCheckState(QtCore.Qt.Checked) - self.optionsLayout.addWidget(self.apocryphaCheckBox) - self.bookNameLayout.addWidget(self.optionsGroupBox) - self.button_box = create_button_box(bookNameDialog, u'button_box', [u'cancel', u'ok']) - self.bookNameLayout.addWidget(self.button_box) + self.corresponding_layout.addWidget(self.correspondingLabel, 1, 0, 1, 1) + self.corresponding_combo_box = QtGui.QComboBox(book_name_dialog) + self.corresponding_combo_box.setObjectName(u'corresponding_combo_box') + self.corresponding_layout.addWidget(self.corresponding_combo_box, 1, 1, 1, 1) + self.book_name_layout.addLayout(self.corresponding_layout) + self.options_group_box = QtGui.QGroupBox(book_name_dialog) + self.options_group_box.setObjectName(u'options_group_box') + self.options_layout = QtGui.QVBoxLayout(self.options_group_box) + self.options_layout.setSpacing(8) + self.options_layout.setMargin(8) + self.options_layout.setObjectName(u'options_layout') + self.old_testament_check_box = QtGui.QCheckBox(self.options_group_box) + self.old_testament_check_box.setObjectName(u'old_testament_check_box') + self.old_testament_check_box.setCheckState(QtCore.Qt.Checked) + self.options_layout.addWidget(self.old_testament_check_box) + self.new_testament_check_box = QtGui.QCheckBox(self.options_group_box) + self.new_testament_check_box.setObjectName(u'new_testament_check_box') + self.new_testament_check_box.setCheckState(QtCore.Qt.Checked) + self.options_layout.addWidget(self.new_testament_check_box) + self.apocrypha_check_box = QtGui.QCheckBox(self.options_group_box) + self.apocrypha_check_box.setObjectName(u'apocrypha_check_box') + self.apocrypha_check_box.setCheckState(QtCore.Qt.Checked) + self.options_layout.addWidget(self.apocrypha_check_box) + self.book_name_layout.addWidget(self.options_group_box) + self.button_box = create_button_box(book_name_dialog, u'button_box', [u'cancel', u'ok']) + self.book_name_layout.addWidget(self.button_box) - self.retranslateUi(bookNameDialog) + self.retranslateUi(book_name_dialog) - def retranslateUi(self, bookNameDialog): - bookNameDialog.setWindowTitle(translate('BiblesPlugin.BookNameDialog', 'Select Book Name')) - self.infoLabel.setText(translate('BiblesPlugin.BookNameDialog', + def retranslateUi(self, book_name_dialog): + book_name_dialog.setWindowTitle(translate('BiblesPlugin.BookNameDialog', 'Select Book Name')) + self.info_label.setText(translate('BiblesPlugin.BookNameDialog', 'The following book name cannot be matched up internally. ' 'Please select the corresponding name from the list.')) self.currentLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Current name:')) self.correspondingLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Corresponding name:')) - self.optionsGroupBox.setTitle(translate('BiblesPlugin.BookNameDialog', 'Show Books From')) - self.oldTestamentCheckBox.setText(translate('BiblesPlugin.BookNameDialog', 'Old Testament')) - self.newTestamentCheckBox.setText(translate('BiblesPlugin.BookNameDialog', 'New Testament')) - self.apocryphaCheckBox.setText(translate('BiblesPlugin.BookNameDialog', 'Apocrypha')) + self.options_group_box.setTitle(translate('BiblesPlugin.BookNameDialog', 'Show Books From')) + self.old_testament_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'Old Testament')) + self.new_testament_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'New Testament')) + self.apocrypha_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'Apocrypha')) diff --git a/openlp/plugins/bibles/forms/booknameform.py b/openlp/plugins/bibles/forms/booknameform.py index c8abaa227..cc213cf96 100644 --- a/openlp/plugins/bibles/forms/booknameform.py +++ b/openlp/plugins/bibles/forms/booknameform.py @@ -66,61 +66,61 @@ class BookNameForm(QDialog, Ui_BookNameDialog): """ Set up the signals used in the booknameform. """ - self.oldTestamentCheckBox.stateChanged.connect(self.onCheckBoxIndexChanged) - self.newTestamentCheckBox.stateChanged.connect(self.onCheckBoxIndexChanged) - self.apocryphaCheckBox.stateChanged.connect(self.onCheckBoxIndexChanged) + self.old_testament_check_box.stateChanged.connect(self.onCheckBoxIndexChanged) + self.new_testament_check_box.stateChanged.connect(self.onCheckBoxIndexChanged) + self.apocrypha_check_box.stateChanged.connect(self.onCheckBoxIndexChanged) def onCheckBoxIndexChanged(self, index): """ 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 """ - self.correspondingComboBox.clear() + self.corresponding_combo_box.clear() items = BiblesResourcesDB.get_books() for item in items: - addBook = True + add_book = True for book in self.books: if book.book_reference_id == item[u'id']: - addBook = False + add_book = False break - if self.oldTestamentCheckBox.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 1: - addBook = False - elif self.newTestamentCheckBox.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 2: - addBook = False - elif self.apocryphaCheckBox.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 3: - addBook = False - if addBook: - self.correspondingComboBox.addItem(self.book_names[item[u'abbreviation']]) + if self.old_testament_check_box.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 1: + add_book = False + elif self.new_testament_check_box.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 2: + add_book = False + elif self.apocrypha_check_box.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 3: + add_book = False + if add_book: + 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 - log.debug(maxbooks) - if maxbooks <= 27: - self.oldTestamentCheckBox.setCheckState(QtCore.Qt.Unchecked) - self.apocryphaCheckBox.setCheckState(QtCore.Qt.Unchecked) - elif maxbooks <= 66: - self.apocryphaCheckBox.setCheckState(QtCore.Qt.Unchecked) - self.reloadComboBox() - self.currentBookLabel.setText(unicode(name)) - self.correspondingComboBox.setFocus() + log.debug(max_books) + if max_books <= 27: + self.old_testament_check_box.setCheckState(QtCore.Qt.Unchecked) + self.apocrypha_check_box.setCheckState(QtCore.Qt.Unchecked) + elif max_books <= 66: + self.apocrypha_check_box.setCheckState(QtCore.Qt.Unchecked) + self.reload_combo_box() + self.current_book_label.setText(unicode(name)) + self.corresponding_combo_box.setFocus() return QDialog.exec_(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.')) - self.correspondingComboBox.setFocus() + self.corresponding_combo_box.setFocus() return False else: - cor_book = self.correspondingComboBox.currentText() + cor_book = self.corresponding_combo_box.currentText() for character in u'\\.^$*+?{}[]()': cor_book = cor_book.replace(character, u'\\' + character) - books = filter(lambda key: - re.match(cor_book, unicode(self.book_names[key]), re.UNICODE), self.book_names.keys()) + books = filter( + 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)) if books: self.book_id = books[0][u'id'] diff --git a/openlp/plugins/bibles/forms/editbibledialog.py b/openlp/plugins/bibles/forms/editbibledialog.py index b45bb2751..79cef2575 100644 --- a/openlp/plugins/bibles/forms/editbibledialog.py +++ b/openlp/plugins/bibles/forms/editbibledialog.py @@ -36,118 +36,118 @@ from openlp.plugins.bibles.lib.db import BiblesResourcesDB class Ui_EditBibleDialog(object): - def setupUi(self, editBibleDialog): - editBibleDialog.setObjectName(u'editBibleDialog') - editBibleDialog.resize(520, 400) - editBibleDialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png')) - editBibleDialog.setModal(True) - self.dialogLayout = QtGui.QVBoxLayout(editBibleDialog) - self.dialogLayout.setSpacing(8) - self.dialogLayout.setContentsMargins(8, 8, 8, 8) - self.dialogLayout.setObjectName(u'dialog_layout') - self.bibleTabWidget = QtGui.QTabWidget(editBibleDialog) - self.bibleTabWidget.setObjectName(u'BibleTabWidget') + def setupUi(self, edit_bible_dialog): + edit_bible_dialog.setObjectName(u'edit_bible_dialog') + edit_bible_dialog.resize(520, 400) + edit_bible_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png')) + edit_bible_dialog.setModal(True) + self.dialog_layout = QtGui.QVBoxLayout(edit_bible_dialog) + self.dialog_layout.setSpacing(8) + self.dialog_layout.setContentsMargins(8, 8, 8, 8) + self.dialog_layout.setObjectName(u'dialog_layout') + self.bible_tab_widget = QtGui.QTabWidget(edit_bible_dialog) + self.bible_tab_widget.setObjectName(u'BibleTabWidget') # Meta tab - self.metaTab = QtGui.QWidget() - self.metaTab.setObjectName(u'metaTab') - self.metaTabLayout = QtGui.QVBoxLayout(self.metaTab) - self.metaTabLayout.setObjectName(u'metaTabLayout') - self.licenseDetailsGroupBox = QtGui.QGroupBox(self.metaTab) - self.licenseDetailsGroupBox.setObjectName(u'licenseDetailsGroupBox') - self.licenseDetailsLayout = QtGui.QFormLayout(self.licenseDetailsGroupBox) - self.licenseDetailsLayout.setObjectName(u'licenseDetailsLayout') - self.versionNameLabel = QtGui.QLabel(self.licenseDetailsGroupBox) - self.versionNameLabel.setObjectName(u'versionNameLabel') - self.versionNameEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox) - self.versionNameEdit.setObjectName(u'versionNameEdit') - self.versionNameLabel.setBuddy(self.versionNameEdit) - self.licenseDetailsLayout.addRow(self.versionNameLabel, self.versionNameEdit) - self.copyrightLabel = QtGui.QLabel(self.licenseDetailsGroupBox) - self.copyrightLabel.setObjectName(u'copyrightLabel') - self.copyrightEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox) - self.copyrightEdit.setObjectName(u'copyright_edit') - self.copyrightLabel.setBuddy(self.copyrightEdit) - self.licenseDetailsLayout.addRow(self.copyrightLabel, self.copyrightEdit) - self.permissionsLabel = QtGui.QLabel(self.licenseDetailsGroupBox) - self.permissionsLabel.setObjectName(u'permissionsLabel') - self.permissionsEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox) - self.permissionsEdit.setObjectName(u'permissionsEdit') - self.permissionsLabel.setBuddy(self.permissionsEdit) - self.licenseDetailsLayout.addRow(self.permissionsLabel, self.permissionsEdit) - self.metaTabLayout.addWidget(self.licenseDetailsGroupBox) - self.languageSelectionGroupBox = QtGui.QGroupBox(self.metaTab) - self.languageSelectionGroupBox.setObjectName(u'languageSelectionGroupBox') - self.languageSelectionLayout = QtGui.QVBoxLayout(self.languageSelectionGroupBox) - self.languageSelectionLabel = QtGui.QLabel(self.languageSelectionGroupBox) - self.languageSelectionLabel.setObjectName(u'languageSelectionLabel') - self.languageSelectionComboBox = QtGui.QComboBox(self.languageSelectionGroupBox) - self.languageSelectionComboBox.setObjectName(u'languageSelectionComboBox') - self.languageSelectionComboBox.addItems([u'', u'', u'', u'']) - self.languageSelectionLayout.addWidget(self.languageSelectionLabel) - self.languageSelectionLayout.addWidget(self.languageSelectionComboBox) - self.metaTabLayout.addWidget(self.languageSelectionGroupBox) - self.metaTabLayout.addStretch() - self.bibleTabWidget.addTab(self.metaTab, u'') + self.meta_tab = QtGui.QWidget() + self.meta_tab.setObjectName(u'meta_tab') + self.meta_tab_layout = QtGui.QVBoxLayout(self.meta_tab) + self.meta_tab_layout.setObjectName(u'meta_tab_layout') + self.license_details_group_box = QtGui.QGroupBox(self.meta_tab) + self.license_details_group_box.setObjectName(u'license_details_group_box') + self.license_details_layout = QtGui.QFormLayout(self.license_details_group_box) + self.license_details_layout.setObjectName(u'license_details_layout') + self.version_name_label = QtGui.QLabel(self.license_details_group_box) + self.version_name_label.setObjectName(u'version_name_label') + self.version_name_edit = QtGui.QLineEdit(self.license_details_group_box) + self.version_name_edit.setObjectName(u'version_name_edit') + self.version_name_label.setBuddy(self.version_name_edit) + self.license_details_layout.addRow(self.version_name_label, self.version_name_edit) + self.copyright_label = QtGui.QLabel(self.license_details_group_box) + self.copyright_label.setObjectName(u'copyright_label') + self.copyright_edit = QtGui.QLineEdit(self.license_details_group_box) + self.copyright_edit.setObjectName(u'copyright_edit') + self.copyright_label.setBuddy(self.copyright_edit) + self.license_details_layout.addRow(self.copyright_label, self.copyright_edit) + self.permissions_label = QtGui.QLabel(self.license_details_group_box) + self.permissions_label.setObjectName(u'permissions_label') + self.permissions_edit = QtGui.QLineEdit(self.license_details_group_box) + self.permissions_edit.setObjectName(u'permissions_edit') + self.permissions_label.setBuddy(self.permissions_edit) + self.license_details_layout.addRow(self.permissions_label, self.permissions_edit) + self.meta_tab_layout.addWidget(self.license_details_group_box) + self.language_selection_group_box = QtGui.QGroupBox(self.meta_tab) + self.language_selection_group_box.setObjectName(u'language_selection_group_box') + self.language_selection_layout = QtGui.QVBoxLayout(self.language_selection_group_box) + self.language_selection_label = QtGui.QLabel(self.language_selection_group_box) + self.language_selection_label.setObjectName(u'language_selection_label') + self.language_selection_combo_box = QtGui.QComboBox(self.language_selection_group_box) + self.language_selection_combo_box.setObjectName(u'language_selection_combo_box') + self.language_selection_combo_box.addItems([u'', u'', u'', u'']) + self.language_selection_layout.addWidget(self.language_selection_label) + self.language_selection_layout.addWidget(self.language_selection_combo_box) + self.meta_tab_layout.addWidget(self.language_selection_group_box) + self.meta_tab_layout.addStretch() + self.bible_tab_widget.addTab(self.meta_tab, u'') # Book name tab - self.bookNameTab = QtGui.QWidget() - self.bookNameTab.setObjectName(u'bookNameTab') - self.bookNameTabLayout = QtGui.QVBoxLayout(self.bookNameTab) - self.bookNameTabLayout.setObjectName(u'bookNameTabLayout') - self.bookNameNotice = QtGui.QLabel(self.bookNameTab) - self.bookNameNotice.setObjectName(u'bookNameNotice') - self.bookNameNotice.setWordWrap(True) - self.bookNameTabLayout.addWidget(self.bookNameNotice) - self.scrollArea = QtGui.QScrollArea(self.bookNameTab) - self.scrollArea.setWidgetResizable(True) - self.scrollArea.setObjectName(u'scrollArea') - self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.bookNameWidget = QtGui.QWidget(self.scrollArea) - self.bookNameWidget.setObjectName(u'bookNameWidget') - self.bookNameWidgetLayout = QtGui.QFormLayout(self.bookNameWidget) - self.bookNameWidgetLayout.setObjectName(u'bookNameWidgetLayout') - self.bookNameLabel = {} - self.bookNameEdit= {} + self.book_name_tab = QtGui.QWidget() + self.book_name_tab.setObjectName(u'book_name_tab') + self.book_name_tab_layout = QtGui.QVBoxLayout(self.book_name_tab) + self.book_name_tab_layout.setObjectName(u'book_name_tab_layout') + self.book_name_notice = QtGui.QLabel(self.book_name_tab) + self.book_name_notice.setObjectName(u'book_name_notice') + self.book_name_notice.setWordWrap(True) + self.book_name_tab_layout.addWidget(self.book_name_notice) + self.scroll_area = QtGui.QScrollArea(self.book_name_tab) + self.scroll_area.setWidgetResizable(True) + self.scroll_area.setObjectName(u'scroll_area') + self.scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.book_name_widget = QtGui.QWidget(self.scroll_area) + self.book_name_widget.setObjectName(u'book_name_widget') + self.book_name_widget_layout = QtGui.QFormLayout(self.book_name_widget) + self.book_name_widget_layout.setObjectName(u'book_name_widget_layout') + self.book_name_label = {} + self.book_name_edit= {} for book in BiblesResourcesDB.get_books(): - self.bookNameLabel[book[u'abbreviation']] = QtGui.QLabel(self.bookNameWidget) - self.bookNameLabel[book[u'abbreviation']].setObjectName(u'bookNameLabel[%s]' % book[u'abbreviation']) - self.bookNameEdit[book[u'abbreviation']] = QtGui.QLineEdit(self.bookNameWidget) - self.bookNameEdit[book[u'abbreviation']].setObjectName(u'bookNameEdit[%s]' % book[u'abbreviation']) - self.bookNameWidgetLayout.addRow( - self.bookNameLabel[book[u'abbreviation']], - self.bookNameEdit[book[u'abbreviation']]) - self.scrollArea.setWidget(self.bookNameWidget) - self.bookNameTabLayout.addWidget(self.scrollArea) - self.bookNameTabLayout.addStretch() - self.bibleTabWidget.addTab(self.bookNameTab, u'') + self.book_name_label[book[u'abbreviation']] = QtGui.QLabel(self.book_name_widget) + self.book_name_label[book[u'abbreviation']].setObjectName(u'book_name_label[%s]' % book[u'abbreviation']) + self.book_name_edit[book[u'abbreviation']] = QtGui.QLineEdit(self.book_name_widget) + self.book_name_edit[book[u'abbreviation']].setObjectName(u'book_name_edit[%s]' % book[u'abbreviation']) + self.book_name_widget_layout.addRow( + self.book_name_label[book[u'abbreviation']], + self.book_name_edit[book[u'abbreviation']]) + self.scroll_area.setWidget(self.book_name_widget) + self.book_name_tab_layout.addWidget(self.scroll_area) + self.book_name_tab_layout.addStretch() + self.bible_tab_widget.addTab(self.book_name_tab, u'') # Last few bits - self.dialogLayout.addWidget(self.bibleTabWidget) - self.button_box = create_button_box(editBibleDialog, u'button_box', [u'cancel', u'save']) - self.dialogLayout.addWidget(self.button_box) - self.retranslateUi(editBibleDialog) - QtCore.QMetaObject.connectSlotsByName(editBibleDialog) + self.dialog_layout.addWidget(self.bible_tab_widget) + self.button_box = create_button_box(edit_bible_dialog, u'button_box', [u'cancel', u'save']) + self.dialog_layout.addWidget(self.button_box) + self.retranslateUi(edit_bible_dialog) + QtCore.QMetaObject.connectSlotsByName(edit_bible_dialog) - def retranslateUi(self, editBibleDialog): + def retranslateUi(self, edit_bible_dialog): self.book_names = BibleStrings().BookNames - editBibleDialog.setWindowTitle(translate('BiblesPlugin.EditBibleForm', 'Bible Editor')) + edit_bible_dialog.setWindowTitle(translate('BiblesPlugin.EditBibleForm', 'Bible Editor')) # 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')) - self.licenseDetailsGroupBox.setTitle(translate('BiblesPlugin.EditBibleForm', 'License Details')) - self.versionNameLabel.setText(translate('BiblesPlugin.EditBibleForm', 'Version name:')) - self.copyrightLabel.setText(translate('BiblesPlugin.EditBibleForm', 'Copyright:')) - self.permissionsLabel.setText(translate('BiblesPlugin.EditBibleForm', 'Permissions:')) - self.languageSelectionGroupBox.setTitle(translate('BiblesPlugin.EditBibleForm', 'Default Bible Language')) - self.languageSelectionLabel.setText(translate('BiblesPlugin.EditBibleForm', + self.license_details_group_box.setTitle(translate('BiblesPlugin.EditBibleForm', 'License Details')) + self.version_name_label.setText(translate('BiblesPlugin.EditBibleForm', 'Version name:')) + self.copyright_label.setText(translate('BiblesPlugin.EditBibleForm', 'Copyright:')) + self.permissions_label.setText(translate('BiblesPlugin.EditBibleForm', 'Permissions:')) + self.language_selection_group_box.setTitle(translate('BiblesPlugin.EditBibleForm', 'Default Bible Language')) + self.language_selection_label.setText(translate('BiblesPlugin.EditBibleForm', 'Book name language in search field, search results and on display:')) - self.languageSelectionComboBox.setItemText(0, translate('BiblesPlugin.EditBibleForm', 'Global Settings')) - self.languageSelectionComboBox.setItemText(LanguageSelection.Bible + 1, + self.language_selection_combo_box.setItemText(0, translate('BiblesPlugin.EditBibleForm', 'Global Settings')) + self.language_selection_combo_box.setItemText(LanguageSelection.Bible + 1, 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')) - self.languageSelectionComboBox.setItemText(LanguageSelection.English + 1, + self.language_selection_combo_box.setItemText(LanguageSelection.English + 1, translate('BiblesPlugin.EditBibleForm', 'English')) # 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')) 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']])) diff --git a/openlp/plugins/bibles/forms/editbibleform.py b/openlp/plugins/bibles/forms/editbibleform.py index cbb02109a..b05fb68fb 100644 --- a/openlp/plugins/bibles/forms/editbibleform.py +++ b/openlp/plugins/bibles/forms/editbibleform.py @@ -65,33 +65,33 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog): """ log.debug(u'Load Bible') self.bible = bible - self.versionNameEdit.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.permissionsEdit.setText(self.manager.get_meta_data(self.bible, u'permissions').value) + self.version_name_edit.setText(self.manager.get_meta_data(self.bible, u'name').value) + self.copyright_edit.setText(self.manager.get_meta_data(self.bible, u'copyright').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') 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.webbible = self.manager.get_meta_data(self.bible, u'download_source') 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.')) - self.scrollArea.hide() + self.scroll_area.hide() 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, ' 'if "Global settings" is selected, on the Bible page in Configure OpenLP.')) for book in BiblesResourcesDB.get_books(): 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: - 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: # It is necessary to remove the Widget otherwise there still # exists the vertical spacing in QFormLayout - self.bookNameWidgetLayout.removeWidget(self.bookNameLabel[book[u'abbreviation']]) - self.bookNameLabel[book[u'abbreviation']].hide() - self.bookNameWidgetLayout.removeWidget(self.bookNameEdit[book[u'abbreviation']]) - self.bookNameEdit[book[u'abbreviation']].hide() + self.book_name_widget_layout.removeWidget(self.book_name_label[book[u'abbreviation']]) + self.book_name_label[book[u'abbreviation']].hide() + self.book_name_widget_layout.removeWidget(self.book_name_edit[book[u'abbreviation']]) + self.book_name_edit[book[u'abbreviation']].hide() def reject(self): """ @@ -106,10 +106,10 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog): Exit Dialog and save data """ log.debug(u'BibleEditForm.accept') - version = self.versionNameEdit.text() - copyright = self.copyrightEdit.text() - permissions = self.permissionsEdit.text() - book_name_language = self.languageSelectionComboBox.currentIndex() - 1 + version = self.version_name_edit.text() + copyright = self.copyright_edit.text() + permissions = self.permissions_edit.text() + book_name_language = self.language_selection_combo_box.currentIndex() - 1 if book_name_language == -1: book_name_language = None if not self.validateMeta(version, copyright): @@ -118,7 +118,7 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog): custom_names = {} for abbr, book in self.books.iteritems(): 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 not self.validateBook(custom_names[abbr], abbr): return @@ -139,19 +139,19 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog): Validate the Meta before saving. """ if not name: - self.versionNameEdit.setFocus() + self.version_name_edit.setFocus() critical_error_message_box(UiStrings().EmptyField, translate('BiblesPlugin.BibleEditForm', 'You need to specify a version name for your Bible.')) return False elif not copyright: - self.copyrightEdit.setFocus() + self.copyright_edit.setFocus() critical_error_message_box(UiStrings().EmptyField, translate('BiblesPlugin.BibleEditForm', 'You need to set a copyright for your Bible. Bibles in the Public Domain need to be marked as such.')) return False elif self.manager.exists(name) and self.manager.get_meta_data(self.bible, u'name').value != \ name: - self.versionNameEdit.setFocus() + self.version_name_edit.setFocus() critical_error_message_box(translate('BiblesPlugin.BibleEditForm', 'Bible Exists'), translate('BiblesPlugin.BibleEditForm', 'This Bible already exists. Please import ' '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]+$') if not new_book_name: - self.bookNameEdit[abbreviation].setFocus() + self.book_name_edit[abbreviation].setFocus() critical_error_message_box(UiStrings().EmptyField, translate('BiblesPlugin.BibleEditForm', 'You need to specify a book name for "%s".') % self.book_names[abbreviation]) return False elif not book_regex.match(new_book_name): - self.bookNameEdit[abbreviation].setFocus() + self.book_name_edit[abbreviation].setFocus() critical_error_message_box(UiStrings().EmptyField, translate('BiblesPlugin.BibleEditForm', '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 abbr == abbreviation: continue - if self.bookNameEdit[abbr].text() == new_book_name: - self.bookNameEdit[abbreviation].setFocus() + if self.book_name_edit[abbr].text() == new_book_name: + self.book_name_edit[abbreviation].setFocus() critical_error_message_box( translate('BiblesPlugin.BibleEditForm', 'Duplicate Book Name'), translate('BiblesPlugin.BibleEditForm', 'The Book Name "%s" has been entered more than once.') diff --git a/openlp/plugins/bibles/forms/languagedialog.py b/openlp/plugins/bibles/forms/languagedialog.py index 9ad16bb30..870957c3c 100644 --- a/openlp/plugins/bibles/forms/languagedialog.py +++ b/openlp/plugins/bibles/forms/languagedialog.py @@ -33,44 +33,44 @@ from openlp.core.lib import translate from openlp.core.lib.ui import create_button_box class Ui_LanguageDialog(object): - def setupUi(self, languageDialog): - languageDialog.setObjectName(u'languageDialog') - languageDialog.resize(400, 165) - self.languageLayout = QtGui.QVBoxLayout(languageDialog) - self.languageLayout.setSpacing(8) - self.languageLayout.setMargin(8) - self.languageLayout.setObjectName(u'languageLayout') - self.bibleLabel = QtGui.QLabel(languageDialog) - self.bibleLabel.setObjectName(u'bibleLabel') - self.languageLayout.addWidget(self.bibleLabel) - self.infoLabel = QtGui.QLabel(languageDialog) - self.infoLabel.setWordWrap(True) - self.infoLabel.setObjectName(u'infoLabel') - self.languageLayout.addWidget(self.infoLabel) - self.languageHBoxLayout = QtGui.QHBoxLayout() - self.languageHBoxLayout.setSpacing(8) - self.languageHBoxLayout.setObjectName(u'languageHBoxLayout') - self.languageLabel = QtGui.QLabel(languageDialog) - self.languageLabel.setObjectName(u'languageLabel') - self.languageHBoxLayout.addWidget(self.languageLabel) - self.languageComboBox = QtGui.QComboBox(languageDialog) + def setupUi(self, language_dialog): + language_dialog.setObjectName(u'language_dialog') + language_dialog.resize(400, 165) + self.language_layout = QtGui.QVBoxLayout(language_dialog) + self.language_layout.setSpacing(8) + self.language_layout.setMargin(8) + self.language_layout.setObjectName(u'language_layout') + self.bible_label = QtGui.QLabel(language_dialog) + self.bible_label.setObjectName(u'bible_label') + self.language_layout.addWidget(self.bible_label) + self.info_label = QtGui.QLabel(language_dialog) + self.info_label.setWordWrap(True) + self.info_label.setObjectName(u'info_label') + self.language_layout.addWidget(self.info_label) + self.language_h_box_layout = QtGui.QHBoxLayout() + self.language_h_box_layout.setSpacing(8) + self.language_h_box_layout.setObjectName(u'language_h_box_layout') + self.language_label = QtGui.QLabel(language_dialog) + self.language_label.setObjectName(u'language_label') + self.language_h_box_layout.addWidget(self.language_label) + self.language_combo_box = QtGui.QComboBox(language_dialog) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.languageComboBox.sizePolicy().hasHeightForWidth()) - self.languageComboBox.setSizePolicy(sizePolicy) - self.languageComboBox.setObjectName(u'languageComboBox') - self.languageHBoxLayout.addWidget(self.languageComboBox) - self.languageLayout.addLayout(self.languageHBoxLayout) - self.button_box = create_button_box(languageDialog, u'button_box', [u'cancel', u'ok']) - self.languageLayout.addWidget(self.button_box) + sizePolicy.setHeightForWidth(self.language_combo_box.sizePolicy().hasHeightForWidth()) + self.language_combo_box.setSizePolicy(sizePolicy) + self.language_combo_box.setObjectName(u'language_combo_box') + self.language_h_box_layout.addWidget(self.language_combo_box) + self.language_layout.addLayout(self.language_h_box_layout) + self.button_box = create_button_box(language_dialog, u'button_box', [u'cancel', u'ok']) + self.language_layout.addWidget(self.button_box) - self.retranslateUi(languageDialog) + self.retranslateUi(language_dialog) - def retranslateUi(self, languageDialog): - languageDialog.setWindowTitle(translate('BiblesPlugin.LanguageDialog', 'Select Language')) - self.bibleLabel.setText(translate('BiblesPlugin.LanguageDialog', '')) - self.infoLabel.setText(translate('BiblesPlugin.LanguageDialog', + def retranslateUi(self, language_dialog): + language_dialog.setWindowTitle(translate('BiblesPlugin.LanguageDialog', 'Select Language')) + self.bible_label.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 ' 'from the list below.')) - self.languageLabel.setText(translate('BiblesPlugin.LanguageDialog', 'Language:')) + self.language_label.setText(translate('BiblesPlugin.LanguageDialog', 'Language:')) diff --git a/openlp/plugins/bibles/forms/languageform.py b/openlp/plugins/bibles/forms/languageform.py index cdac6dec9..2bba3d7a4 100644 --- a/openlp/plugins/bibles/forms/languageform.py +++ b/openlp/plugins/bibles/forms/languageform.py @@ -40,8 +40,10 @@ from openlp.plugins.bibles.forms.languagedialog import \ Ui_LanguageDialog from openlp.plugins.bibles.lib.db import BiblesResourcesDB + log = logging.getLogger(__name__) + class LanguageForm(QDialog, Ui_LanguageDialog): """ Class to manage a dialog which ask the user for a language. @@ -56,19 +58,17 @@ class LanguageForm(QDialog, Ui_LanguageDialog): self.setupUi(self) def exec_(self, bible_name): - self.languageComboBox.addItem(u'') + self.language_combo_box.addItem(u'') if bible_name: - self.bibleLabel.setText(unicode(bible_name)) + self.bible_label.setText(unicode(bible_name)) 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) def accept(self): - if not self.languageComboBox.currentText(): - critical_error_message_box( - message=translate('BiblesPlugin.LanguageForm', - 'You need to choose a language.')) - self.languageComboBox.setFocus() + if not self.language_combo_box.currentText(): + critical_error_message_box(message=translate('BiblesPlugin.LanguageForm', 'You need to choose a language.')) + self.language_combo_box.setFocus() return False else: return QDialog.accept(self) diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 219b91f90..371632d41 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -38,9 +38,11 @@ from openlp.core.lib import Settings, translate log = logging.getLogger(__name__) + REFERENCE_MATCHES = {} REFERENCE_SEPARATORS = {} + class LayoutStyle(object): """ An enumeration for bible screen layout styles. @@ -62,8 +64,7 @@ class DisplayStyle(object): class LanguageSelection(object): """ - An enumeration for bible bookname language. - And standard strings for use throughout the bibles plugin. + An enumeration for bible bookname language. And standard strings for use throughout the bibles plugin. """ Bible = 0 Application = 1 @@ -178,8 +179,7 @@ class BibleStrings(object): def update_reference_separators(): """ - Updates separators and matches for parsing and formating scripture - references. + Updates separators and matches for parsing and formating scripture references. """ default_separators = translate('BiblesPlugin', ':|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): """ - This is the next generation über-awesome function that takes a person's - typed in string and converts it to a list of references to be queried from - the Bible database files. + This is the next generation über-awesome function that takes a person's typed in string and converts it to a list + of references to be queried from the Bible database files. ``reference`` 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. ``language_selection`` - An int. The language selection the user has choosen in settings - section. + An int. The language selection the user has choosen in settings section. ``book_ref_id`` A string. The book reference id. Returns ``None`` or a reference list. - The reference list is a list of tuples, with each tuple structured like - this:: + The reference list is a list of tuples, with each tuple structured like this:: (book, chapter, from_verse, to_verse) @@ -275,8 +272,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): **Reference string details:** - Each reference starts with the book name and a chapter number. These are - both mandatory. + Each reference starts with the book name and a chapter number. These are both mandatory. * ``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-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 - the last selected chapter. + After a verse reference all further single values are treat as verse in the last selected chapter. * ``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 - are build analog to the first ones. This way it is possible to define each - number of verse references. It is not possible to refer to verses in - additional books. + After a list separator it is possible to refer to additional verses. They are build analog to the first ones. This + way it is possible to define each number of verse references. It is not possible to refer to verses in additional + books. * ``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,4:1`` refers to John chapter 3 verses 16 to 18 and - chapter 4 verse 1 + * ``John 3:16-18,4:1`` refers to John chapter 3 verses 16 to 18 and chapter 4 verse 1 - If there is a range separator without further verse declaration the last - refered chapter is addressed until the end. + If there is a range separator without further verse declaration the last refered chapter is addressed until the end. - ``range_regex`` is a regular expression which matches for verse range - declarations: + ``range_regex`` is a regular expression which matches for verse range declarations: ``(?:(?P[0-9]+)%(sep_v)s)?`` - It starts with a optional chapter reference ``from_chapter`` followed by - a verse separator. + It starts with a optional chapter reference ``from_chapter`` followed by a verse separator. ``(?P[0-9]+)`` The verse reference ``from_verse`` is manditory ``(?P%(sep_r)s(?:`` ... ``|%(sep_e)s)?)?`` - A ``range_to`` declaration is optional. It starts with a range separator - and contains optional a chapter and verse declaration or a end - separator. + A ``range_to`` declaration is optional. It starts with a range separator and contains optional a chapter and + verse declaration or a end separator. ``(?:(?P[0-9]+)%(sep_v)s)?`` 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[0-9]+)`` The ``to_verse`` reference is equivalent to group 2. - The full reference is matched against get_reference_match(u'full'). This - regular expression looks like this: + The full reference is matched against get_reference_match(u'full'). This regular expression looks like this: ``^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$`` - The second group contains all ``ranges``. This can be multiple - declarations of range_regex separated by a list separator. + The second group contains all ``ranges``. This can be multiple declarations of range_regex separated by a list + separator. """ log.debug(u'parse_reference("%s")', reference) diff --git a/openlp/plugins/bibles/lib/csvbible.py b/openlp/plugins/bibles/lib/csvbible.py index f57da2ba0..7cf004cfc 100644 --- a/openlp/plugins/bibles/lib/csvbible.py +++ b/openlp/plugins/bibles/lib/csvbible.py @@ -27,8 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`cvsbible` modules provides a facility to import bibles from a set of -CSV files. +The :mod:`cvsbible` modules provides a facility to import bibles from a set of CSV files. 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 "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 -quotes ('"') as the quote symbol. +All CSV files are expected to use a comma (',') as the delimiter and double quotes ('"') as the quote symbol. """ import logging import chardet @@ -65,8 +63,10 @@ import csv from openlp.core.lib import translate from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB + log = logging.getLogger(__name__) + class CSVBible(BibleDB): """ This class provides a specialisation for importing of CSV Bibles. @@ -75,9 +75,8 @@ class CSVBible(BibleDB): def __init__(self, parent, **kwargs): """ - Loads a Bible from a set of CSV files. - This class assumes the files contain all the information and - a clean bible is being loaded. + Loads a Bible from a set of CSV files. This class assumes the files contain all the information and a clean + bible is being loaded. """ log.info(self.__class__.__name__) BibleDB.__init__(self, parent, **kwargs) diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 4193668e2..837dbec38 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -511,7 +511,7 @@ class BibleDB(QtCore.QObject, Manager): language = None language_form = LanguageForm(self.wizard) if language_form.exec_(bible_name): - language = unicode(language_form.languageComboBox.currentText()) + language = unicode(language_form.language_combo_box.currentText()) if not language: return False language = BiblesResourcesDB.get_language(language) diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index b01377a05..efab9106b 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -27,8 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`http` module enables OpenLP to retrieve scripture from bible -websites. +The :mod:`http` module enables OpenLP to retrieve scripture from bible websites. """ import logging import re @@ -36,7 +35,7 @@ import socket import urllib 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.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.db import BibleDB, BiblesResourcesDB, Book +CLEANER_REGEX = re.compile(r' |
|\'\+\'') +FIX_PUNKCTUATION_REGEX = re.compile(r'[ ]+([.,;])') +REDUCE_SPACES_REGEX = re.compile(r'[ ]{2,}') UGLY_CHARS = { u'\u2014': u' - ', u'\u2018': u'\'', @@ -52,9 +54,12 @@ UGLY_CHARS = { u'\u201d': u'"', u' ': u' ' } +VERSE_NUMBER_REGEX = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse.*') + log = logging.getLogger(__name__) + class BGExtract(object): """ Extract verses from BibleGateway @@ -78,9 +83,9 @@ class BGExtract(object): An HTML class attribute for further qualification. """ if class_: - all_tags = parent.findAll(tag, class_) + all_tags = parent.find_all(tag, class_) else: - all_tags = parent.findAll(tag) + all_tags = parent.find_all(tag) for element in all_tags: element.extract() @@ -93,14 +98,15 @@ class BGExtract(object): """ if isinstance(tag, NavigableString): 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() return verse, None - elif tag.get('class') == 'chapternum': + elif tag.get('class')[0] == 'chapternum': verse = '1' return verse, None else: - verse, text = None, '' + verse = None + text = '' for child in tag.contents: c_verse, c_text = self._extract_verse(child) if c_verse: @@ -137,7 +143,8 @@ class BGExtract(object): tags = tags[::-1] current_text = '' for tag in tags: - verse, text = None, '' + verse = None + text = '' for child in tag.contents: c_verse, c_text = self._extract_verse(child) if c_verse: @@ -173,8 +180,8 @@ class BGExtract(object): def _extract_verses_old(self, div): """ - Use the old style of parsing for those Bibles on BG who mysteriously - have not been migrated to the new (still broken) HTML. + Use the old style of parsing for those Bibles on BG who mysteriously have not been migrated to the new (still + broken) HTML. ``div`` The parent div. @@ -185,13 +192,12 @@ class BGExtract(object): if first_verse and first_verse.contents: verse_list[1] = unicode(first_verse.contents[0]) for verse in div(u'sup', u'versenum'): - raw_verse_num = verse.next + raw_verse_num = verse.next_element clean_verse_num = 0 - # Not all verses exist in all translations and may or may not be - # represented by a verse number. 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 garbage may be sucked in to the verse text so - # if we do not get a clean int() then ignore the verse completely. + # Not all verses exist in all translations and may or may not be represented by a verse number. 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 + # garbage may be sucked in to the verse text so if we do not get a clean int() then ignore the verse + # completely. try: clean_verse_num = int(str(raw_verse_num)) except ValueError: @@ -201,16 +207,16 @@ class BGExtract(object): except TypeError: log.warn(u'Illegal verse number: %s', unicode(raw_verse_num)) if clean_verse_num: - verse_text = raw_verse_num.next - part = raw_verse_num.next.next - while not (isinstance(part, Tag) and part.get(u'class') == u'versenum'): + verse_text = raw_verse_num.next_element + part = raw_verse_num.next_element.next_element + 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. if isinstance(part, NavigableString): 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. break - part = part.next + part = part.next_element verse_list[clean_verse_num] = unicode(verse_text) return verse_list @@ -230,15 +236,14 @@ class BGExtract(object): 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_params = u'search=%s+%s&version=%s' % (url_book_name, chapter, version) - cleaner = [(re.compile(' |
|\'\+\''), lambda match: '')] soup = get_soup_for_bible_ref( u'http://www.biblegateway.com/passage/?%s' % url_params, - pre_parse_regex=r'', pre_parse_substitute='', cleaner=cleaner) + pre_parse_regex=r'', pre_parse_substitute='') if not soup: return None div = soup.find('div', 'result-text-style-normal') self._clean_soup(div) - span_list = div.findAll('span', 'text') + span_list = div.find_all('span', 'text') log.debug('Span list: %s', span_list) if not span_list: # 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() content = soup.find(u'table', u'infotable') if content: - content = content.findAll(u'tr') + content = content.find_all(u'tr') if not content: log.error(u'No books found in the Biblegateway response.') send_error_message(u'parse') @@ -341,19 +346,17 @@ class BSExtract(object): log.error(u'No verses found in the Bibleserver response.') send_error_message(u'parse') return None - content = content.find(u'div').findAll(u'div') - verse_number = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse.*') + content = content.find(u'div').find_all(u'div') verses = {} for verse in content: 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') return SearchResults(book_name, chapter, verses) def get_books_from_http(self, version): """ - Load a list of all books a Bible contains from Bibleserver mobile - website. + Load a list of all books a Bible contains from Bibleserver mobile website. ``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.') send_error_message(u'parse') return None - content = content.findAll(u'li') + content = content.find_all(u'li') 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): """ @@ -404,14 +417,12 @@ class CWExtract(object): if not soup: return None 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: log.error(u'No verses found in the CrossWalk response.') send_error_message(u'parse') return None verses = {} - reduce_spaces = re.compile(r'[ ]{2,}') - fix_punctuation = re.compile(r'[ ]+([.,;])') for verse in html_verses: self.application.process_events() verse_number = int(verse.contents[0].contents[0]) @@ -432,11 +443,10 @@ class CWExtract(object): if isinstance(subsub, NavigableString): verse_text += subsub self.application.process_events() - # Fix up leading and trailing spaces, multiple spaces, and spaces - # between text and , and . + # Fix up leading and trailing spaces, multiple spaces, and spaces between text and , and . verse_text = verse_text.strip(u'\n\r\t ') - verse_text = reduce_spaces.sub(u' ', verse_text) - verse_text = fix_punctuation.sub(r'\1', verse_text) + verse_text = REDUCE_SPACES_REGEX.sub(u' ', verse_text) + verse_text = FIX_PUNKCTUATION_REGEX.sub(r'\1', verse_text) verses[verse_number] = verse_text return SearchResults(book_name, chapter, verses) @@ -458,7 +468,7 @@ class CWExtract(object): log.error(u'No books found in the Crosswalk response.') send_error_message(u'parse') return None - content = content.findAll(u'li') + content = content.find_all(u'li') books = [] for book in content: book = book.find(u'a') @@ -481,9 +491,8 @@ class HTTPBible(BibleDB): def __init__(self, parent, **kwargs): """ - Finds all the bibles defined for the system - Creates an Interface Object for each bible containing connection - information + Finds all the bibles defined for the system. Creates an Interface Object for each bible containing connection + information. Throws Exception if no Bibles are found. @@ -492,8 +501,7 @@ class HTTPBible(BibleDB): BibleDB.__init__(self, parent, **kwargs) self.download_source = kwargs[u'download_source'] self.download_name = kwargs[u'download_name'] - # TODO: Clean up proxy stuff. We probably want one global proxy per - # connection type (HTTP and HTTPS) at most. + # TODO: Clean up proxy stuff. We probably want one global proxy per connection type (HTTP and HTTPS) at most. self.proxy_server = None self.proxy_username = None self.proxy_password = None @@ -508,8 +516,8 @@ class HTTPBible(BibleDB): def do_import(self, bible_name=None): """ - Run the import. This method overrides the parent class method. Returns - ``True`` on success, ``False`` on failure. + Run the import. This method overrides the parent class method. Returns ``True`` on success, ``False`` on + failure. """ self.wizard.progress_bar.setMaximum(68) 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: break self.wizard.increment_progress_bar(translate( - 'BiblesPlugin.HTTPBible', 'Importing %s...', - 'Importing ...') % book) + 'BiblesPlugin.HTTPBible', 'Importing %s...', 'Importing ...') % book) book_ref_id = self.get_book_ref_id_by_name(book, len(books), language_id) if not book_ref_id: 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): """ - A reimplementation of the ``BibleDB.get_verses`` method, this one is - specifically for web Bibles. It first checks to see if the particular - chapter exists in the DB, and if not it pulls it from the web. If the - chapter DOES exist, it simply pulls the verses from the DB using the - ancestor method. + A reimplementation of the ``BibleDB.get_verses`` method, this one is specifically for web Bibles. It first + checks to see if the particular chapter exists in the DB, and if not it pulls it from the web. If the chapter + DOES exist, it simply pulls the verses from the DB using the ancestor method. ``reference_list`` - This is the list of references the media manager item wants. It is - a list of tuples, with the following format:: + This is the list of references the media manager item wants. It is a list of tuples, with the following + format:: (book_reference_id, chapter, start_verse, end_verse) - Therefore, when you are looking for multiple items, simply break - them up into references like this, bundle them into a list. This - function then runs through the list, and returns an amalgamated - list of ``Verse`` objects. For example:: + Therefore, when you are looking for multiple items, simply break them up into references like this, bundle + them into a list. This function then runs through the list, and returns an amalgamated list of ``Verse`` + objects. For example:: [(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. ``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) return BiblesResourcesDB.get_chapter_count(book.book_reference_id) @@ -671,8 +675,7 @@ class HTTPBible(BibleDB): application = property(_get_application) -def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, - pre_parse_substitute=None, cleaner=None): +def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre_parse_substitute=None): """ Gets a webpage and returns a parsed and optionally cleaned soup or None. @@ -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. ``pre_parse_regex`` - A regular expression to run on the webpage. Allows manipulation of the - webpage before passing to BeautifulSoup for parsing. + A regular expression to run on the webpage. Allows manipulation of the webpage before passing to BeautifulSoup + for parsing. ``pre_parse_substitute`` The text to replace any matches to the regular expression with. - - ``cleaner`` - An optional regex to use during webpage parsing. """ if not reference_url: 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) soup = None try: - if cleaner: - soup = BeautifulSoup(page_source, markupMassage=cleaner) - else: - soup = BeautifulSoup(page_source) + soup = BeautifulSoup(page_source) + CLEANER_REGEX.sub(u'', unicode(soup)) except HTMLParseError: log.exception(u'BeautifulSoup could not parse the bible page.') 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() return soup + def send_error_message(error_type): """ Send a standard error message informing the user of an issue. diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 15c2e5d80..f701de0aa 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -38,28 +38,21 @@ from csvbible import CSVBible from http import HTTPBible from opensong import OpenSongBible 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__) + class BibleFormat(object): """ - 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. + This is a special enumeration class that holds the various types of Bibles. """ - _format_availability = {} Unknown = -1 OSIS = 0 CSV = 1 OpenSong = 2 WebDownload = 3 - OpenLP1 = 4 @staticmethod def get_class(format): @@ -77,8 +70,6 @@ class BibleFormat(object): return OpenSongBible elif format == BibleFormat.WebDownload: return HTTPBible - elif format == BibleFormat.OpenLP1: - return OpenLP1Bible else: return None @@ -92,17 +83,8 @@ class BibleFormat(object): BibleFormat.CSV, BibleFormat.OpenSong, 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): """ @@ -463,6 +445,5 @@ class BibleManager(object): main_window = property(_get_main_window) -BibleFormat.set_availability(BibleFormat.OpenLP1, HAS_OPENLP1) __all__ = [u'BibleFormat'] diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 86a507612..b67bb99b2 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -61,8 +61,8 @@ class BibleMediaItem(MediaManagerItem): def __init__(self, parent, plugin): self.icon_path = u'songs/song' - self.lockIcon = build_icon(u':/bibles/bibles_search_lock.png') - self.unlockIcon = build_icon(u':/bibles/bibles_search_unlock.png') + self.lock_icon = build_icon(u':/bibles/bibles_search_lock.png') + self.unlock_icon = build_icon(u':/bibles/bibles_search_unlock.png') MediaManagerItem.__init__(self, parent, plugin) # Place to store the search results for both bibles. self.settings = self.plugin.settings_tab @@ -73,7 +73,7 @@ class BibleMediaItem(MediaManagerItem): self.check_search_result() 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. """ @@ -84,7 +84,7 @@ class BibleMediaItem(MediaManagerItem): self.displayResults(bible, second_bible) return 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: self.displayResults(bible, second_bible) elif critical_error_message_box( @@ -95,7 +95,7 @@ class BibleMediaItem(MediaManagerItem): self.list_view.clear() self.displayResults(bible, second_bible) - def _decodeQtObject(self, bitem, key): + def _decode_qt_object(self, bitem, key): reference = bitem.data(QtCore.Qt.UserRole) obj = reference[unicode(key)] return unicode(obj).strip() @@ -159,7 +159,7 @@ class BibleMediaItem(MediaManagerItem): search_button_layout.setObjectName(prefix + u'search_button_layout') search_button_layout.addStretch() lockButton = QtGui.QToolButton(tab) - lockButton.setIcon(self.unlockIcon) + lockButton.setIcon(self.unlock_icon) lockButton.setCheckable(True) lockButton.setObjectName(prefix + u'LockButton') search_button_layout.addWidget(lockButton) @@ -250,7 +250,7 @@ class BibleMediaItem(MediaManagerItem): self.quickSearchEdit.returnPressed.connect(self.onQuickSearchButton) self.searchTabBar.currentChanged.connect(self.onSearchTabBarCurrentChanged) - def onFocus(self): + def on_focus(self): if self.quickTab.isVisible(): self.quickSearchEdit.setFocus() else: @@ -509,9 +509,9 @@ class BibleMediaItem(MediaManagerItem): def onLockButtonToggled(self, checked): if checked: - self.sender().setIcon(self.lockIcon) + self.sender().setIcon(self.lock_icon) else: - self.sender().setIcon(self.unlockIcon) + self.sender().setIcon(self.unlock_icon) def onQuickStyleComboBoxChanged(self): self.settings.layout_style = self.quickStyleComboBox.currentIndex() @@ -633,7 +633,7 @@ class BibleMediaItem(MediaManagerItem): if not self.advancedLockButton.isChecked(): self.list_view.clear() if self.list_view.count() != 0: - self.__checkSecondBible(bible, second_bible) + self.__check_second_bible(bible, second_bible) elif self.search_results: self.displayResults(bible, second_bible) self.advancedSearchButton.setEnabled(True) @@ -689,7 +689,7 @@ class BibleMediaItem(MediaManagerItem): if not self.quickLockButton.isChecked(): self.list_view.clear() 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: self.displayResults(bible, second_bible) self.quickSearchButton.setEnabled(True) @@ -787,19 +787,19 @@ class BibleMediaItem(MediaManagerItem): raw_title = [] verses = VerseReferenceList() for bitem in items: - book = self._decodeQtObject(bitem, 'book') - chapter = int(self._decodeQtObject(bitem, 'chapter')) - verse = int(self._decodeQtObject(bitem, 'verse')) - bible = self._decodeQtObject(bitem, 'bible') - version = self._decodeQtObject(bitem, 'version') - copyright = self._decodeQtObject(bitem, 'copyright') - permissions = self._decodeQtObject(bitem, 'permissions') - text = self._decodeQtObject(bitem, 'text') - second_bible = self._decodeQtObject(bitem, 'second_bible') - second_version = self._decodeQtObject(bitem, 'second_version') - second_copyright = self._decodeQtObject(bitem, 'second_copyright') - second_permissions = self._decodeQtObject(bitem, 'second_permissions') - second_text = self._decodeQtObject(bitem, 'second_text') + book = self._decode_qt_object(bitem, 'book') + chapter = int(self._decode_qt_object(bitem, 'chapter')) + verse = int(self._decode_qt_object(bitem, 'verse')) + bible = self._decode_qt_object(bitem, 'bible') + version = self._decode_qt_object(bitem, 'version') + copyright = self._decode_qt_object(bitem, 'copyright') + permissions = self._decode_qt_object(bitem, 'permissions') + text = self._decode_qt_object(bitem, 'text') + second_bible = self._decode_qt_object(bitem, 'second_bible') + second_version = self._decode_qt_object(bitem, 'second_version') + second_copyright = self._decode_qt_object(bitem, 'second_copyright') + second_permissions = self._decode_qt_object(bitem, 'second_permissions') + second_text = self._decode_qt_object(bitem, 'second_text') verses.add(book, chapter, verse, version, copyright, permissions) verse_text = self.formatVerse(old_chapter, chapter, verse) if second_bible: @@ -867,13 +867,13 @@ class BibleMediaItem(MediaManagerItem): """ verse_separator = get_reference_separator(u'sep_v_display') range_separator = get_reference_separator(u'sep_r_display') - old_chapter = self._decodeQtObject(old_bitem, 'chapter') - old_verse = self._decodeQtObject(old_bitem, 'verse') - start_book = self._decodeQtObject(start_bitem, 'book') - start_chapter = self._decodeQtObject(start_bitem, 'chapter') - start_verse = self._decodeQtObject(start_bitem, 'verse') - start_bible = self._decodeQtObject(start_bitem, 'bible') - start_second_bible = self._decodeQtObject(start_bitem, 'second_bible') + old_chapter = self._decode_qt_object(old_bitem, 'chapter') + old_verse = self._decode_qt_object(old_bitem, 'verse') + start_book = self._decode_qt_object(start_bitem, 'book') + start_chapter = self._decode_qt_object(start_bitem, 'chapter') + start_verse = self._decode_qt_object(start_bitem, 'verse') + start_bible = self._decode_qt_object(start_bitem, 'bible') + start_second_bible = self._decode_qt_object(start_bitem, 'second_bible') if start_second_bible: bibles = u'%s, %s' % (start_bible, start_second_bible) else: @@ -901,16 +901,16 @@ class BibleMediaItem(MediaManagerItem): The item we were previously dealing with. """ # Get all the necessary meta data. - book = self._decodeQtObject(bitem, 'book') - chapter = int(self._decodeQtObject(bitem, 'chapter')) - verse = int(self._decodeQtObject(bitem, 'verse')) - bible = self._decodeQtObject(bitem, 'bible') - second_bible = self._decodeQtObject(bitem, 'second_bible') - old_book = self._decodeQtObject(old_bitem, 'book') - old_chapter = int(self._decodeQtObject(old_bitem, 'chapter')) - old_verse = int(self._decodeQtObject(old_bitem, 'verse')) - old_bible = self._decodeQtObject(old_bitem, 'bible') - old_second_bible = self._decodeQtObject(old_bitem, 'second_bible') + book = self._decode_qt_object(bitem, 'book') + chapter = int(self._decode_qt_object(bitem, 'chapter')) + verse = int(self._decode_qt_object(bitem, 'verse')) + bible = self._decode_qt_object(bitem, 'bible') + second_bible = self._decode_qt_object(bitem, 'second_bible') + old_book = self._decode_qt_object(old_bitem, 'book') + old_chapter = int(self._decode_qt_object(old_bitem, 'chapter')) + old_verse = int(self._decode_qt_object(old_bitem, 'verse')) + old_bible = self._decode_qt_object(old_bitem, '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: # The bible, second bible or book has changed. return True diff --git a/openlp/plugins/bibles/lib/openlp1.py b/openlp/plugins/bibles/lib/openlp1.py deleted file mode 100644 index 5d96e6c26..000000000 --- a/openlp/plugins/bibles/lib/openlp1.py +++ /dev/null @@ -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 diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index 9b4b216d7..9a6c62aad 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -34,16 +34,18 @@ from openlp.core.lib import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB + log = logging.getLogger(__name__) + class OpenSongBible(BibleDB): """ OpenSong Bible format importer class. """ def __init__(self, parent, **kwargs): """ - Constructor to create and set up an instance of the OpenSongBible - class. This class is used to import Bibles from OpenSong's XML format. + Constructor to create and set up an instance of the OpenSongBible class. This class is used to import Bibles + from OpenSong's XML format. """ log.debug(self.__class__.__name__) BibleDB.__init__(self, parent, **kwargs) @@ -75,9 +77,8 @@ class OpenSongBible(BibleDB): file = None success = True try: - # NOTE: We don't need to do any of the normal encoding detection - # here, because lxml does it's own encoding detection, and the two - # mechanisms together interfere with each other. + # NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding + # detection, and the two mechanisms together interfere with each other. file = open(self.filename, u'r') opensong = objectify.parse(file) bible = opensong.getroot() @@ -116,16 +117,11 @@ class OpenSongBible(BibleDB): if len(verse_parts) > 1: number = int(verse_parts[0]) except TypeError: - log.warn(u'Illegal verse number: %s', - unicode(verse.attrib[u'n'])) + log.warn(u'Illegal verse number: %s', unicode(verse.attrib[u'n'])) verse_number = number else: verse_number += 1 - self.create_verse( - db_book.id, - chapter_number, - verse_number, - self.get_text(verse)) + self.create_verse(db_book.id, chapter_number, verse_number, self.get_text(verse)) self.wizard.increment_progress_bar(translate('BiblesPlugin.Opensong', 'Importing %s %s...', 'Importing ...')) % (db_book.name, chapter_number) self.session.commit() diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py index 981d10949..b91cd8319 100644 --- a/openlp/plugins/bibles/lib/osis.py +++ b/openlp/plugins/bibles/lib/osis.py @@ -39,9 +39,11 @@ from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB log = logging.getLogger(__name__) + def replacement(match): return match.group(2).upper() + class OSISBible(BibleDB): """ `OSIS `_ Bible format importer class. @@ -53,8 +55,7 @@ class OSISBible(BibleDB): BibleDB.__init__(self, parent, **kwargs) self.filename = kwargs[u'filename'] self.language_regex = re.compile(r'(.*?)') - self.verse_regex = re.compile( - r'(.*?)') + self.verse_regex = re.compile(r'(.*?)') self.note_regex = re.compile(r'(.*?)') self.title_regex = re.compile(r'(.*?)') self.milestone_regex = re.compile(r'') @@ -68,8 +69,7 @@ class OSISBible(BibleDB): self.q1_regex = re.compile(r'') self.q2_regex = re.compile(r'') self.trans_regex = re.compile(r'(.*?)') - self.divine_name_regex = re.compile( - r'(.*?)') + self.divine_name_regex = re.compile(r'(.*?)') self.spaces_regex = re.compile(r'([ ]{2,})') filepath = os.path.join( 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...', 'Importing ...') % (book_details[u'name'], chapter)) last_chapter = chapter - # All of this rigmarol below is because the mod2osis - # tool from the Sword library embeds XML in the OSIS - # but neglects to enclose the verse text (with XML) in - # <[CDATA[ ]]> tags. + # All of this rigmarol below is because the mod2osis tool from the Sword library embeds XML in the + # OSIS but neglects to enclose the verse text (with XML) in <[CDATA[ ]]> tags. verse_text = self.note_regex.sub(u'', verse_text) verse_text = self.title_regex.sub(u'', verse_text) verse_text = self.milestone_regex.sub(u'', verse_text) diff --git a/openlp/plugins/bibles/lib/upgrade.py b/openlp/plugins/bibles/lib/upgrade.py index b4a358c45..0c1a089b2 100644 --- a/openlp/plugins/bibles/lib/upgrade.py +++ b/openlp/plugins/bibles/lib/upgrade.py @@ -27,8 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`upgrade` module provides a way for the database and schema that is the -backend for the Bibles plugin +The :mod:`upgrade` module provides a way for the database and schema that is the backend for the Bibles plugin. """ import logging diff --git a/openlp/plugins/bibles/lib/versereferencelist.py b/openlp/plugins/bibles/lib/versereferencelist.py index 1cb76ffff..069b86d5e 100644 --- a/openlp/plugins/bibles/lib/versereferencelist.py +++ b/openlp/plugins/bibles/lib/versereferencelist.py @@ -29,8 +29,8 @@ class VerseReferenceList(object): """ - The VerseReferenceList class encapsulates a list of verse references, but - maintains the order in which they were added. + The VerseReferenceList class encapsulates a list of verse references, but maintains the order in which they were + added. """ def __init__(self): diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index c78baa974..580fbde07 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -257,6 +257,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): # We must have at least one slide. if self.slide_list_view.count() == 0: 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 True diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 071f68839..c2c67c304 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -40,6 +40,7 @@ from openlp.plugins.custom.lib.db import CustomSlide log = logging.getLogger(__name__) + class CustomSearch(object): """ An enumeration for custom search methods. @@ -63,14 +64,14 @@ class CustomMediaItem(MediaManagerItem): self.has_search = True # Holds information about whether the edit is remotely triggered and # which Custom is required. - self.remoteCustom = -1 + self.remote_custom = -1 self.manager = plugin.manager def add_end_header_bar(self): self.toolbar.addSeparator() self.add_search_to_toolbar() # 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)'), self.on_search_text_button_clicked) Registry().register_function(u'custom_load_list', self.load_list) @@ -119,14 +120,13 @@ class CustomMediaItem(MediaManagerItem): def on_new_click(self): self.edit_custom_form.load_custom(0) self.edit_custom_form.exec_() - self.onClearTextButtonClick() + self.on_clear_text_button_click() 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 - the custom Id in the payload along with an indicator to say which - type of display is required. + Called by ServiceManager or SlideController by event passing the custom Id in the payload along with an + indicator to say which type of display is required. """ custom_id = int(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) if self.edit_custom_form.exec_() == QtGui.QDialog.Accepted: self.remote_triggered = True - self.remoteCustom = custom_id + self.remote_custom = custom_id self.auto_select_id = -1 self.on_search_text_button_clicked() item = self.build_service_item(remote=True) self.remote_triggered = None - self.remoteCustom = 1 + self.remote_custom = 1 if item: return item return None @@ -163,22 +163,21 @@ class CustomMediaItem(MediaManagerItem): if check_item_selected(self.list_view, UiStrings().SelectDelete): items = self.list_view.selectedIndexes() if QtGui.QMessageBox.question(self, - UiStrings().ConfirmDelete, - translate('CustomPlugin.MediaItem', - 'Are you sure you want to delete the %n selected custom slide(s)?', '', - QtCore.QCoreApplication.CodecForTr, len(items)), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), - QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No: + UiStrings().ConfirmDelete, + translate('CustomPlugin.MediaItem', + 'Are you sure you want to delete the %n selected custom slide(s)?', '', + QtCore.QCoreApplication.CodecForTr, len(items)), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), + QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No: return row_list = [item.row() for item in self.list_view.selectedIndexes()] row_list.sort(reverse=True) - id_list = [(item.data(QtCore.Qt.UserRole)) - for item in self.list_view.selectedIndexes()] + id_list = [(item.data(QtCore.Qt.UserRole)) for item in self.list_view.selectedIndexes()] for id in id_list: self.plugin.manager.delete_object(CustomSlide, id) self.on_search_text_button_clicked() - def onFocus(self): + def on_focus(self): self.search_text_edit.setFocus() 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. """ - 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.CanPreview) service_item.add_capability(ItemCapabilities.CanLoop) service_item.add_capability(ItemCapabilities.CanSoftBreak) service_item.add_capability(ItemCapabilities.OnLoadUpdate) - customSlide = self.plugin.manager.get_object(CustomSlide, item_id) - title = customSlide.title - credit = customSlide.credits + custom_slide = self.plugin.manager.get_object(CustomSlide, item_id) + title = custom_slide.title + credit = custom_slide.credits service_item.edit_id = item_id - theme = customSlide.theme_name + theme = custom_slide.theme_name if theme: service_item.theme = theme - custom_xml = CustomXMLParser(customSlide.text) + custom_xml = CustomXMLParser(custom_slide.text) verse_list = custom_xml.get_verses() raw_slides = [verse[1] for verse in verse_list] 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()) # Reload the list considering the new search type. search_keywords = self.search_text_edit.displayText() - search_results = [] search_type = self.search_text_edit.current_search_type() if search_type == CustomSearch.Titles: log.debug(u'Titles Search') @@ -234,15 +232,14 @@ class CustomMediaItem(MediaManagerItem): def on_search_text_edit_changed(self, text): """ - If search as type enabled invoke the search on each key press. - If the Title is being searched do not start until 2 characters - have been entered. + If search as type enabled invoke the search on each key press. If the Title is being searched do not start until + 2 characters have been entered. """ search_length = 2 if len(text) > search_length: self.on_search_text_button_clicked() elif not text: - self.onClearTextButtonClick() + self.on_clear_text_button_click() def service_load(self, item): """ @@ -255,7 +252,8 @@ class CustomMediaItem(MediaManagerItem): and_(CustomSlide.title == item.title, CustomSlide.theme_name == item.theme, CustomSlide.credits == item.raw_footer[0][len(item.title) + 1:])) if custom: - self.service_manager.service_item_update(custom.id, item.unique_identifier) + item.edit_id = custom.id + return item else: if self.add_custom_from_service: self.create_from_service_item(item) @@ -284,10 +282,8 @@ class CustomMediaItem(MediaManagerItem): custom.text = unicode(custom_xml.extract_xml(), u'utf-8') self.plugin.manager.save_object(custom) 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. """ diff --git a/openlp/plugins/images/__init__.py b/openlp/plugins/images/__init__.py index 12e0cc9e4..9830af231 100644 --- a/openlp/plugins/images/__init__.py +++ b/openlp/plugins/images/__init__.py @@ -27,6 +27,6 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`images` module provides the Images plugin. The Images plugin -provides the facility to display images from OpenLP. +The :mod:`images` module provides the Images plugin. The Images plugin provides the facility to display images from +OpenLP. """ diff --git a/openlp/plugins/images/forms/__init__.py b/openlp/plugins/images/forms/__init__.py index d308a1471..8bb8e966f 100644 --- a/openlp/plugins/images/forms/__init__.py +++ b/openlp/plugins/images/forms/__init__.py @@ -27,20 +27,16 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -Forms in OpenLP are made up of two classes. One class holds all the graphical -elements, like buttons and lists, and the other class holds all the functional -code, like slots and loading and saving. +Forms in OpenLP are made up of two classes. One class holds all the graphical elements, like buttons and lists, and the +other class holds all the functional code, like slots and loading and saving. -The first class, commonly known as the **Dialog** class, is typically named -``Ui_Dialog``. It is a slightly modified version of the class that the -``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. +The first class, commonly known as the **Dialog** class, is typically named ``Ui_Dialog``. It is a slightly +modified version of the class that the ``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. -The second class, commonly known as the **Form** class, is typically named -``Form``. This class is the one which is instantiated and used. It uses -dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class -mentioned above, like so:: +The second class, commonly known as the **Form** class, is typically named ``Form``. This class is the one which +is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned +above, like so:: class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog): @@ -48,9 +44,8 @@ mentioned above, like so:: QtGui.QDialog.__init__(self, parent) self.setupUi(self) -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 -from the .ui files later if necessary. +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 from the .ui files later if necessary. """ from addgroupform import AddGroupForm diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index 7f7986499..4cdc6a73b 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -47,16 +47,16 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog): def exec_(self, clear=True, show_top_level_group=False, selected_group=None): """ - Show the form + Show the form. ``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`` - 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`` - 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: self.name_edit.clear() @@ -72,7 +72,7 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog): 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(): critical_error_message_box(message=translate('ImagePlugin.AddGroupForm', diff --git a/openlp/plugins/images/forms/choosegroupform.py b/openlp/plugins/images/forms/choosegroupform.py index bbb57255c..02f79e63b 100644 --- a/openlp/plugins/images/forms/choosegroupform.py +++ b/openlp/plugins/images/forms/choosegroupform.py @@ -48,10 +48,11 @@ class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog): Show the form ``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: - for i in range(self.group_combobox.count()): - if self.group_combobox.itemData(i) == selected_group: - self.group_combobox.setCurrentIndex(i) + for index in range(self.group_combobox.count()): + if self.group_combobox.itemData(index) == selected_group: + self.group_combobox.setCurrentIndex(index) return QtGui.QDialog.exec_(self) diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index cb25dc375..dfe927a7b 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -70,10 +70,10 @@ class ImagePlugin(Plugin): def app_startup(self): """ - Perform tasks on application startup + Perform tasks on application startup. """ 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) if 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): """ - Called to define all translatable texts of the plugin + Called to define all translatable texts of the plugin. """ ## Name PluginList ## self.text_strings[StringContent.Name] = { @@ -117,8 +117,8 @@ class ImagePlugin(Plugin): def config_update(self): """ - Triggered by saving and changing the image border. Sets the images in image manager to require updates. - Actual update is triggered by the last part of saving the config. + Triggered by saving and changing the image border. Sets the images in image manager to require updates. Actual + update is triggered by the last part of saving the config. """ log.info(u'Images config_update') background = QtGui.QColor(Settings().value(self.settings_section + u'/background color')) diff --git a/openlp/plugins/images/lib/db.py b/openlp/plugins/images/lib/db.py index bc4a94f15..1d8f473d8 100644 --- a/openlp/plugins/images/lib/db.py +++ b/openlp/plugins/images/lib/db.py @@ -27,7 +27,7 @@ # 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 @@ -38,14 +38,14 @@ from openlp.core.lib.db import BaseModel, init_db class ImageGroups(BaseModel): """ - ImageGroups model + ImageGroups model. """ pass class ImageFilenames(BaseModel): """ - ImageFilenames model + ImageFilenames model. """ pass diff --git a/openlp/plugins/images/lib/imagetab.py b/openlp/plugins/images/lib/imagetab.py index 83d015cb8..b43b19097 100644 --- a/openlp/plugins/images/lib/imagetab.py +++ b/openlp/plugins/images/lib/imagetab.py @@ -32,7 +32,6 @@ from PyQt4 import QtGui from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate - class ImageTab(SettingsTab): """ ImageTab is the images settings tab in the settings dialog. diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index fc575ec0a..deed14594 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -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.lib.db import ImageFilenames, ImageGroups + 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.add_group_form.parent_group_combobox) 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() def retranslateUi(self): - self.on_new_prompt = translate('ImagePlugin.MediaItem', - 'Select Image(s)') + self.on_new_prompt = translate('ImagePlugin.MediaItem', 'Select Image(s)') file_formats = get_images_filter() self.on_new_file_masks = u'%s;;%s (*.*) (*)' % (file_formats, UiStrings().AllFiles) self.addGroupAction.setText(UiStrings().AddGroup) self.addGroupAction.setToolTip(UiStrings().AddGroup) - self.replaceAction.setText(UiStrings().ReplaceBG) - self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG) - self.resetAction.setText(UiStrings().ResetBG) - self.resetAction.setToolTip(UiStrings().ResetLiveBG) + self.replace_action.setText(UiStrings().ReplaceBG) + self.replace_action.setToolTip(UiStrings().ReplaceLiveBG) + self.reset_action.setText(UiStrings().ResetBG) + self.reset_action.setToolTip(UiStrings().ResetLiveBG) 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) 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') check_directory_exists(self.servicePath) # 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) def add_list_view_to_toolbar(self): """ - Creates the main widget for listing items the media item is tracking. - This method overloads MediaManagerItem.add_list_view_to_toolbar + Creates the main widget for listing items the media item is tracking. This method overloads + MediaManagerItem.add_list_view_to_toolbar. """ # Add the List widget 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.itemSelectionChanged.connect(self.on_selection_change) 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): """ - 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, - text=UiStrings().AddGroup, - icon=u':/images/image_new_group.png', - triggers=self.onAddGroupClick) + text=UiStrings().AddGroup, icon=u':/images/image_new_group.png', triggers=self.on_add_group_click) create_widget_action(self.list_view, text=self.plugin.get_string(StringContent.Load)[u'tooltip'], - icon=u':/general/general_open.png', - triggers=self.on_file_click) + icon=u':/general/general_open.png', triggers=self.on_file_click) 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', - 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): """ Add custom buttons to the end of the toolbar """ - self.replaceAction = self.toolbar.add_toolbar_action(u'replaceAction', - icon=u':/slides/slide_blank.png', triggers=self.onReplaceClick) - self.resetAction = self.toolbar.add_toolbar_action(u'resetAction', - icon=u':/system/system_close.png', visible=False, triggers=self.onResetClick) + self.replace_action = self.toolbar.add_toolbar_action(u'replace_action', + icon=u':/slides/slide_blank.png', triggers=self.on_replace_click) + self.reset_action = self.toolbar.add_toolbar_action(u'reset_action', + icon=u':/system/system_close.png', visible=False, triggers=self.on_reset_click) 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`` - 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) for image in images: @@ -205,7 +202,7 @@ class ImageMediaItem(MediaManagerItem): def on_delete_click(self): """ - Remove an image item from the list + Remove an image item from the list. """ # Turn off auto preview triggers. 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) elif isinstance(item_data, ImageGroups): if QtGui.QMessageBox.question(self.list_view.parent(), - translate('ImagePlugin.MediaItem', 'Remove group'), - translate('ImagePlugin.MediaItem', - 'Are you sure you want to remove "%s" and everything in it?') % item_data.group_name, - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | - QtGui.QMessageBox.No)) == QtGui.QMessageBox.Yes: + translate('ImagePlugin.MediaItem', 'Remove group'), + translate('ImagePlugin.MediaItem', + 'Are you sure you want to remove "%s" and everything in it?') % item_data.group_name, + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | + QtGui.QMessageBox.No)) == QtGui.QMessageBox.Yes: self.recursively_delete_group(item_data) self.manager.delete_object(ImageGroups, row_item.data(0, QtCore.Qt.UserRole).id) if item_data.parent_id == 0: @@ -246,13 +243,13 @@ class ImageMediaItem(MediaManagerItem): 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`` - The List object that contains all QTreeWidgetItems + The List object that contains all QTreeWidgetItems. ``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.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=''): """ - Recursively add groups to the combobox in the 'Add group' dialog + Recursively add groups to the combobox in the 'Add group' dialog. ``combobox`` - The QComboBox to add the options to + The QComboBox to add the options to. ``parent_group_id`` - The ID of the group that will be added + The ID of the group that will be added. ``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: combobox.clear() @@ -293,13 +290,13 @@ class ImageMediaItem(MediaManagerItem): def expand_group(self, group_id, root_item=None): """ - Expand groups in the widget recursively + Expand groups in the widget recursively. ``group_id`` - The ID of the group that will be expanded + The ID of the group that will be expanded. ``root_item`` - This option is only used for recursion purposes + This option is only used for recursion purposes. """ return_value = False if root_item is None: @@ -314,29 +311,29 @@ class ImageMediaItem(MediaManagerItem): return True 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. ``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`` - 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`` - 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: self.application.set_busy_cursor() self.main_window.display_progress_bar(len(images)) 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 = {} self.add_sub_groups(group_items, parent_group_id=0) if open_group is not None: 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. images.sort(key=lambda image_object: get_locale_key(os.path.split(unicode(image_object.filename))[1])) for imageFile in images: @@ -394,6 +391,7 @@ class ImageMediaItem(MediaManagerItem): ``initial_load`` When set to False, the busy cursor and progressbar will be shown while loading images """ + parent_group = None if target_group is None: # Find out if a group must be pre-selected preselect_group = None @@ -439,6 +437,8 @@ class ImageMediaItem(MediaManagerItem): parent_group.parent_id = 0 parent_group.group_name = self.choose_group_form.new_group_edit.text() 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: parent_group = target_group.data(0, QtCore.Qt.UserRole) if isinstance(parent_group, ImageFilenames): @@ -455,7 +455,7 @@ class ImageMediaItem(MediaManagerItem): self.main_window.display_progress_bar(len(images)) # Save the new images in the database 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) self.application.set_normal_cursor() @@ -482,7 +482,7 @@ class ImageMediaItem(MediaManagerItem): self.manager.save_object(imageFile) self.main_window.increment_progress_bar() 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): """ @@ -530,7 +530,7 @@ class ImageMediaItem(MediaManagerItem): image_items.sort(key=lambda item: get_locale_key(item.text(0))) 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): """ Generate the slide data. Needs to be implemented by the plugin. @@ -553,28 +553,25 @@ class ImageMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.CanAppend) # force a nonexistent theme service_item.theme = -1 - missing_items = [] missing_items_filenames = [] + images_filenames = [] # Expand groups to images for bitem in items: 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()): if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames): - items.append(bitem.child(index)) - items.remove(bitem) + images_filenames.append(bitem.child(index).data(0, QtCore.Qt.UserRole).filename) + 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 - if not items: + if not images_filenames: return False # Find missing files - for bitem in items: - filename = bitem.data(0, QtCore.Qt.UserRole).filename + for filename in images_filenames: if not os.path.exists(filename): - missing_items.append(bitem) missing_items_filenames.append(filename) - for item in missing_items: - items.remove(item) # We cannot continue, as all images do not exist. - if not items: + if not images_filenames: if not remote: critical_error_message_box( translate('ImagePlugin.MediaItem', 'Missing Image(s)'), @@ -582,15 +579,14 @@ class ImageMediaItem(MediaManagerItem): u'\n'.join(missing_items_filenames)) return False # 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', 'The following image(s) no longer exist: %s\n' '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: return False # Continue with the existing images. - for bitem in items: - filename = bitem.data(0, QtCore.Qt.UserRole).filename + for filename in images_filenames: name = os.path.split(filename)[1] service_item.add_from_image(filename, name, background) return True @@ -608,7 +604,7 @@ class ImageMediaItem(MediaManagerItem): else: return False - def onAddGroupClick(self): + def on_add_group_click(self): """ Called to add a new group """ @@ -629,7 +625,7 @@ class ImageMediaItem(MediaManagerItem): group_name=self.add_group_form.name_edit.text()) if not self.check_group_exists(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)) self.expand_group(new_group.id) self.fill_groups_combobox(self.choose_group_form.group_combobox) @@ -638,23 +634,22 @@ class ImageMediaItem(MediaManagerItem): critical_error_message_box( message=translate('ImagePlugin.AddGroupForm', 'Could not add the new group.')) else: - critical_error_message_box( - message=translate('ImagePlugin.AddGroupForm', 'This group already exists.')) + critical_error_message_box(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() 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. """ @@ -663,12 +658,12 @@ class ImageMediaItem(MediaManagerItem): background = QtGui.QColor(Settings().value(self.settings_section + u'/background color')) bitem = self.list_view.selectedItems()[0] 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 filename = bitem.data(0, QtCore.Qt.UserRole).filename if os.path.exists(filename): if self.live_controller.display.direct_image(filename, background): - self.resetAction.setVisible(True) + self.reset_action.setVisible(True) else: critical_error_message_box(UiStrings().LiveBGError, translate('ImagePlugin.MediaItem', 'There was no display item to amend.')) diff --git a/openlp/plugins/media/__init__.py b/openlp/plugins/media/__init__.py index deb4cbeac..2a2e9f5aa 100644 --- a/openlp/plugins/media/__init__.py +++ b/openlp/plugins/media/__init__.py @@ -27,8 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`media` module provides the Media plugin which allows OpenLP to -display videos. The media supported depends not only on the Python support -but also extensively on the codecs installed on the underlying operating system -being picked up and usable by Python. +The :mod:`media` module provides the Media plugin which allows OpenLP to display videos. The media supported depends not +only on the Python support but also extensively on the codecs installed on the underlying operating system being picked +up and usable by Python. """ diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 7d492bc69..30748a225 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -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.utils import AppLocation, get_locale_key + log = logging.getLogger(__name__) + CLAPPERBOARD = u':/media/slidecontroller_multimedia.png' -VIDEO = build_icon(QtGui.QImage(u':/media/media_video.png')) -AUDIO = build_icon(QtGui.QImage(u':/media/media_audio.png')) -DVDICON = build_icon(QtGui.QImage(u':/media/media_video.png')) -ERROR = build_icon(QtGui.QImage(u':/general/general_delete.png')) +VIDEO_ICON = build_icon(u':/media/media_video.png') +AUDIO_ICON = build_icon(u':/media/media_audio.png') +DVD_ICON = build_icon(u':/media/media_video.png') +ERROR_ICON = build_icon(u':/general/general_delete.png') + class MediaMediaItem(MediaManagerItem): """ @@ -79,12 +82,12 @@ class MediaMediaItem(MediaManagerItem): def retranslateUi(self): self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media') - self.replaceAction.setText(UiStrings().ReplaceBG) - self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG) - self.resetAction.setText(UiStrings().ResetBG) - self.resetAction.setToolTip(UiStrings().ResetLiveBG) + self.replace_action.setText(UiStrings().ReplaceBG) + self.replace_action.setToolTip(UiStrings().ReplaceLiveBG) + self.reset_action.setText(UiStrings().ResetBG) + self.reset_action.setToolTip(UiStrings().ResetLiveBG) 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() def required_icons(self): @@ -98,27 +101,28 @@ class MediaMediaItem(MediaManagerItem): def 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): # 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) - 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) - self.mediaWidget = QtGui.QWidget(self) - self.mediaWidget.setObjectName(u'mediaWidget') - self.displayLayout = QtGui.QFormLayout(self.mediaWidget) - self.displayLayout.setMargin(self.displayLayout.spacing()) - self.displayLayout.setObjectName(u'displayLayout') - self.displayTypeLabel = QtGui.QLabel(self.mediaWidget) - self.displayTypeLabel.setObjectName(u'displayTypeLabel') - self.displayTypeComboBox = create_horizontal_adjusting_combo_box(self.mediaWidget, u'displayTypeComboBox') - self.displayTypeLabel.setBuddy(self.displayTypeComboBox) - self.displayLayout.addRow(self.displayTypeLabel, self.displayTypeComboBox) - # Add the Media widget to the page layout - self.page_layout.addWidget(self.mediaWidget) - self.displayTypeComboBox.currentIndexChanged.connect(self.overridePlayerChanged) + self.media_widget = QtGui.QWidget(self) + self.media_widget.setObjectName(u'media_widget') + self.display_layout = QtGui.QFormLayout(self.media_widget) + self.display_layout.setMargin(self.display_layout.spacing()) + self.display_layout.setObjectName(u'display_layout') + self.display_type_label = QtGui.QLabel(self.media_widget) + self.display_type_label.setObjectName(u'display_type_label') + self.display_type_combo_box = create_horizontal_adjusting_combo_box( + self.media_widget, u'display_type_combo_box') + self.display_type_label.setBuddy(self.display_type_combo_box) + self.display_layout.addRow(self.display_type_label, self.display_type_combo_box) + # Add the Media widget to the page layout. + self.page_layout.addWidget(self.media_widget) + self.display_type_combo_box.currentIndexChanged.connect(self.overridePlayerChanged) def overridePlayerChanged(self, index): player = get_media_players()[0] @@ -132,13 +136,13 @@ class MediaMediaItem(MediaManagerItem): Called to reset the Live background with the media selected, """ self.media_controller.media_reset(self.live_controller) - self.resetAction.setVisible(False) + self.reset_action.setVisible(False) def video_background_replaced(self): """ Triggered by main display on change of serviceitem. """ - self.resetAction.setVisible(False) + self.reset_action.setVisible(False) def onReplaceClick(self): """ @@ -151,11 +155,11 @@ class MediaMediaItem(MediaManagerItem): if os.path.exists(filename): service_item = ServiceItem() service_item.title = u'webkit' - service_item.shortname = service_item.title + service_item.processor = u'webkit' (path, name) = os.path.split(filename) service_item.add_from_command(path, name,CLAPPERBOARD) if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True): - self.resetAction.setVisible(True) + self.reset_action.setVisible(True) else: critical_error_message_box(UiStrings().LiveBGError, translate('MediaPlugin.MediaItem', 'There was no display item to amend.')) @@ -164,7 +168,7 @@ class MediaMediaItem(MediaManagerItem): translate('MediaPlugin.MediaItem', '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): """ 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', 'The file %s no longer exists.') % filename) return False - service_item.title = self.displayTypeComboBox.currentText() - service_item.shortname = service_item.title (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) # Only get start and end times if going to a service if context == ServiceItemContext.Service: @@ -192,7 +196,6 @@ class MediaMediaItem(MediaManagerItem): return False service_item.add_capability(ItemCapabilities.CanAutoStartForLive) 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: service_item.will_auto_start = True # force a non-existent theme @@ -209,8 +212,7 @@ class MediaMediaItem(MediaManagerItem): def rebuild_players(self): """ - Rebuild the tab in the media manager when changes are made in - the settings + Rebuild the tab in the media manager when changes are made in the settings. """ self.populateDisplayTypes() self.on_new_file_masks = translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)') % ( @@ -222,29 +224,27 @@ class MediaMediaItem(MediaManagerItem): def populateDisplayTypes(self): """ - Load the combobox with the enabled media players, - allowing user to select a specific player if settings allow + Load the combobox with the enabled media players, allowing user to select a specific player if settings allow. """ - # block signals to avoid unnecessary overridePlayerChanged Signals - # while combo box creation - self.displayTypeComboBox.blockSignals(True) - self.displayTypeComboBox.clear() + # block signals to avoid unnecessary overridePlayerChanged Signals while combo box creation + self.display_type_combo_box.blockSignals(True) + self.display_type_combo_box.clear() usedPlayers, overridePlayer = get_media_players() media_players = self.media_controller.media_players currentIndex = 0 for player in usedPlayers: # 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: - currentIndex = len(self.displayTypeComboBox) - if self.displayTypeComboBox.count() > 1: - self.displayTypeComboBox.insertItem(0, self.automatic) - self.displayTypeComboBox.setCurrentIndex(currentIndex) + currentIndex = len(self.display_type_combo_box) + if self.display_type_combo_box.count() > 1: + self.display_type_combo_box.insertItem(0, self.automatic) + self.display_type_combo_box.setCurrentIndex(currentIndex) if overridePlayer: - self.mediaWidget.show() + self.media_widget.show() else: - self.mediaWidget.hide() - self.displayTypeComboBox.blockSignals(False) + self.media_widget.hide() + self.display_type_combo_box.blockSignals(False) def on_delete_click(self): """ @@ -259,42 +259,41 @@ class MediaMediaItem(MediaManagerItem): Settings().setValue(self.settings_section + u'/media files', self.get_file_list()) def load_list(self, media, target_group=None): - # Sort the media by its filename considering language specific - # characters. + # Sort the media by its filename considering language specific characters. media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1])) for track in media: track_info = QtCore.QFileInfo(track) if not os.path.exists(track): filename = os.path.split(unicode(track))[1] item_name = QtGui.QListWidgetItem(filename) - item_name.setIcon(ERROR) + item_name.setIcon(ERROR_ICON) item_name.setData(QtCore.Qt.UserRole, track) elif track_info.isFile(): filename = os.path.split(unicode(track))[1] item_name = QtGui.QListWidgetItem(filename) 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: - item_name.setIcon(VIDEO) + item_name.setIcon(VIDEO_ICON) item_name.setData(QtCore.Qt.UserRole, track) else: filename = os.path.split(unicode(track))[1] 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.setToolTip(track) 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.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1])) - ext = [] + extension = [] if type == MediaType.Audio: - ext = self.media_controller.audio_extensions_list + extension = self.media_controller.audio_extensions_list else: - ext = self.media_controller.video_extensions_list - ext = map(lambda x: x[1:], ext) - media = filter(lambda x: os.path.splitext(x)[1] in ext, media) + extension = self.media_controller.video_extensions_list + extension = map(lambda x: x[1:], extension) + media = filter(lambda x: os.path.splitext(x)[1] in extension, media) return media def search(self, string, showError): diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 790dce03c..38cd9bb69 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -34,12 +34,14 @@ from PyQt4 import QtCore from openlp.core.lib import Plugin, Registry, StringContent, Settings, build_icon, translate from openlp.plugins.media.lib import MediaMediaItem, MediaTab + log = logging.getLogger(__name__) + # Some settings starting with "media" are in core, because they are needed for core functionality. __default_settings__ = { - u'media/media auto start': QtCore.Qt.Unchecked, - u'media/media files': [] + u'media/media auto start': QtCore.Qt.Unchecked, + u'media/media files': [] } @@ -54,7 +56,7 @@ class MediaPlugin(Plugin): # passed with drag and drop messages self.dnd_id = u'Media' - def create_settings_Tab(self, parent): + def create_settings_tab(self, parent): """ Create the settings Tab """ @@ -94,7 +96,7 @@ class MediaPlugin(Plugin): def finalise(self): """ - Time to tidy up on exit + Time to tidy up on exit. """ log.info(u'Media Finalising') self.media_controller.finalise() @@ -102,19 +104,19 @@ class MediaPlugin(Plugin): 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() def get_display_javascript(self): """ - Add javascript functions to htmlbuilder + Add javascript functions to htmlbuilder. """ return self.media_controller.get_media_display_javascript() def get_display_html(self): """ - Add html code to htmlbuilder + Add html code to htmlbuilder. """ return self.media_controller.get_media_display_html() diff --git a/openlp/plugins/presentations/__init__.py b/openlp/plugins/presentations/__init__.py index f147ea524..d600d3793 100644 --- a/openlp/plugins/presentations/__init__.py +++ b/openlp/plugins/presentations/__init__.py @@ -27,6 +27,6 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`presentations` module provides the Presentations plugin which allows -OpenLP to show presentations from most popular presentation packages. +The :mod:`presentations` module provides the Presentations plugin which allows OpenLP to show presentations from most +popular presentation packages. """ diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index d4c933d33..d30c71078 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -62,13 +62,14 @@ from openlp.core.lib import ScreenList from openlp.core.utils import delete_file, get_uno_command, get_uno_instance from presentationcontroller import PresentationController, PresentationDocument + log = logging.getLogger(__name__) + class ImpressController(PresentationController): """ - Class to control interactions with Impress presentations. - It creates the runtime environment, loads and closes the presentation as - well as triggering the correct activities based on the users input + Class to control interactions with Impress presentations. It creates the runtime environment, loads and closes the + presentation as well as triggering the correct activities based on the users input. """ log.info(u'ImpressController loaded') @@ -79,14 +80,14 @@ class ImpressController(PresentationController): log.debug(u'Initialising') PresentationController.__init__(self, plugin, u'Impress', ImpressDocument) 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.desktop = None self.manager = None 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') if os.name == u'nt': @@ -96,9 +97,8 @@ class ImpressController(PresentationController): def start_process(self): """ - Loads a running version of OpenOffice in the background. - It is not displayed to the user but is available to the UNO interface - when required. + Loads a running version of OpenOffice in the background. It is not displayed to the user but is available to the + UNO interface when required. """ log.debug(u'start process Openoffice') if os.name == u'nt': @@ -113,8 +113,7 @@ class ImpressController(PresentationController): def get_uno_desktop(self): """ - On non-Windows platforms, use Uno. Get the OpenOffice desktop - which will be used to manage impress + On non-Windows platforms, use Uno. Get the OpenOffice desktop which will be used to manage impress. """ log.debug(u'get UNO Desktop Openoffice') uno_instance = None @@ -132,8 +131,7 @@ class ImpressController(PresentationController): loop += 1 try: self.manager = uno_instance.ServiceManager - log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext' - u' - Desktop') + log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext - Desktop') desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance) return desktop except: @@ -142,8 +140,7 @@ class ImpressController(PresentationController): def get_com_desktop(self): """ - On Windows platforms, use COM. Return the desktop object which - will be used to manage Impress + On Windows platforms, use COM. Return the desktop object which will be used to manage Impress. """ log.debug(u'get COM Desktop OpenOffice') if not self.manager: @@ -157,7 +154,7 @@ class ImpressController(PresentationController): 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') try: @@ -168,7 +165,7 @@ class ImpressController(PresentationController): 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') while self.docs: @@ -203,12 +200,12 @@ class ImpressController(PresentationController): 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): """ - Constructor, store information about the file and initialise + Constructor, store information about the file and initialise. """ log.debug(u'Init Presentation OpenOffice') PresentationDocument.__init__(self, controller, presentation) @@ -218,11 +215,9 @@ class ImpressDocument(PresentationDocument): def load_presentation(self): """ - Called when a presentation is added to the SlideController. - It builds the environment, starts communcations with the background - OpenOffice task started earlier. If OpenOffice is not present is is - started. Once the environment is available the presentation is loaded - and started. + Called when a presentation is added to the SlideController. It builds the environment, starts communcations with + the background OpenOffice task started earlier. If OpenOffice is not present is is started. Once the environment + is available the presentation is loaded and started. """ log.debug(u'Load Presentation OpenOffice') if os.name == u'nt': @@ -239,13 +234,12 @@ class ImpressDocument(PresentationDocument): self.desktop = desktop properties = [] if os.name != u'nt': - # Recent versions of Impress on Windows won't start the presentation - # if it starts as minimized. It seems OK on Linux though. + # Recent versions of Impress on Windows won't start the presentation if it starts as minimized. It seems OK + # on Linux though. properties.append(self.create_property(u'Minimized', True)) properties = tuple(properties) try: - self.document = desktop.loadComponentFromURL(url, u'_blank', - 0, properties) + self.document = desktop.loadComponentFromURL(url, u'_blank', 0, properties) except: log.warn(u'Failed to load presentation %s' % url) return False @@ -262,33 +256,33 @@ class ImpressDocument(PresentationDocument): def create_thumbnails(self): """ - Create thumbnail images for presentation + Create thumbnail images for presentation. """ log.debug(u'create thumbnails OpenOffice') if self.check_thumbnails(): return 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') else: - thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder()) - props = [] - props.append(self.create_property(u'FilterName', u'impress_png_Export')) - props = tuple(props) + thumb_dir_url = uno.systemPathToFileUrl(self.get_temp_folder()) + properties = [] + properties.append(self.create_property(u'FilterName', u'impress_png_Export')) + properties = tuple(properties) doc = self.document pages = doc.getDrawPages() if not pages: return if not os.path.isdir(self.get_temp_folder()): os.makedirs(self.get_temp_folder()) - for idx in range(pages.getCount()): - page = pages.getByIndex(idx) + for index in range(pages.getCount()): + page = pages.getByIndex(index) doc.getCurrentController().setCurrentPage(page) - urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1)) - path = os.path.join(self.get_temp_folder(), unicode(idx + 1) + u'.png') + url_path = u'%s/%s.png' % (thumb_dir_url, unicode(index + 1)) + path = os.path.join(self.get_temp_folder(), unicode(index + 1) + u'.png') try: - doc.storeToURL(urlpath, props) - self.convert_thumbnail(path, idx + 1) + doc.storeToURL(url_path, properties) + self.convert_thumbnail(path, index + 1) delete_file(path) except ErrorCodeIOException, exception: log.exception(u'ERROR! ErrorCodeIOException %d' % exception.ErrCode) @@ -297,23 +291,21 @@ class ImpressDocument(PresentationDocument): def create_property(self, name, value): """ - Create an OOo style property object which are passed into some - Uno methods + Create an OOo style property object which are passed into some Uno methods. """ log.debug(u'create property OpenOffice') 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: - prop = PropertyValue() - prop.Name = name - prop.Value = value - return prop + property_object = PropertyValue() + property_object.Name = name + property_object.Value = value + return property_object def close_presentation(self): """ - Close presentation and clean up objects - Triggered by new object being added to SlideController or OpenLP - being shutdown + Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being + shutdown. """ log.debug(u'close Presentation OpenOffice') if self.document: @@ -329,7 +321,7 @@ class ImpressDocument(PresentationDocument): def is_loaded(self): """ - Returns true if a presentation is loaded + Returns true if a presentation is loaded. """ log.debug(u'is loaded OpenOffice') if self.presentation is None or self.document is None: @@ -346,7 +338,7 @@ class ImpressDocument(PresentationDocument): 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') if not self.is_loaded(): @@ -355,21 +347,21 @@ class ImpressDocument(PresentationDocument): def unblank_screen(self): """ - Unblanks the screen + Unblanks the screen. """ log.debug(u'unblank screen OpenOffice') return self.control.resume() def blank_screen(self): """ - Blanks the screen + Blanks the screen. """ log.debug(u'blank screen OpenOffice') self.control.blankScreen(0) def is_blank(self): """ - Returns true if screen is blank + Returns true if screen is blank. """ log.debug(u'is blank OpenOffice') if self.control and self.control.isRunning(): @@ -379,7 +371,7 @@ class ImpressDocument(PresentationDocument): def stop_presentation(self): """ - Stop the presentation, remove from screen + Stop the presentation, remove from screen. """ log.debug(u'stop presentation OpenOffice') # deactivate should hide the screen according to docs, but doesn't @@ -389,18 +381,17 @@ class ImpressDocument(PresentationDocument): def start_presentation(self): """ - Start the presentation from the beginning + Start the presentation from the beginning. """ log.debug(u'start presentation OpenOffice') if self.control is None or not self.control.isRunning(): self.presentation.start() self.control = self.presentation.getController() - # start() returns before the Component is ready. - # Try for 15 seconds - i = 1 - while not self.control and i < 150: + # start() returns before the Component is ready. Try for 15 seconds. + sleep_count = 1 + while not self.control and sleep_count < 150: time.sleep(0.1) - i += 1 + sleep_count += 1 self.control = self.presentation.getController() else: self.control.activate() @@ -408,25 +399,25 @@ class ImpressDocument(PresentationDocument): 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 def get_slide_count(self): """ - Return the total number of slides + Return the total number of slides. """ return self.document.getDrawPages().getCount() def goto_slide(self, slideno): """ - Go to a specific slide (from 1) + Go to a specific slide (from 1). """ self.control.gotoSlideIndex(slideno-1) 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() self.control.gotoNextEffect() @@ -436,7 +427,7 @@ class ImpressDocument(PresentationDocument): def previous_step(self): """ - Triggers the previous slide on the running presentation + Triggers the previous slide on the running presentation. """ self.control.gotoPreviousSlide() @@ -470,8 +461,8 @@ class ImpressDocument(PresentationDocument): page = pages.getByIndex(slide_no - 1) if notes: page = page.getNotesPage() - for idx in range(page.getCount()): - shape = page.getByIndex(idx) + for index in range(page.getCount()): + shape = page.getByIndex(index) if shape.supportsService("com.sun.star.drawing.Text"): text += shape.getString() + '\n' return text diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 52dcd891f..fcfc495ed 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -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.plugins.presentations.lib import MessageListener + log = logging.getLogger(__name__) -ERROR = QtGui.QImage(u':/general/general_delete.png') + +ERROR_IMAGE = QtGui.QImage(u':/general/general_delete.png') + class PresentationMediaItem(MediaManagerItem): """ - This is the Presentation media manager item for Presentation Items. - It can present files using Openoffice and Powerpoint + This is the Presentation media manager item for Presentation Items. It can present files using Openoffice and + Powerpoint """ 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.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): """ - 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: if self.controllers[controller].enabled(): - types = self.controllers[controller].supports + self.controllers[controller].alsosupports - for type in types: - if fileType.find(type) == -1: - fileType += u'*.%s ' % type - self.service_manager.supported_suffixes(type) - self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % fileType + file_types = self.controllers[controller].supports + self.controllers[controller].also_supports + for file_type in file_types: + if file_type.find(file_type) == -1: + file_type += u'*.%s ' % file_type + self.service_manager.supported_suffixes(file_type) + self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % file_type 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) self.has_file_icon = True @@ -98,21 +101,21 @@ class PresentationMediaItem(MediaManagerItem): 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.presentationWidget.setObjectName(u'presentationWidget') - self.displayLayout = QtGui.QFormLayout(self.presentationWidget) - self.displayLayout.setMargin(self.displayLayout.spacing()) - self.displayLayout.setObjectName(u'displayLayout') - self.displayTypeLabel = QtGui.QLabel(self.presentationWidget) - self.displayTypeLabel.setObjectName(u'displayTypeLabel') - self.displayTypeComboBox = create_horizontal_adjusting_combo_box(self.presentationWidget, - u'displayTypeComboBox') - self.displayTypeLabel.setBuddy(self.displayTypeComboBox) - self.displayLayout.addRow(self.displayTypeLabel, self.displayTypeComboBox) - # Add the Presentation widget to the page layout - self.page_layout.addWidget(self.presentationWidget) + self.presentation_widget = QtGui.QWidget(self) + self.presentation_widget.setObjectName(u'presentation_widget') + self.display_layout = QtGui.QFormLayout(self.presentation_widget) + self.display_layout.setMargin(self.display_layout.spacing()) + self.display_layout.setObjectName(u'display_layout') + self.display_type_label = QtGui.QLabel(self.presentation_widget) + self.display_type_label.setObjectName(u'display_type_label') + self.display_type_combo_box = create_horizontal_adjusting_combo_box(self.presentation_widget, + u'display_type_combo_box') + self.display_type_label.setBuddy(self.display_type_combo_box) + self.display_layout.addRow(self.display_type_label, self.display_type_combo_box) + # Add the Presentation widget to the page layout. + self.page_layout.addWidget(self.presentation_widget) def initialise(self): """ @@ -120,55 +123,54 @@ class PresentationMediaItem(MediaManagerItem): """ self.list_view.setIconSize(QtCore.QSize(88, 50)) 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() def populate_display_types(self): """ - Load the combobox with the enabled presentation controllers, - allowing user to select a specific app if settings allow + Load the combobox with the enabled presentation controllers, allowing user to select a specific app if settings + allow. """ - self.displayTypeComboBox.clear() + self.display_type_combo_box.clear() for item in self.controllers: # load the drop down selection if self.controllers[item].enabled(): - self.displayTypeComboBox.addItem(item) - if self.displayTypeComboBox.count() > 1: - self.displayTypeComboBox.insertItem(0, self.Automatic) - self.displayTypeComboBox.setCurrentIndex(0) + self.display_type_combo_box.addItem(item) + if self.display_type_combo_box.count() > 1: + self.display_type_combo_box.insertItem(0, self.Automatic) + self.display_type_combo_box.setCurrentIndex(0) if Settings().value(self.settings_section + u'/override app') == QtCore.Qt.Checked: - self.presentationWidget.show() + self.presentation_widget.show() 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 - 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 + Add presentations into the media manager. 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. """ - currlist = self.get_file_list() - titles = [os.path.split(file)[1] for file in currlist] + current_list = self.get_file_list() + titles = [os.path.split(file)[1] for file in current_list] self.application.set_busy_cursor() - if not initialLoad: + if not initial_load: self.main_window.display_progress_bar(len(files)) # Sort the presentations by its filename considering language specific characters. files.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1])) for file in files: - if not initialLoad: + if not initial_load: self.main_window.increment_progress_bar() - if currlist.count(file) > 0: + if current_list.count(file) > 0: continue filename = os.path.split(unicode(file))[1] if not os.path.exists(file): 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.setToolTip(file) self.list_view.addItem(item_name) else: if titles.count(filename) > 0: - if not initialLoad: + if not initial_load: critical_error_message_box(translate('PresentationPlugin.MediaItem', 'File Exists'), translate('PresentationPlugin.MediaItem', 'A presentation with that filename already exists.') @@ -180,7 +182,7 @@ class PresentationMediaItem(MediaManagerItem): doc = controller.add_document(unicode(file)) thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png') preview = doc.get_thumbnail_path(1, True) - if not preview and not initialLoad: + if not preview and not initial_load: doc.load_presentation() preview = doc.get_thumbnail_path(1, True) doc.close_presentation() @@ -192,7 +194,7 @@ class PresentationMediaItem(MediaManagerItem): else: icon = create_thumb(preview, thumb) else: - if initialLoad: + if initial_load: icon = build_icon(u':/general/general_delete.png') else: critical_error_message_box(UiStrings().UnsupportedFile, @@ -203,13 +205,13 @@ class PresentationMediaItem(MediaManagerItem): item_name.setIcon(icon) item_name.setToolTip(file) self.list_view.addItem(item_name) - if not initialLoad: + if not initial_load: self.main_window.finished_progress_bar() self.application.set_normal_cursor() 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): items = self.list_view.selectedIndexes() @@ -230,12 +232,11 @@ class PresentationMediaItem(MediaManagerItem): self.list_view.takeItem(row) 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): """ - Load the relevant information for displaying the presentation - in the slidecontroller. In the case of powerpoints, an image - for each slide + Load the relevant information for displaying the presentation in the slidecontroller. In the case of + powerpoints, an image for each slide. """ if item: items = [item] @@ -243,22 +244,20 @@ class PresentationMediaItem(MediaManagerItem): items = self.list_view.selectedItems() if len(items) > 1: return False - service_item.title = self.displayTypeComboBox.currentText() - service_item.shortname = self.displayTypeComboBox.currentText() + service_item.processor = self.display_type_combo_box.currentText() service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) - service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay) - shortname = service_item.shortname - if not shortname: + if not self.display_type_combo_box.currentText(): return False for bitem in items: filename = bitem.data(QtCore.Qt.UserRole) + (path, name) = os.path.split(filename) + service_item.title = name if os.path.exists(filename): - if shortname == self.Automatic: - service_item.shortname = self.findControllerByType(filename) - if not service_item.shortname: + if service_item.processor == self.Automatic: + service_item.processor = self.findControllerByType(filename) + if not service_item.processor: return False - controller = self.controllers[service_item.shortname] - (path, name) = os.path.split(filename) + controller = self.controllers[service_item.processor] doc = controller.add_document(filename) if doc.get_thumbnail_path(1, True) is None: doc.load_presentation() @@ -287,26 +286,24 @@ class PresentationMediaItem(MediaManagerItem): def findControllerByType(self, filename): """ - Determine the default application controller to use for the selected - file type. This is used if "Automatic" is set as the preferred - controller. Find the first (alphabetic) enabled controller which - "supports" the extension. If none found, then look for a controller - which "also supports" it instead. + Determine the default application controller to use for the selected file type. This is used if "Automatic" is + set as the preferred controller. Find the first (alphabetic) enabled controller which "supports" the extension. + If none found, then look for a controller which "also supports" it instead. """ - filetype = os.path.splitext(filename)[1][1:] - if not filetype: + file_type = os.path.splitext(filename)[1][1:] + if not file_type: return None for controller in self.controllers: if self.controllers[controller].enabled(): - if filetype in self.controllers[controller].supports: + if file_type in self.controllers[controller].supports: return controller for controller in self.controllers: if self.controllers[controller].enabled(): - if filetype in self.controllers[controller].alsosupports: + if file_type in self.controllers[controller].also_supports: return controller return None - def search(self, string, showError): + def search(self, string, show_error): files = Settings().value(self.settings_section + u'/presentations files') results = [] string = string.lower() diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index d87e7e5dc..cd7c654a2 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -36,10 +36,11 @@ from openlp.core.ui import HideMode log = logging.getLogger(__name__) + class Controller(object): """ - This is the Presentation listener who acts on events from the slide - controller and passes the messages on the the correct presentation handlers + This is the Presentation listener who acts on events from the slide controller and passes the messages on the the + correct presentation handlers. """ log.info(u'Controller loaded') @@ -54,9 +55,8 @@ class Controller(object): def add_handler(self, controller, file, hide_mode, slide_no): """ - Add a handler, which is an instance of a presentation and - slidecontroller combination. If the slidecontroller has a display - then load the presentation. + Add a handler, which is an instance of a presentation and slidecontroller combination. If the slidecontroller + has a display then load the presentation. """ log.debug(u'Live = %s, add_handler %s' % (self.is_live, file)) self.controller = controller @@ -86,8 +86,7 @@ class Controller(object): def activate(self): """ - Active the presentation, and show it on the screen. - Use the last slide number. + Active the presentation, and show it on the screen. Use the last slide number. """ log.debug(u'Live = %s, activate' % self.is_live) if not self.doc: @@ -130,7 +129,7 @@ class Controller(object): 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) if not self.doc: @@ -148,7 +147,7 @@ class Controller(object): 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) if not self.doc: @@ -166,7 +165,7 @@ class Controller(object): 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) if not self.doc: @@ -182,9 +181,8 @@ class Controller(object): return if not self.activate(): return - # The "End of slideshow" screen is after the last slide - # Note, we can't just stop on the last slide, since it may - # contain animations that need to be stepped through. + # The "End of slideshow" screen is after the last slide. Note, we can't just stop on the last slide, since it + # may contain animations that need to be stepped through. if self.doc.slidenumber > self.doc.get_slide_count(): return self.doc.next_step() @@ -192,7 +190,7 @@ class Controller(object): 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) if not self.doc: @@ -213,7 +211,7 @@ class Controller(object): 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) if not self.doc: @@ -223,7 +221,7 @@ class Controller(object): 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) self.hide_mode = hide_mode @@ -244,7 +242,7 @@ class Controller(object): 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) self.hide_mode = HideMode.Screen @@ -260,7 +258,7 @@ class Controller(object): 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) self.hide_mode = None @@ -283,8 +281,8 @@ class Controller(object): class MessageListener(object): """ - This is the Presentation listener who acts on events from the slide - controller and passes the messages on the the correct presentation handlers + This is the Presentation listener who acts on events from the slide controller and passes the messages on the the + correct presentation handlers """ log.info(u'Message Listener loaded') @@ -310,15 +308,14 @@ class MessageListener(object): def startup(self, message): """ - Start of new presentation - Save the handler as any new presentations start here + Start of new presentation. Save the handler as any new presentations start here """ + log.debug(u'Startup called with message %s' % message) is_live = message[1] item = message[0] - log.debug(u'Startup called with message %s' % message) hide_mode = message[2] file = item.get_frame_path() - self.handler = item.title + self.handler = item.processor if self.handler == self.media_item.Automatic: self.handler = self.media_item.findControllerByType(file) if not self.handler: @@ -331,7 +328,7 @@ class MessageListener(object): 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] slide = message[2] @@ -342,7 +339,7 @@ class MessageListener(object): 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] if is_live: @@ -352,7 +349,7 @@ class MessageListener(object): 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] if is_live: @@ -362,7 +359,7 @@ class MessageListener(object): 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] if is_live: @@ -372,7 +369,7 @@ class MessageListener(object): 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] if is_live: @@ -382,8 +379,7 @@ class MessageListener(object): def shutdown(self, message): """ - React to message to shutdown the presentation. I.e. end the show - and close the file + React to message to shutdown the presentation. I.e. end the show and close the file. """ is_live = message[1] if is_live: @@ -393,7 +389,7 @@ class MessageListener(object): def hide(self, message): """ - React to the message to show the desktop + React to the message to show the desktop. """ is_live = message[1] if is_live: @@ -401,7 +397,7 @@ class MessageListener(object): def blank(self, message): """ - React to the message to blank the display + React to the message to blank the display. """ is_live = message[1] hide_mode = message[2] @@ -410,7 +406,7 @@ class MessageListener(object): def unblank(self, message): """ - React to the message to unblank the display + React to the message to unblank the display. """ is_live = message[1] if is_live: @@ -418,9 +414,7 @@ class MessageListener(object): def timeout(self): """ - The presentation may be timed or might be controlled by the - application directly, rather than through OpenLP. Poll occasionally - to check which slide is currently displayed so the slidecontroller - view can be updated + The presentation may be timed or might be controlled by the application directly, rather than through OpenLP. + Poll occasionally to check which slide is currently displayed so the slidecontroller view can be updated. """ self.live_handler.poll() diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 7a9f548ee..6895fda19 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -26,7 +26,10 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # 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 logging @@ -39,16 +42,14 @@ if os.name == u'nt': from openlp.core.lib import ScreenList from presentationcontroller import PresentationController, PresentationDocument + log = logging.getLogger(__name__) -# PPT API documentation: -# http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx class PowerpointController(PresentationController): """ - Class to control interactions with PowerPoint Presentations - It creates the runtime Environment , Loads the and Closes the Presentation - As well as triggering the correct activities based on the users input + Class to control interactions with PowerPoint Presentations. It creates the runtime Environment , Loads the and + Closes the Presentation. As well as triggering the correct activities based on the users input. """ log.info(u'PowerpointController loaded') @@ -63,7 +64,7 @@ class PowerpointController(PresentationController): 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') if os.name == u'nt': @@ -77,7 +78,7 @@ class PowerpointController(PresentationController): if os.name == u'nt': def start_process(self): """ - Loads PowerPoint process + Loads PowerPoint process. """ log.debug(u'start_process') if not self.process: @@ -87,7 +88,7 @@ class PowerpointController(PresentationController): 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') while self.docs: @@ -105,12 +106,12 @@ class PowerpointController(PresentationController): 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): """ - Constructor, store information about the file and initialise + Constructor, store information about the file and initialise. """ log.debug(u'Init Presentation Powerpoint') PresentationDocument.__init__(self, controller, presentation) @@ -118,8 +119,8 @@ class PowerpointDocument(PresentationDocument): def load_presentation(self): """ - Called when a presentation is added to the SlideController. - Opens the PowerPoint file using the process created earlier. + Called when a presentation is added to the SlideController. Opens the PowerPoint file using the process created + earlier. """ log.debug(u'load_presentation') if not self.controller.process or not self.controller.process.Visible: @@ -142,20 +143,19 @@ class PowerpointDocument(PresentationDocument): self.presentation.Slides[n].Copy() thumbnail = QApplication.clipboard.image() - However, for the moment, we want a physical file since it makes life - easier elsewhere. + However, for the moment, we want a physical file since it makes life easier elsewhere. """ log.debug(u'create_thumbnails') if self.check_thumbnails(): return for num in range(self.presentation.Slides.Count): - self.presentation.Slides(num + 1).Export(os.path.join( - self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), 'png', 320, 240) + self.presentation.Slides(num + 1).Export( + os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), 'png', 320, 240) def close_presentation(self): """ - Close presentation and clean up objects. This is triggered by a new - object being added to SlideController or OpenLP being shut down. + Close presentation and clean up objects. This is triggered by a new object being added to SlideController or + OpenLP being shut down. """ log.debug(u'ClosePresentation') if self.presentation: @@ -182,7 +182,6 @@ class PowerpointDocument(PresentationDocument): return False return True - def is_active(self): """ Returns ``True`` if a presentation is currently active. @@ -253,15 +252,14 @@ class PowerpointDocument(PresentationDocument): dpi = win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88) except win32ui.error: dpi = 96 - rect = ScreenList().current[u'size'] + size = ScreenList().current[u'size'] ppt_window = self.presentation.SlideShowSettings.Run() if not ppt_window: return - ppt_window.Top = rect.y() * 72 / dpi - ppt_window.Height = rect.height() * 72 / dpi - ppt_window.Left = rect.x() * 72 / dpi - ppt_window.Width = rect.width() * 72 / dpi - + ppt_window.Top = size.y() * 72 / dpi + ppt_window.Height = size.height() * 72 / dpi + ppt_window.Left = size.x() * 72 / dpi + ppt_window.Width = size.width() * 72 / dpi def get_slide_number(self): """ @@ -318,6 +316,7 @@ class PowerpointDocument(PresentationDocument): """ return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes) + def _get_text_from_shapes(shapes): """ 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. """ text = '' - for idx in range(shapes.Count): - shape = shapes(idx + 1) + for index in range(shapes.Count): + shape = shapes(index + 1) if shape.HasTextFrame: text += shape.TextFrame.TextRange.Text + '\n' return text diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index a2dc56f52..abb9fd11e 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -37,13 +37,14 @@ if os.name == u'nt': from openlp.core.lib import ScreenList from presentationcontroller import PresentationController, PresentationDocument + log = logging.getLogger(__name__) + class PptviewController(PresentationController): """ - Class to control interactions with PowerPoint Viewer Presentations - It creates the runtime Environment , Loads the and Closes the Presentation - As well as triggering the correct activities based on the users input + Class to control interactions with PowerPoint Viewer Presentations. It creates the runtime Environment , Loads the + and Closes the Presentation. As well as triggering the correct activities based on the users input """ log.info(u'PPTViewController loaded') @@ -58,7 +59,7 @@ class PptviewController(PresentationController): 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') if os.name != u'nt': @@ -68,7 +69,7 @@ class PptviewController(PresentationController): if os.name == u'nt': def check_installed(self): """ - Check the viewer is installed + Check the viewer is installed. """ log.debug(u'Check installed') try: @@ -79,14 +80,14 @@ class PptviewController(PresentationController): def start_process(self): """ - Loads the PPTVIEWLIB library + Loads the PPTVIEWLIB library. """ if self.process: return log.debug(u'start PPTView') - dllpath = os.path.join(self.plugin_manager.base_path, u'presentations', u'lib', u'pptviewlib', - u'pptviewlib.dll') - self.process = cdll.LoadLibrary(dllpath) + dll_path = os.path.join( + self.plugin_manager.base_path, u'presentations', u'lib', u'pptviewlib', u'pptviewlib.dll') + self.process = cdll.LoadLibrary(dll_path) if log.isEnabledFor(logging.DEBUG): self.process.SetDebug(1) @@ -101,33 +102,32 @@ class PptviewController(PresentationController): 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): """ - Constructor, store information about the file and initialise + Constructor, store information about the file and initialise. """ log.debug(u'Init Presentation PowerPoint') PresentationDocument.__init__(self, controller, presentation) self.presentation = None - self.pptid = None + self.ppt_id = None self.blanked = False self.hidden = False def load_presentation(self): """ - Called when a presentation is added to the SlideController. - It builds the environment, starts communication with the background - PptView task started earlier. + Called when a presentation is added to the SlideController. It builds the environment, starts communication with + the background PptView task started earlier. """ log.debug(u'LoadPresentation') - rect = ScreenList().current[u'size'] - rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom()) + size = ScreenList().current[u'size'] + rect = RECT(size.x(), size.y(), size.right(), size.bottom()) filepath = str(self.filepath.replace(u'/', u'\\')) if not os.path.isdir(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') - if self.pptid >= 0: + self.ppt_id = self.controller.process.OpenPPT(filepath, None, rect, str(self.get_temp_folder()) + '\\slide') + if self.ppt_id >= 0: self.create_thumbnails() self.stop_presentation() return True @@ -136,8 +136,7 @@ class PptviewDocument(PresentationDocument): def create_thumbnails(self): """ - PPTviewLib creates large BMP's, but we want small PNG's for consistency. - Convert them here. + PPTviewLib creates large BMP's, but we want small PNG's for consistency. Convert them here. """ log.debug(u'create_thumbnails') if self.check_thumbnails(): @@ -149,21 +148,20 @@ class PptviewDocument(PresentationDocument): def close_presentation(self): """ - Close presentation and clean up objects - Triggered by new object being added to SlideController orOpenLP - being shut down + Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being + shut down. """ log.debug(u'ClosePresentation') if self.controller.process: - self.controller.process.ClosePPT(self.pptid) - self.pptid = -1 + self.controller.process.ClosePPT(self.ppt_id) + self.ppt_id = -1 self.controller.remove_doc(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 if self.get_slide_count() < 0: return False @@ -171,74 +169,74 @@ class PptviewDocument(PresentationDocument): 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 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 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 def is_blank(self): """ - Returns true if screen is blank + Returns true if screen is blank. """ log.debug(u'is blank OpenOffice') return self.blanked def stop_presentation(self): """ - Stops the current presentation and hides the output + Stops the current presentation and hides the output. """ self.hidden = True - self.controller.process.Stop(self.pptid) + self.controller.process.Stop(self.ppt_id) def start_presentation(self): """ - Starts a presentation from the beginning + Starts a presentation from the beginning. """ if self.hidden: self.hidden = False - self.controller.process.Resume(self.pptid) + self.controller.process.Resume(self.ppt_id) else: - self.controller.process.RestartShow(self.pptid) + self.controller.process.RestartShow(self.ppt_id) 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): """ - 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): """ - 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): """ - 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): """ - 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) diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 48955ebb2..85ebc42ad 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -38,11 +38,11 @@ from openlp.core.utils import AppLocation log = logging.getLogger(__name__) + class PresentationDocument(object): """ - Base class for presentation documents to inherit from. - Loads and closes the presentation as well as triggering the correct - activities based on the users input + Base class for presentation documents to inherit from. Loads and closes the presentation as well as triggering the + correct activities based on the users input **Hook Functions** @@ -131,20 +131,17 @@ class PresentationDocument(object): """ The location where thumbnail images will be stored """ - return os.path.join( - self.controller.thumbnail_folder, self.get_file_name()) + return os.path.join(self.controller.thumbnail_folder, self.get_file_name()) def get_temp_folder(self): """ The location where thumbnail images will be stored """ - return os.path.join( - self.controller.temp_folder, self.get_file_name()) + return os.path.join(self.controller.temp_folder, self.get_file_name()) def check_thumbnails(self): """ - Returns ``True`` if the thumbnail images exist and are more recent than - the powerpoint file. + Returns ``True`` if the thumbnail images exist and are more recent than the powerpoint file. """ lastimage = self.get_thumbnail_path(self.get_slide_count(), True) if not (lastimage and os.path.isfile(lastimage)): @@ -153,8 +150,7 @@ class PresentationDocument(object): def close_presentation(self): """ - Close presentation and clean up objects - Triggered by new object being added to SlideController + Close presentation and clean up objects. Triggered by new object being added to SlideController """ self.controller.close_presentation() @@ -223,8 +219,8 @@ class PresentationDocument(object): def next_step(self): """ - Triggers the next effect of slide on the running presentation - This might be the next animation on the current slide, or the next slide + Triggers the next effect of slide on the running presentation. This might be the next animation on the current + slide, or the next slide """ pass @@ -236,8 +232,7 @@ class PresentationDocument(object): def convert_thumbnail(self, file, idx): """ - Convert the slide image the application made to a standard 320x240 - .png image. + Convert the slide image the application made to a standard 320x240 .png image. """ if self.check_thumbnails(): return @@ -281,7 +276,7 @@ class PresentationDocument(object): Returns the text on the slide ``slide_no`` - The slide the text is required for, starting at 1 + The slide the text is required for, starting at 1 """ return '' @@ -290,24 +285,21 @@ class PresentationDocument(object): Returns the text on the slide ``slide_no`` - The slide the notes are required for, starting at 1 + The slide the notes are required for, starting at 1 """ return '' class PresentationController(object): """ - This class is used to control interactions with presentation applications - by creating a runtime environment. This is a base class for presentation - controllers to inherit from. + This class is used to control interactions with presentation applications by creating a runtime environment. This is + a base class for presentation controllers to inherit from. - To create a new controller, take a copy of this file and name it so it ends - with ``controller.py``, i.e. ``foobarcontroller.py``. Make sure it inherits - :class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`, - and then fill in the blanks. If possible try to make sure it loads on all - platforms, usually by using :mod:``os.name`` checks, although - ``__init__``, ``check_available`` and ``presentation_deleted`` should - always be implemented. + To create a new controller, take a copy of this file and name it so it ends with ``controller.py``, i.e. + ``foobarcontroller.py``. Make sure it inherits + :class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`, and then fill in the + blanks. If possible try to make sure it loads on all platforms, usually by using :mod:``os.name`` checks, although + ``__init__``, ``check_available`` and ``presentation_deleted`` should always be implemented. See :class:`~openlp.plugins.presentations.lib.impresscontroller.ImpressController`, :class:`~openlp.plugins.presentations.lib.powerpointcontroller.PowerpointController` or @@ -317,36 +309,34 @@ class PresentationController(object): **Basic Attributes** ``name`` - The name that appears in the options and the media manager + The name that appears in the options and the media manager. ``enabled`` - The controller is enabled + The controller is enabled. ``available`` - The controller is available on this machine. Set by init via - call to check_available + The controller is available on this machine. Set by init via call to check_available. ``plugin`` - The presentationplugin object + The presentationplugin object. ``supports`` - The primary native file types this application supports + The primary native file types this application supports. - ``alsosupports`` - Other file types the application can import, although not necessarily - the first choice due to potential incompatibilities + ``also_supports`` + Other file types the application can import, although not necessarily the first choice due to potential + incompatibilities. **Hook Functions** ``kill()`` - Called at system exit to clean up any running presentations + Called at system exit to clean up any running presentations. ``check_available()`` - Returns True if presentation application is installed/can run on this - machine + Returns True if presentation application is installed/can run on this machine. ``presentation_deleted()`` - Deletes presentation specific files, e.g. thumbnails + Deletes presentation specific files, e.g. thumbnails. """ log.info(u'PresentationController loaded') @@ -354,9 +344,8 @@ class PresentationController(object): def __init__(self, plugin=None, name=u'PresentationController', document_class=PresentationDocument): """ - This is the constructor for the presentationcontroller object. This - provides an easy way for descendent plugins to populate common data. - This method *must* be overridden, like so:: + This is the constructor for the presentationcontroller object. This provides an easy way for descendent plugins + to populate common data. This method *must* be overridden, like so:: class MyPresentationController(PresentationController): def __init__(self, plugin): @@ -370,7 +359,7 @@ class PresentationController(object): Name of the application, to appear in the application """ self.supports = [] - self.alsosupports = [] + self.also_supports = [] self.docs = [] self.plugin = plugin self.name = name @@ -399,28 +388,26 @@ class PresentationController(object): def check_available(self): """ - Presentation app is able to run on this machine + Presentation app is able to run on this machine. """ return False def start_process(self): """ - Loads a running version of the presentation application in the - background. + Loads a running version of the presentation application in the background. """ pass def kill(self): """ - Called at system exit to clean up any running presentations and - close the application + Called at system exit to clean up any running presentations and close the application. """ log.debug(u'Kill') self.close_presentation() 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) self.docs.append(document) @@ -428,7 +415,7 @@ class PresentationController(object): 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') if doc is None: diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index cecec53b5..e46467403 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -91,8 +91,7 @@ class PresentationTab(SettingsTab): if checkbox.isEnabled(): checkbox.setText(controller.name) else: - checkbox.setText( - translate('PresentationPlugin.PresentationTab', '%s (unavailable)') % controller.name) + checkbox.setText(translate('PresentationPlugin.PresentationTab', '%s (unavailable)') % controller.name) def load(self): """ @@ -106,8 +105,8 @@ class PresentationTab(SettingsTab): def save(self): """ - Save the settings. If the tab hasn't been made visible to the user then there is nothing to do, - so exit. This removes the need to start presentation applications unnecessarily. + Save the settings. If the tab hasn't been made visible to the user then there is nothing to do, so exit. This + removes the need to start presentation applications unnecessarily. """ if not self.activated: return diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 7872c25b7..cc1516b69 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -27,8 +27,8 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`presentationplugin` module provides the ability for OpenLP to display -presentations from a variety of document formats. +The :mod:`presentationplugin` module provides the ability for OpenLP to display presentations from a variety of document +formats. """ import os import logging @@ -39,22 +39,23 @@ from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.utils import AppLocation from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab + log = logging.getLogger(__name__) + __default_settings__ = { u'presentations/override app': QtCore.Qt.Unchecked, u'presentations/Impress': QtCore.Qt.Checked, u'presentations/Powerpoint': QtCore.Qt.Checked, u'presentations/Powerpoint Viewer': QtCore.Qt.Checked, u'presentations/presentations files': [] - } +} class PresentationPlugin(Plugin): """ - This plugin allowed a Presentation to be opened, controlled and displayed - on the output display. The plugin controls third party applications such - as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer + This plugin allowed a Presentation to be opened, controlled and displayed on the output display. The plugin controls + third party applications such as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer. """ log = logging.getLogger(u'PresentationPlugin') @@ -69,18 +70,16 @@ class PresentationPlugin(Plugin): self.icon_path = u':/plugins/plugin_presentations.png' 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) - self.settings_tab = PresentationTab(parent, self.name, visible_name[u'title'], self.controllers, - self.icon_path) + self.settings_tab = PresentationTab(parent, self.name, visible_name[u'title'], self.controllers, self.icon_path) def initialise(self): """ - Initialise the plugin. Determine which controllers are enabled - are start their processes. + Initialise the plugin. Determine which controllers are enabled are start their processes. """ log.info(u'Presentations Initialising') Plugin.initialise(self) @@ -95,8 +94,8 @@ class PresentationPlugin(Plugin): def finalise(self): """ - Finalise the plugin. Ask all the enabled presentation applications - to close down their applications and release resources. + Finalise the plugin. Ask all the enabled presentation applications to close down their applications and release + resources. """ log.info(u'Plugin Finalise') # Ask each controller to tidy up. @@ -108,26 +107,23 @@ class PresentationPlugin(Plugin): def create_media_manager_item(self): """ - Create the Media Manager List + Create the Media Manager List. """ self.media_item = PresentationMediaItem( self.main_window.media_dock_manager.media_dock, self, self.icon, self.controllers) 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 def check_pre_conditions(self): """ - Check to see if we have any presentation software available - If Not do not install the plugin. + Check to see if we have any presentation software available. If not do not install the plugin. """ log.debug(u'check_pre_conditions') - controller_dir = os.path.join( - AppLocation.get_directory(AppLocation.PluginsDir), - u'presentations', u'lib') + controller_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'presentations', u'lib') for filename in os.listdir(controller_dir): if filename.endswith(u'controller.py') and not filename == 'presentationcontroller.py': path = os.path.join(controller_dir, filename) @@ -146,7 +142,7 @@ class PresentationPlugin(Plugin): def about(self): """ - Return information about this plugin + Return information about this plugin. """ about_text = translate('PresentationPlugin', 'Presentation ' 'Plugin
The presentation plugin provides the ' @@ -157,7 +153,7 @@ class PresentationPlugin(Plugin): 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 ## self.text_strings[StringContent.Name] = { diff --git a/openlp/plugins/remotes/html/openlp.js b/openlp/plugins/remotes/html/openlp.js index 00877e332..10bc9e328 100644 --- a/openlp/plugins/remotes/html/openlp.js +++ b/openlp/plugins/remotes/html/openlp.js @@ -147,7 +147,7 @@ window.OpenLP = { }, pollServer: function () { $.getJSON( - "/api/poll", + "/stage/poll", function (data, status) { var prevItem = OpenLP.currentItem; OpenLP.currentSlide = data.results.slide; diff --git a/openlp/plugins/remotes/html/stage.js b/openlp/plugins/remotes/html/stage.js index dcc2e4b70..42b7712f9 100644 --- a/openlp/plugins/remotes/html/stage.js +++ b/openlp/plugins/remotes/html/stage.js @@ -26,7 +26,7 @@ window.OpenLP = { loadService: function (event) { $.getJSON( - "/api/service/list", + "/stage/service/list", function (data, status) { OpenLP.nextSong = ""; $("#notes").html(""); @@ -46,7 +46,7 @@ window.OpenLP = { }, loadSlides: function (event) { $.getJSON( - "/api/controller/live/text", + "/stage/controller/live/text", function (data, status) { OpenLP.currentSlides = data.results.slides; OpenLP.currentSlide = 0; @@ -137,7 +137,7 @@ window.OpenLP = { }, pollServer: function () { $.getJSON( - "/api/poll", + "/stage/poll", function (data, status) { OpenLP.updateClock(data); if (OpenLP.currentItem != data.results.item || diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index d285baa50..eedc30102 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -43,7 +43,7 @@ the remotes. ``/files/{filename}`` Serve a static file. -``/api/poll`` +``/stage/api/poll`` Poll to see if there are any changes. Returns a JSON-encoded dict of any changes that occurred:: @@ -119,122 +119,198 @@ import os import re import urllib import urlparse +import cherrypy -from PyQt4 import QtCore, QtNetwork from mako.template import Template +from PyQt4 import QtCore from openlp.core.lib import Registry, Settings, PluginStatus, StringContent - from openlp.core.utils import AppLocation, translate +from cherrypy._cpcompat import sha, ntob + 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' - content = '' - headers = { - 'Content-Type': 'text/html; charset="utf-8"\r\n' - } + return sha(ntob(password)).hexdigest() - def __init__(self, content='', headers=None, code=None): - if headers is None: - headers = {} - self.content = content - for key, value in headers.iteritems(): - self.headers[key] = value - if code: - self.code = code + +def fetch_password(username): + """ + Fetch the password for a provided user. + """ + if username != Settings().value(u'remotes/user id'): + return None + return make_sha_hash(Settings().value(u'remotes/password')) class HttpServer(object): """ 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') - self.plugin = plugin - self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html') - self.connections = [] - self.start_tcp() + self.settings_section = u'remotes' + self.router = HttpRouter() - def start_tcp(self): + def start_server(self): """ - Start the http server, use the port in the settings default to 4316. - Listen out for slide and song changes so they can be broadcast to - clients. Listen out for socket connections. + Start the http server based on configuration. """ - log.debug(u'Start TCP server') - port = Settings().value(self.plugin.settings_section + u'/port') - address = Settings().value(self.plugin.settings_section + u'/ip address') - self.server = QtNetwork.QTcpServer() - self.server.listen(QtNetwork.QHostAddress(address), port) - self.server.newConnection.connect(self.new_connection) - log.debug(u'TCP listening on port %d' % port) + log.debug(u'Start CherryPy server') + # Define to security levels and inject the router code + self.root = self.Public() + self.root.files = self.Files() + self.root.stage = self.Stage() + self.root.router = self.router + 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 - communication. + Define the configuration of the server. """ - log.debug(u'new http connection') - socket = self.server.nextPendingConnection() - if socket: - self.connections.append(HttpConnection(self, socket)) + if Settings().value(self.settings_section + u'/https enabled'): + port = Settings().value(self.settings_section + u'/https port') + address = Settings().value(self.settings_section + u'/ip address') + 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') - if connection in self.connections: - self.connections.remove(connection) + @cherrypy.expose + def default(self, *args, **kwargs): + 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): """ Close down the 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 - and the client. + This code is called by the HttpServer upon a request and it processes it based on the routing table. """ - 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 = [ (u'^/$', self.serve_file), (u'^/(stage)$', self.serve_file), (r'^/files/(.*)$', self.serve_file), (r'^/api/poll$', self.poll), + (r'^/stage/poll$', self.poll), (r'^/api/controller/(live|preview)/(.*)$', self.controller), + (r'^/stage/controller/(live|preview)/(.*)$', self.controller), (r'^/api/service/(.*)$', self.service), + (r'^/stage/service/(.*)$', self.service), (r'^/api/display/(hide|show|blank|theme|desktop)$', self.display), (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/(.*)/live$', self.go_live), (r'^/api/(.*)/add$', self.add_to_service) ] - self.socket.readyRead.connect(self.ready_read) - self.socket.disconnected.connect(self.disconnected) 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): + """ + Read the service item in use and return the data as a json object + """ service_items = [] if self.live_controller.service_item: current_unique_identifier = self.live_controller.service_item.unique_identifier @@ -281,40 +357,6 @@ class HttpConnection(object): '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): """ 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' elif filename == u'stage': filename = u'stage.html' - path = os.path.normpath(os.path.join(self.parent.html_dir, filename)) - if not path.startswith(self.parent.html_dir): - return HttpResponse(code=u'404 Not Found') + path = os.path.normpath(os.path.join(self.html_dir, filename)) + if not path.startswith(self.html_dir): + return self._http_not_found() ext = os.path.splitext(filename)[1] html = None if ext == u'.html': @@ -360,11 +402,12 @@ class HttpConnection(object): content = file_handle.read() except IOError: log.exception(u'Failed to open %s' % path) - return HttpResponse(code=u'404 Not Found') + return self._http_not_found() finally: if file_handle: file_handle.close() - return HttpResponse(content, {u'Content-Type': mimetype}) + cherrypy.response.headers['Content-Type'] = mimetype + return content def poll(self): """ @@ -379,18 +422,20 @@ class HttpConnection(object): u'theme': self.live_controller.theme_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): """ Hide or show the display screen. + This is a cross Thread call and UI is updated so Events need to be used. ``action`` This is the action, either ``hide`` or ``show``. """ - Registry().execute(u'slidecontroller_toggle_display', action) - return HttpResponse(json.dumps({u'results': {u'success': True}}), - {u'Content-Type': u'application/json'}) + self.live_controller.emit(QtCore.SIGNAL(u'slidecontroller_toggle_display'), action) + cherrypy.response.headers['Content-Type'] = u'application/json' + return json.dumps({u'results': {u'success': True}}) def alert(self): """ @@ -399,16 +444,16 @@ class HttpConnection(object): plugin = self.plugin_manager.get_plugin_by_name("alerts") if plugin.status == PluginStatus.Active: 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: - return HttpResponse(code=u'400 Bad Request') + return self._http_bad_request() text = urllib.unquote(text) - Registry().execute(u'alerts_text', [text]) + self.alerts_manager.emit(QtCore.SIGNAL(u'alerts_text'), [text]) success = True else: success = False - return HttpResponse(json.dumps({u'results': {u'success': success}}), - {u'Content-Type': u'application/json'}) + cherrypy.response.headers['Content-Type'] = u'application/json' + return json.dumps({u'results': {u'success': success}}) def controller(self, display_type, action): """ @@ -444,44 +489,44 @@ class HttpConnection(object): if current_item: json_data[u'results'][u'item'] = self.live_controller.service_item.unique_identifier else: - if self.url_params and self.url_params.get(u'data'): + if self.request_data: try: - data = json.loads(self.url_params[u'data'][0]) + data = json.loads(self.request_data)[u'request'][u'id'] except KeyError, ValueError: - return HttpResponse(code=u'400 Bad Request') + return self._http_bad_request() log.info(data) # This slot expects an int within a list. - id = data[u'request'][u'id'] - Registry().execute(event, [id]) + self.live_controller.emit(QtCore.SIGNAL(event), [data]) else: - Registry().execute(event) + self.live_controller.emit(QtCore.SIGNAL(event)) 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): """ - Handles requests for service items + Handles requests for service items in the service manager ``action`` The action to perform. """ event = u'servicemanager_%s' % action if action == u'list': - return HttpResponse(json.dumps({u'results': {u'items': self._get_service_items()}}), - {u'Content-Type': u'application/json'}) - else: - event += u'_item' - if self.url_params and self.url_params.get(u'data'): + cherrypy.response.headers['Content-Type'] = u'application/json' + return json.dumps({u'results': {u'items': self._get_service_items()}}) + event += u'_item' + if self.request_data: try: - data = json.loads(self.url_params[u'data'][0]) - except KeyError, ValueError: - return HttpResponse(code=u'400 Bad Request') - Registry().execute(event, data[u'request'][u'id']) + data = json.loads(self.request_data)[u'request'][u'id'] + except KeyError: + return self._http_bad_request() + self.service_manager.emit(QtCore.SIGNAL(event), data) else: 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. @@ -493,8 +538,9 @@ class HttpConnection(object): searches = [] for plugin in self.plugin_manager.plugins: 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'])]) - return HttpResponse(json.dumps({u'results': {u'items': searches}}), {u'Content-Type': u'application/json'}) + searches.append([plugin.name, unicode(plugin.text_strings[StringContent.Name][u'plural'])]) + cherrypy.response.headers['Content-Type'] = u'application/json' + return json.dumps({u'results': {u'items': searches}}) def search(self, plugin_name): """ @@ -504,69 +550,63 @@ class HttpConnection(object): The plugin name to search in. """ 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: - return HttpResponse(code=u'400 Bad Request') + return self._http_bad_request() text = urllib.unquote(text) 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: results = plugin.media_item.search(text, False) else: 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): """ Go live on an item of type ``plugin``. """ 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: - return HttpResponse(code=u'400 Bad Request') + return self._http_bad_request() plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item: - plugin.media_item.go_live(id, remote=True) - return HttpResponse(code=u'200 OK') + plugin.media_item.emit(QtCore.SIGNAL(u'%s_go_live' % plugin_name), [id, True]) + return self._http_success() def add_to_service(self, plugin_name): """ Add item of type ``plugin_name`` to the end of the service. """ 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: - return HttpResponse(code=u'400 Bad Request') + return self._http_bad_request() plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item: - item_id = plugin.media_item.createItemFromId(id) - plugin.media_item.add_to_service(item_id, remote=True) - return HttpResponse(code=u'200 OK') + item_id = plugin.media_item.create_item_from_id(id) + plugin.media_item.emit(QtCore.SIGNAL(u'%s_add_to_service' % plugin_name), [item_id, True]) + self._http_success() - def send_response(self, response): - http = u'HTTP/1.1 %s\r\n' % response.code - for header, value in response.headers.iteritems(): - http += '%s: %s\r\n' % (header, value) - http += '\r\n' - self.socket.write(http) - self.socket.write(response.content) + def _http_success(self): + """ + Set the HTTP success return code. + """ + cherrypy.response.status = 200 - 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') - self.close() + cherrypy.response.status = 400 - 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: - return - log.debug(u'close socket') - self.socket.close() - self.socket = None - self.parent.close_connection(self) + cherrypy.response.status = 404 + cherrypy.response.body = ["Sorry, an error occurred "] def _get_service_manager(self): """ @@ -597,3 +637,13 @@ class HttpConnection(object): return self._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) diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 483b3461b..09934b58c 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -27,9 +27,12 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +import os.path + 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' @@ -53,32 +56,84 @@ class RemoteTab(SettingsTab): self.address_label.setObjectName(u'address_label') self.address_edit = QtGui.QLineEdit(self.server_settings_group_box) self.address_edit.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - self.address_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp( - u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'), self)) + self.address_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'), + self)) self.address_edit.setObjectName(u'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.setObjectName(u'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_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.setObjectName(u'port_spin_box') - self.server_settings_layout.addRow(self.port_label, self.port_spin_box) - self.remote_url_label = QtGui.QLabel(self.server_settings_group_box) + self.http_setting_layout.addRow(self.port_label, self.port_spin_box) + self.remote_url_label = QtGui.QLabel(self.http_settings_group_box) 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.setOpenExternalLinks(True) - self.server_settings_layout.addRow(self.remote_url_label, self.remote_url) - self.stage_url_label = QtGui.QLabel(self.server_settings_group_box) + self.http_setting_layout.addRow(self.remote_url_label, self.remote_url) + self.stage_url_label = QtGui.QLabel(self.http_settings_group_box) 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.setOpenExternalLinks(True) - self.server_settings_layout.addRow(self.stage_url_label, self.stage_url) - self.left_layout.addWidget(self.server_settings_group_box) + self.http_setting_layout.addRow(self.stage_url_label, self.stage_url) + 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.setObjectName(u'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.left_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.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): self.server_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Server Settings')) @@ -112,8 +169,21 @@ class RemoteTab(SettingsTab): 'Scan the QR code or click download to install the ' '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): + """ + Update the display based on the data input on the screen + """ ip_address = u'localhost' if self.address_edit.text() == ZERO_URL: interfaces = QtNetwork.QNetworkInterface.allInterfaces() @@ -129,31 +199,73 @@ class RemoteTab(SettingsTab): break else: ip_address = self.address_edit.text() - url = u'http://%s:%s/' % (ip_address, self.port_spin_box.value()) - self.remote_url.setText(u'%s' % (url, url)) - url += u'stage' - self.stage_url.setText(u'%s' % (url, url)) + http_url = u'http://%s:%s/' % (ip_address, self.port_spin_box.value()) + https_url = u'https://%s:%s/' % (ip_address, self.https_port_spin_box.value()) + self.remote_url.setText(u'%s' % (http_url, http_url)) + self.remote_https_url.setText(u'%s' % (https_url, https_url)) + http_url += u'stage' + https_url += u'stage' + self.stage_url.setText(u'%s' % (http_url, http_url)) + self.stage_https_url.setText(u'%s' % (https_url, https_url)) 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.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.twelve_hour = Settings().value(self.settings_section + u'/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.https_changed() 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 \ - Settings().value(self.settings_section + u'/port') != self.port_spin_box.value(): - changed = True + Settings().value(self.settings_section + u'/port') != self.port_spin_box.value() or \ + 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'/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'/twelve hour', self.twelve_hour) - if changed: - Registry().execute(u'remotes_config_updated') + Settings().setValue(self.settings_section + u'/authentication enabled', self.user_login_group_box.isChecked()) + 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 # we have a set value convert to True/False if check_state == QtCore.Qt.Checked: 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()) + diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index e990101e3..f443fbda4 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -29,6 +29,8 @@ import logging +from PyQt4 import QtGui + from openlp.core.lib import Plugin, StringContent, translate, build_icon from openlp.plugins.remotes.lib import RemoteTab, HttpServer @@ -37,6 +39,11 @@ log = logging.getLogger(__name__) __default_settings__ = { u'remotes/twelve hour': True, 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' } @@ -60,7 +67,8 @@ class RemotesPlugin(Plugin): """ log.debug(u'initialise') Plugin.initialise(self) - self.server = HttpServer(self) + self.server = HttpServer() + self.server.start_server() def finalise(self): """ @@ -70,6 +78,7 @@ class RemotesPlugin(Plugin): Plugin.finalise(self) if self.server: self.server.close() + self.server = None def about(self): """ @@ -99,5 +108,6 @@ class RemotesPlugin(Plugin): """ Called when Config is changed to restart the server on new address or port """ - self.finalise() - self.initialise() + log.debug(u'remote config changed') + self.main_window.information_message(translate('RemotePlugin', 'Configuration Change'), + translate('RemotePlugin', 'OpenLP will need to be restarted for the Remote changes to become active.')) diff --git a/openlp/plugins/songs/forms/__init__.py b/openlp/plugins/songs/forms/__init__.py index a1c177a9d..588f359f6 100644 --- a/openlp/plugins/songs/forms/__init__.py +++ b/openlp/plugins/songs/forms/__init__.py @@ -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 from the .ui files later if necessary. """ +from editsongform import EditSongForm diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 49a20762a..5a100b014 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -320,7 +320,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): for plugin in self.plugin_manager.plugins: if plugin.name == u'media' and plugin.status == PluginStatus.Active: 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 def new_song(self): @@ -714,7 +714,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): text = self.song_book_combo_box.currentText() if item == 0 and text: temp_song_book = text - self.media_item.songMaintenanceForm.exec_(True) + self.media_item.song_maintenance_form.exec_(True) self.load_authors() self.load_books() self.load_topics() diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index f0554f588..36ed50cee 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -235,7 +235,7 @@ class SongExportForm(OpenLPWizard): self.availableListWidget.addItem(item) self.application.set_normal_cursor() - def preWizard(self): + def pre_wizard(self): """ Perform pre export tasks. """ diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 4c749f4d0..add306505 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -325,7 +325,7 @@ class SongImportForm(OpenLPWizard): self.error_copy_to_button.setHidden(True) self.error_save_to_button.setHidden(True) - def preWizard(self): + def pre_wizard(self): """ Perform pre import tasks """ diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index ff018abf3..76b3e0a70 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -50,13 +50,11 @@ from sundayplusimport import SundayPlusImport from foilpresenterimport import FoilPresenterImport from zionworximport import ZionWorxImport # Imports that might fail + + log = logging.getLogger(__name__) -try: - from olp1import import OpenLP1SongImport - HAS_OPENLP1 = True -except ImportError: - log.exception('Error importing %s', 'OpenLP1SongImport') - HAS_OPENLP1 = False + + try: from sofimport import SofImport HAS_SOF = True @@ -144,23 +142,22 @@ class SongFormat(object): Unknown = -1 OpenLyrics = 0 OpenLP2 = 1 - OpenLP1 = 2 - Generic = 3 - CCLI = 4 - DreamBeam = 5 - EasySlides = 6 - EasyWorship = 7 - FoilPresenter = 8 - MediaShout = 9 - OpenSong = 10 - PowerSong = 11 - SongBeamer = 12 - SongPro = 13 - SongShowPlus = 14 - SongsOfFellowship = 15 - SundayPlus = 16 - WordsOfWorship = 17 - ZionWorx = 18 + Generic = 2 + CCLI = 3 + DreamBeam = 4 + EasySlides = 5 + EasyWorship = 6 + FoilPresenter = 7 + MediaShout = 8 + OpenSong = 9 + PowerSong = 10 + SongBeamer = 11 + SongPro = 12 + SongShowPlus = 13 + SongsOfFellowship = 14 + SundayPlus = 15 + WordsOfWorship = 16 + ZionWorx = 17 # Set optional attribute defaults __defaults__ = { @@ -191,14 +188,6 @@ class SongFormat(object): u'selectMode': SongFormatSelect.SingleFile, 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: { u'name': translate('SongsPlugin.ImportWizardForm', 'Generic Document/Presentation'), u'prefix': u'generic', @@ -332,7 +321,6 @@ class SongFormat(object): return [ SongFormat.OpenLyrics, SongFormat.OpenLP2, - SongFormat.OpenLP1, SongFormat.Generic, SongFormat.CCLI, SongFormat.DreamBeam, @@ -387,9 +375,7 @@ class SongFormat(object): """ 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) if HAS_SOF: SongFormat.set(SongFormat.SongsOfFellowship, u'class', SofImport) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index d75124d84..b60c0b162 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -72,27 +72,21 @@ class SongMediaItem(MediaManagerItem): def __init__(self, parent, plugin): self.icon_path = u'songs/song' 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.songMaintenanceForm = SongMaintenanceForm(self.plugin.manager, self) - # Holds information about whether the edit is remotely triggered and - # which Song is required. - self.remoteSong = -1 - self.editItem = None + # Holds information about whether the edit is remotely triggered and which Song is required. + self.remote_song = -1 + self.edit_item = None self.quick_preview_allowed = True self.has_search = True - def _updateBackgroundAudio(self, song, item): + def _update_background_audio(self, song, item): song.media_files = [] for i, bga in enumerate(item.background_audio): dest_file = os.path.join( 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]) - shutil.copyfile(os.path.join(AppLocation.get_section_data_path(u'servicemanager'), bga), - dest_file) - song.media_files.append(MediaFile.populate( - weight=i, file_name=dest_file)) + shutil.copyfile(os.path.join(AppLocation.get_section_data_path(u'servicemanager'), bga), dest_file) + song.media_files.append(MediaFile.populate(weight=i, file_name=dest_file)) self.plugin.manager.save_object(song, True) def add_end_header_bar(self): @@ -100,12 +94,12 @@ class SongMediaItem(MediaManagerItem): ## Song Maintenance Button ## self.maintenanceAction = self.toolbar.add_toolbar_action('maintenanceAction', icon=':/songs/song_maintenance.png', - triggers=self.onSongMaintenanceClick) + triggers=self.on_song_maintenance_click) self.add_search_to_toolbar() # Signals and slots Registry().register_function(u'songs_load_list', self.on_song_list_load) 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)'), 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, 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() 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') - 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.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.')) 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([ (SongSearch.Entire, u':/songs/song_search_all.png', 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()) # Reload the list considering the new search type. search_keywords = unicode(self.search_text_edit.displayText()) - search_results = [] search_type = self.search_text_edit.current_search_type() if search_type == SongSearch.Entire: log.debug(u'Entire Song Search') - search_results = self.searchEntire(search_keywords) - self.displayResultsSong(search_results) + search_results = self.search_entire(search_keywords) + self.display_results_song(search_results) elif search_type == SongSearch.Titles: log.debug(u'Titles Search') search_results = self.plugin.manager.get_all_objects(Song, 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: log.debug(u'Lyrics Search') search_results = self.plugin.manager.get_all_objects(Song, 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: log.debug(u'Authors Search') search_results = self.plugin.manager.get_all_objects(Author, 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: log.debug(u'Books Search') 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, Book.name.like(u'%' + search_keywords[0] + u'%'), Book.name.asc()) 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: log.debug(u'Theme Search') search_results = self.plugin.manager.get_all_objects(Song, Song.theme_name.like(u'%' + search_keywords + u'%')) - self.displayResultsSong(search_results) + self.display_results_song(search_results) self.check_search_result() - def searchEntire(self, search_keywords): + def search_entire(self, search_keywords): return self.plugin.manager.get_all_objects(Song, or_(Song.search_title.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 """ log.debug(u'on_song_list_load - start') - # Called to redisplay the song list screen edit from a search - # or from the exit of the Song edit dialog. If remote editing is active - # Trigger it and clean up so it will not update again. - # Push edits to the service manager to update items - if self.editItem and self.updateServiceOnEdit and not self.remote_triggered: - item = self.build_service_item(self.editItem) + # Called to redisplay the song list screen edit from a search or from the exit of the Song edit dialog. If + # remote editing is active Trigger it and clean up so it will not update again. Push edits to the service + # manager to update items + if self.edit_item and self.updateServiceOnEdit and not self.remote_triggered: + item = self.build_service_item(self.edit_item) self.service_manager.replace_service_item(item) self.on_search_text_button_clicked() log.debug(u'on_song_list_load - finished') - def displayResultsSong(self, searchresults): + def display_results_song(self, searchresults): log.debug(u'display results Song') self.save_auto_select_id() self.list_view.clear() @@ -241,7 +239,7 @@ class SongMediaItem(MediaManagerItem): self.list_view.setCurrentItem(song_name) self.auto_select_id = -1 - def displayResultsAuthor(self, searchresults): + def display_results_author(self, searchresults): log.debug(u'display results Author') self.list_view.clear() for author in searchresults: @@ -254,7 +252,7 @@ class SongMediaItem(MediaManagerItem): song_name.setData(QtCore.Qt.UserRole, song.id) 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') self.list_view.clear() for book in searchresults: @@ -271,7 +269,7 @@ class SongMediaItem(MediaManagerItem): song_name.setData(QtCore.Qt.UserRole, song.id) self.list_view.addItem(song_name) - def onClearTextButtonClick(self): + def on_clear_text_button_click(self): """ Clear the search text. """ @@ -280,11 +278,10 @@ class SongMediaItem(MediaManagerItem): def on_search_text_edit_changed(self, text): """ - If search as type enabled invoke the search on each key press. - If the Lyrics are being searched do not start till 7 characters - have been entered. + If search as type enabled invoke the search on each key press. If the Lyrics are being searched do not start + till 7 characters have been entered. """ - if self.searchAsYouType: + if self.search_as_you_type: search_length = 1 if self.search_text_edit.current_search_type() == SongSearch.Entire: search_length = 4 @@ -293,7 +290,7 @@ class SongMediaItem(MediaManagerItem): if len(text) > search_length: self.on_search_text_button_clicked() elif not text: - self.onClearTextButtonClick() + self.on_clear_text_button_click() def on_import_click(self): if not hasattr(self, u'import_wizard'): @@ -309,33 +306,32 @@ class SongMediaItem(MediaManagerItem): def on_new_click(self): log.debug(u'on_new_click') - self.editSongForm.new_song() - self.editSongForm.exec_() - self.onClearTextButtonClick() + self.edit_song_form.new_song() + self.edit_song_form.exec_() + self.on_clear_text_button_click() self.on_selection_change() self.auto_select_id = -1 - def onSongMaintenanceClick(self): - self.songMaintenanceForm.exec_() + def on_song_maintenance_click(self): + 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 - the Song Id in the payload along with an indicator to say which - type of display is required. + Called by ServiceManager or SlideController by event passing the Song Id in the payload along with an indicator + to say which 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) valid = self.plugin.manager.get_object(Song, song_id) if valid: - self.editSongForm.load_song(song_id, preview) - if self.editSongForm.exec_() == QtGui.QDialog.Accepted: + self.edit_song_form.load_song(song_id, preview) + if self.edit_song_form.exec_() == QtGui.QDialog.Accepted: self.auto_select_id = -1 self.on_song_list_load() - self.remoteSong = song_id + self.remote_song = song_id self.remote_triggered = True item = self.build_service_item(remote=True) - self.remoteSong = -1 + self.remote_song = -1 self.remote_triggered = None if item: return item @@ -347,13 +343,13 @@ class SongMediaItem(MediaManagerItem): """ log.debug(u'on_edit_click') if check_item_selected(self.list_view, UiStrings().SelectEdit): - self.editItem = self.list_view.currentItem() - item_id = self.editItem.data(QtCore.Qt.UserRole) - self.editSongForm.load_song(item_id, False) - self.editSongForm.exec_() + self.edit_item = self.list_view.currentItem() + item_id = self.edit_item.data(QtCore.Qt.UserRole) + self.edit_song_form.load_song(item_id, False) + self.edit_song_form.exec_() self.auto_select_id = -1 self.on_song_list_load() - self.editItem = None + self.edit_item = None def on_delete_click(self): """ @@ -362,11 +358,11 @@ class SongMediaItem(MediaManagerItem): if check_item_selected(self.list_view, UiStrings().SelectDelete): items = self.list_view.selectedIndexes() if QtGui.QMessageBox.question(self, - UiStrings().ConfirmDelete, - translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the %n selected song(s)?', '', - QtCore.QCoreApplication.CodecForTr, len(items)), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), - QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No: + UiStrings().ConfirmDelete, + translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the %n selected song(s)?', '', + QtCore.QCoreApplication.CodecForTr, len(items)), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), + QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No: return self.application.set_busy_cursor() self.main_window.display_progress_bar(len(items)) @@ -390,14 +386,14 @@ class SongMediaItem(MediaManagerItem): self.application.set_normal_cursor() self.on_search_text_button_clicked() - def onCloneClick(self): + def on_clone_click(self): """ Clone a Song """ - log.debug(u'onCloneClick') + log.debug(u'on_clone_click') if check_item_selected(self.list_view, UiStrings().SelectEdit): - self.editItem = self.list_view.currentItem() - item_id = self.editItem.data(QtCore.Qt.UserRole) + self.edit_item = self.list_view.currentItem() + item_id = self.edit_item.data(QtCore.Qt.UserRole) old_song = self.plugin.manager.get_object(Song, item_id) song_xml = self.openLyrics.song_to_xml(old_song) 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. """ - log.debug(u'generate_slide_data: %s, %s, %s' % (service_item, item, self.remoteSong)) - item_id = self._get_id_of_item_to_generate(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.remote_song) service_item.add_capability(ItemCapabilities.CanEdit) service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanLoop) @@ -430,8 +426,8 @@ class SongMediaItem(MediaManagerItem): verse_tags_translated = True if not song.verse_order.strip(): for verse in verse_list: - # We cannot use from_loose_input() here, because database - # is supposed to contain English lowercase singlechar tags. + # We cannot use from_loose_input() here, because database is supposed to contain English lowercase + # singlechar tags. verse_tag = verse[0][u'type'] verse_index = None if len(verse_tag) > 1: @@ -463,16 +459,7 @@ class SongMediaItem(MediaManagerItem): for slide in verses: service_item.add_from_text(unicode(slide)) service_item.title = song.title - author_list = [unicode(author.display_name) for author in song.authors] - 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) - ] + author_list = self.generate_footer(service_item, song) 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) # 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] 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): """ 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: return if item.data_string[u'title'].find(u'@') == -1: - # This file seems to be an old one (prior to 1.9.5), which means, - # that the search title (data_string[u'title']) is probably wrong. - # We add "@" to search title and hope that we do not add any + # FIXME: This file seems to be an old one (prior to 1.9.5), which means, that the search title + # (data_string[u'title']) is probably wrong. We add "@" to search title and hope that we do not add any # duplicate. This should work for songs without alternate title. search_results = self.plugin.manager.get_all_objects(Song, Song.search_title == (re.compile(r'\W+', re.UNICODE).sub(u' ', @@ -499,9 +509,8 @@ class SongMediaItem(MediaManagerItem): else: search_results = self.plugin.manager.get_all_objects(Song, Song.search_title == item.data_string[u'title'], Song.search_title.asc()) - editId = 0 + edit_id = 0 add_song = True - temporary = False if search_results: for song in search_results: author_list = item.data_string[u'authors'] @@ -514,16 +523,16 @@ class SongMediaItem(MediaManagerItem): break if same_authors and author_list.strip(u', ') == u'': add_song = False - editId = song.id + edit_id = song.id break # If there's any backing tracks, copy them over. if item.background_audio: - self._updateBackgroundAudio(song, item) + self._update_background_audio(song, item) if add_song and self.addSongFromService: song = self.openLyrics.xml_to_song(item.xml_version) # If there's any backing tracks, copy them over. if item.background_audio: - self._updateBackgroundAudio(song, item) + self._update_background_audio(song, item) editId = song.id self.on_search_text_button_clicked() elif add_song and not self.addSongFromService: @@ -531,16 +540,16 @@ class SongMediaItem(MediaManagerItem): song = self.openLyrics.xml_to_song(item.xml_version, True) # If there's any backing tracks, copy them over. if item.background_audio: - self._updateBackgroundAudio(song, item) - editId = song.id - temporary = True - # Update service with correct song id. - if editId: - self.service_manager.service_item_update(editId, item.unique_identifier, temporary) + self._update_background_audio(song, item) + edit_id = song.id + # Update service with correct song id and return it to caller. + item.edit_id = edit_id + self.generate_footer(item, song) + return item def search(self, string, showError): """ 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] diff --git a/openlp/plugins/songs/lib/olp1import.py b/openlp/plugins/songs/lib/olp1import.py deleted file mode 100644 index 649b8af1f..000000000 --- a/openlp/plugins/songs/lib/olp1import.py +++ /dev/null @@ -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) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 65056ee5b..4355bc7e5 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -235,8 +235,7 @@ class SongsPlugin(Plugin): u'delete': translate('SongsPlugin', 'Delete the selected song.'), u'preview': translate('SongsPlugin', 'Preview the selected song.'), u'live': translate('SongsPlugin', 'Send the selected song live.'), - u'service': translate('SongsPlugin', - 'Add the selected song to the service.') + u'service': translate('SongsPlugin', 'Add the selected song to the service.') } self.set_plugin_ui_text_strings(tooltips) diff --git a/openlp/plugins/songusage/__init__.py b/openlp/plugins/songusage/__init__.py index d18c787f0..b0d3ecc12 100644 --- a/openlp/plugins/songusage/__init__.py +++ b/openlp/plugins/songusage/__init__.py @@ -27,7 +27,6 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`songusage` module contains the Song Usage plugin. The Song Usage -plugin provides auditing capabilities for reporting the songs you are using to -copyright license organisations. +The :mod:`songusage` module contains the Song Usage plugin. The Song Usage plugin provides auditing capabilities for +reporting the songs you are using to copyright license organisations. """ diff --git a/openlp/plugins/songusage/forms/songusagedeletedialog.py b/openlp/plugins/songusage/forms/songusagedeletedialog.py index a1ad701b2..cffbdf733 100644 --- a/openlp/plugins/songusage/forms/songusagedeletedialog.py +++ b/openlp/plugins/songusage/forms/songusagedeletedialog.py @@ -55,7 +55,8 @@ class Ui_SongUsageDeleteDialog(object): self.retranslateUi(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( 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.')) diff --git a/openlp/plugins/songusage/forms/songusagedetaildialog.py b/openlp/plugins/songusage/forms/songusagedetaildialog.py index 47fc9bf27..1922d261a 100644 --- a/openlp/plugins/songusage/forms/songusagedetaildialog.py +++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py @@ -81,7 +81,8 @@ class Ui_SongUsageDetailDialog(object): self.save_file_push_button.clicked.connect(song_usage_detail_dialog.define_output_location) 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.to_label.setText(translate('SongUsagePlugin.SongUsageDetailForm', 'to')) self.file_group_box.setTitle(translate('SongUsagePlugin.SongUsageDetailForm', 'Report Location')) diff --git a/openlp/plugins/songusage/lib/db.py b/openlp/plugins/songusage/lib/db.py index 048b75542..5d3da7559 100644 --- a/openlp/plugins/songusage/lib/db.py +++ b/openlp/plugins/songusage/lib/db.py @@ -36,12 +36,14 @@ from sqlalchemy.orm import mapper from openlp.core.lib.db import BaseModel, init_db + class SongUsageItem(BaseModel): """ SongUsageItem model """ pass + def init_schema(url): """ Setup the songusage database connection and initialise the database schema diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index 7ca056184..7a730c992 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -48,11 +48,11 @@ if QtCore.QDate().currentDate().month() < 9: __default_settings__ = { - u'songusage/db type': u'sqlite', - u'songusage/active': False, - u'songusage/to date': QtCore.QDate(YEAR, 8, 31), - u'songusage/from date': QtCore.QDate(YEAR - 1, 9, 1), - u'songusage/last directory export': u'' + u'songusage/db type': u'sqlite', + u'songusage/active': False, + u'songusage/to date': QtCore.QDate(YEAR, 8, 31), + u'songusage/from date': QtCore.QDate(YEAR - 1, 9, 1), + u'songusage/last directory export': u'' } @@ -76,12 +76,10 @@ class SongUsagePlugin(Plugin): def add_tools_menu_item(self, tools_menu): """ - Give the SongUsage plugin the opportunity to add items to the - **Tools** menu. + Give the SongUsage plugin the opportunity to add items to the **Tools** menu. ``tools_menu`` - The actual **Tools** menu item, so that your actions can - use it as their parent. + The actual **Tools** menu item, so that your actions can use it as their parent. """ log.info(u'add tools menu') self.toolsMenu = tools_menu @@ -218,8 +216,8 @@ class SongUsagePlugin(Plugin): self.song_usage_detail_form.exec_() def about(self): - about_text = translate('SongUsagePlugin', 'SongUsage Plugin' - '
This plugin tracks the usage of songs in services.') + about_text = translate('SongUsagePlugin', + 'SongUsage Plugin
This plugin tracks the usage of songs in services.') return about_text def set_plugin_text_strings(self): diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index a6e075db4..16a2c10e7 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -79,8 +79,9 @@ MODULES = [ 'lxml', 'chardet', 'enchant', - 'BeautifulSoup', + 'bs4', 'mako', + 'cherrypy', 'migrate', 'uno', 'icu', @@ -88,7 +89,6 @@ MODULES = [ OPTIONAL_MODULES = [ - ('sqlite', ' (SQLite 2 support)'), ('MySQLdb', ' (MySQL support)'), ('psycopg2', ' (PostgreSQL support)'), ('nose', ' (testing framework)'), diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 66cb834f1..b0d1a4bed 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -1,13 +1,20 @@ """ Package to test the openlp.core.lib package. """ +import os + from unittest import TestCase from datetime import datetime, timedelta 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): @@ -15,7 +22,7 @@ class TestLib(TestCase): """ 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 # 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 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 # WHEN: We "convert" it to a bool @@ -125,7 +132,7 @@ class TestLib(TestCase): Test the check_directory_exists() function """ 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 directory_to_check = u'existing/directory' @@ -219,7 +226,7 @@ class TestLib(TestCase): Test the build_icon() function with a resource URI """ 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 MockedQtGui.QIcon = MagicMock MockedQtGui.QIcon.Normal = 1 @@ -261,9 +268,43 @@ class TestLib(TestCase): 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' + 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): """ - 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 MockedQtGui = patch(u'openlp.core.lib.QtGui') @@ -423,7 +464,7 @@ class TestLib(TestCase): 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, \ 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): """ - 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: # 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): """ - 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: # 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): """ - 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: # 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): """ - 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: # GIVEN: A list with a string and the mocked Qt module. diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py index 9d6c30f8e..8317e78dc 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -74,7 +74,7 @@ class TestPluginManager(TestCase): # WHEN: We run hook_settings_tabs() plugin_manager.hook_settings_tabs() - # THEN: The create_settings_Tab() method should have been called + # THEN: The hook_settings_tabs() method should have been called assert mocked_plugin.create_media_manager_item.call_count == 0, \ u'The create_media_manager_item() method should not have been called.' @@ -94,8 +94,8 @@ class TestPluginManager(TestCase): # WHEN: We run hook_settings_tabs() plugin_manager.hook_settings_tabs() - # THEN: The create_settings_Tab() method should not have been called, but the plugins lists should be the same - assert mocked_plugin.create_settings_Tab.call_count == 0, \ + # THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same + assert mocked_plugin.create_settings_tab.call_count == 0, \ u'The create_media_manager_item() method should not have been called.' self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins, u'The plugins on the settings form should be the same as the plugins in the plugin manager') @@ -117,7 +117,7 @@ class TestPluginManager(TestCase): plugin_manager.hook_settings_tabs() # THEN: The create_media_manager_item() method should have been called with the mocked settings form - assert mocked_plugin.create_settings_Tab.call_count == 1, \ + assert mocked_plugin.create_settings_tab.call_count == 1, \ u'The create_media_manager_item() method should have been called once.' self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins, u'The plugins on the settings form should be the same as the plugins in the plugin manager') @@ -135,8 +135,8 @@ class TestPluginManager(TestCase): # WHEN: We run hook_settings_tabs() plugin_manager.hook_settings_tabs() - # THEN: The create_settings_Tab() method should have been called - mocked_plugin.create_settings_Tab.assert_called_with(self.mocked_settings_form) + # THEN: The create_settings_tab() method should have been called + mocked_plugin.create_settings_tab.assert_called_with(self.mocked_settings_form) def hook_import_menu_with_disabled_plugin_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_screen.py b/tests/functional/openlp_core_lib/test_screen.py index 007889a12..7c1bcaf24 100644 --- a/tests/functional/openlp_core_lib/test_screen.py +++ b/tests/functional/openlp_core_lib/test_screen.py @@ -1,7 +1,7 @@ """ Package to test the openlp.core.lib.screenlist package. """ -import copy + from unittest import TestCase from mock import MagicMock diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index d50ddc978..e051f8c76 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -210,7 +210,6 @@ class TestServiceItem(TestCase): # THEN: We should get back a valid service item assert service_item.is_valid is True, u'The new service item should be valid' - print service_item.get_rendered_frame(0) assert service_item.get_rendered_frame(0) == test_file, u'The first frame should match the path to the image' assert service_item.get_frames()[0] == frame_array, u'The return should match frame array1' assert service_item.get_frame_path(0) == test_file, u'The frame path should match the full path to the image' @@ -268,6 +267,26 @@ class TestServiceItem(TestCase): assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ u'This service item should be able to have new items added to it' + def serviceitem_migrate_test_20_22(self): + """ + Test the Service Item - migrating a media only service item from 2.0 to 2.2 format + """ + # GIVEN: A new service item and a mocked add icon function + service_item = ServiceItem(None) + service_item.add_icon = MagicMock() + + # WHEN: adding an media from a saved Service and mocked exists + line = self.convert_file_service_item(u'migrate_video_20_22.osd') + with patch('os.path.exists'): + service_item.set_from_service(line, TEST_PATH) + + # THEN: We should get back a converted service item + assert service_item.is_valid is True, u'The new service item should be valid' + assert service_item.processor is None, u'The Processor should have been set' + assert service_item.title is None, u'The title should be set to a value' + assert service_item.is_capable(ItemCapabilities.HasDetailedTitleDisplay) is False, \ + u'The Capability should have been removed' + def convert_file_service_item(self, name): service_file = os.path.join(TEST_PATH, name) try: @@ -276,5 +295,7 @@ class TestServiceItem(TestCase): first_line = items[0] except IOError: first_line = u'' + finally: + open_file.close() return first_line diff --git a/tests/functional/openlp_core_lib/test_settings.py b/tests/functional/openlp_core_lib/test_settings.py index 827bfa156..786a884a0 100644 --- a/tests/functional/openlp_core_lib/test_settings.py +++ b/tests/functional/openlp_core_lib/test_settings.py @@ -11,7 +11,9 @@ from PyQt4 import QtGui class TestSettings(TestCase): - + """ + Test the functions in the Settings module + """ def setUp(self): """ Create the UI @@ -35,16 +37,16 @@ class TestSettings(TestCase): # GIVEN: A new Settings setup # WHEN reading a setting for the first time - default_value = Settings().value(u'general/has run wizard') + default_value = Settings().value(u'core/has run wizard') # THEN the default value is returned assert default_value is False, u'The default value should be False' # WHEN a new value is saved into config - Settings().setValue(u'general/has run wizard', True) + Settings().setValue(u'core/has run wizard', True) # THEN the new value is returned when re-read - assert Settings().value(u'general/has run wizard') is True, u'The saved value should have been returned' + assert Settings().value(u'core/has run wizard') is True, u'The saved value should have been returned' def settings_override_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_uistrings.py b/tests/functional/openlp_core_lib/test_uistrings.py index 3351657d1..0070533db 100644 --- a/tests/functional/openlp_core_lib/test_uistrings.py +++ b/tests/functional/openlp_core_lib/test_uistrings.py @@ -6,6 +6,7 @@ from unittest import TestCase from openlp.core.lib import UiStrings + class TestUiStrings(TestCase): def check_same_instance_test(self): diff --git a/tests/functional/openlp_core_utils/test_applocation.py b/tests/functional/openlp_core_utils/test_applocation.py index 5473da8c0..b59f41f37 100644 --- a/tests/functional/openlp_core_utils/test_applocation.py +++ b/tests/functional/openlp_core_utils/test_applocation.py @@ -30,8 +30,10 @@ class TestAppLocation(TestCase): mocked_get_directory.return_value = u'test/dir' mocked_check_directory_exists.return_value = True mocked_os.path.normpath.return_value = u'test/dir' + # WHEN: we call AppLocation.get_data_path() data_path = AppLocation.get_data_path() + # THEN: check that all the correct methods were called, and the result is correct mocked_settings.contains.assert_called_with(u'advanced/data path') mocked_get_directory.assert_called_with(AppLocation.DataDir) @@ -49,8 +51,10 @@ class TestAppLocation(TestCase): mocked_settings.contains.return_value = True mocked_settings.value.return_value.toString.return_value = u'custom/dir' mocked_os.path.normpath.return_value = u'custom/dir' + # WHEN: we call AppLocation.get_data_path() data_path = AppLocation.get_data_path() + # THEN: the mocked Settings methods were called and the value returned was our set up value mocked_settings.contains.assert_called_with(u'advanced/data path') mocked_settings.value.assert_called_with(u'advanced/data path') @@ -100,8 +104,10 @@ class TestAppLocation(TestCase): # GIVEN: A mocked out AppLocation.get_data_path() mocked_get_data_path.return_value = u'test/dir' mocked_check_directory_exists.return_value = True + # WHEN: we call AppLocation.get_data_path() data_path = AppLocation.get_section_data_path(u'section') + # THEN: check that all the correct methods were called, and the result is correct mocked_check_directory_exists.assert_called_with(u'test/dir/section') assert data_path == u'test/dir/section', u'Result should be "test/dir/section"' @@ -112,8 +118,10 @@ class TestAppLocation(TestCase): """ with patch(u'openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path: mocked_get_frozen_path.return_value = u'app/dir' + # WHEN: We call AppLocation.get_directory directory = AppLocation.get_directory(AppLocation.AppDir) + # THEN: assert directory == u'app/dir', u'Directory should be "app/dir"' @@ -130,8 +138,10 @@ class TestAppLocation(TestCase): mocked_get_frozen_path.return_value = u'plugins/dir' mocked_sys.frozen = 1 mocked_sys.argv = ['openlp'] + # WHEN: We call AppLocation.get_directory directory = AppLocation.get_directory(AppLocation.PluginsDir) + # THEN: assert directory == u'plugins/dir', u'Directory should be "plugins/dir"' diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index b9decb37e..8e3a427ed 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -5,7 +5,9 @@ from unittest import TestCase from mock import patch -from openlp.core.utils import get_filesystem_encoding, _get_frozen_path, get_locale_key, get_natural_key +from openlp.core.utils import clean_filename, get_filesystem_encoding, _get_frozen_path, get_locale_key, \ + get_natural_key, split_filename + class TestUtils(TestCase): """ @@ -56,6 +58,53 @@ class TestUtils(TestCase): # THEN: The frozen parameter is returned assert _get_frozen_path(u'frozen', u'not frozen') == u'frozen', u'Should return "frozen"' + def split_filename_with_file_path_test(self): + """ + Test the split_filename() function with a path to a file + """ + # GIVEN: A path to a file. + file_path = u'/home/user/myfile.txt' + wanted_result = (u'/home/user', u'myfile.txt') + with patch(u'openlp.core.utils.os.path.isfile') as mocked_is_file: + mocked_is_file.return_value = True + + # WHEN: Split the file name. + result = split_filename(file_path) + + # THEN: A tuple should be returned. + assert result == wanted_result, u'A tuple with the directory and file name should have been returned.' + + def split_filename_with_dir_path_test(self): + """ + Test the split_filename() function with a path to a directory + """ + # GIVEN: A path to a dir. + file_path = u'/home/user/mydir' + wanted_result = (u'/home/user/mydir', u'') + with patch(u'openlp.core.utils.os.path.isfile') as mocked_is_file: + mocked_is_file.return_value = False + + # WHEN: Split the file name. + result = split_filename(file_path) + + # THEN: A tuple should be returned. + assert result == wanted_result, \ + u'A two-entry tuple with the directory and file name (empty) should have been returned.' + + def clean_filename_test(self): + """ + Test the clean_filename() function + """ + # GIVEN: A invalid file name and the valid file name. + invalid_name = u'A_file_with_invalid_characters_[\\/:\*\?"<>\|\+\[\]%].py' + wanted_name = u'A_file_with_invalid_characters______________________.py' + + # WHEN: Clean the name. + result = clean_filename(invalid_name) + + # THEN: The file name should be cleaned. + assert result == wanted_name, u'The file name should not contain any special characters.' + def get_locale_key_test(self): """ Test the get_locale_key(string) function @@ -82,4 +131,3 @@ class TestUtils(TestCase): # THEN: We get a properly sorted list test_passes = sorted(unsorted_list, key=get_natural_key) == [u'1st item', u'item 3b', u'item 10a'] assert test_passes, u'Numbers should be sorted naturally' - diff --git a/tests/functional/openlp_plugins/images/test_lib.py b/tests/functional/openlp_plugins/images/test_lib.py index a355e956b..0aecc0a7f 100644 --- a/tests/functional/openlp_plugins/images/test_lib.py +++ b/tests/functional/openlp_plugins/images/test_lib.py @@ -9,7 +9,7 @@ from unittest import TestCase from mock import MagicMock, patch from openlp.core.lib import Registry -from openlp.plugins.images.lib.db import ImageFilenames +from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups from openlp.plugins.images.lib.mediaitem import ImageMediaItem @@ -23,6 +23,7 @@ class TestImageMediaItem(TestCase): Registry.create() Registry().register(u'service_list', MagicMock()) Registry().register(u'main_window', self.mocked_main_window) + Registry().register(u'live_controller', MagicMock()) mocked_parent = MagicMock() mocked_plugin = MagicMock() with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.__init__') as mocked_init: @@ -35,7 +36,7 @@ class TestImageMediaItem(TestCase): """ # GIVEN: An empty image_list image_list = [] - with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList: + with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: self.media_item.manager = MagicMock() # WHEN: We run save_new_images_list with the empty list @@ -47,37 +48,37 @@ class TestImageMediaItem(TestCase): def save_new_images_list_single_image_with_reload_test(self): """ - Test that the save_new_images_list() calls loadFullList() when reload_list is set to True + Test that the save_new_images_list() calls load_full_list() when reload_list is set to True """ # GIVEN: A list with 1 image image_list = [ u'test_image.jpg' ] - with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList: + with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: ImageFilenames.filename = '' self.media_item.manager = MagicMock() # WHEN: We run save_new_images_list with reload_list=True self.media_item.save_new_images_list(image_list, reload_list=True) - # THEN: loadFullList() should have been called - assert mocked_loadFullList.call_count == 1, u'loadFullList() should have been called' + # THEN: load_full_list() should have been called + assert mocked_load_full_list.call_count == 1, u'load_full_list() should have been called' # CLEANUP: Remove added attribute from ImageFilenames delattr(ImageFilenames, 'filename') def save_new_images_list_single_image_without_reload_test(self): """ - Test that the save_new_images_list() doesn't call loadFullList() when reload_list is set to False + Test that the save_new_images_list() doesn't call load_full_list() when reload_list is set to False """ # GIVEN: A list with 1 image image_list = [ u'test_image.jpg' ] - with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList: + with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: self.media_item.manager = MagicMock() # WHEN: We run save_new_images_list with reload_list=False self.media_item.save_new_images_list(image_list, reload_list=False) - # THEN: loadFullList() should not have been called - assert mocked_loadFullList.call_count == 0, u'loadFullList() should not have been called' + # THEN: load_full_list() should not have been called + assert mocked_load_full_list.call_count == 0, u'load_full_list() should not have been called' def save_new_images_list_multiple_images_test(self): """ @@ -85,15 +86,15 @@ class TestImageMediaItem(TestCase): """ # GIVEN: A list with 3 images image_list = [ u'test_image_1.jpg', u'test_image_2.jpg', u'test_image_3.jpg' ] - with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList: + with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: self.media_item.manager = MagicMock() # WHEN: We run save_new_images_list with the list of 3 images self.media_item.save_new_images_list(image_list, reload_list=False) - # THEN: loadFullList() should not have been called + # THEN: load_full_list() should not have been called assert self.media_item.manager.save_object.call_count == 3, \ - u'loadFullList() should have been called three times' + u'load_full_list() should have been called three times' def save_new_images_list_other_objects_in_list_test(self): """ @@ -101,12 +102,77 @@ class TestImageMediaItem(TestCase): """ # GIVEN: A list with images and objects image_list = [ u'test_image_1.jpg', None, True, ImageFilenames(), 'test_image_2.jpg' ] - with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList: + with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: self.media_item.manager = MagicMock() # WHEN: We run save_new_images_list with the list of images and objects self.media_item.save_new_images_list(image_list, reload_list=False) - # THEN: loadFullList() should not have been called + # THEN: load_full_list() should not have been called assert self.media_item.manager.save_object.call_count == 2, \ - u'loadFullList() should have been called only once' + u'load_full_list() should have been called only once' + + def on_reset_click_test(self): + """ + Test that on_reset_click() actually resets the background + """ + # GIVEN: A mocked version of reset_action + self.media_item.reset_action = MagicMock() + + # WHEN: on_reset_click is called + self.media_item.on_reset_click() + + # THEN: the reset_action should be set visible, and the image should be reset + self.media_item.reset_action.setVisible.assert_called_with(False) + self.media_item.live_controller.display.reset_image.assert_called_with() + + def _recursively_delete_group_side_effect(*args, **kwargs): + """ + Side effect method that creates custom retun values for the recursively_delete_group method + """ + if args[1] == ImageFilenames and args[2]: + # Create some fake objects that should be removed + returned_object1 = ImageFilenames() + returned_object1.id = 1 + returned_object1.filename = u'/tmp/test_file_1.jpg' + returned_object2 = ImageFilenames() + returned_object2.id = 2 + returned_object2.filename = u'/tmp/test_file_2.jpg' + returned_object3 = ImageFilenames() + returned_object3.id = 3 + returned_object3.filename = u'/tmp/test_file_3.jpg' + return [returned_object1, returned_object2, returned_object3] + if args[1] == ImageGroups and args[2]: + # Change the parent_id that is matched so we don't get into an endless loop + ImageGroups.parent_id = 0 + # Create a fake group that will be used in the next run + returned_object1 = ImageGroups() + returned_object1.id = 1 + return [returned_object1] + return [] + + def recursively_delete_group_test(self): + """ + Test that recursively_delete_group() works + """ + # GIVEN: An ImageGroups object and mocked functions + with patch(u'openlp.core.utils.delete_file') as mocked_delete_file: + ImageFilenames.group_id = 1 + ImageGroups.parent_id = 1 + self.media_item.manager = MagicMock() + self.media_item.manager.get_all_objects.side_effect = self._recursively_delete_group_side_effect + self.media_item.servicePath = "" + test_group = ImageGroups() + test_group.id = 1 + + # WHEN: recursively_delete_group() is called + self.media_item.recursively_delete_group(test_group) + + # THEN: + assert mocked_delete_file.call_count == 0, u'delete_file() should not be called' + assert self.media_item.manager.delete_object.call_count == 7, \ + u'manager.delete_object() should be called exactly 7 times' + + # CLEANUP: Remove added attribute from ImageFilenames and ImageGroups + delattr(ImageFilenames, 'group_id') + delattr(ImageGroups, 'parent_id') diff --git a/tests/functional/openlp_plugins/remotes/__init__.py b/tests/functional/openlp_plugins/remotes/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py new file mode 100644 index 000000000..22bee8139 --- /dev/null +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -0,0 +1,108 @@ +""" +This module contains tests for the lib submodule of the Remotes plugin. +""" +import os + +from unittest import TestCase +from tempfile import mkstemp +from mock import patch + +from openlp.core.lib import Settings +from openlp.plugins.remotes.lib.remotetab import RemoteTab + +from PyQt4 import QtGui + +__default_settings__ = { + u'remotes/twelve hour': True, + 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' +} + +ZERO_URL = u'0.0.0.0' + +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'..', u'resources')) + + +class TestRemoteTab(TestCase): + """ + Test the functions in the :mod:`lib` module. + """ + def setUp(self): + """ + Create the UI + """ + fd, self.ini_file = mkstemp(u'.ini') + Settings().set_filename(self.ini_file) + self.application = QtGui.QApplication.instance() + Settings().extend_default_settings(__default_settings__) + self.parent = QtGui.QMainWindow() + self.form = RemoteTab(self.parent, u'Remotes', None, None) + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.application + del self.parent + del self.form + os.unlink(self.ini_file) + + def set_basic_urls_test(self): + """ + Test the set_urls function with standard defaults + """ + # GIVEN: A mocked location + with patch(u'openlp.core.utils.applocation.Settings') as mocked_class, \ + patch(u'openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ + patch(u'openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch(u'openlp.core.utils.applocation.os') as mocked_os: + # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() + mocked_settings = mocked_class.return_value + mocked_settings.contains.return_value = False + mocked_get_directory.return_value = u'test/dir' + mocked_check_directory_exists.return_value = True + mocked_os.path.normpath.return_value = u'test/dir' + + # WHEN: when the set_urls is called having reloaded the form. + self.form.load() + self.form.set_urls() + # THEN: the following screen values should be set + self.assertEqual(self.form.address_edit.text(), ZERO_URL, u'The default URL should be set on the screen') + self.assertEqual(self.form.https_settings_group_box.isEnabled(), False, + u'The Https box should not be enabled') + self.assertEqual(self.form.https_settings_group_box.isChecked(), False, + u'The Https checked box should note be Checked') + self.assertEqual(self.form.user_login_group_box.isChecked(), False, + u'The authentication box should not be enabled') + + def set_certificate_urls_test(self): + """ + Test the set_urls function with certificate available + """ + # GIVEN: A mocked location + with patch(u'openlp.core.utils.applocation.Settings') as mocked_class, \ + patch(u'openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ + patch(u'openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch(u'openlp.core.utils.applocation.os') as mocked_os: + # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() + mocked_settings = mocked_class.return_value + mocked_settings.contains.return_value = False + mocked_get_directory.return_value = TEST_PATH + mocked_check_directory_exists.return_value = True + mocked_os.path.normpath.return_value = TEST_PATH + + # WHEN: when the set_urls is called having reloaded the form. + self.form.load() + self.form.set_urls() + # THEN: the following screen values should be set + self.assertEqual(self.form.http_settings_group_box.isEnabled(), True, + u'The Http group box should be enabled') + self.assertEqual(self.form.https_settings_group_box.isChecked(), False, + u'The Https checked box should be Checked') + self.assertEqual(self.form.https_settings_group_box.isEnabled(), True, + u'The Https box should be enabled') diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py new file mode 100644 index 000000000..2980a339b --- /dev/null +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -0,0 +1,99 @@ +""" +This module contains tests for the lib submodule of the Remotes plugin. +""" +import os + +from unittest import TestCase +from tempfile import mkstemp +from mock import MagicMock + +from openlp.core.lib import Settings +from openlp.plugins.remotes.lib.httpserver import HttpRouter, fetch_password, make_sha_hash +from PyQt4 import QtGui + +__default_settings__ = { + u'remotes/twelve hour': True, + 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' +} + + +class TestRouter(TestCase): + """ + Test the functions in the :mod:`lib` module. + """ + def setUp(self): + """ + Create the UI + """ + fd, self.ini_file = mkstemp(u'.ini') + Settings().set_filename(self.ini_file) + self.application = QtGui.QApplication.instance() + Settings().extend_default_settings(__default_settings__) + self.router = HttpRouter() + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.application + os.unlink(self.ini_file) + + def fetch_password_unknown_test(self): + """ + Test the fetch password code with an unknown userid + """ + # GIVEN: A default configuration + # WHEN: called with the defined userid + password = fetch_password(u'itwinkle') + + # THEN: the function should return None + self.assertEqual(password, None, u'The result for fetch_password should be None') + + def fetch_password_known_test(self): + """ + Test the fetch password code with the defined userid + """ + # GIVEN: A default configuration + # WHEN: called with the defined userid + password = fetch_password(u'openlp') + required_password = make_sha_hash(u'password') + + # THEN: the function should return the correct password + self.assertEqual(password, required_password, u'The result for fetch_password should be the defined password') + + def sha_password_encrypter_test(self): + """ + Test hash password function + """ + # GIVEN: A default configuration + # WHEN: called with the defined userid + required_password = make_sha_hash(u'password') + test_value = u'5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8' + + # THEN: the function should return the correct password + self.assertEqual(required_password, test_value, + u'The result for make_sha_hash should return the correct encrypted password') + + def process_http_request_test(self): + """ + Test the router control functionality + """ + # GIVEN: A testing set of Routes + mocked_function = MagicMock() + test_route = [ + (r'^/stage/api/poll$', mocked_function), + ] + self.router.routes = test_route + + # WHEN: called with a poll route + self.router.process_http_request(u'/stage/api/poll', None) + + # THEN: the function should have been called only once + assert mocked_function.call_count == 1, \ + u'The mocked function should have been matched and called once.' diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py new file mode 100644 index 000000000..37f1c76e4 --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -0,0 +1,125 @@ +""" +This module contains tests for the lib submodule of the Songs plugin. +""" +import os +from tempfile import mkstemp +from unittest import TestCase + +from mock import patch, MagicMock + +from PyQt4 import QtGui + +from openlp.core.lib import Registry, ServiceItem, Settings + +from openlp.plugins.songs.lib.mediaitem import SongMediaItem + + +class TestMediaItem(TestCase): + """ + Test the functions in the :mod:`lib` module. + """ + def setUp(self): + """ + Set up the components need for all tests. + """ + Registry.create() + Registry().register(u'service_list', MagicMock()) + Registry().register(u'main_window', MagicMock()) + with patch('openlp.core.lib.mediamanageritem.MediaManagerItem.__init__'), \ + patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'): + self.media_item = SongMediaItem(MagicMock(), MagicMock()) + + fd, self.ini_file = mkstemp(u'.ini') + Settings().set_filename(self.ini_file) + self.application = QtGui.QApplication.instance() + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.application + # Not all tests use settings! + try: + os.unlink(self.ini_file) + os.unlink(Settings().fileName()) + except Exception: + pass + + def build_song_footer_one_author_test(self): + """ + Test build songs footer with basic song and one author + """ + # GIVEN: A Song and a Service Item + mock_song = MagicMock() + mock_song.title = u'My Song' + mock_author = MagicMock() + mock_author.display_name = u'my author' + mock_song.authors = [] + mock_song.authors.append(mock_author) + mock_song.copyright = u'My copyright' + service_item = ServiceItem(None) + + # WHEN: I generate the Footer with default settings + author_list = self.media_item.generate_footer(service_item, mock_song) + + # THEN: I get the following Array returned + self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright'], + u'The array should be returned correctly with a song, one author and copyright') + self.assertEqual(author_list, [u'my author'], + u'The author list should be returned correctly with one author') + + def build_song_footer_two_authors_test(self): + """ + Test build songs footer with basic song and two authors + """ + # GIVEN: A Song and a Service Item + mock_song = MagicMock() + mock_song.title = u'My Song' + mock_author = MagicMock() + mock_author.display_name = u'my author' + mock_song.authors = [] + mock_song.authors.append(mock_author) + mock_author = MagicMock() + mock_author.display_name = u'another author' + mock_song.authors.append(mock_author) + mock_song.copyright = u'My copyright' + service_item = ServiceItem(None) + + # WHEN: I generate the Footer with default settings + author_list = self.media_item.generate_footer(service_item, mock_song) + + # THEN: I get the following Array returned + self.assertEqual(service_item.raw_footer, [u'My Song', u'my author and another author', u'My copyright'], + u'The array should be returned correctly with a song, two authors and copyright') + self.assertEqual(author_list, [u'my author', u'another author'], + u'The author list should be returned correctly with two authors') + + def build_song_footer_base_ccli_test(self): + """ + Test build songs footer with basic song and two authors + """ + # GIVEN: A Song and a Service Item and a configured CCLI license + mock_song = MagicMock() + mock_song.title = u'My Song' + mock_author = MagicMock() + mock_author.display_name = u'my author' + mock_song.authors = [] + mock_song.authors.append(mock_author) + mock_song.copyright = u'My copyright' + service_item = ServiceItem(None) + Settings().setValue(u'core/ccli number', u'1234') + + # WHEN: I generate the Footer with default settings + self.media_item.generate_footer(service_item, mock_song) + + # THEN: I get the following Array returned + self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright', u'CCLI License: 1234'], + u'The array should be returned correctly with a song, an author, copyright and ccli') + + # WHEN: I amend the CCLI value + Settings().setValue(u'core/ccli number', u'4321') + self.media_item.generate_footer(service_item, mock_song) + + # THEN: I would get an amended footer string + self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright', u'CCLI License: 4321'], + u'The array should be returned correctly with a song, an author, copyright and amended ccli') diff --git a/tests/interfaces/openlp_plugins/custom/forms/test_customform.py b/tests/interfaces/openlp_plugins/custom/forms/test_customform.py index 6e6318ca6..41671403b 100644 --- a/tests/interfaces/openlp_plugins/custom/forms/test_customform.py +++ b/tests/interfaces/openlp_plugins/custom/forms/test_customform.py @@ -36,19 +36,29 @@ class TestEditCustomForm(TestCase): del self.main_window del self.app + def load_themes_test(self): + """ + Test the load_themes() method. + """ + # GIVEN: A theme list. + theme_list = [u'First Theme', u'Second Theme'] + + # WHEN: Show the dialog and add pass a theme list. + self.form.load_themes(theme_list) + + # THEN: There should be three items in the combo box. + assert self.form.theme_combo_box.count() == 3, u'There should be three items (themes) in the combo box.' + def load_custom_test(self): """ Test the load_custom() method. """ - # GIVEN: A mocked QDialog.exec_() method - with patch(u'PyQt4.QtGui.QDialog.exec_') as mocked_exec: - # WHEN: Show the dialog and create a new custom item. - self.form.exec_() - self.form.load_custom(0) + # WHEN: Create a new custom item. + self.form.load_custom(0) - #THEN: The line edits should not contain any text. - self.assertEqual(self.form.title_edit.text(), u'', u'The title edit should be empty') - self.assertEqual(self.form.credit_edit.text(), u'', u'The credit edit should be empty') + # THEN: The line edits should not contain any text. + self.assertEqual(self.form.title_edit.text(), u'', u'The title edit should be empty') + self.assertEqual(self.form.credit_edit.text(), u'', u'The credit edit should be empty') def on_add_button_clicked_test(self): @@ -57,8 +67,50 @@ class TestEditCustomForm(TestCase): """ # GIVEN: A mocked QDialog.exec_() method with patch(u'PyQt4.QtGui.QDialog.exec_') as mocked_exec: - # WHEN: Show the dialog and add a new slide. - self.form.exec_() + # WHEN: Add a new slide. QtTest.QTest.mouseClick(self.form.add_button, QtCore.Qt.LeftButton) - #THEN: One slide should be added. + + # THEN: One slide should be added. assert self.form.slide_list_view.count() == 1, u'There should be one slide added.' + + def validate_not_valid_part1_test(self): + """ + Test the _validate() method. + """ + # GIVEN: Mocked methods. + with patch(u'openlp.plugins.custom.forms.editcustomform.critical_error_message_box') as \ + mocked_critical_error_message_box: + mocked_displayText = MagicMock() + mocked_displayText.return_value = u'' + self.form.title_edit.displayText = mocked_displayText + mocked_setFocus = MagicMock() + self.form.title_edit.setFocus = mocked_setFocus + + # WHEN: Call the method. + result = self.form._validate() + + # THEN: The validate method should have returned False. + assert not result, u'The _validate() method should have retured False' + mocked_setFocus.assert_called_with() + mocked_critical_error_message_box.assert_called_with(message=u'You need to type in a title.') + + def validate_not_valid_part2_test(self): + """ + Test the _validate() method. + """ + # GIVEN: Mocked methods. + with patch(u'openlp.plugins.custom.forms.editcustomform.critical_error_message_box') as \ + mocked_critical_error_message_box: + mocked_displayText = MagicMock() + mocked_displayText.return_value = u'something' + self.form.title_edit.displayText = mocked_displayText + mocked_count = MagicMock() + mocked_count.return_value = 0 + self.form.slide_list_view.count = mocked_count + + # WHEN: Call the method. + result = self.form._validate() + + # THEN: The validate method should have returned False. + assert not result, u'The _validate() method should have retured False' + mocked_critical_error_message_box.assert_called_with(message=u'You need to add at least one slide.') diff --git a/tests/interfaces/openlp_plugins/remotes/__init__.py b/tests/interfaces/openlp_plugins/remotes/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/interfaces/openlp_plugins/remotes/test_server.py b/tests/interfaces/openlp_plugins/remotes/test_server.py new file mode 100644 index 000000000..8795eeaf3 --- /dev/null +++ b/tests/interfaces/openlp_plugins/remotes/test_server.py @@ -0,0 +1,138 @@ +""" +This module contains tests for the lib submodule of the Remotes plugin. +""" +import os + +from unittest import TestCase +from tempfile import mkstemp +from mock import MagicMock +import urllib2 +import cherrypy + +from BeautifulSoup import BeautifulSoup + +from openlp.core.lib import Settings +from openlp.plugins.remotes.lib.httpserver import HttpServer +from PyQt4 import QtGui + +__default_settings__ = { + u'remotes/twelve hour': True, + 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' +} + + +class TestRouter(TestCase): + """ + Test the functions in the :mod:`lib` module. + """ + def setUp(self): + """ + Create the UI + """ + fd, self.ini_file = mkstemp(u'.ini') + Settings().set_filename(self.ini_file) + self.application = QtGui.QApplication.instance() + Settings().extend_default_settings(__default_settings__) + self.server = HttpServer() + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.application + os.unlink(self.ini_file) + self.server.close() + + def start_server(self): + """ + Common function to start server then mock out the router. CherryPy crashes if you mock before you start + """ + self.server.start_server() + self.server.router = MagicMock() + self.server.router.process_http_request = process_http_request + + def start_default_server_test(self): + """ + Test the default server serves the correct initial page + """ + # GIVEN: A default configuration + Settings().setValue(u'remotes/authentication enabled', False) + self.start_server() + + # WHEN: called the route location + code, page = call_remote_server(u'http://localhost:4316') + + # THEN: default title will be returned + self.assertEqual(BeautifulSoup(page).title.text, u'OpenLP 2.1 Remote', + u'The default menu should be returned') + + def start_authenticating_server_test(self): + """ + Test the default server serves the correctly with authentication + """ + # GIVEN: A default authorised configuration + Settings().setValue(u'remotes/authentication enabled', True) + self.start_server() + + # WHEN: called the route location with no user details + code, page = call_remote_server(u'http://localhost:4316') + + # THEN: then server will ask for details + self.assertEqual(code, 401, u'The basic authorisation request should be returned') + + # WHEN: called the route location with user details + code, page = call_remote_server(u'http://localhost:4316', u'openlp', u'password') + + # THEN: default title will be returned + self.assertEqual(BeautifulSoup(page).title.text, u'OpenLP 2.1 Remote', + u'The default menu should be returned') + + # WHEN: called the route location with incorrect user details + code, page = call_remote_server(u'http://localhost:4316', u'itwinkle', u'password') + + # THEN: then server will ask for details + self.assertEqual(code, 401, u'The basic authorisation request should be returned') + + +def call_remote_server(url, username=None, password=None): + """ + Helper function + + ``username`` + The username. + + ``password`` + The password. + """ + if username: + passman = urllib2.HTTPPasswordMgrWithDefaultRealm() + passman.add_password(None, url, username, password) + authhandler = urllib2.HTTPBasicAuthHandler(passman) + opener = urllib2.build_opener(authhandler) + urllib2.install_opener(opener) + try: + page = urllib2.urlopen(url) + return 0, page.read() + except urllib2.HTTPError, e: + return e.code, u'' + + +def process_http_request(url_path, *args): + """ + Override function to make the Mock work but does nothing. + + ``Url_path`` + The url_path. + + ``*args`` + Some args. + """ + cherrypy.response.status = 200 + return None + diff --git a/tests/resources/migrate_video_20_22.osd b/tests/resources/migrate_video_20_22.osd new file mode 100644 index 000000000..ee4b2c0c4 --- /dev/null +++ b/tests/resources/migrate_video_20_22.osd @@ -0,0 +1,98 @@ +(lp1 +(dp2 +Vserviceitem +p3 +(dp4 +Vheader +p5 +(dp6 +Vxml_version +p7 +NsVauto_play_slides_loop +p8 +I00 +sVauto_play_slides_once +p9 +I00 +sVwill_auto_start +p10 +I01 +sVtitle +p11 +VVLC +p12 +sVcapabilities +p13 +(lp14 +I12 +aI16 +aI4 +aI11 +asVtheme +p15 +I-1 +sVbackground_audio +p16 +(lp17 +sVicon +p18 +V:/plugins/plugin_media.png +p19 +sVtype +p20 +I3 +sVstart_time +p21 +I0 +sVfrom_plugin +p22 +I00 +sVmedia_length +p23 +I144 +sVdata +p24 +V +sVtimed_slide_interval +p25 +I0 +sVaudit +p26 +V +sVsearch +p27 +V +sVname +p28 +Vmedia +p29 +sVfooter +p30 +(lp31 +sVnotes +p32 +V +sVplugin +p33 +g29 +sVtheme_overwritten +p34 +I00 +sVend_time +p35 +I0 +ssg24 +(lp36 +(dp37 +Vpath +p38 +V/home/tim/Videos/puppets +p39 +sVimage +p40 +V:/media/slidecontroller_multimedia.png +p41 +sg11 +VMVI_3405.MOV +p42 +sassa. \ No newline at end of file diff --git a/tests/resources/remotes/openlp.crt b/tests/resources/remotes/openlp.crt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/resources/remotes/openlp.key b/tests/resources/remotes/openlp.key new file mode 100644 index 000000000..e69de29bb