eric6/UI/NotificationWidget.py

Mon, 04 Jan 2021 15:59:22 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 04 Jan 2021 15:59:22 +0100
changeset 7952
1849f61cf2b6
parent 7923
91e843545d9a
child 7956
7db67b70e6a8
permissions
-rw-r--r--

Changed the notification widget to be able to show multiple notification simultaneously.

# -*- coding: utf-8 -*-

# Copyright (c) 2012 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a Notification widget.
"""

from PyQt5.QtCore import Qt, QTimer, QPoint
from PyQt5.QtWidgets import QFrame, QWidget, QVBoxLayout

from .Ui_NotificationFrame import Ui_NotificationFrame

import Globals
import Preferences


class NotificationFrame(QFrame, Ui_NotificationFrame):
    """
    Class implementing a Notification widget.
    """
    def __init__(self, icon, heading, text, parent=None):
        """
        Constructor
        
        @param icon icon to be used
        @type QPixmap
        @param heading heading to be used
        @type str
        @param text text to be used
        @type str
        @param parent reference to the parent widget
        @type QWidget
        """
        super(NotificationFrame, self).__init__(parent)
        self.setupUi(self)
        
        self.layout().setAlignment(
            self.verticalLayout, Qt.AlignLeft | Qt.AlignVCenter)
        
        self.icon.setPixmap(icon)
        self.heading.setText(heading)
        self.text.setText(text)
        
        self.show()
        self.adjustSize()


class NotificationWidget(QWidget):
    """
    Class implementing a Notification list widget.
    """
    def __init__(self, parent=None, setPosition=False):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        @param setPosition flag indicating to set the display
            position interactively
        @type bool
        """
        super(NotificationWidget, self).__init__(parent)
        
        self.__layout = QVBoxLayout(self)
        self.__layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.__layout)
        
        self.__timeout = 5000
        self.__dragPosition = QPoint()
        self.__timers = {}
        
        self.__settingPosition = setPosition
        
        flags = (
            Qt.Tool |
            Qt.FramelessWindowHint |
            Qt.WindowStaysOnTopHint |
            Qt.X11BypassWindowManagerHint
        )
        if Globals.isWindowsPlatform():
            flags |= Qt.ToolTip
        self.setWindowFlags(flags)
        
        if self.__settingPosition:
            self.setCursor(Qt.OpenHandCursor)
    
    def showNotification(self, icon, heading, text, timeout=0):
        """
        Public method to show a notification.
        
        @param icon icon to be used
        @type QPixmap
        @param heading heading to be used
        @type str
        @param text text to be used
        @type str
        @param timeout timeout in seconds after which the notification is
            to be removed (0 = do not remove until it is clicked on)
        """
        notificationFrame = NotificationFrame(icon, heading, text, self)
        self.__layout.addWidget(notificationFrame)
        
        self.show()
        
        self.__adjustSizeAndPosition()
        
        if timeout:
            timer = QTimer()
            self.__timers[repr(notificationFrame)] = timer
            timer.setSingleShot(True)
            timer.timeout.connect(
                lambda: self.__removeNotification(notificationFrame)
            )
            timer.setInterval(timeout * 1000)
            timer.start()
    
    def __adjustSizeAndPosition(self):
        """
        Private slot to adjust the notification list widget size and position.
        """
        self.adjustSize()
        
        if not self.__settingPosition:
            pos = Preferences.getUI("NotificationPosition")
            try:
                screen = self.screen()
            except AttributeError:
                # < Qt 5.15
                from PyQt5.QtGui import QGuiApplication
                screen = QGuiApplication.screenAt(pos)
            screenGeom = screen.geometry()
            
            newX = pos.x()
            newY = pos.y()
            if newX < screenGeom.x():
                newX = screenGeom.x()
            if newY < screenGeom.y():
                newY = screenGeom.y()
            if newX + self.width() > screenGeom.width():
                newX = screenGeom.width() - self.width()
            if newY + self.height() > screenGeom.height():
                newY = screenGeom.height() - self.height()
            
            self.move(newX, newY)
    
    def __removeNotification(self, notification):
        """
        Private method to remove a notification from the list.
        
        @param notification reference to the notification to be removed
        @type NotificationFrame
        """
        notification.hide()
        
        # delete timer of an auto close notification
        key = repr(notification)
        if key in self.__timers:
            self.__timers[key].stop()
            del self.__timers[key]
        
        # delete the notification
        index = self.__layout.indexOf(notification)
        self.__layout.takeAt(index)
        notification.deleteLater()
        
        if self.__layout.count():
            self.__adjustSizeAndPosition()
        else:
            self.close()
    
    def mousePressEvent(self, evt):
        """
        Protected method to handle presses of a mouse button.
        
        @param evt reference to the mouse event (QMouseEvent)
        """
        if not self.__settingPosition:
            clickedLabel = self.childAt(evt.pos())
            clickedNotification = clickedLabel.parent()
            self.__removeNotification(clickedNotification)
            return
        
        if evt.button() == Qt.LeftButton:
            self.__dragPosition = (
                evt.globalPos() - self.frameGeometry().topLeft()
            )
            self.setCursor(Qt.ClosedHandCursor)
            evt.accept()
    
    def mouseReleaseEvent(self, evt):
        """
        Protected method to handle releases of a mouse button.
        
        @param evt reference to the mouse event (QMouseEvent)
        """
        if self.__settingPosition and evt.button() == Qt.LeftButton:
            self.setCursor(Qt.OpenHandCursor)
    
    def mouseMoveEvent(self, evt):
        """
        Protected method to handle dragging the window.
        
        @param evt reference to the mouse event (QMouseEvent)
        """
        if evt.buttons() & Qt.LeftButton:
            self.move(evt.globalPos() - self.__dragPosition)
            evt.accept()

eric ide

mercurial