- added an 'intelligent' image queue with priorities

- resize image in the thread
- change instances variables (based on qt)
This commit is contained in:
Andreas Preikschat 2011-06-25 06:42:49 +02:00
parent e6cab2ce84
commit 356ea8fff8

View File

@ -32,6 +32,7 @@ to wait for the conversion to happen.
""" """
import logging import logging
import time import time
import Queue
from PyQt4 import QtCore from PyQt4 import QtCore
@ -53,15 +54,59 @@ class ImageThread(QtCore.QThread):
""" """
Run the thread. Run the thread.
""" """
self.imageManager.process() self.imageManager._process()
class Priority(object):
"""
Enumeration class for different priorities.
``Low``
Only the image's byte stream has to be generated. Neither the QImage nor
the byte stream has been requested yet.
``Normal``
The image's byte stream as well as the image has to be generated.
Neither the QImage nor the byte stream has been requested yet.
``High``
The image's byte stream as well as the image has to be generated. The
QImage for this image has been requested.
``Urgent``
The image's byte stream as well as the image has to be generated. The
byte stream for this image has been requested.
"""
Low = 3
Normal = 2
High = 1
Urgent = 0
class Image(object): class Image(object):
name = '' def __init__(self, name='', path=''):
path = '' self.name = name
dirty = True self.path = path
image = None self.image = None
image_bytes = None self.image_bytes = None
self.priority = Priority.Normal
class PriorityQueue(Queue.PriorityQueue):
"""
Customised ``Queue.PriorityQueue``.
"""
def remove(self, item):
"""
Removes the given ``item`` from the queue.
``item``
The item to remove. This should be a tuple::
``(Priority, Image)``
"""
if item in self.queue:
self.queue.remove(item)
class ImageManager(QtCore.QObject): class ImageManager(QtCore.QObject):
@ -76,50 +121,64 @@ class ImageManager(QtCore.QObject):
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()
self._cache = {} self._cache = {}
self._thread_running = False self._imageThread = ImageThread(self)
self._cache_dirty = False self._clean_queue = PriorityQueue()
self.image_thread = ImageThread(self)
def update_display(self): def update_display(self):
""" """
Screen has changed size so rebuild the cache to new size Screen has changed size so rebuild the cache to new size.
""" """
log.debug(u'update_display') log.debug(u'update_display')
current_screen = ScreenList.get_instance().current current_screen = ScreenList.get_instance().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 # Mark the images as dirty for a rebuild by setting the image and byte
for key in self._cache.keys(): # stream to None.
image = self._cache[key] self._clean_queue = PriorityQueue()
image.dirty = True for key, image in self._cache.iteritems():
image.image = resize_image(image.path, self.width, self.height) image.priority = Priority.Normal
self._cache_dirty = True image.image = None
# only one thread please image.image_bytes = None
if not self._thread_running: self._clean_queue.put((image.priority, image))
self.image_thread.start() # We want only one thread.
if not self._imageThread.isRunning():
self._imageThread.start()
def get_image(self, name): def get_image(self, name):
""" """
Return the Qimage from the cache Return the ``QImage`` from the cache. If not present wait for the
background thread to process it.
""" """
log.debug(u'get_image %s' % name) log.debug(u'get_image %s' % name)
return self._cache[name].image image = self._cache[name]
if image.image is None:
self._clean_queue.remove((image.priority, image))
image.priority = Priority.High
self._clean_queue.put((image.priority, image))
while image.image is None:
log.debug(u'get_image - waiting')
time.sleep(0.1)
return image.image
def get_image_bytes(self, name): def get_image_bytes(self, name):
""" """
Returns the byte string for an image Returns the byte string for an image. If not present wait for the
If not present wait for the background thread to process it. background thread to process it.
""" """
log.debug(u'get_image_bytes %s' % name) log.debug(u'get_image_bytes %s' % name)
if not self._cache[name].image_bytes: image = self._cache[name]
while self._cache[name].dirty: if image.image_bytes is None:
self._clean_queue.remove((image.priority, image))
image.priority = Priority.Urgent
self._clean_queue.put((image.priority, image))
while image.image_bytes is None:
log.debug(u'get_image_bytes - waiting') log.debug(u'get_image_bytes - waiting')
time.sleep(0.1) time.sleep(0.1)
return self._cache[name].image_bytes return image.image_bytes
def del_image(self, name): def del_image(self, name):
""" """
Delete the Image from the Cache Delete the Image from the cache.
""" """
log.debug(u'del_image %s' % name) log.debug(u'del_image %s' % name)
if name in self._cache: if name in self._cache:
@ -127,45 +186,44 @@ class ImageManager(QtCore.QObject):
def add_image(self, name, path): def add_image(self, name, path):
""" """
Add image to cache if it is not already there Add image to cache if it is not already there.
""" """
log.debug(u'add_image %s:%s' % (name, path)) log.debug(u'add_image %s:%s' % (name, path))
if not name in self._cache: if not name in self._cache:
image = Image() image = Image(name, path)
image.name = name
image.path = path
image.image = resize_image(path, self.width, self.height)
self._cache[name] = image self._cache[name] = image
self._clean_queue.put((image.priority, image))
else: else:
log.debug(u'Image in cache %s:%s' % (name, path)) log.debug(u'Image in cache %s:%s' % (name, path))
self._cache_dirty = True # We want only one thread.
# only one thread please if not self._imageThread.isRunning():
if not self._thread_running: self._imageThread.start()
self.image_thread.start()
def process(self): def _process(self):
""" """
Controls the processing called from a QThread Controls the processing called from a ``QtCore.QThread``.
""" """
log.debug(u'process - started') log.debug(u'_process - started')
self._thread_running = True while not self._clean_queue.empty():
self.clean_cache() self._clean_cache()
# data loaded since we started ? log.debug(u'_process - ended')
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): def _clean_cache(self):
""" """
Actually does the work. Actually does the work.
""" """
log.debug(u'clean_cache') log.debug(u'_clean_cache')
# we will clean the cache now image = self._clean_queue.get()[1]
self._cache_dirty = False # Generate the QImage for the image.
for key in self._cache.keys(): if image.image is None:
image = self._cache[key] image.image = resize_image(image.path, self.width, self.height)
if image.dirty: # If the priority is not urgent, then set the priority to low and
# do not start to generate the byte stream.
if image.priority != Priority.Urgent:
self._clean_queue.remove((image.priority, image))
image.priority = Priority.Low
self._clean_queue.put((image.priority, image))
return
# Generate the byte stream for the image.
if image.image_bytes is None:
image.image_bytes = image_to_byte(image.image) image.image_bytes = image_to_byte(image.image)
image.dirty = False