PKdH֠QNotifications/QNotification.py# -*- coding: utf-8 -*- """ @author: Daniel Schreij This file is part of QNotifications. QNotifications is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QNotifications is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GPLv3 License along with this module.>. """ # Python3 compatibility from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals from qtpy import QtWidgets, QtGui, QtCore from QNotifications.abstractions import * __author__ = u"Daniel Schreij" __license__ = u"GPLv3" class MessageLabel(QtWidgets.QLabel): """ Subclass of QLabel, which reimplements the resizeEvent() function. This is necessary because otherwise the notifications take up too much vertical space when texts they display become longer. This is because normally the height of a notification is calculated as the minimum height necessary for the text when the widget is horizontally resized to its minimum. """ def resizeEvent(self, event): super(MessageLabel, self).resizeEvent(event) if ( self.wordWrap() and \ self.sizePolicy().verticalPolicy() == QtWidgets.QSizePolicy.Minimum ): self.setMaximumHeight( self.heightForWidth( self.width() ) ) class QNotification(QtWidgets.QWidget): """ Class representing a single notification """ closeClicked = QtCore.pyqtSignal() def __init__(self, message, category, *args, **kwargs): """ Constructor create a notification Parameters ---------- message : str The message to show category : str The type of notification. Adheres to bootstrap standard classes which are [primary, success, info, warning, danger] """ super(QNotification, self).__init__(*args, **kwargs) # Store instance variables self.message = message self.category = category # Set Object name for reference self.setObjectName(category) self.setLayout(QtWidgets.QHBoxLayout()) self.setContentsMargins(0,0,0,0) # self.setSizePolicy(QtWidgets.QSizePolicy.Minimum, # QtWidgets.QSizePolicy.Fixed) # Create a message area #contents = QtWidgets.QWidget(self) messageArea = QtWidgets.QHBoxLayout() messageArea.setContentsMargins(0,0,0,0) # Create the layout self.message_display = MessageLabel() self.message_display.setObjectName("message") self.message_display.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.message_display.setWordWrap(True) # Create a button that can close notifications close_button = QtWidgets.QPushButton(u"\u2715") close_button.setObjectName("closeButton") close_button.setFixedWidth(20) close_button.setFlat(True) close_button.clicked.connect(self.closeClicked) # Add everything together messageArea.addWidget(self.message_display) # messageArea.addStretch(1) messageArea.addWidget(close_button) self.layout().addLayout(messageArea) # Initialize some variables # self.setStyle(category) self.setVisible(False) # Flag that is set if notification is being removed. This can be used to # make sure that even though the notification has not been really removed # yet (because it is for example in an fade out animation), it is in the # process of being removed self.isBeingRemoved = False self.__init_graphic_effects() def __init_graphic_effects(self): """ Initializes graphic effects """ # Opacityeffect for fade in/out self.opacityEffect = QtWidgets.QGraphicsOpacityEffect(self) # Fade in animation self.fadeInAnimation = QtCore.QPropertyAnimation(self.opacityEffect, safe_encode("opacity")) self.fadeInAnimation.setStartValue(0.0) self.fadeInAnimation.setEndValue(1.0) # Fade out animation self.fadeOutAnimation = QtCore.QPropertyAnimation(self.opacityEffect, safe_encode("opacity")) self.fadeOutAnimation.setStartValue(1.0) self.fadeOutAnimation.setEndValue(0.0) def display(self): """ Display the notification """ self.message_display.setText(self.message) self.show() def close(self): """ Close the notification """ super(QNotification,self).close() self.deleteLater() def fadeIn(self, duration): """ Fade in the notification Parameters ---------- duration : int The desired duration of the animation """ self.setGraphicsEffect(self.opacityEffect) self.fadeInAnimation.setDuration(duration) self.display() self.fadeInAnimation.start() def fadeOut(self, finishedCallback, duration): """ Fade out the notification Parameters ---------- finishedCallback : callable The function to call after the animation has finished (to for instance clean up the notification) duration : int The desired duration of the animation """ self.setGraphicsEffect(self.opacityEffect) self.fadeOutAnimation.setDuration(duration) self.fadeOutAnimation.finished.connect(lambda: finishedCallback(self)) self.isBeingRemoved = True self.fadeOutAnimation.start() def paintEvent(self, pe): """ redefinition of paintEvent, to make class QNotification available in style sheets. Interal Qt function. Do not call directly. """ o = QtWidgets.QStyleOption() o.initFrom(self) p = QtGui.QPainter(self) self.style().drawPrimitive(QtWidgets.QStyle.PE_Widget, o, p, self) ### Property attributes @property def message(self): """ The currently set message to display """ return self._message @message.setter def message(self, value): """ Sets the message to display """ self._message = value @property def category(self): """ The currently set category of this notification """ return self._category @category.setter def category(self, value): """ Sets the category of this notification. Should be one of [u'primary',u'success',u'info',u'warning',u'danger'] """ allowed_values = [u'primary',u'success',u'info',u'warning',u'danger'] if not value in allowed_values: raise ValueError(_(u'{} not a valid value. ' 'Should be one of').format(value, allowed_values)) self._category = value PKIHddQNotifications/__init__.py# -*- coding: utf-8 -*- """ @author: Daniel Schreij This file is part of QNotifications. QNotifications is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QNotifications is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GPLv3 License along with this module.>. """ # Do some base imports from QNotifications.QNotificationArea import QNotificationArea from QNotifications.QNotification import QNotification __version__ = "1.0.0" __author__ = "Daniel Schreij (dschreij@gmail.com)"PKIH.QNotifications/abstractions.py# -*- coding: utf-8 -*- """ Created on Fri Mar 11 13:11:14 2016 """ import sys from qtpy import QtCore if sys.version_info >= (3,0,0): py3 = True basestring = str universal_newline_mode = u'r' else: bytes = str str = unicode py3 = False universal_newline_mode = u'rU' def safe_decode(s, enc='utf-8', errors='strict'): if isinstance(s, str): return s if isinstance(s, bytes): return s.decode(enc, errors) # Numeric values are encoded right away if isinstance(s, int) or isinstance(s, float): return str(s) # Some types need to be converted to unicode, but require the encoding # and errors parameters. Notable examples are Exceptions, which have # strange characters under some locales, such as French. It even appears # that, at least in some cases, they have to be encodeed to str first. # Presumably, there is a better way to do this, but for now this at # least gives sensible results. try: return safe_decode(bytes(s), enc=enc, errors=errors) except: pass # For other types, the unicode representation doesn't require a specific # encoding. This mostly applies to non-stringy things, such as integers. return str(s) def safe_encode(s, enc='utf-8', errors='strict'): if isinstance(s, bytes): return s return s.encode(enc, errors) if py3: safe_str = safe_decode else: safe_str = safe_encode __all__ = ['py3', 'safe_decode', 'safe_encode', 'safe_str', 'universal_newline_mode'] if not py3: __all__ += ['str', 'bytes'] else: __all__ += ['basestring'] PKdH ASS#QNotifications/QNotificationArea.py# -*- coding: utf-8 -*- """ @author: Daniel Schreij This file is part of QNotifications. QNotifications is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QNotifications is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GPLv3 License along with this module.>. """ # Python3 compatibility from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals from qtpy import QtWidgets, QtCore, QtGui from QNotifications.QNotification import QNotification from QNotifications.abstractions import * __author__ = u"Daniel Schreij" __license__ = u"GPLv3" class QNotificationArea(QtWidgets.QWidget): """ Notification area to show notifications in. Will be projected on top of another QWidget which should be passed as an argument to this class. """ default_notification_styles = u""" QNotification { font-size: 16px; padding: 0px; margin: 0px; border-radius: 6px; } QNotification #message{ color: #FFFFFF; padding: 0px; margin: 0px; width: 100%; } QNotification #closeButton{ color: #FFFFFF; padding: 0px; margin: 0px; } QNotification#primary { background-color: #337ab7; border-color: #2e6da4; } QNotification#success { background-color: #5cb85c; border-color: #4cae4c; } QNotification#info { background-color: #5bc0de; border-color: #46b8da; } QNotification#warning { background-color: #f0ad4e; border-color: #eea236; } QNotification#danger { background-color: #d9534f; border-color: #d43f3a; } """ ### OpenSesame events def __init__(self, targetWidget, *args, **kwargs): """ Parameters ---------- targetWidget : QtWidgets.QWidget The widget to project the norifications on useGlobalCSS : bool (default: False) Flag which indicates whether global style sheets should be used (which have been set at app-level). If False, the default style sheets stored at self.default_notification_styles will be loaded. """ useGlobalCSS = kwargs.pop(u'useGlobalCSS', False) super(QNotificationArea, self).__init__(*args, **kwargs) if not useGlobalCSS: self.setStyleSheet(self.default_notification_styles) self.setParent(targetWidget) self.targetWidget = targetWidget self.setContentsMargins(0,0,0,0) notification_area_layout = QtWidgets.QVBoxLayout() self.setLayout(notification_area_layout) self.mapTo(targetWidget, QtCore.QPoint(0,0)) # Init effects to None self.entryEffect = None self.entryEffectDuration = None self.exitEffect = None self.exitEffectDuration = None # Store original target classes resizeEvent to be called in our own # function self.target_resize_event = targetWidget.resizeEvent # Overwrite resizeEvent function of targetWidget to capture it ourself # (parent's resizeEvent will be called in our function too) self.targetWidget.resizeEvent = self.resizeEvent def __delete_notification(self, notification=None): """ Close and destroy the supplied notification """ notification.close() self.layout().removeWidget(notification) self.adjustSize() # Public functions def setEntryEffect(self, effect, duration=250): """ Set the effect with which the notifications are to appear Parameters ---------- effect : str or None the effect which should be used (for now only 'fadeIn' is available) if None is passed for this argument, no effect will be used and the notifcations will just appear directly. duration : int (default: 250 ms) The duration of the effect in milliseconds """ if not effect in [u'fadeIn', None]: raise ValueError(u'Invalid entry effect') if not isinstance(duration, int): raise TypeError(u'Duration should be an int') if duration < 0: raise ValueError(u'Duration should be larger than 0') self.entryEffect = effect self.entryEffectDuration = duration def setExitEffect(self, effect, duration=500): """ Set the effect with which the notifications are to disappear Parameters ---------- effect : str or None the effect which should be used (for now only 'fadeOut' is available) if None is passed for this argument, no effect will be used and the notifcations will just appear directly. duration : int (default: 1000 ms) The duration of the effect in milliseconds """ if not effect in [u'fadeOut', None]: raise ValueError(u'Invalid exit effect') if not isinstance(duration, int): raise TypeError(u'Duration should be an int') if duration < 0: raise ValueError(u'Duration should be larger than 0') self.exitEffect = effect self.exitEffectDuration = duration # Events @QtCore.pyqtSlot('QString', 'QString', int) def display(self, message, category, timeout=5000): """ Displays a notification Parameters ---------- message : str The message to display category : str The type of notification that should be shown. Adheres to bootstrap standards which are [primary, success, info, warning, danger] timeout : int (default: 5000) The duration for which the notification should be shown. If None then the notification will be shown indefinitely """ notification = QNotification(message, category, self) notification.closeClicked.connect(self.remove) self.layout().addWidget(notification) # Check for entry effects if not self.entryEffect is None: if self.entryEffect == u"fadeIn": notification.fadeIn(self.entryEffectDuration) else: notification.display() self.adjustSize() if not timeout is None and timeout > 0: QtCore.QTimer.singleShot(timeout, lambda : self.remove(notification)) @QtCore.pyqtSlot() def remove(self, notification = None): """ Removes a notification Parameters ---------- notification : QNotification (default: None) The notification to remove. This function also serves as a PyQt slot for signals emitted from a QNotification. In this case, the QNotification object is retrieved by using self.sender() """ # This function also functions as a pyqt slot. In that case, no # notification argument is passed, but this is set as self.sender() if notification is None: try: notification = self.sender() except: raise ValueError(u'QNotification object needs to be passed ' 'or this function should be used as a slot for a signal' ' emitted by a QNotification') if notification.isBeingRemoved: return else: notification.isBeingRemoved = True # Check if notification is still present (and has not manually been # closed before this function is called by a timeout) if self.layout().indexOf(notification) < 0: return # Implement animation here if self.exitEffect == u'fadeOut': notification.fadeOut(self.__delete_notification, self.exitEffectDuration) else: self.__delete_notification(notification) # Internal Qt functions def resizeEvent(self, event): """ Internal QT functions (do not call directly) """ self.target_resize_event(event) newsize = event.size() self.setFixedWidth(newsize.width()) self.adjustSize() def paintEvent(self, pe): """ redefinition of paintEvent, to make class QNotificationArea available in style sheets. Internal QT function (do not call directly) """ o = QtWidgets.QStyleOption() o.initFrom(self) p = QtGui.QPainter(self) self.style().drawPrimitive(QtWidgets.QStyle.PE_Widget, o, p, self) PK-dH^- 5python_qnotifications-1.0.0.dist-info/DESCRIPTION.rstUNKNOWN PK-dHPؗx  3python_qnotifications-1.0.0.dist-info/metadata.json{"classifiers": ["Intended Audience :: Developers", "Topic :: Desktop Environment", "Topic :: Communications :: Email", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"contacts": [{"email": "dschreij@gmail.com", "name": "Daniel Schreij", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/dschreij/QNotifications"}}}, "generator": "bdist_wheel (0.26.0)", "metadata_version": "2.0", "name": "python-qnotifications", "summary": "Pretty in-app notifications for PyQt", "version": "1.0.0"}PK-dHo3python_qnotifications-1.0.0.dist-info/top_level.txtQNotifications PK-dH''\\+python_qnotifications-1.0.0.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any PK-dHM.python_qnotifications-1.0.0.dist-info/METADATAMetadata-Version: 2.0 Name: python-qnotifications Version: 1.0.0 Summary: Pretty in-app notifications for PyQt Home-page: https://github.com/dschreij/QNotifications Author: Daniel Schreij Author-email: dschreij@gmail.com License: UNKNOWN Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: Topic :: Desktop Environment Classifier: Topic :: Communications :: Email Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 UNKNOWN PK-dHwx,python_qnotifications-1.0.0.dist-info/RECORDQNotifications/QNotification.py,sha256=Bk-sMBVKK9YStlMWGTPbs9wZq1NVSDHBGIAUrnk4kEs,6379 QNotifications/QNotificationArea.py,sha256=SiT8EatyW2S_bfoN_4UlUaNd5ygYUZC6YwAaRzrqX4A,7763 QNotifications/__init__.py,sha256=Fzt4vwgcz6uvw5Y48OUNq5lWRwuMZVyfamF97QZ9rio,868 QNotifications/abstractions.py,sha256=zFa93-d3N_SJ8HtTO-LMEMoHDiyQoCBYNaR5bi6EIZE,1499 python_qnotifications-1.0.0.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 python_qnotifications-1.0.0.dist-info/METADATA,sha256=pbPIqVrNy4mHx_7UkJZfYaIiisAYPlSLqtXtOxt0MhY,710 python_qnotifications-1.0.0.dist-info/RECORD,, python_qnotifications-1.0.0.dist-info/WHEEL,sha256=JTb7YztR8fkPg6aSjc571Q4eiVHCwmUDlX8PhuuqIIE,92 python_qnotifications-1.0.0.dist-info/metadata.json,sha256=LsaSsR3DcNUVPpwkKde9Cu4Sfq7yoIvwyfFmMI6J0dM,800 python_qnotifications-1.0.0.dist-info/top_level.txt,sha256=Ez9jX_z-5_4hG3nwAewXOnfP4qrYlAvLLJ5o4Er6qoE,15 PKdH֠QNotifications/QNotification.pyPKIHdd(QNotifications/__init__.pyPKIH.QNotifications/abstractions.pyPKdH ASS#"QNotifications/QNotificationArea.pyPK-dH^- 5oApython_qnotifications-1.0.0.dist-info/DESCRIPTION.rstPK-dHPؗx  3Apython_qnotifications-1.0.0.dist-info/metadata.jsonPK-dHo3=Epython_qnotifications-1.0.0.dist-info/top_level.txtPK-dH''\\+Epython_qnotifications-1.0.0.dist-info/WHEELPK-dHM.BFpython_qnotifications-1.0.0.dist-info/METADATAPK-dHwx,TIpython_qnotifications-1.0.0.dist-info/RECORDPK f=M