openlp/openlp/core/ui/maindisplay.py

661 lines
26 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
2012-12-29 13:15:42 +00:00
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
2012-12-29 20:56:56 +00:00
# Copyright (c) 2008-2013 Raoul Snyman #
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
2012-11-11 21:16:14 +00:00
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
2012-10-21 13:16:22 +00:00
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
2010-12-06 19:25:44 +00:00
"""
2013-02-01 20:36:27 +00:00
The :mod:`maindisplay` 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`_
2010-12-06 19:25:44 +00:00
"""
2013-04-24 19:05:34 +00:00
from __future__ import division
import cgi
import logging
2013-06-23 19:51:17 +00:00
import os
import sys
2011-07-10 21:43:07 +00:00
from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
from PyQt4.phonon import Phonon
2013-02-07 08:42:17 +00:00
from openlp.core.lib import ServiceItem, Settings, ImageSource, Registry, build_html, expand_tags, \
2013-02-05 08:05:28 +00:00
image_to_byte, translate
2012-03-05 19:40:16 +00:00
from openlp.core.lib.theme import BackgroundType
2011-01-05 16:57:49 +00:00
from openlp.core.lib import ScreenList
from openlp.core.ui import HideMode, AlertLocation
log = logging.getLogger(__name__)
2011-06-06 19:12:14 +00:00
class Display(QtGui.QGraphicsView):
"""
2011-12-10 14:34:28 +00:00
This is a general display screen class. Here the general display settings
2012-10-15 18:38:58 +00:00
will done. It will be used as specialized classes by Main Display and
Preview display.
"""
2011-10-24 20:04:29 +00:00
def __init__(self, parent, live, controller):
2013-02-01 20:36:27 +00:00
"""
Constructor
"""
if live:
2013-07-18 19:44:28 +00:00
super(Display, self).__init__()
2011-10-31 07:08:51 +00:00
# Overwrite the parent() method.
2011-10-15 07:52:25 +00:00
self.parent = lambda: parent
else:
2013-07-18 19:42:07 +00:00
super(Display, self).__init__(parent)
self.is_live = live
2011-06-06 19:12:14 +00:00
self.controller = controller
2011-11-11 16:45:25 +00:00
self.screen = {}
# FIXME: On Mac OS X (tested on 10.7) the display screen is corrupt with
# OpenGL. Only white blank screen is shown on the 2nd monitor all the
# time. We need to investigate more how to use OpenGL properly on Mac OS
# X.
if sys.platform != 'darwin':
self.setViewport(QtOpenGL.QGLWidget())
2011-05-10 20:39:17 +00:00
def setup(self):
"""
2011-11-02 20:27:53 +00:00
Set up and build the screen base
2011-05-10 20:39:17 +00:00
"""
log.debug(u'Start Display base setup (live = %s)' % self.is_live)
2011-11-02 20:27:53 +00:00
self.setGeometry(self.screen[u'size'])
log.debug(u'Setup webView')
2013-02-20 19:31:51 +00:00
self.web_view = QtWebKit.QWebView(self)
self.web_view.setGeometry(0, 0, self.screen[u'size'].width(), self.screen[u'size'].height())
self.web_view.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)
palette = self.web_view.palette()
2012-01-04 17:19:49 +00:00
palette.setBrush(QtGui.QPalette.Base, QtCore.Qt.transparent)
2013-02-20 19:31:51 +00:00
self.web_view.page().setPalette(palette)
self.web_view.setAttribute(QtCore.Qt.WA_OpaquePaintEvent, False)
self.page = self.web_view.page()
2011-05-10 20:39:17 +00:00
self.frame = self.page.mainFrame()
if self.is_live and log.getEffectiveLevel() == logging.DEBUG:
2013-02-20 19:31:51 +00:00
self.web_view.settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
self.web_view.loadFinished.connect(self.is_web_loaded)
2011-11-02 20:27:53 +00:00
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
2013-02-20 19:31:51 +00:00
self.frame.setScrollBarPolicy(QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff)
self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff)
2011-01-17 15:38:10 +00:00
2012-06-09 15:46:01 +00:00
def resizeEvent(self, event):
2013-02-01 20:36:27 +00:00
"""
React to resizing of this display
"""
2013-02-20 19:31:51 +00:00
self.web_view.setGeometry(0, 0, self.width(), self.height())
2011-07-18 21:25:10 +00:00
2013-02-20 19:31:51 +00:00
def is_web_loaded(self):
2011-11-30 17:06:57 +00:00
"""
Called by webView event to show display is fully loaded
"""
2013-02-20 19:31:51 +00:00
log.debug(u'is web loaded')
2013-03-07 08:14:26 +00:00
self.web_loaded = True
2011-11-30 17:06:57 +00:00
2011-11-02 20:27:53 +00:00
2011-07-20 20:47:29 +00:00
class MainDisplay(Display):
"""
2011-11-02 20:27:53 +00:00
This is the display screen as a specialized class from the Display class
"""
2013-01-22 21:09:43 +00:00
def __init__(self, parent, live, controller):
2013-02-01 20:36:27 +00:00
"""
Constructor
"""
2013-07-18 19:42:07 +00:00
super(MainDisplay, self).__init__(parent, live, controller)
self.screens = ScreenList()
2013-02-20 19:31:51 +00:00
self.rebuild_css = False
self.hide_mode = None
self.override = {}
self.retranslateUi()
2013-02-20 19:31:51 +00:00
self.media_object = None
2011-08-30 20:29:29 +00:00
if live:
self.audio_player = AudioPlayer(self)
2011-08-30 20:29:29 +00:00
else:
self.audio_player = None
self.first_time = True
self.web_loaded = True
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
window_flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint
if Settings().value(u'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 sys.platform == 'darwin':
window_flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Window
# For primary screen ensure it stays above the OS X dock
# and menu bar
if self.screens.current[u'primary']:
self.setWindowState(QtCore.Qt.WindowFullScreen)
self.setWindowFlags(window_flags)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
2013-02-20 19:31:51 +00:00
self.set_transparency(False)
if self.is_live:
2013-02-07 11:33:47 +00:00
Registry().register_function(u'live_display_hide', self.hide_display)
Registry().register_function(u'live_display_show', self.show_display)
Registry().register_function(u'update_display_css', self.css_changed)
2011-10-11 19:54:18 +00:00
2013-02-20 19:31:51 +00:00
def set_transparency(self, enabled):
2013-02-01 20:36:27 +00:00
"""
Set the transparency of the window
"""
2012-01-19 19:13:19 +00:00
if enabled:
self.setAutoFillBackground(False)
else:
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled)
self.repaint()
2013-02-07 11:33:47 +00:00
def css_changed(self):
2011-10-15 06:32:01 +00:00
"""
2013-03-10 20:19:42 +00:00
We need to rebuild the CSS on the live display.
2011-10-15 06:32:01 +00:00
"""
2013-03-10 20:19:42 +00:00
for plugin in self.plugin_manager.plugins:
2013-03-25 07:13:40 +00:00
plugin.refresh_css(self.frame)
2010-07-11 07:45:49 +00:00
def retranslateUi(self):
"""
Setup the interface translation strings.
"""
self.setWindowTitle(translate('OpenLP.MainDisplay', 'OpenLP Display'))
2010-07-23 05:05:34 +00:00
def setup(self):
2010-08-28 15:49:51 +00:00
"""
Set up and build the output screen
"""
log.debug(u'Start MainDisplay setup (live = %s)' % self.is_live)
2010-07-23 05:05:34 +00:00
self.screen = self.screens.current
self.setVisible(False)
2011-11-02 20:27:53 +00:00
Display.setup(self)
if self.is_live:
2010-09-04 07:48:58 +00:00
# Build the initial frame.
background_color = QtGui.QColor()
background_color.setNamedColor(Settings().value(u'advanced/default color'))
if not background_color.isValid():
background_color = QtCore.Qt.white
image_file = Settings().value(u'advanced/default image')
splash_image = QtGui.QImage(image_file)
self.initial_fame = QtGui.QImage(
self.screen[u'size'].width(),
self.screen[u'size'].height(),
2010-07-23 05:05:34 +00:00
QtGui.QImage.Format_ARGB32_Premultiplied)
painter_image = QtGui.QPainter()
painter_image.begin(self.initial_fame)
painter_image.fillRect(self.initial_fame.rect(), background_color)
2010-07-23 05:05:34 +00:00
painter_image.drawImage(
2013-04-24 19:05:34 +00:00
(self.screen[u'size'].width() - splash_image.width()) // 2,
(self.screen[u'size'].height() - splash_image.height()) // 2,
splash_image)
2013-03-06 22:23:01 +00:00
service_item = ServiceItem()
service_item.bg_image_bytes = image_to_byte(self.initial_fame)
2013-03-06 22:23:01 +00:00
self.web_view.setHtml(build_html(service_item, self.screen, self.is_live, None,
2013-01-22 21:09:43 +00:00
plugins=self.plugin_manager.plugins))
self.__hideMouse()
log.debug(u'Finished MainDisplay setup')
2010-07-23 05:05:34 +00:00
2012-08-27 19:04:53 +00:00
def text(self, slide, animate=True):
2010-07-24 16:55:06 +00:00
"""
Add the slide text from slideController
``slide``
2010-07-24 16:55:06 +00:00
The slide text to be displayed
2012-08-27 19:04:53 +00:00
``animate``
2012-08-27 19:12:06 +00:00
Perform transitions if applicable when setting the text
2010-07-24 16:55:06 +00:00
"""
2010-10-15 15:33:06 +00:00
log.debug(u'text to display')
# Wait for the webview to update before displaying text.
while not self.web_loaded:
2013-02-03 19:23:12 +00:00
self.application.process_events()
self.setGeometry(self.screen[u'size'])
2012-08-27 19:04:53 +00:00
if animate:
2012-12-29 13:15:42 +00:00
self.frame.evaluateJavaScript(u'show_text("%s")' % slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
2012-08-27 19:04:53 +00:00
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.
2012-08-27 20:00:09 +00:00
# Setting the div elements direct seems to solve the issue
2012-08-27 19:04:53 +00:00
self.frame.findFirstElement("#lyricsmain").setInnerXml(slide)
self.frame.findFirstElement("#lyricsoutline").setInnerXml(slide)
self.frame.findFirstElement("#lyricsshadow").setInnerXml(slide)
2010-07-11 07:45:49 +00:00
def alert(self, text, location):
2010-07-24 16:55:06 +00:00
"""
2011-10-16 14:53:39 +00:00
Display an alert.
2010-07-24 16:55:06 +00:00
2011-10-16 14:53:39 +00:00
``text``
The text to be displayed.
2010-07-24 16:55:06 +00:00
"""
2010-10-15 15:33:06 +00:00
log.debug(u'alert to display')
2012-01-02 23:43:59 +00:00
# First we convert <>& marks to html variants, then apply
# formattingtags, finally we double all backslashes for JavaScript.
2012-12-29 13:15:42 +00:00
text_prepared = expand_tags(cgi.escape(text)).replace(u'\\', u'\\\\').replace(u'\"', u'\\\"')
if self.height() != self.screen[u'size'].height() or not self.isVisible():
2010-08-09 21:21:04 +00:00
shrink = True
2012-01-02 23:43:59 +00:00
js = u'show_alert("%s", "%s")' % (text_prepared, u'top')
2010-08-09 21:21:04 +00:00
else:
2010-08-21 10:07:59 +00:00
shrink = False
2012-01-02 23:43:59 +00:00
js = u'show_alert("%s", "")' % text_prepared
2010-08-07 06:18:05 +00:00
height = self.frame.evaluateJavaScript(js)
if shrink:
if text:
2012-05-19 15:10:05 +00:00
alert_height = int(height)
2011-09-22 18:22:35 +00:00
self.resize(self.width(), alert_height)
self.setVisible(True)
2011-10-29 19:13:11 +00:00
if location == AlertLocation.Middle:
2013-04-24 19:05:34 +00:00
self.move(self.screen[u'size'].left(), (self.screen[u'size'].height() - alert_height) // 2)
2011-10-29 19:13:11 +00:00
elif location == AlertLocation.Bottom:
2012-12-29 13:15:42 +00:00
self.move(self.screen[u'size'].left(), self.screen[u'size'].height() - alert_height)
2010-08-07 06:18:05 +00:00
else:
2011-09-22 18:22:35 +00:00
self.setVisible(False)
self.setGeometry(self.screen[u'size'])
2010-07-11 07:45:49 +00:00
2013-02-20 19:31:51 +00:00
def direct_image(self, path, background):
2010-10-23 07:23:49 +00:00
"""
API for replacement backgrounds so Images are added directly to cache.
2010-10-23 07:23:49 +00:00
"""
self.image_manager.add_image(path, ImageSource.ImagePlugin, background)
2013-03-06 22:23:01 +00:00
if not hasattr(self, u'service_item'):
return False
self.override[u'image'] = path
2013-03-06 22:23:01 +00:00
self.override[u'theme'] = self.service_item.themedata.background_filename
self.image(path)
# Update the preview frame.
if self.is_live:
2013-06-16 07:54:16 +00:00
self.live_controller.update_preview()
return True
2010-10-23 07:23:49 +00:00
def image(self, path):
2010-07-24 16:55:06 +00:00
"""
2012-06-14 15:46:47 +00:00
Add an image as the background. The image has already been added to the
cache.
2010-07-24 16:55:06 +00:00
``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.
2010-07-24 16:55:06 +00:00
"""
2010-10-15 15:33:06 +00:00
log.debug(u'image to display')
image = self.image_manager.get_image_bytes(path, ImageSource.ImagePlugin)
2013-01-23 21:05:25 +00:00
self.controller.media_controller.media_reset(self.controller)
2013-02-21 17:05:56 +00:00
self.display_image(image)
2010-08-07 06:18:05 +00:00
2013-02-20 19:31:51 +00:00
def display_image(self, image):
2010-08-07 06:18:05 +00:00
"""
Display an image, as is.
"""
self.setGeometry(self.screen[u'size'])
2010-08-07 06:18:05 +00:00
if image:
2010-10-23 07:23:49 +00:00
js = u'show_image("data:image/png;base64,%s");' % image
2010-08-07 06:18:05 +00:00
else:
2010-08-09 21:21:04 +00:00
js = u'show_image("");'
2010-08-07 06:18:05 +00:00
self.frame.evaluateJavaScript(js)
2010-07-11 07:45:49 +00:00
2013-02-20 19:31:51 +00:00
def reset_image(self):
2010-08-02 05:16:07 +00:00
"""
2013-02-21 17:05:56 +00:00
Reset the background image to the service item image. Used after the
image plugin has changed the background.
2010-08-02 05:16:07 +00:00
"""
2013-02-21 17:05:56 +00:00
log.debug(u'reset_image')
2013-03-06 22:23:01 +00:00
if hasattr(self, u'service_item'):
self.display_image(self.service_item.bg_image_bytes)
else:
2013-02-20 19:31:51 +00:00
self.display_image(None)
# Update the preview frame.
if self.is_live:
self.live_controller.update_preview()
# clear the cache
2011-01-24 17:26:13 +00:00
self.override = {}
2010-07-28 17:21:32 +00:00
2010-07-17 08:59:15 +00:00
def preview(self):
2010-08-02 05:16:07 +00:00
"""
Generates a preview of the image displayed.
"""
log.debug(u'preview for %s', self.is_live)
2013-03-05 17:12:40 +00:00
was_visible = self.isVisible()
2013-02-03 19:23:12 +00:00
self.application.process_events()
# We must have a service item to preview.
2013-03-06 22:23:01 +00:00
if self.is_live and hasattr(self, u'service_item'):
# Wait for the fade to finish before geting the preview.
# Important otherwise preview will have incorrect text if at all!
2013-03-06 22:23:01 +00:00
if self.service_item.themedata and self.service_item.themedata.display_slide_transition:
2013-03-05 10:36:41 +00:00
while not self.frame.evaluateJavaScript(u'show_text_completed()'):
2013-02-03 19:23:12 +00:00
self.application.process_events()
# Wait for the webview to update before getting the preview.
2010-08-07 06:18:05 +00:00
# Important otherwise first preview will miss the background !
while not self.web_loaded:
2013-02-03 19:23:12 +00:00
self.application.process_events()
2010-09-21 17:24:01 +00:00
# if was hidden keep it hidden
if self.is_live:
2013-02-20 19:31:51 +00:00
if self.hide_mode:
self.hide_display(self.hide_mode)
2013-03-05 17:08:15 +00:00
# Only continue if the visibility wasn't changed during method call.
elif was_visible == self.isVisible():
2013-03-06 21:49:46 +00:00
# Single screen active
if self.screens.display_count == 1:
# Only make visible if setting enabled.
if Settings().value(u'core/display on monitor'):
self.setVisible(True)
else:
self.setVisible(True)
return QtGui.QPixmap.grabWidget(self)
2010-07-11 07:45:49 +00:00
2013-03-06 22:23:01 +00:00
def build_html(self, service_item, image_path=u''):
2010-07-17 08:59:15 +00:00
"""
2013-03-06 22:23:01 +00:00
Store the service_item and build the new HTML from it. Add the
2010-07-17 08:59:15 +00:00
HTML to the display
"""
2013-02-20 19:31:51 +00:00
log.debug(u'build_html')
self.web_loaded = False
self.initial_fame = None
2013-03-06 22:23:01 +00:00
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 u'video' in self.override:
2013-02-07 08:42:17 +00:00
Registry().execute(u'video_background_replaced')
self.override = {}
# We have a different theme.
2013-03-06 22:23:01 +00:00
elif self.override[u'theme'] != service_item.themedata.background_filename:
2013-02-07 08:42:17 +00:00
Registry().execute(u'live_theme_changed')
self.override = {}
else:
# replace the background
background = self.image_manager.get_image_bytes(self.override[u'image'], ImageSource.ImagePlugin)
2013-03-06 22:23:01 +00:00
self.set_transparency(self.service_item.themedata.background_type ==
2012-03-05 19:40:16 +00:00
BackgroundType.to_string(BackgroundType.Transparent))
2013-03-06 22:23:01 +00:00
if self.service_item.themedata.background_filename:
self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
self.service_item.themedata.background_filename, ImageSource.Theme
2013-02-01 20:36:27 +00:00
)
if image_path:
image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
else:
image_bytes = None
2013-03-06 22:23:01 +00:00
html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,
2013-01-22 21:09:43 +00:00
plugins=self.plugin_manager.plugins)
2010-09-16 21:19:51 +00:00
log.debug(u'buildHtml - pre setHtml')
2013-02-20 19:31:51 +00:00
self.web_view.setHtml(html)
2010-09-16 21:19:51 +00:00
log.debug(u'buildHtml - post setHtml')
2013-03-06 22:23:01 +00:00
if service_item.foot_text:
self.footer(service_item.foot_text)
# if was hidden keep it hidden
2013-03-06 22:23:01 +00:00
if self.hide_mode and self.is_live and not service_item.is_media():
if Settings().value(u'core/auto unblank'):
2013-02-07 08:42:17 +00:00
Registry().execute(u'slidecontroller_live_unblank')
else:
2013-02-20 19:31:51 +00:00
self.hide_display(self.hide_mode)
self.__hideMouse()
2010-08-09 21:21:04 +00:00
def footer(self, text):
2010-08-28 15:49:51 +00:00
"""
Display the Footer
"""
2010-08-09 21:21:04 +00:00
log.debug(u'footer')
2012-12-29 13:15:42 +00:00
js = u'show_footer(\'' + text.replace(u'\\', u'\\\\').replace(u'\'', u'\\\'') + u'\')'
2010-08-09 21:21:04 +00:00
self.frame.evaluateJavaScript(js)
2010-07-12 19:36:42 +00:00
2013-02-07 11:33:47 +00:00
def hide_display(self, mode=HideMode.Screen):
2010-07-23 05:05:34 +00:00
"""
Hide the display by making all layers transparent
Store the images so they can be replaced when required
"""
2013-02-07 11:33:47 +00:00
log.debug(u'hide_display mode = %d', mode)
if self.screens.display_count == 1:
# Only make visible if setting enabled.
if not Settings().value(u'core/display on monitor'):
return
2010-07-23 05:05:34 +00:00
if mode == HideMode.Screen:
2010-08-09 21:21:04 +00:00
self.frame.evaluateJavaScript(u'show_blank("desktop");')
2010-07-23 05:05:34 +00:00
self.setVisible(False)
elif mode == HideMode.Blank or self.initial_fame:
2010-08-09 21:21:04 +00:00
self.frame.evaluateJavaScript(u'show_blank("black");')
2010-07-23 05:05:34 +00:00
else:
2010-08-09 21:21:04 +00:00
self.frame.evaluateJavaScript(u'show_blank("theme");')
if mode != HideMode.Screen:
if self.isHidden():
self.setVisible(True)
2013-02-20 19:31:51 +00:00
self.web_view.setVisible(True)
self.hide_mode = mode
2010-07-23 05:05:34 +00:00
2013-02-07 11:33:47 +00:00
def show_display(self):
2010-07-23 05:05:34 +00:00
"""
2013-02-20 19:31:51 +00:00
Show the stored layers so the screen reappears as it was originally.
2010-07-23 05:05:34 +00:00
Make the stored images None to release memory.
"""
2013-02-07 11:33:47 +00:00
log.debug(u'show_display')
if self.screens.display_count == 1:
# Only make visible if setting enabled.
if not Settings().value(u'core/display on monitor'):
return
2010-08-09 21:21:04 +00:00
self.frame.evaluateJavaScript('show_blank("show");')
2010-07-23 05:05:34 +00:00
if self.isHidden():
self.setVisible(True)
2013-02-20 19:31:51 +00:00
self.hide_mode = None
# Trigger actions when display is active again.
if self.is_live:
2013-02-07 08:42:17 +00:00
Registry().execute(u'live_display_active')
def __hideMouse(self):
"""
Hide mouse cursor when moved over display.
"""
if Settings().value(u'advanced/hide mouse'):
self.setCursor(QtCore.Qt.BlankCursor)
self.frame.evaluateJavaScript('document.body.style.cursor = "none"')
else:
self.setCursor(QtCore.Qt.ArrowCursor)
self.frame.evaluateJavaScript('document.body.style.cursor = "auto"')
2013-01-22 21:09:43 +00:00
def _get_plugin_manager(self):
"""
Adds the Renderer to the class dynamically
"""
if not hasattr(self, u'_plugin_manager'):
self._plugin_manager = Registry().get(u'plugin_manager')
return self._plugin_manager
plugin_manager = property(_get_plugin_manager)
def _get_image_manager(self):
"""
Adds the image manager to the class dynamically
"""
if not hasattr(self, u'_image_manager'):
self._image_manager = Registry().get(u'image_manager')
return self._image_manager
image_manager = property(_get_image_manager)
2013-02-03 15:06:17 +00:00
2013-02-03 19:23:12 +00:00
def _get_application(self):
2013-02-03 09:07:31 +00:00
"""
2013-06-21 05:16:35 +00:00
Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
2013-02-03 09:07:31 +00:00
"""
2013-06-21 05:16:35 +00:00
if os.name == u'nt':
2013-06-24 16:54:23 +00:00
return Registry().get(u'application')
2013-06-21 05:16:35 +00:00
else:
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
2013-02-03 09:07:31 +00:00
2013-02-03 19:23:12 +00:00
application = property(_get_application)
2013-01-22 21:09:43 +00:00
2013-02-14 21:31:17 +00:00
def _get_live_controller(self):
"""
Adds the live controller to the class dynamically
"""
if not hasattr(self, u'_live_controller'):
self._live_controller = Registry().get(u'live_controller')
return self._live_controller
live_controller = property(_get_live_controller)
2011-01-17 15:38:10 +00:00
class AudioPlayer(QtCore.QObject):
"""
2010-07-06 21:59:52 +00:00
This Class will play audio only allowing components to work with a
2010-07-11 07:45:49 +00:00
soundtrack independent of the user interface.
"""
log.info(u'AudioPlayer Loaded')
def __init__(self, parent):
"""
The constructor for the display form.
``parent``
The parent widget.
"""
log.debug(u'AudioPlayer Initialisation started')
2013-07-18 19:42:07 +00:00
super(AudioPlayer, self).__init__(parent)
2011-08-30 20:29:29 +00:00
self.currentIndex = -1
self.playlist = []
self.repeat = False
2013-02-20 19:31:51 +00:00
self.media_object = Phonon.MediaObject()
self.media_object.setTickInterval(100)
self.audio_object = Phonon.AudioOutput(Phonon.VideoCategory)
Phonon.createPath(self.media_object, self.audio_object)
self.media_object.aboutToFinish.connect(self.on_about_to_finish)
self.media_object.finished.connect(self.on_finished)
2011-08-30 20:29:29 +00:00
def __del__(self):
"""
Shutting down so clean up connections
"""
2011-08-30 20:29:29 +00:00
self.stop()
2013-02-20 19:31:51 +00:00
for path in self.media_object.outputPaths():
2010-07-08 08:14:10 +00:00
path.disconnect()
2013-02-20 19:31:51 +00:00
def on_about_to_finish(self):
"""
Just before the audio player finishes the current track, queue the next
item in the playlist, if there is one.
"""
self.currentIndex += 1
if len(self.playlist) > self.currentIndex:
2013-02-20 19:31:51 +00:00
self.media_object.enqueue(self.playlist[self.currentIndex])
2013-02-20 19:31:51 +00:00
def on_finished(self):
2013-02-01 20:36:27 +00:00
"""
When the audio track finishes.
"""
if self.repeat:
log.debug(u'Repeat is enabled... here we go again!')
2013-02-20 19:31:51 +00:00
self.media_object.clearQueue()
self.media_object.clear()
self.currentIndex = -1
self.play()
2011-08-30 21:20:32 +00:00
def connectVolumeSlider(self, slider):
2013-02-01 20:36:27 +00:00
"""
Connect the volume slider to the output channel.
"""
2013-02-20 19:31:51 +00:00
slider.setAudioOutput(self.audio_object)
2011-08-30 21:20:32 +00:00
2011-08-30 20:29:29 +00:00
def reset(self):
"""
2011-08-30 20:29:29 +00:00
Reset the audio player, clearing the playlist and the queue.
"""
2011-08-30 20:29:29 +00:00
self.currentIndex = -1
self.playlist = []
self.stop()
2013-02-20 19:31:51 +00:00
self.media_object.clear()
2011-08-30 20:29:29 +00:00
def play(self):
"""
2011-08-30 20:29:29 +00:00
We want to play the file so start it
"""
2011-08-30 20:29:29 +00:00
log.debug(u'AudioPlayer.play() called')
if self.currentIndex == -1:
2013-02-20 19:31:51 +00:00
self.on_about_to_finish()
self.media_object.play()
2011-08-30 20:29:29 +00:00
def pause(self):
"""
Pause the Audio
"""
2011-08-30 20:29:29 +00:00
log.debug(u'AudioPlayer.pause() called')
2013-02-20 19:31:51 +00:00
self.media_object.pause()
2011-08-30 20:29:29 +00:00
def stop(self):
"""
Stop the Audio and clean up
"""
2011-08-30 20:29:29 +00:00
log.debug(u'AudioPlayer.stop() called')
2013-02-20 19:31:51 +00:00
self.media_object.stop()
2013-02-20 19:31:51 +00:00
def add_to_playlist(self, filenames):
2011-08-30 20:29:29 +00:00
"""
Add another file to the playlist.
``filenames``
A list with files to be added to the playlist.
2011-08-30 20:29:29 +00:00
"""
if not isinstance(filenames, list):
filenames = [filenames]
self.playlist.extend(map(Phonon.MediaSource, filenames))
def next(self):
2013-02-01 20:36:27 +00:00
"""
Skip forward to the next track in the list
"""
2012-08-09 21:48:05 +00:00
if not self.repeat and self.currentIndex + 1 >= len(self.playlist):
return
2013-02-20 19:31:51 +00:00
isPlaying = self.media_object.state() == Phonon.PlayingState
self.currentIndex += 1
if self.repeat and self.currentIndex == len(self.playlist):
self.currentIndex = 0
2013-02-20 19:31:51 +00:00
self.media_object.clearQueue()
self.media_object.clear()
self.media_object.enqueue(self.playlist[self.currentIndex])
if isPlaying:
2013-02-20 19:31:51 +00:00
self.media_object.play()
2013-02-20 19:31:51 +00:00
def go_to(self, index):
2013-02-01 20:36:27 +00:00
"""
Go to a particular track in the list
"""
2013-02-20 19:31:51 +00:00
isPlaying = self.media_object.state() == Phonon.PlayingState
self.media_object.clearQueue()
self.media_object.clear()
self.currentIndex = index
2013-02-20 19:31:51 +00:00
self.media_object.enqueue(self.playlist[self.currentIndex])
if isPlaying:
2013-02-20 19:31:51 +00:00
self.media_object.play()
def connectSlot(self, signal, slot):
2013-02-01 20:36:27 +00:00
"""
2013-02-21 17:05:56 +00:00
Connect a slot to a signal on the media object. Used by slidecontroller to connect to audio object.
2013-02-01 20:36:27 +00:00
"""
2013-03-07 08:14:26 +00:00
QtCore.QObject.connect(self.media_object, signal, slot)