diff --git a/gotify_tray/database/default_settings.py b/gotify_tray/database/default_settings.py
index db8d6dc..b2f32d4 100644
--- a/gotify_tray/database/default_settings.py
+++ b/gotify_tray/database/default_settings.py
@@ -20,4 +20,7 @@ DEFAULT_SETTINGS = {
"MainWindow/label/size": 25,
"MainWindow/button/size": 33,
"MainWindow/application/icon/size": 40,
+ "ImagePopup/enabled": False,
+ "ImagePopup/w": 400,
+ "ImagePopup/h": 400,
}
diff --git a/gotify_tray/gui/MainApplication.py b/gotify_tray/gui/MainApplication.py
index 1d16eb5..e3db4a1 100644
--- a/gotify_tray/gui/MainApplication.py
+++ b/gotify_tray/gui/MainApplication.py
@@ -31,7 +31,7 @@ from .models import (
MessagesModelItem,
MessageItemDataRole,
)
-from .widgets import MainWindow, SettingsDialog, Tray
+from .widgets import ImagePopup, MainWindow, SettingsDialog, Tray
settings = Settings("gotify-tray")
@@ -65,6 +65,7 @@ class MainApplication(QtWidgets.QApplication):
settings.value("Server/client_token", type=str),
)
+ self.cache = Cache()
self.downloader = Downloader()
self.messages_model = MessagesModel()
@@ -308,6 +309,14 @@ class MainApplication(QtWidgets.QApplication):
return
self.messages_model.clear()
+
+ def image_popup_callback(self, link: str, pos: QtCore.QPoint):
+ if (filename := self.cache.lookup(link)) or (filename := self.downloader.get_filename(link)): # TODO: preload links
+ self.image_popup = ImagePopup(filename, pos, link)
+ self.image_popup.show()
+ else:
+ # TODO
+ logger.warning(f"Image {link} is not in the cache")
def refresh_callback(self):
# Manual refresh -> also reset the image cache
@@ -362,6 +371,7 @@ class MainApplication(QtWidgets.QApplication):
self.application_selection_changed_callback
)
self.main_window.delete_message.connect(self.delete_message_callback)
+ self.main_window.image_popup.connect(self.image_popup_callback)
self.watchdog.closed.connect(lambda: self.listener_closed_callback(None, None))
diff --git a/gotify_tray/gui/designs/widget_settings.py b/gotify_tray/gui/designs/widget_settings.py
index 530e3e3..a4bddb7 100644
--- a/gotify_tray/gui/designs/widget_settings.py
+++ b/gotify_tray/gui/designs/widget_settings.py
@@ -105,18 +105,26 @@ class Ui_Dialog(object):
self.verticalLayout.setObjectName("verticalLayout")
self.groupBox = QtWidgets.QGroupBox(self.tab_advanced)
self.groupBox.setObjectName("groupBox")
- self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox)
- self.verticalLayout_2.setObjectName("verticalLayout_2")
- self.pb_export = QtWidgets.QPushButton(self.groupBox)
- self.pb_export.setObjectName("pb_export")
- self.verticalLayout_2.addWidget(self.pb_export)
- self.pb_import = QtWidgets.QPushButton(self.groupBox)
- self.pb_import.setObjectName("pb_import")
- self.verticalLayout_2.addWidget(self.pb_import)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.groupBox)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.pb_reset = QtWidgets.QPushButton(self.groupBox)
self.pb_reset.setObjectName("pb_reset")
- self.verticalLayout_2.addWidget(self.pb_reset)
+ self.horizontalLayout_2.addWidget(self.pb_reset)
+ self.pb_import = QtWidgets.QPushButton(self.groupBox)
+ self.pb_import.setObjectName("pb_import")
+ self.horizontalLayout_2.addWidget(self.pb_import)
+ self.pb_export = QtWidgets.QPushButton(self.groupBox)
+ self.pb_export.setObjectName("pb_export")
+ self.horizontalLayout_2.addWidget(self.pb_export)
self.verticalLayout.addWidget(self.groupBox)
+ self.groupBox_2 = QtWidgets.QGroupBox(self.tab_advanced)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox_2)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.cb_image_popup = QtWidgets.QCheckBox(self.groupBox_2)
+ self.cb_image_popup.setObjectName("cb_image_popup")
+ self.gridLayout_2.addWidget(self.cb_image_popup, 0, 0, 1, 1)
+ self.verticalLayout.addWidget(self.groupBox_2)
self.groupBox_logging = QtWidgets.QGroupBox(self.tab_advanced)
self.groupBox_logging.setObjectName("groupBox_logging")
self.gridLayout_6 = QtWidgets.QGridLayout(self.groupBox_logging)
@@ -170,9 +178,11 @@ class Ui_Dialog(object):
self.pb_font_message_content.setText(_translate("Dialog", "Message"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_fonts), _translate("Dialog", "Fonts"))
self.groupBox.setTitle(_translate("Dialog", "Settings"))
- self.pb_export.setText(_translate("Dialog", "Export"))
- self.pb_import.setText(_translate("Dialog", "Import"))
self.pb_reset.setText(_translate("Dialog", "Reset"))
+ self.pb_import.setText(_translate("Dialog", "Import"))
+ self.pb_export.setText(_translate("Dialog", "Export"))
+ self.groupBox_2.setTitle(_translate("Dialog", "Image pop-up"))
+ self.cb_image_popup.setText(_translate("Dialog", "Show an image pop-up when hovering over image URLs"))
self.groupBox_logging.setTitle(_translate("Dialog", "Logging"))
self.label_logging.setText(_translate("Dialog", "Level"))
self.pb_open_log.setToolTip(_translate("Dialog", "Open logfile"))
diff --git a/gotify_tray/gui/designs/widget_settings.ui b/gotify_tray/gui/designs/widget_settings.ui
index 3abaad3..d261fde 100644
--- a/gotify_tray/gui/designs/widget_settings.ui
+++ b/gotify_tray/gui/designs/widget_settings.ui
@@ -240,11 +240,11 @@
Settings
-
+
-
-
+
- Export
+ Reset
@@ -256,9 +256,25 @@
-
-
+
- Reset
+ Export
+
+
+
+
+
+
+ -
+
+
+ Image pop-up
+
+
+
-
+
+
+ Show an image pop-up when hovering over image URLs
diff --git a/gotify_tray/gui/widgets/ImagePopup.py b/gotify_tray/gui/widgets/ImagePopup.py
new file mode 100644
index 0000000..e61955d
--- /dev/null
+++ b/gotify_tray/gui/widgets/ImagePopup.py
@@ -0,0 +1,46 @@
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+from gotify_tray.database import Settings
+
+
+settings = Settings("gotify-tray")
+
+
+class ImagePopup(QtWidgets.QLabel):
+ def __init__(self, filename: str, pos: QtCore.QPoint, link: str = None):
+ """Create and show a pop-up image under the cursor
+
+ Args:
+ filename (str): The path to the image to display
+ pos (QtCore.QPoint): The location at which the image should be displayed
+ link (str, optional): The URL of the image. Defaults to None.
+ """
+ super(ImagePopup, self).__init__()
+ self.link = link
+
+ self.setWindowFlags(QtCore.Qt.WindowType.ToolTip)
+ self.installEventFilter(self)
+
+ self.setPixmap(
+ QtGui.QPixmap(filename).scaled(
+ settings.value("ImagePopup/w", type=int),
+ settings.value("ImagePopup/h", type=int),
+ QtCore.Qt.AspectRatioMode.KeepAspectRatio,
+ )
+ )
+ self.move(pos - QtCore.QPoint(30, 30))
+ self.show()
+
+ def eventFilter(self, object: QtCore.QObject, event: QtCore.QEvent) -> bool:
+ if event.type() == QtCore.QEvent.Type.Leave:
+ # Close the pop-up on mouse leave
+ self.close()
+ elif (
+ event.type() == QtCore.QEvent.Type.MouseButtonPress
+ and event.button() == QtCore.Qt.MouseButton.LeftButton
+ and self.link
+ ):
+ # Open the image URL on left click
+ QtGui.QDesktopServices.openUrl(QtCore.QUrl(self.link))
+
+ return super().eventFilter(object, event)
diff --git a/gotify_tray/gui/widgets/MainWindow.py b/gotify_tray/gui/widgets/MainWindow.py
index 21faa98..541f44c 100644
--- a/gotify_tray/gui/widgets/MainWindow.py
+++ b/gotify_tray/gui/widgets/MainWindow.py
@@ -21,6 +21,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
delete_all = QtCore.pyqtSignal(QtGui.QStandardItem)
delete_message = QtCore.pyqtSignal(MessagesModelItem)
application_selection_changed = QtCore.pyqtSignal(QtGui.QStandardItem)
+ image_popup = QtCore.pyqtSignal(str, QtCore.QPoint)
def __init__(
self, application_model: ApplicationModel, messages_model: MessagesModel
@@ -103,6 +104,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.messages_model.indexFromItem(message_item), message_widget
)
message_widget.deletion_requested.connect(self.delete_message.emit)
+ message_widget.image_popup.connect(self.image_popup.emit)
def currentApplicationIndex(self) -> QtCore.QModelIndex:
return self.listView_applications.selectionModel().currentIndex()
diff --git a/gotify_tray/gui/widgets/MessageWidget.py b/gotify_tray/gui/widgets/MessageWidget.py
index db1cde6..3b6eb9c 100644
--- a/gotify_tray/gui/widgets/MessageWidget.py
+++ b/gotify_tray/gui/widgets/MessageWidget.py
@@ -1,8 +1,10 @@
+import os
+
from PyQt6 import QtCore, QtGui, QtWidgets
from ..models.MessagesModel import MessageItemDataRole, MessagesModelItem
from ..designs.widget_message import Ui_Form
-from gotify_tray.database import Settings
+from gotify_tray.database import Cache, Settings
from gotify_tray.utils import convert_links, get_abs_path
@@ -11,6 +13,7 @@ settings = Settings("gotify-tray")
class MessageWidget(QtWidgets.QWidget, Ui_Form):
deletion_requested = QtCore.pyqtSignal(MessagesModelItem)
+ image_popup = QtCore.pyqtSignal(str, QtCore.QPoint)
def __init__(self, message_item: MessagesModelItem, image_path: str = ""):
super(MessageWidget, self).__init__()
@@ -86,7 +89,16 @@ class MessageWidget(QtWidgets.QWidget, Ui_Form):
self.label_date.setFont(font_date)
self.label_message.setFont(font_content)
+ def link_hovered_callback(self, link: str):
+ if not settings.value("ImagePopup/enabled", type=bool):
+ return
+
+ _, ext = os.path.splitext(link)
+ if ext in [".jpg", ".jpeg", ".png"]:
+ self.image_popup.emit(link, QtGui.QCursor.pos())
+
def link_callbacks(self):
self.pb_delete.clicked.connect(
lambda: self.deletion_requested.emit(self.message_item)
)
+ self.label_message.linkHovered.connect(self.link_hovered_callback)
diff --git a/gotify_tray/gui/widgets/SettingsDialog.py b/gotify_tray/gui/widgets/SettingsDialog.py
index 45fb368..7d38427 100644
--- a/gotify_tray/gui/widgets/SettingsDialog.py
+++ b/gotify_tray/gui/widgets/SettingsDialog.py
@@ -86,6 +86,10 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
)
self.layout_fonts_message.addWidget(self.message_widget)
+ # Advanced
+ self.cb_image_popup.setChecked(settings.value("ImagePopup/enabled", type=bool))
+ # TODO: w/h
+
def change_server_info_callback(self):
self.server_changed = verify_server(force_new=True, enable_import=False)
@@ -179,6 +183,7 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
self.pb_export.clicked.connect(self.export_callback)
self.pb_import.clicked.connect(self.import_callback)
self.pb_reset.clicked.connect(self.reset_callback)
+ self.cb_image_popup.stateChanged.connect(self.settings_changed_callback)
def apply_settings(self):
# Priority
@@ -211,6 +216,9 @@ class SettingsDialog(QtWidgets.QDialog, Ui_Dialog):
self.message_widget.label_message.font().toString(),
)
+ # Advanced
+ settings.setValue("ImagePopup/enabled", self.cb_image_popup.isChecked())
+
self.settings_changed = False
self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Apply
diff --git a/gotify_tray/gui/widgets/__init__.py b/gotify_tray/gui/widgets/__init__.py
index d43f65f..2f9952f 100644
--- a/gotify_tray/gui/widgets/__init__.py
+++ b/gotify_tray/gui/widgets/__init__.py
@@ -1,3 +1,4 @@
+from .ImagePopup import ImagePopup
from .MessageWidget import MessageWidget
from .MainWindow import MainWindow
from .ServerInfoDialog import ServerInfoDialog