Mon, 04 Jan 2021 15:59:22 +0100
Changed the notification widget to be able to show multiple notification simultaneously.
--- 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):