← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~trb143/openlp/images into lp:openlp

 

Tim Bentley has proposed merging lp:~trb143/openlp/images into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)


Just checking changes
-- 
https://code.launchpad.net/~trb143/openlp/images/+merge/39217
Your team OpenLP Core is requested to review the proposed merge of lp:~trb143/openlp/images into lp:openlp.
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2010-10-15 15:37:29 +0000
+++ openlp/core/lib/__init__.py	2010-10-24 06:36:06 +0000
@@ -314,6 +314,7 @@
 
 from spelltextedit import SpellTextEdit
 from eventreceiver import Receiver
+from imagemanager import ImageManager
 from settingsmanager import SettingsManager
 from plugin import PluginStatus, StringContent, Plugin
 from pluginmanager import PluginManager

=== modified file 'openlp/core/lib/htmlbuilder.py'
--- openlp/core/lib/htmlbuilder.py	2010-10-07 21:49:25 +0000
+++ openlp/core/lib/htmlbuilder.py	2010-10-24 06:36:06 +0000
@@ -90,16 +90,16 @@
     var transition = %s;
 
     function show_video(state, path, volume, loop){
-        // Note, the preferred method for looping would be to use the 
+        // Note, the preferred method for looping would be to use the
         // video tag loop attribute.
         // But QtWebKit doesn't support this. Neither does it support the
         // onended event, hence the setInterval()
         // In addition, setting the currentTime attribute to zero to restart
         // the video raises an INDEX_SIZE_ERROR: DOM Exception 1
-        // To complicate it further, sometimes vid.currentTime stops 
+        // To complicate it further, sometimes vid.currentTime stops
         // slightly short of vid.duration and vid.ended is intermittent!
         //
-        // Note, currently the background may go black between loops. Not 
+        // Note, currently the background may go black between loops. Not
         // desirable. Need to investigate using two <video>'s, and hiding/
         // preloading one, and toggle between the two when looping.
 
@@ -132,8 +132,8 @@
                 vid.style.visibility = 'visible';
                 if(vid.looping){
                     video_timer = setInterval(
-                        function() { 
-                            show_video('poll'); 
+                        function() {
+                            show_video('poll');
                         }, 200);
                 }
                 break;
@@ -328,6 +328,7 @@
     height = screen[u'size'].height()
     theme = item.themedata
     webkitvers = webkit_version()
+    # Image generated and poked in
     if item.bg_image_bytes:
         image = u'src="data:image/png;base64,%s"' % item.bg_image_bytes
     else:
@@ -455,7 +456,7 @@
             if theme.display_outline and webkitvers < 534.3:
                 shadow = u'padding-left: %spx; padding-top: %spx;' % \
                     (int(theme.display_shadow_size) +
-                    (int(theme.display_outline_size) * 2), 
+                    (int(theme.display_outline_size) * 2),
                     theme.display_shadow_size)
                 shadow += build_lyrics_outline_css(theme, True)
             else:

=== added file 'openlp/core/lib/imagemanager.py'
--- openlp/core/lib/imagemanager.py	1970-01-01 00:00:00 +0000
+++ openlp/core/lib/imagemanager.py	2010-10-24 06:36:06 +0000
@@ -0,0 +1,158 @@
+# -*- 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      #
+# 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                          #
+###############################################################################
+"""
+Provides the store and management for Images automatically caching them and
+resizing them when needed.  Only one copy of each image is needed in the system.
+A Thread is used to convert the image to a byte array so the user does not need
+to wait for the conversion to happen.
+"""
+import logging
+import os
+import time
+
+from PyQt4 import QtCore
+
+from openlp.core.lib import resize_image, image_to_byte
+
+log = logging.getLogger(__name__)
+
+class ImageThread(QtCore.QThread):
+    """
+    A special Qt thread class to speed up the display of text based frames.
+    This is threaded so it loads the frames in background
+    """
+    def __init__(self, manager):
+        QtCore.QThread.__init__(self, None)
+        self.image_mamager = manager
+
+    def run(self):
+        """
+        Run the thread.
+        """
+        self.image_mamager.process()
+
+
+class Image(object):
+    name = ''
+    path = ''
+    dirty = True
+    image = None
+    image_bytes = None
+
+class ImageManager(QtCore.QObject):
+    """
+    Image Manager handles the conversion and sizing of images.
+
+    """
+    log.info(u'Image Manager loaded')
+
+    def __init__(self):
+        self._cache = {}
+        self._thread_running = False
+        self._cache_dirty = False
+        self.image_thread = ImageThread(self)
+
+    def update_display(self, width, height):
+        """
+        Screen has changed size so rebuild the cache to new size
+        """
+        log.debug(u'update_display')
+        self.width = width
+        self.height = height
+        # mark the images as dirty for a rebuild
+        for key in self._cache.keys():
+            image = self._cache[key]
+            image.dirty = True
+            fullpath = os.path.join(image.path, image.name)
+            image.image = resize_image(fullpath,
+                self.width, self.height)
+        self._cache_dirty = True
+        # only one thread please
+        if not self._thread_running:
+            self.image_thread.start()
+
+    def get_image(self, name):
+        """
+        Return the Qimage from the cache
+        """
+        log.debug(u'get_image %s' % name)
+        return self._cache[name].image
+
+    def get_image_bytes(self, name):
+        """
+        Returns the byte string for an image
+        If not present wait for the background thread to process it.
+        """
+        log.debug(u'get_image_bytes %s' % name)
+        if not self._cache[name].image_bytes:
+            while self._cache[name].dirty:
+                log.debug(u'get_image_bytes - waiting')
+                time.sleep(0.1)
+        return self._cache[name].image_bytes
+
+    def add_image(self, name, path):
+        """
+        Add image to cache if it is not already there
+        """
+        log.debug(u'add_image %s:%s' % (name, path))
+        if not name in self._cache:
+            image = Image()
+            image.name = name
+            image.path = path
+            image.image = resize_image(path,
+                self.width, self.height)
+            self._cache[name] = image
+        self._cache_dirty = True
+        # only one thread please
+        if not self._thread_running:
+            self.image_thread.start()
+
+    def process(self):
+        """
+        Controls the processing called from a QThread
+        """
+        log.debug(u'process - started')
+        self._thread_running = True
+        self.clean_cache()
+        # data loaded since we started ?
+        while self._cache_dirty:
+            log.debug(u'process - recycle')
+            self.clean_cache()
+        self._thread_running = False
+        log.debug(u'process - ended')
+
+    def clean_cache(self):
+        """
+        Actually does the work.
+        """
+        log.debug(u'clean_cache')
+        # we will clean the cache now
+        self._cache_dirty = False
+        for key in self._cache.keys():
+            image = self._cache[key]
+            if image.dirty:
+                image.image_bytes = image_to_byte(image.image)
+                image.dirty = False

=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2010-09-29 20:39:15 +0000
+++ openlp/core/lib/renderer.py	2010-10-24 06:36:06 +0000
@@ -51,11 +51,6 @@
         self._rect = None
         self.theme_name = None
         self._theme = None
-        self._bg_image_filename = None
-        self.frame = None
-        self.bg_frame = None
-        self.bg_image = None
-        self.bg_image_bytes = None
 
     def set_theme(self, theme):
         """
@@ -66,14 +61,7 @@
         """
         log.debug(u'set theme')
         self._theme = theme
-        self.bg_frame = None
-        self.bg_image = None
-        self.bg_image_bytes = None
-        self._bg_image_filename = None
         self.theme_name = theme.theme_name
-        if theme.background_type == u'image':
-            if theme.background_filename:
-                self._bg_image_filename = unicode(theme.background_filename)
 
     def set_text_rectangle(self, rect_main, rect_footer):
         """
@@ -105,39 +93,6 @@
             (build_lyrics_format_css(self._theme, self.page_width,
             self.page_height), build_lyrics_outline_css(self._theme))
 
-    def set_frame_dest(self, frame_width, frame_height):
-        """
-        Set the size of the slide.
-
-        ``frame_width``
-            The width of the slide.
-
-        ``frame_height``
-            The height of the slide.
-
-        """
-        log.debug(u'set frame dest (frame) w %d h %d', frame_width,
-            frame_height)
-        self.frame = QtGui.QImage(frame_width, frame_height,
-            QtGui.QImage.Format_ARGB32_Premultiplied)
-        if self._bg_image_filename and not self.bg_image:
-            self.bg_image = resize_image(self._bg_image_filename,
-                self.frame.width(), self.frame.height())
-        if self._theme.background_type == u'image':
-            self.bg_frame = QtGui.QImage(self.frame.width(),
-                self.frame.height(),
-                QtGui.QImage.Format_ARGB32_Premultiplied)
-            painter = QtGui.QPainter()
-            painter.begin(self.bg_frame)
-            painter.fillRect(self.frame.rect(), QtCore.Qt.black)
-            if self.bg_image:
-                painter.drawImage(0, 0, self.bg_image)
-            painter.end()
-            self.bg_image_bytes = image_to_byte(self.bg_frame)
-        else:
-            self.bg_frame = None
-            self.bg_image_bytes = None
-
     def format_slide(self, words, line_break):
         """
         Figure out how much text can appear on a slide, using the current

=== modified file 'openlp/core/lib/rendermanager.py'
--- openlp/core/lib/rendermanager.py	2010-09-27 18:34:40 +0000
+++ openlp/core/lib/rendermanager.py	2010-10-24 06:36:06 +0000
@@ -28,7 +28,7 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.lib import Renderer, ThemeLevel, ServiceItem
+from openlp.core.lib import Renderer, ThemeLevel, ServiceItem, ImageManager
 from openlp.core.ui import MainDisplay
 
 log = logging.getLogger(__name__)
@@ -56,7 +56,9 @@
         """
         log.debug(u'Initilisation started')
         self.screens = screens
+        self.image_manager = ImageManager()
         self.display = MainDisplay(self, screens, False)
+        self.display.imageManager = self.image_manager
         self.display.setup()
         self.theme_manager = theme_manager
         self.renderer = Renderer()
@@ -75,9 +77,11 @@
         log.debug(u'Update Display')
         self.calculate_default(self.screens.current[u'size'])
         self.display = MainDisplay(self, self.screens, False)
+        self.display.imageManager = self.image_manager
         self.display.setup()
         self.renderer.bg_frame = None
         self.themedata = None
+        self.image_manager.update_display(self.width, self.height)
 
     def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
         """
@@ -153,7 +157,8 @@
             self.calculate_default(self.screens.current[u'size'])
             self.renderer.set_theme(self.themedata)
             self.build_text_rectangle(self.themedata)
-            self.renderer.set_frame_dest(self.width, self.height)
+            self.image_manager.add_image(self.themedata.theme_name,
+                self.themedata.background_filename)
         return self.renderer._rect, self.renderer._rect_footer
 
     def build_text_rectangle(self, theme):
@@ -211,7 +216,7 @@
         serviceItem.raw_footer = footer
         serviceItem.render(True)
         self.display.buildHtml(serviceItem)
-        raw_html = serviceItem.get_rendered_frame(0)[1]
+        raw_html = serviceItem.get_rendered_frame(0)
         preview = self.display.text(raw_html)
         # Reset the real screen size for subsequent render requests
         self.calculate_default(self.screens.current[u'size'])

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2010-10-15 08:35:00 +0000
+++ openlp/core/lib/serviceitem.py	2010-10-24 06:36:06 +0000
@@ -30,7 +30,6 @@
 
 import logging
 import os
-import time
 import uuid
 
 from PyQt4 import QtGui
@@ -160,12 +159,10 @@
             theme = self.theme
         self.main, self.footer = \
             self.render_manager.set_override_theme(theme, useOverride)
-        self.bg_image_bytes = self.render_manager.renderer.bg_image_bytes
         self.themedata = self.render_manager.renderer._theme
         if self.service_item_type == ServiceItemType.Text:
             log.debug(u'Formatting slides')
             for slide in self._raw_frames:
-                before = time.time()
                 formatted = self.render_manager \
                     .format_slide(slide[u'raw_slide'], line_break)
                 for page in formatted:
@@ -174,12 +171,8 @@
                         u'text': clean_tags(page.rstrip()),
                         u'html': expand_tags(page.rstrip()),
                         u'verseTag': slide[u'verseTag'] })
-                log.log(15, u'Formatting took %4s' % (time.time() - before))
-        elif self.service_item_type == ServiceItemType.Image:
-            for slide in self._raw_frames:
-                slide[u'image'] = resize_image(slide[u'image'],
-                    self.render_manager.width, self.render_manager.height)
-        elif self.service_item_type == ServiceItemType.Command:
+        elif self.service_item_type == ServiceItemType.Image or \
+            self.service_item_type == ServiceItemType.Command:
             pass
         else:
             log.error(u'Invalid value renderer :%s' % self.service_item_type)
@@ -192,7 +185,7 @@
                 else:
                     self.foot_text = u'%s<br>%s' % (self.foot_text, foot)
 
-    def add_from_image(self, path, title, image):
+    def add_from_image(self, path, title):
         """
         Add an image slide to the service item.
 
@@ -201,13 +194,11 @@
 
         ``title``
             A title for the slide in the service item.
-
-        ``image``
-            The actual image file name.
         """
         self.service_item_type = ServiceItemType.Image
         self._raw_frames.append(
-            {u'title': title, u'image': image, u'path': path})
+            {u'title': title, u'path': path})
+        self.render_manager.image_manager.add_image(title, path)
         self._new_item()
 
     def add_from_text(self, title, raw_slide, verse_tag=None):
@@ -241,7 +232,7 @@
         """
         self.service_item_type = ServiceItemType.Command
         self._raw_frames.append(
-            {u'title': file_name, u'image': image, u'path': path})
+            {u'title': file_name, u'image':image, u'path': path})
         self._new_item()
 
     def get_service_repr(self):
@@ -310,8 +301,7 @@
         elif self.service_item_type == ServiceItemType.Image:
             for text_image in serviceitem[u'serviceitem'][u'data']:
                 filename = os.path.join(path, text_image)
-                real_image = QtGui.QImage(unicode(filename))
-                self.add_from_image(path, text_image, real_image)
+                self.add_from_image(filename, text_image)
         elif self.service_item_type == ServiceItemType.Command:
             for text_image in serviceitem[u'serviceitem'][u'data']:
                 filename = os.path.join(path, text_image[u'title'])
@@ -387,9 +377,11 @@
         renders it if required.
         """
         if self.service_item_type == ServiceItemType.Text:
-            return None, self._display_frames[row][u'html'].split(u'\n')[0]
+            return self._display_frames[row][u'html'].split(u'\n')[0]
+        elif self.service_item_type == ServiceItemType.Image:
+            return self._raw_frames[row][u'title']
         else:
-            return self._raw_frames[row][u'image'], u''
+            return self._raw_frames[row][u'image']
 
     def get_frame_title(self, row=0):
         """

=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py	2010-10-17 14:25:03 +0000
+++ openlp/core/ui/maindisplay.py	2010-10-24 06:36:06 +0000
@@ -225,7 +225,14 @@
                 shrinkItem.resize(self.screen[u'size'].width(),
                     self.screen[u'size'].height())
 
-    def image(self, image):
+    def directImage(self, name, path):
+        """
+        API for replacement backgrounds so Images are added directly to cache
+        """
+        image = self.imageManager.add_image(name, path)
+        self.image(name)
+
+    def image(self, name):
         """
         Add an image as the background.  The image is converted to a
         bytestream on route.
@@ -234,25 +241,20 @@
             The Image to be displayed can be QImage or QPixmap
         """
         log.debug(u'image to display')
-        if not isinstance(image, QtGui.QImage):
-            image = resize_image(image, self.screen[u'size'].width(),
-                self.screen[u'size'].height())
+        image = self.imageManager.get_image_bytes(name)
         self.resetVideo()
         self.displayImage(image)
         # show screen
         if self.isLive:
             self.setVisible(True)
+        return self.preview()
 
     def displayImage(self, image):
         """
         Display an image, as is.
         """
         if image:
-            if isinstance(image, QtGui.QImage):
-                js = u'show_image("data:image/png;base64,%s");' % \
-                    image_to_byte(image)
-            else:
-                js = u'show_image("data:image/png;base64,%s");' % image
+            js = u'show_image("data:image/png;base64,%s");' % image
         else:
             js = u'show_image("");'
         self.frame.evaluateJavaScript(js)
@@ -399,6 +401,9 @@
         self.loaded = False
         self.initialFrame = False
         self.serviceItem = serviceItem
+        if self.serviceItem.themedata.background_filename:
+            self.serviceItem.bg_image_bytes = self.imageManager. \
+                get_image_bytes(self.serviceItem.themedata.theme_name)
         html = build_html(self.serviceItem, self.screen, self.parent.alertTab,
             self.isLive)
         log.debug(u'buildHtml - pre setHtml')

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2010-10-17 07:26:14 +0000
+++ openlp/core/ui/slidecontroller.py	2010-10-24 06:36:06 +0000
@@ -26,6 +26,7 @@
 
 import logging
 import os
+import time
 
 from PyQt4 import QtCore, QtGui
 from PyQt4.phonon import Phonon
@@ -400,6 +401,7 @@
         log.debug(u'screenSizeChanged live = %s' % self.isLive)
         # rebuild display as screen size changed
         self.display = MainDisplay(self, self.screens, self.isLive)
+        self.display.imageManager = self.parent.RenderManager.image_manager
         self.display.alertTab = self.alertTab
         self.ratio = float(self.screens.current[u'size'].width()) / \
             float(self.screens.current[u'size'].height())
@@ -585,13 +587,14 @@
                 label = QtGui.QLabel()
                 label.setMargin(4)
                 label.setScaledContents(True)
-                if isinstance(frame[u'image'], QtGui.QImage):
-                    label.setPixmap(QtGui.QPixmap.fromImage(frame[u'image']))
-                else:
-                    pixmap = resize_image(frame[u'image'],
+                if self.serviceItem.is_command():
+                    image = resize_image(frame[u'image'],
                         self.parent.RenderManager.width,
                         self.parent.RenderManager.height)
-                    label.setPixmap(QtGui.QPixmap.fromImage(pixmap))
+                else:
+                    image = self.parent.RenderManager.image_manager. \
+                            get_image(frame[u'title'])
+                label.setPixmap(QtGui.QPixmap.fromImage(image))
                 self.PreviewListWidget.setCellWidget(framenumber, 0, label)
                 slideHeight = width * self.parent.RenderManager.screen_ratio
                 row += 1
@@ -782,15 +785,12 @@
                     [self.serviceItem, self.isLive, row])
                 self.updatePreview()
             else:
-                frame, raw_html = self.serviceItem.get_rendered_frame(row)
+                toDisplay = self.serviceItem.get_rendered_frame(row)
                 if self.serviceItem.is_text():
-                    frame = self.display.text(raw_html)
-                else:
-                    self.display.image(frame)
-                if isinstance(frame, QtGui.QImage):
-                    self.SlidePreview.setPixmap(QtGui.QPixmap.fromImage(frame))
-                else:
-                    self.SlidePreview.setPixmap(QtGui.QPixmap(frame))
+                    frame = self.display.text(toDisplay)
+                else:
+                    frame = self.display.image(toDisplay)
+                self.SlidePreview.setPixmap(QtGui.QPixmap.fromImage(frame))
             self.selectedRow = row
         Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
             row)

=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py	2010-09-27 18:34:40 +0000
+++ openlp/plugins/images/lib/mediaitem.py	2010-10-24 06:36:06 +0000
@@ -166,9 +166,8 @@
             for item in items:
                 bitem = self.listView.item(item.row())
                 filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
-                frame = QtGui.QImage(unicode(filename))
                 (path, name) = os.path.split(filename)
-                service_item.add_from_image(path, name, frame)
+                service_item.add_from_image(filename, name)
             return True
         else:
             return False
@@ -185,7 +184,8 @@
             for item in items:
                 bitem = self.listView.item(item.row())
                 filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
-                self.parent.liveController.display.image(filename)
+                (path, name) = os.path.split(filename)
+                self.parent.liveController.display.directImage(name, filename)
         self.resetButton.setVisible(True)
 
     def onPreviewClick(self):