From 9c35b7dcdc9982fc7066f2e1f8a6229a33148b60 Mon Sep 17 00:00:00 2001 From: Phill Date: Wed, 3 Jul 2019 14:23:23 +0100 Subject: [PATCH 01/22] Minor fixes, and changes --- openlp/core/common/__init__.py | 8 +- openlp/core/common/i18n.py | 6 +- openlp/core/lib/db.py | 2 +- openlp/core/lib/formattingtags.py | 181 +++++++++--------- openlp/core/projectors/manager.py | 4 +- openlp/core/projectors/pjlink.py | 2 +- openlp/core/projectors/sourceselectform.py | 6 +- openlp/core/ui/__init__.py | 2 +- openlp/core/ui/formattingtagcontroller.py | 2 +- openlp/core/ui/icons.py | 6 +- openlp/core/ui/shortcutlistdialog.py | 2 +- openlp/core/ui/shortcutlistform.py | 2 +- openlp/core/widgets/views.py | 6 +- openlp/core/widgets/widgets.py | 4 +- openlp/plugins/bibles/lib/manager.py | 2 +- openlp/plugins/bibles/lib/mediaitem.py | 2 +- .../presentations/lib/impresscontroller.py | 16 +- openlp/plugins/songs/lib/__init__.py | 18 +- .../songs/lib/importers/easyworship.py | 4 +- .../songs/lib/importers/foilpresenter.py | 2 +- .../plugins/songs/lib/importers/openoffice.py | 4 +- openlp/plugins/songs/lib/importers/opspro.py | 4 +- .../songs/lib/importers/propresenter.py | 8 +- .../plugins/songs/lib/importers/videopsalm.py | 2 +- openlp/plugins/songs/lib/openlyricsxml.py | 2 +- .../openlp_core/lib/test_image_manager.py | 2 +- .../functional/openlp_core/lib/test_theme.py | 73 ++++++- 27 files changed, 219 insertions(+), 153 deletions(-) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index bcdb99109..d9b401368 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -45,7 +45,7 @@ log = logging.getLogger(__name__ + '.__init__') FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)') SECOND_CAMEL_REGEX = re.compile('([a-z0-9])([A-Z])') CONTROL_CHARS = re.compile(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]') -INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]') +INVALID_FILE_CHARS = re.compile(r'[\\/:*?"<>|+\[\]%]') IMAGES_FILTER = None REPLACMENT_CHARS_MAP = str.maketrans({'\u2018': '\'', '\u2019': '\'', '\u201c': '"', '\u201d': '"', '\u2026': '...', '\u2013': '-', '\u2014': '-', '\v': '\n\n', '\f': '\n\n'}) @@ -112,21 +112,21 @@ def trace_error_handler(logger): logger.error(log_string) -def extension_loader(glob_pattern, excluded_files=[]): +def extension_loader(glob_pattern, excluded_files=None): """ A utility function to find and load OpenLP extensions, such as plugins, presentation and media controllers and importers. :param str glob_pattern: A glob pattern used to find the extension(s) to be imported. Should be relative to the application directory. i.e. plugins/*/*plugin.py - :param list[str] excluded_files: A list of file names to exclude that the glob pattern may find. + :param list[str] | None excluded_files: A list of file names to exclude that the glob pattern may find. :rtype: None """ from openlp.core.common.applocation import AppLocation app_dir = AppLocation.get_directory(AppLocation.AppDir) for extension_path in app_dir.glob(glob_pattern): extension_path = extension_path.relative_to(app_dir) - if extension_path.name in excluded_files: + if extension_path.name in (excluded_files or []): continue log.debug('Attempting to import %s', extension_path) module_name = path_to_module(extension_path) diff --git a/openlp/core/common/i18n.py b/openlp/core/common/i18n.py index 85fe35e2f..5b2aabf20 100644 --- a/openlp/core/common/i18n.py +++ b/openlp/core/common/i18n.py @@ -338,8 +338,8 @@ class UiStrings(object): Override the default object creation method to return a single instance. """ if not cls.__instance__: - cls.__instance__ = object.__new__(cls) - cls.load(cls) + cls.__instance__ = super().__new__(cls) + cls.__instance__.load() return cls.__instance__ def load(self): @@ -503,7 +503,7 @@ def format_time(text, local_time): """ return local_time.strftime(match.group()) - return re.sub(r'\%[a-zA-Z]', match_formatting, text) + return re.sub(r'%[a-zA-Z]', match_formatting, text) def get_locale_key(string, numeric=False): diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index a0445730f..e2ef2e33e 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -265,7 +265,7 @@ def upgrade_db(url, upgrade): """ if not database_exists(url): log.warning("Database {db} doesn't exist - skipping upgrade checks".format(db=url)) - return (0, 0) + return 0, 0 log.debug('Checking upgrades for DB {db}'.format(db=url)) diff --git a/openlp/core/lib/formattingtags.py b/openlp/core/lib/formattingtags.py index 560cbb5f2..dc58cafd0 100644 --- a/openlp/core/lib/formattingtags.py +++ b/openlp/core/lib/formattingtags.py @@ -59,97 +59,98 @@ class FormattingTags(object): """ temporary_tags = [tag for tag in FormattingTags.html_expands if tag.get('temporary')] FormattingTags.html_expands = [] - base_tags = [] + base_tags = [ + { + 'desc': translate('OpenLP.FormattingTags', 'Red'), + 'start tag': '{r}', + 'start html': '', + 'end tag': '{/r}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Black'), + 'start tag': '{b}', + 'start html': '', + 'end tag': '{/b}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Blue'), + 'start tag': '{bl}', + 'start html': '', + 'end tag': '{/bl}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Yellow'), + 'start tag': '{y}', + 'start html': '', + 'end tag': '{/y}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Green'), + 'start tag': '{g}', + 'start html': '', + 'end tag': '{/g}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Pink'), + 'start tag': '{pk}', + 'start html': '', + 'end tag': '{/pk}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Orange'), + 'start tag': '{o}', + 'start html': '', + 'end tag': '{/o}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Purple'), + 'start tag': '{pp}', + 'start html': '', + 'end tag': '{/pp}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'White'), + 'start tag': '{w}', + 'start html': '', + 'end tag': '{/w}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Superscript'), + 'start tag': '{su}', 'start html': '', + 'end tag': '{/su}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Subscript'), + 'start tag': '{sb}', 'start html': '', + 'end tag': '{/sb}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Paragraph'), + 'start tag': '{p}', 'start html': '

', 'end tag': '{/p}', + 'end html': '

', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Bold'), + 'start tag': '{st}', 'start html': '', + 'end tag': '{/st}', 'end html': '', + 'protected': True, 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Italics'), + 'start tag': '{it}', 'start html': '', 'end tag': '{/it}', + 'end html': '', 'protected': True, 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Underline'), + 'start tag': '{u}', + 'start html': '', + 'end tag': '{/u}', 'end html': '', 'protected': True, + 'temporary': False + }, { + 'desc': translate('OpenLP.FormattingTags', 'Break'), + 'start tag': '{br}', 'start html': '
', 'end tag': '', + 'end html': '', 'protected': True, + 'temporary': False + }] # Append the base tags. - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Red'), - 'start tag': '{r}', - 'start html': '', - 'end tag': '{/r}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Black'), - 'start tag': '{b}', - 'start html': '', - 'end tag': '{/b}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Blue'), - 'start tag': '{bl}', - 'start html': '', - 'end tag': '{/bl}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Yellow'), - 'start tag': '{y}', - 'start html': '', - 'end tag': '{/y}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Green'), - 'start tag': '{g}', - 'start html': '', - 'end tag': '{/g}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Pink'), - 'start tag': '{pk}', - 'start html': '', - 'end tag': '{/pk}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Orange'), - 'start tag': '{o}', - 'start html': '', - 'end tag': '{/o}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Purple'), - 'start tag': '{pp}', - 'start html': '', - 'end tag': '{/pp}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'White'), - 'start tag': '{w}', - 'start html': '', - 'end tag': '{/w}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Superscript'), - 'start tag': '{su}', 'start html': '', - 'end tag': '{/su}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Subscript'), - 'start tag': '{sb}', 'start html': '', - 'end tag': '{/sb}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Paragraph'), - 'start tag': '{p}', 'start html': '

', 'end tag': '{/p}', - 'end html': '

', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Bold'), - 'start tag': '{st}', 'start html': '', - 'end tag': '{/st}', 'end html': '', - 'protected': True, 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Italics'), - 'start tag': '{it}', 'start html': '', 'end tag': '{/it}', - 'end html': '', 'protected': True, 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Underline'), - 'start tag': '{u}', - 'start html': '', - 'end tag': '{/u}', 'end html': '', 'protected': True, - 'temporary': False}) - base_tags.append({ - 'desc': translate('OpenLP.FormattingTags', 'Break'), - 'start tag': '{br}', 'start html': '
', 'end tag': '', - 'end html': '', 'protected': True, - 'temporary': False}) FormattingTags.add_html_tags(base_tags) FormattingTags.add_html_tags(temporary_tags) user_expands_string = str(Settings().value('formattingTags/html_tags')) diff --git a/openlp/core/projectors/manager.py b/openlp/core/projectors/manager.py index 02b1c71f2..91b604b14 100644 --- a/openlp/core/projectors/manager.py +++ b/openlp/core/projectors/manager.py @@ -344,14 +344,14 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM """ log.debug('Checking for UDP port {port} listener deletion'.format(port=port)) if port not in self.pjlink_udp: - log.warn('UDP listener for port {port} not there - skipping delete'.format(port=port)) + log.warning('UDP listener for port {port} not there - skipping delete'.format(port=port)) return keep_port = False for item in self.projector_list: if port == item.link.port: keep_port = True if keep_port: - log.warn('UDP listener for port {port} needed for other projectors - skipping delete'.format(port=port)) + log.warning('UDP listener for port {port} needed for other projectors - skipping delete'.format(port=port)) return Registry().execute('udp_broadcast_remove', port=port) del self.pjlink_udp[port] diff --git a/openlp/core/projectors/pjlink.py b/openlp/core/projectors/pjlink.py index 5ca3d9ba3..403b96b7c 100644 --- a/openlp/core/projectors/pjlink.py +++ b/openlp/core/projectors/pjlink.py @@ -552,7 +552,7 @@ class PJLink(QtNetwork.QTcpSocket): data = data_in.strip() self.receive_data_signal() # Initial packet checks - if (len(data) < 7): + if len(data) < 7: self._trash_buffer(msg='get_data(): Invalid packet - length') return elif len(data) > self.max_size: diff --git a/openlp/core/projectors/sourceselectform.py b/openlp/core/projectors/sourceselectform.py index 1d40436a2..f34439d33 100644 --- a/openlp/core/projectors/sourceselectform.py +++ b/openlp/core/projectors/sourceselectform.py @@ -177,7 +177,7 @@ class FingerTabBarWidget(QtWidgets.QTabBar): :param height: Remove default height parameter in kwargs """ self.tabSize = QtCore.QSize(kwargs.pop('width', 100), kwargs.pop('height', 25)) - QtWidgets.QTabBar.__init__(self, parent, *args, **kwargs) + super().__init__(parent) def paintEvent(self, event): """ @@ -215,11 +215,11 @@ class FingerTabWidget(QtWidgets.QTabWidget): Based on thread discussion http://www.riverbankcomputing.com/pipermail/pyqt/2005-December/011724.html """ - def __init__(self, parent, *args): + def __init__(self, parent): """ Initialize FingerTabWidget instance """ - QtWidgets.QTabWidget.__init__(self, parent, *args) + super().__init__(parent) self.setTabBar(FingerTabBarWidget(self)) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 220bda7ce..2e9a7a48c 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -93,4 +93,4 @@ class SingleColumnTableWidget(QtWidgets.QTableWidget): self.resizeRowsToContents() -__all__ = ['SingleColumnTableWidget', 'DisplayControllerType'] +__all__ = ['AlertLocation', 'DisplayControllerType', 'HideMode', 'SingleColumnTableWidget'] diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index 5b26851b5..0b88702aa 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -40,7 +40,7 @@ class FormattingTagController(object): """ self.html_tag_regex = re.compile( r'<(?:(?P/(?=[^\s/>]+>))?' - r'(?P[^\s/!\?>]+)(?:\s+[^\s=]+="[^"]*")*\s*(?P/)?' + r'(?P[^\s/!?>]+)(?:\s+[^\s=]+="[^"]*")*\s*(?P/)?' r'|(?P!\[CDATA\[(?:(?!\]\]>).)*\]\])' r'|(?P\?(?:(?!\?>).)*\?)' r'|(?P!--(?:(?!-->).)*--))>') diff --git a/openlp/core/ui/icons.py b/openlp/core/ui/icons.py index 925becf1f..701c25a09 100644 --- a/openlp/core/ui/icons.py +++ b/openlp/core/ui/icons.py @@ -45,8 +45,8 @@ class UiIcons(object): Override the default object creation method to return a single instance. """ if not cls.__instance__: - cls.__instance__ = object.__new__(cls) - cls.load(cls) + cls.__instance__ = super().__new__(cls) + cls.__instance__.load() return cls.__instance__ def load(self): @@ -164,7 +164,7 @@ class UiIcons(object): 'video': {'icon': 'fa.file-video-o'}, 'volunteer': {'icon': 'fa.group'} } - self.load_icons(self, icon_list) + self.load_icons(icon_list) def load_icons(self, icon_list): """ diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index efc795501..a6440b63f 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -101,7 +101,7 @@ class Ui_ShortcutListDialog(object): self.primary_push_button = CaptureShortcutButton(shortcutListDialog) self.primary_push_button.setObjectName('primary_push_button') self.primary_push_button.setMinimumSize(QtCore.QSize(84, 0)) - self.primary_push_button.setIcon(UiIcons.shortcuts) + self.primary_push_button.setIcon(UiIcons().shortcuts) self.primary_layout.addWidget(self.primary_push_button) self.clear_primary_button = QtWidgets.QToolButton(shortcutListDialog) self.clear_primary_button.setObjectName('clear_primary_button') diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 360c631c0..6d37130ab 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -34,7 +34,7 @@ from openlp.core.common.settings import Settings from openlp.core.ui.shortcutlistdialog import Ui_ShortcutListDialog -REMOVE_AMPERSAND = re.compile(r'&{1}') +REMOVE_AMPERSAND = re.compile(r'&') log = logging.getLogger(__name__) diff --git a/openlp/core/widgets/views.py b/openlp/core/widgets/views.py index b5c07993c..c3f4be0ca 100644 --- a/openlp/core/widgets/views.py +++ b/openlp/core/widgets/views.py @@ -80,7 +80,7 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): An empty ``ServiceItem`` is used by default. replace_service_manager_item() needs to be called to make this widget display something. """ - super(QtWidgets.QTableWidget, self).__init__(parent) + super().__init__(parent) self._setup(screen_ratio) def _setup(self, screen_ratio): @@ -124,7 +124,7 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): max_img_row_height = Settings().value('advanced/slide max height') # Adjust for row height cap if in use. if isinstance(max_img_row_height, int): - if max_img_row_height > 0 and height > max_img_row_height: + if 0 < max_img_row_height < height: height = max_img_row_height elif max_img_row_height < 0: # If auto setting, show that number of slides, or if the resulting slides too small, 100px. @@ -219,7 +219,7 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): slide_height = width // self.screen_ratio max_img_row_height = Settings().value('advanced/slide max height') if isinstance(max_img_row_height, int): - if max_img_row_height > 0 and slide_height > max_img_row_height: + if 0 < max_img_row_height < slide_height: slide_height = max_img_row_height elif max_img_row_height < 0: # If auto setting, show that number of slides, or if the resulting slides too small, 100px. diff --git a/openlp/core/widgets/widgets.py b/openlp/core/widgets/widgets.py index 0415d73a5..42f83511d 100644 --- a/openlp/core/widgets/widgets.py +++ b/openlp/core/widgets/widgets.py @@ -194,11 +194,11 @@ class ScreenButton(QtWidgets.QPushButton): class ScreenSelectionWidget(QtWidgets.QWidget): - def __init__(self, parent=None, screens=[]): + def __init__(self, parent=None, screens=None): super().__init__(parent) self.current_screen = None self.identify_labels = [] - self.screens = screens + self.screens = screens or [] self.timer = QtCore.QTimer() self.timer.setSingleShot(True) self.timer.setInterval(3000) diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 79f66cef4..53926d78b 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -401,4 +401,4 @@ class BibleManager(LogMixin, RegistryProperties): self.db_cache[bible].finalise() -__all__ = ['BibleFormat'] +__all__ = ['BibleFormat', 'BibleManager'] diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 185a6832e..b239defdd 100755 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -109,7 +109,7 @@ class BibleMediaItem(MediaManagerItem): :param kwargs: Keyword arguments to pass to the super method. (dict) """ self.clear_icon = UiIcons().square - self.save_results_icon = UiIcons.save + self.save_results_icon = UiIcons().save self.sort_icon = UiIcons().sort self.bible = None self.second_bible = None diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 873e91b86..dd8ad9df4 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -55,7 +55,7 @@ if is_win(): class SlideShowListenerImport(XSlideShowListenerObj.__class__): pass except (AttributeError, pywintypes.com_error): - class SlideShowListenerImport(): + class SlideShowListenerImport(object): pass # Declare an empty exception to match the exception imported from UNO @@ -76,7 +76,7 @@ else: except ImportError: uno_available = False - class SlideShowListenerImport(): + class SlideShowListenerImport(object): pass log = logging.getLogger(__name__) @@ -233,9 +233,7 @@ class ImpressController(PresentationController): self.conf_provider = self.manager.createInstanceWithContext( 'com.sun.star.configuration.ConfigurationProvider', uno.getComponentContext()) # Setup lookup properties to get Impress settings - properties = [] - properties.append(self.create_property('nodepath', 'org.openoffice.Office.Impress')) - properties = tuple(properties) + properties = tuple(self.create_property('nodepath', 'org.openoffice.Office.Impress')) try: # Get an updateable configuration view impress_conf_props = self.conf_provider.createInstanceWithArguments( @@ -311,9 +309,7 @@ class ImpressDocument(PresentationDocument): if desktop is None: return False self.desktop = desktop - properties = [] - properties.append(self.controller.create_property('Hidden', True)) - properties = tuple(properties) + properties = tuple(self.controller.create_property('Hidden', True)) try: self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties) except Exception: @@ -338,9 +334,7 @@ class ImpressDocument(PresentationDocument): return temp_folder_path = self.get_temp_folder() thumb_dir_url = temp_folder_path.as_uri() - properties = [] - properties.append(self.controller.create_property('FilterName', 'impress_png_Export')) - properties = tuple(properties) + properties = tuple(self.controller.create_property('FilterName', 'impress_png_Export')) doc = self.document pages = doc.getDrawPages() if not pages: diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index e79dbc95a..243eedcc8 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -615,14 +615,16 @@ def transpose_chord(chord, transpose_value, notation): :return: The transposed chord. """ # See https://en.wikipedia.org/wiki/Musical_note#12-tone_chromatic_scale - notes_sharp_notation = {} - notes_flat_notation = {} - notes_sharp_notation['german'] = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'H'] - notes_flat_notation['german'] = ['C', 'Db', 'D', 'Eb', 'Fb', 'F', 'Gb', 'G', 'Ab', 'A', 'B', 'H'] - notes_sharp_notation['english'] = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] - notes_flat_notation['english'] = ['C', 'Db', 'D', 'Eb', 'Fb', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'] - notes_sharp_notation['neo-latin'] = ['Do', 'Do#', 'Re', 'Re#', 'Mi', 'Fa', 'Fa#', 'Sol', 'Sol#', 'La', 'La#', 'Si'] - notes_flat_notation['neo-latin'] = ['Do', 'Reb', 'Re', 'Mib', 'Fab', 'Fa', 'Solb', 'Sol', 'Lab', 'La', 'Sib', 'Si'] + notes_sharp_notation = { + 'german': ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'H'], + 'english': ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'], + 'neo-latin': ['Do', 'Do#', 'Re', 'Re#', 'Mi', 'Fa', 'Fa#', 'Sol', 'Sol#', 'La', 'La#', 'Si'] + } + notes_flat_notation = { + 'german': ['C', 'Db', 'D', 'Eb', 'Fb', 'F', 'Gb', 'G', 'Ab', 'A', 'B', 'H'], + 'english': ['C', 'Db', 'D', 'Eb', 'Fb', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'], + 'neo-latin': ['Do', 'Reb', 'Re', 'Mib', 'Fab', 'Fa', 'Solb', 'Sol', 'Lab', 'La', 'Sib', 'Si'] + } chord_split = chord.replace('♭', 'b').split('/') transposed_chord = '' last_chord = '' diff --git a/openlp/plugins/songs/lib/importers/easyworship.py b/openlp/plugins/songs/lib/importers/easyworship.py index 9468031fa..2061195d3 100644 --- a/openlp/plugins/songs/lib/importers/easyworship.py +++ b/openlp/plugins/songs/lib/importers/easyworship.py @@ -118,8 +118,8 @@ class EasyWorshipSongImport(SongImport): # 40/48/56 Entry count int32le 4 Number of items in the schedule # 44/52/60 Entry length int16le 2 Length of schedule entries: 0x0718 = 1816 # Get file version - type, = struct.unpack('<38s', self.ews_file.read(38)) - version = type.decode()[-3:] + file_type, = struct.unpack('<38s', self.ews_file.read(38)) + version = file_type.decode()[-3:] # Set fileposition based on filetype/version file_pos = 0 if version == ' 5': diff --git a/openlp/plugins/songs/lib/importers/foilpresenter.py b/openlp/plugins/songs/lib/importers/foilpresenter.py index da16c9b9e..ed64210b6 100644 --- a/openlp/plugins/songs/lib/importers/foilpresenter.py +++ b/openlp/plugins/songs/lib/importers/foilpresenter.py @@ -302,7 +302,7 @@ class FoilPresenter(object): break author_temp = [] for author in strings: - temp = re.split(r',(?=\D{2})|(?<=\D),|\/(?=\D{3,})|(?<=\D);', author) + temp = re.split(r',(?=\D{2})|(?<=\D),|/(?=\D{3,})|(?<=\D);', author) for tempx in temp: author_temp.append(tempx) for author in author_temp: diff --git a/openlp/plugins/songs/lib/importers/openoffice.py b/openlp/plugins/songs/lib/importers/openoffice.py index 2346dc039..0fc057e82 100644 --- a/openlp/plugins/songs/lib/importers/openoffice.py +++ b/openlp/plugins/songs/lib/importers/openoffice.py @@ -145,9 +145,7 @@ class OpenOfficeImport(SongImport): """ self.file_path = file_path url = file_path.as_uri() - properties = [] - properties.append(self.create_property('Hidden', True)) - properties = tuple(properties) + properties = tuple(self.create_property('Hidden', True)) try: self.document = self.desktop.loadComponentFromURL(url, '_blank', 0, properties) if not self.document.supportsService("com.sun.star.presentation.PresentationDocument") and not \ diff --git a/openlp/plugins/songs/lib/importers/opspro.py b/openlp/plugins/songs/lib/importers/opspro.py index ece7dc41e..2ad14487e 100644 --- a/openlp/plugins/songs/lib/importers/opspro.py +++ b/openlp/plugins/songs/lib/importers/opspro.py @@ -136,8 +136,8 @@ class OPSProImport(SongImport): verse_text = re.sub(r'^\d+\r\n', '', verse_text) verse_def = 'v' + verse_number.group(1) # Detect verse tags - elif re.match(r'^.+?\:\r\n', verse_text): - tag_match = re.match(r'^(.+?)\:\r\n(.*)', verse_text, flags=re.DOTALL) + elif re.match(r'^.+?:\r\n', verse_text): + tag_match = re.match(r'^(.+?):\r\n(.*)', verse_text, flags=re.DOTALL) tag = tag_match.group(1).lower() tag = tag.split(' ')[0] verse_text = tag_match.group(2) diff --git a/openlp/plugins/songs/lib/importers/propresenter.py b/openlp/plugins/songs/lib/importers/propresenter.py index 0d666e449..05ac8aff5 100644 --- a/openlp/plugins/songs/lib/importers/propresenter.py +++ b/openlp/plugins/songs/lib/importers/propresenter.py @@ -80,7 +80,7 @@ class ProPresenterImport(SongImport): self.parse_author(author) # ProPresenter 4 - if(self.version >= 400 and self.version < 500): + if 400 <= self.version < 500: self.copyright = root.get('CCLICopyrightInfo') self.ccli_number = root.get('CCLILicenseNumber') count = 0 @@ -95,7 +95,7 @@ class ProPresenterImport(SongImport): self.add_verse(words, "v{count}".format(count=count)) # ProPresenter 5 - elif(self.version >= 500 and self.version < 600): + elif 500 <= self.version < 600: self.copyright = root.get('CCLICopyrightInfo') self.ccli_number = root.get('CCLILicenseNumber') count = 0 @@ -111,7 +111,7 @@ class ProPresenterImport(SongImport): self.add_verse(words, "v{count:d}".format(count=count)) # ProPresenter 6 - elif(self.version >= 600 and self.version < 700): + elif 600 <= self.version < 700: self.copyright = root.get('CCLICopyrightYear') self.ccli_number = root.get('CCLISongNumber') count = 0 @@ -128,7 +128,7 @@ class ProPresenterImport(SongImport): b64Data = contents.text data = base64.standard_b64decode(b64Data) words = None - if(contents.get('rvXMLIvarName') == "RTFData"): + if contents.get('rvXMLIvarName') == "RTFData": words, encoding = strip_rtf(data.decode()) break if words: diff --git a/openlp/plugins/songs/lib/importers/videopsalm.py b/openlp/plugins/songs/lib/importers/videopsalm.py index bc0e06093..ff3eec52b 100644 --- a/openlp/plugins/songs/lib/importers/videopsalm.py +++ b/openlp/plugins/songs/lib/importers/videopsalm.py @@ -75,7 +75,7 @@ class VideoPsalmImport(SongImport): c = next(file_content_it) processed_content += '"' + c # Remove control characters - elif (c < chr(32)): + elif c < chr(32): processed_content += ' ' # Handle escaped characters elif c == '\\': diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py index efa3103d2..7cb1f7a38 100644 --- a/openlp/plugins/songs/lib/openlyricsxml.py +++ b/openlp/plugins/songs/lib/openlyricsxml.py @@ -221,7 +221,7 @@ class OpenLyrics(object): """ IMPLEMENTED_VERSION = '0.8' START_TAGS_REGEX = re.compile(r'\{(\w+)\}') - END_TAGS_REGEX = re.compile(r'\{\/(\w+)\}') + END_TAGS_REGEX = re.compile(r'\{/(\w+)\}') VERSE_TAG_SPLITTER = re.compile('([a-zA-Z]+)([0-9]*)([a-zA-Z]?)') def __init__(self, manager): diff --git a/tests/functional/openlp_core/lib/test_image_manager.py b/tests/functional/openlp_core/lib/test_image_manager.py index 10470f0f6..1f6e0e326 100644 --- a/tests/functional/openlp_core/lib/test_image_manager.py +++ b/tests/functional/openlp_core/lib/test_image_manager.py @@ -32,7 +32,7 @@ from PyQt5 import QtGui from openlp.core.common.registry import Registry from openlp.core.display.screens import ScreenList -from openlp.core.lib.imagemanager import ImageManager, ImageWorker, Priority, PriorityQueue +from openlp.core.lib.imagemanager import Image, ImageManager, ImageWorker, Priority, PriorityQueue from tests.helpers.testmixin import TestMixin from tests.utils.constants import RESOURCE_PATH diff --git a/tests/functional/openlp_core/lib/test_theme.py b/tests/functional/openlp_core/lib/test_theme.py index f53d4367c..e7f935918 100644 --- a/tests/functional/openlp_core/lib/test_theme.py +++ b/tests/functional/openlp_core/lib/test_theme.py @@ -25,7 +25,78 @@ Package to test the openlp.core.lib.theme package. from pathlib import Path from unittest import TestCase -from openlp.core.lib.theme import Theme +from openlp.core.lib.theme import BackgroundType + + +class TestBackgroundType(TestCase): + """ + Test the BackgroundType enum methods. + """ + def test_solid_to_string(self): + """ + Test the to_string method of :class:`BackgroundType` + """ + # GIVEN: A BackgroundType member + background_type = BackgroundType.Solid + + # WHEN: Calling BackgroundType.to_string + # THEN: The string equivalent should have been returned + assert BackgroundType.to_string(background_type) == 'solid' + + def test_gradient_to_string(self): + """ + Test the to_string method of :class:`BackgroundType` + """ + # GIVEN: A BackgroundType member + background_type = BackgroundType.Gradient + + # WHEN: Calling BackgroundType.to_string + # THEN: The string equivalent should have been returned + assert BackgroundType.to_string(background_type) == 'gradient' + + def test_image_to_string(self): + """ + Test the to_string method of :class:`BackgroundType` + """ + # GIVEN: A BackgroundType member + background_type = BackgroundType.Image + + # WHEN: Calling BackgroundType.to_string + # THEN: The string equivalent should have been returned + assert BackgroundType.to_string(background_type) == 'image' + + def test_transparent_to_string(self): + """ + Test the to_string method of :class:`BackgroundType` + """ + # GIVEN: A BackgroundType member + background_type = BackgroundType.Transparent + + # WHEN: Calling BackgroundType.to_string + # THEN: The string equivalent should have been returned + assert BackgroundType.to_string(background_type) == 'transparent' + + def test_video_to_string(self): + """ + Test the to_string method of :class:`BackgroundType` + """ + # GIVEN: A BackgroundType member + background_type = BackgroundType.Video + + # WHEN: Calling BackgroundType.to_string + # THEN: The string equivalent should have been returned + assert BackgroundType.to_string(background_type) == 'video' + + def test_stream_to_string(self): + """ + Test the to_string method of :class:`BackgroundType` + """ + # GIVEN: A BackgroundType member + background_type = BackgroundType.Stream + + # WHEN: Calling BackgroundType.to_string + # THEN: The string equivalent should have been returned + assert BackgroundType.to_string(background_type) == 'stream' class TestTheme(TestCase): From ba99025a5fb7c902929e6b3fe45a5a96115e860b Mon Sep 17 00:00:00 2001 From: Phill Date: Thu, 18 Jul 2019 20:14:58 +0100 Subject: [PATCH 02/22] Fix tests --- tests/functional/openlp_core/lib/test_theme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core/lib/test_theme.py b/tests/functional/openlp_core/lib/test_theme.py index e7f935918..e294e5214 100644 --- a/tests/functional/openlp_core/lib/test_theme.py +++ b/tests/functional/openlp_core/lib/test_theme.py @@ -25,7 +25,7 @@ Package to test the openlp.core.lib.theme package. from pathlib import Path from unittest import TestCase -from openlp.core.lib.theme import BackgroundType +from openlp.core.lib.theme import BackgroundType, Theme class TestBackgroundType(TestCase): From c1ff239002731ac901bae2237bb1a0b11bfc4127 Mon Sep 17 00:00:00 2001 From: Phill Date: Thu, 18 Jul 2019 20:26:54 +0100 Subject: [PATCH 03/22] Lint fix --- tests/functional/openlp_core/lib/test_image_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core/lib/test_image_manager.py b/tests/functional/openlp_core/lib/test_image_manager.py index 1f6e0e326..10470f0f6 100644 --- a/tests/functional/openlp_core/lib/test_image_manager.py +++ b/tests/functional/openlp_core/lib/test_image_manager.py @@ -32,7 +32,7 @@ from PyQt5 import QtGui from openlp.core.common.registry import Registry from openlp.core.display.screens import ScreenList -from openlp.core.lib.imagemanager import Image, ImageManager, ImageWorker, Priority, PriorityQueue +from openlp.core.lib.imagemanager import ImageManager, ImageWorker, Priority, PriorityQueue from tests.helpers.testmixin import TestMixin from tests.utils.constants import RESOURCE_PATH From dd4d9b9255b26eaa909a136379a4c0ba1847dce8 Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 20 Jul 2019 13:27:28 +0100 Subject: [PATCH 04/22] Add a singletom Metaclass --- openlp/core/common/__init__.py | 15 +++++++ openlp/core/common/i18n.py | 17 ++----- openlp/core/common/registry.py | 25 +---------- openlp/core/display/screens.py | 12 +---- openlp/core/state.py | 13 +----- openlp/core/ui/icons.py | 18 ++------ openlp/plugins/bibles/lib/__init__.py | 13 +----- .../openlp_core/common/test_common.py | 44 ++++++++++++++++++- tests/functional/openlp_core/ui/test_icons.py | 2 +- 9 files changed, 74 insertions(+), 85 deletions(-) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 7ea86cfa4..96e1181db 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -172,6 +172,21 @@ class SlideLimits(object): Next = 3 +class Singleton(type): + """ + Provide a `Singleton` metaclass https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python + """ + _instances = {} + + def __call__(cls, *args, **kwargs): + """ + Create a new instance if one does not already exist. + """ + if cls not in cls._instances: + cls._instances[cls] = super().__call__(*args, **kwargs) + return cls._instances[cls] + + def de_hump(name): """ Change any Camel Case string to python string diff --git a/openlp/core/common/i18n.py b/openlp/core/common/i18n.py index 5b2aabf20..c05c6fdf0 100644 --- a/openlp/core/common/i18n.py +++ b/openlp/core/common/i18n.py @@ -29,7 +29,7 @@ from collections import namedtuple from PyQt5 import QtCore, QtWidgets -from openlp.core.common import is_macosx, is_win +from openlp.core.common import Singleton, is_macosx, is_win from openlp.core.common.applocation import AppLocation from openlp.core.common.settings import Settings @@ -327,22 +327,11 @@ class LanguageManager(object): return LanguageManager.__qm_list__ -class UiStrings(object): +class UiStrings(metaclass=Singleton): """ Provide standard strings for objects to use. """ - __instance__ = None - - def __new__(cls): - """ - Override the default object creation method to return a single instance. - """ - if not cls.__instance__: - cls.__instance__ = super().__new__(cls) - cls.__instance__.load() - return cls.__instance__ - - def load(self): + def __init__(self): """ These strings should need a good reason to be retranslated elsewhere. Should some/more/less of these have an & attached? diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index 10992d6fe..7c30ddc0d 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -23,29 +23,19 @@ Provide Registry Services """ import logging -import sys -from openlp.core.common import de_hump, trace_error_handler +from openlp.core.common import Singleton, de_hump, trace_error_handler log = logging.getLogger(__name__) -class Registry(object): +class Registry(metaclass=Singleton): """ This is the Component Registry. It is a singleton object and is used to provide a look up service for common objects. """ log.info('Registry loaded') - __instance__ = None - - def __new__(cls): - """ - Re-implement the __new__ method to make sure we create a true singleton. - """ - if not cls.__instance__: - cls.__instance__ = object.__new__(cls) - return cls.__instance__ @classmethod def create(cls): @@ -57,20 +47,9 @@ class Registry(object): registry.service_list = {} registry.functions_list = {} registry.working_flags = {} - # Allow the tests to remove Registry entries but not the live system - registry.running_under_test = 'nose' in sys.argv[0] or 'pytest' in sys.argv[0] 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 diff --git a/openlp/core/display/screens.py b/openlp/core/display/screens.py index fccb9741f..04f4ea321 100644 --- a/openlp/core/display/screens.py +++ b/openlp/core/display/screens.py @@ -28,6 +28,7 @@ from functools import cmp_to_key from PyQt5 import QtCore, QtWidgets +from openlp.core.common import Singleton from openlp.core.common.i18n import translate from openlp.core.common.registry import Registry from openlp.core.common.settings import Settings @@ -142,24 +143,15 @@ class Screen(object): screen_dict['custom_geometry']['height']) -class ScreenList(object): +class ScreenList(metaclass=Singleton): """ Wrapper to handle the parameters of the display screen. To get access to the screen list call ``ScreenList()``. """ log.info('Screen loaded') - __instance__ = None screens = [] - def __new__(cls): - """ - Re-implement __new__ to create a true singleton. - """ - if not cls.__instance__: - cls.__instance__ = object.__new__(cls) - return cls.__instance__ - def __iter__(self): """ Convert this object into an iterable, so that we can iterate over it instead of the inner list diff --git a/openlp/core/state.py b/openlp/core/state.py index c76644a57..b0474c8c2 100644 --- a/openlp/core/state.py +++ b/openlp/core/state.py @@ -28,6 +28,7 @@ contained within the openlp.core module. """ import logging +from openlp.core.common import Singleton from openlp.core.common.registry import Registry from openlp.core.common.mixins import LogMixin from openlp.core.lib.plugin import PluginStatus @@ -52,17 +53,7 @@ class StateModule(LogMixin): self.text = None -class State(LogMixin): - - __instance__ = None - - def __new__(cls): - """ - Re-implement the __new__ method to make sure we create a true singleton. - """ - if not cls.__instance__: - cls.__instance__ = object.__new__(cls) - return cls.__instance__ +class State(LogMixin, metaclass=Singleton): def load_settings(self): self.modules = {} diff --git a/openlp/core/ui/icons.py b/openlp/core/ui/icons.py index 701c25a09..b70d10996 100644 --- a/openlp/core/ui/icons.py +++ b/openlp/core/ui/icons.py @@ -27,6 +27,7 @@ import logging import qtawesome as qta from PyQt5 import QtGui, QtWidgets +from openlp.core.common import Singleton from openlp.core.common.applocation import AppLocation from openlp.core.lib import build_icon @@ -34,22 +35,11 @@ from openlp.core.lib import build_icon log = logging.getLogger(__name__) -class UiIcons(object): +class UiIcons(metaclass=Singleton): """ Provide standard icons for objects to use. """ - __instance__ = None - - def __new__(cls): - """ - Override the default object creation method to return a single instance. - """ - if not cls.__instance__: - cls.__instance__ = super().__new__(cls) - cls.__instance__.load() - return cls.__instance__ - - def load(self): + def __init__(self): """ These are the font icons used in the code. """ @@ -165,6 +155,7 @@ class UiIcons(object): 'volunteer': {'icon': 'fa.group'} } self.load_icons(icon_list) + self.main_icon = build_icon(':/icon/openlp-logo.svg') def load_icons(self, icon_list): """ @@ -184,7 +175,6 @@ class UiIcons(object): setattr(self, key, qta.icon('fa.plus-circle', color='red')) except Exception: setattr(self, key, qta.icon('fa.plus-circle', color='red')) - self.main_icon = build_icon(':/icon/openlp-logo.svg') @staticmethod def _print_icons(): diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index e0a06fa2d..6045476a4 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -26,6 +26,7 @@ plugin. import logging import re +from openlp.core.common import Singleton from openlp.core.common.i18n import translate from openlp.core.common.settings import Settings @@ -64,20 +65,10 @@ class LanguageSelection(object): English = 2 -class BibleStrings(object): +class BibleStrings(metaclass=Singleton): """ Provide standard strings for objects to use. """ - __instance__ = None - - def __new__(cls): - """ - Override the default object creation method to return a single instance. - """ - if not cls.__instance__: - cls.__instance__ = object.__new__(cls) - return cls.__instance__ - def __init__(self): """ These strings should need a good reason to be retranslated elsewhere. diff --git a/tests/functional/openlp_core/common/test_common.py b/tests/functional/openlp_core/common/test_common.py index 21f246a75..6624a4819 100644 --- a/tests/functional/openlp_core/common/test_common.py +++ b/tests/functional/openlp_core/common/test_common.py @@ -26,7 +26,7 @@ from pathlib import Path from unittest import TestCase from unittest.mock import MagicMock, call, patch -from openlp.core.common import clean_button_text, de_hump, extension_loader, is_linux, is_macosx, is_win, \ +from openlp.core.common import Singleton, clean_button_text, de_hump, extension_loader, is_linux, is_macosx, is_win, \ normalize_str, path_to_module, trace_error_handler @@ -163,6 +163,48 @@ class TestCommonFunctions(TestCase): mocked_logger.error.assert_called_with( 'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test') + def test_singleton_metaclass_multiple_init(self): + """ + Test that a class using the Singleton Metaclass is only initialised once despite being called several times and + that the same instance is returned each time.. + """ + # GIVEN: The Singleton Metaclass and a test class using it + class SingletonClass(metaclass=Singleton): + def __init__(self): + pass + + with patch.object(SingletonClass, '__init__', return_value=None) as patched_init: + + # WHEN: Initialising the class multiple times + inst_1 = SingletonClass() + inst_2 = SingletonClass() + + # THEN: The __init__ method of the SingletonClass should have only been called once, and both returned values + # should be the same instance. + assert inst_1 is inst_2 + assert patched_init.call_count == 1 + + def test_singleton_metaclass_multiple_classes(self): + """ + Test that multiple classes using the Singleton Metaclass return the different an appropriate instances. + """ + # GIVEN: Two different classes using the Singleton Metaclass + class SingletonClass1(metaclass=Singleton): + def __init__(self): + pass + + class SingletonClass2(metaclass=Singleton): + def __init__(self): + pass + + # WHEN: Initialising both classes + s_c1 = SingletonClass1() + s_c2 = SingletonClass2() + + # THEN: The instances should be an instance of the appropriate class + assert isinstance(s_c1, SingletonClass1) + assert isinstance(s_c2, SingletonClass2) + def test_is_win(self): """ Test the is_win() function diff --git a/tests/functional/openlp_core/ui/test_icons.py b/tests/functional/openlp_core/ui/test_icons.py index 9d91ec367..6bcdc641c 100644 --- a/tests/functional/openlp_core/ui/test_icons.py +++ b/tests/functional/openlp_core/ui/test_icons.py @@ -33,7 +33,7 @@ from tests.helpers.testmixin import TestMixin class TestIcons(TestCase, TestMixin): - @patch('openlp.core.ui.icons.UiIcons.load') + @patch('openlp.core.ui.icons.UiIcons.__init__', return_value=None) def test_simple_icon(self, _): # GIVEN: an basic set of icons icons = UiIcons() From bbb2fa935c2e1ab0372feff0b77f33c8338e51c0 Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 27 Jul 2019 07:37:16 +0100 Subject: [PATCH 05/22] Few performance changes and fixes to prevent possible bugs --- openlp/core/common/__init__.py | 2 +- openlp/core/common/actions.py | 2 +- openlp/core/display/html/display.js | 50 +++++++++++-------- openlp/core/display/render.py | 2 +- openlp/core/lib/theme.py | 1 + .../plugins/songs/lib/importers/cclifile.py | 2 + .../plugins/songs/lib/importers/dreambeam.py | 3 +- .../plugins/songs/lib/importers/easyslides.py | 2 + .../songs/lib/importers/easyworship.py | 2 +- 9 files changed, 40 insertions(+), 26 deletions(-) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 96e1181db..1f1ddb1d1 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -400,7 +400,7 @@ def get_images_filter(): global IMAGES_FILTER if not IMAGES_FILTER: log.debug('Generating images filter.') - formats = list(map(bytes.decode, list(map(bytes, QtGui.QImageReader.supportedImageFormats())))) + formats = list(map(bytes.decode, map(bytes, QtGui.QImageReader.supportedImageFormats()))) visible_formats = '(*.{text})'.format(text='; *.'.join(formats)) actual_formats = '(*.{text})'.format(text=' *.'.join(formats)) IMAGES_FILTER = '{text} {visible} {actual}'.format(text=translate('OpenLP', 'Image Files'), diff --git a/openlp/core/common/actions.py b/openlp/core/common/actions.py index 6d3cda905..8681888e8 100644 --- a/openlp/core/common/actions.py +++ b/openlp/core/common/actions.py @@ -260,7 +260,7 @@ class ActionList(object): return # We have to do this to ensure that the loaded shortcut list e. g. STRG+O (German) is converted to CTRL+O, # which is only done when we convert the strings in this way (QKeySequencet -> uncode). - shortcuts = list(map(QtGui.QKeySequence.toString, list(map(QtGui.QKeySequence, shortcuts)))) + shortcuts = list(map(QtGui.QKeySequence.toString, map(QtGui.QKeySequence, shortcuts))) # Check the alternate shortcut first, to avoid problems when the alternate shortcut becomes the primary shortcut # after removing the (initial) primary shortcut due to conflicts. if len(shortcuts) == 2: diff --git a/openlp/core/display/html/display.js b/openlp/core/display/html/display.js index e198eeee6..cc44b9171 100644 --- a/openlp/core/display/html/display.js +++ b/openlp/core/display/html/display.js @@ -281,8 +281,9 @@ var Display = { * Checks if the present slide content fits within the slide */ doesContentFit: function () { - console.debug("scrollHeight: " + $(".slides")[0].scrollHeight + ", clientHeight: " + $(".slides")[0].clientHeight); - return $(".slides")[0].clientHeight >= $(".slides")[0].scrollHeight; + var currSlide = $(".slides")[0]; + console.debug("scrollHeight: " + currSlide.scrollHeight + ", clientHeight: " + currSlide.clientHeight); + return currSlide.clientHeight >= currSlide.scrollHeight; }, /** * Generate the OpenLP startup splashscreen @@ -333,7 +334,7 @@ var Display = { /** * Set fullscreen image from base64 data * @param {string} bg_color - The background color - * @param {string} image - Path to the image + * @param {string} image_data - base64 encoded image data */ setFullscreenImageFromData: function(bg_color, image_data) { Display.clearSlides(); @@ -372,7 +373,6 @@ var Display = { * @param {string} verse - The verse number, e.g. "v1" * @param {string} text - The HTML for the verse, e.g. "line1
line2" * @param {string} footer_text - The HTML for the footer" - * @param {bool} [reinit=true] - Re-initialize Reveal. Defaults to true. */ addTextSlide: function (verse, text, footer_text) { var html = _prepareText(text); @@ -476,7 +476,8 @@ var Display = { * Play a video */ playVideo: function () { - if ($("#video").length == 1) { + var videoElem = $("#video"); + if (videoElem.length == 1) { $("#video")[0].play(); } }, @@ -484,17 +485,19 @@ var Display = { * Pause a video */ pauseVideo: function () { - if ($("#video").length == 1) { - $("#video")[0].pause(); + var videoElem = $("#video"); + if ($videoElem.length == 1) { + videoElem[0].pause(); } }, /** * Stop a video */ stopVideo: function () { - if ($("#video").length == 1) { - $("#video")[0].pause(); - $("#video")[0].currentTime = 0.0; + var videoElem = $("#video"); + if (videoElem.length == 1) { + videoElem[0].pause(); + videoElem[0].currentTime = 0.0; } }, /** @@ -502,8 +505,9 @@ var Display = { * @param seconds The position in seconds to seek to */ seekVideo: function (seconds) { - if ($("#video").length == 1) { - $("#video")[0].currentTime = seconds; + var videoElem = $("#video"); + if (videoElem.length == 1) { + videoElem[0].currentTime = seconds; } }, /** @@ -511,8 +515,9 @@ var Display = { * @param rate A Double of the rate. 1.0 => 100% speed, 0.75 => 75% speed, 1.25 => 125% speed, etc. */ setPlaybackRate: function (rate) { - if ($("#video").length == 1) { - $("#video")[0].playbackRate = rate; + var videoElem = $("#video"); + if (videoElem.length == 1) { + videoElem[0].playbackRate = rate; } }, /** @@ -520,24 +525,27 @@ var Display = { * @param level The volume level from 0 to 100. */ setVideoVolume: function (level) { - if ($("#video").length == 1) { - $("#video")[0].volume = level / 100.0; + var videoElem = $("#video"); + if (videoElem.length == 1) { + videoElem[0].volume = level / 100.0; } }, /** * Mute the volume */ toggleVideoMute: function () { - if ($("#video").length == 1) { - $("#video")[0].muted = !$("#video")[0].muted; + var videoElem = $("#video"); + if (videoElem.length == 1) { + videoElem[0].muted = !videoElem[0].muted; } }, /** * Clear the background audio playlist */ clearPlaylist: function () { - if ($("#background-audio").length == 1) { - var audio = $("#background-audio")[0]; + var backgroundAudoElem = $("#background-audio"); + if (backgroundAudoElem.length == 1) { + var audio = backgroundAudoElem[0]; /* audio.playList */ } }, @@ -619,7 +627,6 @@ var Display = { }, setTheme: function (theme) { this._theme = theme; - var slidesDiv = $(".slides") // Set the background var globalBackground = $("#global-background")[0]; var backgroundStyle = {}; @@ -721,6 +728,7 @@ var Display = { for (var key in mainStyle) { if (mainStyle.hasOwnProperty(key)) { slidesDiv.style.setProperty(key, mainStyle[key]); + console.log(slidesDiv.style.getPropertyValue("font-family")); } } // Set up the footer diff --git a/openlp/core/display/render.py b/openlp/core/display/render.py index 1097b5ac0..b7477db6b 100644 --- a/openlp/core/display/render.py +++ b/openlp/core/display/render.py @@ -47,7 +47,7 @@ log = logging.getLogger(__name__) SLIM_CHARS = 'fiíIÍjlĺľrtť.,;/ ()|"\'!:\\' CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)' - r'([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?') + r'([\u0080-\uFFFF\w\s\.\,\!\?\;\:\|\"\'\-\_]*)(\Z)?') CHORD_TEMPLATE = '{chord}' FIRST_CHORD_TEMPLATE = '{chord}' CHORD_LINE_TEMPLATE = '{chord}{tail}{whitespace}{remainder}' diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index bc5ba69b8..1a1f7f1cb 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -170,6 +170,7 @@ class Theme(object): jsn = get_text_file_string(json_path) self.load_theme(jsn) self.background_filename = None + self.version = 2 def expand_json(self, var, prev=None): """ diff --git a/openlp/plugins/songs/lib/importers/cclifile.py b/openlp/plugins/songs/lib/importers/cclifile.py index 48d536265..72c2eafec 100644 --- a/openlp/plugins/songs/lib/importers/cclifile.py +++ b/openlp/plugins/songs/lib/importers/cclifile.py @@ -146,7 +146,9 @@ class CCLIFileImport(SongImport): """ log.debug('USR file text: {text}'.format(text=text_list)) song_author = '' + song_fields = '' song_topics = '' + song_words = '' for line in text_list: if line.startswith('[S '): ccli, line = line.split(']', 1) diff --git a/openlp/plugins/songs/lib/importers/dreambeam.py b/openlp/plugins/songs/lib/importers/dreambeam.py index e591fe379..8e76e2f0c 100644 --- a/openlp/plugins/songs/lib/importers/dreambeam.py +++ b/openlp/plugins/songs/lib/importers/dreambeam.py @@ -87,6 +87,7 @@ class DreamBeamImport(SongImport): if self.stop_import_flag: return self.set_defaults() + author_copyright = '' parser = etree.XMLParser(remove_blank_text=True) try: with file_path.open('r') as xml_file: @@ -142,7 +143,7 @@ class DreamBeamImport(SongImport): author_copyright = song_xml.Text2.Text.text if author_copyright: author_copyright = str(author_copyright) - if author_copyright.find(str(SongStrings.CopyrightSymbol)) >= 0: + if author_copyright.find(SongStrings.CopyrightSymbol) >= 0: self.add_copyright(author_copyright) else: self.parse_author(author_copyright) diff --git a/openlp/plugins/songs/lib/importers/easyslides.py b/openlp/plugins/songs/lib/importers/easyslides.py index 7fd5b25c1..7f03afb5a 100644 --- a/openlp/plugins/songs/lib/importers/easyslides.py +++ b/openlp/plugins/songs/lib/importers/easyslides.py @@ -137,9 +137,11 @@ class EasySlidesImport(SongImport): except UnicodeDecodeError: log.exception('Unicode decode error while decoding Contents') self._success = False + return except AttributeError: log.exception('no Contents') self._success = False + return lines = lyrics.split('\n') # we go over all lines first, to determine information, # which tells us how to parse verses later diff --git a/openlp/plugins/songs/lib/importers/easyworship.py b/openlp/plugins/songs/lib/importers/easyworship.py index 2061195d3..eeb9feb11 100644 --- a/openlp/plugins/songs/lib/importers/easyworship.py +++ b/openlp/plugins/songs/lib/importers/easyworship.py @@ -268,13 +268,13 @@ class EasyWorshipSongImport(SongImport): self.db_set_record_struct(field_descriptions) # Pick out the field description indexes we will need try: - success = True fi_title = self.db_find_field(b'Title') fi_author = self.db_find_field(b'Author') fi_copy = self.db_find_field(b'Copyright') fi_admin = self.db_find_field(b'Administrator') fi_words = self.db_find_field(b'Words') fi_ccli = self.db_find_field(b'Song Number') + success = True except IndexError: # This is the wrong table success = False From 2e8da5a79e5d25663815e705e52bddaec32fc797 Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 27 Jul 2019 11:01:59 +0100 Subject: [PATCH 06/22] Fix check box alignment Fixes: https://launchpad.net/bugs/1832942 --- openlp/core/ui/advancedtab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 72b9133a0..38542e59f 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -81,7 +81,7 @@ class AdvancedTab(SettingsTab): self.ui_layout.addRow(self.media_plugin_check_box) self.hide_mouse_check_box = QtWidgets.QCheckBox(self.ui_group_box) self.hide_mouse_check_box.setObjectName('hide_mouse_check_box') - self.ui_layout.addWidget(self.hide_mouse_check_box) + self.ui_layout.addRow(self.hide_mouse_check_box) self.double_click_live_check_box = QtWidgets.QCheckBox(self.ui_group_box) self.double_click_live_check_box.setObjectName('double_click_live_check_box') self.ui_layout.addRow(self.double_click_live_check_box) From aa5e33da7d41a565592d32eb33f5c40af475d2b0 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 28 Jul 2019 09:30:24 +0100 Subject: [PATCH 07/22] Fix settings screen conversion --- openlp/core/common/settings.py | 13 +++++++------ openlp/core/display/screens.py | 9 +++++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 6fbb8a8ed..6042703c5 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -90,12 +90,13 @@ def upgrade_screens(number, x_position, y_position, height, width, can_override, number: { 'number': number, geometry_key: { - 'x': x_position, - 'y': y_position, - 'height': height, - 'width': width + 'x': int(x_position), + 'y': int(y_position), + 'height': int(height), + 'width': int(width) }, - 'is_display': is_display_screen + 'is_display': is_display_screen, + 'is_primary': number } } @@ -309,7 +310,7 @@ class Settings(QtCore.QSettings): ('songuasge/db hostname', 'songusage/db hostname', []), ('songuasge/db database', 'songusage/db database', []), ('presentations / Powerpoint Viewer', '', []), - (['core/monitor', 'core/x position', 'core/y position', 'core/height', 'core/width', 'core/override', + (['core/monitor', 'core/x position', 'core/y position', 'core/height', 'core/width', 'core/override position', 'core/display on monitor'], 'core/screens', [(upgrade_screens, [1, 0, 0, None, None, False, False])]), ('bibles/proxy name', '', []), # Just remove these bible proxy settings. They weren't used in 2.4! ('bibles/proxy address', '', []), diff --git a/openlp/core/display/screens.py b/openlp/core/display/screens.py index fccb9741f..efa71cdd5 100644 --- a/openlp/core/display/screens.py +++ b/openlp/core/display/screens.py @@ -133,8 +133,13 @@ class Screen(object): self.number = int(screen_dict['number']) self.is_display = screen_dict['is_display'] self.is_primary = screen_dict['is_primary'] - self.geometry = QtCore.QRect(screen_dict['geometry']['x'], screen_dict['geometry']['y'], - screen_dict['geometry']['width'], screen_dict['geometry']['height']) + try: + self.geometry = QtCore.QRect(screen_dict['geometry']['x'], screen_dict['geometry']['y'], + screen_dict['geometry']['width'], screen_dict['geometry']['height']) + except: + # Preserve the current values as this has come from the settings update which does not have + # the geometry information + pass if 'custom_geometry' in screen_dict: self.custom_geometry = QtCore.QRect(screen_dict['custom_geometry']['x'], screen_dict['custom_geometry']['y'], From 095881cb903ec7daa313fed9ca833c86c1454fe8 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 28 Jul 2019 09:40:38 +0100 Subject: [PATCH 08/22] Update logging settings --- openlp/core/threading.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/core/threading.py b/openlp/core/threading.py index 9a4043292..38c68638a 100644 --- a/openlp/core/threading.py +++ b/openlp/core/threading.py @@ -24,10 +24,11 @@ The :mod:`openlp.core.threading` module contains some common threading code """ from PyQt5 import QtCore +from openlp.core.common.mixins import LogMixin from openlp.core.common.registry import Registry -class ThreadWorker(QtCore.QObject): +class ThreadWorker(QtCore.QObject, LogMixin): """ The :class:`~openlp.core.threading.ThreadWorker` class provides a base class for all worker objects """ From da75c96d03fc45531eb9b73884ddf2f285660064 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 28 Jul 2019 15:40:07 +0100 Subject: [PATCH 09/22] allow last service to load on startup --- openlp/core/ui/mainwindow.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 2313e4705..f2389908d 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -635,7 +635,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert # if self.live_controller.display.isVisible(): # self.live_controller.display.setFocus() self.activateWindow() - if self.application.args: + # We will aloways have 1 but more need to be processed + if self.application.args and len(self.application.args) > 1: self.open_cmd_line_files(self.application.args) elif Settings().value(self.general_settings_section + '/auto open'): self.service_manager_contents.load_last_file() From 7f5166f3d0b5289aa8834c1c24f329d42818edef Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 28 Jul 2019 16:56:28 +0100 Subject: [PATCH 10/22] Text fixes --- openlp/core/display/render.py | 1 + openlp/core/ui/mainwindow.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/core/display/render.py b/openlp/core/display/render.py index 1097b5ac0..53e50446c 100644 --- a/openlp/core/display/render.py +++ b/openlp/core/display/render.py @@ -482,6 +482,7 @@ class ThemePreviewRenderer(LogMixin, DisplayWindow): :param theme_data: The theme to generated a preview for. :param force_page: Flag to tell message lines per page need to be generated. + :param generate_screenshot: Do I need to generate a screen shot? :rtype: QtGui.QPixmap """ # save value for use in format_slide diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index f2389908d..1e9e196e4 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -635,7 +635,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert # if self.live_controller.display.isVisible(): # self.live_controller.display.setFocus() self.activateWindow() - # We will aloways have 1 but more need to be processed + # We will always have 1 but more need to be processed if self.application.args and len(self.application.args) > 1: self.open_cmd_line_files(self.application.args) elif Settings().value(self.general_settings_section + '/auto open'): From 603a6ff7d646b74fe84eda2a0bf36bbd8aab947d Mon Sep 17 00:00:00 2001 From: Phill Date: Mon, 29 Jul 2019 21:13:41 +0100 Subject: [PATCH 11/22] Fix bible reference search when text matches multiple books Fixes: https://launchpad.net/bugs/1799005 --- openlp/plugins/bibles/lib/__init__.py | 37 ++++++++++----------- openlp/plugins/bibles/lib/db.py | 46 ++++++++++++--------------- openlp/plugins/bibles/lib/manager.py | 6 ++-- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 6045476a4..e6f6620c1 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -327,11 +327,11 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): log.debug('Matched reference {text}'.format(text=reference)) book = match.group('book') if not book_ref_id: - book_ref_id = bible.get_book_ref_id_by_localised_name(book, language_selection) + book_ref_ids = bible.get_book_ref_id_by_localised_name(book, language_selection) elif not bible.get_book_by_book_ref_id(book_ref_id): return [] # We have not found the book so do not continue - if not book_ref_id: + if not book_ref_ids: return [] ranges = match.group('ranges') range_list = get_reference_match('range_separator').split(ranges) @@ -372,22 +372,23 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): to_chapter = to_verse to_verse = None # Append references to the list - if has_range: - if not from_verse: - from_verse = 1 - if not to_verse: - to_verse = -1 - if to_chapter and to_chapter > from_chapter: - ref_list.append((book_ref_id, from_chapter, from_verse, -1)) - for i in range(from_chapter + 1, to_chapter): - ref_list.append((book_ref_id, i, 1, -1)) - ref_list.append((book_ref_id, to_chapter, 1, to_verse)) - elif to_verse >= from_verse or to_verse == -1: - ref_list.append((book_ref_id, from_chapter, from_verse, to_verse)) - elif from_verse: - ref_list.append((book_ref_id, from_chapter, from_verse, from_verse)) - else: - ref_list.append((book_ref_id, from_chapter, 1, -1)) + for book_ref_id in book_ref_ids: + if has_range: + if not from_verse: + from_verse = 1 + if not to_verse: + to_verse = -1 + if to_chapter and to_chapter > from_chapter: + ref_list.append((book_ref_id, from_chapter, from_verse, -1)) + for i in range(from_chapter + 1, to_chapter): + ref_list.append((book_ref_id, i, 1, -1)) + ref_list.append((book_ref_id, to_chapter, 1, to_verse)) + elif to_verse >= from_verse or to_verse == -1: + ref_list.append((book_ref_id, from_chapter, from_verse, to_verse)) + elif from_verse: + ref_list.append((book_ref_id, from_chapter, from_verse, from_verse)) + else: + ref_list.append((book_ref_id, from_chapter, 1, -1)) return ref_list else: log.debug('Invalid reference: {text}'.format(text=reference)) diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 3f48ca7b2..ce14641e5 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -281,13 +281,14 @@ class BibleDB(Manager): log.debug('BibleDB.get_book("{book}")'.format(book=book)) return self.get_object_filtered(Book, Book.name.like(book + '%')) - def get_books(self): + def get_books(self, book=None): """ A wrapper so both local and web bibles have a get_books() method that manager can call. Used in the media manager advanced search tab. """ - log.debug('BibleDB.get_books()') - return self.get_all_objects(Book, order_by_ref=Book.id) + log.debug('BibleDB.get_books("{book}")'.format(book=book)) + filter = Book.name.like(book + '%') if book else None + return self.get_all_objects(Book, filter_clause=filter, order_by_ref=Book.id) def get_book_by_book_ref_id(self, ref_id): """ @@ -300,39 +301,34 @@ class BibleDB(Manager): def get_book_ref_id_by_localised_name(self, book, language_selection): """ - Return the id of a named book. + Return the ids of a matching named book. :param book: The name of the book, according to the selected language. :param language_selection: The language selection the user has chosen in the settings section of the Bible. + :rtype: list[int] """ log.debug('get_book_ref_id_by_localised_name("{book}", "{lang}")'.format(book=book, lang=language_selection)) from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings book_names = BibleStrings().BookNames # escape reserved characters - book_escaped = book for character in RESERVED_CHARACTERS: - book_escaped = book_escaped.replace(character, '\\' + character) + book_escaped = book.replace(character, '\\' + character) regex_book = re.compile('\\s*{book}\\s*'.format(book='\\s*'.join(book_escaped.split())), re.IGNORECASE) if language_selection == LanguageSelection.Bible: - db_book = self.get_book(book) - if db_book: - return db_book.book_reference_id - elif language_selection == LanguageSelection.Application: - books = [key for key in list(book_names.keys()) if regex_book.match(str(book_names[key]))] - books = [_f for _f in map(BiblesResourcesDB.get_book, books) if _f] - for value in books: - if self.get_book_by_book_ref_id(value['id']): - return value['id'] - elif language_selection == LanguageSelection.English: - books = BiblesResourcesDB.get_books_like(book) - if books: - book_list = [value for value in books if regex_book.match(value['name'])] - if not book_list: - book_list = books - for value in book_list: - if self.get_book_by_book_ref_id(value['id']): - return value['id'] - return False + db_books = self.get_books(book) + return [db_book.book_reference_id for db_book in db_books] + else: + if language_selection == LanguageSelection.Application: + books = [key for key in list(book_names.keys()) if regex_book.match(book_names[key])] + book_list = [_f for _f in map(BiblesResourcesDB.get_book, books) if _f] + elif language_selection == LanguageSelection.English: + books = BiblesResourcesDB.get_books_like(book) + if books: + book_list = [value for value in books if regex_book.match(value['name'])] + if not book_list: + book_list = books + return [value['id'] for value in book_list if self.get_book_by_book_ref_id(value['id'])] + return [] def get_verses(self, reference_list, show_error=True): """ diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 53926d78b..44d18b92d 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -240,8 +240,10 @@ class BibleManager(LogMixin, RegistryProperties): book=book, chapter=chapter)) language_selection = self.get_language_selection(bible) - book_ref_id = self.db_cache[bible].get_book_ref_id_by_localised_name(book, language_selection) - return self.db_cache[bible].get_verse_count(book_ref_id, chapter) + book_ref_ids = self.db_cache[bible].get_book_ref_id_by_localised_name(book, language_selection) + if book_ref_ids: + return self.db_cache[bible].get_verse_count(book_ref_ids[0], chapter) + return 0 def get_verse_count_by_book_ref_id(self, bible, book_ref_id, chapter): """ From 5fb9eb70aff3d1368e5b16347b5a85e6639264bb Mon Sep 17 00:00:00 2001 From: Phill Date: Tue, 30 Jul 2019 20:52:10 +0100 Subject: [PATCH 12/22] Fix deleting song books Fixes: https://launchpad.net/bugs/1600510 --- openlp/plugins/songs/lib/db.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 3dd3b643f..ef6641a9b 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -374,7 +374,9 @@ def init_schema(url): mapper(SongBookEntry, songs_songbooks_table, properties={ 'songbook': relation(Book) }) - mapper(Book, song_books_table) + mapper(Book, song_books_table, properties={ + 'songs': relation(Song, secondary=songs_songbooks_table) + }) mapper(MediaFile, media_files_table) mapper(Song, songs_table, properties={ # Use the authors_songs relation when you need access to the 'author_type' attribute From 8ce94115c993e22f7b26e8952a45a1b804e99140 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 31 Jul 2019 17:26:25 +0100 Subject: [PATCH 13/22] Fix service Print code --- openlp/core/ui/printserviceform.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 0f8ea064b..e81f82f9a 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -219,13 +219,13 @@ class PrintServiceForm(QtWidgets.QDialog, Ui_PrintServiceDialog, RegistryPropert verse_def = None verse_html = None for slide in item.get_frames(): - if not verse_def or verse_def != slide['verseTag'] or verse_html == slide['printing_html']: + if not verse_def or verse_def != slide['verse'] or verse_html == slide['text']: text_div = self._add_element('div', parent=div, class_id='itemText') - elif 'chordspacing' not in slide['printing_html']: + elif 'chordspacing' not in slide['text']: self._add_element('br', parent=text_div) - self._add_element('span', slide['printing_html'], text_div) - verse_def = slide['verseTag'] - verse_html = slide['printing_html'] + self._add_element('span', slide['text'], text_div) + verse_def = slide['verse'] + verse_html = slide['text'] # Break the page before the div element. if index != 0 and self.page_break_after_text.isChecked(): div.set('class', 'item newPage') From 6b9e36e7af43348e4cb1f367c480468f39c194f8 Mon Sep 17 00:00:00 2001 From: Phill Date: Wed, 31 Jul 2019 21:12:11 +0100 Subject: [PATCH 14/22] Make button action clearer Fixes: https://launchpad.net/bugs/1673113 --- openlp/core/common/i18n.py | 1 + openlp/plugins/custom/forms/editcustomdialog.py | 2 ++ openlp/plugins/songs/forms/editsongdialog.py | 2 ++ 3 files changed, 5 insertions(+) diff --git a/openlp/core/common/i18n.py b/openlp/core/common/i18n.py index c05c6fdf0..91a2a6f22 100644 --- a/openlp/core/common/i18n.py +++ b/openlp/core/common/i18n.py @@ -425,6 +425,7 @@ class UiStrings(metaclass=Singleton): self.ResetLiveBG = translate('OpenLP.Ui', 'Reset live background.') self.RequiredShowInFooter = translate('OpenLP.Ui', 'Required, this will be displayed in footer.') self.Seconds = translate('OpenLP.Ui', 's', 'The abbreviated unit for seconds') + self.SaveAndClose = translate('OpenLP.ui', translate('SongsPlugin.EditSongForm', '&Save && Close')) self.SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview') self.Search = translate('OpenLP.Ui', 'Search') self.SearchThemes = translate('OpenLP.Ui', 'Search Themes...', 'Search bar place holder text ') diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 6193cd017..470eccba7 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -97,6 +97,7 @@ class Ui_CustomEditDialog(object): self.preview_button = QtWidgets.QPushButton() self.button_box = create_button_box(custom_edit_dialog, 'button_box', ['cancel', 'save'], [self.preview_button]) + self.save_button = self.button_box.button(QtWidgets.QDialogButtonBox.Save) self.dialog_layout.addWidget(self.button_box) self.retranslate_ui(custom_edit_dialog) @@ -112,3 +113,4 @@ class Ui_CustomEditDialog(object): self.theme_label.setText(translate('CustomPlugin.EditCustomForm', 'The&me:')) self.credit_label.setText(translate('CustomPlugin.EditCustomForm', '&Credits:')) self.preview_button.setText(UiStrings().SaveAndPreview) + self.save_button.setText(UiStrings().SaveAndClose) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index bf5910cf3..1fa042c37 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -291,6 +291,7 @@ class Ui_EditSongDialog(object): self.warning_label.setObjectName('warning_label') self.bottom_layout.addWidget(self.warning_label) self.button_box = create_button_box(edit_song_dialog, 'button_box', ['cancel', 'save']) + self.save_button = self.button_box.button(QtWidgets.QDialogButtonBox.Save) self.bottom_layout.addWidget(self.button_box) self.dialog_layout.addLayout(self.bottom_layout) self.retranslate_ui(edit_song_dialog) @@ -341,6 +342,7 @@ class Ui_EditSongDialog(object): translate('SongsPlugin.EditSongForm', 'Warning: Not all of the verses are in use.') self.no_verse_order_entered_warning = \ translate('SongsPlugin.EditSongForm', 'Warning: You have not entered a verse order.') + self.save_button.setText(UiStrings().SaveAndPreview) def create_combo_box(parent, name, editable=True): From df2ace541e1b17c74d79c0863376aa07c82b51d6 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 31 Jul 2019 22:01:22 +0100 Subject: [PATCH 15/22] fix missing state code --- openlp/core/ui/servicemanager.py | 3 ++- openlp/core/ui/thememanager.py | 5 +++-- openlp/plugins/songs/forms/editsongform.py | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 92b34badd..aad6f1fec 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -34,6 +34,7 @@ from tempfile import NamedTemporaryFile from PyQt5 import QtCore, QtGui, QtWidgets +from openlp.core.state import State from openlp.core.common import ThemeLevel, delete_file from openlp.core.common.actions import ActionList, CategoryOrder from openlp.core.common.applocation import AppLocation @@ -828,7 +829,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi self.auto_start_action.setIcon(UiIcons().inactive) self.auto_start_action.setText(translate('OpenLP.ServiceManager', '&Auto Start - inactive')) if service_item['service_item'].is_text(): - for plugin in self.plugin_manager.plugins: + for plugin in State().list_plugins(): if plugin.name == 'custom' and plugin.status == PluginStatus.Active: self.create_custom_action.setVisible(True) break diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 4a3aa5073..06a76372d 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -30,6 +30,7 @@ from xml.etree.ElementTree import XML, ElementTree from PyQt5 import QtCore, QtWidgets +from openlp.core.state import State from openlp.core.common import delete_file from openlp.core.common.applocation import AppLocation from openlp.core.common.i18n import UiStrings, get_locale_key, translate @@ -293,7 +294,7 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R old_theme_data = self.get_theme_data(old_theme_name) self.clone_theme_data(old_theme_data, new_theme_name) self.delete_theme(old_theme_name) - for plugin in self.plugin_manager.plugins: + for plugin in State().list_plugins(): if plugin.uses_theme(old_theme_name): plugin.rename_theme(old_theme_name, new_theme_name) self.renderer.set_theme(self.get_theme_data(new_theme_name)) @@ -771,7 +772,7 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R # check for use in the system else where. if test_plugin: plugin_usage = "" - for plugin in self.plugin_manager.plugins: + for plugin in State().list_plugins(): used_count = plugin.uses_theme(theme) if used_count: plugin_usage = "{plug}{text}".format(plug=plugin_usage, diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 62d0eb7d7..e442e66d5 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -29,6 +29,7 @@ from shutil import copyfile from PyQt5 import QtCore, QtWidgets +from openlp.core.state import State from openlp.core.common.applocation import AppLocation from openlp.core.common.i18n import UiStrings, get_natural_key, translate from openlp.core.common.mixins import RegistryProperties @@ -416,7 +417,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): Load the media files into a combobox. """ self.from_media_button.setVisible(False) - for plugin in self.plugin_manager.plugins: + for plugin in State().list_plugins(): if plugin.name == 'media' and plugin.status == PluginStatus.Active: self.from_media_button.setVisible(True) self.media_form.populate_files(plugin.media_item.get_list(MediaType.Audio)) From a8af8a8d18ab30ab8884917817e672423eb0a00f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 4 Aug 2019 08:53:56 +0100 Subject: [PATCH 16/22] fix custom copy --- openlp/plugins/custom/lib/mediaitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 3db190e8d..768bef391 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -349,7 +349,7 @@ class CustomMediaItem(MediaManagerItem): custom.credits = '' custom_xml = CustomXMLBuilder() for (idx, slide) in enumerate(item.slides): - custom_xml.add_verse_to_lyrics('custom', str(idx + 1), slide['raw_slide']) + custom_xml.add_verse_to_lyrics('custom', str(idx + 1), slide['text']) custom.text = str(custom_xml.extract_xml(), 'utf-8') self.plugin.db_manager.save_object(custom) self.on_search_text_button_clicked() From 24ab4f2cb149e2a25184482cb9e6e429e33c4dc4 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 4 Aug 2019 14:13:33 +0100 Subject: [PATCH 17/22] minor fixes --- openlp/core/ui/mainwindow.py | 4 +++- openlp/core/ui/servicemanager.py | 2 +- openlp/plugins/songs/forms/editsongform.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 1e9e196e4..da8b72856 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -635,7 +635,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert # if self.live_controller.display.isVisible(): # self.live_controller.display.setFocus() self.activateWindow() - # We will always have 1 but more need to be processed + # We have -disable-web-security added by our code. + # If a file is passed in we will have that as well so count of 2 + # If not we need to see if we want to use the previous file.so count of 1 if self.application.args and len(self.application.args) > 1: self.open_cmd_line_files(self.application.args) elif Settings().value(self.general_settings_section + '/auto open'): diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index aad6f1fec..202a926dd 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -829,7 +829,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi self.auto_start_action.setIcon(UiIcons().inactive) self.auto_start_action.setText(translate('OpenLP.ServiceManager', '&Auto Start - inactive')) if service_item['service_item'].is_text(): - for plugin in State().list_plugins(): + for plugin in State().list_plugins(): if plugin.name == 'custom' and plugin.status == PluginStatus.Active: self.create_custom_action.setVisible(True) break diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index e442e66d5..899e62122 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -417,7 +417,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): Load the media files into a combobox. """ self.from_media_button.setVisible(False) - for plugin in State().list_plugins(): + for plugin in State().list_plugins(): if plugin.name == 'media' and plugin.status == PluginStatus.Active: self.from_media_button.setVisible(True) self.media_form.populate_files(plugin.media_item.get_list(MediaType.Audio)) From 0e96c55c8edfde2a85ed9172a79d6230d5cebcf8 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 4 Aug 2019 15:06:00 +0100 Subject: [PATCH 18/22] pep 8 --- openlp/core/display/screens.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/display/screens.py b/openlp/core/display/screens.py index efa71cdd5..7d0d7f0a0 100644 --- a/openlp/core/display/screens.py +++ b/openlp/core/display/screens.py @@ -136,7 +136,7 @@ class Screen(object): try: self.geometry = QtCore.QRect(screen_dict['geometry']['x'], screen_dict['geometry']['y'], screen_dict['geometry']['width'], screen_dict['geometry']['height']) - except: + except KeyError: # Preserve the current values as this has come from the settings update which does not have # the geometry information pass From 493f33b764c66c819c155f7fa3b93de2a69fa8a7 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 4 Aug 2019 16:56:07 +0100 Subject: [PATCH 19/22] minor fix --- openlp/core/common/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 6042703c5..bbe0b731a 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -96,7 +96,7 @@ def upgrade_screens(number, x_position, y_position, height, width, can_override, 'width': int(width) }, 'is_display': is_display_screen, - 'is_primary': number + 'is_primary': can_override } } From 197bd42c9def83654d1a5f8413bd293555d542da Mon Sep 17 00:00:00 2001 From: Phill Date: Mon, 5 Aug 2019 20:43:18 +0100 Subject: [PATCH 20/22] Fix for invalid song beamer files --- openlp/plugins/songs/lib/importers/songbeamer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/importers/songbeamer.py b/openlp/plugins/songs/lib/importers/songbeamer.py index 3b5bf4aac..7f19086ac 100644 --- a/openlp/plugins/songs/lib/importers/songbeamer.py +++ b/openlp/plugins/songs/lib/importers/songbeamer.py @@ -128,7 +128,7 @@ class SongBeamerImport(SongImport): # The encoding should only be ANSI (cp1252), UTF-8, Unicode, Big-Endian-Unicode. # So if it doesn't start with 'u' we default to cp1252. See: # https://forum.songbeamer.com/viewtopic.php?p=419&sid=ca4814924e37c11e4438b7272a98b6f2 - if not self.input_file_encoding.lower().startswith('u'): + if self.input_file_encoding and not self.input_file_encoding.lower().startswith('u'): self.input_file_encoding = 'cp1252' with file_path.open(encoding=self.input_file_encoding) as song_file: song_data = song_file.readlines() From a950a9bccefd5892b5a518c22b7d7de1ca723433 Mon Sep 17 00:00:00 2001 From: Phill Date: Tue, 6 Aug 2019 22:33:43 +0100 Subject: [PATCH 21/22] fixes --- openlp/plugins/bibles/lib/__init__.py | 2 ++ openlp/plugins/bibles/lib/db.py | 1 + tests/functional/openlp_core/lib/test_theme.py | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index e6f6620c1..b0d5189e1 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -330,6 +330,8 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): book_ref_ids = bible.get_book_ref_id_by_localised_name(book, language_selection) elif not bible.get_book_by_book_ref_id(book_ref_id): return [] + else: + book_ref_ids = [book_ref_id] # We have not found the book so do not continue if not book_ref_ids: return [] diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index ce14641e5..5a629d14e 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -318,6 +318,7 @@ class BibleDB(Manager): db_books = self.get_books(book) return [db_book.book_reference_id for db_book in db_books] else: + book_list = [] if language_selection == LanguageSelection.Application: books = [key for key in list(book_names.keys()) if regex_book.match(book_names[key])] book_list = [_f for _f in map(BiblesResourcesDB.get_book, books) if _f] diff --git a/tests/functional/openlp_core/lib/test_theme.py b/tests/functional/openlp_core/lib/test_theme.py index e294e5214..800e9c584 100644 --- a/tests/functional/openlp_core/lib/test_theme.py +++ b/tests/functional/openlp_core/lib/test_theme.py @@ -182,4 +182,4 @@ class TestTheme(TestCase): assert 0 == theme.display_vertical_align, 'display_vertical_align should be 0' assert theme.font_footer_bold is False, 'font_footer_bold should be False' assert 'Arial' == theme.font_main_name, 'font_main_name should be "Arial"' - assert 47 == len(theme.__dict__), 'The theme should have 47 attributes' + assert 48 == len(theme.__dict__), 'The theme should have 48 attributes' From b6512adf2a5f9aa54fc879c2ced3e0b4d010bfe2 Mon Sep 17 00:00:00 2001 From: Phill Date: Tue, 6 Aug 2019 22:46:00 +0100 Subject: [PATCH 22/22] Fixes --- openlp/core/display/html/display.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openlp/core/display/html/display.js b/openlp/core/display/html/display.js index cc44b9171..56e3c0a48 100644 --- a/openlp/core/display/html/display.js +++ b/openlp/core/display/html/display.js @@ -478,7 +478,7 @@ var Display = { playVideo: function () { var videoElem = $("#video"); if (videoElem.length == 1) { - $("#video")[0].play(); + videoElem[0].play(); } }, /** @@ -486,7 +486,7 @@ var Display = { */ pauseVideo: function () { var videoElem = $("#video"); - if ($videoElem.length == 1) { + if (videoElem.length == 1) { videoElem[0].pause(); } }, @@ -728,7 +728,6 @@ var Display = { for (var key in mainStyle) { if (mainStyle.hasOwnProperty(key)) { slidesDiv.style.setProperty(key, mainStyle[key]); - console.log(slidesDiv.style.getPropertyValue("font-family")); } } // Set up the footer