← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~googol/openlp/image-queue into lp:openlp

 

Andreas Preikschat has proposed merging lp:~googol/openlp/image-queue into lp:openlp.

Requested reviews:
  Tim Bentley (trb143)

For more details, see:
https://code.launchpad.net/~googol/openlp/image-queue/+merge/112948

Hello

- update the image cache when the image timestamp changes (1. Bug)
- reworked image manager's _cache (2. Bug and 3. Bug)

1. Bug:
1) Display/preview an image.
2) Edit the image.
3) Send it live/preview again.

Result:
Cache is not updated.

Expected:
Cache is updated. You especially expect this when you delete and add the image again.

2. Bug:
1) Open a service file with an image named test.jpg
2) Show it live/preview it
3) Send another image live/preview which has the same name (test.jpg), but a different content.

Result:
The second image shown is not correct.

Expected:
The second image should show the correct image.

3. Bug:
1) Send an item live/preview it (should not have an theme assigned)
2) Change a theme's background image (do not complete the wizard, cancel it on the last page).
3) Send the same item live again

Result:
The item incorrectly uses the image you changed in the theme wizard.

Expected:
The item should still use the correct image.
-- 
https://code.launchpad.net/~googol/openlp/image-queue/+merge/112948
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2012-06-22 14:14:53 +0000
+++ openlp/core/lib/__init__.py	2012-07-01 19:42:22 +0000
@@ -36,6 +36,23 @@
 
 log = logging.getLogger(__name__)
 
+
+class ImageSource(object):
+    """
+    This enumeration class represents different image sources. An image sources
+    states where an image is used. This enumeration class is need in the context
+    of the :class:~openlp.core.lib.imagemanager`.
+
+    ``ImagePlugin``
+        This states that an image is being used by the image plugin.
+
+    ``Theme``
+        This says, that the image is used by a theme.
+    """
+    ImagePlugin = 1
+    Theme = 2
+
+
 class MediaType(object):
     """
     An enumeration class for types of media.

=== modified file 'openlp/core/lib/imagemanager.py'
--- openlp/core/lib/imagemanager.py	2012-06-22 14:14:53 +0000
+++ openlp/core/lib/imagemanager.py	2012-07-01 19:42:22 +0000
@@ -32,6 +32,7 @@
 to wait for the conversion to happen.
 """
 import logging
+import os
 import time
 import Queue
 
@@ -97,19 +98,34 @@
 
 class Image(object):
     """
-    This class represents an image. To mark an image as *dirty* set the instance
-    variables ``image`` and ``image_bytes`` to ``None`` and add the image object
-    to the queue of images to process.
+    This class represents an image. To mark an image as *dirty* call the
+    :class:`ImageManager`'s ``_resetImage`` method with the Image instance as
+    argument.
     """
     secondary_priority = 0
-    def __init__(self, name, path, source, background):
-        self.name = name
+
+    def __init__(self, path, source, background):
+        """
+        Create an image for the :class:`ImageManager`'s cache.
+
+        ``path``
+            The image's file path. This should be an existing file path.
+
+        ``source``
+            The source describes the image's origin. Possible values are
+            described in the :class:`~openlp.core.lib.ImageSource` class.
+
+        ``background``
+            A ``QtGui.QColor`` object specifying the colour to be used to fill
+            the gabs if the image's ratio does not match with the display ratio.
+        """
         self.path = path
         self.image = None
         self.image_bytes = None
         self.priority = Priority.Normal
         self.source = source
         self.background = background
+        self.timestamp = os.stat(path).st_mtime
         self.secondary_priority = Image.secondary_priority
         Image.secondary_priority += 1
 
@@ -118,7 +134,7 @@
     """
     Customised ``Queue.PriorityQueue``.
 
-    Each item in the queue must be tuple with three values. The first value
+    Each item in the queue must be a tuple with three values. The first value
     is the :class:`Image`'s ``priority`` attribute, the second value
     the :class:`Image`'s ``secondary_priority`` attribute. The last value the
     :class:`Image` instance itself::
@@ -187,7 +203,7 @@
         for image in self._cache.values():
             self._resetImage(image)
 
-    def updateImages(self, imageType, background):
+    def updateImagesBorder(self, source, background):
         """
         Border has changed so update all the images affected.
         """
@@ -195,23 +211,27 @@
         # Mark the images as dirty for a rebuild by setting the image and byte
         # stream to None.
         for image in self._cache.values():
-            if image.source == imageType:
+            if image.source == source:
                 image.background = background
                 self._resetImage(image)
 
-    def updateImage(self, name, imageType, background):
+    def updateImageBorder(self, path, source, background):
         """
         Border has changed so update the image affected.
         """
         log.debug(u'updateImage')
-        # Mark the images as dirty for a rebuild by setting the image and byte
+        # Mark the image as dirty for a rebuild by setting the image and byte
         # stream to None.
-        for image in self._cache.values():
-            if image.source == imageType and image.name == name:
-                image.background = background
-                self._resetImage(image)
+        image = self._cache[(path, source)]
+        if image.source == source:
+            image.background = background
+            self._resetImage(image)
 
     def _resetImage(self, image):
+        """
+        Mark the given :class:`Image` instance as dirt by setting its ``image``
+        and ``image_bytes`` attributes to None.
+        """
         image.image = None
         image.image_bytes = None
         self._conversionQueue.modify_priority(image, Priority.Normal)
@@ -224,13 +244,13 @@
         if not self.imageThread.isRunning():
             self.imageThread.start()
 
-    def getImage(self, name):
+    def getImage(self, path, source):
         """
         Return the ``QImage`` from the cache. If not present wait for the
         background thread to process it.
         """
-        log.debug(u'getImage %s' % name)
-        image = self._cache[name]
+        log.debug(u'getImage %s' % path)
+        image = self._cache[(path, source)]
         if image.image is None:
             self._conversionQueue.modify_priority(image, Priority.High)
             # make sure we are running and if not give it a kick
@@ -246,13 +266,13 @@
             self._conversionQueue.modify_priority(image, Priority.Low)
         return image.image
 
-    def getImageBytes(self, name):
+    def getImageBytes(self, path, source):
         """
         Returns the byte string for an image. If not present wait for the
         background thread to process it.
         """
-        log.debug(u'getImageBytes %s' % name)
-        image = self._cache[name]
+        log.debug(u'getImageBytes %s' % path)
+        image = self._cache[(path, source)]
         if image.image_bytes is None:
             self._conversionQueue.modify_priority(image, Priority.Urgent)
             # make sure we are running and if not give it a kick
@@ -262,27 +282,22 @@
                 time.sleep(0.1)
         return image.image_bytes
 
-    def deleteImage(self, name):
-        """
-        Delete the Image from the cache.
-        """
-        log.debug(u'deleteImage %s' % name)
-        if name in self._cache:
-            self._conversionQueue.remove(self._cache[name])
-            del self._cache[name]
-
-    def addImage(self, name, path, source, background):
+    def addImage(self, path, source, background):
         """
         Add image to cache if it is not already there.
         """
-        log.debug(u'addImage %s:%s' % (name, path))
-        if not name in self._cache:
-            image = Image(name, path, source, background)
-            self._cache[name] = image
+        log.debug(u'addImage %s' % path)
+        if not (path, source) in self._cache:
+            image = Image(path, source, background)
+            self._cache[(path, source)] = image
             self._conversionQueue.put(
                 (image.priority, image.secondary_priority, image))
-        else:
-            log.debug(u'Image in cache %s:%s' % (name, path))
+        # Check if the there are any images with the same path and check if the
+        # timestamp has changed.
+        for image in self._cache.values():
+            if image.path == path and image.timestamp != os.stat(path).st_mtime:
+                image.timestamp = os.stat(path).st_mtime
+                self._resetImage(image)
         # We want only one thread.
         if not self.imageThread.isRunning():
             self.imageThread.start()

=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2012-06-22 14:14:53 +0000
+++ openlp/core/lib/renderer.py	2012-07-01 19:42:22 +0000
@@ -32,7 +32,7 @@
 
 from openlp.core.lib import ServiceItem, expand_tags, \
     build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
-    ItemCapabilities, FormattingTags
+    ItemCapabilities, FormattingTags, ImageSource
 from openlp.core.lib.theme import ThemeLevel
 from openlp.core.ui import MainDisplay, ScreenList
 
@@ -137,8 +137,8 @@
                 self._theme_dimensions[theme_name]
         # if No file do not update cache
         if theme_data.background_filename:
-            self.image_manager.addImage(theme_data.theme_name,
-                theme_data.background_filename, u'theme',
+            self.image_manager.addImage(theme_data.background_filename,
+                ImageSource.Theme,
                 QtGui.QColor(theme_data.background_border_color))
 
     def pre_render(self, override_theme_data=None):
@@ -237,14 +237,13 @@
             # make big page for theme edit dialog to get line count
             serviceItem.add_from_text(VERSE_FOR_LINE_COUNT)
         else:
-            self.image_manager.deleteImage(theme_data.theme_name)
             serviceItem.add_from_text(VERSE)
         serviceItem.renderer = self
         serviceItem.raw_footer = FOOTER
         # if No file do not update cache
         if theme_data.background_filename:
-            self.image_manager.addImage(theme_data.theme_name,
-                theme_data.background_filename, u'theme',
+            self.image_manager.addImage(theme_data.background_filename,
+                ImageSource.Theme,
                 QtGui.QColor(theme_data.background_border_color))
         theme_data, main, footer = self.pre_render(theme_data)
         serviceItem.themedata = theme_data

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2012-06-22 14:14:53 +0000
+++ openlp/core/lib/serviceitem.py	2012-07-01 19:42:22 +0000
@@ -36,7 +36,8 @@
 import os
 import uuid
 
-from openlp.core.lib import build_icon, clean_tags, expand_tags, translate
+from openlp.core.lib import build_icon, clean_tags, expand_tags, translate, \
+    ImageSource
 
 log = logging.getLogger(__name__)
 
@@ -217,8 +218,8 @@
             self.image_border = background
         self.service_item_type = ServiceItemType.Image
         self._raw_frames.append({u'title': title, u'path': path})
-        self.renderer.image_manager.addImage(title, path, u'image',
-            self.image_border)
+        self.renderer.image_manager.addImage(
+            path, ImageSource.ImagePlugin, self.image_border)
         self._new_item()
 
     def add_from_text(self, raw_slide, verse_tag=None):
@@ -432,13 +433,12 @@
 
     def get_rendered_frame(self, row):
         """
-        Returns the correct frame for a given list and
-        renders it if required.
+        Returns the correct frame for a given list and renders it if required.
         """
         if self.service_item_type == ServiceItemType.Text:
             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']
+            return self._raw_frames[row][u'path']
         else:
             return self._raw_frames[row][u'image']
 

=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py	2012-06-22 14:14:53 +0000
+++ openlp/core/ui/maindisplay.py	2012-07-01 19:42:22 +0000
@@ -37,7 +37,7 @@
 from PyQt4.phonon import Phonon
 
 from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \
-    translate, PluginManager, expand_tags
+    translate, PluginManager, expand_tags, ImageSource
 from openlp.core.lib.theme import BackgroundType
 from openlp.core.lib.settings import Settings
 
@@ -274,31 +274,33 @@
                 self.setVisible(False)
                 self.setGeometry(self.screen[u'size'])
 
-    def directImage(self, name, path, background):
+    def directImage(self, path, background):
         """
         API for replacement backgrounds so Images are added directly to cache.
         """
-        self.imageManager.addImage(name, path, u'image', background)
-        if hasattr(self, u'serviceItem'):
-            self.override[u'image'] = name
-            self.override[u'theme'] = self.serviceItem.themedata.theme_name
-            self.image(name)
-            # Update the preview frame.
-            if self.isLive:
-                self.parent().updatePreview()
-            return True
-        return False
+        self.imageManager.addImage(path, ImageSource.ImagePlugin, background)
+        if not hasattr(self, u'serviceItem'):
+            return False
+        self.override[u'image'] = path
+        self.override[u'theme'] = self.serviceItem.themedata.background_filename
+        self.image(path)
+        # Update the preview frame.
+        if self.isLive:
+            self.parent().updatePreview()
+        return True
 
-    def image(self, name):
+    def image(self, path):
         """
         Add an image as the background. The image has already been added to the
         cache.
 
-        ``name``
-            The name of the image to be displayed.
+        ``path``
+            The path to the image to be displayed. **Note**, the path is only
+            passed to identify the image. If the image has changed it has to be
+            re-added to the image manager.
         """
         log.debug(u'image to display')
-        image = self.imageManager.getImageBytes(name)
+        image = self.imageManager.getImageBytes(path, ImageSource.ImagePlugin)
         self.controller.mediaController.video_reset(self.controller)
         self.displayImage(image)
 
@@ -360,7 +362,7 @@
                     self.setVisible(True)
         return QtGui.QPixmap.grabWidget(self)
 
-    def buildHtml(self, serviceItem, image=None):
+    def buildHtml(self, serviceItem, image_path=u''):
         """
         Store the serviceItem and build the new HTML from it. Add the
         HTML to the display
@@ -377,20 +379,23 @@
                 Receiver.send_message(u'video_background_replaced')
                 self.override = {}
             # We have a different theme.
-            elif self.override[u'theme'] != serviceItem.themedata.theme_name:
+            elif self.override[u'theme'] != \
+                serviceItem.themedata.background_filename:
                 Receiver.send_message(u'live_theme_changed')
                 self.override = {}
             else:
                 # replace the background
-                background = self.imageManager. \
-                    getImageBytes(self.override[u'image'])
+                background = self.imageManager.getImageBytes(
+                    self.override[u'image'], ImageSource.ImagePlugin)
         self.setTransparency(self.serviceItem.themedata.background_type ==
             BackgroundType.to_string(BackgroundType.Transparent))
         if self.serviceItem.themedata.background_filename:
-            self.serviceItem.bg_image_bytes = self.imageManager. \
-                getImageBytes(self.serviceItem.themedata.theme_name)
-        if image:
-            image_bytes = self.imageManager.getImageBytes(image)
+            self.serviceItem.bg_image_bytes = self.imageManager.getImageBytes(
+                self.serviceItem.themedata.background_filename,
+                ImageSource.Theme)
+        if image_path:
+            image_bytes = self.imageManager.getImageBytes(
+                image_path, ImageSource.ImagePlugin)
         else:
             image_bytes = None
         html = build_html(self.serviceItem, self.screen, self.isLive,

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2012-06-22 14:14:53 +0000
+++ openlp/core/ui/slidecontroller.py	2012-07-01 19:42:22 +0000
@@ -34,7 +34,7 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, \
-    translate, build_icon, build_html, PluginManager, ServiceItem
+    translate, build_icon, build_html, PluginManager, ServiceItem, ImageSource
 from openlp.core.lib.ui import UiStrings, create_action
 from openlp.core.lib.settings import Settings
 from openlp.core.lib import SlideLimits, ServiceItemAction
@@ -861,8 +861,10 @@
                     # If current slide set background to image
                     if framenumber == slideno:
                         self.serviceItem.bg_image_bytes = \
-                            self.imageManager.getImageBytes(frame[u'title'])
-                    image = self.imageManager.getImage(frame[u'title'])
+                            self.imageManager.getImageBytes(frame[u'path'],
+                            ImageSource.ImagePlugin)
+                    image = self.imageManager.getImage(frame[u'path'],
+                        ImageSource.ImagePlugin)
                     label.setPixmap(QtGui.QPixmap.fromImage(image))
                 self.previewListWidget.setCellWidget(framenumber, 0, label)
                 slideHeight = width * (1 / self.ratio)
@@ -1092,14 +1094,14 @@
                         u'%s_slide' % self.serviceItem.name.lower(),
                         [self.serviceItem, self.isLive, row])
             else:
-                toDisplay = self.serviceItem.get_rendered_frame(row)
+                to_display = self.serviceItem.get_rendered_frame(row)
                 if self.serviceItem.is_text():
-                    self.display.text(toDisplay)
+                    self.display.text(to_display)
                 else:
                     if start:
-                        self.display.buildHtml(self.serviceItem, toDisplay)
+                        self.display.buildHtml(self.serviceItem, to_display)
                     else:
-                        self.display.image(toDisplay)
+                        self.display.image(to_display)
                     # reset the store used to display first image
                     self.serviceItem.bg_image_bytes = None
             self.updatePreview()

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2012-06-22 14:14:53 +0000
+++ openlp/core/ui/thememanager.py	2012-07-01 19:42:22 +0000
@@ -38,7 +38,7 @@
 
 from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, \
     Receiver, SettingsManager, translate, check_item_selected, \
-    check_directory_exists, create_thumb, validate_thumb
+    check_directory_exists, create_thumb, validate_thumb, ImageSource
 from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, \
     BackgroundGradientType
 from openlp.core.lib.settings import Settings
@@ -669,8 +669,9 @@
         self._writeTheme(theme, image_from, image_to)
         if theme.background_type == \
             BackgroundType.to_string(BackgroundType.Image):
-            self.mainwindow.imageManager.updateImage(theme.theme_name,
-                u'theme', QtGui.QColor(theme.background_border_color))
+            self.mainwindow.imageManager.updateImageBorder(
+                theme.background_filename,
+                ImageSource.Theme, QtGui.QColor(theme.background_border_color))
             self.mainwindow.imageManager.processUpdates()
         self.loadThemes()
 

=== modified file 'openlp/plugins/images/imageplugin.py'
--- openlp/plugins/images/imageplugin.py	2012-06-22 14:14:53 +0000
+++ openlp/plugins/images/imageplugin.py	2012-07-01 19:42:22 +0000
@@ -31,7 +31,7 @@
 import logging
 
 from openlp.core.lib import Plugin, StringContent, build_icon, translate, \
-    Receiver
+    Receiver, ImageSource
 from openlp.core.lib.settings import Settings
 from openlp.plugins.images.lib import ImageMediaItem, ImageTab
 
@@ -98,4 +98,5 @@
         """
         background = QtGui.QColor(Settings().value(self.settingsSection
             + u'/background color', QtCore.QVariant(u'#000000')))
-        self.liveController.imageManager.updateImages(u'image', background)
+        self.liveController.imageManager.updateImagesBorder(
+            ImageSource.ImagePlugin, background)

=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py	2012-06-22 14:14:53 +0000
+++ openlp/plugins/images/lib/mediaitem.py	2012-07-01 19:42:22 +0000
@@ -229,8 +229,7 @@
             bitem = self.listView.item(item.row())
             filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
             if os.path.exists(filename):
-                name = os.path.split(filename)[1]
-                if self.plugin.liveController.display.directImage(name,
+                if self.plugin.liveController.display.directImage(
                     filename, background):
                     self.resetAction.setVisible(True)
                 else:

=== modified file 'openlp/plugins/presentations/lib/messagelistener.py'
--- openlp/plugins/presentations/lib/messagelistener.py	2012-06-22 14:14:53 +0000
+++ openlp/plugins/presentations/lib/messagelistener.py	2012-07-01 19:42:22 +0000
@@ -278,8 +278,7 @@
         item = message[0]
         log.debug(u'Startup called with message %s' % message)
         hide_mode = message[2]
-        file = os.path.join(item.get_frame_path(),
-            item.get_frame_title())
+        file = os.path.join(item.get_frame_path(), item.get_frame_title())
         self.handler = item.title
         if self.handler == self.mediaitem.Automatic:
             self.handler = self.mediaitem.findControllerByType(file)


Follow ups