diff options
author | Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr> | 2017-08-18 14:48:52 +0200 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr> | 2017-08-18 14:48:52 +0200 |
commit | f7bdc2acff3c13a6d632c28c4569690ab106eed7 (patch) | |
tree | 9d67cdb7152ee4e711379e03fe0546c7c3b97303 /silx/gui/icons.py |
Import Upstream version 0.5.0+dfsg
Diffstat (limited to 'silx/gui/icons.py')
-rw-r--r-- | silx/gui/icons.py | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/silx/gui/icons.py b/silx/gui/icons.py new file mode 100644 index 0000000..eaf83b8 --- /dev/null +++ b/silx/gui/icons.py @@ -0,0 +1,360 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ###########################################################################*/ +"""Set of icons for buttons. + +Use :func:`getQIcon` to create Qt QIcon from the name identifying an icon. +""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "25/04/2017" + + +import logging +import weakref +from . import qt +from silx.resources import resource_filename +from silx.utils import weakref as silxweakref +from silx.utils.decorators import deprecated + + +_logger = logging.getLogger(__name__) +"""Module logger""" + + +_cached_icons = weakref.WeakValueDictionary() +"""Cache loaded icons in a weak structure""" + + +_supported_formats = None +"""Order of file format extension to check""" + + +class AbstractAnimatedIcon(qt.QObject): + """Store an animated icon. + + It provides an event containing the new icon everytime it is updated.""" + + def __init__(self, parent=None): + """Constructor + + :param qt.QObject parent: Parent of the QObject + :raises: ValueError when name is not known + """ + qt.QObject.__init__(self, parent) + + self.__targets = silxweakref.WeakList() + self.__currentIcon = None + + iconChanged = qt.Signal(qt.QIcon) + """Signal sent with a QIcon everytime the animation changed.""" + + def register(self, obj): + """Register an object to the AnimatedIcon. + If no object are registred, the animation is paused. + Object are stored in a weaked list. + + :param object obj: An object + """ + if obj not in self.__targets: + self.__targets.append(obj) + self._updateState() + + def unregister(self, obj): + """Remove the object from the registration. + If no object are registred the animation is paused. + + :param object obj: A registered object + """ + if obj in self.__targets: + self.__targets.remove(obj) + self._updateState() + + def hasRegistredObjects(self): + """Returns true if any object is registred. + + :rtype: bool + """ + return len(self.__targets) + + def isRegistered(self, obj): + """Returns true if the object is registred in the AnimatedIcon. + + :param object obj: An object + :rtype: bool + """ + return obj in self.__targets + + def currentIcon(self): + """Returns the icon of the current frame. + + :rtype: qt.QIcon + """ + return self.__currentIcon + + def _updateState(self): + """Update the object according to the connected objects.""" + pass + + def _setCurrentIcon(self, icon): + """Store the current icon and emit a `iconChanged` event. + + :param qt.QIcon icon: The current icon + """ + self.__currentIcon = icon + self.iconChanged.emit(self.__currentIcon) + + +class MovieAnimatedIcon(AbstractAnimatedIcon): + """Store a looping QMovie to provide icons for each frames. + Provides an event with the new icon everytime the movie frame + is updated.""" + + def __init__(self, filename, parent=None): + """Constructor + + :param str filename: An icon name to an animated format + :param qt.QObject parent: Parent of the QObject + :raises: ValueError when name is not known + """ + AbstractAnimatedIcon.__init__(self, parent) + + qfile = getQFile(filename) + self.__movie = qt.QMovie(qfile.fileName(), qt.QByteArray(), parent) + self.__movie.setCacheMode(qt.QMovie.CacheAll) + self.__movie.frameChanged.connect(self.__frameChanged) + self.__cacheIcons = {} + + self.__movie.jumpToFrame(0) + self.__updateIconAtFrame(0) + + def __frameChanged(self, frameId): + """Callback everytime the QMovie frame change + :param int frameId: Current frame id + """ + self.__updateIconAtFrame(frameId) + + def __updateIconAtFrame(self, frameId): + """ + Update the current stored QIcon + + :param int frameId: Current frame id + """ + if frameId in self.__cacheIcons: + icon = self.__cacheIcons[frameId] + else: + icon = qt.QIcon(self.__movie.currentPixmap()) + self.__cacheIcons[frameId] = icon + self._setCurrentIcon(icon) + + def _updateState(self): + """Update the movie play according to internal stat of the + AnimatedIcon.""" + self.__movie.setPaused(not self.hasRegistredObjects()) + + +class MultiImageAnimatedIcon(AbstractAnimatedIcon): + """Store a looping QMovie to provide icons for each frames. + Provides an event with the new icon everytime the movie frame + is updated.""" + + def __init__(self, filename, parent=None): + """Constructor + + :param str filename: An icon name to an animated format + :param qt.QObject parent: Parent of the QObject + :raises: ValueError when name is not known + """ + AbstractAnimatedIcon.__init__(self, parent) + + self.__frames = [] + for i in range(100): + try: + pixmap = getQPixmap("animated/%s-%02d" % (filename, i)) + except ValueError: + break + icon = qt.QIcon(pixmap) + self.__frames.append(icon) + + if len(self.__frames) == 0: + raise ValueError("Animated icon '%s' do not exists" % filename) + + self.__frameId = -1 + self.__timer = qt.QTimer(self) + self.__timer.timeout.connect(self.__increaseFrame) + self.__updateIconAtFrame(0) + + def __increaseFrame(self): + """Callback called every timer timeout to change the current frame of + the animation + """ + frameId = (self.__frameId + 1) % len(self.__frames) + self.__updateIconAtFrame(frameId) + + def __updateIconAtFrame(self, frameId): + """ + Update the current stored QIcon + + :param int frameId: Current frame id + """ + self.__frameId = frameId + icon = self.__frames[frameId] + self._setCurrentIcon(icon) + + def _updateState(self): + """Update the object to wake up or sleep it according to its use.""" + if self.hasRegistredObjects(): + if not self.__timer.isActive(): + self.__timer.start(100) + else: + if self.__timer.isActive(): + self.__timer.stop() + + +class AnimatedIcon(MovieAnimatedIcon): + """Store a looping QMovie to provide icons for each frames. + Provides an event with the new icon everytime the movie frame + is updated. + + It may not be available anymore for the silx release 0.6. + + .. deprecated:: 0.5 + Use :class:`MovieAnimatedIcon` instead. + """ + + @deprecated + def __init__(self, filename, parent=None): + MovieAnimatedIcon.__init__(self, filename, parent=parent) + + +def getWaitIcon(): + """Returns a cached version of the waiting AbstractAnimatedIcon. + + :rtype: AbstractAnimatedIcon + """ + return getAnimatedIcon("process-working") + + +def getAnimatedIcon(name): + """Create an AbstractAnimatedIcon from a name. + + Try to load a mng or a gif file, then try to load a multi-image animated + icon. + + In Qt5 mng or gif are not used. It does not take care very well of the + transparency. + + :param str name: Name of the icon, in one of the defined icons + in this module. + :return: Corresponding AbstractAnimatedIcon + :raises: ValueError when name is not known + """ + key = name + "__anim" + if key not in _cached_icons: + + qtMajorVersion = int(qt.qVersion().split(".")[0]) + icon = None + + # ignore mng and gif in Qt5 + if qtMajorVersion != 5: + try: + icon = MovieAnimatedIcon(name) + except ValueError: + icon = None + + if icon is None: + try: + icon = MultiImageAnimatedIcon(name) + except ValueError: + icon = None + + if icon is None: + raise ValueError("Not an animated icon name: %s", name) + + _cached_icons[key] = icon + else: + icon = _cached_icons[key] + return icon + + +def getQIcon(name): + """Create a QIcon from its name. + + :param str name: Name of the icon, in one of the defined icons + in this module. + :return: Corresponding QIcon + :raises: ValueError when name is not known + """ + if name not in _cached_icons: + qfile = getQFile(name) + icon = qt.QIcon(qfile.fileName()) + _cached_icons[name] = icon + else: + icon = _cached_icons[name] + return icon + + +def getQPixmap(name): + """Create a QPixmap from its name. + + :param str name: Name of the icon, in one of the defined icons + in this module. + :return: Corresponding QPixmap + :raises: ValueError when name is not known + """ + qfile = getQFile(name) + return qt.QPixmap(qfile.fileName()) + + +def getQFile(name): + """Create a QFile from an icon name. Filename is found + according to supported Qt formats. + + :param str name: Name of the icon, in one of the defined icons + in this module. + :return: Corresponding QFile + :rtype: qt.QFile + :raises: ValueError when name is not known + """ + global _supported_formats + if _supported_formats is None: + _supported_formats = [] + supported_formats = qt.supportedImageFormats() + order = ["mng", "gif", "svg", "png", "jpg"] + for format_ in order: + if format_ in supported_formats: + _supported_formats.append(format_) + if len(_supported_formats) == 0: + _logger.error("No format supported for icons") + else: + _logger.debug("Format %s supported", ", ".join(_supported_formats)) + + for format_ in _supported_formats: + format_ = str(format_) + filename = resource_filename('gui/icons/%s.%s' % (name, format_)) + qfile = qt.QFile(filename) + if qfile.exists(): + return qfile + raise ValueError('Not an icon name: %s' % name) |