Source code for sibl_gui.ui.cache

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
**cache.py**

**Platform:**
	Windows, Linux, Mac Os X.

**Description:**
	This module defines the Application cache classes.

**Others:**

"""

#**********************************************************************************************************************
#***	External imports.
#**********************************************************************************************************************
import os
import itertools
from PyQt4.QtCore import QObject
from PyQt4.QtCore import pyqtSignal

#**********************************************************************************************************************
#***	Internal imports.
#**********************************************************************************************************************
import foundations.exceptions
import foundations.verbose
import sibl_gui.ui.common
import sibl_gui.ui.workers

#**********************************************************************************************************************
#***	Module attributes.
#**********************************************************************************************************************
__author__ = "Thomas Mansencal"
__copyright__ = "Copyright (C) 2008 - 2012 - Thomas Mansencal"
__license__ = "GPL V3.0 - http://www.gnu.org/licenses/"
__maintainer__ = "Thomas Mansencal"
__email__ = "thomas.mansencal@gmail.com"
__status__ = "Production"

__all__ = ["LOGGER",
			"CacheMetrics",
			"AbstractResourcesCache",
			"AsynchronousGraphicsItemsCache"]

LOGGER = foundations.verbose.installLogger()

#**********************************************************************************************************************
#***	Module classes and definitions.
#**********************************************************************************************************************
[docs]class CacheMetrics(foundations.dataStructures.Structure): """ This class represents a storage object for cache metrics. """ def __init__(self, **kwargs): """ .. Sphinx: Statements updated for auto-documentation purpose. :param kwargs: type, content. ( Key / Value pairs ) """ LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__)) foundations.dataStructures.Structure.__init__(self, **kwargs)
[docs]class AbstractResourcesCache(QObject): """ This class is a `QObject <http://doc.qt.nokia.com/qobject.html>`_ subclass used as an abstract resources cache. """ contentAdded = pyqtSignal(list) """ This signal is emited by the :class:`AsynchronousGraphicsItemsCache` class whenever content has been added. ( pyqtSignal ) :return: Content added to the cache. ( List ) """ contentRemoved = pyqtSignal(list) """ This signal is emited by the :class:`AsynchronousGraphicsItemsCache` class whenever content has been removed. ( pyqtSignal ) :return: Content removed from the cache. ( List ) """ def __init__(self, parent=None): """ .. Sphinx: Statements updated for auto-documentation purpose. :param parent: Object parent. ( QObject ) """ QObject.__init__(self, parent) self.__mapping = {} #****************************************************************************************************************** #*** Attributes properties. #****************************************************************************************************************** @property def mapping(self): """ This method is the property for **self.__mapping** attribute. :return: self.__mapping. ( Dictionary ) """ return self.__mapping @mapping.setter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def mapping(self, value): """ This method is the setter method for **self.__mapping** attribute. :param value: Attribute value. ( Dictionary ) """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "mapping")) @mapping.deleter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def mapping(self): """ This method is the deleter method for **self.__mapping** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "mapping")) #****************************************************************************************************************** #*** Class methods. #******************************************************************************************************************
def __getitem__(self, item): """ This method reimplements the :meth:`object.__getitem__` method. :param item: Item name. ( String ) :return: Item. ( Object ) """ return self.__mapping.__getitem__(item) def __setitem__(self, key, value): """ This method reimplements the :meth:`object.__setitem__` method. :param key: Key. ( String ) :param value: Item. ( Object ) """ self.__mapping.__setitem__(key, value) def __iter__(self): """ This method reimplements the :meth:`object.__iter__` method. :return: Paths iterator. ( Object ) """ return self.__mapping.iteritems() def __contains__(self, item): """ This method reimplements the :meth:`object.__contains__` method. :param item: Item name. ( String ) :return: Item existence. ( Boolean ) """ return item in self.__mapping.keys() def __len__(self): """ This method reimplements the :meth:`object.__len__` method. :return: Paths count. ( Integer ) """ return len(self.__mapping.keys())
[docs] def isCached(self, key): """ This method returns if given content is cached. :param key: Content to retrieve. ( Object ) :return: Is content cached. ( Boolean ) """ return key in self
[docs] def listContent(self): """ This method lists the cache content. :return: Cache content. ( List ) """ return self.__mapping.keys()
[docs] def addContent(self, **content): """ This method adds given content to the cache. :param \*\*content: Content to add. ( \*\* ) :return: Method success. ( Boolean ) """ LOGGER.debug("> Adding '{0}' content to the cache.".format(self.__class__.__name__, content)) self.__mapping.update(**content) self.contentAdded.emit(content.keys()) return True
[docs] def removeContent(self, *keys): """ This method removes given content from the cache. :param \*keys: Content to remove. ( \* ) :return: Method success. ( Boolean ) """ LOGGER.debug("> Removing '{0}' content from the cache.".format(self.__class__.__name__, keys)) for key in keys: if not key in self: raise KeyError("{0} | '{1}' key doesn't exists in cache content!".format(self.__class__.__name__, key)) del(self.__mapping[key]) self.contentRemoved.emit([key]) return True
[docs] def getContent(self, key): """ This method gets given content from the cache. :param key: Content to retrieve. ( Object ) :return: Content. ( Object ) """ LOGGER.debug("> Retrieving '{0}' content from the cache.".format(self.__class__.__name__, key)) return self.__mapping.get(key)
[docs] def flushContent(self): """ This method flushes the cache content. :return: Method success. ( Boolean ) """ LOGGER.debug("> Flushing cache content.".format(self.__class__.__name__)) content = self.__mapping.keys() self.__mapping.clear() self.contentRemoved.emit(content) return True
[docs] def getMetrics(self): """ This method returns the cache metrics. """ cacheMetrics = CacheMetrics() cacheMetrics.type = None cacheMetrics.content = dict(zip(self.__mapping.keys(), itertools.repeat(None, len(self.__mapping)))) return cacheMetrics
[docs]class AsynchronousGraphicsItemsCache(AbstractResourcesCache): """ This class provides an asynchronous graphics items cache. """ def __init__(self, parent=None, type=None, default=None): """ .. Sphinx: Statements updated for auto-documentation purpose. :param parent: Object parent. ( QObject ) :param type: Cache type. ( QImage / QPixmap / QIcon ) :param default: Default image. ( String ) """ LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__)) AbstractResourcesCache.__init__(self, parent) # --- Setting class attributes. --- self.__type = type self.__default = default self.__defaultGraphicsItem = None self.__worker = sibl_gui.ui.workers.GraphicsItem_worker() self.__worker.start() self.__worker.imageLoaded.connect(self.__worker__imageLoaded) self.__setDefaultGraphicsItem(default) #****************************************************************************************************************** #*** Attributes properties. #****************************************************************************************************************** @property def type(self): """ This method is the property for **self.__type** attribute. :return: self.__type. ( QObject ) """ return self.__type @type.setter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def type(self, value): """ This method is the setter method for **self.__type** attribute. :param value: Attribute value. ( QObject ) """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "type")) @type.deleter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def type(self): """ This method is the deleter method for **self.__type** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "type"))
@property def default(self): """ This method is the property for **self.__default** attribute. :return: self.__default. ( String ) """ return self.__default @default.setter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def default(self, value): """ This method is the setter method for **self.__default** attribute. :param value: Attribute value. ( String ) """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "default")) @default.deleter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def default(self): """ This method is the deleter method for **self.__default** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "default"))
@property def defaultGraphicsItem(self): """ This method is the property for **self.__defaultGraphicsItem** attribute. :return: self.__defaultGraphicsItem. ( QObject ) """ return self.__defaultGraphicsItem @defaultGraphicsItem.setter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def defaultGraphicsItem(self, value): """ This method is the setter method for **self.__defaultGraphicsItem** attribute. :param value: Attribute value. ( QObject ) """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "defaultGraphicsItem")) @defaultGraphicsItem.deleter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def defaultGraphicsItem(self): """ This method is the deleter method for **self.__defaultGraphicsItem** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "defaultGraphicsItem"))
@property def worker(self): """ This method is the property for **self.__worker** attribute. :return: self.__worker. ( QThread ) """ return self.__worker @worker.setter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(AssertionError) def worker(self, value): """ This method is the setter method for **self.__worker** attribute. :param value: Attribute value. ( QThread ) """ if value is not None: assert type(value) in (str, unicode), "'{0}' attribute: '{1}' type is not 'str' or 'unicode'!".format( "worker", value) assert os.path.exists(value), "'{0}' attribute: '{1}' file doesn't exists!".format("worker", value) self.__worker = value @worker.deleter #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def worker(self): """ This method is the deleter method for **self.__worker** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "worker")) #****************************************************************************************************************** #*** Class methods. #******************************************************************************************************************
def __worker__imageLoaded(self, image): """ This method is triggered by the :obj:`AsynchronousGraphicsItemsCache.worker` method when an image has been loaded. :param image: Loaded image. ( QImage ) """ graphicsItem = sibl_gui.ui.common.convertImage(image, self.__type) graphicsItem.data = image.data path = graphicsItem.data.path self[path] = graphicsItem self.contentAdded.emit([path]) def __setDefaultGraphicsItem(self, path): """ This method sets the defaultGraphicsItem graphics item. :param path: Default image path. ( String ) """ if not foundations.common.pathExists(path): LOGGER.warning( "!> {0} | '{1}' default graphics item file doesn't exists, unexpected behavior may occur!".format( self.__class__.__name__, self)) return self.__defaultGraphicsItem = self.__type(path) self.__defaultGraphicsItem.data = sibl_gui.ui.common.getImageInformationsHeader(path, self.__defaultGraphicsItem)
[docs] def addContent(self, **content): """ This method reimplements the :meth:`AbstractResourcesCache.addContent` method. :param \*\*content: Content to add. ( \*\* ) :return: Method success. ( Boolean ) """ LOGGER.debug("> Adding '{0}' content to the cache.".format(self.__class__.__name__, content)) for path, item in content.items(): if not foundations.common.pathExists(path): LOGGER.warning("!> {0} | '{1}' file doesn't exists and has been skipped!".format( self.__class__.__name__, path)) del(content[path]) continue if type(item) is not self.__type: LOGGER.warning("!> {0} | '{1}' item type is not '{2}' type and has been skipped!".format( self.__class__.__name__, item, self.__type)) del(content[path]) for key, value in content.iteritems(): value.data = sibl_gui.ui.common.getImageInformationsHeader(key, value) self[key] = value self.contentAdded.emit([key]) return True #*** Sphinx: Decorator commented for auto-documentation purpose. @foundations.exceptions.handleExceptions(foundations.exceptions.FileExistsError)
[docs] def addDeferredContent(self, *content): """ This method adds given content to the cache. :param \*content: Paths to add. ( \* ) :return: Method success. ( Boolean ) """ LOGGER.debug("> Adding '{0}' content to the cache.".format(self.__class__.__name__, content)) for path in content: if not foundations.common.pathExists(path): raise foundations.exceptions.FileExistsError("{0} | '{1}' file doesn't exists!".format( self.__class__.__name__, path)) if path in self: image = self.getContent(path) if not hasattr(image, "data"): LOGGER.debug("> {0} | '{1}' object has not 'data' attribute and has been skipped!".format( self.__class__.__name__, image)) continue if image.data.path != path: continue if image.data.osStats.st_mtime == os.stat(path).st_mtime: continue else: LOGGER.info("{0} | '{1}' file has been modified and will be reloaded!".format( self.__class__.__name__, path)) self[path] = self.__defaultGraphicsItem self.contentAdded.emit([path]) self.__worker.addRequest(path) return True
[docs] def getMetrics(self): """ This method reimplements the :meth:`AbstractResourcesCache.getMetrics` method. """ cacheMetrics = AbstractResourcesCache.getMetrics(self) cacheMetrics.type = self.__type cacheMetrics.content = dict(zip(self.mapping.keys(), (value.data for value in self.mapping.itervalues()))) return cacheMetrics