2010-07-03 19:02:36 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
# OpenLP - Open Source Lyrics Projection #
|
|
|
|
# --------------------------------------------------------------------------- #
|
2010-12-26 11:04:47 +00:00
|
|
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
2011-05-26 16:25:54 +00:00
|
|
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
|
|
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
2011-05-26 17:11:22 +00:00
|
|
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
2011-05-26 16:25:54 +00:00
|
|
|
# Põldaru, Christian Richter, Philip Ridout, Jeffrey Smith, Maikel #
|
|
|
|
# Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
2010-07-03 19:02:36 +00:00
|
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
# 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
|
|
|
"""
|
2011-01-14 18:58:47 +00:00
|
|
|
The :mod:`maindisplay` module provides the functionality to display screens
|
|
|
|
and play multimedia within OpenLP.
|
2010-12-06 19:25:44 +00:00
|
|
|
"""
|
2010-07-03 19:02:36 +00:00
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
|
|
|
|
from PyQt4 import QtCore, QtGui, QtWebKit
|
|
|
|
from PyQt4.phonon import Phonon
|
|
|
|
|
2010-12-23 17:42:28 +00:00
|
|
|
from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \
|
2011-04-22 07:06:38 +00:00
|
|
|
translate
|
2011-01-05 16:57:49 +00:00
|
|
|
|
2011-05-04 17:31:02 +00:00
|
|
|
from openlp.core.ui import HideMode, ScreenList
|
2010-07-03 19:02:36 +00:00
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
#http://www.steveheffernan.com/html5-video-player/demo-video-player.html
|
2010-11-20 16:36:54 +00:00
|
|
|
#http://html5demos.com/two-videos
|
2010-07-03 19:02:36 +00:00
|
|
|
|
2011-05-06 19:55:16 +00:00
|
|
|
class MainDisplay(QtGui.QGraphicsView):
|
2010-10-16 22:50:36 +00:00
|
|
|
"""
|
|
|
|
This is the display screen.
|
|
|
|
"""
|
2011-05-05 15:16:55 +00:00
|
|
|
def __init__(self, parent, image_manager, live):
|
2011-05-28 20:12:32 +00:00
|
|
|
QtGui.QGraphicsView.__init__(self, parent)
|
2010-07-17 08:59:15 +00:00
|
|
|
self.isLive = live
|
2011-05-05 15:16:55 +00:00
|
|
|
self.image_manager = image_manager
|
2011-05-05 11:51:47 +00:00
|
|
|
self.screens = ScreenList.get_instance()
|
2011-02-12 16:07:11 +00:00
|
|
|
self.alertTab = None
|
2010-11-27 20:51:54 +00:00
|
|
|
self.hideMode = None
|
2011-03-14 08:27:25 +00:00
|
|
|
self.videoHide = False
|
2011-01-23 14:08:49 +00:00
|
|
|
self.override = {}
|
2011-01-01 13:22:12 +00:00
|
|
|
self.retranslateUi()
|
2011-04-29 11:55:49 +00:00
|
|
|
self.mediaObject = None
|
|
|
|
self.firstTime = True
|
2010-09-19 20:59:00 +00:00
|
|
|
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
|
2011-02-05 09:45:08 +00:00
|
|
|
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool |
|
2010-07-11 11:38:09 +00:00
|
|
|
QtCore.Qt.WindowStaysOnTopHint)
|
2010-07-24 16:55:06 +00:00
|
|
|
if self.isLive:
|
|
|
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
|
|
|
QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay)
|
|
|
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
|
|
|
QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay)
|
2011-04-29 16:27:27 +00:00
|
|
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
|
|
|
QtCore.SIGNAL(u'openlp_phonon_creation'),
|
|
|
|
self.createMediaObject)
|
2010-07-11 07:45:49 +00:00
|
|
|
|
2011-01-01 13:22:12 +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
|
|
|
|
"""
|
2011-05-06 19:09:40 +00:00
|
|
|
log.debug(u'Start MainDisplay setup (live = %s)' % self.isLive)
|
2010-10-01 22:47:37 +00:00
|
|
|
self.usePhonon = QtCore.QSettings().value(
|
2010-10-05 22:23:46 +00:00
|
|
|
u'media/use phonon', QtCore.QVariant(True)).toBool()
|
|
|
|
self.phononActive = False
|
2010-07-23 05:05:34 +00:00
|
|
|
self.screen = self.screens.current
|
|
|
|
self.setVisible(False)
|
|
|
|
self.setGeometry(self.screen[u'size'])
|
2010-10-05 22:23:46 +00:00
|
|
|
self.videoWidget = Phonon.VideoWidget(self)
|
|
|
|
self.videoWidget.setVisible(False)
|
|
|
|
self.videoWidget.setGeometry(QtCore.QRect(0, 0,
|
|
|
|
self.screen[u'size'].width(), self.screen[u'size'].height()))
|
2011-04-29 16:27:27 +00:00
|
|
|
if self.isLive:
|
|
|
|
if not self.firstTime:
|
2011-04-29 11:55:49 +00:00
|
|
|
self.createMediaObject()
|
2011-05-06 19:09:40 +00:00
|
|
|
log.debug(u'Setup webView')
|
2010-10-05 22:23:46 +00:00
|
|
|
self.webView = QtWebKit.QWebView(self)
|
|
|
|
self.webView.setGeometry(0, 0,
|
|
|
|
self.screen[u'size'].width(), self.screen[u'size'].height())
|
2010-07-23 05:05:34 +00:00
|
|
|
self.page = self.webView.page()
|
|
|
|
self.frame = self.page.mainFrame()
|
|
|
|
QtCore.QObject.connect(self.webView,
|
2011-01-23 14:08:49 +00:00
|
|
|
QtCore.SIGNAL(u'loadFinished(bool)'), self.isWebLoaded)
|
2010-09-04 19:13:26 +00:00
|
|
|
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
|
|
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
2010-07-23 05:05:34 +00:00
|
|
|
self.frame.setScrollBarPolicy(QtCore.Qt.Vertical,
|
|
|
|
QtCore.Qt.ScrollBarAlwaysOff)
|
|
|
|
self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
|
|
|
|
QtCore.Qt.ScrollBarAlwaysOff)
|
|
|
|
if self.isLive:
|
2010-07-24 16:55:06 +00:00
|
|
|
# Build the initial frame.
|
|
|
|
self.black = QtGui.QImage(
|
2011-05-06 19:09:40 +00:00
|
|
|
self.screen[u'size'].width(),
|
|
|
|
self.screen[u'size'].height(),
|
2010-07-24 16:55:06 +00:00
|
|
|
QtGui.QImage.Format_ARGB32_Premultiplied)
|
|
|
|
painter_image = QtGui.QPainter()
|
|
|
|
painter_image.begin(self.black)
|
|
|
|
painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
|
2010-09-04 07:48:58 +00:00
|
|
|
# Build the initial frame.
|
2011-02-23 06:42:33 +00:00
|
|
|
image_file = QtCore.QSettings().value(u'advanced/default image',
|
|
|
|
QtCore.QVariant(u':/graphics/openlp-splash-screen.png'))\
|
|
|
|
.toString()
|
2011-04-27 09:00:37 +00:00
|
|
|
background_color = QtGui.QColor()
|
|
|
|
background_color.setNamedColor(QtCore.QSettings().value(
|
2011-02-23 06:42:33 +00:00
|
|
|
u'advanced/default color',
|
2011-04-27 10:30:29 +00:00
|
|
|
QtCore.QVariant(u'#ffffff')).toString())
|
2011-02-23 06:42:33 +00:00
|
|
|
if not background_color.isValid():
|
|
|
|
background_color = QtCore.Qt.white
|
|
|
|
splash_image = QtGui.QImage(image_file)
|
2011-03-12 17:06:26 +00:00
|
|
|
self.initialFrame = QtGui.QImage(
|
2011-05-06 19:09:40 +00:00
|
|
|
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()
|
2011-03-12 17:06:26 +00:00
|
|
|
painter_image.begin(self.initialFrame)
|
|
|
|
painter_image.fillRect(self.initialFrame.rect(), background_color)
|
2010-07-23 05:05:34 +00:00
|
|
|
painter_image.drawImage(
|
2011-05-06 19:09:40 +00:00
|
|
|
(self.screen[u'size'].width() - splash_image.width()) / 2,
|
|
|
|
(self.screen[u'size'].height() - splash_image.height()) / 2,
|
|
|
|
splash_image)
|
2010-07-23 05:05:34 +00:00
|
|
|
serviceItem = ServiceItem()
|
2011-03-12 17:06:26 +00:00
|
|
|
serviceItem.bg_image_bytes = image_to_byte(self.initialFrame)
|
2010-08-29 14:44:14 +00:00
|
|
|
self.webView.setHtml(build_html(serviceItem, self.screen,
|
2011-02-12 16:07:11 +00:00
|
|
|
self.alertTab, self.isLive, None))
|
2011-03-03 17:07:36 +00:00
|
|
|
self.__hideMouse()
|
2010-07-24 16:55:06 +00:00
|
|
|
# To display or not to display?
|
|
|
|
if not self.screen[u'primary']:
|
2010-08-29 18:12:36 +00:00
|
|
|
self.show()
|
2010-07-24 16:55:06 +00:00
|
|
|
self.primary = False
|
|
|
|
else:
|
|
|
|
self.primary = True
|
2011-05-06 19:09:40 +00:00
|
|
|
log.debug(u'Finished MainDisplay setup')
|
2010-07-23 05:05:34 +00:00
|
|
|
|
2011-04-29 11:55:49 +00:00
|
|
|
def createMediaObject(self):
|
2011-04-29 16:27:27 +00:00
|
|
|
self.firstTime = False
|
|
|
|
log.debug(u'Creating Phonon objects - Start for %s', self.isLive)
|
2011-04-29 11:55:49 +00:00
|
|
|
self.mediaObject = Phonon.MediaObject(self)
|
|
|
|
self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
|
|
|
|
Phonon.createPath(self.mediaObject, self.videoWidget)
|
|
|
|
Phonon.createPath(self.mediaObject, self.audio)
|
|
|
|
QtCore.QObject.connect(self.mediaObject,
|
|
|
|
QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'),
|
|
|
|
self.videoState)
|
|
|
|
QtCore.QObject.connect(self.mediaObject,
|
|
|
|
QtCore.SIGNAL(u'finished()'),
|
|
|
|
self.videoFinished)
|
|
|
|
QtCore.QObject.connect(self.mediaObject,
|
|
|
|
QtCore.SIGNAL(u'tick(qint64)'),
|
|
|
|
self.videoTick)
|
2011-04-29 16:27:27 +00:00
|
|
|
log.debug(u'Creating Phonon objects - Finished for %s', self.isLive)
|
2011-04-29 11:55:49 +00:00
|
|
|
|
2010-07-11 07:45:49 +00:00
|
|
|
def text(self, slide):
|
2010-07-24 16:55:06 +00:00
|
|
|
"""
|
|
|
|
Add the slide text from slideController
|
|
|
|
|
|
|
|
`slide`
|
|
|
|
The slide text to be displayed
|
|
|
|
"""
|
2010-10-15 15:33:06 +00:00
|
|
|
log.debug(u'text to display')
|
2011-01-12 18:52:16 +00:00
|
|
|
# Wait for the webview to update before displaying text.
|
2011-01-23 14:08:49 +00:00
|
|
|
while not self.webLoaded:
|
2010-09-25 06:29:03 +00:00
|
|
|
Receiver.send_message(u'openlp_process_events')
|
2011-03-12 00:12:52 +00:00
|
|
|
self.setGeometry(self.screen[u'size'])
|
2010-08-09 21:21:04 +00:00
|
|
|
self.frame.evaluateJavaScript(u'show_text("%s")' % \
|
|
|
|
slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
2010-07-17 08:59:15 +00:00
|
|
|
return self.preview()
|
2010-07-11 07:45:49 +00:00
|
|
|
|
2010-08-09 21:21:04 +00:00
|
|
|
def alert(self, text):
|
2010-07-24 16:55:06 +00:00
|
|
|
"""
|
|
|
|
Add the alert text
|
|
|
|
|
|
|
|
`slide`
|
|
|
|
The slide text to be displayed
|
|
|
|
"""
|
2010-10-15 15:33:06 +00:00
|
|
|
log.debug(u'alert to display')
|
2011-05-05 13:02:12 +00:00
|
|
|
if self.height() != self.screen[u'size'].height() or not \
|
|
|
|
self.isVisible() or self.videoWidget.isVisible():
|
2010-08-09 21:21:04 +00:00
|
|
|
shrink = True
|
|
|
|
else:
|
2010-08-21 10:07:59 +00:00
|
|
|
shrink = False
|
2010-12-27 22:57:35 +00:00
|
|
|
js = u'show_alert("%s", "%s")' % (
|
2010-08-09 21:21:04 +00:00
|
|
|
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'),
|
|
|
|
u'top' if shrink else u'')
|
2010-08-07 06:18:05 +00:00
|
|
|
height = self.frame.evaluateJavaScript(js)
|
|
|
|
if shrink:
|
2010-10-05 22:23:46 +00:00
|
|
|
if self.phononActive:
|
|
|
|
shrinkItem = self.webView
|
|
|
|
else:
|
|
|
|
shrinkItem = self
|
2010-08-07 06:18:05 +00:00
|
|
|
if text:
|
2011-03-12 00:12:52 +00:00
|
|
|
alert_height = int(height.toString())
|
|
|
|
shrinkItem.resize(self.width(), alert_height)
|
2010-10-05 22:23:46 +00:00
|
|
|
shrinkItem.setVisible(True)
|
2011-03-12 00:12:52 +00:00
|
|
|
if self.alertTab.location == 1:
|
|
|
|
shrinkItem.move(self.screen[u'size'].left(),
|
|
|
|
(self.screen[u'size'].height() - alert_height) / 2)
|
|
|
|
elif self.alertTab.location == 2:
|
|
|
|
shrinkItem.move(self.screen[u'size'].left(),
|
|
|
|
self.screen[u'size'].height() - alert_height)
|
2010-08-07 06:18:05 +00:00
|
|
|
else:
|
2010-10-05 22:23:46 +00:00
|
|
|
shrinkItem.setVisible(False)
|
2011-03-12 00:12:52 +00:00
|
|
|
self.setGeometry(self.screen[u'size'])
|
2010-07-11 07:45:49 +00:00
|
|
|
|
2010-10-23 07:23:49 +00:00
|
|
|
def directImage(self, name, path):
|
|
|
|
"""
|
2010-10-24 06:26:07 +00:00
|
|
|
API for replacement backgrounds so Images are added directly to cache
|
2010-10-23 07:23:49 +00:00
|
|
|
"""
|
2011-05-05 15:16:55 +00:00
|
|
|
self.image_manager.add_image(name, path)
|
2010-10-23 07:23:49 +00:00
|
|
|
self.image(name)
|
2011-01-23 14:08:49 +00:00
|
|
|
if hasattr(self, u'serviceItem'):
|
|
|
|
self.override[u'image'] = name
|
|
|
|
self.override[u'theme'] = self.serviceItem.themedata.theme_name
|
2010-10-23 07:23:49 +00:00
|
|
|
|
2010-10-22 17:24:56 +00:00
|
|
|
def image(self, name):
|
2010-07-24 16:55:06 +00:00
|
|
|
"""
|
2011-01-23 08:25:21 +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
|
|
|
|
|
|
|
`Image`
|
2011-01-23 08:25:21 +00:00
|
|
|
The name of the image to be displayed
|
2010-07-24 16:55:06 +00:00
|
|
|
"""
|
2010-10-15 15:33:06 +00:00
|
|
|
log.debug(u'image to display')
|
2011-05-05 15:16:55 +00:00
|
|
|
image = self.image_manager.get_image_bytes(name)
|
2010-08-07 06:18:05 +00:00
|
|
|
self.resetVideo()
|
|
|
|
self.displayImage(image)
|
2010-10-23 17:37:10 +00:00
|
|
|
return self.preview()
|
2010-08-07 06:18:05 +00:00
|
|
|
|
|
|
|
def displayImage(self, image):
|
|
|
|
"""
|
|
|
|
Display an image, as is.
|
|
|
|
"""
|
2011-03-12 00:12:52 +00:00
|
|
|
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)
|
2011-01-17 15:38:10 +00:00
|
|
|
# Update the preview frame.
|
2011-03-07 18:04:40 +00:00
|
|
|
if self.isLive:
|
|
|
|
Receiver.send_message(u'maindisplay_active')
|
2010-07-11 07:45:49 +00:00
|
|
|
|
2010-07-28 17:21:32 +00:00
|
|
|
def resetImage(self):
|
2010-08-02 05:16:07 +00:00
|
|
|
"""
|
|
|
|
Reset the backgound image to the service item image.
|
|
|
|
Used after Image plugin has changed the background
|
|
|
|
"""
|
2010-07-28 17:21:32 +00:00
|
|
|
log.debug(u'resetImage')
|
2011-01-22 18:16:25 +00:00
|
|
|
if hasattr(self, u'serviceItem'):
|
|
|
|
self.displayImage(self.serviceItem.bg_image_bytes)
|
|
|
|
else:
|
|
|
|
self.displayImage(None)
|
2011-03-14 08:27:25 +00:00
|
|
|
# clear the cache
|
2011-01-24 17:26:13 +00:00
|
|
|
self.override = {}
|
2011-01-17 15:38:10 +00:00
|
|
|
# Update the preview frame.
|
2011-03-07 18:04:40 +00:00
|
|
|
if self.isLive:
|
|
|
|
Receiver.send_message(u'maindisplay_active')
|
2010-07-28 17:21:32 +00:00
|
|
|
|
|
|
|
def resetVideo(self):
|
2010-08-02 05:16:07 +00:00
|
|
|
"""
|
|
|
|
Used after Video plugin has changed the background
|
|
|
|
"""
|
2010-07-28 17:21:32 +00:00
|
|
|
log.debug(u'resetVideo')
|
2010-10-05 22:23:46 +00:00
|
|
|
if self.phononActive:
|
2010-10-01 22:47:37 +00:00
|
|
|
self.mediaObject.stop()
|
|
|
|
self.mediaObject.clearQueue()
|
2010-10-05 22:23:46 +00:00
|
|
|
self.webView.setVisible(True)
|
|
|
|
self.videoWidget.setVisible(False)
|
|
|
|
self.phononActive = False
|
|
|
|
else:
|
|
|
|
self.frame.evaluateJavaScript(u'show_video("close");')
|
2011-01-24 17:26:13 +00:00
|
|
|
self.override = {}
|
2011-01-17 15:38:10 +00:00
|
|
|
# Update the preview frame.
|
2011-03-07 18:04:40 +00:00
|
|
|
if self.isLive:
|
|
|
|
Receiver.send_message(u'maindisplay_active')
|
2010-07-19 20:05:25 +00:00
|
|
|
|
2010-08-01 16:46:24 +00:00
|
|
|
def videoPlay(self):
|
2010-08-02 05:16:07 +00:00
|
|
|
"""
|
|
|
|
Responds to the request to play a loaded video
|
|
|
|
"""
|
2010-08-01 16:46:24 +00:00
|
|
|
log.debug(u'videoPlay')
|
2010-10-05 22:23:46 +00:00
|
|
|
if self.phononActive:
|
2010-10-01 22:47:37 +00:00
|
|
|
self.mediaObject.play()
|
|
|
|
else:
|
|
|
|
self.frame.evaluateJavaScript(u'show_video("play");')
|
2010-08-26 05:01:29 +00:00
|
|
|
# show screen
|
|
|
|
if self.isLive:
|
|
|
|
self.setVisible(True)
|
2010-08-01 16:46:24 +00:00
|
|
|
|
|
|
|
def videoPause(self):
|
2010-08-02 05:16:07 +00:00
|
|
|
"""
|
|
|
|
Responds to the request to pause a loaded video
|
|
|
|
"""
|
2010-08-01 16:46:24 +00:00
|
|
|
log.debug(u'videoPause')
|
2010-10-05 22:23:46 +00:00
|
|
|
if self.phononActive:
|
2010-10-01 22:47:37 +00:00
|
|
|
self.mediaObject.pause()
|
|
|
|
else:
|
|
|
|
self.frame.evaluateJavaScript(u'show_video("pause");')
|
2010-08-01 16:46:24 +00:00
|
|
|
|
|
|
|
def videoStop(self):
|
2010-08-02 05:16:07 +00:00
|
|
|
"""
|
|
|
|
Responds to the request to stop a loaded video
|
|
|
|
"""
|
2010-08-01 16:46:24 +00:00
|
|
|
log.debug(u'videoStop')
|
2010-10-05 22:23:46 +00:00
|
|
|
if self.phononActive:
|
2010-10-01 22:47:37 +00:00
|
|
|
self.mediaObject.stop()
|
|
|
|
else:
|
|
|
|
self.frame.evaluateJavaScript(u'show_video("stop");')
|
2010-08-01 16:46:24 +00:00
|
|
|
|
2010-08-04 19:09:43 +00:00
|
|
|
def videoVolume(self, volume):
|
2010-08-02 05:16:07 +00:00
|
|
|
"""
|
|
|
|
Changes the volume of a running video
|
|
|
|
"""
|
2010-08-04 19:09:43 +00:00
|
|
|
log.debug(u'videoVolume %d' % volume)
|
2011-03-05 18:38:33 +00:00
|
|
|
vol = float(volume) / float(10)
|
2010-10-05 22:23:46 +00:00
|
|
|
if self.phononActive:
|
2010-10-08 19:40:59 +00:00
|
|
|
self.audio.setVolume(vol)
|
2010-10-01 22:47:37 +00:00
|
|
|
else:
|
|
|
|
self.frame.evaluateJavaScript(u'show_video(null, null, %s);' %
|
2010-10-08 19:40:59 +00:00
|
|
|
str(vol))
|
2010-10-05 22:23:46 +00:00
|
|
|
|
2010-10-01 22:47:37 +00:00
|
|
|
def video(self, videoPath, volume, isBackground=False):
|
2010-08-02 05:16:07 +00:00
|
|
|
"""
|
|
|
|
Loads and starts a video to run with the option of sound
|
|
|
|
"""
|
2011-04-29 11:55:49 +00:00
|
|
|
if not self.mediaObject:
|
|
|
|
self.createMediaObject()
|
2010-07-25 08:58:08 +00:00
|
|
|
log.debug(u'video')
|
2011-01-23 14:08:49 +00:00
|
|
|
self.webLoaded = True
|
2011-03-12 00:12:52 +00:00
|
|
|
self.setGeometry(self.screen[u'size'])
|
2011-01-23 15:36:15 +00:00
|
|
|
# We are running a background theme
|
|
|
|
self.override[u'theme'] = u''
|
|
|
|
self.override[u'video'] = True
|
2011-03-05 18:38:33 +00:00
|
|
|
vol = float(volume) / float(10)
|
2010-10-01 22:47:37 +00:00
|
|
|
if isBackground or not self.usePhonon:
|
|
|
|
js = u'show_video("init", "%s", %s, true); show_video("play");' % \
|
2011-02-08 02:45:37 +00:00
|
|
|
(videoPath.replace(u'\\', u'\\\\'), str(vol))
|
2010-10-01 22:47:37 +00:00
|
|
|
self.frame.evaluateJavaScript(js)
|
|
|
|
else:
|
2010-10-05 22:23:46 +00:00
|
|
|
self.phononActive = True
|
2010-10-01 22:47:37 +00:00
|
|
|
self.mediaObject.stop()
|
|
|
|
self.mediaObject.clearQueue()
|
|
|
|
self.mediaObject.setCurrentSource(Phonon.MediaSource(videoPath))
|
2011-03-14 10:10:10 +00:00
|
|
|
# Need the timer to trigger set the trigger to 200ms
|
|
|
|
# Value taken from web documentation.
|
2011-03-19 12:40:33 +00:00
|
|
|
if self.serviceItem.end_time != 0:
|
2011-03-14 08:27:25 +00:00
|
|
|
self.mediaObject.setTickInterval(200)
|
2010-10-01 22:47:37 +00:00
|
|
|
self.mediaObject.play()
|
|
|
|
self.webView.setVisible(False)
|
2010-10-05 22:23:46 +00:00
|
|
|
self.videoWidget.setVisible(True)
|
2010-10-08 19:40:59 +00:00
|
|
|
self.audio.setVolume(vol)
|
2011-01-17 15:38:10 +00:00
|
|
|
# Update the preview frame.
|
2011-03-07 18:04:40 +00:00
|
|
|
if self.isLive:
|
|
|
|
Receiver.send_message(u'maindisplay_active')
|
2010-08-05 19:01:24 +00:00
|
|
|
return self.preview()
|
2010-07-11 07:45:49 +00:00
|
|
|
|
2011-03-19 22:52:56 +00:00
|
|
|
def videoState(self, newState, oldState):
|
2011-02-12 10:04:10 +00:00
|
|
|
"""
|
|
|
|
Start the video at a predetermined point.
|
|
|
|
"""
|
2011-03-19 17:26:17 +00:00
|
|
|
if newState == Phonon.PlayingState \
|
|
|
|
and oldState != Phonon.PausedState \
|
|
|
|
and self.serviceItem.start_time > 0:
|
2011-03-14 10:10:10 +00:00
|
|
|
# set start time in milliseconds
|
2011-02-13 13:11:15 +00:00
|
|
|
self.mediaObject.seek(self.serviceItem.start_time * 1000)
|
2011-02-12 10:04:10 +00:00
|
|
|
|
2011-03-14 08:27:25 +00:00
|
|
|
def videoFinished(self):
|
|
|
|
"""
|
|
|
|
Blank the Video when it has finished so the final frame is not left
|
|
|
|
hanging
|
|
|
|
"""
|
|
|
|
self.videoStop()
|
|
|
|
self.hideDisplay(HideMode.Blank)
|
|
|
|
self.phononActive = False
|
|
|
|
self.videoHide = True
|
|
|
|
|
|
|
|
def videoTick(self, tick):
|
|
|
|
"""
|
|
|
|
Triggered on video tick every 200 milli seconds
|
|
|
|
"""
|
2011-03-19 12:40:33 +00:00
|
|
|
if tick > self.serviceItem.end_time * 1000:
|
|
|
|
self.videoFinished()
|
2011-03-14 08:27:25 +00:00
|
|
|
|
2011-01-23 14:08:49 +00:00
|
|
|
def isWebLoaded(self):
|
2010-07-25 08:58:08 +00:00
|
|
|
"""
|
|
|
|
Called by webView event to show display is fully loaded
|
|
|
|
"""
|
2011-01-23 14:08:49 +00:00
|
|
|
log.debug(u'Webloaded')
|
|
|
|
self.webLoaded = True
|
2010-07-17 10:25:52 +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.
|
|
|
|
"""
|
2010-08-26 05:01:29 +00:00
|
|
|
log.debug(u'preview for %s', self.isLive)
|
2010-09-17 20:32:00 +00:00
|
|
|
Receiver.send_message(u'openlp_process_events')
|
2011-01-22 18:16:25 +00:00
|
|
|
# We must have a service item to preview
|
|
|
|
if self.isLive and hasattr(self, u'serviceItem'):
|
2010-08-28 18:44:49 +00:00
|
|
|
# Wait for the fade to finish before geting the preview.
|
2011-01-22 18:16:25 +00:00
|
|
|
# Important otherwise preview will have incorrect text if at all!
|
2010-08-28 18:44:49 +00:00
|
|
|
if self.serviceItem.themedata and \
|
2010-09-11 06:59:36 +00:00
|
|
|
self.serviceItem.themedata.display_slide_transition:
|
2010-08-28 18:44:49 +00:00
|
|
|
while self.frame.evaluateJavaScript(u'show_text_complete()') \
|
|
|
|
.toString() == u'false':
|
|
|
|
Receiver.send_message(u'openlp_process_events')
|
2010-08-07 06:18:05 +00:00
|
|
|
# Wait for the webview to update before geting the preview.
|
|
|
|
# Important otherwise first preview will miss the background !
|
2011-01-23 14:08:49 +00:00
|
|
|
while not self.webLoaded:
|
2010-07-17 10:25:52 +00:00
|
|
|
Receiver.send_message(u'openlp_process_events')
|
2010-09-21 17:24:01 +00:00
|
|
|
# if was hidden keep it hidden
|
2010-09-17 20:32:00 +00:00
|
|
|
if self.isLive:
|
2011-03-06 15:29:25 +00:00
|
|
|
if self.hideMode:
|
|
|
|
self.hideDisplay(self.hideMode)
|
|
|
|
else:
|
2011-03-15 08:34:27 +00:00
|
|
|
# Single screen active
|
2011-05-06 19:09:40 +00:00
|
|
|
if self.screens.display_count == 1:
|
2011-03-15 08:34:27 +00:00
|
|
|
# Only make visible if setting enabled
|
|
|
|
if QtCore.QSettings().value(u'general/display on monitor',
|
|
|
|
QtCore.QVariant(True)).toBool():
|
|
|
|
self.setVisible(True)
|
|
|
|
else:
|
|
|
|
self.setVisible(True)
|
2010-07-11 15:53:40 +00:00
|
|
|
preview = QtGui.QImage(self.screen[u'size'].width(),
|
|
|
|
self.screen[u'size'].height(),
|
|
|
|
QtGui.QImage.Format_ARGB32_Premultiplied)
|
2010-07-11 07:45:49 +00:00
|
|
|
painter = QtGui.QPainter(preview)
|
|
|
|
painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
|
|
|
self.frame.render(painter)
|
|
|
|
painter.end()
|
|
|
|
return preview
|
|
|
|
|
2011-04-10 22:10:23 +00:00
|
|
|
def buildHtml(self, serviceItem, image=None):
|
2010-07-17 08:59:15 +00:00
|
|
|
"""
|
|
|
|
Store the serviceItem and build the new HTML from it. Add the
|
|
|
|
HTML to the display
|
|
|
|
"""
|
2010-07-25 08:58:08 +00:00
|
|
|
log.debug(u'buildHtml')
|
2011-01-23 14:08:49 +00:00
|
|
|
self.webLoaded = False
|
2011-03-12 17:06:26 +00:00
|
|
|
self.initialFrame = None
|
2010-07-17 08:59:15 +00:00
|
|
|
self.serviceItem = serviceItem
|
2011-01-23 14:08:49 +00:00
|
|
|
background = None
|
|
|
|
# We have an image override so keep the image till the theme changes
|
|
|
|
if self.override:
|
2011-01-23 15:36:15 +00:00
|
|
|
# We have an video override so allow it to be stopped
|
|
|
|
if u'video' in self.override:
|
|
|
|
Receiver.send_message(u'video_background_replaced')
|
|
|
|
self.override = {}
|
2011-03-14 08:27:25 +00:00
|
|
|
# We have a different theme.
|
2011-02-08 02:45:37 +00:00
|
|
|
elif self.override[u'theme'] != serviceItem.themedata.theme_name:
|
2011-01-23 14:08:49 +00:00
|
|
|
Receiver.send_message(u'live_theme_changed')
|
|
|
|
self.override = {}
|
|
|
|
else:
|
2011-03-14 08:27:25 +00:00
|
|
|
# replace the background
|
2011-05-05 15:16:55 +00:00
|
|
|
background = self.image_manager. \
|
2011-01-23 14:08:49 +00:00
|
|
|
get_image_bytes(self.override[u'image'])
|
2010-10-23 07:23:49 +00:00
|
|
|
if self.serviceItem.themedata.background_filename:
|
2011-05-05 15:16:55 +00:00
|
|
|
self.serviceItem.bg_image_bytes = self.image_manager. \
|
2010-10-23 07:23:49 +00:00
|
|
|
get_image_bytes(self.serviceItem.themedata.theme_name)
|
2011-04-10 22:10:23 +00:00
|
|
|
if image:
|
2011-05-05 15:16:55 +00:00
|
|
|
image_bytes = self.image_manager.get_image_bytes(image)
|
2011-04-10 22:10:23 +00:00
|
|
|
else:
|
|
|
|
image_bytes = None
|
2011-02-12 16:07:11 +00:00
|
|
|
html = build_html(self.serviceItem, self.screen, self.alertTab,
|
2011-04-10 22:10:23 +00:00
|
|
|
self.isLive, background, image_bytes)
|
2010-09-16 21:19:51 +00:00
|
|
|
log.debug(u'buildHtml - pre setHtml')
|
2010-07-12 19:36:42 +00:00
|
|
|
self.webView.setHtml(html)
|
2010-09-16 21:19:51 +00:00
|
|
|
log.debug(u'buildHtml - post setHtml')
|
2011-01-23 14:08:49 +00:00
|
|
|
if serviceItem.foot_text:
|
2010-08-09 21:21:04 +00:00
|
|
|
self.footer(serviceItem.foot_text)
|
2010-09-05 19:52:49 +00:00
|
|
|
# if was hidden keep it hidden
|
2010-11-27 20:51:54 +00:00
|
|
|
if self.hideMode and self.isLive:
|
2011-03-24 21:28:03 +00:00
|
|
|
if QtCore.QSettings().value(u'general/auto unblank',
|
|
|
|
QtCore.QVariant(False)).toBool():
|
|
|
|
Receiver.send_message(u'slidecontroller_live_unblank')
|
|
|
|
else:
|
|
|
|
self.hideDisplay(self.hideMode)
|
2011-03-14 08:27:25 +00:00
|
|
|
# display hidden for video end we have a new item so must be shown
|
|
|
|
if self.videoHide and self.isLive:
|
|
|
|
self.videoHide = False
|
|
|
|
self.showDisplay()
|
2011-03-03 17:07:36 +00:00
|
|
|
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')
|
2010-12-27 22:57:35 +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
|
|
|
|
2010-07-23 05:05:34 +00:00
|
|
|
def hideDisplay(self, mode=HideMode.Screen):
|
|
|
|
"""
|
|
|
|
Hide the display by making all layers transparent
|
|
|
|
Store the images so they can be replaced when required
|
|
|
|
"""
|
|
|
|
log.debug(u'hideDisplay mode = %d', mode)
|
2010-10-05 22:23:46 +00:00
|
|
|
if self.phononActive:
|
|
|
|
self.videoPause()
|
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)
|
2010-08-05 07:49:25 +00:00
|
|
|
elif mode == HideMode.Blank or self.initialFrame:
|
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");')
|
2010-10-05 22:23:46 +00:00
|
|
|
if mode != HideMode.Screen:
|
|
|
|
if self.isHidden():
|
|
|
|
self.setVisible(True)
|
|
|
|
if self.phononActive:
|
|
|
|
self.webView.setVisible(True)
|
2010-11-27 20:51:54 +00:00
|
|
|
self.hideMode = mode
|
2010-07-23 05:05:34 +00:00
|
|
|
|
2010-07-24 19:27:25 +00:00
|
|
|
def showDisplay(self):
|
2010-07-23 05:05:34 +00:00
|
|
|
"""
|
|
|
|
Show the stored layers so the screen reappears as it was
|
|
|
|
originally.
|
|
|
|
Make the stored images None to release memory.
|
|
|
|
"""
|
|
|
|
log.debug(u'showDisplay')
|
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)
|
2010-10-05 22:23:46 +00:00
|
|
|
if self.phononActive:
|
|
|
|
self.webView.setVisible(False)
|
|
|
|
self.videoPlay()
|
2010-11-27 20:51:54 +00:00
|
|
|
self.hideMode = None
|
2010-07-24 16:55:06 +00:00
|
|
|
# Trigger actions when display is active again
|
2011-03-07 18:04:40 +00:00
|
|
|
if self.isLive:
|
|
|
|
Receiver.send_message(u'maindisplay_active')
|
2010-07-03 19:02:36 +00:00
|
|
|
|
2011-03-03 17:07:36 +00:00
|
|
|
def __hideMouse(self):
|
|
|
|
# Hide mouse cursor when moved over display if enabled in settings
|
|
|
|
if QtCore.QSettings().value(u'advanced/hide mouse',
|
|
|
|
QtCore.QVariant(False)).toBool():
|
|
|
|
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"')
|
|
|
|
|
2011-01-17 15:38:10 +00:00
|
|
|
|
2010-07-03 19:02:36 +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.
|
2010-07-03 19:02:36 +00:00
|
|
|
"""
|
|
|
|
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')
|
2010-07-06 21:59:52 +00:00
|
|
|
QtCore.QObject.__init__(self, parent)
|
2010-07-03 19:02:36 +00:00
|
|
|
self.message = None
|
|
|
|
self.mediaObject = Phonon.MediaObject()
|
|
|
|
self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory)
|
|
|
|
Phonon.createPath(self.mediaObject, self.audioObject)
|
|
|
|
|
|
|
|
def setup(self):
|
|
|
|
"""
|
|
|
|
Sets up the Audio Player for use
|
|
|
|
"""
|
|
|
|
log.debug(u'AudioPlayer Setup')
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
"""
|
|
|
|
Shutting down so clean up connections
|
|
|
|
"""
|
|
|
|
self.onMediaStop()
|
2010-07-07 14:44:22 +00:00
|
|
|
for path in self.mediaObject.outputPaths():
|
2010-07-08 08:14:10 +00:00
|
|
|
path.disconnect()
|
2010-07-03 19:02:36 +00:00
|
|
|
|
|
|
|
def onMediaQueue(self, message):
|
|
|
|
"""
|
|
|
|
Set up a video to play from the serviceitem.
|
|
|
|
"""
|
|
|
|
log.debug(u'AudioPlayer Queue new media message %s' % message)
|
2010-08-05 20:39:20 +00:00
|
|
|
mfile = os.path.join(message[0].get_frame_path(),
|
2010-07-03 19:02:36 +00:00
|
|
|
message[0].get_frame_title())
|
2010-08-05 20:39:20 +00:00
|
|
|
self.mediaObject.setCurrentSource(Phonon.MediaSource(mfile))
|
2010-07-03 19:02:36 +00:00
|
|
|
self.onMediaPlay()
|
|
|
|
|
|
|
|
def onMediaPlay(self):
|
|
|
|
"""
|
|
|
|
We want to play the play so start it
|
|
|
|
"""
|
|
|
|
log.debug(u'AudioPlayer _play called')
|
|
|
|
self.mediaObject.play()
|
|
|
|
|
|
|
|
def onMediaPause(self):
|
|
|
|
"""
|
|
|
|
Pause the Audio
|
|
|
|
"""
|
|
|
|
log.debug(u'AudioPlayer Media paused by user')
|
|
|
|
self.mediaObject.pause()
|
|
|
|
|
|
|
|
def onMediaStop(self):
|
|
|
|
"""
|
|
|
|
Stop the Audio and clean up
|
|
|
|
"""
|
|
|
|
log.debug(u'AudioPlayer Media stopped by user')
|
|
|
|
self.message = None
|
|
|
|
self.mediaObject.stop()
|
|
|
|
self.onMediaFinish()
|
|
|
|
|
|
|
|
def onMediaFinish(self):
|
|
|
|
"""
|
|
|
|
Clean up the Object queue
|
|
|
|
"""
|
|
|
|
log.debug(u'AudioPlayer Reached end of media playlist')
|
2010-07-26 16:42:19 +00:00
|
|
|
self.mediaObject.clearQueue()
|