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

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 7951
fa058239133f
child 7953
68c12f4cb876

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

eric6.e4p file | annotate | diff | comparison | revisions
eric6/Preferences/ConfigurationPages/NotificationsPage.py file | annotate | diff | comparison | revisions
eric6/UI/NotificationFrame.ui file | annotate | diff | comparison | revisions
eric6/UI/NotificationWidget.py file | annotate | diff | comparison | revisions
eric6/UI/NotificationWidget.ui file | annotate | diff | comparison | revisions
eric6/UI/UserInterface.py file | annotate | diff | comparison | revisions
eric6/WebBrowser/WebBrowserWindow.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Mon Jan 04 15:51:18 2021 +0100
+++ b/eric6.e4p	Mon Jan 04 15:59:22 2021 +0100
@@ -1992,7 +1992,7 @@
     <Form>eric6/UI/FindFileDialog.ui</Form>
     <Form>eric6/UI/FindFileNameDialog.ui</Form>
     <Form>eric6/UI/InstallInfoDialog.ui</Form>
-    <Form>eric6/UI/NotificationWidget.ui</Form>
+    <Form>eric6/UI/NotificationFrame.ui</Form>
     <Form>eric6/UI/NumbersWidget.ui</Form>
     <Form>eric6/UI/Previewers/PreviewerQSS.ui</Form>
     <Form>eric6/UI/PythonDisViewer.ui</Form>
--- a/eric6/Preferences/ConfigurationPages/NotificationsPage.py	Mon Jan 04 15:51:18 2021 +0100
+++ b/eric6/Preferences/ConfigurationPages/NotificationsPage.py	Mon Jan 04 15:59:22 2021 +0100
@@ -29,18 +29,11 @@
         self.setupUi(self)
         self.setObjectName("NotificationsPage")
         
-        minX, maxX = self.xSpinBox.maximum(), self.xSpinBox.minimum()
-        minY, maxY = self.ySpinBox.maximum(), self.ySpinBox.minimum()
-        for screen in QApplication.screens():
-            geom = screen.availableGeometry()
-            minX = min(minX, geom.x())
-            maxX = max(maxX, geom.x() + geom.width())
-            minY = min(minY, geom.y())
-            maxY = max(maxY, geom.y() + geom.height())
-        self.xSpinBox.setMinimum(minX)
-        self.xSpinBox.setMaximum(maxX)
-        self.ySpinBox.setMinimum(minY)
-        self.ySpinBox.setMaximum(maxY)
+        geom = QApplication.screens()[0].availableVirtualGeometry()
+        self.xSpinBox.setMinimum(geom.x())
+        self.xSpinBox.setMaximum(geom.width())
+        self.ySpinBox.setMinimum(geom.y())
+        self.ySpinBox.setMaximum(geom.height())
         
         self.__notification = None
         self.__firstTime = True
@@ -52,6 +45,9 @@
         point = Preferences.getUI("NotificationPosition")
         self.xSpinBox.setValue(point.x())
         self.ySpinBox.setValue(point.y())
+        
+        self.xSpinBox.valueChanged.connect(self.__moveNotification)
+        self.ySpinBox.valueChanged.connect(self.__moveNotification)
     
     def save(self):
         """
@@ -74,15 +70,15 @@
             from UI.NotificationWidget import NotificationWidget
             self.__notification = NotificationWidget(
                 parent=self, setPosition=True)
-            self.__notification.setPixmap(
-                UI.PixmapCache.getPixmap("notification48"))
-            self.__notification.setHeading(self.tr("Visual Selection"))
-            self.__notification.setText(
+            self.__notification.showNotification(
+                UI.PixmapCache.getPixmap("notification48"),
+                self.tr("Visual Selection"),
                 self.tr("Drag the notification window to"
-                        " the desired place and release the button."))
+                        " the desired place and release the button."),
+                timeout=0
+            )
             self.__notification.move(
                 QPoint(self.xSpinBox.value(), self.ySpinBox.value()))
-            self.__notification.show()
             if self.__firstTime:
                 # adjust the maximum values to the width of the notification
                 self.xSpinBox.setMaximum(
@@ -98,6 +94,17 @@
             self.__notification.close()
             self.__notification = None
     
+    @pyqtSlot()
+    def __moveNotification(self):
+        """
+        Private slot to move the notification widget.
+        """
+        if self.visualButton.isChecked():
+            self.__notification.move(
+                self.xSpinBox.value(),
+                self.ySpinBox.value()
+            )
+
 
 def create(dlg):
     """
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/UI/NotificationFrame.ui	Mon Jan 04 15:59:22 2021 +0100
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NotificationFrame</class>
+ <widget class="QFrame" name="NotificationFrame">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>80</height>
+   </rect>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <property name="leftMargin">
+    <number>4</number>
+   </property>
+   <property name="topMargin">
+    <number>4</number>
+   </property>
+   <property name="rightMargin">
+    <number>4</number>
+   </property>
+   <property name="bottomMargin">
+    <number>4</number>
+   </property>
+   <item>
+    <widget class="QLabel" name="icon">
+     <property name="maximumSize">
+      <size>
+       <width>48</width>
+       <height>48</height>
+      </size>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="heading">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>335</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="text">
+       <property name="minimumSize">
+        <size>
+         <width>335</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+       <property name="wordWrap">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
--- a/eric6/UI/NotificationWidget.py	Mon Jan 04 15:51:18 2021 +0100
+++ b/eric6/UI/NotificationWidget.py	Mon Jan 04 15:59:22 2021 +0100
@@ -8,34 +8,68 @@
 """
 
 from PyQt5.QtCore import Qt, QTimer, QPoint
-from PyQt5.QtGui import QPixmap
-from PyQt5.QtWidgets import QWidget
+from PyQt5.QtWidgets import QFrame, QWidget, QVBoxLayout
 
-from .Ui_NotificationWidget import Ui_NotificationWidget
+from .Ui_NotificationFrame import Ui_NotificationFrame
 
 import Globals
+import Preferences
 
 
-class NotificationWidget(QWidget, Ui_NotificationWidget):
+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 (QWidget)
+        @param parent reference to the parent widget
+        @type QWidget
         @param setPosition flag indicating to set the display
-            position interactively (boolean)
+            position interactively
+        @type bool
         """
         super(NotificationWidget, self).__init__(parent)
-        self.setupUi(self)
+        
+        self.__layout = QVBoxLayout(self)
+        self.__layout.setContentsMargins(0, 0, 0, 0)
+        self.setLayout(self.__layout)
         
         self.__timeout = 5000
-        self.__icon = QPixmap()
-        self.__heading = ""
-        self.__text = ""
         self.__dragPosition = QPoint()
+        self.__timers = {}
         
         self.__settingPosition = setPosition
         
@@ -49,67 +83,92 @@
             flags |= Qt.ToolTip
         self.setWindowFlags(flags)
         
-        self.frame.layout().setAlignment(
-            self.verticalLayout, Qt.AlignLeft | Qt.AlignVCenter)
-        
-        self.__timer = QTimer(self)
-        self.__timer.setSingleShot(True)
-        self.__timer.timeout.connect(self.close)
-        
         if self.__settingPosition:
             self.setCursor(Qt.OpenHandCursor)
     
-    def setPixmap(self, icon):
-        """
-        Public method to set the icon for the notification.
-        
-        @param icon icon to be used (QPixmap)
-        """
-        self.__icon = QPixmap(icon)
-    
-    def setHeading(self, heading):
+    def showNotification(self, icon, heading, text, timeout=0):
         """
-        Public method to set the heading for the notification.
+        Public method to show a notification.
         
-        @param heading heading to be used (string)
+        @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)
         """
-        self.__heading = heading
-    
-    def setText(self, text):
-        """
-        Public method to set the text for the notification.
+        notificationFrame = NotificationFrame(icon, heading, text, self)
+        self.__layout.addWidget(notificationFrame)
+        
+        self.show()
         
-        @param text text to be used (string)
-        """
-        self.__text = text
-    
-    def setTimeout(self, timeout):
-        """
-        Public method to set the timeout for the notification.
+        self.__adjustSizeAndPosition()
         
-        @param timeout timeout to be used in seconds (0 = indefinitely)
-        @type int
-        """
-        self.__timeout = timeout * 1000
+        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 show(self):
-        """
-        Public method to show the notification.
+    def __adjustSizeAndPosition(self):
         """
-        self.icon.setPixmap(self.__icon)
-        self.heading.setText(self.__heading)
-        self.text.setText(self.__text)
+        Private slot to adjust the notification list widget size and position.
+        """
+        self.adjustSize()
         
         if not self.__settingPosition:
-            self.__timer.stop()
-            if self.__timeout > 0:
-                self.__timer.setInterval(self.__timeout)
-                self.__timer.start()
+            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.
         
-        super(NotificationWidget, self).show()
+        @param notification reference to the notification to be removed
+        @type NotificationFrame
+        """
+        notification.hide()
         
-        sh = self.sizeHint()
-        self.resize(max(self.width(), sh.width()), sh.height())
+        # 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):
         """
@@ -118,7 +177,9 @@
         @param evt reference to the mouse event (QMouseEvent)
         """
         if not self.__settingPosition:
-            self.close()
+            clickedLabel = self.childAt(evt.pos())
+            clickedNotification = clickedLabel.parent()
+            self.__removeNotification(clickedNotification)
             return
         
         if evt.button() == Qt.LeftButton:
--- a/eric6/UI/NotificationWidget.ui	Mon Jan 04 15:51:18 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>NotificationWidget</class>
- <widget class="QWidget" name="NotificationWidget">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>400</width>
-    <height>80</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string/>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout_2">
-   <property name="margin">
-    <number>0</number>
-   </property>
-   <item>
-    <widget class="QFrame" name="frame">
-     <property name="frameShape">
-      <enum>QFrame::StyledPanel</enum>
-     </property>
-     <property name="frameShadow">
-      <enum>QFrame::Raised</enum>
-     </property>
-     <layout class="QHBoxLayout" name="horizontalLayout">
-      <property name="margin">
-       <number>4</number>
-      </property>
-      <item>
-       <widget class="QLabel" name="icon">
-        <property name="maximumSize">
-         <size>
-          <width>48</width>
-          <height>48</height>
-         </size>
-        </property>
-        <property name="text">
-         <string/>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <layout class="QVBoxLayout" name="verticalLayout">
-        <item>
-         <widget class="QLabel" name="heading">
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="minimumSize">
-           <size>
-            <width>335</width>
-            <height>0</height>
-           </size>
-          </property>
-          <property name="font">
-           <font>
-            <weight>75</weight>
-            <bold>true</bold>
-           </font>
-          </property>
-          <property name="text">
-           <string/>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QLabel" name="text">
-          <property name="minimumSize">
-           <size>
-            <width>335</width>
-            <height>0</height>
-           </size>
-          </property>
-          <property name="text">
-           <string/>
-          </property>
-          <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
-          </property>
-          <property name="wordWrap">
-           <bool>true</bool>
-          </property>
-         </widget>
-        </item>
-       </layout>
-      </item>
-     </layout>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
--- a/eric6/UI/UserInterface.py	Mon Jan 04 15:51:18 2021 +0100
+++ b/eric6/UI/UserInterface.py	Mon Jan 04 15:59:22 2021 +0100
@@ -7435,14 +7435,10 @@
             if self.__notification is None:
                 from .NotificationWidget import NotificationWidget
                 self.__notification = NotificationWidget(parent=self)
-            self.__notification.setPixmap(icon)
-            self.__notification.setHeading(heading)
-            self.__notification.setText(text)
             if timeout is None:
                 timeout = Preferences.getUI("NotificationTimeout")
-            self.__notification.setTimeout(timeout)
-            self.__notification.move(Preferences.getUI("NotificationPosition"))
-            self.__notification.show()
+            self.__notification.showNotification(icon, heading, text,
+                                                 timeout=timeout)
     
     def notificationsEnabled(self):
         """
--- a/eric6/WebBrowser/WebBrowserWindow.py	Mon Jan 04 15:51:18 2021 +0100
+++ b/eric6/WebBrowser/WebBrowserWindow.py	Mon Jan 04 15:59:22 2021 +0100
@@ -4736,15 +4736,10 @@
             if cls._notification is None:
                 from UI.NotificationWidget import NotificationWidget
                 cls._notification = NotificationWidget()
-            cls._notification.setPixmap(icon)
-            cls._notification.setHeading(heading)
-            cls._notification.setText(text)
             if timeout is None:
                 timeout = Preferences.getUI("NotificationTimeout")
-            cls._notification.setTimeout(timeout)
-            cls._notification.move(
-                Preferences.getUI("NotificationPosition"))
-            cls._notification.show()
+            cls._notification.showNotification(icon, heading, text,
+                                               timeout=timeout)
     
     @classmethod
     def notificationsEnabled(cls):

eric ide

mercurial