diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 1669ade30..5966e39f9 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -36,6 +36,23 @@ from PyQt4 import QtCore, QtGui, Qt 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. diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index 42cdf87ac..c6891db33 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -32,6 +32,7 @@ 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 import Queue @@ -97,19 +98,34 @@ class Priority(object): 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 @@ class PriorityQueue(Queue.PriorityQueue): """ 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 @@ class ImageManager(QtCore.QObject): 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 @@ class ImageManager(QtCore.QObject): # 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 dirty 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 @@ class ImageManager(QtCore.QObject): 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 @@ class ImageManager(QtCore.QObject): 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 @@ class ImageManager(QtCore.QObject): 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() diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index e6c5b0599..ce0797240 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -352,24 +352,23 @@ class MediaManagerItem(QtGui.QWidget): ``files`` The list of files to be loaded """ - #FIXME: change local variables to words_separated_by_underscores. - newFiles = [] - errorShown = False + new_files = [] + error_shown = False for file in files: type = file.split(u'.')[-1] if type.lower() not in self.onNewFileMasks: - if not errorShown: + if not error_shown: critical_error_message_box( translate('OpenLP.MediaManagerItem', 'Invalid File Type'), unicode(translate('OpenLP.MediaManagerItem', 'Invalid File %s.\nSuffix not supported')) % file) - errorShown = True + error_shown = True else: - newFiles.append(file) - if files: - self.validateAndLoad(newFiles) + new_files.append(file) + if new_files: + self.validateAndLoad(new_files) def validateAndLoad(self, files): """ @@ -379,30 +378,29 @@ class MediaManagerItem(QtGui.QWidget): ``files`` The files to be loaded. """ - #FIXME: change local variables to words_separated_by_underscores. names = [] - fullList = [] + full_list = [] for count in range(self.listView.count()): names.append(unicode(self.listView.item(count).text())) - fullList.append(unicode(self.listView.item(count). + full_list.append(unicode(self.listView.item(count). data(QtCore.Qt.UserRole).toString())) - duplicatesFound = False - filesAdded = False + duplicates_found = False + files_added = False for file in files: filename = os.path.split(unicode(file))[1] if filename in names: - duplicatesFound = True + duplicates_found = True else: - filesAdded = True - fullList.append(file) - if fullList and filesAdded: + files_added = True + full_list.append(file) + if full_list and files_added: self.listView.clear() - self.loadList(fullList) - lastDir = os.path.split(unicode(files[0]))[0] - SettingsManager.set_last_dir(self.settingsSection, lastDir) + self.loadList(full_list) + last_dir = os.path.split(unicode(files[0]))[0] + SettingsManager.set_last_dir(self.settingsSection, last_dir) SettingsManager.set_list(self.settingsSection, self.settingsSection, self.getFileList()) - if duplicatesFound: + if duplicates_found: critical_error_message_box( UiStrings().Duplicate, unicode(translate('OpenLP.MediaManagerItem', @@ -422,13 +420,13 @@ class MediaManagerItem(QtGui.QWidget): Return the current list of files """ count = 0 - filelist = [] + file_list = [] while count < self.listView.count(): bitem = self.listView.item(count) filename = unicode(bitem.data(QtCore.Qt.UserRole).toString()) - filelist.append(filename) + file_list.append(filename) count += 1 - return filelist + return file_list def loadList(self, list): raise NotImplementedError(u'MediaManagerItem.loadList needs to be ' @@ -477,9 +475,8 @@ class MediaManagerItem(QtGui.QWidget): Allows the change of current item in the list to be actioned """ if Settings().value(u'advanced/single click preview', - QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \ - and self.listView.selectedIndexes() \ - and self.autoSelectId == -1: + QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed and \ + self.listView.selectedIndexes() and self.autoSelectId == -1: self.onPreviewClick(True) def onPreviewClick(self, keepFocus=False): diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 9ccaf4cab..81d5361cb 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -32,7 +32,7 @@ from PyQt4 import QtGui, QtCore, QtWebKit 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 @@ -82,6 +82,9 @@ class Renderer(object): self._calculate_default() QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'theme_update_global'), self.set_global_theme) + self.web = QtWebKit.QWebView() + self.web.setVisible(False) + self.web_frame = self.web.page().mainFrame() def update_display(self): """ @@ -137,8 +140,8 @@ class Renderer(object): 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 +240,13 @@ class Renderer(object): # 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 @@ -404,10 +406,7 @@ class Renderer(object): if theme_data.font_main_shadow: self.page_width -= int(theme_data.font_main_shadow_size) self.page_height -= int(theme_data.font_main_shadow_size) - self.web = QtWebKit.QWebView() - self.web.setVisible(False) self.web.resize(self.page_width, self.page_height) - self.web_frame = self.web.page().mainFrame() # Adjust width and height to account for shadow. outline done in css. html = u"""