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..9bccc526f 100755
--- a/openlp.py
+++ b/openlp.py
@@ -27,16 +27,27 @@ 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.
"""
+ faulthandler.enable()
+ 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..782940f2d 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 LogMixin, RegistryProperties
+from openlp.core.common.path import create_paths
+from openlp.core.common.registry import Registry, RegistryBase
+from openlp.core.common.settings import Settings
+from openlp.core.common.i18n import translate
log = logging.getLogger(__name__)
@@ -73,7 +73,7 @@ class HttpWorker(QtCore.QObject):
pass
-class HttpServer(RegistryMixin, RegistryProperties, OpenLPMixin):
+class HttpServer(RegistryBase, RegistryProperties, LogMixin):
"""
Wrapper round a server instance
"""
@@ -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..d2d36f60a 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.mixins 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..90dca8208 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 LogMixin, RegistryProperties
+from openlp.core.common.registry import Registry
+from openlp.core.common.settings import Settings
log = logging.getLogger(__name__)
@@ -61,7 +61,7 @@ class WebSocketWorker(QtCore.QObject):
self.ws_server.stop = True
-class WebSocketServer(RegistryProperties, OpenLPMixin):
+class WebSocketServer(RegistryProperties, LogMixin):
"""
Wrapper round a server instance
"""
diff --git a/openlp/core/app.py b/openlp/core/app.py
new file mode 100644
index 000000000..19943e3f0
--- /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 LogMixin
+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(QtWidgets.QApplication, LogMixin):
+ """
+ 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 a0c927666..279c49002 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 = 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/registryproperties.py b/openlp/core/common/mixins.py
similarity index 69%
rename from openlp/core/common/registryproperties.py
rename to openlp/core/common/mixins.py
index 29bd3b2ce..1bc6907a0 100644
--- a/openlp/core/common/registryproperties.py
+++ b/openlp/core/common/mixins.py
@@ -20,16 +20,87 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
-Provide Registry values for adding to classes
+Provide Error Handling and login Services
"""
-from openlp.core.common import Registry, is_win
+import logging
+import inspect
+
+from openlp.core.common import is_win, trace_error_handler
+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']
+
+
+class LogMixin(object):
+ """
+ Base Calling object for OpenLP classes.
+ """
+ @property
+ def logger(self):
+ if hasattr(self, '_logger') and self._logger:
+ return self._logger
+ else:
+ self._logger = logging.getLogger("%s.%s" % (self.__module__, self.__class__.__name__))
+ if self._logger.getEffectiveLevel() == logging.DEBUG:
+ for name, m in inspect.getmembers(self, inspect.ismethod):
+ if name not in DO_NOT_TRACE_EVENTS:
+ if not name.startswith("_") and not name.startswith("log"):
+ setattr(self, name, self.logging_wrapper(m, self))
+ return self._logger
+
+ def logging_wrapper(self, func, parent):
+ """
+ Code to added debug wrapper to work on called functions within a decorated class.
+ """
+ def wrapped(*args, **kwargs):
+ parent.logger.debug("Entering {function}".format(function=func.__name__))
+ try:
+ return func(*args, **kwargs)
+ except Exception as e:
+ if parent.logger.getEffectiveLevel() <= logging.ERROR:
+ parent.logger.error('Exception in {function} : {error}'.format(function=func.__name__,
+ error=e))
+ raise e
+ return wrapped
+
+ def log_debug(self, message):
+ """
+ Common log debug handler
+ """
+ self.logger.debug(message)
+
+ def log_info(self, message):
+ """
+ Common log info handler
+ """
+ self.logger.info(message)
+
+ def log_warning(self, message):
+ """
+ Common log warning handler
+ """
+ self.logger.warning(message)
+
+ def log_error(self, message):
+ """
+ Common log error handler which prints the calling path
+ """
+ trace_error_handler(self.logger)
+ self.logger.error(message)
+
+ def log_exception(self, message):
+ """
+ Common log exception handler which prints the calling path
+ """
+ trace_error_handler(self.logger)
+ self.logger.exception(message)
class RegistryProperties(object):
"""
This adds registry components to classes to use at run time.
"""
-
@property
def application(self):
"""
diff --git a/openlp/core/common/openlpmixin.py b/openlp/core/common/openlpmixin.py
deleted file mode 100644
index 0e63a339c..000000000
--- a/openlp/core/common/openlpmixin.py
+++ /dev/null
@@ -1,92 +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 Error Handling and login Services
-"""
-import logging
-import inspect
-
-from openlp.core.common import trace_error_handler
-
-DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent', 'drag_enter_event', 'drop_event', 'on_controller_size_changed',
- 'preview_size_changed', 'resizeEvent']
-
-
-class OpenLPMixin(object):
- """
- Base Calling object for OpenLP classes.
- """
- def __init__(self, *args, **kwargs):
- super(OpenLPMixin, self).__init__(*args, **kwargs)
- self.logger = logging.getLogger("%s.%s" % (self.__module__, self.__class__.__name__))
- if self.logger.getEffectiveLevel() == logging.DEBUG:
- for name, m in inspect.getmembers(self, inspect.ismethod):
- if name not in DO_NOT_TRACE_EVENTS:
- if not name.startswith("_") and not name.startswith("log"):
- setattr(self, name, self.logging_wrapper(m, self))
-
- def logging_wrapper(self, func, parent):
- """
- Code to added debug wrapper to work on called functions within a decorated class.
- """
- def wrapped(*args, **kwargs):
- parent.logger.debug("Entering {function}".format(function=func.__name__))
- try:
- return func(*args, **kwargs)
- except Exception as e:
- if parent.logger.getEffectiveLevel() <= logging.ERROR:
- parent.logger.error('Exception in {function} : {error}'.format(function=func.__name__,
- error=e))
- raise e
- return wrapped
-
- def log_debug(self, message):
- """
- Common log debug handler
- """
- self.logger.debug(message)
-
- def log_info(self, message):
- """
- Common log info handler
- """
- self.logger.info(message)
-
- def log_warning(self, message):
- """
- Common log warning handler
- """
- self.logger.warning(message)
-
- def log_error(self, message):
- """
- Common log error handler which prints the calling path
- """
- trace_error_handler(self.logger)
- self.logger.error(message)
-
- def log_exception(self, message):
- """
- Common log exception handler which prints the calling path
- """
- trace_error_handler(self.logger)
- self.logger.exception(message)
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 1894ac458..252274d4d 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 de_hump, trace_error_handler
log = logging.getLogger(__name__)
@@ -61,6 +61,15 @@ class Registry(object):
registry.initialising = True
return registry
+ @classmethod
+ def destroy(cls):
+ """
+ Destroy the Registry.
+ """
+ if cls.__instance__.running_under_test:
+ del cls.__instance__
+ cls.__instance__ = None
+
def get(self, key):
"""
Extracts the registry value from the list based on the key passed in
@@ -119,7 +128,7 @@ class Registry(object):
:param event: The function description..
:param function: The function to be called when the event happens.
"""
- if event in self.functions_list:
+ if event in self.functions_list and function in self.functions_list[event]:
self.functions_list[event].remove(function)
def execute(self, event, *args, **kwargs):
@@ -176,3 +185,32 @@ class Registry(object):
"""
if key in self.working_flags:
del self.working_flags[key]
+
+
+class RegistryBase(object):
+ """
+ This adds registry components to classes to use at run time.
+ """
+ def __init__(self, *args, **kwargs):
+ """
+ Register the class and bootstrap hooks.
+ """
+ try:
+ super().__init__(*args, **kwargs)
+ except TypeError:
+ super().__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/settings.py b/openlp/core/common/settings.py
index 6f681b7d5..aa0e4a6f6 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)]),
@@ -304,6 +292,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):
@@ -615,11 +604,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 69cedce50..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', 'Overflow &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 944ca6fe4..9dd7f3d1f 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 LogMixin, RegistryProperties
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, RegistryBase
+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' \
@@ -43,7 +46,7 @@ VERSE_FOR_LINE_COUNT = '\n'.join(map(str, range(100)))
FOOTER = ['Arky Arky (Unknown)', 'Public Domain', 'CCLI 123456']
-class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
+class Renderer(RegistryBase, LogMixin, RegistryProperties):
"""
Class to pull all Renderer interactions into one place. The plugins will call helper methods to do the rendering but
this class will provide display defense code.
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..9e3c5aff9 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__)
@@ -348,6 +350,7 @@ class Manager(object):
resulting in the plugin_name being used.
:param upgrade_mod: The upgrade_schema function for this database
"""
+ super().__init__()
self.is_dirty = False
self.session = None
self.db_url = None
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/tests/functional/openlp_core_api/__init__.py b/openlp/core/lib/json/__init__.py
similarity index 100%
rename from tests/functional/openlp_core_api/__init__.py
rename to openlp/core/lib/json/__init__.py
diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py
index ce106854a..c650ad80e 100644
--- a/openlp/core/lib/mediamanageritem.py
+++ b/openlp/core/lib/mediamanageritem.py
@@ -28,14 +28,17 @@ 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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+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
-from openlp.core.ui.lib.filedialog import FileDialog
-from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD
-from openlp.core.ui.lib.toolbar import OpenLPToolbar
+from openlp.core.widgets.dialogs import FileDialog
+from openlp.core.widgets.edits import SearchEdit
+from openlp.core.widgets.toolbar import OpenLPToolbar
+from openlp.core.widgets.views import ListWidgetWithDnD
log = logging.getLogger(__name__)
diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py
index 6a05638ac..f155b3ce7 100644
--- a/openlp/core/lib/plugin.py
+++ b/openlp/core/lib/plugin.py
@@ -26,7 +26,10 @@ import logging
from PyQt5 import QtCore
-from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings
+from openlp.core.common.i18n import UiStrings
+from openlp.core.common.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+from openlp.core.common.settings import Settings
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..061788b25 100644
--- a/openlp/core/lib/pluginmanager.py
+++ b/openlp/core/lib/pluginmanager.py
@@ -24,11 +24,14 @@ Provide plugin management
"""
import os
+from openlp.core.common import extension_loader
+from openlp.core.common.applocation import AppLocation
+from openlp.core.common.mixins import LogMixin, RegistryProperties
+from openlp.core.common.registry import RegistryBase
from openlp.core.lib import Plugin, PluginStatus
-from openlp.core.common import AppLocation, RegistryProperties, OpenLPMixin, RegistryMixin, extension_loader
-class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
+class PluginManager(RegistryBase, LogMixin, RegistryProperties):
"""
This is the Plugin manager, which loads all the plugins,
and executes all the hooks, as and when necessary.
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/db.py b/openlp/core/lib/projector/db.py
index 223159a51..fa8934ae2 100644
--- a/openlp/core/lib/projector/db.py
+++ b/openlp/core/lib/projector/db.py
@@ -49,7 +49,7 @@ from openlp.core.lib.projector import upgrade
Base = declarative_base(MetaData())
-class CommonBase(object):
+class CommonMixin(object):
"""
Base class to automate table name and ID column.
"""
@@ -60,7 +60,7 @@ class CommonBase(object):
id = Column(Integer, primary_key=True)
-class Manufacturer(CommonBase, Base):
+class Manufacturer(Base, CommonMixin):
"""
Projector manufacturer table.
@@ -85,7 +85,7 @@ class Manufacturer(CommonBase, Base):
lazy='joined')
-class Model(CommonBase, Base):
+class Model(Base, CommonMixin):
"""
Projector model table.
@@ -113,7 +113,7 @@ class Model(CommonBase, Base):
lazy='joined')
-class Source(CommonBase, Base):
+class Source(Base, CommonMixin):
"""
Projector video source table.
@@ -140,7 +140,7 @@ class Source(CommonBase, Base):
text = Column(String(30))
-class Projector(CommonBase, Base):
+class Projector(Base, CommonMixin):
"""
Projector table.
@@ -213,7 +213,7 @@ class Projector(CommonBase, Base):
lazy='joined')
-class ProjectorSource(CommonBase, Base):
+class ProjectorSource(Base, CommonMixin):
"""
Projector local source table
This table allows mapping specific projector source input to a local
diff --git a/openlp/core/lib/projector/pjlink.py b/openlp/core/lib/projector/pjlink.py
index 1a5e9e832..2272b971f 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
@@ -508,7 +514,7 @@ class PJLinkCommands(object):
self.sw_version_received = data
-class PJLink(PJLinkCommands, QtNetwork.QTcpSocket):
+class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
"""
Socket service for PJLink TCP socket.
"""
diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py
deleted file mode 100644
index e2e70a934..000000000
--- a/openlp/core/lib/searchedit.py
+++ /dev/null
@@ -1,175 +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 logging
-
-from PyQt5 import QtCore, QtWidgets
-
-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__)
-
-
-class SearchEdit(QtWidgets.QLineEdit):
- """
- This is a specialised QLineEdit with a "clear" button inside for searches.
- """
- searchTypeChanged = QtCore.pyqtSignal(QtCore.QVariant)
- cleared = QtCore.pyqtSignal()
-
- def __init__(self, parent, settings_section):
- """
- Constructor.
- """
- super().__init__(parent)
- self.settings_section = settings_section
- self._current_search_type = -1
- self.clear_button = QtWidgets.QToolButton(self)
- self.clear_button.setIcon(build_icon(':/system/clear_shortcut.png'))
- self.clear_button.setCursor(QtCore.Qt.ArrowCursor)
- self.clear_button.setStyleSheet('QToolButton { border: none; padding: 0px; }')
- self.clear_button.resize(18, 18)
- self.clear_button.hide()
- self.clear_button.clicked.connect(self._on_clear_button_clicked)
- self.textChanged.connect(self._on_search_edit_text_changed)
- self._update_style_sheet()
- self.setAcceptDrops(False)
-
- def _update_style_sheet(self):
- """
- Internal method to update the stylesheet depending on which widgets are available and visible.
- """
- frame_width = self.style().pixelMetric(QtWidgets.QStyle.PM_DefaultFrameWidth)
- right_padding = self.clear_button.width() + frame_width
- if hasattr(self, 'menu_button'):
- left_padding = self.menu_button.width()
- stylesheet = 'QLineEdit {{ padding-left:{left}px; padding-right: {right}px; }} '.format(left=left_padding,
- right=right_padding)
- else:
- stylesheet = 'QLineEdit {{ padding-right: {right}px; }} '.format(right=right_padding)
- self.setStyleSheet(stylesheet)
- msz = self.minimumSizeHint()
- self.setMinimumSize(max(msz.width(), self.clear_button.width() + (frame_width * 2) + 2),
- max(msz.height(), self.clear_button.height() + (frame_width * 2) + 2))
-
- def resizeEvent(self, event):
- """
- Reimplemented method to react to resizing of the widget.
-
- :param event: The event that happened.
- """
- size = self.clear_button.size()
- frame_width = self.style().pixelMetric(QtWidgets.QStyle.PM_DefaultFrameWidth)
- self.clear_button.move(self.rect().right() - frame_width - size.width(),
- (self.rect().bottom() + 1 - size.height()) // 2)
- if hasattr(self, 'menu_button'):
- size = self.menu_button.size()
- self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) // 2)
-
- def current_search_type(self):
- """
- Readonly property to return the current search type.
- """
- return self._current_search_type
-
- def set_current_search_type(self, identifier):
- """
- Set a new current search type.
-
- :param identifier: The search type identifier (int).
- """
- menu = self.menu_button.menu()
- for action in menu.actions():
- if identifier == action.data():
- self.setPlaceholderText(action.placeholder_text)
- self.menu_button.setDefaultAction(action)
- self._current_search_type = identifier
- Settings().setValue('{section}/last used search type'.format(section=self.settings_section), identifier)
- self.searchTypeChanged.emit(identifier)
- return True
-
- def set_search_types(self, items):
- """
- A list of tuples to be used in the search type menu. The first item in the list will be preselected as the
- default.
-
- :param items: The list of tuples to use. The tuples should contain an integer identifier, an icon (QIcon
- instance or string) and a title for the item in the menu. In short, they should look like this::
-
- (, , , )
-
- For instance::
-
- (1, , "Titles", "Search Song Titles...")
-
- Or::
-
- (2, ":/songs/authors.png", "Authors", "Search Authors...")
- """
- menu = QtWidgets.QMenu(self)
- for identifier, icon, title, placeholder in items:
- action = create_widget_action(
- menu, text=title, icon=icon, data=identifier, triggers=self._on_menu_action_triggered)
- action.placeholder_text = placeholder
- if not hasattr(self, 'menu_button'):
- self.menu_button = QtWidgets.QToolButton(self)
- self.menu_button.setIcon(build_icon(':/system/clear_shortcut.png'))
- self.menu_button.setCursor(QtCore.Qt.ArrowCursor)
- self.menu_button.setPopupMode(QtWidgets.QToolButton.InstantPopup)
- self.menu_button.setStyleSheet('QToolButton { border: none; padding: 0px 10px 0px 0px; }')
- self.menu_button.resize(QtCore.QSize(28, 18))
- self.menu_button.setMenu(menu)
- self.set_current_search_type(
- Settings().value('{section}/last used search type'.format(section=self.settings_section)))
- self.menu_button.show()
- self._update_style_sheet()
-
- def _on_search_edit_text_changed(self, text):
- """
- Internally implemented slot to react to when the text in the line edit has changed so that we can show or hide
- the clear button.
-
- :param text: A :class:`~PyQt5.QtCore.QString` instance which represents the text in the line edit.
- """
- self.clear_button.setVisible(bool(text))
-
- def _on_clear_button_clicked(self):
- """
- Internally implemented slot to react to the clear button being clicked to clear the line edit. Once it has
- cleared the line edit, it emits the ``cleared()`` signal so that an application can react to the clearing of the
- line edit.
- """
- self.clear()
- self.cleared.emit()
-
- def _on_menu_action_triggered(self):
- """
- Internally implemented slot to react to the select of one of the search types in the menu. Once it has set the
- correct action on the button, and set the current search type (using the list of identifiers provided by the
- developer), the ``searchTypeChanged(int)`` signal is emitted with the identifier.
- """
- for action in self.menu_button.menu().actions():
- # Why is this needed?
- action.setChecked(False)
- self.set_current_search_type(self.sender().data())
diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py
index 124b158e8..3a824d424 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.mixins 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..06009ee3d 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.mixins 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..2d434c0c6 100644
--- a/openlp/core/ui/advancedtab.py
+++ b/openlp/core/ui/advancedtab.py
@@ -27,12 +27,14 @@ 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
+from openlp.core.widgets.edits import PathEdit
+from openlp.core.widgets.enums import PathEditType
log = logging.getLogger(__name__)
@@ -121,7 +123,7 @@ class AdvancedTab(SettingsTab):
self.data_directory_layout.setObjectName('data_directory_layout')
self.data_directory_new_label = QtWidgets.QLabel(self.data_directory_group_box)
self.data_directory_new_label.setObjectName('data_directory_current_label')
- self.data_directory_path_edit = PathEdit(self.data_directory_group_box, path_type=PathType.Directories,
+ self.data_directory_path_edit = PathEdit(self.data_directory_group_box, path_type=PathEditType.Directories,
default_path=AppLocation.get_directory(AppLocation.DataDir))
self.data_directory_layout.addRow(self.data_directory_new_label, self.data_directory_path_edit)
self.new_data_directory_has_files_label = QtWidgets.QLabel(self.data_directory_group_box)
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..70fe2c416 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.common import is_linux
+from openlp.core.common.i18n import UiStrings, translate
+from openlp.core.common.mixins import RegistryProperties
+from openlp.core.common.settings import Settings
+from openlp.core.ui.exceptiondialog import Ui_ExceptionDialog
+from openlp.core.widgets.dialogs import FileDialog
from openlp.core.version import get_version
-from openlp.core.ui.lib.filedialog import FileDialog
-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..249ea1f10 100644
--- a/openlp/core/ui/filerenameform.py
+++ b/openlp/core/ui/filerenameform.py
@@ -22,12 +22,12 @@
"""
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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+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..37fac2dd4 100644
--- a/openlp/core/ui/firsttimeform.py
+++ b/openlp/core/ui/firsttimeform.py
@@ -34,9 +34,13 @@ 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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+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 +281,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 +601,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..a908ac91d 100644
--- a/openlp/core/ui/generaltab.py
+++ b/openlp/core/ui/generaltab.py
@@ -26,10 +26,15 @@ 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.ui.lib import ColorButton, PathEdit
+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.widgets.buttons import ColorButton
+from openlp.core.widgets.edits import PathEdit
log = logging.getLogger(__name__)
diff --git a/openlp/core/ui/lib/historycombobox.py b/openlp/core/ui/lib/historycombobox.py
deleted file mode 100644
index 6320bc383..000000000
--- a/openlp/core/ui/lib/historycombobox.py
+++ /dev/null
@@ -1,84 +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:`~openlp.core.ui.lib.historycombobox` module contains the HistoryComboBox widget
-"""
-
-from PyQt5 import QtCore, QtWidgets
-
-
-class HistoryComboBox(QtWidgets.QComboBox):
- """
- The :class:`~openlp.core.common.historycombobox.HistoryComboBox` widget emulates the QLineEdit ``returnPressed``
- signal for when the :kbd:`Enter` or :kbd:`Return` keys are pressed, and saves anything that is typed into the edit
- box into its list.
- """
- returnPressed = QtCore.pyqtSignal()
-
- def __init__(self, parent=None):
- """
- Initialise the combo box, setting duplicates to False and the insert policy to insert items at the top.
-
- :param parent: The parent widget
- """
- super().__init__(parent)
- self.setDuplicatesEnabled(False)
- self.setEditable(True)
- self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
- self.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
-
- def keyPressEvent(self, event):
- """
- Override the inherited keyPressEvent method to emit the ``returnPressed`` signal and to save the current text to
- the dropdown list.
-
- :param event: The keyboard event
- """
- # Handle Enter and Return ourselves
- if event.key() == QtCore.Qt.Key_Enter or event.key() == QtCore.Qt.Key_Return:
- # Emit the returnPressed signal
- self.returnPressed.emit()
- # Save the current text to the dropdown list
- if self.currentText() and self.findText(self.currentText()) == -1:
- self.insertItem(0, self.currentText())
- # Let the parent handle any keypress events
- super().keyPressEvent(event)
-
- def focusOutEvent(self, event):
- """
- Override the inherited focusOutEvent to save the current text to the dropdown list.
-
- :param event: The focus event
- """
- # Save the current text to the dropdown list
- if self.currentText() and self.findText(self.currentText()) == -1:
- self.insertItem(0, self.currentText())
- # Let the parent handle any keypress events
- super().focusOutEvent(event)
-
- def getItems(self):
- """
- Get all the items from the history
-
- :return: A list of strings
- """
- return [self.itemText(i) for i in range(self.count())]
diff --git a/openlp/core/ui/lib/listwidgetwithdnd.py b/openlp/core/ui/lib/listwidgetwithdnd.py
deleted file mode 100755
index e8f0b729f..000000000
--- a/openlp/core/ui/lib/listwidgetwithdnd.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 #
-###############################################################################
-"""
-Extend QListWidget to handle drag and drop functionality
-"""
-import os
-
-from PyQt5 import QtCore, QtGui, QtWidgets
-
-from openlp.core.common import Registry, UiStrings
-
-
-class ListWidgetWithDnD(QtWidgets.QListWidget):
- """
- Provide a list widget to store objects and handle drag and drop events
- """
- def __init__(self, parent=None, name=''):
- """
- Initialise the list widget
- """
- super().__init__(parent)
- self.mime_data_text = name
- self.no_results_text = UiStrings().NoResults
- self.setSpacing(1)
- self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
- self.setAlternatingRowColors(True)
- self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
-
- def activateDnD(self):
- """
- Activate DnD of widget
- """
- self.setAcceptDrops(True)
- self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
- Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)
-
- def clear(self, search_while_typing=False):
- """
- Re-implement clear, so that we can customise feedback when using 'Search as you type'
-
- :param search_while_typing: True if we want to display the customised message
- :return: None
- """
- if search_while_typing:
- self.no_results_text = UiStrings().ShortResults
- else:
- self.no_results_text = UiStrings().NoResults
- super().clear()
-
- def mouseMoveEvent(self, event):
- """
- Drag and drop event does not care what data is selected as the recipient will use events to request the data
- move just tell it what plugin to call
- """
- if event.buttons() != QtCore.Qt.LeftButton:
- event.ignore()
- return
- if not self.selectedItems():
- event.ignore()
- return
- drag = QtGui.QDrag(self)
- mime_data = QtCore.QMimeData()
- drag.setMimeData(mime_data)
- mime_data.setText(self.mime_data_text)
- drag.exec(QtCore.Qt.CopyAction)
-
- def dragEnterEvent(self, event):
- """
- When something is dragged into this object, check if you should be able to drop it in here.
- """
- if event.mimeData().hasUrls():
- event.accept()
- else:
- event.ignore()
-
- def dragMoveEvent(self, event):
- """
- Make an object droppable, and set it to copy the contents of the object, not move it.
- """
- if event.mimeData().hasUrls():
- event.setDropAction(QtCore.Qt.CopyAction)
- event.accept()
- else:
- event.ignore()
-
- def dropEvent(self, event):
- """
- Receive drop event check if it is a file and process it if it is.
-
- :param event: Handle of the event pint passed
- """
- if event.mimeData().hasUrls():
- event.setDropAction(QtCore.Qt.CopyAction)
- event.accept()
- files = []
- for url in event.mimeData().urls():
- local_file = os.path.normpath(url.toLocalFile())
- if os.path.isfile(local_file):
- files.append(local_file)
- elif os.path.isdir(local_file):
- listing = os.listdir(local_file)
- for file in listing:
- files.append(os.path.join(local_file, file))
- Registry().execute('{mime_data}_dnd'.format(mime_data=self.mime_data_text),
- {'files': files})
- else:
- event.ignore()
-
- def allItems(self):
- """
- An generator to list all the items in the widget
-
- :return: a generator
- """
- for row in range(self.count()):
- yield self.item(row)
-
- def paintEvent(self, event):
- """
- Re-implement paintEvent so that we can add 'No Results' text when the listWidget is empty.
-
- :param event: A QPaintEvent
- :return: None
- """
- super().paintEvent(event)
- if not self.count():
- viewport = self.viewport()
- painter = QtGui.QPainter(viewport)
- font = QtGui.QFont()
- font.setItalic(True)
- painter.setFont(font)
- painter.drawText(QtCore.QRect(0, 0, viewport.width(), viewport.height()),
- (QtCore.Qt.AlignHCenter | QtCore.Qt.TextWordWrap), self.no_results_text)
diff --git a/openlp/core/ui/lib/pathedit.py b/openlp/core/ui/lib/pathedit.py
deleted file mode 100644
index d5b9272fa..000000000
--- a/openlp/core/ui/lib/pathedit.py
+++ /dev/null
@@ -1,196 +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 #
-###############################################################################
-from enum import Enum
-
-from PyQt5 import QtCore, QtWidgets
-
-from openlp.core.common 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
-
-
-class PathType(Enum):
- Files = 1
- Directories = 2
-
-
-class PathEdit(QtWidgets.QWidget):
- """
- The :class:`~openlp.core.ui.lib.pathedit.PathEdit` class subclasses QWidget to create a custom widget for use when
- a file or directory needs to be selected.
- """
- pathChanged = QtCore.pyqtSignal(Path)
-
- def __init__(self, parent=None, path_type=PathType.Files, default_path=None, dialog_caption=None, show_revert=True):
- """
- Initialise the PathEdit widget
-
- :param QtWidget.QWidget | None: The parent of the widget. This is just passed to the super method.
- :param str dialog_caption: Used to customise the caption in the QFileDialog.
- :param openlp.core.common.path.Path default_path: The default path. This is set as the path when the revert
- button is clicked
- :param bool show_revert: Used to determine if the 'revert button' should be visible.
- :rtype: None
- """
- super().__init__(parent)
- self.default_path = default_path
- self.dialog_caption = dialog_caption
- self._path_type = path_type
- self._path = None
- self.filters = '{all_files} (*)'.format(all_files=UiStrings().AllFiles)
- self._setup(show_revert)
-
- def _setup(self, show_revert):
- """
- Set up the widget
- :param bool show_revert: Show or hide the revert button
- :rtype: None
- """
- widget_layout = QtWidgets.QHBoxLayout()
- widget_layout.setContentsMargins(0, 0, 0, 0)
- self.line_edit = QtWidgets.QLineEdit(self)
- widget_layout.addWidget(self.line_edit)
- self.browse_button = QtWidgets.QToolButton(self)
- self.browse_button.setIcon(build_icon(':/general/general_open.png'))
- widget_layout.addWidget(self.browse_button)
- self.revert_button = QtWidgets.QToolButton(self)
- self.revert_button.setIcon(build_icon(':/general/general_revert.png'))
- self.revert_button.setVisible(show_revert)
- widget_layout.addWidget(self.revert_button)
- self.setLayout(widget_layout)
- # Signals and Slots
- self.browse_button.clicked.connect(self.on_browse_button_clicked)
- self.revert_button.clicked.connect(self.on_revert_button_clicked)
- self.line_edit.editingFinished.connect(self.on_line_edit_editing_finished)
- self.update_button_tool_tips()
-
- @property
- def path(self):
- """
- A property getter method to return the selected path.
-
- :return: The selected path
- :rtype: openlp.core.common.path.Path
- """
- return self._path
-
- @path.setter
- def path(self, path):
- """
- A Property setter method to set the selected path
-
- :param openlp.core.common.path.Path path: The path to set the widget to
- :rtype: None
- """
- self._path = path
- text = path_to_str(path)
- self.line_edit.setText(text)
- self.line_edit.setToolTip(text)
-
- @property
- def path_type(self):
- """
- A property getter method to return the path_type. Path type allows you to sepecify if the user is restricted to
- selecting a file or directory.
-
- :return: The type selected
- :rtype: PathType
- """
- return self._path_type
-
- @path_type.setter
- def path_type(self, path_type):
- """
- A Property setter method to set the path type
-
- :param PathType path_type: The type of path to select
- :rtype: None
- """
- self._path_type = path_type
- self.update_button_tool_tips()
-
- def update_button_tool_tips(self):
- """
- Called to update the tooltips on the buttons. This is changing path types, and when the widget is initalised
-
- :rtype: None
- """
- if self._path_type == PathType.Directories:
- self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for directory.'))
- self.revert_button.setToolTip(translate('OpenLP.PathEdit', 'Revert to default directory.'))
- else:
- self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for file.'))
- self.revert_button.setToolTip(translate('OpenLP.PathEdit', 'Revert to default file.'))
-
- def on_browse_button_clicked(self):
- """
- A handler to handle a click on the browse button.
-
- Show the QFileDialog and process the input from the user
-
- :rtype: None
- """
- caption = self.dialog_caption
- path = None
- if self._path_type == PathType.Directories:
- if not caption:
- caption = translate('OpenLP.PathEdit', 'Select Directory')
- path = FileDialog.getExistingDirectory(self, caption, self._path, FileDialog.ShowDirsOnly)
- elif self._path_type == PathType.Files:
- if not caption:
- caption = self.dialog_caption = translate('OpenLP.PathEdit', 'Select File')
- path, filter_used = FileDialog.getOpenFileName(self, caption, self._path, self.filters)
- if path:
- self.on_new_path(path)
-
- def on_revert_button_clicked(self):
- """
- A handler to handle a click on the revert button.
-
- Set the new path to the value of the default_path instance variable.
-
- :rtype: None
- """
- self.on_new_path(self.default_path)
-
- def on_line_edit_editing_finished(self):
- """
- A handler to handle when the line edit has finished being edited.
-
- :rtype: None
- """
- path = str_to_path(self.line_edit.text())
- self.on_new_path(path)
-
- def on_new_path(self, path):
- """
- A method called to validate and set a new path.
-
- Emits the pathChanged Signal
-
- :param openlp.core.common.path.Path path: The new path
- :rtype: None
- """
- if self._path != path:
- self.path = path
- self.pathChanged.emit(path)
diff --git a/openlp/core/ui/lib/spelltextedit.py b/openlp/core/ui/lib/spelltextedit.py
deleted file mode 100644
index 41e28a7e5..000000000
--- a/openlp/core/ui/lib/spelltextedit.py
+++ /dev/null
@@ -1,204 +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:`~openlp.core.lib.spelltextedit` module contains a classes to add spell checking to an edit widget.
-"""
-
-import logging
-import re
-
-try:
- import enchant
- from enchant import DictNotFoundError
- from enchant.errors import Error
- ENCHANT_AVAILABLE = True
-except ImportError:
- ENCHANT_AVAILABLE = False
-
-# based on code from http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check
-
-from PyQt5 import QtCore, QtGui, QtWidgets
-
-from openlp.core.lib import translate, FormattingTags
-from openlp.core.lib.ui import create_action
-
-log = logging.getLogger(__name__)
-
-
-class SpellTextEdit(QtWidgets.QPlainTextEdit):
- """
- Spell checking widget based on QPlanTextEdit.
- """
- def __init__(self, parent=None, formatting_tags_allowed=True):
- """
- Constructor.
- """
- global ENCHANT_AVAILABLE
- super(SpellTextEdit, self).__init__(parent)
- self.formatting_tags_allowed = formatting_tags_allowed
- # Default dictionary based on the current locale.
- if ENCHANT_AVAILABLE:
- try:
- self.dictionary = enchant.Dict()
- self.highlighter = Highlighter(self.document())
- self.highlighter.spelling_dictionary = self.dictionary
- except (Error, DictNotFoundError):
- ENCHANT_AVAILABLE = False
- log.debug('Could not load default dictionary')
-
- def mousePressEvent(self, event):
- """
- Handle mouse clicks within the text edit region.
- """
- if event.button() == QtCore.Qt.RightButton:
- # Rewrite the mouse event to a left button event so the cursor is moved to the location of the pointer.
- event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
- event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier)
- QtWidgets.QPlainTextEdit.mousePressEvent(self, event)
-
- def contextMenuEvent(self, event):
- """
- Provide the context menu for the text edit region.
- """
- popup_menu = self.createStandardContextMenu()
- # Select the word under the cursor.
- cursor = self.textCursor()
- # only select text if not already selected
- if not cursor.hasSelection():
- cursor.select(QtGui.QTextCursor.WordUnderCursor)
- self.setTextCursor(cursor)
- # Add menu with available languages.
- if ENCHANT_AVAILABLE:
- lang_menu = QtWidgets.QMenu(translate('OpenLP.SpellTextEdit', 'Language:'))
- for lang in enchant.list_languages():
- action = create_action(lang_menu, lang, text=lang, checked=lang == self.dictionary.tag)
- lang_menu.addAction(action)
- popup_menu.insertSeparator(popup_menu.actions()[0])
- popup_menu.insertMenu(popup_menu.actions()[0], lang_menu)
- lang_menu.triggered.connect(self.set_language)
- # Check if the selected word is misspelled and offer spelling suggestions if it is.
- if ENCHANT_AVAILABLE and self.textCursor().hasSelection():
- text = self.textCursor().selectedText()
- if not self.dictionary.check(text):
- spell_menu = QtWidgets.QMenu(translate('OpenLP.SpellTextEdit', 'Spelling Suggestions'))
- for word in self.dictionary.suggest(text):
- action = SpellAction(word, spell_menu)
- action.correct.connect(self.correct_word)
- spell_menu.addAction(action)
- # Only add the spelling suggests to the menu if there are suggestions.
- if spell_menu.actions():
- popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
- tag_menu = QtWidgets.QMenu(translate('OpenLP.SpellTextEdit', 'Formatting Tags'))
- if self.formatting_tags_allowed:
- for html in FormattingTags.get_html_tags():
- action = SpellAction(html['desc'], tag_menu)
- action.correct.connect(self.html_tag)
- tag_menu.addAction(action)
- popup_menu.insertSeparator(popup_menu.actions()[0])
- popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
- popup_menu.exec(event.globalPos())
-
- def set_language(self, action):
- """
- Changes the language for this spelltextedit.
-
- :param action: The action.
- """
- self.dictionary = enchant.Dict(action.text())
- self.highlighter.spelling_dictionary = self.dictionary
- self.highlighter.highlightBlock(self.toPlainText())
- self.highlighter.rehighlight()
-
- def correct_word(self, word):
- """
- Replaces the selected text with word.
- """
- cursor = self.textCursor()
- cursor.beginEditBlock()
- cursor.removeSelectedText()
- cursor.insertText(word)
- cursor.endEditBlock()
-
- def html_tag(self, tag):
- """
- Replaces the selected text with word.
- """
- tag = tag.replace('&', '')
- for html in FormattingTags.get_html_tags():
- if tag == html['desc']:
- cursor = self.textCursor()
- if self.textCursor().hasSelection():
- text = cursor.selectedText()
- cursor.beginEditBlock()
- cursor.removeSelectedText()
- cursor.insertText(html['start tag'])
- cursor.insertText(text)
- cursor.insertText(html['end tag'])
- cursor.endEditBlock()
- else:
- cursor = self.textCursor()
- cursor.insertText(html['start tag'])
- cursor.insertText(html['end tag'])
-
-
-class Highlighter(QtGui.QSyntaxHighlighter):
- """
- Provides a text highlighter for pointing out spelling errors in text.
- """
- WORDS = r'(?iu)[\w\']+'
-
- def __init__(self, *args):
- """
- Constructor
- """
- super(Highlighter, self).__init__(*args)
- self.spelling_dictionary = None
-
- def highlightBlock(self, text):
- """
- Highlight mis spelt words in a block of text.
-
- Note, this is a Qt hook.
- """
- if not self.spelling_dictionary:
- return
- text = str(text)
- char_format = QtGui.QTextCharFormat()
- char_format.setUnderlineColor(QtCore.Qt.red)
- char_format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
- for word_object in re.finditer(self.WORDS, text):
- if not self.spelling_dictionary.check(word_object.group()):
- self.setFormat(word_object.start(), word_object.end() - word_object.start(), char_format)
-
-
-class SpellAction(QtWidgets.QAction):
- """
- A special QAction that returns the text in a signal.
- """
- correct = QtCore.pyqtSignal(str)
-
- def __init__(self, *args):
- """
- Constructor
- """
- super(SpellAction, self).__init__(*args)
- self.triggered.connect(lambda x: self.correct.emit(self.text()))
diff --git a/openlp/core/ui/lib/treewidgetwithdnd.py b/openlp/core/ui/lib/treewidgetwithdnd.py
deleted file mode 100644
index fe45666f5..000000000
--- a/openlp/core/ui/lib/treewidgetwithdnd.py
+++ /dev/null
@@ -1,144 +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 #
-###############################################################################
-"""
-Extend QTreeWidget to handle drag and drop functionality
-"""
-import os
-
-from PyQt5 import QtCore, QtGui, QtWidgets
-
-from openlp.core.common import Registry, is_win
-
-
-class TreeWidgetWithDnD(QtWidgets.QTreeWidget):
- """
- Provide a tree widget to store objects and handle drag and drop events
- """
- def __init__(self, parent=None, name=''):
- """
- Initialise the tree widget
- """
- super(TreeWidgetWithDnD, self).__init__(parent)
- self.mime_data_text = name
- self.allow_internal_dnd = False
- self.header().close()
- self.default_indentation = self.indentation()
- self.setIndentation(0)
- self.setAnimated(True)
-
- def activateDnD(self):
- """
- Activate DnD of widget
- """
- self.setAcceptDrops(True)
- self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
- Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)
- Registry().register_function(('%s_dnd_internal' % self.mime_data_text), self.parent().dnd_move_internal)
-
- def mouseMoveEvent(self, event):
- """
- Drag and drop event does not care what data is selected as the recipient will use events to request the data
- move just tell it what plugin to call
-
- :param event: The event that occurred
- """
- if event.buttons() != QtCore.Qt.LeftButton:
- event.ignore()
- return
- if not self.selectedItems():
- event.ignore()
- return
- drag = QtGui.QDrag(self)
- mime_data = QtCore.QMimeData()
- drag.setMimeData(mime_data)
- mime_data.setText(self.mime_data_text)
- drag.exec(QtCore.Qt.CopyAction)
-
- def dragEnterEvent(self, event):
- """
- Receive drag enter event, check if it is a file or internal object and allow it if it is.
-
- :param event: The event that occurred
- """
- if event.mimeData().hasUrls():
- event.accept()
- elif self.allow_internal_dnd:
- event.accept()
- else:
- event.ignore()
-
- def dragMoveEvent(self, event):
- """
- Receive drag move event, check if it is a file or internal object and allow it if it is.
-
- :param event: The event that occurred
- """
- QtWidgets.QTreeWidget.dragMoveEvent(self, event)
- if event.mimeData().hasUrls():
- event.setDropAction(QtCore.Qt.CopyAction)
- event.accept()
- elif self.allow_internal_dnd:
- event.setDropAction(QtCore.Qt.CopyAction)
- event.accept()
- else:
- event.ignore()
-
- def dropEvent(self, event):
- """
- Receive drop event, check if it is a file or internal object and process it if it is.
-
- :param event: Handle of the event pint passed
- """
- # If we are on Windows, OpenLP window will not be set on top. For example, user can drag images to Library and
- # the folder stays on top of the group creation box. This piece of code fixes this issue.
- if is_win():
- self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
- self.setWindowState(QtCore.Qt.WindowNoState)
- if event.mimeData().hasUrls():
- event.setDropAction(QtCore.Qt.CopyAction)
- event.accept()
- files = []
- for url in event.mimeData().urls():
- local_file = url.toLocalFile()
- if os.path.isfile(local_file):
- files.append(local_file)
- elif os.path.isdir(local_file):
- listing = os.listdir(local_file)
- for file_name in listing:
- files.append(os.path.join(local_file, file_name))
- Registry().execute('%s_dnd' % self.mime_data_text, {'files': files, 'target': self.itemAt(event.pos())})
- elif self.allow_internal_dnd:
- event.setDropAction(QtCore.Qt.CopyAction)
- event.accept()
- Registry().execute('%s_dnd_internal' % self.mime_data_text, self.itemAt(event.pos()))
- else:
- event.ignore()
-
- # Convenience methods for emulating a QListWidget. This helps keeping MediaManagerItem simple.
- def addItem(self, item):
- self.addTopLevelItem(item)
-
- def count(self):
- return self.topLevelItemCount()
-
- def item(self, index):
- return self.topLevelItem(index)
diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py
index 097d92699..ca634910e 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 LogMixin, RegistryProperties
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
+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
@@ -128,7 +131,7 @@ class Display(QtWidgets.QGraphicsView):
self.web_loaded = True
-class MainDisplay(OpenLPMixin, Display, RegistryProperties):
+class MainDisplay(Display, LogMixin, RegistryProperties):
"""
This is the display screen as a specialized class from the Display class
"""
@@ -600,7 +603,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
self.web_view.setGeometry(0, 0, self.width(), self.height())
-class AudioPlayer(OpenLPMixin, QtCore.QObject):
+class AudioPlayer(LogMixin, QtCore.QObject):
"""
This Class will play audio only allowing components to work with a soundtrack independent of the user interface.
"""
diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py
index 4a1200084..33cdc3301 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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+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.widgets.dialogs import FileDialog
+from openlp.core.widgets.docks import OpenLPDockWidget, 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.lib.dockwidget import OpenLPDockWidget
-from openlp.core.ui.lib.filedialog import FileDialog
-from openlp.core.ui.lib.mediadockmanager import MediaDockManager
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..c72d6669d 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 LogMixin, RegistryProperties
+from openlp.core.common.registry import Registry, RegistryBase
+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
@@ -39,7 +42,7 @@ from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
from openlp.core.ui.media.mediaplayer import MediaPlayer
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players,\
parse_optical_path
-from openlp.core.ui.lib.toolbar import OpenLPToolbar
+from openlp.core.widgets.toolbar import OpenLPToolbar
log = logging.getLogger(__name__)
@@ -88,7 +91,7 @@ class MediaSlider(QtWidgets.QSlider):
QtWidgets.QSlider.mouseReleaseEvent(self, event)
-class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
+class MediaController(RegistryBase, LogMixin, RegistryProperties):
"""
The implementation of the Media Controller. The Media Controller adds an own class for every Player.
Currently these are QtWebkit, Phonon and Vlc. display_controllers are an array of controllers keyed on the
diff --git a/openlp/core/ui/media/mediaplayer.py b/openlp/core/ui/media/mediaplayer.py
index edf08608b..77d089c89 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.mixins 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..28d7798ee 100644
--- a/openlp/core/ui/media/playertab.py
+++ b/openlp/core/ui/media/playertab.py
@@ -23,13 +23,16 @@
The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff.
"""
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
-from openlp.core.ui.lib.colorbutton import ColorButton
+from openlp.core.widgets.buttons import ColorButton
class MediaQCheckBox(QtWidgets.QCheckBox):
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..43fa5e68d 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.mixins 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..ee3b07080 100644
--- a/openlp/core/ui/printservicedialog.py
+++ b/openlp/core/ui/printservicedialog.py
@@ -24,9 +24,9 @@ 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
+from openlp.core.widgets.edits import SpellTextEdit
class ZoomSize(object):
diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py
index 9394de9ce..07ab24496 100644
--- a/openlp/core/ui/printserviceform.py
+++ b/openlp/core/ui/printserviceform.py
@@ -23,16 +23,18 @@
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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+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..0770886e4 100644
--- a/openlp/core/ui/projector/manager.py
+++ b/openlp/core/ui/projector/manager.py
@@ -20,18 +20,19 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
- :mod: openlp.core.ui.projector.manager` module
+:mod: openlp.core.ui.projector.manager` module
- Provides the functions for the display/control of Projectors.
+Provides the functions for the display/control of Projectors.
"""
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 LogMixin, RegistryProperties
+from openlp.core.common.registry import RegistryBase
+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, \
@@ -41,6 +42,7 @@ from openlp.core.lib.projector.db import ProjectorDB
from openlp.core.lib.projector.pjlink import PJLink, PJLinkUDP
from openlp.core.ui.projector.editform import ProjectorEditForm
from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle
+from openlp.core.widgets.toolbar import OpenLPToolbar
log = logging.getLogger(__name__)
log.debug('projectormanager loaded')
@@ -274,7 +276,7 @@ class UiProjectorManager(object):
self.update_icons()
-class ProjectorManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, UiProjectorManager, RegistryProperties):
+class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogMixin, RegistryProperties):
"""
Manage the projectors.
"""
@@ -286,7 +288,7 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, UiProjecto
:param projectordb: Database session inherited from superclass.
"""
log.debug('__init__()')
- super().__init__(parent)
+ super(ProjectorManager, self).__init__(parent)
self.settings_section = 'projector'
self.projectordb = projectordb
self.projector_list = []
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..0a4b7cab6 100644
--- a/openlp/core/ui/serviceitemeditform.py
+++ b/openlp/core/ui/serviceitemeditform.py
@@ -24,9 +24,9 @@ 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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+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 42a45cafd..5dda18044 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -32,16 +32,19 @@ 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 LogMixin, RegistryProperties
+from openlp.core.common.path import Path, create_paths, path_to_str, str_to_path
+from openlp.core.common.registry import Registry, RegistryBase
+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
-from openlp.core.ui.lib import OpenLPToolbar
-from openlp.core.ui.lib.filedialog import FileDialog
+from openlp.core.widgets.dialogs import FileDialog
+from openlp.core.widgets.toolbar import OpenLPToolbar
class ServiceManagerList(QtWidgets.QTreeWidget):
@@ -53,8 +56,24 @@ class ServiceManagerList(QtWidgets.QTreeWidget):
Constructor
"""
super(ServiceManagerList, self).__init__(parent)
+ self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
+ self.setAlternatingRowColors(True)
+ self.setHeaderHidden(True)
+ self.setExpandsOnDoubleClick(False)
self.service_manager = service_manager
+ def dragEnterEvent(self, event):
+ """
+ React to a drag enter event
+ """
+ event.accept()
+
+ def dragMoveEvent(self, event):
+ """
+ React to a drage move event
+ """
+ event.accept()
+
def keyPressEvent(self, event):
"""
Capture Key press and respond accordingly.
@@ -114,7 +133,7 @@ class Ui_ServiceManager(object):
self.layout.setSpacing(0)
self.layout.setContentsMargins(0, 0, 0, 0)
# Create the top toolbar
- self.toolbar = OpenLPToolbar(widget)
+ self.toolbar = OpenLPToolbar(self)
self.toolbar.add_toolbar_action('newService', text=UiStrings().NewService, icon=':/general/general_new.png',
tooltip=UiStrings().CreateService, triggers=self.on_new_service_clicked)
self.toolbar.add_toolbar_action('openService', text=UiStrings().OpenService,
@@ -144,73 +163,67 @@ class Ui_ServiceManager(object):
QtWidgets.QAbstractItemView.CurrentChanged |
QtWidgets.QAbstractItemView.DoubleClicked |
QtWidgets.QAbstractItemView.EditKeyPressed)
- self.service_manager_list.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
- self.service_manager_list.setAlternatingRowColors(True)
- self.service_manager_list.setHeaderHidden(True)
- self.service_manager_list.setExpandsOnDoubleClick(False)
self.service_manager_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.service_manager_list.customContextMenuRequested.connect(self.context_menu)
self.service_manager_list.setObjectName('service_manager_list')
# enable drop
- self.service_manager_list.__class__.dragEnterEvent = lambda x, event: event.accept()
- self.service_manager_list.__class__.dragMoveEvent = lambda x, event: event.accept()
- self.service_manager_list.__class__.dropEvent = self.drop_event
+ self.service_manager_list.dropEvent = self.drop_event
self.layout.addWidget(self.service_manager_list)
# Add the bottom toolbar
self.order_toolbar = OpenLPToolbar(widget)
action_list = ActionList.get_instance()
action_list.add_category(UiStrings().Service, CategoryOrder.standard_toolbar)
- self.service_manager_list.move_top = self.order_toolbar.add_toolbar_action(
+ self.move_top_action = self.order_toolbar.add_toolbar_action(
'moveTop',
text=translate('OpenLP.ServiceManager', 'Move to &top'), icon=':/services/service_top.png',
tooltip=translate('OpenLP.ServiceManager', 'Move item to the top of the service.'),
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_top)
- self.service_manager_list.move_up = self.order_toolbar.add_toolbar_action(
+ self.move_up_action = self.order_toolbar.add_toolbar_action(
'moveUp',
text=translate('OpenLP.ServiceManager', 'Move &up'), icon=':/services/service_up.png',
tooltip=translate('OpenLP.ServiceManager', 'Move item up one position in the service.'),
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_up)
- self.service_manager_list.move_down = self.order_toolbar.add_toolbar_action(
+ self.move_down_action = self.order_toolbar.add_toolbar_action(
'moveDown',
text=translate('OpenLP.ServiceManager', 'Move &down'), icon=':/services/service_down.png',
tooltip=translate('OpenLP.ServiceManager', 'Move item down one position in the service.'),
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_down)
- self.service_manager_list.move_bottom = self.order_toolbar.add_toolbar_action(
+ self.move_bottom_action = self.order_toolbar.add_toolbar_action(
'moveBottom',
text=translate('OpenLP.ServiceManager', 'Move to &bottom'), icon=':/services/service_bottom.png',
tooltip=translate('OpenLP.ServiceManager', 'Move item to the end of the service.'),
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_end)
- self.service_manager_list.down = self.order_toolbar.add_toolbar_action(
+ self.down_action = self.order_toolbar.add_toolbar_action(
'down',
text=translate('OpenLP.ServiceManager', 'Move &down'), can_shortcuts=True,
tooltip=translate('OpenLP.ServiceManager', 'Moves the selection down the window.'), visible=False,
triggers=self.on_move_selection_down)
- action_list.add_action(self.service_manager_list.down)
- self.service_manager_list.up = self.order_toolbar.add_toolbar_action(
+ action_list.add_action(self.down_action)
+ self.up_action = self.order_toolbar.add_toolbar_action(
'up',
text=translate('OpenLP.ServiceManager', 'Move up'), can_shortcuts=True,
tooltip=translate('OpenLP.ServiceManager', 'Moves the selection up the window.'), visible=False,
triggers=self.on_move_selection_up)
- action_list.add_action(self.service_manager_list.up)
+ action_list.add_action(self.up_action)
self.order_toolbar.addSeparator()
- self.service_manager_list.delete = self.order_toolbar.add_toolbar_action(
+ self.delete_action = self.order_toolbar.add_toolbar_action(
'delete', can_shortcuts=True,
text=translate('OpenLP.ServiceManager', '&Delete From Service'), icon=':/general/general_delete.png',
tooltip=translate('OpenLP.ServiceManager', 'Delete the selected item from the service.'),
triggers=self.on_delete_from_service)
self.order_toolbar.addSeparator()
- self.service_manager_list.expand = self.order_toolbar.add_toolbar_action(
+ self.expand_action = self.order_toolbar.add_toolbar_action(
'expand', can_shortcuts=True,
text=translate('OpenLP.ServiceManager', '&Expand all'), icon=':/services/service_expand_all.png',
tooltip=translate('OpenLP.ServiceManager', 'Expand all the service items.'),
category=UiStrings().Service, triggers=self.on_expand_all)
- self.service_manager_list.collapse = self.order_toolbar.add_toolbar_action(
+ self.collapse_action = self.order_toolbar.add_toolbar_action(
'collapse', can_shortcuts=True,
text=translate('OpenLP.ServiceManager', '&Collapse all'), icon=':/services/service_collapse_all.png',
tooltip=translate('OpenLP.ServiceManager', 'Collapse all the service items.'),
category=UiStrings().Service, triggers=self.on_collapse_all)
self.order_toolbar.addSeparator()
- self.service_manager_list.make_live = self.order_toolbar.add_toolbar_action(
+ self.make_live_action = self.order_toolbar.add_toolbar_action(
'make_live', can_shortcuts=True,
text=translate('OpenLP.ServiceManager', 'Go Live'), icon=':/general/general_live.png',
tooltip=translate('OpenLP.ServiceManager', 'Send the selected item to Live.'),
@@ -251,7 +264,7 @@ class Ui_ServiceManager(object):
icon=':/media/auto-start_active.png',
triggers=self.on_auto_start)
# Add already existing delete action to the menu.
- self.menu.addAction(self.service_manager_list.delete)
+ self.menu.addAction(self.delete_action)
self.create_custom_action = create_widget_action(self.menu,
text=translate('OpenLP.ServiceManager', 'Create New &Custom '
'Slide'),
@@ -282,28 +295,20 @@ class Ui_ServiceManager(object):
self.preview_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', 'Show &Preview'),
icon=':/general/general_preview.png', triggers=self.make_preview)
# Add already existing make live action to the menu.
- self.menu.addAction(self.service_manager_list.make_live)
+ self.menu.addAction(self.make_live_action)
self.menu.addSeparator()
self.theme_menu = QtWidgets.QMenu(translate('OpenLP.ServiceManager', '&Change Item Theme'))
self.menu.addMenu(self.theme_menu)
- self.service_manager_list.addActions(
- [self.service_manager_list.move_down,
- self.service_manager_list.move_up,
- self.service_manager_list.make_live,
- self.service_manager_list.move_top,
- self.service_manager_list.move_bottom,
- self.service_manager_list.up,
- self.service_manager_list.down,
- self.service_manager_list.expand,
- self.service_manager_list.collapse
- ])
+ self.service_manager_list.addActions([self.move_down_action, self.move_up_action, self.make_live_action,
+ self.move_top_action, self.move_bottom_action, self.up_action,
+ self.down_action, self.expand_action, self.collapse_action])
Registry().register_function('theme_update_list', self.update_theme_list)
Registry().register_function('config_screen_changed', self.regenerate_service_items)
Registry().register_function('theme_update_global', self.theme_change)
Registry().register_function('mediaitem_suffix_reset', self.reset_supported_suffixes)
-class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceManager, RegistryProperties):
+class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixin, RegistryProperties):
"""
Manages the services. This involves taking text strings from plugins and adding them to the service. This service
can then be zipped up with all the resources used into one OSZ or oszl file for use on any OpenLP v2 installation.
@@ -317,7 +322,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
"""
Sets up the service manager, toolbars, list view, et al.
"""
- super(ServiceManager, self).__init__(parent)
+ super().__init__(parent)
self.active = build_icon(':/media/auto-start_active.png')
self.inactive = build_icon(':/media/auto-start_inactive.png')
self.service_items = []
@@ -600,7 +605,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..179248570 100644
--- a/openlp/core/ui/servicenoteform.py
+++ b/openlp/core/ui/servicenoteform.py
@@ -24,9 +24,11 @@ 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.ui.lib import SpellTextEdit
+from openlp.core.common.i18n import translate
+from openlp.core.common.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
from openlp.core.lib.ui import create_button_box
+from openlp.core.widgets.edits import SpellTextEdit
class ServiceNoteForm(QtWidgets.QDialog, RegistryProperties):
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..e7a86bac2 100644
--- a/openlp/core/ui/settingsform.py
+++ b/openlp/core/ui/settingsform.py
@@ -27,12 +27,13 @@ import logging
from PyQt5 import QtCore, QtWidgets
from openlp.core.api import ApiTab
-from openlp.core.common import Registry, RegistryProperties
+from openlp.core.common.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
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..92a0e789f 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.mixins 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 7f4fa469f..80eb155e0 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -22,7 +22,6 @@
"""
The :mod:`slidecontroller` module contains the most important part of OpenLP - the slide controller
"""
-
import copy
import os
from collections import deque
@@ -30,15 +29,18 @@ 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 LogMixin, RegistryProperties
+from openlp.core.common.registry import Registry, RegistryBase
+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
from openlp.core.ui import HideMode, MainDisplay, Display, DisplayControllerType
+from openlp.core.widgets.toolbar import OpenLPToolbar
+from openlp.core.widgets.views import ListPreviewWidget
# Threshold which has to be trespassed to toggle.
@@ -79,11 +81,11 @@ class DisplayController(QtWidgets.QWidget):
"""
Controller is a general display controller widget.
"""
- def __init__(self, parent):
+ def __init__(self, *args, **kwargs):
"""
Set up the general Controller.
"""
- super(DisplayController, self).__init__(parent)
+ super().__init__(*args, **kwargs)
self.is_live = False
self.display = None
self.controller_type = None
@@ -130,16 +132,16 @@ class InfoLabel(QtWidgets.QLabel):
super().setText(text)
-class SlideController(DisplayController, RegistryProperties):
+class SlideController(DisplayController, LogMixin, RegistryProperties):
"""
SlideController is the slide controller widget. This widget is what the
user uses to control the displaying of verses/slides/etc on the screen.
"""
- def __init__(self, parent):
+ def __init__(self, *args, **kwargs):
"""
Set up the Slide Controller.
"""
- super(SlideController, self).__init__(parent)
+ super().__init__(*args, **kwargs)
def post_set_up(self):
"""
@@ -1502,7 +1504,7 @@ class SlideController(DisplayController, RegistryProperties):
self.display.audio_player.go_to(action.data())
-class PreviewController(RegistryMixin, OpenLPMixin, SlideController):
+class PreviewController(RegistryBase, SlideController):
"""
Set up the Preview Controller.
"""
@@ -1510,11 +1512,12 @@ class PreviewController(RegistryMixin, OpenLPMixin, SlideController):
slidecontroller_preview_next = QtCore.pyqtSignal()
slidecontroller_preview_previous = QtCore.pyqtSignal()
- def __init__(self, parent):
+ def __init__(self, *args, **kwargs):
"""
Set up the base Controller as a preview.
"""
- super(PreviewController, self).__init__(parent)
+ self.__registry_name = 'preview_slidecontroller'
+ super().__init__(*args, **kwargs)
self.split = 0
self.type_prefix = 'preview'
self.category = 'Preview Toolbar'
@@ -1526,7 +1529,7 @@ class PreviewController(RegistryMixin, OpenLPMixin, SlideController):
self.post_set_up()
-class LiveController(RegistryMixin, OpenLPMixin, SlideController):
+class LiveController(RegistryBase, SlideController):
"""
Set up the Live Controller.
"""
@@ -1538,11 +1541,11 @@ class LiveController(RegistryMixin, OpenLPMixin, SlideController):
mediacontroller_live_pause = QtCore.pyqtSignal()
mediacontroller_live_stop = QtCore.pyqtSignal()
- def __init__(self, parent):
+ def __init__(self, *args, **kwargs):
"""
Set up the base Controller as a live.
"""
- super(LiveController, self).__init__(parent)
+ super().__init__(*args, **kwargs)
self.is_live = True
self.split = 1
self.type_prefix = 'live'
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..00d0bff40 100644
--- a/openlp/core/ui/starttimeform.py
+++ b/openlp/core/ui/starttimeform.py
@@ -24,10 +24,11 @@ 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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
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..f8449986a 100644
--- a/openlp/core/ui/themeform.py
+++ b/openlp/core/ui/themeform.py
@@ -23,11 +23,13 @@
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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
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..5b4c5cbb9 100644
--- a/openlp/core/ui/thememanager.py
+++ b/openlp/core/ui/thememanager.py
@@ -28,17 +28,20 @@ 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 LogMixin, RegistryProperties
+from openlp.core.common.path import Path, copyfile, create_paths, path_to_str, rmtree
+from openlp.core.common.registry import Registry, RegistryBase
+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
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
from openlp.core.ui import FileRenameForm, ThemeForm
-from openlp.core.ui.lib import OpenLPToolbar
-from openlp.core.ui.lib.filedialog import FileDialog
+from openlp.core.widgets.dialogs import FileDialog
+from openlp.core.widgets.toolbar import OpenLPToolbar
class Ui_ThemeManager(object):
@@ -122,7 +125,7 @@ class Ui_ThemeManager(object):
self.theme_list_widget.currentItemChanged.connect(self.check_list_state)
-class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManager, RegistryProperties):
+class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, RegistryProperties):
"""
Manages the orders of Theme.
"""
@@ -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..d7bc5f822 100644
--- a/openlp/core/ui/themewizard.py
+++ b/openlp/core/ui/themewizard.py
@@ -24,12 +24,13 @@ 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
-from openlp.core.ui.lib import ColorButton, PathEdit
+from openlp.core.widgets.buttons import ColorButton
+from openlp.core.widgets.edits import PathEdit
class Ui_ThemeWizard(object):
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/tests/functional/openlp_core_api_http/__init__.py b/openlp/core/widgets/__init__.py
similarity index 100%
rename from tests/functional/openlp_core_api_http/__init__.py
rename to openlp/core/widgets/__init__.py
diff --git a/openlp/core/ui/lib/colorbutton.py b/openlp/core/widgets/buttons.py
similarity index 98%
rename from openlp/core/ui/lib/colorbutton.py
rename to openlp/core/widgets/buttons.py
index 662b83033..6eef707b2 100644
--- a/openlp/core/ui/lib/colorbutton.py
+++ b/openlp/core/widgets/buttons.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/filedialog.py b/openlp/core/widgets/dialogs.py
similarity index 100%
rename from openlp/core/ui/lib/filedialog.py
rename to openlp/core/widgets/dialogs.py
diff --git a/openlp/core/ui/lib/mediadockmanager.py b/openlp/core/widgets/docks.py
similarity index 76%
rename from openlp/core/ui/lib/mediadockmanager.py
rename to openlp/core/widgets/docks.py
index 8fee1d55d..a1b4e9789 100644
--- a/openlp/core/ui/lib/mediadockmanager.py
+++ b/openlp/core/widgets/docks.py
@@ -20,15 +20,41 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
-The media manager dock.
+The :mod:`~openlp.core.widgets.docks` module contains a customised base dock widget and dock widgets
"""
import logging
-from openlp.core.lib import StringContent
+from PyQt5 import QtWidgets
+
+from openlp.core.display.screens import ScreenList
+from openlp.core.lib import StringContent, build_icon
log = logging.getLogger(__name__)
+class OpenLPDockWidget(QtWidgets.QDockWidget):
+ """
+ Custom DockWidget class to handle events
+ """
+ def __init__(self, parent=None, name=None, icon=None):
+ """
+ Initialise the DockWidget
+ """
+ log.debug('Initialise the %s widget' % name)
+ super(OpenLPDockWidget, self).__init__(parent)
+ if name:
+ self.setObjectName(name)
+ if icon:
+ self.setWindowIcon(build_icon(icon))
+ # Sort out the minimum width.
+ screens = ScreenList()
+ main_window_docbars = screens.current['size'].width() // 5
+ if main_window_docbars > 300:
+ self.setMinimumWidth(300)
+ else:
+ self.setMinimumWidth(main_window_docbars)
+
+
class MediaDockManager(object):
"""
Provide a repository for MediaManagerItems
diff --git a/openlp/core/widgets/edits.py b/openlp/core/widgets/edits.py
new file mode 100644
index 000000000..c2396810f
--- /dev/null
+++ b/openlp/core/widgets/edits.py
@@ -0,0 +1,573 @@
+# -*- 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:`~openlp.core.widgets.edits` module contains all the customised edit widgets used in OpenLP
+"""
+import logging
+import re
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+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.settings import Settings
+from openlp.core.lib import FormattingTags, build_icon
+from openlp.core.lib.ui import create_widget_action, create_action
+from openlp.core.widgets.dialogs import FileDialog
+from openlp.core.widgets.enums import PathEditType
+
+try:
+ import enchant
+ from enchant import DictNotFoundError
+ from enchant.errors import Error
+ ENCHANT_AVAILABLE = True
+except ImportError:
+ ENCHANT_AVAILABLE = False
+
+log = logging.getLogger(__name__)
+
+
+class SearchEdit(QtWidgets.QLineEdit):
+ """
+ This is a specialised QLineEdit with a "clear" button inside for searches.
+ """
+ searchTypeChanged = QtCore.pyqtSignal(QtCore.QVariant)
+ cleared = QtCore.pyqtSignal()
+
+ def __init__(self, parent, settings_section):
+ """
+ Constructor.
+ """
+ super().__init__(parent)
+ self.settings_section = settings_section
+ self._current_search_type = -1
+ self.clear_button = QtWidgets.QToolButton(self)
+ self.clear_button.setIcon(build_icon(':/system/clear_shortcut.png'))
+ self.clear_button.setCursor(QtCore.Qt.ArrowCursor)
+ self.clear_button.setStyleSheet('QToolButton { border: none; padding: 0px; }')
+ self.clear_button.resize(18, 18)
+ self.clear_button.hide()
+ self.clear_button.clicked.connect(self._on_clear_button_clicked)
+ self.textChanged.connect(self._on_search_edit_text_changed)
+ self._update_style_sheet()
+ self.setAcceptDrops(False)
+
+ def _update_style_sheet(self):
+ """
+ Internal method to update the stylesheet depending on which widgets are available and visible.
+ """
+ frame_width = self.style().pixelMetric(QtWidgets.QStyle.PM_DefaultFrameWidth)
+ right_padding = self.clear_button.width() + frame_width
+ if hasattr(self, 'menu_button'):
+ left_padding = self.menu_button.width()
+ stylesheet = 'QLineEdit {{ padding-left:{left}px; padding-right: {right}px; }} '.format(left=left_padding,
+ right=right_padding)
+ else:
+ stylesheet = 'QLineEdit {{ padding-right: {right}px; }} '.format(right=right_padding)
+ self.setStyleSheet(stylesheet)
+ msz = self.minimumSizeHint()
+ self.setMinimumSize(max(msz.width(), self.clear_button.width() + (frame_width * 2) + 2),
+ max(msz.height(), self.clear_button.height() + (frame_width * 2) + 2))
+
+ def resizeEvent(self, event):
+ """
+ Reimplemented method to react to resizing of the widget.
+
+ :param event: The event that happened.
+ """
+ size = self.clear_button.size()
+ frame_width = self.style().pixelMetric(QtWidgets.QStyle.PM_DefaultFrameWidth)
+ self.clear_button.move(self.rect().right() - frame_width - size.width(),
+ (self.rect().bottom() + 1 - size.height()) // 2)
+ if hasattr(self, 'menu_button'):
+ size = self.menu_button.size()
+ self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) // 2)
+
+ def current_search_type(self):
+ """
+ Readonly property to return the current search type.
+ """
+ return self._current_search_type
+
+ def set_current_search_type(self, identifier):
+ """
+ Set a new current search type.
+
+ :param identifier: The search type identifier (int).
+ """
+ menu = self.menu_button.menu()
+ for action in menu.actions():
+ if identifier == action.data():
+ self.setPlaceholderText(action.placeholder_text)
+ self.menu_button.setDefaultAction(action)
+ self._current_search_type = identifier
+ Settings().setValue('{section}/last used search type'.format(section=self.settings_section), identifier)
+ self.searchTypeChanged.emit(identifier)
+ return True
+
+ def set_search_types(self, items):
+ """
+ A list of tuples to be used in the search type menu. The first item in the list will be preselected as the
+ default.
+
+ :param items: The list of tuples to use. The tuples should contain an integer identifier, an icon (QIcon
+ instance or string) and a title for the item in the menu. In short, they should look like this::
+
+ (, , , )
+
+ For instance::
+
+ (1, , "Titles", "Search Song Titles...")
+
+ Or::
+
+ (2, ":/songs/authors.png", "Authors", "Search Authors...")
+ """
+ menu = QtWidgets.QMenu(self)
+ for identifier, icon, title, placeholder in items:
+ action = create_widget_action(
+ menu, text=title, icon=icon, data=identifier, triggers=self._on_menu_action_triggered)
+ action.placeholder_text = placeholder
+ if not hasattr(self, 'menu_button'):
+ self.menu_button = QtWidgets.QToolButton(self)
+ self.menu_button.setIcon(build_icon(':/system/clear_shortcut.png'))
+ self.menu_button.setCursor(QtCore.Qt.ArrowCursor)
+ self.menu_button.setPopupMode(QtWidgets.QToolButton.InstantPopup)
+ self.menu_button.setStyleSheet('QToolButton { border: none; padding: 0px 10px 0px 0px; }')
+ self.menu_button.resize(QtCore.QSize(28, 18))
+ self.menu_button.setMenu(menu)
+ self.set_current_search_type(
+ Settings().value('{section}/last used search type'.format(section=self.settings_section)))
+ self.menu_button.show()
+ self._update_style_sheet()
+
+ def _on_search_edit_text_changed(self, text):
+ """
+ Internally implemented slot to react to when the text in the line edit has changed so that we can show or hide
+ the clear button.
+
+ :param text: A :class:`~PyQt5.QtCore.QString` instance which represents the text in the line edit.
+ """
+ self.clear_button.setVisible(bool(text))
+
+ def _on_clear_button_clicked(self):
+ """
+ Internally implemented slot to react to the clear button being clicked to clear the line edit. Once it has
+ cleared the line edit, it emits the ``cleared()`` signal so that an application can react to the clearing of the
+ line edit.
+ """
+ self.clear()
+ self.cleared.emit()
+
+ def _on_menu_action_triggered(self):
+ """
+ Internally implemented slot to react to the select of one of the search types in the menu. Once it has set the
+ correct action on the button, and set the current search type (using the list of identifiers provided by the
+ developer), the ``searchTypeChanged(int)`` signal is emitted with the identifier.
+ """
+ for action in self.menu_button.menu().actions():
+ # Why is this needed?
+ action.setChecked(False)
+ self.set_current_search_type(self.sender().data())
+
+
+class PathEdit(QtWidgets.QWidget):
+ """
+ The :class:`~openlp.core.widgets.edits.PathEdit` class subclasses QWidget to create a custom widget for use when
+ a file or directory needs to be selected.
+ """
+ pathChanged = QtCore.pyqtSignal(Path)
+
+ def __init__(self, parent=None, path_type=PathEditType.Files, default_path=None, dialog_caption=None,
+ show_revert=True):
+ """
+ Initialise the PathEdit widget
+
+ :param QtWidget.QWidget | None: The parent of the widget. This is just passed to the super method.
+ :param str dialog_caption: Used to customise the caption in the QFileDialog.
+ :param openlp.core.common.path.Path default_path: The default path. This is set as the path when the revert
+ button is clicked
+ :param bool show_revert: Used to determine if the 'revert button' should be visible.
+ :rtype: None
+ """
+ super().__init__(parent)
+ self.default_path = default_path
+ self.dialog_caption = dialog_caption
+ self._path_type = path_type
+ self._path = None
+ self.filters = '{all_files} (*)'.format(all_files=UiStrings().AllFiles)
+ self._setup(show_revert)
+
+ def _setup(self, show_revert):
+ """
+ Set up the widget
+ :param bool show_revert: Show or hide the revert button
+ :rtype: None
+ """
+ widget_layout = QtWidgets.QHBoxLayout()
+ widget_layout.setContentsMargins(0, 0, 0, 0)
+ self.line_edit = QtWidgets.QLineEdit(self)
+ widget_layout.addWidget(self.line_edit)
+ self.browse_button = QtWidgets.QToolButton(self)
+ self.browse_button.setIcon(build_icon(':/general/general_open.png'))
+ widget_layout.addWidget(self.browse_button)
+ self.revert_button = QtWidgets.QToolButton(self)
+ self.revert_button.setIcon(build_icon(':/general/general_revert.png'))
+ self.revert_button.setVisible(show_revert)
+ widget_layout.addWidget(self.revert_button)
+ self.setLayout(widget_layout)
+ # Signals and Slots
+ self.browse_button.clicked.connect(self.on_browse_button_clicked)
+ self.revert_button.clicked.connect(self.on_revert_button_clicked)
+ self.line_edit.editingFinished.connect(self.on_line_edit_editing_finished)
+ self.update_button_tool_tips()
+
+ @property
+ def path(self):
+ """
+ A property getter method to return the selected path.
+
+ :return: The selected path
+ :rtype: openlp.core.common.path.Path
+ """
+ return self._path
+
+ @path.setter
+ def path(self, path):
+ """
+ A Property setter method to set the selected path
+
+ :param openlp.core.common.path.Path path: The path to set the widget to
+ :rtype: None
+ """
+ self._path = path
+ text = path_to_str(path)
+ self.line_edit.setText(text)
+ self.line_edit.setToolTip(text)
+
+ @property
+ def path_type(self):
+ """
+ A property getter method to return the path_type. Path type allows you to sepecify if the user is restricted to
+ selecting a file or directory.
+
+ :return: The type selected
+ :rtype: PathType
+ """
+ return self._path_type
+
+ @path_type.setter
+ def path_type(self, path_type):
+ """
+ A Property setter method to set the path type
+
+ :param PathType path_type: The type of path to select
+ :rtype: None
+ """
+ self._path_type = path_type
+ self.update_button_tool_tips()
+
+ def update_button_tool_tips(self):
+ """
+ Called to update the tooltips on the buttons. This is changing path types, and when the widget is initalised
+
+ :rtype: None
+ """
+ if self._path_type == PathEditType.Directories:
+ self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for directory.'))
+ self.revert_button.setToolTip(translate('OpenLP.PathEdit', 'Revert to default directory.'))
+ else:
+ self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for file.'))
+ self.revert_button.setToolTip(translate('OpenLP.PathEdit', 'Revert to default file.'))
+
+ def on_browse_button_clicked(self):
+ """
+ A handler to handle a click on the browse button.
+
+ Show the QFileDialog and process the input from the user
+
+ :rtype: None
+ """
+ caption = self.dialog_caption
+ path = None
+ if self._path_type == PathEditType.Directories:
+ if not caption:
+ caption = translate('OpenLP.PathEdit', 'Select Directory')
+ path = FileDialog.getExistingDirectory(self, caption, self._path, FileDialog.ShowDirsOnly)
+ elif self._path_type == PathEditType.Files:
+ if not caption:
+ caption = self.dialog_caption = translate('OpenLP.PathEdit', 'Select File')
+ path, filter_used = FileDialog.getOpenFileName(self, caption, self._path, self.filters)
+ if path:
+ self.on_new_path(path)
+
+ def on_revert_button_clicked(self):
+ """
+ A handler to handle a click on the revert button.
+
+ Set the new path to the value of the default_path instance variable.
+
+ :rtype: None
+ """
+ self.on_new_path(self.default_path)
+
+ def on_line_edit_editing_finished(self):
+ """
+ A handler to handle when the line edit has finished being edited.
+
+ :rtype: None
+ """
+ path = str_to_path(self.line_edit.text())
+ self.on_new_path(path)
+
+ def on_new_path(self, path):
+ """
+ A method called to validate and set a new path.
+
+ Emits the pathChanged Signal
+
+ :param openlp.core.common.path.Path path: The new path
+ :rtype: None
+ """
+ if self._path != path:
+ self.path = path
+ self.pathChanged.emit(path)
+
+
+class SpellTextEdit(QtWidgets.QPlainTextEdit):
+ """
+ Spell checking widget based on QPlanTextEdit.
+
+ Based on code from http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check
+ """
+ def __init__(self, parent=None, formatting_tags_allowed=True):
+ """
+ Constructor.
+ """
+ global ENCHANT_AVAILABLE
+ super(SpellTextEdit, self).__init__(parent)
+ self.formatting_tags_allowed = formatting_tags_allowed
+ # Default dictionary based on the current locale.
+ if ENCHANT_AVAILABLE:
+ try:
+ self.dictionary = enchant.Dict()
+ self.highlighter = Highlighter(self.document())
+ self.highlighter.spelling_dictionary = self.dictionary
+ except (Error, DictNotFoundError):
+ ENCHANT_AVAILABLE = False
+ log.debug('Could not load default dictionary')
+
+ def mousePressEvent(self, event):
+ """
+ Handle mouse clicks within the text edit region.
+ """
+ if event.button() == QtCore.Qt.RightButton:
+ # Rewrite the mouse event to a left button event so the cursor is moved to the location of the pointer.
+ event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
+ event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier)
+ QtWidgets.QPlainTextEdit.mousePressEvent(self, event)
+
+ def contextMenuEvent(self, event):
+ """
+ Provide the context menu for the text edit region.
+ """
+ popup_menu = self.createStandardContextMenu()
+ # Select the word under the cursor.
+ cursor = self.textCursor()
+ # only select text if not already selected
+ if not cursor.hasSelection():
+ cursor.select(QtGui.QTextCursor.WordUnderCursor)
+ self.setTextCursor(cursor)
+ # Add menu with available languages.
+ if ENCHANT_AVAILABLE:
+ lang_menu = QtWidgets.QMenu(translate('OpenLP.SpellTextEdit', 'Language:'))
+ for lang in enchant.list_languages():
+ action = create_action(lang_menu, lang, text=lang, checked=lang == self.dictionary.tag)
+ lang_menu.addAction(action)
+ popup_menu.insertSeparator(popup_menu.actions()[0])
+ popup_menu.insertMenu(popup_menu.actions()[0], lang_menu)
+ lang_menu.triggered.connect(self.set_language)
+ # Check if the selected word is misspelled and offer spelling suggestions if it is.
+ if ENCHANT_AVAILABLE and self.textCursor().hasSelection():
+ text = self.textCursor().selectedText()
+ if not self.dictionary.check(text):
+ spell_menu = QtWidgets.QMenu(translate('OpenLP.SpellTextEdit', 'Spelling Suggestions'))
+ for word in self.dictionary.suggest(text):
+ action = SpellAction(word, spell_menu)
+ action.correct.connect(self.correct_word)
+ spell_menu.addAction(action)
+ # Only add the spelling suggests to the menu if there are suggestions.
+ if spell_menu.actions():
+ popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
+ tag_menu = QtWidgets.QMenu(translate('OpenLP.SpellTextEdit', 'Formatting Tags'))
+ if self.formatting_tags_allowed:
+ for html in FormattingTags.get_html_tags():
+ action = SpellAction(html['desc'], tag_menu)
+ action.correct.connect(self.html_tag)
+ tag_menu.addAction(action)
+ popup_menu.insertSeparator(popup_menu.actions()[0])
+ popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
+ popup_menu.exec(event.globalPos())
+
+ def set_language(self, action):
+ """
+ Changes the language for this spelltextedit.
+
+ :param action: The action.
+ """
+ self.dictionary = enchant.Dict(action.text())
+ self.highlighter.spelling_dictionary = self.dictionary
+ self.highlighter.highlightBlock(self.toPlainText())
+ self.highlighter.rehighlight()
+
+ def correct_word(self, word):
+ """
+ Replaces the selected text with word.
+ """
+ cursor = self.textCursor()
+ cursor.beginEditBlock()
+ cursor.removeSelectedText()
+ cursor.insertText(word)
+ cursor.endEditBlock()
+
+ def html_tag(self, tag):
+ """
+ Replaces the selected text with word.
+ """
+ tag = tag.replace('&', '')
+ for html in FormattingTags.get_html_tags():
+ if tag == html['desc']:
+ cursor = self.textCursor()
+ if self.textCursor().hasSelection():
+ text = cursor.selectedText()
+ cursor.beginEditBlock()
+ cursor.removeSelectedText()
+ cursor.insertText(html['start tag'])
+ cursor.insertText(text)
+ cursor.insertText(html['end tag'])
+ cursor.endEditBlock()
+ else:
+ cursor = self.textCursor()
+ cursor.insertText(html['start tag'])
+ cursor.insertText(html['end tag'])
+
+
+class Highlighter(QtGui.QSyntaxHighlighter):
+ """
+ Provides a text highlighter for pointing out spelling errors in text.
+ """
+ WORDS = r'(?iu)[\w\']+'
+
+ def __init__(self, *args):
+ """
+ Constructor
+ """
+ super(Highlighter, self).__init__(*args)
+ self.spelling_dictionary = None
+
+ def highlightBlock(self, text):
+ """
+ Highlight mis spelt words in a block of text.
+
+ Note, this is a Qt hook.
+ """
+ if not self.spelling_dictionary:
+ return
+ text = str(text)
+ char_format = QtGui.QTextCharFormat()
+ char_format.setUnderlineColor(QtCore.Qt.red)
+ char_format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
+ for word_object in re.finditer(self.WORDS, text):
+ if not self.spelling_dictionary.check(word_object.group()):
+ self.setFormat(word_object.start(), word_object.end() - word_object.start(), char_format)
+
+
+class SpellAction(QtWidgets.QAction):
+ """
+ A special QAction that returns the text in a signal.
+ """
+ correct = QtCore.pyqtSignal(str)
+
+ def __init__(self, *args):
+ """
+ Constructor
+ """
+ super(SpellAction, self).__init__(*args)
+ self.triggered.connect(lambda x: self.correct.emit(self.text()))
+
+
+class HistoryComboBox(QtWidgets.QComboBox):
+ """
+ The :class:`~openlp.core.common.historycombobox.HistoryComboBox` widget emulates the QLineEdit ``returnPressed``
+ signal for when the :kbd:`Enter` or :kbd:`Return` keys are pressed, and saves anything that is typed into the edit
+ box into its list.
+ """
+ returnPressed = QtCore.pyqtSignal()
+
+ def __init__(self, parent=None):
+ """
+ Initialise the combo box, setting duplicates to False and the insert policy to insert items at the top.
+
+ :param parent: The parent widget
+ """
+ super().__init__(parent)
+ self.setDuplicatesEnabled(False)
+ self.setEditable(True)
+ self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
+ self.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
+
+ def keyPressEvent(self, event):
+ """
+ Override the inherited keyPressEvent method to emit the ``returnPressed`` signal and to save the current text to
+ the dropdown list.
+
+ :param event: The keyboard event
+ """
+ # Handle Enter and Return ourselves
+ if event.key() == QtCore.Qt.Key_Enter or event.key() == QtCore.Qt.Key_Return:
+ # Emit the returnPressed signal
+ self.returnPressed.emit()
+ # Save the current text to the dropdown list
+ if self.currentText() and self.findText(self.currentText()) == -1:
+ self.insertItem(0, self.currentText())
+ # Let the parent handle any keypress events
+ super().keyPressEvent(event)
+
+ def focusOutEvent(self, event):
+ """
+ Override the inherited focusOutEvent to save the current text to the dropdown list.
+
+ :param event: The focus event
+ """
+ # Save the current text to the dropdown list
+ if self.currentText() and self.findText(self.currentText()) == -1:
+ self.insertItem(0, self.currentText())
+ # Let the parent handle any keypress events
+ super().focusOutEvent(event)
+
+ def getItems(self):
+ """
+ Get all the items from the history
+
+ :return: A list of strings
+ """
+ return [self.itemText(i) for i in range(self.count())]
diff --git a/openlp/core/ui/lib/__init__.py b/openlp/core/widgets/enums.py
similarity index 69%
rename from openlp/core/ui/lib/__init__.py
rename to openlp/core/widgets/enums.py
index cf55b9d20..f79dd775c 100644
--- a/openlp/core/ui/lib/__init__.py
+++ b/openlp/core/widgets/enums.py
@@ -19,18 +19,12 @@
# 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.widgets.enums` module contains enumerations used by the widgets
+"""
+from enum import Enum
-from .colorbutton import ColorButton
-from .listpreviewwidget import ListPreviewWidget
-from .listwidgetwithdnd import ListWidgetWithDnD
-from .mediadockmanager import MediaDockManager
-from .dockwidget import OpenLPDockWidget
-from .toolbar import OpenLPToolbar
-from .wizard import OpenLPWizard, WizardStrings
-from .pathedit import PathEdit, PathType
-from .spelltextedit import SpellTextEdit
-from .treewidgetwithdnd import TreeWidgetWithDnD
-__all__ = ['ColorButton', 'ListPreviewWidget', 'ListWidgetWithDnD', 'MediaDockManager', 'OpenLPDockWidget',
- 'OpenLPToolbar', 'OpenLPWizard', 'PathEdit', 'PathType', 'SpellTextEdit', 'TreeWidgetWithDnD',
- 'WizardStrings']
+class PathEditType(Enum):
+ Files = 1
+ Directories = 2
diff --git a/openlp/core/ui/lib/toolbar.py b/openlp/core/widgets/toolbar.py
similarity index 98%
rename from openlp/core/ui/lib/toolbar.py
rename to openlp/core/widgets/toolbar.py
index 68343889f..8c76ce50e 100644
--- a/openlp/core/ui/lib/toolbar.py
+++ b/openlp/core/widgets/toolbar.py
@@ -40,7 +40,7 @@ class OpenLPToolbar(QtWidgets.QToolBar):
"""
Initialise the toolbar.
"""
- super(OpenLPToolbar, self).__init__(parent)
+ super().__init__(parent)
# useful to be able to reuse button icons...
self.setIconSize(QtCore.QSize(20, 20))
self.actions = {}
diff --git a/openlp/core/ui/lib/listpreviewwidget.py b/openlp/core/widgets/views.py
similarity index 56%
rename from openlp/core/ui/lib/listpreviewwidget.py
rename to openlp/core/widgets/views.py
index c5c2b01d2..219dd145f 100644
--- a/openlp/core/ui/lib/listpreviewwidget.py
+++ b/openlp/core/widgets/views.py
@@ -23,10 +23,15 @@
The :mod:`listpreviewwidget` is a widget that lists the slides in the slide controller.
It is based on a QTableWidget but represents its contents in list form.
"""
+import os
from PyQt5 import QtCore, QtGui, QtWidgets
-from openlp.core.common import RegistryProperties, Settings
+from openlp.core.common import is_win
+from openlp.core.common.i18n import UiStrings
+from openlp.core.common.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+from openlp.core.common.settings import Settings
from openlp.core.lib import ImageSource, ItemCapabilities, ServiceItem
@@ -237,3 +242,241 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
Returns the number of slides this widget holds.
"""
return super(ListPreviewWidget, self).rowCount()
+
+
+class ListWidgetWithDnD(QtWidgets.QListWidget):
+ """
+ Provide a list widget to store objects and handle drag and drop events
+ """
+ def __init__(self, parent=None, name=''):
+ """
+ Initialise the list widget
+ """
+ super().__init__(parent)
+ self.mime_data_text = name
+ self.no_results_text = UiStrings().NoResults
+ self.setSpacing(1)
+ self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.setAlternatingRowColors(True)
+ self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+
+ def activateDnD(self):
+ """
+ Activate DnD of widget
+ """
+ self.setAcceptDrops(True)
+ self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
+ Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)
+
+ def clear(self, search_while_typing=False):
+ """
+ Re-implement clear, so that we can customise feedback when using 'Search as you type'
+
+ :param search_while_typing: True if we want to display the customised message
+ :return: None
+ """
+ if search_while_typing:
+ self.no_results_text = UiStrings().ShortResults
+ else:
+ self.no_results_text = UiStrings().NoResults
+ super().clear()
+
+ def mouseMoveEvent(self, event):
+ """
+ Drag and drop event does not care what data is selected as the recipient will use events to request the data
+ move just tell it what plugin to call
+ """
+ if event.buttons() != QtCore.Qt.LeftButton:
+ event.ignore()
+ return
+ if not self.selectedItems():
+ event.ignore()
+ return
+ drag = QtGui.QDrag(self)
+ mime_data = QtCore.QMimeData()
+ drag.setMimeData(mime_data)
+ mime_data.setText(self.mime_data_text)
+ drag.exec(QtCore.Qt.CopyAction)
+
+ def dragEnterEvent(self, event):
+ """
+ When something is dragged into this object, check if you should be able to drop it in here.
+ """
+ if event.mimeData().hasUrls():
+ event.accept()
+ else:
+ event.ignore()
+
+ def dragMoveEvent(self, event):
+ """
+ Make an object droppable, and set it to copy the contents of the object, not move it.
+ """
+ if event.mimeData().hasUrls():
+ event.setDropAction(QtCore.Qt.CopyAction)
+ event.accept()
+ else:
+ event.ignore()
+
+ def dropEvent(self, event):
+ """
+ Receive drop event check if it is a file and process it if it is.
+
+ :param event: Handle of the event pint passed
+ """
+ if event.mimeData().hasUrls():
+ event.setDropAction(QtCore.Qt.CopyAction)
+ event.accept()
+ files = []
+ for url in event.mimeData().urls():
+ local_file = os.path.normpath(url.toLocalFile())
+ if os.path.isfile(local_file):
+ files.append(local_file)
+ elif os.path.isdir(local_file):
+ listing = os.listdir(local_file)
+ for file in listing:
+ files.append(os.path.join(local_file, file))
+ Registry().execute('{mime_data}_dnd'.format(mime_data=self.mime_data_text),
+ {'files': files})
+ else:
+ event.ignore()
+
+ def allItems(self):
+ """
+ An generator to list all the items in the widget
+
+ :return: a generator
+ """
+ for row in range(self.count()):
+ yield self.item(row)
+
+ def paintEvent(self, event):
+ """
+ Re-implement paintEvent so that we can add 'No Results' text when the listWidget is empty.
+
+ :param event: A QPaintEvent
+ :return: None
+ """
+ super().paintEvent(event)
+ if not self.count():
+ viewport = self.viewport()
+ painter = QtGui.QPainter(viewport)
+ font = QtGui.QFont()
+ font.setItalic(True)
+ painter.setFont(font)
+ painter.drawText(QtCore.QRect(0, 0, viewport.width(), viewport.height()),
+ (QtCore.Qt.AlignHCenter | QtCore.Qt.TextWordWrap), self.no_results_text)
+
+
+class TreeWidgetWithDnD(QtWidgets.QTreeWidget):
+ """
+ Provide a tree widget to store objects and handle drag and drop events
+ """
+ def __init__(self, parent=None, name=''):
+ """
+ Initialise the tree widget
+ """
+ super(TreeWidgetWithDnD, self).__init__(parent)
+ self.mime_data_text = name
+ self.allow_internal_dnd = False
+ self.header().close()
+ self.default_indentation = self.indentation()
+ self.setIndentation(0)
+ self.setAnimated(True)
+
+ def activateDnD(self):
+ """
+ Activate DnD of widget
+ """
+ self.setAcceptDrops(True)
+ self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
+ Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)
+ Registry().register_function(('%s_dnd_internal' % self.mime_data_text), self.parent().dnd_move_internal)
+
+ def mouseMoveEvent(self, event):
+ """
+ Drag and drop event does not care what data is selected as the recipient will use events to request the data
+ move just tell it what plugin to call
+
+ :param event: The event that occurred
+ """
+ if event.buttons() != QtCore.Qt.LeftButton:
+ event.ignore()
+ return
+ if not self.selectedItems():
+ event.ignore()
+ return
+ drag = QtGui.QDrag(self)
+ mime_data = QtCore.QMimeData()
+ drag.setMimeData(mime_data)
+ mime_data.setText(self.mime_data_text)
+ drag.exec(QtCore.Qt.CopyAction)
+
+ def dragEnterEvent(self, event):
+ """
+ Receive drag enter event, check if it is a file or internal object and allow it if it is.
+
+ :param event: The event that occurred
+ """
+ if event.mimeData().hasUrls():
+ event.accept()
+ elif self.allow_internal_dnd:
+ event.accept()
+ else:
+ event.ignore()
+
+ def dragMoveEvent(self, event):
+ """
+ Receive drag move event, check if it is a file or internal object and allow it if it is.
+
+ :param event: The event that occurred
+ """
+ QtWidgets.QTreeWidget.dragMoveEvent(self, event)
+ if event.mimeData().hasUrls():
+ event.setDropAction(QtCore.Qt.CopyAction)
+ event.accept()
+ elif self.allow_internal_dnd:
+ event.setDropAction(QtCore.Qt.CopyAction)
+ event.accept()
+ else:
+ event.ignore()
+
+ def dropEvent(self, event):
+ """
+ Receive drop event, check if it is a file or internal object and process it if it is.
+
+ :param event: Handle of the event pint passed
+ """
+ # If we are on Windows, OpenLP window will not be set on top. For example, user can drag images to Library and
+ # the folder stays on top of the group creation box. This piece of code fixes this issue.
+ if is_win():
+ self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
+ self.setWindowState(QtCore.Qt.WindowNoState)
+ if event.mimeData().hasUrls():
+ event.setDropAction(QtCore.Qt.CopyAction)
+ event.accept()
+ files = []
+ for url in event.mimeData().urls():
+ local_file = url.toLocalFile()
+ if os.path.isfile(local_file):
+ files.append(local_file)
+ elif os.path.isdir(local_file):
+ listing = os.listdir(local_file)
+ for file_name in listing:
+ files.append(os.path.join(local_file, file_name))
+ Registry().execute('%s_dnd' % self.mime_data_text, {'files': files, 'target': self.itemAt(event.pos())})
+ elif self.allow_internal_dnd:
+ event.setDropAction(QtCore.Qt.CopyAction)
+ event.accept()
+ Registry().execute('%s_dnd_internal' % self.mime_data_text, self.itemAt(event.pos()))
+ else:
+ event.ignore()
+
+ # Convenience methods for emulating a QListWidget. This helps keeping MediaManagerItem simple.
+ def addItem(self, item):
+ self.addTopLevelItem(item)
+
+ def count(self):
+ return self.topLevelItemCount()
+
+ def item(self, index):
+ return self.topLevelItem(index)
diff --git a/openlp/core/ui/lib/wizard.py b/openlp/core/widgets/wizard.py
similarity index 97%
rename from openlp/core/ui/lib/wizard.py
rename to openlp/core/widgets/wizard.py
index 677949b33..dc8288bc9 100644
--- a/openlp/core/ui/lib/wizard.py
+++ b/openlp/core/widgets/wizard.py
@@ -23,14 +23,17 @@
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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+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
+from openlp.core.widgets.dialogs import FileDialog
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..e5ae93093 100644
--- a/openlp/plugins/alerts/lib/alertsmanager.py
+++ b/openlp/plugins/alerts/lib/alertsmanager.py
@@ -23,13 +23,15 @@
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 LogMixin, RegistryProperties
+from openlp.core.common.registry import Registry, RegistryBase
+from openlp.core.common.settings import Settings
-class AlertsManager(OpenLPMixin, RegistryMixin, QtCore.QObject, RegistryProperties):
+class AlertsManager(QtCore.QObject, RegistryBase, LogMixin, RegistryProperties):
"""
AlertsManager manages the settings of Alerts.
"""
diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py
index f5934a30e..51d92cf06 100644
--- a/openlp/plugins/alerts/lib/alertstab.py
+++ b/openlp/plugins/alerts/lib/alertstab.py
@@ -22,10 +22,11 @@
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
+from openlp.core.widgets.buttons import ColorButton
class AlertsTab(SettingsTab):
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..111e361f8 100644
--- a/openlp/plugins/bibles/forms/bibleimportform.py
+++ b/openlp/plugins/bibles/forms/bibleimportform.py
@@ -34,12 +34,14 @@ 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
-from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
+from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
from openlp.plugins.bibles.lib.db import clean_filename
from openlp.plugins.bibles.lib.importers.http import CWExtract, BGExtract, BSExtract
from openlp.plugins.bibles.lib.manager import BibleFormat
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..8899087ba 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.mixins 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..5083d7f6b 100644
--- a/openlp/plugins/bibles/lib/bibleimport.py
+++ b/openlp/plugins/bibles/lib/bibleimport.py
@@ -23,13 +23,15 @@
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 LogMixin, RegistryProperties
+from openlp.core.common.registry import Registry
+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
-class BibleImport(OpenLPMixin, RegistryProperties, BibleDB):
+class BibleImport(BibleDB, LogMixin, RegistryProperties):
"""
Helper class to import bibles from a third party source into OpenLP
"""
@@ -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..bc8ce4150 100644
--- a/openlp/plugins/bibles/lib/db.py
+++ b/openlp/plugins/bibles/lib/db.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 chardet
import logging
import os
@@ -33,7 +32,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 +547,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/tests/functional/openlp_core_common/__init__.py b/openlp/plugins/bibles/lib/importers/__init__.py
similarity index 100%
rename from tests/functional/openlp_core_common/__init__.py
rename to openlp/plugins/bibles/lib/importers/__init__.py
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..b88dbe7a9 100644
--- a/openlp/plugins/bibles/lib/importers/http.py
+++ b/openlp/plugins/bibles/lib/importers/http.py
@@ -30,9 +30,11 @@ 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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+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..7290ca862 100644
--- a/openlp/plugins/bibles/lib/manager.py
+++ b/openlp/plugins/bibles/lib/manager.py
@@ -22,8 +22,12 @@
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 LogMixin, RegistryProperties
from openlp.core.common.path import Path
+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
@@ -93,7 +97,7 @@ class BibleFormat(object):
]
-class BibleManager(OpenLPMixin, RegistryProperties):
+class BibleManager(LogMixin, RegistryProperties):
"""
The Bible manager which holds and manages all the Bibles.
"""
diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py
index a05747705..fdfc4b51d 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.core.widgets.edits import SearchEdit
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..5ccc94cc6 100644
--- a/openlp/plugins/custom/forms/editcustomslidedialog.py
+++ b/openlp/plugins/custom/forms/editcustomslidedialog.py
@@ -22,10 +22,10 @@
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
+from openlp.core.widgets.edits import SpellTextEdit
class Ui_CustomSlideEditDialog(object):
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..e2b64a6cc 100644
--- a/openlp/plugins/images/lib/imagetab.py
+++ b/openlp/plugins/images/lib/imagetab.py
@@ -22,9 +22,10 @@
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
+from openlp.core.widgets.buttons import ColorButton
class ImageTab(SettingsTab):
diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py
index 5eac4b3b5..a4e76d51b 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.core.widgets.views import TreeWidgetWithDnD
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..af08870df 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.mixins 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..ed787166b 100644
--- a/openlp/plugins/media/lib/mediaitem.py
+++ b/openlp/plugins/media/lib/mediaitem.py
@@ -25,15 +25,17 @@ 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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+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 +304,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 e4b45465c..befa93601 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 5ad46d0fe..2bfd1b3ed 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 fa253ffda..df46dc7d5 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 547636026..0a403df37 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 304d70833..dd099c130 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..d6a4f5190 100644
--- a/openlp/plugins/presentations/lib/presentationtab.py
+++ b/openlp/plugins/presentations/lib/presentationtab.py
@@ -22,11 +22,11 @@
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
+from openlp.core.widgets.edits import PathEdit
from openlp.plugins.presentations.lib.pdfcontroller import PdfController
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 0135b35ba..8e7724808 100644
--- a/openlp/plugins/songs/forms/duplicatesongremovalform.py
+++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py
@@ -28,8 +28,10 @@ import multiprocessing
from PyQt5 import QtCore, QtWidgets
-from openlp.core.common import Registry, RegistryProperties, translate
-from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
+from openlp.core.common.i18n import translate
+from openlp.core.common.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
from openlp.plugins.songs.lib import delete_song
from openlp.plugins.songs.lib.db import Song
from openlp.plugins.songs.forms.songreviewwidget import SongReviewWidget
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..fa475a63f 100644
--- a/openlp/plugins/songs/forms/editsongform.py
+++ b/openlp/plugins/songs/forms/editsongform.py
@@ -24,17 +24,18 @@ 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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
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
+from openlp.core.widgets.dialogs import FileDialog
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
from openlp.plugins.songs.forms.editverseform import EditVerseForm
from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm
@@ -1066,7 +1067,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 04e3a8c8f..76dc70c17 100644
--- a/openlp/plugins/songs/forms/editversedialog.py
+++ b/openlp/plugins/songs/forms/editversedialog.py
@@ -22,10 +22,11 @@
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.core.widgets.edits import SpellTextEdit
from openlp.plugins.songs.lib import VerseType
diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py
index 51dae7b4b..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__)
@@ -232,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..c6446d74c 100644
--- a/openlp/plugins/songs/forms/songexportform.py
+++ b/openlp/plugins/songs/forms/songexportform.py
@@ -27,11 +27,14 @@ 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
-from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
+from openlp.core.widgets.edits import PathEdit
+from openlp.core.widgets.enums import PathEditType
+from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
from openlp.plugins.songs.lib.db import Song
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
@@ -122,7 +125,7 @@ class SongExportForm(OpenLPWizard):
self.selected_list_widget.setObjectName('selected_list_widget')
self.grid_layout.addWidget(self.selected_list_widget, 1, 0, 1, 2)
self.output_directory_path_edit = PathEdit(
- self.export_song_page, PathType.Directories,
+ self.export_song_page, PathEditType.Directories,
dialog_caption=translate('SongsPlugin.ExportWizardForm', 'Select Destination Folder'), show_revert=False)
self.output_directory_path_edit.path = Settings().value('songs/last directory export')
self.directory_label = QtWidgets.QLabel(self.export_song_page)
diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py
index 5a88ad4cb..af88bfb35 100644
--- a/openlp/plugins/songs/forms/songimportform.py
+++ b/openlp/plugins/songs/forms/songimportform.py
@@ -26,11 +26,14 @@ 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.mixins 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
-from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
+from openlp.core.widgets.dialogs import FileDialog
+from openlp.core.widgets.edits import PathEdit
+from openlp.core.widgets.enums import PathEditType
+from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect
log = logging.getLogger(__name__)
@@ -191,7 +194,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:
@@ -382,10 +384,10 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
file_path_label = QtWidgets.QLabel(import_widget)
file_path_layout.addWidget(file_path_label)
if select_mode == SongFormatSelect.SingleFile:
- path_type = PathType.Files
+ path_type = PathEditType.Files
dialog_caption = WizardStrings.OpenTypeFile.format(file_type=format_name)
else:
- path_type = PathType.Directories
+ path_type = PathEditType.Directories
dialog_caption = WizardStrings.OpenTypeFolder.format(folder_name=format_name)
path_edit = PathEdit(
parent=import_widget, path_type=path_type, dialog_caption=dialog_caption, show_revert=False)
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..7f0236d24 100644
--- a/openlp/plugins/songs/forms/songmaintenanceform.py
+++ b/openlp/plugins/songs/forms/songmaintenanceform.py
@@ -24,9 +24,10 @@ 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.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
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..bc0bd7415 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.widgets.edits 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 a82d357d0..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
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..e306dae87 100644
--- a/openlp/plugins/songs/lib/importer.py
+++ b/openlp/plugins/songs/lib/importer.py
@@ -22,11 +22,11 @@
"""
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.ui.lib.wizard import WizardStrings
+from openlp.core.common import is_win
+from openlp.core.common.i18n import UiStrings, translate
+from openlp.core.widgets.wizard import WizardStrings
from .importers.opensong import OpenSongImport
from .importers.easyslides import EasySlidesImport
from .importers.openlp import OpenLPSongImport
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..8f096099f 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
@@ -44,7 +44,7 @@ NOTE_REGEX = re.compile(r'\(.*?\)')
log = logging.getLogger(__name__)
-class FieldDescEntry:
+class FieldDescEntry(object):
def __init__(self, name, field_type, size):
self.name = name
self.field_type = field_type
diff --git a/openlp/plugins/songs/lib/importers/foilpresenter.py b/openlp/plugins/songs/lib/importers/foilpresenter.py
index 98ec9d6ac..f164dfda2 100644
--- a/openlp/plugins/songs/lib/importers/foilpresenter.py
+++ b/openlp/plugins/songs/lib/importers/foilpresenter.py
@@ -87,8 +87,8 @@ import re
from lxml import etree, objectify
-from openlp.core.lib import translate
-from openlp.core.ui.lib.wizard import WizardStrings
+from openlp.core.common.i18n import translate
+from openlp.core.widgets.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
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..80a981d02 100644
--- a/openlp/plugins/songs/lib/importers/openlp.py
+++ b/openlp/plugins/songs/lib/importers/openlp.py
@@ -29,9 +29,9 @@ 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.core.widgets.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic, MediaFile
from .songimport import SongImport
diff --git a/openlp/plugins/songs/lib/importers/openlyrics.py b/openlp/plugins/songs/lib/importers/openlyrics.py
index 44f5f96bf..f443cfb34 100644
--- a/openlp/plugins/songs/lib/importers/openlyrics.py
+++ b/openlp/plugins/songs/lib/importers/openlyrics.py
@@ -27,7 +27,7 @@ import logging
from lxml import etree
-from openlp.core.ui.lib.wizard import WizardStrings
+from openlp.core.widgets.wizard import WizardStrings
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, OpenLyricsError
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/powerpraise.py b/openlp/plugins/songs/lib/importers/powerpraise.py
index a08652e3f..fd6f64360 100644
--- a/openlp/plugins/songs/lib/importers/powerpraise.py
+++ b/openlp/plugins/songs/lib/importers/powerpraise.py
@@ -25,8 +25,8 @@ Powerpraise song files into the current database.
"""
from lxml import objectify
-from openlp.core.ui.lib.wizard import WizardStrings
-from .songimport import SongImport
+from openlp.core.widgets.wizard import WizardStrings
+from openlp.plugins.songs.lib.importers.songimport import SongImport
class PowerPraiseImport(SongImport):
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..c8f9a16f9 100644
--- a/openlp/plugins/songs/lib/importers/presentationmanager.py
+++ b/openlp/plugins/songs/lib/importers/presentationmanager.py
@@ -27,9 +27,10 @@ import re
from lxml import objectify, etree
-from openlp.core.common import get_file_encoding, translate
-from openlp.core.ui.lib.wizard import WizardStrings
-from .songimport import SongImport
+from openlp.core.common.i18n import translate
+from openlp.core.common import get_file_encoding
+from openlp.core.widgets.wizard import WizardStrings
+from openlp.plugins.songs.lib.importers.songimport import SongImport
class PresentationManagerImport(SongImport):
diff --git a/openlp/plugins/songs/lib/importers/propresenter.py b/openlp/plugins/songs/lib/importers/propresenter.py
index 582b1a6ee..0de0e5300 100644
--- a/openlp/plugins/songs/lib/importers/propresenter.py
+++ b/openlp/plugins/songs/lib/importers/propresenter.py
@@ -27,9 +27,9 @@ import base64
import logging
from lxml import objectify
-from openlp.core.ui.lib.wizard import WizardStrings
+from openlp.core.widgets.wizard import WizardStrings
from openlp.plugins.songs.lib import strip_rtf
-from .songimport import SongImport
+from openlp.plugins.songs.lib.importers.songimport import SongImport
log = logging.getLogger(__name__)
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..a67c17fe7 100644
--- a/openlp/plugins/songs/lib/importers/songimport.py
+++ b/openlp/plugins/songs/lib/importers/songimport.py
@@ -25,9 +25,11 @@ 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.ui.lib.wizard import WizardStrings
+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.widgets.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings
@@ -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/songshowplus.py b/openlp/plugins/songs/lib/importers/songshowplus.py
index 2fcf414dd..3109fae1d 100644
--- a/openlp/plugins/songs/lib/importers/songshowplus.py
+++ b/openlp/plugins/songs/lib/importers/songshowplus.py
@@ -27,7 +27,7 @@ import logging
import re
import struct
-from openlp.core.ui.lib.wizard import WizardStrings
+from openlp.core.widgets.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
from openlp.plugins.songs.lib.importers.songimport import SongImport
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 a036b0006..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)
diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py
index 430e37da5..b07298253 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.mixins 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 8edb64e84..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
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..808c05b57 100644
--- a/openlp/plugins/songs/reporting.py
+++ b/openlp/plugins/songs/reporting.py
@@ -25,10 +25,11 @@ 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.core.widgets.dialogs import FileDialog
from openlp.plugins.songs.lib.db import Song
log = logging.getLogger(__name__)
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..9042756d6 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.mixins 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..7f6d1c16b 100644
--- a/openlp/plugins/songusage/forms/songusagedetaildialog.py
+++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py
@@ -21,10 +21,11 @@
###############################################################################
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
+from openlp.core.widgets.edits import PathEdit
+from openlp.core.widgets.enums import PathEditType
class Ui_SongUsageDetailDialog(object):
@@ -68,7 +69,7 @@ class Ui_SongUsageDetailDialog(object):
self.file_horizontal_layout.setSpacing(8)
self.file_horizontal_layout.setContentsMargins(8, 8, 8, 8)
self.file_horizontal_layout.setObjectName('file_horizontal_layout')
- self.report_path_edit = PathEdit(self.file_group_box, path_type=PathType.Directories, show_revert=False)
+ self.report_path_edit = PathEdit(self.file_group_box, path_type=PathEditType.Directories, show_revert=False)
self.file_horizontal_layout.addWidget(self.report_path_edit)
self.vertical_layout.addWidget(self.file_group_box)
self.button_box = create_button_box(song_usage_detail_dialog, 'button_box', ['cancel', 'ok'])
diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py
index 930baf1d6..147e26d10 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.mixins 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 8d5e08322..a49661143 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/interfaces/openlp_core_api/__init__.py b/tests/functional/openlp_core/api/__init__.py
similarity index 100%
rename from tests/interfaces/openlp_core_api/__init__.py
rename to tests/functional/openlp_core/api/__init__.py
diff --git a/tests/interfaces/openlp_core_common/__init__.py b/tests/functional/openlp_core/api/http/__init__.py
similarity index 100%
rename from tests/interfaces/openlp_core_common/__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/interfaces/openlp_core_lib/__init__.py b/tests/functional/openlp_core/common/__init__.py
similarity index 100%
rename from tests/interfaces/openlp_core_lib/__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_mixins.py b/tests/functional/openlp_core/common/test_mixins.py
new file mode 100644
index 000000000..8eee60c6f
--- /dev/null
+++ b/tests/functional/openlp_core/common/test_mixins.py
@@ -0,0 +1,98 @@
+# -*- 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.common package.
+"""
+from unittest import TestCase
+from unittest.mock import MagicMock, patch
+
+from openlp.core.common.mixins import RegistryProperties
+from openlp.core.common.registry import Registry
+
+
+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.mixins.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.mixins.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_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 85%
rename from tests/functional/openlp_core_common/test_registry.py
rename to tests/functional/openlp_core/common/test_registry.py
index ad30efb73..a274243bd 100644
--- a/tests/functional/openlp_core_common/test_registry.py
+++ b/tests/functional/openlp_core/common/test_registry.py
@@ -26,7 +26,7 @@ import os
from unittest import TestCase
from unittest.mock import MagicMock
-from openlp.core.common import Registry
+from openlp.core.common.registry import Registry, RegistryBase
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..', 'resources'))
@@ -149,3 +149,42 @@ class TestRegistry(TestCase):
def dummy_function_2(self):
return "function_2"
+
+
+class PlainStub(object):
+ def __init__(self):
+ pass
+
+
+class RegistryStub(RegistryBase):
+ def __init__(self):
+ super().__init__()
+
+
+class TestRegistryBase(TestCase):
+
+ def test_registry_mixin_missing(self):
+ """
+ Test the registry creation and its usage
+ """
+ # GIVEN: A new registry
+ Registry.create()
+
+ # WHEN: I create an instance of a class that doesn't inherit from RegistryMixin
+ PlainStub()
+
+ # 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):
+ """
+ Test the registry creation and its usage
+ """
+ # GIVEN: A new registry
+ Registry.create()
+
+ # WHEN: I create an instance of a class that inherits from RegistryMixin
+ RegistryStub()
+
+ # THEN: The bootstrap methods should be registered
+ self.assertEqual(len(Registry().functions_list), 2), 'The bootstrap functions should be in the dict.'
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/openlp/core/ui/lib/dockwidget.py b/tests/functional/openlp_core/display/__init__.py
similarity index 61%
rename from openlp/core/ui/lib/dockwidget.py
rename to tests/functional/openlp_core/display/__init__.py
index d8bcab0fa..96d55e7e8 100644
--- a/openlp/core/ui/lib/dockwidget.py
+++ b/tests/functional/openlp_core/display/__init__.py
@@ -19,38 +19,6 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
-
"""
-Provide additional functionality required by OpenLP from the inherited QDockWidget.
+Test the :mod:`~openlp.core.display` module
"""
-
-import logging
-
-from PyQt5 import QtWidgets
-
-from openlp.core.lib import ScreenList, build_icon
-
-log = logging.getLogger(__name__)
-
-
-class OpenLPDockWidget(QtWidgets.QDockWidget):
- """
- Custom DockWidget class to handle events
- """
- def __init__(self, parent=None, name=None, icon=None):
- """
- Initialise the DockWidget
- """
- log.debug('Initialise the %s widget' % name)
- super(OpenLPDockWidget, self).__init__(parent)
- if name:
- self.setObjectName(name)
- if icon:
- self.setWindowIcon(build_icon(icon))
- # Sort out the minimum width.
- screens = ScreenList()
- main_window_docbars = screens.current['size'].width() // 5
- if main_window_docbars > 300:
- self.setMinimumWidth(300)
- else:
- self.setMinimumWidth(main_window_docbars)
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..eccb81447
--- /dev/null
+++ b/tests/functional/openlp_core/test_app.py
@@ -0,0 +1,365 @@
+# -*- 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
+
+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
+ """
+ def setUp(self):
+ self.build_settings()
+ self.qapplication_patcher = patch('openlp.core.app.QtGui.QApplication')
+ self.mocked_qapplication = self.qapplication_patcher.start()
+ self.openlp = OpenLP([])
+
+ def tearDown(self):
+ self.qapplication_patcher.stop()
+ self.destroy_settings()
+ del self.openlp
+ self.openlp = None
+
+ def test_exec(self):
+ """
+ Test the exec method
+ """
+ # GIVEN: An app
+ self.openlp.shared_memory = MagicMock()
+ self.mocked_qapplication.exec.return_value = False
+
+ # WHEN: exec() is called
+ result = self.openlp.exec()
+
+ # THEN: The right things should be called
+ assert self.openlp.is_event_loop_active is True
+ self.mocked_qapplication.exec.assert_called_once_with()
+ self.openlp.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
+
+ # WHEN: is_already_running() is called
+ result = self.openlp.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
+
+ # WHEN: is_already_running() is called
+ result = self.openlp.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
+
+ # WHEN: is_already_running() is called
+ result = self.openlp.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
+ # WHEN: process_events() is called
+ with patch.object(self.openlp, 'processEvents') as mocked_processEvents:
+ self.openlp.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
+ # WHEN: set_busy_cursor() is called
+ with patch.object(self.openlp, 'setOverrideCursor') as mocked_setOverrideCursor, \
+ patch.object(self.openlp, 'processEvents') as mocked_processEvents:
+ self.openlp.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
+ # WHEN: set_normal_cursor() is called
+ with patch.object(self.openlp, 'restoreOverrideCursor') as mocked_restoreOverrideCursor, \
+ patch.object(self.openlp, 'processEvents') as mocked_processEvents:
+ self.openlp.set_normal_cursor()
+
+ # THEN: The cursor should have been set
+ mocked_restoreOverrideCursor.assert_called_once_with()
+ mocked_processEvents.assert_called_once_with()
+
+ 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_listwidgetwithdnd.py b/tests/functional/openlp_core/ui/lib/test_listwidgetwithdnd.py
similarity index 100%
rename from tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py
rename to tests/functional/openlp_core/ui/lib/test_listwidgetwithdnd.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 61%
rename from tests/functional/openlp_core_ui/test_mainwindow.py
rename to tests/functional/openlp_core/ui/test_mainwindow.py
index 2582acfa5..5e1a69cbc 100644
--- a/tests/functional/openlp_core_ui/test_mainwindow.py
+++ b/tests/functional/openlp_core/ui/test_mainwindow.py
@@ -26,9 +26,11 @@ import os
from unittest import TestCase
from unittest.mock import MagicMock, patch
-from PyQt5 import QtWidgets
+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.core.display.screens import ScreenList
from openlp.core.ui.mainwindow import MainWindow
from tests.helpers.testmixin import TestMixin
@@ -36,8 +38,23 @@ from tests.utils.constants import TEST_RESOURCES_PATH
class TestMainWindow(TestCase, TestMixin):
+ """
+ Test the main window
+ """
+ def _create_mock_action(self, parent, name, **kwargs):
+ """
+ Create a fake action with some "real" attributes
+ """
+ action = QtWidgets.QAction(parent)
+ action.setObjectName(name)
+ if kwargs.get('triggers'):
+ action.triggered.connect(kwargs.pop('triggers'))
+ return action
def setUp(self):
+ """
+ Set up the objects we need for all of the tests
+ """
Registry.create()
self.registry = Registry()
self.setup_application()
@@ -47,30 +64,18 @@ class TestMainWindow(TestCase, TestMixin):
self.app.args = []
Registry().register('application', self.app)
Registry().set_flag('no_web_server', False)
- # 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.Renderer') as mocked_renderer, \
- patch('openlp.core.ui.mainwindow.websockets.WebSocketServer') as mocked_websocketserver, \
- patch('openlp.core.ui.mainwindow.server.HttpServer') as mocked_httpserver:
- self.mocked_settings_form = mocked_settings_form
- self.mocked_image_manager = mocked_image_manager
- self.mocked_live_controller = mocked_live_controller
- self.mocked_preview_controller = mocked_preview_controller
- self.mocked_dock_widget = mocked_dock_widget
- self.mocked_q_tool_box_class = mocked_q_tool_box_class
- self.mocked_add_dock_method = mocked_add_dock_method
- self.mocked_theme_manager = mocked_theme_manager
- self.mocked_renderer = mocked_renderer
+ self.add_toolbar_action_patcher = patch('openlp.core.ui.mainwindow.create_action')
+ self.mocked_add_toolbar_action = self.add_toolbar_action_patcher.start()
+ self.mocked_add_toolbar_action.side_effect = self._create_mock_action
+ with patch('openlp.core.display.screens.ScreenList.__instance__', spec=ScreenList) as mocked_screen_list:
+ mocked_screen_list.current = {'number': 0, 'size': QtCore.QSize(600, 800), 'primary': True}
self.main_window = MainWindow()
def tearDown(self):
+ """
+ Delete all the C++ objects and stop all the patchers
+ """
+ self.add_toolbar_action_patcher.stop()
del self.main_window
def test_cmd_line_file(self):
@@ -80,13 +85,13 @@ class TestMainWindow(TestCase, TestMixin):
# GIVEN a service as an argument to openlp
service = os.path.join(TEST_RESOURCES_PATH, 'service', 'test.osz')
self.main_window.arguments = [service]
- with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path:
- # WHEN the argument is processed
+ # WHEN the argument is processed
+ with patch.object(self.main_window.service_manager, 'load_file') as mocked_load_file:
self.main_window.open_cmd_line_files(service)
- # THEN the service from the arguments is loaded
- mocked_load_path.assert_called_with(service), 'load_path should have been called with the service\'s path'
+ # THEN the service from the arguments is loaded
+ mocked_load_file.assert_called_with(service)
def test_cmd_line_arg(self):
"""
@@ -95,13 +100,13 @@ class TestMainWindow(TestCase, TestMixin):
# GIVEN a non service file as an argument to openlp
service = os.path.join('openlp.py')
self.main_window.arguments = [service]
- with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path:
+ with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_file:
# WHEN the argument is processed
self.main_window.open_cmd_line_files("")
# THEN the file should not be opened
- assert not mocked_load_path.called, 'load_path should not have been called'
+ assert not mocked_load_file.called, 'load_file should not have been called'
def test_main_window_title(self):
"""
@@ -150,14 +155,14 @@ class TestMainWindow(TestCase, TestMixin):
# WHEN: you check the started functions
# THEN: the following registry functions should have been registered
- self.assertEqual(len(self.registry.service_list), 6, 'The registry should have 6 services.')
- self.assertEqual(len(self.registry.functions_list), 18, 'The registry should have 18 functions')
- self.assertTrue('application' in self.registry.service_list, 'The application should have been registered.')
- self.assertTrue('main_window' in self.registry.service_list, 'The main_window should have been registered.')
- self.assertTrue('media_controller' in self.registry.service_list, 'The media_controller should have been '
- 'registered.')
- self.assertTrue('plugin_manager' in self.registry.service_list,
- 'The plugin_manager should have been registered.')
+ assert len(self.registry.service_list) == 12, \
+ 'The registry should have 12 services, got {}'.format(self.registry.service_list.keys())
+ assert len(self.registry.functions_list) == 19, \
+ 'The registry should have 19 functions, got {}'.format(self.registry.functions_list.keys())
+ assert 'application' in self.registry.service_list, 'The application should have been registered.'
+ assert 'main_window' in self.registry.service_list, 'The main_window should have been registered.'
+ assert 'media_controller' in self.registry.service_list, 'The media_controller should have been registered.'
+ assert 'plugin_manager' in self.registry.service_list, 'The plugin_manager should have been registered.'
def test_projector_manager_hidden_on_startup(self):
"""
@@ -166,7 +171,7 @@ class TestMainWindow(TestCase, TestMixin):
# GIVEN: A built main window
# WHEN: OpenLP is started
# THEN: The projector manager should be hidden
- self.main_window.projector_manager_dock.setVisible.assert_called_once_with(False)
+ assert self.main_window.projector_manager_dock.isVisible() is False
def test_on_search_shortcut_triggered_shows_media_manager(self):
"""
@@ -202,56 +207,38 @@ class TestMainWindow(TestCase, TestMixin):
self.assertEqual(0, mocked_media_manager_dock.setVisible.call_count)
mocked_widget.on_focus.assert_called_with()
- @patch('openlp.core.ui.mainwindow.MainWindow.plugin_manager')
- @patch('openlp.core.ui.mainwindow.MainWindow.first_time')
- @patch('openlp.core.ui.mainwindow.MainWindow.application')
@patch('openlp.core.ui.mainwindow.FirstTimeForm')
@patch('openlp.core.ui.mainwindow.QtWidgets.QMessageBox.warning')
@patch('openlp.core.ui.mainwindow.Settings')
- def test_on_first_time_wizard_clicked_show_projectors_after(self, mocked_Settings, mocked_warning,
- mocked_FirstTimeForm, mocked_application,
- mocked_first_time,
- mocked_plugin_manager):
+ def test_on_first_time_wizard_clicked_show_projectors_after(self, MockSettings, mocked_warning, MockWizard):
+ """Test that the projector manager is shown after the FTW is run"""
# GIVEN: Main_window, patched things, patched "Yes" as confirmation to re-run wizard, settings to True.
- mocked_Settings_obj = MagicMock()
- mocked_Settings_obj.value.return_value = True
- mocked_Settings.return_value = mocked_Settings_obj
+ MockSettings.return_value.value.return_value = True
mocked_warning.return_value = QtWidgets.QMessageBox.Yes
- mocked_FirstTimeForm_obj = MagicMock()
- mocked_FirstTimeForm_obj.was_cancelled = False
- mocked_FirstTimeForm.return_value = mocked_FirstTimeForm_obj
- mocked_plugin_manager.plugins = []
- self.main_window.projector_manager_dock = MagicMock()
+ MockWizard.return_value.was_cancelled = False
- # WHEN: on_first_time_wizard_clicked is called
- self.main_window.on_first_time_wizard_clicked()
+ with patch.object(self.main_window, 'projector_manager_dock') as mocked_dock, \
+ patch.object(self.registry, 'execute'), patch.object(self.main_window, 'theme_manager_contents'):
+ # WHEN: on_first_time_wizard_clicked is called
+ self.main_window.on_first_time_wizard_clicked()
# THEN: projector_manager_dock.setVisible should had been called once
- self.main_window.projector_manager_dock.setVisible.assert_called_once_with(True)
+ mocked_dock.setVisible.assert_called_once_with(True)
- @patch('openlp.core.ui.mainwindow.MainWindow.plugin_manager')
- @patch('openlp.core.ui.mainwindow.MainWindow.first_time')
- @patch('openlp.core.ui.mainwindow.MainWindow.application')
@patch('openlp.core.ui.mainwindow.FirstTimeForm')
@patch('openlp.core.ui.mainwindow.QtWidgets.QMessageBox.warning')
@patch('openlp.core.ui.mainwindow.Settings')
- def test_on_first_time_wizard_clicked_hide_projectors_after(self, mocked_Settings, mocked_warning,
- mocked_FirstTimeForm, mocked_application,
- mocked_first_time,
- mocked_plugin_manager):
+ def test_on_first_time_wizard_clicked_hide_projectors_after(self, MockSettings, mocked_warning, MockWizard):
+ """Test that the projector manager is hidden after the FTW is run"""
# GIVEN: Main_window, patched things, patched "Yes" as confirmation to re-run wizard, settings to False.
- mocked_Settings_obj = MagicMock()
- mocked_Settings_obj.value.return_value = False
- mocked_Settings.return_value = mocked_Settings_obj
+ MockSettings.return_value.value.return_value = False
mocked_warning.return_value = QtWidgets.QMessageBox.Yes
- mocked_FirstTimeForm_obj = MagicMock()
- mocked_FirstTimeForm_obj.was_cancelled = False
- mocked_FirstTimeForm.return_value = mocked_FirstTimeForm_obj
- mocked_plugin_manager.plugins = []
- self.main_window.projector_manager_dock = MagicMock()
+ MockWizard.return_value.was_cancelled = False
# WHEN: on_first_time_wizard_clicked is called
- self.main_window.on_first_time_wizard_clicked()
+ with patch.object(self.main_window, 'projector_manager_dock') as mocked_dock, \
+ patch.object(self.registry, 'execute'), patch.object(self.main_window, 'theme_manager_contents'):
+ self.main_window.on_first_time_wizard_clicked()
# THEN: projector_manager_dock.setVisible should had been called once
- self.main_window.projector_manager_dock.setVisible.assert_called_once_with(False)
+ mocked_dock.setVisible.assert_called_once_with(False)
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..adf241d35 100644
--- a/tests/functional/openlp_core_ui/test_servicemanager.py
+++ b/tests/functional/openlp_core/ui/test_servicemanager.py
@@ -28,10 +28,11 @@ 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
+from openlp.core.widgets.toolbar import OpenLPToolbar
class TestServiceManager(TestCase):
@@ -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 58%
rename from tests/functional/openlp_core_ui/test_thememanager.py
rename to tests/functional/openlp_core/ui/test_thememanager.py
index e4b044b29..62bf980b4 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
@@ -52,25 +52,24 @@ class TestThemeManager(TestCase):
"""
shutil.rmtree(self.temp_folder)
- def test_export_theme(self):
+ @patch('openlp.core.ui.thememanager.zipfile.ZipFile.__init__')
+ @patch('openlp.core.ui.thememanager.zipfile.ZipFile.write')
+ def test_export_theme(self, mocked_zipfile_write, mocked_zipfile_init):
"""
Test exporting a theme .
"""
# GIVEN: A new ThemeManager instance.
theme_manager = ThemeManager()
theme_manager.theme_path = Path(TEST_RESOURCES_PATH, 'themes')
- with patch('zipfile.ZipFile.__init__') as mocked_zipfile_init, \
- patch('zipfile.ZipFile.write') as mocked_zipfile_write:
- mocked_zipfile_init.return_value = None
+ mocked_zipfile_init.return_value = None
- # WHEN: The theme is exported
- theme_manager._export_theme(Path('some', 'path', 'Default.otz'), 'Default')
+ # WHEN: The theme is exported
+ theme_manager._export_theme(Path('some', 'path', 'Default.otz'), 'Default')
- # THEN: The zipfile should be created at the given path
- mocked_zipfile_init.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w')
- mocked_zipfile_write.assert_called_with(os.path.join(TEST_RESOURCES_PATH, 'themes',
- 'Default', 'Default.xml'),
- os.path.join('Default', 'Default.xml'))
+ # THEN: The zipfile should be created at the given path
+ mocked_zipfile_init.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w')
+ mocked_zipfile_write.assert_called_with(os.path.join(TEST_RESOURCES_PATH, 'themes', 'Default', 'Default.xml'),
+ os.path.join('Default', 'Default.xml'))
def test_initial_theme_manager(self):
"""
@@ -83,53 +82,53 @@ class TestThemeManager(TestCase):
# THEN: The the controller should be registered in the registry.
self.assertIsNotNone(Registry().get('theme_manager'), 'The base theme manager should be registered')
- def test_write_theme_same_image(self):
+ @patch('openlp.core.ui.thememanager.copyfile')
+ @patch('openlp.core.ui.thememanager.create_paths')
+ def test_write_theme_same_image(self, mocked_create_paths, mocked_copyfile):
"""
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.
- with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \
- patch('openlp.core.ui.thememanager.check_directory_exists'):
- theme_manager = ThemeManager(None)
- theme_manager.old_background_image = None
- theme_manager.generate_and_save_image = MagicMock()
- theme_manager.theme_path = MagicMock()
- mocked_theme = MagicMock()
- mocked_theme.theme_name = 'themename'
- mocked_theme.extract_formatted_xml = MagicMock()
- mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
+ # theme, create_paths and thememanager-attributes.
+ theme_manager = ThemeManager(None)
+ theme_manager.old_background_image = None
+ theme_manager.generate_and_save_image = MagicMock()
+ theme_manager.theme_path = MagicMock()
+ mocked_theme = MagicMock()
+ mocked_theme.theme_name = 'themename'
+ mocked_theme.extract_formatted_xml = MagicMock()
+ mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
- # WHEN: Calling _write_theme with path to the same image, but the path written slightly different
- file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
- theme_manager._write_theme(mocked_theme, file_name1, file_name1)
+ # WHEN: Calling _write_theme with path to the same image, but the path written slightly different
+ file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
+ theme_manager._write_theme(mocked_theme, file_name1, file_name1)
- # THEN: The mocked_copyfile should not have been called
- self.assertFalse(mocked_copyfile.called, 'copyfile should not be called')
+ # THEN: The mocked_copyfile should not have been called
+ assert mocked_copyfile.called is False, 'copyfile should not be called'
- def test_write_theme_diff_images(self):
+ @patch('openlp.core.ui.thememanager.copyfile')
+ @patch('openlp.core.ui.thememanager.create_paths')
+ def test_write_theme_diff_images(self, mocked_create_paths, mocked_copyfile):
"""
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.
- with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \
- patch('openlp.core.ui.thememanager.check_directory_exists'):
- theme_manager = ThemeManager(None)
- theme_manager.old_background_image = None
- theme_manager.generate_and_save_image = MagicMock()
- theme_manager.theme_path = MagicMock()
- mocked_theme = MagicMock()
- mocked_theme.theme_name = 'themename'
- mocked_theme.filename = "filename"
+ # theme, create_paths and thememanager-attributes.
+ theme_manager = ThemeManager(None)
+ theme_manager.old_background_image = None
+ theme_manager.generate_and_save_image = MagicMock()
+ theme_manager.theme_path = MagicMock()
+ mocked_theme = MagicMock()
+ mocked_theme.theme_name = 'themename'
+ mocked_theme.filename = "filename"
- # WHEN: Calling _write_theme with path to different images
- file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
- file_name2 = Path(TEST_RESOURCES_PATH, 'church2.jpg')
- theme_manager._write_theme(mocked_theme, file_name1, file_name2)
+ # WHEN: Calling _write_theme with path to different images
+ file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
+ file_name2 = Path(TEST_RESOURCES_PATH, 'church2.jpg')
+ theme_manager._write_theme(mocked_theme, file_name1, file_name2)
- # THEN: The mocked_copyfile should not have been called
- self.assertTrue(mocked_copyfile.called, 'copyfile should be called')
+ # THEN: The mocked_copyfile should not have been called
+ assert mocked_copyfile.called is True, 'copyfile should be called'
def test_write_theme_special_char_name(self):
"""
@@ -151,45 +150,43 @@ class TestThemeManager(TestCase):
self.assertTrue(os.path.exists(os.path.join(self.temp_folder, 'theme æ„› name', 'theme æ„› name.json')),
'Theme with special characters should have been created!')
- def test_over_write_message_box_yes(self):
+ @patch('openlp.core.ui.thememanager.QtWidgets.QMessageBox.question', return_value=QtWidgets.QMessageBox.Yes)
+ @patch('openlp.core.ui.thememanager.translate')
+ def test_over_write_message_box_yes(self, mocked_translate, mocked_qmessagebox_question):
"""
Test that theme_manager.over_write_message_box returns True when the user clicks yes.
"""
# GIVEN: A patched QMessageBox.question and an instance of ThemeManager
- with patch('openlp.core.ui.thememanager.QtWidgets.QMessageBox.question',
- return_value=QtWidgets.QMessageBox.Yes) as mocked_qmessagebox_question,\
- patch('openlp.core.ui.thememanager.translate') as mocked_translate:
- mocked_translate.side_effect = lambda context, text: text
- theme_manager = ThemeManager(None)
+ mocked_translate.side_effect = lambda context, text: text
+ theme_manager = ThemeManager(None)
- # WHEN: Calling over_write_message_box with 'Theme Name'
- result = theme_manager.over_write_message_box('Theme Name')
+ # WHEN: Calling over_write_message_box with 'Theme Name'
+ result = theme_manager.over_write_message_box('Theme Name')
- # THEN: over_write_message_box should return True and the message box should contain the theme name
- self.assertTrue(result)
- mocked_qmessagebox_question.assert_called_once_with(
- theme_manager, 'Theme Already Exists', 'Theme Theme Name already exists. Do you want to replace it?',
- defaultButton=ANY)
+ # THEN: over_write_message_box should return True and the message box should contain the theme name
+ assert result is True
+ mocked_qmessagebox_question.assert_called_once_with(
+ theme_manager, 'Theme Already Exists', 'Theme Theme Name already exists. Do you want to replace it?',
+ defaultButton=ANY)
- def test_over_write_message_box_no(self):
+ @patch('openlp.core.ui.thememanager.QtWidgets.QMessageBox.question', return_value=QtWidgets.QMessageBox.No)
+ @patch('openlp.core.ui.thememanager.translate')
+ def test_over_write_message_box_no(self, mocked_translate, mocked_qmessagebox_question):
"""
Test that theme_manager.over_write_message_box returns False when the user clicks no.
"""
# GIVEN: A patched QMessageBox.question and an instance of ThemeManager
- with patch('openlp.core.ui.thememanager.QtWidgets.QMessageBox.question', return_value=QtWidgets.QMessageBox.No)\
- as mocked_qmessagebox_question,\
- patch('openlp.core.ui.thememanager.translate') as mocked_translate:
- mocked_translate.side_effect = lambda context, text: text
- theme_manager = ThemeManager(None)
+ mocked_translate.side_effect = lambda context, text: text
+ theme_manager = ThemeManager(None)
- # WHEN: Calling over_write_message_box with 'Theme Name'
- result = theme_manager.over_write_message_box('Theme Name')
+ # WHEN: Calling over_write_message_box with 'Theme Name'
+ result = theme_manager.over_write_message_box('Theme Name')
- # THEN: over_write_message_box should return False and the message box should contain the theme name
- self.assertFalse(result)
- mocked_qmessagebox_question.assert_called_once_with(
- theme_manager, 'Theme Already Exists', 'Theme Theme Name already exists. Do you want to replace it?',
- defaultButton=ANY)
+ # THEN: over_write_message_box should return False and the message box should contain the theme name
+ assert result is False
+ mocked_qmessagebox_question.assert_called_once_with(
+ theme_manager, 'Theme Already Exists', 'Theme Theme Name already exists. Do you want to replace it?',
+ defaultButton=ANY)
def test_unzip_theme(self):
"""
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/interfaces/openlp_core_ui_lib/__init__.py b/tests/functional/openlp_core/widgets/__init__.py
similarity index 100%
rename from tests/interfaces/openlp_core_ui_lib/__init__.py
rename to tests/functional/openlp_core/widgets/__init__.py
diff --git a/tests/functional/openlp_core_ui_lib/test_colorbutton.py b/tests/functional/openlp_core/widgets/test_buttons.py
similarity index 70%
rename from tests/functional/openlp_core_ui_lib/test_colorbutton.py
rename to tests/functional/openlp_core/widgets/test_buttons.py
index b010cbbdc..4abd38da4 100644
--- a/tests/functional/openlp_core_ui_lib/test_colorbutton.py
+++ b/tests/functional/openlp_core/widgets/test_buttons.py
@@ -20,12 +20,12 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
-This module contains tests for the openlp.core.ui.lib.colorbutton module
+This module contains tests for the openlp.core.widgets.buttons module
"""
from unittest import TestCase
from unittest.mock import MagicMock, call, patch
-from openlp.core.ui.lib import ColorButton
+from openlp.core.widgets.buttons import ColorButton
class TestColorDialog(TestCase):
@@ -33,11 +33,11 @@ class TestColorDialog(TestCase):
Test the :class:`~openlp.core.lib.colorbutton.ColorButton` class
"""
def setUp(self):
- self.change_color_patcher = patch('openlp.core.ui.lib.colorbutton.ColorButton.change_color')
- self.clicked_patcher = patch('openlp.core.ui.lib.colorbutton.ColorButton.clicked')
- self.color_changed_patcher = patch('openlp.core.ui.lib.colorbutton.ColorButton.colorChanged')
- self.qt_gui_patcher = patch('openlp.core.ui.lib.colorbutton.QtWidgets')
- self.translate_patcher = patch('openlp.core.ui.lib.colorbutton.translate', **{'return_value': 'Tool Tip Text'})
+ self.change_color_patcher = patch('openlp.core.widgets.buttons.ColorButton.change_color')
+ self.clicked_patcher = patch('openlp.core.widgets.buttons.ColorButton.clicked')
+ self.color_changed_patcher = patch('openlp.core.widgets.buttons.ColorButton.colorChanged')
+ self.qt_gui_patcher = patch('openlp.core.widgets.buttons.QtWidgets')
+ self.translate_patcher = patch('openlp.core.widgets.buttons.translate', **{'return_value': 'Tool Tip Text'})
self.addCleanup(self.change_color_patcher.stop)
self.addCleanup(self.clicked_patcher.stop)
self.addCleanup(self.color_changed_patcher.stop)
@@ -49,41 +49,40 @@ class TestColorDialog(TestCase):
self.mocked_qt_widgets = self.qt_gui_patcher.start()
self.mocked_translate = self.translate_patcher.start()
- def test_constructor(self):
+ @patch('openlp.core.widgets.buttons.ColorButton.setToolTip')
+ def test_constructor(self, mocked_set_tool_tip):
"""
Test that constructing a ColorButton object works correctly
"""
# GIVEN: The ColorButton class, a mocked change_color, setToolTip methods and clicked signal
- with patch('openlp.core.ui.lib.colorbutton.ColorButton.setToolTip') as mocked_set_tool_tip:
+ # WHEN: The ColorButton object is instantiated
+ widget = ColorButton()
- # WHEN: The ColorButton object is instantiated
- widget = ColorButton()
+ # THEN: The widget __init__ method should have the correct properties and methods called
+ self.assertEqual(widget.parent, None,
+ 'The parent should be the same as the one that the class was instianted with')
+ self.mocked_change_color.assert_called_once_with('#ffffff')
+ mocked_set_tool_tip.assert_called_once_with('Tool Tip Text')
+ self.mocked_clicked.connect.assert_called_once_with(widget.on_clicked)
- # THEN: The widget __init__ method should have the correct properties and methods called
- self.assertEqual(widget.parent, None,
- 'The parent should be the same as the one that the class was instianted with')
- self.mocked_change_color.assert_called_once_with('#ffffff')
- mocked_set_tool_tip.assert_called_once_with('Tool Tip Text')
- self.mocked_clicked.connect.assert_called_once_with(widget.on_clicked)
-
- def test_change_color(self):
+ @patch('openlp.core.widgets.buttons.ColorButton.setStyleSheet')
+ def test_change_color(self, mocked_set_style_sheet):
"""
Test that change_color sets the new color and the stylesheet
"""
self.change_color_patcher.stop()
# GIVEN: An instance of the ColorButton object, and a mocked out setStyleSheet
- with patch('openlp.core.ui.lib.colorbutton.ColorButton.setStyleSheet') as mocked_set_style_sheet:
- widget = ColorButton()
+ widget = ColorButton()
- # WHEN: Changing the color
- widget.change_color('#000000')
+ # WHEN: Changing the color
+ widget.change_color('#000000')
- # THEN: The _color attribute should be set to #000000 and setStyleSheet should have been called twice
- self.assertEqual(widget._color, '#000000', '_color should have been set to #000000')
- mocked_set_style_sheet.assert_has_calls(
- [call('background-color: #ffffff'), call('background-color: #000000')])
+ # THEN: The _color attribute should be set to #000000 and setStyleSheet should have been called twice
+ self.assertEqual(widget._color, '#000000', '_color should have been set to #000000')
+ mocked_set_style_sheet.assert_has_calls(
+ [call('background-color: #ffffff'), call('background-color: #000000')])
self.mocked_change_color = self.change_color_patcher.start()
@@ -91,22 +90,6 @@ class TestColorDialog(TestCase):
"""
Test that the color property method returns the set color
"""
-
- # GIVEN: An instance of ColorButton, with a set _color attribute
- widget = ColorButton()
- widget._color = '#000000'
-
- # WHEN: Accesing the color property
- value = widget.color
-
- # THEN: The value set in _color should be returned
- self.assertEqual(value, '#000000', 'The value returned should be equal to the one we set')
-
- def test_color(self):
- """
- Test that the color property method returns the set color
- """
-
# GIVEN: An instance of ColorButton, with a set _color attribute
widget = ColorButton()
widget._color = '#000000'
@@ -117,20 +100,19 @@ class TestColorDialog(TestCase):
# THEN: The value set in _color should be returned
self.assertEqual(value, '#000000', 'The value returned should be equal to the one we set')
+ # @patch('openlp.core.widgets.buttons.ColorButton.__init__', **{'return_value': None})
def test_color_setter(self):
"""
Test that the color property setter method sets the color
"""
-
# GIVEN: An instance of ColorButton, with a mocked __init__
- with patch('openlp.core.ui.lib.colorbutton.ColorButton.__init__', **{'return_value': None}):
- widget = ColorButton()
+ widget = ColorButton()
- # WHEN: Setting the color property
- widget.color = '#000000'
+ # WHEN: Setting the color property
+ widget.color = '#000000'
- # THEN: Then change_color should have been called with the value we set
- self.mocked_change_color.assert_called_once_with('#000000')
+ # THEN: Then change_color should have been called with the value we set
+ self.mocked_change_color.assert_called_with('#000000')
def test_on_clicked_invalid_color(self):
"""
diff --git a/tests/functional/openlp_core_ui_lib/test_filedialog.py b/tests/functional/openlp_core/widgets/test_dialogs.py
similarity index 95%
rename from tests/functional/openlp_core_ui_lib/test_filedialog.py
rename to tests/functional/openlp_core/widgets/test_dialogs.py
index 777ff65ec..fd65de33e 100755
--- a/tests/functional/openlp_core_ui_lib/test_filedialog.py
+++ b/tests/functional/openlp_core/widgets/test_dialogs.py
@@ -5,12 +5,12 @@ from unittest.mock import patch
from PyQt5 import QtWidgets
from openlp.core.common.path import Path
-from openlp.core.ui.lib.filedialog import FileDialog
+from openlp.core.widgets.dialogs import FileDialog
class TestFileDialogPatches(TestCase):
"""
- Tests for the :mod:`openlp.core.ui.lib.filedialogpatches` module
+ Tests for the :mod:`openlp.core.widgets.dialogs` module
"""
def test_file_dialog(self):
@@ -55,7 +55,7 @@ class TestFileDialogPatches(TestCase):
order
"""
# GIVEN: FileDialog
- with patch('openlp.core.ui.lib.filedialog.QtWidgets.QFileDialog.getExistingDirectory', return_value='') \
+ with patch('openlp.core.widgets.dialogs.QtWidgets.QFileDialog.getExistingDirectory', return_value='') \
as mocked_get_existing_directory:
# WHEN: Calling the getExistingDirectory method with all parameters set
diff --git a/tests/functional/openlp_core_ui_lib/test_pathedit.py b/tests/functional/openlp_core/widgets/test_edits.py
similarity index 81%
rename from tests/functional/openlp_core_ui_lib/test_pathedit.py
rename to tests/functional/openlp_core/widgets/test_edits.py
index 227a4317a..5ce6dc9df 100755
--- a/tests/functional/openlp_core_ui_lib/test_pathedit.py
+++ b/tests/functional/openlp_core/widgets/test_edits.py
@@ -20,23 +20,24 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
-This module contains tests for the openlp.core.ui.lib.pathedit module
+This module contains tests for the openlp.core.widgets.edits module
"""
import os
from unittest import TestCase
from unittest.mock import MagicMock, PropertyMock, patch
from openlp.core.common.path import Path
-from openlp.core.ui.lib import PathEdit, PathType
-from openlp.core.ui.lib.filedialog import FileDialog
+from openlp.core.widgets.edits import PathEdit
+from openlp.core.widgets.enums import PathEditType
+from openlp.core.widgets.dialogs import FileDialog
class TestPathEdit(TestCase):
"""
- Test the :class:`~openlp.core.lib.pathedit.PathEdit` class
+ Test the :class:`~openlp.core.widgets.edits.PathEdit` class
"""
def setUp(self):
- with patch('openlp.core.ui.lib.pathedit.PathEdit._setup'):
+ with patch('openlp.core.widgets.edits.PathEdit._setup'):
self.widget = PathEdit()
def test_path_getter(self):
@@ -73,7 +74,7 @@ class TestPathEdit(TestCase):
# GIVEN: An instance of PathEdit
# WHEN: Reading the `path` property
# THEN: The default value should be returned
- self.assertEqual(self.widget.path_type, PathType.Files)
+ self.assertEqual(self.widget.path_type, PathEditType.Files)
def test_path_type_setter(self):
"""
@@ -83,11 +84,11 @@ class TestPathEdit(TestCase):
with patch.object(self.widget, 'update_button_tool_tips') as mocked_update_button_tool_tips:
# WHEN: Writing to a different value than default to the `path_type` property
- self.widget.path_type = PathType.Directories
+ self.widget.path_type = PathEditType.Directories
# THEN: The `_path_type` instance variable should be set with the test data and not the default. The
# update_button_tool_tips should have been called.
- self.assertEqual(self.widget._path_type, PathType.Directories)
+ self.assertEqual(self.widget._path_type, PathEditType.Directories)
mocked_update_button_tool_tips.assert_called_once_with()
def test_update_button_tool_tips_directories(self):
@@ -97,7 +98,7 @@ class TestPathEdit(TestCase):
# GIVEN: An instance of PathEdit with the `path_type` set to `Directories`
self.widget.browse_button = MagicMock()
self.widget.revert_button = MagicMock()
- self.widget._path_type = PathType.Directories
+ self.widget._path_type = PathEditType.Directories
# WHEN: Calling update_button_tool_tips
self.widget.update_button_tool_tips()
@@ -112,7 +113,7 @@ class TestPathEdit(TestCase):
# GIVEN: An instance of PathEdit with the `path_type` set to `Files`
self.widget.browse_button = MagicMock()
self.widget.revert_button = MagicMock()
- self.widget._path_type = PathType.Files
+ self.widget._path_type = PathEditType.Files
# WHEN: Calling update_button_tool_tips
self.widget.update_button_tool_tips()
@@ -120,26 +121,25 @@ class TestPathEdit(TestCase):
self.widget.browse_button.setToolTip.assert_called_once_with('Browse for file.')
self.widget.revert_button.setToolTip.assert_called_once_with('Revert to default file.')
- def test_on_browse_button_clicked_directory(self):
+ @patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory', return_value=None)
+ @patch('openlp.core.widgets.edits.FileDialog.getOpenFileName')
+ def test_on_browse_button_clicked_directory(self, mocked_get_open_file_name, mocked_get_existing_directory):
"""
Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Directories.
"""
# GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked
# QFileDialog.getExistingDirectory
- with patch('openlp.core.ui.lib.pathedit.FileDialog.getExistingDirectory', return_value=None) as \
- mocked_get_existing_directory, \
- patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName') as mocked_get_open_file_name:
- self.widget._path_type = PathType.Directories
- self.widget._path = Path('test', 'path')
+ self.widget._path_type = PathEditType.Directories
+ self.widget._path = Path('test', 'path')
- # WHEN: Calling on_browse_button_clicked
- self.widget.on_browse_button_clicked()
+ # WHEN: Calling on_browse_button_clicked
+ self.widget.on_browse_button_clicked()
- # THEN: The FileDialog.getExistingDirectory should have been called with the default caption
- mocked_get_existing_directory.assert_called_once_with(self.widget, 'Select Directory',
- Path('test', 'path'),
- FileDialog.ShowDirsOnly)
- self.assertFalse(mocked_get_open_file_name.called)
+ # THEN: The FileDialog.getExistingDirectory should have been called with the default caption
+ mocked_get_existing_directory.assert_called_once_with(self.widget, 'Select Directory',
+ Path('test', 'path'),
+ FileDialog.ShowDirsOnly)
+ self.assertFalse(mocked_get_open_file_name.called)
def test_on_browse_button_clicked_directory_custom_caption(self):
"""
@@ -148,10 +148,10 @@ class TestPathEdit(TestCase):
"""
# GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked
# QFileDialog.getExistingDirectory with `default_caption` set.
- with patch('openlp.core.ui.lib.pathedit.FileDialog.getExistingDirectory', return_value=None) as \
+ with patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory', return_value=None) as \
mocked_get_existing_directory, \
- patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName') as mocked_get_open_file_name:
- self.widget._path_type = PathType.Directories
+ patch('openlp.core.widgets.edits.FileDialog.getOpenFileName') as mocked_get_open_file_name:
+ self.widget._path_type = PathEditType.Directories
self.widget._path = Path('test', 'path')
self.widget.dialog_caption = 'Directory Caption'
@@ -169,10 +169,10 @@ class TestPathEdit(TestCase):
Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files.
"""
# GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName
- with patch('openlp.core.ui.lib.pathedit.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \
- patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName', return_value=(None, '')) as \
+ with patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \
+ patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', return_value=(None, '')) as \
mocked_get_open_file_name:
- self.widget._path_type = PathType.Files
+ self.widget._path_type = PathEditType.Files
self.widget._path = Path('test', 'pat.h')
# WHEN: Calling on_browse_button_clicked
@@ -190,10 +190,10 @@ class TestPathEdit(TestCase):
"""
# GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName
# with `default_caption` set.
- with patch('openlp.core.ui.lib.pathedit.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \
- patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName', return_value=(None, '')) as \
+ with patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \
+ patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', return_value=(None, '')) as \
mocked_get_open_file_name:
- self.widget._path_type = PathType.Files
+ self.widget._path_type = PathEditType.Files
self.widget._path = Path('test', 'pat.h')
self.widget.dialog_caption = 'File Caption'
@@ -212,7 +212,7 @@ class TestPathEdit(TestCase):
"""
# GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns an empty str for the
# file path.
- with patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName', return_value=(None, '')) as \
+ with patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', return_value=(None, '')) as \
mocked_get_open_file_name:
# WHEN: Calling on_browse_button_clicked
@@ -228,7 +228,7 @@ class TestPathEdit(TestCase):
"""
# GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns a str for the file
# path.
- with patch('openlp.core.ui.lib.pathedit.FileDialog.getOpenFileName',
+ with patch('openlp.core.widgets.edits.FileDialog.getOpenFileName',
return_value=(Path('test', 'pat.h'), '')) as mocked_get_open_file_name, \
patch.object(self.widget, 'on_new_path'):
@@ -272,7 +272,7 @@ class TestPathEdit(TestCase):
Test `on_new_path` when called with a path that is the same as the existing path.
"""
# GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal
- with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock):
+ with patch('openlp.core.widgets.edits.PathEdit.path', new_callable=PropertyMock):
self.widget._path = Path('/old', 'test', 'pat.h')
self.widget.pathChanged = MagicMock()
@@ -287,7 +287,7 @@ class TestPathEdit(TestCase):
Test `on_new_path` when called with a path that is the different to the existing path.
"""
# GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal
- with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock):
+ with patch('openlp.core.widgets.edits.PathEdit.path', new_callable=PropertyMock):
self.widget._path = Path('/old', 'test', 'pat.h')
self.widget.pathChanged = MagicMock()
diff --git a/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py b/tests/functional/openlp_core/widgets/test_views.py
similarity index 75%
rename from tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py
rename to tests/functional/openlp_core/widgets/test_views.py
index 77c63bb91..d931a5ef5 100644
--- a/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py
+++ b/tests/functional/openlp_core/widgets/test_views.py
@@ -20,16 +20,17 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
-Package to test the openlp.core.ui.lib.listpreviewwidget package.
+Package to test the openlp.core.widgets.views package.
"""
+from types import GeneratorType
from unittest import TestCase
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.common.i18n import UiStrings
+from openlp.core.lib import ImageSource
+from openlp.core.widgets.views import ListPreviewWidget, ListWidgetWithDnD, TreeWidgetWithDnD
class TestListPreviewWidget(TestCase):
@@ -39,13 +40,13 @@ class TestListPreviewWidget(TestCase):
Mock out stuff for all the tests
"""
# Mock self.parent().width()
- self.parent_patcher = patch('openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.parent')
+ self.parent_patcher = patch('openlp.core.widgets.views.ListPreviewWidget.parent')
self.mocked_parent = self.parent_patcher.start()
self.mocked_parent.width.return_value = 100
self.addCleanup(self.parent_patcher.stop)
# Mock Settings().value()
- self.Settings_patcher = patch('openlp.core.ui.lib.listpreviewwidget.Settings')
+ self.Settings_patcher = patch('openlp.core.widgets.views.Settings')
self.mocked_Settings = self.Settings_patcher.start()
self.mocked_Settings_obj = MagicMock()
self.mocked_Settings_obj.value.return_value = None
@@ -53,7 +54,7 @@ class TestListPreviewWidget(TestCase):
self.addCleanup(self.Settings_patcher.stop)
# Mock self.viewport().width()
- self.viewport_patcher = patch('openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.viewport')
+ self.viewport_patcher = patch('openlp.core.widgets.views.ListPreviewWidget.viewport')
self.mocked_viewport = self.viewport_patcher.start()
self.mocked_viewport_obj = MagicMock()
self.mocked_viewport_obj.width.return_value = 200
@@ -73,9 +74,9 @@ class TestListPreviewWidget(TestCase):
self.assertIsNotNone(list_preview_widget, 'The ListPreviewWidget object should not be None')
self.assertEquals(list_preview_widget.screen_ratio, 1, 'Should not be called')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.image_manager')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.image_manager')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight')
def test_replace_service_item_thumbs(self, mocked_setRowHeight, mocked_resizeRowsToContents,
mocked_image_manager):
"""
@@ -120,8 +121,8 @@ class TestListPreviewWidget(TestCase):
call('TEST3', ImageSource.CommandPlugins), call('TEST4', ImageSource.CommandPlugins)]
mocked_image_manager.get_image.assert_has_calls(calls)
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight')
def test_replace_recalculate_layout_text(self, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." enabled, txt slides unchanged in replace_service_item & __recalc...
@@ -152,8 +153,8 @@ class TestListPreviewWidget(TestCase):
self.assertEquals(mocked_resizeRowsToContents.call_count, 2, 'Should be called')
self.assertEquals(mocked_setRowHeight.call_count, 0, 'Should not be called')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight')
def test_replace_recalculate_layout_img(self, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." disabled, img slides unchanged in replace_service_item & __recalc...
@@ -189,8 +190,8 @@ class TestListPreviewWidget(TestCase):
calls = [call(0, 200), call(1, 200), call(0, 400), call(1, 400), call(0, 400), call(1, 400)]
mocked_setRowHeight.assert_has_calls(calls)
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight')
def test_replace_recalculate_layout_img_max(self, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." enabled, img slides resized in replace_service_item & __recalc...
@@ -224,8 +225,8 @@ class TestListPreviewWidget(TestCase):
calls = [call(0, 100), call(1, 100), call(0, 100), call(1, 100)]
mocked_setRowHeight.assert_has_calls(calls)
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight')
def test_replace_recalculate_layout_img_auto(self, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." auto, img slides resized in replace_service_item & __recalc...
@@ -262,9 +263,9 @@ class TestListPreviewWidget(TestCase):
calls = [call(0, 100), call(1, 100), call(0, 150), call(1, 150), call(0, 100), call(1, 100)]
mocked_setRowHeight.assert_has_calls(calls)
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.cellWidget')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.cellWidget')
def test_row_resized_text(self, mocked_cellWidget, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." enabled, text-based slides not affected in row_resized.
@@ -296,9 +297,9 @@ class TestListPreviewWidget(TestCase):
# THEN: self.cellWidget(row, 0).children()[1].setMaximumWidth() should not be called
self.assertEquals(mocked_cellWidget_child.setMaximumWidth.call_count, 0, 'Should not be called')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.cellWidget')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.cellWidget')
def test_row_resized_img(self, mocked_cellWidget, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." disabled, image-based slides not affected in row_resized.
@@ -333,9 +334,9 @@ class TestListPreviewWidget(TestCase):
# THEN: self.cellWidget(row, 0).children()[1].setMaximumWidth() should not be called
self.assertEquals(mocked_cellWidget_child.setMaximumWidth.call_count, 0, 'Should not be called')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.cellWidget')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.cellWidget')
def test_row_resized_img_max(self, mocked_cellWidget, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." enabled, image-based slides are scaled in row_resized.
@@ -368,9 +369,9 @@ class TestListPreviewWidget(TestCase):
# THEN: self.cellWidget(row, 0).children()[1].setMaximumWidth() should be called
mocked_cellWidget_child.setMaximumWidth.assert_called_once_with(150)
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.cellWidget')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.cellWidget')
def test_row_resized_setting_changed(self, mocked_cellWidget, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." enabled while item live, program doesn't crash on row_resized.
@@ -403,10 +404,10 @@ class TestListPreviewWidget(TestCase):
# THEN: self.cellWidget(row, 0).children()[1].setMaximumWidth() should fail
self.assertRaises(Exception)
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.selectRow')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.scrollToItem')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.item')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.slide_count')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.selectRow')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.scrollToItem')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.item')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.slide_count')
def test_autoscroll_setting_invalid(self, mocked_slide_count, mocked_item, mocked_scrollToItem, mocked_selectRow):
"""
Test if 'advanced/autoscrolling' setting None or invalid, that no autoscrolling occurs on change_slide().
@@ -439,10 +440,10 @@ class TestListPreviewWidget(TestCase):
self.assertEquals(mocked_selectRow.call_count, 0, 'Should not be called')
self.assertEquals(mocked_item.call_count, 0, 'Should not be called')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.selectRow')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.scrollToItem')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.item')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.slide_count')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.selectRow')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.scrollToItem')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.item')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.slide_count')
def test_autoscroll_dist_bounds(self, mocked_slide_count, mocked_item, mocked_scrollToItem, mocked_selectRow):
"""
Test if 'advanced/autoscrolling' setting asks to scroll beyond list bounds, that it does not beyond.
@@ -469,10 +470,10 @@ class TestListPreviewWidget(TestCase):
calls = [call(0, 0), call(0, 0)]
mocked_item.assert_has_calls(calls)
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.selectRow')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.scrollToItem')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.item')
- @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.slide_count')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.selectRow')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.scrollToItem')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.item')
+ @patch(u'openlp.core.widgets.views.ListPreviewWidget.slide_count')
def test_autoscroll_normal(self, mocked_slide_count, mocked_item, mocked_scrollToItem, mocked_selectRow):
"""
Test if 'advanced/autoscrolling' setting valid, autoscrolling called as expected.
@@ -500,3 +501,130 @@ class TestListPreviewWidget(TestCase):
self.assertEquals(mocked_item.call_count, 3, 'Should be called')
calls = [call(0, 0), call(1, 0), call(2, 0)]
mocked_item.assert_has_calls(calls)
+
+
+class TestListWidgetWithDnD(TestCase):
+ """
+ Test the :class:`~openlp.core.widgets.views.ListWidgetWithDnD` class
+ """
+ def test_clear(self):
+ """
+ Test the clear method when called without any arguments.
+ """
+ # GIVEN: An instance of ListWidgetWithDnD
+ widget = ListWidgetWithDnD()
+
+ # WHEN: Calling clear with out any arguments
+ widget.clear()
+
+ # THEN: The results text should be the standard 'no results' text.
+ self.assertEqual(widget.no_results_text, UiStrings().NoResults)
+
+ def test_clear_search_while_typing(self):
+ """
+ Test the clear method when called with the search_while_typing argument set to True
+ """
+ # GIVEN: An instance of ListWidgetWithDnD
+ widget = ListWidgetWithDnD()
+
+ # WHEN: Calling clear with search_while_typing set to True
+ widget.clear(search_while_typing=True)
+
+ # THEN: The results text should be the 'short results' text.
+ self.assertEqual(widget.no_results_text, UiStrings().ShortResults)
+
+ def test_all_items_no_list_items(self):
+ """
+ Test allItems when there are no items in the list widget
+ """
+ # GIVEN: An instance of ListWidgetWithDnD
+ widget = ListWidgetWithDnD()
+ with patch.object(widget, 'count', return_value=0), \
+ patch.object(widget, 'item', side_effect=lambda x: [][x]):
+
+ # WHEN: Calling allItems
+ result = widget.allItems()
+
+ # THEN: An instance of a Generator object should be returned. The generator should not yeild any results
+ self.assertIsInstance(result, GeneratorType)
+ self.assertEqual(list(result), [])
+
+ def test_all_items_list_items(self):
+ """
+ Test allItems when the list widget contains some items.
+ """
+ # GIVEN: An instance of ListWidgetWithDnD
+ widget = ListWidgetWithDnD()
+ with patch.object(widget, 'count', return_value=2), \
+ patch.object(widget, 'item', side_effect=lambda x: [5, 3][x]):
+
+ # WHEN: Calling allItems
+ result = widget.allItems()
+
+ # THEN: An instance of a Generator object should be returned. The generator should not yeild any results
+ self.assertIsInstance(result, GeneratorType)
+ self.assertEqual(list(result), [5, 3])
+
+ def test_paint_event(self):
+ """
+ Test the paintEvent method when the list is not empty
+ """
+ # GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 1
+ # (i.e the list has an item)
+ widget = ListWidgetWithDnD()
+ with patch('openlp.core.widgets.views.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \
+ patch.object(widget, 'count', return_value=1), \
+ patch.object(widget, 'viewport') as mocked_viewport:
+ mocked_event = MagicMock()
+
+ # WHEN: Calling paintEvent
+ widget.paintEvent(mocked_event)
+
+ # THEN: The overridden paintEvnet should have been called
+ mocked_paint_event.assert_called_once_with(mocked_event)
+ self.assertFalse(mocked_viewport.called)
+
+ def test_paint_event_no_items(self):
+ """
+ Test the paintEvent method when the list is empty
+ """
+ # GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 0
+ # (i.e the list is empty)
+ widget = ListWidgetWithDnD()
+ mocked_painter_instance = MagicMock()
+ mocked_qrect = MagicMock()
+ with patch('openlp.core.widgets.views.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \
+ patch.object(widget, 'count', return_value=0), \
+ patch.object(widget, 'viewport'), \
+ patch('openlp.core.widgets.views.QtGui.QPainter',
+ return_value=mocked_painter_instance) as mocked_qpainter, \
+ patch('openlp.core.widgets.views.QtCore.QRect', return_value=mocked_qrect):
+ mocked_event = MagicMock()
+
+ # WHEN: Calling paintEvent
+ widget.paintEvent(mocked_event)
+
+ # THEN: The overridden paintEvnet should have been called, and some text should be drawn.
+ mocked_paint_event.assert_called_once_with(mocked_event)
+ mocked_qpainter.assert_called_once_with(widget.viewport())
+ mocked_painter_instance.drawText.assert_called_once_with(mocked_qrect, 4100, 'No Search Results')
+
+
+class TestTreeWidgetWithDnD(TestCase):
+ """
+ Test the :class:`~openlp.core.widgets.views.TreeWidgetWithDnD` class
+ """
+ def test_constructor(self):
+ """
+ Test the constructor
+ """
+ # GIVEN: A TreeWidgetWithDnD
+ # WHEN: An instance is created
+ widget = TreeWidgetWithDnD(name='Test')
+
+ # THEN: It should be initialised correctly
+ assert widget.mime_data_text == 'Test'
+ assert widget.allow_internal_dnd is False
+ assert widget.indentation() == 0
+ assert widget.isAnimated() is True
+
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 78dd4ebb5..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
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_ul_media_vendor/__init__.py b/tests/interfaces/openlp_core/__init__.py
similarity index 100%
rename from tests/interfaces/openlp_core_ul_media_vendor/__init__.py
rename to tests/interfaces/openlp_core/__init__.py
diff --git a/tests/functional/openlp_core_common/test_registrymixin.py b/tests/interfaces/openlp_core/api/__init__.py
similarity index 55%
rename from tests/functional/openlp_core_common/test_registrymixin.py
rename to tests/interfaces/openlp_core/api/__init__.py
index 4681d9bdb..ea62548f4 100644
--- a/tests/functional/openlp_core_common/test_registrymixin.py
+++ b/tests/interfaces/openlp_core/api/__init__.py
@@ -19,51 +19,3 @@
# 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.common package.
-"""
-import os
-from unittest import TestCase
-
-from openlp.core.common import RegistryMixin, Registry
-
-TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..', 'resources'))
-
-
-class TestRegistryMixin(TestCase):
-
- def test_registry_mixin_missing(self):
- """
- Test the registry creation and its usage
- """
- # GIVEN: A new registry
- Registry.create()
-
- # WHEN: I create a new class
- mock_1 = Test1()
-
- # THEN: The following methods are missing
- self.assertEqual(len(Registry().functions_list), 0), 'The function should not be in the dict anymore.'
-
- def test_registry_mixin_present(self):
- """
- Test the registry creation and its usage
- """
- # GIVEN: A new registry
- Registry.create()
-
- # WHEN: I create a new class
- mock_2 = Test2()
-
- # THEN: The following bootstrap methods should be present
- 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/interfaces/openlp_core/common/__init__.py b/tests/interfaces/openlp_core/common/__init__.py
new file mode 100644
index 000000000..ea62548f4
--- /dev/null
+++ b/tests/interfaces/openlp_core/common/__init__.py
@@ -0,0 +1,21 @@
+# -*- 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 #
+###############################################################################
diff --git a/tests/interfaces/openlp_core_common/test_utils.py b/tests/interfaces/openlp_core/common/test_utils.py
similarity index 100%
rename from tests/interfaces/openlp_core_common/test_utils.py
rename to tests/interfaces/openlp_core/common/test_utils.py
diff --git a/tests/interfaces/openlp_core/lib/__init__.py b/tests/interfaces/openlp_core/lib/__init__.py
new file mode 100644
index 000000000..ea62548f4
--- /dev/null
+++ b/tests/interfaces/openlp_core/lib/__init__.py
@@ -0,0 +1,21 @@
+# -*- 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 #
+###############################################################################
diff --git a/tests/interfaces/openlp_core_lib/test_pluginmanager.py b/tests/interfaces/openlp_core/lib/test_pluginmanager.py
similarity index 97%
rename from tests/interfaces/openlp_core_lib/test_pluginmanager.py
rename to 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_ui/__init__.py b/tests/interfaces/openlp_core/ui/__init__.py
similarity index 100%
rename from tests/interfaces/openlp_core_ui/__init__.py
rename to tests/interfaces/openlp_core/ui/__init__.py
diff --git a/tests/interfaces/openlp_core/ui/lib/__init__.py b/tests/interfaces/openlp_core/ui/lib/__init__.py
new file mode 100644
index 000000000..ea62548f4
--- /dev/null
+++ b/tests/interfaces/openlp_core/ui/lib/__init__.py
@@ -0,0 +1,21 @@
+# -*- 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 #
+###############################################################################
diff --git a/tests/interfaces/openlp_core_ui_lib/test_historycombobox.py b/tests/interfaces/openlp_core/ui/lib/test_historycombobox.py
similarity index 100%
rename from tests/interfaces/openlp_core_ui_lib/test_historycombobox.py
rename to tests/interfaces/openlp_core/ui/lib/test_historycombobox.py
diff --git a/tests/interfaces/openlp_core/ui/media/__init__.py b/tests/interfaces/openlp_core/ui/media/__init__.py
new file mode 100644
index 000000000..ea62548f4
--- /dev/null
+++ b/tests/interfaces/openlp_core/ui/media/__init__.py
@@ -0,0 +1,21 @@
+# -*- 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 #
+###############################################################################
diff --git a/tests/interfaces/openlp_core/ui/media/vendor/__init__.py b/tests/interfaces/openlp_core/ui/media/vendor/__init__.py
new file mode 100644
index 000000000..ea62548f4
--- /dev/null
+++ b/tests/interfaces/openlp_core/ui/media/vendor/__init__.py
@@ -0,0 +1,21 @@
+# -*- 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 #
+###############################################################################
diff --git a/tests/interfaces/openlp_core_ul_media_vendor/test_mediainfoWrapper.py b/tests/interfaces/openlp_core/ui/media/vendor/test_mediainfoWrapper.py
similarity index 97%
rename from tests/interfaces/openlp_core_ul_media_vendor/test_mediainfoWrapper.py
rename to tests/interfaces/openlp_core/ui/media/vendor/test_mediainfoWrapper.py
index d4c1891b1..f8fee253d 100644
--- a/tests/interfaces/openlp_core_ul_media_vendor/test_mediainfoWrapper.py
+++ b/tests/interfaces/openlp_core/ui/media/vendor/test_mediainfoWrapper.py
@@ -28,7 +28,7 @@ from unittest import TestCase
from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
-TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources', 'media'))
+TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', '..', 'resources', 'media'))
TEST_MEDIA = [['avi_file.avi', 61495], ['mp3_file.mp3', 134426], ['mpg_file.mpg', 9404], ['mp4_file.mp4', 188336]]
diff --git a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py b/tests/interfaces/openlp_core/ui/test_filerenamedialog.py
similarity index 96%
rename from tests/interfaces/openlp_core_ui/test_filerenamedialog.py
rename to 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
similarity index 99%
rename from tests/interfaces/openlp_core_ui/test_mainwindow.py
rename to 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
similarity index 98%
rename from tests/interfaces/openlp_core_ui/test_projectoreditform.py
rename to 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
similarity index 95%
rename from tests/interfaces/openlp_core_ui/test_projectormanager.py
rename to 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
similarity index 99%
rename from tests/interfaces/openlp_core_ui/test_projectorsourceform.py
rename to 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
similarity index 85%
rename from tests/interfaces/openlp_core_ui/test_servicemanager.py
rename to tests/interfaces/openlp_core/ui/test_servicemanager.py
index 3cac77f15..3426fffcb 100644
--- a/tests/interfaces/openlp_core_ui/test_servicemanager.py
+++ b/tests/interfaces/openlp_core/ui/test_servicemanager.py
@@ -25,103 +25,103 @@
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.ui.mainwindow import MainWindow
-from openlp.core.ui.servicemanager import ServiceManagerList
-from openlp.core.lib.serviceitem import ServiceItem
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+from openlp.core.common.registry import Registry
+from openlp.core.lib import ServiceItem, ItemCapabilities
+from openlp.core.ui.servicemanager import ServiceManager
from tests.helpers.testmixin import TestMixin
-from PyQt5 import QtCore, QtGui, QtWidgets
-
class TestServiceManager(TestCase, TestMixin):
+ """
+ Test the service manager
+ """
+
+ def _create_mock_action(self, name, **kwargs):
+ """
+ Create a fake action with some "real" attributes
+ """
+ action = QtWidgets.QAction(self.service_manager)
+ action.setObjectName(name)
+ if kwargs.get('triggers'):
+ action.triggered.connect(kwargs.pop('triggers'))
+ self.service_manager.toolbar.actions[name] = action
+ return action
def setUp(self):
"""
Create the UI
"""
Registry.create()
- Registry().set_flag('no_web_server', False)
self.setup_application()
- 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:
- self.main_window = MainWindow()
- self.service_manager = Registry().get('service_manager')
+ Registry().register('main_window', MagicMock(service_manager_settings_section='servicemanager'))
+ self.service_manager = ServiceManager()
+ self.add_toolbar_action_patcher = patch('openlp.core.ui.servicemanager.OpenLPToolbar.add_toolbar_action')
+ self.mocked_add_toolbar_action = self.add_toolbar_action_patcher.start()
+ self.mocked_add_toolbar_action.side_effect = self._create_mock_action
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
- del self.main_window
+ self.add_toolbar_action_patcher.stop()
+ del self.service_manager
def test_basic_service_manager(self):
"""
Test the Service Manager UI Functionality
"""
# GIVEN: A New Service Manager instance
-
# WHEN I have set up the display
self.service_manager.setup_ui(self.service_manager)
+
# THEN the count of items should be zero
self.assertEqual(self.service_manager.service_manager_list.topLevelItemCount(), 0,
'The service manager list should be empty ')
- def test_default_context_menu(self):
+ @patch('openlp.core.ui.servicemanager.QtWidgets.QTreeWidget.itemAt')
+ @patch('openlp.core.ui.servicemanager.QtWidgets.QWidget.mapToGlobal')
+ @patch('openlp.core.ui.servicemanager.QtWidgets.QMenu.exec')
+ def test_default_context_menu(self, mocked_exec, mocked_mapToGlobal, mocked_item_at_method):
"""
Test the context_menu() method with a default service item
"""
# GIVEN: A service item added
+ mocked_item = MagicMock()
+ mocked_item.parent.return_value = None
+ mocked_item_at_method.return_value = mocked_item
+ mocked_item.data.return_value = 1
self.service_manager.setup_ui(self.service_manager)
- with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
- patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
- patch('PyQt5.QtWidgets.QMenu.exec'):
- mocked_item = MagicMock()
- mocked_item.parent.return_value = None
- mocked_item_at_method.return_value = mocked_item
- # We want 1 to be returned for the position
- mocked_item.data.return_value = 1
- # A service item without capabilities.
- service_item = ServiceItem()
- self.service_manager.service_items = [{'service_item': service_item}]
- q_point = None
- # Mocked actions.
- self.service_manager.edit_action.setVisible = MagicMock()
- self.service_manager.create_custom_action.setVisible = MagicMock()
- self.service_manager.maintain_action.setVisible = MagicMock()
- self.service_manager.notes_action.setVisible = MagicMock()
- self.service_manager.time_action.setVisible = MagicMock()
- self.service_manager.auto_start_action.setVisible = MagicMock()
+ # A service item without capabilities.
+ service_item = ServiceItem()
+ self.service_manager.service_items = [{'service_item': service_item}]
+ q_point = None
+ # Mocked actions.
+ self.service_manager.edit_action.setVisible = MagicMock()
+ self.service_manager.create_custom_action.setVisible = MagicMock()
+ self.service_manager.maintain_action.setVisible = MagicMock()
+ self.service_manager.notes_action.setVisible = MagicMock()
+ self.service_manager.time_action.setVisible = MagicMock()
+ self.service_manager.auto_start_action.setVisible = MagicMock()
- # WHEN: Show the context menu.
- self.service_manager.context_menu(q_point)
+ # WHEN: Show the context menu.
+ self.service_manager.context_menu(q_point)
- # THEN: The following actions should be not visible.
- self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
- 'The action should be set invisible.'
- self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
- 'The action should be set invisible.'
- self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
- 'The action should be set invisible.'
- self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
- self.service_manager.time_action.setVisible.assert_called_once_with(False), \
- 'The action should be set invisible.'
- self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
- 'The action should be set invisible.'
+ # THEN: The following actions should be not visible.
+ self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
+ 'The action should be set invisible.'
+ self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
+ 'The action should be set invisible.'
+ self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
+ 'The action should be set invisible.'
+ self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
+ self.service_manager.time_action.setVisible.assert_called_once_with(False), \
+ 'The action should be set invisible.'
+ self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
+ 'The action should be set invisible.'
def test_edit_context_menu(self):
"""
diff --git a/tests/interfaces/openlp_core_ui/test_servicenotedialog.py b/tests/interfaces/openlp_core/ui/test_servicenotedialog.py
similarity index 98%
rename from tests/interfaces/openlp_core_ui/test_servicenotedialog.py
rename to 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
similarity index 98%
rename from tests/interfaces/openlp_core_ui/test_settings_form.py
rename to 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
similarity index 99%
rename from tests/interfaces/openlp_core_ui/test_shortcutlistform.py
rename to 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
similarity index 99%
rename from tests/interfaces/openlp_core_ui/test_starttimedialog.py
rename to 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
similarity index 66%
rename from tests/interfaces/openlp_core_ui/test_thememanager.py
rename to 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/widgets/__init__.py b/tests/interfaces/openlp_core/widgets/__init__.py
new file mode 100644
index 000000000..ea62548f4
--- /dev/null
+++ b/tests/interfaces/openlp_core/widgets/__init__.py
@@ -0,0 +1,21 @@
+# -*- 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 #
+###############################################################################
diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core/widgets/test_edits.py
similarity index 83%
rename from tests/interfaces/openlp_core_lib/test_searchedit.py
rename to tests/interfaces/openlp_core/widgets/test_edits.py
index 4f4c4d68c..3951e5f80 100644
--- a/tests/interfaces/openlp_core_lib/test_searchedit.py
+++ b/tests/interfaces/openlp_core/widgets/test_edits.py
@@ -27,8 +27,8 @@ from unittest.mock import MagicMock, call, patch
from PyQt5 import QtCore, QtGui, QtTest, QtWidgets
-from openlp.core.common import Registry
-from openlp.core.lib.searchedit import SearchEdit
+from openlp.core.common.registry import Registry
+from openlp.core.widgets.edits import SearchEdit, HistoryComboBox
from tests.helpers.testmixin import TestMixin
@@ -60,7 +60,7 @@ class TestSearchEdit(TestCase, TestMixin):
Registry().register('main_window', self.main_window)
settings_patcher = patch(
- 'openlp.core.lib.searchedit.Settings', return_value=MagicMock(**{'value.return_value': SearchTypes.First}))
+ 'openlp.core.widgets.edits.Settings', return_value=MagicMock(**{'value.return_value': SearchTypes.First}))
self.addCleanup(settings_patcher.stop)
self.mocked_settings = settings_patcher.start()
@@ -135,3 +135,35 @@ class TestSearchEdit(TestCase, TestMixin):
# THEN: The search edit text should be cleared and the button be hidden.
assert not self.search_edit.text(), "The search edit should not have any text."
assert self.search_edit.clear_button.isHidden(), "The clear button should be hidden."
+
+
+class TestHistoryComboBox(TestCase, TestMixin):
+ def setUp(self):
+ """
+ Some pre-test setup required.
+ """
+ Registry.create()
+ self.setup_application()
+ self.main_window = QtWidgets.QMainWindow()
+ Registry().register('main_window', self.main_window)
+ self.combo = HistoryComboBox(self.main_window)
+
+ def tearDown(self):
+ """
+ Delete all the C++ objects at the end so that we don't have a segfault
+ """
+ del self.combo
+ del self.main_window
+
+ def test_get_items(self):
+ """
+ Test the getItems() method
+ """
+ # GIVEN: The combo.
+
+ # WHEN: Add two items.
+ self.combo.addItem('test1')
+ self.combo.addItem('test2')
+
+ # THEN: The list of items should contain both strings.
+ self.assertEqual(self.combo.getItems(), ['test1', 'test2'])
diff --git a/tests/interfaces/openlp_core_ui_lib/test_listpreviewwidget.py b/tests/interfaces/openlp_core/widgets/test_views.py
similarity index 96%
rename from tests/interfaces/openlp_core_ui_lib/test_listpreviewwidget.py
rename to tests/interfaces/openlp_core/widgets/test_views.py
index 87eb9660d..f4a493f4d 100644
--- a/tests/interfaces/openlp_core_ui_lib/test_listpreviewwidget.py
+++ b/tests/interfaces/openlp_core/widgets/test_views.py
@@ -20,16 +20,16 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
- Package to test the openlp.core.ui.lib.listpreviewwidget.
+ Package to test the openlp.core.widgets.views.
"""
from unittest import TestCase
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
+from openlp.core.widgets.views import ListPreviewWidget
from tests.utils.osdinteraction import read_service_from_file
from tests.helpers.testmixin import TestMixin
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