src/eric7/UI/NotificationWidget.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
22 22
23 class NotificationTypes(enum.Enum): 23 class NotificationTypes(enum.Enum):
24 """ 24 """
25 Class implementing the notification types. 25 Class implementing the notification types.
26 """ 26 """
27
27 INFORMATION = 0 28 INFORMATION = 0
28 WARNING = 1 29 WARNING = 1
29 CRITICAL = 2 30 CRITICAL = 2
30 OTHER = 99 31 OTHER = 99
31 32
32 33
33 class NotificationFrame(QFrame, Ui_NotificationFrame): 34 class NotificationFrame(QFrame, Ui_NotificationFrame):
34 """ 35 """
35 Class implementing a Notification widget. 36 Class implementing a Notification widget.
36 """ 37 """
38
37 NotificationStyleSheetTemplate = "color:{0};background-color:{1};" 39 NotificationStyleSheetTemplate = "color:{0};background-color:{1};"
38 40
39 def __init__(self, icon, heading, text, 41 def __init__(
40 kind=NotificationTypes.INFORMATION, parent=None): 42 self, icon, heading, text, kind=NotificationTypes.INFORMATION, parent=None
43 ):
41 """ 44 """
42 Constructor 45 Constructor
43 46
44 @param icon icon to be used 47 @param icon icon to be used
45 @type QPixmap 48 @type QPixmap
46 @param heading heading to be used 49 @param heading heading to be used
47 @type str 50 @type str
48 @param text text to be used 51 @param text text to be used
52 @param parent reference to the parent widget 55 @param parent reference to the parent widget
53 @type QWidget 56 @type QWidget
54 """ 57 """
55 super().__init__(parent) 58 super().__init__(parent)
56 self.setupUi(self) 59 self.setupUi(self)
57 60
58 self.layout().setAlignment( 61 self.layout().setAlignment(
59 self.verticalLayout, 62 self.verticalLayout,
60 Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter 63 Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter,
61 ) 64 )
62 65
63 self.setStyleSheet(NotificationFrame.getStyleSheet(kind)) 66 self.setStyleSheet(NotificationFrame.getStyleSheet(kind))
64 67
65 if icon is None: 68 if icon is None:
66 icon = NotificationFrame.getIcon(kind) 69 icon = NotificationFrame.getIcon(kind)
67 self.icon.setPixmap(icon) 70 self.icon.setPixmap(icon)
68 71
69 self.heading.setText(heading) 72 self.heading.setText(heading)
70 self.text.setText(text) 73 self.text.setText(text)
71 74
72 self.show() 75 self.show()
73 self.adjustSize() 76 self.adjustSize()
74 77
75 @classmethod 78 @classmethod
76 def getIcon(cls, kind): 79 def getIcon(cls, kind):
77 """ 80 """
78 Class method to get the icon for a specific notification kind. 81 Class method to get the icon for a specific notification kind.
79 82
80 @param kind notification kind 83 @param kind notification kind
81 @type NotificationTypes 84 @type NotificationTypes
82 @return icon for the notification kind 85 @return icon for the notification kind
83 @rtype QPixmap 86 @rtype QPixmap
84 """ 87 """
85 if kind == NotificationTypes.CRITICAL: 88 if kind == NotificationTypes.CRITICAL:
86 return UI.PixmapCache.getPixmap("notificationCritical48") 89 return UI.PixmapCache.getPixmap("notificationCritical48")
87 elif kind == NotificationTypes.WARNING: # __NO-TASK__ 90 elif kind == NotificationTypes.WARNING: # __NO-TASK__
88 return UI.PixmapCache.getPixmap("notificationWarning48") 91 return UI.PixmapCache.getPixmap("notificationWarning48")
89 elif kind == NotificationTypes.INFORMATION: 92 elif kind == NotificationTypes.INFORMATION:
90 return UI.PixmapCache.getPixmap("notificationInformation48") 93 return UI.PixmapCache.getPixmap("notificationInformation48")
91 else: 94 else:
92 return UI.PixmapCache.getPixmap("notification48") 95 return UI.PixmapCache.getPixmap("notification48")
93 96
94 @classmethod 97 @classmethod
95 def getStyleSheet(cls, kind): 98 def getStyleSheet(cls, kind):
96 """ 99 """
97 Class method to get a style sheet for specific notification kind. 100 Class method to get a style sheet for specific notification kind.
98 101
99 @param kind notification kind 102 @param kind notification kind
100 @type NotificationTypes 103 @type NotificationTypes
101 @return string containing the style sheet for the notification kind 104 @return string containing the style sheet for the notification kind
102 @rtype str 105 @rtype str
103 """ 106 """
104 if kind == NotificationTypes.CRITICAL: 107 if kind == NotificationTypes.CRITICAL:
105 return NotificationFrame.NotificationStyleSheetTemplate.format( 108 return NotificationFrame.NotificationStyleSheetTemplate.format(
106 Preferences.getUI("NotificationCriticalForeground"), 109 Preferences.getUI("NotificationCriticalForeground"),
107 Preferences.getUI("NotificationCriticalBackground") 110 Preferences.getUI("NotificationCriticalBackground"),
108 ) 111 )
109 elif kind == NotificationTypes.WARNING: # __NO-TASK__ 112 elif kind == NotificationTypes.WARNING: # __NO-TASK__
110 return NotificationFrame.NotificationStyleSheetTemplate.format( 113 return NotificationFrame.NotificationStyleSheetTemplate.format(
111 Preferences.getUI("NotificationWarningForeground"), 114 Preferences.getUI("NotificationWarningForeground"),
112 Preferences.getUI("NotificationWarningBackground") 115 Preferences.getUI("NotificationWarningBackground"),
113 ) 116 )
114 else: 117 else:
115 return "" 118 return ""
116 119
117 120
118 class NotificationWidget(QWidget): 121 class NotificationWidget(QWidget):
119 """ 122 """
120 Class implementing a Notification list widget. 123 Class implementing a Notification list widget.
121 """ 124 """
125
122 def __init__(self, parent=None, setPosition=False): 126 def __init__(self, parent=None, setPosition=False):
123 """ 127 """
124 Constructor 128 Constructor
125 129
126 @param parent reference to the parent widget 130 @param parent reference to the parent widget
127 @type QWidget 131 @type QWidget
128 @param setPosition flag indicating to set the display 132 @param setPosition flag indicating to set the display
129 position interactively 133 position interactively
130 @type bool 134 @type bool
131 """ 135 """
132 super().__init__(parent) 136 super().__init__(parent)
133 137
134 self.__layout = QVBoxLayout(self) 138 self.__layout = QVBoxLayout(self)
135 self.__layout.setContentsMargins(0, 0, 0, 0) 139 self.__layout.setContentsMargins(0, 0, 0, 0)
136 self.setLayout(self.__layout) 140 self.setLayout(self.__layout)
137 141
138 self.__timeout = 5000 142 self.__timeout = 5000
139 self.__dragPosition = QPoint() 143 self.__dragPosition = QPoint()
140 self.__timers = {} 144 self.__timers = {}
141 self.__notifications = [] 145 self.__notifications = []
142 146
143 self.__settingPosition = setPosition 147 self.__settingPosition = setPosition
144 148
145 flags = ( 149 flags = (
146 Qt.WindowType.Tool | 150 Qt.WindowType.Tool
147 Qt.WindowType.FramelessWindowHint | 151 | Qt.WindowType.FramelessWindowHint
148 Qt.WindowType.WindowStaysOnTopHint | 152 | Qt.WindowType.WindowStaysOnTopHint
149 Qt.WindowType.X11BypassWindowManagerHint 153 | Qt.WindowType.X11BypassWindowManagerHint
150 ) 154 )
151 if Globals.isWindowsPlatform(): 155 if Globals.isWindowsPlatform():
152 flags |= Qt.WindowType.ToolTip 156 flags |= Qt.WindowType.ToolTip
153 self.setWindowFlags(flags) 157 self.setWindowFlags(flags)
154 158
155 if self.__settingPosition: 159 if self.__settingPosition:
156 self.setCursor(Qt.CursorShape.OpenHandCursor) 160 self.setCursor(Qt.CursorShape.OpenHandCursor)
157 161
158 def showNotification(self, icon, heading, text, 162 def showNotification(
159 kind=NotificationTypes.INFORMATION, timeout=0): 163 self, icon, heading, text, kind=NotificationTypes.INFORMATION, timeout=0
164 ):
160 """ 165 """
161 Public method to show a notification. 166 Public method to show a notification.
162 167
163 @param icon icon to be used 168 @param icon icon to be used
164 @type QPixmap 169 @type QPixmap
165 @param heading heading to be used 170 @param heading heading to be used
166 @type str 171 @type str
167 @param text text to be used 172 @param text text to be used
171 @param timeout timeout in seconds after which the notification is 176 @param timeout timeout in seconds after which the notification is
172 to be removed (0 = do not remove until it is clicked on) 177 to be removed (0 = do not remove until it is clicked on)
173 @type int 178 @type int
174 """ 179 """
175 notificationFrame = NotificationFrame( 180 notificationFrame = NotificationFrame(
176 icon, heading, text, kind=kind, parent=self) 181 icon, heading, text, kind=kind, parent=self
182 )
177 self.__layout.addWidget(notificationFrame) 183 self.__layout.addWidget(notificationFrame)
178 self.__notifications.append(notificationFrame) 184 self.__notifications.append(notificationFrame)
179 185
180 self.show() 186 self.show()
181 187
182 self.__adjustSizeAndPosition() 188 self.__adjustSizeAndPosition()
183 189
184 if timeout: 190 if timeout:
185 timer = QTimer() 191 timer = QTimer()
186 self.__timers[id(notificationFrame)] = timer 192 self.__timers[id(notificationFrame)] = timer
187 timer.setSingleShot(True) 193 timer.setSingleShot(True)
188 timer.timeout.connect( 194 timer.timeout.connect(lambda: self.__removeNotification(notificationFrame))
189 lambda: self.__removeNotification(notificationFrame)
190 )
191 timer.setInterval(timeout * 1000) 195 timer.setInterval(timeout * 1000)
192 timer.start() 196 timer.start()
193 197
194 def __adjustSizeAndPosition(self): 198 def __adjustSizeAndPosition(self):
195 """ 199 """
196 Private slot to adjust the notification list widget size and position. 200 Private slot to adjust the notification list widget size and position.
197 """ 201 """
198 self.adjustSize() 202 self.adjustSize()
199 203
200 if not self.__settingPosition: 204 if not self.__settingPosition:
201 pos = Preferences.getUI("NotificationPosition") 205 pos = Preferences.getUI("NotificationPosition")
202 try: 206 try:
203 screen = self.screen() 207 screen = self.screen()
204 except AttributeError: 208 except AttributeError:
205 # < Qt 5.15 209 # < Qt 5.15
206 from PyQt6.QtGui import QGuiApplication 210 from PyQt6.QtGui import QGuiApplication
211
207 screen = QGuiApplication.screenAt(pos) 212 screen = QGuiApplication.screenAt(pos)
208 screenGeom = screen.geometry() 213 screenGeom = screen.geometry()
209 214
210 newX = pos.x() 215 newX = pos.x()
211 newY = pos.y() 216 newY = pos.y()
212 if newX < screenGeom.x(): 217 if newX < screenGeom.x():
213 newX = screenGeom.x() 218 newX = screenGeom.x()
214 if newY < screenGeom.y(): 219 if newY < screenGeom.y():
215 newY = screenGeom.y() 220 newY = screenGeom.y()
216 if newX + self.width() > screenGeom.width(): 221 if newX + self.width() > screenGeom.width():
217 newX = screenGeom.width() - self.width() 222 newX = screenGeom.width() - self.width()
218 if newY + self.height() > screenGeom.height(): 223 if newY + self.height() > screenGeom.height():
219 newY = screenGeom.height() - self.height() 224 newY = screenGeom.height() - self.height()
220 225
221 self.move(newX, newY) 226 self.move(newX, newY)
222 227
223 def __removeNotification(self, notification): 228 def __removeNotification(self, notification):
224 """ 229 """
225 Private method to remove a notification from the list. 230 Private method to remove a notification from the list.
226 231
227 @param notification reference to the notification to be removed 232 @param notification reference to the notification to be removed
228 @type NotificationFrame 233 @type NotificationFrame
229 """ 234 """
230 notification.hide() 235 notification.hide()
231 236
232 # delete timer of an auto close notification 237 # delete timer of an auto close notification
233 key = id(notification) 238 key = id(notification)
234 if key in self.__timers: 239 if key in self.__timers:
235 self.__timers[key].stop() 240 self.__timers[key].stop()
236 del self.__timers[key] 241 del self.__timers[key]
237 242
238 # delete the notification 243 # delete the notification
239 index = self.__layout.indexOf(notification) 244 index = self.__layout.indexOf(notification)
240 self.__layout.takeAt(index) 245 self.__layout.takeAt(index)
241 with contextlib.suppress(ValueError): 246 with contextlib.suppress(ValueError):
242 self.__notifications.remove(notification) 247 self.__notifications.remove(notification)
243 notification.deleteLater() 248 notification.deleteLater()
244 249
245 if self.__layout.count(): 250 if self.__layout.count():
246 self.__adjustSizeAndPosition() 251 self.__adjustSizeAndPosition()
247 else: 252 else:
248 self.hide() 253 self.hide()
249 254
250 def mousePressEvent(self, evt): 255 def mousePressEvent(self, evt):
251 """ 256 """
252 Protected method to handle presses of a mouse button. 257 Protected method to handle presses of a mouse button.
253 258
254 @param evt reference to the mouse event (QMouseEvent) 259 @param evt reference to the mouse event (QMouseEvent)
255 """ 260 """
256 if not self.__settingPosition: 261 if not self.__settingPosition:
257 clickedLabel = self.childAt(evt.position().toPoint()) 262 clickedLabel = self.childAt(evt.position().toPoint())
258 if clickedLabel: 263 if clickedLabel:
259 clickedNotification = clickedLabel.parent() 264 clickedNotification = clickedLabel.parent()
260 self.__removeNotification(clickedNotification) 265 self.__removeNotification(clickedNotification)
261 return 266 return
262 267
263 if evt.button() == Qt.MouseButton.LeftButton: 268 if evt.button() == Qt.MouseButton.LeftButton:
264 self.__dragPosition = ( 269 self.__dragPosition = (
265 evt.globalPosition().toPoint() - self.frameGeometry().topLeft() 270 evt.globalPosition().toPoint() - self.frameGeometry().topLeft()
266 ) 271 )
267 self.setCursor(Qt.CursorShape.ClosedHandCursor) 272 self.setCursor(Qt.CursorShape.ClosedHandCursor)
268 evt.accept() 273 evt.accept()
269 274
270 def mouseReleaseEvent(self, evt): 275 def mouseReleaseEvent(self, evt):
271 """ 276 """
272 Protected method to handle releases of a mouse button. 277 Protected method to handle releases of a mouse button.
273 278
274 @param evt reference to the mouse event (QMouseEvent) 279 @param evt reference to the mouse event (QMouseEvent)
275 """ 280 """
276 if ( 281 if self.__settingPosition and evt.button() == Qt.MouseButton.LeftButton:
277 self.__settingPosition and
278 evt.button() == Qt.MouseButton.LeftButton
279 ):
280 self.setCursor(Qt.CursorShape.OpenHandCursor) 282 self.setCursor(Qt.CursorShape.OpenHandCursor)
281 283
282 def mouseMoveEvent(self, evt): 284 def mouseMoveEvent(self, evt):
283 """ 285 """
284 Protected method to handle dragging the window. 286 Protected method to handle dragging the window.
285 287
286 @param evt reference to the mouse event (QMouseEvent) 288 @param evt reference to the mouse event (QMouseEvent)
287 """ 289 """
288 if evt.buttons() & Qt.MouseButton.LeftButton: 290 if evt.buttons() & Qt.MouseButton.LeftButton:
289 self.move(evt.globalPosition().toPoint() - self.__dragPosition) 291 self.move(evt.globalPosition().toPoint() - self.__dragPosition)
290 evt.accept() 292 evt.accept()

eric ide

mercurial