forked from openlp/openlp
Head
This commit is contained in:
commit
66534a58ab
@ -46,6 +46,7 @@ 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
|
||||
|
@ -39,9 +39,9 @@ from openlp.core.api.http import application
|
||||
from openlp.core.api.poll import Poller
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import UiStrings
|
||||
from openlp.core.common.mixins import OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.path import create_paths
|
||||
from openlp.core.common.registry import RegistryProperties, Registry
|
||||
from openlp.core.common.registry import Registry, RegistryBase
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.common.i18n import translate
|
||||
|
||||
@ -73,7 +73,7 @@ class HttpWorker(QtCore.QObject):
|
||||
pass
|
||||
|
||||
|
||||
class HttpServer(RegistryMixin, RegistryProperties, OpenLPMixin):
|
||||
class HttpServer(RegistryBase, RegistryProperties, LogMixin):
|
||||
"""
|
||||
Wrapper round a server instance
|
||||
"""
|
||||
|
@ -23,7 +23,7 @@
|
||||
import json
|
||||
|
||||
from openlp.core.common.httputils import get_web_page
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.settings import Settings
|
||||
|
||||
|
||||
|
@ -31,8 +31,8 @@ import time
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.common.mixins import OpenLPMixin
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
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
|
||||
"""
|
||||
|
@ -38,7 +38,7 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.common import is_macosx, is_win
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import LanguageManager, UiStrings, translate
|
||||
from openlp.core.common.mixins import OpenLPMixin
|
||||
from openlp.core.common.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
|
||||
@ -59,7 +59,7 @@ __all__ = ['OpenLP', 'main']
|
||||
log = logging.getLogger()
|
||||
|
||||
|
||||
class OpenLP(OpenLPMixin, QtWidgets.QApplication):
|
||||
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.
|
||||
|
@ -25,25 +25,29 @@ Provide Error Handling and login Services
|
||||
import logging
|
||||
import inspect
|
||||
|
||||
from openlp.core.common import trace_error_handler, de_hump
|
||||
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 OpenLPMixin(object):
|
||||
class LogMixin(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:
|
||||
@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):
|
||||
"""
|
||||
@ -93,30 +97,127 @@ class OpenLPMixin(object):
|
||||
self.logger.exception(message)
|
||||
|
||||
|
||||
class RegistryMixin(object):
|
||||
class RegistryProperties(object):
|
||||
"""
|
||||
This adds registry components to classes to use at run time.
|
||||
"""
|
||||
def __init__(self, parent):
|
||||
@property
|
||||
def application(self):
|
||||
"""
|
||||
Register the class and bootstrap hooks.
|
||||
Adds the openlp to the class dynamically.
|
||||
Windows needs to access the application in a dynamic manner.
|
||||
"""
|
||||
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)
|
||||
if is_win():
|
||||
return Registry().get('application')
|
||||
else:
|
||||
if not hasattr(self, '_application') or not self._application:
|
||||
self._application = Registry().get('application')
|
||||
return self._application
|
||||
|
||||
def bootstrap_initialise(self):
|
||||
@property
|
||||
def plugin_manager(self):
|
||||
"""
|
||||
Dummy method to be overridden
|
||||
Adds the plugin manager to the class dynamically
|
||||
"""
|
||||
pass
|
||||
if not hasattr(self, '_plugin_manager') or not self._plugin_manager:
|
||||
self._plugin_manager = Registry().get('plugin_manager')
|
||||
return self._plugin_manager
|
||||
|
||||
def bootstrap_post_set_up(self):
|
||||
@property
|
||||
def image_manager(self):
|
||||
"""
|
||||
Dummy method to be overridden
|
||||
Adds the image manager to the class dynamically
|
||||
"""
|
||||
pass
|
||||
if not hasattr(self, '_image_manager') or not self._image_manager:
|
||||
self._image_manager = Registry().get('image_manager')
|
||||
return self._image_manager
|
||||
|
||||
@property
|
||||
def media_controller(self):
|
||||
"""
|
||||
Adds the media controller to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_media_controller') or not self._media_controller:
|
||||
self._media_controller = Registry().get('media_controller')
|
||||
return self._media_controller
|
||||
|
||||
@property
|
||||
def service_manager(self):
|
||||
"""
|
||||
Adds the service manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_service_manager') or not self._service_manager:
|
||||
self._service_manager = Registry().get('service_manager')
|
||||
return self._service_manager
|
||||
|
||||
@property
|
||||
def preview_controller(self):
|
||||
"""
|
||||
Adds the preview controller to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_preview_controller') or not self._preview_controller:
|
||||
self._preview_controller = Registry().get('preview_controller')
|
||||
return self._preview_controller
|
||||
|
||||
@property
|
||||
def live_controller(self):
|
||||
"""
|
||||
Adds the live controller to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_live_controller') or not self._live_controller:
|
||||
self._live_controller = Registry().get('live_controller')
|
||||
return self._live_controller
|
||||
|
||||
@property
|
||||
def main_window(self):
|
||||
"""
|
||||
Adds the main window to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_main_window') or not self._main_window:
|
||||
self._main_window = Registry().get('main_window')
|
||||
return self._main_window
|
||||
|
||||
@property
|
||||
def renderer(self):
|
||||
"""
|
||||
Adds the Renderer to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_renderer') or not self._renderer:
|
||||
self._renderer = Registry().get('renderer')
|
||||
return self._renderer
|
||||
|
||||
@property
|
||||
def theme_manager(self):
|
||||
"""
|
||||
Adds the theme manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_theme_manager') or not self._theme_manager:
|
||||
self._theme_manager = Registry().get('theme_manager')
|
||||
return self._theme_manager
|
||||
|
||||
@property
|
||||
def settings_form(self):
|
||||
"""
|
||||
Adds the settings form to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_settings_form') or not self._settings_form:
|
||||
self._settings_form = Registry().get('settings_form')
|
||||
return self._settings_form
|
||||
|
||||
@property
|
||||
def alerts_manager(self):
|
||||
"""
|
||||
Adds the alerts manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_alerts_manager') or not self._alerts_manager:
|
||||
self._alerts_manager = Registry().get('alerts_manager')
|
||||
return self._alerts_manager
|
||||
|
||||
@property
|
||||
def projector_manager(self):
|
||||
"""
|
||||
Adds the projector manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_projector_manager') or not self._projector_manager:
|
||||
self._projector_manager = Registry().get('projector_manager')
|
||||
return self._projector_manager
|
||||
|
@ -25,7 +25,7 @@ Provide Registry Services
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from openlp.core.common import is_win, 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):
|
||||
@ -178,128 +187,30 @@ class Registry(object):
|
||||
del self.working_flags[key]
|
||||
|
||||
|
||||
class RegistryProperties(object):
|
||||
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)
|
||||
|
||||
@property
|
||||
def application(self):
|
||||
def bootstrap_initialise(self):
|
||||
"""
|
||||
Adds the openlp to the class dynamically.
|
||||
Windows needs to access the application in a dynamic manner.
|
||||
Dummy method to be overridden
|
||||
"""
|
||||
if is_win():
|
||||
return Registry().get('application')
|
||||
else:
|
||||
if not hasattr(self, '_application') or not self._application:
|
||||
self._application = Registry().get('application')
|
||||
return self._application
|
||||
pass
|
||||
|
||||
@property
|
||||
def plugin_manager(self):
|
||||
def bootstrap_post_set_up(self):
|
||||
"""
|
||||
Adds the plugin manager to the class dynamically
|
||||
Dummy method to be overridden
|
||||
"""
|
||||
if not hasattr(self, '_plugin_manager') or not self._plugin_manager:
|
||||
self._plugin_manager = Registry().get('plugin_manager')
|
||||
return self._plugin_manager
|
||||
|
||||
@property
|
||||
def image_manager(self):
|
||||
"""
|
||||
Adds the image manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_image_manager') or not self._image_manager:
|
||||
self._image_manager = Registry().get('image_manager')
|
||||
return self._image_manager
|
||||
|
||||
@property
|
||||
def media_controller(self):
|
||||
"""
|
||||
Adds the media controller to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_media_controller') or not self._media_controller:
|
||||
self._media_controller = Registry().get('media_controller')
|
||||
return self._media_controller
|
||||
|
||||
@property
|
||||
def service_manager(self):
|
||||
"""
|
||||
Adds the service manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_service_manager') or not self._service_manager:
|
||||
self._service_manager = Registry().get('service_manager')
|
||||
return self._service_manager
|
||||
|
||||
@property
|
||||
def preview_controller(self):
|
||||
"""
|
||||
Adds the preview controller to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_preview_controller') or not self._preview_controller:
|
||||
self._preview_controller = Registry().get('preview_controller')
|
||||
return self._preview_controller
|
||||
|
||||
@property
|
||||
def live_controller(self):
|
||||
"""
|
||||
Adds the live controller to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_live_controller') or not self._live_controller:
|
||||
self._live_controller = Registry().get('live_controller')
|
||||
return self._live_controller
|
||||
|
||||
@property
|
||||
def main_window(self):
|
||||
"""
|
||||
Adds the main window to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_main_window') or not self._main_window:
|
||||
self._main_window = Registry().get('main_window')
|
||||
return self._main_window
|
||||
|
||||
@property
|
||||
def renderer(self):
|
||||
"""
|
||||
Adds the Renderer to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_renderer') or not self._renderer:
|
||||
self._renderer = Registry().get('renderer')
|
||||
return self._renderer
|
||||
|
||||
@property
|
||||
def theme_manager(self):
|
||||
"""
|
||||
Adds the theme manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_theme_manager') or not self._theme_manager:
|
||||
self._theme_manager = Registry().get('theme_manager')
|
||||
return self._theme_manager
|
||||
|
||||
@property
|
||||
def settings_form(self):
|
||||
"""
|
||||
Adds the settings form to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_settings_form') or not self._settings_form:
|
||||
self._settings_form = Registry().get('settings_form')
|
||||
return self._settings_form
|
||||
|
||||
@property
|
||||
def alerts_manager(self):
|
||||
"""
|
||||
Adds the alerts manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_alerts_manager') or not self._alerts_manager:
|
||||
self._alerts_manager = Registry().get('alerts_manager')
|
||||
return self._alerts_manager
|
||||
|
||||
@property
|
||||
def projector_manager(self):
|
||||
"""
|
||||
Adds the projector manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, '_projector_manager') or not self._projector_manager:
|
||||
self._projector_manager = Registry().get('projector_manager')
|
||||
return self._projector_manager
|
||||
pass
|
||||
|
@ -25,9 +25,9 @@ import re
|
||||
from string import Template
|
||||
from PyQt5 import QtGui, QtCore, QtWebKitWidgets
|
||||
|
||||
from openlp.core.common.mixins import OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.path import path_to_str
|
||||
from openlp.core.common.registry import Registry, 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 FormattingTags, ImageSource, ItemCapabilities, ServiceItem, expand_tags, build_chords_css, \
|
||||
@ -46,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.
|
||||
|
@ -350,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
|
||||
|
@ -30,14 +30,15 @@ from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.path import Path, path_to_str, str_to_path
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
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,9 +26,10 @@ import logging
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.common.i18n import UiStrings
|
||||
from openlp.core.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__)
|
||||
|
@ -26,12 +26,12 @@ import os
|
||||
|
||||
from openlp.core.common import extension_loader
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.registry import RegistryBase
|
||||
from openlp.core.lib import Plugin, PluginStatus
|
||||
|
||||
|
||||
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.
|
||||
|
@ -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
|
||||
|
@ -514,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.common.settings import Settings
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.ui import create_widget_action
|
||||
|
||||
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())
|
@ -35,7 +35,7 @@ from PyQt5 import QtGui
|
||||
from openlp.core.common import md5_hash
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags, expand_chords
|
||||
|
||||
|
@ -25,7 +25,7 @@ own tab to the settings dialog.
|
||||
"""
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
|
||||
|
||||
class SettingsTab(QtWidgets.QWidget, RegistryProperties):
|
||||
|
@ -32,8 +32,9 @@ 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__)
|
||||
|
||||
@ -122,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)
|
||||
|
@ -72,10 +72,10 @@ except ImportError:
|
||||
|
||||
from openlp.core.common import is_linux
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.ui.exceptiondialog import Ui_ExceptionDialog
|
||||
from openlp.core.ui.lib.filedialog import FileDialog
|
||||
from openlp.core.widgets.dialogs import FileDialog
|
||||
from openlp.core.version import get_version
|
||||
|
||||
|
||||
|
@ -25,7 +25,8 @@ The file rename dialog.
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui.filerenamedialog import Ui_FileRenameDialog
|
||||
|
||||
|
||||
|
@ -38,7 +38,8 @@ from openlp.core.common import clean_button_text, trace_error_handler
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.path import Path, create_paths
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.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
|
||||
|
@ -33,7 +33,8 @@ from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.lib import SettingsTab
|
||||
from openlp.core.ui.lib import ColorButton, PathEdit
|
||||
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,153 +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.i18n import UiStrings
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
|
||||
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, 'target': self.itemAt(event.pos())})
|
||||
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.i18n import UiStrings, translate
|
||||
from openlp.core.common.path import Path, path_to_str, str_to_path
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.ui.lib.filedialog import FileDialog
|
||||
|
||||
|
||||
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()
|
||||
|
||||
@QtCore.pyqtProperty('QVariant')
|
||||
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,205 +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.common.i18n import translate
|
||||
from openlp.core.lib import 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,145 +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 is_win
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
|
||||
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)
|
@ -36,9 +36,9 @@ from PyQt5 import QtCore, QtWidgets, QtWebKit, QtWebKitWidgets, QtGui, QtMultime
|
||||
from openlp.core.common import is_macosx, is_win
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.mixins import OpenLPMixin
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.path import path_to_str
|
||||
from openlp.core.common.registry import Registry, 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.lib import ServiceItem, ImageSource, build_html, expand_tags, image_to_byte
|
||||
@ -131,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
|
||||
"""
|
||||
@ -603,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.
|
||||
"""
|
||||
|
@ -41,7 +41,8 @@ from openlp.core.common.actions import ActionList, CategoryOrder
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import LanguageManager, UiStrings, translate
|
||||
from openlp.core.common.path import Path, copyfile, create_paths, path_to_str, str_to_path
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.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
|
||||
@ -50,9 +51,8 @@ from openlp.core.lib.ui import create_action
|
||||
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, LiveController, PluginForm, \
|
||||
ShortcutListForm, FormattingTagForm, PreviewController
|
||||
from openlp.core.ui.firsttimeform import FirstTimeForm
|
||||
from openlp.core.ui.lib.dockwidget import OpenLPDockWidget
|
||||
from openlp.core.ui.lib.filedialog import FileDialog
|
||||
from openlp.core.ui.lib.mediadockmanager import MediaDockManager
|
||||
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
|
||||
|
@ -31,8 +31,8 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.api.http import register_endpoint
|
||||
from openlp.core.common import extension_loader
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.mixins import OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.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
|
||||
@ -42,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__)
|
||||
@ -91,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.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.ui.media import MediaState
|
||||
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
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.i18n import UiStrings, translate
|
||||
@ -31,7 +32,7 @@ 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):
|
||||
|
@ -27,7 +27,7 @@ import logging
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.lib import PluginStatus
|
||||
from openlp.core.ui.plugindialog import Ui_PluginViewDialog
|
||||
|
||||
|
@ -26,7 +26,7 @@ from PyQt5 import QtCore, QtWidgets, QtPrintSupport
|
||||
|
||||
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):
|
||||
|
@ -30,7 +30,8 @@ from PyQt5 import QtCore, QtGui, QtWidgets, QtPrintSupport
|
||||
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.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
|
||||
|
@ -30,8 +30,8 @@ import logging
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.mixins import OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.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
|
||||
@ -40,9 +40,9 @@ from openlp.core.lib.projector.constants import ERROR_MSG, ERROR_STRING, E_AUTHE
|
||||
S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, S_STANDBY, S_WARMUP
|
||||
from openlp.core.lib.projector.db import ProjectorDB
|
||||
from openlp.core.lib.projector.pjlink import PJLink, PJLinkUDP
|
||||
from openlp.core.ui.lib import OpenLPToolbar
|
||||
from openlp.core.ui.projector.editform import ProjectorEditForm
|
||||
from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle
|
||||
from openlp.core.widgets.toolbar import OpenLPToolbar
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.debug('projectormanager loaded')
|
||||
@ -276,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.
|
||||
"""
|
||||
@ -288,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 = []
|
||||
|
@ -24,7 +24,8 @@ The service item edit dialog
|
||||
"""
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui.serviceitemeditdialog import Ui_ServiceItemEditDialog
|
||||
|
||||
|
||||
|
@ -36,15 +36,15 @@ from openlp.core.common import ThemeLevel, split_filename, delete_file
|
||||
from openlp.core.common.actions import ActionList, CategoryOrder
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import UiStrings, format_time, translate
|
||||
from openlp.core.common.mixins import OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common.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, RegistryProperties
|
||||
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):
|
||||
@ -56,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.
|
||||
@ -117,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,
|
||||
@ -147,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.'),
|
||||
@ -254,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'),
|
||||
@ -285,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.
|
||||
@ -320,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 = []
|
||||
|
@ -25,9 +25,10 @@ The :mod:`~openlp.core.ui.servicenoteform` module contains the `ServiceNoteForm`
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.ui.lib import SpellTextEdit
|
||||
from openlp.core.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):
|
||||
|
@ -27,7 +27,8 @@ import logging
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.api import ApiTab
|
||||
from openlp.core.common.registry 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
|
||||
|
@ -29,7 +29,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common.actions import ActionList
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.ui.shortcutlistdialog import Ui_ShortcutListDialog
|
||||
|
||||
|
@ -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
|
||||
@ -33,15 +32,15 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from openlp.core.common import SlideLimits
|
||||
from openlp.core.common.actions import ActionList, CategoryOrder
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.mixins import OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.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.
|
||||
@ -82,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
|
||||
@ -133,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):
|
||||
"""
|
||||
@ -1505,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.
|
||||
"""
|
||||
@ -1513,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'
|
||||
@ -1529,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.
|
||||
"""
|
||||
@ -1541,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'
|
||||
|
@ -25,7 +25,8 @@ The actual start time form.
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
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
|
||||
|
||||
|
@ -28,7 +28,8 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common import get_images_filter, is_not_image_file
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.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
|
||||
|
@ -31,17 +31,17 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from openlp.core.common import delete_file
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import UiStrings, translate, get_locale_key
|
||||
from openlp.core.common.mixins import OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.path import Path, copyfile, create_paths, path_to_str, rmtree
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.registry import Registry, RegistryBase
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \
|
||||
check_item_selected, create_thumb, validate_thumb
|
||||
from openlp.core.lib.theme import Theme, BackgroundType
|
||||
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
|
||||
from openlp.core.ui import FileRenameForm, ThemeForm
|
||||
from openlp.core.ui.lib import OpenLPToolbar
|
||||
from openlp.core.ui.lib.filedialog import FileDialog
|
||||
from openlp.core.widgets.dialogs import FileDialog
|
||||
from openlp.core.widgets.toolbar import OpenLPToolbar
|
||||
|
||||
|
||||
class Ui_ThemeManager(object):
|
||||
@ -125,7 +125,7 @@ class Ui_ThemeManager(object):
|
||||
self.theme_list_widget.currentItemChanged.connect(self.check_list_state)
|
||||
|
||||
|
||||
class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManager, RegistryProperties):
|
||||
class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, RegistryProperties):
|
||||
"""
|
||||
Manages the orders of Theme.
|
||||
"""
|
||||
|
@ -29,7 +29,8 @@ from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType
|
||||
from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets
|
||||
from openlp.core.ui.lib import ColorButton, PathEdit
|
||||
from openlp.core.widgets.buttons import ColorButton
|
||||
from openlp.core.widgets.edits import PathEdit
|
||||
|
||||
|
||||
class Ui_ThemeWizard(object):
|
||||
|
@ -20,15 +20,41 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The media manager dock.
|
||||
The :mod:`~openlp.core.widgets.docks` module contains a customised base dock widget and dock widgets
|
||||
"""
|
||||
import logging
|
||||
|
||||
from openlp.core.lib import StringContent
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.lib import StringContent, build_icon
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OpenLPDockWidget(QtWidgets.QDockWidget):
|
||||
"""
|
||||
Custom DockWidget class to handle events
|
||||
"""
|
||||
def __init__(self, parent=None, name=None, icon=None):
|
||||
"""
|
||||
Initialise the DockWidget
|
||||
"""
|
||||
log.debug('Initialise the %s widget' % name)
|
||||
super(OpenLPDockWidget, self).__init__(parent)
|
||||
if name:
|
||||
self.setObjectName(name)
|
||||
if icon:
|
||||
self.setWindowIcon(build_icon(icon))
|
||||
# Sort out the minimum width.
|
||||
screens = ScreenList()
|
||||
main_window_docbars = screens.current['size'].width() // 5
|
||||
if main_window_docbars > 300:
|
||||
self.setMinimumWidth(300)
|
||||
else:
|
||||
self.setMinimumWidth(main_window_docbars)
|
||||
|
||||
|
||||
class MediaDockManager(object):
|
||||
"""
|
||||
Provide a repository for MediaManagerItems
|
573
openlp/core/widgets/edits.py
Normal file
573
openlp/core/widgets/edits.py
Normal file
@ -0,0 +1,573 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2017 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.core.widgets.edits` module contains all the customised edit widgets used in OpenLP
|
||||
"""
|
||||
import logging
|
||||
import re
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.path import Path, path_to_str, str_to_path
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import FormattingTags, build_icon
|
||||
from openlp.core.lib.ui import create_widget_action, create_action
|
||||
from openlp.core.widgets.dialogs import FileDialog
|
||||
from openlp.core.widgets.enums import PathEditType
|
||||
|
||||
try:
|
||||
import enchant
|
||||
from enchant import DictNotFoundError
|
||||
from enchant.errors import Error
|
||||
ENCHANT_AVAILABLE = True
|
||||
except ImportError:
|
||||
ENCHANT_AVAILABLE = False
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SearchEdit(QtWidgets.QLineEdit):
|
||||
"""
|
||||
This is a specialised QLineEdit with a "clear" button inside for searches.
|
||||
"""
|
||||
searchTypeChanged = QtCore.pyqtSignal(QtCore.QVariant)
|
||||
cleared = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, parent, settings_section):
|
||||
"""
|
||||
Constructor.
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.settings_section = settings_section
|
||||
self._current_search_type = -1
|
||||
self.clear_button = QtWidgets.QToolButton(self)
|
||||
self.clear_button.setIcon(build_icon(':/system/clear_shortcut.png'))
|
||||
self.clear_button.setCursor(QtCore.Qt.ArrowCursor)
|
||||
self.clear_button.setStyleSheet('QToolButton { border: none; padding: 0px; }')
|
||||
self.clear_button.resize(18, 18)
|
||||
self.clear_button.hide()
|
||||
self.clear_button.clicked.connect(self._on_clear_button_clicked)
|
||||
self.textChanged.connect(self._on_search_edit_text_changed)
|
||||
self._update_style_sheet()
|
||||
self.setAcceptDrops(False)
|
||||
|
||||
def _update_style_sheet(self):
|
||||
"""
|
||||
Internal method to update the stylesheet depending on which widgets are available and visible.
|
||||
"""
|
||||
frame_width = self.style().pixelMetric(QtWidgets.QStyle.PM_DefaultFrameWidth)
|
||||
right_padding = self.clear_button.width() + frame_width
|
||||
if hasattr(self, 'menu_button'):
|
||||
left_padding = self.menu_button.width()
|
||||
stylesheet = 'QLineEdit {{ padding-left:{left}px; padding-right: {right}px; }} '.format(left=left_padding,
|
||||
right=right_padding)
|
||||
else:
|
||||
stylesheet = 'QLineEdit {{ padding-right: {right}px; }} '.format(right=right_padding)
|
||||
self.setStyleSheet(stylesheet)
|
||||
msz = self.minimumSizeHint()
|
||||
self.setMinimumSize(max(msz.width(), self.clear_button.width() + (frame_width * 2) + 2),
|
||||
max(msz.height(), self.clear_button.height() + (frame_width * 2) + 2))
|
||||
|
||||
def resizeEvent(self, event):
|
||||
"""
|
||||
Reimplemented method to react to resizing of the widget.
|
||||
|
||||
:param event: The event that happened.
|
||||
"""
|
||||
size = self.clear_button.size()
|
||||
frame_width = self.style().pixelMetric(QtWidgets.QStyle.PM_DefaultFrameWidth)
|
||||
self.clear_button.move(self.rect().right() - frame_width - size.width(),
|
||||
(self.rect().bottom() + 1 - size.height()) // 2)
|
||||
if hasattr(self, 'menu_button'):
|
||||
size = self.menu_button.size()
|
||||
self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) // 2)
|
||||
|
||||
def current_search_type(self):
|
||||
"""
|
||||
Readonly property to return the current search type.
|
||||
"""
|
||||
return self._current_search_type
|
||||
|
||||
def set_current_search_type(self, identifier):
|
||||
"""
|
||||
Set a new current search type.
|
||||
|
||||
:param identifier: The search type identifier (int).
|
||||
"""
|
||||
menu = self.menu_button.menu()
|
||||
for action in menu.actions():
|
||||
if identifier == action.data():
|
||||
self.setPlaceholderText(action.placeholder_text)
|
||||
self.menu_button.setDefaultAction(action)
|
||||
self._current_search_type = identifier
|
||||
Settings().setValue('{section}/last used search type'.format(section=self.settings_section), identifier)
|
||||
self.searchTypeChanged.emit(identifier)
|
||||
return True
|
||||
|
||||
def set_search_types(self, items):
|
||||
"""
|
||||
A list of tuples to be used in the search type menu. The first item in the list will be preselected as the
|
||||
default.
|
||||
|
||||
:param items: The list of tuples to use. The tuples should contain an integer identifier, an icon (QIcon
|
||||
instance or string) and a title for the item in the menu. In short, they should look like this::
|
||||
|
||||
(<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())
|
||||
|
||||
|
||||
class PathEdit(QtWidgets.QWidget):
|
||||
"""
|
||||
The :class:`~openlp.core.widgets.edits.PathEdit` class subclasses QWidget to create a custom widget for use when
|
||||
a file or directory needs to be selected.
|
||||
"""
|
||||
pathChanged = QtCore.pyqtSignal(Path)
|
||||
|
||||
def __init__(self, parent=None, path_type=PathEditType.Files, default_path=None, dialog_caption=None,
|
||||
show_revert=True):
|
||||
"""
|
||||
Initialise the PathEdit widget
|
||||
|
||||
:param QtWidget.QWidget | None: The parent of the widget. This is just passed to the super method.
|
||||
:param str dialog_caption: Used to customise the caption in the QFileDialog.
|
||||
:param openlp.core.common.path.Path default_path: The default path. This is set as the path when the revert
|
||||
button is clicked
|
||||
:param bool show_revert: Used to determine if the 'revert button' should be visible.
|
||||
:rtype: None
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.default_path = default_path
|
||||
self.dialog_caption = dialog_caption
|
||||
self._path_type = path_type
|
||||
self._path = None
|
||||
self.filters = '{all_files} (*)'.format(all_files=UiStrings().AllFiles)
|
||||
self._setup(show_revert)
|
||||
|
||||
def _setup(self, show_revert):
|
||||
"""
|
||||
Set up the widget
|
||||
:param bool show_revert: Show or hide the revert button
|
||||
:rtype: None
|
||||
"""
|
||||
widget_layout = QtWidgets.QHBoxLayout()
|
||||
widget_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.line_edit = QtWidgets.QLineEdit(self)
|
||||
widget_layout.addWidget(self.line_edit)
|
||||
self.browse_button = QtWidgets.QToolButton(self)
|
||||
self.browse_button.setIcon(build_icon(':/general/general_open.png'))
|
||||
widget_layout.addWidget(self.browse_button)
|
||||
self.revert_button = QtWidgets.QToolButton(self)
|
||||
self.revert_button.setIcon(build_icon(':/general/general_revert.png'))
|
||||
self.revert_button.setVisible(show_revert)
|
||||
widget_layout.addWidget(self.revert_button)
|
||||
self.setLayout(widget_layout)
|
||||
# Signals and Slots
|
||||
self.browse_button.clicked.connect(self.on_browse_button_clicked)
|
||||
self.revert_button.clicked.connect(self.on_revert_button_clicked)
|
||||
self.line_edit.editingFinished.connect(self.on_line_edit_editing_finished)
|
||||
self.update_button_tool_tips()
|
||||
|
||||
@QtCore.pyqtProperty('QVariant')
|
||||
def path(self):
|
||||
"""
|
||||
A property getter method to return the selected path.
|
||||
|
||||
:return: The selected path
|
||||
:rtype: openlp.core.common.path.Path
|
||||
"""
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, path):
|
||||
"""
|
||||
A Property setter method to set the selected path
|
||||
|
||||
:param openlp.core.common.path.Path path: The path to set the widget to
|
||||
:rtype: None
|
||||
"""
|
||||
self._path = path
|
||||
text = path_to_str(path)
|
||||
self.line_edit.setText(text)
|
||||
self.line_edit.setToolTip(text)
|
||||
|
||||
@property
|
||||
def path_type(self):
|
||||
"""
|
||||
A property getter method to return the path_type. Path type allows you to sepecify if the user is restricted to
|
||||
selecting a file or directory.
|
||||
|
||||
:return: The type selected
|
||||
:rtype: PathType
|
||||
"""
|
||||
return self._path_type
|
||||
|
||||
@path_type.setter
|
||||
def path_type(self, path_type):
|
||||
"""
|
||||
A Property setter method to set the path type
|
||||
|
||||
:param PathType path_type: The type of path to select
|
||||
:rtype: None
|
||||
"""
|
||||
self._path_type = path_type
|
||||
self.update_button_tool_tips()
|
||||
|
||||
def update_button_tool_tips(self):
|
||||
"""
|
||||
Called to update the tooltips on the buttons. This is changing path types, and when the widget is initalised
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if self._path_type == PathEditType.Directories:
|
||||
self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for directory.'))
|
||||
self.revert_button.setToolTip(translate('OpenLP.PathEdit', 'Revert to default directory.'))
|
||||
else:
|
||||
self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for file.'))
|
||||
self.revert_button.setToolTip(translate('OpenLP.PathEdit', 'Revert to default file.'))
|
||||
|
||||
def on_browse_button_clicked(self):
|
||||
"""
|
||||
A handler to handle a click on the browse button.
|
||||
|
||||
Show the QFileDialog and process the input from the user
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
caption = self.dialog_caption
|
||||
path = None
|
||||
if self._path_type == PathEditType.Directories:
|
||||
if not caption:
|
||||
caption = translate('OpenLP.PathEdit', 'Select Directory')
|
||||
path = FileDialog.getExistingDirectory(self, caption, self._path, FileDialog.ShowDirsOnly)
|
||||
elif self._path_type == PathEditType.Files:
|
||||
if not caption:
|
||||
caption = self.dialog_caption = translate('OpenLP.PathEdit', 'Select File')
|
||||
path, filter_used = FileDialog.getOpenFileName(self, caption, self._path, self.filters)
|
||||
if path:
|
||||
self.on_new_path(path)
|
||||
|
||||
def on_revert_button_clicked(self):
|
||||
"""
|
||||
A handler to handle a click on the revert button.
|
||||
|
||||
Set the new path to the value of the default_path instance variable.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
self.on_new_path(self.default_path)
|
||||
|
||||
def on_line_edit_editing_finished(self):
|
||||
"""
|
||||
A handler to handle when the line edit has finished being edited.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
path = str_to_path(self.line_edit.text())
|
||||
self.on_new_path(path)
|
||||
|
||||
def on_new_path(self, path):
|
||||
"""
|
||||
A method called to validate and set a new path.
|
||||
|
||||
Emits the pathChanged Signal
|
||||
|
||||
:param openlp.core.common.path.Path path: The new path
|
||||
:rtype: None
|
||||
"""
|
||||
if self._path != path:
|
||||
self._path = path
|
||||
self.pathChanged.emit(path)
|
||||
|
||||
|
||||
class SpellTextEdit(QtWidgets.QPlainTextEdit):
|
||||
"""
|
||||
Spell checking widget based on QPlanTextEdit.
|
||||
|
||||
Based on code from http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check
|
||||
"""
|
||||
def __init__(self, parent=None, formatting_tags_allowed=True):
|
||||
"""
|
||||
Constructor.
|
||||
"""
|
||||
global ENCHANT_AVAILABLE
|
||||
super(SpellTextEdit, self).__init__(parent)
|
||||
self.formatting_tags_allowed = formatting_tags_allowed
|
||||
# Default dictionary based on the current locale.
|
||||
if ENCHANT_AVAILABLE:
|
||||
try:
|
||||
self.dictionary = enchant.Dict()
|
||||
self.highlighter = Highlighter(self.document())
|
||||
self.highlighter.spelling_dictionary = self.dictionary
|
||||
except (Error, DictNotFoundError):
|
||||
ENCHANT_AVAILABLE = False
|
||||
log.debug('Could not load default dictionary')
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""
|
||||
Handle mouse clicks within the text edit region.
|
||||
"""
|
||||
if event.button() == QtCore.Qt.RightButton:
|
||||
# Rewrite the mouse event to a left button event so the cursor is moved to the location of the pointer.
|
||||
event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
|
||||
event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier)
|
||||
QtWidgets.QPlainTextEdit.mousePressEvent(self, event)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
"""
|
||||
Provide the context menu for the text edit region.
|
||||
"""
|
||||
popup_menu = self.createStandardContextMenu()
|
||||
# Select the word under the cursor.
|
||||
cursor = self.textCursor()
|
||||
# only select text if not already selected
|
||||
if not cursor.hasSelection():
|
||||
cursor.select(QtGui.QTextCursor.WordUnderCursor)
|
||||
self.setTextCursor(cursor)
|
||||
# Add menu with available languages.
|
||||
if ENCHANT_AVAILABLE:
|
||||
lang_menu = QtWidgets.QMenu(translate('OpenLP.SpellTextEdit', 'Language:'))
|
||||
for lang in enchant.list_languages():
|
||||
action = create_action(lang_menu, lang, text=lang, checked=lang == self.dictionary.tag)
|
||||
lang_menu.addAction(action)
|
||||
popup_menu.insertSeparator(popup_menu.actions()[0])
|
||||
popup_menu.insertMenu(popup_menu.actions()[0], lang_menu)
|
||||
lang_menu.triggered.connect(self.set_language)
|
||||
# Check if the selected word is misspelled and offer spelling suggestions if it is.
|
||||
if ENCHANT_AVAILABLE and self.textCursor().hasSelection():
|
||||
text = self.textCursor().selectedText()
|
||||
if not self.dictionary.check(text):
|
||||
spell_menu = QtWidgets.QMenu(translate('OpenLP.SpellTextEdit', 'Spelling Suggestions'))
|
||||
for word in self.dictionary.suggest(text):
|
||||
action = SpellAction(word, spell_menu)
|
||||
action.correct.connect(self.correct_word)
|
||||
spell_menu.addAction(action)
|
||||
# Only add the spelling suggests to the menu if there are suggestions.
|
||||
if spell_menu.actions():
|
||||
popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
|
||||
tag_menu = QtWidgets.QMenu(translate('OpenLP.SpellTextEdit', 'Formatting Tags'))
|
||||
if self.formatting_tags_allowed:
|
||||
for html in FormattingTags.get_html_tags():
|
||||
action = SpellAction(html['desc'], tag_menu)
|
||||
action.correct.connect(self.html_tag)
|
||||
tag_menu.addAction(action)
|
||||
popup_menu.insertSeparator(popup_menu.actions()[0])
|
||||
popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
|
||||
popup_menu.exec(event.globalPos())
|
||||
|
||||
def set_language(self, action):
|
||||
"""
|
||||
Changes the language for this spelltextedit.
|
||||
|
||||
:param action: The action.
|
||||
"""
|
||||
self.dictionary = enchant.Dict(action.text())
|
||||
self.highlighter.spelling_dictionary = self.dictionary
|
||||
self.highlighter.highlightBlock(self.toPlainText())
|
||||
self.highlighter.rehighlight()
|
||||
|
||||
def correct_word(self, word):
|
||||
"""
|
||||
Replaces the selected text with word.
|
||||
"""
|
||||
cursor = self.textCursor()
|
||||
cursor.beginEditBlock()
|
||||
cursor.removeSelectedText()
|
||||
cursor.insertText(word)
|
||||
cursor.endEditBlock()
|
||||
|
||||
def html_tag(self, tag):
|
||||
"""
|
||||
Replaces the selected text with word.
|
||||
"""
|
||||
tag = tag.replace('&', '')
|
||||
for html in FormattingTags.get_html_tags():
|
||||
if tag == html['desc']:
|
||||
cursor = self.textCursor()
|
||||
if self.textCursor().hasSelection():
|
||||
text = cursor.selectedText()
|
||||
cursor.beginEditBlock()
|
||||
cursor.removeSelectedText()
|
||||
cursor.insertText(html['start tag'])
|
||||
cursor.insertText(text)
|
||||
cursor.insertText(html['end tag'])
|
||||
cursor.endEditBlock()
|
||||
else:
|
||||
cursor = self.textCursor()
|
||||
cursor.insertText(html['start tag'])
|
||||
cursor.insertText(html['end tag'])
|
||||
|
||||
|
||||
class Highlighter(QtGui.QSyntaxHighlighter):
|
||||
"""
|
||||
Provides a text highlighter for pointing out spelling errors in text.
|
||||
"""
|
||||
WORDS = r'(?iu)[\w\']+'
|
||||
|
||||
def __init__(self, *args):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
super(Highlighter, self).__init__(*args)
|
||||
self.spelling_dictionary = None
|
||||
|
||||
def highlightBlock(self, text):
|
||||
"""
|
||||
Highlight mis spelt words in a block of text.
|
||||
|
||||
Note, this is a Qt hook.
|
||||
"""
|
||||
if not self.spelling_dictionary:
|
||||
return
|
||||
text = str(text)
|
||||
char_format = QtGui.QTextCharFormat()
|
||||
char_format.setUnderlineColor(QtCore.Qt.red)
|
||||
char_format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
|
||||
for word_object in re.finditer(self.WORDS, text):
|
||||
if not self.spelling_dictionary.check(word_object.group()):
|
||||
self.setFormat(word_object.start(), word_object.end() - word_object.start(), char_format)
|
||||
|
||||
|
||||
class SpellAction(QtWidgets.QAction):
|
||||
"""
|
||||
A special QAction that returns the text in a signal.
|
||||
"""
|
||||
correct = QtCore.pyqtSignal(str)
|
||||
|
||||
def __init__(self, *args):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
super(SpellAction, self).__init__(*args)
|
||||
self.triggered.connect(lambda x: self.correct.emit(self.text()))
|
||||
|
||||
|
||||
class HistoryComboBox(QtWidgets.QComboBox):
|
||||
"""
|
||||
The :class:`~openlp.core.common.historycombobox.HistoryComboBox` widget emulates the QLineEdit ``returnPressed``
|
||||
signal for when the :kbd:`Enter` or :kbd:`Return` keys are pressed, and saves anything that is typed into the edit
|
||||
box into its list.
|
||||
"""
|
||||
returnPressed = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
Initialise the combo box, setting duplicates to False and the insert policy to insert items at the top.
|
||||
|
||||
:param parent: The parent widget
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.setDuplicatesEnabled(False)
|
||||
self.setEditable(True)
|
||||
self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
||||
self.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""
|
||||
Override the inherited keyPressEvent method to emit the ``returnPressed`` signal and to save the current text to
|
||||
the dropdown list.
|
||||
|
||||
:param event: The keyboard event
|
||||
"""
|
||||
# Handle Enter and Return ourselves
|
||||
if event.key() == QtCore.Qt.Key_Enter or event.key() == QtCore.Qt.Key_Return:
|
||||
# Emit the returnPressed signal
|
||||
self.returnPressed.emit()
|
||||
# Save the current text to the dropdown list
|
||||
if self.currentText() and self.findText(self.currentText()) == -1:
|
||||
self.insertItem(0, self.currentText())
|
||||
# Let the parent handle any keypress events
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def focusOutEvent(self, event):
|
||||
"""
|
||||
Override the inherited focusOutEvent to save the current text to the dropdown list.
|
||||
|
||||
:param event: The focus event
|
||||
"""
|
||||
# Save the current text to the dropdown list
|
||||
if self.currentText() and self.findText(self.currentText()) == -1:
|
||||
self.insertItem(0, self.currentText())
|
||||
# Let the parent handle any keypress events
|
||||
super().focusOutEvent(event)
|
||||
|
||||
def getItems(self):
|
||||
"""
|
||||
Get all the items from the history
|
||||
|
||||
:return: A list of strings
|
||||
"""
|
||||
return [self.itemText(i) for i in range(self.count())]
|
@ -19,18 +19,12 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.core.widgets.enums` module contains enumerations used by the widgets
|
||||
"""
|
||||
from enum import Enum
|
||||
|
||||
from .colorbutton import ColorButton
|
||||
from .listpreviewwidget import ListPreviewWidget
|
||||
from .listwidgetwithdnd import ListWidgetWithDnD
|
||||
from .mediadockmanager import MediaDockManager
|
||||
from .dockwidget import OpenLPDockWidget
|
||||
from .toolbar import OpenLPToolbar
|
||||
from .wizard import OpenLPWizard, WizardStrings
|
||||
from .pathedit import PathEdit, PathType
|
||||
from .spelltextedit import SpellTextEdit
|
||||
from .treewidgetwithdnd import TreeWidgetWithDnD
|
||||
|
||||
__all__ = ['ColorButton', 'ListPreviewWidget', 'ListWidgetWithDnD', 'MediaDockManager', 'OpenLPDockWidget',
|
||||
'OpenLPToolbar', 'OpenLPWizard', 'PathEdit', 'PathType', 'SpellTextEdit', 'TreeWidgetWithDnD',
|
||||
'WizardStrings']
|
||||
class PathEditType(Enum):
|
||||
Files = 1
|
||||
Directories = 2
|
@ -40,7 +40,7 @@ class OpenLPToolbar(QtWidgets.QToolBar):
|
||||
"""
|
||||
Initialise the toolbar.
|
||||
"""
|
||||
super(OpenLPToolbar, self).__init__(parent)
|
||||
super().__init__(parent)
|
||||
# useful to be able to reuse button icons...
|
||||
self.setIconSize(QtCore.QSize(20, 20))
|
||||
self.actions = {}
|
@ -23,10 +23,14 @@
|
||||
The :mod:`listpreviewwidget` is a widget that lists the slides in the slide controller.
|
||||
It is based on a QTableWidget but represents its contents in list form.
|
||||
"""
|
||||
import os
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common import is_win
|
||||
from openlp.core.common.i18n import UiStrings
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import ImageSource, ItemCapabilities, ServiceItem
|
||||
|
||||
@ -238,3 +242,241 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
|
||||
Returns the number of slides this widget holds.
|
||||
"""
|
||||
return super(ListPreviewWidget, self).rowCount()
|
||||
|
||||
|
||||
class ListWidgetWithDnD(QtWidgets.QListWidget):
|
||||
"""
|
||||
Provide a list widget to store objects and handle drag and drop events
|
||||
"""
|
||||
def __init__(self, parent=None, name=''):
|
||||
"""
|
||||
Initialise the list widget
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.mime_data_text = name
|
||||
self.no_results_text = UiStrings().NoResults
|
||||
self.setSpacing(1)
|
||||
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
self.setAlternatingRowColors(True)
|
||||
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
|
||||
def activateDnD(self):
|
||||
"""
|
||||
Activate DnD of widget
|
||||
"""
|
||||
self.setAcceptDrops(True)
|
||||
self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
|
||||
Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)
|
||||
|
||||
def clear(self, search_while_typing=False):
|
||||
"""
|
||||
Re-implement clear, so that we can customise feedback when using 'Search as you type'
|
||||
|
||||
:param search_while_typing: True if we want to display the customised message
|
||||
:return: None
|
||||
"""
|
||||
if search_while_typing:
|
||||
self.no_results_text = UiStrings().ShortResults
|
||||
else:
|
||||
self.no_results_text = UiStrings().NoResults
|
||||
super().clear()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
"""
|
||||
Drag and drop event does not care what data is selected as the recipient will use events to request the data
|
||||
move just tell it what plugin to call
|
||||
"""
|
||||
if event.buttons() != QtCore.Qt.LeftButton:
|
||||
event.ignore()
|
||||
return
|
||||
if not self.selectedItems():
|
||||
event.ignore()
|
||||
return
|
||||
drag = QtGui.QDrag(self)
|
||||
mime_data = QtCore.QMimeData()
|
||||
drag.setMimeData(mime_data)
|
||||
mime_data.setText(self.mime_data_text)
|
||||
drag.exec(QtCore.Qt.CopyAction)
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
"""
|
||||
When something is dragged into this object, check if you should be able to drop it in here.
|
||||
"""
|
||||
if event.mimeData().hasUrls():
|
||||
event.accept()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
"""
|
||||
Make an object droppable, and set it to copy the contents of the object, not move it.
|
||||
"""
|
||||
if event.mimeData().hasUrls():
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def dropEvent(self, event):
|
||||
"""
|
||||
Receive drop event check if it is a file and process it if it is.
|
||||
|
||||
:param event: Handle of the event pint passed
|
||||
"""
|
||||
if event.mimeData().hasUrls():
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
files = []
|
||||
for url in event.mimeData().urls():
|
||||
local_file = os.path.normpath(url.toLocalFile())
|
||||
if os.path.isfile(local_file):
|
||||
files.append(local_file)
|
||||
elif os.path.isdir(local_file):
|
||||
listing = os.listdir(local_file)
|
||||
for file in listing:
|
||||
files.append(os.path.join(local_file, file))
|
||||
Registry().execute('{mime_data}_dnd'.format(mime_data=self.mime_data_text),
|
||||
{'files': files, 'target': self.itemAt(event.pos())})
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def allItems(self):
|
||||
"""
|
||||
An generator to list all the items in the widget
|
||||
|
||||
:return: a generator
|
||||
"""
|
||||
for row in range(self.count()):
|
||||
yield self.item(row)
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""
|
||||
Re-implement paintEvent so that we can add 'No Results' text when the listWidget is empty.
|
||||
|
||||
:param event: A QPaintEvent
|
||||
:return: None
|
||||
"""
|
||||
super().paintEvent(event)
|
||||
if not self.count():
|
||||
viewport = self.viewport()
|
||||
painter = QtGui.QPainter(viewport)
|
||||
font = QtGui.QFont()
|
||||
font.setItalic(True)
|
||||
painter.setFont(font)
|
||||
painter.drawText(QtCore.QRect(0, 0, viewport.width(), viewport.height()),
|
||||
(QtCore.Qt.AlignHCenter | QtCore.Qt.TextWordWrap), self.no_results_text)
|
||||
|
||||
|
||||
class TreeWidgetWithDnD(QtWidgets.QTreeWidget):
|
||||
"""
|
||||
Provide a tree widget to store objects and handle drag and drop events
|
||||
"""
|
||||
def __init__(self, parent=None, name=''):
|
||||
"""
|
||||
Initialise the tree widget
|
||||
"""
|
||||
super(TreeWidgetWithDnD, self).__init__(parent)
|
||||
self.mime_data_text = name
|
||||
self.allow_internal_dnd = False
|
||||
self.header().close()
|
||||
self.default_indentation = self.indentation()
|
||||
self.setIndentation(0)
|
||||
self.setAnimated(True)
|
||||
|
||||
def activateDnD(self):
|
||||
"""
|
||||
Activate DnD of widget
|
||||
"""
|
||||
self.setAcceptDrops(True)
|
||||
self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
|
||||
Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)
|
||||
Registry().register_function(('%s_dnd_internal' % self.mime_data_text), self.parent().dnd_move_internal)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
"""
|
||||
Drag and drop event does not care what data is selected as the recipient will use events to request the data
|
||||
move just tell it what plugin to call
|
||||
|
||||
:param event: The event that occurred
|
||||
"""
|
||||
if event.buttons() != QtCore.Qt.LeftButton:
|
||||
event.ignore()
|
||||
return
|
||||
if not self.selectedItems():
|
||||
event.ignore()
|
||||
return
|
||||
drag = QtGui.QDrag(self)
|
||||
mime_data = QtCore.QMimeData()
|
||||
drag.setMimeData(mime_data)
|
||||
mime_data.setText(self.mime_data_text)
|
||||
drag.exec(QtCore.Qt.CopyAction)
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
"""
|
||||
Receive drag enter event, check if it is a file or internal object and allow it if it is.
|
||||
|
||||
:param event: The event that occurred
|
||||
"""
|
||||
if event.mimeData().hasUrls():
|
||||
event.accept()
|
||||
elif self.allow_internal_dnd:
|
||||
event.accept()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
"""
|
||||
Receive drag move event, check if it is a file or internal object and allow it if it is.
|
||||
|
||||
:param event: The event that occurred
|
||||
"""
|
||||
QtWidgets.QTreeWidget.dragMoveEvent(self, event)
|
||||
if event.mimeData().hasUrls():
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
elif self.allow_internal_dnd:
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def dropEvent(self, event):
|
||||
"""
|
||||
Receive drop event, check if it is a file or internal object and process it if it is.
|
||||
|
||||
:param event: Handle of the event pint passed
|
||||
"""
|
||||
# If we are on Windows, OpenLP window will not be set on top. For example, user can drag images to Library and
|
||||
# the folder stays on top of the group creation box. This piece of code fixes this issue.
|
||||
if is_win():
|
||||
self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
|
||||
self.setWindowState(QtCore.Qt.WindowNoState)
|
||||
if event.mimeData().hasUrls():
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
files = []
|
||||
for url in event.mimeData().urls():
|
||||
local_file = url.toLocalFile()
|
||||
if os.path.isfile(local_file):
|
||||
files.append(local_file)
|
||||
elif os.path.isdir(local_file):
|
||||
listing = os.listdir(local_file)
|
||||
for file_name in listing:
|
||||
files.append(os.path.join(local_file, file_name))
|
||||
Registry().execute('%s_dnd' % self.mime_data_text, {'files': files, 'target': self.itemAt(event.pos())})
|
||||
elif self.allow_internal_dnd:
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
Registry().execute('%s_dnd_internal' % self.mime_data_text, self.itemAt(event.pos()))
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
# Convenience methods for emulating a QListWidget. This helps keeping MediaManagerItem simple.
|
||||
def addItem(self, item):
|
||||
self.addTopLevelItem(item)
|
||||
|
||||
def count(self):
|
||||
return self.topLevelItemCount()
|
||||
|
||||
def item(self, index):
|
||||
return self.topLevelItem(index)
|
@ -28,11 +28,12 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common import is_macosx
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.ui import add_welcome_page
|
||||
from openlp.core.ui.lib.filedialog import FileDialog
|
||||
from openlp.core.widgets.dialogs import FileDialog
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -26,12 +26,12 @@ displaying of alerts.
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.mixins import OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.registry import Registry, RegistryBase
|
||||
from openlp.core.common.settings import Settings
|
||||
|
||||
|
||||
class AlertsManager(OpenLPMixin, RegistryMixin, QtCore.QObject, RegistryProperties):
|
||||
class AlertsManager(QtCore.QObject, RegistryBase, LogMixin, RegistryProperties):
|
||||
"""
|
||||
AlertsManager manages the settings of Alerts.
|
||||
"""
|
||||
|
@ -26,7 +26,7 @@ from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import SettingsTab
|
||||
from openlp.core.lib.ui import create_valign_selection_widgets
|
||||
from openlp.core.ui.lib.colorbutton import ColorButton
|
||||
from openlp.core.widgets.buttons import ColorButton
|
||||
|
||||
|
||||
class AlertsTab(SettingsTab):
|
||||
|
@ -40,8 +40,8 @@ from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib.db import delete_database
|
||||
from openlp.core.lib.exceptions import ValidationError
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.lib.pathedit import PathEdit
|
||||
from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.core.widgets.edits import PathEdit
|
||||
from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.plugins.bibles.lib.db import clean_filename
|
||||
from openlp.plugins.bibles.lib.importers.http import CWExtract, BGExtract, BSExtract
|
||||
from openlp.plugins.bibles.lib.manager import BibleFormat
|
||||
|
@ -26,7 +26,7 @@ import re
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from .editbibledialog import Ui_EditBibleDialog
|
||||
from openlp.plugins.bibles.lib import BibleStrings
|
||||
|
@ -23,15 +23,15 @@
|
||||
from lxml import etree, objectify
|
||||
from zipfile import is_zipfile
|
||||
|
||||
from openlp.core.common.mixins import OpenLPMixin
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.i18n import get_language, translate
|
||||
from openlp.core.lib import ValidationError
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.plugins.bibles.lib.db import AlternativeBookNamesDB, BibleDB, BiblesResourcesDB
|
||||
|
||||
|
||||
class BibleImport(OpenLPMixin, RegistryProperties, BibleDB):
|
||||
class BibleImport(BibleDB, LogMixin, RegistryProperties):
|
||||
"""
|
||||
Helper class to import bibles from a third party source into OpenLP
|
||||
"""
|
||||
@ -96,6 +96,8 @@ class BibleImport(OpenLPMixin, RegistryProperties, BibleDB):
|
||||
if language_form.exec(bible_name):
|
||||
combo_box = language_form.language_combo_box
|
||||
language_id = combo_box.itemData(combo_box.currentIndex())
|
||||
else:
|
||||
return False
|
||||
if not language_id:
|
||||
return None
|
||||
self.save_meta('language_id', language_id)
|
||||
|
@ -19,7 +19,6 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import chardet
|
||||
import logging
|
||||
import os
|
||||
|
@ -32,7 +32,8 @@ from bs4 import BeautifulSoup, NavigableString, Tag
|
||||
|
||||
from openlp.core.common.httputils import get_web_page
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.plugins.bibles.lib import SearchResults
|
||||
from openlp.plugins.bibles.lib.bibleimport import BibleImport
|
||||
|
@ -24,9 +24,8 @@ import logging
|
||||
from openlp.core.common import delete_file
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.mixins import OpenLPMixin
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.path import Path
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.plugins.bibles.lib import LanguageSelection, parse_reference
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
|
||||
@ -97,7 +96,7 @@ class BibleFormat(object):
|
||||
]
|
||||
|
||||
|
||||
class BibleManager(OpenLPMixin, RegistryProperties):
|
||||
class BibleManager(LogMixin, RegistryProperties):
|
||||
"""
|
||||
The Bible manager which holds and manages all the Bibles.
|
||||
"""
|
||||
|
@ -30,9 +30,9 @@ from openlp.core.common.i18n import UiStrings, translate, get_locale_key
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext
|
||||
from openlp.core.lib.searchedit import SearchEdit
|
||||
from openlp.core.lib.ui import set_case_insensitive_completer, create_horizontal_adjusting_combo_box, \
|
||||
critical_error_message_box, find_and_set_in_combo_box, build_icon
|
||||
from openlp.core.widgets.edits import SearchEdit
|
||||
from openlp.plugins.bibles.forms.bibleimportform import BibleImportForm
|
||||
from openlp.plugins.bibles.forms.editbibleform import EditBibleForm
|
||||
from openlp.plugins.bibles.lib import DisplayStyle, LayoutStyle, VerseReferenceList, \
|
||||
|
@ -25,7 +25,7 @@ from PyQt5 import QtWidgets
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.ui import create_button, create_button_box
|
||||
from openlp.core.ui.lib import SpellTextEdit
|
||||
from openlp.core.widgets.edits import SpellTextEdit
|
||||
|
||||
|
||||
class Ui_CustomSlideEditDialog(object):
|
||||
|
@ -25,7 +25,7 @@ from PyQt5 import QtWidgets
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import SettingsTab
|
||||
from openlp.core.ui.lib.colorbutton import ColorButton
|
||||
from openlp.core.widgets.buttons import ColorButton
|
||||
|
||||
|
||||
class ImageTab(SettingsTab):
|
||||
|
@ -33,7 +33,7 @@ from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import ItemCapabilities, MediaManagerItem, ServiceItemContext, StringContent, build_icon, \
|
||||
check_item_selected, create_thumb, validate_thumb
|
||||
from openlp.core.lib.ui import create_widget_action, critical_error_message_box
|
||||
from openlp.core.ui.lib.treewidgetwithdnd import TreeWidgetWithDnD
|
||||
from openlp.core.widgets.views import TreeWidgetWithDnD
|
||||
from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm
|
||||
from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
|
||||
|
||||
|
@ -29,7 +29,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common import is_win, is_linux, is_macosx
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.plugins.media.forms.mediaclipselectordialog import Ui_MediaClipSelector
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.media.vlcplayer import get_vlc
|
||||
|
@ -28,7 +28,8 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import UiStrings, translate, get_locale_key
|
||||
from openlp.core.common.path import Path, path_to_str, create_paths
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, ServiceItem, ServiceItemContext, \
|
||||
build_icon, check_item_selected
|
||||
|
@ -26,7 +26,7 @@ from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import SettingsTab
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.lib import PathEdit
|
||||
from openlp.core.widgets.edits import PathEdit
|
||||
from openlp.plugins.presentations.lib.pdfcontroller import PdfController
|
||||
|
||||
|
||||
|
@ -29,8 +29,9 @@ import multiprocessing
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.plugins.songs.lib import delete_song
|
||||
from openlp.plugins.songs.lib.db import Song
|
||||
from openlp.plugins.songs.forms.songreviewwidget import SongReviewWidget
|
||||
|
@ -31,10 +31,11 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import UiStrings, translate, get_natural_key
|
||||
from openlp.core.common.path import create_paths, copyfile
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib import PluginStatus, MediaType, create_separated_list
|
||||
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
|
||||
from openlp.core.ui.lib.filedialog import FileDialog
|
||||
from openlp.core.widgets.dialogs import FileDialog
|
||||
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
|
||||
from openlp.plugins.songs.forms.editverseform import EditVerseForm
|
||||
from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm
|
||||
|
@ -26,7 +26,7 @@ from openlp.core.common.settings import Settings
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.ui import create_button_box
|
||||
from openlp.core.ui.lib import SpellTextEdit
|
||||
from openlp.core.widgets.edits import SpellTextEdit
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
|
||||
|
||||
|
@ -32,8 +32,9 @@ from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import create_separated_list
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.lib import PathEdit, PathType
|
||||
from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.core.widgets.edits import PathEdit
|
||||
from openlp.core.widgets.enums import PathEditType
|
||||
from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.plugins.songs.lib.db import Song
|
||||
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
|
||||
|
||||
@ -124,7 +125,7 @@ class SongExportForm(OpenLPWizard):
|
||||
self.selected_list_widget.setObjectName('selected_list_widget')
|
||||
self.grid_layout.addWidget(self.selected_list_widget, 1, 0, 1, 2)
|
||||
self.output_directory_path_edit = PathEdit(
|
||||
self.export_song_page, PathType.Directories,
|
||||
self.export_song_page, PathEditType.Directories,
|
||||
dialog_caption=translate('SongsPlugin.ExportWizardForm', 'Select Destination Folder'), show_revert=False)
|
||||
self.output_directory_path_edit.path = Settings().value('songs/last directory export')
|
||||
self.directory_label = QtWidgets.QLabel(self.export_song_page)
|
||||
|
@ -27,12 +27,13 @@ import logging
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.lib import PathEdit, PathType
|
||||
from openlp.core.ui.lib.filedialog import FileDialog
|
||||
from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.core.widgets.dialogs import FileDialog
|
||||
from openlp.core.widgets.edits import PathEdit
|
||||
from openlp.core.widgets.enums import PathEditType
|
||||
from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -383,10 +384,10 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
|
||||
file_path_label = QtWidgets.QLabel(import_widget)
|
||||
file_path_layout.addWidget(file_path_label)
|
||||
if select_mode == SongFormatSelect.SingleFile:
|
||||
path_type = PathType.Files
|
||||
path_type = PathEditType.Files
|
||||
dialog_caption = WizardStrings.OpenTypeFile.format(file_type=format_name)
|
||||
else:
|
||||
path_type = PathType.Directories
|
||||
path_type = PathEditType.Directories
|
||||
dialog_caption = WizardStrings.OpenTypeFolder.format(folder_name=format_name)
|
||||
path_edit = PathEdit(
|
||||
parent=import_widget, path_type=path_type, dialog_caption=dialog_caption, show_revert=False)
|
||||
|
@ -25,7 +25,8 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from sqlalchemy.sql import and_
|
||||
|
||||
from openlp.core.common.i18n import UiStrings, translate, get_natural_key
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.plugins.songs.forms.authorsform import AuthorsForm
|
||||
from openlp.plugins.songs.forms.topicsform import TopicsForm
|
||||
|
@ -28,7 +28,7 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.ui import SingleColumnTableWidget
|
||||
from openlp.core.ui.lib.historycombobox import HistoryComboBox
|
||||
from openlp.core.widgets.edits import HistoryComboBox
|
||||
|
||||
|
||||
class Ui_SongSelectDialog(object):
|
||||
|
@ -26,7 +26,7 @@ import logging
|
||||
|
||||
from openlp.core.common import is_win
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.ui.lib.wizard import WizardStrings
|
||||
from openlp.core.widgets.wizard import WizardStrings
|
||||
from .importers.opensong import OpenSongImport
|
||||
from .importers.easyslides import EasySlidesImport
|
||||
from .importers.openlp import OpenLPSongImport
|
||||
|
@ -44,7 +44,7 @@ NOTE_REGEX = re.compile(r'\(.*?\)')
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FieldDescEntry:
|
||||
class FieldDescEntry(object):
|
||||
def __init__(self, name, field_type, size):
|
||||
self.name = name
|
||||
self.field_type = field_type
|
||||
|
@ -88,7 +88,7 @@ import re
|
||||
from lxml import etree, objectify
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.ui.lib.wizard import WizardStrings
|
||||
from openlp.core.widgets.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib import clean_song, VerseType
|
||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
||||
|
@ -31,7 +31,7 @@ from sqlalchemy.orm.exc import UnmappedClassError
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.lib.db import BaseModel
|
||||
from openlp.core.ui.lib.wizard import WizardStrings
|
||||
from openlp.core.widgets.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib import clean_song
|
||||
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic, MediaFile
|
||||
from .songimport import SongImport
|
||||
|
@ -27,7 +27,7 @@ import logging
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from openlp.core.ui.lib.wizard import WizardStrings
|
||||
from openlp.core.widgets.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||
from openlp.plugins.songs.lib.ui import SongStrings
|
||||
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, OpenLyricsError
|
||||
|
@ -25,8 +25,8 @@ Powerpraise song files into the current database.
|
||||
"""
|
||||
from lxml import objectify
|
||||
|
||||
from openlp.core.ui.lib.wizard import WizardStrings
|
||||
from .songimport import SongImport
|
||||
from openlp.core.widgets.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||
|
||||
|
||||
class PowerPraiseImport(SongImport):
|
||||
|
@ -29,8 +29,8 @@ from lxml import objectify, etree
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common import get_file_encoding
|
||||
from openlp.core.ui.lib.wizard import WizardStrings
|
||||
from .songimport import SongImport
|
||||
from openlp.core.widgets.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||
|
||||
|
||||
class PresentationManagerImport(SongImport):
|
||||
|
@ -27,9 +27,9 @@ import base64
|
||||
import logging
|
||||
from lxml import objectify
|
||||
|
||||
from openlp.core.ui.lib.wizard import WizardStrings
|
||||
from openlp.core.widgets.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib import strip_rtf
|
||||
from .songimport import SongImport
|
||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -29,7 +29,7 @@ from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.path import copyfile, create_paths
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui.lib.wizard import WizardStrings
|
||||
from openlp.core.widgets.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib import clean_song, VerseType
|
||||
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
|
||||
from openlp.plugins.songs.lib.ui import SongStrings
|
||||
|
@ -27,7 +27,7 @@ import logging
|
||||
import re
|
||||
import struct
|
||||
|
||||
from openlp.core.ui.lib.wizard import WizardStrings
|
||||
from openlp.core.widgets.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
|
||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||
|
||||
|
@ -30,7 +30,7 @@ from lxml import etree
|
||||
from openlp.core.common import clean_filename
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.path import create_paths
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -29,7 +29,7 @@ from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.path import Path
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.lib.filedialog import FileDialog
|
||||
from openlp.core.widgets.dialogs import FileDialog
|
||||
from openlp.plugins.songs.lib.db import Song
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -23,7 +23,7 @@
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.plugins.songusage.lib.db import SongUsageItem
|
||||
from openlp.plugins.songusage.forms.songusagedeletedialog import Ui_SongUsageDeleteDialog
|
||||
|
||||
|
@ -24,7 +24,8 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.ui import create_button_box
|
||||
from openlp.core.ui.lib import PathEdit, PathType
|
||||
from openlp.core.widgets.edits import PathEdit
|
||||
from openlp.core.widgets.enums import PathEditType
|
||||
|
||||
|
||||
class Ui_SongUsageDetailDialog(object):
|
||||
@ -68,7 +69,7 @@ class Ui_SongUsageDetailDialog(object):
|
||||
self.file_horizontal_layout.setSpacing(8)
|
||||
self.file_horizontal_layout.setContentsMargins(8, 8, 8, 8)
|
||||
self.file_horizontal_layout.setObjectName('file_horizontal_layout')
|
||||
self.report_path_edit = PathEdit(self.file_group_box, path_type=PathType.Directories, show_revert=False)
|
||||
self.report_path_edit = PathEdit(self.file_group_box, path_type=PathEditType.Directories, show_revert=False)
|
||||
self.file_horizontal_layout.addWidget(self.report_path_edit)
|
||||
self.vertical_layout.addWidget(self.file_group_box)
|
||||
self.button_box = create_button_box(song_usage_detail_dialog, 'button_box', ['cancel', 'ok'])
|
||||
|
@ -25,7 +25,7 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from sqlalchemy.sql import and_
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import RegistryProperties
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.common.path import create_paths
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
|
@ -347,7 +347,7 @@ class TestInit(TestCase, TestMixin):
|
||||
self.assertTrue(mocked_log.exception.called)
|
||||
self.assertFalse(result, 'delete_file should return False when an OSError is raised')
|
||||
|
||||
def test_get_file_encoding_done_test(self):
|
||||
def test_get_file_encoding_done(self):
|
||||
"""
|
||||
Test get_file_encoding when the detector sets done to True
|
||||
"""
|
||||
@ -368,7 +368,7 @@ class TestInit(TestCase, TestMixin):
|
||||
mocked_universal_detector_inst.close.assert_called_once_with()
|
||||
self.assertEqual(result, encoding_result)
|
||||
|
||||
def test_get_file_encoding_eof_test(self):
|
||||
def test_get_file_encoding_eof(self):
|
||||
"""
|
||||
Test get_file_encoding when the end of the file is reached
|
||||
"""
|
||||
@ -390,7 +390,7 @@ class TestInit(TestCase, TestMixin):
|
||||
mocked_universal_detector_inst.close.assert_called_once_with()
|
||||
self.assertEqual(result, encoding_result)
|
||||
|
||||
def test_get_file_encoding_oserror_test(self):
|
||||
def test_get_file_encoding_oserror(self):
|
||||
"""
|
||||
Test get_file_encoding when the end of the file is reached
|
||||
"""
|
||||
|
@ -23,45 +23,76 @@
|
||||
Package to test the openlp.core.common package.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.common.mixins import RegistryMixin
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
|
||||
class PlainStub(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class MixinStub(RegistryMixin):
|
||||
def __init__(self):
|
||||
super().__init__(None)
|
||||
|
||||
|
||||
class TestRegistryMixin(TestCase):
|
||||
|
||||
def test_registry_mixin_missing(self):
|
||||
class TestRegistryProperties(TestCase, RegistryProperties):
|
||||
"""
|
||||
Test the registry creation and its usage
|
||||
Test the functions in the ThemeManager module
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the Register
|
||||
"""
|
||||
# GIVEN: A new registry
|
||||
Registry.create()
|
||||
|
||||
# WHEN: I create an instance of a class that doesn't inherit from RegistryMixin
|
||||
PlainStub()
|
||||
|
||||
# THEN: Nothing is registered with the registry
|
||||
self.assertEqual(len(Registry().functions_list), 0), 'The function should not be in the dict anymore.'
|
||||
|
||||
def test_registry_mixin_present(self):
|
||||
def test_no_application(self):
|
||||
"""
|
||||
Test the registry creation and its usage
|
||||
Test property if no registry value assigned
|
||||
"""
|
||||
# GIVEN: A new registry
|
||||
Registry.create()
|
||||
# GIVEN an Empty Registry
|
||||
# WHEN there is no Application
|
||||
# THEN the application should be none
|
||||
self.assertEqual(self.application, None, 'The application value should be None')
|
||||
|
||||
# WHEN: I create an instance of a class that inherits from RegistryMixin
|
||||
MixinStub()
|
||||
def test_application(self):
|
||||
"""
|
||||
Test property if registry value assigned
|
||||
"""
|
||||
# GIVEN an Empty Registry
|
||||
application = MagicMock()
|
||||
|
||||
# THEN: The bootstrap methods should be registered
|
||||
self.assertEqual(len(Registry().functions_list), 2), 'The bootstrap functions should be in the dict.'
|
||||
# WHEN the application is registered
|
||||
Registry().register('application', application)
|
||||
|
||||
# THEN the application should be none
|
||||
self.assertEqual(self.application, application, 'The application value should match')
|
||||
|
||||
@patch('openlp.core.common.mixins.is_win')
|
||||
def test_application_on_windows(self, mocked_is_win):
|
||||
"""
|
||||
Test property if registry value assigned on Windows
|
||||
"""
|
||||
# GIVEN an Empty Registry and we're on Windows
|
||||
application = MagicMock()
|
||||
mocked_is_win.return_value = True
|
||||
|
||||
# WHEN the application is registered
|
||||
Registry().register('application', application)
|
||||
|
||||
# THEN the application should be none
|
||||
self.assertEqual(self.application, application, 'The application value should match')
|
||||
|
||||
@patch('openlp.core.common.mixins.is_win')
|
||||
def test_get_application_on_windows(self, mocked_is_win):
|
||||
"""
|
||||
Set that getting the application object on Windows happens dynamically
|
||||
"""
|
||||
# GIVEN an Empty Registry and we're on Windows
|
||||
mocked_is_win.return_value = True
|
||||
mock_application = MagicMock()
|
||||
reg_props = RegistryProperties()
|
||||
registry = Registry()
|
||||
|
||||
# WHEN the application is accessed
|
||||
with patch.object(registry, 'get') as mocked_get:
|
||||
mocked_get.return_value = mock_application
|
||||
actual_application = reg_props.application
|
||||
|
||||
# THEN the application should be the mock object, and the correct function should have been called
|
||||
self.assertEqual(mock_application, actual_application, 'The application value should match')
|
||||
mocked_is_win.assert_called_with()
|
||||
mocked_get.assert_called_with('application')
|
||||
|
@ -24,9 +24,9 @@ Package to test the openlp.core.lib package.
|
||||
"""
|
||||
import os
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from openlp.core.common.registry import Registry, RegistryProperties
|
||||
from openlp.core.common.registry import Registry, RegistryBase
|
||||
|
||||
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..', 'resources'))
|
||||
|
||||
@ -151,70 +151,40 @@ class TestRegistry(TestCase):
|
||||
return "function_2"
|
||||
|
||||
|
||||
class TestRegistryProperties(TestCase, RegistryProperties):
|
||||
class PlainStub(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class RegistryStub(RegistryBase):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
|
||||
class TestRegistryBase(TestCase):
|
||||
|
||||
def test_registry_mixin_missing(self):
|
||||
"""
|
||||
Test the functions in the ThemeManager module
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the Register
|
||||
Test the registry creation and its usage
|
||||
"""
|
||||
# GIVEN: A new registry
|
||||
Registry.create()
|
||||
|
||||
def test_no_application(self):
|
||||
# WHEN: I create an instance of a class that doesn't inherit from RegistryMixin
|
||||
PlainStub()
|
||||
|
||||
# THEN: Nothing is registered with the registry
|
||||
self.assertEqual(len(Registry().functions_list), 0), 'The function should not be in the dict anymore.'
|
||||
|
||||
def test_registry_mixin_present(self):
|
||||
"""
|
||||
Test property if no registry value assigned
|
||||
Test the registry creation and its usage
|
||||
"""
|
||||
# GIVEN an Empty Registry
|
||||
# WHEN there is no Application
|
||||
# THEN the application should be none
|
||||
self.assertEqual(self.application, None, 'The application value should be None')
|
||||
# GIVEN: A new registry
|
||||
Registry.create()
|
||||
|
||||
def test_application(self):
|
||||
"""
|
||||
Test property if registry value assigned
|
||||
"""
|
||||
# GIVEN an Empty Registry
|
||||
application = MagicMock()
|
||||
# WHEN: I create an instance of a class that inherits from RegistryMixin
|
||||
RegistryStub()
|
||||
|
||||
# WHEN the application is registered
|
||||
Registry().register('application', application)
|
||||
|
||||
# THEN the application should be none
|
||||
self.assertEqual(self.application, application, 'The application value should match')
|
||||
|
||||
@patch('openlp.core.common.registry.is_win')
|
||||
def test_application_on_windows(self, mocked_is_win):
|
||||
"""
|
||||
Test property if registry value assigned on Windows
|
||||
"""
|
||||
# GIVEN an Empty Registry and we're on Windows
|
||||
application = MagicMock()
|
||||
mocked_is_win.return_value = True
|
||||
|
||||
# WHEN the application is registered
|
||||
Registry().register('application', application)
|
||||
|
||||
# THEN the application should be none
|
||||
self.assertEqual(self.application, application, 'The application value should match')
|
||||
|
||||
@patch('openlp.core.common.registry.is_win')
|
||||
def test_get_application_on_windows(self, mocked_is_win):
|
||||
"""
|
||||
Set that getting the application object on Windows happens dynamically
|
||||
"""
|
||||
# GIVEN an Empty Registry and we're on Windows
|
||||
mocked_is_win.return_value = True
|
||||
mock_application = MagicMock()
|
||||
reg_props = RegistryProperties()
|
||||
registry = Registry()
|
||||
|
||||
# WHEN the application is accessed
|
||||
with patch.object(registry, 'get') as mocked_get:
|
||||
mocked_get.return_value = mock_application
|
||||
actual_application = reg_props.application
|
||||
|
||||
# THEN the application should be the mock object, and the correct function should have been called
|
||||
self.assertEqual(mock_application, actual_application, 'The application value should match')
|
||||
mocked_is_win.assert_called_with()
|
||||
mocked_get.assert_called_with('application')
|
||||
# THEN: The bootstrap methods should be registered
|
||||
self.assertEqual(len(Registry().functions_list), 2), 'The bootstrap functions should be in the dict.'
|
||||
|
@ -29,8 +29,6 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.app import OpenLP, parse_options
|
||||
from openlp.core.common.settings import Settings
|
||||
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'resources'))
|
||||
|
||||
|
||||
@ -141,23 +139,33 @@ class TestOpenLP(TestCase):
|
||||
"""
|
||||
Test the OpenLP app class
|
||||
"""
|
||||
@patch('openlp.core.app.QtWidgets.QApplication.exec')
|
||||
def test_exec(self, mocked_exec):
|
||||
def setUp(self):
|
||||
self.build_settings()
|
||||
self.qapplication_patcher = patch('openlp.core.app.QtGui.QApplication')
|
||||
self.mocked_qapplication = self.qapplication_patcher.start()
|
||||
self.openlp = OpenLP([])
|
||||
|
||||
def tearDown(self):
|
||||
self.qapplication_patcher.stop()
|
||||
self.destroy_settings()
|
||||
del self.openlp
|
||||
self.openlp = None
|
||||
|
||||
def test_exec(self):
|
||||
"""
|
||||
Test the exec method
|
||||
"""
|
||||
# GIVEN: An app
|
||||
app = OpenLP([])
|
||||
app.shared_memory = MagicMock()
|
||||
mocked_exec.return_value = False
|
||||
self.openlp.shared_memory = MagicMock()
|
||||
self.mocked_qapplication.exec.return_value = False
|
||||
|
||||
# WHEN: exec() is called
|
||||
result = app.exec()
|
||||
result = self.openlp.exec()
|
||||
|
||||
# THEN: The right things should be called
|
||||
assert app.is_event_loop_active is True
|
||||
mocked_exec.assert_called_once_with()
|
||||
app.shared_memory.detach.assert_called_once_with()
|
||||
assert self.openlp.is_event_loop_active is True
|
||||
self.mocked_qapplication.exec.assert_called_once_with()
|
||||
self.openlp.shared_memory.detach.assert_called_once_with()
|
||||
assert result is False
|
||||
|
||||
@patch('openlp.core.app.QtCore.QSharedMemory')
|
||||
@ -169,10 +177,9 @@ class TestOpenLP(TestCase):
|
||||
mocked_shared_memory = MagicMock()
|
||||
mocked_shared_memory.attach.return_value = False
|
||||
MockedSharedMemory.return_value = mocked_shared_memory
|
||||
app = OpenLP([])
|
||||
|
||||
# WHEN: is_already_running() is called
|
||||
result = app.is_already_running()
|
||||
result = self.openlp.is_already_running()
|
||||
|
||||
# THEN: The result should be false
|
||||
MockedSharedMemory.assert_called_once_with('OpenLP')
|
||||
@ -193,10 +200,9 @@ class TestOpenLP(TestCase):
|
||||
MockedSharedMemory.return_value = mocked_shared_memory
|
||||
MockedStandardButtons.return_value = 0
|
||||
mocked_critical.return_value = QtWidgets.QMessageBox.Yes
|
||||
app = OpenLP([])
|
||||
|
||||
# WHEN: is_already_running() is called
|
||||
result = app.is_already_running()
|
||||
result = self.openlp.is_already_running()
|
||||
|
||||
# THEN: The result should be false
|
||||
MockedSharedMemory.assert_called_once_with('OpenLP')
|
||||
@ -218,10 +224,9 @@ class TestOpenLP(TestCase):
|
||||
MockedSharedMemory.return_value = mocked_shared_memory
|
||||
MockedStandardButtons.return_value = 0
|
||||
mocked_critical.return_value = QtWidgets.QMessageBox.No
|
||||
app = OpenLP([])
|
||||
|
||||
# WHEN: is_already_running() is called
|
||||
result = app.is_already_running()
|
||||
result = self.openlp.is_already_running()
|
||||
|
||||
# THEN: The result should be false
|
||||
MockedSharedMemory.assert_called_once_with('OpenLP')
|
||||
@ -235,11 +240,9 @@ class TestOpenLP(TestCase):
|
||||
Test that the app.process_events() method simply calls the Qt method
|
||||
"""
|
||||
# GIVEN: An app
|
||||
app = OpenLP([])
|
||||
|
||||
# WHEN: process_events() is called
|
||||
with patch.object(app, 'processEvents') as mocked_processEvents:
|
||||
app.process_events()
|
||||
with patch.object(self.openlp, 'processEvents') as mocked_processEvents:
|
||||
self.openlp.process_events()
|
||||
|
||||
# THEN: processEvents was called
|
||||
mocked_processEvents.assert_called_once_with()
|
||||
@ -249,12 +252,10 @@ class TestOpenLP(TestCase):
|
||||
Test that the set_busy_cursor() method sets the cursor
|
||||
"""
|
||||
# GIVEN: An app
|
||||
app = OpenLP([])
|
||||
|
||||
# WHEN: set_busy_cursor() is called
|
||||
with patch.object(app, 'setOverrideCursor') as mocked_setOverrideCursor, \
|
||||
patch.object(app, 'processEvents') as mocked_processEvents:
|
||||
app.set_busy_cursor()
|
||||
with patch.object(self.openlp, 'setOverrideCursor') as mocked_setOverrideCursor, \
|
||||
patch.object(self.openlp, 'processEvents') as mocked_processEvents:
|
||||
self.openlp.set_busy_cursor()
|
||||
|
||||
# THEN: The cursor should have been set
|
||||
mocked_setOverrideCursor.assert_called_once_with(QtCore.Qt.BusyCursor)
|
||||
@ -265,29 +266,15 @@ class TestOpenLP(TestCase):
|
||||
Test that the set_normal_cursor() method resets the cursor
|
||||
"""
|
||||
# GIVEN: An app
|
||||
app = OpenLP([])
|
||||
|
||||
# WHEN: set_normal_cursor() is called
|
||||
with patch.object(app, 'restoreOverrideCursor') as mocked_restoreOverrideCursor, \
|
||||
patch.object(app, 'processEvents') as mocked_processEvents:
|
||||
app.set_normal_cursor()
|
||||
with patch.object(self.openlp, 'restoreOverrideCursor') as mocked_restoreOverrideCursor, \
|
||||
patch.object(self.openlp, 'processEvents') as mocked_processEvents:
|
||||
self.openlp.set_normal_cursor()
|
||||
|
||||
# THEN: The cursor should have been set
|
||||
mocked_restoreOverrideCursor.assert_called_once_with()
|
||||
mocked_processEvents.assert_called_once_with()
|
||||
|
||||
|
||||
class TestInit(TestCase, TestMixin):
|
||||
def setUp(self):
|
||||
self.build_settings()
|
||||
with patch('openlp.core.app.OpenLPMixin.__init__') as constructor:
|
||||
constructor.return_value = None
|
||||
self.openlp = OpenLP(list())
|
||||
|
||||
def tearDown(self):
|
||||
self.destroy_settings()
|
||||
del self.openlp
|
||||
|
||||
def test_event(self):
|
||||
"""
|
||||
Test the reimplemented event method
|
||||
|
@ -1,137 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2017 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
This module contains tests for the openlp.core.lib.listwidgetwithdnd module
|
||||
"""
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
from types import GeneratorType
|
||||
|
||||
from openlp.core.common.i18n import UiStrings
|
||||
from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD
|
||||
|
||||
|
||||
class TestListWidgetWithDnD(TestCase):
|
||||
"""
|
||||
Test the :class:`~openlp.core.lib.listwidgetwithdnd.ListWidgetWithDnD` class
|
||||
"""
|
||||
def test_clear(self):
|
||||
"""
|
||||
Test the clear method when called without any arguments.
|
||||
"""
|
||||
# GIVEN: An instance of ListWidgetWithDnD
|
||||
widget = ListWidgetWithDnD()
|
||||
|
||||
# WHEN: Calling clear with out any arguments
|
||||
widget.clear()
|
||||
|
||||
# THEN: The results text should be the standard 'no results' text.
|
||||
self.assertEqual(widget.no_results_text, UiStrings().NoResults)
|
||||
|
||||
def test_clear_search_while_typing(self):
|
||||
"""
|
||||
Test the clear method when called with the search_while_typing argument set to True
|
||||
"""
|
||||
# GIVEN: An instance of ListWidgetWithDnD
|
||||
widget = ListWidgetWithDnD()
|
||||
|
||||
# WHEN: Calling clear with search_while_typing set to True
|
||||
widget.clear(search_while_typing=True)
|
||||
|
||||
# THEN: The results text should be the 'short results' text.
|
||||
self.assertEqual(widget.no_results_text, UiStrings().ShortResults)
|
||||
|
||||
def test_all_items_no_list_items(self):
|
||||
"""
|
||||
Test allItems when there are no items in the list widget
|
||||
"""
|
||||
# GIVEN: An instance of ListWidgetWithDnD
|
||||
widget = ListWidgetWithDnD()
|
||||
with patch.object(widget, 'count', return_value=0), \
|
||||
patch.object(widget, 'item', side_effect=lambda x: [][x]):
|
||||
|
||||
# WHEN: Calling allItems
|
||||
result = widget.allItems()
|
||||
|
||||
# THEN: An instance of a Generator object should be returned. The generator should not yeild any results
|
||||
self.assertIsInstance(result, GeneratorType)
|
||||
self.assertEqual(list(result), [])
|
||||
|
||||
def test_all_items_list_items(self):
|
||||
"""
|
||||
Test allItems when the list widget contains some items.
|
||||
"""
|
||||
# GIVEN: An instance of ListWidgetWithDnD
|
||||
widget = ListWidgetWithDnD()
|
||||
with patch.object(widget, 'count', return_value=2), \
|
||||
patch.object(widget, 'item', side_effect=lambda x: [5, 3][x]):
|
||||
|
||||
# WHEN: Calling allItems
|
||||
result = widget.allItems()
|
||||
|
||||
# THEN: An instance of a Generator object should be returned. The generator should not yeild any results
|
||||
self.assertIsInstance(result, GeneratorType)
|
||||
self.assertEqual(list(result), [5, 3])
|
||||
|
||||
def test_paint_event(self):
|
||||
"""
|
||||
Test the paintEvent method when the list is not empty
|
||||
"""
|
||||
# GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 1
|
||||
# (i.e the list has an item)
|
||||
widget = ListWidgetWithDnD()
|
||||
with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \
|
||||
patch.object(widget, 'count', return_value=1), \
|
||||
patch.object(widget, 'viewport') as mocked_viewport:
|
||||
mocked_event = MagicMock()
|
||||
|
||||
# WHEN: Calling paintEvent
|
||||
widget.paintEvent(mocked_event)
|
||||
|
||||
# THEN: The overridden paintEvnet should have been called
|
||||
mocked_paint_event.assert_called_once_with(mocked_event)
|
||||
self.assertFalse(mocked_viewport.called)
|
||||
|
||||
def test_paint_event_no_items(self):
|
||||
"""
|
||||
Test the paintEvent method when the list is empty
|
||||
"""
|
||||
# GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 0
|
||||
# (i.e the list is empty)
|
||||
widget = ListWidgetWithDnD()
|
||||
mocked_painter_instance = MagicMock()
|
||||
mocked_qrect = MagicMock()
|
||||
with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \
|
||||
patch.object(widget, 'count', return_value=0), \
|
||||
patch.object(widget, 'viewport'), \
|
||||
patch('openlp.core.ui.lib.listwidgetwithdnd.QtGui.QPainter',
|
||||
return_value=mocked_painter_instance) as mocked_qpainter, \
|
||||
patch('openlp.core.ui.lib.listwidgetwithdnd.QtCore.QRect', return_value=mocked_qrect):
|
||||
mocked_event = MagicMock()
|
||||
|
||||
# WHEN: Calling paintEvent
|
||||
widget.paintEvent(mocked_event)
|
||||
|
||||
# THEN: The overridden paintEvnet should have been called, and some text should be drawn.
|
||||
mocked_paint_event.assert_called_once_with(mocked_event)
|
||||
mocked_qpainter.assert_called_once_with(widget.viewport())
|
||||
mocked_painter_instance.drawText.assert_called_once_with(mocked_qrect, 4100, 'No Search Results')
|
@ -62,7 +62,7 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
self.assertTrue('OpenLP 3.1.5 build 3000' in about_form.about_text_edit.toPlainText(),
|
||||
"The build number should be set correctly")
|
||||
|
||||
def test_about_form_date_test(self):
|
||||
def test_about_form_date(self):
|
||||
"""
|
||||
Test that the copyright date is included correctly
|
||||
"""
|
||||
|
@ -26,10 +26,11 @@ import os
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import UiStrings
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.ui.mainwindow import MainWindow
|
||||
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
@ -37,8 +38,23 @@ from tests.utils.constants import TEST_RESOURCES_PATH
|
||||
|
||||
|
||||
class TestMainWindow(TestCase, TestMixin):
|
||||
"""
|
||||
Test the main window
|
||||
"""
|
||||
def _create_mock_action(self, parent, name, **kwargs):
|
||||
"""
|
||||
Create a fake action with some "real" attributes
|
||||
"""
|
||||
action = QtWidgets.QAction(parent)
|
||||
action.setObjectName(name)
|
||||
if kwargs.get('triggers'):
|
||||
action.triggered.connect(kwargs.pop('triggers'))
|
||||
return action
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the objects we need for all of the tests
|
||||
"""
|
||||
Registry.create()
|
||||
self.registry = Registry()
|
||||
self.setup_application()
|
||||
@ -48,30 +64,18 @@ class TestMainWindow(TestCase, TestMixin):
|
||||
self.app.args = []
|
||||
Registry().register('application', self.app)
|
||||
Registry().set_flag('no_web_server', False)
|
||||
# Mock classes and methods used by mainwindow.
|
||||
with patch('openlp.core.ui.mainwindow.SettingsForm') as mocked_settings_form, \
|
||||
patch('openlp.core.ui.mainwindow.ImageManager') as mocked_image_manager, \
|
||||
patch('openlp.core.ui.mainwindow.LiveController') as mocked_live_controller, \
|
||||
patch('openlp.core.ui.mainwindow.PreviewController') as mocked_preview_controller, \
|
||||
patch('openlp.core.ui.mainwindow.OpenLPDockWidget') as mocked_dock_widget, \
|
||||
patch('openlp.core.ui.mainwindow.QtWidgets.QToolBox') as mocked_q_tool_box_class, \
|
||||
patch('openlp.core.ui.mainwindow.QtWidgets.QMainWindow.addDockWidget') as mocked_add_dock_method, \
|
||||
patch('openlp.core.ui.mainwindow.ThemeManager') as mocked_theme_manager, \
|
||||
patch('openlp.core.ui.mainwindow.Renderer') as mocked_renderer, \
|
||||
patch('openlp.core.ui.mainwindow.websockets.WebSocketServer'), \
|
||||
patch('openlp.core.ui.mainwindow.server.HttpServer'):
|
||||
self.mocked_settings_form = mocked_settings_form
|
||||
self.mocked_image_manager = mocked_image_manager
|
||||
self.mocked_live_controller = mocked_live_controller
|
||||
self.mocked_preview_controller = mocked_preview_controller
|
||||
self.mocked_dock_widget = mocked_dock_widget
|
||||
self.mocked_q_tool_box_class = mocked_q_tool_box_class
|
||||
self.mocked_add_dock_method = mocked_add_dock_method
|
||||
self.mocked_theme_manager = mocked_theme_manager
|
||||
self.mocked_renderer = mocked_renderer
|
||||
self.add_toolbar_action_patcher = patch('openlp.core.ui.mainwindow.create_action')
|
||||
self.mocked_add_toolbar_action = self.add_toolbar_action_patcher.start()
|
||||
self.mocked_add_toolbar_action.side_effect = self._create_mock_action
|
||||
with patch('openlp.core.display.screens.ScreenList.__instance__', spec=ScreenList) as mocked_screen_list:
|
||||
mocked_screen_list.current = {'number': 0, 'size': QtCore.QSize(600, 800), 'primary': True}
|
||||
self.main_window = MainWindow()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects and stop all the patchers
|
||||
"""
|
||||
self.add_toolbar_action_patcher.stop()
|
||||
del self.main_window
|
||||
|
||||
def test_cmd_line_file(self):
|
||||
@ -81,13 +85,13 @@ class TestMainWindow(TestCase, TestMixin):
|
||||
# GIVEN a service as an argument to openlp
|
||||
service = os.path.join(TEST_RESOURCES_PATH, 'service', 'test.osz')
|
||||
self.main_window.arguments = [service]
|
||||
with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path:
|
||||
|
||||
# WHEN the argument is processed
|
||||
with patch.object(self.main_window.service_manager, 'load_file') as mocked_load_file:
|
||||
self.main_window.open_cmd_line_files(service)
|
||||
|
||||
# THEN the service from the arguments is loaded
|
||||
mocked_load_path.assert_called_with(service), 'load_path should have been called with the service\'s path'
|
||||
mocked_load_file.assert_called_with(service)
|
||||
|
||||
def test_cmd_line_arg(self):
|
||||
"""
|
||||
@ -96,13 +100,13 @@ class TestMainWindow(TestCase, TestMixin):
|
||||
# GIVEN a non service file as an argument to openlp
|
||||
service = os.path.join('openlp.py')
|
||||
self.main_window.arguments = [service]
|
||||
with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path:
|
||||
with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_file:
|
||||
|
||||
# WHEN the argument is processed
|
||||
self.main_window.open_cmd_line_files("")
|
||||
|
||||
# THEN the file should not be opened
|
||||
assert not mocked_load_path.called, 'load_path should not have been called'
|
||||
assert not mocked_load_file.called, 'load_file should not have been called'
|
||||
|
||||
def test_main_window_title(self):
|
||||
"""
|
||||
@ -151,14 +155,14 @@ class TestMainWindow(TestCase, TestMixin):
|
||||
# WHEN: you check the started functions
|
||||
|
||||
# THEN: the following registry functions should have been registered
|
||||
self.assertEqual(len(self.registry.service_list), 6, 'The registry should have 6 services.')
|
||||
self.assertEqual(len(self.registry.functions_list), 18, 'The registry should have 18 functions')
|
||||
self.assertTrue('application' in self.registry.service_list, 'The application should have been registered.')
|
||||
self.assertTrue('main_window' in self.registry.service_list, 'The main_window should have been registered.')
|
||||
self.assertTrue('media_controller' in self.registry.service_list, 'The media_controller should have been '
|
||||
'registered.')
|
||||
self.assertTrue('plugin_manager' in self.registry.service_list,
|
||||
'The plugin_manager should have been registered.')
|
||||
assert len(self.registry.service_list) == 12, \
|
||||
'The registry should have 12 services, got {}'.format(self.registry.service_list.keys())
|
||||
assert len(self.registry.functions_list) == 19, \
|
||||
'The registry should have 19 functions, got {}'.format(self.registry.functions_list.keys())
|
||||
assert 'application' in self.registry.service_list, 'The application should have been registered.'
|
||||
assert 'main_window' in self.registry.service_list, 'The main_window should have been registered.'
|
||||
assert 'media_controller' in self.registry.service_list, 'The media_controller should have been registered.'
|
||||
assert 'plugin_manager' in self.registry.service_list, 'The plugin_manager should have been registered.'
|
||||
|
||||
def test_projector_manager_hidden_on_startup(self):
|
||||
"""
|
||||
@ -167,7 +171,7 @@ class TestMainWindow(TestCase, TestMixin):
|
||||
# GIVEN: A built main window
|
||||
# WHEN: OpenLP is started
|
||||
# THEN: The projector manager should be hidden
|
||||
self.main_window.projector_manager_dock.setVisible.assert_called_once_with(False)
|
||||
assert self.main_window.projector_manager_dock.isVisible() is False
|
||||
|
||||
def test_on_search_shortcut_triggered_shows_media_manager(self):
|
||||
"""
|
||||
@ -203,56 +207,38 @@ class TestMainWindow(TestCase, TestMixin):
|
||||
self.assertEqual(0, mocked_media_manager_dock.setVisible.call_count)
|
||||
mocked_widget.on_focus.assert_called_with()
|
||||
|
||||
@patch('openlp.core.ui.mainwindow.MainWindow.plugin_manager')
|
||||
@patch('openlp.core.ui.mainwindow.MainWindow.first_time')
|
||||
@patch('openlp.core.ui.mainwindow.MainWindow.application')
|
||||
@patch('openlp.core.ui.mainwindow.FirstTimeForm')
|
||||
@patch('openlp.core.ui.mainwindow.QtWidgets.QMessageBox.warning')
|
||||
@patch('openlp.core.ui.mainwindow.Settings')
|
||||
def test_on_first_time_wizard_clicked_show_projectors_after(self, mocked_Settings, mocked_warning,
|
||||
mocked_FirstTimeForm, mocked_application,
|
||||
mocked_first_time,
|
||||
mocked_plugin_manager):
|
||||
def test_on_first_time_wizard_clicked_show_projectors_after(self, MockSettings, mocked_warning, MockWizard):
|
||||
"""Test that the projector manager is shown after the FTW is run"""
|
||||
# GIVEN: Main_window, patched things, patched "Yes" as confirmation to re-run wizard, settings to True.
|
||||
mocked_Settings_obj = MagicMock()
|
||||
mocked_Settings_obj.value.return_value = True
|
||||
mocked_Settings.return_value = mocked_Settings_obj
|
||||
MockSettings.return_value.value.return_value = True
|
||||
mocked_warning.return_value = QtWidgets.QMessageBox.Yes
|
||||
mocked_FirstTimeForm_obj = MagicMock()
|
||||
mocked_FirstTimeForm_obj.was_cancelled = False
|
||||
mocked_FirstTimeForm.return_value = mocked_FirstTimeForm_obj
|
||||
mocked_plugin_manager.plugins = []
|
||||
self.main_window.projector_manager_dock = MagicMock()
|
||||
MockWizard.return_value.was_cancelled = False
|
||||
|
||||
with patch.object(self.main_window, 'projector_manager_dock') as mocked_dock, \
|
||||
patch.object(self.registry, 'execute'), patch.object(self.main_window, 'theme_manager_contents'):
|
||||
# WHEN: on_first_time_wizard_clicked is called
|
||||
self.main_window.on_first_time_wizard_clicked()
|
||||
|
||||
# THEN: projector_manager_dock.setVisible should had been called once
|
||||
self.main_window.projector_manager_dock.setVisible.assert_called_once_with(True)
|
||||
mocked_dock.setVisible.assert_called_once_with(True)
|
||||
|
||||
@patch('openlp.core.ui.mainwindow.MainWindow.plugin_manager')
|
||||
@patch('openlp.core.ui.mainwindow.MainWindow.first_time')
|
||||
@patch('openlp.core.ui.mainwindow.MainWindow.application')
|
||||
@patch('openlp.core.ui.mainwindow.FirstTimeForm')
|
||||
@patch('openlp.core.ui.mainwindow.QtWidgets.QMessageBox.warning')
|
||||
@patch('openlp.core.ui.mainwindow.Settings')
|
||||
def test_on_first_time_wizard_clicked_hide_projectors_after(self, mocked_Settings, mocked_warning,
|
||||
mocked_FirstTimeForm, mocked_application,
|
||||
mocked_first_time,
|
||||
mocked_plugin_manager):
|
||||
def test_on_first_time_wizard_clicked_hide_projectors_after(self, MockSettings, mocked_warning, MockWizard):
|
||||
"""Test that the projector manager is hidden after the FTW is run"""
|
||||
# GIVEN: Main_window, patched things, patched "Yes" as confirmation to re-run wizard, settings to False.
|
||||
mocked_Settings_obj = MagicMock()
|
||||
mocked_Settings_obj.value.return_value = False
|
||||
mocked_Settings.return_value = mocked_Settings_obj
|
||||
MockSettings.return_value.value.return_value = False
|
||||
mocked_warning.return_value = QtWidgets.QMessageBox.Yes
|
||||
mocked_FirstTimeForm_obj = MagicMock()
|
||||
mocked_FirstTimeForm_obj.was_cancelled = False
|
||||
mocked_FirstTimeForm.return_value = mocked_FirstTimeForm_obj
|
||||
mocked_plugin_manager.plugins = []
|
||||
self.main_window.projector_manager_dock = MagicMock()
|
||||
MockWizard.return_value.was_cancelled = False
|
||||
|
||||
# WHEN: on_first_time_wizard_clicked is called
|
||||
with patch.object(self.main_window, 'projector_manager_dock') as mocked_dock, \
|
||||
patch.object(self.registry, 'execute'), patch.object(self.main_window, 'theme_manager_contents'):
|
||||
self.main_window.on_first_time_wizard_clicked()
|
||||
|
||||
# THEN: projector_manager_dock.setVisible should had been called once
|
||||
self.main_window.projector_manager_dock.setVisible.assert_called_once_with(False)
|
||||
mocked_dock.setVisible.assert_called_once_with(False)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user