Compare commits

..

10 Commits

Author SHA1 Message Date
kdusek
2108568f50 Add persistent notifications for priority 10 messages
Some checks failed
build / build-pip (push) Failing after 1m13s
build / build-win64 (push) Has been cancelled
build / build-macos (push) Has been cancelled
- 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
seird
4e4fd9cdc9 Merge pull request #45 from hydrargyrum/develop
mainwindow: add standard shortcut for "refresh" button
2025-04-05 19:05:26 +02:00
Hg
a8a03321f2 mainwindow: add standard shortcut for "refresh" button 2025-04-05 09:07:48 +02:00
dries.k
490044d9a7 update widgets_settings design 2025-03-27 18:32:58 +01:00
seird
a3ae246580 Merge pull request #44 from hydrargyrum/patch-1
widget_settings.ui: allow minimum priority of 0
2025-03-27 18:27:10 +01:00
hydrargyrum
0730c160f6 widget_settings.ui: allow minimum priority of 0
it's a valid priority for gotify

closes #43
2025-03-27 10:45:21 +01:00
dries.k
ed815fb459 fix typo in setup.py 2024-10-29 17:15:07 +01:00
dries.k
05da3a8295 v0.5.3 2024-10-29 17:10:20 +01:00
dries.k
f40f154f30 remove linux builds 2024-10-29 16:58:20 +01:00
dries.k
4e34c5e614 update github actions 2024-10-29 16:52:18 +01:00
18 changed files with 717 additions and 226 deletions

View File

@@ -12,11 +12,11 @@ jobs:
build-win64: build-win64:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.10.8' python-version: '3.13'
- name: Upgrade pip and enable wheel support - name: Upgrade pip and enable wheel support
run: python -m pip install --upgrade pip setuptools wheel run: python -m pip install --upgrade pip setuptools wheel
- name: Install Requirements - name: Install Requirements
@@ -30,49 +30,19 @@ jobs:
mv inno-output\gotify-tray-installer.exe gotify-tray-installer-win.exe mv inno-output\gotify-tray-installer.exe gotify-tray-installer-win.exe
shell: cmd shell: cmd
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: gotify-tray-installer-win.exe name: gotify-tray-installer-win.exe
path: gotify-tray-installer-win.exe path: gotify-tray-installer-win.exe
build-ubuntu:
strategy:
matrix:
tag: [jammy]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10.8'
- name: Upgrade pip and enable wheel support
run: python -m pip install --upgrade pip setuptools wheel
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.0'
- name: Build
run: |
pip install -r requirements.txt
pip install pyinstaller
gem install fpm
chmod +x build-linux.sh
./build-linux.sh deb
mv "dist/gotify-tray_$(cat version.txt)_amd64.deb" "gotify-tray_$(cat version.txt)_amd64_${{ matrix.tag }}.deb"
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
path: "gotify-tray_*_amd64_${{ matrix.tag }}.deb"
build-macos: build-macos:
runs-on: macos-12 runs-on: macos-12
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.10.8' python-version: '3.13'
- name: Upgrade pip and enable wheel support - name: Upgrade pip and enable wheel support
run: python -m pip install --upgrade pip setuptools wheel run: python -m pip install --upgrade pip setuptools wheel
- name: Build - name: Build
@@ -81,7 +51,7 @@ jobs:
brew install create-dmg brew install create-dmg
create-dmg --volname "Gotify Tray" --app-drop-link 0 0 --no-internet-enable "gotify-tray.dmg" "./dist/Gotify Tray.app" create-dmg --volname "Gotify Tray" --app-drop-link 0 0 --no-internet-enable "gotify-tray.dmg" "./dist/Gotify Tray.app"
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: gotify-tray.dmg name: gotify-tray.dmg
path: gotify-tray.dmg path: gotify-tray.dmg
@@ -89,11 +59,11 @@ jobs:
build-pip: build-pip:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.10.8' python-version: '3.13'
- name: Upgrade pip and enable wheel support - name: Upgrade pip and enable wheel support
run: python -m pip install --upgrade pip setuptools wheel run: python -m pip install --upgrade pip setuptools wheel
- name: install requirements - name: install requirements
@@ -103,6 +73,6 @@ jobs:
- name: create pip package - name: create pip package
run: python -m build run: python -m build
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
path: dist/gotify_tray-*.whl path: dist/gotify_tray-*.whl

View File

@@ -10,11 +10,11 @@ jobs:
build-win64: build-win64:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.10.8' python-version: '3.13'
- name: Upgrade pip and enable wheel support - name: Upgrade pip and enable wheel support
run: python -m pip install --upgrade pip setuptools wheel run: python -m pip install --upgrade pip setuptools wheel
- name: Install Requirements - name: Install Requirements
@@ -28,49 +28,19 @@ jobs:
mv inno-output\gotify-tray-installer.exe gotify-tray-installer-win.exe mv inno-output\gotify-tray-installer.exe gotify-tray-installer-win.exe
shell: cmd shell: cmd
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: gotify-tray-installer-win.exe name: gotify-tray-installer-win.exe
path: gotify-tray-installer-win.exe path: gotify-tray-installer-win.exe
build-debian:
strategy:
matrix:
tag: [bullseye, bookworm]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10.8'
- name: Upgrade pip and enable wheel support
run: python -m pip install --upgrade pip setuptools wheel
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.0'
- name: Build
run: |
pip install -r requirements.txt
pip install pyinstaller
gem install fpm
chmod +x build-linux.sh
./build-linux.sh deb
mv "dist/gotify-tray_$(cat version.txt)_amd64.deb" "gotify-tray_$(cat version.txt)_amd64_${{ matrix.tag }}.deb"
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: gotify-tray_${{github.ref_name}}_amd64_${{ matrix.tag }}.deb
path: gotify-tray_${{github.ref_name}}_amd64_${{ matrix.tag }}.deb
build-macos: build-macos:
runs-on: macos-12 runs-on: macos-12
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.10.8' python-version: '3.13'
- name: Upgrade pip and enable wheel support - name: Upgrade pip and enable wheel support
run: python -m pip install --upgrade pip setuptools wheel run: python -m pip install --upgrade pip setuptools wheel
- name: Build - name: Build
@@ -79,7 +49,7 @@ jobs:
brew install create-dmg brew install create-dmg
create-dmg --volname "Gotify Tray" --app-drop-link 0 0 --no-internet-enable "gotify-tray.dmg" "./dist/Gotify Tray.app" create-dmg --volname "Gotify Tray" --app-drop-link 0 0 --no-internet-enable "gotify-tray.dmg" "./dist/Gotify Tray.app"
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: gotify-tray.dmg name: gotify-tray.dmg
path: gotify-tray.dmg path: gotify-tray.dmg
@@ -92,11 +62,11 @@ jobs:
permissions: permissions:
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.10.8' python-version: '3.13'
- name: Upgrade pip and enable wheel support - name: Upgrade pip and enable wheel support
run: python -m pip install --upgrade pip setuptools wheel run: python -m pip install --upgrade pip setuptools wheel
- name: install requirements - name: install requirements
@@ -108,17 +78,17 @@ jobs:
- name: upload to pypi - name: upload to pypi
uses: pypa/gh-action-pypi-publish@release/v1 uses: pypa/gh-action-pypi-publish@release/v1
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: gotify_tray-${{github.ref_name}}-py3-none-any.whl name: gotify_tray-${{github.ref_name}}-py3-none-any.whl
path: dist/gotify_tray-${{github.ref_name}}-py3-none-any.whl path: dist/gotify_tray-${{github.ref_name}}-py3-none-any.whl
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build-win64, build-debian, build-macos, pypi] needs: [build-win64, build-macos, pypi]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
- name: Release - name: Release
uses: marvinpinto/action-automatic-releases@latest uses: marvinpinto/action-automatic-releases@latest
with: with:
@@ -127,6 +97,4 @@ jobs:
files: | files: |
gotify-tray-installer-win.exe gotify-tray-installer-win.exe
gotify-tray.dmg gotify-tray.dmg
gotify-tray_${{github.ref_name}}_amd64_bullseye.deb
gotify-tray_${{github.ref_name}}_amd64_bookworm.deb
gotify_tray-${{github.ref_name}}-py3-none-any.whl gotify_tray-${{github.ref_name}}-py3-none-any.whl

19
AGENTS.md Normal file
View File

@@ -0,0 +1,19 @@
# Agent Guidelines
## Type Checking
Run `pyright .` in the project root to perform static type checking with Pyright. Address any critical errors before committing changes.
Note: Pyright may report many Qt-related type issues that are false positives due to PyQt6 stubs limitations. Focus on logical errors rather than Qt API mismatches.
To ignore PyQt6-related errors, create a `pyrightconfig.json` in the project root with:
```json
{
"reportOptionalMemberAccess": false,
"reportAttributeAccessIssue": false,
"reportIncompatibleMethodOverride": false,
"reportArgumentType": false,
"reportAssignmentType": false,
"reportReturnType": false
}
```
This suppresses common PyQt6 false positives while still catching real issues.

0
build-linux.sh Normal file → Executable file
View File

View File

@@ -3,4 +3,4 @@ __description__ = (
"A tray notification application for receiving messages from a Gotify server." "A tray notification application for receiving messages from a Gotify server."
) )
__url__ = "https://github.com/seird/gotify-tray" __url__ = "https://github.com/seird/gotify-tray"
__version__ = "0.5.2" __version__ = "0.5.3"

View File

@@ -16,6 +16,8 @@ DEFAULT_SETTINGS = {
"tray/notifications/duration_ms": 5000, "tray/notifications/duration_ms": 5000,
"tray/notifications/icon/show": True, "tray/notifications/icon/show": True,
"tray/notifications/click": True, "tray/notifications/click": True,
"tray/notifications/priority10_persistent": True,
"tray/notifications/sound_only_priority10": True,
"tray/icon/unread": False, "tray/icon/unread": False,
"watchdog/enabled": True, "watchdog/enabled": True,
"watchdog/interval/s": 60, "watchdog/interval/s": 60,

View File

@@ -8,6 +8,7 @@ import tempfile
from gotify_tray import gotify from gotify_tray import gotify
from gotify_tray.__version__ import __title__ from gotify_tray.__version__ import __title__
from gotify_tray.database import Downloader, Settings from gotify_tray.database import Downloader, Settings
from .widgets.PersistentNotification import PersistentNotification
from gotify_tray.tasks import ( from gotify_tray.tasks import (
ClearCacheTask, ClearCacheTask,
DeleteApplicationMessagesTask, DeleteApplicationMessagesTask,
@@ -22,6 +23,7 @@ from gotify_tray.tasks import (
from gotify_tray.gui.themes import set_theme from gotify_tray.gui.themes import set_theme
from gotify_tray.utils import get_icon, verify_server from gotify_tray.utils import get_icon, verify_server
from PyQt6 import QtCore, QtGui, QtWidgets from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtMultimedia import QSoundEffect
from ..__version__ import __title__ from ..__version__ import __title__
from .models import ( from .models import (
@@ -47,7 +49,9 @@ def init_logger(logger: logging.Logger):
else: else:
logging.disable() logging.disable()
logdir = QtCore.QStandardPaths.standardLocations(QtCore.QStandardPaths.StandardLocation.AppDataLocation)[0] logdir = QtCore.QStandardPaths.standardLocations(
QtCore.QStandardPaths.StandardLocation.AppDataLocation
)[0]
if not os.path.exists(logdir): if not os.path.exists(logdir):
os.mkdir(logdir) os.mkdir(logdir)
logging.basicConfig( logging.basicConfig(
@@ -57,6 +61,28 @@ def init_logger(logger: logging.Logger):
class MainApplication(QtWidgets.QApplication): class MainApplication(QtWidgets.QApplication):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Initialize notification sound effect
self.notification_sound = QSoundEffect()
sound_path = os.path.join(
os.path.dirname(__file__), "images", "notification.wav"
)
self.notification_sound.setSource(QtCore.QUrl.fromLocalFile(sound_path))
self.notification_sound.setVolume(0.5) # Set volume (0.0 to 1.0)
self.persistent_notifications = []
self.next_y_offset = 0
def close_all_persistent_notifications(self):
for notification in self.persistent_notifications:
notification.close()
self.persistent_notifications.clear()
self.next_y_offset = 0
def _on_tray_activated(self, reason):
if reason == QtWidgets.QSystemTrayIcon.ActivationReason.Trigger:
self.close_all_persistent_notifications()
def init_ui(self): def init_ui(self):
self.gotify_client = gotify.GotifyClient( self.gotify_client = gotify.GotifyClient(
settings.value("Server/url", type=str), settings.value("Server/url", type=str),
@@ -69,7 +95,9 @@ class MainApplication(QtWidgets.QApplication):
self.application_model = ApplicationModel() self.application_model = ApplicationModel()
self.application_proxy_model = ApplicationProxyModel(self.application_model) self.application_proxy_model = ApplicationProxyModel(self.application_model)
self.main_window = MainWindow(self.application_model, self.application_proxy_model, self.messages_model) self.main_window = MainWindow(
self.application_model, self.application_proxy_model, self.messages_model
)
self.main_window.show() # The initial .show() is necessary to get the correct sizes when adding MessageWigets self.main_window.show() # The initial .show() is necessary to get the correct sizes when adding MessageWigets
QtCore.QTimer.singleShot(0, self.main_window.hide) QtCore.QTimer.singleShot(0, self.main_window.hide)
@@ -77,6 +105,7 @@ class MainApplication(QtWidgets.QApplication):
self.tray = Tray() self.tray = Tray()
self.tray.show() self.tray.show()
self.tray.activated.connect(self._on_tray_activated)
self.first_connect = True self.first_connect = True
@@ -100,17 +129,30 @@ class MainApplication(QtWidgets.QApplication):
self.application_model.setItem(0, 0, ApplicationAllMessagesItem()) self.application_model.setItem(0, 0, ApplicationAllMessagesItem())
self.get_applications_task = GetApplicationsTask(self.gotify_client) self.get_applications_task = GetApplicationsTask(self.gotify_client)
self.get_applications_task.success.connect(self.get_applications_success_callback) self.get_applications_task.success.connect(
self.get_applications_task.started.connect(self.main_window.disable_applications) self.get_applications_success_callback
self.get_applications_task.finished.connect(self.main_window.enable_applications) )
self.get_applications_task.started.connect(
self.main_window.disable_applications
)
self.get_applications_task.finished.connect(
self.main_window.enable_applications
)
self.get_applications_task.start() self.get_applications_task.start()
def get_applications_success_callback( def get_applications_success_callback(
self, applications: list[gotify.GotifyApplicationModel], self,
applications: list[gotify.GotifyApplicationModel],
): ):
for i, application in enumerate(applications): for i, application in enumerate(applications):
icon = QtGui.QIcon(self.downloader.get_filename(f"{self.gotify_client.url}/{application.image}")) icon = QtGui.QIcon(
self.application_model.setItem(i + 1, 0, ApplicationModelItem(application, icon)) self.downloader.get_filename(
f"{self.gotify_client.url}/{application.image}"
)
)
self.application_model.setItem(
i + 1, 0, ApplicationModelItem(application, icon)
)
def update_last_id(self, i: int): def update_last_id(self, i: int):
if i > settings.value("message/last", type=int): if i > settings.value("message/last", type=int):
@@ -170,19 +212,28 @@ class MainApplication(QtWidgets.QApplication):
task.message.disconnect() task.message.disconnect()
except TypeError: except TypeError:
pass pass
for task in aborted_tasks: for task in aborted_tasks:
task.wait() task.wait()
def application_selection_changed_callback(self, item: ApplicationModelItem | ApplicationAllMessagesItem): def application_selection_changed_callback(
self, item: ApplicationModelItem | ApplicationAllMessagesItem
):
self.main_window.disable_buttons() self.main_window.disable_buttons()
self.abort_get_messages_task() self.abort_get_messages_task()
self.messages_model.clear() self.messages_model.clear()
if isinstance(item, ApplicationModelItem): if isinstance(item, ApplicationModelItem):
self.get_application_messages_task = GetApplicationMessagesTask(item.data(ApplicationItemDataRole.ApplicationRole).id, self.gotify_client) self.get_application_messages_task = GetApplicationMessagesTask(
self.get_application_messages_task.message.connect(self.messages_model.append_message) item.data(ApplicationItemDataRole.ApplicationRole).id,
self.get_application_messages_task.finished.connect(self.main_window.enable_buttons) self.gotify_client,
)
self.get_application_messages_task.message.connect(
self.messages_model.append_message
)
self.get_application_messages_task.finished.connect(
self.main_window.enable_buttons
)
self.get_application_messages_task.start() self.get_application_messages_task.start()
elif isinstance(item, ApplicationAllMessagesItem): elif isinstance(item, ApplicationAllMessagesItem):
@@ -191,21 +242,29 @@ class MainApplication(QtWidgets.QApplication):
self.get_messages_task.finished.connect(self.main_window.enable_buttons) self.get_messages_task.finished.connect(self.main_window.enable_buttons)
self.get_messages_task.start() self.get_messages_task.start()
def add_message_to_model(self, message: gotify.GotifyMessageModel, process: bool = True): def add_message_to_model(
self, message: gotify.GotifyMessageModel, process: bool = True
):
if self.application_model.itemFromId(message.appid): if self.application_model.itemFromId(message.appid):
application_index = self.main_window.currentApplicationIndex() application_index = self.main_window.currentApplicationIndex()
if selected_application_item := self.application_model.itemFromIndex(self.application_proxy_model.mapToSource(application_index)): if selected_application_item := self.application_model.itemFromIndex(
self.application_proxy_model.mapToSource(application_index)
):
def insert_message_helper(): def insert_message_helper():
if isinstance(selected_application_item, ApplicationModelItem): if isinstance(selected_application_item, ApplicationModelItem):
# A single application is selected # A single application is selected
# -> Only insert the message if the appid matches the selected appid # -> Only insert the message if the appid matches the selected appid
if ( if (
message.appid message.appid
== selected_application_item.data(ApplicationItemDataRole.ApplicationRole).id == selected_application_item.data(
ApplicationItemDataRole.ApplicationRole
).id
): ):
self.messages_model.insert_message(0, message) self.messages_model.insert_message(0, message)
elif isinstance(selected_application_item, ApplicationAllMessagesItem): elif isinstance(
selected_application_item, ApplicationAllMessagesItem
):
# "All messages' is selected # "All messages' is selected
self.messages_model.insert_message(0, message) self.messages_model.insert_message(0, message)
@@ -216,10 +275,14 @@ class MainApplication(QtWidgets.QApplication):
else: else:
insert_message_helper() insert_message_helper()
else: else:
logger.error(f"App id {message.appid} could not be found. Refreshing applications.") logger.error(
f"App id {message.appid} could not be found. Refreshing applications."
)
self.refresh_applications() self.refresh_applications()
def new_message_callback(self, message: gotify.GotifyMessageModel, process: bool = True): def new_message_callback(
self, message: gotify.GotifyMessageModel, process: bool = True
):
self.add_message_to_model(message, process=process) self.add_message_to_model(message, process=process)
# Don't show a notification if it's low priority or the window is active # Don't show a notification if it's low priority or the window is active
@@ -237,20 +300,51 @@ class MainApplication(QtWidgets.QApplication):
self.tray.set_icon_unread() self.tray.set_icon_unread()
# Get the application icon # Get the application icon
if ( if settings.value("tray/notifications/icon/show", type=bool) and (
settings.value("tray/notifications/icon/show", type=bool) application_item := self.application_model.itemFromId(message.appid)
and (application_item := self.application_model.itemFromId(message.appid))
): ):
icon = application_item.icon() icon = application_item.icon()
else: else:
icon = QtWidgets.QSystemTrayIcon.MessageIcon.Information icon = QtWidgets.QSystemTrayIcon.MessageIcon.Information
self.tray.showMessage( # Show notification
message.title, if message.priority == 10 and settings.value(
message.message, "tray/notifications/priority10_persistent", type=bool
icon, ):
msecs=settings.value("tray/notifications/duration_ms", type=int), # Create persistent notification
) notification = PersistentNotification(
message.title or "",
message.message or "",
icon,
y_offset=self.next_y_offset,
flash=True,
)
notification.close_all_requested.connect(
self.close_all_persistent_notifications
)
self.persistent_notifications.append(notification)
notification.show()
self.next_y_offset += notification.height() + 10
else:
# Use system tray notification
msecs = settings.value("tray/notifications/duration_ms", type=int)
self.tray.showMessage(
message.title,
message.message,
icon,
msecs=msecs,
)
# Play notification sound
if (
not settings.value("tray/notifications/sound_only_priority10", type=bool)
or message.priority == 10
):
if self.notification_sound.isLoaded():
self.notification_sound.play()
else:
# Try to play anyway (QSoundEffect will queue if not loaded yet)
self.notification_sound.play()
def delete_message_callback(self, message_item: MessagesModelItem): def delete_message_callback(self, message_item: MessagesModelItem):
self.delete_message_task = DeleteMessageTask( self.delete_message_task = DeleteMessageTask(
@@ -269,9 +363,9 @@ class MainApplication(QtWidgets.QApplication):
) )
self.delete_application_messages_task.start() self.delete_application_messages_task.start()
elif isinstance(item, ApplicationAllMessagesItem): elif isinstance(item, ApplicationAllMessagesItem):
self.clear_cache_task = ClearCacheTask() self.clear_cache_task = ClearCacheTask()
self.clear_cache_task.start() self.clear_cache_task.start()
self.delete_all_messages_task = DeleteAllMessagesTask(self.gotify_client) self.delete_all_messages_task = DeleteAllMessagesTask(self.gotify_client)
self.delete_all_messages_task.start() self.delete_all_messages_task.start()
else: else:
@@ -299,7 +393,11 @@ class MainApplication(QtWidgets.QApplication):
# Update the message widget icons # Update the message widget icons
for r in range(self.messages_model.rowCount()): for r in range(self.messages_model.rowCount()):
message_widget: MessageWidget = self.main_window.listView_messages.indexWidget(self.messages_model.index(r, 0)) message_widget: MessageWidget = (
self.main_window.listView_messages.indexWidget(
self.messages_model.index(r, 0)
)
)
message_widget.set_icons() message_widget.set_icons()
def settings_callback(self): def settings_callback(self):
@@ -341,15 +439,21 @@ class MainApplication(QtWidgets.QApplication):
self.main_window.refresh.connect(self.refresh_applications) self.main_window.refresh.connect(self.refresh_applications)
self.main_window.delete_all.connect(self.delete_all_messages_callback) self.main_window.delete_all.connect(self.delete_all_messages_callback)
self.main_window.application_selection_changed.connect(self.application_selection_changed_callback) self.main_window.application_selection_changed.connect(
self.application_selection_changed_callback
)
self.main_window.delete_message.connect(self.delete_message_callback) self.main_window.delete_message.connect(self.delete_message_callback)
self.main_window.image_popup.connect(self.image_popup_callback) self.main_window.image_popup.connect(self.image_popup_callback)
self.main_window.hidden.connect(self.main_window_hidden_callback) self.main_window.hidden.connect(self.main_window_hidden_callback)
self.main_window.activated.connect(self.tray.revert_icon) self.main_window.activated.connect(self.tray.revert_icon)
self.styleHints().colorSchemeChanged.connect(self.theme_change_requested_callback)
self.messages_model.rowsInserted.connect(self.main_window.display_message_widgets) self.styleHints().colorSchemeChanged.connect(
self.theme_change_requested_callback
)
self.messages_model.rowsInserted.connect(
self.main_window.display_message_widgets
)
self.gotify_client.opened.connect(self.listener_opened_callback) self.gotify_client.opened.connect(self.listener_opened_callback)
self.gotify_client.closed.connect(self.listener_closed_callback) self.gotify_client.closed.connect(self.listener_closed_callback)
@@ -366,7 +470,9 @@ class MainApplication(QtWidgets.QApplication):
def acquire_lock(self) -> bool: def acquire_lock(self) -> bool:
temp_dir = tempfile.gettempdir() temp_dir = tempfile.gettempdir()
lock_filename = os.path.join(temp_dir, __title__ + "-" + getpass.getuser() + ".lock") lock_filename = os.path.join(
temp_dir, __title__ + "-" + getpass.getuser() + ".lock"
)
self.lock_file = QtCore.QLockFile(lock_filename) self.lock_file = QtCore.QLockFile(lock_filename)
self.lock_file.setStaleLockTime(0) self.lock_file.setStaleLockTime(0)
return self.lock_file.tryLock() return self.lock_file.tryLock()

View File

@@ -17,7 +17,11 @@ class Ui_Dialog(object):
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Apply|QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Apply
| QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Ok
)
self.buttonBox.setObjectName("buttonBox") self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 1, 4, 1, 1) self.gridLayout.addWidget(self.buttonBox, 1, 4, 1, 1)
self.tabWidget = QtWidgets.QTabWidget(parent=Dialog) self.tabWidget = QtWidgets.QTabWidget(parent=Dialog)
@@ -30,7 +34,9 @@ class Ui_Dialog(object):
self.groupBox_notifications.setObjectName("groupBox_notifications") self.groupBox_notifications.setObjectName("groupBox_notifications")
self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_notifications) self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_notifications)
self.gridLayout_4.setObjectName("gridLayout_4") self.gridLayout_4.setObjectName("gridLayout_4")
self.label_notification_duration = QtWidgets.QLabel(parent=self.groupBox_notifications) self.label_notification_duration = QtWidgets.QLabel(
parent=self.groupBox_notifications
)
self.label_notification_duration.setObjectName("label_notification_duration") self.label_notification_duration.setObjectName("label_notification_duration")
self.gridLayout_4.addWidget(self.label_notification_duration, 1, 0, 1, 1) self.gridLayout_4.addWidget(self.label_notification_duration, 1, 0, 1, 1)
self.spin_duration = QtWidgets.QSpinBox(parent=self.groupBox_notifications) self.spin_duration = QtWidgets.QSpinBox(parent=self.groupBox_notifications)
@@ -39,29 +45,54 @@ class Ui_Dialog(object):
self.spin_duration.setSingleStep(100) self.spin_duration.setSingleStep(100)
self.spin_duration.setObjectName("spin_duration") self.spin_duration.setObjectName("spin_duration")
self.gridLayout_4.addWidget(self.spin_duration, 1, 1, 1, 1) self.gridLayout_4.addWidget(self.spin_duration, 1, 1, 1, 1)
self.cb_notification_click = QtWidgets.QCheckBox(parent=self.groupBox_notifications) self.cb_notification_click = QtWidgets.QCheckBox(
parent=self.groupBox_notifications
)
self.cb_notification_click.setObjectName("cb_notification_click") self.cb_notification_click.setObjectName("cb_notification_click")
self.gridLayout_4.addWidget(self.cb_notification_click, 3, 0, 1, 3) self.gridLayout_4.addWidget(self.cb_notification_click, 3, 0, 1, 3)
self.label_notification_duration_ms = QtWidgets.QLabel(parent=self.groupBox_notifications) self.label_notification_duration_ms = QtWidgets.QLabel(
self.label_notification_duration_ms.setObjectName("label_notification_duration_ms") parent=self.groupBox_notifications
)
self.label_notification_duration_ms.setObjectName(
"label_notification_duration_ms"
)
self.gridLayout_4.addWidget(self.label_notification_duration_ms, 1, 2, 1, 1) self.gridLayout_4.addWidget(self.label_notification_duration_ms, 1, 2, 1, 1)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.gridLayout_4.addItem(spacerItem, 0, 2, 1, 1) self.gridLayout_4.addItem(spacerItem, 0, 2, 1, 1)
self.cb_notify = QtWidgets.QCheckBox(parent=self.groupBox_notifications) self.cb_notify = QtWidgets.QCheckBox(parent=self.groupBox_notifications)
self.cb_notify.setObjectName("cb_notify") self.cb_notify.setObjectName("cb_notify")
self.gridLayout_4.addWidget(self.cb_notify, 2, 0, 1, 3) self.gridLayout_4.addWidget(self.cb_notify, 2, 0, 1, 3)
self.spin_priority = QtWidgets.QSpinBox(parent=self.groupBox_notifications) self.spin_priority = QtWidgets.QSpinBox(parent=self.groupBox_notifications)
self.spin_priority.setMinimum(1) self.spin_priority.setMinimum(0)
self.spin_priority.setMaximum(10) self.spin_priority.setMaximum(10)
self.spin_priority.setProperty("value", 5) self.spin_priority.setProperty("value", 5)
self.spin_priority.setObjectName("spin_priority") self.spin_priority.setObjectName("spin_priority")
self.gridLayout_4.addWidget(self.spin_priority, 0, 1, 1, 1) self.gridLayout_4.addWidget(self.spin_priority, 0, 1, 1, 1)
self.label_notification_priority = QtWidgets.QLabel(parent=self.groupBox_notifications) self.label_notification_priority = QtWidgets.QLabel(
parent=self.groupBox_notifications
)
self.label_notification_priority.setObjectName("label_notification_priority") self.label_notification_priority.setObjectName("label_notification_priority")
self.gridLayout_4.addWidget(self.label_notification_priority, 0, 0, 1, 1) self.gridLayout_4.addWidget(self.label_notification_priority, 0, 0, 1, 1)
self.cb_tray_icon_unread = QtWidgets.QCheckBox(parent=self.groupBox_notifications) self.cb_tray_icon_unread = QtWidgets.QCheckBox(
parent=self.groupBox_notifications
)
self.cb_tray_icon_unread.setObjectName("cb_tray_icon_unread") self.cb_tray_icon_unread.setObjectName("cb_tray_icon_unread")
self.gridLayout_4.addWidget(self.cb_tray_icon_unread, 4, 0, 1, 3) self.gridLayout_4.addWidget(self.cb_tray_icon_unread, 4, 0, 1, 3)
self.cb_priority10_persistent = QtWidgets.QCheckBox(
parent=self.groupBox_notifications
)
self.cb_priority10_persistent.setObjectName("cb_priority10_persistent")
self.gridLayout_4.addWidget(self.cb_priority10_persistent, 5, 0, 1, 3)
self.cb_sound_only_priority10 = QtWidgets.QCheckBox(
parent=self.groupBox_notifications
)
self.cb_sound_only_priority10.setObjectName("cb_sound_only_priority10")
self.gridLayout_4.addWidget(self.cb_sound_only_priority10, 6, 0, 1, 3)
self.verticalLayout_4.addWidget(self.groupBox_notifications) self.verticalLayout_4.addWidget(self.groupBox_notifications)
self.groupBox_2 = QtWidgets.QGroupBox(parent=self.tab_general) self.groupBox_2 = QtWidgets.QGroupBox(parent=self.tab_general)
self.groupBox_2.setObjectName("groupBox_2") self.groupBox_2.setObjectName("groupBox_2")
@@ -84,13 +115,25 @@ class Ui_Dialog(object):
self.groupBox_server_info.setObjectName("groupBox_server_info") self.groupBox_server_info.setObjectName("groupBox_server_info")
self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox_server_info) self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox_server_info)
self.gridLayout_3.setObjectName("gridLayout_3") self.gridLayout_3.setObjectName("gridLayout_3")
self.pb_change_server_info = QtWidgets.QPushButton(parent=self.groupBox_server_info) self.pb_change_server_info = QtWidgets.QPushButton(
parent=self.groupBox_server_info
)
self.pb_change_server_info.setObjectName("pb_change_server_info") self.pb_change_server_info.setObjectName("pb_change_server_info")
self.gridLayout_3.addWidget(self.pb_change_server_info, 0, 0, 1, 1) self.gridLayout_3.addWidget(self.pb_change_server_info, 0, 0, 1, 1)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem1 = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.gridLayout_3.addItem(spacerItem1, 0, 1, 1, 1) self.gridLayout_3.addItem(spacerItem1, 0, 1, 1, 1)
self.verticalLayout_4.addWidget(self.groupBox_server_info) self.verticalLayout_4.addWidget(self.groupBox_server_info)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) spacerItem2 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout_4.addItem(spacerItem2) self.verticalLayout_4.addItem(spacerItem2)
self.tabWidget.addTab(self.tab_general, "") self.tabWidget.addTab(self.tab_general, "")
self.tab_fonts = QtWidgets.QWidget() self.tab_fonts = QtWidgets.QWidget()
@@ -111,18 +154,29 @@ class Ui_Dialog(object):
self.layout_fonts_message.setObjectName("layout_fonts_message") self.layout_fonts_message.setObjectName("layout_fonts_message")
self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout") self.horizontalLayout.setObjectName("horizontalLayout")
self.pb_font_message_title = QtWidgets.QPushButton(parent=self.groupBox_fonts_message) self.pb_font_message_title = QtWidgets.QPushButton(
parent=self.groupBox_fonts_message
)
self.pb_font_message_title.setObjectName("pb_font_message_title") self.pb_font_message_title.setObjectName("pb_font_message_title")
self.horizontalLayout.addWidget(self.pb_font_message_title) self.horizontalLayout.addWidget(self.pb_font_message_title)
self.pb_font_message_date = QtWidgets.QPushButton(parent=self.groupBox_fonts_message) self.pb_font_message_date = QtWidgets.QPushButton(
parent=self.groupBox_fonts_message
)
self.pb_font_message_date.setObjectName("pb_font_message_date") self.pb_font_message_date.setObjectName("pb_font_message_date")
self.horizontalLayout.addWidget(self.pb_font_message_date) self.horizontalLayout.addWidget(self.pb_font_message_date)
self.pb_font_message_content = QtWidgets.QPushButton(parent=self.groupBox_fonts_message) self.pb_font_message_content = QtWidgets.QPushButton(
parent=self.groupBox_fonts_message
)
self.pb_font_message_content.setObjectName("pb_font_message_content") self.pb_font_message_content.setObjectName("pb_font_message_content")
self.horizontalLayout.addWidget(self.pb_font_message_content) self.horizontalLayout.addWidget(self.pb_font_message_content)
self.layout_fonts_message.addLayout(self.horizontalLayout) self.layout_fonts_message.addLayout(self.horizontalLayout)
self.verticalLayout_5.addWidget(self.groupBox_fonts_message) self.verticalLayout_5.addWidget(self.groupBox_fonts_message)
spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) spacerItem3 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout_5.addItem(spacerItem3) self.verticalLayout_5.addItem(spacerItem3)
self.tabWidget.addTab(self.tab_fonts, "") self.tabWidget.addTab(self.tab_fonts, "")
self.tab_advanced = QtWidgets.QWidget() self.tab_advanced = QtWidgets.QWidget()
@@ -168,7 +222,12 @@ class Ui_Dialog(object):
self.spin_popup_h.setMaximum(10000) self.spin_popup_h.setMaximum(10000)
self.spin_popup_h.setObjectName("spin_popup_h") self.spin_popup_h.setObjectName("spin_popup_h")
self.horizontalLayout_4.addWidget(self.spin_popup_h) self.horizontalLayout_4.addWidget(self.spin_popup_h)
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem4 = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_4.addItem(spacerItem4) self.horizontalLayout_4.addItem(spacerItem4)
self.gridLayout_2.addLayout(self.horizontalLayout_4, 0, 0, 1, 1) self.gridLayout_2.addLayout(self.horizontalLayout_4, 0, 0, 1, 1)
self.verticalLayout.addWidget(self.groupbox_image_popup) self.verticalLayout.addWidget(self.groupbox_image_popup)
@@ -190,7 +249,12 @@ class Ui_Dialog(object):
self.label_watchdog_interval_s = QtWidgets.QLabel(parent=self.groupbox_watchdog) self.label_watchdog_interval_s = QtWidgets.QLabel(parent=self.groupbox_watchdog)
self.label_watchdog_interval_s.setObjectName("label_watchdog_interval_s") self.label_watchdog_interval_s.setObjectName("label_watchdog_interval_s")
self.horizontalLayout_3.addWidget(self.label_watchdog_interval_s) self.horizontalLayout_3.addWidget(self.label_watchdog_interval_s)
spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem5 = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_3.addItem(spacerItem5) self.horizontalLayout_3.addItem(spacerItem5)
self.verticalLayout_3.addLayout(self.horizontalLayout_3) self.verticalLayout_3.addLayout(self.horizontalLayout_3)
self.verticalLayout.addWidget(self.groupbox_watchdog) self.verticalLayout.addWidget(self.groupbox_watchdog)
@@ -207,7 +271,12 @@ class Ui_Dialog(object):
self.label_cache = QtWidgets.QLabel(parent=self.groupBox_cache) self.label_cache = QtWidgets.QLabel(parent=self.groupBox_cache)
self.label_cache.setObjectName("label_cache") self.label_cache.setObjectName("label_cache")
self.horizontalLayout_6.addWidget(self.label_cache) self.horizontalLayout_6.addWidget(self.label_cache)
spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem6 = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_6.addItem(spacerItem6) self.horizontalLayout_6.addItem(spacerItem6)
self.verticalLayout.addWidget(self.groupBox_cache) self.verticalLayout.addWidget(self.groupBox_cache)
self.groupBox_logging = QtWidgets.QGroupBox(parent=self.tab_advanced) self.groupBox_logging = QtWidgets.QGroupBox(parent=self.tab_advanced)
@@ -217,7 +286,12 @@ class Ui_Dialog(object):
self.combo_logging = QtWidgets.QComboBox(parent=self.groupBox_logging) self.combo_logging = QtWidgets.QComboBox(parent=self.groupBox_logging)
self.combo_logging.setObjectName("combo_logging") self.combo_logging.setObjectName("combo_logging")
self.gridLayout_6.addWidget(self.combo_logging, 0, 1, 1, 1) self.gridLayout_6.addWidget(self.combo_logging, 0, 1, 1, 1)
spacerItem7 = QtWidgets.QSpacerItem(190, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem7 = QtWidgets.QSpacerItem(
190,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.gridLayout_6.addItem(spacerItem7, 0, 3, 1, 1) self.gridLayout_6.addItem(spacerItem7, 0, 3, 1, 1)
self.pb_open_log = QtWidgets.QPushButton(parent=self.groupBox_logging) self.pb_open_log = QtWidgets.QPushButton(parent=self.groupBox_logging)
self.pb_open_log.setMaximumSize(QtCore.QSize(30, 16777215)) self.pb_open_log.setMaximumSize(QtCore.QSize(30, 16777215))
@@ -227,7 +301,12 @@ class Ui_Dialog(object):
self.label_logging.setObjectName("label_logging") self.label_logging.setObjectName("label_logging")
self.gridLayout_6.addWidget(self.label_logging, 0, 0, 1, 1) self.gridLayout_6.addWidget(self.label_logging, 0, 0, 1, 1)
self.verticalLayout.addWidget(self.groupBox_logging) self.verticalLayout.addWidget(self.groupBox_logging)
spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) spacerItem8 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout.addItem(spacerItem8) self.verticalLayout.addItem(spacerItem8)
self.tabWidget.addTab(self.tab_advanced, "") self.tabWidget.addTab(self.tab_advanced, "")
self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 5) self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 5)
@@ -250,15 +329,17 @@ class Ui_Dialog(object):
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.tabWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0)
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog)
Dialog.setTabOrder(self.tabWidget, self.spin_priority) Dialog.setTabOrder(self.tabWidget, self.spin_priority)
Dialog.setTabOrder(self.spin_priority, self.spin_duration) Dialog.setTabOrder(self.spin_priority, self.spin_duration)
Dialog.setTabOrder(self.spin_duration, self.cb_notify) Dialog.setTabOrder(self.spin_duration, self.cb_notify)
Dialog.setTabOrder(self.cb_notify, self.cb_notification_click) Dialog.setTabOrder(self.cb_notify, self.cb_notification_click)
Dialog.setTabOrder(self.cb_notification_click, self.cb_tray_icon_unread) Dialog.setTabOrder(self.cb_notification_click, self.cb_tray_icon_unread)
Dialog.setTabOrder(self.cb_tray_icon_unread, self.cb_priority_colors) Dialog.setTabOrder(self.cb_tray_icon_unread, self.cb_priority10_persistent)
Dialog.setTabOrder(self.cb_priority10_persistent, self.cb_sound_only_priority10)
Dialog.setTabOrder(self.cb_sound_only_priority10, self.cb_priority_colors)
Dialog.setTabOrder(self.cb_priority_colors, self.cb_image_urls) Dialog.setTabOrder(self.cb_priority_colors, self.cb_image_urls)
Dialog.setTabOrder(self.cb_image_urls, self.cb_locale) Dialog.setTabOrder(self.cb_image_urls, self.cb_locale)
Dialog.setTabOrder(self.cb_locale, self.cb_sort_applications) Dialog.setTabOrder(self.cb_locale, self.cb_sort_applications)
@@ -284,40 +365,88 @@ class Ui_Dialog(object):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog")) Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.groupBox_notifications.setTitle(_translate("Dialog", "Notifications")) self.groupBox_notifications.setTitle(_translate("Dialog", "Notifications"))
self.label_notification_duration.setText(_translate("Dialog", "Notification duration:")) self.label_notification_duration.setText(
self.cb_notification_click.setText(_translate("Dialog", "Clicking the notification pop-up opens the main window")) _translate("Dialog", "Notification duration:")
)
self.cb_notification_click.setText(
_translate(
"Dialog", "Clicking the notification pop-up opens the main window"
)
)
self.label_notification_duration_ms.setText(_translate("Dialog", "ms")) self.label_notification_duration_ms.setText(_translate("Dialog", "ms"))
self.cb_notify.setText(_translate("Dialog", "Show a notification for missed messages after reconnecting")) self.cb_notify.setText(
self.label_notification_priority.setText(_translate("Dialog", "Minimum priority to show notifications:")) _translate(
self.cb_tray_icon_unread.setText(_translate("Dialog", "Change the tray icon color when there are unread notifications")) "Dialog", "Show a notification for missed messages after reconnecting"
)
)
self.label_notification_priority.setText(
_translate("Dialog", "Minimum priority to show notifications:")
)
self.cb_tray_icon_unread.setText(
_translate(
"Dialog",
"Change the tray icon color when there are unread notifications",
)
)
self.cb_priority10_persistent.setText(
_translate(
"Dialog",
"Make priority 10 notifications persistent (stay until clicked)",
)
)
self.cb_sound_only_priority10.setText(
_translate(
"Dialog",
"Play notification sound only for priority 10 messages",
)
)
self.groupBox_2.setTitle(_translate("Dialog", "Interface")) self.groupBox_2.setTitle(_translate("Dialog", "Interface"))
self.cb_priority_colors.setToolTip(_translate("Dialog", "4..7 -> medium\n" self.cb_priority_colors.setToolTip(
"8..10 -> high")) _translate("Dialog", "4..7 -> medium\n8..10 -> high")
self.cb_priority_colors.setText(_translate("Dialog", "Show message priority colors")) )
self.cb_priority_colors.setText(
_translate("Dialog", "Show message priority colors")
)
self.cb_image_urls.setText(_translate("Dialog", "Show image urls as images")) self.cb_image_urls.setText(_translate("Dialog", "Show image urls as images"))
self.cb_locale.setText(_translate("Dialog", "Display date in the system locale format")) self.cb_locale.setText(
self.cb_sort_applications.setText(_translate("Dialog", "Sort the application list alphabetically (requires restart)")) _translate("Dialog", "Display date in the system locale format")
self.groupBox_server_info.setTitle(_translate("Dialog", "Server info")) )
self.cb_sort_applications.setText(
_translate(
"Dialog", "Sort the application list alphabetically (requires restart)"
)
)
self.groupBox_server_info.setTitle(
_translate("Dialog", "Server info - kadu customized")
)
self.pb_change_server_info.setText(_translate("Dialog", "Change server info")) self.pb_change_server_info.setText(_translate("Dialog", "Change server info"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_general), _translate("Dialog", "General")) self.tabWidget.setTabText(
self.tabWidget.indexOf(self.tab_general), _translate("Dialog", "General")
)
self.pb_reset_fonts.setText(_translate("Dialog", "Reset all fonts")) self.pb_reset_fonts.setText(_translate("Dialog", "Reset all fonts"))
self.groupBox_fonts_message.setTitle(_translate("Dialog", "Message")) self.groupBox_fonts_message.setTitle(_translate("Dialog", "Message"))
self.pb_font_message_title.setText(_translate("Dialog", "Title")) self.pb_font_message_title.setText(_translate("Dialog", "Title"))
self.pb_font_message_date.setText(_translate("Dialog", "Date")) self.pb_font_message_date.setText(_translate("Dialog", "Date"))
self.pb_font_message_content.setText(_translate("Dialog", "Message")) self.pb_font_message_content.setText(_translate("Dialog", "Message"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_fonts), _translate("Dialog", "Fonts")) self.tabWidget.setTabText(
self.tabWidget.indexOf(self.tab_fonts), _translate("Dialog", "Fonts")
)
self.groupBox.setTitle(_translate("Dialog", "Settings")) self.groupBox.setTitle(_translate("Dialog", "Settings"))
self.pb_reset.setText(_translate("Dialog", "Reset")) self.pb_reset.setText(_translate("Dialog", "Reset"))
self.pb_import.setText(_translate("Dialog", "Import")) self.pb_import.setText(_translate("Dialog", "Import"))
self.pb_export.setText(_translate("Dialog", "Export")) self.pb_export.setText(_translate("Dialog", "Export"))
self.groupbox_image_popup.setTitle(_translate("Dialog", "Image pop-up for URLs")) self.groupbox_image_popup.setTitle(
_translate("Dialog", "Image pop-up for URLs")
)
self.label.setToolTip(_translate("Dialog", "Maximum pop-up width")) self.label.setToolTip(_translate("Dialog", "Maximum pop-up width"))
self.label.setText(_translate("Dialog", "Width")) self.label.setText(_translate("Dialog", "Width"))
self.spin_popup_w.setToolTip(_translate("Dialog", "Maximum pop-up width")) self.spin_popup_w.setToolTip(_translate("Dialog", "Maximum pop-up width"))
self.label_2.setToolTip(_translate("Dialog", "Maximum pop-up height")) self.label_2.setToolTip(_translate("Dialog", "Maximum pop-up height"))
self.label_2.setText(_translate("Dialog", "Height")) self.label_2.setText(_translate("Dialog", "Height"))
self.spin_popup_h.setToolTip(_translate("Dialog", "Maximum pop-up height")) self.spin_popup_h.setToolTip(_translate("Dialog", "Maximum pop-up height"))
self.groupbox_watchdog.setTitle(_translate("Dialog", "Server watchdog thread (requires restart)")) self.groupbox_watchdog.setTitle(
_translate("Dialog", "Server watchdog thread (requires restart)")
)
self.label_watchdog_interval.setText(_translate("Dialog", "Interval:")) self.label_watchdog_interval.setText(_translate("Dialog", "Interval:"))
self.label_watchdog_interval_s.setText(_translate("Dialog", "s")) self.label_watchdog_interval_s.setText(_translate("Dialog", "s"))
self.groupBox_cache.setTitle(_translate("Dialog", "Cache")) self.groupBox_cache.setTitle(_translate("Dialog", "Cache"))
@@ -328,11 +457,14 @@ class Ui_Dialog(object):
self.pb_open_log.setToolTip(_translate("Dialog", "Open logfile")) self.pb_open_log.setToolTip(_translate("Dialog", "Open logfile"))
self.pb_open_log.setText(_translate("Dialog", "...")) self.pb_open_log.setText(_translate("Dialog", "..."))
self.label_logging.setText(_translate("Dialog", "Level")) self.label_logging.setText(_translate("Dialog", "Level"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_advanced), _translate("Dialog", "Advanced")) self.tabWidget.setTabText(
self.tabWidget.indexOf(self.tab_advanced), _translate("Dialog", "Advanced")
)
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
app = QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog() Dialog = QtWidgets.QDialog()
ui = Ui_Dialog() ui = Ui_Dialog()

View File

@@ -97,7 +97,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QSpinBox" name="spin_priority"> <widget class="QSpinBox" name="spin_priority">
<property name="minimum"> <property name="minimum">
<number>1</number> <number>0</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>10</number> <number>10</number>

Binary file not shown.

View File

@@ -69,6 +69,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.link_callbacks() self.link_callbacks()
# set refresh shortcut (usually ctrl-r)
# unfortunately this cannot be done with designer
self.pb_refresh.setShortcut(QtGui.QKeySequence(QtGui.QKeySequence.StandardKey.Refresh))
def set_icons(self): def set_icons(self):
# Set button icons # Set button icons
self.pb_refresh.setIcon(QtGui.QIcon(get_theme_file("refresh.svg"))) self.pb_refresh.setIcon(QtGui.QIcon(get_theme_file("refresh.svg")))

View File

@@ -0,0 +1,145 @@
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)

View File

@@ -31,7 +31,7 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
super(SettingsDialog, self).__init__() super(SettingsDialog, self).__init__()
self.setupUi(self) self.setupUi(self)
self.setWindowTitle("Settings") self.setWindowTitle("Settings")
self.settings_changed = False self.settings_changed = False
self.changes_applied = False self.changes_applied = False
self.server_changed = False self.server_changed = False
@@ -41,29 +41,55 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
self.link_callbacks() self.link_callbacks()
def initUI(self): def initUI(self):
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Apply).setEnabled(False) self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Apply
).setEnabled(False)
# Notifications # Notifications
self.spin_priority.setValue(settings.value("tray/notifications/priority", type=int)) self.spin_priority.setValue(
settings.value("tray/notifications/priority", type=int)
)
self.spin_duration.setValue(settings.value("tray/notifications/duration_ms", type=int)) self.spin_duration.setValue(
settings.value("tray/notifications/duration_ms", type=int)
)
if platform.system() == "Windows": if platform.system() == "Windows":
# The notification duration setting is ignored by windows # The notification duration setting is ignored by windows
self.label_notification_duration.hide() self.label_notification_duration.hide()
self.spin_duration.hide() self.spin_duration.hide()
self.label_notification_duration_ms.hide() self.label_notification_duration_ms.hide()
self.cb_notify.setChecked(settings.value("message/check_missed/notify", type=bool)) self.cb_notify.setChecked(
settings.value("message/check_missed/notify", type=bool)
)
self.cb_notification_click.setChecked(settings.value("tray/notifications/click", type=bool)) self.cb_notification_click.setChecked(
settings.value("tray/notifications/click", type=bool)
)
self.cb_tray_icon_unread.setChecked(settings.value("tray/icon/unread", type=bool)) self.cb_tray_icon_unread.setChecked(
settings.value("tray/icon/unread", type=bool)
)
self.cb_priority10_persistent.setChecked(
settings.value("tray/notifications/priority10_persistent", type=bool)
)
self.cb_sound_only_priority10.setChecked(
settings.value("tray/notifications/sound_only_priority10", type=bool)
)
# Interface # Interface
self.cb_priority_colors.setChecked(settings.value("MessageWidget/priority_color", type=bool)) self.cb_priority_colors.setChecked(
self.cb_image_urls.setChecked(settings.value("MessageWidget/image_urls", type=bool)) settings.value("MessageWidget/priority_color", type=bool)
)
self.cb_image_urls.setChecked(
settings.value("MessageWidget/image_urls", type=bool)
)
self.cb_locale.setChecked(settings.value("locale", type=bool)) self.cb_locale.setChecked(settings.value("locale", type=bool))
self.cb_sort_applications.setChecked(settings.value("ApplicationModel/sort", type=bool)) self.cb_sort_applications.setChecked(
settings.value("ApplicationModel/sort", type=bool)
)
# Logging # Logging
self.combo_logging.addItems( self.combo_logging.addItems(
@@ -81,18 +107,22 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
self.add_message_widget() self.add_message_widget()
# Advanced # Advanced
self.groupbox_image_popup.setChecked(settings.value("ImagePopup/enabled", type=bool)) self.groupbox_image_popup.setChecked(
settings.value("ImagePopup/enabled", type=bool)
)
self.spin_popup_w.setValue(settings.value("ImagePopup/w", type=int)) self.spin_popup_w.setValue(settings.value("ImagePopup/w", type=int))
self.spin_popup_h.setValue(settings.value("ImagePopup/h", type=int)) self.spin_popup_h.setValue(settings.value("ImagePopup/h", type=int))
self.label_cache.setText("0 MB") self.label_cache.setText("0 MB")
self.compute_cache_size() self.compute_cache_size()
self.groupbox_watchdog.setChecked(settings.value("watchdog/enabled", type=bool)) self.groupbox_watchdog.setChecked(settings.value("watchdog/enabled", type=bool))
self.spin_watchdog_interval.setValue(settings.value("watchdog/interval/s", type=int)) self.spin_watchdog_interval.setValue(
settings.value("watchdog/interval/s", type=int)
)
self.label_app_version.setText(__version__) self.label_app_version.setText(__version__)
self.label_qt_version.setText(QtCore.QT_VERSION_STR) self.label_qt_version.setText(QtCore.QT_VERSION_STR)
self.label_app_icon.setPixmap(QtGui.QIcon(get_image("logo.ico")).pixmap(22,22)) self.label_app_icon.setPixmap(QtGui.QIcon(get_image("logo.ico")).pixmap(22, 22))
self.label_qt_icon.setPixmap(QtGui.QIcon(get_image("qt.png")).pixmap(22,22)) self.label_qt_icon.setPixmap(QtGui.QIcon(get_image("qt.png")).pixmap(22, 22))
def add_message_widget(self): def add_message_widget(self):
self.message_widget = MessageWidget( self.message_widget = MessageWidget(
@@ -113,18 +143,18 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
def compute_cache_size(self): def compute_cache_size(self):
self.cache_size_task = CacheSizeTask() self.cache_size_task = CacheSizeTask()
self.cache_size_task.size.connect(lambda size: self.label_cache.setText(f"{round(size/1e6, 1)} MB")) self.cache_size_task.size.connect(
lambda size: self.label_cache.setText(f"{round(size / 1e6, 1)} MB")
)
self.cache_size_task.start() self.cache_size_task.start()
def set_value(self, key: str, value: Any, widget: QtWidgets.QWidget): def set_value(self, key: str, value: Any, widget: QtWidgets.QWidget):
"""Set a Settings value, only if the widget's value_changed attribute has been set """Set a Settings value, only if the widget's value_changed attribute has been set"""
"""
if hasattr(widget, "value_changed"): if hasattr(widget, "value_changed"):
settings.setValue(key, value) settings.setValue(key, value)
def connect_signal(self, signal: QtCore.pyqtBoundSignal, widget: QtWidgets.QWidget): def connect_signal(self, signal: QtCore.pyqtBoundSignal, widget: QtWidgets.QWidget):
"""Connect to a signal and set the value_changed attribute for a widget on trigger """Connect to a signal and set the value_changed attribute for a widget on trigger"""
"""
signal.connect(lambda *args: self.setting_changed_callback(widget)) signal.connect(lambda *args: self.setting_changed_callback(widget))
def change_server_info_callback(self): def change_server_info_callback(self):
@@ -132,13 +162,17 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
def setting_changed_callback(self, widget: QtWidgets.QWidget): def setting_changed_callback(self, widget: QtWidgets.QWidget):
self.settings_changed = True self.settings_changed = True
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Apply).setEnabled(True) self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Apply
).setEnabled(True)
setattr(widget, "value_changed", True) setattr(widget, "value_changed", True)
def change_font_callback(self, name: str): def change_font_callback(self, name: str):
label: QtWidgets.QLabel = getattr(self.message_widget, "label_" + name) label: QtWidgets.QLabel = getattr(self.message_widget, "label_" + name)
font, accepted = QtWidgets.QFontDialog.getFont(label.font(), self, f"Select a {name} font") font, accepted = QtWidgets.QFontDialog.getFont(
label.font(), self, f"Select a {name} font"
)
if accepted: if accepted:
self.setting_changed_callback(label) self.setting_changed_callback(label)
@@ -146,7 +180,10 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
def export_callback(self): def export_callback(self):
fname = QtWidgets.QFileDialog.getSaveFileName( fname = QtWidgets.QFileDialog.getSaveFileName(
self, "Export Settings", settings.value("export/path", type=str), "*", self,
"Export Settings",
settings.value("export/path", type=str),
"*",
)[0] )[0]
if fname and os.path.exists(os.path.dirname(fname)): if fname and os.path.exists(os.path.dirname(fname)):
self.export_settings_task = ExportSettingsTask(fname) self.export_settings_task = ExportSettingsTask(fname)
@@ -162,7 +199,10 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
def import_callback(self): def import_callback(self):
fname = QtWidgets.QFileDialog.getOpenFileName( fname = QtWidgets.QFileDialog.getOpenFileName(
self, "Import Settings", settings.value("export/path", type=str), "*", self,
"Import Settings",
settings.value("export/path", type=str),
"*",
)[0] )[0]
if fname and os.path.exists(fname): if fname and os.path.exists(fname):
self.import_settings_task = ImportSettingsTask(fname) self.import_settings_task = ImportSettingsTask(fname)
@@ -202,60 +242,128 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
self.label_cache.setText("0 MB") self.label_cache.setText("0 MB")
def link_callbacks(self): def link_callbacks(self):
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Apply).clicked.connect(self.apply_settings) self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Apply
).clicked.connect(self.apply_settings)
# Notifications # Notifications
self.connect_signal(self.spin_priority.valueChanged, self.spin_priority) self.connect_signal(self.spin_priority.valueChanged, self.spin_priority)
self.connect_signal(self.spin_duration.valueChanged, self.spin_duration) self.connect_signal(self.spin_duration.valueChanged, self.spin_duration)
self.connect_signal(self.cb_notify.stateChanged, self.cb_notify) self.connect_signal(self.cb_notify.stateChanged, self.cb_notify)
self.connect_signal(self.cb_notification_click.stateChanged, self.cb_notification_click) self.connect_signal(
self.connect_signal(self.cb_tray_icon_unread.stateChanged, self.cb_tray_icon_unread) self.cb_notification_click.stateChanged, self.cb_notification_click
)
self.connect_signal(
self.cb_tray_icon_unread.stateChanged, self.cb_tray_icon_unread
)
self.connect_signal(
self.cb_priority10_persistent.stateChanged, self.cb_priority10_persistent
)
self.connect_signal(
self.cb_sound_only_priority10.stateChanged, self.cb_sound_only_priority10
)
# Interface # Interface
self.connect_signal(self.cb_priority_colors.stateChanged, self.cb_priority_colors) self.connect_signal(
self.cb_priority_colors.stateChanged, self.cb_priority_colors
)
self.connect_signal(self.cb_image_urls.stateChanged, self.cb_image_urls) self.connect_signal(self.cb_image_urls.stateChanged, self.cb_image_urls)
self.connect_signal(self.cb_locale.stateChanged, self.cb_locale) self.connect_signal(self.cb_locale.stateChanged, self.cb_locale)
self.connect_signal(self.cb_sort_applications.stateChanged, self.cb_sort_applications) self.connect_signal(
self.cb_sort_applications.stateChanged, self.cb_sort_applications
)
# Server info # Server info
self.pb_change_server_info.clicked.connect(self.change_server_info_callback) self.pb_change_server_info.clicked.connect(self.change_server_info_callback)
# Logging # Logging
self.connect_signal(self.combo_logging.currentTextChanged, self.combo_logging) self.connect_signal(self.combo_logging.currentTextChanged, self.combo_logging)
self.pb_open_log.clicked.connect(lambda: open_file(logger.root.handlers[0].baseFilename)) self.pb_open_log.clicked.connect(
lambda: open_file(logger.root.handlers[0].baseFilename)
)
# Fonts # Fonts
self.pb_reset_fonts.clicked.connect(self.reset_fonts_callback) self.pb_reset_fonts.clicked.connect(self.reset_fonts_callback)
self.pb_font_message_title.clicked.connect(lambda: self.change_font_callback("title")) self.pb_font_message_title.clicked.connect(
self.pb_font_message_date.clicked.connect(lambda: self.change_font_callback("date")) lambda: self.change_font_callback("title")
self.pb_font_message_content.clicked.connect(lambda: self.change_font_callback("message")) )
self.pb_font_message_date.clicked.connect(
lambda: self.change_font_callback("date")
)
self.pb_font_message_content.clicked.connect(
lambda: self.change_font_callback("message")
)
# Advanced # Advanced
self.pb_export.clicked.connect(self.export_callback) self.pb_export.clicked.connect(self.export_callback)
self.pb_import.clicked.connect(self.import_callback) self.pb_import.clicked.connect(self.import_callback)
self.pb_reset.clicked.connect(self.reset_callback) self.pb_reset.clicked.connect(self.reset_callback)
self.connect_signal(self.groupbox_image_popup.toggled, self.groupbox_image_popup) self.connect_signal(
self.groupbox_image_popup.toggled, self.groupbox_image_popup
)
self.connect_signal(self.spin_popup_w.valueChanged, self.spin_popup_w) self.connect_signal(self.spin_popup_w.valueChanged, self.spin_popup_w)
self.connect_signal(self.spin_popup_h.valueChanged, self.spin_popup_h) self.connect_signal(self.spin_popup_h.valueChanged, self.spin_popup_h)
self.pb_clear_cache.clicked.connect(self.clear_cache_callback) self.pb_clear_cache.clicked.connect(self.clear_cache_callback)
self.pb_open_cache_dir.clicked.connect(lambda: open_file(Cache().directory())) self.pb_open_cache_dir.clicked.connect(lambda: open_file(Cache().directory()))
self.connect_signal(self.groupbox_watchdog.toggled, self.groupbox_watchdog) self.connect_signal(self.groupbox_watchdog.toggled, self.groupbox_watchdog)
self.connect_signal(self.spin_watchdog_interval.valueChanged, self.spin_watchdog_interval) self.connect_signal(
self.spin_watchdog_interval.valueChanged, self.spin_watchdog_interval
)
def apply_settings(self): def apply_settings(self):
# Priority # Priority
self.set_value("tray/notifications/priority", self.spin_priority.value(), self.spin_priority) self.set_value(
self.set_value("tray/notifications/duration_ms", self.spin_duration.value(), self.spin_duration) "tray/notifications/priority",
self.set_value("message/check_missed/notify", self.cb_notify.isChecked(), self.cb_notify) self.spin_priority.value(),
self.set_value("tray/notifications/click", self.cb_notification_click.isChecked(), self.cb_notification_click) self.spin_priority,
self.set_value("tray/icon/unread", self.cb_tray_icon_unread.isChecked(), self.cb_tray_icon_unread) )
self.set_value(
"tray/notifications/duration_ms",
self.spin_duration.value(),
self.spin_duration,
)
self.set_value(
"message/check_missed/notify", self.cb_notify.isChecked(), self.cb_notify
)
self.set_value(
"tray/notifications/click",
self.cb_notification_click.isChecked(),
self.cb_notification_click,
)
self.set_value(
"tray/icon/unread",
self.cb_tray_icon_unread.isChecked(),
self.cb_tray_icon_unread,
)
self.set_value(
"tray/notifications/priority10_persistent",
self.cb_priority10_persistent.isChecked(),
self.cb_priority10_persistent,
)
self.set_value(
"tray/notifications/sound_only_priority10",
self.cb_sound_only_priority10.isChecked(),
self.cb_sound_only_priority10,
)
# Interface # Interface
self.set_value("MessageWidget/priority_color", self.cb_priority_colors.isChecked(), self.cb_priority_colors) self.set_value(
self.set_value("MessageWidget/image_urls", self.cb_image_urls.isChecked(), self.cb_image_urls) "MessageWidget/priority_color",
self.cb_priority_colors.isChecked(),
self.cb_priority_colors,
)
self.set_value(
"MessageWidget/image_urls",
self.cb_image_urls.isChecked(),
self.cb_image_urls,
)
self.set_value("locale", self.cb_locale.isChecked(), self.cb_locale) self.set_value("locale", self.cb_locale.isChecked(), self.cb_locale)
self.set_value("ApplicationModel/sort", self.cb_sort_applications.isChecked(), self.cb_sort_applications) self.set_value(
"ApplicationModel/sort",
self.cb_sort_applications.isChecked(),
self.cb_sort_applications,
)
# Logging # Logging
selected_level = self.combo_logging.currentText() selected_level = self.combo_logging.currentText()
@@ -267,18 +375,44 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
logger.setLevel(selected_level) logger.setLevel(selected_level)
# Fonts # Fonts
self.set_value("MessageWidget/font/title", self.message_widget.label_title.font().toString(), self.message_widget.label_title) self.set_value(
self.set_value("MessageWidget/font/date", self.message_widget.label_date.font().toString(), self.message_widget.label_date) "MessageWidget/font/title",
self.set_value("MessageWidget/font/message", self.message_widget.label_message.font().toString(), self.message_widget.label_message) self.message_widget.label_title.font().toString(),
self.message_widget.label_title,
)
self.set_value(
"MessageWidget/font/date",
self.message_widget.label_date.font().toString(),
self.message_widget.label_date,
)
self.set_value(
"MessageWidget/font/message",
self.message_widget.label_message.font().toString(),
self.message_widget.label_message,
)
# Advanced # Advanced
self.set_value("ImagePopup/enabled", self.groupbox_image_popup.isChecked(), self.groupbox_image_popup) self.set_value(
"ImagePopup/enabled",
self.groupbox_image_popup.isChecked(),
self.groupbox_image_popup,
)
self.set_value("ImagePopup/w", self.spin_popup_w.value(), self.spin_popup_w) self.set_value("ImagePopup/w", self.spin_popup_w.value(), self.spin_popup_w)
self.set_value("ImagePopup/h", self.spin_popup_h.value(), self.spin_popup_h) self.set_value("ImagePopup/h", self.spin_popup_h.value(), self.spin_popup_h)
self.set_value("watchdog/enabled", self.groupbox_watchdog.isChecked(), self.groupbox_watchdog) self.set_value(
self.set_value("watchdog/interval/s", self.spin_watchdog_interval.value(), self.spin_watchdog_interval) "watchdog/enabled",
self.groupbox_watchdog.isChecked(),
self.groupbox_watchdog,
)
self.set_value(
"watchdog/interval/s",
self.spin_watchdog_interval.value(),
self.spin_watchdog_interval,
)
self.settings_changed = False self.settings_changed = False
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Apply).setEnabled(False) self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Apply
).setEnabled(False)
self.changes_applied = True self.changes_applied = True

8
pyrightconfig.json Normal file
View File

@@ -0,0 +1,8 @@
{
"reportOptionalMemberAccess": false,
"reportAttributeAccessIssue": false,
"reportIncompatibleMethodOverride": false,
"reportArgumentType": false,
"reportAssignmentType": false,
"reportReturnType": false
}

View File

@@ -1,2 +1,4 @@
requests==2.32.3 requests
pyqt6==6.7.1 pyqt6>=6.7.1
pyqt6-qt6
pyqt6-qt6-multimedia

View File

@@ -15,7 +15,7 @@ with open("version.txt", "r") as f:
# What packages are required for this module to be executed? # What packages are required for this module to be executed?
REQUIRED = [ REQUIRED = [
'requests==2.32.3', 'pyqt6==6.7.1' 'requests', 'pyqt6>=6.7.1'
] ]
# What packages are optional? # What packages are optional?
@@ -86,6 +86,7 @@ setup(
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12' 'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13'
] ]
) )

View File

@@ -6,8 +6,8 @@ VSVersionInfo(
ffi=FixedFileInfo( ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0. # Set not needed items to zero 0.
filevers=(0, 5, 2, 0), filevers=(0, 5, 3, 0),
prodvers=(0, 5, 2, 0), prodvers=(0, 5, 3, 0),
# Contains a bitmask that specifies the valid bits 'flags'r # Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3F, mask=0x3F,
# Contains a bitmask that specifies the Boolean attributes of the file. # Contains a bitmask that specifies the Boolean attributes of the file.
@@ -34,12 +34,12 @@ VSVersionInfo(
StringStruct(u"Comments", u"Gotify Tray"), StringStruct(u"Comments", u"Gotify Tray"),
StringStruct(u"CompanyName", u""), StringStruct(u"CompanyName", u""),
StringStruct(u"FileDescription", u"Gotifiy Tray"), StringStruct(u"FileDescription", u"Gotifiy Tray"),
StringStruct(u"FileVersion", u"0.5.2"), StringStruct(u"FileVersion", u"0.5.3"),
StringStruct(u"InternalName", u"gotify-tray"), StringStruct(u"InternalName", u"gotify-tray"),
StringStruct(u"LegalCopyright", u""), StringStruct(u"LegalCopyright", u""),
StringStruct(u"OriginalFilename", u"gotify-tray.exe"), StringStruct(u"OriginalFilename", u"gotify-tray.exe"),
StringStruct(u"ProductName", u"Gotify Tray"), StringStruct(u"ProductName", u"Gotify Tray"),
StringStruct(u"ProductVersion", u"0.5.2"), StringStruct(u"ProductVersion", u"0.5.3"),
], ],
) )
] ]

View File

@@ -1 +1 @@
0.5.2 0.5.3