From 111304495502b57752de708b6c9767678c81d287 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 19 May 2013 20:56:48 +0100 Subject: [PATCH] Add basic Live display to remote --- openlp/core/ui/slidecontroller.py | 25 ++++--- openlp/plugins/remotes/html/live.html | 42 +++++++++++ openlp/plugins/remotes/html/live.js | 92 ++++++++++++++++++++++++ openlp/plugins/remotes/lib/httpserver.py | 16 +++++ 4 files changed, 164 insertions(+), 11 deletions(-) create mode 100644 openlp/plugins/remotes/html/live.html create mode 100644 openlp/plugins/remotes/html/live.js diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 86b114e1e..e2d318549 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -136,6 +136,8 @@ class SlideController(DisplayController): self.keypress_loop = False self.category = UiStrings().LiveToolbar ActionList.get_instance().add_category(unicode(self.category), CategoryOrder.standard_toolbar) + self.slide_count = 0 + self.slide_image = None else: Registry().register(u'preview_controller', self) self.type_label.setText(UiStrings().Preview) @@ -1050,27 +1052,28 @@ class SlideController(DisplayController): def updatePreview(self): """ - This updates the preview frame, for example after changing a slide or - using *Blank to Theme*. + This updates the preview frame, for example after changing a slide or using *Blank to Theme*. """ log.debug(u'updatePreview %s ' % self.screens.current[u'primary']) if not self.screens.current[u'primary'] and self.service_item and \ self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay): - # Grab now, but try again in a couple of seconds if slide change - # is slow - QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) - QtCore.QTimer.singleShot(2.5, self.grabMainDisplay) + # Grab now, but try again in a couple of seconds if slide change is slow + QtCore.QTimer.singleShot(0.5, self.grab_maindisplay) + QtCore.QTimer.singleShot(2.5, self.grab_maindisplay) else: - self.slidePreview.setPixmap(self.display.preview()) + self.slide_image = self.display.preview() + self.slidePreview.setPixmap(self.slide_image) + self.slide_count += 1 - def grabMainDisplay(self): + def grab_maindisplay(self): """ Creates an image of the current screen and updates the preview frame. """ - winid = QtGui.QApplication.desktop().winId() + win_id = QtGui.QApplication.desktop().winId() rect = self.screens.current[u'size'] - winimg = QtGui.QPixmap.grabWindow(winid, rect.x(), rect.y(), rect.width(), rect.height()) - self.slidePreview.setPixmap(winimg) + win_image = QtGui.QPixmap.grabWindow(win_id, rect.x(), rect.y(), rect.width(), rect.height()) + self.slidePreview.setPixmap(win_image) + self.slide_image = win_image def on_slide_selected_next_action(self, checked): """ diff --git a/openlp/plugins/remotes/html/live.html b/openlp/plugins/remotes/html/live.html new file mode 100644 index 000000000..2f505f98e --- /dev/null +++ b/openlp/plugins/remotes/html/live.html @@ -0,0 +1,42 @@ + + + + + + ${live_title} + + + + + + + +
A
+ + \ No newline at end of file diff --git a/openlp/plugins/remotes/html/live.js b/openlp/plugins/remotes/html/live.js new file mode 100644 index 000000000..a44ce02ca --- /dev/null +++ b/openlp/plugins/remotes/html/live.js @@ -0,0 +1,92 @@ +/****************************************************************************** + * OpenLP - Open Source Lyrics Projection * + * --------------------------------------------------------------------------- * + * 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, * + * Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. * + * 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 * + * --------------------------------------------------------------------------- * + * 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 * + ******************************************************************************/ +window.OpenLP = { + loadSlide: function (event) { + $.getJSON( + "/live/image", + function (data, status) { + OpenLP.currentSlides = data.results.slides; + OpenLP.currentSlide = 0; + OpenLP.currentTags = Array(); + var div = $("#verseorder"); + div.html(""); + var tag = ""; + var tags = 0; + var lastChange = 0; + $.each(data.results.slides, function(idx, slide) { + var prevtag = tag; + tag = slide["tag"]; + if (tag != prevtag) { + // If the tag has changed, add new one to the list + lastChange = idx; + tags = tags + 1; + div.append(" "); + $("#verseorder span").last().attr("id", "tag" + tags).text(tag); + } + else { + if ((slide["text"] == data.results.slides[lastChange]["text"]) && + (data.results.slides.length > idx + (idx - lastChange))) { + // If the tag hasn't changed, check to see if the same verse + // has been repeated consecutively. Note the verse may have been + // split over several slides, so search through. If so, repeat the tag. + var match = true; + for (var idx2 = 0; idx2 < idx - lastChange; idx2++) { + if(data.results.slides[lastChange + idx2]["text"] != data.results.slides[idx + idx2]["text"]) { + match = false; + break; + } + } + if (match) { + lastChange = idx; + tags = tags + 1; + div.append(" "); + $("#verseorder span").last().attr("id", "tag" + tags).text(tag); + } + } + } + OpenLP.currentTags[idx] = tags; + if (slide["selected"]) + OpenLP.currentSlide = idx; + }) + } + ); + }, + pollServer: function () { + $.getJSON( + "/live/poll", + function (data, status) { + if (OpenLP.slideCount != data.results.slide_count) { + OpenLP.slideCount = data.results.slide_count; + OpenLP.loadSlide(); + } + } + ); + } +} +$.ajaxSetup({ cache: false }); +setInterval("OpenLP.pollServer();", 500); +OpenLP.pollServer(); + diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index eedc30102..7a7acab2b 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -265,9 +265,11 @@ class HttpRouter(object): self.routes = [ (u'^/$', self.serve_file), (u'^/(stage)$', self.serve_file), + (u'^/(live)$', self.serve_file), (r'^/files/(.*)$', self.serve_file), (r'^/api/poll$', self.poll), (r'^/stage/poll$', self.poll), + (r'^/live/poll$', self.live_poll), (r'^/api/controller/(live|preview)/(.*)$', self.controller), (r'^/stage/controller/(live|preview)/(.*)$', self.controller), (r'^/api/service/(.*)$', self.service), @@ -305,6 +307,7 @@ class HttpRouter(object): if response: return response else: + log.debug('Path not found %s', url_path) return self._http_not_found() def _get_service_items(self): @@ -334,6 +337,7 @@ class HttpRouter(object): self.template_vars = { 'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Remote'), 'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Stage View'), + 'live_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Live View'), 'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'), 'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'), 'alerts': translate('RemotePlugin.Mobile', 'Alerts'), @@ -371,6 +375,8 @@ class HttpRouter(object): filename = u'index.html' elif filename == u'stage': filename = u'stage.html' + elif filename == u'live': + filename = u'live.html' path = os.path.normpath(os.path.join(self.html_dir, filename)) if not path.startswith(self.html_dir): return self._http_not_found() @@ -425,6 +431,16 @@ class HttpRouter(object): cherrypy.response.headers['Content-Type'] = u'application/json' return json.dumps({u'results': result}) + def live_poll(self): + """ + Poll OpenLP to determine the current display value. + """ + result = { + u'slide_count': self.live_controller.slide_count + } + cherrypy.response.headers['Content-Type'] = u'application/json' + return json.dumps({u'results': result}) + def display(self, action): """ Hide or show the display screen.