diff --git a/openlp/core/display/render.py b/openlp/core/display/render.py index e74a66e73..d9c905b83 100644 --- a/openlp/core/display/render.py +++ b/openlp/core/display/render.py @@ -514,6 +514,26 @@ class ThemePreviewRenderer(LogMixin, DisplayWindow): self.force_page = False return None + def get_theme(self, item): + """ + :param item: The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object + :return string: The name of the theme to be used + + """ + # Just assume we use the global theme. + theme_name = Registry().get('theme_manager').global_theme + # The theme level is either set to Service or Item. Use the service theme if one is set. We also have to use the + # service theme, even when the theme level is set to Item, because the item does not necessarily have to have a + # theme. + if self.theme_level != ThemeLevel.Global: + # When the theme level is at Service and we actually have a service theme then use it. + if self.theme_level == ThemeLevel.Service: + theme_name = Registry().get('service_manager').service_theme + # If we have Item level and have an item theme then use it. + if self.theme_level == ThemeLevel.Song and item.theme: + theme_name = item.theme + return theme_name + def format_slide(self, text, item): """ Calculate how much text can fit on a slide. @@ -526,7 +546,7 @@ class ThemePreviewRenderer(LogMixin, DisplayWindow): QtWidgets.QApplication.instance().processEvents() self.log_debug('format slide') if item: - theme_name = item.theme if item.theme else Registry().get('theme_manager').global_theme + theme_name = self.get_theme(item) theme_data = Registry().get('theme_manager').get_theme_data(theme_name) self.theme_height = theme_data.font_main_height # Set theme for preview diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 60e80acba..bce008eeb 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -350,6 +350,13 @@ class ServiceItem(RegistryProperties): 'display_title': slide['display_title'], 'notes': slide['notes']}) return {'header': service_header, 'data': service_data} + def render_text_items(self): + """ + This method forces the display to be regenerated + """ + self._display_slides = [] + self._rendered_slides = [] + def set_from_service(self, service_item, path=None): """ This method takes a service item from a saved service file (passed from the ServiceManager) and extracts the @@ -532,7 +539,6 @@ class ServiceItem(RegistryProperties): :param row: The service item slide to be returned """ if self.service_item_type == ServiceItemType.Text: - # return self.display_frames[row]['html'].split('\n')[0] return self.rendered_slides[row]['text'] elif self.service_item_type == ServiceItemType.Image: return self.slides[row]['path'] diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index da8b72856..8f3a8aa0f 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -546,7 +546,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert Wait for the threads """ # Sometimes the threads haven't finished, let's wait for them - wait_dialog = QtWidgets.QProgressDialog('Waiting for some things to finish...', '', 0, 0, self) + wait_dialog = QtWidgets.QProgressDialog(translate('OpenLP.MainWindow', 'Waiting for some things to finish...'), + '', 0, 0, self) wait_dialog.setWindowModality(QtCore.Qt.WindowModal) wait_dialog.setAutoClose(False) wait_dialog.setCancelButton(None) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 202a926dd..4e0b3a74b 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -46,7 +46,7 @@ from openlp.core.common.settings import Settings from openlp.core.lib import build_icon from openlp.core.lib.exceptions import ValidationError from openlp.core.lib.plugin import PluginStatus -from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem +from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem, ServiceItemType from openlp.core.lib.ui import create_widget_action, critical_error_message_box, find_and_set_in_combo_box from openlp.core.ui.icons import UiIcons from openlp.core.ui.media import AUDIO_EXT, VIDEO_EXT @@ -749,9 +749,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi if theme: find_and_set_in_combo_box(self.theme_combo_box, theme, set_missing=False) if theme == self.theme_combo_box.currentText(): - # TODO: Use a local display widget - # self.preview_display.set_theme(get_theme_from_name(theme)) - pass + self.service_theme = theme else: if self._save_lite: service_item.set_from_service(item) @@ -1197,9 +1195,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi service_item_from_item = item['service_item'] tree_widget_item = QtWidgets.QTreeWidgetItem(self.service_manager_list) if service_item_from_item.is_valid: + icon = service_item_from_item.icon.pixmap(80, 80).toImage() + icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) if service_item_from_item.notes: - icon = service_item_from_item.icon.pixmap(80, 80).toImage() - icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) overlay = UiIcons().notes.pixmap(40, 40).toImage() overlay = overlay.scaled(40, 40, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) painter = QtGui.QPainter(icon) @@ -1207,8 +1205,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi painter.end() tree_widget_item.setIcon(0, build_icon(icon)) elif service_item_from_item.temporary_edit: - icon = service_item_from_item.icon.pixmap(80, 80).toImage() - icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) overlay = QtGui.QImage(UiIcons().upload) overlay = overlay.scaled(40, 40, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) painter = QtGui.QPainter(icon) @@ -1242,13 +1238,14 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi tree_widget_item.setData(0, QtCore.Qt.UserRole, item['order']) tree_widget_item.setSelected(item['selected']) # Add the children to their parent tree_widget_item. - for slide_index, slide in enumerate(service_item_from_item.slides): + for slide_index, slide in enumerate(service_item_from_item.get_frames()): child = QtWidgets.QTreeWidgetItem(tree_widget_item) # prefer to use a display_title - if service_item_from_item.is_capable(ItemCapabilities.HasDisplayTitle): - text = slide['display_title'].replace('\n', ' ') - else: + if service_item_from_item.is_capable(ItemCapabilities.HasDisplayTitle) or \ + service_item_from_item.service_item_type == ServiceItemType.Image: text = slide['title'].replace('\n', ' ') + else: + text = service_item_from_item.get_rendered_frame(slide_index) child.setText(0, text[:40]) child.setData(0, QtCore.Qt.UserRole, slide_index) if service_item == item_index: @@ -1275,8 +1272,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi :param current_index: The combo box index for the selected item """ self.service_theme = self.theme_combo_box.currentText() - # TODO: Use a local display widget - # self.preview_display.set_theme(get_theme_from_name(theme)) Settings().setValue(self.main_window.service_manager_settings_section + '/service theme', self.service_theme) self.regenerate_service_items(True) @@ -1335,7 +1330,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi """ for item_count, item in enumerate(self.service_items): if item['service_item'].edit_id == new_item.edit_id and item['service_item'].name == new_item.name: - new_item.render() + new_item.create_slides() new_item.merge(item['service_item']) item['service_item'] = new_item self.repaint_service_list(item_count + 1, 0) @@ -1368,7 +1363,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi self.repaint_service_list(s_item, child) self.live_controller.replace_service_manager_item(item) else: - # item.render() + item.render_text_items() # nothing selected for dnd if self.drop_position == -1: if isinstance(item, list): @@ -1617,8 +1612,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi theme_group.addAction(create_widget_action(self.theme_menu, theme, text=theme, checked=False, triggers=self.on_theme_change_action)) find_and_set_in_combo_box(self.theme_combo_box, self.service_theme) - # TODO: Sort this out - # self.renderer.set_service_theme(self.service_theme) self.regenerate_service_items() def on_theme_change_action(self): diff --git a/openlp/plugins/presentations/lib/maclocontroller.py b/openlp/plugins/presentations/lib/maclocontroller.py index 10ba08e5e..1b0cfea8c 100644 --- a/openlp/plugins/presentations/lib/maclocontroller.py +++ b/openlp/plugins/presentations/lib/maclocontroller.py @@ -27,6 +27,7 @@ from Pyro4 import Proxy from openlp.core.common import delete_file, is_macosx from openlp.core.common.applocation import AppLocation +from openlp.core.common.mixins import LogMixin from openlp.core.common.path import Path from openlp.core.common.registry import Registry from openlp.core.display.screens import ScreenList @@ -47,7 +48,7 @@ log = logging.getLogger(__name__) register_classes() -class MacLOController(PresentationController): +class MacLOController(PresentationController, LogMixin): """ Class to control interactions with MacLO presentations on Mac OS X via Pyro4. It starts the Pyro4 nameserver, starts the LibreOfficeServer, and then controls MacLO via Pyro4. diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 1fa042c37..bf5910cf3 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -291,7 +291,6 @@ 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) @@ -342,7 +341,6 @@ 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): diff --git a/tests/functional/openlp_core/display/test_render.py b/tests/functional/openlp_core/display/test_render.py index ac47b42fe..a4ed729ee 100644 --- a/tests/functional/openlp_core/display/test_render.py +++ b/tests/functional/openlp_core/display/test_render.py @@ -22,10 +22,20 @@ """ Test the :mod:`~openlp.core.display.render` package. """ -from unittest.mock import patch +import sys +from unittest import TestCase +from unittest.mock import MagicMock, patch + +from openlp.core.common import ThemeLevel +from tests.helpers.testmixin import TestMixin + +from PyQt5 import QtWidgets +sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock() + +from openlp.core.common.registry import Registry from openlp.core.display.render import compare_chord_lyric_width, find_formatting_tags, remove_tags, render_chords, \ - render_chords_for_printing, render_tags + render_chords_for_printing, render_tags, ThemePreviewRenderer from openlp.core.lib.formattingtags import FormattingTags @@ -208,3 +218,101 @@ def test_find_formatting_tags(): # THEN: The list of active tags should contain only 'st' assert active_tags == ['st'], 'The list of active tags should contain only "st"' + + +class TestThemePreviewRenderer(TestMixin, TestCase): + + def setUp(self): + """ + Set up the components need for all tests. + """ + # Create the Registry + self.application = QtWidgets.QApplication.instance() + Registry.create() + self.application.setOrganizationName('OpenLP-tests') + self.application.setOrganizationDomain('openlp.org') + + def test_get_theme_global(self): + """ + Test the return of the global theme if set to Global level + """ + # GIVEN: A set up with a Global Theme and settings at Global + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'my_global_theme' + Registry().register('theme_manager', mocked_theme_manager) + with patch('openlp.core.display.webengine.WebEngineView'), \ + patch('PyQt5.QtWidgets.QVBoxLayout'): + tpr = ThemePreviewRenderer() + tpr.theme_level = ThemeLevel.Global + # WHEN: I Request the theme to Use + theme = tpr.get_theme(MagicMock()) + + # THEN: The list of active tags should contain only 'st' + assert theme == mocked_theme_manager.global_theme, 'The Theme returned is not that of the global theme' + + def test_get_theme_service(self): + """ + Test the return of the global theme if set to Global level + """ + # GIVEN: A set up with a Global Theme and settings at Global + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'my_global_theme' + mocked_service_manager = MagicMock() + mocked_service_manager.service_theme = 'my_service_theme' + Registry().register('theme_manager', mocked_theme_manager) + Registry().register('service_manager', mocked_service_manager) + with patch('openlp.core.display.webengine.WebEngineView'), \ + patch('PyQt5.QtWidgets.QVBoxLayout'): + tpr = ThemePreviewRenderer() + tpr.theme_level = ThemeLevel.Service + # WHEN: I Request the theme to Use + theme = tpr.get_theme(MagicMock()) + + # THEN: The list of active tags should contain only 'st' + assert theme == mocked_service_manager.service_theme, 'The Theme returned is not that of the Service theme' + + def test_get_theme_item_level_none(self): + """ + Test the return of the global theme if set to Global level + """ + # GIVEN: A set up with a Global Theme and settings at Global + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'my_global_theme' + mocked_service_manager = MagicMock() + mocked_service_manager.service_theme = 'my_service_theme' + mocked_item = MagicMock() + mocked_item.theme = None + Registry().register('theme_manager', mocked_theme_manager) + Registry().register('service_manager', mocked_service_manager) + with patch('openlp.core.display.webengine.WebEngineView'), \ + patch('PyQt5.QtWidgets.QVBoxLayout'): + tpr = ThemePreviewRenderer() + tpr.theme_level = ThemeLevel.Song + # WHEN: I Request the theme to Use + theme = tpr.get_theme(mocked_item) + + # THEN: The list of active tags should contain only 'st' + assert theme == mocked_theme_manager.global_theme, 'The Theme returned is not that of the global theme' + + def test_get_theme_item_level_set(self): + """ + Test the return of the global theme if set to Global level + """ + # GIVEN: A set up with a Global Theme and settings at Global + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'my_global_theme' + mocked_service_manager = MagicMock() + mocked_service_manager.service_theme = 'my_service_theme' + mocked_item = MagicMock() + mocked_item.theme = "my_item_theme" + Registry().register('theme_manager', mocked_theme_manager) + Registry().register('service_manager', mocked_service_manager) + with patch('openlp.core.display.webengine.WebEngineView'), \ + patch('PyQt5.QtWidgets.QVBoxLayout'): + tpr = ThemePreviewRenderer() + tpr.theme_level = ThemeLevel.Song + # WHEN: I Request the theme to Use + theme = tpr.get_theme(mocked_item) + + # THEN: The list of active tags should contain only 'st' + assert theme == mocked_item.theme, 'The Theme returned is not that of the item theme'