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(' ', ' '),
- # '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):