diff --git a/.bzrignore b/.bzrignore index c7abfd456..01c96078a 100644 --- a/.bzrignore +++ b/.bzrignore @@ -44,3 +44,4 @@ coverage tags output htmlcov +openlp-test-projectordb.sqlite diff --git a/openlp.py b/openlp.py index 73c0d9033..02287cd5e 100755 --- a/openlp.py +++ b/openlp.py @@ -27,16 +27,26 @@ import faulthandler import multiprocessing import sys +from openlp.core.app import main from openlp.core.common import is_win, is_macosx from openlp.core.common.applocation import AppLocation -from openlp.core import main +from openlp.core.common.path import create_paths + + +def set_up_fault_handling(): + """ + Set up the Python fault handler + """ + # Create the cache directory if it doesn't exist, and enable the fault handler to log to an error log file + create_paths(AppLocation.get_directory(AppLocation.CacheDir)) + faulthandler.enable(open(str(AppLocation.get_directory(AppLocation.CacheDir) / 'error.log'), 'wb')) -faulthandler.enable(open(str(AppLocation.get_directory(AppLocation.CacheDir) / 'error.log'), 'wb')) if __name__ == '__main__': """ Instantiate and run the application. """ + set_up_fault_handling() # Add support for using multiprocessing from frozen Windows executable (built using PyInstaller), # see https://docs.python.org/3/library/multiprocessing.html#multiprocessing.freeze_support if is_win(): diff --git a/openlp/__init__.py b/openlp/__init__.py index 16d57e9af..2074ec39a 100644 --- a/openlp/__init__.py +++ b/openlp/__init__.py @@ -22,7 +22,3 @@ """ The :mod:`openlp` module contains all the project produced OpenLP functionality """ - -from openlp import core, plugins - -__all__ = ['core', 'plugins'] diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index c9e446143..b3fa9b6a6 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -26,396 +26,3 @@ The :mod:`core` module provides all core application functions All the core functions of the OpenLP application including the GUI, settings, logging and a plugin framework are contained within the openlp.core module. """ -import argparse -import logging -import sys -import time -from datetime import datetime -from traceback import format_exception - -from PyQt5 import QtCore, QtGui, QtWidgets - -from openlp.core.common import Registry, OpenLPMixin, AppLocation, LanguageManager, Settings, UiStrings, \ - check_directory_exists, is_macosx, is_win, translate -from openlp.core.common.path import Path, copytree -from openlp.core.version import check_for_update, get_version -from openlp.core.lib import ScreenList -from openlp.core.resources import qInitResources -from openlp.core.ui import SplashScreen -from openlp.core.ui.exceptionform import ExceptionForm -from openlp.core.ui.firsttimeform import FirstTimeForm -from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm -from openlp.core.ui.mainwindow import MainWindow -from openlp.core.ui.style import get_application_stylesheet - - -__all__ = ['OpenLP', 'main'] - - -log = logging.getLogger() - - -class OpenLP(OpenLPMixin, QtWidgets.QApplication): - """ - The core application class. This class inherits from Qt's QApplication - class in order to provide the core of the application. - """ - - args = [] - - def exec(self): - """ - Override exec method to allow the shared memory to be released on exit - """ - self.is_event_loop_active = True - result = QtWidgets.QApplication.exec() - self.shared_memory.detach() - return result - - def run(self, args): - """ - Run the OpenLP application. - - :param args: Some Args - """ - self.is_event_loop_active = False - # On Windows, the args passed into the constructor are ignored. Not very handy, so set the ones we want to use. - # On Linux and FreeBSD, in order to set the WM_CLASS property for X11, we pass "OpenLP" in as a command line - # argument. This interferes with files being passed in as command line arguments, so we remove it from the list. - if 'OpenLP' in args: - args.remove('OpenLP') - self.args.extend(args) - # Decide how many screens we have and their size - screens = ScreenList.create(self.desktop()) - # First time checks in settings - has_run_wizard = Settings().value('core/has run wizard') - if not has_run_wizard: - ftw = FirstTimeForm() - ftw.initialize(screens) - if ftw.exec() == QtWidgets.QDialog.Accepted: - Settings().setValue('core/has run wizard', True) - elif ftw.was_cancelled: - QtCore.QCoreApplication.exit() - sys.exit() - # Correct stylesheet bugs - application_stylesheet = get_application_stylesheet() - if application_stylesheet: - self.setStyleSheet(application_stylesheet) - can_show_splash = Settings().value('core/show splash') - if can_show_splash: - self.splash = SplashScreen() - self.splash.show() - # make sure Qt really display the splash screen - self.processEvents() - # Check if OpenLP has been upgrade and if a backup of data should be created - self.backup_on_upgrade(has_run_wizard, can_show_splash) - # start the main app window - self.main_window = MainWindow() - Registry().execute('bootstrap_initialise') - Registry().execute('bootstrap_post_set_up') - Registry().initialise = False - self.main_window.show() - if can_show_splash: - # now kill the splashscreen - self.splash.finish(self.main_window) - log.debug('Splashscreen closed') - # make sure Qt really display the splash screen - self.processEvents() - self.main_window.repaint() - self.processEvents() - if not has_run_wizard: - self.main_window.first_time() - if Settings().value('core/update check'): - check_for_update(self.main_window) - self.main_window.is_display_blank() - self.main_window.app_startup() - return self.exec() - - def is_already_running(self): - """ - Look to see if OpenLP is already running and ask if a 2nd instance is to be started. - """ - self.shared_memory = QtCore.QSharedMemory('OpenLP') - if self.shared_memory.attach(): - status = QtWidgets.QMessageBox.critical(None, UiStrings().Error, UiStrings().OpenLPStart, - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.No)) - if status == QtWidgets.QMessageBox.No: - return True - return False - else: - self.shared_memory.create(1) - return False - - def is_data_path_missing(self): - """ - Check if the data folder path exists. - """ - data_folder_path = AppLocation.get_data_path() - if not data_folder_path.exists(): - log.critical('Database was not found in: %s', data_folder_path) - status = QtWidgets.QMessageBox.critical( - None, translate('OpenLP', 'Data Directory Error'), - translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}\n\nThe location of the data folder ' - 'was previously changed from the OpenLP\'s default location. If the data was ' - 'stored on removable device, that device needs to be made available.\n\nYou may ' - 'reset the data location back to the default location, or you can try to make the ' - 'current location available.\n\nDo you want to reset to the default data location? ' - 'If not, OpenLP will be closed so you can try to fix the the problem.') - .format(path=data_folder_path), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), - QtWidgets.QMessageBox.No) - if status == QtWidgets.QMessageBox.No: - # If answer was "No", return "True", it will shutdown OpenLP in def main - log.info('User requested termination') - return True - # If answer was "Yes", remove the custom data path thus resetting the default location. - Settings().remove('advanced/data path') - log.info('Database location has been reset to the default settings.') - return False - - def hook_exception(self, exc_type, value, traceback): - """ - Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where - users cannot see it and cannot report when we encounter these problems. - - :param exc_type: The class of exception. - :param value: The actual exception object. - :param traceback: A traceback object with the details of where the exception occurred. - """ - # We can't log.exception here because the last exception no longer exists, we're actually busy handling it. - log.critical(''.join(format_exception(exc_type, value, traceback))) - if not hasattr(self, 'exception_form'): - self.exception_form = ExceptionForm() - self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exc_type, value, traceback))) - self.set_normal_cursor() - is_splash_visible = False - if hasattr(self, 'splash') and self.splash.isVisible(): - is_splash_visible = True - self.splash.hide() - self.exception_form.exec() - if is_splash_visible: - self.splash.show() - - def backup_on_upgrade(self, has_run_wizard, can_show_splash): - """ - Check if OpenLP has been upgraded, and ask if a backup of data should be made - - :param has_run_wizard: OpenLP has been run before - :param can_show_splash: Should OpenLP show the splash screen - """ - data_version = Settings().value('core/application version') - openlp_version = get_version()['version'] - # New installation, no need to create backup - if not has_run_wizard: - Settings().setValue('core/application version', openlp_version) - # If data_version is different from the current version ask if we should backup the data folder - elif data_version != openlp_version: - if can_show_splash and self.splash.isVisible(): - self.splash.hide() - if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'), - translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n' - 'a backup of the old data folder?'), - defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: - # Create copy of data folder - data_folder_path = AppLocation.get_data_path() - timestamp = time.strftime("%Y%m%d-%H%M%S") - data_folder_backup_path = data_folder_path.with_name(data_folder_path.name + '-' + timestamp) - try: - copytree(data_folder_path, data_folder_backup_path) - except OSError: - QtWidgets.QMessageBox.warning(None, translate('OpenLP', 'Backup'), - translate('OpenLP', 'Backup of the data folder failed!')) - return - message = translate('OpenLP', - 'A backup of the data folder has been created at:\n\n' - '{text}').format(text=data_folder_backup_path) - QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message) - - # Update the version in the settings - Settings().setValue('core/application version', openlp_version) - if can_show_splash: - self.splash.show() - - def process_events(self): - """ - Wrapper to make ProcessEvents visible and named correctly - """ - self.processEvents() - - def set_busy_cursor(self): - """ - Sets the Busy Cursor for the Application - """ - self.setOverrideCursor(QtCore.Qt.BusyCursor) - self.processEvents() - - def set_normal_cursor(self): - """ - Sets the Normal Cursor for the Application - """ - self.restoreOverrideCursor() - self.processEvents() - - def event(self, event): - """ - Enables platform specific event handling i.e. direct file opening on OS X - - :param event: The event - """ - if event.type() == QtCore.QEvent.FileOpen: - file_name = event.file() - log.debug('Got open file event for {name}!'.format(name=file_name)) - self.args.insert(0, file_name) - return True - # Mac OS X should restore app window when user clicked on the OpenLP icon - # in the Dock bar. However, OpenLP consists of multiple windows and this - # does not work. This workaround fixes that. - # The main OpenLP window is restored when it was previously minimized. - elif event.type() == QtCore.QEvent.ApplicationActivate: - if is_macosx() and hasattr(self, 'main_window'): - if self.main_window.isMinimized(): - # Copied from QWidget.setWindowState() docs on how to restore and activate a minimized window - # while preserving its maximized and/or full-screen state. - self.main_window.setWindowState(self.main_window.windowState() & ~QtCore.Qt.WindowMinimized | - QtCore.Qt.WindowActive) - return True - return QtWidgets.QApplication.event(self, event) - - -def parse_options(args=None): - """ - Parse the command line arguments - - :param args: list of command line arguments - :return: a tuple of parsed options of type optparse.Value and a list of remaining argsZ - """ - # Set up command line options. - parser = argparse.ArgumentParser(prog='openlp.py') - parser.add_argument('-e', '--no-error-form', dest='no_error_form', action='store_true', - help='Disable the error notification form.') - parser.add_argument('-l', '--log-level', dest='loglevel', default='warning', metavar='LEVEL', - help='Set logging to LEVEL level. Valid values are "debug", "info", "warning".') - parser.add_argument('-p', '--portable', dest='portable', action='store_true', - help='Specify if this should be run as a portable app, ' - 'off a USB flash drive (not implemented).') - parser.add_argument('-d', '--dev-version', dest='dev_version', action='store_true', - help='Ignore the version file and pull the version directly from Bazaar') - parser.add_argument('-s', '--style', dest='style', help='Set the Qt5 style (passed directly to Qt5).') - parser.add_argument('-w', '--no-web-server', dest='no_web_server', action='store_false', - help='Turn off the Web and Socket Server ') - parser.add_argument('rargs', nargs='?', default=[]) - # Parse command line options and deal with them. Use args supplied pragmatically if possible. - return parser.parse_args(args) if args else parser.parse_args() - - -def set_up_logging(log_path): - """ - Setup our logging using log_path - - :param openlp.core.common.path.Path log_path: The file to save the log to. - :rtype: None - """ - check_directory_exists(log_path, True) - file_path = log_path / 'openlp.log' - # TODO: FileHandler accepts a Path object in Py3.6 - logfile = logging.FileHandler(str(file_path), 'w', encoding='UTF-8') - logfile.setFormatter(logging.Formatter('%(asctime)s %(name)-55s %(levelname)-8s %(message)s')) - log.addHandler(logfile) - if log.isEnabledFor(logging.DEBUG): - print('Logging to: {name}'.format(name=file_path)) - - -def main(args=None): - """ - The main function which parses command line options and then runs - - :param args: Some args - """ - args = parse_options(args) - qt_args = [] - if args and args.loglevel.lower() in ['d', 'debug']: - log.setLevel(logging.DEBUG) - elif args and args.loglevel.lower() in ['w', 'warning']: - log.setLevel(logging.WARNING) - else: - log.setLevel(logging.INFO) - if args and args.style: - qt_args.extend(['-style', args.style]) - # Throw the rest of the arguments at Qt, just in case. - qt_args.extend(args.rargs) - # Bug #1018855: Set the WM_CLASS property in X11 - if not is_win() and not is_macosx(): - qt_args.append('OpenLP') - # Initialise the resources - qInitResources() - # Now create and actually run the application. - application = OpenLP(qt_args) - application.setOrganizationName('OpenLP') - application.setOrganizationDomain('openlp.org') - application.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) - application.setAttribute(QtCore.Qt.AA_DontCreateNativeWidgetSiblings, True) - if args and args.portable: - application.setApplicationName('OpenLPPortable') - Settings.setDefaultFormat(Settings.IniFormat) - # Get location OpenLPPortable.ini - portable_path = (AppLocation.get_directory(AppLocation.AppDir) / '..' / '..').resolve() - data_path = portable_path / 'Data' - set_up_logging(portable_path / 'Other') - log.info('Running portable') - portable_settings_path = data_path / 'OpenLP.ini' - # Make this our settings file - log.info('INI file: {name}'.format(name=portable_settings_path)) - Settings.set_filename(str(portable_settings_path)) - portable_settings = Settings() - # Set our data path - log.info('Data path: {name}'.format(name=data_path)) - # Point to our data path - portable_settings.setValue('advanced/data path', data_path) - portable_settings.setValue('advanced/is portable', True) - portable_settings.sync() - else: - application.setApplicationName('OpenLP') - set_up_logging(AppLocation.get_directory(AppLocation.CacheDir)) - Registry.create() - Registry().register('application', application) - Registry().set_flag('no_web_server', args.no_web_server) - application.setApplicationVersion(get_version()['version']) - # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one - if application.is_already_running(): - sys.exit() - # If the custom data path is missing and the user wants to restore the data path, quit OpenLP. - if application.is_data_path_missing(): - application.shared_memory.detach() - sys.exit() - # Upgrade settings. - settings = Settings() - if settings.can_upgrade(): - now = datetime.now() - # Only back up if OpenLP has previously run. - if settings.value('core/has run wizard'): - back_up_path = AppLocation.get_data_path() / (now.strftime('%Y-%m-%d %H-%M') + '.conf') - log.info('Settings about to be upgraded. Existing settings are being backed up to {back_up_path}' - .format(back_up_path=back_up_path)) - QtWidgets.QMessageBox.information( - None, translate('OpenLP', 'Settings Upgrade'), - translate('OpenLP', 'Your settings are about to upgraded. A backup will be created at {back_up_path}') - .format(back_up_path=back_up_path)) - settings.export(back_up_path) - settings.upgrade_settings() - # First time checks in settings - if not Settings().value('core/has run wizard'): - if not FirstTimeLanguageForm().exec(): - # if cancel then stop processing - sys.exit() - # i18n Set Language - language = LanguageManager.get_language() - translators = LanguageManager.get_translators(language) - for translator in translators: - if not translator.isEmpty(): - application.installTranslator(translator) - if not translators: - log.debug('Could not find translators.') - if args and not args.no_error_form: - sys.excepthook = application.hook_exception - sys.exit(application.run(qt_args)) diff --git a/openlp/core/api/deploy.py b/openlp/core/api/deploy.py index 9b1e6e793..0419b45db 100644 --- a/openlp/core/api/deploy.py +++ b/openlp/core/api/deploy.py @@ -25,7 +25,8 @@ Download and "install" the remote web client import os from zipfile import ZipFile -from openlp.core.common import AppLocation, Registry +from openlp.core.common.applocation import AppLocation +from openlp.core.common.registry import Registry from openlp.core.common.httputils import url_get_file, get_web_page, get_url_file_size diff --git a/openlp/core/api/endpoint/controller.py b/openlp/core/api/endpoint/controller.py index c3f5fd89f..8ecfdb732 100644 --- a/openlp/core/api/endpoint/controller.py +++ b/openlp/core/api/endpoint/controller.py @@ -27,7 +27,9 @@ import json from openlp.core.api.http.endpoint import Endpoint from openlp.core.api.http import requires_auth -from openlp.core.common import Registry, AppLocation, Settings +from openlp.core.common.applocation import AppLocation +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import ItemCapabilities, create_thumb log = logging.getLogger(__name__) diff --git a/openlp/core/api/endpoint/core.py b/openlp/core/api/endpoint/core.py index 5814651b1..2988e03aa 100644 --- a/openlp/core/api/endpoint/core.py +++ b/openlp/core/api/endpoint/core.py @@ -19,13 +19,17 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`~openlp.core.api.endpoint.core` module contains the core API endpoints +""" import logging import os -from openlp.core.api.http.endpoint import Endpoint from openlp.core.api.http import requires_auth -from openlp.core.common import Registry, UiStrings, translate -from openlp.core.lib import image_to_byte, PluginStatus, StringContent +from openlp.core.api.http.endpoint import Endpoint +from openlp.core.common.registry import Registry +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.lib import PluginStatus, StringContent, image_to_byte template_dir = 'templates' diff --git a/openlp/core/api/endpoint/pluginhelpers.py b/openlp/core/api/endpoint/pluginhelpers.py index d2955ef61..9377bde6a 100644 --- a/openlp/core/api/endpoint/pluginhelpers.py +++ b/openlp/core/api/endpoint/pluginhelpers.py @@ -24,11 +24,11 @@ import json import re import urllib -from urllib.parse import urlparse from webob import Response from openlp.core.api.http.errors import NotFound -from openlp.core.common import Registry, AppLocation +from openlp.core.common.applocation import AppLocation +from openlp.core.common.registry import Registry from openlp.core.lib import PluginStatus, image_to_byte diff --git a/openlp/core/api/endpoint/service.py b/openlp/core/api/endpoint/service.py index 4e3b53fbb..ab9ae6740 100644 --- a/openlp/core/api/endpoint/service.py +++ b/openlp/core/api/endpoint/service.py @@ -22,9 +22,9 @@ import logging import json -from openlp.core.api.http.endpoint import Endpoint from openlp.core.api.http import requires_auth -from openlp.core.common import Registry +from openlp.core.api.http.endpoint import Endpoint +from openlp.core.common.registry import Registry log = logging.getLogger(__name__) diff --git a/openlp/core/api/http/endpoint.py b/openlp/core/api/http/endpoint.py index d6a923a50..fe2b11d9a 100644 --- a/openlp/core/api/http/endpoint.py +++ b/openlp/core/api/http/endpoint.py @@ -22,12 +22,12 @@ """ The Endpoint class, which provides plugins with a way to serve their own portion of the API """ - import os -from openlp.core.common import AppLocation from mako.template import Template +from openlp.core.common.applocation import AppLocation + class Endpoint(object): """ diff --git a/openlp/core/api/http/server.py b/openlp/core/api/http/server.py index 2a2ec7292..fad135f2b 100644 --- a/openlp/core/api/http/server.py +++ b/openlp/core/api/http/server.py @@ -19,31 +19,31 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - """ The :mod:`http` module contains the API web server. This is a lightweight web server used by remotes to interact with OpenLP. It uses JSON to communicate with the remotes. """ - import logging import time from PyQt5 import QtCore, QtWidgets from waitress import serve -from openlp.core.api.http import register_endpoint -from openlp.core.api.http import application -from openlp.core.common import AppLocation, RegistryMixin, RegistryProperties, OpenLPMixin, \ - Settings, Registry, UiStrings, check_directory_exists -from openlp.core.lib import translate - from openlp.core.api.deploy import download_and_check, download_sha256 -from openlp.core.api.poll import Poller from openlp.core.api.endpoint.controller import controller_endpoint, api_controller_endpoint from openlp.core.api.endpoint.core import chords_endpoint, stage_endpoint, blank_endpoint, main_endpoint from openlp.core.api.endpoint.service import service_endpoint, api_service_endpoint from openlp.core.api.endpoint.remote import remote_endpoint - +from openlp.core.api.http import register_endpoint +from openlp.core.api.http import application +from openlp.core.api.poll import Poller +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings +from openlp.core.common.mixins import OpenLPMixin, RegistryMixin +from openlp.core.common.path import create_paths +from openlp.core.common.registry import RegistryProperties, Registry +from openlp.core.common.settings import Settings +from openlp.core.common.i18n import translate log = logging.getLogger(__name__) @@ -115,11 +115,11 @@ class HttpServer(RegistryMixin, RegistryProperties, OpenLPMixin): Create the internal file structure if it does not exist :return: """ - check_directory_exists(AppLocation.get_section_data_path('remotes') / 'assets') - check_directory_exists(AppLocation.get_section_data_path('remotes') / 'images') - check_directory_exists(AppLocation.get_section_data_path('remotes') / 'static') - check_directory_exists(AppLocation.get_section_data_path('remotes') / 'static' / 'index') - check_directory_exists(AppLocation.get_section_data_path('remotes') / 'templates') + create_paths(AppLocation.get_section_data_path('remotes') / 'assets', + AppLocation.get_section_data_path('remotes') / 'images', + AppLocation.get_section_data_path('remotes') / 'static', + AppLocation.get_section_data_path('remotes') / 'static' / 'index', + AppLocation.get_section_data_path('remotes') / 'templates') def first_time(self): """ diff --git a/openlp/core/api/http/wsgiapp.py b/openlp/core/api/http/wsgiapp.py index 153344ab5..f948d4096 100644 --- a/openlp/core/api/http/wsgiapp.py +++ b/openlp/core/api/http/wsgiapp.py @@ -31,7 +31,7 @@ import re from webob import Request, Response from webob.static import DirectoryApp -from openlp.core.common import AppLocation +from openlp.core.common.applocation import AppLocation from openlp.core.api.http.errors import HttpError, NotFound, ServerError diff --git a/openlp/core/api/poll.py b/openlp/core/api/poll.py index c4b25848a..5b3fb33c4 100644 --- a/openlp/core/api/poll.py +++ b/openlp/core/api/poll.py @@ -22,8 +22,9 @@ import json -from openlp.core.common import RegistryProperties, Settings from openlp.core.common.httputils import get_web_page +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.settings import Settings class Poller(RegistryProperties): diff --git a/openlp/core/api/tab.py b/openlp/core/api/tab.py index 7f645d6c6..ddca61176 100644 --- a/openlp/core/api/tab.py +++ b/openlp/core/api/tab.py @@ -19,10 +19,14 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - +""" +The :mod:`~openlp.core.api.tab` module contains the settings tab for the API +""" from PyQt5 import QtCore, QtGui, QtNetwork, QtWidgets -from openlp.core.common import UiStrings, Registry, Settings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab ZERO_URL = '0.0.0.0' diff --git a/openlp/core/api/websockets.py b/openlp/core/api/websockets.py index cf4d83425..d64fdf3cc 100644 --- a/openlp/core/api/websockets.py +++ b/openlp/core/api/websockets.py @@ -19,12 +19,10 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - """ The :mod:`http` module contains the API web server. This is a lightweight web server used by remotes to interact with OpenLP. It uses JSON to communicate with the remotes. """ - import asyncio import websockets import json @@ -33,7 +31,9 @@ import time from PyQt5 import QtCore -from openlp.core.common import Settings, RegistryProperties, OpenLPMixin, Registry +from openlp.core.common.mixins import OpenLPMixin +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings log = logging.getLogger(__name__) diff --git a/openlp/core/app.py b/openlp/core/app.py new file mode 100644 index 000000000..7c3938cfe --- /dev/null +++ b/openlp/core/app.py @@ -0,0 +1,425 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 OpenLP Developers # +# --------------------------------------------------------------------------- # +# 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:`core` module provides all core application functions + +All the core functions of the OpenLP application including the GUI, settings, +logging and a plugin framework are contained within the openlp.core module. +""" +import argparse +import logging +import sys +import time +from datetime import datetime +from traceback import format_exception + +from PyQt5 import QtCore, QtWidgets + +from openlp.core.common import is_macosx, is_win +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import LanguageManager, UiStrings, translate +from openlp.core.common.mixins import OpenLPMixin +from openlp.core.common.path import create_paths, copytree +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings +from openlp.core.version import check_for_update, get_version +from openlp.core.display.screens import ScreenList +from openlp.core.resources import qInitResources +from openlp.core.ui import SplashScreen +from openlp.core.ui.exceptionform import ExceptionForm +from openlp.core.ui.firsttimeform import FirstTimeForm +from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm +from openlp.core.ui.mainwindow import MainWindow +from openlp.core.ui.style import get_application_stylesheet + + +__all__ = ['OpenLP', 'main'] + + +log = logging.getLogger() + + +class OpenLP(OpenLPMixin, QtWidgets.QApplication): + """ + The core application class. This class inherits from Qt's QApplication + class in order to provide the core of the application. + """ + + args = [] + + def exec(self): + """ + Override exec method to allow the shared memory to be released on exit + """ + self.is_event_loop_active = True + result = QtWidgets.QApplication.exec() + self.shared_memory.detach() + return result + + def run(self, args): + """ + Run the OpenLP application. + + :param args: Some Args + """ + self.is_event_loop_active = False + # On Windows, the args passed into the constructor are ignored. Not very handy, so set the ones we want to use. + # On Linux and FreeBSD, in order to set the WM_CLASS property for X11, we pass "OpenLP" in as a command line + # argument. This interferes with files being passed in as command line arguments, so we remove it from the list. + if 'OpenLP' in args: + args.remove('OpenLP') + self.args.extend(args) + # Decide how many screens we have and their size + screens = ScreenList.create(self.desktop()) + # First time checks in settings + has_run_wizard = Settings().value('core/has run wizard') + if not has_run_wizard: + ftw = FirstTimeForm() + ftw.initialize(screens) + if ftw.exec() == QtWidgets.QDialog.Accepted: + Settings().setValue('core/has run wizard', True) + elif ftw.was_cancelled: + QtCore.QCoreApplication.exit() + sys.exit() + # Correct stylesheet bugs + application_stylesheet = get_application_stylesheet() + if application_stylesheet: + self.setStyleSheet(application_stylesheet) + can_show_splash = Settings().value('core/show splash') + if can_show_splash: + self.splash = SplashScreen() + self.splash.show() + # make sure Qt really display the splash screen + self.processEvents() + # Check if OpenLP has been upgrade and if a backup of data should be created + self.backup_on_upgrade(has_run_wizard, can_show_splash) + # start the main app window + self.main_window = MainWindow() + Registry().execute('bootstrap_initialise') + Registry().execute('bootstrap_post_set_up') + Registry().initialise = False + self.main_window.show() + if can_show_splash: + # now kill the splashscreen + self.splash.finish(self.main_window) + log.debug('Splashscreen closed') + # make sure Qt really display the splash screen + self.processEvents() + self.main_window.repaint() + self.processEvents() + if not has_run_wizard: + self.main_window.first_time() + if Settings().value('core/update check'): + check_for_update(self.main_window) + self.main_window.is_display_blank() + self.main_window.app_startup() + return self.exec() + + def is_already_running(self): + """ + Look to see if OpenLP is already running and ask if a 2nd instance is to be started. + """ + self.shared_memory = QtCore.QSharedMemory('OpenLP') + if self.shared_memory.attach(): + status = QtWidgets.QMessageBox.critical(None, UiStrings().Error, UiStrings().OpenLPStart, + QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | + QtWidgets.QMessageBox.No)) + if status == QtWidgets.QMessageBox.No: + return True + return False + else: + self.shared_memory.create(1) + return False + + def is_data_path_missing(self): + """ + Check if the data folder path exists. + """ + data_folder_path = AppLocation.get_data_path() + if not data_folder_path.exists(): + log.critical('Database was not found in: %s', data_folder_path) + status = QtWidgets.QMessageBox.critical( + None, translate('OpenLP', 'Data Directory Error'), + translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}\n\nThe location of the data folder ' + 'was previously changed from the OpenLP\'s default location. If the data was ' + 'stored on removable device, that device needs to be made available.\n\nYou may ' + 'reset the data location back to the default location, or you can try to make the ' + 'current location available.\n\nDo you want to reset to the default data location? ' + 'If not, OpenLP will be closed so you can try to fix the the problem.') + .format(path=data_folder_path), + QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), + QtWidgets.QMessageBox.No) + if status == QtWidgets.QMessageBox.No: + # If answer was "No", return "True", it will shutdown OpenLP in def main + log.info('User requested termination') + return True + # If answer was "Yes", remove the custom data path thus resetting the default location. + Settings().remove('advanced/data path') + log.info('Database location has been reset to the default settings.') + return False + + def hook_exception(self, exc_type, value, traceback): + """ + Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where + users cannot see it and cannot report when we encounter these problems. + + :param exc_type: The class of exception. + :param value: The actual exception object. + :param traceback: A traceback object with the details of where the exception occurred. + """ + # We can't log.exception here because the last exception no longer exists, we're actually busy handling it. + log.critical(''.join(format_exception(exc_type, value, traceback))) + if not hasattr(self, 'exception_form'): + self.exception_form = ExceptionForm() + self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exc_type, value, traceback))) + self.set_normal_cursor() + is_splash_visible = False + if hasattr(self, 'splash') and self.splash.isVisible(): + is_splash_visible = True + self.splash.hide() + self.exception_form.exec() + if is_splash_visible: + self.splash.show() + + def backup_on_upgrade(self, has_run_wizard, can_show_splash): + """ + Check if OpenLP has been upgraded, and ask if a backup of data should be made + + :param has_run_wizard: OpenLP has been run before + :param can_show_splash: Should OpenLP show the splash screen + """ + data_version = Settings().value('core/application version') + openlp_version = get_version()['version'] + # New installation, no need to create backup + if not has_run_wizard: + Settings().setValue('core/application version', openlp_version) + # If data_version is different from the current version ask if we should backup the data folder + elif data_version != openlp_version: + if can_show_splash and self.splash.isVisible(): + self.splash.hide() + if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'), + translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n' + 'a backup of the old data folder?'), + defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: + # Create copy of data folder + data_folder_path = AppLocation.get_data_path() + timestamp = time.strftime("%Y%m%d-%H%M%S") + data_folder_backup_path = data_folder_path.with_name(data_folder_path.name + '-' + timestamp) + try: + copytree(data_folder_path, data_folder_backup_path) + except OSError: + QtWidgets.QMessageBox.warning(None, translate('OpenLP', 'Backup'), + translate('OpenLP', 'Backup of the data folder failed!')) + return + message = translate('OpenLP', + 'A backup of the data folder has been created at:\n\n' + '{text}').format(text=data_folder_backup_path) + QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message) + + # Update the version in the settings + Settings().setValue('core/application version', openlp_version) + if can_show_splash: + self.splash.show() + + def process_events(self): + """ + Wrapper to make ProcessEvents visible and named correctly + """ + self.processEvents() + + def set_busy_cursor(self): + """ + Sets the Busy Cursor for the Application + """ + self.setOverrideCursor(QtCore.Qt.BusyCursor) + self.processEvents() + + def set_normal_cursor(self): + """ + Sets the Normal Cursor for the Application + """ + self.restoreOverrideCursor() + self.processEvents() + + def event(self, event): + """ + Enables platform specific event handling i.e. direct file opening on OS X + + :param event: The event + """ + if event.type() == QtCore.QEvent.FileOpen: + file_name = event.file() + log.debug('Got open file event for {name}!'.format(name=file_name)) + self.args.insert(0, file_name) + return True + # Mac OS X should restore app window when user clicked on the OpenLP icon + # in the Dock bar. However, OpenLP consists of multiple windows and this + # does not work. This workaround fixes that. + # The main OpenLP window is restored when it was previously minimized. + elif event.type() == QtCore.QEvent.ApplicationActivate: + if is_macosx() and hasattr(self, 'main_window'): + if self.main_window.isMinimized(): + # Copied from QWidget.setWindowState() docs on how to restore and activate a minimized window + # while preserving its maximized and/or full-screen state. + self.main_window.setWindowState(self.main_window.windowState() & ~QtCore.Qt.WindowMinimized | + QtCore.Qt.WindowActive) + return True + return QtWidgets.QApplication.event(self, event) + + +def parse_options(args=None): + """ + Parse the command line arguments + + :param args: list of command line arguments + :return: a tuple of parsed options of type optparse.Value and a list of remaining argsZ + """ + # Set up command line options. + parser = argparse.ArgumentParser(prog='openlp.py') + parser.add_argument('-e', '--no-error-form', dest='no_error_form', action='store_true', + help='Disable the error notification form.') + parser.add_argument('-l', '--log-level', dest='loglevel', default='warning', metavar='LEVEL', + help='Set logging to LEVEL level. Valid values are "debug", "info", "warning".') + parser.add_argument('-p', '--portable', dest='portable', action='store_true', + help='Specify if this should be run as a portable app, ' + 'off a USB flash drive (not implemented).') + parser.add_argument('-d', '--dev-version', dest='dev_version', action='store_true', + help='Ignore the version file and pull the version directly from Bazaar') + parser.add_argument('-s', '--style', dest='style', help='Set the Qt5 style (passed directly to Qt5).') + parser.add_argument('-w', '--no-web-server', dest='no_web_server', action='store_false', + help='Turn off the Web and Socket Server ') + parser.add_argument('rargs', nargs='?', default=[]) + # Parse command line options and deal with them. Use args supplied pragmatically if possible. + return parser.parse_args(args) if args else parser.parse_args() + + +def set_up_logging(log_path): + """ + Setup our logging using log_path + + :param openlp.core.common.path.Path log_path: The file to save the log to. + :rtype: None + """ + create_paths(log_path, do_not_log=True) + file_path = log_path / 'openlp.log' + # TODO: FileHandler accepts a Path object in Py3.6 + logfile = logging.FileHandler(str(file_path), 'w', encoding='UTF-8') + logfile.setFormatter(logging.Formatter('%(asctime)s %(name)-55s %(levelname)-8s %(message)s')) + log.addHandler(logfile) + if log.isEnabledFor(logging.DEBUG): + print('Logging to: {name}'.format(name=file_path)) + + +def main(args=None): + """ + The main function which parses command line options and then runs + + :param args: Some args + """ + args = parse_options(args) + qt_args = [] + if args and args.loglevel.lower() in ['d', 'debug']: + log.setLevel(logging.DEBUG) + elif args and args.loglevel.lower() in ['w', 'warning']: + log.setLevel(logging.WARNING) + else: + log.setLevel(logging.INFO) + if args and args.style: + qt_args.extend(['-style', args.style]) + # Throw the rest of the arguments at Qt, just in case. + qt_args.extend(args.rargs) + # Bug #1018855: Set the WM_CLASS property in X11 + if not is_win() and not is_macosx(): + qt_args.append('OpenLP') + # Initialise the resources + qInitResources() + # Now create and actually run the application. + application = OpenLP(qt_args) + application.setOrganizationName('OpenLP') + application.setOrganizationDomain('openlp.org') + application.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) + application.setAttribute(QtCore.Qt.AA_DontCreateNativeWidgetSiblings, True) + if args and args.portable: + application.setApplicationName('OpenLPPortable') + Settings.setDefaultFormat(Settings.IniFormat) + # Get location OpenLPPortable.ini + portable_path = (AppLocation.get_directory(AppLocation.AppDir) / '..' / '..').resolve() + data_path = portable_path / 'Data' + set_up_logging(portable_path / 'Other') + log.info('Running portable') + portable_settings_path = data_path / 'OpenLP.ini' + # Make this our settings file + log.info('INI file: {name}'.format(name=portable_settings_path)) + Settings.set_filename(str(portable_settings_path)) + portable_settings = Settings() + # Set our data path + log.info('Data path: {name}'.format(name=data_path)) + # Point to our data path + portable_settings.setValue('advanced/data path', data_path) + portable_settings.setValue('advanced/is portable', True) + portable_settings.sync() + else: + application.setApplicationName('OpenLP') + set_up_logging(AppLocation.get_directory(AppLocation.CacheDir)) + Registry.create() + Registry().register('application', application) + Registry().set_flag('no_web_server', args.no_web_server) + application.setApplicationVersion(get_version()['version']) + # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one + if application.is_already_running(): + sys.exit() + # If the custom data path is missing and the user wants to restore the data path, quit OpenLP. + if application.is_data_path_missing(): + application.shared_memory.detach() + sys.exit() + # Upgrade settings. + settings = Settings() + if settings.can_upgrade(): + now = datetime.now() + # Only back up if OpenLP has previously run. + if settings.value('core/has run wizard'): + back_up_path = AppLocation.get_data_path() / (now.strftime('%Y-%m-%d %H-%M') + '.conf') + log.info('Settings about to be upgraded. Existing settings are being backed up to {back_up_path}' + .format(back_up_path=back_up_path)) + QtWidgets.QMessageBox.information( + None, translate('OpenLP', 'Settings Upgrade'), + translate('OpenLP', 'Your settings are about to upgraded. A backup will be created at {back_up_path}') + .format(back_up_path=back_up_path)) + settings.export(back_up_path) + settings.upgrade_settings() + # First time checks in settings + if not Settings().value('core/has run wizard'): + if not FirstTimeLanguageForm().exec(): + # if cancel then stop processing + sys.exit() + # i18n Set Language + language = LanguageManager.get_language() + translators = LanguageManager.get_translators(language) + for translator in translators: + if not translator.isEmpty(): + application.installTranslator(translator) + if not translators: + log.debug('Could not find translators.') + if args and not args.no_error_form: + sys.excepthook = application.hook_exception + sys.exit(application.run(qt_args)) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 9a4a1935b..f8017fdbd 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -35,7 +35,7 @@ from ipaddress import IPv4Address, IPv6Address, AddressValueError from shutil import which from subprocess import check_output, CalledProcessError, STDOUT -from PyQt5 import QtCore, QtGui +from PyQt5 import QtGui from PyQt5.QtCore import QCryptographicHash as QHash log = logging.getLogger(__name__ + '.__init__') @@ -56,30 +56,10 @@ def trace_error_handler(logger): """ log_string = "OpenLP Error trace" for tb in traceback.extract_stack(): - log_string += '\n File {file} at line {line} \n\t called {data}'.format(file=tb[0], - line=tb[1], - data=tb[3]) + log_string += '\n File {file} at line {line} \n\t called {data}'.format(file=tb[0], line=tb[1], data=tb[3]) logger.error(log_string) -def check_directory_exists(directory, do_not_log=False): - """ - Check a directory exists and if not create it - - :param openlp.core.common.path.Path directory: The directory to make sure exists - :param bool do_not_log: To not log anything. This is need for the start up, when the log isn't ready. - :rtype: None - """ - if not do_not_log: - log.debug('check_directory_exists {text}'.format(text=directory)) - try: - if not directory.exists(): - directory.mkdir(parents=True) - except IOError: - if not do_not_log: - log.exception('failed to check if directory exists or create directory') - - def extension_loader(glob_pattern, excluded_files=[]): """ A utility function to find and load OpenLP extensions, such as plugins, presentation and media controllers and @@ -90,6 +70,7 @@ def extension_loader(glob_pattern, excluded_files=[]): :param list[str] excluded_files: A list of file names to exclude that the glob pattern may find. :rtype: None """ + from openlp.core.common.applocation import AppLocation app_dir = AppLocation.get_directory(AppLocation.AppDir) for extension_path in app_dir.glob(glob_pattern): extension_path = extension_path.relative_to(app_dir) @@ -137,19 +118,6 @@ class ThemeLevel(object): Song = 3 -def translate(context, text, comment=None, qt_translate=QtCore.QCoreApplication.translate): - """ - A special shortcut method to wrap around the Qt5 translation functions. This abstracts the translation procedure so - that we can change it if at a later date if necessary, without having to redo the whole of OpenLP. - - :param context: The translation context, used to give each string a context or a namespace. - :param text: The text to put into the translation tables for translation. - :param comment: An identifying string for when the same text is used in different roles within the same context. - :param qt_translate: - """ - return qt_translate(context, text, comment) - - class SlideLimits(object): """ Provides an enumeration for behaviour of OpenLP at the end limits of each service item when pressing the up/down @@ -203,7 +171,7 @@ def verify_ipv4(addr): :returns: bool """ try: - valid = IPv4Address(addr) + IPv4Address(addr) return True except AddressValueError: return False @@ -217,7 +185,7 @@ def verify_ipv6(addr): :returns: bool """ try: - valid = IPv6Address(addr) + IPv6Address(addr) return True except AddressValueError: return False @@ -290,20 +258,6 @@ def clean_button_text(button_text): return button_text.replace('&', '').replace('< ', '').replace(' >', '') -from .openlpmixin import OpenLPMixin -from .registry import Registry -from .registrymixin import RegistryMixin -from .registryproperties import RegistryProperties -from .uistrings import UiStrings -from .settings import Settings -from .applocation import AppLocation -from .actions import ActionList -from .languagemanager import LanguageManager - -if is_win(): - from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW - - def add_actions(target, actions): """ Adds multiple actions to a menu or toolbar in one command. @@ -394,6 +348,7 @@ def get_images_filter(): """ Returns a filter string for a file dialog containing all the supported image formats. """ + from openlp.core.common.i18n import translate global IMAGES_FILTER if not IMAGES_FILTER: log.debug('Generating images filter.') @@ -446,6 +401,7 @@ def check_binary_exists(program_path): try: # Setup startupinfo options for check_output to avoid console popping up on windows if is_win(): + from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW startupinfo = STARTUPINFO() startupinfo.dwFlags |= STARTF_USESHOWWINDOW else: diff --git a/openlp/core/common/actions.py b/openlp/core/common/actions.py index c39cbc824..a5b417017 100644 --- a/openlp/core/common/actions.py +++ b/openlp/core/common/actions.py @@ -27,7 +27,7 @@ import logging from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Settings +from openlp.core.common.settings import Settings log = logging.getLogger(__name__) diff --git a/openlp/core/common/applocation.py b/openlp/core/common/applocation.py index 02a872303..cf70edfc1 100644 --- a/openlp/core/common/applocation.py +++ b/openlp/core/common/applocation.py @@ -26,8 +26,10 @@ import logging import os import sys -from openlp.core.common import Settings, is_win, is_macosx -from openlp.core.common.path import Path +import openlp +from openlp.core.common import get_frozen_path, is_win, is_macosx +from openlp.core.common.path import Path, create_paths +from openlp.core.common.settings import Settings if not is_win() and not is_macosx(): try: @@ -36,10 +38,6 @@ if not is_win() and not is_macosx(): except ImportError: XDG_BASE_AVAILABLE = False -import openlp -from openlp.core.common import check_directory_exists, get_frozen_path - - log = logging.getLogger(__name__) FROZEN_APP_PATH = Path(sys.argv[0]).parent @@ -88,7 +86,7 @@ class AppLocation(object): path = Settings().value('advanced/data path') else: path = AppLocation.get_directory(AppLocation.DataDir) - check_directory_exists(path) + create_paths(path) return path @staticmethod @@ -121,7 +119,7 @@ class AppLocation(object): :rtype: openlp.core.common.path.Path """ path = AppLocation.get_data_path() / section - check_directory_exists(path) + create_paths(path) return path diff --git a/openlp/core/common/httputils.py b/openlp/core/common/httputils.py index a92cd92c9..11ae7b563 100644 --- a/openlp/core/common/httputils.py +++ b/openlp/core/common/httputils.py @@ -30,7 +30,8 @@ from random import randint import requests -from openlp.core.common import Registry, trace_error_handler +from openlp.core.common import trace_error_handler +from openlp.core.common.registry import Registry log = logging.getLogger(__name__ + '.__init__') diff --git a/openlp/core/common/i18n.py b/openlp/core/common/i18n.py new file mode 100644 index 000000000..1f4357808 --- /dev/null +++ b/openlp/core/common/i18n.py @@ -0,0 +1,556 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 OpenLP Developers # +# --------------------------------------------------------------------------- # +# 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:`languages` module provides a list of language names with utility functions. +""" +import itertools +import locale +import logging +import re +from collections import namedtuple + +from PyQt5 import QtCore, QtWidgets + +from openlp.core.common import is_win, is_macosx +from openlp.core.common.applocation import AppLocation +from openlp.core.common.settings import Settings + +log = logging.getLogger(__name__) + + +# Due to dependency issues, this HAS to be at the top of the file +def translate(context, text, comment=None, qt_translate=QtCore.QCoreApplication.translate): + """ + A special shortcut method to wrap around the Qt5 translation functions. This abstracts the translation procedure so + that we can change it if at a later date if necessary, without having to redo the whole of OpenLP. + + :param context: The translation context, used to give each string a context or a namespace. + :param text: The text to put into the translation tables for translation. + :param comment: An identifying string for when the same text is used in different roles within the same context. + :param qt_translate: + """ + return qt_translate(context, text, comment) + + +Language = namedtuple('Language', ['id', 'name', 'code']) +ICU_COLLATOR = None +DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+', re.UNICODE) +LANGUAGES = sorted([ + Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'), + Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'), + Language(3, translate('common.languages', 'Afar', 'Language code: aa'), 'aa'), + Language(4, translate('common.languages', 'Afrikaans', 'Language code: af'), 'af'), + Language(5, translate('common.languages', 'Albanian', 'Language code: sq'), 'sq'), + Language(6, translate('common.languages', 'Amharic', 'Language code: am'), 'am'), + Language(140, translate('common.languages', 'Amuzgo', 'Language code: amu'), 'amu'), + Language(152, translate('common.languages', 'Ancient Greek', 'Language code: grc'), 'grc'), + Language(7, translate('common.languages', 'Arabic', 'Language code: ar'), 'ar'), + Language(8, translate('common.languages', 'Armenian', 'Language code: hy'), 'hy'), + Language(9, translate('common.languages', 'Assamese', 'Language code: as'), 'as'), + Language(10, translate('common.languages', 'Aymara', 'Language code: ay'), 'ay'), + Language(11, translate('common.languages', 'Azerbaijani', 'Language code: az'), 'az'), + Language(12, translate('common.languages', 'Bashkir', 'Language code: ba'), 'ba'), + Language(13, translate('common.languages', 'Basque', 'Language code: eu'), 'eu'), + Language(14, translate('common.languages', 'Bengali', 'Language code: bn'), 'bn'), + Language(15, translate('common.languages', 'Bhutani', 'Language code: dz'), 'dz'), + Language(16, translate('common.languages', 'Bihari', 'Language code: bh'), 'bh'), + Language(17, translate('common.languages', 'Bislama', 'Language code: bi'), 'bi'), + Language(18, translate('common.languages', 'Breton', 'Language code: br'), 'br'), + Language(19, translate('common.languages', 'Bulgarian', 'Language code: bg'), 'bg'), + Language(20, translate('common.languages', 'Burmese', 'Language code: my'), 'my'), + Language(21, translate('common.languages', 'Byelorussian', 'Language code: be'), 'be'), + Language(142, translate('common.languages', 'Cakchiquel', 'Language code: cak'), 'cak'), + Language(22, translate('common.languages', 'Cambodian', 'Language code: km'), 'km'), + Language(23, translate('common.languages', 'Catalan', 'Language code: ca'), 'ca'), + Language(24, translate('common.languages', 'Chinese', 'Language code: zh'), 'zh'), + Language(141, translate('common.languages', 'Comaltepec Chinantec', 'Language code: cco'), 'cco'), + Language(25, translate('common.languages', 'Corsican', 'Language code: co'), 'co'), + Language(26, translate('common.languages', 'Croatian', 'Language code: hr'), 'hr'), + Language(27, translate('common.languages', 'Czech', 'Language code: cs'), 'cs'), + Language(28, translate('common.languages', 'Danish', 'Language code: da'), 'da'), + Language(29, translate('common.languages', 'Dutch', 'Language code: nl'), 'nl'), + Language(30, translate('common.languages', 'English', 'Language code: en'), 'en'), + Language(31, translate('common.languages', 'Esperanto', 'Language code: eo'), 'eo'), + Language(32, translate('common.languages', 'Estonian', 'Language code: et'), 'et'), + Language(33, translate('common.languages', 'Faeroese', 'Language code: fo'), 'fo'), + Language(34, translate('common.languages', 'Fiji', 'Language code: fj'), 'fj'), + Language(35, translate('common.languages', 'Finnish', 'Language code: fi'), 'fi'), + Language(36, translate('common.languages', 'French', 'Language code: fr'), 'fr'), + Language(37, translate('common.languages', 'Frisian', 'Language code: fy'), 'fy'), + Language(38, translate('common.languages', 'Galician', 'Language code: gl'), 'gl'), + Language(39, translate('common.languages', 'Georgian', 'Language code: ka'), 'ka'), + Language(40, translate('common.languages', 'German', 'Language code: de'), 'de'), + Language(41, translate('common.languages', 'Greek', 'Language code: el'), 'el'), + Language(42, translate('common.languages', 'Greenlandic', 'Language code: kl'), 'kl'), + Language(43, translate('common.languages', 'Guarani', 'Language code: gn'), 'gn'), + Language(44, translate('common.languages', 'Gujarati', 'Language code: gu'), 'gu'), + Language(143, translate('common.languages', 'Haitian Creole', 'Language code: ht'), 'ht'), + Language(45, translate('common.languages', 'Hausa', 'Language code: ha'), 'ha'), + Language(46, translate('common.languages', 'Hebrew (former iw)', 'Language code: he'), 'he'), + Language(144, translate('common.languages', 'Hiligaynon', 'Language code: hil'), 'hil'), + Language(47, translate('common.languages', 'Hindi', 'Language code: hi'), 'hi'), + Language(48, translate('common.languages', 'Hungarian', 'Language code: hu'), 'hu'), + Language(49, translate('common.languages', 'Icelandic', 'Language code: is'), 'is'), + Language(50, translate('common.languages', 'Indonesian (former in)', 'Language code: id'), 'id'), + Language(51, translate('common.languages', 'Interlingua', 'Language code: ia'), 'ia'), + Language(52, translate('common.languages', 'Interlingue', 'Language code: ie'), 'ie'), + Language(54, translate('common.languages', 'Inuktitut (Eskimo)', 'Language code: iu'), 'iu'), + Language(53, translate('common.languages', 'Inupiak', 'Language code: ik'), 'ik'), + Language(55, translate('common.languages', 'Irish', 'Language code: ga'), 'ga'), + Language(56, translate('common.languages', 'Italian', 'Language code: it'), 'it'), + Language(145, translate('common.languages', 'Jakalteko', 'Language code: jac'), 'jac'), + Language(57, translate('common.languages', 'Japanese', 'Language code: ja'), 'ja'), + Language(58, translate('common.languages', 'Javanese', 'Language code: jw'), 'jw'), + Language(150, translate('common.languages', 'K\'iche\'', 'Language code: quc'), 'quc'), + Language(59, translate('common.languages', 'Kannada', 'Language code: kn'), 'kn'), + Language(60, translate('common.languages', 'Kashmiri', 'Language code: ks'), 'ks'), + Language(61, translate('common.languages', 'Kazakh', 'Language code: kk'), 'kk'), + Language(146, translate('common.languages', 'Kekchí ', 'Language code: kek'), 'kek'), + Language(62, translate('common.languages', 'Kinyarwanda', 'Language code: rw'), 'rw'), + Language(63, translate('common.languages', 'Kirghiz', 'Language code: ky'), 'ky'), + Language(64, translate('common.languages', 'Kirundi', 'Language code: rn'), 'rn'), + Language(65, translate('common.languages', 'Korean', 'Language code: ko'), 'ko'), + Language(66, translate('common.languages', 'Kurdish', 'Language code: ku'), 'ku'), + Language(67, translate('common.languages', 'Laothian', 'Language code: lo'), 'lo'), + Language(68, translate('common.languages', 'Latin', 'Language code: la'), 'la'), + Language(69, translate('common.languages', 'Latvian, Lettish', 'Language code: lv'), 'lv'), + Language(70, translate('common.languages', 'Lingala', 'Language code: ln'), 'ln'), + Language(71, translate('common.languages', 'Lithuanian', 'Language code: lt'), 'lt'), + Language(72, translate('common.languages', 'Macedonian', 'Language code: mk'), 'mk'), + Language(73, translate('common.languages', 'Malagasy', 'Language code: mg'), 'mg'), + Language(74, translate('common.languages', 'Malay', 'Language code: ms'), 'ms'), + Language(75, translate('common.languages', 'Malayalam', 'Language code: ml'), 'ml'), + Language(76, translate('common.languages', 'Maltese', 'Language code: mt'), 'mt'), + Language(148, translate('common.languages', 'Mam', 'Language code: mam'), 'mam'), + Language(77, translate('common.languages', 'Maori', 'Language code: mi'), 'mi'), + Language(147, translate('common.languages', 'Maori', 'Language code: mri'), 'mri'), + Language(78, translate('common.languages', 'Marathi', 'Language code: mr'), 'mr'), + Language(79, translate('common.languages', 'Moldavian', 'Language code: mo'), 'mo'), + Language(80, translate('common.languages', 'Mongolian', 'Language code: mn'), 'mn'), + Language(149, translate('common.languages', 'Nahuatl', 'Language code: nah'), 'nah'), + Language(81, translate('common.languages', 'Nauru', 'Language code: na'), 'na'), + Language(82, translate('common.languages', 'Nepali', 'Language code: ne'), 'ne'), + Language(83, translate('common.languages', 'Norwegian', 'Language code: no'), 'no'), + Language(84, translate('common.languages', 'Occitan', 'Language code: oc'), 'oc'), + Language(85, translate('common.languages', 'Oriya', 'Language code: or'), 'or'), + Language(86, translate('common.languages', 'Pashto, Pushto', 'Language code: ps'), 'ps'), + Language(87, translate('common.languages', 'Persian', 'Language code: fa'), 'fa'), + Language(151, translate('common.languages', 'Plautdietsch', 'Language code: pdt'), 'pdt'), + Language(88, translate('common.languages', 'Polish', 'Language code: pl'), 'pl'), + Language(89, translate('common.languages', 'Portuguese', 'Language code: pt'), 'pt'), + Language(90, translate('common.languages', 'Punjabi', 'Language code: pa'), 'pa'), + Language(91, translate('common.languages', 'Quechua', 'Language code: qu'), 'qu'), + Language(92, translate('common.languages', 'Rhaeto-Romance', 'Language code: rm'), 'rm'), + Language(93, translate('common.languages', 'Romanian', 'Language code: ro'), 'ro'), + Language(94, translate('common.languages', 'Russian', 'Language code: ru'), 'ru'), + Language(95, translate('common.languages', 'Samoan', 'Language code: sm'), 'sm'), + Language(96, translate('common.languages', 'Sangro', 'Language code: sg'), 'sg'), + Language(97, translate('common.languages', 'Sanskrit', 'Language code: sa'), 'sa'), + Language(98, translate('common.languages', 'Scots Gaelic', 'Language code: gd'), 'gd'), + Language(99, translate('common.languages', 'Serbian', 'Language code: sr'), 'sr'), + Language(100, translate('common.languages', 'Serbo-Croatian', 'Language code: sh'), 'sh'), + Language(101, translate('common.languages', 'Sesotho', 'Language code: st'), 'st'), + Language(102, translate('common.languages', 'Setswana', 'Language code: tn'), 'tn'), + Language(103, translate('common.languages', 'Shona', 'Language code: sn'), 'sn'), + Language(104, translate('common.languages', 'Sindhi', 'Language code: sd'), 'sd'), + Language(105, translate('common.languages', 'Singhalese', 'Language code: si'), 'si'), + Language(106, translate('common.languages', 'Siswati', 'Language code: ss'), 'ss'), + Language(107, translate('common.languages', 'Slovak', 'Language code: sk'), 'sk'), + Language(108, translate('common.languages', 'Slovenian', 'Language code: sl'), 'sl'), + Language(109, translate('common.languages', 'Somali', 'Language code: so'), 'so'), + Language(110, translate('common.languages', 'Spanish', 'Language code: es'), 'es'), + Language(111, translate('common.languages', 'Sudanese', 'Language code: su'), 'su'), + Language(112, translate('common.languages', 'Swahili', 'Language code: sw'), 'sw'), + Language(113, translate('common.languages', 'Swedish', 'Language code: sv'), 'sv'), + Language(114, translate('common.languages', 'Tagalog', 'Language code: tl'), 'tl'), + Language(115, translate('common.languages', 'Tajik', 'Language code: tg'), 'tg'), + Language(116, translate('common.languages', 'Tamil', 'Language code: ta'), 'ta'), + Language(117, translate('common.languages', 'Tatar', 'Language code: tt'), 'tt'), + Language(118, translate('common.languages', 'Tegulu', 'Language code: te'), 'te'), + Language(119, translate('common.languages', 'Thai', 'Language code: th'), 'th'), + Language(120, translate('common.languages', 'Tibetan', 'Language code: bo'), 'bo'), + Language(121, translate('common.languages', 'Tigrinya', 'Language code: ti'), 'ti'), + Language(122, translate('common.languages', 'Tonga', 'Language code: to'), 'to'), + Language(123, translate('common.languages', 'Tsonga', 'Language code: ts'), 'ts'), + Language(124, translate('common.languages', 'Turkish', 'Language code: tr'), 'tr'), + Language(125, translate('common.languages', 'Turkmen', 'Language code: tk'), 'tk'), + Language(126, translate('common.languages', 'Twi', 'Language code: tw'), 'tw'), + Language(127, translate('common.languages', 'Uigur', 'Language code: ug'), 'ug'), + Language(128, translate('common.languages', 'Ukrainian', 'Language code: uk'), 'uk'), + Language(129, translate('common.languages', 'Urdu', 'Language code: ur'), 'ur'), + Language(153, translate('common.languages', 'Uspanteco', 'Language code: usp'), 'usp'), + Language(130, translate('common.languages', 'Uzbek', 'Language code: uz'), 'uz'), + Language(131, translate('common.languages', 'Vietnamese', 'Language code: vi'), 'vi'), + Language(132, translate('common.languages', 'Volapuk', 'Language code: vo'), 'vo'), + Language(133, translate('common.languages', 'Welch', 'Language code: cy'), 'cy'), + Language(134, translate('common.languages', 'Wolof', 'Language code: wo'), 'wo'), + Language(135, translate('common.languages', 'Xhosa', 'Language code: xh'), 'xh'), + Language(136, translate('common.languages', 'Yiddish (former ji)', 'Language code: yi'), 'yi'), + Language(137, translate('common.languages', 'Yoruba', 'Language code: yo'), 'yo'), + Language(138, translate('common.languages', 'Zhuang', 'Language code: za'), 'za'), + Language(139, translate('common.languages', 'Zulu', 'Language code: zu'), 'zu') +], key=lambda language: language.name) + + +class LanguageManager(object): + """ + Helper for Language selection + """ + __qm_list__ = {} + auto_language = False + + @staticmethod + def get_translators(language): + """ + Set up a translator to use in this instance of OpenLP + + :param language: The language to load into the translator + """ + if LanguageManager.auto_language: + language = QtCore.QLocale.system().name() + lang_path = str(AppLocation.get_directory(AppLocation.LanguageDir)) + app_translator = QtCore.QTranslator() + app_translator.load(language, lang_path) + # A translator for buttons and other default strings provided by Qt. + if not is_win() and not is_macosx(): + lang_path = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath) + # As of Qt5, the core translations come in 2 files per language + default_translator = QtCore.QTranslator() + default_translator.load('qt_%s' % language, lang_path) + base_translator = QtCore.QTranslator() + base_translator.load('qtbase_%s' % language, lang_path) + return app_translator, default_translator, base_translator + + @staticmethod + def find_qm_files(): + """ + Find all available language files in this OpenLP install + """ + log.debug('Translation files: {files}'.format(files=AppLocation.get_directory(AppLocation.LanguageDir))) + trans_dir = QtCore.QDir(str(AppLocation.get_directory(AppLocation.LanguageDir))) + file_names = trans_dir.entryList(['*.qm'], QtCore.QDir.Files, QtCore.QDir.Name) + # Remove qm files from the list which start with "qt". + file_names = [file_ for file_ in file_names if not file_.startswith('qt')] + return list(map(trans_dir.filePath, file_names)) + + @staticmethod + def language_name(qm_file): + """ + Load the language name from a language file + + :param qm_file: The file to obtain the name from + """ + translator = QtCore.QTranslator() + translator.load(qm_file) + return translator.translate('OpenLP.MainWindow', 'English', 'Please add the name of your language here') + + @staticmethod + def get_language(): + """ + Retrieve a saved language to use from settings + """ + language = Settings().value('core/language') + language = str(language) + log.info("Language file: '{language}' Loaded from conf file".format(language=language)) + if re.match(r'[[].*[]]', language): + LanguageManager.auto_language = True + language = re.sub(r'[\[\]]', '', language) + return language + + @staticmethod + def set_language(action, message=True): + """ + Set the language to translate OpenLP into + + :param action: The language menu option + :param message: Display the message option + """ + language = 'en' + if action: + action_name = str(action.objectName()) + if action_name == 'autoLanguageItem': + LanguageManager.auto_language = True + else: + LanguageManager.auto_language = False + qm_list = LanguageManager.get_qm_list() + language = str(qm_list[action_name]) + if LanguageManager.auto_language: + language = '[{language}]'.format(language=language) + Settings().setValue('core/language', language) + log.info("Language file: '{language}' written to conf file".format(language=language)) + if message: + QtWidgets.QMessageBox.information(None, + translate('OpenLP.LanguageManager', 'Language'), + translate('OpenLP.LanguageManager', + 'Please restart OpenLP to use your new language setting.')) + + @staticmethod + def init_qm_list(): + """ + Initialise the list of available translations + """ + LanguageManager.__qm_list__ = {} + qm_files = LanguageManager.find_qm_files() + for counter, qmf in enumerate(qm_files): + reg_ex = QtCore.QRegExp("^.*i18n/(.*).qm") + if reg_ex.exactMatch(qmf): + name = '{regex}'.format(regex=reg_ex.cap(1)) + LanguageManager.__qm_list__[ + '{count:>2d} {name}'.format(count=counter + 1, name=LanguageManager.language_name(qmf))] = name + + @staticmethod + def get_qm_list(): + """ + Return the list of available translations + """ + if not LanguageManager.__qm_list__: + LanguageManager.init_qm_list() + return LanguageManager.__qm_list__ + + +class UiStrings(object): + """ + Provide standard strings for objects to use. + """ + __instance__ = None + + def __new__(cls): + """ + Override the default object creation method to return a single instance. + """ + if not cls.__instance__: + cls.__instance__ = object.__new__(cls) + return cls.__instance__ + + def __init__(self): + """ + These strings should need a good reason to be retranslated elsewhere. + Should some/more/less of these have an & attached? + """ + self.About = translate('OpenLP.Ui', 'About') + self.Add = translate('OpenLP.Ui', '&Add') + self.AddGroup = translate('OpenLP.Ui', 'Add group') + self.AddGroupDot = translate('OpenLP.Ui', 'Add group.') + self.Advanced = translate('OpenLP.Ui', 'Advanced') + self.AllFiles = translate('OpenLP.Ui', 'All Files') + self.Automatic = translate('OpenLP.Ui', 'Automatic') + self.BackgroundColor = translate('OpenLP.Ui', 'Background Color') + self.BackgroundColorColon = translate('OpenLP.Ui', 'Background color:') + self.BibleShortSearchTitle = translate('OpenLP.Ui', 'Search is Empty or too Short') + self.BibleShortSearch = translate('OpenLP.Ui', 'The search you have entered is empty or shorter ' + 'than 3 characters long.

Please try again with ' + 'a longer search.') + self.BibleNoBiblesTitle = translate('OpenLP.Ui', 'No Bibles Available') + self.BibleNoBibles = translate('OpenLP.Ui', 'There are no Bibles currently installed.

' + 'Please use the Import Wizard to install one or more Bibles.') + self.Bottom = translate('OpenLP.Ui', 'Bottom') + self.Browse = translate('OpenLP.Ui', 'Browse...') + self.Cancel = translate('OpenLP.Ui', 'Cancel') + self.CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:') + self.CCLISongNumberLabel = translate('OpenLP.Ui', 'CCLI song number:') + self.CreateService = translate('OpenLP.Ui', 'Create a new service.') + self.ConfirmDelete = translate('OpenLP.Ui', 'Confirm Delete') + self.Continuous = translate('OpenLP.Ui', 'Continuous') + self.Default = translate('OpenLP.Ui', 'Default') + self.DefaultColor = translate('OpenLP.Ui', 'Default Color:') + self.DefaultServiceName = translate('OpenLP.Ui', 'Service %Y-%m-%d %H-%M', + 'This may not contain any of the following characters: /\\?*|<>[]":+\n' + 'See http://docs.python.org/library/datetime' + '.html#strftime-strptime-behavior for more information.') + self.Delete = translate('OpenLP.Ui', '&Delete') + self.DisplayStyle = translate('OpenLP.Ui', 'Display style:') + self.Duplicate = translate('OpenLP.Ui', 'Duplicate Error') + self.Edit = translate('OpenLP.Ui', '&Edit') + self.EmptyField = translate('OpenLP.Ui', 'Empty Field') + self.Error = translate('OpenLP.Ui', 'Error') + self.Export = translate('OpenLP.Ui', 'Export') + self.File = translate('OpenLP.Ui', 'File') + self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit') + self.Help = translate('OpenLP.Ui', 'Help') + self.Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours') + self.IFdSs = translate('OpenLP.Ui', 'Invalid Folder Selected', 'Singular') + self.IFSs = translate('OpenLP.Ui', 'Invalid File Selected', 'Singular') + self.IFSp = translate('OpenLP.Ui', 'Invalid Files Selected', 'Plural') + self.Image = translate('OpenLP.Ui', 'Image') + self.Import = translate('OpenLP.Ui', 'Import') + self.LayoutStyle = translate('OpenLP.Ui', 'Layout style:') + self.Live = translate('OpenLP.Ui', 'Live') + self.LiveBGError = translate('OpenLP.Ui', 'Live Background Error') + self.LiveToolbar = translate('OpenLP.Ui', 'Live Toolbar') + self.Load = translate('OpenLP.Ui', 'Load') + self.Manufacturer = translate('OpenLP.Ui', 'Manufacturer', 'Singular') + self.Manufacturers = translate('OpenLP.Ui', 'Manufacturers', 'Plural') + self.Model = translate('OpenLP.Ui', 'Model', 'Singular') + self.Models = translate('OpenLP.Ui', 'Models', 'Plural') + self.Minutes = translate('OpenLP.Ui', 'm', 'The abbreviated unit for minutes') + self.Middle = translate('OpenLP.Ui', 'Middle') + self.New = translate('OpenLP.Ui', 'New') + self.NewService = translate('OpenLP.Ui', 'New Service') + self.NewTheme = translate('OpenLP.Ui', 'New Theme') + self.NextTrack = translate('OpenLP.Ui', 'Next Track') + self.NFdSs = translate('OpenLP.Ui', 'No Folder Selected', 'Singular') + self.NFSs = translate('OpenLP.Ui', 'No File Selected', 'Singular') + 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.NoResults = translate('OpenLP.Ui', 'No Search Results') + self.OpenLP = translate('OpenLP.Ui', 'OpenLP') + self.OpenLPv2AndUp = translate('OpenLP.Ui', 'OpenLP 2.0 and up') + self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?') + self.OpenService = translate('OpenLP.Ui', 'Open service.') + self.OptionalShowInFooter = translate('OpenLP.Ui', 'Optional, this will be displayed in footer.') + self.OptionalHideInFooter = translate('OpenLP.Ui', 'Optional, this won\'t be displayed in footer.') + self.PlaySlidesInLoop = translate('OpenLP.Ui', 'Play Slides in Loop') + self.PlaySlidesToEnd = translate('OpenLP.Ui', 'Play Slides to End') + self.Preview = translate('OpenLP.Ui', 'Preview') + self.PreviewToolbar = translate('OpenLP.Ui', 'Preview Toolbar') + self.PrintService = translate('OpenLP.Ui', 'Print Service') + self.Projector = translate('OpenLP.Ui', 'Projector', 'Singular') + self.Projectors = translate('OpenLP.Ui', 'Projectors', 'Plural') + self.ReplaceBG = translate('OpenLP.Ui', 'Replace Background') + self.ReplaceLiveBG = translate('OpenLP.Ui', 'Replace live background.') + self.ReplaceLiveBGDisabled = translate('OpenLP.Ui', 'Replace live background is not available when the WebKit ' + 'player is disabled.') + self.ResetBG = translate('OpenLP.Ui', 'Reset Background') + self.ResetLiveBG = translate('OpenLP.Ui', 'Reset live background.') + self.RequiredShowInFooter = translate('OpenLP.Ui', 'Required, this will be displayed in footer.') + self.Seconds = translate('OpenLP.Ui', 's', 'The abbreviated unit for seconds') + self.SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview') + self.Search = translate('OpenLP.Ui', 'Search') + self.SearchThemes = translate('OpenLP.Ui', 'Search Themes...', 'Search bar place holder text ') + self.SelectDelete = translate('OpenLP.Ui', 'You must select an item to delete.') + self.SelectEdit = translate('OpenLP.Ui', 'You must select an item to edit.') + self.Settings = translate('OpenLP.Ui', 'Settings') + self.SaveService = translate('OpenLP.Ui', 'Save Service') + self.Service = translate('OpenLP.Ui', 'Service') + self.ShortResults = translate('OpenLP.Ui', 'Please type more text to use \'Search As You Type\'') + self.Split = translate('OpenLP.Ui', 'Optional &Split') + self.SplitToolTip = translate('OpenLP.Ui', + 'Split a slide into two only if it does not fit on the screen as one slide.') + self.StartingImport = translate('OpenLP.Ui', 'Starting import...') + self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop') + self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End') + self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular') + self.Themes = translate('OpenLP.Ui', 'Themes', 'Plural') + self.Tools = translate('OpenLP.Ui', 'Tools') + self.Top = translate('OpenLP.Ui', 'Top') + self.UnsupportedFile = translate('OpenLP.Ui', 'Unsupported File') + self.VersePerSlide = translate('OpenLP.Ui', 'Verse Per Slide') + self.VersePerLine = translate('OpenLP.Ui', 'Verse Per Line') + self.Version = translate('OpenLP.Ui', 'Version') + self.View = translate('OpenLP.Ui', 'View') + self.ViewMode = translate('OpenLP.Ui', 'View Mode') + self.Video = translate('OpenLP.Ui', 'Video') + self.WebDownloadText = translate('OpenLP.Ui', 'Web Interface, Download and Install latest Version') + book_chapter = translate('OpenLP.Ui', 'Book Chapter') + chapter = translate('OpenLP.Ui', 'Chapter') + verse = translate('OpenLP.Ui', 'Verse') + gap = ' | ' + psalm = translate('OpenLP.Ui', 'Psalm') + may_shorten = translate('OpenLP.Ui', 'Book names may be shortened from full names, for an example Ps 23 = ' + 'Psalm 23') + bible_scripture_items = \ + [book_chapter, gap, psalm, ' 23
', + book_chapter, '%(range)s', chapter, gap, psalm, ' 23%(range)s24
', + book_chapter, '%(verse)s', verse, '%(range)s', verse, gap, psalm, ' 23%(verse)s1%(range)s2
', + book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', verse, '%(range)s', verse, gap, psalm, + ' 23%(verse)s1%(range)s2%(list)s5%(range)s6
', + book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', chapter, '%(verse)s', verse, '%(range)s', + verse, gap, psalm, ' 23%(verse)s1%(range)s2%(list)s24%(verse)s1%(range)s3
', + book_chapter, '%(verse)s', verse, '%(range)s', chapter, '%(verse)s', verse, gap, psalm, + ' 23%(verse)s1%(range)s24%(verse)s1

', may_shorten] + itertools.chain.from_iterable(itertools.repeat(strings, 1) if isinstance(strings, str) + else strings for strings in bible_scripture_items) + self.BibleScriptureError = ''.join(str(joined) for joined in bible_scripture_items) + + +def format_time(text, local_time): + """ + Workaround for Python built-in time formatting function time.strftime(). + + time.strftime() accepts only ascii characters. This function accepts + unicode string and passes individual % placeholders to time.strftime(). + This ensures only ascii characters are passed to time.strftime(). + + :param text: The text to be processed. + :param local_time: The time to be used to add to the string. This is a time object + """ + + def match_formatting(match): + """ + Format the match + """ + return local_time.strftime(match.group()) + + return re.sub(r'\%[a-zA-Z]', match_formatting, text) + + +def get_locale_key(string): + """ + Creates a key for case insensitive, locale aware string sorting. + + :param string: The corresponding string. + """ + string = string.lower() + # ICU is the prefered way to handle locale sort key, we fallback to locale.strxfrm which will work in most cases. + global ICU_COLLATOR + try: + if ICU_COLLATOR is None: + import icu + language = LanguageManager.get_language() + icu_locale = icu.Locale(language) + ICU_COLLATOR = icu.Collator.createInstance(icu_locale) + return ICU_COLLATOR.getSortKey(string) + except: + return locale.strxfrm(string).encode() + + +def get_natural_key(string): + """ + Generate a key for locale aware natural string sorting. + + :param string: string to be sorted by + Returns a list of string compare keys and integers. + """ + key = DIGITS_OR_NONDIGITS.findall(string) + key = [int(part) if part.isdigit() else get_locale_key(part) for part in key] + # Python 3 does not support comparison of different types anymore. So make sure, that we do not compare str + # and int. + if string and string[0].isdigit(): + return [b''] + key + return key + + +def get_language(name): + """ + Find the language by its name or code. + + :param name: The name or abbreviation of the language. + :return: The first match as a Language namedtuple or None + """ + if name: + name_lower = name.lower() + name_title = name_lower[:1].upper() + name_lower[1:] + for language in LANGUAGES: + if language.name == name_title or language.code == name_lower: + return language + return None diff --git a/openlp/core/common/languagemanager.py b/openlp/core/common/languagemanager.py deleted file mode 100644 index 40e4930fb..000000000 --- a/openlp/core/common/languagemanager.py +++ /dev/null @@ -1,210 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# 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:`languagemanager` module provides all the translation settings and language file loading for OpenLP. -""" -import locale -import logging -import re - -from PyQt5 import QtCore, QtWidgets - - -from openlp.core.common import AppLocation, Settings, translate, is_win, is_macosx - -log = logging.getLogger(__name__) - -ICU_COLLATOR = None -DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+', re.UNICODE) - - -class LanguageManager(object): - """ - Helper for Language selection - """ - __qm_list__ = {} - auto_language = False - - @staticmethod - def get_translators(language): - """ - Set up a translator to use in this instance of OpenLP - - :param language: The language to load into the translator - """ - if LanguageManager.auto_language: - language = QtCore.QLocale.system().name() - lang_path = str(AppLocation.get_directory(AppLocation.LanguageDir)) - app_translator = QtCore.QTranslator() - app_translator.load(language, lang_path) - # A translator for buttons and other default strings provided by Qt. - if not is_win() and not is_macosx(): - lang_path = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath) - # As of Qt5, the core translations come in 2 files per language - default_translator = QtCore.QTranslator() - default_translator.load('qt_%s' % language, lang_path) - base_translator = QtCore.QTranslator() - base_translator.load('qtbase_%s' % language, lang_path) - return app_translator, default_translator, base_translator - - @staticmethod - def find_qm_files(): - """ - Find all available language files in this OpenLP install - """ - log.debug('Translation files: {files}'.format(files=AppLocation.get_directory(AppLocation.LanguageDir))) - trans_dir = QtCore.QDir(str(AppLocation.get_directory(AppLocation.LanguageDir))) - file_names = trans_dir.entryList(['*.qm'], QtCore.QDir.Files, QtCore.QDir.Name) - # Remove qm files from the list which start with "qt". - file_names = [file_ for file_ in file_names if not file_.startswith('qt')] - return list(map(trans_dir.filePath, file_names)) - - @staticmethod - def language_name(qm_file): - """ - Load the language name from a language file - - :param qm_file: The file to obtain the name from - """ - translator = QtCore.QTranslator() - translator.load(qm_file) - return translator.translate('OpenLP.MainWindow', 'English', 'Please add the name of your language here') - - @staticmethod - def get_language(): - """ - Retrieve a saved language to use from settings - """ - language = Settings().value('core/language') - language = str(language) - log.info("Language file: '{language}' Loaded from conf file".format(language=language)) - if re.match(r'[[].*[]]', language): - LanguageManager.auto_language = True - language = re.sub(r'[\[\]]', '', language) - return language - - @staticmethod - def set_language(action, message=True): - """ - Set the language to translate OpenLP into - - :param action: The language menu option - :param message: Display the message option - """ - language = 'en' - if action: - action_name = str(action.objectName()) - if action_name == 'autoLanguageItem': - LanguageManager.auto_language = True - else: - LanguageManager.auto_language = False - qm_list = LanguageManager.get_qm_list() - language = str(qm_list[action_name]) - if LanguageManager.auto_language: - language = '[{language}]'.format(language=language) - Settings().setValue('core/language', language) - log.info("Language file: '{language}' written to conf file".format(language=language)) - if message: - QtWidgets.QMessageBox.information(None, - translate('OpenLP.LanguageManager', 'Language'), - translate('OpenLP.LanguageManager', - 'Please restart OpenLP to use your new language setting.')) - - @staticmethod - def init_qm_list(): - """ - Initialise the list of available translations - """ - LanguageManager.__qm_list__ = {} - qm_files = LanguageManager.find_qm_files() - for counter, qmf in enumerate(qm_files): - reg_ex = QtCore.QRegExp("^.*i18n/(.*).qm") - if reg_ex.exactMatch(qmf): - name = '{regex}'.format(regex=reg_ex.cap(1)) - LanguageManager.__qm_list__[ - '{count:>2d} {name}'.format(count=counter + 1, name=LanguageManager.language_name(qmf))] = name - - @staticmethod - def get_qm_list(): - """ - Return the list of available translations - """ - if not LanguageManager.__qm_list__: - LanguageManager.init_qm_list() - return LanguageManager.__qm_list__ - - -def format_time(text, local_time): - """ - Workaround for Python built-in time formatting function time.strftime(). - - time.strftime() accepts only ascii characters. This function accepts - unicode string and passes individual % placeholders to time.strftime(). - This ensures only ascii characters are passed to time.strftime(). - - :param text: The text to be processed. - :param local_time: The time to be used to add to the string. This is a time object - """ - - def match_formatting(match): - """ - Format the match - """ - return local_time.strftime(match.group()) - - return re.sub(r'\%[a-zA-Z]', match_formatting, text) - - -def get_locale_key(string): - """ - Creates a key for case insensitive, locale aware string sorting. - - :param string: The corresponding string. - """ - string = string.lower() - # ICU is the prefered way to handle locale sort key, we fallback to locale.strxfrm which will work in most cases. - global ICU_COLLATOR - try: - if ICU_COLLATOR is None: - import icu - language = LanguageManager.get_language() - icu_locale = icu.Locale(language) - ICU_COLLATOR = icu.Collator.createInstance(icu_locale) - return ICU_COLLATOR.getSortKey(string) - except: - return locale.strxfrm(string).encode() - - -def get_natural_key(string): - """ - Generate a key for locale aware natural string sorting. - - :param string: string to be sorted by - Returns a list of string compare keys and integers. - """ - key = DIGITS_OR_NONDIGITS.findall(string) - key = [int(part) if part.isdigit() else get_locale_key(part) for part in key] - # Python 3 does not support comparison of different types anymore. So make sure, that we do not compare str - # and int. - if string and string[0].isdigit(): - return [b''] + key - return key diff --git a/openlp/core/common/languages.py b/openlp/core/common/languages.py deleted file mode 100644 index 77d0b1558..000000000 --- a/openlp/core/common/languages.py +++ /dev/null @@ -1,201 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# 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:`languages` module provides a list of language names with utility functions. -""" -from collections import namedtuple - -from openlp.core.common import translate - - -Language = namedtuple('Language', ['id', 'name', 'code']) -languages = sorted([ - Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'), - Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'), - Language(3, translate('common.languages', 'Afar', 'Language code: aa'), 'aa'), - Language(4, translate('common.languages', 'Afrikaans', 'Language code: af'), 'af'), - Language(5, translate('common.languages', 'Albanian', 'Language code: sq'), 'sq'), - Language(6, translate('common.languages', 'Amharic', 'Language code: am'), 'am'), - Language(140, translate('common.languages', 'Amuzgo', 'Language code: amu'), 'amu'), - Language(152, translate('common.languages', 'Ancient Greek', 'Language code: grc'), 'grc'), - Language(7, translate('common.languages', 'Arabic', 'Language code: ar'), 'ar'), - Language(8, translate('common.languages', 'Armenian', 'Language code: hy'), 'hy'), - Language(9, translate('common.languages', 'Assamese', 'Language code: as'), 'as'), - Language(10, translate('common.languages', 'Aymara', 'Language code: ay'), 'ay'), - Language(11, translate('common.languages', 'Azerbaijani', 'Language code: az'), 'az'), - Language(12, translate('common.languages', 'Bashkir', 'Language code: ba'), 'ba'), - Language(13, translate('common.languages', 'Basque', 'Language code: eu'), 'eu'), - Language(14, translate('common.languages', 'Bengali', 'Language code: bn'), 'bn'), - Language(15, translate('common.languages', 'Bhutani', 'Language code: dz'), 'dz'), - Language(16, translate('common.languages', 'Bihari', 'Language code: bh'), 'bh'), - Language(17, translate('common.languages', 'Bislama', 'Language code: bi'), 'bi'), - Language(18, translate('common.languages', 'Breton', 'Language code: br'), 'br'), - Language(19, translate('common.languages', 'Bulgarian', 'Language code: bg'), 'bg'), - Language(20, translate('common.languages', 'Burmese', 'Language code: my'), 'my'), - Language(21, translate('common.languages', 'Byelorussian', 'Language code: be'), 'be'), - Language(142, translate('common.languages', 'Cakchiquel', 'Language code: cak'), 'cak'), - Language(22, translate('common.languages', 'Cambodian', 'Language code: km'), 'km'), - Language(23, translate('common.languages', 'Catalan', 'Language code: ca'), 'ca'), - Language(24, translate('common.languages', 'Chinese', 'Language code: zh'), 'zh'), - Language(141, translate('common.languages', 'Comaltepec Chinantec', 'Language code: cco'), 'cco'), - Language(25, translate('common.languages', 'Corsican', 'Language code: co'), 'co'), - Language(26, translate('common.languages', 'Croatian', 'Language code: hr'), 'hr'), - Language(27, translate('common.languages', 'Czech', 'Language code: cs'), 'cs'), - Language(28, translate('common.languages', 'Danish', 'Language code: da'), 'da'), - Language(29, translate('common.languages', 'Dutch', 'Language code: nl'), 'nl'), - Language(30, translate('common.languages', 'English', 'Language code: en'), 'en'), - Language(31, translate('common.languages', 'Esperanto', 'Language code: eo'), 'eo'), - Language(32, translate('common.languages', 'Estonian', 'Language code: et'), 'et'), - Language(33, translate('common.languages', 'Faeroese', 'Language code: fo'), 'fo'), - Language(34, translate('common.languages', 'Fiji', 'Language code: fj'), 'fj'), - Language(35, translate('common.languages', 'Finnish', 'Language code: fi'), 'fi'), - Language(36, translate('common.languages', 'French', 'Language code: fr'), 'fr'), - Language(37, translate('common.languages', 'Frisian', 'Language code: fy'), 'fy'), - Language(38, translate('common.languages', 'Galician', 'Language code: gl'), 'gl'), - Language(39, translate('common.languages', 'Georgian', 'Language code: ka'), 'ka'), - Language(40, translate('common.languages', 'German', 'Language code: de'), 'de'), - Language(41, translate('common.languages', 'Greek', 'Language code: el'), 'el'), - Language(42, translate('common.languages', 'Greenlandic', 'Language code: kl'), 'kl'), - Language(43, translate('common.languages', 'Guarani', 'Language code: gn'), 'gn'), - Language(44, translate('common.languages', 'Gujarati', 'Language code: gu'), 'gu'), - Language(143, translate('common.languages', 'Haitian Creole', 'Language code: ht'), 'ht'), - Language(45, translate('common.languages', 'Hausa', 'Language code: ha'), 'ha'), - Language(46, translate('common.languages', 'Hebrew (former iw)', 'Language code: he'), 'he'), - Language(144, translate('common.languages', 'Hiligaynon', 'Language code: hil'), 'hil'), - Language(47, translate('common.languages', 'Hindi', 'Language code: hi'), 'hi'), - Language(48, translate('common.languages', 'Hungarian', 'Language code: hu'), 'hu'), - Language(49, translate('common.languages', 'Icelandic', 'Language code: is'), 'is'), - Language(50, translate('common.languages', 'Indonesian (former in)', 'Language code: id'), 'id'), - Language(51, translate('common.languages', 'Interlingua', 'Language code: ia'), 'ia'), - Language(52, translate('common.languages', 'Interlingue', 'Language code: ie'), 'ie'), - Language(54, translate('common.languages', 'Inuktitut (Eskimo)', 'Language code: iu'), 'iu'), - Language(53, translate('common.languages', 'Inupiak', 'Language code: ik'), 'ik'), - Language(55, translate('common.languages', 'Irish', 'Language code: ga'), 'ga'), - Language(56, translate('common.languages', 'Italian', 'Language code: it'), 'it'), - Language(145, translate('common.languages', 'Jakalteko', 'Language code: jac'), 'jac'), - Language(57, translate('common.languages', 'Japanese', 'Language code: ja'), 'ja'), - Language(58, translate('common.languages', 'Javanese', 'Language code: jw'), 'jw'), - Language(150, translate('common.languages', 'K\'iche\'', 'Language code: quc'), 'quc'), - Language(59, translate('common.languages', 'Kannada', 'Language code: kn'), 'kn'), - Language(60, translate('common.languages', 'Kashmiri', 'Language code: ks'), 'ks'), - Language(61, translate('common.languages', 'Kazakh', 'Language code: kk'), 'kk'), - Language(146, translate('common.languages', 'Kekchí ', 'Language code: kek'), 'kek'), - Language(62, translate('common.languages', 'Kinyarwanda', 'Language code: rw'), 'rw'), - Language(63, translate('common.languages', 'Kirghiz', 'Language code: ky'), 'ky'), - Language(64, translate('common.languages', 'Kirundi', 'Language code: rn'), 'rn'), - Language(65, translate('common.languages', 'Korean', 'Language code: ko'), 'ko'), - Language(66, translate('common.languages', 'Kurdish', 'Language code: ku'), 'ku'), - Language(67, translate('common.languages', 'Laothian', 'Language code: lo'), 'lo'), - Language(68, translate('common.languages', 'Latin', 'Language code: la'), 'la'), - Language(69, translate('common.languages', 'Latvian, Lettish', 'Language code: lv'), 'lv'), - Language(70, translate('common.languages', 'Lingala', 'Language code: ln'), 'ln'), - Language(71, translate('common.languages', 'Lithuanian', 'Language code: lt'), 'lt'), - Language(72, translate('common.languages', 'Macedonian', 'Language code: mk'), 'mk'), - Language(73, translate('common.languages', 'Malagasy', 'Language code: mg'), 'mg'), - Language(74, translate('common.languages', 'Malay', 'Language code: ms'), 'ms'), - Language(75, translate('common.languages', 'Malayalam', 'Language code: ml'), 'ml'), - Language(76, translate('common.languages', 'Maltese', 'Language code: mt'), 'mt'), - Language(148, translate('common.languages', 'Mam', 'Language code: mam'), 'mam'), - Language(77, translate('common.languages', 'Maori', 'Language code: mi'), 'mi'), - Language(147, translate('common.languages', 'Maori', 'Language code: mri'), 'mri'), - Language(78, translate('common.languages', 'Marathi', 'Language code: mr'), 'mr'), - Language(79, translate('common.languages', 'Moldavian', 'Language code: mo'), 'mo'), - Language(80, translate('common.languages', 'Mongolian', 'Language code: mn'), 'mn'), - Language(149, translate('common.languages', 'Nahuatl', 'Language code: nah'), 'nah'), - Language(81, translate('common.languages', 'Nauru', 'Language code: na'), 'na'), - Language(82, translate('common.languages', 'Nepali', 'Language code: ne'), 'ne'), - Language(83, translate('common.languages', 'Norwegian', 'Language code: no'), 'no'), - Language(84, translate('common.languages', 'Occitan', 'Language code: oc'), 'oc'), - Language(85, translate('common.languages', 'Oriya', 'Language code: or'), 'or'), - Language(86, translate('common.languages', 'Pashto, Pushto', 'Language code: ps'), 'ps'), - Language(87, translate('common.languages', 'Persian', 'Language code: fa'), 'fa'), - Language(151, translate('common.languages', 'Plautdietsch', 'Language code: pdt'), 'pdt'), - Language(88, translate('common.languages', 'Polish', 'Language code: pl'), 'pl'), - Language(89, translate('common.languages', 'Portuguese', 'Language code: pt'), 'pt'), - Language(90, translate('common.languages', 'Punjabi', 'Language code: pa'), 'pa'), - Language(91, translate('common.languages', 'Quechua', 'Language code: qu'), 'qu'), - Language(92, translate('common.languages', 'Rhaeto-Romance', 'Language code: rm'), 'rm'), - Language(93, translate('common.languages', 'Romanian', 'Language code: ro'), 'ro'), - Language(94, translate('common.languages', 'Russian', 'Language code: ru'), 'ru'), - Language(95, translate('common.languages', 'Samoan', 'Language code: sm'), 'sm'), - Language(96, translate('common.languages', 'Sangro', 'Language code: sg'), 'sg'), - Language(97, translate('common.languages', 'Sanskrit', 'Language code: sa'), 'sa'), - Language(98, translate('common.languages', 'Scots Gaelic', 'Language code: gd'), 'gd'), - Language(99, translate('common.languages', 'Serbian', 'Language code: sr'), 'sr'), - Language(100, translate('common.languages', 'Serbo-Croatian', 'Language code: sh'), 'sh'), - Language(101, translate('common.languages', 'Sesotho', 'Language code: st'), 'st'), - Language(102, translate('common.languages', 'Setswana', 'Language code: tn'), 'tn'), - Language(103, translate('common.languages', 'Shona', 'Language code: sn'), 'sn'), - Language(104, translate('common.languages', 'Sindhi', 'Language code: sd'), 'sd'), - Language(105, translate('common.languages', 'Singhalese', 'Language code: si'), 'si'), - Language(106, translate('common.languages', 'Siswati', 'Language code: ss'), 'ss'), - Language(107, translate('common.languages', 'Slovak', 'Language code: sk'), 'sk'), - Language(108, translate('common.languages', 'Slovenian', 'Language code: sl'), 'sl'), - Language(109, translate('common.languages', 'Somali', 'Language code: so'), 'so'), - Language(110, translate('common.languages', 'Spanish', 'Language code: es'), 'es'), - Language(111, translate('common.languages', 'Sudanese', 'Language code: su'), 'su'), - Language(112, translate('common.languages', 'Swahili', 'Language code: sw'), 'sw'), - Language(113, translate('common.languages', 'Swedish', 'Language code: sv'), 'sv'), - Language(114, translate('common.languages', 'Tagalog', 'Language code: tl'), 'tl'), - Language(115, translate('common.languages', 'Tajik', 'Language code: tg'), 'tg'), - Language(116, translate('common.languages', 'Tamil', 'Language code: ta'), 'ta'), - Language(117, translate('common.languages', 'Tatar', 'Language code: tt'), 'tt'), - Language(118, translate('common.languages', 'Tegulu', 'Language code: te'), 'te'), - Language(119, translate('common.languages', 'Thai', 'Language code: th'), 'th'), - Language(120, translate('common.languages', 'Tibetan', 'Language code: bo'), 'bo'), - Language(121, translate('common.languages', 'Tigrinya', 'Language code: ti'), 'ti'), - Language(122, translate('common.languages', 'Tonga', 'Language code: to'), 'to'), - Language(123, translate('common.languages', 'Tsonga', 'Language code: ts'), 'ts'), - Language(124, translate('common.languages', 'Turkish', 'Language code: tr'), 'tr'), - Language(125, translate('common.languages', 'Turkmen', 'Language code: tk'), 'tk'), - Language(126, translate('common.languages', 'Twi', 'Language code: tw'), 'tw'), - Language(127, translate('common.languages', 'Uigur', 'Language code: ug'), 'ug'), - Language(128, translate('common.languages', 'Ukrainian', 'Language code: uk'), 'uk'), - Language(129, translate('common.languages', 'Urdu', 'Language code: ur'), 'ur'), - Language(153, translate('common.languages', 'Uspanteco', 'Language code: usp'), 'usp'), - Language(130, translate('common.languages', 'Uzbek', 'Language code: uz'), 'uz'), - Language(131, translate('common.languages', 'Vietnamese', 'Language code: vi'), 'vi'), - Language(132, translate('common.languages', 'Volapuk', 'Language code: vo'), 'vo'), - Language(133, translate('common.languages', 'Welch', 'Language code: cy'), 'cy'), - Language(134, translate('common.languages', 'Wolof', 'Language code: wo'), 'wo'), - Language(135, translate('common.languages', 'Xhosa', 'Language code: xh'), 'xh'), - Language(136, translate('common.languages', 'Yiddish (former ji)', 'Language code: yi'), 'yi'), - Language(137, translate('common.languages', 'Yoruba', 'Language code: yo'), 'yo'), - Language(138, translate('common.languages', 'Zhuang', 'Language code: za'), 'za'), - Language(139, translate('common.languages', 'Zulu', 'Language code: zu'), 'zu') -], key=lambda language: language.name) - - -def get_language(name): - """ - Find the language by its name or code. - - :param name: The name or abbreviation of the language. - :return: The first match as a Language namedtuple or None - """ - if name: - name_lower = name.lower() - name_title = name_lower[:1].upper() + name_lower[1:] - for language in languages: - if language.name == name_title or language.code == name_lower: - return language - return None diff --git a/openlp/core/common/openlpmixin.py b/openlp/core/common/mixins.py similarity index 80% rename from openlp/core/common/openlpmixin.py rename to openlp/core/common/mixins.py index 0e63a339c..6a39b8f2c 100644 --- a/openlp/core/common/openlpmixin.py +++ b/openlp/core/common/mixins.py @@ -25,7 +25,8 @@ Provide Error Handling and login Services import logging import inspect -from openlp.core.common import trace_error_handler +from openlp.core.common import trace_error_handler, de_hump +from openlp.core.common.registry import Registry DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent', 'drag_enter_event', 'drop_event', 'on_controller_size_changed', 'preview_size_changed', 'resizeEvent'] @@ -90,3 +91,32 @@ class OpenLPMixin(object): """ trace_error_handler(self.logger) self.logger.exception(message) + + +class RegistryMixin(object): + """ + This adds registry components to classes to use at run time. + """ + def __init__(self, parent): + """ + Register the class and bootstrap hooks. + """ + try: + super(RegistryMixin, self).__init__(parent) + except TypeError: + super(RegistryMixin, self).__init__() + Registry().register(de_hump(self.__class__.__name__), self) + Registry().register_function('bootstrap_initialise', self.bootstrap_initialise) + Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up) + + def bootstrap_initialise(self): + """ + Dummy method to be overridden + """ + pass + + def bootstrap_post_set_up(self): + """ + Dummy method to be overridden + """ + pass diff --git a/openlp/core/common/path.py b/openlp/core/common/path.py index cdb115940..19e17470b 100644 --- a/openlp/core/common/path.py +++ b/openlp/core/common/path.py @@ -19,6 +19,7 @@ # 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 shutil from contextlib import suppress @@ -29,6 +30,45 @@ if is_win(): else: from pathlib import PosixPath as PathVariant +log = logging.getLogger(__name__) + + +class Path(PathVariant): + """ + Subclass pathlib.Path, so we can add json conversion methods + """ + @staticmethod + def encode_json(obj, base_path=None, **kwargs): + """ + Create a Path object from a dictionary representation. The dictionary has been constructed by JSON encoding of + a JSON reprensation of a Path object. + + :param dict[str] obj: The dictionary representation + :param openlp.core.common.path.Path base_path: If specified, an absolute path to base the relative path off of. + :param kwargs: Contains any extra parameters. Not used! + :return: The reconstructed Path object + :rtype: openlp.core.common.path.Path + """ + path = Path(*obj['__Path__']) + if base_path and not path.is_absolute(): + return base_path / path + return path + + def json_object(self, base_path=None, **kwargs): + """ + Create a dictionary that can be JSON decoded. + + :param openlp.core.common.path.Path base_path: If specified, an absolute path to make a relative path from. + :param kwargs: Contains any extra parameters. Not used! + :return: The dictionary representation of this Path object. + :rtype: dict[tuple] + """ + path = self + if base_path: + with suppress(ValueError): + path = path.relative_to(base_path) + return {'__Path__': path.parts} + def replace_params(args, kwargs, params): """ @@ -179,38 +219,32 @@ def str_to_path(string): return Path(string) -class Path(PathVariant): +def create_paths(*paths, **kwargs): """ - Subclass pathlib.Path, so we can add json conversion methods + Create one or more paths + + :param openlp.core.common.path.Path paths: The paths to create + :param bool do_not_log: To not log anything. This is need for the start up, when the log isn't ready. + :rtype: None """ - @staticmethod - def encode_json(obj, base_path=None, **kwargs): - """ - Create a Path object from a dictionary representation. The dictionary has been constructed by JSON encoding of - a JSON reprensation of a Path object. + for path in paths: + if not kwargs.get('do_not_log', False): + log.debug('create_path {path}'.format(path=path)) + try: + if not path.exists(): + path.mkdir(parents=True) + except IOError: + if not kwargs.get('do_not_log', False): + log.exception('failed to check if directory exists or create directory') - :param dict[str] obj: The dictionary representation - :param openlp.core.common.path.Path base_path: If specified, an absolute path to base the relative path off of. - :param kwargs: Contains any extra parameters. Not used! - :return: The reconstructed Path object - :rtype: openlp.core.common.path.Path - """ - path = Path(*obj['__Path__']) - if base_path and not path.is_absolute(): - return base_path / path - return path - def json_object(self, base_path=None, **kwargs): - """ - Create a dictionary that can be JSON decoded. +def files_to_paths(file_names): + """ + Convert a list of file names in to a list of file paths. - :param openlp.core.common.path.Path base_path: If specified, an absolute path to make a relative path from. - :param kwargs: Contains any extra parameters. Not used! - :return: The dictionary representation of this Path object. - :rtype: dict[tuple] - """ - path = self - if base_path: - with suppress(ValueError): - path = path.relative_to(base_path) - return {'__Path__': path.parts} + :param list[str] file_names: The list of file names to convert. + :return: The list converted to file paths + :rtype: openlp.core.common.path.Path + """ + if file_names: + return [str_to_path(file_name) for file_name in file_names] diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index be9183a2f..f01c41b0b 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -25,7 +25,7 @@ Provide Registry Services import logging import sys -from openlp.core.common import trace_error_handler +from openlp.core.common import is_win, trace_error_handler log = logging.getLogger(__name__) @@ -176,3 +176,130 @@ class Registry(object): """ if key in self.working_flags: del self.working_flags[key] + + +class RegistryProperties(object): + """ + This adds registry components to classes to use at run time. + """ + + @property + def application(self): + """ + Adds the openlp to the class dynamically. + Windows needs to access the application in a dynamic manner. + """ + if is_win(): + return Registry().get('application') + else: + if not hasattr(self, '_application') or not self._application: + self._application = Registry().get('application') + return self._application + + @property + def plugin_manager(self): + """ + Adds the plugin manager to the class dynamically + """ + if not hasattr(self, '_plugin_manager') or not self._plugin_manager: + self._plugin_manager = Registry().get('plugin_manager') + return self._plugin_manager + + @property + def image_manager(self): + """ + Adds the image manager to the class dynamically + """ + if not hasattr(self, '_image_manager') or not self._image_manager: + self._image_manager = Registry().get('image_manager') + return self._image_manager + + @property + def media_controller(self): + """ + Adds the media controller to the class dynamically + """ + if not hasattr(self, '_media_controller') or not self._media_controller: + self._media_controller = Registry().get('media_controller') + return self._media_controller + + @property + def service_manager(self): + """ + Adds the service manager to the class dynamically + """ + if not hasattr(self, '_service_manager') or not self._service_manager: + self._service_manager = Registry().get('service_manager') + return self._service_manager + + @property + def preview_controller(self): + """ + Adds the preview controller to the class dynamically + """ + if not hasattr(self, '_preview_controller') or not self._preview_controller: + self._preview_controller = Registry().get('preview_controller') + return self._preview_controller + + @property + def live_controller(self): + """ + Adds the live controller to the class dynamically + """ + if not hasattr(self, '_live_controller') or not self._live_controller: + self._live_controller = Registry().get('live_controller') + return self._live_controller + + @property + def main_window(self): + """ + Adds the main window to the class dynamically + """ + if not hasattr(self, '_main_window') or not self._main_window: + self._main_window = Registry().get('main_window') + return self._main_window + + @property + def renderer(self): + """ + Adds the Renderer to the class dynamically + """ + if not hasattr(self, '_renderer') or not self._renderer: + self._renderer = Registry().get('renderer') + return self._renderer + + @property + def theme_manager(self): + """ + Adds the theme manager to the class dynamically + """ + if not hasattr(self, '_theme_manager') or not self._theme_manager: + self._theme_manager = Registry().get('theme_manager') + return self._theme_manager + + @property + def settings_form(self): + """ + Adds the settings form to the class dynamically + """ + if not hasattr(self, '_settings_form') or not self._settings_form: + self._settings_form = Registry().get('settings_form') + return self._settings_form + + @property + def alerts_manager(self): + """ + Adds the alerts manager to the class dynamically + """ + if not hasattr(self, '_alerts_manager') or not self._alerts_manager: + self._alerts_manager = Registry().get('alerts_manager') + return self._alerts_manager + + @property + def projector_manager(self): + """ + Adds the projector manager to the class dynamically + """ + if not hasattr(self, '_projector_manager') or not self._projector_manager: + self._projector_manager = Registry().get('projector_manager') + return self._projector_manager diff --git a/openlp/core/common/registryproperties.py b/openlp/core/common/registryproperties.py deleted file mode 100644 index 29bd3b2ce..000000000 --- a/openlp/core/common/registryproperties.py +++ /dev/null @@ -1,152 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# 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 # -############################################################################### -""" -Provide Registry values for adding to classes -""" -from openlp.core.common import Registry, is_win - - -class RegistryProperties(object): - """ - This adds registry components to classes to use at run time. - """ - - @property - def application(self): - """ - Adds the openlp to the class dynamically. - Windows needs to access the application in a dynamic manner. - """ - if is_win(): - return Registry().get('application') - else: - if not hasattr(self, '_application') or not self._application: - self._application = Registry().get('application') - return self._application - - @property - def plugin_manager(self): - """ - Adds the plugin manager to the class dynamically - """ - if not hasattr(self, '_plugin_manager') or not self._plugin_manager: - self._plugin_manager = Registry().get('plugin_manager') - return self._plugin_manager - - @property - def image_manager(self): - """ - Adds the image manager to the class dynamically - """ - if not hasattr(self, '_image_manager') or not self._image_manager: - self._image_manager = Registry().get('image_manager') - return self._image_manager - - @property - def media_controller(self): - """ - Adds the media controller to the class dynamically - """ - if not hasattr(self, '_media_controller') or not self._media_controller: - self._media_controller = Registry().get('media_controller') - return self._media_controller - - @property - def service_manager(self): - """ - Adds the service manager to the class dynamically - """ - if not hasattr(self, '_service_manager') or not self._service_manager: - self._service_manager = Registry().get('service_manager') - return self._service_manager - - @property - def preview_controller(self): - """ - Adds the preview controller to the class dynamically - """ - if not hasattr(self, '_preview_controller') or not self._preview_controller: - self._preview_controller = Registry().get('preview_controller') - return self._preview_controller - - @property - def live_controller(self): - """ - Adds the live controller to the class dynamically - """ - if not hasattr(self, '_live_controller') or not self._live_controller: - self._live_controller = Registry().get('live_controller') - return self._live_controller - - @property - def main_window(self): - """ - Adds the main window to the class dynamically - """ - if not hasattr(self, '_main_window') or not self._main_window: - self._main_window = Registry().get('main_window') - return self._main_window - - @property - def renderer(self): - """ - Adds the Renderer to the class dynamically - """ - if not hasattr(self, '_renderer') or not self._renderer: - self._renderer = Registry().get('renderer') - return self._renderer - - @property - def theme_manager(self): - """ - Adds the theme manager to the class dynamically - """ - if not hasattr(self, '_theme_manager') or not self._theme_manager: - self._theme_manager = Registry().get('theme_manager') - return self._theme_manager - - @property - def settings_form(self): - """ - Adds the settings form to the class dynamically - """ - if not hasattr(self, '_settings_form') or not self._settings_form: - self._settings_form = Registry().get('settings_form') - return self._settings_form - - @property - def alerts_manager(self): - """ - Adds the alerts manager to the class dynamically - """ - if not hasattr(self, '_alerts_manager') or not self._alerts_manager: - self._alerts_manager = Registry().get('alerts_manager') - return self._alerts_manager - - @property - def projector_manager(self): - """ - Adds the projector manager to the class dynamically - """ - if not hasattr(self, '_projector_manager') or not self._projector_manager: - self._projector_manager = Registry().get('projector_manager') - return self._projector_manager diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index ca59d4e6f..a6bc549f1 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -28,11 +28,11 @@ import json import os from tempfile import gettempdir -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtGui -from openlp.core.common import SlideLimits, ThemeLevel, UiStrings, is_linux, is_win, translate +from openlp.core.common import SlideLimits, ThemeLevel, is_linux, is_win from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder -from openlp.core.common.path import Path, str_to_path +from openlp.core.common.path import Path, str_to_path, files_to_paths log = logging.getLogger(__name__) @@ -62,18 +62,6 @@ def media_players_conv(string): return string -def file_names_conv(file_names): - """ - Convert a list of file names in to a list of file paths. - - :param list[str] file_names: The list of file names to convert. - :return: The list converted to file paths - :rtype: openlp.core.common.path.Path - """ - if file_names: - return [str_to_path(file_name) for file_name in file_names] - - class Settings(QtCore.QSettings): """ Class to wrap QSettings. @@ -116,7 +104,7 @@ class Settings(QtCore.QSettings): 'advanced/default service enabled': True, 'advanced/default service hour': 11, 'advanced/default service minute': 0, - 'advanced/default service name': UiStrings().DefaultServiceName, + 'advanced/default service name': 'Service %Y-%m-%d %H-%M', 'advanced/display size': 0, 'advanced/double click live': False, 'advanced/enable exit confirmation': True, @@ -261,9 +249,9 @@ class Settings(QtCore.QSettings): ('songs/last directory import', 'songs/last directory import', [(str_to_path, None)]), ('songs/last directory export', 'songs/last directory export', [(str_to_path, None)]), ('songusage/last directory export', 'songusage/last directory export', [(str_to_path, None)]), - ('core/recent files', 'core/recent files', [(file_names_conv, None)]), - ('media/media files', 'media/media files', [(file_names_conv, None)]), - ('presentations/presentations files', 'presentations/presentations files', [(file_names_conv, None)]), + ('core/recent files', 'core/recent files', [(files_to_paths, None)]), + ('media/media files', 'media/media files', [(files_to_paths, None)]), + ('presentations/presentations files', 'presentations/presentations files', [(files_to_paths, None)]), ('core/logo file', 'core/logo file', [(str_to_path, None)]), ('presentations/last directory', 'presentations/last directory', [(str_to_path, None)]), ('images/last directory', 'images/last directory', [(str_to_path, None)]), @@ -298,6 +286,7 @@ class Settings(QtCore.QSettings): """ # Make sure the string is translated (when building the dict the string is not translated because the translate # function was not set up as this stage). + from openlp.core.common.i18n import UiStrings Settings.__default_settings__['advanced/default service name'] = UiStrings().DefaultServiceName def __init__(self, *args): @@ -609,11 +598,5 @@ class Settings(QtCore.QSettings): if file_record.find('@Invalid()') == -1: file_record = file_record.replace('%20', ' ') export_conf_file.write(file_record) - except OSError as ose: - QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'), - translate('OpenLP.MainWindow', - 'An error occurred while exporting the settings: {err}' - ).format(err=ose.strerror), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok)) finally: temp_path.unlink() diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py deleted file mode 100644 index 9dd24a866..000000000 --- a/openlp/core/common/uistrings.py +++ /dev/null @@ -1,187 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# 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:`uistrings` module provides standard strings for OpenLP. -""" -import logging -import itertools - -from openlp.core.common import translate - - -log = logging.getLogger(__name__) - - -class UiStrings(object): - """ - Provide standard strings for objects to use. - """ - __instance__ = None - - def __new__(cls): - """ - Override the default object creation method to return a single instance. - """ - if not cls.__instance__: - cls.__instance__ = object.__new__(cls) - return cls.__instance__ - - def __init__(self): - """ - These strings should need a good reason to be retranslated elsewhere. - Should some/more/less of these have an & attached? - """ - self.About = translate('OpenLP.Ui', 'About') - self.Add = translate('OpenLP.Ui', '&Add') - self.AddGroup = translate('OpenLP.Ui', 'Add group') - self.AddGroupDot = translate('OpenLP.Ui', 'Add group.') - self.Advanced = translate('OpenLP.Ui', 'Advanced') - self.AllFiles = translate('OpenLP.Ui', 'All Files') - self.Automatic = translate('OpenLP.Ui', 'Automatic') - self.BackgroundColor = translate('OpenLP.Ui', 'Background Color') - self.BackgroundColorColon = translate('OpenLP.Ui', 'Background color:') - self.BibleShortSearchTitle = translate('OpenLP.Ui', 'Search is Empty or too Short') - self.BibleShortSearch = translate('OpenLP.Ui', 'The search you have entered is empty or shorter ' - 'than 3 characters long.

Please try again with ' - 'a longer search.') - self.BibleNoBiblesTitle = translate('OpenLP.Ui', 'No Bibles Available') - self.BibleNoBibles = translate('OpenLP.Ui', 'There are no Bibles currently installed.

' - 'Please use the Import Wizard to install one or more Bibles.') - self.Bottom = translate('OpenLP.Ui', 'Bottom') - self.Browse = translate('OpenLP.Ui', 'Browse...') - self.Cancel = translate('OpenLP.Ui', 'Cancel') - self.CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:') - self.CCLISongNumberLabel = translate('OpenLP.Ui', 'CCLI song number:') - self.CreateService = translate('OpenLP.Ui', 'Create a new service.') - self.ConfirmDelete = translate('OpenLP.Ui', 'Confirm Delete') - self.Continuous = translate('OpenLP.Ui', 'Continuous') - self.Default = translate('OpenLP.Ui', 'Default') - self.DefaultColor = translate('OpenLP.Ui', 'Default Color:') - self.DefaultServiceName = translate('OpenLP.Ui', 'Service %Y-%m-%d %H-%M', - 'This may not contain any of the following characters: /\\?*|<>[]":+\n' - 'See http://docs.python.org/library/datetime' - '.html#strftime-strptime-behavior for more information.') - self.Delete = translate('OpenLP.Ui', '&Delete') - self.DisplayStyle = translate('OpenLP.Ui', 'Display style:') - self.Duplicate = translate('OpenLP.Ui', 'Duplicate Error') - self.Edit = translate('OpenLP.Ui', '&Edit') - self.EmptyField = translate('OpenLP.Ui', 'Empty Field') - self.Error = translate('OpenLP.Ui', 'Error') - self.Export = translate('OpenLP.Ui', 'Export') - self.File = translate('OpenLP.Ui', 'File') - self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit') - self.Help = translate('OpenLP.Ui', 'Help') - self.Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours') - self.IFdSs = translate('OpenLP.Ui', 'Invalid Folder Selected', 'Singular') - self.IFSs = translate('OpenLP.Ui', 'Invalid File Selected', 'Singular') - self.IFSp = translate('OpenLP.Ui', 'Invalid Files Selected', 'Plural') - self.Image = translate('OpenLP.Ui', 'Image') - self.Import = translate('OpenLP.Ui', 'Import') - self.LayoutStyle = translate('OpenLP.Ui', 'Layout style:') - self.Live = translate('OpenLP.Ui', 'Live') - self.LiveBGError = translate('OpenLP.Ui', 'Live Background Error') - self.LiveToolbar = translate('OpenLP.Ui', 'Live Toolbar') - self.Load = translate('OpenLP.Ui', 'Load') - self.Manufacturer = translate('OpenLP.Ui', 'Manufacturer', 'Singular') - self.Manufacturers = translate('OpenLP.Ui', 'Manufacturers', 'Plural') - self.Model = translate('OpenLP.Ui', 'Model', 'Singular') - self.Models = translate('OpenLP.Ui', 'Models', 'Plural') - self.Minutes = translate('OpenLP.Ui', 'm', 'The abbreviated unit for minutes') - self.Middle = translate('OpenLP.Ui', 'Middle') - self.New = translate('OpenLP.Ui', 'New') - self.NewService = translate('OpenLP.Ui', 'New Service') - self.NewTheme = translate('OpenLP.Ui', 'New Theme') - self.NextTrack = translate('OpenLP.Ui', 'Next Track') - self.NFdSs = translate('OpenLP.Ui', 'No Folder Selected', 'Singular') - self.NFSs = translate('OpenLP.Ui', 'No File Selected', 'Singular') - 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.NoResults = translate('OpenLP.Ui', 'No Search Results') - self.OpenLP = translate('OpenLP.Ui', 'OpenLP') - self.OpenLPv2AndUp = translate('OpenLP.Ui', 'OpenLP 2.0 and up') - self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?') - self.OpenService = translate('OpenLP.Ui', 'Open service.') - self.OptionalShowInFooter = translate('OpenLP.Ui', 'Optional, this will be displayed in footer.') - self.OptionalHideInFooter = translate('OpenLP.Ui', 'Optional, this won\'t be displayed in footer.') - self.PlaySlidesInLoop = translate('OpenLP.Ui', 'Play Slides in Loop') - self.PlaySlidesToEnd = translate('OpenLP.Ui', 'Play Slides to End') - self.Preview = translate('OpenLP.Ui', 'Preview') - self.PreviewToolbar = translate('OpenLP.Ui', 'Preview Toolbar') - self.PrintService = translate('OpenLP.Ui', 'Print Service') - self.Projector = translate('OpenLP.Ui', 'Projector', 'Singular') - self.Projectors = translate('OpenLP.Ui', 'Projectors', 'Plural') - self.ReplaceBG = translate('OpenLP.Ui', 'Replace Background') - self.ReplaceLiveBG = translate('OpenLP.Ui', 'Replace live background.') - self.ReplaceLiveBGDisabled = translate('OpenLP.Ui', 'Replace live background is not available when the WebKit ' - 'player is disabled.') - self.ResetBG = translate('OpenLP.Ui', 'Reset Background') - self.ResetLiveBG = translate('OpenLP.Ui', 'Reset live background.') - self.RequiredShowInFooter = translate('OpenLP.Ui', 'Required, this will be displayed in footer.') - self.Seconds = translate('OpenLP.Ui', 's', 'The abbreviated unit for seconds') - self.SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview') - self.Search = translate('OpenLP.Ui', 'Search') - self.SearchThemes = translate('OpenLP.Ui', 'Search Themes...', 'Search bar place holder text ') - self.SelectDelete = translate('OpenLP.Ui', 'You must select an item to delete.') - self.SelectEdit = translate('OpenLP.Ui', 'You must select an item to edit.') - self.Settings = translate('OpenLP.Ui', 'Settings') - self.SaveService = translate('OpenLP.Ui', 'Save Service') - self.Service = translate('OpenLP.Ui', 'Service') - self.ShortResults = translate('OpenLP.Ui', 'Please type more text to use \'Search As You Type\'') - self.Split = translate('OpenLP.Ui', 'Optional &Split') - self.SplitToolTip = translate('OpenLP.Ui', - 'Split a slide into two only if it does not fit on the screen as one slide.') - self.StartingImport = translate('OpenLP.Ui', 'Starting import...') - self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop') - self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End') - self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular') - self.Themes = translate('OpenLP.Ui', 'Themes', 'Plural') - self.Tools = translate('OpenLP.Ui', 'Tools') - self.Top = translate('OpenLP.Ui', 'Top') - self.UnsupportedFile = translate('OpenLP.Ui', 'Unsupported File') - self.VersePerSlide = translate('OpenLP.Ui', 'Verse Per Slide') - self.VersePerLine = translate('OpenLP.Ui', 'Verse Per Line') - self.Version = translate('OpenLP.Ui', 'Version') - self.View = translate('OpenLP.Ui', 'View') - self.ViewMode = translate('OpenLP.Ui', 'View Mode') - self.Video = translate('OpenLP.Ui', 'Video') - self.WebDownloadText = translate('OpenLP.Ui', 'Web Interface, Download and Install latest Version') - book_chapter = translate('OpenLP.Ui', 'Book Chapter') - chapter = translate('OpenLP.Ui', 'Chapter') - verse = translate('OpenLP.Ui', 'Verse') - gap = ' | ' - psalm = translate('OpenLP.Ui', 'Psalm') - may_shorten = translate('OpenLP.Ui', 'Book names may be shortened from full names, for an example Ps 23 = ' - 'Psalm 23') - bible_scripture_items = \ - [book_chapter, gap, psalm, ' 23
', - book_chapter, '%(range)s', chapter, gap, psalm, ' 23%(range)s24
', - book_chapter, '%(verse)s', verse, '%(range)s', verse, gap, psalm, ' 23%(verse)s1%(range)s2
', - book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', verse, '%(range)s', verse, gap, psalm, - ' 23%(verse)s1%(range)s2%(list)s5%(range)s6
', - book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', chapter, '%(verse)s', verse, '%(range)s', - verse, gap, psalm, ' 23%(verse)s1%(range)s2%(list)s24%(verse)s1%(range)s3
', - book_chapter, '%(verse)s', verse, '%(range)s', chapter, '%(verse)s', verse, gap, psalm, - ' 23%(verse)s1%(range)s24%(verse)s1

', may_shorten] - itertools.chain.from_iterable(itertools.repeat(strings, 1) if isinstance(strings, str) - else strings for strings in bible_scripture_items) - self.BibleScriptureError = ''.join(str(joined) for joined in bible_scripture_items) diff --git a/openlp/core/common/registrymixin.py b/openlp/core/display/__init__.py similarity index 63% rename from openlp/core/common/registrymixin.py rename to openlp/core/display/__init__.py index ffc1f9264..dea4ef4db 100644 --- a/openlp/core/common/registrymixin.py +++ b/openlp/core/display/__init__.py @@ -20,35 +20,5 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -Provide Registry Services +The :mod:`~openlp.core.display` module contains all the code related to rendering and output """ -from openlp.core.common import Registry, de_hump - - -class RegistryMixin(object): - """ - This adds registry components to classes to use at run time. - """ - def __init__(self, parent): - """ - Register the class and bootstrap hooks. - """ - try: - super(RegistryMixin, self).__init__(parent) - except TypeError: - super(RegistryMixin, self).__init__() - Registry().register(de_hump(self.__class__.__name__), self) - Registry().register_function('bootstrap_initialise', self.bootstrap_initialise) - Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up) - - def bootstrap_initialise(self): - """ - Dummy method to be overridden - """ - pass - - def bootstrap_post_set_up(self): - """ - Dummy method to be overridden - """ - pass diff --git a/openlp/core/lib/renderer.py b/openlp/core/display/renderer.py similarity index 98% rename from openlp/core/lib/renderer.py rename to openlp/core/display/renderer.py index 8b8d5669c..fd040d9c6 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/display/renderer.py @@ -25,12 +25,15 @@ import re from string import Template from PyQt5 import QtGui, QtCore, QtWebKitWidgets -from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, RegistryMixin, Settings +from openlp.core.common.mixins import OpenLPMixin, RegistryMixin from openlp.core.common.path import path_to_str -from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \ - build_lyrics_format_css, build_lyrics_outline_css, build_chords_css +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings +from openlp.core.display.screens import ScreenList +from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ServiceItem, expand_tags, build_chords_css, \ + build_lyrics_format_css, build_lyrics_outline_css from openlp.core.common import ThemeLevel -from openlp.core.ui import MainDisplay +from openlp.core.ui.maindisplay import MainDisplay VERSE = 'The Lord said to {r}Noah{/r}: \n' \ 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \ @@ -243,6 +246,9 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): elif item.is_capable(ItemCapabilities.CanSoftBreak): pages = [] if '[---]' in text: + # Remove Overflow split if at start of the text + if text.startswith('[---]'): + text = text[5:] # Remove two or more option slide breaks next to each other (causing infinite loop). while '\n[---]\n[---]\n' in text: text = text.replace('\n[---]\n[---]\n', '\n[---]\n') diff --git a/openlp/core/lib/screen.py b/openlp/core/display/screens.py similarity index 98% rename from openlp/core/lib/screen.py rename to openlp/core/display/screens.py index 2e642f497..0a1a4cd28 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/display/screens.py @@ -23,13 +23,14 @@ The :mod:`screen` module provides management functionality for a machines' displays. """ - import logging import copy from PyQt5 import QtCore -from openlp.core.common import Registry, Settings, translate +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings +from openlp.core.common.i18n import translate log = logging.getLogger(__name__) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 4602fee2c..0f4078420 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -29,9 +29,9 @@ import os import re import math -from PyQt5 import QtCore, QtGui, Qt, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.common.path import Path log = logging.getLogger(__name__ + '.__init__') @@ -612,7 +612,6 @@ def create_separated_list(string_list): from .exceptions import ValidationError -from .screen import ScreenList from .formattingtags import FormattingTags from .plugin import PluginStatus, StringContent, Plugin from .pluginmanager import PluginManager @@ -620,7 +619,6 @@ from .settingstab import SettingsTab from .serviceitem import ServiceItem, ServiceItemType, ItemCapabilities from .htmlbuilder import build_html, build_lyrics_format_css, build_lyrics_outline_css, build_chords_css from .imagemanager import ImageManager -from .renderer import Renderer from .mediamanageritem import MediaManagerItem from .projector.db import ProjectorDB, Projector from .projector.pjlink import PJLink diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 9d6330dc6..698b1d73e 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -29,17 +29,19 @@ import os from copy import copy from urllib.parse import quote_plus as urlquote -from sqlalchemy import Table, MetaData, Column, types, create_engine, UnicodeText +from alembic.migration import MigrationContext +from alembic.operations import Operations +from sqlalchemy import Table, MetaData, Column, UnicodeText, types, create_engine from sqlalchemy.engine.url import make_url from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError, OperationalError, ProgrammingError from sqlalchemy.orm import scoped_session, sessionmaker, mapper from sqlalchemy.pool import NullPool -from alembic.migration import MigrationContext -from alembic.operations import Operations - -from openlp.core.common import AppLocation, Settings, delete_file, translate +from openlp.core.common import delete_file +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import translate from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder +from openlp.core.common.settings import Settings from openlp.core.lib.ui import critical_error_message_box log = logging.getLogger(__name__) diff --git a/openlp/core/lib/formattingtags.py b/openlp/core/lib/formattingtags.py index de16bbc7b..c364d388e 100644 --- a/openlp/core/lib/formattingtags.py +++ b/openlp/core/lib/formattingtags.py @@ -24,8 +24,8 @@ Provide HTML Tag management and Formatting Tag access class """ import json -from openlp.core.common import Settings -from openlp.core.lib import translate +from openlp.core.common.settings import Settings +from openlp.core.common.i18n import translate class FormattingTags(object): diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index df88f5f91..0330f10c8 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -411,7 +411,7 @@ import logging from string import Template from PyQt5 import QtWebKit -from openlp.core.common import Settings +from openlp.core.common.settings import Settings from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, VerticalType, HorizontalType log = logging.getLogger(__name__) diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index 79c3a2c90..81a1b659d 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -31,8 +31,10 @@ import queue from PyQt5 import QtCore -from openlp.core.common import Registry, Settings -from openlp.core.lib import ScreenList, resize_image, image_to_byte +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings +from openlp.core.display.screens import ScreenList +from openlp.core.lib import resize_image, image_to_byte log = logging.getLogger(__name__) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 1c7a5b4ef..dda12e10e 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -28,8 +28,10 @@ import re from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.path import Path, path_to_str, str_to_path +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib import ServiceItem, StringContent, ServiceItemContext from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import create_widget_action, critical_error_message_box diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 6a05638ac..059b7a314 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -26,7 +26,9 @@ import logging from PyQt5 import QtCore -from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings +from openlp.core.common.i18n import UiStrings from openlp.core.version import get_version log = logging.getLogger(__name__) diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 20af89e6c..1bdb020aa 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -24,8 +24,11 @@ Provide plugin management """ import os +from openlp.core.common import extension_loader +from openlp.core.common.applocation import AppLocation +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.mixins import OpenLPMixin, RegistryMixin from openlp.core.lib import Plugin, PluginStatus -from openlp.core.common import AppLocation, RegistryProperties, OpenLPMixin, RegistryMixin, extension_loader class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties): diff --git a/openlp/core/lib/projector/constants.py b/openlp/core/lib/projector/constants.py index f2d78e31c..715896133 100644 --- a/openlp/core/lib/projector/constants.py +++ b/openlp/core/lib/projector/constants.py @@ -20,37 +20,15 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ - :mod:`openlp.core.lib.projector.constants` module - - Provides the constants used for projector errors/status/defaults +The :mod:`openlp.core.lib.projector.constants` module provides the constants used for projector errors/status/defaults """ - import logging + +from openlp.core.common.i18n import translate + log = logging.getLogger(__name__) log.debug('projector_constants loaded') -from openlp.core.common import translate - - -__all__ = ['S_OK', 'E_GENERAL', 'E_NOT_CONNECTED', 'E_FAN', 'E_LAMP', 'E_TEMP', - 'E_COVER', 'E_FILTER', 'E_AUTHENTICATION', 'E_NO_AUTHENTICATION', - 'E_UNDEFINED', 'E_PARAMETER', 'E_UNAVAILABLE', 'E_PROJECTOR', - 'E_INVALID_DATA', 'E_WARN', 'E_ERROR', 'E_CLASS', 'E_PREFIX', - 'E_CONNECTION_REFUSED', 'E_REMOTE_HOST_CLOSED_CONNECTION', 'E_HOST_NOT_FOUND', - 'E_SOCKET_ACCESS', 'E_SOCKET_RESOURCE', 'E_SOCKET_TIMEOUT', 'E_DATAGRAM_TOO_LARGE', - 'E_NETWORK', 'E_ADDRESS_IN_USE', 'E_SOCKET_ADDRESS_NOT_AVAILABLE', - 'E_UNSUPPORTED_SOCKET_OPERATION', 'E_PROXY_AUTHENTICATION_REQUIRED', - 'E_SLS_HANDSHAKE_FAILED', 'E_UNFINISHED_SOCKET_OPERATION', 'E_PROXY_CONNECTION_REFUSED', - 'E_PROXY_CONNECTION_CLOSED', 'E_PROXY_CONNECTION_TIMEOUT', 'E_PROXY_NOT_FOUND', - 'E_PROXY_PROTOCOL', 'E_UNKNOWN_SOCKET_ERROR', - 'S_NOT_CONNECTED', 'S_CONNECTING', 'S_CONNECTED', - 'S_STATUS', 'S_OFF', 'S_INITIALIZE', 'S_STANDBY', 'S_WARMUP', 'S_ON', 'S_COOLDOWN', - 'S_INFO', 'S_NETWORK_SENDING', 'S_NETWORK_RECEIVED', - 'ERROR_STRING', 'CR', 'LF', 'PJLINK_ERST_DATA', 'PJLINK_ERST_STATUS', 'PJLINK_POWR_STATUS', - 'PJLINK_PORT', 'PJLINK_MAX_PACKET', 'TIMEOUT', 'ERROR_MSG', 'PJLINK_ERRORS', - 'STATUS_STRING', 'PJLINK_VALID_CMD', 'CONNECTION_ERRORS', - 'PJLINK_DEFAULT_SOURCES', 'PJLINK_DEFAULT_CODES', 'PJLINK_DEFAULT_ITEMS'] - # Set common constants. CR = chr(0x0D) # \r LF = chr(0x0A) # \n diff --git a/openlp/core/lib/projector/pjlink.py b/openlp/core/lib/projector/pjlink.py index 1a5e9e832..2eb4da32c 100644 --- a/openlp/core/lib/projector/pjlink.py +++ b/openlp/core/lib/projector/pjlink.py @@ -20,39 +20,40 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ - :mod:`openlp.core.lib.projector.pjlink` module - Provides the necessary functions for connecting to a PJLink-capable projector. +The :mod:`openlp.core.lib.projector.pjlink` module provides the necessary functions for connecting to a PJLink-capable +projector. - PJLink Class 1 Specifications. - http://pjlink.jbmia.or.jp/english/dl_class1.html - Section 5-1 PJLink Specifications - Section 5-5 Guidelines for Input Terminals +PJLink Class 1 Specifications +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - PJLink Class 2 Specifications. - http://pjlink.jbmia.or.jp/english/dl_class2.html - Section 5-1 PJLink Specifications - Section 5-5 Guidelines for Input Terminals +Website: http://pjlink.jbmia.or.jp/english/dl_class1.html - NOTE: - Function names follow the following syntax: - def process_CCCC(...): - WHERE: - CCCC = PJLink command being processed. +- Section 5-1 PJLink Specifications +- Section 5-5 Guidelines for Input Terminals + +PJLink Class 2 Specifications +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Website: http://pjlink.jbmia.or.jp/english/dl_class2.html + +- Section 5-1 PJLink Specifications +- Section 5-5 Guidelines for Input Terminals + +.. note: + Function names follow the following syntax:: + + def process_CCCC(...): + + where ``CCCC`` is the PJLink command being processed """ - import logging -log = logging.getLogger(__name__) - -log.debug('pjlink loaded') - -__all__ = ['PJLink'] - import re from codecs import decode from PyQt5 import QtCore, QtNetwork -from openlp.core.common import translate, qmd5_hash +from openlp.core.common import qmd5_hash +from openlp.core.common.i18n import translate from openlp.core.lib.projector.constants import CONNECTION_ERRORS, CR, ERROR_MSG, ERROR_STRING, \ E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, E_OK, \ E_PARAMETER, E_PROJECTOR, E_SOCKET_TIMEOUT, E_UNAVAILABLE, E_UNDEFINED, PJLINK_ERRORS, PJLINK_ERST_DATA, \ @@ -60,6 +61,11 @@ from openlp.core.lib.projector.constants import CONNECTION_ERRORS, CR, ERROR_MSG STATUS_STRING, S_CONNECTED, S_CONNECTING, S_INFO, S_NETWORK_RECEIVED, S_NETWORK_SENDING, \ S_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_STATUS +log = logging.getLogger(__name__) +log.debug('pjlink loaded') + +__all__ = ['PJLink'] + # Shortcuts SocketError = QtNetwork.QAbstractSocket.SocketError SocketSTate = QtNetwork.QAbstractSocket.SocketState diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index e2e70a934..db45486f9 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -24,9 +24,9 @@ import logging from PyQt5 import QtCore, QtWidgets +from openlp.core.common.settings import Settings from openlp.core.lib import build_icon from openlp.core.lib.ui import create_widget_action -from openlp.core.common import Settings log = logging.getLogger(__name__) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 124b158e8..22b1f9b5f 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -23,7 +23,6 @@ The :mod:`serviceitem` provides the service item functionality including the type and capability of an item. """ - import datetime import html import logging @@ -33,8 +32,12 @@ import ntpath from PyQt5 import QtGui -from openlp.core.common import RegistryProperties, Settings, translate, AppLocation, md5_hash -from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags, expand_chords, create_thumb +from openlp.core.common import md5_hash +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import translate +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.settings import Settings +from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags, expand_chords log = logging.getLogger(__name__) diff --git a/openlp/core/lib/settingstab.py b/openlp/core/lib/settingstab.py index 7eb4a6877..4b259465f 100644 --- a/openlp/core/lib/settingstab.py +++ b/openlp/core/lib/settingstab.py @@ -23,12 +23,9 @@ 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 PyQt5 import QtWidgets - -from openlp.core.common import RegistryProperties +from openlp.core.common.registry import RegistryProperties class SettingsTab(QtWidgets.QWidget, RegistryProperties): diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 8522225d9..92d12ebfd 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -26,10 +26,12 @@ import json import logging from lxml import etree, objectify -from openlp.core.common import AppLocation, de_hump + +from openlp.core.common import de_hump +from openlp.core.common.applocation import AppLocation from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder -from openlp.core.common.path import Path, str_to_path -from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string +from openlp.core.display.screens import ScreenList +from openlp.core.lib import str_to_bool, get_text_file_string log = logging.getLogger(__name__) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index fe67c53ba..0d5b3c0bd 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -26,8 +26,10 @@ import logging from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, UiStrings, translate, is_macosx +from openlp.core.common import is_macosx from openlp.core.common.actions import ActionList +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry from openlp.core.lib import build_icon diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index 0c37c7624..8cd422ecf 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -23,7 +23,7 @@ import datetime from PyQt5 import QtGui, QtWidgets -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button, create_button_box diff --git a/openlp/core/ui/aboutform.py b/openlp/core/ui/aboutform.py index bed83785b..e2b4eca87 100644 --- a/openlp/core/ui/aboutform.py +++ b/openlp/core/ui/aboutform.py @@ -27,7 +27,7 @@ import webbrowser from PyQt5 import QtCore, QtWidgets from openlp.core.version import get_version -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from .aboutdialog import UiAboutDialog diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 26303f1cb..9581dc6f9 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -27,9 +27,10 @@ from datetime import datetime, timedelta from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import AppLocation, Settings, SlideLimits, UiStrings, translate -from openlp.core.common.languagemanager import format_time -from openlp.core.common.path import path_to_str +from openlp.core.common import SlideLimits +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, format_time, translate +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab, build_icon from openlp.core.ui.lib import PathEdit, PathType from openlp.core.ui.style import HAS_DARK_STYLE diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py index ab001f3e1..0cdd3d28d 100644 --- a/openlp/core/ui/exceptiondialog.py +++ b/openlp/core/ui/exceptiondialog.py @@ -25,7 +25,8 @@ The GUI widgets of the exception dialog. from PyQt5 import QtGui, QtWidgets -from openlp.core.lib import translate, build_icon +from openlp.core.common.i18n import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button, create_button_box diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 2a5f6b2e7..1ceeed989 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -70,11 +70,14 @@ try: except ImportError: VLC_VERSION = '-' -from openlp.core.common import RegistryProperties, Settings, UiStrings, is_linux, translate -from openlp.core.version import get_version +from openlp.core.common import is_linux +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.settings import Settings +from openlp.core.ui.exceptiondialog import Ui_ExceptionDialog from openlp.core.ui.lib.filedialog import FileDialog +from openlp.core.version import get_version -from .exceptiondialog import Ui_ExceptionDialog log = logging.getLogger(__name__) diff --git a/openlp/core/ui/filerenamedialog.py b/openlp/core/ui/filerenamedialog.py index ea6a8593f..0c2e089af 100644 --- a/openlp/core/ui/filerenamedialog.py +++ b/openlp/core/ui/filerenamedialog.py @@ -24,7 +24,8 @@ The UI widgets for the rename dialog """ from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.lib import translate, build_icon +from openlp.core.common.i18n import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/filerenameform.py b/openlp/core/ui/filerenameform.py index 2314fda43..d6a519240 100644 --- a/openlp/core/ui/filerenameform.py +++ b/openlp/core/ui/filerenameform.py @@ -22,12 +22,11 @@ """ The file rename dialog. """ - from PyQt5 import QtCore, QtWidgets -from .filerenamedialog import Ui_FileRenameDialog - -from openlp.core.common import Registry, RegistryProperties, translate +from openlp.core.common.i18n import translate +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.ui.filerenamedialog import Ui_FileRenameDialog class FileRenameForm(QtWidgets.QDialog, Ui_FileRenameDialog, RegistryProperties): diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index f948f8145..ea98577c8 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -34,9 +34,12 @@ from tempfile import gettempdir from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, \ - translate, clean_button_text, trace_error_handler -from openlp.core.common.path import Path +from openlp.core.common import clean_button_text, trace_error_handler +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import translate +from openlp.core.common.path import Path, create_paths +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib import PluginStatus, build_icon from openlp.core.lib.ui import critical_error_message_box from openlp.core.common.httputils import get_web_page, get_url_file_size, url_get_file, CONNECTION_TIMEOUT @@ -277,7 +280,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): self.no_internet_cancel_button.setVisible(False) # Check if this is a re-run of the wizard. self.has_run_wizard = Settings().value('core/has run wizard') - check_directory_exists(Path(gettempdir(), 'openlp')) + create_paths(Path(gettempdir(), 'openlp')) def update_screen_list_combo(self): """ @@ -597,7 +600,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): 'The following files were not able to be ' 'downloaded:
{text}'.format(text=file_list))) msg.setStandardButtons(msg.Ok) - ans = msg.exec() + msg.exec() return True def _set_plugin_status(self, field, tag): diff --git a/openlp/core/ui/firsttimelanguagedialog.py b/openlp/core/ui/firsttimelanguagedialog.py index 2a177f918..abe12aa63 100644 --- a/openlp/core/ui/firsttimelanguagedialog.py +++ b/openlp/core/ui/firsttimelanguagedialog.py @@ -24,7 +24,7 @@ The UI widgets of the language selection dialog. """ from PyQt5 import QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/firsttimelanguageform.py b/openlp/core/ui/firsttimelanguageform.py index bfe593b83..1131883bf 100644 --- a/openlp/core/ui/firsttimelanguageform.py +++ b/openlp/core/ui/firsttimelanguageform.py @@ -24,8 +24,8 @@ The language selection dialog. """ from PyQt5 import QtCore, QtWidgets +from openlp.core.common.i18n import LanguageManager from openlp.core.lib.ui import create_action -from openlp.core.common import LanguageManager from .firsttimelanguagedialog import Ui_FirstTimeLanguageDialog diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index d7f81372e..be893cd5e 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -24,8 +24,9 @@ The UI widgets for the first time wizard. """ from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common.uistrings import UiStrings -from openlp.core.common import translate, is_macosx, clean_button_text, Settings +from openlp.core.common import is_macosx, clean_button_text +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.settings import Settings from openlp.core.lib import build_icon from openlp.core.lib.ui import add_welcome_page diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index 062eb21fc..e92173fed 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -24,9 +24,9 @@ The :mod:`formattingtagform` provides an Tag Edit facility. The Base set are pro Custom tags can be defined and saved. The Custom Tag arrays are saved in a pickle so QSettings works on them. Base Tags cannot be changed. """ - import re -from openlp.core.common import translate + +from openlp.core.common.i18n import translate from openlp.core.lib import FormattingTags diff --git a/openlp/core/ui/formattingtagdialog.py b/openlp/core/ui/formattingtagdialog.py index c6e173128..fa36fa818 100644 --- a/openlp/core/ui/formattingtagdialog.py +++ b/openlp/core/ui/formattingtagdialog.py @@ -24,7 +24,7 @@ The UI widgets for the formatting tags window. """ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index a4c32440a..ab0bfc79f 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -24,10 +24,9 @@ The :mod:`formattingtagform` provides an Tag Edit facility. The Base set are pro Custom tags can be defined and saved. The Custom Tag arrays are saved in a json string so QSettings works on them. Base Tags cannot be changed. """ - from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import FormattingTags from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog from openlp.core.ui.formattingtagcontroller import FormattingTagController diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index cc382badf..8488f13a1 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -26,9 +26,13 @@ import logging from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, Settings, UiStrings, translate, get_images_filter -from openlp.core.common.path import Path, path_to_str, str_to_path -from openlp.core.lib import SettingsTab, ScreenList +from openlp.core.common import get_images_filter +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.path import Path +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings +from openlp.core.display.screens import ScreenList +from openlp.core.lib import SettingsTab from openlp.core.ui.lib import ColorButton, PathEdit log = logging.getLogger(__name__) diff --git a/openlp/core/ui/lib/colorbutton.py b/openlp/core/ui/lib/colorbutton.py index 662b83033..6eef707b2 100644 --- a/openlp/core/ui/lib/colorbutton.py +++ b/openlp/core/ui/lib/colorbutton.py @@ -25,7 +25,7 @@ Provide a custom widget based on QPushButton for the selection of colors """ from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate class ColorButton(QtWidgets.QPushButton): diff --git a/openlp/core/ui/lib/dockwidget.py b/openlp/core/ui/lib/dockwidget.py index d8bcab0fa..398d1e674 100644 --- a/openlp/core/ui/lib/dockwidget.py +++ b/openlp/core/ui/lib/dockwidget.py @@ -28,7 +28,8 @@ import logging from PyQt5 import QtWidgets -from openlp.core.lib import ScreenList, build_icon +from openlp.core.display.screens import ScreenList +from openlp.core.lib import build_icon log = logging.getLogger(__name__) diff --git a/openlp/core/ui/lib/listpreviewwidget.py b/openlp/core/ui/lib/listpreviewwidget.py index 142034785..d03261e8c 100644 --- a/openlp/core/ui/lib/listpreviewwidget.py +++ b/openlp/core/ui/lib/listpreviewwidget.py @@ -26,7 +26,8 @@ It is based on a QTableWidget but represents its contents in list form. from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import RegistryProperties, Settings +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib import ImageSource, ItemCapabilities, ServiceItem @@ -209,21 +210,21 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): Switches to the given row. """ # Retrieve setting - autoscrolling = Settings().value('advanced/autoscrolling') + auto_scrolling = Settings().value('advanced/autoscrolling') # Check if auto-scroll disabled (None) and validate value as dict containing 'dist' and 'pos' # 'dist' represents the slide to scroll to relative to the new slide (-1 = previous, 0 = current, 1 = next) # 'pos' represents the vert position of of the slide (0 = in view, 1 = top, 2 = middle, 3 = bottom) - if not (isinstance(autoscrolling, dict) and 'dist' in autoscrolling and 'pos' in autoscrolling and - isinstance(autoscrolling['dist'], int) and isinstance(autoscrolling['pos'], int)): + if not (isinstance(auto_scrolling, dict) and 'dist' in auto_scrolling and 'pos' in auto_scrolling and + isinstance(auto_scrolling['dist'], int) and isinstance(auto_scrolling['pos'], int)): return # prevent scrolling past list bounds - scroll_to_slide = slide + autoscrolling['dist'] + scroll_to_slide = slide + auto_scrolling['dist'] if scroll_to_slide < 0: scroll_to_slide = 0 if scroll_to_slide >= self.slide_count(): scroll_to_slide = self.slide_count() - 1 # Scroll to item if possible. - self.scrollToItem(self.item(scroll_to_slide, 0), autoscrolling['pos']) + self.scrollToItem(self.item(scroll_to_slide, 0), auto_scrolling['pos']) self.selectRow(slide) def current_slide_number(self): diff --git a/openlp/core/ui/lib/listwidgetwithdnd.py b/openlp/core/ui/lib/listwidgetwithdnd.py index 1758d46fb..5648ff8f4 100755 --- a/openlp/core/ui/lib/listwidgetwithdnd.py +++ b/openlp/core/ui/lib/listwidgetwithdnd.py @@ -26,7 +26,8 @@ import os from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, UiStrings +from openlp.core.common.i18n import UiStrings +from openlp.core.common.registry import Registry class ListWidgetWithDnD(QtWidgets.QListWidget): diff --git a/openlp/core/ui/lib/pathedit.py b/openlp/core/ui/lib/pathedit.py index d5b9272fa..7b28c47ba 100644 --- a/openlp/core/ui/lib/pathedit.py +++ b/openlp/core/ui/lib/pathedit.py @@ -23,7 +23,7 @@ from enum import Enum from PyQt5 import QtCore, QtWidgets -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.path import Path, path_to_str, str_to_path from openlp.core.lib import build_icon from openlp.core.ui.lib.filedialog import FileDialog diff --git a/openlp/core/ui/lib/spelltextedit.py b/openlp/core/ui/lib/spelltextedit.py index 41e28a7e5..d0fc25af2 100644 --- a/openlp/core/ui/lib/spelltextedit.py +++ b/openlp/core/ui/lib/spelltextedit.py @@ -38,7 +38,8 @@ except ImportError: from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.lib import translate, FormattingTags +from openlp.core.common.i18n import translate +from openlp.core.lib import FormattingTags from openlp.core.lib.ui import create_action log = logging.getLogger(__name__) diff --git a/openlp/core/ui/lib/treewidgetwithdnd.py b/openlp/core/ui/lib/treewidgetwithdnd.py index fe45666f5..792fa8ab8 100644 --- a/openlp/core/ui/lib/treewidgetwithdnd.py +++ b/openlp/core/ui/lib/treewidgetwithdnd.py @@ -26,7 +26,8 @@ import os from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, is_win +from openlp.core.common import is_win +from openlp.core.common.registry import Registry class TreeWidgetWithDnD(QtWidgets.QTreeWidget): diff --git a/openlp/core/ui/lib/wizard.py b/openlp/core/ui/lib/wizard.py index 677949b33..1da24eab3 100644 --- a/openlp/core/ui/lib/wizard.py +++ b/openlp/core/ui/lib/wizard.py @@ -23,11 +23,13 @@ The :mod:``wizard`` module provides generic wizard tools for OpenLP. """ import logging -import os from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate, is_macosx +from openlp.core.common import is_macosx +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib import build_icon from openlp.core.lib.ui import add_welcome_page from openlp.core.ui.lib.filedialog import FileDialog diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 097d92699..d7f2264ee 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -26,19 +26,22 @@ Some of the code for this form is based on the examples at: * `http://www.steveheffernan.com/html5-video-player/demo-video-player.html`_ * `http://html5demos.com/two-videos`_ - """ - import html import logging import os from PyQt5 import QtCore, QtWidgets, QtWebKit, QtWebKitWidgets, QtGui, QtMultimedia -from openlp.core.common import AppLocation, Registry, RegistryProperties, OpenLPMixin, Settings, translate,\ - is_macosx, is_win +from openlp.core.common import is_macosx, is_win +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import translate +from openlp.core.common.mixins import OpenLPMixin from openlp.core.common.path import path_to_str -from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings +from openlp.core.display.screens import ScreenList +from openlp.core.lib import ServiceItem, ImageSource, build_html, expand_tags, image_to_byte from openlp.core.lib.theme import BackgroundType from openlp.core.ui import HideMode, AlertLocation, DisplayControllerType diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 25fe818ee..26a8b921a 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -36,21 +36,26 @@ from PyQt5 import QtCore, QtGui, QtWidgets from openlp.core.api import websockets from openlp.core.api.http import server -from openlp.core.common import Registry, RegistryProperties, AppLocation, LanguageManager, Settings, UiStrings, \ - check_directory_exists, translate, is_win, is_macosx, add_actions +from openlp.core.common import is_win, is_macosx, add_actions from openlp.core.common.actions import ActionList, CategoryOrder -from openlp.core.common.path import Path, copyfile, path_to_str, str_to_path -from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, ScreenList, build_icon +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import LanguageManager, UiStrings, translate +from openlp.core.common.path import Path, copyfile, create_paths, path_to_str, str_to_path +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings +from openlp.core.display.screens import ScreenList +from openlp.core.display.renderer import Renderer +from openlp.core.lib import PluginManager, ImageManager, PluginStatus, build_icon from openlp.core.lib.ui import create_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, LiveController, PluginForm, \ ShortcutListForm, FormattingTagForm, PreviewController from openlp.core.ui.firsttimeform import FirstTimeForm -from openlp.core.ui.media import MediaController -from openlp.core.ui.printserviceform import PrintServiceForm -from openlp.core.ui.projector.manager import ProjectorManager from openlp.core.ui.lib.dockwidget import OpenLPDockWidget from openlp.core.ui.lib.filedialog import FileDialog from openlp.core.ui.lib.mediadockmanager import MediaDockManager +from openlp.core.ui.media import MediaController +from openlp.core.ui.printserviceform import PrintServiceForm +from openlp.core.ui.projector.manager import ProjectorManager from openlp.core.ui.style import PROGRESSBAR_STYLE, get_library_stylesheet from openlp.core.version import get_version @@ -853,7 +858,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties): setting_sections.extend([plugin.name for plugin in self.plugin_manager.plugins]) # Copy the settings file to the tmp dir, because we do not want to change the original one. temp_dir_path = Path(gettempdir(), 'openlp') - check_directory_exists(temp_dir_path) + create_paths(temp_dir_path) temp_config_path = temp_dir_path / import_file_path.name copyfile(import_file_path, temp_config_path) settings = Settings() @@ -896,6 +901,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties): try: value = import_settings.value(section_key) except KeyError: + value = None log.warning('The key "{key}" does not exist (anymore), so it will be skipped.'.format(key=section_key)) if value is not None: settings.setValue('{key}'.format(key=section_key), value) @@ -929,7 +935,14 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties): # Make sure it's a .conf file. export_file_path = export_file_path.with_suffix('.conf') self.save_settings() - Settings().export(export_file_path) + try: + Settings().export(export_file_path) + except OSError as ose: + QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'), + translate('OpenLP.MainWindow', + 'An error occurred while exporting the settings: {err}' + ).format(err=ose.strerror), + QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok)) def on_mode_default_item_clicked(self): """ diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 9294ea16f..145f5e8bd 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -26,7 +26,7 @@ import logging from PyQt5 import QtCore -from openlp.core.common import Settings +from openlp.core.common.settings import Settings log = logging.getLogger(__name__ + '.__init__') diff --git a/openlp/core/ui/media/endpoint.py b/openlp/core/ui/media/endpoint.py index 83aca8871..af7623003 100644 --- a/openlp/core/ui/media/endpoint.py +++ b/openlp/core/ui/media/endpoint.py @@ -19,15 +19,16 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`~openlp.core.api.endpoint` module contains various API endpoints +""" import logging from openlp.core.api.http.endpoint import Endpoint from openlp.core.api.http import requires_auth -from openlp.core.common import Registry - +from openlp.core.common.registry import Registry log = logging.getLogger(__name__) - media_endpoint = Endpoint('media') diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index b778c68dd..bdc315556 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -29,8 +29,11 @@ import datetime from PyQt5 import QtCore, QtWidgets from openlp.core.api.http import register_endpoint -from openlp.core.common import OpenLPMixin, Registry, RegistryMixin, RegistryProperties, Settings, UiStrings, \ - extension_loader, translate +from openlp.core.common import extension_loader +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.mixins import OpenLPMixin, RegistryMixin +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib import ItemCapabilities from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui import DisplayControllerType diff --git a/openlp/core/ui/media/mediaplayer.py b/openlp/core/ui/media/mediaplayer.py index edf08608b..e4d210513 100644 --- a/openlp/core/ui/media/mediaplayer.py +++ b/openlp/core/ui/media/mediaplayer.py @@ -22,7 +22,7 @@ """ The :mod:`~openlp.core.ui.media.mediaplayer` module contains the MediaPlayer class. """ -from openlp.core.common import RegistryProperties +from openlp.core.common.registry import RegistryProperties from openlp.core.ui.media import MediaState diff --git a/openlp/core/ui/media/playertab.py b/openlp/core/ui/media/playertab.py index da8386734..f719a167b 100644 --- a/openlp/core/ui/media/playertab.py +++ b/openlp/core/ui/media/playertab.py @@ -25,7 +25,9 @@ The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab fo import platform from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab from openlp.core.lib.ui import create_button from openlp.core.ui.media import get_media_players, set_media_players diff --git a/openlp/core/ui/media/systemplayer.py b/openlp/core/ui/media/systemplayer.py index 52ad1e749..a6423db02 100644 --- a/openlp/core/ui/media/systemplayer.py +++ b/openlp/core/ui/media/systemplayer.py @@ -28,7 +28,7 @@ import mimetypes from PyQt5 import QtCore, QtMultimedia, QtMultimediaWidgets -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from openlp.core.ui.media import MediaState from openlp.core.ui.media.mediaplayer import MediaPlayer diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 8d330f683..605f6a8a1 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -31,8 +31,9 @@ import sys import ctypes from PyQt5 import QtWidgets -from openlp.core.common import Settings, is_win, is_macosx, is_linux -from openlp.core.lib import translate +from openlp.core.common import is_win, is_macosx, is_linux +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings from openlp.core.ui.media import MediaState, MediaType from openlp.core.ui.media.mediaplayer import MediaPlayer diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index 571f21e48..e9142463e 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -26,8 +26,8 @@ import logging from PyQt5 import QtGui, QtWebKitWidgets -from openlp.core.common import Settings -from openlp.core.lib import translate +from openlp.core.common.settings import Settings +from openlp.core.common.i18n import translate from openlp.core.ui.media import MediaState from openlp.core.ui.media.mediaplayer import MediaPlayer diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py index a89b0ab6a..76087a33a 100644 --- a/openlp/core/ui/plugindialog.py +++ b/openlp/core/ui/plugindialog.py @@ -24,7 +24,7 @@ The UI widgets of the plugin view dialog #""" from PyQt5 import QtCore, QtWidgets -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 0299683a5..b34d79714 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -26,9 +26,10 @@ import logging from PyQt5 import QtCore, QtWidgets -from openlp.core.common import RegistryProperties, translate +from openlp.core.common.i18n import translate +from openlp.core.common.registry import RegistryProperties from openlp.core.lib import PluginStatus -from .plugindialog import Ui_PluginViewDialog +from openlp.core.ui.plugindialog import Ui_PluginViewDialog log = logging.getLogger(__name__) diff --git a/openlp/core/ui/printservicedialog.py b/openlp/core/ui/printservicedialog.py index 925b88599..d71b42cdb 100644 --- a/openlp/core/ui/printservicedialog.py +++ b/openlp/core/ui/printservicedialog.py @@ -24,7 +24,7 @@ The UI widgets of the print service dialog. """ from PyQt5 import QtCore, QtWidgets, QtPrintSupport -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib import build_icon from openlp.core.ui.lib import SpellTextEdit diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 9394de9ce..482d0f084 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -23,16 +23,17 @@ The actual print service dialog """ import datetime -import os import html import lxml.html from PyQt5 import QtCore, QtGui, QtWidgets, QtPrintSupport -from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib import get_text_file_string from openlp.core.ui.printservicedialog import Ui_PrintServiceDialog, ZoomSize -from openlp.core.common import AppLocation DEFAULT_CSS = """/* Edit this file to customize the service order print. Note, that not all CSS diff --git a/openlp/core/ui/projector/editform.py b/openlp/core/ui/projector/editform.py index 6a6f0a362..bd3267665 100644 --- a/openlp/core/ui/projector/editform.py +++ b/openlp/core/ui/projector/editform.py @@ -20,22 +20,22 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ - :mod: `openlp.core.ui.projector.editform` module - - Provides the functions for adding/editing entries in the projector database. +The :mod: `openlp.core.ui.projector.editform` module provides the functions for adding/editing entries in the projector +database. """ - import logging -log = logging.getLogger(__name__) -log.debug('editform loaded') from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate, verify_ip_address +from openlp.core.common import verify_ip_address +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.projector.db import Projector from openlp.core.lib.projector.constants import PJLINK_PORT +log = logging.getLogger(__name__) +log.debug('editform loaded') + class Ui_ProjectorEditForm(object): """ diff --git a/openlp/core/ui/projector/manager.py b/openlp/core/ui/projector/manager.py index d3c1880d2..47bbb832e 100644 --- a/openlp/core/ui/projector/manager.py +++ b/openlp/core/ui/projector/manager.py @@ -29,9 +29,10 @@ import logging from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import RegistryProperties, Settings, OpenLPMixin, \ - RegistryMixin, translate -from openlp.core.ui.lib import OpenLPToolbar +from openlp.core.common.i18n import translate +from openlp.core.common.mixins import OpenLPMixin, RegistryMixin +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib.ui import create_widget_action from openlp.core.lib.projector import DialogSourceStyle from openlp.core.lib.projector.constants import ERROR_MSG, ERROR_STRING, E_AUTHENTICATION, E_ERROR, \ @@ -39,6 +40,7 @@ from openlp.core.lib.projector.constants import ERROR_MSG, ERROR_STRING, E_AUTHE S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, S_STANDBY, S_WARMUP from openlp.core.lib.projector.db import ProjectorDB from openlp.core.lib.projector.pjlink import PJLink, PJLinkUDP +from openlp.core.ui.lib import OpenLPToolbar from openlp.core.ui.projector.editform import ProjectorEditForm from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle diff --git a/openlp/core/ui/projector/sourceselectform.py b/openlp/core/ui/projector/sourceselectform.py index dc231d3e9..0c150d25b 100644 --- a/openlp/core/ui/projector/sourceselectform.py +++ b/openlp/core/ui/projector/sourceselectform.py @@ -28,7 +28,8 @@ import logging from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate, is_macosx +from openlp.core.common import is_macosx +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.projector.db import ProjectorSource from openlp.core.lib.projector.constants import PJLINK_DEFAULT_SOURCES, PJLINK_DEFAULT_CODES diff --git a/openlp/core/ui/projector/tab.py b/openlp/core/ui/projector/tab.py index 06d9e53a7..b7c2e5dda 100644 --- a/openlp/core/ui/projector/tab.py +++ b/openlp/core/ui/projector/tab.py @@ -20,21 +20,20 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ - :mod:`openlp.core.ui.projector.tab` - - Provides the settings tab in the settings dialog. +The :mod:`openlp.core.ui.projector.tab` module provides the settings tab in the settings dialog. """ - import logging -log = logging.getLogger(__name__) -log.debug('projectortab module loaded') from PyQt5 import QtWidgets -from openlp.core.common import Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab from openlp.core.lib.projector import DialogSourceStyle +log = logging.getLogger(__name__) +log.debug('projectortab module loaded') + class ProjectorTab(SettingsTab): """ diff --git a/openlp/core/ui/serviceitemeditdialog.py b/openlp/core/ui/serviceitemeditdialog.py index be5da152a..80be015f8 100644 --- a/openlp/core/ui/serviceitemeditdialog.py +++ b/openlp/core/ui/serviceitemeditdialog.py @@ -24,7 +24,7 @@ The UI widgets for the service item edit dialog """ from PyQt5 import QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box, create_button diff --git a/openlp/core/ui/serviceitemeditform.py b/openlp/core/ui/serviceitemeditform.py index f23956e70..17f648ecc 100644 --- a/openlp/core/ui/serviceitemeditform.py +++ b/openlp/core/ui/serviceitemeditform.py @@ -24,9 +24,8 @@ The service item edit dialog """ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, RegistryProperties - -from .serviceitemeditdialog import Ui_ServiceItemEditDialog +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.ui.serviceitemeditdialog import Ui_ServiceItemEditDialog class ServiceItemEditForm(QtWidgets.QDialog, Ui_ServiceItemEditDialog, RegistryProperties): diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 153034e33..ff6ab9a47 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -32,11 +32,14 @@ from tempfile import mkstemp from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, ThemeLevel, OpenLPMixin, \ - RegistryMixin, check_directory_exists, UiStrings, translate, split_filename, delete_file +from openlp.core.common import ThemeLevel, split_filename, delete_file from openlp.core.common.actions import ActionList, CategoryOrder -from openlp.core.common.languagemanager import format_time -from openlp.core.common.path import Path, path_to_str, str_to_path +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, format_time, translate +from openlp.core.common.mixins import OpenLPMixin, RegistryMixin +from openlp.core.common.path import Path, create_paths, path_to_str, str_to_path +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm @@ -594,7 +597,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa audio_from = os.path.join(self.service_path, audio_from) save_file = os.path.join(self.service_path, audio_to) save_path = os.path.split(save_file)[0] - check_directory_exists(Path(save_path)) + create_paths(Path(save_path)) if not os.path.exists(save_file): shutil.copy(audio_from, save_file) zip_file.write(audio_from, audio_to) diff --git a/openlp/core/ui/servicenoteform.py b/openlp/core/ui/servicenoteform.py index fc4da438a..998431636 100644 --- a/openlp/core/ui/servicenoteform.py +++ b/openlp/core/ui/servicenoteform.py @@ -24,7 +24,8 @@ The :mod:`~openlp.core.ui.servicenoteform` module contains the `ServiceNoteForm` """ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, RegistryProperties, translate +from openlp.core.common.i18n import translate +from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.ui.lib import SpellTextEdit from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 61626fa6e..0e591e6ad 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -24,7 +24,7 @@ The UI widgets of the settings dialog. """ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index f77c2be59..5524b89ad 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -27,12 +27,12 @@ import logging from PyQt5 import QtCore, QtWidgets from openlp.core.api import ApiTab -from openlp.core.common import Registry, RegistryProperties +from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.lib import build_icon from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab from openlp.core.ui.media import PlayerTab -from .settingsdialog import Ui_SettingsDialog from openlp.core.ui.projector.tab import ProjectorTab +from openlp.core.ui.settingsdialog import Ui_SettingsDialog log = logging.getLogger(__name__) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index f47b79eb4..22da4109f 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -24,7 +24,7 @@ The list of shortcuts within a dialog. """ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index fc9a77ac8..3db127d75 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -20,15 +20,18 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`~openlp.core.ui.shortcutlistform` module contains the form class""" +The :mod:`~openlp.core.ui.shortcutlistform` module contains the form class +""" import logging import re from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import RegistryProperties, Settings, translate from openlp.core.common.actions import ActionList -from .shortcutlistdialog import Ui_ShortcutListDialog +from openlp.core.common.i18n import translate +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.settings import Settings +from openlp.core.ui.shortcutlistdialog import Ui_ShortcutListDialog REMOVE_AMPERSAND = re.compile(r'&{1}') diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 26bbeb38d..062adbd71 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -30,11 +30,14 @@ from threading import Lock from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, RegistryProperties, Settings, SlideLimits, UiStrings, translate, \ - RegistryMixin, OpenLPMixin +from openlp.core.common import SlideLimits from openlp.core.common.actions import ActionList, CategoryOrder -from openlp.core.lib import ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, ScreenList, build_icon, \ - build_html +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.mixins import OpenLPMixin, RegistryMixin +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings +from openlp.core.display.screens import ScreenList +from openlp.core.lib import ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, build_icon, build_html from openlp.core.lib.ui import create_action from openlp.core.ui.lib.toolbar import OpenLPToolbar from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py index 256bf8f55..fd5163593 100644 --- a/openlp/core/ui/starttimedialog.py +++ b/openlp/core/ui/starttimedialog.py @@ -24,7 +24,7 @@ The UI widgets for the time dialog """ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index ae82d4c9e..e1ad9d9a6 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -24,10 +24,10 @@ The actual start time form. """ from PyQt5 import QtCore, QtWidgets -from .starttimedialog import Ui_StartTimeDialog - -from openlp.core.common import Registry, RegistryProperties, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.lib.ui import critical_error_message_box +from openlp.core.ui.starttimedialog import Ui_StartTimeDialog class StartTimeForm(QtWidgets.QDialog, Ui_StartTimeDialog, RegistryProperties): diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index 37978d10a..c15110569 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -23,11 +23,12 @@ The Theme wizard """ import logging -import os from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, RegistryProperties, UiStrings, translate, get_images_filter, is_not_image_file +from openlp.core.common import get_images_filter, is_not_image_file +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.lib.theme import BackgroundType, BackgroundGradientType from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui import ThemeLayoutForm diff --git a/openlp/core/ui/themelayoutdialog.py b/openlp/core/ui/themelayoutdialog.py index dc48234da..d9ed9638d 100644 --- a/openlp/core/ui/themelayoutdialog.py +++ b/openlp/core/ui/themelayoutdialog.py @@ -24,7 +24,7 @@ The layout of the theme """ from PyQt5 import QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index dffa82d47..f27eb6abb 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -28,10 +28,13 @@ from xml.etree.ElementTree import ElementTree, XML from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \ - UiStrings, check_directory_exists, translate, delete_file -from openlp.core.common.languagemanager import get_locale_key -from openlp.core.common.path import Path, copyfile, path_to_str, rmtree +from openlp.core.common import delete_file +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, translate, get_locale_key +from openlp.core.common.mixins import OpenLPMixin, RegistryMixin +from openlp.core.common.path import Path, copyfile, create_paths, path_to_str, rmtree +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \ check_item_selected, create_thumb, validate_thumb from openlp.core.lib.theme import Theme, BackgroundType @@ -176,9 +179,8 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage :rtype: None """ self.theme_path = AppLocation.get_section_data_path(self.settings_section) - check_directory_exists(self.theme_path) self.thumb_path = self.theme_path / 'thumbnails' - check_directory_exists(self.thumb_path) + create_paths(self.theme_path, self.thumb_path) def check_list_state(self, item, field=None): """ @@ -594,7 +596,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage # is directory or preview file continue full_name = directory_path / zipped_file_rel_path - check_directory_exists(full_name.parent) + create_paths(full_name.parent) if zipped_file_rel_path.suffix.lower() == '.xml' or zipped_file_rel_path.suffix.lower() == '.json': file_xml = str(theme_zip.read(zipped_file), 'utf-8') with full_name.open('w', encoding='utf-8') as out_file: @@ -661,7 +663,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage name = theme.theme_name theme_pretty = theme.export_theme(self.theme_path) theme_dir = self.theme_path / name - check_directory_exists(theme_dir) + create_paths(theme_dir) theme_path = theme_dir / '{file_name}.json'.format(file_name=name) try: theme_path.write_text(theme_pretty) diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index bf4be809c..d578e0082 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -22,11 +22,12 @@ """ The Themes configuration tab """ - - from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, Settings, ThemeLevel, UiStrings, translate +from openlp.core.common import ThemeLevel +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab from openlp.core.lib.ui import find_and_set_in_combo_box diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index 80efa5fdf..c50cd1a85 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -24,8 +24,8 @@ The Create/Edit theme wizard """ from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import UiStrings, translate, is_macosx -from openlp.core.common.path import Path +from openlp.core.common import is_macosx +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib import build_icon from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets diff --git a/openlp/core/version.py b/openlp/core/version.py index 14ddc40ff..6d038a3d9 100644 --- a/openlp/core/version.py +++ b/openlp/core/version.py @@ -34,7 +34,8 @@ from subprocess import Popen, PIPE import requests from PyQt5 import QtCore -from openlp.core.common import AppLocation, Settings +from openlp.core.common.applocation import AppLocation +from openlp.core.common.settings import Settings from openlp.core.threading import run_thread log = logging.getLogger(__name__) diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index ba63c61f4..992c5c360 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -25,8 +25,9 @@ import logging from PyQt5 import QtGui from openlp.core.api.http import register_endpoint -from openlp.core.common import Settings, UiStrings, translate from openlp.core.common.actions import ActionList +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.settings import Settings from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.theme import VerticalType diff --git a/openlp/plugins/alerts/endpoint.py b/openlp/plugins/alerts/endpoint.py index 65800c5f6..19449b863 100644 --- a/openlp/plugins/alerts/endpoint.py +++ b/openlp/plugins/alerts/endpoint.py @@ -26,7 +26,7 @@ from urllib.parse import urlparse from openlp.core.api.http.endpoint import Endpoint from openlp.core.api.http import requires_auth -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.lib import PluginStatus diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py index e6e97d467..79dff94c0 100644 --- a/openlp/plugins/alerts/forms/alertdialog.py +++ b/openlp/plugins/alerts/forms/alertdialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button, create_button_box diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index a27d93390..c7f9487c4 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -22,7 +22,8 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, translate +from openlp.core.common.i18n import translate +from openlp.core.common.registry import Registry from openlp.plugins.alerts.lib.db import AlertItem from .alertdialog import Ui_AlertDialog diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index 9b1abd226..6eb26ff57 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -23,10 +23,12 @@ The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of the plugin which manages storing and displaying of alerts. """ - from PyQt5 import QtCore -from openlp.core.common import OpenLPMixin, RegistryMixin, Registry, RegistryProperties, Settings, translate +from openlp.core.common.i18n import translate +from openlp.core.common.mixins import OpenLPMixin, RegistryMixin +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings class AlertsManager(OpenLPMixin, RegistryMixin, QtCore.QObject, RegistryProperties): diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index f5934a30e..406e35607 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -22,7 +22,8 @@ from PyQt5 import QtGui, QtWidgets -from openlp.core.common import Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab from openlp.core.lib.ui import create_valign_selection_widgets from openlp.core.ui.lib.colorbutton import ColorButton diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 4829ff234..7d5baf859 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -23,9 +23,9 @@ import logging from openlp.core.api.http import register_endpoint -from openlp.core.common import UiStrings +from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.actions import ActionList -from openlp.core.lib import Plugin, StringContent, build_icon, translate +from openlp.core.lib import Plugin, StringContent, build_icon from openlp.plugins.bibles.endpoint import api_bibles_endpoint, bibles_endpoint from openlp.core.lib.ui import create_action from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem, LayoutStyle, DisplayStyle, \ diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index fd270fced..4cb9ee453 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -34,8 +34,10 @@ try: except: PYSWORD_AVAILABLE = False -from openlp.core.common import AppLocation, Settings, UiStrings, trace_error_handler, translate -from openlp.core.common.languagemanager import get_locale_key +from openlp.core.common import trace_error_handler +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, translate, get_locale_key +from openlp.core.common.settings import Settings from openlp.core.lib.db import delete_database from openlp.core.lib.exceptions import ValidationError from openlp.core.lib.ui import critical_error_message_box diff --git a/openlp/plugins/bibles/forms/booknamedialog.py b/openlp/plugins/bibles/forms/booknamedialog.py index 6118ac293..07d749755 100644 --- a/openlp/plugins/bibles/forms/booknamedialog.py +++ b/openlp/plugins/bibles/forms/booknamedialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/bibles/forms/booknameform.py b/openlp/plugins/bibles/forms/booknameform.py index 588ba96d4..f78559ce5 100644 --- a/openlp/plugins/bibles/forms/booknameform.py +++ b/openlp/plugins/bibles/forms/booknameform.py @@ -29,7 +29,7 @@ import re from PyQt5.QtWidgets import QDialog from PyQt5 import QtCore -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.forms.booknamedialog import Ui_BookNameDialog from openlp.plugins.bibles.lib import BibleStrings diff --git a/openlp/plugins/bibles/forms/editbibledialog.py b/openlp/plugins/bibles/forms/editbibledialog.py index ecd672503..799c2af7c 100644 --- a/openlp/plugins/bibles/forms/editbibledialog.py +++ b/openlp/plugins/bibles/forms/editbibledialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings diff --git a/openlp/plugins/bibles/forms/editbibleform.py b/openlp/plugins/bibles/forms/editbibleform.py index 543531c2d..365d98a4e 100644 --- a/openlp/plugins/bibles/forms/editbibleform.py +++ b/openlp/plugins/bibles/forms/editbibleform.py @@ -21,12 +21,12 @@ ############################################################################### import logging -import os import re from PyQt5 import QtCore, QtWidgets -from openlp.core.common import RegistryProperties, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import RegistryProperties from openlp.core.lib.ui import critical_error_message_box from .editbibledialog import Ui_EditBibleDialog from openlp.plugins.bibles.lib import BibleStrings diff --git a/openlp/plugins/bibles/forms/languagedialog.py b/openlp/plugins/bibles/forms/languagedialog.py index f51bc79fc..fad18bf05 100644 --- a/openlp/plugins/bibles/forms/languagedialog.py +++ b/openlp/plugins/bibles/forms/languagedialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/bibles/forms/languageform.py b/openlp/plugins/bibles/forms/languageform.py index 6494950c8..80ce9e989 100644 --- a/openlp/plugins/bibles/forms/languageform.py +++ b/openlp/plugins/bibles/forms/languageform.py @@ -28,8 +28,7 @@ import logging from PyQt5.QtWidgets import QDialog from PyQt5 import QtCore -from openlp.core.common import translate -from openlp.core.common.languages import languages +from openlp.core.common.i18n import LANGUAGES, translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.forms.languagedialog import Ui_LanguageDialog @@ -55,7 +54,7 @@ class LanguageForm(QDialog, Ui_LanguageDialog): if bible_name: self.bible_label.setText(bible_name) self.language_combo_box.addItem('') - for language in languages: + for language in LANGUAGES: self.language_combo_box.addItem(language.name, language.id) return QDialog.exec(self) diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 9d90a94e2..9247485c1 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -26,8 +26,8 @@ plugin. import logging import re -from openlp.core.common import Settings -from openlp.core.lib import translate +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/lib/bibleimport.py b/openlp/plugins/bibles/lib/bibleimport.py index 00c7262a0..61aa35a3e 100644 --- a/openlp/plugins/bibles/lib/bibleimport.py +++ b/openlp/plugins/bibles/lib/bibleimport.py @@ -23,7 +23,9 @@ from lxml import etree, objectify from zipfile import is_zipfile -from openlp.core.common import OpenLPMixin, Registry, RegistryProperties, languages, translate +from openlp.core.common.mixins import OpenLPMixin +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.i18n import get_language, translate from openlp.core.lib import ValidationError from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.lib.db import AlternativeBookNamesDB, BibleDB, BiblesResourcesDB @@ -109,7 +111,7 @@ class BibleImport(OpenLPMixin, RegistryProperties, BibleDB): """ language_id = None if file_language: - language = languages.get_language(file_language) + language = get_language(file_language) if language and language.id: language_id = language.id if not language_id: diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 1420dcc33..21970c106 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -24,7 +24,9 @@ import logging from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab from openlp.core.lib.ui import find_and_set_in_combo_box from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, update_reference_separators, \ diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 64744113b..5d82fe613 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -33,7 +33,9 @@ from sqlalchemy.exc import OperationalError from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm.exc import UnmappedClassError -from openlp.core.common import AppLocation, translate, clean_filename +from openlp.core.common import clean_filename +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import translate from openlp.core.lib.db import BaseModel, init_db, Manager from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.lib import BibleStrings, LanguageSelection, upgrade @@ -546,7 +548,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): """ log.debug('BiblesResourcesDB.get_book_like("{text}")'.format(text=string)) if not isinstance(string, str): - name = str(string) + string = str(string) books = BiblesResourcesDB.run_sql( 'SELECT id, testament_id, name, abbreviation, chapters FROM book_reference WHERE ' 'LOWER(name) LIKE ? OR LOWER(abbreviation) LIKE ?', diff --git a/openlp/plugins/bibles/lib/importers/csvbible.py b/openlp/plugins/bibles/lib/importers/csvbible.py index e319bdf36..e897c4803 100644 --- a/openlp/plugins/bibles/lib/importers/csvbible.py +++ b/openlp/plugins/bibles/lib/importers/csvbible.py @@ -52,12 +52,12 @@ All CSV files are expected to use a comma (',') as the delimiter and double quot import csv from collections import namedtuple -from openlp.core.common import get_file_encoding, translate +from openlp.core.common import get_file_encoding +from openlp.core.common.i18n import translate from openlp.core.common.path import Path from openlp.core.lib.exceptions import ValidationError from openlp.plugins.bibles.lib.bibleimport import BibleImport - Book = namedtuple('Book', 'id, testament_id, name, abbreviation') Verse = namedtuple('Verse', 'book_id_name, chapter_number, number, text') diff --git a/openlp/plugins/bibles/lib/importers/http.py b/openlp/plugins/bibles/lib/importers/http.py index ffc2ddfbf..5d3098b4d 100644 --- a/openlp/plugins/bibles/lib/importers/http.py +++ b/openlp/plugins/bibles/lib/importers/http.py @@ -30,9 +30,10 @@ import urllib.error from bs4 import BeautifulSoup, NavigableString, Tag -from openlp.core.common import Registry, RegistryProperties, translate -from openlp.core.lib.ui import critical_error_message_box from openlp.core.common.httputils import get_web_page +from openlp.core.common.i18n import translate +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.lib import SearchResults from openlp.plugins.bibles.lib.bibleimport import BibleImport from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB, Book diff --git a/openlp/plugins/bibles/lib/importers/sword.py b/openlp/plugins/bibles/lib/importers/sword.py index e01783087..fd7b91a44 100644 --- a/openlp/plugins/bibles/lib/importers/sword.py +++ b/openlp/plugins/bibles/lib/importers/sword.py @@ -23,7 +23,7 @@ import logging from pysword import modules -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.lib.bibleimport import BibleImport from openlp.plugins.bibles.lib.db import BiblesResourcesDB diff --git a/openlp/plugins/bibles/lib/importers/zefania.py b/openlp/plugins/bibles/lib/importers/zefania.py index 221d57cb3..a3849a7a0 100644 --- a/openlp/plugins/bibles/lib/importers/zefania.py +++ b/openlp/plugins/bibles/lib/importers/zefania.py @@ -22,7 +22,7 @@ import logging -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.lib.bibleimport import BibleImport from openlp.plugins.bibles.lib.db import BiblesResourcesDB diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 5c8728e58..55444f080 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -22,8 +22,13 @@ import logging -from openlp.core.common import AppLocation, OpenLPMixin, RegistryProperties, Settings, translate, delete_file, UiStrings +from openlp.core.common import delete_file +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.mixins import OpenLPMixin from openlp.core.common.path import Path +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.settings import Settings from openlp.plugins.bibles.lib import LanguageSelection, parse_reference from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta from .importers.csvbible import CSVBible diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index a05747705..10bb8d6ec 100755 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -26,12 +26,13 @@ from enum import IntEnum, unique from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate, get_locale_key +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import set_case_insensitive_completer, create_horizontal_adjusting_combo_box, \ critical_error_message_box, find_and_set_in_combo_box, build_icon -from openlp.core.common.languagemanager import get_locale_key from openlp.plugins.bibles.forms.bibleimportform import BibleImportForm from openlp.plugins.bibles.forms.editbibleform import EditBibleForm from openlp.plugins.bibles.lib import DisplayStyle, LayoutStyle, VerseReferenceList, \ diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index 99eda1d52..8cb02ab17 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -27,7 +27,8 @@ for the Custom Slides plugin. import logging from openlp.core.api.http import register_endpoint -from openlp.core.lib import Plugin, StringContent, build_icon, translate +from openlp.core.common.i18n import translate +from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.plugins.custom.endpoint import api_custom_endpoint, custom_endpoint from openlp.plugins.custom.lib import CustomMediaItem, CustomTab diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index c71ac3d70..e0f5646d3 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -19,10 +19,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - from PyQt5 import QtWidgets -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box, create_button diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 1bd2187f2..ee2d7b415 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -24,7 +24,8 @@ import logging from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, translate +from openlp.core.common.i18n import translate +from openlp.core.common.registry import Registry from openlp.core.lib.ui import critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.custom.lib import CustomXMLBuilder, CustomXMLParser from openlp.plugins.custom.lib.db import CustomSlide diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py index ede46a6ee..452146feb 100644 --- a/openlp/plugins/custom/forms/editcustomslidedialog.py +++ b/openlp/plugins/custom/forms/editcustomslidedialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtWidgets -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button, create_button_box from openlp.core.ui.lib import SpellTextEdit diff --git a/openlp/plugins/custom/lib/customtab.py b/openlp/plugins/custom/lib/customtab.py index 167aa6d0d..2239ac2ce 100644 --- a/openlp/plugins/custom/lib/customtab.py +++ b/openlp/plugins/custom/lib/customtab.py @@ -23,10 +23,10 @@ The :mod:`~openlp.plugins.custom.lib.customtab` module contains the settings tab for the Custom Slides plugin, which is inserted into the configuration dialog. """ - from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Settings, translate +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab diff --git a/openlp/plugins/custom/lib/db.py b/openlp/plugins/custom/lib/db.py index b1e42e3d9..dc1f74567 100644 --- a/openlp/plugins/custom/lib/db.py +++ b/openlp/plugins/custom/lib/db.py @@ -23,12 +23,11 @@ The :mod:`db` module provides the database and schema that is the backend for the Custom plugin """ - from sqlalchemy import Column, Table, types from sqlalchemy.orm import mapper +from openlp.core.common.i18n import get_locale_key from openlp.core.lib.db import BaseModel, init_db -from openlp.core.common.languagemanager import get_locale_key class CustomSlide(BaseModel): diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index c90d14e10..35eddc117 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -25,7 +25,9 @@ import logging from PyQt5 import QtCore, QtWidgets from sqlalchemy.sql import or_, func, and_ -from openlp.core.common import Registry, Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext, PluginStatus, \ check_item_selected from openlp.plugins.custom.forms.editcustomform import EditCustomForm diff --git a/openlp/plugins/images/forms/addgroupdialog.py b/openlp/plugins/images/forms/addgroupdialog.py index 233bfef31..665fcd824 100644 --- a/openlp/plugins/images/forms/addgroupdialog.py +++ b/openlp/plugins/images/forms/addgroupdialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index 953d02339..88f9377f3 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.images.forms.addgroupdialog import Ui_AddGroupDialog diff --git a/openlp/plugins/images/forms/choosegroupdialog.py b/openlp/plugins/images/forms/choosegroupdialog.py index f71a74e70..35e959fb5 100644 --- a/openlp/plugins/images/forms/choosegroupdialog.py +++ b/openlp/plugins/images/forms/choosegroupdialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 36051a505..cfa5448f5 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -25,7 +25,8 @@ from PyQt5 import QtGui import logging from openlp.core.api.http import register_endpoint -from openlp.core.common import Settings, translate +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings from openlp.core.lib import Plugin, StringContent, ImageSource, build_icon from openlp.core.lib.db import Manager from openlp.plugins.images.endpoint import api_images_endpoint, images_endpoint diff --git a/openlp/plugins/images/lib/imagetab.py b/openlp/plugins/images/lib/imagetab.py index 565ef6543..23b742bfb 100644 --- a/openlp/plugins/images/lib/imagetab.py +++ b/openlp/plugins/images/lib/imagetab.py @@ -22,7 +22,8 @@ from PyQt5 import QtWidgets -from openlp.core.common import Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab from openlp.core.ui.lib.colorbutton import ColorButton diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index b436d2708..134dbe2eb 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -24,14 +24,16 @@ import logging from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, AppLocation, Settings, UiStrings, check_directory_exists, translate, \ - delete_file, get_images_filter -from openlp.core.common.path import Path +from openlp.core.common import delete_file, get_images_filter +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, translate, get_locale_key +from openlp.core.common.path import Path, create_paths +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import ItemCapabilities, MediaManagerItem, ServiceItemContext, StringContent, build_icon, \ check_item_selected, create_thumb, validate_thumb from openlp.core.lib.ui import create_widget_action, critical_error_message_box from openlp.core.ui.lib.treewidgetwithdnd import TreeWidgetWithDnD -from openlp.core.common.languagemanager import get_locale_key from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups @@ -99,7 +101,7 @@ class ImageMediaItem(MediaManagerItem): self.list_view.setIndentation(self.list_view.default_indentation) self.list_view.allow_internal_dnd = True self.service_path = AppLocation.get_section_data_path(self.settings_section) / 'thumbnails' - check_directory_exists(self.service_path) + create_paths(self.service_path) # Load images from the database self.load_full_list( self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.file_path), initial_load=True) diff --git a/openlp/plugins/images/lib/upgrade.py b/openlp/plugins/images/lib/upgrade.py index d467e9d3c..bc1310d79 100644 --- a/openlp/plugins/images/lib/upgrade.py +++ b/openlp/plugins/images/lib/upgrade.py @@ -27,7 +27,7 @@ import logging from sqlalchemy import Column, Table -from openlp.core.common import AppLocation +from openlp.core.common.applocation import AppLocation from openlp.core.common.db import drop_columns from openlp.core.common.json import OpenLPJsonEncoder from openlp.core.common.path import Path diff --git a/openlp/plugins/media/forms/mediaclipselectordialog.py b/openlp/plugins/media/forms/mediaclipselectordialog.py index 82e9a77b5..38ecda99c 100644 --- a/openlp/plugins/media/forms/mediaclipselectordialog.py +++ b/openlp/plugins/media/forms/mediaclipselectordialog.py @@ -23,7 +23,7 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon diff --git a/openlp/plugins/media/forms/mediaclipselectorform.py b/openlp/plugins/media/forms/mediaclipselectorform.py index d6e6fd352..f48e9b09e 100644 --- a/openlp/plugins/media/forms/mediaclipselectorform.py +++ b/openlp/plugins/media/forms/mediaclipselectorform.py @@ -19,7 +19,6 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - import os import logging import re @@ -28,7 +27,9 @@ from datetime import datetime from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import translate, is_win, is_linux, is_macosx, RegistryProperties +from openlp.core.common import is_win, is_linux, is_macosx +from openlp.core.common.i18n import translate +from openlp.core.common.registry import RegistryProperties from openlp.plugins.media.forms.mediaclipselectordialog import Ui_MediaClipSelector from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.media.vlcplayer import get_vlc @@ -683,7 +684,6 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro elif is_macosx(): # Look for DVD folders in devices to find optical devices volumes = os.listdir('/Volumes') - candidates = list() for volume in volumes: if volume.startswith('.'): continue diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 154d033c1..ab0cf4968 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -25,15 +25,16 @@ import os from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, UiStrings,\ - translate -from openlp.core.common.path import Path, path_to_str, str_to_path +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, translate, get_locale_key +from openlp.core.common.path import Path, path_to_str, create_paths +from openlp.core.common.registry import Registry, RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, ServiceItem, ServiceItemContext, \ build_icon, check_item_selected from openlp.core.lib.ui import create_widget_action, critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.ui import DisplayControllerType from openlp.core.ui.media import get_media_players, set_media_players, parse_optical_path, format_milliseconds -from openlp.core.common.languagemanager import get_locale_key from openlp.core.ui.media.vlcplayer import get_vlc if get_vlc() is not None: @@ -302,7 +303,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): """ self.list_view.clear() self.service_path = os.path.join(str(AppLocation.get_section_data_path(self.settings_section)), 'thumbnails') - check_directory_exists(Path(self.service_path)) + create_paths(Path(self.service_path)) self.load_list([path_to_str(file) for file in Settings().value(self.settings_section + '/media files')]) self.rebuild_players() diff --git a/openlp/plugins/media/lib/mediatab.py b/openlp/plugins/media/lib/mediatab.py index a4f2652f6..84c339a27 100644 --- a/openlp/plugins/media/lib/mediatab.py +++ b/openlp/plugins/media/lib/mediatab.py @@ -22,7 +22,8 @@ from PyQt5 import QtWidgets -from openlp.core.common import Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index f73cae56d..5efe8c910 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -22,14 +22,16 @@ """ The Media plugin """ - import logging import os import re + from PyQt5 import QtCore from openlp.core.api.http import register_endpoint -from openlp.core.common import AppLocation, translate, check_binary_exists +from openlp.core.common import check_binary_exists +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import translate from openlp.core.common.path import Path from openlp.core.lib import Plugin, StringContent, build_icon from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 0e57f9577..cb1d7ec0e 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -36,8 +36,9 @@ import time from PyQt5 import QtCore -from openlp.core.common import Registry, delete_file, get_uno_command, get_uno_instance, is_win -from openlp.core.lib import ScreenList +from openlp.core.common import delete_file, get_uno_command, get_uno_instance, is_win +from openlp.core.common.registry import Registry +from openlp.core.display.screens import ScreenList from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \ TextType diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 8061bb193..b801597b1 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -23,9 +23,10 @@ import logging from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import Registry, Settings, UiStrings, translate -from openlp.core.common.languagemanager import get_locale_key +from openlp.core.common.i18n import UiStrings, translate, get_locale_key from openlp.core.common.path import Path, path_to_str, str_to_path +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext,\ build_icon, check_item_selected, create_thumb, validate_thumb from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index ef4a0bdc0..53674f84b 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -24,7 +24,8 @@ import logging from PyQt5 import QtCore -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.common.path import Path from openlp.core.lib import ServiceItemContext from openlp.core.ui import HideMode diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 81fa3994a..26eb87d85 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -25,10 +25,11 @@ import logging import re from subprocess import check_output, CalledProcessError -from openlp.core.common import AppLocation, check_binary_exists -from openlp.core.common import Settings, is_win +from openlp.core.common import check_binary_exists, is_win +from openlp.core.common.applocation import AppLocation from openlp.core.common.path import which -from openlp.core.lib import ScreenList +from openlp.core.common.settings import Settings +from openlp.core.display.screens import ScreenList from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument if is_win(): @@ -131,7 +132,6 @@ class PdfController(PresentationController): elif (application_path / 'mutool.exe').is_file(): self.mutoolbin = application_path / 'mutool.exe' else: - DEVNULL = open(os.devnull, 'wb') # First try to find mudraw self.mudrawbin = which('mudraw') # if mudraw isn't installed, try mutool diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 7cbce786c..b01c90700 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -25,11 +25,15 @@ This module is for controlling powerpoint. PPT API documentation: 2010: https://msdn.microsoft.com/en-us/library/office/ff743835%28v=office.14%29.aspx 2013: https://msdn.microsoft.com/en-us/library/office/ff743835.aspx """ -import os import logging -import time -from openlp.core.common import is_win, Settings +from openlp.core.common import is_win, trace_error_handler +from openlp.core.common.i18n import UiStrings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings +from openlp.core.display.screens import ScreenList +from openlp.core.lib.ui import critical_error_message_box, translate +from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument if is_win(): from win32com.client import Dispatch @@ -39,12 +43,6 @@ if is_win(): import winreg import pywintypes - -from openlp.core.common import Registry, UiStrings, trace_error_handler -from openlp.core.lib import ScreenList -from openlp.core.lib.ui import critical_error_message_box, translate -from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument - log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 1bd30ca97..daa73f48f 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -19,25 +19,20 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - -import os import logging import zipfile import re from xml.etree import ElementTree - from openlp.core.common import is_win +from openlp.core.common.applocation import AppLocation +from openlp.core.display.screens import ScreenList +from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument if is_win(): from ctypes import cdll from ctypes.wintypes import RECT -from openlp.core.common import AppLocation -from openlp.core.lib import ScreenList -from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument - - log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 0cdd29f19..b19d91837 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -23,8 +23,11 @@ import logging from PyQt5 import QtCore -from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, md5_hash -from openlp.core.common.path import Path, rmtree +from openlp.core.common import md5_hash +from openlp.core.common.applocation import AppLocation +from openlp.core.common.path import Path, create_paths, rmtree +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import create_thumb, validate_thumb log = logging.getLogger(__name__) @@ -103,7 +106,7 @@ class PresentationDocument(object): """ self.slide_number = 0 self.file_path = document_path - check_directory_exists(self.get_thumbnail_folder()) + create_paths(self.get_thumbnail_folder()) def load_presentation(self): """ @@ -428,8 +431,7 @@ class PresentationController(object): self.temp_folder = AppLocation.get_section_data_path(self.settings_section) / name self.thumbnail_folder = AppLocation.get_section_data_path(self.settings_section) / 'thumbnails' self.thumbnail_prefix = 'slide' - check_directory_exists(self.thumbnail_folder) - check_directory_exists(self.temp_folder) + create_paths(self.thumbnail_folder, self.temp_folder) def enabled(self): """ diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index ca9ceacbc..96fcc573a 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -22,8 +22,8 @@ from PyQt5 import QtWidgets -from openlp.core.common import Settings, UiStrings, translate -from openlp.core.common.path import path_to_str, str_to_path +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.lib import PathEdit diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index dc9065d81..7f3333049 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -29,7 +29,8 @@ import logging from PyQt5 import QtCore from openlp.core.api.http import register_endpoint -from openlp.core.common import extension_loader, translate +from openlp.core.common import extension_loader +from openlp.core.common.i18n import translate from openlp.core.lib import Plugin, StringContent, build_icon from openlp.plugins.presentations.endpoint import api_presentations_endpoint, presentations_endpoint from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index 45b94daec..d4f560e9b 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -22,7 +22,8 @@ from PyQt5 import QtWidgets -from openlp.core.lib import translate, build_icon +from openlp.core.common.i18n import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/songs/forms/authorsform.py b/openlp/plugins/songs/forms/authorsform.py index 84e0343e9..75ae61035 100644 --- a/openlp/plugins/songs/forms/authorsform.py +++ b/openlp/plugins/songs/forms/authorsform.py @@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.songs.forms.authorsdialog import Ui_AuthorsDialog diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index b89748402..d2238d4b7 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -25,14 +25,14 @@ The duplicate song removal logic for OpenLP. import logging import multiprocessing -import os from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, RegistryProperties, translate +from openlp.core.common.i18n import translate +from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings from openlp.plugins.songs.lib import delete_song -from openlp.plugins.songs.lib.db import Song, MediaFile +from openlp.plugins.songs.lib.db import Song from openlp.plugins.songs.forms.songreviewwidget import SongReviewWidget from openlp.plugins.songs.lib.songcompare import songs_probably_equal diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 46f9b899c..9cf133f06 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box, create_button from openlp.core.ui import SingleColumnTableWidget diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index db7053b91..bdb1a9353 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -24,14 +24,14 @@ The :mod:`~openlp.plugins.songs.forms.editsongform` module contains the form used to edit songs. """ import logging -import os import re from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStrings, check_directory_exists, translate -from openlp.core.common.languagemanager import get_natural_key -from openlp.core.common.path import copyfile +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, translate, get_natural_key +from openlp.core.common.path import create_paths, copyfile +from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.lib import PluginStatus, MediaType, create_separated_list from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box from openlp.core.ui.lib.filedialog import FileDialog @@ -1066,7 +1066,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): audio_paths = [a.file_path for a in self.song.media_files] log.debug(audio_paths) save_path = AppLocation.get_section_data_path(self.media_item.plugin.name) / 'audio' / str(self.song.id) - check_directory_exists(save_path) + create_paths(save_path) self.song.media_files = [] file_paths = [] for row in range(self.audio_list_widget.count()): diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py index 4826b1a5d..63ca0cf27 100644 --- a/openlp/plugins/songs/forms/editversedialog.py +++ b/openlp/plugins/songs/forms/editversedialog.py @@ -22,8 +22,9 @@ from PyQt5 import QtWidgets -from openlp.core.common import Settings, UiStrings -from openlp.core.lib import build_icon, translate +from openlp.core.common.settings import Settings +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box from openlp.core.ui.lib import SpellTextEdit from openlp.plugins.songs.lib import VerseType @@ -42,10 +43,14 @@ class Ui_EditVerseDialog(object): self.dialog_layout.addWidget(self.verse_text_edit) self.verse_type_layout = QtWidgets.QHBoxLayout() self.verse_type_layout.setObjectName('verse_type_layout') - self.split_button = QtWidgets.QPushButton(edit_verse_dialog) - self.split_button.setIcon(build_icon(':/general/general_add.png')) - self.split_button.setObjectName('split_button') - self.verse_type_layout.addWidget(self.split_button) + self.forced_split_button = QtWidgets.QPushButton(edit_verse_dialog) + self.forced_split_button.setIcon(build_icon(':/general/general_add.png')) + self.forced_split_button.setObjectName('forced_split_button') + self.verse_type_layout.addWidget(self.forced_split_button) + self.overflow_split_button = QtWidgets.QPushButton(edit_verse_dialog) + self.overflow_split_button.setIcon(build_icon(':/general/general_add.png')) + self.overflow_split_button.setObjectName('overflow_split_button') + self.verse_type_layout.addWidget(self.overflow_split_button) self.verse_type_label = QtWidgets.QLabel(edit_verse_dialog) self.verse_type_label.setObjectName('verse_type_label') self.verse_type_layout.addWidget(self.verse_type_label) @@ -93,8 +98,11 @@ class Ui_EditVerseDialog(object): self.verse_type_combo_box.setItemText(VerseType.Intro, VerseType.translated_names[VerseType.Intro]) self.verse_type_combo_box.setItemText(VerseType.Ending, VerseType.translated_names[VerseType.Ending]) self.verse_type_combo_box.setItemText(VerseType.Other, VerseType.translated_names[VerseType.Other]) - self.split_button.setText(UiStrings().Split) - self.split_button.setToolTip(UiStrings().SplitToolTip) + self.overflow_split_button.setText(UiStrings().Split) + self.overflow_split_button.setToolTip(UiStrings().SplitToolTip) + self.forced_split_button.setText(translate('SongsPlugin.EditVerseForm', '&Forced Split')) + self.forced_split_button.setToolTip(translate('SongsPlugin.EditVerseForm', 'Split the verse when displayed ' + 'regardless of the screen size.')) self.insert_button.setText(translate('SongsPlugin.EditVerseForm', '&Insert')) self.insert_button.setToolTip(translate('SongsPlugin.EditVerseForm', 'Split a slide into two by inserting a verse splitter.')) diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index 1e36df1e4..09ba1e300 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -25,10 +25,11 @@ import logging from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.plugins.songs.lib import VerseType, transpose_lyrics from openlp.core.lib.ui import critical_error_message_box -from openlp.core.common import translate, Settings -from .editversedialog import Ui_EditVerseDialog +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings +from openlp.plugins.songs.forms.editversedialog import Ui_EditVerseDialog +from openlp.plugins.songs.lib import VerseType, transpose_lyrics log = logging.getLogger(__name__) @@ -48,12 +49,13 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): self.setupUi(self) self.has_single_verse = False self.insert_button.clicked.connect(self.on_insert_button_clicked) - self.split_button.clicked.connect(self.on_split_button_clicked) + self.overflow_split_button.clicked.connect(self.on_overflow_split_button_clicked) self.verse_text_edit.cursorPositionChanged.connect(self.on_cursor_position_changed) self.verse_type_combo_box.currentIndexChanged.connect(self.on_verse_type_combo_box_changed) + self.forced_split_button.clicked.connect(self.on_forced_split_button_clicked) if Settings().value('songs/enable chords'): - self.transpose_down_button.clicked.connect(self.on_transepose_down_button_clicked) - self.transpose_up_button.clicked.connect(self.on_transepose_up_button_clicked) + self.transpose_down_button.clicked.connect(self.on_transpose_down_button_clicked) + self.transpose_up_button.clicked.connect(self.on_transpose_up_button_clicked) def insert_verse(self, verse_tag, verse_num=1): """ @@ -68,13 +70,27 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): self.verse_text_edit.insertPlainText('---[{tag}:{number}]---\n'.format(tag=verse_tag, number=verse_num)) self.verse_text_edit.setFocus() - def on_split_button_clicked(self): + def on_overflow_split_button_clicked(self): """ - The split button has been pressed + The optional split button has been pressed so we need add the split + """ + self._add_splitter_to_text('[---]') + + def on_forced_split_button_clicked(self): + """ + The force split button has been pressed so we need add the split + """ + self._add_splitter_to_text('[--}{--]') + + def _add_splitter_to_text(self, insert_string): + """ + Add a custom splitter to the song text + + :param insert_string: The string to insert + :return: """ text = self.verse_text_edit.toPlainText() position = self.verse_text_edit.textCursor().position() - insert_string = '[---]' if position and text[position - 1] != '\n': insert_string = '\n' + insert_string if position == len(text) or text[position] != '\n': @@ -101,7 +117,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): """ self.update_suggested_verse_number() - def on_transepose_up_button_clicked(self): + def on_transpose_up_button_clicked(self): """ The transpose up button clicked """ @@ -118,7 +134,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): self.verse_text_edit.setFocus() self.verse_text_edit.moveCursor(QtGui.QTextCursor.End) - def on_transepose_down_button_clicked(self): + def on_transpose_down_button_clicked(self): """ The transpose down button clicked """ @@ -217,7 +233,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): """ if Settings().value('songs/enable chords'): try: - transposed_lyrics = transpose_lyrics(self.verse_text_edit.toPlainText(), 1) + transpose_lyrics(self.verse_text_edit.toPlainText(), 1) super(EditVerseForm, self).accept() except ValueError as ve: # Transposing failed diff --git a/openlp/plugins/songs/forms/mediafilesdialog.py b/openlp/plugins/songs/forms/mediafilesdialog.py index 4f9ec4f27..41a6862d3 100644 --- a/openlp/plugins/songs/forms/mediafilesdialog.py +++ b/openlp/plugins/songs/forms/mediafilesdialog.py @@ -22,7 +22,8 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.lib import translate, build_icon +from openlp.core.common.i18n import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py index 1242803b7..4a248cb48 100644 --- a/openlp/plugins/songs/forms/songbookdialog.py +++ b/openlp/plugins/songs/forms/songbookdialog.py @@ -22,7 +22,8 @@ from PyQt5 import QtWidgets -from openlp.core.lib import translate, build_icon +from openlp.core.common.i18n import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/songs/forms/songbookform.py b/openlp/plugins/songs/forms/songbookform.py index fc1b87c09..594768aaf 100644 --- a/openlp/plugins/songs/forms/songbookform.py +++ b/openlp/plugins/songs/forms/songbookform.py @@ -25,7 +25,7 @@ This module contains the song book form from PyQt5 import QtCore, QtWidgets -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.songs.forms.songbookdialog import Ui_SongBookDialog diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 582d63a84..28ca8054c 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -27,7 +27,9 @@ import logging from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import create_separated_list from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.lib import PathEdit, PathType diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 5a88ad4cb..acfa4b5b8 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -26,7 +26,9 @@ import logging from PyQt5 import QtCore, QtWidgets -from openlp.core.common import RegistryProperties, Settings, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.settings import Settings from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.lib import PathEdit, PathType from openlp.core.ui.lib.filedialog import FileDialog @@ -191,7 +193,6 @@ class SongImportForm(OpenLPWizard, RegistryProperties): Re-implement the validateCurrentPage() method. Validate the current page before moving on to the next page. Provide each song format class with a chance to validate its input by overriding is_valid_source(). """ - completeChanged = QtCore.pyqtSignal() if self.currentPage() == self.welcome_page: return True elif self.currentPage() == self.source_page: diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index be226f55e..728861f26 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import UiStrings +from openlp.core.common.i18n import UiStrings from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 766231712..4dc485e24 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -24,9 +24,9 @@ import logging from PyQt5 import QtCore, QtWidgets from sqlalchemy.sql import and_ -from openlp.core.common import Registry, RegistryProperties, UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate, get_natural_key +from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.lib.ui import critical_error_message_box -from openlp.core.common.languagemanager import get_natural_key from openlp.plugins.songs.forms.authorsform import AuthorsForm from openlp.plugins.songs.forms.topicsform import TopicsForm from openlp.plugins.songs.forms.songbookform import SongBookForm diff --git a/openlp/plugins/songs/forms/songselectdialog.py b/openlp/plugins/songs/forms/songselectdialog.py index 512e7b26d..b784071bd 100644 --- a/openlp/plugins/songs/forms/songselectdialog.py +++ b/openlp/plugins/songs/forms/songselectdialog.py @@ -25,9 +25,10 @@ The :mod:`~openlp.plugins.songs.forms.songselectdialog` module contains the user from PyQt5 import QtCore, QtWidgets -from openlp.core.ui.lib.historycombobox import HistoryComboBox -from openlp.core.lib import translate, build_icon +from openlp.core.common.i18n import translate +from openlp.core.lib import build_icon from openlp.core.ui import SingleColumnTableWidget +from openlp.core.ui.lib.historycombobox import HistoryComboBox class Ui_SongSelectDialog(object): diff --git a/openlp/plugins/songs/forms/songselectform.py b/openlp/plugins/songs/forms/songselectform.py index 4863529a9..5d175bb5e 100644 --- a/openlp/plugins/songs/forms/songselectform.py +++ b/openlp/plugins/songs/forms/songselectform.py @@ -22,16 +22,15 @@ """ The :mod:`~openlp.plugins.songs.forms.songselectform` module contains the GUI for the SongSelect importer """ - import logging -import os from time import sleep from PyQt5 import QtCore, QtWidgets -from openlp.core import Settings -from openlp.core.common import Registry, is_win -from openlp.core.lib import translate +from openlp.core.common import is_win +from openlp.core.common.i18n import translate +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.plugins.songs.forms.songselectdialog import Ui_SongSelectDialog from openlp.plugins.songs.lib.songselect import SongSelectImport diff --git a/openlp/plugins/songs/forms/topicsdialog.py b/openlp/plugins/songs/forms/topicsdialog.py index 4cc32d245..fe5020941 100644 --- a/openlp/plugins/songs/forms/topicsdialog.py +++ b/openlp/plugins/songs/forms/topicsdialog.py @@ -22,7 +22,8 @@ from PyQt5 import QtWidgets -from openlp.core.lib import translate, build_icon +from openlp.core.common.i18n import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/songs/forms/topicsform.py b/openlp/plugins/songs/forms/topicsform.py index f0c7f0ab1..2956c7e26 100644 --- a/openlp/plugins/songs/forms/topicsform.py +++ b/openlp/plugins/songs/forms/topicsform.py @@ -25,7 +25,7 @@ This module contains the topic edit form. from PyQt5 import QtCore, QtWidgets -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.songs.forms.topicsdialog import Ui_TopicsDialog diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 68effbe9b..f88aa8678 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -29,8 +29,11 @@ import re from PyQt5 import QtWidgets -from openlp.core.common import AppLocation, CONTROL_CHARS, Settings -from openlp.core.lib import translate, clean_tags +from openlp.core.common import CONTROL_CHARS +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings +from openlp.core.lib import clean_tags from openlp.plugins.songs.lib.db import Author, MediaFile, Song, Topic from openlp.plugins.songs.lib.ui import SongStrings @@ -546,12 +549,12 @@ def delete_song(song_id, song_plugin): song_plugin.manager.delete_object(Song, song_id) -def transpose_lyrics(lyrics, transepose_value): +def transpose_lyrics(lyrics, transpose_value): """ - Transepose lyrics + Transpose lyrics - :param lyrcs: The lyrics to be transposed - :param transepose_value: The value to transpose the lyrics with + :param lyrics: The lyrics to be transposed + :param transpose_value: The value to transpose the lyrics with :return: The transposed lyrics """ # Split text by verse delimiter - both normal and optional @@ -562,16 +565,17 @@ def transpose_lyrics(lyrics, transepose_value): if verse.startswith('---[') or verse == '[---]': transposed_lyrics += verse else: - transposed_lyrics += transpose_verse(verse, transepose_value, notation) + transposed_lyrics += transpose_verse(verse, transpose_value, notation) return transposed_lyrics -def transpose_verse(verse_text, transepose_value, notation): +def transpose_verse(verse_text, transpose_value, notation): """ - Transepose lyrics + Transpose Verse - :param lyrcs: The lyrics to be transposed - :param transepose_value: The value to transpose the lyrics with + :param verse_text: The lyrics to be transposed + :param transpose_value: The value to transpose the lyrics with + :param notation: which notation to use :return: The transposed lyrics """ if '[' not in verse_text: @@ -589,11 +593,11 @@ def transpose_verse(verse_text, transepose_value, notation): if word == ']': in_tag = False transposed_lyrics += word - elif word == '/': + elif word == '/' or word == '--}{--': transposed_lyrics += word else: # This MUST be a chord - transposed_lyrics += transpose_chord(word, transepose_value, notation) + transposed_lyrics += transpose_chord(word, transpose_value, notation) # If still inside a chord tag something is wrong! if in_tag: return verse_text @@ -629,36 +633,36 @@ def transpose_chord(chord, transpose_value, notation): for i in range(0, len(chord_split)): if i > 0: transposed_chord += '/' - currentchord = chord_split[i] - if currentchord and currentchord[0] == '(': + current_chord = chord_split[i] + if current_chord and current_chord[0] == '(': transposed_chord += '(' - if len(currentchord) > 1: - currentchord = currentchord[1:] + if len(current_chord) > 1: + current_chord = current_chord[1:] else: - currentchord = '' - if len(currentchord) > 0: - if len(currentchord) > 1: - if '#b'.find(currentchord[1]) == -1: - note = currentchord[0:1] - rest = currentchord[1:] + current_chord = '' + if len(current_chord) > 0: + if len(current_chord) > 1: + if '#b'.find(current_chord[1]) == -1: + note = current_chord[0:1] + rest = current_chord[1:] else: - note = currentchord[0:2] - rest = currentchord[2:] + note = current_chord[0:2] + rest = current_chord[2:] else: - note = currentchord + note = current_chord rest = '' - notenumber = notes_flat.index(note) if note not in notes_sharp else notes_sharp.index(note) - notenumber += transpose_value - while notenumber > 11: - notenumber -= 12 - while notenumber < 0: - notenumber += 12 + note_number = notes_flat.index(note) if note not in notes_sharp else notes_sharp.index(note) + note_number += transpose_value + while note_number > 11: + note_number -= 12 + while note_number < 0: + note_number += 12 if i == 0: - current_chord = notes_sharp[notenumber] if notes_preferred[notenumber] == '#' else notes_flat[ - notenumber] + current_chord = notes_sharp[note_number] if notes_preferred[note_number] == '#' else notes_flat[ + note_number] last_chord = current_chord else: - current_chord = notes_flat[notenumber] if last_chord not in notes_sharp else notes_sharp[notenumber] + current_chord = notes_flat[note_number] if last_chord not in notes_sharp else notes_sharp[note_number] if not (note not in notes_flat and note not in notes_sharp): transposed_chord += current_chord + rest else: diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 5910bd1a8..0963fd084 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -23,14 +23,11 @@ The :mod:`db` module provides the database and schema that is the backend for the Songs plugin """ -from contextlib import suppress from sqlalchemy import Column, ForeignKey, Table, types from sqlalchemy.orm import mapper, relation, reconstructor from sqlalchemy.sql.expression import func, text -from openlp.core.common.applocation import AppLocation -from openlp.core.common.languagemanager import get_natural_key -from openlp.core.lib import translate +from openlp.core.common.i18n import translate, get_natural_key from openlp.core.lib.db import BaseModel, PathType, init_db diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 9f925b35c..3b8081999 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -22,10 +22,10 @@ """ The :mod:`importer` modules provides the general song import functionality. """ -import os import logging -from openlp.core.common import translate, UiStrings, is_win +from openlp.core.common import is_win +from openlp.core.common.i18n import UiStrings, translate from openlp.core.ui.lib.wizard import WizardStrings from .importers.opensong import OpenSongImport from .importers.easyslides import EasySlidesImport diff --git a/openlp/plugins/songs/lib/importers/cclifile.py b/openlp/plugins/songs/lib/importers/cclifile.py index 223263183..10a8b09e1 100644 --- a/openlp/plugins/songs/lib/importers/cclifile.py +++ b/openlp/plugins/songs/lib/importers/cclifile.py @@ -23,7 +23,7 @@ import chardet import codecs import logging -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib import VerseType from .songimport import SongImport diff --git a/openlp/plugins/songs/lib/importers/chordpro.py b/openlp/plugins/songs/lib/importers/chordpro.py index 2250e65f1..0d4c4d4f2 100644 --- a/openlp/plugins/songs/lib/importers/chordpro.py +++ b/openlp/plugins/songs/lib/importers/chordpro.py @@ -23,27 +23,25 @@ The :mod:`chordpro` module provides the functionality for importing ChordPro files into the current database. """ - import logging import re -from openlp.core.common import Settings - -from .songimport import SongImport - +from openlp.core.common.settings import Settings +from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) class ChordProImport(SongImport): """ - The :class:`ChordProImport` class provides OpenLP with the - ability to import ChordPro files. + The :class:`ChordProImport` class provides OpenLP with the ability to import ChordPro files. + This importer is based on the information available on these webpages: - http://webchord.sourceforge.net/tech.html - http://www.vromans.org/johan/projects/Chordii/chordpro/ - http://www.tenbyten.com/software/songsgen/help/HtmlHelp/files_reference.htm - http://linkesoft.com/songbook/chordproformat.html + + - http://webchord.sourceforge.net/tech.html + - http://www.vromans.org/johan/projects/Chordii/chordpro/ + - http://www.tenbyten.com/software/songsgen/help/HtmlHelp/files_reference.htm + - http://linkesoft.com/songbook/chordproformat.html """ def do_import(self): self.import_wizard.progress_bar.setMaximum(len(self.import_source)) diff --git a/openlp/plugins/songs/lib/importers/dreambeam.py b/openlp/plugins/songs/lib/importers/dreambeam.py index 094827792..eb2ec1ded 100644 --- a/openlp/plugins/songs/lib/importers/dreambeam.py +++ b/openlp/plugins/songs/lib/importers/dreambeam.py @@ -26,7 +26,7 @@ import logging from lxml import etree, objectify -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/lib/importers/easyworship.py b/openlp/plugins/songs/lib/importers/easyworship.py index ac22029a3..c82a7e6ad 100644 --- a/openlp/plugins/songs/lib/importers/easyworship.py +++ b/openlp/plugins/songs/lib/importers/easyworship.py @@ -30,8 +30,8 @@ import zlib import sqlite3 +from openlp.core.common.i18n import translate from openlp.core.common.path import Path -from openlp.core.lib import translate from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import retrieve_windows_encoding, strip_rtf from .songimport import SongImport diff --git a/openlp/plugins/songs/lib/importers/foilpresenter.py b/openlp/plugins/songs/lib/importers/foilpresenter.py index 98ec9d6ac..860177172 100644 --- a/openlp/plugins/songs/lib/importers/foilpresenter.py +++ b/openlp/plugins/songs/lib/importers/foilpresenter.py @@ -87,7 +87,7 @@ import re from lxml import etree, objectify -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from openlp.core.ui.lib.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song, VerseType from openlp.plugins.songs.lib.importers.songimport import SongImport diff --git a/openlp/plugins/songs/lib/importers/lyrix.py b/openlp/plugins/songs/lib/importers/lyrix.py index 87df138a0..c8dbac24b 100644 --- a/openlp/plugins/songs/lib/importers/lyrix.py +++ b/openlp/plugins/songs/lib/importers/lyrix.py @@ -26,7 +26,7 @@ exproted from Lyrix.""" import logging import re -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/importers/mediashout.py b/openlp/plugins/songs/lib/importers/mediashout.py index c3d42681b..67cf0d0fb 100644 --- a/openlp/plugins/songs/lib/importers/mediashout.py +++ b/openlp/plugins/songs/lib/importers/mediashout.py @@ -30,7 +30,7 @@ a MediaShout database into the OpenLP database. import pyodbc import logging -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib.importers.songimport import SongImport VERSE_TAGS = ['V', 'C', 'B', 'O', 'P', 'I', 'E'] diff --git a/openlp/plugins/songs/lib/importers/openlp.py b/openlp/plugins/songs/lib/importers/openlp.py index 2e348a867..252b8fd8b 100644 --- a/openlp/plugins/songs/lib/importers/openlp.py +++ b/openlp/plugins/songs/lib/importers/openlp.py @@ -29,7 +29,7 @@ from sqlalchemy import create_engine, MetaData, Table from sqlalchemy.orm import class_mapper, mapper, relation, scoped_session, sessionmaker from sqlalchemy.orm.exc import UnmappedClassError -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib.db import BaseModel from openlp.core.ui.lib.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song diff --git a/openlp/plugins/songs/lib/importers/openoffice.py b/openlp/plugins/songs/lib/importers/openoffice.py index f03f7b4a4..a097d8b85 100644 --- a/openlp/plugins/songs/lib/importers/openoffice.py +++ b/openlp/plugins/songs/lib/importers/openoffice.py @@ -25,7 +25,7 @@ import time from PyQt5 import QtCore from openlp.core.common import is_win, get_uno_command, get_uno_instance -from openlp.core.lib import translate +from openlp.core.common.i18n import translate from .songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/importers/opensong.py b/openlp/plugins/songs/lib/importers/opensong.py index 3f6e76685..e6924e7b2 100644 --- a/openlp/plugins/songs/lib/importers/opensong.py +++ b/openlp/plugins/songs/lib/importers/opensong.py @@ -25,7 +25,8 @@ import re from lxml import objectify from lxml.etree import Error, LxmlError -from openlp.core.common import translate, Settings +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/lib/importers/opspro.py b/openlp/plugins/songs/lib/importers/opspro.py index 3ede706f4..f7dba83b8 100644 --- a/openlp/plugins/songs/lib/importers/opspro.py +++ b/openlp/plugins/songs/lib/importers/opspro.py @@ -32,7 +32,7 @@ import re import pyodbc import struct -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/importers/powersong.py b/openlp/plugins/songs/lib/importers/powersong.py index fd51ec7e6..7fac4ef75 100644 --- a/openlp/plugins/songs/lib/importers/powersong.py +++ b/openlp/plugins/songs/lib/importers/powersong.py @@ -27,7 +27,7 @@ import logging import fnmatch import os -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/importers/presentationmanager.py b/openlp/plugins/songs/lib/importers/presentationmanager.py index 2ea23679b..e7fec2a6c 100644 --- a/openlp/plugins/songs/lib/importers/presentationmanager.py +++ b/openlp/plugins/songs/lib/importers/presentationmanager.py @@ -27,7 +27,8 @@ import re from lxml import objectify, etree -from openlp.core.common import get_file_encoding, translate +from openlp.core.common.i18n import translate +from openlp.core.common import get_file_encoding from openlp.core.ui.lib.wizard import WizardStrings from .songimport import SongImport diff --git a/openlp/plugins/songs/lib/importers/songbeamer.py b/openlp/plugins/songs/lib/importers/songbeamer.py index 346b1588d..adc364325 100644 --- a/openlp/plugins/songs/lib/importers/songbeamer.py +++ b/openlp/plugins/songs/lib/importers/songbeamer.py @@ -28,7 +28,8 @@ import re import base64 import math -from openlp.core.common import Settings, is_win, is_macosx, get_file_encoding +from openlp.core.common import is_win, is_macosx, get_file_encoding +from openlp.core.common.settings import Settings from openlp.core.common.path import Path from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.importers.songimport import SongImport @@ -313,7 +314,8 @@ class SongBeamerImport(SongImport): elif tag_val[0] == '#QuickFind': pass elif tag_val[0] == '#Rights': - song_book_pub = tag_val[1] + # song_book_pub = tag_val[1] + pass elif tag_val[0] == '#Songbook' or tag_val[0] == '#SongBook': book_data = tag_val[1].split('/') self.song_book_name = book_data[0].strip() diff --git a/openlp/plugins/songs/lib/importers/songimport.py b/openlp/plugins/songs/lib/importers/songimport.py index a74aaf9e7..e0cc5220e 100644 --- a/openlp/plugins/songs/lib/importers/songimport.py +++ b/openlp/plugins/songs/lib/importers/songimport.py @@ -25,8 +25,10 @@ import re from PyQt5 import QtCore -from openlp.core.common import Registry, AppLocation, check_directory_exists, translate -from openlp.core.common.path import copyfile +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import translate +from openlp.core.common.path import copyfile, create_paths +from openlp.core.common.registry import Registry from openlp.core.ui.lib.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song, VerseType from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile @@ -423,7 +425,7 @@ class SongImport(QtCore.QObject): """ if not hasattr(self, 'save_path'): self.save_path = AppLocation.get_section_data_path(self.import_wizard.plugin.name) / 'audio' / str(song_id) - check_directory_exists(self.save_path) + create_paths(self.save_path) if self.save_path not in file_path.parents: old_path, file_path = file_path, self.save_path / file_path.name copyfile(old_path, file_path) diff --git a/openlp/plugins/songs/lib/importers/videopsalm.py b/openlp/plugins/songs/lib/importers/videopsalm.py index ef020997a..180dbd333 100644 --- a/openlp/plugins/songs/lib/importers/videopsalm.py +++ b/openlp/plugins/songs/lib/importers/videopsalm.py @@ -21,12 +21,13 @@ ############################################################################### """ The :mod:`lyrix` module provides the functionality for importing songs which are -exproted from Lyrix.""" +exported from Lyrix.""" import json import logging import re -from openlp.core.common import translate, Settings +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings from openlp.core.common.path import Path from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.db import AuthorType diff --git a/openlp/plugins/songs/lib/importers/wordsofworship.py b/openlp/plugins/songs/lib/importers/wordsofworship.py index 62ad367bf..e1c561361 100644 --- a/openlp/plugins/songs/lib/importers/wordsofworship.py +++ b/openlp/plugins/songs/lib/importers/wordsofworship.py @@ -27,7 +27,7 @@ import os import logging from openlp.core.common.path import Path -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib.importers.songimport import SongImport BLOCK_TYPES = ('V', 'C', 'B') diff --git a/openlp/plugins/songs/lib/importers/worshipassistant.py b/openlp/plugins/songs/lib/importers/worshipassistant.py index b4cb87576..f9354427d 100644 --- a/openlp/plugins/songs/lib/importers/worshipassistant.py +++ b/openlp/plugins/songs/lib/importers/worshipassistant.py @@ -23,12 +23,12 @@ The :mod:`worshipassistant` module provides the functionality for importing Worship Assistant songs into the OpenLP database. """ -import chardet import csv import logging import re -from openlp.core.common import get_file_encoding, translate +from openlp.core.common import get_file_encoding +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.importers.songimport import SongImport diff --git a/openlp/plugins/songs/lib/importers/worshipcenterpro.py b/openlp/plugins/songs/lib/importers/worshipcenterpro.py index 426e8c153..6bba00682 100644 --- a/openlp/plugins/songs/lib/importers/worshipcenterpro.py +++ b/openlp/plugins/songs/lib/importers/worshipcenterpro.py @@ -27,7 +27,7 @@ import logging import re import pyodbc -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/importers/zionworx.py b/openlp/plugins/songs/lib/importers/zionworx.py index b4aec2c16..5cfc0576d 100644 --- a/openlp/plugins/songs/lib/importers/zionworx.py +++ b/openlp/plugins/songs/lib/importers/zionworx.py @@ -25,7 +25,7 @@ The :mod:`zionworx` module provides the functionality for importing ZionWorx son import csv import logging -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 6632387f5..c6ad9c927 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -19,16 +19,17 @@ # 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 os from PyQt5 import QtCore, QtWidgets from sqlalchemy.sql import and_, or_ -from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate -from openlp.core.common.languagemanager import get_natural_key -from openlp.core.common.path import copyfile +from openlp.core.common.applocation import AppLocation +from openlp.core.common.i18n import UiStrings, translate, get_natural_key +from openlp.core.common.path import copyfile, create_paths +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \ check_item_selected, create_separated_list from openlp.core.lib.ui import create_widget_action @@ -89,7 +90,7 @@ class SongMediaItem(MediaManagerItem): for i, bga in enumerate(item.background_audio): dest_path =\ AppLocation.get_section_data_path(self.plugin.name) / 'audio' / str(song.id) / os.path.split(bga)[1] - check_directory_exists(dest_path.parent) + create_paths(dest_path.parent) copyfile(AppLocation.get_section_data_path('servicemanager') / bga, dest_path) song.media_files.append(MediaFile.populate(weight=i, file_path=dest_path)) self.plugin.manager.save_object(song, True) @@ -534,7 +535,7 @@ class SongMediaItem(MediaManagerItem): # Copy audio files from the old to the new song if len(old_song.media_files) > 0: save_path = AppLocation.get_section_data_path(self.plugin.name) / 'audio' / str(new_song.id) - check_directory_exists(save_path) + create_paths(save_path) for media_file in old_song.media_files: new_media_file_path = save_path / media_file.file_path.name copyfile(media_file.file_path, new_media_file_path) @@ -577,7 +578,7 @@ class SongMediaItem(MediaManagerItem): 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. + # single char tags. verse_tag = verse[0]['type'] verse_index = None if len(verse_tag) > 1: @@ -588,7 +589,9 @@ class SongMediaItem(MediaManagerItem): verse_index = VerseType.from_tag(verse_tag) verse_tag = VerseType.translated_tags[verse_index].upper() verse_def = '{tag}{label}'.format(tag=verse_tag, label=verse[0]['label']) - service_item.add_from_text(str(verse[1]), verse_def) + force_verse = verse[1].split('[--}{--]\n') + for split_verse in force_verse: + service_item.add_from_text(split_verse, verse_def) else: # Loop through the verse list and expand the song accordingly. for order in song.verse_order.lower().split(): @@ -603,7 +606,9 @@ class SongMediaItem(MediaManagerItem): verse_index = VerseType.from_tag(verse[0]['type']) verse_tag = VerseType.translated_tags[verse_index] verse_def = '{tag}{label}'.format(tag=verse_tag, label=verse[0]['label']) - service_item.add_from_text(verse[1], verse_def) + force_verse = verse[1].split('[--}{--]\n') + for split_verse in force_verse: + service_item.add_from_text(split_verse, verse_def) service_item.title = song.title author_list = self.generate_footer(service_item, song) service_item.data_string = {'title': song.search_title, 'authors': ', '.join(author_list)} diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index 430e37da5..ca7fe0bc7 100644 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -27,7 +27,10 @@ import logging from lxml import etree -from openlp.core.common import RegistryProperties, check_directory_exists, translate, clean_filename +from openlp.core.common import clean_filename +from openlp.core.common.i18n import translate +from openlp.core.common.path import create_paths +from openlp.core.common.registry import RegistryProperties from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics log = logging.getLogger(__name__) @@ -49,7 +52,7 @@ class OpenLyricsExport(RegistryProperties): self.manager = parent.plugin.manager self.songs = songs self.save_path = save_path - check_directory_exists(self.save_path) + create_paths(self.save_path) def do_export(self): """ diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py index 4819e61de..74d91068c 100644 --- a/openlp/plugins/songs/lib/openlyricsxml.py +++ b/openlp/plugins/songs/lib/openlyricsxml.py @@ -61,7 +61,8 @@ import re from lxml import etree, objectify -from openlp.core.common import translate, Settings +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings from openlp.core.version import get_version from openlp.core.lib import FormattingTags from openlp.plugins.songs.lib import VerseType, clean_song @@ -71,6 +72,7 @@ log = logging.getLogger(__name__) NAMESPACE = 'http://openlyrics.info/namespace/2009/song' NSMAP = '{{' + NAMESPACE + '}}{tag}' +NEWPAGETAG = '

' class SongXML(object): @@ -281,7 +283,7 @@ class OpenLyrics(object): tags_element = None match = re.search('\{/?\w+\}', song.lyrics, re.UNICODE) if match: - # Named 'format_' - 'format' is built-in fuction in Python. + # Named 'format_' - 'format' is built-in function in Python. format_ = etree.SubElement(song_xml, 'format') tags_element = etree.SubElement(format_, 'tags') tags_element.set('application', 'OpenLP') @@ -472,6 +474,7 @@ class OpenLyrics(object): text = text.replace('{{/{tag}}}'.format(tag=tag), '') # Replace \n with
. text = text.replace('\n', '
') + text = text.replace('[--}{--]', NEWPAGETAG) element = etree.XML('{text}'.format(text=text)) verse_element.append(element) return element @@ -634,6 +637,9 @@ class OpenLyrics(object): if element.tail: text += element.tail return text + elif newlines and element.tag == NSMAP.format(tag='p') and 'page-break-after' in str(element.attrib): + text += '[--}{--]' + return text # Start formatting tag. if element.tag == NSMAP.format(tag='tag'): text += '{{{name}}}'.format(name=element.get('name')) diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index d1044b6c3..69f9bb1cd 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -22,7 +22,8 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Settings, translate +from openlp.core.common.i18n import translate +from openlp.core.common.settings import Settings from openlp.core.lib import SettingsTab from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index 977c94f68..e318577a0 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -23,7 +23,7 @@ The :mod:`openlp.plugins.songs.lib.ui` module provides standard UI components for the songs plugin. """ -from openlp.core.lib import translate +from openlp.core.common.i18n import translate class SongStrings(object): diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index bc7c95624..eca18876b 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -29,7 +29,7 @@ import logging from sqlalchemy import Table, Column, ForeignKey, types from sqlalchemy.sql.expression import func, false, null, text -from openlp.core.common import AppLocation +from openlp.core.common.applocation import AppLocation from openlp.core.common.db import drop_columns from openlp.core.common.json import OpenLPJsonEncoder from openlp.core.common.path import Path diff --git a/openlp/plugins/songs/reporting.py b/openlp/plugins/songs/reporting.py index 8f06906e7..01fb0af6c 100644 --- a/openlp/plugins/songs/reporting.py +++ b/openlp/plugins/songs/reporting.py @@ -25,8 +25,9 @@ The :mod:`db` module provides the ability to provide a csv file of all songs import csv import logging -from openlp.core.common import Registry, translate +from openlp.core.common.i18n import translate from openlp.core.common.path import Path +from openlp.core.common.registry import Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.lib.filedialog import FileDialog from openlp.plugins.songs.lib.db import Song diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 728fee5a9..b35cbe2c9 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -32,8 +32,9 @@ from tempfile import gettempdir from PyQt5 import QtCore, QtWidgets from openlp.core.api.http import register_endpoint -from openlp.core.common import UiStrings, Registry, translate from openlp.core.common.actions import ActionList +from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.registry import Registry from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action diff --git a/openlp/plugins/songusage/forms/songusagedeletedialog.py b/openlp/plugins/songusage/forms/songusagedeletedialog.py index 6a65e3650..fe199009b 100644 --- a/openlp/plugins/songusage/forms/songusagedeletedialog.py +++ b/openlp/plugins/songusage/forms/songusagedeletedialog.py @@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/songusage/forms/songusagedeleteform.py b/openlp/plugins/songusage/forms/songusagedeleteform.py index 37664b06a..4a619e64a 100644 --- a/openlp/plugins/songusage/forms/songusagedeleteform.py +++ b/openlp/plugins/songusage/forms/songusagedeleteform.py @@ -22,9 +22,10 @@ from PyQt5 import QtCore, QtWidgets -from openlp.core.common import RegistryProperties, translate +from openlp.core.common.i18n import translate +from openlp.core.common.registry import RegistryProperties from openlp.plugins.songusage.lib.db import SongUsageItem -from .songusagedeletedialog import Ui_SongUsageDeleteDialog +from openlp.plugins.songusage.forms.songusagedeletedialog import Ui_SongUsageDeleteDialog class SongUsageDeleteForm(QtWidgets.QDialog, Ui_SongUsageDeleteDialog, RegistryProperties): diff --git a/openlp/plugins/songusage/forms/songusagedetaildialog.py b/openlp/plugins/songusage/forms/songusagedetaildialog.py index 23ae92957..9d51b041a 100644 --- a/openlp/plugins/songusage/forms/songusagedetaildialog.py +++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py @@ -21,7 +21,7 @@ ############################################################################### from PyQt5 import QtCore, QtWidgets -from openlp.core.common import translate +from openlp.core.common.i18n import translate from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box from openlp.core.ui.lib import PathEdit, PathType diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index 930baf1d6..afc013bbd 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -20,13 +20,14 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### import logging -import os from PyQt5 import QtCore, QtWidgets from sqlalchemy.sql import and_ -from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate -from openlp.core.common.path import Path, path_to_str, str_to_path +from openlp.core.common.i18n import translate +from openlp.core.common.registry import RegistryProperties +from openlp.core.common.settings import Settings +from openlp.core.common.path import create_paths from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.songusage.lib.db import SongUsageItem from .songusagedetaildialog import Ui_SongUsageDetailDialog @@ -79,7 +80,7 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP ' song usage report. \nPlease select an existing path on your computer.') ) return - check_directory_exists(path) + create_paths(path) file_name = translate('SongUsagePlugin.SongUsageDetailForm', 'usage_detail_{old}_{new}.txt' ).format(old=self.from_date_calendar.selectedDate().toString('ddMMyyyy'), diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index d0c2f7fe7..a9acd791a 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -25,8 +25,10 @@ from datetime import datetime from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, Settings, translate from openlp.core.common.actions import ActionList +from openlp.core.common.i18n import translate +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action diff --git a/tests/functional/openlp_core_api/__init__.py b/tests/functional/openlp_core/api/__init__.py similarity index 100% rename from tests/functional/openlp_core_api/__init__.py rename to tests/functional/openlp_core/api/__init__.py diff --git a/tests/functional/openlp_core_api_http/__init__.py b/tests/functional/openlp_core/api/http/__init__.py similarity index 100% rename from tests/functional/openlp_core_api_http/__init__.py rename to tests/functional/openlp_core/api/http/__init__.py diff --git a/tests/functional/openlp_core_api_http/test_error.py b/tests/functional/openlp_core/api/http/test_error.py similarity index 100% rename from tests/functional/openlp_core_api_http/test_error.py rename to tests/functional/openlp_core/api/http/test_error.py diff --git a/tests/functional/openlp_core_api_http/test_http.py b/tests/functional/openlp_core/api/http/test_http.py similarity index 97% rename from tests/functional/openlp_core_api_http/test_http.py rename to tests/functional/openlp_core/api/http/test_http.py index f93edf49a..d9002b2ec 100644 --- a/tests/functional/openlp_core_api_http/test_http.py +++ b/tests/functional/openlp_core/api/http/test_http.py @@ -22,11 +22,10 @@ """ Functional tests to test the Http Server Class. """ - from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.api.http.server import HttpServer @@ -50,7 +49,7 @@ class TestHttpServer(TestCase): """ # GIVEN: A new httpserver # WHEN: I start the server - server = HttpServer() + HttpServer() # THEN: the api environment should have been created self.assertEquals(1, mock_qthread.call_count, 'The qthread should have been called once') diff --git a/tests/functional/openlp_core_api_http/test_wsgiapp.py b/tests/functional/openlp_core/api/http/test_wsgiapp.py similarity index 100% rename from tests/functional/openlp_core_api_http/test_wsgiapp.py rename to tests/functional/openlp_core/api/http/test_wsgiapp.py diff --git a/tests/functional/openlp_core_api/test_deploy.py b/tests/functional/openlp_core/api/test_deploy.py similarity index 98% rename from tests/functional/openlp_core_api/test_deploy.py rename to tests/functional/openlp_core/api/test_deploy.py index 273894b99..be36fb9c7 100644 --- a/tests/functional/openlp_core_api/test_deploy.py +++ b/tests/functional/openlp_core/api/test_deploy.py @@ -27,7 +27,7 @@ from unittest import TestCase from openlp.core.api.deploy import deploy_zipfile -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources')) class TestRemoteDeploy(TestCase): diff --git a/tests/functional/openlp_core_api/test_tab.py b/tests/functional/openlp_core/api/test_tab.py similarity index 77% rename from tests/functional/openlp_core_api/test_tab.py rename to tests/functional/openlp_core/api/test_tab.py index 23d999236..f3c4f31e6 100644 --- a/tests/functional/openlp_core_api/test_tab.py +++ b/tests/functional/openlp_core/api/test_tab.py @@ -29,8 +29,8 @@ from unittest.mock import patch from PyQt5 import QtWidgets - -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.api.tab import ApiTab from tests.helpers.testmixin import TestMixin @@ -95,30 +95,6 @@ class TestApiTab(TestCase, TestMixin): # THEN: the default ip address will be returned self.assertEqual(ip_address, given_ip, 'The return value should be %s' % given_ip) - def test_set_basic_urls(self): - """ - Test the set_urls function with standard defaults - """ - # GIVEN: A mocked location - with patch('openlp.core.common.Settings') as mocked_class, \ - patch('openlp.core.common.applocation.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.common.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.common.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/dir' - mocked_check_directory_exists.return_value = True - mocked_os.path.normpath.return_value = '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, 'The default URL should be set on the screen') - self.assertEqual(self.form.user_login_group_box.isChecked(), False, - 'The authentication box should not be enabled') - def test_set_urls(self): """ Test the set_url function to generate correct url links diff --git a/tests/functional/openlp_core_api/test_websockets.py b/tests/functional/openlp_core/api/test_websockets.py similarity index 98% rename from tests/functional/openlp_core_api/test_websockets.py rename to tests/functional/openlp_core/api/test_websockets.py index c66b73c64..99abcdbc0 100644 --- a/tests/functional/openlp_core_api/test_websockets.py +++ b/tests/functional/openlp_core/api/test_websockets.py @@ -22,11 +22,11 @@ """ Functional tests to test the Http Server Class. """ - from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.api.websockets import WebSocketServer from openlp.core.api.poll import Poller from tests.helpers.testmixin import TestMixin diff --git a/tests/functional/openlp_core_common/__init__.py b/tests/functional/openlp_core/common/__init__.py similarity index 100% rename from tests/functional/openlp_core_common/__init__.py rename to tests/functional/openlp_core/common/__init__.py diff --git a/tests/functional/openlp_core_common/test_actions.py b/tests/functional/openlp_core/common/test_actions.py similarity index 99% rename from tests/functional/openlp_core_common/test_actions.py rename to tests/functional/openlp_core/common/test_actions.py index 1b28d9216..bd59d6577 100644 --- a/tests/functional/openlp_core_common/test_actions.py +++ b/tests/functional/openlp_core/common/test_actions.py @@ -27,8 +27,8 @@ from unittest.mock import MagicMock from PyQt5 import QtGui, QtCore, QtWidgets -from openlp.core.common import Settings from openlp.core.common.actions import CategoryActionList, ActionList +from openlp.core.common.settings import Settings from tests.helpers.testmixin import TestMixin diff --git a/tests/functional/openlp_core/common/test_applocation.py b/tests/functional/openlp_core/common/test_applocation.py new file mode 100644 index 000000000..551287476 --- /dev/null +++ b/tests/functional/openlp_core/common/test_applocation.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 OpenLP Developers # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Functional tests to test the AppLocation class and related methods. +""" +import os +from unittest.mock import patch + +from openlp.core.common import get_frozen_path +from openlp.core.common.applocation import AppLocation +from openlp.core.common.path import Path + +FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3'] + + +@patch('openlp.core.common.applocation.Settings') +@patch('openlp.core.common.applocation.AppLocation.get_directory') +@patch('openlp.core.common.applocation.create_paths') +@patch('openlp.core.common.applocation.os') +def test_get_data_path(mocked_os, mocked_create_paths, mocked_get_directory, MockSettings): + """ + Test the AppLocation.get_data_path() method + """ + # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() + MockSettings.return_value.contains.return_value = False + mocked_get_directory.return_value = os.path.join('tests', 'dir') + mocked_create_paths.return_value = True + mocked_os.path.normpath.return_value = os.path.join('tests', '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 + MockSettings.return_value.contains.assert_called_with('advanced/data path') + mocked_get_directory.assert_called_with(AppLocation.DataDir) + mocked_create_paths.assert_called_with(os.path.join('tests', 'dir')) + assert data_path == os.path.join('tests', 'dir'), 'Result should be "tests/dir"' + + +@patch('openlp.core.common.applocation.Settings') +def test_get_data_path_with_custom_location(MockSettings): + """ + Test the AppLocation.get_data_path() method when a custom location is set in the settings + """ + # GIVEN: A mocked out Settings class which returns a custom data location + MockSettings.return_value.contains.return_value = True + MockSettings.return_value.value.return_value = Path('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 + MockSettings.return_value.contains.assert_called_with('advanced/data path') + MockSettings.return_value.value.assert_called_with('advanced/data path') + assert data_path == Path('custom', 'dir'), 'Result should be "custom/dir"' + + +@patch('openlp.core.common.applocation.Path.glob') +@patch('openlp.core.common.applocation.AppLocation.get_data_path') +def test_get_files_no_section_no_extension(mocked_get_data_path, mocked_glob): + """ + Test the AppLocation.get_files() method with no parameters passed. + """ + # GIVEN: Our mocked modules/methods. + mocked_glob.return_value = [Path('/dir/file5.mp3'), Path('/dir/file6.mp3')] + mocked_get_data_path.return_value = Path('/dir') + + # When: Get the list of files. + result = AppLocation.get_files() + + # Then: Check if the section parameter was used correctly, and the glob argument was passed. + mocked_glob.assert_called_once_with('*') + assert result == [Path('file5.mp3'), Path('file6.mp3')], 'The file lists should be identical.' + + +@patch('openlp.core.common.applocation.Path.glob') +@patch('openlp.core.common.applocation.AppLocation.get_data_path') +def test_get_files(mocked_get_data_path, mocked_glob): + """ + Test the AppLocation.get_files() method with all parameters passed. + """ + # GIVEN: Our mocked modules/methods. + mocked_glob.return_value = [Path('/dir/section/file5.mp3'), Path('/dir/section/file6.mp3')] + mocked_get_data_path.return_value = Path('/dir') + + # When: Get the list of files. + result = AppLocation.get_files('section', '.mp3') + + # Then: The section parameter was used correctly, and the glob argument was passed.. + mocked_glob.assert_called_once_with('*.mp3') + assert result == [Path('file5.mp3'), Path('file6.mp3')], 'The file lists should be identical.' + + +@patch('openlp.core.common.applocation.AppLocation.get_data_path') +@patch('openlp.core.common.applocation.create_paths') +def test_get_section_data_path(mocked_create_paths, mocked_get_data_path): + """ + Test the AppLocation.get_section_data_path() method + """ + # GIVEN: A mocked out AppLocation.get_data_path() + mocked_get_data_path.return_value = Path('test', 'dir') + mocked_create_paths.return_value = True + + # WHEN: we call AppLocation.get_data_path() + data_path = AppLocation.get_section_data_path('section') + + # THEN: check that all the correct methods were called, and the result is correct + mocked_create_paths.assert_called_with(Path('test', 'dir', 'section')) + assert data_path == Path('test', 'dir', 'section'), 'Result should be "test/dir/section"' + + +@patch('openlp.core.common.applocation.get_frozen_path') +def test_get_directory_for_app_dir(mocked_get_frozen_path): + """ + Test the AppLocation.get_directory() method for AppLocation.AppDir + """ + # GIVEN: A mocked out _get_frozen_path function + mocked_get_frozen_path.return_value = Path('app', 'dir') + + # WHEN: We call AppLocation.get_directory + directory = AppLocation.get_directory(AppLocation.AppDir) + + # THEN: check that the correct directory is returned + assert directory == Path('app', 'dir'), 'Directory should be "app/dir"' + + +@patch('openlp.core.common.applocation.get_frozen_path') +@patch('openlp.core.common.applocation.os.path.abspath') +@patch('openlp.core.common.applocation.os.path.split') +@patch('openlp.core.common.applocation.sys') +def test_get_directory_for_plugins_dir(mocked_sys, mocked_split, mocked_abspath, mocked_get_frozen_path): + """ + Test the AppLocation.get_directory() method for AppLocation.PluginsDir + """ + # GIVEN: _get_frozen_path, abspath, split and sys are mocked out + mocked_abspath.return_value = os.path.join('plugins', 'dir') + mocked_split.return_value = ['openlp'] + mocked_get_frozen_path.return_value = Path('dir') + mocked_sys.frozen = 1 + mocked_sys.argv = ['openlp'] + + # WHEN: We call AppLocation.get_directory + directory = AppLocation.get_directory(AppLocation.PluginsDir) + + # THEN: The correct directory should be returned + assert directory == Path('dir', 'plugins'), 'Directory should be "dir/plugins"' + + +@patch('openlp.core.common.sys') +def test_get_frozen_path_in_unfrozen_app(mocked_sys): + """ + Test the _get_frozen_path() function when the application is not frozen (compiled by PyInstaller) + """ + # GIVEN: The sys module "without" a "frozen" attribute + mocked_sys.frozen = None + + # WHEN: We call _get_frozen_path() with two parameters + frozen_path = get_frozen_path('frozen', 'not frozen') + + # THEN: The non-frozen parameter is returned + assert frozen_path == 'not frozen', '_get_frozen_path should return "not frozen"' + + +@patch('openlp.core.common.sys') +def test_get_frozen_path_in_frozen_app(mocked_sys): + """ + Test the get_frozen_path() function when the application is frozen (compiled by PyInstaller) + """ + # GIVEN: The sys module *with* a "frozen" attribute + mocked_sys.frozen = 1 + + # WHEN: We call _get_frozen_path() with two parameters + frozen_path = get_frozen_path('frozen', 'not frozen') + + # THEN: The frozen parameter is returned + assert frozen_path == 'frozen', 'Should return "frozen"' diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core/common/test_common.py similarity index 68% rename from tests/functional/openlp_core_common/test_common.py rename to tests/functional/openlp_core/common/test_common.py index e279cc83a..1d817ee90 100644 --- a/tests/functional/openlp_core_common/test_common.py +++ b/tests/functional/openlp_core/common/test_common.py @@ -25,8 +25,8 @@ Functional tests to test the AppLocation class and related methods. from unittest import TestCase from unittest.mock import MagicMock, call, patch -from openlp.core.common import check_directory_exists, clean_button_text, de_hump, extension_loader, is_macosx, \ - is_linux, is_win, path_to_module, trace_error_handler, translate +from openlp.core.common import clean_button_text, de_hump, extension_loader, is_macosx, is_linux, is_win, \ + path_to_module, trace_error_handler from openlp.core.common.path import Path @@ -34,77 +34,13 @@ class TestCommonFunctions(TestCase): """ A test suite to test out various functions in the openlp.core.common module. """ - def test_check_directory_exists_dir_exists(self): - """ - Test the check_directory_exists() function when the path already exists - """ - # GIVEN: A `Path` to check with patched out mkdir and exists methods - with patch.object(Path, 'exists') as mocked_exists, \ - patch.object(Path, 'mkdir') as mocked_mkdir, \ - patch('openlp.core.common.log'): - - # WHEN: `check_directory_exists` is called and the path exists - mocked_exists.return_value = True - check_directory_exists(Path('existing', 'directory')) - - # THEN: The function should not attempt to create the directory - mocked_exists.assert_called_with() - self.assertFalse(mocked_mkdir.called) - - def test_check_directory_exists_dir_doesnt_exists(self): - """ - Test the check_directory_exists() function when the path does not already exist - """ - # GIVEN: A `Path` to check with patched out mkdir and exists methods - with patch.object(Path, 'exists') as mocked_exists, \ - patch.object(Path, 'mkdir') as mocked_mkdir, \ - patch('openlp.core.common.log'): - - # WHEN: `check_directory_exists` is called and the path does not exist - mocked_exists.return_value = False - check_directory_exists(Path('existing', 'directory')) - - # THEN: The directory should have been created - mocked_exists.assert_called_with() - mocked_mkdir.assert_called_with(parents=True) - - def test_check_directory_exists_dir_io_error(self): - """ - Test the check_directory_exists() when an IOError is raised - """ - # GIVEN: A `Path` to check with patched out mkdir and exists methods - with patch.object(Path, 'exists') as mocked_exists, \ - patch.object(Path, 'mkdir'), \ - patch('openlp.core.common.log') as mocked_logger: - - # WHEN: An IOError is raised when checking the if the path exists. - mocked_exists.side_effect = IOError() - check_directory_exists(Path('existing', 'directory')) - - # THEN: The Error should have been logged - mocked_logger.exception.assert_called_once_with('failed to check if directory exists or create directory') - - def test_check_directory_exists_dir_value_error(self): - """ - Test the check_directory_exists() when an error other than IOError is raised - """ - # GIVEN: A `Path` to check with patched out mkdir and exists methods - with patch.object(Path, 'exists') as mocked_exists, \ - patch.object(Path, 'mkdir'), \ - patch('openlp.core.common.log'): - - # WHEN: Some other exception is raised - mocked_exists.side_effect = ValueError() - - # THEN: `check_directory_exists` raises an exception - self.assertRaises(ValueError, check_directory_exists, Path('existing', 'directory')) - def test_extension_loader_no_files_found(self): """ Test the `extension_loader` function when no files are found """ # GIVEN: A mocked `Path.glob` method which does not match any files - with patch('openlp.core.common.AppLocation.get_directory', return_value=Path('/', 'app', 'dir', 'openlp')), \ + with patch('openlp.core.common.applocation.AppLocation.get_directory', + return_value=Path('/', 'app', 'dir', 'openlp')), \ patch.object(Path, 'glob', return_value=[]), \ patch('openlp.core.common.importlib.import_module') as mocked_import_module: @@ -119,7 +55,8 @@ class TestCommonFunctions(TestCase): Test the `extension_loader` function when it successfully finds and loads some files """ # GIVEN: A mocked `Path.glob` method which returns a list of files - with patch('openlp.core.common.AppLocation.get_directory', return_value=Path('/', 'app', 'dir', 'openlp')), \ + with patch('openlp.core.common.applocation.AppLocation.get_directory', + return_value=Path('/', 'app', 'dir', 'openlp')), \ patch.object(Path, 'glob', return_value=[ Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py'), Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file2.py'), @@ -139,7 +76,8 @@ class TestCommonFunctions(TestCase): Test the `extension_loader` function when `SourceFileLoader` raises a `ImportError` """ # GIVEN: A mocked `import_module` which raises an `ImportError` - with patch('openlp.core.common.AppLocation.get_directory', return_value=Path('/', 'app', 'dir', 'openlp')), \ + with patch('openlp.core.common.applocation.AppLocation.get_directory', + return_value=Path('/', 'app', 'dir', 'openlp')), \ patch.object(Path, 'glob', return_value=[ Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \ patch('openlp.core.common.importlib.import_module', side_effect=ImportError()), \ @@ -156,7 +94,8 @@ class TestCommonFunctions(TestCase): Test the `extension_loader` function when `import_module` raises a `ImportError` """ # GIVEN: A mocked `SourceFileLoader` which raises an `OSError` - with patch('openlp.core.common.AppLocation.get_directory', return_value=Path('/', 'app', 'dir', 'openlp')), \ + with patch('openlp.core.common.applocation.AppLocation.get_directory', + return_value=Path('/', 'app', 'dir', 'openlp')), \ patch.object(Path, 'glob', return_value=[ Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \ patch('openlp.core.common.importlib.import_module', side_effect=OSError()), \ @@ -223,23 +162,6 @@ class TestCommonFunctions(TestCase): mocked_logger.error.assert_called_with( 'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test') - def test_translate(self): - """ - Test the translate() function - """ - # GIVEN: A string to translate and a mocked Qt translate function - context = 'OpenLP.Tests' - text = 'Untranslated string' - comment = 'A comment' - mocked_translate = MagicMock(return_value='Translated string') - - # WHEN: we call the translate function - result = translate(context, text, comment, mocked_translate) - - # THEN: the translated string should be returned, and the mocked function should have been called - mocked_translate.assert_called_with(context, text, comment) - self.assertEqual('Translated string', result, 'The translated string should have been returned') - def test_is_win(self): """ Test the is_win() function diff --git a/tests/functional/openlp_core_common/test_db.py b/tests/functional/openlp_core/common/test_db.py similarity index 100% rename from tests/functional/openlp_core_common/test_db.py rename to tests/functional/openlp_core/common/test_db.py diff --git a/tests/functional/openlp_core_common/test_httputils.py b/tests/functional/openlp_core/common/test_httputils.py similarity index 100% rename from tests/functional/openlp_core_common/test_httputils.py rename to tests/functional/openlp_core/common/test_httputils.py diff --git a/tests/functional/openlp_core/common/test_i18n.py b/tests/functional/openlp_core/common/test_i18n.py new file mode 100644 index 000000000..d6828fb6f --- /dev/null +++ b/tests/functional/openlp_core/common/test_i18n.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 OpenLP Developers # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.lib.languages package. +""" +from unittest.mock import MagicMock, patch + +from openlp.core.common.i18n import LANGUAGES, Language, UiStrings, get_language, get_locale_key, get_natural_key, \ + translate + + +def test_languages_type(): + """ + Test the languages variable type + """ + # GIVEN: The languages module + # WHEN: Accessing the languages variable + # THEN: It should be of type list + assert isinstance(LANGUAGES, list), 'LANGUAGES should be of type list' + + +def test_language_selection_languages_type(): + """ + Test the selection of a language + """ + # GIVEN: A list of languages from the languages module + # WHEN: Selecting the first item + language = LANGUAGES[0] + + # THEN: It should be an instance of the Language namedtuple + assert isinstance(language, Language) + assert language.id == 1 + assert language.name == '(Afan) Oromo' + assert language.code == 'om' + + +def test_get_language_name(): + """ + Test get_language() when supplied with a language name. + """ + + # GIVEN: A language name, in capitals + # WHEN: Calling get_language with it + language = get_language('YORUBA') + + # THEN: The Language found using that name should be returned + assert isinstance(language, Language) + assert language.id == 137 + assert language.name == 'Yoruba' + assert language.code == 'yo' + + +def test_get_language_code(): + """ + Test get_language() when supplied with a language code. + """ + # GIVEN: A language code in capitals + # WHEN: Calling get_language with it + language = get_language('IA') + + # THEN: The Language found using that code should be returned + assert isinstance(language, Language) + assert language.id == 51 + assert language.name == 'Interlingua' + assert language.code == 'ia' + + +def test_get_language_invalid(): + """ + Test get_language() when supplied with a string which is not a valid language name or code. + """ + + # GIVEN: A language code + # WHEN: Calling get_language with it + language = get_language('qwerty') + + # THEN: None should be returned + assert language is None + + +def test_get_language_invalid_with_none(): + """ + Test get_language() when supplied with a string which is not a valid language name or code. + """ + + # GIVEN: A language code + # WHEN: Calling get_language with it + language = get_language(None) + + # THEN: None should be returned + assert language is None + + +def test_get_locale_key(): + """ + Test the get_locale_key(string) function + """ + with patch('openlp.core.common.i18n.LanguageManager.get_language') as mocked_get_language: + # GIVEN: The language is German + # 0x00C3 (A with diaresis) should be sorted as "A". 0x00DF (sharp s) should be sorted as "ss". + mocked_get_language.return_value = 'de' + unsorted_list = ['Auszug', 'Aushang', '\u00C4u\u00DFerung'] + + # WHEN: We sort the list and use get_locale_key() to generate the sorting keys + sorted_list = sorted(unsorted_list, key=get_locale_key) + + # THEN: We get a properly sorted list + assert sorted_list == ['Aushang', '\u00C4u\u00DFerung', 'Auszug'], 'Strings should be sorted properly' + + +def test_get_natural_key(): + """ + Test the get_natural_key(string) function + """ + with patch('openlp.core.common.i18n.LanguageManager.get_language') as mocked_get_language: + # GIVEN: The language is English (a language, which sorts digits before letters) + mocked_get_language.return_value = 'en' + unsorted_list = ['item 10a', 'item 3b', '1st item'] + + # WHEN: We sort the list and use get_natural_key() to generate the sorting keys + sorted_list = sorted(unsorted_list, key=get_natural_key) + + # THEN: We get a properly sorted list + assert sorted_list == ['1st item', 'item 3b', 'item 10a'], 'Numbers should be sorted naturally' + + +def test_check_same_instance(): + """ + Test the UiStrings class - we always should have only one instance of the UiStrings class. + """ + # WHEN: Create two instances of the UiStrings class. + first_instance = UiStrings() + second_instance = UiStrings() + + # THEN: Check if the instances are the same. + assert first_instance is second_instance, 'Two UiStrings objects should be the same instance' + + +def test_translate(self): + """ + Test the translate() function + """ + # GIVEN: A string to translate and a mocked Qt translate function + context = 'OpenLP.Tests' + text = 'Untranslated string' + comment = 'A comment' + mocked_translate = MagicMock(return_value='Translated string') + + # WHEN: we call the translate function + result = translate(context, text, comment, mocked_translate) + + # THEN: the translated string should be returned, and the mocked function should have been called + mocked_translate.assert_called_with(context, text, comment) + assert result == 'Translated string', 'The translated string should have been returned' diff --git a/tests/functional/openlp_core_common/test_init.py b/tests/functional/openlp_core/common/test_init.py similarity index 100% rename from tests/functional/openlp_core_common/test_init.py rename to tests/functional/openlp_core/common/test_init.py diff --git a/tests/functional/openlp_core_common/test_json.py b/tests/functional/openlp_core/common/test_json.py similarity index 100% rename from tests/functional/openlp_core_common/test_json.py rename to tests/functional/openlp_core/common/test_json.py diff --git a/tests/functional/openlp_core_common/test_languagemanager.py b/tests/functional/openlp_core/common/test_languagemanager.py similarity index 100% rename from tests/functional/openlp_core_common/test_languagemanager.py rename to tests/functional/openlp_core/common/test_languagemanager.py diff --git a/tests/functional/openlp_core_common/test_registrymixin.py b/tests/functional/openlp_core/common/test_mixins.py similarity index 81% rename from tests/functional/openlp_core_common/test_registrymixin.py rename to tests/functional/openlp_core/common/test_mixins.py index 4681d9bdb..7cb8604af 100644 --- a/tests/functional/openlp_core_common/test_registrymixin.py +++ b/tests/functional/openlp_core/common/test_mixins.py @@ -22,12 +22,20 @@ """ Package to test the openlp.core.common package. """ -import os from unittest import TestCase -from openlp.core.common import RegistryMixin, Registry +from openlp.core.common.mixins import RegistryMixin +from openlp.core.common.registry import Registry -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..', 'resources')) + +class PlainStub(object): + def __init__(self): + pass + + +class MixinStub(RegistryMixin): + def __init__(self): + super().__init__(None) class TestRegistryMixin(TestCase): @@ -39,10 +47,10 @@ class TestRegistryMixin(TestCase): # GIVEN: A new registry Registry.create() - # WHEN: I create a new class - mock_1 = Test1() + # WHEN: I create an instance of a class that doesn't inherit from RegistryMixin + PlainStub() - # THEN: The following methods are missing + # THEN: Nothing is registered with the registry self.assertEqual(len(Registry().functions_list), 0), 'The function should not be in the dict anymore.' def test_registry_mixin_present(self): @@ -52,18 +60,8 @@ class TestRegistryMixin(TestCase): # GIVEN: A new registry Registry.create() - # WHEN: I create a new class - mock_2 = Test2() + # WHEN: I create an instance of a class that inherits from RegistryMixin + MixinStub() - # THEN: The following bootstrap methods should be present + # THEN: The bootstrap methods should be registered self.assertEqual(len(Registry().functions_list), 2), 'The bootstrap functions should be in the dict.' - - -class Test1(object): - def __init__(self): - pass - - -class Test2(RegistryMixin): - def __init__(self): - super(Test2, self).__init__(None) diff --git a/tests/functional/openlp_core_common/test_path.py b/tests/functional/openlp_core/common/test_path.py similarity index 86% rename from tests/functional/openlp_core_common/test_path.py rename to tests/functional/openlp_core/common/test_path.py index 0f35319c9..4b30bd2cb 100644 --- a/tests/functional/openlp_core_common/test_path.py +++ b/tests/functional/openlp_core/common/test_path.py @@ -26,8 +26,8 @@ import os from unittest import TestCase from unittest.mock import ANY, MagicMock, patch -from openlp.core.common.path import Path, copy, copyfile, copytree, path_to_str, replace_params, rmtree, str_to_path, \ - which +from openlp.core.common.path import Path, copy, copyfile, copytree, create_paths, path_to_str, replace_params, rmtree, \ + str_to_path, which class TestShutil(TestCase): @@ -337,3 +337,64 @@ class TestPath(TestCase): # THEN: A JSON decodable object should have been returned. self.assertEqual(obj, {'__Path__': ('path', 'to', 'fi.le')}) + + def test_create_paths_dir_exists(self): + """ + Test the create_paths() function when the path already exists + """ + # GIVEN: A `Path` to check with patched out mkdir and exists methods + mocked_path = MagicMock() + mocked_path.exists.return_value = True + + # WHEN: `create_paths` is called and the path exists + create_paths(mocked_path) + + # THEN: The function should not attempt to create the directory + mocked_path.exists.assert_called_once_with() + assert mocked_path.mkdir.call_count == 0, 'mkdir should not have been called' + + def test_create_paths_dir_doesnt_exists(self): + """ + Test the create_paths() function when the path does not already exist + """ + # GIVEN: A `Path` to check with patched out mkdir and exists methods + mocked_path = MagicMock() + mocked_path.exists.return_value = False + + # WHEN: `create_paths` is called and the path does not exist + create_paths(mocked_path) + + # THEN: The directory should have been created + mocked_path.exists.assert_called_once_with() + mocked_path.mkdir.assert_called_once_with(parents=True) + + @patch('openlp.core.common.path.log') + def test_create_paths_dir_io_error(self, mocked_logger): + """ + Test the create_paths() when an IOError is raised + """ + # GIVEN: A `Path` to check with patched out mkdir and exists methods + mocked_path = MagicMock() + mocked_path.exists.side_effect = IOError('Cannot make directory') + + # WHEN: An IOError is raised when checking the if the path exists. + create_paths(mocked_path) + + # THEN: The Error should have been logged + mocked_logger.exception.assert_called_once_with('failed to check if directory exists or create directory') + + def test_create_paths_dir_value_error(self): + """ + Test the create_paths() when an error other than IOError is raised + """ + # GIVEN: A `Path` to check with patched out mkdir and exists methods + mocked_path = MagicMock() + mocked_path.exists.side_effect = ValueError('Some other error') + + # WHEN: Some other exception is raised + try: + create_paths(mocked_path) + assert False, 'create_paths should have thrown an exception' + except: + # THEN: `create_paths` raises an exception + pass diff --git a/tests/functional/openlp_core_common/test_projector_utilities.py b/tests/functional/openlp_core/common/test_projector_utilities.py similarity index 100% rename from tests/functional/openlp_core_common/test_projector_utilities.py rename to tests/functional/openlp_core/common/test_projector_utilities.py diff --git a/tests/functional/openlp_core_common/test_registry.py b/tests/functional/openlp_core/common/test_registry.py similarity index 72% rename from tests/functional/openlp_core_common/test_registry.py rename to tests/functional/openlp_core/common/test_registry.py index ad30efb73..6428a27c3 100644 --- a/tests/functional/openlp_core_common/test_registry.py +++ b/tests/functional/openlp_core/common/test_registry.py @@ -24,9 +24,9 @@ Package to test the openlp.core.lib package. """ import os from unittest import TestCase -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry, RegistryProperties TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..', 'resources')) @@ -149,3 +149,72 @@ class TestRegistry(TestCase): def dummy_function_2(self): return "function_2" + + +class TestRegistryProperties(TestCase, RegistryProperties): + """ + Test the functions in the ThemeManager module + """ + def setUp(self): + """ + Create the Register + """ + Registry.create() + + def test_no_application(self): + """ + Test property if no registry value assigned + """ + # GIVEN an Empty Registry + # WHEN there is no Application + # THEN the application should be none + self.assertEqual(self.application, None, 'The application value should be None') + + def test_application(self): + """ + Test property if registry value assigned + """ + # GIVEN an Empty Registry + application = MagicMock() + + # WHEN the application is registered + Registry().register('application', application) + + # THEN the application should be none + self.assertEqual(self.application, application, 'The application value should match') + + @patch('openlp.core.common.registry.is_win') + def test_application_on_windows(self, mocked_is_win): + """ + Test property if registry value assigned on Windows + """ + # GIVEN an Empty Registry and we're on Windows + application = MagicMock() + mocked_is_win.return_value = True + + # WHEN the application is registered + Registry().register('application', application) + + # THEN the application should be none + self.assertEqual(self.application, application, 'The application value should match') + + @patch('openlp.core.common.registry.is_win') + def test_get_application_on_windows(self, mocked_is_win): + """ + Set that getting the application object on Windows happens dynamically + """ + # GIVEN an Empty Registry and we're on Windows + mocked_is_win.return_value = True + mock_application = MagicMock() + reg_props = RegistryProperties() + registry = Registry() + + # WHEN the application is accessed + with patch.object(registry, 'get') as mocked_get: + mocked_get.return_value = mock_application + actual_application = reg_props.application + + # THEN the application should be the mock object, and the correct function should have been called + self.assertEqual(mock_application, actual_application, 'The application value should match') + mocked_is_win.assert_called_with() + mocked_get.assert_called_with('application') diff --git a/tests/functional/openlp_core_common/test_registryproperties.py b/tests/functional/openlp_core/common/test_registryproperties.py similarity index 100% rename from tests/functional/openlp_core_common/test_registryproperties.py rename to tests/functional/openlp_core/common/test_registryproperties.py diff --git a/tests/functional/openlp_core_common/test_settings.py b/tests/functional/openlp_core/common/test_settings.py similarity index 98% rename from tests/functional/openlp_core_common/test_settings.py rename to tests/functional/openlp_core/common/test_settings.py index fe6e68604..d54a6a1e1 100644 --- a/tests/functional/openlp_core_common/test_settings.py +++ b/tests/functional/openlp_core/common/test_settings.py @@ -25,7 +25,7 @@ Package to test the openlp.core.lib.settings package. from unittest import TestCase from unittest.mock import patch -from openlp.core.common import Settings +from openlp.core.common.settings import Settings from tests.helpers.testmixin import TestMixin @@ -118,7 +118,7 @@ class TestSettings(TestCase, TestMixin): # GIVEN: A new Settings setup with self.assertRaises(KeyError) as cm: # WHEN reading a setting that doesn't exists - does_not_exist_value = Settings().value('core/does not exists') + Settings().value('core/does not exists') # THEN: An exception with the non-existing key should be thrown self.assertEqual(str(cm.exception), "'core/does not exists'", 'We should get an exception') diff --git a/tests/functional/openlp_core_common/test_uistrings.py b/tests/functional/openlp_core/common/test_uistrings.py similarity index 100% rename from tests/functional/openlp_core_common/test_uistrings.py rename to tests/functional/openlp_core/common/test_uistrings.py diff --git a/tests/functional/openlp_core/display/__init__.py b/tests/functional/openlp_core/display/__init__.py new file mode 100644 index 000000000..96d55e7e8 --- /dev/null +++ b/tests/functional/openlp_core/display/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 OpenLP Developers # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Test the :mod:`~openlp.core.display` module +""" diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core/display/test_renderer.py similarity index 90% rename from tests/functional/openlp_core_lib/test_renderer.py rename to tests/functional/openlp_core/display/test_renderer.py index feaefcd4d..c30fe083b 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core/display/test_renderer.py @@ -27,9 +27,10 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore -from openlp.core.common import Registry -from openlp.core.lib import Renderer, ScreenList, ServiceItem, FormattingTags -from openlp.core.lib.renderer import words_split, get_start_tags +from openlp.core.common.registry import Registry +from openlp.core.display.screens import ScreenList +from openlp.core.display.renderer import Renderer, words_split, get_start_tags +from openlp.core.lib import ServiceItem from openlp.core.lib.theme import Theme @@ -83,15 +84,16 @@ class TestRenderer(TestCase): Test the default layout calculations """ # GIVEN: A new renderer instance. - renderer = Renderer() # WHEN: given the default screen size has been created. - # THEN: The renderer have created a default screen. - self.assertEqual(renderer.width, 1024, 'The base renderer should be a live controller') - self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller') - self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller') - self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller') + renderer = Renderer() - @patch('openlp.core.lib.renderer.FormattingTags.get_html_tags') + # THEN: The renderer have created a default screen. + assert renderer.width == 1024, 'The base renderer should be a live controller' + assert renderer.height == 768, 'The base renderer should be a live controller' + assert renderer.screen_ratio == 0.75, 'The base renderer should be a live controller' + assert renderer.footer_start == 691, 'The base renderer should be a live controller' + + @patch('openlp.core.display.renderer.FormattingTags.get_html_tags') def test_get_start_tags(self, mocked_get_html_tags): """ Test the get_start_tags() method @@ -179,10 +181,10 @@ class TestRenderer(TestCase): # THEN: The blanks have been removed. self.assertListEqual(result_words, expected_words) - @patch('openlp.core.lib.renderer.QtWebKitWidgets.QWebView') - @patch('openlp.core.lib.renderer.build_lyrics_format_css') - @patch('openlp.core.lib.renderer.build_lyrics_outline_css') - @patch('openlp.core.lib.renderer.build_chords_css') + @patch('openlp.core.display.renderer.QtWebKitWidgets.QWebView') + @patch('openlp.core.display.renderer.build_lyrics_format_css') + @patch('openlp.core.display.renderer.build_lyrics_outline_css') + @patch('openlp.core.display.renderer.build_chords_css') def test_set_text_rectangle(self, mock_build_chords_css, mock_outline_css, mock_lyrics_css, mock_webview): """ Test set_text_rectangle returns a proper html string diff --git a/tests/functional/openlp_core_lib/test_screen.py b/tests/functional/openlp_core/display/test_screens.py similarity index 97% rename from tests/functional/openlp_core_lib/test_screen.py rename to tests/functional/openlp_core/display/test_screens.py index cfb705624..258f3b69a 100644 --- a/tests/functional/openlp_core_lib/test_screen.py +++ b/tests/functional/openlp_core/display/test_screens.py @@ -27,8 +27,8 @@ from unittest.mock import MagicMock from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry -from openlp.core.lib import ScreenList +from openlp.core.common.registry import Registry +from openlp.core.display.screens import ScreenList SCREEN = { 'primary': False, diff --git a/tests/functional/openlp_core_lib/__init__.py b/tests/functional/openlp_core/lib/__init__.py similarity index 100% rename from tests/functional/openlp_core_lib/__init__.py rename to tests/functional/openlp_core/lib/__init__.py diff --git a/tests/functional/openlp_core_lib/test_db.py b/tests/functional/openlp_core/lib/test_db.py similarity index 97% rename from tests/functional/openlp_core_lib/test_db.py rename to tests/functional/openlp_core/lib/test_db.py index 51aa08830..871980498 100644 --- a/tests/functional/openlp_core_lib/test_db.py +++ b/tests/functional/openlp_core/lib/test_db.py @@ -34,7 +34,6 @@ from sqlalchemy import MetaData from openlp.core.common.path import Path from openlp.core.lib.db import init_db, get_upgrade_op, delete_database, upgrade_db -from openlp.core.lib.projector import upgrade as pjlink_upgrade class TestDB(TestCase): @@ -163,16 +162,16 @@ class TestDB(TestCase): mocked_delete_file.assert_called_with(test_location) self.assertFalse(result, 'The result of delete_file should be False (was rigged that way)') - @patch('tests.functional.openlp_core_lib.test_db.pjlink_upgrade') - def test_skip_db_upgrade_with_no_database(self, mocked_upgrade): + def test_skip_db_upgrade_with_no_database(self): """ Test the upgrade_db function does not try to update a missing database """ # GIVEN: Database URL that does not (yet) exist url = 'sqlite:///{tmp}/test_db.sqlite'.format(tmp=self.tmp_folder) + mocked_upgrade = MagicMock() # WHEN: We attempt to upgrade a non-existant database - upgrade_db(url, pjlink_upgrade) + upgrade_db(url, mocked_upgrade) # THEN: upgrade should NOT have been called self.assertFalse(mocked_upgrade.called, 'Database upgrade function should NOT have been called') diff --git a/tests/functional/openlp_core_lib/test_formattingtags.py b/tests/functional/openlp_core/lib/test_formattingtags.py similarity index 100% rename from tests/functional/openlp_core_lib/test_formattingtags.py rename to tests/functional/openlp_core/lib/test_formattingtags.py diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core/lib/test_htmlbuilder.py similarity index 99% rename from tests/functional/openlp_core_lib/test_htmlbuilder.py rename to tests/functional/openlp_core/lib/test_htmlbuilder.py index 6e6806733..7c8d7809f 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core/lib/test_htmlbuilder.py @@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtWebKit -from openlp.core.common import Settings +from openlp.core.common.settings import Settings from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_lyrics_css, build_lyrics_outline_css, \ build_lyrics_format_css, build_footer_css, webkit_version, build_chords_css from openlp.core.lib.theme import HorizontalType, VerticalType diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core/lib/test_image_manager.py similarity index 96% rename from tests/functional/openlp_core_lib/test_image_manager.py rename to tests/functional/openlp_core/lib/test_image_manager.py index 743cb4761..c39f6e8f4 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core/lib/test_image_manager.py @@ -30,13 +30,13 @@ from unittest.mock import patch from PyQt5 import QtGui -from openlp.core.common import Registry -from openlp.core.lib import ImageManager, ScreenList -from openlp.core.lib.imagemanager import Priority +from openlp.core.common.registry import Registry +from openlp.core.display.screens import ScreenList +from openlp.core.lib.imagemanager import ImageManager, Priority from tests.helpers.testmixin import TestMixin -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources')) class TestImageManager(TestCase, TestMixin): @@ -152,8 +152,8 @@ class TestImageManager(TestCase, TestMixin): # Allow the queue to process. self.lock.release() # Request some "data". - image_bytes = self.image_manager.get_image_bytes(TEST_PATH, image4) - image_object = self.image_manager.get_image(TEST_PATH, image3) + self.image_manager.get_image_bytes(TEST_PATH, image4) + self.image_manager.get_image(TEST_PATH, image3) # Now the mocked methods/functions do not have to sleep anymore. self.sleep_time = 0 # Wait for the queue to finish. diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core/lib/test_lib.py similarity index 95% rename from tests/functional/openlp_core_lib/test_lib.py rename to tests/functional/openlp_core/lib/test_lib.py index 96e78e351..f2bfaf79c 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core/lib/test_lib.py @@ -23,7 +23,6 @@ Package to test the openlp.core.lib package. """ import os -from datetime import datetime, timedelta from unittest import TestCase from unittest.mock import MagicMock, patch @@ -34,7 +33,7 @@ from openlp.core.lib import FormattingTags, build_icon, check_item_selected, cle create_separated_list, create_thumb, expand_chords, expand_chords_for_printing, expand_tags, find_formatting_tags, \ get_text_file_string, image_to_byte, resize_image, str_to_bool, validate_thumb -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources')) class TestLib(TestCase): @@ -496,7 +495,7 @@ class TestLib(TestCase): Test that the check_item_selected() function returns True when there are selected indexes """ # GIVEN: A mocked out QtWidgets module and a list widget with selected indexes - mocked_QtWidgets = patch('openlp.core.lib.QtWidgets') + # mocked_QtWidgets = patch('openlp.core.lib.QtWidgets') mocked_list_widget = MagicMock() mocked_list_widget.selectedIndexes.return_value = True message = 'message' @@ -595,7 +594,7 @@ class TestLib(TestCase): Test the validate_thumb() function when the thumbnail does not exist """ # GIVEN: A mocked out os module, with path.exists returning False, and fake paths to a file and a thumb - with patch.object(Path, 'exists', return_value=False) as mocked_path_exists: + with patch.object(Path, 'exists', return_value=False): file_path = Path('path', 'to', 'file') thumb_path = Path('path', 'to', 'thumb') @@ -678,40 +677,34 @@ class TestLib(TestCase): self.assertEqual(wanted_width, result_size.width(), 'The image should have the requested width.') self.assertEqual(image.pixel(0, 0), wanted_background_rgb, 'The background should be white.') - def test_create_separated_list_qlocate(self): + @patch('openlp.core.lib.QtCore.QLocale.createSeparatedList') + def test_create_separated_list_qlocate(self, mocked_createSeparatedList): """ Test the create_separated_list function using the Qt provided method """ - with patch('openlp.core.lib.Qt') as mocked_qt, \ - patch('openlp.core.lib.QtCore.QLocale.createSeparatedList') as mocked_createSeparatedList: - # GIVEN: A list of strings and the mocked Qt module. - mocked_qt.PYQT_VERSION_STR = '4.9' - mocked_qt.qVersion.return_value = '4.8' - mocked_createSeparatedList.return_value = 'Author 1, Author 2, and Author 3' - string_list = ['Author 1', 'Author 2', 'Author 3'] + # GIVEN: A list of strings and the mocked Qt module. + mocked_createSeparatedList.return_value = 'Author 1, Author 2, and Author 3' + string_list = ['Author 1', 'Author 2', 'Author 3'] - # WHEN: We get a string build from the entries it the list and a separator. - string_result = create_separated_list(string_list) + # WHEN: We get a string build from the entries it the list and a separator. + string_result = create_separated_list(string_list) - # THEN: We should have "Author 1, Author 2, and Author 3" - self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, ' - 'Author 2, and Author 3".') + # THEN: We should have "Author 1, Author 2, and Author 3" + self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, ' + 'Author 2, and Author 3".') def test_create_separated_list_empty_list(self): """ Test the create_separated_list function with an empty list """ - with patch('openlp.core.lib.Qt') as mocked_qt: - # GIVEN: An empty list and the mocked Qt module. - mocked_qt.PYQT_VERSION_STR = '4.8' - mocked_qt.qVersion.return_value = '4.7' - string_list = [] + # GIVEN: An empty list and the mocked Qt module. + string_list = [] - # WHEN: We get a string build from the entries it the list and a separator. - string_result = create_separated_list(string_list) + # WHEN: We get a string build from the entries it the list and a separator. + string_result = create_separated_list(string_list) - # THEN: We shoud have an emptry string. - self.assertEqual(string_result, '', 'The string sould be empty.') + # THEN: We shoud have an emptry string. + self.assertEqual(string_result, '', 'The string sould be empty.') def test_create_separated_list_with_one_item(self): """ @@ -773,7 +766,6 @@ class TestLib(TestCase): """ Test that the expanding of chords works as expected when special chars are involved. """ - import html # GIVEN: A lyrics-line with chords text_with_chords = "I[D]'M NOT MOVED BY WHAT I SEE HALLE[F]LUJA[C]H" diff --git a/tests/functional/openlp_core_lib/test_mediamanageritem.py b/tests/functional/openlp_core/lib/test_mediamanageritem.py similarity index 100% rename from tests/functional/openlp_core_lib/test_mediamanageritem.py rename to tests/functional/openlp_core/lib/test_mediamanageritem.py diff --git a/tests/functional/openlp_core_lib/test_path.py b/tests/functional/openlp_core/lib/test_path.py similarity index 100% rename from tests/functional/openlp_core_lib/test_path.py rename to tests/functional/openlp_core/lib/test_path.py diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core/lib/test_pluginmanager.py similarity index 99% rename from tests/functional/openlp_core_lib/test_pluginmanager.py rename to tests/functional/openlp_core/lib/test_pluginmanager.py index d829feccb..52e9d91bf 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core/lib/test_pluginmanager.py @@ -25,7 +25,8 @@ Package to test the openlp.core.lib.pluginmanager package. from unittest import TestCase from unittest.mock import MagicMock -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib.pluginmanager import PluginManager from openlp.core.lib import PluginStatus diff --git a/tests/functional/openlp_core_lib/test_projector_constants.py b/tests/functional/openlp_core/lib/test_projector_constants.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_constants.py rename to tests/functional/openlp_core/lib/test_projector_constants.py diff --git a/tests/functional/openlp_core_lib/test_projector_db.py b/tests/functional/openlp_core/lib/test_projector_db.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_db.py rename to tests/functional/openlp_core/lib/test_projector_db.py diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink_base.py b/tests/functional/openlp_core/lib/test_projector_pjlink_base.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_pjlink_base.py rename to tests/functional/openlp_core/lib/test_projector_pjlink_base.py diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink_cmd_routing.py b/tests/functional/openlp_core/lib/test_projector_pjlink_cmd_routing.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_pjlink_cmd_routing.py rename to tests/functional/openlp_core/lib/test_projector_pjlink_cmd_routing.py diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink_commands.py b/tests/functional/openlp_core/lib/test_projector_pjlink_commands.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_pjlink_commands.py rename to tests/functional/openlp_core/lib/test_projector_pjlink_commands.py diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core/lib/test_serviceitem.py similarity index 99% rename from tests/functional/openlp_core_lib/test_serviceitem.py rename to tests/functional/openlp_core/lib/test_serviceitem.py index bcf2df959..78ad018fb 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core/lib/test_serviceitem.py @@ -26,7 +26,8 @@ import os from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry, md5_hash +from openlp.core.common import md5_hash +from openlp.core.common.registry import Registry from openlp.core.lib import ItemCapabilities, ServiceItem, ServiceItemType, FormattingTags from tests.utils import assert_length, convert_file_service_item @@ -56,7 +57,7 @@ RENDERED_VERSE = 'The Lord said to Noa 'e'\ 'n of the Lord\n' FOOTER = ['Arky Arky (Unknown)', 'Public Domain', 'CCLI 123456'] -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources', 'service')) +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'service')) class TestServiceItem(TestCase): diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core/lib/test_theme.py similarity index 100% rename from tests/functional/openlp_core_lib/test_theme.py rename to tests/functional/openlp_core/lib/test_theme.py diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core/lib/test_ui.py similarity index 99% rename from tests/functional/openlp_core_lib/test_ui.py rename to tests/functional/openlp_core/lib/test_ui.py index 28ff17122..8aec310ba 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core/lib/test_ui.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import UiStrings, translate +from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib.ui import add_welcome_page, create_button_box, create_horizontal_adjusting_combo_box, \ create_button, create_action, create_valign_selection_widgets, find_and_set_in_combo_box, create_widget_action, \ set_case_insensitive_completer diff --git a/tests/functional/openlp_core/test_app.py b/tests/functional/openlp_core/test_app.py new file mode 100644 index 000000000..0f5633c3e --- /dev/null +++ b/tests/functional/openlp_core/test_app.py @@ -0,0 +1,378 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 OpenLP Developers # +# --------------------------------------------------------------------------- # +# 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 os +import sys +from unittest import TestCase, skip +from unittest.mock import MagicMock, patch + +from PyQt5 import QtCore, QtWidgets + +from openlp.core.app import OpenLP, parse_options +from openlp.core.common.settings import Settings + +from tests.helpers.testmixin import TestMixin + +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'resources')) + + +def test_parse_options_basic(): + """ + Test the parse options process works + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = [] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # THEN: the following fields will have been extracted. + assert args.dev_version is False, 'The dev_version flag should be False' + assert args.loglevel == 'warning', 'The log level should be set to warning' + assert args.no_error_form is False, 'The no_error_form should be set to False' + assert args.portable is False, 'The portable flag should be set to false' + assert args.style is None, 'There are no style flags to be processed' + assert args.rargs == [], 'The service file should be blank' + + +def test_parse_options_debug(): + """ + Test the parse options process works for debug only + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = ['-l debug'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # THEN: the following fields will have been extracted. + assert args.dev_version is False, 'The dev_version flag should be False' + assert args.loglevel == ' debug', 'The log level should be set to debug' + assert args.no_error_form is False, 'The no_error_form should be set to False' + assert args.portable is False, 'The portable flag should be set to false' + assert args.style is None, 'There are no style flags to be processed' + assert args.rargs == [], 'The service file should be blank' + + +def test_parse_options_debug_and_portable(): + """ + Test the parse options process works for debug and portable + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = ['--portable'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # THEN: the following fields will have been extracted. + assert args.dev_version is False, 'The dev_version flag should be False' + assert args.loglevel == 'warning', 'The log level should be set to warning' + assert args.no_error_form is False, 'The no_error_form should be set to False' + assert args.portable is True, 'The portable flag should be set to true' + assert args.style is None, 'There are no style flags to be processed' + assert args.rargs == [], 'The service file should be blank' + + +def test_parse_options_all_no_file(): + """ + Test the parse options process works with two options + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = ['-l debug', '-d'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # THEN: the following fields will have been extracted. + assert args.dev_version is True, 'The dev_version flag should be True' + assert args.loglevel == ' debug', 'The log level should be set to debug' + assert args.no_error_form is False, 'The no_error_form should be set to False' + assert args.portable is False, 'The portable flag should be set to false' + assert args.style is None, 'There are no style flags to be processed' + assert args.rargs == [], 'The service file should be blank' + + +def test_parse_options_file(): + """ + Test the parse options process works with a file + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = ['dummy_temp'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # THEN: the following fields will have been extracted. + assert args.dev_version is False, 'The dev_version flag should be False' + assert args.loglevel == 'warning', 'The log level should be set to warning' + assert args.no_error_form is False, 'The no_error_form should be set to False' + assert args.portable is False, 'The portable flag should be set to false' + assert args.style is None, 'There are no style flags to be processed' + assert args.rargs == 'dummy_temp', 'The service file should not be blank' + + +def test_parse_options_file_and_debug(): + """ + Test the parse options process works with a file and the debug log level + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = ['-l debug', 'dummy_temp'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # THEN: the following fields will have been extracted. + assert args.dev_version is False, 'The dev_version flag should be False' + assert args.loglevel == ' debug', 'The log level should be set to debug' + assert args.no_error_form is False, 'The no_error_form should be set to False' + assert args.portable is False, 'The portable flag should be set to false' + assert args.style is None, 'There are no style flags to be processed' + assert args.rargs == 'dummy_temp', 'The service file should not be blank' + + +@skip('Figure out why this is causing a segfault') +class TestOpenLP(TestCase): + """ + Test the OpenLP app class + """ + @patch('openlp.core.app.QtWidgets.QApplication.exec') + def test_exec(self, mocked_exec): + """ + Test the exec method + """ + # GIVEN: An app + app = OpenLP([]) + app.shared_memory = MagicMock() + mocked_exec.return_value = False + + # WHEN: exec() is called + result = app.exec() + + # THEN: The right things should be called + assert app.is_event_loop_active is True + mocked_exec.assert_called_once_with() + app.shared_memory.detach.assert_called_once_with() + assert result is False + + @patch('openlp.core.app.QtCore.QSharedMemory') + def test_is_already_running_not_running(self, MockedSharedMemory): + """ + Test the is_already_running() method when OpenLP is NOT running + """ + # GIVEN: An OpenLP app and some mocks + mocked_shared_memory = MagicMock() + mocked_shared_memory.attach.return_value = False + MockedSharedMemory.return_value = mocked_shared_memory + app = OpenLP([]) + + # WHEN: is_already_running() is called + result = app.is_already_running() + + # THEN: The result should be false + MockedSharedMemory.assert_called_once_with('OpenLP') + mocked_shared_memory.attach.assert_called_once_with() + mocked_shared_memory.create.assert_called_once_with(1) + assert result is False + + @patch('openlp.core.app.QtWidgets.QMessageBox.critical') + @patch('openlp.core.app.QtWidgets.QMessageBox.StandardButtons') + @patch('openlp.core.app.QtCore.QSharedMemory') + def test_is_already_running_is_running_continue(self, MockedSharedMemory, MockedStandardButtons, mocked_critical): + """ + Test the is_already_running() method when OpenLP IS running and the user chooses to continue + """ + # GIVEN: An OpenLP app and some mocks + mocked_shared_memory = MagicMock() + mocked_shared_memory.attach.return_value = True + MockedSharedMemory.return_value = mocked_shared_memory + MockedStandardButtons.return_value = 0 + mocked_critical.return_value = QtWidgets.QMessageBox.Yes + app = OpenLP([]) + + # WHEN: is_already_running() is called + result = app.is_already_running() + + # THEN: The result should be false + MockedSharedMemory.assert_called_once_with('OpenLP') + mocked_shared_memory.attach.assert_called_once_with() + MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) + assert result is False + + @patch('openlp.core.app.QtWidgets.QMessageBox.critical') + @patch('openlp.core.app.QtWidgets.QMessageBox.StandardButtons') + @patch('openlp.core.app.QtCore.QSharedMemory') + def test_is_already_running_is_running_stop(self, MockedSharedMemory, MockedStandardButtons, mocked_critical): + """ + Test the is_already_running() method when OpenLP IS running and the user chooses to stop + """ + # GIVEN: An OpenLP app and some mocks + mocked_shared_memory = MagicMock() + mocked_shared_memory.attach.return_value = True + MockedSharedMemory.return_value = mocked_shared_memory + MockedStandardButtons.return_value = 0 + mocked_critical.return_value = QtWidgets.QMessageBox.No + app = OpenLP([]) + + # WHEN: is_already_running() is called + result = app.is_already_running() + + # THEN: The result should be false + MockedSharedMemory.assert_called_once_with('OpenLP') + mocked_shared_memory.attach.assert_called_once_with() + MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) + assert result is True + + def test_process_events(self): + """ + Test that the app.process_events() method simply calls the Qt method + """ + # GIVEN: An app + app = OpenLP([]) + + # WHEN: process_events() is called + with patch.object(app, 'processEvents') as mocked_processEvents: + app.process_events() + + # THEN: processEvents was called + mocked_processEvents.assert_called_once_with() + + def test_set_busy_cursor(self): + """ + Test that the set_busy_cursor() method sets the cursor + """ + # GIVEN: An app + app = OpenLP([]) + + # WHEN: set_busy_cursor() is called + with patch.object(app, 'setOverrideCursor') as mocked_setOverrideCursor, \ + patch.object(app, 'processEvents') as mocked_processEvents: + app.set_busy_cursor() + + # THEN: The cursor should have been set + mocked_setOverrideCursor.assert_called_once_with(QtCore.Qt.BusyCursor) + mocked_processEvents.assert_called_once_with() + + def test_set_normal_cursor(self): + """ + Test that the set_normal_cursor() method resets the cursor + """ + # GIVEN: An app + app = OpenLP([]) + + # WHEN: set_normal_cursor() is called + with patch.object(app, 'restoreOverrideCursor') as mocked_restoreOverrideCursor, \ + patch.object(app, 'processEvents') as mocked_processEvents: + app.set_normal_cursor() + + # THEN: The cursor should have been set + mocked_restoreOverrideCursor.assert_called_once_with() + mocked_processEvents.assert_called_once_with() + + +class TestInit(TestCase, TestMixin): + def setUp(self): + self.build_settings() + with patch('openlp.core.app.OpenLPMixin.__init__') as constructor: + constructor.return_value = None + self.openlp = OpenLP(list()) + + def tearDown(self): + self.destroy_settings() + del self.openlp + + def test_event(self): + """ + Test the reimplemented event method + """ + # GIVEN: A file path and a QEvent. + file_path = os.path.join(TEST_PATH, 'church.jpg') + mocked_file_method = MagicMock(return_value=file_path) + event = QtCore.QEvent(QtCore.QEvent.FileOpen) + event.file = mocked_file_method + + # WHEN: Call the vent method. + result = self.openlp.event(event) + + # THEN: The path should be inserted. + self.assertTrue(result, "The method should have returned True.") + mocked_file_method.assert_called_once_with() + self.assertEqual(self.openlp.args[0], file_path, "The path should be in args.") + + @patch('openlp.core.app.is_macosx') + def test_application_activate_event(self, mocked_is_macosx): + """ + Test that clicking on the dock icon on Mac OS X restores the main window if it is minimized + """ + # GIVEN: Mac OS X and an ApplicationActivate event + mocked_is_macosx.return_value = True + event = MagicMock() + event.type.return_value = QtCore.QEvent.ApplicationActivate + mocked_main_window = MagicMock() + self.openlp.main_window = mocked_main_window + + # WHEN: The icon in the dock is clicked + result = self.openlp.event(event) + + # THEN: + self.assertTrue(result, "The method should have returned True.") + # self.assertFalse(self.openlp.main_window.isMinimized()) + + @patch('openlp.core.app.get_version') + @patch('openlp.core.app.QtWidgets.QMessageBox.question') + def test_backup_on_upgrade_first_install(self, mocked_question, mocked_get_version): + """ + Test that we don't try to backup on a new install + """ + # GIVEN: Mocked data version and OpenLP version which are the same + old_install = False + MOCKED_VERSION = { + 'full': '2.2.0-bzr000', + 'version': '2.2.0', + 'build': 'bzr000' + } + Settings().setValue('core/application version', '2.2.0') + mocked_get_version.return_value = MOCKED_VERSION + mocked_question.return_value = QtWidgets.QMessageBox.No + + # WHEN: We check if a backup should be created + self.openlp.backup_on_upgrade(old_install, False) + + # THEN: It should not ask if we want to create a backup + self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be the same!') + self.assertEqual(mocked_question.call_count, 0, 'No question should have been asked!') + + @patch('openlp.core.app.get_version') + @patch('openlp.core.app.QtWidgets.QMessageBox.question') + def test_backup_on_upgrade(self, mocked_question, mocked_get_version): + """ + Test that we try to backup on a new install + """ + # GIVEN: Mocked data version and OpenLP version which are different + old_install = True + MOCKED_VERSION = { + 'full': '2.2.0-bzr000', + 'version': '2.2.0', + 'build': 'bzr000' + } + Settings().setValue('core/application version', '2.0.5') + self.openlp.splash = MagicMock() + self.openlp.splash.isVisible.return_value = True + mocked_get_version.return_value = MOCKED_VERSION + mocked_question.return_value = QtWidgets.QMessageBox.No + + # WHEN: We check if a backup should be created + self.openlp.backup_on_upgrade(old_install, True) + + # THEN: It should ask if we want to create a backup + self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be upgraded!') + self.assertEqual(mocked_question.call_count, 1, 'A question should have been asked!') + self.openlp.splash.hide.assert_called_once_with() + self.openlp.splash.show.assert_called_once_with() diff --git a/tests/functional/openlp_core/test_init.py b/tests/functional/openlp_core/test_init.py deleted file mode 100644 index dfe0c34cb..000000000 --- a/tests/functional/openlp_core/test_init.py +++ /dev/null @@ -1,273 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# 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 sys -from unittest import TestCase, skip -from unittest.mock import MagicMock, patch - -from PyQt5 import QtCore, QtWidgets - -from openlp.core import OpenLP, parse_options - - -class TestInitFunctions(TestCase): - - def test_parse_options_basic(self): - """ - Test the parse options process works - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = [] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, 'warning', 'The log level should be set to warning') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, [], 'The service file should be blank') - - def test_parse_options_debug(self): - """ - Test the parse options process works for debug only - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['-l debug'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, ' debug', 'The log level should be set to debug') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, [], 'The service file should be blank') - - def test_parse_options_debug_and_portable(self): - """ - Test the parse options process works for debug and portable - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['--portable'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, 'warning', 'The log level should be set to warning') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertTrue(args.portable, 'The portable flag should be set to true') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, [], 'The service file should be blank') - - def test_parse_options_all_no_file(self): - """ - Test the parse options process works with two options - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['-l debug', '-d'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertTrue(args.dev_version, 'The dev_version flag should be True') - self.assertEquals(args.loglevel, ' debug', 'The log level should be set to debug') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, [], 'The service file should be blank') - - def test_parse_options_file(self): - """ - Test the parse options process works with a file - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['dummy_temp'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, 'warning', 'The log level should be set to warning') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, 'dummy_temp', 'The service file should not be blank') - - def test_parse_options_file_and_debug(self): - """ - Test the parse options process works with a file and the debug log level - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['-l debug', 'dummy_temp'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, ' debug', 'The log level should be set to debug') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, 'dummy_temp', 'The service file should not be blank') - - -@skip('Figure out why this is causing a segfault') -class TestOpenLP(TestCase): - """ - Test the OpenLP app class - """ - @patch('openlp.core.QtWidgets.QApplication.exec') - def test_exec(self, mocked_exec): - """ - Test the exec method - """ - # GIVEN: An app - app = OpenLP([]) - app.shared_memory = MagicMock() - mocked_exec.return_value = False - - # WHEN: exec() is called - result = app.exec() - - # THEN: The right things should be called - assert app.is_event_loop_active is True - mocked_exec.assert_called_once_with() - app.shared_memory.detach.assert_called_once_with() - assert result is False - - @patch('openlp.core.QtCore.QSharedMemory') - def test_is_already_running_not_running(self, MockedSharedMemory): - """ - Test the is_already_running() method when OpenLP is NOT running - """ - # GIVEN: An OpenLP app and some mocks - mocked_shared_memory = MagicMock() - mocked_shared_memory.attach.return_value = False - MockedSharedMemory.return_value = mocked_shared_memory - app = OpenLP([]) - - # WHEN: is_already_running() is called - result = app.is_already_running() - - # THEN: The result should be false - MockedSharedMemory.assert_called_once_with('OpenLP') - mocked_shared_memory.attach.assert_called_once_with() - mocked_shared_memory.create.assert_called_once_with(1) - assert result is False - - @patch('openlp.core.QtWidgets.QMessageBox.critical') - @patch('openlp.core.QtWidgets.QMessageBox.StandardButtons') - @patch('openlp.core.QtCore.QSharedMemory') - def test_is_already_running_is_running_continue(self, MockedSharedMemory, MockedStandardButtons, mocked_critical): - """ - Test the is_already_running() method when OpenLP IS running and the user chooses to continue - """ - # GIVEN: An OpenLP app and some mocks - mocked_shared_memory = MagicMock() - mocked_shared_memory.attach.return_value = True - MockedSharedMemory.return_value = mocked_shared_memory - MockedStandardButtons.return_value = 0 - mocked_critical.return_value = QtWidgets.QMessageBox.Yes - app = OpenLP([]) - - # WHEN: is_already_running() is called - result = app.is_already_running() - - # THEN: The result should be false - MockedSharedMemory.assert_called_once_with('OpenLP') - mocked_shared_memory.attach.assert_called_once_with() - MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) - mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) - assert result is False - - @patch('openlp.core.QtWidgets.QMessageBox.critical') - @patch('openlp.core.QtWidgets.QMessageBox.StandardButtons') - @patch('openlp.core.QtCore.QSharedMemory') - def test_is_already_running_is_running_stop(self, MockedSharedMemory, MockedStandardButtons, mocked_critical): - """ - Test the is_already_running() method when OpenLP IS running and the user chooses to stop - """ - # GIVEN: An OpenLP app and some mocks - mocked_shared_memory = MagicMock() - mocked_shared_memory.attach.return_value = True - MockedSharedMemory.return_value = mocked_shared_memory - MockedStandardButtons.return_value = 0 - mocked_critical.return_value = QtWidgets.QMessageBox.No - app = OpenLP([]) - - # WHEN: is_already_running() is called - result = app.is_already_running() - - # THEN: The result should be false - MockedSharedMemory.assert_called_once_with('OpenLP') - mocked_shared_memory.attach.assert_called_once_with() - MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) - mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) - assert result is True - - def test_process_events(self): - """ - Test that the app.process_events() method simply calls the Qt method - """ - # GIVEN: An app - app = OpenLP([]) - - # WHEN: process_events() is called - with patch.object(app, 'processEvents') as mocked_processEvents: - app.process_events() - - # THEN: processEvents was called - mocked_processEvents.assert_called_once_with() - - def test_set_busy_cursor(self): - """ - Test that the set_busy_cursor() method sets the cursor - """ - # GIVEN: An app - app = OpenLP([]) - - # WHEN: set_busy_cursor() is called - with patch.object(app, 'setOverrideCursor') as mocked_setOverrideCursor, \ - patch.object(app, 'processEvents') as mocked_processEvents: - app.set_busy_cursor() - - # THEN: The cursor should have been set - mocked_setOverrideCursor.assert_called_once_with(QtCore.Qt.BusyCursor) - mocked_processEvents.assert_called_once_with() - - def test_set_normal_cursor(self): - """ - Test that the set_normal_cursor() method resets the cursor - """ - # GIVEN: An app - app = OpenLP([]) - - # WHEN: set_normal_cursor() is called - with patch.object(app, 'restoreOverrideCursor') as mocked_restoreOverrideCursor, \ - patch.object(app, 'processEvents') as mocked_processEvents: - app.set_normal_cursor() - - # THEN: The cursor should have been set - mocked_restoreOverrideCursor.assert_called_once_with() - mocked_processEvents.assert_called_once_with() diff --git a/tests/functional/openlp_core_ui/__init__.py b/tests/functional/openlp_core/ui/__init__.py similarity index 100% rename from tests/functional/openlp_core_ui/__init__.py rename to tests/functional/openlp_core/ui/__init__.py diff --git a/tests/functional/openlp_core_ui_lib/__init__.py b/tests/functional/openlp_core/ui/lib/__init__.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/__init__.py rename to tests/functional/openlp_core/ui/lib/__init__.py diff --git a/tests/functional/openlp_core_ui_lib/test_colorbutton.py b/tests/functional/openlp_core/ui/lib/test_colorbutton.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/test_colorbutton.py rename to tests/functional/openlp_core/ui/lib/test_colorbutton.py diff --git a/tests/functional/openlp_core_ui_lib/test_filedialog.py b/tests/functional/openlp_core/ui/lib/test_filedialog.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/test_filedialog.py rename to tests/functional/openlp_core/ui/lib/test_filedialog.py diff --git a/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py b/tests/functional/openlp_core/ui/lib/test_listpreviewwidget.py similarity index 99% rename from tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py rename to tests/functional/openlp_core/ui/lib/test_listpreviewwidget.py index 77c63bb91..a64ae5e0e 100644 --- a/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py +++ b/tests/functional/openlp_core/ui/lib/test_listpreviewwidget.py @@ -27,9 +27,8 @@ from unittest.mock import MagicMock, patch, call from PyQt5 import QtGui -from openlp.core.common import Settings from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget -from openlp.core.lib import ImageSource, ServiceItem +from openlp.core.lib import ImageSource class TestListPreviewWidget(TestCase): diff --git a/tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py b/tests/functional/openlp_core/ui/lib/test_listwidgetwithdnd.py similarity index 99% rename from tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py rename to tests/functional/openlp_core/ui/lib/test_listwidgetwithdnd.py index 0b79f7ab1..2a4e039fa 100755 --- a/tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py +++ b/tests/functional/openlp_core/ui/lib/test_listwidgetwithdnd.py @@ -23,11 +23,11 @@ This module contains tests for the openlp.core.lib.listwidgetwithdnd module """ from unittest import TestCase +from unittest.mock import MagicMock, patch from types import GeneratorType -from openlp.core.common.uistrings import UiStrings +from openlp.core.common.i18n import UiStrings from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD -from unittest.mock import MagicMock, patch class TestListWidgetWithDnD(TestCase): diff --git a/tests/functional/openlp_core_ui_lib/test_pathedit.py b/tests/functional/openlp_core/ui/lib/test_pathedit.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/test_pathedit.py rename to tests/functional/openlp_core/ui/lib/test_pathedit.py diff --git a/tests/functional/openlp_core_ui_media/__init__.py b/tests/functional/openlp_core/ui/media/__init__.py similarity index 100% rename from tests/functional/openlp_core_ui_media/__init__.py rename to tests/functional/openlp_core/ui/media/__init__.py diff --git a/tests/functional/openlp_core_ui_media/test_mediacontroller.py b/tests/functional/openlp_core/ui/media/test_mediacontroller.py similarity index 99% rename from tests/functional/openlp_core_ui_media/test_mediacontroller.py rename to tests/functional/openlp_core/ui/media/test_mediacontroller.py index 318623434..f468b02be 100644 --- a/tests/functional/openlp_core_ui_media/test_mediacontroller.py +++ b/tests/functional/openlp_core/ui/media/test_mediacontroller.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from openlp.core.ui.media.mediacontroller import MediaController from openlp.core.ui.media.mediaplayer import MediaPlayer -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from tests.helpers.testmixin import TestMixin diff --git a/tests/functional/openlp_core_ui_media/test_systemplayer.py b/tests/functional/openlp_core/ui/media/test_systemplayer.py similarity index 99% rename from tests/functional/openlp_core_ui_media/test_systemplayer.py rename to tests/functional/openlp_core/ui/media/test_systemplayer.py index 396ae910e..2fe2e0619 100644 --- a/tests/functional/openlp_core_ui_media/test_systemplayer.py +++ b/tests/functional/openlp_core/ui/media/test_systemplayer.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, call, patch from PyQt5 import QtCore, QtMultimedia -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui.media import MediaState from openlp.core.ui.media.systemplayer import SystemPlayer, CheckMediaWorker, ADDITIONAL_EXT diff --git a/tests/functional/openlp_core_ui_media/test_vlcplayer.py b/tests/functional/openlp_core/ui/media/test_vlcplayer.py similarity index 99% rename from tests/functional/openlp_core_ui_media/test_vlcplayer.py rename to tests/functional/openlp_core/ui/media/test_vlcplayer.py index 28ebd55fe..6e2fe73c6 100644 --- a/tests/functional/openlp_core_ui_media/test_vlcplayer.py +++ b/tests/functional/openlp_core/ui/media/test_vlcplayer.py @@ -28,7 +28,7 @@ from datetime import datetime, timedelta from unittest import TestCase, skip from unittest.mock import MagicMock, patch, call -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui.media import MediaState, MediaType from openlp.core.ui.media.vlcplayer import AUDIO_EXT, VIDEO_EXT, VlcPlayer, get_vlc diff --git a/tests/functional/openlp_core_ui_media/test_webkitplayer.py b/tests/functional/openlp_core/ui/media/test_webkitplayer.py similarity index 100% rename from tests/functional/openlp_core_ui_media/test_webkitplayer.py rename to tests/functional/openlp_core/ui/media/test_webkitplayer.py diff --git a/tests/functional/openlp_core_ui/test_aboutform.py b/tests/functional/openlp_core/ui/test_aboutform.py similarity index 100% rename from tests/functional/openlp_core_ui/test_aboutform.py rename to tests/functional/openlp_core/ui/test_aboutform.py diff --git a/tests/functional/openlp_core_ui/test_advancedtab.py b/tests/functional/openlp_core/ui/test_advancedtab.py similarity index 98% rename from tests/functional/openlp_core_ui/test_advancedtab.py rename to tests/functional/openlp_core/ui/test_advancedtab.py index a1e8157da..ca7bb8ca5 100644 --- a/tests/functional/openlp_core_ui/test_advancedtab.py +++ b/tests/functional/openlp_core/ui/test_advancedtab.py @@ -24,7 +24,7 @@ Package to test the openlp.core.ui.advancedtab package. """ from unittest import TestCase -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui.advancedtab import AdvancedTab from openlp.core.ui.settingsform import SettingsForm diff --git a/tests/functional/openlp_core_ui/test_exceptionform.py b/tests/functional/openlp_core/ui/test_exceptionform.py similarity index 99% rename from tests/functional/openlp_core_ui/test_exceptionform.py rename to tests/functional/openlp_core/ui/test_exceptionform.py index 28707410c..3bc99d63f 100644 --- a/tests/functional/openlp_core_ui/test_exceptionform.py +++ b/tests/functional/openlp_core/ui/test_exceptionform.py @@ -28,7 +28,7 @@ import tempfile from unittest import TestCase from unittest.mock import call, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.core.ui import exceptionform diff --git a/tests/functional/openlp_core_ui/test_first_time.py b/tests/functional/openlp_core/ui/test_first_time.py similarity index 100% rename from tests/functional/openlp_core_ui/test_first_time.py rename to tests/functional/openlp_core/ui/test_first_time.py diff --git a/tests/functional/openlp_core_ui/test_firsttimeform.py b/tests/functional/openlp_core/ui/test_firsttimeform.py similarity index 97% rename from tests/functional/openlp_core_ui/test_firsttimeform.py rename to tests/functional/openlp_core/ui/test_firsttimeform.py index c90cdd80b..543d4334c 100644 --- a/tests/functional/openlp_core_ui/test_firsttimeform.py +++ b/tests/functional/openlp_core/ui/test_firsttimeform.py @@ -28,7 +28,7 @@ import urllib from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.core.ui.firsttimeform import FirstTimeForm @@ -111,7 +111,7 @@ class TestFirstTimeForm(TestCase, TestMixin): patch.object(Registry, 'register_function') as mocked_register_function, \ patch('openlp.core.ui.firsttimeform.Settings') as MockedSettings, \ patch('openlp.core.ui.firsttimeform.gettempdir') as mocked_gettempdir, \ - patch('openlp.core.ui.firsttimeform.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.ui.firsttimeform.create_paths') as mocked_create_paths, \ patch.object(frw.application, 'set_normal_cursor'): mocked_settings = MagicMock() mocked_settings.value.return_value = True @@ -132,7 +132,7 @@ class TestFirstTimeForm(TestCase, TestMixin): mocked_no_internet_finish_btn.setVisible.assert_called_with(False) mocked_settings.value.assert_called_with('core/has run wizard') mocked_gettempdir.assert_called_with() - mocked_check_directory_exists.assert_called_with(expected_temp_path) + mocked_create_paths.assert_called_with(expected_temp_path) def test_update_screen_list_combo(self): """ diff --git a/tests/functional/openlp_core_ui/test_formattingtagscontroller.py b/tests/functional/openlp_core/ui/test_formattingtagscontroller.py similarity index 100% rename from tests/functional/openlp_core_ui/test_formattingtagscontroller.py rename to tests/functional/openlp_core/ui/test_formattingtagscontroller.py diff --git a/tests/functional/openlp_core_ui/test_formattingtagsform.py b/tests/functional/openlp_core/ui/test_formattingtagsform.py similarity index 100% rename from tests/functional/openlp_core_ui/test_formattingtagsform.py rename to tests/functional/openlp_core/ui/test_formattingtagsform.py diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core/ui/test_maindisplay.py similarity index 98% rename from tests/functional/openlp_core_ui/test_maindisplay.py rename to tests/functional/openlp_core/ui/test_maindisplay.py index c3f798982..1516418f4 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core/ui/test_maindisplay.py @@ -27,9 +27,11 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore -from openlp.core.common import Registry, is_macosx +from openlp.core.common import is_macosx from openlp.core.common.path import Path -from openlp.core.lib import ScreenList, PluginManager +from openlp.core.common.registry import Registry +from openlp.core.display.screens import ScreenList +from openlp.core.lib import PluginManager from openlp.core.ui import MainDisplay, AudioPlayer from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET diff --git a/tests/functional/openlp_core_ui/test_mainwindow.py b/tests/functional/openlp_core/ui/test_mainwindow.py similarity index 99% rename from tests/functional/openlp_core_ui/test_mainwindow.py rename to tests/functional/openlp_core/ui/test_mainwindow.py index 2582acfa5..4bb1cee37 100644 --- a/tests/functional/openlp_core_ui/test_mainwindow.py +++ b/tests/functional/openlp_core/ui/test_mainwindow.py @@ -28,7 +28,8 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtWidgets -from openlp.core.common import Registry, UiStrings +from openlp.core.common.i18n import UiStrings +from openlp.core.common.registry import Registry from openlp.core.ui.mainwindow import MainWindow from tests.helpers.testmixin import TestMixin @@ -57,8 +58,8 @@ class TestMainWindow(TestCase, TestMixin): patch('openlp.core.ui.mainwindow.QtWidgets.QMainWindow.addDockWidget') as mocked_add_dock_method, \ patch('openlp.core.ui.mainwindow.ThemeManager') as mocked_theme_manager, \ patch('openlp.core.ui.mainwindow.Renderer') as mocked_renderer, \ - patch('openlp.core.ui.mainwindow.websockets.WebSocketServer') as mocked_websocketserver, \ - patch('openlp.core.ui.mainwindow.server.HttpServer') as mocked_httpserver: + patch('openlp.core.ui.mainwindow.websockets.WebSocketServer'), \ + patch('openlp.core.ui.mainwindow.server.HttpServer'): self.mocked_settings_form = mocked_settings_form self.mocked_image_manager = mocked_image_manager self.mocked_live_controller = mocked_live_controller diff --git a/tests/functional/openlp_core_ui/test_media.py b/tests/functional/openlp_core/ui/test_media.py similarity index 100% rename from tests/functional/openlp_core_ui/test_media.py rename to tests/functional/openlp_core/ui/test_media.py diff --git a/tests/functional/openlp_core_ui/test_servicemanager.py b/tests/functional/openlp_core/ui/test_servicemanager.py similarity index 99% rename from tests/functional/openlp_core_ui/test_servicemanager.py rename to tests/functional/openlp_core/ui/test_servicemanager.py index 589f6e28d..487f56362 100644 --- a/tests/functional/openlp_core_ui/test_servicemanager.py +++ b/tests/functional/openlp_core/ui/test_servicemanager.py @@ -28,7 +28,8 @@ from unittest.mock import MagicMock, patch import PyQt5 -from openlp.core.common import Registry, ThemeLevel +from openlp.core.common import ThemeLevel +from openlp.core.common.registry import Registry from openlp.core.lib import ServiceItem, ServiceItemType, ItemCapabilities from openlp.core.ui import ServiceManager from openlp.core.ui.lib.toolbar import OpenLPToolbar @@ -695,7 +696,7 @@ class TestServiceManager(TestCase): # WHEN: The service manager has a Global theme mocked_renderer.theme_level = ThemeLevel.Global - result = service_manager.theme_change() + service_manager.theme_change() # THEN: The the theme toolbar should not be visible self.assertFalse(service_manager.toolbar.actions['theme_combo_box'].isVisible(), @@ -716,7 +717,7 @@ class TestServiceManager(TestCase): # WHEN: The service manager has a Service theme mocked_renderer.theme_level = ThemeLevel.Service - result = service_manager.theme_change() + service_manager.theme_change() # THEN: The the theme toolbar should be visible self.assertTrue(service_manager.toolbar.actions['theme_combo_box'].isVisible(), @@ -737,7 +738,7 @@ class TestServiceManager(TestCase): # WHEN: The service manager has a Song theme mocked_renderer.theme_level = ThemeLevel.Song - result = service_manager.theme_change() + service_manager.theme_change() # THEN: The the theme toolbar should be visible self.assertTrue(service_manager.toolbar.actions['theme_combo_box'].isVisible(), diff --git a/tests/functional/openlp_core_ui/test_settingsform.py b/tests/functional/openlp_core/ui/test_settingsform.py similarity index 99% rename from tests/functional/openlp_core_ui/test_settingsform.py rename to tests/functional/openlp_core/ui/test_settingsform.py index ee3bbd227..6cbc0534e 100644 --- a/tests/functional/openlp_core_ui/test_settingsform.py +++ b/tests/functional/openlp_core/ui/test_settingsform.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui.settingsform import SettingsForm diff --git a/tests/functional/openlp_core_ui/test_shortcutlistdialog.py b/tests/functional/openlp_core/ui/test_shortcutlistdialog.py similarity index 100% rename from tests/functional/openlp_core_ui/test_shortcutlistdialog.py rename to tests/functional/openlp_core/ui/test_shortcutlistdialog.py diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core/ui/test_slidecontroller.py similarity index 99% rename from tests/functional/openlp_core_ui/test_slidecontroller.py rename to tests/functional/openlp_core/ui/test_slidecontroller.py index d86610ab3..145f779ac 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core/ui/test_slidecontroller.py @@ -27,8 +27,8 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtGui -from openlp.core import Registry -from openlp.core.lib import ImageSource, ServiceItemAction +from openlp.core.common.registry import Registry +from openlp.core.lib import ServiceItemAction from openlp.core.ui import SlideController, LiveController, PreviewController from openlp.core.ui.slidecontroller import InfoLabel, WIDE_MENU, NON_TEXT_MENU diff --git a/tests/functional/openlp_core_ui/test_style.py b/tests/functional/openlp_core/ui/test_style.py similarity index 96% rename from tests/functional/openlp_core_ui/test_style.py rename to tests/functional/openlp_core/ui/test_style.py index 7435df1c7..fc017e87d 100644 --- a/tests/functional/openlp_core_ui/test_style.py +++ b/tests/functional/openlp_core/ui/test_style.py @@ -22,6 +22,7 @@ """ Package to test the :mod:`~openlp.core.ui.style` module. """ +from unittest import skipIf from unittest.mock import MagicMock, patch import openlp.core.ui.style @@ -29,9 +30,10 @@ from openlp.core.ui.style import MEDIA_MANAGER_STYLE, WIN_REPAIR_STYLESHEET, get get_library_stylesheet +@skipIf(not hasattr(openlp.core.ui.style, 'qdarkstyle'), 'qdarkstyle is not installed') @patch('openlp.core.ui.style.HAS_DARK_STYLE', True) @patch('openlp.core.ui.style.Settings') -@patch.object(openlp.core.ui.style, 'qdarkstyle') +@patch('openlp.core.ui.style.qdarkstyle') def test_get_application_stylesheet_dark(mocked_qdarkstyle, MockSettings): """Test that the dark stylesheet is returned when available and enabled""" # GIVEN: We're on Windows and no dark style is set diff --git a/tests/functional/openlp_core_ui/test_themeform.py b/tests/functional/openlp_core/ui/test_themeform.py similarity index 100% rename from tests/functional/openlp_core_ui/test_themeform.py rename to tests/functional/openlp_core/ui/test_themeform.py diff --git a/tests/functional/openlp_core_ui/test_thememanager.py b/tests/functional/openlp_core/ui/test_thememanager.py similarity index 97% rename from tests/functional/openlp_core_ui/test_thememanager.py rename to tests/functional/openlp_core/ui/test_thememanager.py index e4b044b29..c1ba80f1d 100644 --- a/tests/functional/openlp_core_ui/test_thememanager.py +++ b/tests/functional/openlp_core/ui/test_thememanager.py @@ -30,7 +30,7 @@ from unittest.mock import ANY, MagicMock, patch from PyQt5 import QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.core.ui import ThemeManager @@ -88,9 +88,9 @@ class TestThemeManager(TestCase): Test that we don't try to overwrite a theme background image with itself """ # GIVEN: A new theme manager instance, with mocked builtins.open, copyfile, - # theme, check_directory_exists and thememanager-attributes. + # theme, create_paths and thememanager-attributes. with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \ - patch('openlp.core.ui.thememanager.check_directory_exists'): + patch('openlp.core.ui.thememanager.create_paths'): theme_manager = ThemeManager(None) theme_manager.old_background_image = None theme_manager.generate_and_save_image = MagicMock() @@ -112,9 +112,9 @@ class TestThemeManager(TestCase): Test that we do overwrite a theme background image when a new is submitted """ # GIVEN: A new theme manager instance, with mocked builtins.open, copyfile, - # theme, check_directory_exists and thememanager-attributes. + # theme, create_paths and thememanager-attributes. with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \ - patch('openlp.core.ui.thememanager.check_directory_exists'): + patch('openlp.core.ui.thememanager.create_paths'): theme_manager = ThemeManager(None) theme_manager.old_background_image = None theme_manager.generate_and_save_image = MagicMock() diff --git a/tests/functional/openlp_core_ui/test_themetab.py b/tests/functional/openlp_core/ui/test_themetab.py similarity index 98% rename from tests/functional/openlp_core_ui/test_themetab.py rename to tests/functional/openlp_core/ui/test_themetab.py index 8c562f158..82a2568cd 100644 --- a/tests/functional/openlp_core_ui/test_themetab.py +++ b/tests/functional/openlp_core/ui/test_themetab.py @@ -25,7 +25,7 @@ Package to test the openlp.core.ui.ThemeTab package. from unittest import TestCase from unittest.mock import MagicMock -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui.themestab import ThemesTab from openlp.core.ui.settingsform import SettingsForm diff --git a/tests/functional/openlp_core_common/test_applocation.py b/tests/functional/openlp_core_common/test_applocation.py deleted file mode 100644 index 7dff1a073..000000000 --- a/tests/functional/openlp_core_common/test_applocation.py +++ /dev/null @@ -1,191 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# 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 # -############################################################################### -""" -Functional tests to test the AppLocation class and related methods. -""" -import os -from unittest import TestCase -from unittest.mock import MagicMock, patch - -from openlp.core.common import AppLocation, get_frozen_path -from openlp.core.common.path import Path - -FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3'] - - -class TestAppLocation(TestCase): - """ - A test suite to test out various methods around the AppLocation class. - """ - def test_get_data_path(self): - """ - Test the AppLocation.get_data_path() method - """ - with patch('openlp.core.common.applocation.Settings') as mocked_class, \ - patch('openlp.core.common.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.common.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 = os.path.join('test', 'dir') - mocked_check_directory_exists.return_value = True - mocked_os.path.normpath.return_value = os.path.join('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('advanced/data path') - mocked_get_directory.assert_called_with(AppLocation.DataDir) - mocked_check_directory_exists.assert_called_with(os.path.join('test', 'dir')) - self.assertEqual(os.path.join('test', 'dir'), data_path, 'Result should be "test/dir"') - - def test_get_data_path_with_custom_location(self): - """ - Test the AppLocation.get_data_path() method when a custom location is set in the settings - """ - # GIVEN: A mocked out Settings class which returns a custom data location - mocked_settings_instance = MagicMock( - **{'contains.return_value': True, 'value.return_value': Path('custom', 'dir')}) - with patch('openlp.core.common.applocation.Settings', return_value=mocked_settings_instance): - - # 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_instance.contains.assert_called_with('advanced/data path') - mocked_settings_instance.value.assert_called_with('advanced/data path') - self.assertEqual(Path('custom', 'dir'), data_path, 'Result should be "custom/dir"') - - def test_get_files_no_section_no_extension(self): - """ - Test the AppLocation.get_files() method with no parameters passed. - """ - # GIVEN: Our mocked modules/methods. - with patch.object(Path, 'glob', return_value=[Path('/dir/file5.mp3'), Path('/dir/file6.mp3')]) as mocked_glob, \ - patch('openlp.core.common.AppLocation.get_data_path', return_value=Path('/dir')): - - # When: Get the list of files. - result = AppLocation.get_files() - - # Then: Check if the section parameter was used correctly, and the glob argument was passed. - mocked_glob.assert_called_once_with('*') - - # Then: check if the file lists are identical. - self.assertListEqual([Path('file5.mp3'), Path('file6.mp3')], result, 'The file lists should be identical.') - - def test_get_files(self): - """ - Test the AppLocation.get_files() method with all parameters passed. - """ - # GIVEN: Our mocked modules/methods. - with patch.object(Path, 'glob', return_value=[Path('/dir/section/file5.mp3'), Path('/dir/section/file6.mp3')]) \ - as mocked_glob, \ - patch('openlp.core.common.AppLocation.get_data_path', return_value=Path('/dir')): - - # When: Get the list of files. - result = AppLocation.get_files('section', '.mp3') - - # Then: The section parameter was used correctly, and the glob argument was passed.. - mocked_glob.assert_called_once_with('*.mp3') - self.assertListEqual([Path('file5.mp3'), Path('file6.mp3')], result, 'The file lists should be identical.') - - def test_get_section_data_path(self): - """ - Test the AppLocation.get_section_data_path() method - """ - with patch('openlp.core.common.AppLocation.get_data_path') as mocked_get_data_path, \ - patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists: - # GIVEN: A mocked out AppLocation.get_data_path() - mocked_get_data_path.return_value = Path('test', 'dir') - mocked_check_directory_exists.return_value = True - - # WHEN: we call AppLocation.get_data_path() - data_path = AppLocation.get_section_data_path('section') - - # THEN: check that all the correct methods were called, and the result is correct - mocked_check_directory_exists.assert_called_with(Path('test', 'dir', 'section')) - self.assertEqual(Path('test', 'dir', 'section'), data_path, 'Result should be "test/dir/section"') - - def test_get_directory_for_app_dir(self): - """ - Test the AppLocation.get_directory() method for AppLocation.AppDir - """ - # GIVEN: A mocked out _get_frozen_path function - with patch('openlp.core.common.applocation.get_frozen_path') as mocked_get_frozen_path: - mocked_get_frozen_path.return_value = Path('app', 'dir') - - # WHEN: We call AppLocation.get_directory - directory = AppLocation.get_directory(AppLocation.AppDir) - - # THEN: check that the correct directory is returned - self.assertEqual(Path('app', 'dir'), directory, 'Directory should be "app/dir"') - - def test_get_directory_for_plugins_dir(self): - """ - Test the AppLocation.get_directory() method for AppLocation.PluginsDir - """ - # GIVEN: _get_frozen_path, abspath, split and sys are mocked out - with patch('openlp.core.common.applocation.get_frozen_path') as mocked_get_frozen_path, \ - patch('openlp.core.common.applocation.os.path.abspath') as mocked_abspath, \ - patch('openlp.core.common.applocation.os.path.split') as mocked_split, \ - patch('openlp.core.common.applocation.sys') as mocked_sys: - mocked_abspath.return_value = os.path.join('plugins', 'dir') - mocked_split.return_value = ['openlp'] - mocked_get_frozen_path.return_value = Path('dir') - mocked_sys.frozen = 1 - mocked_sys.argv = ['openlp'] - - # WHEN: We call AppLocation.get_directory - directory = AppLocation.get_directory(AppLocation.PluginsDir) - - # THEN: The correct directory should be returned - self.assertEqual(Path('dir', 'plugins'), directory, 'Directory should be "dir/plugins"') - - def test_get_frozen_path_in_unfrozen_app(self): - """ - Test the _get_frozen_path() function when the application is not frozen (compiled by PyInstaller) - """ - with patch('openlp.core.common.sys') as mocked_sys: - # GIVEN: The sys module "without" a "frozen" attribute - mocked_sys.frozen = None - - # WHEN: We call _get_frozen_path() with two parameters - frozen_path = get_frozen_path('frozen', 'not frozen') - - # THEN: The non-frozen parameter is returned - self.assertEqual('not frozen', frozen_path, '_get_frozen_path should return "not frozen"') - - def test_get_frozen_path_in_frozen_app(self): - """ - Test the get_frozen_path() function when the application is frozen (compiled by PyInstaller) - """ - with patch('openlp.core.common.sys') as mocked_sys: - # GIVEN: The sys module *with* a "frozen" attribute - mocked_sys.frozen = 1 - - # WHEN: We call _get_frozen_path() with two parameters - frozen_path = get_frozen_path('frozen', 'not frozen') - - # THEN: The frozen parameter is returned - self.assertEqual('frozen', frozen_path, 'Should return "frozen"') diff --git a/tests/functional/openlp_core_common/test_languages.py b/tests/functional/openlp_core_common/test_languages.py deleted file mode 100644 index c49233551..000000000 --- a/tests/functional/openlp_core_common/test_languages.py +++ /dev/null @@ -1,109 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# 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 # -############################################################################### -""" -Package to test the openlp.core.lib.languages package. -""" -from unittest import TestCase - -from openlp.core.common import languages - - -class TestLanguages(TestCase): - - def languages_type_test(self): - """ - Test the languages variable type - """ - - # GIVEN: The languages module - # WHEN: Accessing the languages variable - # THEN: It should be of type list - self.assertIsInstance(languages.languages, list, 'languages.languages should be of type list') - - def language_selection_languages_type_test(self): - """ - Test the selection of a language - """ - - # GIVEN: A list of languages from the languages module - # WHEN: Selecting the first item - language = languages.languages[0] - - # THEN: It should be an instance of the Language namedtuple - self.assertIsInstance(language, languages.Language) - self.assertEqual(language.id, 1) - self.assertEqual(language.name, '(Afan) Oromo') - self.assertEqual(language.code, 'om') - - def get_language_name_test(self): - """ - Test get_language() when supplied with a language name. - """ - - # GIVEN: A language name, in capitals - # WHEN: Calling get_language with it - language = languages.get_language('YORUBA') - - # THEN: The Language found using that name should be returned - self.assertIsInstance(language, languages.Language) - self.assertEqual(language.id, 137) - self.assertEqual(language.name, 'Yoruba') - self.assertEqual(language.code, 'yo') - - def get_language_code_test(self): - """ - Test get_language() when supplied with a language code. - """ - - # GIVEN: A language code in capitals - # WHEN: Calling get_language with it - language = languages.get_language('IA') - - # THEN: The Language found using that code should be returned - self.assertIsInstance(language, languages.Language) - self.assertEqual(language.id, 51) - self.assertEqual(language.name, 'Interlingua') - self.assertEqual(language.code, 'ia') - - def get_language_invalid_test(self): - """ - Test get_language() when supplied with a string which is not a valid language name or code. - """ - - # GIVEN: A language code - # WHEN: Calling get_language with it - language = languages.get_language('qwerty') - - # THEN: None should be returned - self.assertIsNone(language) - - def get_language_invalid__none_test(self): - """ - Test get_language() when supplied with a string which is not a valid language name or code. - """ - - # GIVEN: A language code - # WHEN: Calling get_language with it - language = languages.get_language(None) - - # THEN: None should be returned - self.assertIsNone(language) diff --git a/tests/functional/openlp_plugins/bibles/test_bibleimport.py b/tests/functional/openlp_plugins/bibles/test_bibleimport.py index b28ab0dfa..a75741d76 100644 --- a/tests/functional/openlp_plugins/bibles/test_bibleimport.py +++ b/tests/functional/openlp_plugins/bibles/test_bibleimport.py @@ -29,7 +29,7 @@ from unittest.mock import MagicMock, patch from lxml import etree, objectify from PyQt5.QtWidgets import QDialog -from openlp.core.common.languages import Language +from openlp.core.common.i18n import Language from openlp.core.lib.exceptions import ValidationError from openlp.plugins.bibles.lib.bibleimport import BibleImport from openlp.plugins.bibles.lib.db import BibleDB diff --git a/tests/functional/openlp_plugins/bibles/test_mediaitem.py b/tests/functional/openlp_plugins/bibles/test_mediaitem.py index a26802be7..ddc66254f 100755 --- a/tests/functional/openlp_plugins/bibles/test_mediaitem.py +++ b/tests/functional/openlp_plugins/bibles/test_mediaitem.py @@ -29,7 +29,7 @@ from PyQt5 import QtCore, QtWidgets from tests.helpers.testmixin import TestMixin -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.lib import MediaManagerItem from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem, BibleSearch, ResultsTab, SearchStatus, SearchTabs, \ get_reference_separators, VALID_TEXT_SEARCH diff --git a/tests/functional/openlp_plugins/bibles/test_opensongimport.py b/tests/functional/openlp_plugins/bibles/test_opensongimport.py index 4842e9c5f..5afd2d7d9 100644 --- a/tests/functional/openlp_plugins/bibles/test_opensongimport.py +++ b/tests/functional/openlp_plugins/bibles/test_opensongimport.py @@ -29,7 +29,7 @@ from unittest.mock import MagicMock, patch, call from lxml import objectify -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.bibles.lib.importers.opensong import OpenSongBible, get_text, parse_chapter_number from openlp.plugins.bibles.lib.bibleimport import BibleImport diff --git a/tests/functional/openlp_plugins/custom/test_mediaitem.py b/tests/functional/openlp_plugins/custom/test_mediaitem.py index a14ac1fb5..d75fa727d 100644 --- a/tests/functional/openlp_plugins/custom/test_mediaitem.py +++ b/tests/functional/openlp_plugins/custom/test_mediaitem.py @@ -27,7 +27,7 @@ from unittest.mock import patch, MagicMock from PyQt5 import QtCore -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.lib import ServiceItem, PluginStatus from openlp.plugins.custom.lib import CustomMediaItem diff --git a/tests/functional/openlp_plugins/images/test_imagetab.py b/tests/functional/openlp_plugins/images/test_imagetab.py index b6875fef4..1000001c2 100644 --- a/tests/functional/openlp_plugins/images/test_imagetab.py +++ b/tests/functional/openlp_plugins/images/test_imagetab.py @@ -23,13 +23,12 @@ This module contains tests for the lib submodule of the Images plugin. """ from unittest import TestCase -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock from PyQt5 import QtWidgets -from openlp.core.common import Registry, Settings -from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups -from openlp.plugins.images.lib.mediaitem import ImageMediaItem +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.plugins.images.lib import ImageTab from tests.helpers.testmixin import TestMixin diff --git a/tests/functional/openlp_plugins/images/test_lib.py b/tests/functional/openlp_plugins/images/test_lib.py index 650179e07..4dfef57da 100644 --- a/tests/functional/openlp_plugins/images/test_lib.py +++ b/tests/functional/openlp_plugins/images/test_lib.py @@ -27,7 +27,7 @@ from unittest.mock import ANY, MagicMock, patch from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups from openlp.plugins.images.lib.mediaitem import ImageMediaItem diff --git a/tests/functional/openlp_plugins/images/test_upgrade.py b/tests/functional/openlp_plugins/images/test_upgrade.py index f5ac85b5b..471c33609 100644 --- a/tests/functional/openlp_plugins/images/test_upgrade.py +++ b/tests/functional/openlp_plugins/images/test_upgrade.py @@ -28,8 +28,9 @@ from tempfile import mkdtemp from unittest import TestCase from unittest.mock import patch -from openlp.core.common import AppLocation, Settings +from openlp.core.common.applocation import AppLocation from openlp.core.common.path import Path +from openlp.core.common.settings import Settings from openlp.core.lib.db import Manager from openlp.plugins.images.lib import upgrade from openlp.plugins.images.lib.db import ImageFilenames, init_schema diff --git a/tests/functional/openlp_plugins/media/test_mediaitem.py b/tests/functional/openlp_plugins/media/test_mediaitem.py index d0ed7499f..4f753c237 100644 --- a/tests/functional/openlp_plugins/media/test_mediaitem.py +++ b/tests/functional/openlp_plugins/media/test_mediaitem.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore -from openlp.core import Settings +from openlp.core.common.settings import Settings from openlp.core.common.path import Path from openlp.plugins.media.lib.mediaitem import MediaMediaItem diff --git a/tests/functional/openlp_plugins/media/test_mediaplugin.py b/tests/functional/openlp_plugins/media/test_mediaplugin.py index b7cda1fd6..3ab5b3034 100644 --- a/tests/functional/openlp_plugins/media/test_mediaplugin.py +++ b/tests/functional/openlp_plugins/media/test_mediaplugin.py @@ -23,9 +23,9 @@ Test the media plugin """ from unittest import TestCase -from unittest.mock import MagicMock, patch +from unittest.mock import patch -from openlp.core import Registry +from openlp.core.common.registry import Registry from openlp.plugins.media.mediaplugin import MediaPlugin, process_check_binary from tests.helpers.testmixin import TestMixin diff --git a/tests/functional/openlp_plugins/presentations/test_impresscontroller.py b/tests/functional/openlp_plugins/presentations/test_impresscontroller.py index a792988e2..f08e0b8ac 100644 --- a/tests/functional/openlp_plugins/presentations/test_impresscontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_impresscontroller.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock import shutil from tempfile import mkdtemp -from openlp.core.common import Settings +from openlp.core.common.settings import Settings from openlp.core.common.path import Path from openlp.plugins.presentations.lib.impresscontroller import ImpressController, ImpressDocument, TextType from openlp.plugins.presentations.presentationplugin import __default_settings__ diff --git a/tests/functional/openlp_plugins/presentations/test_mediaitem.py b/tests/functional/openlp_plugins/presentations/test_mediaitem.py index 9ce0a5fdc..1116ce4cb 100644 --- a/tests/functional/openlp_plugins/presentations/test_mediaitem.py +++ b/tests/functional/openlp_plugins/presentations/test_mediaitem.py @@ -25,7 +25,7 @@ This module contains tests for the lib submodule of the Presentations plugin. from unittest import TestCase from unittest.mock import patch, MagicMock, call -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem diff --git a/tests/functional/openlp_plugins/presentations/test_messagelistener.py b/tests/functional/openlp_plugins/presentations/test_messagelistener.py index f4c268ee1..e38eeaf90 100644 --- a/tests/functional/openlp_plugins/presentations/test_messagelistener.py +++ b/tests/functional/openlp_plugins/presentations/test_messagelistener.py @@ -25,7 +25,7 @@ This module contains tests for the lib submodule of the Presentations plugin. from unittest import TestCase from unittest.mock import patch, MagicMock -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.presentations.lib.mediaitem import MessageListener, PresentationMediaItem from openlp.plugins.presentations.lib.messagelistener import Controller diff --git a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py index 25a8394f0..a7281e062 100644 --- a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py @@ -31,9 +31,9 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtGui from openlp.plugins.presentations.lib.pdfcontroller import PdfController, PdfDocument -from openlp.core.common import Settings +from openlp.core.common.settings import Settings from openlp.core.common.path import Path -from openlp.core.lib import ScreenList +from openlp.core.display.screens import ScreenList from tests.utils.constants import TEST_RESOURCES_PATH from tests.helpers.testmixin import TestMixin diff --git a/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py b/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py index c1deedfa0..5bab87b6a 100644 --- a/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py @@ -28,9 +28,10 @@ from unittest import TestCase from unittest.mock import patch, MagicMock from tempfile import mkdtemp -from openlp.plugins.presentations.lib.powerpointcontroller import PowerpointController, PowerpointDocument,\ +from openlp.core.common import is_win +from openlp.core.common.settings import Settings +from openlp.plugins.presentations.lib.powerpointcontroller import PowerpointController, PowerpointDocument, \ _get_text_from_shapes -from openlp.core.common import is_win, Settings from tests.helpers.testmixin import TestMixin from tests.utils.constants import TEST_RESOURCES_PATH diff --git a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py index bfa74a7fa..1245be274 100644 --- a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py @@ -24,7 +24,7 @@ This module contains tests for the pptviewcontroller module of the Presentations """ import shutil from tempfile import mkdtemp -from unittest import TestCase +from unittest import TestCase, skipIf from unittest.mock import MagicMock, patch from openlp.plugins.presentations.lib.pptviewcontroller import PptviewDocument, PptviewController @@ -34,9 +34,6 @@ from openlp.core.common.path import Path from tests.helpers.testmixin import TestMixin from tests.utils.constants import TEST_RESOURCES_PATH -if is_win(): - from ctypes import cdll - class TestPptviewController(TestCase, TestMixin): """ @@ -102,7 +99,6 @@ class TestPptviewDocument(TestCase): """ Set up the patches and mocks need for all tests. """ - self.os_isdir_patcher = patch('openlp.plugins.presentations.lib.pptviewcontroller.os.path.isdir') self.pptview_document_create_thumbnails_patcher = patch( 'openlp.plugins.presentations.lib.pptviewcontroller.PptviewDocument.create_thumbnails') self.pptview_document_stop_presentation_patcher = patch( @@ -113,7 +109,6 @@ class TestPptviewDocument(TestCase): 'openlp.plugins.presentations.lib.pptviewcontroller.PresentationDocument._setup') self.screen_list_patcher = patch('openlp.plugins.presentations.lib.pptviewcontroller.ScreenList') self.rect_patcher = MagicMock() - self.mock_os_isdir = self.os_isdir_patcher.start() self.mock_pptview_document_create_thumbnails = self.pptview_document_create_thumbnails_patcher.start() self.mock_pptview_document_stop_presentation = self.pptview_document_stop_presentation_patcher.start() self.mock_presentation_document_get_temp_folder = self.presentation_document_get_temp_folder_patcher.start() @@ -129,7 +124,6 @@ class TestPptviewDocument(TestCase): """ Stop the patches """ - self.os_isdir_patcher.stop() self.pptview_document_create_thumbnails_patcher.stop() self.pptview_document_stop_presentation_patcher.stop() self.presentation_document_get_temp_folder_patcher.stop() @@ -138,45 +132,40 @@ class TestPptviewDocument(TestCase): self.screen_list_patcher.stop() shutil.rmtree(self.temp_folder) - def test_load_presentation_succesfull(self): + @skipIf(not is_win(), 'Not Windows') + def test_load_presentation_succesful(self): """ Test the PptviewDocument.load_presentation() method when the PPT is successfully opened """ # GIVEN: A reset mocked_os - self.mock_os_isdir.reset() - - # WHEN: The temporary directory exists and OpenPPT returns successfully (not -1) - self.mock_os_isdir.return_value = True self.mock_controller.process.OpenPPT.return_value = 0 instance = PptviewDocument(self.mock_controller, self.mock_presentation) instance.file_path = 'test\path.ppt' - if is_win(): - result = instance.load_presentation() + # WHEN: The temporary directory exists and OpenPPT returns successfully (not -1) + result = instance.load_presentation() - # THEN: PptviewDocument.load_presentation should return True - self.assertTrue(result) + # THEN: PptviewDocument.load_presentation should return True + assert result is True - def test_load_presentation_un_succesfull(self): + @skipIf(not is_win(), 'Not Windows') + def test_load_presentation_un_succesful(self): """ Test the PptviewDocument.load_presentation() method when the temporary directory does not exist and the PPT is not successfully opened """ # GIVEN: A reset mock_os_isdir - self.mock_os_isdir.reset() + self.mock_controller.process.OpenPPT.return_value = -1 + instance = PptviewDocument(self.mock_controller, self.mock_presentation) + instance.file_path = 'test\path.ppt' # WHEN: The temporary directory does not exist and OpenPPT returns unsuccessfully (-1) - with patch('openlp.plugins.presentations.lib.pptviewcontroller.os.makedirs') as mock_makedirs: - self.mock_os_isdir.return_value = False - self.mock_controller.process.OpenPPT.return_value = -1 - instance = PptviewDocument(self.mock_controller, self.mock_presentation) - instance.file_path = 'test\path.ppt' - if is_win(): - result = instance.load_presentation() + with patch.object(instance, 'get_temp_folder') as mocked_get_folder: + mocked_get_folder.return_value = MagicMock(spec=Path) + result = instance.load_presentation() - # THEN: The temp folder should be created and PptviewDocument.load_presentation should return False - mock_makedirs.assert_called_once_with(self.temp_folder) - self.assertFalse(result) + # THEN: The temp folder should be created and PptviewDocument.load_presentation should return False + self.assertFalse(result) def test_create_titles_and_notes(self): """ @@ -202,7 +191,7 @@ class TestPptviewDocument(TestCase): # GIVEN: mocked PresentationController.save_titles_and_notes and an nonexistent file with patch('builtins.open') as mocked_open, \ patch.object(Path, 'exists') as mocked_path_exists, \ - patch('openlp.plugins.presentations.lib.presentationcontroller.check_directory_exists') as \ + patch('openlp.plugins.presentations.lib.presentationcontroller.create_paths') as \ mocked_dir_exists: mocked_path_exists.return_value = False mocked_dir_exists.return_value = False diff --git a/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py b/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py index 4cf2a1a01..30ab11561 100644 --- a/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py @@ -162,14 +162,14 @@ class TestPresentationDocument(TestCase): """ Set up the patches and mocks need for all tests. """ - self.check_directory_exists_patcher = \ - patch('openlp.plugins.presentations.lib.presentationcontroller.check_directory_exists') + self.create_paths_patcher = \ + patch('openlp.plugins.presentations.lib.presentationcontroller.create_paths') self.get_thumbnail_folder_patcher = \ patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') self._setup_patcher = \ patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument._setup') - self.mock_check_directory_exists = self.check_directory_exists_patcher.start() + self.mock_create_paths = self.create_paths_patcher.start() self.mock_get_thumbnail_folder = self.get_thumbnail_folder_patcher.start() self.mock_setup = self._setup_patcher.start() @@ -181,7 +181,7 @@ class TestPresentationDocument(TestCase): """ Stop the patches """ - self.check_directory_exists_patcher.stop() + self.create_paths_patcher.stop() self.get_thumbnail_folder_patcher.stop() self._setup_patcher.stop() @@ -204,13 +204,13 @@ class TestPresentationDocument(TestCase): """ self._setup_patcher.stop() - # GIVEN: A mocked controller, patched check_directory_exists and get_thumbnail_folder methods + # GIVEN: A mocked controller, patched create_paths and get_thumbnail_folder methods # WHEN: Creating an instance of PresentationDocument PresentationDocument(self.mock_controller, 'Name') - # THEN: check_directory_exists should have been called with 'returned/path/' - self.mock_check_directory_exists.assert_called_once_with(Path('returned', 'path/')) + # THEN: create_paths should have been called with 'returned/path/' + self.mock_create_paths.assert_called_once_with(Path('returned', 'path/')) self._setup_patcher.start() diff --git a/tests/functional/openlp_plugins/songs/test_editsongform.py b/tests/functional/openlp_plugins/songs/test_editsongform.py index bb5120f7d..5a5e861c7 100644 --- a/tests/functional/openlp_plugins/songs/test_editsongform.py +++ b/tests/functional/openlp_plugins/songs/test_editsongform.py @@ -27,10 +27,8 @@ from unittest.mock import patch, MagicMock from PyQt5 import QtCore -from openlp.core.common import Registry, Settings -from openlp.core.lib import ServiceItem +from openlp.core.common.registry import Registry from openlp.plugins.songs.forms.editsongform import EditSongForm -from openlp.plugins.songs.lib.db import AuthorType from tests.helpers.testmixin import TestMixin diff --git a/tests/functional/openlp_plugins/songs/test_editverseform.py b/tests/functional/openlp_plugins/songs/test_editverseform.py index 3e97abbb7..10b339dc2 100644 --- a/tests/functional/openlp_plugins/songs/test_editverseform.py +++ b/tests/functional/openlp_plugins/songs/test_editverseform.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock from PyQt5 import QtCore -from openlp.core.common import Settings +from openlp.core.common.settings import Settings from openlp.plugins.songs.forms.editverseform import EditVerseForm from tests.helpers.testmixin import TestMixin @@ -72,3 +72,31 @@ class TestEditVerseForm(TestCase, TestMixin): # THEN the verse number must not be changed self.assertEqual(3, self.edit_verse_form.verse_number_box.value(), 'The verse number should be 3') + + def test_on_divide_split_button_clicked(self): + """ + Test that divide adds text at the correct position + """ + # GIVEN some input values + self.edit_verse_form.verse_type_combo_box.currentIndex = MagicMock(return_value=4) + self.edit_verse_form.verse_text_edit.setPlainText('Text\n') + + # WHEN the method is called + self.edit_verse_form.on_forced_split_button_clicked() + # THEN the verse number must not be changed + self.assertEqual('[--}{--]\nText\n', self.edit_verse_form.verse_text_edit.toPlainText(), + 'The verse number should be [--}{--]\nText\n') + + def test_on_split_button_clicked(self): + """ + Test that divide adds text at the correct position + """ + # GIVEN some input values + self.edit_verse_form.verse_type_combo_box.currentIndex = MagicMock(return_value=4) + self.edit_verse_form.verse_text_edit.setPlainText('Text\n') + + # WHEN the method is called + self.edit_verse_form.on_overflow_split_button_clicked() + # THEN the verse number must not be changed + self.assertEqual('[---]\nText\n', self.edit_verse_form.verse_text_edit.toPlainText(), + 'The verse number should be [---]\nText\n') diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 2440c1271..ae9f873c5 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -26,7 +26,7 @@ import os from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.songs.lib.importers.easyworship import EasyWorshipSongImport, FieldDescEntry, FieldType TEST_PATH = os.path.abspath( diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 15c8e5ef0..530c1cf6b 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -27,7 +27,8 @@ from unittest.mock import MagicMock, patch, call from PyQt5 import QtCore -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.lib import ServiceItem from openlp.plugins.songs.lib.db import AuthorType, Song from openlp.plugins.songs.lib.mediaitem import SongMediaItem @@ -570,7 +571,6 @@ class TestMediaItem(TestCase, TestMixin): """ # GIVEN: A song media item, a keyword and some mocks keyword = 'Jesus' - mocked_song = MagicMock() mocked_or.side_effect = lambda a, b, c, d, e: ' '.join([a, b, c, d, e]) MockedSong.search_title.like.side_effect = lambda a: a MockedSong.search_lyrics.like.side_effect = lambda a: a diff --git a/tests/functional/openlp_plugins/songs/test_mediashout.py b/tests/functional/openlp_plugins/songs/test_mediashout.py index 2726f8f95..8fc452ea4 100644 --- a/tests/functional/openlp_plugins/songs/test_mediashout.py +++ b/tests/functional/openlp_plugins/songs/test_mediashout.py @@ -26,7 +26,7 @@ from collections import namedtuple from unittest import TestCase, skipUnless from unittest.mock import MagicMock, patch, call -from openlp.core.common import Registry +from openlp.core.common.registry import Registry try: from openlp.plugins.songs.lib.importers.mediashout import MediaShoutImport CAN_RUN_TESTS = True diff --git a/tests/functional/openlp_plugins/songs/test_openlpimporter.py b/tests/functional/openlp_plugins/songs/test_openlpimporter.py index 9a8f6b6a7..590d09b79 100644 --- a/tests/functional/openlp_plugins/songs/test_openlpimporter.py +++ b/tests/functional/openlp_plugins/songs/test_openlpimporter.py @@ -25,7 +25,7 @@ This module contains tests for the OpenLP song importer. from unittest import TestCase from unittest.mock import patch, MagicMock -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.songs.lib.importers.openlp import OpenLPSongImport diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsexport.py b/tests/functional/openlp_plugins/songs/test_openlyricsexport.py index 5335110d5..0fd4767db 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsexport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsexport.py @@ -27,7 +27,7 @@ from tempfile import mkdtemp from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path, rmtree from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index ac4e40665..088711fbc 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -29,8 +29,9 @@ from unittest.mock import MagicMock, patch from lxml import etree, objectify -from openlp.core.common import Registry, Settings from openlp.core.common.path import Path +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.plugins.songs.lib.importers.openlyrics import OpenLyricsImport from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics @@ -161,7 +162,7 @@ class TestOpenLyricsImport(TestCase, TestMixin): Test that _process_authors works """ # GIVEN: A OpenLyric XML with authors and a mocked out manager - with patch('openlp.plugins.songs.lib.openlyricsxml.Author') as mocked_author: + with patch('openlp.plugins.songs.lib.openlyricsxml.Author'): mocked_manager = MagicMock() mocked_manager.get_object_filtered.return_value = None ol = OpenLyrics(mocked_manager) diff --git a/tests/functional/openlp_plugins/songs/test_openoffice.py b/tests/functional/openlp_plugins/songs/test_openoffice.py index 3351123d7..4172a553c 100644 --- a/tests/functional/openlp_plugins/songs/test_openoffice.py +++ b/tests/functional/openlp_plugins/songs/test_openoffice.py @@ -25,7 +25,7 @@ This module contains tests for the OpenOffice/LibreOffice importer. from unittest import TestCase, SkipTest from unittest.mock import MagicMock, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry try: from openlp.plugins.songs.lib.importers.openoffice import OpenOfficeImport except ImportError: diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py index f84ddf724..a8582b3e4 100644 --- a/tests/functional/openlp_plugins/songs/test_opensongimport.py +++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py @@ -26,7 +26,7 @@ import os from unittest import TestCase from unittest.mock import patch, MagicMock -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.plugins.songs.lib.importers.opensong import OpenSongImport diff --git a/tests/functional/openlp_plugins/songs/test_opsproimport.py b/tests/functional/openlp_plugins/songs/test_opsproimport.py index 4558b74f2..8dac8eca4 100644 --- a/tests/functional/openlp_plugins/songs/test_opsproimport.py +++ b/tests/functional/openlp_plugins/songs/test_opsproimport.py @@ -28,7 +28,7 @@ from unittest import TestCase, skipUnless from unittest.mock import patch, MagicMock try: - from openlp.core.common import Registry + from openlp.core.common.registry import Registry from openlp.plugins.songs.lib.importers.opspro import OPSProImport CAN_RUN_TESTS = True except ImportError: diff --git a/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py b/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py index cd668f684..4df5806ae 100644 --- a/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py +++ b/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py @@ -23,7 +23,6 @@ The :mod:`powerpraiseimport` module provides the functionality for importing ProPresenter song files into the current installation database. """ - import os from openlp.core.common.path import Path diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index 0084fa688..165abd1a9 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -26,7 +26,7 @@ import os from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.plugins.songs.lib.importers.songbeamer import SongBeamerImport, SongBeamerTypes diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index 9a92e3788..191bc4e5a 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -30,11 +30,11 @@ from urllib.error import URLError from PyQt5 import QtWidgets -from openlp.core import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.plugins.songs.forms.songselectform import SongSelectForm, SearchWorker from openlp.plugins.songs.lib import Song -from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGIN_PAGE, LOGOUT_URL, BASE_URL +from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGOUT_URL, BASE_URL from tests.helpers.songfileimport import SongImportTestHelper from tests.helpers.testmixin import TestMixin @@ -308,7 +308,7 @@ class TestSongSelectImport(TestCase, TestMixin): importer.run_search = True # WHEN: The stop method is called - results = importer.stop() + importer.stop() # THEN: Searching should have stopped self.assertFalse(importer.run_search, 'Searching should have been stopped') diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 152048e24..02c365a4b 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -27,7 +27,7 @@ from unittest.mock import patch, MagicMock try: import pyodbc - from openlp.core.common import Registry + from openlp.core.common.registry import Registry from openlp.plugins.songs.lib.importers.worshipcenterpro import WorshipCenterProImport CAN_RUN_TESTS = True except ImportError: diff --git a/tests/functional/openlp_plugins/songs/test_zionworximport.py b/tests/functional/openlp_plugins/songs/test_zionworximport.py index 31bfc94ad..42991382e 100644 --- a/tests/functional/openlp_plugins/songs/test_zionworximport.py +++ b/tests/functional/openlp_plugins/songs/test_zionworximport.py @@ -26,7 +26,7 @@ import os from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.plugins.songs.lib.importers.zionworx import ZionWorxImport from openlp.plugins.songs.lib.importers.songimport import SongImport diff --git a/tests/functional/openlp_plugins/songusage/test_songusage.py b/tests/functional/openlp_plugins/songusage/test_songusage.py index 728ab9c47..92f972271 100644 --- a/tests/functional/openlp_plugins/songusage/test_songusage.py +++ b/tests/functional/openlp_plugins/songusage/test_songusage.py @@ -25,7 +25,7 @@ This module contains tests for the Songusage plugin. from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core import Registry +from openlp.core.common.registry import Registry from openlp.plugins.songusage.lib import upgrade from openlp.plugins.songusage.lib.db import init_schema from openlp.plugins.songusage.songusageplugin import SongUsagePlugin diff --git a/tests/functional/test_init.py b/tests/functional/test_init.py deleted file mode 100644 index 3f30b253e..000000000 --- a/tests/functional/test_init.py +++ /dev/null @@ -1,137 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# 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 # -############################################################################### -""" -Package to test the openlp.core.__init__ package. -""" -import os -from unittest import TestCase -from unittest.mock import MagicMock, patch - -from PyQt5 import QtCore, QtWidgets - -from openlp.core import OpenLP -from openlp.core.common import Settings - -from tests.helpers.testmixin import TestMixin - -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'resources')) - - -class TestInit(TestCase, TestMixin): - def setUp(self): - self.build_settings() - with patch('openlp.core.common.OpenLPMixin.__init__') as constructor: - constructor.return_value = None - self.openlp = OpenLP(list()) - - def tearDown(self): - self.destroy_settings() - del self.openlp - - def test_event(self): - """ - Test the reimplemented event method - """ - # GIVEN: A file path and a QEvent. - file_path = os.path.join(TEST_PATH, 'church.jpg') - mocked_file_method = MagicMock(return_value=file_path) - event = QtCore.QEvent(QtCore.QEvent.FileOpen) - event.file = mocked_file_method - - # WHEN: Call the vent method. - result = self.openlp.event(event) - - # THEN: The path should be inserted. - self.assertTrue(result, "The method should have returned True.") - mocked_file_method.assert_called_once_with() - self.assertEqual(self.openlp.args[0], file_path, "The path should be in args.") - - @patch('openlp.core.is_macosx') - def test_application_activate_event(self, mocked_is_macosx): - """ - Test that clicking on the dock icon on Mac OS X restores the main window if it is minimized - """ - # GIVEN: Mac OS X and an ApplicationActivate event - mocked_is_macosx.return_value = True - event = MagicMock() - event.type.return_value = QtCore.QEvent.ApplicationActivate - mocked_main_window = MagicMock() - self.openlp.main_window = mocked_main_window - - # WHEN: The icon in the dock is clicked - result = self.openlp.event(event) - - # THEN: - self.assertTrue(result, "The method should have returned True.") - # self.assertFalse(self.openlp.main_window.isMinimized()) - - def test_backup_on_upgrade_first_install(self): - """ - Test that we don't try to backup on a new install - """ - # GIVEN: Mocked data version and OpenLP version which are the same - old_install = False - MOCKED_VERSION = { - 'full': '2.2.0-bzr000', - 'version': '2.2.0', - 'build': 'bzr000' - } - Settings().setValue('core/application version', '2.2.0') - with patch('openlp.core.get_version') as mocked_get_version,\ - patch('openlp.core.QtWidgets.QMessageBox.question') as mocked_question: - mocked_get_version.return_value = MOCKED_VERSION - mocked_question.return_value = QtWidgets.QMessageBox.No - - # WHEN: We check if a backup should be created - self.openlp.backup_on_upgrade(old_install, False) - - # THEN: It should not ask if we want to create a backup - self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be the same!') - self.assertEqual(mocked_question.call_count, 0, 'No question should have been asked!') - - def test_backup_on_upgrade(self): - """ - Test that we try to backup on a new install - """ - # GIVEN: Mocked data version and OpenLP version which are different - old_install = True - MOCKED_VERSION = { - 'full': '2.2.0-bzr000', - 'version': '2.2.0', - 'build': 'bzr000' - } - Settings().setValue('core/application version', '2.0.5') - self.openlp.splash = MagicMock() - self.openlp.splash.isVisible.return_value = True - with patch('openlp.core.get_version') as mocked_get_version, \ - patch('openlp.core.QtWidgets.QMessageBox.question') as mocked_question: - mocked_get_version.return_value = MOCKED_VERSION - mocked_question.return_value = QtWidgets.QMessageBox.No - - # WHEN: We check if a backup should be created - self.openlp.backup_on_upgrade(old_install, True) - - # THEN: It should ask if we want to create a backup - self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be upgraded!') - self.assertEqual(mocked_question.call_count, 1, 'A question should have been asked!') - self.openlp.splash.hide.assert_called_once_with() - self.openlp.splash.show.assert_called_once_with() diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 7b0f0cb14..2128d28f9 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -28,7 +28,7 @@ import logging from unittest import TestCase from unittest.mock import MagicMock, patch, call -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.songs.lib.importers.opensong import OpenSongImport log = logging.getLogger(__name__) diff --git a/tests/helpers/testmixin.py b/tests/helpers/testmixin.py index 10a3b71a6..facd949f9 100644 --- a/tests/helpers/testmixin.py +++ b/tests/helpers/testmixin.py @@ -26,7 +26,7 @@ import os from tempfile import mkstemp from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Settings +from openlp.core.common.settings import Settings class TestMixin(object): diff --git a/tests/interfaces/openlp_core_lib/test_pluginmanager.py b/tests/interfaces/openlp_core_lib/test_pluginmanager.py index 3dbae0e03..2e7d979a6 100644 --- a/tests/interfaces/openlp_core_lib/test_pluginmanager.py +++ b/tests/interfaces/openlp_core_lib/test_pluginmanager.py @@ -31,7 +31,8 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtWidgets -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.common.path import Path from openlp.core.lib.pluginmanager import PluginManager diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core_lib/test_searchedit.py index 4f4c4d68c..d4751ddf0 100644 --- a/tests/interfaces/openlp_core_lib/test_searchedit.py +++ b/tests/interfaces/openlp_core_lib/test_searchedit.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, call, patch from PyQt5 import QtCore, QtGui, QtTest, QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.lib.searchedit import SearchEdit from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py index c2d5e192f..cb074af1a 100644 --- a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py +++ b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py @@ -20,14 +20,14 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ - Package to test the openlp.core.ui package. +Package to test the openlp.core.ui package. """ from unittest import TestCase from unittest.mock import MagicMock, patch from PyQt5 import QtTest, QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui import filerenameform from tests.helpers.testmixin import TestMixin @@ -57,7 +57,7 @@ class TestStartFileRenameForm(TestCase, TestMixin): Test the windowTitle of the FileRenameDialog """ # GIVEN: A mocked QDialog.exec() method - with patch('PyQt5.QtWidgets.QDialog.exec') as mocked_exec: + with patch('PyQt5.QtWidgets.QDialog.exec'): # WHEN: The form is executed with no args self.form.exec() diff --git a/tests/interfaces/openlp_core_ui/test_mainwindow.py b/tests/interfaces/openlp_core_ui/test_mainwindow.py index 7c5d96ab3..4a8ced265 100644 --- a/tests/interfaces/openlp_core_ui/test_mainwindow.py +++ b/tests/interfaces/openlp_core_ui/test_mainwindow.py @@ -25,7 +25,7 @@ Package to test the openlp.core.ui.mainwindow package. from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui.mainwindow import MainWindow from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_core_ui/test_projectoreditform.py b/tests/interfaces/openlp_core_ui/test_projectoreditform.py index b4a476875..fc261d9f5 100644 --- a/tests/interfaces/openlp_core_ui/test_projectoreditform.py +++ b/tests/interfaces/openlp_core_ui/test_projectoreditform.py @@ -27,7 +27,7 @@ import os from unittest import TestCase from unittest.mock import patch -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.lib.projector.db import Projector, ProjectorDB from openlp.core.ui import ProjectorEditForm diff --git a/tests/interfaces/openlp_core_ui/test_projectormanager.py b/tests/interfaces/openlp_core_ui/test_projectormanager.py index 96bbe4a4c..ff95c4276 100644 --- a/tests/interfaces/openlp_core_ui/test_projectormanager.py +++ b/tests/interfaces/openlp_core_ui/test_projectormanager.py @@ -26,12 +26,12 @@ import os from unittest import TestCase from unittest.mock import patch, MagicMock -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry from openlp.core.ui import ProjectorManager, ProjectorEditForm -from openlp.core.lib.projector.db import Projector, ProjectorDB +from openlp.core.lib.projector.db import ProjectorDB from tests.helpers.testmixin import TestMixin -from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA +from tests.resources.projector.data import TEST_DB class TestProjectorManager(TestCase, TestMixin): diff --git a/tests/interfaces/openlp_core_ui/test_projectorsourceform.py b/tests/interfaces/openlp_core_ui/test_projectorsourceform.py index 27fa04348..4b9e2f402 100644 --- a/tests/interfaces/openlp_core_ui/test_projectorsourceform.py +++ b/tests/interfaces/openlp_core_ui/test_projectorsourceform.py @@ -31,7 +31,7 @@ from unittest.mock import patch from PyQt5.QtWidgets import QDialog -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry from openlp.core.lib.projector.db import ProjectorDB, Projector from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES, PJLINK_DEFAULT_SOURCES from openlp.core.ui.projector.sourceselectform import source_group, SourceSelectSingle diff --git a/tests/interfaces/openlp_core_ui/test_servicemanager.py b/tests/interfaces/openlp_core_ui/test_servicemanager.py index 3cac77f15..1deb1a97f 100644 --- a/tests/interfaces/openlp_core_ui/test_servicemanager.py +++ b/tests/interfaces/openlp_core_ui/test_servicemanager.py @@ -25,11 +25,10 @@ from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry -from openlp.core.lib import ScreenList, ServiceItem, ItemCapabilities +from openlp.core.common.registry import Registry +from openlp.core.display.screens import ScreenList +from openlp.core.lib import ServiceItem, ItemCapabilities from openlp.core.ui.mainwindow import MainWindow -from openlp.core.ui.servicemanager import ServiceManagerList -from openlp.core.lib.serviceitem import ServiceItem from tests.helpers.testmixin import TestMixin @@ -48,18 +47,18 @@ class TestServiceManager(TestCase, TestMixin): ScreenList.create(self.app.desktop()) Registry().register('application', MagicMock()) # Mock classes and methods used by mainwindow. - with patch('openlp.core.ui.mainwindow.SettingsForm') as mocked_settings_form, \ - patch('openlp.core.ui.mainwindow.ImageManager') as mocked_image_manager, \ - patch('openlp.core.ui.mainwindow.LiveController') as mocked_live_controller, \ - patch('openlp.core.ui.mainwindow.PreviewController') as mocked_preview_controller, \ - patch('openlp.core.ui.mainwindow.OpenLPDockWidget') as mocked_dock_widget, \ - patch('openlp.core.ui.mainwindow.QtWidgets.QToolBox') as mocked_q_tool_box_class, \ - patch('openlp.core.ui.mainwindow.QtWidgets.QMainWindow.addDockWidget') as mocked_add_dock_method, \ - patch('openlp.core.ui.mainwindow.ThemeManager') as mocked_theme_manager, \ - patch('openlp.core.ui.mainwindow.ProjectorManager') as mocked_projector_manager, \ - patch('openlp.core.ui.mainwindow.Renderer') as mocked_renderer, \ - patch('openlp.core.ui.mainwindow.websockets.WebSocketServer') as mocked_websocketserver, \ - patch('openlp.core.ui.mainwindow.server.HttpServer') as mocked_httpserver: + with patch('openlp.core.ui.mainwindow.SettingsForm'), \ + patch('openlp.core.ui.mainwindow.ImageManager'), \ + patch('openlp.core.ui.mainwindow.LiveController'), \ + patch('openlp.core.ui.mainwindow.PreviewController'), \ + patch('openlp.core.ui.mainwindow.OpenLPDockWidget'), \ + patch('openlp.core.ui.mainwindow.QtWidgets.QToolBox'), \ + patch('openlp.core.ui.mainwindow.QtWidgets.QMainWindow.addDockWidget'), \ + patch('openlp.core.ui.mainwindow.ThemeManager'), \ + patch('openlp.core.ui.mainwindow.ProjectorManager'), \ + patch('openlp.core.ui.mainwindow.Renderer'), \ + patch('openlp.core.ui.mainwindow.websockets.WebSocketServer'), \ + patch('openlp.core.ui.mainwindow.server.HttpServer'): self.main_window = MainWindow() self.service_manager = Registry().get('service_manager') diff --git a/tests/interfaces/openlp_core_ui/test_servicenotedialog.py b/tests/interfaces/openlp_core_ui/test_servicenotedialog.py index 82d56bca9..94e34be44 100644 --- a/tests/interfaces/openlp_core_ui/test_servicenotedialog.py +++ b/tests/interfaces/openlp_core_ui/test_servicenotedialog.py @@ -27,7 +27,7 @@ from unittest.mock import patch from PyQt5 import QtCore, QtTest, QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui import servicenoteform from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_core_ui/test_settings_form.py b/tests/interfaces/openlp_core_ui/test_settings_form.py index d78de3eb5..f4ec5ccbc 100644 --- a/tests/interfaces/openlp_core_ui/test_settings_form.py +++ b/tests/interfaces/openlp_core_ui/test_settings_form.py @@ -27,9 +27,9 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtTest -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui import settingsform -from openlp.core.lib import ScreenList +from openlp.core.display.screens import ScreenList from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py index dfea62b0c..a95390236 100644 --- a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py +++ b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py @@ -25,9 +25,9 @@ Package to test the openlp.core.ui.shortcutform package. from unittest import TestCase from unittest.mock import MagicMock, patch -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui.shortcutlistform import ShortcutListForm from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_core_ui/test_starttimedialog.py b/tests/interfaces/openlp_core_ui/test_starttimedialog.py index d754d9e51..5d0140e0d 100644 --- a/tests/interfaces/openlp_core_ui/test_starttimedialog.py +++ b/tests/interfaces/openlp_core_ui/test_starttimedialog.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtTest, QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui import starttimeform from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_core_ui/test_thememanager.py b/tests/interfaces/openlp_core_ui/test_thememanager.py index 0797aa9b8..7f3927cf5 100644 --- a/tests/interfaces/openlp_core_ui/test_thememanager.py +++ b/tests/interfaces/openlp_core_ui/test_thememanager.py @@ -25,7 +25,8 @@ Interface tests to test the themeManager class and related methods. from unittest import TestCase from unittest.mock import patch, MagicMock -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.common.path import Path from openlp.core.ui import ThemeManager @@ -57,57 +58,38 @@ class TestThemeManager(TestCase, TestMixin): Test the thememanager initialise - basic test """ # GIVEN: A new a call to initialise + self.theme_manager.setup_ui = MagicMock() self.theme_manager.build_theme_path = MagicMock() self.theme_manager.load_first_time_themes = MagicMock() + self.theme_manager.upgrade_themes = MagicMock() Settings().setValue('themes/global theme', 'my_theme') # WHEN: the initialisation is run self.theme_manager.bootstrap_initialise() # THEN: - self.assertEqual(1, self.theme_manager.build_theme_path.call_count, - 'The function build_theme_path should have been called') - self.assertEqual(1, self.theme_manager.load_first_time_themes.call_count, - 'The function load_first_time_themes should have been called only once') - self.assertEqual(self.theme_manager.global_theme, 'my_theme', - 'The global theme should have been set to my_theme') + self.theme_manager.setup_ui.assert_called_once_with(self.theme_manager) + assert self.theme_manager.global_theme == 'my_theme' + self.theme_manager.build_theme_path.assert_called_once_with() + self.theme_manager.load_first_time_themes.assert_called_once_with() + self.theme_manager.upgrade_themes.assert_called_once_with() - def test_build_theme_path(self): + @patch('openlp.core.ui.thememanager.create_paths') + @patch('openlp.core.ui.thememanager.AppLocation.get_section_data_path') + def test_build_theme_path(self, mocked_get_section_data_path, mocked_create_paths): """ - Test the thememanager build_theme_path - basic test + Test the thememanager build_theme_path """ - # GIVEN: A new a call to initialise - with patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists: - # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() - mocked_check_directory_exists.return_value = True - Settings().setValue('themes/global theme', 'my_theme') - - self.theme_manager.theme_form = MagicMock() - self.theme_manager.load_first_time_themes = MagicMock() + # GIVEN: A mocked out AppLocation.get_directory() and mocked create_paths + mocked_get_section_data_path.return_value = Path('tests/my_theme') # WHEN: the build_theme_path is run self.theme_manager.build_theme_path() - # THEN: - assert self.theme_manager.thumb_path.startswith(self.theme_manager.path) is True, \ - 'The thumb path and the main path should start with the same value' - - def test_build_theme_path(self): - """ - Test the thememanager build_theme_path - basic test - """ - # GIVEN: A new a call to initialise - with patch('openlp.core.common.AppLocation.get_section_data_path', return_value=Path('test/path')): - Settings().setValue('themes/global theme', 'my_theme') - - self.theme_manager.theme_form = MagicMock() - self.theme_manager.load_first_time_themes = MagicMock() - - # WHEN: the build_theme_path is run - self.theme_manager.build_theme_path() - - # THEN: The thumbnail path should be a sub path of the test path - self.assertEqual(self.theme_manager.thumb_path, Path('test/path/thumbnails')) + # THEN: The theme path and the thumb path should be correct + assert self.theme_manager.theme_path == Path('tests/my_theme') + assert self.theme_manager.thumb_path == Path('tests/my_theme/thumbnails') + mocked_create_paths.assert_called_once_with(Path('tests/my_theme'), Path('tests/my_theme/thumbnails')) def test_click_on_new_theme(self): """ diff --git a/tests/interfaces/openlp_core_ui_lib/test_historycombobox.py b/tests/interfaces/openlp_core_ui_lib/test_historycombobox.py index 8dfe50229..96c835398 100644 --- a/tests/interfaces/openlp_core_ui_lib/test_historycombobox.py +++ b/tests/interfaces/openlp_core_ui_lib/test_historycombobox.py @@ -27,7 +27,7 @@ from unittest import TestCase from PyQt5 import QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.ui.lib.historycombobox import HistoryComboBox from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_core_ui_lib/test_listpreviewwidget.py b/tests/interfaces/openlp_core_ui_lib/test_listpreviewwidget.py index 87eb9660d..e22f9e9a4 100644 --- a/tests/interfaces/openlp_core_ui_lib/test_listpreviewwidget.py +++ b/tests/interfaces/openlp_core_ui_lib/test_listpreviewwidget.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtGui, QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.core.lib import ServiceItem from openlp.core.ui.lib import ListWidgetWithDnD, ListPreviewWidget diff --git a/tests/interfaces/openlp_plugins/bibles/forms/test_bibleimportform.py b/tests/interfaces/openlp_plugins/bibles/forms/test_bibleimportform.py index a7bf3c562..cbbef1372 100644 --- a/tests/interfaces/openlp_plugins/bibles/forms/test_bibleimportform.py +++ b/tests/interfaces/openlp_plugins/bibles/forms/test_bibleimportform.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.bibles.forms.bibleimportform import BibleImportForm, PYSWORD_AVAILABLE from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py index b896cd9e1..1ec6cb8ba 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py @@ -26,7 +26,7 @@ import os from unittest import TestCase, skipIf from unittest.mock import MagicMock -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.bibles.lib.importers.http import BGExtract, CWExtract, BSExtract diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_manager.py b/tests/interfaces/openlp_plugins/bibles/test_lib_manager.py index 663c16b0c..45e68a572 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_manager.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_manager.py @@ -25,7 +25,8 @@ Functional tests to test the Bible Manager class and related methods. from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.plugins.bibles.lib import BibleManager, LanguageSelection from tests.utils.constants import TEST_RESOURCES_PATH @@ -54,13 +55,13 @@ class TestBibleManager(TestCase, TestMixin): } Settings().extend_default_settings(bible_settings) with patch('openlp.core.common.applocation.Settings') as mocked_class, \ - patch('openlp.core.common.AppLocation.get_section_data_path') as mocked_get_section_data_path, \ - patch('openlp.core.common.AppLocation.get_files') as mocked_get_files: + patch('openlp.core.common.applocation.AppLocation.get_section_data_path') as mocked_get_data_path, \ + patch('openlp.core.common.applocation.AppLocation.get_files') as mocked_get_files: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_files() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False mocked_get_files.return_value = ["tests.sqlite"] - mocked_get_section_data_path.return_value = TEST_RESOURCES_PATH + "/bibles" + mocked_get_data_path.return_value = TEST_RESOURCES_PATH + "/bibles" self.manager = BibleManager(MagicMock()) def tearDown(self): diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py index eec46c762..18f5f5762 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -25,7 +25,8 @@ This module contains tests for the lib submodule of the Bibles plugin. from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.plugins.bibles.lib import BibleManager, parse_reference, LanguageSelection from tests.utils.constants import TEST_RESOURCES_PATH @@ -54,13 +55,13 @@ class TestBibleManager(TestCase, TestMixin): } Settings().extend_default_settings(bible_settings) with patch('openlp.core.common.applocation.Settings') as mocked_class, \ - patch('openlp.core.common.AppLocation.get_section_data_path') as mocked_get_section_data_path, \ - patch('openlp.core.common.AppLocation.get_files') as mocked_get_files: + patch('openlp.core.common.applocation.AppLocation.get_section_data_path') as mocked_get_data_path, \ + patch('openlp.core.common.applocation.AppLocation.get_files') as mocked_get_files: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_files() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False mocked_get_files.return_value = ["tests.sqlite"] - mocked_get_section_data_path.return_value = TEST_RESOURCES_PATH + "/bibles" + mocked_get_data_path.return_value = TEST_RESOURCES_PATH + "/bibles" self.manager = BibleManager(MagicMock()) def tearDown(self): diff --git a/tests/interfaces/openlp_plugins/custom/forms/test_customform.py b/tests/interfaces/openlp_plugins/custom/forms/test_customform.py index 73d8673db..5f486a219 100644 --- a/tests/interfaces/openlp_plugins/custom/forms/test_customform.py +++ b/tests/interfaces/openlp_plugins/custom/forms/test_customform.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtTest, QtCore, QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.custom.lib.mediaitem import CustomMediaItem from openlp.plugins.custom.forms.editcustomform import EditCustomForm diff --git a/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py b/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py index 5284fdde5..97d9bb652 100644 --- a/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py +++ b/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.custom.forms.editcustomslideform import EditCustomSlideForm from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py b/tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py index 4b06f6b03..a49a35d48 100644 --- a/tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py +++ b/tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py @@ -33,7 +33,7 @@ if os.name == 'nt' and not get_vlc(): from PyQt5 import QtTest, QtCore, QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.media.forms.mediaclipselectorform import MediaClipSelectorForm from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py b/tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py index ca6f976f2..a2de38693 100644 --- a/tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py +++ b/tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py @@ -27,7 +27,7 @@ from unittest.mock import patch from PyQt5 import QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.songs.forms.authorsform import AuthorsForm from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py b/tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py index e7d8308fa..446368373 100644 --- a/tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py +++ b/tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py @@ -27,8 +27,9 @@ from unittest.mock import MagicMock from PyQt5 import QtWidgets -from openlp.core.common import Registry, Settings -from openlp.core.common.uistrings import UiStrings +from openlp.core.common.i18n import UiStrings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.plugins.songs.forms.editsongform import EditSongForm from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py b/tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py index 78c5c3016..d12305089 100644 --- a/tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py +++ b/tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py @@ -26,7 +26,8 @@ from unittest import TestCase from PyQt5 import QtCore, QtTest, QtWidgets -from openlp.core.common import Registry, Settings +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.plugins.songs.forms.editverseform import EditVerseForm from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py b/tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py index 09c12c136..d9786db7a 100644 --- a/tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py +++ b/tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py @@ -27,7 +27,8 @@ from unittest.mock import MagicMock, patch, call from PyQt5 import QtCore, QtWidgets -from openlp.core.common import Registry, UiStrings +from openlp.core.common.i18n import UiStrings +from openlp.core.common.registry import Registry from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm from tests.helpers.testmixin import TestMixin diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_topicsform.py b/tests/interfaces/openlp_plugins/songs/forms/test_topicsform.py index 773668a7c..07808339f 100644 --- a/tests/interfaces/openlp_plugins/songs/forms/test_topicsform.py +++ b/tests/interfaces/openlp_plugins/songs/forms/test_topicsform.py @@ -26,7 +26,7 @@ from unittest import TestCase from PyQt5 import QtWidgets -from openlp.core.common import Registry +from openlp.core.common.registry import Registry from openlp.plugins.songs.forms.topicsform import TopicsForm from tests.helpers.testmixin import TestMixin