This commit is contained in:
Phill Ridout 2017-10-27 22:11:29 +01:00
commit fe5430e157
378 changed files with 4524 additions and 4273 deletions

View File

@ -44,3 +44,4 @@ coverage
tags
output
htmlcov
openlp-test-projectordb.sqlite

View File

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

View File

@ -22,7 +22,3 @@
"""
The :mod:`openlp` module contains all the project produced OpenLP functionality
"""
from openlp import core, plugins
__all__ = ['core', 'plugins']

View File

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

View File

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

View File

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

View File

@ -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'

View File

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

View File

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

View File

@ -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):
"""

View File

@ -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):
"""

View File

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

View File

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

View File

@ -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'

View File

@ -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
"""

425
openlp/core/app.py Normal file
View File

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

View File

@ -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:

View File

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

View File

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

View File

@ -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__')

556
openlp/core/common/i18n.py Normal file
View File

@ -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 &amp; 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', '<strong>The search you have entered is empty or shorter '
'than 3 characters long.</strong><br><br>Please try again with '
'a longer search.')
self.BibleNoBiblesTitle = translate('OpenLP.Ui', 'No Bibles Available')
self.BibleNoBibles = translate('OpenLP.Ui', '<strong>There are no Bibles currently installed.</strong><br><br>'
'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<br>',
book_chapter, '%(range)s', chapter, gap, psalm, ' 23%(range)s24<br>',
book_chapter, '%(verse)s', verse, '%(range)s', verse, gap, psalm, ' 23%(verse)s1%(range)s2<br>',
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<br>',
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<br>',
book_chapter, '%(verse)s', verse, '%(range)s', chapter, '%(verse)s', verse, gap, psalm,
' 23%(verse)s1%(range)s24%(verse)s1<br><br>', 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

View File

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

View File

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

View File

@ -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):
"""

View File

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

View File

@ -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]

View File

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

View File

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

View File

@ -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 &amp; 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', '<strong>The search you have entered is empty or shorter '
'than 3 characters long.</strong><br><br>Please try again with '
'a longer search.')
self.BibleNoBiblesTitle = translate('OpenLP.Ui', 'No Bibles Available')
self.BibleNoBibles = translate('OpenLP.Ui', '<strong>There are no Bibles currently installed.</strong><br><br>'
'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<br>',
book_chapter, '%(range)s', chapter, gap, psalm, ' 23%(range)s24<br>',
book_chapter, '%(verse)s', verse, '%(range)s', verse, gap, psalm, ' 23%(verse)s1%(range)s2<br>',
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<br>',
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<br>',
book_chapter, '%(verse)s', verse, '%(range)s', chapter, '%(verse)s', verse, gap, psalm,
' 23%(verse)s1%(range)s24%(verse)s1<br><br>', 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)

View File

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

View File

@ -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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.

View File

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

View File

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

View File

@ -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.
"""

View File

@ -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::
(<identifier>, <icon>, <title>, <place holder text>)
For instance::
(1, <QIcon instance>, "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())

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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:<br \\>{text}'.format(text=file_list)))
msg.setStandardButtons(msg.Ok)
ans = msg.exec()
msg.exec()
return True
def _set_plugin_status(self, field, tag):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.
"""

View File

@ -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):
"""

View File

@ -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__')

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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):
"""

View File

@ -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 = []

View File

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

View File

@ -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):
"""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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}')

View File

@ -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'

View File

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

View File

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

View File

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

View File

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

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