mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-02-01 07:39:10 +00:00
feat(ui): improved datetime field modal using QDateTimeEdit (#946)
* feat: custom modal making use of QDateTimeEdit * fix: add back license notice * refactor: remove unnecessary line * feat: use date and hour format from settings for date time picker
This commit is contained in:
@@ -79,7 +79,8 @@ class GlobalSettings(BaseModel):
|
||||
with open(path, "w") as f:
|
||||
toml.dump(self.model_dump(), f, encoder=TomlEnumEncoder())
|
||||
|
||||
def format_datetime(self, dt: datetime) -> str:
|
||||
@property
|
||||
def datetime_format(self) -> str:
|
||||
date_format = self.date_format
|
||||
is_24h = self.hour_format
|
||||
hour_format = "%H:%M:%S" if is_24h else "%I:%M:%S %p"
|
||||
@@ -94,5 +95,7 @@ class GlobalSettings(BaseModel):
|
||||
hour_format = hour_format.replace("%H", f"%{zero_padding_symbol}H").replace(
|
||||
"%I", f"%{zero_padding_symbol}I"
|
||||
)
|
||||
return f"{date_format}, {hour_format}"
|
||||
|
||||
return datetime.strftime(dt, f"{date_format}, {hour_format}")
|
||||
def format_datetime(self, dt: datetime) -> str:
|
||||
return datetime.strftime(dt, self.datetime_format)
|
||||
|
||||
@@ -37,7 +37,6 @@ from PySide6.QtGui import (
|
||||
QMouseEvent,
|
||||
QPalette,
|
||||
)
|
||||
from PySide6.QtUiTools import QUiLoader
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QFileDialog,
|
||||
@@ -298,8 +297,6 @@ class QtDriver(DriverMixin, QObject):
|
||||
|
||||
def start(self) -> None:
|
||||
"""Launch the main Qt window."""
|
||||
_ = QUiLoader()
|
||||
|
||||
if self.settings.theme == Theme.SYSTEM and platform.system() == "Windows":
|
||||
sys.argv += ["-platform", "windows:darkmode=2"]
|
||||
self.app = QApplication(sys.argv)
|
||||
|
||||
82
src/tagstudio/qt/widgets/datetime_picker.py
Normal file
82
src/tagstudio/qt/widgets/datetime_picker.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import typing
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime as dt
|
||||
from typing import cast
|
||||
|
||||
from PySide6.QtCore import QDateTime
|
||||
from PySide6.QtWidgets import QDateTimeEdit, QVBoxLayout
|
||||
|
||||
from tagstudio.qt.widgets.panel import PanelWidget
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from tagstudio.qt.ts_qt import QtDriver
|
||||
|
||||
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
|
||||
QDTF2DTF = {
|
||||
"%d": "dd",
|
||||
"%m": "MM",
|
||||
"%y": "yy",
|
||||
"%H": "HH",
|
||||
"%M": "mm",
|
||||
"%S": "ss",
|
||||
"%Y": "yyyy",
|
||||
"%I": "hh",
|
||||
"%p": "AP",
|
||||
"%x": "MM/dd/yy",
|
||||
}
|
||||
|
||||
|
||||
def qdtf2dtf(dtf: str) -> str:
|
||||
out = dtf
|
||||
for old, new in QDTF2DTF.items():
|
||||
out = out.replace(old, new)
|
||||
return out
|
||||
|
||||
|
||||
class DatetimePicker(PanelWidget):
|
||||
def __init__(self, driver: "QtDriver", datetime: dt | str):
|
||||
super().__init__()
|
||||
self.root_layout = QVBoxLayout(self)
|
||||
self.root_layout.setContentsMargins(6, 0, 6, 0)
|
||||
|
||||
if isinstance(datetime, str):
|
||||
datetime = DatetimePicker.string2dt(datetime)
|
||||
self.datetime_edit = QDateTimeEdit()
|
||||
self.datetime_edit.setCalendarPopup(True)
|
||||
self.datetime_edit.setDateTime(DatetimePicker.dt2qdt(datetime))
|
||||
# sketchy way to show seconds without showing the day of the week;
|
||||
# while also still having localisation
|
||||
self.datetime_edit.setDisplayFormat(qdtf2dtf(driver.settings.datetime_format))
|
||||
|
||||
self.initial_value = datetime
|
||||
self.root_layout.addWidget(self.datetime_edit)
|
||||
|
||||
def get_content(self):
|
||||
return DatetimePicker.dt2string(DatetimePicker.qdt2dt(self.datetime_edit.dateTime()))
|
||||
|
||||
def reset(self):
|
||||
self.datetime_edit.setDateTime(DatetimePicker.dt2qdt(self.initial_value))
|
||||
|
||||
def add_callback(self, callback: Callable, event: str = "returnPressed"):
|
||||
if event == "returnPressed":
|
||||
pass
|
||||
else:
|
||||
raise ValueError(f"unknown event type: {event}")
|
||||
|
||||
@staticmethod
|
||||
def qdt2dt(qdt: QDateTime) -> dt:
|
||||
return cast(dt, qdt.toPython())
|
||||
|
||||
@staticmethod
|
||||
def dt2qdt(datetime: dt) -> QDateTime:
|
||||
return QDateTime.fromSecsSinceEpoch(int(datetime.timestamp()))
|
||||
|
||||
@staticmethod
|
||||
def string2dt(datetime_str: str) -> dt:
|
||||
return dt.strptime(datetime_str, DATETIME_FORMAT)
|
||||
|
||||
@staticmethod
|
||||
def dt2string(datetime: dt) -> str:
|
||||
return dt.strftime(datetime, DATETIME_FORMAT)
|
||||
@@ -33,6 +33,7 @@ from tagstudio.core.library.alchemy.fields import (
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.models import Entry, Tag
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.widgets.datetime_picker import DatetimePicker
|
||||
from tagstudio.qt.widgets.fields import FieldContainer
|
||||
from tagstudio.qt.widgets.panel import PanelModal
|
||||
from tagstudio.qt.widgets.tag_box import TagBoxWidget
|
||||
@@ -390,21 +391,23 @@ class FieldContainers(QWidget):
|
||||
if not is_mixed:
|
||||
container.set_title(field.type.name)
|
||||
container.set_inline(False)
|
||||
|
||||
title = f"{field.type.name} (Date)"
|
||||
try:
|
||||
title = f"{field.type.name} (Date)"
|
||||
assert field.value is not None
|
||||
text = self.driver.settings.format_datetime(
|
||||
dt.strptime(field.value or "", "%Y-%m-%d %H:%M:%S")
|
||||
DatetimePicker.string2dt(field.value)
|
||||
)
|
||||
except ValueError:
|
||||
title = f"{field.type.name} (Date) (Unknown Format)"
|
||||
except (ValueError, AssertionError):
|
||||
title += " (Unknown Format)"
|
||||
text = str(field.value)
|
||||
|
||||
inner_widget = TextWidget(title, text)
|
||||
container.set_inner_widget(inner_widget)
|
||||
|
||||
modal = PanelModal( # TODO Replace with proper date picker including timezone etc.
|
||||
EditTextLine(field.value),
|
||||
title=f"Edit {field.type.name} in 'YYYY-MM-DD HH:MM:SS' format",
|
||||
modal = PanelModal(
|
||||
DatetimePicker(self.driver, field.value or dt.now()),
|
||||
title=f"Edit {field.type.name}",
|
||||
window_title=f"Edit {field.type.name}",
|
||||
save_callback=(
|
||||
lambda content: (
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
|
||||
# Licensed under the GPL-3.0 License.
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
from PySide6.QtWidgets import QLineEdit, QVBoxLayout
|
||||
|
||||
Reference in New Issue
Block a user