image manager: code standards

This commit is contained in:
Andreas Preikschat 2013-03-01 11:55:14 +01:00
parent a6617825ef
commit d6a57aa5cf

View File

@ -27,10 +27,9 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
Provides the store and management for Images automatically caching them and Provides the store and management for Images automatically caching them and resizing them when needed. Only one copy of
resizing them when needed. Only one copy of each image is needed in the system. 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
A Thread is used to convert the image to a byte array so the user does not need wait for the conversion to happen.
to wait for the conversion to happen.
""" """
import logging import logging
import os import os
@ -46,8 +45,8 @@ log = logging.getLogger(__name__)
class ImageThread(QtCore.QThread): class ImageThread(QtCore.QThread):
""" """
A special Qt thread class to speed up the display of images. This is A special Qt thread class to speed up the display of images. This is threaded so it loads the frames and generates
threaded so it loads the frames and generates byte stream in background. byte stream in background.
""" """
def __init__(self, manager): def __init__(self, manager):
""" """
@ -71,30 +70,25 @@ class Priority(object):
Enumeration class for different priorities. Enumeration class for different priorities.
``Lowest`` ``Lowest``
Only the image's byte stream has to be generated. But neither the Only the image's byte stream has to be generated. But neither the ``QImage`` nor the byte stream has been
``QImage`` nor the byte stream has been requested yet. requested yet.
``Low`` ``Low``
Only the image's byte stream has to be generated. Because the image's Only the image's byte stream has to be generated. Because the image's ``QImage`` has been requested previously
``QImage`` has been requested previously it is reasonable to assume that it is reasonable to assume that the byte stream will be needed before the byte stream of other images whose
the byte stream will be needed before the byte stream of other images ``QImage`` were not generated due to a request.
whose ``QImage`` were not generated due to a request.
``Normal`` ``Normal``
The image's byte stream as well as the image has to be generated. The image's byte stream as well as the image has to be generated. Neither the ``QImage`` nor the byte stream has
Neither the ``QImage`` nor the byte stream has been requested yet. been requested yet.
``High`` ``High``
The image's byte stream as well as the image has to be generated. The The image's byte stream as well as the image has to be generated. The ``QImage`` for this image has been
``QImage`` for this image has been requested. requested. **Note**, this priority is only set when the ``QImage`` has not been generated yet.
**Note**, this priority is only set when the ``QImage`` has not been
generated yet.
``Urgent`` ``Urgent``
The image's byte stream as well as the image has to be generated. The The image's byte stream as well as the image has to be generated. The byte stream for this image has been
byte stream for this image has been requested. requested. **Note**, this priority is only set when the byte stream has not been generated yet.
**Note**, this priority is only set when the byte stream has not been
generated yet.
""" """
Lowest = 4 Lowest = 4
Low = 3 Low = 3
@ -105,9 +99,8 @@ class Priority(object):
class Image(object): class Image(object):
""" """
This class represents an image. To mark an image as *dirty* call the This class represents an image. To mark an image as *dirty* call the :class:`ImageManager`'s ``_resetImage`` method
:class:`ImageManager`'s ``_resetImage`` method with the Image instance as with the Image instance as argument.
argument.
""" """
secondary_priority = 0 secondary_priority = 0
@ -119,12 +112,12 @@ class Image(object):
The image's file path. This should be an existing file path. The image's file path. This should be an existing file path.
``source`` ``source``
The source describes the image's origin. Possible values are The source describes the image's origin. Possible values are described in the
described in the :class:`~openlp.core.lib.ImageSource` class. :class:`~openlp.core.lib.ImageSource` class.
``background`` ``background``
A ``QtGui.QColor`` object specifying the colour to be used to fill A ``QtGui.QColor`` object specifying the colour to be used to fill the gabs if the image's ratio does not
the gabs if the image's ratio does not match with the display ratio. match with the display ratio.
""" """
self.path = path self.path = path
self.image = None self.image = None
@ -133,6 +126,7 @@ class Image(object):
self.source = source self.source = source
self.background = background self.background = background
self.timestamp = 0 self.timestamp = 0
# FIXME: We assume that the path exist. The caller has to take care that it exists!
if os.path.exists(path): if os.path.exists(path):
self.timestamp = os.stat(path).st_mtime self.timestamp = os.stat(path).st_mtime
self.secondary_priority = Image.secondary_priority self.secondary_priority = Image.secondary_priority
@ -143,16 +137,14 @@ class PriorityQueue(Queue.PriorityQueue):
""" """
Customised ``Queue.PriorityQueue``. Customised ``Queue.PriorityQueue``.
Each item in the queue must be a 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``
is the :class:`Image`'s ``priority`` attribute, the second value attribute, the second value the :class:`Image`'s ``secondary_priority`` attribute. The last value the :class:`Image`
the :class:`Image`'s ``secondary_priority`` attribute. The last value the instance itself::
:class:`Image` instance itself::
(image.priority, image.secondary_priority, image) (image.priority, image.secondary_priority, image)
Doing this, the :class:`Queue.PriorityQueue` will sort the images according Doing this, the :class:`Queue.PriorityQueue` will sort the images according to their priorities, but also according
to their priorities, but also according to there number. However, the number to there number. However, the number only has an impact on the result if there are more images with the same
only has an impact on the result if there are more images with the same
priority. In such case the image which has been added earlier is privileged. priority. In such case the image which has been added earlier is privileged.
""" """
def modify_priority(self, image, new_priority): def modify_priority(self, image, new_priority):
@ -163,8 +155,7 @@ class PriorityQueue(Queue.PriorityQueue):
The image to remove. This should be an :class:`Image` instance. The image to remove. This should be an :class:`Image` instance.
``new_priority`` ``new_priority``
The image's new priority. See the :class:`Priority` class for The image's new priority. See the :class:`Priority` class for priorities.
priorities.
""" """
self.remove(image) self.remove(image)
image.priority = new_priority image.priority = new_priority
@ -210,8 +201,7 @@ class ImageManager(QtCore.QObject):
current_screen = ScreenList().current current_screen = ScreenList().current
self.width = current_screen[u'size'].width() self.width = current_screen[u'size'].width()
self.height = current_screen[u'size'].height() self.height = current_screen[u'size'].height()
# Mark the images as dirty for a rebuild by setting the image and byte # Mark the images as dirty for a rebuild by setting the image and byte stream to None.
# stream to None.
for image in self._cache.values(): for image in self._cache.values():
self._reset_image(image) self._reset_image(image)
@ -220,8 +210,7 @@ class ImageManager(QtCore.QObject):
Border has changed so update all the images affected. Border has changed so update all the images affected.
""" """
log.debug(u'update_images_border') log.debug(u'update_images_border')
# Mark the images as dirty for a rebuild by setting the image and byte # Mark the images as dirty for a rebuild by setting the image and byte stream to None.
# stream to None.
for image in self._cache.values(): for image in self._cache.values():
if image.source == source: if image.source == source:
image.background = background image.background = background
@ -232,8 +221,7 @@ class ImageManager(QtCore.QObject):
Border has changed so update the image affected. Border has changed so update the image affected.
""" """
log.debug(u'update_image_border') log.debug(u'update_image_border')
# Mark the image 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.
# stream to None.
image = self._cache[(path, source)] image = self._cache[(path, source)]
if image.source == source: if image.source == source:
image.background = background image.background = background
@ -241,8 +229,7 @@ class ImageManager(QtCore.QObject):
def _reset_image(self, image): def _reset_image(self, image):
""" """
Mark the given :class:`Image` instance as dirty by setting its ``image`` Mark the given :class:`Image` instance as dirty by setting its ``image`` and ``image_bytes`` attributes to None.
and ``image_bytes`` attributes to None.
""" """
image.image = None image.image = None
image.image_bytes = None image.image_bytes = None
@ -258,8 +245,7 @@ class ImageManager(QtCore.QObject):
def get_image(self, path, source): def get_image(self, path, source):
""" """
Return the ``QImage`` from the cache. If not present wait for the Return the ``QImage`` from the cache. If not present wait for the background thread to process it.
background thread to process it.
""" """
log.debug(u'getImage %s' % path) log.debug(u'getImage %s' % path)
image = self._cache[(path, source)] image = self._cache[(path, source)]
@ -271,17 +257,15 @@ class ImageManager(QtCore.QObject):
log.debug(u'getImage - waiting') log.debug(u'getImage - waiting')
time.sleep(0.1) time.sleep(0.1)
elif image.image_bytes is None: elif image.image_bytes is None:
# Set the priority to Low, because the image was requested but the # Set the priority to Low, because the image was requested but the byte stream was not generated yet.
# byte stream was not generated yet. However, we only need to do # However, we only need to do this, when the image was generated before it was requested (otherwise this is
# this, when the image was generated before it was requested # already taken care of).
# (otherwise this is already taken care of).
self._conversion_queue.modify_priority(image, Priority.Low) self._conversion_queue.modify_priority(image, Priority.Low)
return image.image return image.image
def get_image_bytes(self, path, source): def get_image_bytes(self, path, source):
""" """
Returns the byte string for an image. If not present wait for the Returns the byte string for an image. If not present wait for the background thread to process it.
background thread to process it.
""" """
log.debug(u'get_image_bytes %s' % path) log.debug(u'get_image_bytes %s' % path)
image = self._cache[(path, source)] image = self._cache[(path, source)]
@ -303,8 +287,7 @@ class ImageManager(QtCore.QObject):
image = Image(path, source, background) image = Image(path, source, background)
self._cache[(path, source)] = image self._cache[(path, source)] = image
self._conversion_queue.put((image.priority, image.secondary_priority, image)) self._conversion_queue.put((image.priority, image.secondary_priority, image))
# Check if the there are any images with the same path and check if the # Check if the there are any images with the same path and check if the timestamp has changed.
# timestamp has changed.
for image in self._cache.values(): for image in self._cache.values():
if os.path.exists(path): if os.path.exists(path):
if image.path == path and image.timestamp != os.stat(path).st_mtime: if image.path == path and image.timestamp != os.stat(path).st_mtime:
@ -332,15 +315,12 @@ class ImageManager(QtCore.QObject):
# Generate the QImage for the image. # Generate the QImage for the image.
if image.image is None: if image.image is None:
image.image = resize_image(image.path, self.width, self.height, image.background) image.image = resize_image(image.path, self.width, self.height, image.background)
# Set the priority to Lowest and stop here as we need to process # Set the priority to Lowest and stop here as we need to process more important images first.
# more important images first.
if image.priority == Priority.Normal: if image.priority == Priority.Normal:
self._conversion_queue.modify_priority(image, Priority.Lowest) self._conversion_queue.modify_priority(image, Priority.Lowest)
return return
# For image with high priority we set the priority to Low, as the # For image with high priority we set the priority to Low, as the byte stream might be needed earlier the
# byte stream might be needed earlier the byte stream of image with # byte stream of image with Normal priority. We stop here as we need to process more important images first.
# Normal priority. We stop here as we need to process more important
# images first.
elif image.priority == Priority.High: elif image.priority == Priority.High:
self._conversion_queue.modify_priority(image, Priority.Low) self._conversion_queue.modify_priority(image, Priority.Low)
return return