forked from openlp/openlp
Head
This commit is contained in:
commit
37c77623b4
@ -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
|
||||||
|
|
||||||
@ -42,8 +43,8 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class ImageThread(QtCore.QThread):
|
class ImageThread(QtCore.QThread):
|
||||||
"""
|
"""
|
||||||
A special Qt thread class to speed up the display of text based frames.
|
A special Qt thread class to speed up the display of images. This is
|
||||||
This is threaded so it loads the frames in background
|
threaded so it loads the frames and generates byte stream in background.
|
||||||
"""
|
"""
|
||||||
def __init__(self, manager):
|
def __init__(self, manager):
|
||||||
QtCore.QThread.__init__(self, None)
|
QtCore.QThread.__init__(self, None)
|
||||||
@ -53,15 +54,75 @@ class ImageThread(QtCore.QThread):
|
|||||||
"""
|
"""
|
||||||
Run the thread.
|
Run the thread.
|
||||||
"""
|
"""
|
||||||
self.imageManager.process()
|
self.imageManager._process()
|
||||||
|
|
||||||
|
|
||||||
|
class Priority(object):
|
||||||
|
"""
|
||||||
|
Enumeration class for different priorities.
|
||||||
|
|
||||||
|
``Lowest``
|
||||||
|
Only the image's byte stream has to be generated. But neither the
|
||||||
|
``QImage`` nor the byte stream has been requested yet.
|
||||||
|
|
||||||
|
``Low``
|
||||||
|
Only the image's byte stream has to be generated. Because the image's
|
||||||
|
``QImage`` has been requested previously it is reasonable to assume that
|
||||||
|
the byte stream will be needed before the byte stream of other images
|
||||||
|
whose ``QImage`` were not generated due to a request.
|
||||||
|
|
||||||
|
``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.
|
||||||
|
**Note**, this priority is only set when the ``QImage`` has not been
|
||||||
|
generated yet.
|
||||||
|
|
||||||
|
``Urgent``
|
||||||
|
The image's byte stream as well as the image has to be generated. The
|
||||||
|
byte stream for this image has been requested.
|
||||||
|
**Note**, this priority is only set when the byte stream has not been
|
||||||
|
generated yet.
|
||||||
|
"""
|
||||||
|
Lowest = 4
|
||||||
|
Low = 3
|
||||||
|
Normal = 2
|
||||||
|
High = 1
|
||||||
|
Urgent = 0
|
||||||
|
|
||||||
|
|
||||||
class Image(object):
|
class Image(object):
|
||||||
name = ''
|
"""
|
||||||
path = ''
|
This class represents an image. To mark an image as *dirty* set the instance
|
||||||
dirty = True
|
variables ``image`` and ``image_bytes`` to ``None`` and add the image object
|
||||||
image = None
|
to the queue of images to process.
|
||||||
image_bytes = None
|
"""
|
||||||
|
def __init__(self, name='', path=''):
|
||||||
|
self.name = name
|
||||||
|
self.path = path
|
||||||
|
self.image = 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,96 +137,120 @@ 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._conversion_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._conversion_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._conversion_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._conversion_queue.remove((image.priority, image))
|
||||||
|
image.priority = Priority.High
|
||||||
|
self._conversion_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._conversion_queue.remove((image.priority, image))
|
||||||
|
image.priority = Priority.Urgent
|
||||||
|
self._conversion_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:
|
||||||
|
self._conversion_queue.remove(
|
||||||
|
(self._cache[name].priority, self._cache[name]))
|
||||||
del self._cache[name]
|
del self._cache[name]
|
||||||
|
|
||||||
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._conversion_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._conversion_queue.empty():
|
||||||
self.clean_cache()
|
self._process_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 _process_cache(self):
|
||||||
"""
|
"""
|
||||||
Actually does the work.
|
Actually does the work.
|
||||||
"""
|
"""
|
||||||
log.debug(u'clean_cache')
|
log.debug(u'_process_cache')
|
||||||
# we will clean the cache now
|
image = self._conversion_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:
|
# Set the priority to Lowest and stop here as we need to process
|
||||||
image.image_bytes = image_to_byte(image.image)
|
# more important images first.
|
||||||
image.dirty = False
|
if image.priority == Priority.Normal:
|
||||||
|
self._conversion_queue.remove((image.priority, image))
|
||||||
|
image.priority = Priority.Lowest
|
||||||
|
self._conversion_queue.put((image.priority, image))
|
||||||
|
return
|
||||||
|
# For image with high priority we set the priority to Low, as the
|
||||||
|
# byte stream might be needed earlier the byte stream of image with
|
||||||
|
# Normal priority. We stop here as we need to process more important
|
||||||
|
# images first.
|
||||||
|
elif image.priority == Priority.High:
|
||||||
|
self._conversion_queue.remove((image.priority, image))
|
||||||
|
image.priority = Priority.Low
|
||||||
|
self._conversion_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)
|
||||||
|
Loading…
Reference in New Issue
Block a user