openlp/openlp/core/ui/maindisplay.py

433 lines
16 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
2010-07-24 22:10:47 +00:00
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, Frode Woldsund #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
import logging
import os
from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.phonon import Phonon
2010-08-05 20:39:20 +00:00
from openlp.core.lib import Receiver, resize_image, build_html, ServiceItem, \
image_to_byte
from openlp.core.ui import HideMode
log = logging.getLogger(__name__)
#http://www.steveheffernan.com/html5-video-player/demo-video-player.html
class DisplayWidget(QtGui.QGraphicsView):
"""
Customised version of QTableWidget which can respond to keyboard
events.
"""
2010-07-11 07:45:49 +00:00
log.info(u'Display Widget loaded')
2010-07-24 16:55:06 +00:00
def __init__(self, live, parent=None):
2010-07-11 07:45:49 +00:00
QtGui.QGraphicsView.__init__(self)
self.parent = parent
2010-07-11 15:53:40 +00:00
self.live = live
2010-07-24 16:55:06 +00:00
self.hotkey_map = {
QtCore.Qt.Key_Return: 'servicemanager_next_item',
QtCore.Qt.Key_Space: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_Enter: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_0: 'servicemanager_next_item',
QtCore.Qt.Key_Backspace: 'slidecontroller_live_previous_noloop'}
def keyPressEvent(self, event):
2010-07-23 05:05:34 +00:00
# Key events only needed for live
if not self.live:
2010-07-11 15:53:40 +00:00
return
2010-07-24 16:55:06 +00:00
if isinstance(event, QtGui.QKeyEvent):
# Here accept the event and do something
if event.key() == QtCore.Qt.Key_Up:
Receiver.send_message(u'slidecontroller_live_previous')
event.accept()
elif event.key() == QtCore.Qt.Key_Down:
Receiver.send_message(u'slidecontroller_live_next')
event.accept()
elif event.key() == QtCore.Qt.Key_PageUp:
Receiver.send_message(u'slidecontroller_live_first')
event.accept()
elif event.key() == QtCore.Qt.Key_PageDown:
Receiver.send_message(u'slidecontroller_live_last')
event.accept()
elif event.key() in self.hotkey_map:
Receiver.send_message(self.hotkey_map[event.key()])
event.accept()
elif event.key() == QtCore.Qt.Key_Escape:
2010-08-05 07:49:25 +00:00
self.setVisible(False)
2010-07-24 16:55:06 +00:00
event.accept()
event.ignore()
else:
event.ignore()
2010-07-23 05:05:34 +00:00
class MainDisplay(DisplayWidget):
2010-07-11 15:53:40 +00:00
def __init__(self, parent, screens, live):
DisplayWidget.__init__(self, live, parent=None)
2010-07-12 16:49:38 +00:00
self.parent = parent
2010-07-11 11:38:09 +00:00
self.screens = screens
2010-07-17 08:59:15 +00:00
self.isLive = live
2010-08-01 08:28:31 +00:00
self.alertTab = None
2010-07-11 11:38:09 +00:00
self.setWindowTitle(u'OpenLP Display')
self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
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)
2010-07-11 07:45:49 +00:00
2010-07-23 05:05:34 +00:00
def setup(self):
log.debug(u'Setup %s for %s ' % (
self.screens, self.screens.monitor_number))
self.screen = self.screens.current
self.setVisible(False)
self.setGeometry(self.screen[u'size'])
self.webView = QtWebKit.QWebView(self)
2010-08-05 20:39:20 +00:00
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,
2010-08-09 21:21:04 +00:00
QtCore.SIGNAL(u'loadFinished(bool)'), self.isLoaded)
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(
self.screens.current[u'size'].width(),
self.screens.current[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
painter_image = QtGui.QPainter()
painter_image.begin(self.black)
painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
2010-07-23 05:05:34 +00:00
#Build the initial frame.
initialFrame = QtGui.QImage(
self.screens.current[u'size'].width(),
self.screens.current[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png')
painter_image = QtGui.QPainter()
painter_image.begin(initialFrame)
painter_image.fillRect(initialFrame.rect(), QtCore.Qt.white)
painter_image.drawImage(
2010-08-05 20:39:20 +00:00
(self.screens.current[u'size'].width() \
- splash_image.width()) / 2,
(self.screens.current[u'size'].height() \
- splash_image.height()) / 2,
2010-07-23 05:05:34 +00:00
splash_image)
serviceItem = ServiceItem()
serviceItem.bg_frame = initialFrame
2010-08-05 20:39:20 +00:00
self.webView.setHtml(build_html(serviceItem, self.screen, \
self.parent.alertTab))
2010-08-05 07:49:25 +00:00
self.initialFrame = True
2010-07-23 05:05:34 +00:00
self.show()
2010-07-24 16:55:06 +00:00
# To display or not to display?
if not self.screen[u'primary']:
self.primary = False
else:
self.primary = True
2010-07-23 05:05:34 +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-07-25 08:58:08 +00:00
log.debug(u'text')
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-07-25 08:58:08 +00:00
log.debug(u'alert')
2010-08-09 21:21:04 +00:00
if self.height() != self.screen[u'size'].height() \
or not self.isVisible():
shrink = True
else:
shrink = False
js = u'show_alert("%s", "%s")' % (
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:
if text:
self.resize(self.width(), int(height.toString()))
2010-08-09 21:21:04 +00:00
self.setVisible(True)
2010-08-07 06:18:05 +00:00
else:
self.setGeometry(self.screen[u'size'])
2010-08-09 21:21:04 +00:00
self.setVisible(False)
2010-07-11 07:45:49 +00:00
2010-07-17 11:00:36 +00:00
def image(self, image):
2010-07-24 16:55:06 +00:00
"""
Add an image as the background. The image is converted to a
bytestream on route.
`Image`
The Image to be displayed can be QImage or QPixmap
"""
2010-07-25 08:58:08 +00:00
log.debug(u'image')
image = resize_image(image, self.screen[u'size'].width(),
self.screen[u'size'].height())
2010-08-07 06:18:05 +00:00
self.resetVideo()
self.displayImage(image)
def displayImage(self, image):
"""
Display an image, as is.
"""
if image:
2010-08-09 21:21:04 +00:00
js = u'show_image("data:image/png;base64,%s");' % \
image_to_byte(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
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')
2010-08-07 06:18:05 +00:00
self.displayImage(self.serviceItem.bg_frame)
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-08-09 21:21:04 +00:00
self.frame.evaluateJavaScript(u'show_video("close");')
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-08-09 21:21:04 +00:00
self.frame.evaluateJavaScript(u'show_video("play");')
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-08-09 21:21:04 +00:00
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-08-09 21:21:04 +00:00
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)
2010-08-09 21:21:04 +00:00
self.frame.evaluateJavaScript(u'show_video(null, null, %s);' %
2010-08-04 19:09:43 +00:00
str(float(volume)/float(10)))
2010-08-01 16:46:24 +00:00
2010-08-04 19:09:43 +00:00
def video(self, videoPath, volume):
2010-08-02 05:16:07 +00:00
"""
Loads and starts a video to run with the option of sound
"""
2010-07-25 08:58:08 +00:00
log.debug(u'video')
2010-08-07 06:18:05 +00:00
self.loaded = True
2010-08-09 21:21:04 +00:00
js = u'show_video("play", "%s", %s, true);' % \
(videoPath.replace(u'\\', u'\\\\'), str(float(volume)/float(10)))
2010-08-07 06:18:05 +00:00
self.frame.evaluateJavaScript(js)
2010-08-05 19:01:24 +00:00
return self.preview()
2010-07-11 07:45:49 +00:00
2010-08-09 21:21:04 +00:00
def isLoaded(self):
2010-07-25 08:58:08 +00:00
"""
Called by webView event to show display is fully loaded
"""
2010-08-07 06:18:05 +00:00
log.debug(u'loaded')
2010-07-17 10:25:52 +00:00
self.loaded = True
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-07-25 08:58:08 +00:00
log.debug(u'preview')
2010-08-07 06:18:05 +00:00
# Wait for the fade to finish before geting the preview.
# Important otherwise preview will have incorrect text if at all !
if self.serviceItem.themedata.display_slideTransition:
2010-08-09 21:21:04 +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 !
2010-07-17 10:25:52 +00:00
while not self.loaded:
Receiver.send_message(u'openlp_process_events')
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()
2010-07-25 07:21:10 +00:00
# save preview for debugging
2010-07-17 10:25:52 +00:00
if log.isEnabledFor(logging.DEBUG):
2010-08-09 21:21:04 +00:00
preview.save(u'temp.png', u'png')
2010-07-11 07:45:49 +00:00
return preview
2010-07-17 08:59:15 +00:00
def buildHtml(self, serviceItem):
"""
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')
2010-07-17 10:25:52 +00:00
self.loaded = False
2010-08-05 07:49:25 +00:00
self.initialFrame = False
2010-07-17 08:59:15 +00:00
self.serviceItem = serviceItem
2010-08-03 05:15:50 +00:00
html = build_html(self.serviceItem, self.screen, self.parent.alertTab)
2010-07-12 19:36:42 +00:00
self.webView.setHtml(html)
2010-07-25 07:21:10 +00:00
if serviceItem.footer and serviceItem.foot_text:
2010-08-09 21:21:04 +00:00
self.footer(serviceItem.foot_text)
def footer(self, text):
log.debug(u'footer')
js = "show_footer('" + \
text.replace("\\", "\\\\").replace("\'", "\\\'") + "')"
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)
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-07-23 05:05:34 +00:00
if mode != HideMode.Screen and self.isHidden():
self.setVisible(True)
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-07-24 16:55:06 +00:00
# Trigger actions when display is active again
2010-07-23 05:05:34 +00:00
Receiver.send_message(u'maindisplay_active')
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.
``screens``
The list of screens.
"""
log.debug(u'AudioPlayer Initialisation started')
2010-07-06 21:59:52 +00:00
QtCore.QObject.__init__(self, parent)
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()
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(),
message[0].get_frame_title())
2010-08-05 20:39:20 +00:00
self.mediaObject.setCurrentSource(Phonon.MediaSource(mfile))
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()