mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-01-29 22:30:57 +00:00
feat(ui): add configurable splash screens (#703)
* refactor: move splash images to resource_manager * feat(ui): add new splash screen widget * style: restore old resource manager log style * fix: scale font size for Segoe UI
This commit is contained in:
committed by
GitHub
parent
857f40f2e3
commit
a02c43c115
Binary file not shown.
|
Before Width: | Height: | Size: 397 KiB |
BIN
tagstudio/resources/qt/images/splash/classic.png
Normal file
BIN
tagstudio/resources/qt/images/splash/classic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 173 KiB |
BIN
tagstudio/resources/qt/images/splash/goo_gears.png
Normal file
BIN
tagstudio/resources/qt/images/splash/goo_gears.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 388 KiB |
@@ -7,7 +7,11 @@ from typing import Any
|
||||
|
||||
import structlog
|
||||
import ujson
|
||||
from PIL import Image
|
||||
from PIL import (
|
||||
Image,
|
||||
ImageQt,
|
||||
)
|
||||
from PySide6.QtGui import QPixmap
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -25,7 +29,10 @@ class ResourceManager:
|
||||
if not ResourceManager._initialized:
|
||||
with open(Path(__file__).parent / "resources.json", encoding="utf-8") as f:
|
||||
ResourceManager._map = ujson.load(f)
|
||||
logger.info("resources registered", count=len(ResourceManager._map.items()))
|
||||
logger.info(
|
||||
"[ResourceManager] Resources Registered:",
|
||||
count=len(ResourceManager._map.items()),
|
||||
)
|
||||
ResourceManager._initialized = True
|
||||
|
||||
@staticmethod
|
||||
@@ -76,12 +83,15 @@ class ResourceManager:
|
||||
elif res and res.get("mode") == "pil":
|
||||
data = Image.open(ResourceManager._res_folder / "resources" / res.get("path"))
|
||||
return data
|
||||
elif res.get("mode") in ["qt"]:
|
||||
# TODO: Qt resource loading logic
|
||||
pass
|
||||
elif res.get("mode") in ["qpixmap"]:
|
||||
data = Image.open(ResourceManager._res_folder / "resources" / res.get("path"))
|
||||
qim = ImageQt.ImageQt(data)
|
||||
pixmap = QPixmap.fromImage(qim)
|
||||
ResourceManager._cache[id] = pixmap
|
||||
return pixmap
|
||||
except FileNotFoundError:
|
||||
path: Path = ResourceManager._res_folder / "resources" / res.get("path")
|
||||
logger.error("[ResourceManager][ERROR]: Could not find resource: ", path)
|
||||
logger.error("[ResourceManager][ERROR]: Could not find resource: ", path=path)
|
||||
return None
|
||||
|
||||
def __getattr__(self, __name: str) -> Any:
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
{
|
||||
"splash_classic": {
|
||||
"path": "qt/images/splash/classic.png",
|
||||
"mode": "qpixmap"
|
||||
},
|
||||
"splash_goo_gears": {
|
||||
"path": "qt/images/splash/goo_gears.png",
|
||||
"mode": "qpixmap"
|
||||
},
|
||||
"logo": {
|
||||
"path": "icon.png",
|
||||
"mode": "pil"
|
||||
|
||||
@@ -8,6 +8,5 @@
|
||||
<!-- <file alias = "images/edit_icon_128.png">../../resources/qt/images/edit_icon_128.png</file> -->
|
||||
<!-- <file alias = "images/trash_icon_128.png">../../resources/qt/images/trash_icon_128.png</file> -->
|
||||
<!-- <file alias = "images/clipboard_icon_128.png">../../resources/qt/images/clipboard_icon_128.png</file> -->
|
||||
<file alias = "images/splash.png">../../resources/qt/images/splash.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
File diff suppressed because it is too large
Load Diff
170
tagstudio/src/qt/splash.py
Normal file
170
tagstudio/src/qt/splash.py
Normal file
@@ -0,0 +1,170 @@
|
||||
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
|
||||
# Licensed under the GPL-3.0 License.
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
import math
|
||||
|
||||
import structlog
|
||||
from PySide6.QtCore import QRect, Qt
|
||||
from PySide6.QtGui import (
|
||||
QColor,
|
||||
QFont,
|
||||
QPainter,
|
||||
QPen,
|
||||
QPixmap,
|
||||
)
|
||||
from PySide6.QtWidgets import (
|
||||
QSplashScreen,
|
||||
QWidget,
|
||||
)
|
||||
from src.core.constants import (
|
||||
VERSION,
|
||||
VERSION_BRANCH,
|
||||
)
|
||||
from src.qt.resource_manager import ResourceManager
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class Splash:
|
||||
"""The custom splash screen widget for TagStudio."""
|
||||
|
||||
COPYRIGHT_YEARS: str = "2021-2025"
|
||||
COPYRIGHT_STR: str = f"© {COPYRIGHT_YEARS} Travis Abendshien (CyanVoxel)"
|
||||
VERSION_STR: str = (
|
||||
f"Version {VERSION} {(" (" + VERSION_BRANCH + ")") if VERSION_BRANCH else ""}"
|
||||
)
|
||||
|
||||
SPLASH_CLASSIC: str = "classic"
|
||||
SPLASH_GOO_GEARS: str = "goo_gears"
|
||||
SPLASH_95: str = "95"
|
||||
DEFAULT_SPLASH: str = SPLASH_GOO_GEARS
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
resource_manager: ResourceManager,
|
||||
screen_width: int,
|
||||
splash_name: str,
|
||||
device_ratio: float = 1,
|
||||
):
|
||||
self.rm = resource_manager
|
||||
self.screen_width = screen_width
|
||||
self.ratio: float = device_ratio
|
||||
self.splash_screen: QSplashScreen | None = None
|
||||
self.splash_name: str = splash_name if splash_name else Splash.DEFAULT_SPLASH
|
||||
|
||||
def get_pixmap(self) -> QPixmap:
|
||||
"""Get the pixmap used for the splash screen."""
|
||||
pixmap: QPixmap | None = self.rm.get(f"splash_{self.splash_name}")
|
||||
if not pixmap:
|
||||
logger.error("[Splash] Splash screen not found:", splash_name=self.splash_name)
|
||||
pixmap = QPixmap(960, 540)
|
||||
pixmap.fill(QColor("black"))
|
||||
painter = QPainter(pixmap)
|
||||
point_size_scale: float = 1.0
|
||||
match painter.font().family():
|
||||
case "Segoe UI":
|
||||
point_size_scale = 0.75
|
||||
|
||||
# TODO: Store any differing data elsewhere and load dynamically instead of hardcoding.
|
||||
match self.splash_name:
|
||||
case Splash.SPLASH_CLASSIC:
|
||||
# Copyright
|
||||
font = painter.font()
|
||||
font.setPointSize(math.floor(22 * point_size_scale))
|
||||
painter.setFont(font)
|
||||
pen = QPen(QColor("#9782ff"))
|
||||
painter.setPen(pen)
|
||||
painter.drawText(
|
||||
QRect(0, -50, 960, 540),
|
||||
int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter),
|
||||
Splash.COPYRIGHT_STR,
|
||||
)
|
||||
# Version
|
||||
pen = QPen(QColor("#809782ff"))
|
||||
painter.setPen(pen)
|
||||
painter.drawText(
|
||||
QRect(0, -25, 960, 540),
|
||||
int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter),
|
||||
Splash.VERSION_STR,
|
||||
)
|
||||
|
||||
case Splash.SPLASH_GOO_GEARS:
|
||||
# Copyright
|
||||
font = painter.font()
|
||||
font.setPointSize(math.floor(22 * point_size_scale))
|
||||
painter.setFont(font)
|
||||
pen = QPen(QColor("#9782ff"))
|
||||
painter.setPen(pen)
|
||||
painter.drawText(
|
||||
QRect(40, 450, 960, 540),
|
||||
Splash.COPYRIGHT_STR,
|
||||
)
|
||||
# Version
|
||||
font = painter.font()
|
||||
font.setPointSize(math.floor(22 * point_size_scale))
|
||||
painter.setFont(font)
|
||||
pen = QPen(QColor("#809782ff"))
|
||||
painter.setPen(pen)
|
||||
painter.drawText(
|
||||
QRect(40, 475, 960, 540),
|
||||
Splash.VERSION_STR,
|
||||
)
|
||||
|
||||
case Splash.SPLASH_95:
|
||||
# Copyright
|
||||
font = QFont()
|
||||
font.setFamily("Times")
|
||||
font.setPointSize(math.floor(22 * point_size_scale))
|
||||
font.setWeight(QFont.Weight.DemiBold)
|
||||
font.setStyleHint(QFont.StyleHint.Serif)
|
||||
painter.setFont(font)
|
||||
pen = QPen(QColor("#000000"))
|
||||
painter.setPen(pen)
|
||||
painter.drawText(
|
||||
QRect(88, -25, 960, 540),
|
||||
int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignLeft),
|
||||
Splash.COPYRIGHT_STR,
|
||||
)
|
||||
# Version
|
||||
font.setPointSize(math.floor(22 * point_size_scale))
|
||||
painter.setFont(font)
|
||||
pen = QPen(QColor("#AA2A0044"))
|
||||
painter.setPen(pen)
|
||||
painter.drawText(
|
||||
QRect(-30, 25, 960, 540),
|
||||
int(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignRight),
|
||||
Splash.VERSION_STR,
|
||||
)
|
||||
|
||||
case _:
|
||||
pass
|
||||
|
||||
pixmap.setDevicePixelRatio(self.ratio)
|
||||
pixmap = pixmap.scaledToWidth(
|
||||
math.floor(
|
||||
min(
|
||||
(self.screen_width * self.ratio) / 4,
|
||||
pixmap.width(),
|
||||
)
|
||||
),
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
|
||||
return pixmap
|
||||
|
||||
def _build_splash_screen(self):
|
||||
"""Build the internal splash screen."""
|
||||
self.splash_screen = QSplashScreen(self.get_pixmap(), Qt.WindowType.WindowStaysOnTopHint)
|
||||
|
||||
def show(self):
|
||||
"""Show the splash screen."""
|
||||
if not self.splash_screen:
|
||||
self._build_splash_screen()
|
||||
if self.splash_screen:
|
||||
self.splash_screen.show()
|
||||
|
||||
def finish(self, widget: QWidget):
|
||||
"""Hide the splash screen with this widget is finished displaying."""
|
||||
if self.splash_screen:
|
||||
self.splash_screen.finish(widget)
|
||||
@@ -25,7 +25,6 @@ from PySide6 import QtCore
|
||||
from PySide6.QtCore import QObject, QSettings, Qt, QThread, QThreadPool, QTimer, Signal
|
||||
from PySide6.QtGui import (
|
||||
QAction,
|
||||
QColor,
|
||||
QDragEnterEvent,
|
||||
QDragMoveEvent,
|
||||
QDropEvent,
|
||||
@@ -33,7 +32,6 @@ from PySide6.QtGui import (
|
||||
QGuiApplication,
|
||||
QIcon,
|
||||
QMouseEvent,
|
||||
QPixmap,
|
||||
)
|
||||
from PySide6.QtUiTools import QUiLoader
|
||||
from PySide6.QtWidgets import (
|
||||
@@ -46,7 +44,6 @@ from PySide6.QtWidgets import (
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
QScrollArea,
|
||||
QSplashScreen,
|
||||
QWidget,
|
||||
)
|
||||
from src.core.constants import (
|
||||
@@ -84,6 +81,7 @@ from src.qt.modals.fix_unlinked import FixUnlinkedEntriesModal
|
||||
from src.qt.modals.folders_to_tags import FoldersToTagsModal
|
||||
from src.qt.modals.tag_database import TagDatabasePanel
|
||||
from src.qt.resource_manager import ResourceManager
|
||||
from src.qt.splash import Splash
|
||||
from src.qt.translations import Translations
|
||||
from src.qt.widgets.item_thumb import BadgeType, ItemThumb
|
||||
from src.qt.widgets.migration_modal import JsonMigrationModal
|
||||
@@ -219,13 +217,6 @@ class QtDriver(DriverMixin, QObject):
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
app.setStyle("Fusion")
|
||||
# pal: QPalette = app.palette()
|
||||
# pal.setColor(QPalette.ColorGroup.Active,
|
||||
# QPalette.ColorRole.Highlight, QColor('#6E4BCE'))
|
||||
# pal.setColor(QPalette.ColorGroup.Normal,
|
||||
# QPalette.ColorRole.Window, QColor('#110F1B'))
|
||||
# app.setPalette(pal)
|
||||
# home_path = Path(__file__).parent / "ui/home.ui"
|
||||
icon_path = Path(__file__).parents[2] / "resources/icon.png"
|
||||
|
||||
# Handle OS signals
|
||||
@@ -243,23 +234,12 @@ class QtDriver(DriverMixin, QObject):
|
||||
self.main_window.dragMoveEvent = self.drag_move_event # type: ignore[method-assign]
|
||||
self.main_window.dropEvent = self.drop_event # type: ignore[method-assign]
|
||||
|
||||
splash_pixmap = QPixmap(":/images/splash.png")
|
||||
splash_pixmap.setDevicePixelRatio(self.main_window.devicePixelRatio())
|
||||
splash_pixmap = splash_pixmap.scaledToWidth(
|
||||
math.floor(
|
||||
min(
|
||||
(
|
||||
QGuiApplication.primaryScreen().geometry().width()
|
||||
* self.main_window.devicePixelRatio()
|
||||
)
|
||||
/ 4,
|
||||
splash_pixmap.width(),
|
||||
)
|
||||
),
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
self.splash: Splash = Splash(
|
||||
resource_manager=self.rm,
|
||||
screen_width=QGuiApplication.primaryScreen().geometry().width(),
|
||||
splash_name="", # TODO: Get splash name from config
|
||||
device_ratio=self.main_window.devicePixelRatio(),
|
||||
)
|
||||
self.splash = QSplashScreen(splash_pixmap, Qt.WindowType.WindowStaysOnTopHint)
|
||||
# self.splash.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||
self.splash.show()
|
||||
|
||||
if os.name == "nt":
|
||||
@@ -533,15 +513,7 @@ class QtDriver(DriverMixin, QObject):
|
||||
self.migration_modal: JsonMigrationModal = None
|
||||
|
||||
path_result = self.evaluate_path(str(self.args.open).lstrip().rstrip())
|
||||
# check status of library path evaluating
|
||||
if path_result.success and path_result.library_path:
|
||||
self.splash.showMessage(
|
||||
Translations.translate_formatted(
|
||||
"splash.opening_library", library_path=path_result.library_path
|
||||
),
|
||||
int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter),
|
||||
QColor("#9782ff"),
|
||||
)
|
||||
self.open_library(path_result.library_path)
|
||||
|
||||
# check ffmpeg and show warning if not
|
||||
|
||||
Reference in New Issue
Block a user