diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py index f26b41fc5..c7e29415d 100644 --- a/openlp/core/common/uistrings.py +++ b/openlp/core/common/uistrings.py @@ -152,3 +152,4 @@ class UiStrings(object): self.Version = translate('OpenLP.Ui', 'Version') self.View = translate('OpenLP.Ui', 'View') self.ViewMode = translate('OpenLP.Ui', 'View Mode') + self.Video = translate('OpenLP.Ui', 'Video') diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 0ef0065a7..bd35c4bdb 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -44,6 +44,7 @@ class BackgroundType(object): Gradient = 1 Image = 2 Transparent = 3 + Video = 4 @staticmethod def to_string(background_type): @@ -58,6 +59,8 @@ class BackgroundType(object): return 'image' elif background_type == BackgroundType.Transparent: return 'transparent' + elif background_type == BackgroundType.Video: + return 'video' @staticmethod def from_string(type_string): @@ -72,6 +75,8 @@ class BackgroundType(object): return BackgroundType.Image elif type_string == 'transparent': return BackgroundType.Transparent + elif type_string == 'video': + return BackgroundType.Video class BackgroundGradientType(object): @@ -184,7 +189,7 @@ class ThemeXML(object): :param path: The path name to be added. """ - if self.background_type == 'image': + if self.background_type == 'image' or self.background_type == 'video': if self.background_filename and path: self.theme_name = self.theme_name.strip() self.background_filename = self.background_filename.strip() @@ -255,6 +260,21 @@ class ThemeXML(object): # Create endColor element self.child_element(background, 'borderColor', str(border_color)) + def add_background_video(self, filename, border_color): + """ + Add a video background. + + :param filename: The file name of the video. + :param border_color: + """ + background = self.theme_xml.createElement('background') + background.setAttribute('type', 'video') + self.theme.appendChild(background) + # Create Filename element + self.child_element(background, 'filename', filename) + # Create endColor element + self.child_element(background, 'borderColor', str(border_color)) + def add_font(self, name, color, size, override, fonttype='main', bold='False', italics='False', line_adjustment=0, xpos=0, ypos=0, width=0, height=0, outline='False', outline_color='#ffffff', outline_pixel=2, shadow='False', shadow_color='#ffffff', shadow_pixel=5): @@ -512,6 +532,9 @@ class ThemeXML(object): elif self.background_type == BackgroundType.to_string(BackgroundType.Image): filename = os.path.split(self.background_filename)[1] self.add_background_image(filename, self.background_border_color) + elif self.background_type == BackgroundType.to_string(BackgroundType.Video): + filename = os.path.split(self.background_filename)[1] + self.add_background_video(filename, self.background_border_color) elif self.background_type == BackgroundType.to_string(BackgroundType.Transparent): self.add_background_transparent() self.add_font( diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index b803df205..00f4be7ca 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -31,13 +31,15 @@ Some of the code for this form is based on the examples at: import html import logging +import os from PyQt5 import QtCore, QtWidgets, QtWebKit, QtWebKitWidgets, QtOpenGL, QtGui, QtMultimedia -from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, Settings, translate, is_macosx, is_win +from openlp.core.common import AppLocation, Registry, RegistryProperties, OpenLPMixin, Settings, translate,\ + is_macosx, is_win from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte from openlp.core.lib.theme import BackgroundType -from openlp.core.ui import HideMode, AlertLocation +from openlp.core.ui import HideMode, AlertLocation, DisplayControllerType if is_macosx(): from ctypes import pythonapi, c_void_p, c_char_p, py_object @@ -459,13 +461,13 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): background = self.image_manager.get_image_bytes(self.override['image'], ImageSource.ImagePlugin) self.set_transparency(self.service_item.theme_data.background_type == BackgroundType.to_string(BackgroundType.Transparent)) - if self.service_item.theme_data.background_filename: - self.service_item.bg_image_bytes = self.image_manager.get_image_bytes( - self.service_item.theme_data.background_filename, ImageSource.Theme) - if image_path: - image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin) - else: - image_bytes = None + image_bytes = None + if self.service_item.theme_data.background_type == 'image': + if self.service_item.theme_data.background_filename: + self.service_item.bg_image_bytes = self.image_manager.get_image_bytes( + self.service_item.theme_data.background_filename, ImageSource.Theme) + if image_path: + image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin) html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes, plugins=self.plugin_manager.plugins) self.web_view.setHtml(html) @@ -477,6 +479,17 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): Registry().execute('slidecontroller_live_unblank') else: self.hide_display(self.hide_mode) + if self.service_item.theme_data.background_type == 'video' and self.is_live: + if self.service_item.theme_data.background_filename: + service_item = ServiceItem() + service_item.title = 'webkit' + service_item.processor = 'webkit' + path = os.path.join(AppLocation.get_section_data_path('themes'), + self.service_item.theme_data.theme_name) + service_item.add_from_command(path, + self.service_item.theme_data.background_filename, + ':/media/slidecontroller_multimedia.png') + self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True) self._hide_mouse() def footer(self, text): diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index cc8c7f55c..19221ace0 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -45,7 +45,7 @@ VIDEO_CSS = """ """ VIDEO_JS = """ - function show_video(state, path, volume, loop, variable_value){ + function show_video(state, path, volume, variable_value){ // Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent! var video = document.getElementById('video'); @@ -55,9 +55,6 @@ VIDEO_JS = """ switch(state){ case 'load': video.src = 'file:///' + path; - if(loop == true) { - video.loop = true; - } video.load(); break; case 'play': @@ -180,12 +177,8 @@ class WebkitPlayer(MediaPlayer): else: vol = 0 path = controller.media_info.file_info.absoluteFilePath() - if controller.media_info.is_background: - loop = 'true' - else: - loop = 'false' display.web_view.setVisible(True) - js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol), loop) + js = 'show_video("load", "{path}", {vol});'.format(path=path.replace('\\', '\\\\'), vol=str(vol)) display.frame.evaluateJavaScript(js) return True diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 82b489344..a59f94377 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1323,7 +1323,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa """ The theme may have changed in the settings dialog so make sure the theme combo box is in the correct state. """ - visible = self.renderer.theme_level == ThemeLevel.Global + visible = not self.renderer.theme_level == ThemeLevel.Global self.theme_label.setVisible(visible) self.theme_combo_box.setVisible(visible) diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index fc231a859..2ec733629 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -31,7 +31,7 @@ from openlp.core.common import Registry, RegistryProperties, UiStrings, translat from openlp.core.lib.theme import BackgroundType, BackgroundGradientType from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui import ThemeLayoutForm -from openlp.core.ui.lib.colorbutton import ColorButton +from openlp.core.ui.media.webkitplayer import VIDEO_EXT from .themewizard import Ui_ThemeWizard log = logging.getLogger(__name__) @@ -66,10 +66,13 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): self.gradient_combo_box.currentIndexChanged.connect(self.on_gradient_combo_box_current_index_changed) self.color_button.colorChanged.connect(self.on_color_changed) self.image_color_button.colorChanged.connect(self.on_image_color_changed) + self.video_color_button.colorChanged.connect(self.on_video_color_changed) self.gradient_start_button.colorChanged.connect(self.on_gradient_start_color_changed) self.gradient_end_button.colorChanged.connect(self.on_gradient_end_color_changed) self.image_browse_button.clicked.connect(self.on_image_browse_button_clicked) self.image_file_edit.editingFinished.connect(self.on_image_file_edit_editing_finished) + self.video_browse_button.clicked.connect(self.on_video_browse_button_clicked) + self.video_file_edit.editingFinished.connect(self.on_video_file_edit_editing_finished) self.main_color_button.colorChanged.connect(self.on_main_color_changed) self.outline_color_button.colorChanged.connect(self.on_outline_color_changed) self.shadow_color_button.colorChanged.connect(self.on_shadow_color_changed) @@ -307,6 +310,10 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): self.image_color_button.color = self.theme.background_border_color self.image_file_edit.setText(self.theme.background_filename) self.setField('background_type', 2) + elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): + self.video_color_button.color = self.theme.background_border_color + self.video_file_edit.setText(self.theme.background_filename) + self.setField('background_type', 4) elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent): self.setField('background_type', 3) if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal): @@ -384,10 +391,12 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): if self.update_theme_allowed: self.theme.background_type = BackgroundType.to_string(index) if self.theme.background_type != BackgroundType.to_string(BackgroundType.Image) and \ + self.theme.background_type != BackgroundType.to_string(BackgroundType.Video) and \ self.temp_background_filename == '': self.temp_background_filename = self.theme.background_filename self.theme.background_filename = '' - if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) and \ + if (self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or + self.theme.background_type != BackgroundType.to_string(BackgroundType.Video)) and \ self.temp_background_filename != '': self.theme.background_filename = self.temp_background_filename self.temp_background_filename = '' @@ -413,6 +422,12 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): """ self.theme.background_border_color = color + def on_video_color_changed(self, color): + """ + Background / Gradient 1 _color button pushed. + """ + self.theme.background_border_color = color + def on_gradient_start_color_changed(self, color): """ Gradient 2 _color button pushed. @@ -444,6 +459,28 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): """ self.theme.background_filename = str(self.image_file_edit.text()) + def on_video_browse_button_clicked(self): + """ + Background video button pushed. + """ + visible_formats = '(%s)' % '; '.join(VIDEO_EXT) + actual_formats = '(%s)' % ' '.join(VIDEO_EXT) + video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'), + visible=visible_formats, actual=actual_formats) + video_filter = '{video};;{ui} (*.*)'.format(video=video_filter, ui=UiStrings().AllFiles) + filename, filter_used = QtWidgets.QFileDialog.getOpenFileName( + self, translate('OpenLP.ThemeWizard', 'Select Video'), + self.video_file_edit.text(), video_filter) + if filename: + self.theme.background_filename = filename + self.set_background_page_values() + + def on_video_file_edit_editing_finished(self): + """ + Background video path edited + """ + self.theme.background_filename = str(self.image_file_edit.text()) + def on_main_color_changed(self, color): """ Set the main colour value @@ -519,7 +556,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): return save_from = None save_to = None - if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image): + if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \ + self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): filename = os.path.split(str(self.theme.background_filename))[1] save_to = os.path.join(self.path, self.theme.theme_name, filename) save_from = self.theme.background_filename diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 32975e9aa..14a4663f4 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -300,7 +300,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage """ save_to = None save_from = None - if theme_data.background_type == 'image': + if theme_data.background_type == 'image' or theme_data.background_type == 'video': save_to = os.path.join(self.path, new_theme_name, os.path.split(str(theme_data.background_filename))[1]) save_from = theme_data.background_filename theme_data.theme_name = new_theme_name @@ -318,7 +318,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage translate('OpenLP.ThemeManager', 'You must select a theme to edit.')): item = self.theme_list_widget.currentItem() theme = self.get_theme_data(item.data(QtCore.Qt.UserRole)) - if theme.background_type == 'image': + if theme.background_type == 'image' or theme.background_type == 'video': self.old_background_image = theme.background_filename self.theme_form.theme = theme self.theme_form.exec(True) diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index ab8854ef2..b546c1872 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -62,7 +62,7 @@ class Ui_ThemeWizard(object): self.background_label = QtWidgets.QLabel(self.background_page) self.background_label.setObjectName('background_label') self.background_combo_box = QtWidgets.QComboBox(self.background_page) - self.background_combo_box.addItems(['', '', '', '']) + self.background_combo_box.addItems(['', '', '', '', '']) self.background_combo_box.setObjectName('background_combo_box') self.background_type_layout.addRow(self.background_label, self.background_combo_box) self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer) @@ -135,6 +135,30 @@ class Ui_ThemeWizard(object): self.transparent_layout.setObjectName('Transparent_layout') self.background_stack.addWidget(self.transparent_widget) self.background_layout.addLayout(self.background_stack) + self.video_widget = QtWidgets.QWidget(self.background_page) + self.video_widget.setObjectName('video_widget') + self.video_layout = QtWidgets.QFormLayout(self.video_widget) + self.video_layout.setContentsMargins(0, 0, 0, 0) + self.video_layout.setObjectName('video_layout') + self.video_color_label = QtWidgets.QLabel(self.color_widget) + self.video_color_label.setObjectName('video_color_label') + self.video_color_button = ColorButton(self.color_widget) + self.video_color_button.setObjectName('video_color_button') + self.video_layout.addRow(self.video_color_label, self.video_color_button) + self.video_label = QtWidgets.QLabel(self.video_widget) + self.video_label.setObjectName('video_label') + self.video_file_layout = QtWidgets.QHBoxLayout() + self.video_file_layout.setObjectName('video_file_layout') + self.video_file_edit = QtWidgets.QLineEdit(self.video_widget) + self.video_file_edit.setObjectName('video_file_edit') + self.video_file_layout.addWidget(self.video_file_edit) + self.video_browse_button = QtWidgets.QToolButton(self.video_widget) + self.video_browse_button.setObjectName('video_browse_button') + self.video_browse_button.setIcon(build_icon(':/general/general_open.png')) + self.video_file_layout.addWidget(self.video_browse_button) + self.video_layout.addRow(self.video_label, self.video_file_layout) + self.video_layout.setItem(2, QtWidgets.QFormLayout.LabelRole, self.spacer) + self.background_stack.addWidget(self.video_widget) theme_wizard.addPage(self.background_page) # Main Area Page self.main_area_page = QtWidgets.QWizardPage() @@ -390,11 +414,10 @@ class Ui_ThemeWizard(object): self.background_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Set up your theme\'s background ' 'according to the parameters below.')) self.background_label.setText(translate('OpenLP.ThemeWizard', 'Background type:')) - self.background_combo_box.setItemText(BackgroundType.Solid, - translate('OpenLP.ThemeWizard', 'Solid color')) - self.background_combo_box.setItemText(BackgroundType.Gradient, - translate('OpenLP.ThemeWizard', 'Gradient')) + self.background_combo_box.setItemText(BackgroundType.Solid, translate('OpenLP.ThemeWizard', 'Solid color')) + self.background_combo_box.setItemText(BackgroundType.Gradient, translate('OpenLP.ThemeWizard', 'Gradient')) self.background_combo_box.setItemText(BackgroundType.Image, UiStrings().Image) + self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video) self.background_combo_box.setItemText(BackgroundType.Transparent, translate('OpenLP.ThemeWizard', 'Transparent')) self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:')) @@ -413,6 +436,8 @@ class Ui_ThemeWizard(object): translate('OpenLP.ThemeWizard', 'Bottom Left - Top Right')) self.image_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:')) self.image_label.setText('%s:' % UiStrings().Image) + self.video_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:')) + self.video_label.setText('%s:' % UiStrings().Video) self.main_area_page.setTitle(translate('OpenLP.ThemeWizard', 'Main Area Font Details')) self.main_area_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Define the font and display ' 'characteristics for the Display text')) diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index 221948acc..a82ac44cf 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -60,7 +60,7 @@ import webbrowser from PyQt5 import QtCore from lxml import etree, objectify -SERVER_URL = 'http://www.transifex.net/api/2/project/openlp/resource/openlp-24x/' +SERVER_URL = 'http://www.transifex.com/api/2/project/openlp/resource/openlp-26x/' IGNORED_PATHS = ['scripts'] IGNORED_FILES = ['setup.py'] @@ -270,7 +270,7 @@ def update_translations(): return else: os.chdir(os.path.abspath('..')) - run('pylupdate4 -verbose -noobsolete openlp.pro') + run('pylupdate5 -verbose -noobsolete openlp.pro') os.chdir(os.path.abspath('scripts')) diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index fc7ae4910..1eb92e93e 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -27,8 +27,9 @@ from unittest import TestCase, skipUnless from PyQt5 import QtCore from openlp.core.common import Registry, is_macosx, Settings -from openlp.core.lib import ScreenList +from openlp.core.lib import ScreenList, PluginManager from openlp.core.ui import MainDisplay +from openlp.core.ui.media import MediaController from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET from tests.helpers.testmixin import TestMixin @@ -223,3 +224,61 @@ class TestMainDisplay(TestCase, TestMixin): # THEN: setVisible should had not been called main_display.setVisible.assert_not_called() + + @patch(u'openlp.core.ui.maindisplay.Settings') + @patch(u'openlp.core.ui.maindisplay.build_html') + def build_html_no_video_test(self, MockedSettings, Mocked_build_html): + # GIVEN: Mocked display + display = MagicMock() + mocked_media_controller = MagicMock() + Registry.create() + Registry().register('media_controller', mocked_media_controller) + main_display = MainDisplay(display) + main_display.frame = MagicMock() + mocked_settings = MagicMock() + mocked_settings.value.return_value = False + MockedSettings.return_value = mocked_settings + main_display.shake_web_view = MagicMock() + service_item = MagicMock() + mocked_plugin = MagicMock() + display.plugin_manager = PluginManager() + display.plugin_manager.plugins = [mocked_plugin] + main_display.web_view = MagicMock() + + # WHEN: build_html is called with a normal service item and a non video theme. + main_display.build_html(service_item) + + # THEN: the following should had not been called + self.assertEquals(main_display.web_view.setHtml.call_count, 1, 'setHTML should be called once') + self.assertEquals(main_display.media_controller.video.call_count, 0, + 'Media Controller video should not have been called') + + @patch(u'openlp.core.ui.maindisplay.Settings') + @patch(u'openlp.core.ui.maindisplay.build_html') + def build_html_video_test(self, MockedSettings, Mocked_build_html): + # GIVEN: Mocked display + display = MagicMock() + mocked_media_controller = MagicMock() + Registry.create() + Registry().register('media_controller', mocked_media_controller) + main_display = MainDisplay(display) + main_display.frame = MagicMock() + mocked_settings = MagicMock() + mocked_settings.value.return_value = False + MockedSettings.return_value = mocked_settings + main_display.shake_web_view = MagicMock() + service_item = MagicMock() + service_item.theme_data = MagicMock() + service_item.theme_data.background_type = 'video' + mocked_plugin = MagicMock() + display.plugin_manager = PluginManager() + display.plugin_manager.plugins = [mocked_plugin] + main_display.web_view = MagicMock() + + # WHEN: build_html is called with a normal service item and a video theme. + main_display.build_html(service_item) + + # THEN: the following should had not been called + self.assertEquals(main_display.web_view.setHtml.call_count, 1, 'setHTML should be called once') + self.assertEquals(main_display.media_controller.video.call_count, 1, + 'Media Controller video should have been called once')