From a036caaca63bf07ed7ed354b0d63506bf0f771c4 Mon Sep 17 00:00:00 2001 From: "dries.k" Date: Tue, 28 Sep 2021 19:05:37 +0200 Subject: [PATCH] allow reordering of applications --- gotify_tray/gui/ApplicationModel.py | 49 +++++++++++++++- gotify_tray/gui/MainWindow.py | 81 +++++++++++++++++++++----- gotify_tray/gui/MessageWidget.py | 4 +- gotify_tray/gui/MessagesModel.py | 8 ++- gotify_tray/gui/designs/widget_main.py | 4 +- gotify_tray/gui/designs/widget_main.ui | 8 ++- 6 files changed, 131 insertions(+), 23 deletions(-) diff --git a/gotify_tray/gui/ApplicationModel.py b/gotify_tray/gui/ApplicationModel.py index e59d02c..556b232 100644 --- a/gotify_tray/gui/ApplicationModel.py +++ b/gotify_tray/gui/ApplicationModel.py @@ -1,6 +1,17 @@ +import enum + from typing import Optional, Union from PyQt6 import QtCore, QtGui from gotify_tray import gotify +from gotify_tray.database import Settings + + +settings = Settings("gotify-tray") + + +class ApplicationItemDataRole(enum.IntEnum): + ApplicationRole = QtCore.Qt.ItemDataRole.UserRole + 1 + IconRole = QtCore.Qt.ItemDataRole.UserRole + 2 class ApplicationModelItem(QtGui.QStandardItem): @@ -12,18 +23,39 @@ class ApplicationModelItem(QtGui.QStandardItem): **kwargs ): super(ApplicationModelItem, self).__init__(application.name) - self.application = application + self.setDropEnabled(False) + self.setData(application, ApplicationItemDataRole.ApplicationRole) + self.setData(icon, ApplicationItemDataRole.IconRole) if icon: self.setIcon(icon) + def clone(self): + return ApplicationModelItem( + self.data(ApplicationItemDataRole.ApplicationRole), + self.data(ApplicationItemDataRole.IconRole), + ) + class ApplicationAllMessagesItem(QtGui.QStandardItem): def __init__(self, *args, **kwargs): super(ApplicationAllMessagesItem, self).__init__("ALL MESSAGES") + self.setDropEnabled(False) + self.setDragEnabled(False) class ApplicationModel(QtGui.QStandardItemModel): - def setItem(self, row: int, column: int, item: Union[ApplicationModelItem, ApplicationAllMessagesItem]) -> None: + def __init__(self): + super(ApplicationModel, self).__init__() + self.setItemPrototype( + ApplicationModelItem(gotify.GotifyApplicationModel({"name": ""}), None) + ) + + def setItem( + self, + row: int, + column: int, + item: Union[ApplicationModelItem, ApplicationAllMessagesItem], + ) -> None: super(ApplicationModel, self).setItem(row, column, item) def itemFromIndex( @@ -36,6 +68,17 @@ class ApplicationModel(QtGui.QStandardItemModel): item = self.item(row, 0) if not isinstance(item, ApplicationModelItem): continue - if item.application.id == appid: + if item.data(ApplicationItemDataRole.ApplicationRole).id == appid: return item return None + + def save_order(self, *args): + try: + application_ids = [ + self.item(i, 0).data(ApplicationItemDataRole.ApplicationRole).id + for i in range(1, self.rowCount()) + ] + except AttributeError: + return + + settings.setValue("ApplicationModel/order", application_ids) diff --git a/gotify_tray/gui/MainWindow.py b/gotify_tray/gui/MainWindow.py index 7747663..884ffda 100644 --- a/gotify_tray/gui/MainWindow.py +++ b/gotify_tray/gui/MainWindow.py @@ -18,13 +18,14 @@ from PyQt6 import QtCore, QtGui, QtWidgets from ..__version__ import __title__ from .ApplicationModel import ( + ApplicationItemDataRole, ApplicationAllMessagesItem, ApplicationModel, ApplicationModelItem, ) from .designs.widget_main import Ui_Form as Ui_Main from .themes import set_theme -from .MessagesModel import MessagesModel, MessagesModelItem +from .MessagesModel import MessageItemDataRole, MessagesModel, MessagesModelItem from .MessageWidget import MessageWidget from .SettingsDialog import SettingsDialog from .Tray import Tray @@ -126,7 +127,32 @@ class MainWindow(QtWidgets.QMainWindow): def get_applications_callback( applications: List[gotify.GotifyApplicationModel], ): - for i, application in enumerate(applications): + stored_application_ids_order = [ + int(x) for x in settings.value("ApplicationModel/order", type=list) + ] + fetched_application_ids = [application.id for application in applications] + # Remove ids from stored_application_ids that are not in fetched_application_ids + application_ids_order = list( + filter( + lambda x: x in fetched_application_ids, stored_application_ids_order + ) + ) + # Add new ids to the back of the list + application_ids_order += list( + filter( + lambda x: x not in stored_application_ids_order, + fetched_application_ids, + ) + ) + + for i, application_id in enumerate(application_ids_order): + application = list( + filter( + lambda application: application.id == application_id, + applications, + ) + )[0] + icon = ( QtGui.QIcon( downloader.get_filename( @@ -140,6 +166,8 @@ class MainWindow(QtWidgets.QMainWindow): i + 1, 0, ApplicationModelItem(application, icon), ) + self.application_model.save_order() + self.get_applications_task = GetApplicationsTask(self.gotify_client) self.get_applications_task.success.connect(get_applications_callback) self.get_applications_task.finished.connect( @@ -200,10 +228,15 @@ class MainWindow(QtWidgets.QMainWindow): page: gotify.GotifyPagedMessagesModel, ): for i, message in enumerate(page.messages): - self.insert_message(i, message, item.application) + self.insert_message( + i, + message, + item.data(ApplicationItemDataRole.ApplicationRole), + ) self.get_application_messages_task = GetApplicationMessagesTask( - item.application.id, self.gotify_client + item.data(ApplicationItemDataRole.ApplicationRole).id, + self.gotify_client, ) self.get_application_messages_task.success.connect( get_application_messages_callback @@ -215,16 +248,18 @@ class MainWindow(QtWidgets.QMainWindow): def get_messages_callback(page: gotify.GotifyPagedMessagesModel): for i, message in enumerate(page.messages): if item := self.application_model.itemFromId(message.appid): - self.insert_message(i, message, item.application) + self.insert_message( + i, + message, + item.data(ApplicationItemDataRole.ApplicationRole), + ) self.get_messages_task = GetMessagesTask(self.gotify_client) self.get_messages_task.success.connect(get_messages_callback) self.get_messages_task.start() def refresh_callback(self): - self.application_model.clear() - self.messages_model.clear() - + self.application_model.save_order() self.refresh_applications() if not self.gotify_client.listener.running: self.gotify_client.listener.reset_wait_time() @@ -239,7 +274,8 @@ class MainWindow(QtWidgets.QMainWindow): if isinstance(item, ApplicationModelItem): self.delete_application_messages_task = DeleteApplicationMessagesTask( - item.application.id, self.gotify_client + item.data(ApplicationItemDataRole.ApplicationRole).id, + self.gotify_client, ) self.delete_application_messages_task.start() elif isinstance(item, ApplicationAllMessagesItem): @@ -254,13 +290,14 @@ class MainWindow(QtWidgets.QMainWindow): logger.error( f"MainWindow.new_message_callback: App id {message.appid} could not be found. Refreshing applications." ) + self.application_model.save_order() self.refresh_applications() return if not self.isActiveWindow() and message.priority >= settings.value( "tray/notifications/priority", type=int ): - image_url = f"{self.gotify_client.url}/{application_item.application.image}" + image_url = f"{self.gotify_client.url}/{application_item.data(ApplicationItemDataRole.ApplicationRole).image}" self.tray.showMessage( message.title, message.message, @@ -279,17 +316,30 @@ class MainWindow(QtWidgets.QMainWindow): ): if isinstance(selected_application_item, ApplicationModelItem): # A single application is selected - if message.appid == selected_application_item.application.id: - self.insert_message(0, message, application_item.application) + if ( + message.appid + == selected_application_item.data( + ApplicationItemDataRole.ApplicationRole + ).id + ): + self.insert_message( + 0, + message, + application_item.data(ApplicationItemDataRole.ApplicationRole), + ) elif isinstance(selected_application_item, ApplicationAllMessagesItem): # "All messages' is selected - self.insert_message(0, message, application_item.application) + self.insert_message( + 0, + message, + application_item.data(ApplicationItemDataRole.ApplicationRole), + ) def message_deletion_requested_callback(self, message_item: MessagesModelItem): - self.messages_model.removeRow(message_item.row()) self.delete_message_task = DeleteMessageTask( - message_item.message.id, self.gotify_client + message_item.data(MessageItemDataRole.MessageRole).id, self.gotify_client ) + self.messages_model.removeRow(message_item.row()) self.delete_message_task.start() def tray_activated_callback( @@ -392,6 +442,7 @@ class MainWindow(QtWidgets.QMainWindow): def closeEvent(self, e: QtGui.QCloseEvent) -> None: self.save_window_state() + self.application_model.save_order() if settings.value("tray/show", type=bool): self.tray.hide() diff --git a/gotify_tray/gui/MessageWidget.py b/gotify_tray/gui/MessageWidget.py index 783e984..a82fc84 100644 --- a/gotify_tray/gui/MessageWidget.py +++ b/gotify_tray/gui/MessageWidget.py @@ -2,7 +2,7 @@ import re from PyQt6 import QtCore, QtGui, QtWidgets -from .MessagesModel import MessagesModelItem +from .MessagesModel import MessageItemDataRole, MessagesModelItem from .designs.widget_message import Ui_Form from gotify_tray.database import Settings @@ -36,7 +36,7 @@ class MessageWidget(QtWidgets.QWidget, Ui_Form): self.setAutoFillBackground(True) self.message_item = message_item - message = self.message_item.message + message = message_item.data(MessageItemDataRole.MessageRole) # Fonts font_title = QtGui.QFont() diff --git a/gotify_tray/gui/MessagesModel.py b/gotify_tray/gui/MessagesModel.py index ccaadc0..ee45b44 100644 --- a/gotify_tray/gui/MessagesModel.py +++ b/gotify_tray/gui/MessagesModel.py @@ -1,12 +1,18 @@ +import enum + from typing import cast from PyQt6 import QtCore, QtGui, QtWidgets from gotify_tray import gotify +class MessageItemDataRole(enum.IntEnum): + MessageRole = QtCore.Qt.ItemDataRole.UserRole + 1 + + class MessagesModelItem(QtGui.QStandardItem): def __init__(self, message: gotify.GotifyMessageModel, *args, **kwargs): super(MessagesModelItem, self).__init__() - self.message = message + self.setData(message, MessageItemDataRole.MessageRole) class MessagesModel(QtGui.QStandardItemModel): diff --git a/gotify_tray/gui/designs/widget_main.py b/gotify_tray/gui/designs/widget_main.py index 9f10da0..297f9bd 100644 --- a/gotify_tray/gui/designs/widget_main.py +++ b/gotify_tray/gui/designs/widget_main.py @@ -25,6 +25,8 @@ class Ui_Form(object): font.setPointSize(13) self.listView_applications.setFont(font) self.listView_applications.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.listView_applications.setDragEnabled(True) + self.listView_applications.setDragDropMode(QtWidgets.QAbstractItemView.DragDropMode.InternalMove) self.listView_applications.setWordWrap(True) self.listView_applications.setObjectName("listView_applications") self.gridLayout_2.addWidget(self.listView_applications, 0, 0, 1, 1) @@ -45,6 +47,7 @@ class Ui_Form(object): font.setPointSize(15) font.setBold(True) self.label_selected.setFont(font) + self.label_selected.setText("") self.label_selected.setObjectName("label_selected") self.gridLayout.addWidget(self.label_selected, 0, 2, 1, 1) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) @@ -81,7 +84,6 @@ class Ui_Form(object): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Form")) self.pb_delete_all.setText(_translate("Form", "Delete All")) - self.label_selected.setText(_translate("Form", "TextLabel")) self.pb_refresh.setText(_translate("Form", "Refresh")) diff --git a/gotify_tray/gui/designs/widget_main.ui b/gotify_tray/gui/designs/widget_main.ui index ed0e464..a669da1 100644 --- a/gotify_tray/gui/designs/widget_main.ui +++ b/gotify_tray/gui/designs/widget_main.ui @@ -30,6 +30,12 @@ QAbstractItemView::NoEditTriggers + + true + + + QAbstractItemView::InternalMove + true @@ -83,7 +89,7 @@ - TextLabel +