forked from openlp/openlp
HEAD
This commit is contained in:
commit
fe5430e157
@ -44,3 +44,4 @@ coverage
|
||||
tags
|
||||
output
|
||||
htmlcov
|
||||
openlp-test-projectordb.sqlite
|
||||
|
15
openlp.py
15
openlp.py
@ -27,16 +27,27 @@ import faulthandler
|
||||
import multiprocessing
|
||||
import sys
|
||||
|
||||
from openlp.core.app import main
|
||||
from openlp.core.common import is_win, is_macosx
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core import main
|
||||
from openlp.core.common.path import create_paths
|
||||
|
||||
|
||||
def set_up_fault_handling():
|
||||
"""
|
||||
Set up the Python fault handler
|
||||
"""
|
||||
# Create the cache directory if it doesn't exist, and enable the fault handler to log to an error log file
|
||||
create_paths(AppLocation.get_directory(AppLocation.CacheDir))
|
||||
faulthandler.enable(open(str(AppLocation.get_directory(AppLocation.CacheDir) / 'error.log'), 'wb'))
|
||||
|
||||
faulthandler.enable(open(str(AppLocation.get_directory(AppLocation.CacheDir) / 'error.log'), 'wb'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""
|
||||
Instantiate and run the application.
|
||||
"""
|
||||
faulthandler.enable()
|
||||
set_up_fault_handling()
|
||||
# Add support for using multiprocessing from frozen Windows executable (built using PyInstaller),
|
||||
# see https://docs.python.org/3/library/multiprocessing.html#multiprocessing.freeze_support
|
||||
if is_win():
|
||||
|
@ -22,7 +22,3 @@
|
||||
"""
|
||||
The :mod:`openlp` module contains all the project produced OpenLP functionality
|
||||
"""
|
||||
|
||||
from openlp import core, plugins
|
||||
|
||||
__all__ = ['core', 'plugins']
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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__)
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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__)
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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'
|
||||
|
@ -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
425
openlp/core/app.py
Normal 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))
|
@ -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:
|
||||
|
@ -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__)
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
556
openlp/core/common/i18n.py
Normal 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 & 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
|
@ -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
|
@ -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
|
@ -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):
|
||||
"""
|
@ -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)
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -1,187 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2017 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`uistrings` module provides standard strings for OpenLP.
|
||||
"""
|
||||
import logging
|
||||
import itertools
|
||||
|
||||
from openlp.core.common import translate
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UiStrings(object):
|
||||
"""
|
||||
Provide standard strings for objects to use.
|
||||
"""
|
||||
__instance__ = None
|
||||
|
||||
def __new__(cls):
|
||||
"""
|
||||
Override the default object creation method to return a single instance.
|
||||
"""
|
||||
if not cls.__instance__:
|
||||
cls.__instance__ = object.__new__(cls)
|
||||
return cls.__instance__
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
These strings should need a good reason to be retranslated elsewhere.
|
||||
Should some/more/less of these have an & attached?
|
||||
"""
|
||||
self.About = translate('OpenLP.Ui', 'About')
|
||||
self.Add = translate('OpenLP.Ui', '&Add')
|
||||
self.AddGroup = translate('OpenLP.Ui', 'Add group')
|
||||
self.AddGroupDot = translate('OpenLP.Ui', 'Add group.')
|
||||
self.Advanced = translate('OpenLP.Ui', 'Advanced')
|
||||
self.AllFiles = translate('OpenLP.Ui', 'All Files')
|
||||
self.Automatic = translate('OpenLP.Ui', 'Automatic')
|
||||
self.BackgroundColor = translate('OpenLP.Ui', 'Background Color')
|
||||
self.BackgroundColorColon = translate('OpenLP.Ui', 'Background color:')
|
||||
self.BibleShortSearchTitle = translate('OpenLP.Ui', 'Search is Empty or too Short')
|
||||
self.BibleShortSearch = translate('OpenLP.Ui', '<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)
|
@ -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
|
@ -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.
|
@ -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__)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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__)
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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__)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
"""
|
||||
|
@ -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())
|
@ -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__)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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())]
|
@ -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)
|
@ -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)
|
@ -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()))
|
@ -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)
|
@ -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.
|
||||
"""
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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__')
|
||||
|
||||
|
@ -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')
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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 = []
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}')
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user