diff --git a/openlp/core/display/canvas.py b/openlp/core/display/canvas.py deleted file mode 100644 index 8e21838d9..000000000 --- a/openlp/core/display/canvas.py +++ /dev/null @@ -1,769 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 OpenLP Developers # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### -""" -The :mod:`canvas` module provides the functionality to display screens and play multimedia within OpenLP. - -Some of the code for this form is based on the examples at: - -* `http://www.steveheffernan.com/html5-video-player/demo-video-player.html`_ -* `http://html5demos.com/two-videos`_ -""" -import html -import json -import logging -import os - -from PyQt5 import QtCore, QtGui, QtMultimedia, QtWebChannel, QtWebEngineWidgets, QtWidgets - -from openlp.core.common import is_macosx, is_win -from openlp.core.common.applocation import AppLocation -from openlp.core.common.i18n import translate -from openlp.core.common.mixins import LogMixin, RegistryProperties -from openlp.core.common.path import path_to_str -from openlp.core.common.registry import Registry -from openlp.core.common.settings import Settings -from openlp.core.display.screens import ScreenList -from openlp.core.display.webengine import WebEngineView -from openlp.core.display.window import MediaWatcher -from openlp.core.lib import ImageSource, ServiceItem, build_html, expand_tags, image_to_byte -from openlp.core.lib.theme import BackgroundType -from openlp.core.ui import AlertLocation, DisplayControllerType, HideMode - - -if is_macosx(): - from ctypes import pythonapi, c_void_p, c_char_p, py_object - - from sip import voidptr - from objc import objc_object - from AppKit import NSMainMenuWindowLevel, NSWindowCollectionBehaviorManaged - -log = logging.getLogger(__name__) - -OPAQUE_STYLESHEET = """ -QWidget { - border: 0px; - margin: 0px; - padding: 0px; -} -QGraphicsView {} -""" -TRANSPARENT_STYLESHEET = """ -QWidget { - border: 0px; - margin: 0px; - padding: 0px; -} -QGraphicsView { - background: transparent; - border: 0px; -} -""" - - -class Canvas(QtWidgets.QGraphicsView): - """ - This is a general display screen class. Here the general display settings will done. It will be used as - specialized classes by Main Display and Preview display. - """ - def __init__(self, parent): - """ - Constructor - """ - self.is_live = False - if hasattr(parent, 'is_live') and parent.is_live: - self.is_live = True - if self.is_live: - self.parent = lambda: parent - super(Canvas, self).__init__() - self.controller = parent - self.screen = {} - - def setup(self): - """ - Set up and build the screen base - """ - self.setGeometry(self.screen['size']) - #self.web_view = QtWebKitWidgets.QWebView(self) - #self.web_view.setGeometry(0, 0, self.screen['size'].width(), self.screen['size'].height()) - #self.web_view.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True) - #palette = self.web_view.palette() - #palette.setBrush(QtGui.QPalette.Base, QtCore.Qt.transparent) - #self.web_view.page().setPalette(palette) - #self.web_view.setAttribute(QtCore.Qt.WA_OpaquePaintEvent, False) - #self.page = self.web_view.page() - #self.frame = self.page.mainFrame() - #if self.is_live and log.getEffectiveLevel() == logging.DEBUG: - # self.web_view.settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True) - - - self.webview = WebEngineView(self) - self.webview.setGeometry(0, 0, self.screen['size'].width(), self.screen['size'].height()) - self.webview.settings().setAttribute(QtWebEngineWidgets.QWebEngineSettings.PluginsEnabled, True) - self.layout = QtWidgets.QVBoxLayout(self) - self.layout.setContentsMargins(0, 0, 0, 0) - self.layout.addWidget(self.webview) - self.webview.loadFinished.connect(self.after_loaded) - self.set_url(QtCore.QUrl('file://' + os.getcwd() + '/display.html')) - self.media_watcher = MediaWatcher(self) - self.channel = QtWebChannel.QWebChannel(self) - self.channel.registerObject('mediaWatcher', self.media_watcher) - self.webview.page().setWebChannel(self.channel) - self.webview.loadFinished.connect(self.is_web_loaded) - - self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - - def resizeEvent(self, event): - """ - React to resizing of this display - - :param event: The event to be handled - """ - if hasattr(self, 'web_view'): - self.web_view.setGeometry(0, 0, self.width(), self.height()) - - def is_web_loaded(self, field=None): - """ - Called by webView event to show display is fully loaded - """ - self.web_loaded = True - - def set_url(self, url): - """ - Set the URL of the webview - """ - if not isinstance(url, QtCore.QUrl): - url = QtCore.QUrl(url) - self.webview.setUrl(url) - - def set_html(self, html): - """ - Set the html - """ - self.webview.setHtml(html) - - def after_loaded(self, state): - """ - Add stuff after page initialisation - """ - self.run_javascript('Display.init();') - - def add_script_source(self, fname, source): - """ - Add a script of source code - """ - js = QtWebEngineWidgets.QWebEngineScript() - js.setSourceCode(source) - js.setName(fname) - js.setWorldId(QtWebEngineWidgets.QWebEngineScript.MainWorld) - self.webview.page().scripts().insert(js) - - def add_script(self, fname): - """ - Add a script to the page - """ - js_file = QtCore.QFile(fname) - if not js_file.open(QtCore.QIODevice.ReadOnly): - log.warning('Could not open %s: %s', fname, js_file.errorString()) - return - self.add_script_source(os.path.basename(fname), str(bytes(js_file.readAll()), 'utf-8')) - - def run_javascript(self, script, is_sync=False): - """ - Run some Javascript in the WebView - - :param script: The script to run, a string - :param is_sync: Run the script synchronously. Defaults to False - """ - if not is_sync: - self.webview.page().runJavaScript(script) - else: - self.__script_done = False - self.__script_result = None - - def handle_result(result): - """ - Handle the result from the asynchronous call - """ - self.__script_done = True - self.__script_result = result - - self.webview.page().runJavaScript(script, handle_result) - while not self.__script_done: - # TODO: Figure out how to break out of a potentially infinite loop - QtWidgets.QApplication.instance().processEvents() - return self.__script_result - - -class MainCanvas(OpenLPMixin, Canvas, RegistryProperties): - """ - This is the display screen as a specialized class from the Display class - """ - def __init__(self, parent): - """ - Constructor - """ - super(MainCanvas, self).__init__(parent) - self.screens = ScreenList() - self.rebuild_css = False - self.hide_mode = None - self.override = {} - self.retranslate_ui() - self.media_object = None - if self.is_live: - self.audio_player = AudioPlayer(self) - else: - self.audio_player = None - self.first_time = True - self.web_loaded = True - self.setStyleSheet(OPAQUE_STYLESHEET) - window_flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint - if Settings().value('advanced/x11 bypass wm'): - window_flags |= QtCore.Qt.X11BypassWindowManagerHint - # TODO: The following combination of window_flags works correctly - # on Mac OS X. For next OpenLP version we should test it on other - # platforms. For OpenLP 2.0 keep it only for OS X to not cause any - # regressions on other platforms. - if is_macosx(): - window_flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Window | QtCore.Qt.NoDropShadowWindowHint - self.setWindowFlags(window_flags) - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - self.set_transparency(False) - if is_macosx(): - if self.is_live: - # Get a pointer to the underlying NSView - try: - nsview_pointer = self.winId().ascapsule() - except: - nsview_pointer = voidptr(self.winId()).ascapsule() - # Set PyCapsule name so pyobjc will accept it - pythonapi.PyCapsule_SetName.restype = c_void_p - pythonapi.PyCapsule_SetName.argtypes = [py_object, c_char_p] - pythonapi.PyCapsule_SetName(nsview_pointer, c_char_p(b"objc.__object__")) - # Covert the NSView pointer into a pyobjc NSView object - self.pyobjc_nsview = objc_object(cobject=nsview_pointer) - # Set the window level so that the MainCanvas is above the menu bar and dock - self.pyobjc_nsview.window().setLevel_(NSMainMenuWindowLevel + 2) - # Set the collection behavior so the window is visible when Mission Control is activated - self.pyobjc_nsview.window().setCollectionBehavior_(NSWindowCollectionBehaviorManaged) - if self.screens.current['primary']: - # Connect focusWindowChanged signal so we can change the window level when the display is not in - # focus on the primary screen - self.application.focusWindowChanged.connect(self.change_window_level) - if self.is_live: - Registry().register_function('live_display_hide', self.hide_display) - Registry().register_function('live_display_show', self.show_display) - Registry().register_function('update_display_css', self.css_changed) - self.close_display = False - - def closeEvent(self, event): - """ - Catch the close event, and check that the close event is triggered by OpenLP closing the display. - On Windows this event can be triggered by pressing ALT+F4, which we want to ignore. - - :param event: The triggered event - """ - if self.close_display: - super().closeEvent(event) - else: - event.ignore() - - def close(self): - """ - Remove registered function on close. - """ - if self.is_live: - if is_macosx(): - # Block signals so signal we are disconnecting can't get called while we disconnect it - self.blockSignals(True) - if self.screens.current['primary']: - self.application.focusWindowChanged.disconnect() - self.blockSignals(False) - Registry().remove_function('live_display_hide', self.hide_display) - Registry().remove_function('live_display_show', self.show_display) - Registry().remove_function('update_display_css', self.css_changed) - self.close_display = True - super().close() - - def set_transparency(self, enabled): - """ - Set the transparency of the window - - :param enabled: Is transparency enabled - """ - if enabled: - self.setAutoFillBackground(False) - self.setStyleSheet(TRANSPARENT_STYLESHEET) - else: - self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False) - self.setStyleSheet(OPAQUE_STYLESHEET) - self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled) - self.repaint() - - def css_changed(self): - """ - We need to rebuild the CSS on the live display. - """ - for plugin in self.plugin_manager.plugins: - plugin.refresh_css(self.frame) - - def retranslate_ui(self): - """ - Setup the interface translation strings. - """ - self.setWindowTitle(translate('OpenLP.MainCanvas', 'OpenLP Display')) - - def setup(self): - """ - Set up and build the output screen - """ - self.log_debug('Start MainCanvas setup (live = {islive})'.format(islive=self.is_live)) - self.screen = self.screens.current - self.setVisible(False) - Canvas.setup(self) - if self.is_live: - # Build the initial frame. - background_color = QtGui.QColor() - background_color.setNamedColor(Settings().value('core/logo background color')) - if not background_color.isValid(): - background_color = QtCore.Qt.white - image_file = path_to_str(Settings().value('core/logo file')) - splash_image = QtGui.QImage(image_file) - self.initial_fame = QtGui.QImage( - self.screen['size'].width(), - self.screen['size'].height(), - QtGui.QImage.Format_ARGB32_Premultiplied) - painter_image = QtGui.QPainter() - painter_image.begin(self.initial_fame) - painter_image.fillRect(self.initial_fame.rect(), background_color) - painter_image.drawImage( - (self.screen['size'].width() - splash_image.width()) // 2, - (self.screen['size'].height() - splash_image.height()) // 2, - splash_image) - service_item = ServiceItem() - service_item.bg_image_bytes = image_to_byte(self.initial_fame) - self.webview.setHtml(build_html(service_item, self.screen, self.is_live, None, - plugins=self.plugin_manager.plugins)) - self._hide_mouse() - - def text(self, slide, animate=True): - """ - Add the slide text from slideController - - :param slide: The slide text to be displayed - :param animate: Perform transitions if applicable when setting the text - """ - # Wait for the webview to update before displaying text. - while not self.web_loaded: - self.application.process_events() - self.setGeometry(self.screen['size']) - json_verses = json.dumps(slide) - self.run_javascript('Display.setTextSlides({verses});'.format(verses=json_verses)) - #if animate: - # # NOTE: Verify this works with ''.format() - # _text = slide.replace('\\', '\\\\').replace('\"', '\\\"') - # self.frame.runJavaScript('show_text("{text}")'.format(text=_text)) - #else: - # # This exists for https://bugs.launchpad.net/openlp/+bug/1016843 - # # For unknown reasons if evaluateJavaScript is called - # # from the themewizard, then it causes a crash on - # # Windows if there are many items in the service to re-render. - # # Setting the div elements direct seems to solve the issue - # self.frame.findFirstElement("#lyricsmain").setInnerXml(slide) - - def alert(self, text, location): - """ - Display an alert. - - :param text: The text to be displayed. - :param location: Where on the screen is the text to be displayed - """ - # First we convert <>& marks to html variants, then apply - # formattingtags, finally we double all backslashes for JavaScript. - text_prepared = expand_tags(html.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"') - if self.height() != self.screen['size'].height() or not self.isVisible(): - shrink = True - js = 'show_alert("{text}", "{top}")'.format(text=text_prepared, top='top') - else: - shrink = False - js = 'show_alert("{text}", "")'.format(text=text_prepared) - height = self.run_javascript(js) - if shrink: - if text: - alert_height = int(height) - self.resize(self.width(), alert_height) - self.setVisible(True) - if location == AlertLocation.Middle: - self.move(self.screen['size'].left(), (self.screen['size'].height() - alert_height) // 2) - elif location == AlertLocation.Bottom: - self.move(self.screen['size'].left(), self.screen['size'].height() - alert_height) - else: - self.setVisible(False) - self.setGeometry(self.screen['size']) - - def direct_image(self, path, background): - """ - API for replacement backgrounds so Images are added directly to cache. - - :param path: Path to Image - :param background: The background color - """ - self.image_manager.add_image(path, ImageSource.ImagePlugin, background) - if not hasattr(self, 'service_item'): - return False - self.override['image'] = path - self.override['theme'] = path_to_str(self.service_item.theme_data.background_filename) - self.image(path) - # Update the preview frame. - if self.is_live: - self.live_controller.update_preview() - return True - - def image(self, path): - """ - Add an image as the background. The image has already been added to the - cache. - - :param path: The path to the image to be displayed. **Note**, the path is only passed to identify the image. - If the image has changed it has to be re-added to the image manager. - """ - image = self.image_manager.get_image_bytes(path, ImageSource.ImagePlugin) - self.controller.media_controller.media_reset(self.controller) - self.display_image(image) - - def display_image(self, image): - """ - Display an image, as is. - - :param image: The image to be displayed - """ - self.setGeometry(self.screen['size']) - #if image: - # self.set_im - # js = 'show_image("data:image/png;base64,{image}");'.format(image=image) - #else: - # js = 'show_image("");' - #self.frame.evaluateJavaScript(js) - if not image['file'].startswith('file://'): - image['file'] = 'file://' + image['file'] - json_images = json.dumps(images) - self.run_javascript('Display.setImageSlides({images});'.format(images=json_images)) - - - def reset_image(self): - """ - Reset the background image to the service item image. Used after the image plugin has changed the background. - """ - if hasattr(self, 'service_item'): - self.display_image(self.service_item.bg_image_bytes) - else: - self.display_image(None) - # Update the preview frame. - if self.is_live: - self.live_controller.update_preview() - # clear the cache - self.override = {} - - def preview(self): - """ - Generates a preview of the image displayed. - """ - was_visible = self.isVisible() - self.application.process_events() - # We must have a service item to preview. - if self.is_live and hasattr(self, 'service_item'): - # Wait for the fade to finish before geting the preview. - # Important otherwise preview will have incorrect text if at all! - if self.service_item.theme_data and self.service_item.theme_data.display_slide_transition: - while not self.run_javascript('show_text_completed()'): - self.application.process_events() - # Wait for the webview to update before getting the preview. - # Important otherwise first preview will miss the background ! - while not self.web_loaded: - self.application.process_events() - # if was hidden keep it hidden - if self.is_live: - if self.hide_mode: - self.hide_display(self.hide_mode) - # Only continue if the visibility wasn't changed during method call. - elif was_visible == self.isVisible(): - # Single screen active - if self.screens.display_count == 1: - # Only make visible if setting enabled. - if Settings().value('core/display on monitor'): - self.setVisible(True) - else: - self.setVisible(True) - return self.grab() - - def build_html(self, service_item, image_path=''): - """ - Store the service_item and build the new HTML from it. Add the HTML to the display - - :param service_item: The Service item to be used - :param image_path: Where the image resides. - """ - self.web_loaded = False - self.initial_fame = None - self.service_item = service_item - background = None - # We have an image override so keep the image till the theme changes. - if self.override: - # We have an video override so allow it to be stopped. - if 'video' in self.override: - Registry().execute('video_background_replaced') - self.override = {} - # We have a different theme. - elif self.override['theme'] != path_to_str(service_item.theme_data.background_filename): - Registry().execute('live_theme_changed') - self.override = {} - else: - # replace the background - 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)) - 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( - path_to_str(self.service_item.theme_data.background_filename), ImageSource.Theme) - if image_path: - image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin) - created_html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes, - plugins=self.plugin_manager.plugins) - self.webview.setHtml(created_html) - if service_item.foot_text: - self.footer(service_item.foot_text) - # if was hidden keep it hidden - if self.hide_mode and self.is_live and not service_item.is_media(): - if Settings().value('core/auto unblank'): - 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(str(AppLocation.get_section_data_path('themes')), - self.service_item.theme_data.theme_name) - service_item.add_from_command(path, - path_to_str(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): - """ - Display the Footer - - :param text: footer text to be displayed - """ - js = 'show_footer(\'' + text.replace('\\', '\\\\').replace('\'', '\\\'') + '\')' - self.run_javascript(js) - - def hide_display(self, mode=HideMode.Screen): - """ - Hide the display by making all layers transparent Store the images so they can be replaced when required - - :param mode: How the screen is to be hidden - """ - self.log_debug('hide_display mode = {mode:d}'.format(mode=mode)) - if self.screens.display_count == 1: - # Only make visible if setting enabled. - if not Settings().value('core/display on monitor'): - return - if mode == HideMode.Screen: - self.run_javascript('show_blank("desktop");') - self.setVisible(False) - elif mode == HideMode.Blank or self.initial_fame: - self.run_javascript('show_blank("black");') - else: - self.run_javascript('show_blank("theme");') - if mode != HideMode.Screen: - if self.isHidden(): - self.setVisible(True) - self.webview.setVisible(True) - self.hide_mode = mode - - def show_display(self): - """ - Show the stored layers so the screen reappears as it was originally. - Make the stored images None to release memory. - """ - if self.screens.display_count == 1: - # Only make visible if setting enabled. - if not Settings().value('core/display on monitor'): - return - self.run_javascript('show_blank("show");') - # Check if setting for hiding logo on startup is enabled. - # If it is, display should remain hidden, otherwise logo is shown. (from def setup) - if self.isHidden() and not Settings().value('core/logo hide on startup'): - self.setVisible(True) - self.hide_mode = None - # Trigger actions when display is active again. - if self.is_live: - Registry().execute('live_display_active') - - def _hide_mouse(self): - """ - Hide mouse cursor when moved over display. - """ - if Settings().value('advanced/hide mouse'): - self.setCursor(QtCore.Qt.BlankCursor) - self.run_javascript('document.body.style.cursor = "none"') - else: - self.setCursor(QtCore.Qt.ArrowCursor) - self.run_javascript('document.body.style.cursor = "auto"') - - def change_window_level(self, window): - """ - Changes the display window level on Mac OS X so that the main window can be brought into focus but still allow - the main display to be above the menu bar and dock when it in focus. - - :param window: Window from our application that focus changed to or None if outside our application - """ - if is_macosx(): - if window: - # Get different window ids' as int's - try: - window_id = window.winId().__int__() - main_window_id = self.main_window.winId().__int__() - self_id = self.winId().__int__() - except: - return - # If the passed window has the same id as our window make sure the display has the proper level and - # collection behavior. - if window_id == self_id: - self.pyobjc_nsview.window().setLevel_(NSMainMenuWindowLevel + 2) - self.pyobjc_nsview.window().setCollectionBehavior_(NSWindowCollectionBehaviorManaged) - # Else set the displays window level back to normal since we are trying to focus a window other than - # the display. - else: - self.pyobjc_nsview.window().setLevel_(0) - self.pyobjc_nsview.window().setCollectionBehavior_(NSWindowCollectionBehaviorManaged) - # If we are trying to focus the main window raise it now to complete the focus change. - if window_id == main_window_id: - self.main_window.raise_() - - -class AudioPlayer(LogMixin, QtCore.QObject): - """ - This Class will play audio only allowing components to work with a soundtrack independent of the user interface. - """ - position_changed = QtCore.pyqtSignal(int) - - def __init__(self, parent): - """ - The constructor for the display form. - - :param parent: The parent widget. - """ - super(AudioPlayer, self).__init__(parent) - self.player = QtMultimedia.QMediaPlayer() - self.playlist = QtMultimedia.QMediaPlaylist(self.player) - self.volume_slider = None - self.player.setPlaylist(self.playlist) - self.player.positionChanged.connect(self._on_position_changed) - - def __del__(self): - """ - Shutting down so clean up connections - """ - self.stop() - - def _on_position_changed(self, position): - """ - Emit a signal when the position of the media player updates - """ - self.position_changed.emit(position) - - def set_volume_slider(self, slider): - """ - Connect the volume slider to the media player - :param slider: - """ - self.volume_slider = slider - self.volume_slider.setMinimum(0) - self.volume_slider.setMaximum(100) - self.volume_slider.setValue(self.player.volume()) - self.volume_slider.valueChanged.connect(self.set_volume) - - def set_volume(self, volume): - """ - Set the volume of the media player - - :param volume: - """ - self.player.setVolume(volume) - - def reset(self): - """ - Reset the audio player, clearing the playlist and the queue. - """ - self.stop() - self.playlist.clear() - - def play(self): - """ - We want to play the file so start it - """ - self.player.play() - - def pause(self): - """ - Pause the Audio - """ - self.player.pause() - - def stop(self): - """ - Stop the Audio and clean up - """ - self.player.stop() - - def add_to_playlist(self, file_names): - """ - Add another file to the playlist. - - :param file_names: A list with files to be added to the playlist. - """ - if not isinstance(file_names, list): - file_names = [file_names] - for file_name in file_names: - self.playlist.addMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(str(file_name)))) - - def next(self): - """ - Skip forward to the next track in the list - """ - self.playlist.next() - - def go_to(self, index): - """ - Go to a particular track in the list - - :param index: The track to go to - """ - self.playlist.setCurrentIndex(index) - if self.player.state() == QtMultimedia.QMediaPlayer.PlayingState: - self.player.play() diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 868cca352..a035b786e 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -202,60 +202,6 @@ class ServiceItem(RegistryProperties): self._create_slides() return self._display_slides - # def render(self, provides_own_theme_data=False): - # """ - # The render method is what generates the frames for the screen and obtains the display information from the - # renderer. At this point all slides are built for the given display size. - # - # :param provides_own_theme_data: This switch disables the usage of the item's theme. However, this is - # disabled by default. If this is used, it has to be taken care, that - # the renderer knows the correct theme data. However, this is needed - # for the theme manager. - # """ - # log.debug('Render called') - # self._display_frames = [] - # self.bg_image_bytes = None - # # if not provides_own_theme_data: - # # self.renderer.set_item_theme(self.theme) - # # self.theme_data, self.main, self.footer = self.renderer.pre_render() - # if self.service_item_type == ServiceItemType.Text: - # can_render_chords = hasattr(self, 'name') and self.name == 'songs' and Settings().value( - # 'songs/enable chords') - # log.debug('Formatting slides: {title}'.format(title=self.title)) - # # Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to - # # the dict instead of rendering them again. - # previous_pages = {} - # for slide in self._raw_frames: - # verse_tag = slide['verseTag'] - # if verse_tag in previous_pages and previous_pages[verse_tag][0] == slide['raw_slide']: - # pages = previous_pages[verse_tag][1] - # else: - # # pages = self.renderer.format_slide(slide['raw_slide'], self) - # previous_pages[verse_tag] = (slide['raw_slide'], pages) - # for page in pages: - # page = page.replace('
', '{br}') - # html_data = render_tags(page.rstrip(), can_render_chords) - # new_frame = { - # 'title': remove_tags(page), - # 'text': remove_tags(page.rstrip(), can_render_chords), - # 'chords_text': render_chords(remove_tags(page.rstrip(), False)), - # 'html': html_data.replace('&nbsp;', ' '), - # 'printing_html': render_tags(html.escape(page.rstrip()), can_render_chords, is_printing=True), - # 'verseTag': verse_tag, - # } - # self._display_frames.append(new_frame) - # elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command: - # pass - # else: - # log.error('Invalid value renderer: {item}'.format(item=self.service_item_type)) - # self.title = remove_tags(self.title) - # # The footer should never be None, but to be compatible with a few - # # nightly builds between 1.9.4 and 1.9.5, we have to correct this to - # # avoid tracebacks. - # if self.raw_footer is None: - # self.raw_footer = [] - # self.foot_text = '
'.join([_f for _f in self.raw_footer if _f]) - def add_from_image(self, filename, title, background=None, thumbnail=None): """ Add an image slide to the service item. @@ -313,8 +259,6 @@ class ServiceItem(RegistryProperties): file_location_hash = md5_hash(file_location.encode('utf-8')) image = os.path.join(str(AppLocation.get_section_data_path(self.name)), 'thumbnails', file_location_hash, ntpath.basename(image)) - #self.slides.append({'title': file_name, 'image': image, 'path': path, - # 'display_title': display_title, 'notes': notes}) self.slides.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title, 'notes': notes, 'thumbnail' : image}) diff --git a/openlp/core/version.py b/openlp/core/version.py index 3a405c7dd..5ca2b6940 100644 --- a/openlp/core/version.py +++ b/openlp/core/version.py @@ -52,7 +52,6 @@ LIBRARIES = OrderedDict([ ('Chardet', ('chardet',)), ('PyEnchant', ('enchant',)), ('Mako', ('mako',)), - ('pyICU', ('icu', 'VERSION')), ('VLC', ('openlp.core.ui.media.vlcplayer', 'VERSION')), ]) diff --git a/tests/functional/openlp_core/api/endpoint/test_controller.py b/tests/functional/openlp_core/api/endpoint/test_controller.py index 242699184..ad9359739 100644 --- a/tests/functional/openlp_core/api/endpoint/test_controller.py +++ b/tests/functional/openlp_core/api/endpoint/test_controller.py @@ -15,10 +15,11 @@ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # # more details. # # # - - +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +import sys from unittest import TestCase # You should have received a copy of the GNU General Public License along # # with this program; if not, write to the Free Software Foundation, Inc., 59 # @@ -26,9 +27,12 @@ from unittest.mock import MagicMock from PyQt5 import QtCore +# Mock QtWebEngineWidgets +sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock() + from openlp.core.api.endpoint.controller import controller_direction, controller_text from openlp.core.common.registry import Registry -from openlp.core.display.renderer import Renderer +from openlp.core.display.render import Renderer from openlp.core.display.screens import ScreenList from openlp.core.lib.serviceitem import ServiceItem from tests.utils import convert_file_service_item diff --git a/tests/functional/openlp_core/test_app.py b/tests/functional/openlp_core/test_app.py index 5b0a0d885..7a27b2296 100644 --- a/tests/functional/openlp_core/test_app.py +++ b/tests/functional/openlp_core/test_app.py @@ -25,6 +25,9 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtWidgets +# Mock QtWebEngineWidgets +sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock() + from openlp.core.app import OpenLP, parse_options from openlp.core.common.settings import Settings from tests.utils.constants import RESOURCE_PATH diff --git a/tests/interfaces/openlp_core/common/test_utils.py b/tests/interfaces/openlp_core/common/test_utils.py index b3be13a0d..b697f7f38 100644 --- a/tests/interfaces/openlp_core/common/test_utils.py +++ b/tests/interfaces/openlp_core/common/test_utils.py @@ -26,6 +26,7 @@ from unittest import TestCase from openlp.core.common import is_not_image_file from tests.utils.constants import RESOURCE_PATH +from tests.helpers.testmixin import TestMixin class TestUtils(TestCase, TestMixin):