From 111304495502b57752de708b6c9767678c81d287 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 19 May 2013 20:56:48 +0100 Subject: [PATCH 1/6] 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. From e1f3024ff1e2965e770a7cd68f6aea942e0efab1 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 21 May 2013 18:45:33 +0100 Subject: [PATCH 2/6] Latest version --- .../presentations/lib/messagelistener.py | 1 + openlp/plugins/remotes/html/live.html | 14 ++++-- openlp/plugins/remotes/html/live.js | 46 ++----------------- openlp/plugins/remotes/lib/httpserver.py | 38 ++++++++++++--- 4 files changed, 46 insertions(+), 53 deletions(-) diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 330c36f5c..219cae9a6 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -36,6 +36,7 @@ from openlp.core.ui import HideMode log = logging.getLogger(__name__) + class Controller(object): """ This is the Presentation listener who acts on events from the slide controller and passes the messages on the the diff --git a/openlp/plugins/remotes/html/live.html b/openlp/plugins/remotes/html/live.html index 2f505f98e..33a14fd52 100644 --- a/openlp/plugins/remotes/html/live.html +++ b/openlp/plugins/remotes/html/live.html @@ -29,14 +29,22 @@ --> - ${live_title} + ${live_title}A + - -
A
+ \ No newline at end of file diff --git a/openlp/plugins/remotes/html/live.js b/openlp/plugins/remotes/html/live.js index a44ce02ca..d55072c16 100644 --- a/openlp/plugins/remotes/html/live.js +++ b/openlp/plugins/remotes/html/live.js @@ -28,49 +28,9 @@ window.OpenLP = { $.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; - }) + var img = document.getElementById('image'); + img.src = data.results.slide_image; + img.style.display = 'block'; } ); }, diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 7a7acab2b..8d4872cfc 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -124,7 +124,7 @@ import cherrypy from mako.template import Template from PyQt4 import QtCore -from openlp.core.lib import Registry, Settings, PluginStatus, StringContent +from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte from openlp.core.utils import AppLocation, translate from cherrypy._cpcompat import sha, ntob @@ -175,9 +175,11 @@ class HttpServer(object): self.root = self.Public() self.root.files = self.Files() self.root.stage = self.Stage() + self.root.live = self.Live() self.root.router = self.router self.root.files.router = self.router self.root.stage.router = self.router + self.root.live.router = self.router cherrypy.tree.mount(self.root, '/', config=self.define_config()) # Turn off the flood of access messages cause by poll cherrypy.log.access_log.propagate = False @@ -212,6 +214,9 @@ class HttpServer(object): u'tools.staticdir.dir': self.router.html_dir, u'tools.basic_auth.on': False}, u'/stage': {u'tools.staticdir.on': True, + u'tools.staticdir.dir': self.router.html_dir, + u'tools.basic_auth.on': False}, + u'/live': {u'tools.staticdir.on': True, u'tools.staticdir.dir': self.router.html_dir, u'tools.basic_auth.on': False}} return directory_config @@ -239,7 +244,16 @@ class HttpServer(object): class Stage(object): """ - Stageview is read only so security is not relevant and would reduce it's usability + Stage view is read only so security is not relevant and would reduce it's usability + """ + @cherrypy.expose + def default(self, *args, **kwargs): + url = urlparse.urlparse(cherrypy.url()) + return self.router.process_http_request(url.path, *args) + + class Live(object): + """ + Live view is read only so security is not relevant and would reduce it's usability """ @cherrypy.expose def default(self, *args, **kwargs): @@ -270,6 +284,7 @@ class HttpRouter(object): (r'^/api/poll$', self.poll), (r'^/stage/poll$', self.poll), (r'^/live/poll$', self.live_poll), + (r'^/live/image$', self.live_image), (r'^/api/controller/(live|preview)/(.*)$', self.controller), (r'^/stage/controller/(live|preview)/(.*)$', self.controller), (r'^/api/service/(.*)$', self.service), @@ -363,12 +378,11 @@ class HttpRouter(object): def serve_file(self, filename=None): """ - Send a file to the socket. For now, just a subset of file types - and must be top level inside the html folder. + Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder. If subfolders requested return 404, easier for security for the present. - Ultimately for i18n, this could first look for xx/file.html before - falling back to file.html... where xx is the language, e.g. 'en' + Ultimately for i18n, this could first look for xx/file.html before falling back to file.html. + where xx is the language, e.g. 'en' """ log.debug(u'serve file request %s' % filename) if not filename: @@ -433,7 +447,7 @@ class HttpRouter(object): def live_poll(self): """ - Poll OpenLP to determine the current display value. + Poll OpenLP to determine the current slide count. """ result = { u'slide_count': self.live_controller.slide_count @@ -441,6 +455,16 @@ class HttpRouter(object): cherrypy.response.headers['Content-Type'] = u'application/json' return json.dumps({u'results': result}) + def live_image(self): + """ + Return the latest display image as a byte stream. + """ + result = { + u'slide_image': str(image_to_byte(self.live_controller.slide_image)) + } + cherrypy.response.headers['Content-Type'] = u'application/json' + return json.dumps({u'results': result}) + def display(self, action): """ Hide or show the display screen. From 20bd86fbce5d60f03b0b7d300b93f29c4adc174f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 25 May 2013 18:05:44 +0100 Subject: [PATCH 3/6] Live works! --- openlp/core/lib/__init__.py | 3 ++- openlp/plugins/remotes/html/live.html | 2 ++ openlp/plugins/remotes/lib/httpserver.py | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index d6c338271..6bd20f2fd 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -183,7 +183,8 @@ def image_to_byte(image): # use buffer to store pixmap into byteArray buffie = QtCore.QBuffer(byte_array) buffie.open(QtCore.QIODevice.WriteOnly) - image.save(buffie, "PNG") + if isinstance(image, QtGui.QImage): + image.save(buffie, "PNG") log.debug(u'image_to_byte - end') # convert to base64 encoding so does not get missed! return byte_array.toBase64() diff --git a/openlp/plugins/remotes/html/live.html b/openlp/plugins/remotes/html/live.html index 33a14fd52..fed8f6c98 100644 --- a/openlp/plugins/remotes/html/live.html +++ b/openlp/plugins/remotes/html/live.html @@ -42,6 +42,8 @@ top: 0px; width: 100%%; height: 100%%; + background-size: cover; + background-repeat: no-repeat; } diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 8d4872cfc..a2abbb41e 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -136,6 +136,7 @@ def make_sha_hash(password): """ Create an encrypted password for the given password. """ + log.debug("make_sha_hash") return sha(ntob(password)).hexdigest() @@ -143,6 +144,7 @@ def fetch_password(username): """ Fetch the password for a provided user. """ + log.debug("Fetch Password") if username != Settings().value(u'remotes/user id'): return None return make_sha_hash(Settings().value(u'remotes/password')) @@ -460,7 +462,7 @@ class HttpRouter(object): Return the latest display image as a byte stream. """ result = { - u'slide_image': str(image_to_byte(self.live_controller.slide_image)) + u'slide_image': u'data:image/png;base64,' + str(image_to_byte(self.live_controller.slide_image)) } cherrypy.response.headers['Content-Type'] = u'application/json' return json.dumps({u'results': result}) From 512155ff68b54052691df9e8cf704f25ce3844b7 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 3 Jun 2013 18:19:42 +0100 Subject: [PATCH 4/6] Add css file --- openlp/core/lib/__init__.py | 3 +-- openlp/plugins/remotes/html/live.css | 39 +++++++++++++++++++++++++++ openlp/plugins/remotes/html/live.html | 15 ++--------- 3 files changed, 42 insertions(+), 15 deletions(-) create mode 100644 openlp/plugins/remotes/html/live.css diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 6bd20f2fd..d6c338271 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -183,8 +183,7 @@ def image_to_byte(image): # use buffer to store pixmap into byteArray buffie = QtCore.QBuffer(byte_array) buffie.open(QtCore.QIODevice.WriteOnly) - if isinstance(image, QtGui.QImage): - image.save(buffie, "PNG") + image.save(buffie, "PNG") log.debug(u'image_to_byte - end') # convert to base64 encoding so does not get missed! return byte_array.toBase64() diff --git a/openlp/plugins/remotes/html/live.css b/openlp/plugins/remotes/html/live.css new file mode 100644 index 000000000..7181129d9 --- /dev/null +++ b/openlp/plugins/remotes/html/live.css @@ -0,0 +1,39 @@ +/****************************************************************************** +* 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 * +******************************************************************************/ +body { + background-color: black; + font-family: sans-serif; + overflow: hidden; +} + +.size { + position: absolute; + top: 0px; + vertical-align: middle; + height: 100%; + background-size: cover; + background-repeat: no-repeat; +} \ No newline at end of file diff --git a/openlp/plugins/remotes/html/live.html b/openlp/plugins/remotes/html/live.html index fed8f6c98..f9a2c874c 100644 --- a/openlp/plugins/remotes/html/live.html +++ b/openlp/plugins/remotes/html/live.html @@ -29,23 +29,12 @@ --> - ${live_title}A - + ${live_title} + - From 9160832e0a1e3b4e292e8caafd0f65d938c28fc5 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 3 Jun 2013 19:03:45 +0100 Subject: [PATCH 5/6] fix error --- openlp/core/ui/slidecontroller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index e2d318549..51ac4f525 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -121,6 +121,8 @@ class SlideController(DisplayController): self.update_slide_limits() self.panel = QtGui.QWidget(parent.controlSplitter) self.slideList = {} + self.slide_count = 0 + self.slide_image = None # Layout for holding panel self.panel_layout = QtGui.QVBoxLayout(self.panel) self.panel_layout.setSpacing(0) @@ -136,8 +138,6 @@ 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) From 6e5588b7cfc5d2787d4a05ff88715b1b2c5f683a Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 3 Jun 2013 19:47:25 +0100 Subject: [PATCH 6/6] Update settings and minor cleanups --- openlp/core/ui/mainwindow.py | 8 +-- openlp/core/ui/slidecontroller.py | 81 ++++++++++++------------- openlp/plugins/remotes/lib/remotetab.py | 26 ++++++-- 3 files changed, 65 insertions(+), 50 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 2afbb4eb0..6b86bfe7a 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -779,8 +779,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): """ We need to make sure, that the SlidePreview's size is correct. """ - self.preview_controller.previewSizeChanged() - self.live_controller.previewSizeChanged() + self.preview_controller.preview_size_changed() + self.live_controller.preview_size_changed() def on_settings_shortcuts_item_clicked(self): """ @@ -989,8 +989,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.application.set_busy_cursor() self.image_manager.update_display() self.renderer.update_display() - self.preview_controller.screenSizeChanged() - self.live_controller.screenSizeChanged() + self.preview_controller.screen_size_changed() + self.live_controller.screen_size_changed() self.setFocus() self.activateWindow() self.application.set_normal_cursor() diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 51ac4f525..febef0850 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -89,7 +89,7 @@ class SlideController(DisplayController): Set up the Slide Controller. """ DisplayController.__init__(self, parent, is_live) - Registry().register_function(u'bootstrap_post_set_up', self.screenSizeChanged) + Registry().register_function(u'bootstrap_post_set_up', self.screen_size_changed) self.screens = ScreenList() try: self.ratio = float(self.screens.current[u'size'].width()) / float(self.screens.current[u'size'].height()) @@ -323,18 +323,18 @@ class SlideController(DisplayController): self.slide_layout.insertWidget(0, self.preview_display) self.preview_display.hide() # Actual preview screen - self.slidePreview = QtGui.QLabel(self) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.slidePreview.sizePolicy().hasHeightForWidth()) - self.slidePreview.setSizePolicy(sizePolicy) - self.slidePreview.setFrameShape(QtGui.QFrame.Box) - self.slidePreview.setFrameShadow(QtGui.QFrame.Plain) - self.slidePreview.setLineWidth(1) - self.slidePreview.setScaledContents(True) - self.slidePreview.setObjectName(u'slidePreview') - self.slide_layout.insertWidget(0, self.slidePreview) + self.slide_preview = QtGui.QLabel(self) + size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.slide_preview.sizePolicy().hasHeightForWidth()) + self.slide_preview.setSizePolicy(size_policy) + self.slide_preview.setFrameShape(QtGui.QFrame.Box) + self.slide_preview.setFrameShadow(QtGui.QFrame.Plain) + self.slide_preview.setLineWidth(1) + self.slide_preview.setScaledContents(True) + self.slide_preview.setObjectName(u'slide_preview') + self.slide_layout.insertWidget(0, self.slide_preview) self.grid.addLayout(self.slide_layout, 0, 0, 1, 1) if self.is_live: self.current_shortcut = u'' @@ -519,10 +519,9 @@ class SlideController(DisplayController): self.service_manager.next_item() self.keypress_loop = False - def screenSizeChanged(self): + def screen_size_changed(self): """ - Settings dialog has changed the screen size of adjust output and - screen previews. + Settings dialog has changed the screen size of adjust output and screen previews. """ # rebuild display as screen size changed if self.display: @@ -538,14 +537,14 @@ class SlideController(DisplayController): except ZeroDivisionError: self.ratio = 1 self.media_controller.setup_display(self.display, False) - self.previewSizeChanged() + self.preview_size_changed() self.preview_display.setup() service_item = ServiceItem() self.preview_display.web_view.setHtml(build_html(service_item, self.preview_display.screen, None, self.is_live, plugins=self.plugin_manager.plugins)) self.media_controller.setup_display(self.preview_display, True) if self.service_item: - self.refreshServiceItem() + self.refresh_service_item() def __addActionsToWidget(self, widget): """ @@ -556,7 +555,7 @@ class SlideController(DisplayController): self.previousService, self.nextService, self.escapeItem]) - def previewSizeChanged(self): + def preview_size_changed(self): """ Takes care of the SlidePreview's size. Is called when one of the the splitters is moved or when the screen size is changed. Note, that this @@ -565,14 +564,14 @@ class SlideController(DisplayController): if self.ratio < float(self.preview_frame.width()) / float(self.preview_frame.height()): # We have to take the height as limit. max_height = self.preview_frame.height() - self.grid.margin() * 2 - self.slidePreview.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height)) + self.slide_preview.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height)) self.preview_display.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height)) self.preview_display.screen = { u'size': self.preview_display.geometry()} else: # We have to take the width as limit. max_width = self.preview_frame.width() - self.grid.margin() * 2 - self.slidePreview.setFixedSize(QtCore.QSize(max_width, max_width / self.ratio)) + self.slide_preview.setFixedSize(QtCore.QSize(max_width, max_width / self.ratio)) self.preview_display.setFixedSize(QtCore.QSize(max_width, max_width / self.ratio)) self.preview_display.screen = { u'size': self.preview_display.geometry()} @@ -626,17 +625,16 @@ class SlideController(DisplayController): """ self.slide_limits = Settings().value(self.main_window.advanced_settings_section + u'/slide limits') - def enableToolBar(self, item): + def enable_tool_bar(self, item): """ - Allows the toolbars to be reconfigured based on Controller Type - and ServiceItem Type + Allows the toolbars to be reconfigured based on Controller Type and ServiceItem Type """ if self.is_live: - self.enableLiveToolBar(item) + self.enable_live_tool_bar(item) else: - self.enablePreviewToolBar(item) + self.enable_preview_tool_bar(item) - def enableLiveToolBar(self, item): + def enable_live_tool_bar(self, item): """ Allows the live toolbar to be customised """ @@ -665,7 +663,7 @@ class SlideController(DisplayController): # See bug #791050 self.toolbar.show() - def enablePreviewToolBar(self, item): + def enable_preview_tool_bar(self, item): """ Allows the Preview toolbar to be customised """ @@ -684,15 +682,15 @@ class SlideController(DisplayController): # See bug #791050 self.toolbar.show() - def refreshServiceItem(self): + def refresh_service_item(self): """ Method to update the service item if the screen has changed """ - log.debug(u'refreshServiceItem live = %s' % self.is_live) + log.debug(u'refresh_service_item live = %s' % self.is_live) if self.service_item.is_text() or self.service_item.is_image(): item = self.service_item item.render() - self._processItem(item, self.selected_row) + self._process_item(item, self.selected_row) def add_service_item(self, item): """ @@ -705,14 +703,14 @@ class SlideController(DisplayController): if self.song_edit: slideno = self.selected_row self.song_edit = False - self._processItem(item, slideno) + self._process_item(item, slideno) def replaceServiceManagerItem(self, item): """ Replacement item following a remote edit """ if item == self.service_item: - self._processItem(item, self.preview_list_widget.currentRow()) + self._process_item(item, self.preview_list_widget.currentRow()) def addServiceManagerItem(self, item, slideno): """ @@ -731,7 +729,7 @@ class SlideController(DisplayController): self.__checkUpdateSelectedSlide(slidenum) self.slideSelected() else: - self._processItem(item, slidenum) + self._process_item(item, slidenum) if self.is_live and item.auto_play_slides_loop and item.timed_slide_interval > 0: self.play_slides_loop.setChecked(item.auto_play_slides_loop) self.delay_spin_box.setValue(int(item.timed_slide_interval)) @@ -741,7 +739,7 @@ class SlideController(DisplayController): self.delay_spin_box.setValue(int(item.timed_slide_interval)) self.onPlaySlidesOnce() - def _processItem(self, service_item, slideno): + def _process_item(self, service_item, slideno): """ Loads a ServiceItem into the system from ServiceManager Display the slide number passed @@ -829,10 +827,9 @@ class SlideController(DisplayController): self.preview_list_widget.setVerticalHeaderLabels(text) if self.service_item.is_text(): self.preview_list_widget.resizeRowsToContents() - self.preview_list_widget.setColumnWidth(0, - self.preview_list_widget.viewport().size().width()) + self.preview_list_widget.setColumnWidth(0, self.preview_list_widget.viewport().size().width()) self.__updatePreviewSelection(slideno) - self.enableToolBar(service_item) + self.enable_tool_bar(service_item) # Pass to display for viewing. # Postpone image build, we need to do this later to avoid the theme # flashing on the screen @@ -1062,7 +1059,7 @@ class SlideController(DisplayController): QtCore.QTimer.singleShot(2.5, self.grab_maindisplay) else: self.slide_image = self.display.preview() - self.slidePreview.setPixmap(self.slide_image) + self.slide_preview.setPixmap(self.slide_image) self.slide_count += 1 def grab_maindisplay(self): @@ -1072,7 +1069,7 @@ class SlideController(DisplayController): win_id = QtGui.QApplication.desktop().winId() rect = self.screens.current[u'size'] win_image = QtGui.QPixmap.grabWindow(win_id, rect.x(), rect.y(), rect.width(), rect.height()) - self.slidePreview.setPixmap(win_image) + self.slide_preview.setPixmap(win_image) self.slide_image = win_image def on_slide_selected_next_action(self, checked): @@ -1279,7 +1276,7 @@ class SlideController(DisplayController): self.media_controller.video(self.controller_type, item, self.hide_mode()) if not self.is_live: self.preview_display.show() - self.slidePreview.hide() + self.slide_preview.hide() def onMediaClose(self): """ @@ -1288,7 +1285,7 @@ class SlideController(DisplayController): log.debug(u'SlideController onMediaClose') self.media_controller.media_reset(self) self.preview_display.hide() - self.slidePreview.show() + self.slide_preview.show() def _resetBlank(self): """ diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 09934b58c..c8ed9303e 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -86,6 +86,12 @@ class RemoteTab(SettingsTab): self.stage_url.setObjectName(u'stage_url') self.stage_url.setOpenExternalLinks(True) self.http_setting_layout.addRow(self.stage_url_label, self.stage_url) + self.live_url_label = QtGui.QLabel(self.http_settings_group_box) + self.live_url_label.setObjectName(u'live_url_label') + self.live_url = QtGui.QLabel(self.http_settings_group_box) + self.live_url.setObjectName(u'live_url') + self.live_url.setOpenExternalLinks(True) + self.http_setting_layout.addRow(self.live_url_label, self.live_url) self.left_layout.addWidget(self.http_settings_group_box) self.https_settings_group_box = QtGui.QGroupBox(self.left_column) self.https_settings_group_box.setCheckable(True) @@ -116,6 +122,12 @@ class RemoteTab(SettingsTab): self.stage_https_url.setObjectName(u'stage_https_url') self.stage_https_url.setOpenExternalLinks(True) self.https_settings_layout.addRow(self.stage_https_url_label, self.stage_https_url) + self.live_https_url_label = QtGui.QLabel(self.https_settings_group_box) + self.live_https_url_label.setObjectName(u'live_url_label') + self.live_https_url = QtGui.QLabel(self.https_settings_group_box) + self.live_https_url.setObjectName(u'live_https_url') + self.live_https_url.setOpenExternalLinks(True) + self.https_settings_layout.addRow(self.live_https_url_label, self.live_https_url) self.left_layout.addWidget(self.https_settings_group_box) self.user_login_group_box = QtGui.QGroupBox(self.left_column) self.user_login_group_box.setCheckable(True) @@ -163,6 +175,7 @@ class RemoteTab(SettingsTab): self.port_label.setText(translate('RemotePlugin.RemoteTab', 'Port number:')) self.remote_url_label.setText(translate('RemotePlugin.RemoteTab', 'Remote URL:')) self.stage_url_label.setText(translate('RemotePlugin.RemoteTab', 'Stage view URL:')) + self.live_url_label.setText(translate('RemotePlugin.RemoteTab', 'Live view URL:')) self.twelve_hour_check_box.setText(translate('RemotePlugin.RemoteTab', 'Display stage time in 12h format')) self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App')) self.qr_description_label.setText(translate('RemotePlugin.RemoteTab', @@ -176,6 +189,7 @@ class RemoteTab(SettingsTab): self.https_port_label.setText(self.port_label.text()) self.remote_https_url_label.setText(self.remote_url_label.text()) self.stage_https_url_label.setText(self.stage_url_label.text()) + self.live_https_url_label.setText(self.live_url_label.text()) self.user_login_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'User Authentication')) self.user_id_label.setText(translate('RemotePlugin.RemoteTab', 'User id:')) self.password_label.setText(translate('RemotePlugin.RemoteTab', 'Password:')) @@ -203,10 +217,14 @@ class RemoteTab(SettingsTab): https_url = u'https://%s:%s/' % (ip_address, self.https_port_spin_box.value()) self.remote_url.setText(u'%s' % (http_url, http_url)) self.remote_https_url.setText(u'%s' % (https_url, https_url)) - http_url += u'stage' - https_url += u'stage' - self.stage_url.setText(u'%s' % (http_url, http_url)) - self.stage_https_url.setText(u'%s' % (https_url, https_url)) + http_url_temp = http_url + u'stage' + https_url_temp = https_url + u'stage' + self.stage_url.setText(u'%s' % (http_url_temp, http_url_temp)) + self.stage_https_url.setText(u'%s' % (https_url_temp, https_url_temp)) + http_url_temp = http_url + u'live' + https_url_temp = https_url + u'live' + self.live_url.setText(u'%s' % (http_url_temp, http_url_temp)) + self.live_https_url.setText(u'%s' % (https_url_temp, https_url_temp)) def load(self): """