Files
gotify-tray-customized/gotify_tray/gui/widgets/PersistentNotification.py
kdusek 2108568f50
Some checks failed
build / build-pip (push) Failing after 1m13s
build / build-win64 (push) Has been cancelled
build / build-macos (push) Has been cancelled
Add persistent notifications for priority 10 messages
- Implement custom PersistentNotification widget with flashing background
- Add settings for persistent priority 10 notifications and sound control
- Modify notification logic to show persistent pop-ups for priority 10
- Allow closing all persistent notifications via tray icon click
- Add AGENTS.md with type checking guidelines
- Configure pyright to suppress PyQt6 false positives
- Update UI in settings dialog for new options
- Add notification sound file
2025-11-26 15:10:50 +01:00

146 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
from PyQt6 import QtCore, QtGui, QtWidgets
logger = logging.getLogger("gotify-tray")
class PersistentNotification(QtWidgets.QWidget):
close_all_requested = QtCore.pyqtSignal()
def __init__(
self,
title: str,
message: str,
icon: QtGui.QIcon | QtWidgets.QSystemTrayIcon.MessageIcon,
y_offset: int = 0,
flash: bool = False,
parent=None,
):
super().__init__(parent)
self.y_offset = y_offset
self.flash = flash
self.flash_state = False
self.original_stylesheet = ""
self.setWindowFlags(
QtCore.Qt.WindowType.Window
| QtCore.Qt.WindowType.FramelessWindowHint
| QtCore.Qt.WindowType.WindowStaysOnTopHint
)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_OpaquePaintEvent, True)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_ShowWithoutActivating)
# Layout
layout = QtWidgets.QHBoxLayout(self)
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(10)
# Icon
self.icon_label = QtWidgets.QLabel()
if isinstance(icon, QtGui.QIcon):
self.icon_label.setPixmap(icon.pixmap(32, 32))
else:
# For MessageIcon
if icon == QtWidgets.QSystemTrayIcon.MessageIcon.Information:
standard_icon = QtWidgets.QStyle.StandardPixmap.SP_MessageBoxInformation
elif icon == QtWidgets.QSystemTrayIcon.MessageIcon.Warning:
standard_icon = QtWidgets.QStyle.StandardPixmap.SP_MessageBoxWarning
elif icon == QtWidgets.QSystemTrayIcon.MessageIcon.Critical:
standard_icon = QtWidgets.QStyle.StandardPixmap.SP_MessageBoxCritical
else:
standard_icon = QtWidgets.QStyle.StandardPixmap.SP_MessageBoxInformation
system_icon = self.style().standardIcon(standard_icon)
self.icon_label.setPixmap(system_icon.pixmap(32, 32))
layout.addWidget(self.icon_label)
# Text
text_layout = QtWidgets.QVBoxLayout()
self.title_label = QtWidgets.QLabel(title)
self.title_label.setStyleSheet("font-weight: bold;")
text_layout.addWidget(self.title_label)
self.message_label = QtWidgets.QLabel(message)
self.message_label.setWordWrap(True)
text_layout.addWidget(self.message_label)
layout.addLayout(text_layout, 1)
# Close button
self.close_button = QtWidgets.QPushButton("×")
self.close_button.setFixedSize(20, 20)
self.close_button.clicked.connect(self._on_close)
layout.addWidget(self.close_button)
# Style
self.setAutoFillBackground(True)
self.setBackgroundRole(QtGui.QPalette.ColorRole.Window)
palette = self.palette()
palette.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor("white"))
palette.setColor(QtGui.QPalette.ColorRole.WindowText, QtGui.QColor("red"))
self.setPalette(palette)
self.setStyleSheet("""
PersistentNotification {
border-radius: 10px;
border: 1px solid rgba(100, 100, 100, 200);
}
QPushButton {
background-color: transparent;
color: white;
border: none;
font-size: 16px;
}
QPushButton:hover {
background-color: rgba(100, 100, 100, 100);
}
""")
if self.flash:
self.flash_timer = QtCore.QTimer(self)
self.flash_timer.timeout.connect(self._toggle_flash)
# Size and position
self.adjustSize()
self.setFixedWidth(300)
self._position()
# Timer for fade out if not clicked, but since persistent, maybe not needed
# But to avoid staying forever if forgotten, perhaps auto-close after long time
# But user wants until clicked, so no timer.
def _position(self):
screen = QtWidgets.QApplication.primaryScreen().availableGeometry()
# Stack from bottom right
self.move(
screen.width() - self.width() - 10,
screen.height() - self.height() - 50 - self.y_offset,
)
def _toggle_flash(self):
self.flash_state = not self.flash_state
palette = self.palette()
if self.flash_state:
palette.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor("red"))
palette.setColor(QtGui.QPalette.ColorRole.WindowText, QtGui.QColor("white"))
else:
palette.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor("white"))
palette.setColor(QtGui.QPalette.ColorRole.WindowText, QtGui.QColor("red"))
self.setPalette(palette)
self.update()
self.repaint()
def mousePressEvent(self, event):
self._on_close()
def _on_close(self):
if self.flash:
self.flash_timer.stop()
self.close_all_requested.emit()
self.close()
def showEvent(self, event):
super().showEvent(event)
self.raise_()
if self.flash:
self.flash_timer.start(1000)