generated from dopt-python/py311
begin adding state management to new document selection
This commit is contained in:
+10
-10
@@ -25,6 +25,7 @@ from wce_crm.types import ConsultingType, EntityIds, EntityType, RecordingType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from wce_crm.gui import InitRecForm, Page_Consulting_ConsultingSession # noqa: F401
|
||||
from wce_crm.gui_components.initrec_file_selection import FileItemWidget
|
||||
from wce_crm.types import ConsId, EntityType, RecId, UserId
|
||||
|
||||
ValidAge = Annotated[int, Field(ge=0, le=99)]
|
||||
@@ -267,29 +268,28 @@ class Page_InitRec_Form_State(PageState[Module]):
|
||||
|
||||
|
||||
@dc.dataclass(slots=True, kw_only=True)
|
||||
class Page_InitRec_DocumentSection_State:
|
||||
class Page_InitRec_DocumentSection_State(PageState["FileItemWidget"]):
|
||||
session: Session
|
||||
pers_id: int | None
|
||||
doc_count: int = 0
|
||||
docs: list[Page_InitRec_DocumentSection_Doc_State] = dc.field(default_factory=list)
|
||||
locked: bool
|
||||
geloescht: bool = False
|
||||
|
||||
@property
|
||||
def doc_count(self) -> int:
|
||||
return len(self.docs)
|
||||
|
||||
|
||||
@dc.dataclass(slots=True, kw_only=True)
|
||||
class Page_InitRec_DocumentSection_Doc_State:
|
||||
class Page_InitRec_DocumentSection_Doc_State(PageState[Module]):
|
||||
session: Session
|
||||
id: int | None
|
||||
pers_id: int | None
|
||||
datum_erstellt_utc: datetime.datetime = dc.field(
|
||||
default_factory=lambda: datetime.datetime.now(datetime.UTC)
|
||||
)
|
||||
|
||||
speicher_pfad: Path
|
||||
dateiname_original: str
|
||||
dateigroesse: int | None
|
||||
speicher_pfad: Path | None
|
||||
dateiname_original: str = ""
|
||||
dateigroesse_bytes: int | None = None
|
||||
mime_type: str = ""
|
||||
anmerkung: str = ""
|
||||
locked: bool
|
||||
geloescht: bool = False
|
||||
|
||||
|
||||
+1
-2
@@ -130,7 +130,6 @@ if TYPE_CHECKING:
|
||||
)
|
||||
from wce_crm.types import (
|
||||
ConsId,
|
||||
ExtMaId,
|
||||
LinkingConsultationsEntryCompany,
|
||||
LinkingConsultationsEntryPerson,
|
||||
)
|
||||
@@ -5626,7 +5625,7 @@ class Page_Consulting_ConsultingSession(QWidget):
|
||||
self.btn_delete.setEnabled(False)
|
||||
else:
|
||||
unlock_form(self.widget_registry)
|
||||
self.btn_delete.setEnabled(False)
|
||||
self.btn_delete.setEnabled(True)
|
||||
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
self.STATE.zeitstempel = self.timestamp.get_pydatetime()
|
||||
|
||||
@@ -0,0 +1,616 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from pprint import pformat
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from PySide6.QtCore import (
|
||||
QFileInfo,
|
||||
QMimeDatabase,
|
||||
Qt,
|
||||
QUrl,
|
||||
Signal,
|
||||
)
|
||||
from PySide6.QtGui import QDesktopServices, QDragEnterEvent, QDropEvent
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QComboBox,
|
||||
QFileDialog,
|
||||
QFileIconProvider,
|
||||
QFrame,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QListWidget,
|
||||
QListWidgetItem,
|
||||
QMainWindow,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
import wce_crm.constants
|
||||
from wce_crm.constants import TIMEZONE_CEST
|
||||
from wce_crm.data_models import (
|
||||
Page_InitRec_DocumentSection_Doc_State,
|
||||
Page_InitRec_DocumentSection_State,
|
||||
Session,
|
||||
set_page_state,
|
||||
)
|
||||
from wce_crm.logging import logger_gui as logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from wce_crm.data_models import Module
|
||||
|
||||
DEBUG: bool = True
|
||||
if not wce_crm.constants.Config.DEVELOPMENT_STATE:
|
||||
DEBUG = False
|
||||
|
||||
|
||||
def get_mime_type(
|
||||
file_path: Path,
|
||||
) -> str:
|
||||
"""gets the MIME type of a file via PySide6 / QMimeDatabase"""
|
||||
db = QMimeDatabase()
|
||||
mime_type = db.mimeTypeForFile(str(file_path))
|
||||
|
||||
# .name() -> for example "application/pdf" or "image/png"
|
||||
return mime_type.name()
|
||||
|
||||
|
||||
def _format_file_size(
|
||||
size_in_bytes: int,
|
||||
) -> str:
|
||||
"""converts bytes in readable string, e.g. KB or MB"""
|
||||
if size_in_bytes < 1024:
|
||||
return f"{size_in_bytes} B"
|
||||
elif size_in_bytes < 1024 * 1024:
|
||||
return f"{size_in_bytes / 1024:.1f} KB"
|
||||
else:
|
||||
return f"{size_in_bytes / (1024 * 1024):.1f} MB"
|
||||
|
||||
|
||||
class _DropZoneWidget(QFrame):
|
||||
files_dropped = Signal(list) # sends list of pathlib.Path objects
|
||||
|
||||
STYLE_NORMAL = """
|
||||
_DropZoneWidget {
|
||||
border: 2px dashed #B0B0B0;
|
||||
border-radius: 8px;
|
||||
background-color: #F9F9F9;
|
||||
}
|
||||
_DropZoneWidget:hover {
|
||||
border-color: #0078D4;
|
||||
background-color: #F0F6FF;
|
||||
}
|
||||
"""
|
||||
|
||||
STYLE_DRAG_ACTIVE = """
|
||||
_DropZoneWidget {
|
||||
border: 2px dashed #0078D4;
|
||||
border-radius: 8px;
|
||||
background-color: #E1EFFA;
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: QWidget | None = None,
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.setAcceptDrops(True)
|
||||
self.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
|
||||
self.setStyleSheet(self.STYLE_NORMAL)
|
||||
self.setFixedHeight(60)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
self.label = QLabel(
|
||||
"📁 <b>Dateien hierher ziehen</b> oder <u>klicken</u> zum Auswählen"
|
||||
)
|
||||
self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
layout.addWidget(self.label)
|
||||
|
||||
def dragEnterEvent(
|
||||
self,
|
||||
event: QDragEnterEvent,
|
||||
):
|
||||
if event.mimeData().hasUrls():
|
||||
# check if MDB/explorer gives at least one real file (no folder)
|
||||
has_files = False
|
||||
for url in event.mimeData().urls():
|
||||
pfad = url.toLocalFile()
|
||||
if pfad and os.path.isfile(pfad):
|
||||
has_files = True
|
||||
break
|
||||
|
||||
if has_files:
|
||||
event.acceptProposedAction()
|
||||
self.setStyleSheet(self.STYLE_DRAG_ACTIVE)
|
||||
|
||||
def dragLeaveEvent(self, event):
|
||||
self.setStyleSheet(self.STYLE_NORMAL)
|
||||
|
||||
def dropEvent(self, event: QDropEvent):
|
||||
self.dragLeaveEvent(None)
|
||||
|
||||
valid_files: list[Path] = []
|
||||
for url in event.mimeData().urls():
|
||||
if url.isLocalFile():
|
||||
path = Path(url.toLocalFile()).resolve()
|
||||
# only add if it is a file
|
||||
if path.is_file():
|
||||
valid_files.append(path)
|
||||
|
||||
if valid_files:
|
||||
self.files_dropped.emit(valid_files)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
files, _ = QFileDialog.getOpenFileNames(self, "Dokumente auswählen")
|
||||
if files:
|
||||
files_pathlib = [Path(file).resolve() for file in files]
|
||||
self.files_dropped.emit(files_pathlib)
|
||||
|
||||
|
||||
class FileItemWidget(QFrame):
|
||||
request_remove = Signal(QWidget) # signal to parent to remove entry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
state: Page_InitRec_DocumentSection_Doc_State,
|
||||
parent: QWidget | None = None,
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.STATE = state
|
||||
|
||||
self.setStyleSheet("""
|
||||
_FileItemWidget {
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #E0E0E0;
|
||||
border-radius: 6px;
|
||||
padding: 6px;
|
||||
}
|
||||
_FileItemWidget:hover {
|
||||
border-color: #B0B0B0;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setContentsMargins(8, 6, 8, 6)
|
||||
|
||||
# file icon: use dummy to avoid icons with checkmarks (synchronised folder)
|
||||
self.icon_label = QLabel()
|
||||
self.icon_provider = QFileIconProvider()
|
||||
layout.addWidget(self.icon_label)
|
||||
|
||||
# file info: name, size, date, mime type
|
||||
info_vbox = QVBoxLayout()
|
||||
info_vbox.setSpacing(2)
|
||||
|
||||
self.file_size_display: str = ""
|
||||
|
||||
self.name_label = QLabel()
|
||||
self.meta_label = QLabel()
|
||||
self.meta_label.setStyleSheet("font-size: 11px;")
|
||||
|
||||
# additional note field for more comprehensible file descriptions
|
||||
self.note_edit = QLineEdit()
|
||||
self.note_edit.setPlaceholderText("Eigene Anmerkung / Bez. eingeben (optional)...")
|
||||
self.note_edit.setStyleSheet("""
|
||||
QLineEdit {
|
||||
font-size: 11px;
|
||||
padding: 2px 5px;
|
||||
min-height: 22px; /* avoid vertical cropping of text */
|
||||
border: 1px solid #CCCCCC;
|
||||
border-radius: 3px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border-color: #0078D4; /* blue frame on click */
|
||||
}
|
||||
""")
|
||||
|
||||
info_vbox.addWidget(self.name_label)
|
||||
info_vbox.addWidget(self.meta_label)
|
||||
info_vbox.addWidget(self.note_edit)
|
||||
layout.addLayout(info_vbox, stretch=2)
|
||||
|
||||
# category dropdown: not needed by WCE, but still included to re-enable later
|
||||
# TODO maybe add later
|
||||
self.category_combo = QComboBox()
|
||||
# self.category_combo.addItems(
|
||||
# [
|
||||
# "Allgemein / Unsortiert",
|
||||
# "Identitätsnachweis",
|
||||
# "Vertrag",
|
||||
# "Gewerbeanmeldung",
|
||||
# "Sonstiges",
|
||||
# ]
|
||||
# )
|
||||
# self.category_combo.setFixedWidth(160)
|
||||
layout.addWidget(self.category_combo)
|
||||
self.category_combo.setVisible(False)
|
||||
|
||||
# action buttons: view and delete
|
||||
self.btn_preview = QPushButton("👁️")
|
||||
self.btn_preview.setToolTip("Datei im Standardprogramm öffnen")
|
||||
self.btn_preview.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.btn_preview.setFixedWidth(36)
|
||||
self.btn_preview.clicked.connect(self._open_preview)
|
||||
|
||||
self.btn_delete = QPushButton("🗑️")
|
||||
self.btn_delete.setToolTip("Datei entfernen")
|
||||
self.btn_delete.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.btn_delete.setFixedWidth(36)
|
||||
self.btn_delete.clicked.connect(lambda: self.request_remove.emit(self))
|
||||
|
||||
layout.addWidget(self.btn_preview)
|
||||
layout.addWidget(self.btn_delete)
|
||||
|
||||
if DEBUG:
|
||||
btn_state = QPushButton("STATE")
|
||||
btn_state.setToolTip("Debug call...")
|
||||
btn_state.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
btn_state.clicked.connect(self.get_state)
|
||||
layout.addWidget(btn_state)
|
||||
|
||||
self._sync_state_to_GUI(None)
|
||||
|
||||
def _sync_state_to_GUI(
|
||||
self,
|
||||
section: Literal["locking", "content"] | None,
|
||||
) -> None:
|
||||
|
||||
if section is None or section == "content":
|
||||
created_at_string = self.STATE.datum_erstellt_utc.astimezone(
|
||||
TIMEZONE_CEST
|
||||
).strftime("%d.%m.%Y %H:%M")
|
||||
|
||||
file_icon = self.icon_provider.icon(
|
||||
QFileInfo(f"dummy{self.STATE.speicher_pfad.suffix}")
|
||||
)
|
||||
self.icon_label.setPixmap(file_icon.pixmap(28, 28))
|
||||
|
||||
self.STATE.dateiname_original = self.STATE.speicher_pfad.name
|
||||
self.STATE.mime_type = get_mime_type(self.STATE.speicher_pfad)
|
||||
|
||||
try:
|
||||
self.STATE.dateigroesse_bytes = self.STATE.speicher_pfad.stat().st_size
|
||||
self.file_size_display = _format_file_size(self.STATE.dateigroesse_bytes)
|
||||
except OSError:
|
||||
self.file_size_display = "Unbekannt"
|
||||
|
||||
self.name_label.setText(f"<b>{self.STATE.dateiname_original}</b>")
|
||||
self.meta_label.setText(
|
||||
(
|
||||
f"<span style='color: #666666;'>{self.file_size_display} • Hinzugefügt: "
|
||||
f"{created_at_string}</span>"
|
||||
)
|
||||
)
|
||||
self.note_edit.setText(self.STATE.anmerkung)
|
||||
|
||||
if section is None or section == "locking":
|
||||
if self.STATE.locked:
|
||||
self.btn_delete.setEnabled(False)
|
||||
self.note_edit.setReadOnly(True)
|
||||
else:
|
||||
self.btn_delete.setEnabled(True)
|
||||
self.note_edit.setReadOnly(False)
|
||||
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
self.STATE.anmerkung = self.note_edit.text().strip()
|
||||
|
||||
def _open_preview(self):
|
||||
QDesktopServices.openUrl(QUrl.fromLocalFile(self.STATE.speicher_pfad))
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
err_list: list[str] = []
|
||||
return err_list
|
||||
|
||||
def save_data(self) -> None:
|
||||
pass
|
||||
|
||||
def load_state(
|
||||
self,
|
||||
new_state: Page_InitRec_DocumentSection_Doc_State,
|
||||
) -> None:
|
||||
set_page_state(self.STATE, new_state)
|
||||
self._sync_state_to_GUI(None)
|
||||
|
||||
def lock(self) -> None:
|
||||
self.STATE.locked = True
|
||||
self._sync_state_to_GUI("locking")
|
||||
|
||||
def unlock(self) -> None:
|
||||
self.STATE.locked = False
|
||||
self._sync_state_to_GUI("locking")
|
||||
|
||||
def get_state(self) -> Page_InitRec_DocumentSection_Doc_State:
|
||||
self._sync_GUI_to_state()
|
||||
logger.debug(
|
||||
"[Module -- DocumentSelection] Current state of row:\n%s",
|
||||
pformat(self.STATE),
|
||||
)
|
||||
return self.STATE
|
||||
|
||||
|
||||
class DocumentSectionWidget(QWidget):
|
||||
"""main widget for file selection"""
|
||||
|
||||
count_changed = Signal(int) # sends current number of files contained by the widget
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
state: Page_InitRec_DocumentSection_State,
|
||||
parent: QWidget | None = None,
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.STATE = state
|
||||
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Dropzone at the top
|
||||
self.drop_zone = _DropZoneWidget()
|
||||
self.drop_zone.files_dropped.connect(self._add_files)
|
||||
main_layout.addWidget(self.drop_zone)
|
||||
|
||||
# list containing the file items
|
||||
self.file_list_widget = QListWidget()
|
||||
self.file_list_widget.setStyleSheet("""
|
||||
QListWidget {
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
QListWidget::item {
|
||||
padding: 0px;
|
||||
margin: 0px 0px 6px 0px; /* 6px Abstand zwischen den einzelnen Datei-Zeilen */
|
||||
}
|
||||
""")
|
||||
|
||||
main_layout.addWidget(self.file_list_widget)
|
||||
|
||||
def _sync_state_to_GUI(self, section: Literal["content", "locking"] | None) -> None:
|
||||
self.setUpdatesEnabled(False)
|
||||
|
||||
if section is None or section == "content":
|
||||
self.remove_all_rows()
|
||||
for doc in self.STATE.docs:
|
||||
if not doc.geloescht:
|
||||
self.add_row(doc, True)
|
||||
|
||||
self.count_changed.emit(self.STATE.doc_count)
|
||||
|
||||
if section is None or section == "locking":
|
||||
if self.STATE.locked:
|
||||
for m in self.STATE.child_modules:
|
||||
m.lock()
|
||||
else:
|
||||
for m in self.STATE.child_modules:
|
||||
m.unlock()
|
||||
|
||||
self.setUpdatesEnabled(True)
|
||||
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
logger.debug(
|
||||
(
|
||||
"[Module -- DocumentSelection] Document selection widget: Call "
|
||||
"syncing GUI to state"
|
||||
)
|
||||
)
|
||||
logger.debug(
|
||||
"[Module -- DocumentSelection] Document selection widget - Child modules: %s",
|
||||
pformat(self.STATE.child_modules),
|
||||
)
|
||||
|
||||
# Child modules are ACTIVE elements, viewable and changeable by the user.
|
||||
# The states are already part of this widget and linked by reference, but
|
||||
# the current user input must be synced with these states. Therefore, the
|
||||
# `get_state` method of these children is called, which implicitly updates
|
||||
# their state. The returned state reference is not needed.
|
||||
for m in self.STATE.child_modules:
|
||||
_ = m.get_state()
|
||||
|
||||
self.STATE.doc_count = self.file_list_widget.count()
|
||||
|
||||
def lock(self) -> None:
|
||||
self.STATE.locked = True
|
||||
self._sync_state_to_GUI("locking")
|
||||
|
||||
def unlock(self) -> None:
|
||||
self.STATE.locked = False
|
||||
self._sync_state_to_GUI("locking")
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
err_list: list[str] = []
|
||||
for m in self.STATE.child_modules:
|
||||
err_list.extend(m.validate())
|
||||
|
||||
return err_list
|
||||
|
||||
def load_state(
|
||||
self,
|
||||
new_state: Page_InitRec_DocumentSection_State,
|
||||
) -> None:
|
||||
set_page_state(self.STATE, new_state)
|
||||
self._sync_state_to_GUI(None)
|
||||
|
||||
def save_data(self) -> None:
|
||||
pass
|
||||
|
||||
def remove_all_rows(self) -> None:
|
||||
while self.STATE.child_modules:
|
||||
self._remove_row(self.STATE.child_modules[0])
|
||||
|
||||
def add_row(
|
||||
self,
|
||||
row_state: Page_InitRec_DocumentSection_Doc_State | None,
|
||||
state_exists: bool,
|
||||
) -> None:
|
||||
if row_state is None:
|
||||
new_state = Page_InitRec_DocumentSection_Doc_State(
|
||||
session=self.STATE.session,
|
||||
id=None,
|
||||
pers_id=self.STATE.pers_id,
|
||||
speicher_pfad=None,
|
||||
locked=self.STATE.locked,
|
||||
)
|
||||
|
||||
item_widget = FileItemWidget(new_state)
|
||||
item_widget.request_remove.connect(self._remove_row)
|
||||
# embed in QListWidget
|
||||
list_item = QListWidgetItem(self.file_list_widget)
|
||||
# min size to avoid cropping of text
|
||||
size_hint = item_widget.sizeHint()
|
||||
size_hint.setHeight(size_hint.height() + 4)
|
||||
list_item.setSizeHint(size_hint)
|
||||
|
||||
self.file_list_widget.addItem(list_item)
|
||||
self.file_list_widget.setItemWidget(list_item, item_widget)
|
||||
|
||||
self.STATE.child_modules.append(item_widget)
|
||||
if not state_exists:
|
||||
self.STATE.docs.append(new_state)
|
||||
|
||||
self.STATE.doc_count = self.file_list_widget.count()
|
||||
|
||||
def _add_files(
|
||||
self,
|
||||
filepaths: Iterable[Path],
|
||||
):
|
||||
for path in filepaths:
|
||||
# create custom row widget
|
||||
new_state = Page_InitRec_DocumentSection_Doc_State(
|
||||
session=self.STATE.session,
|
||||
id=None,
|
||||
pers_id=self.STATE.pers_id,
|
||||
speicher_pfad=path,
|
||||
locked=self.STATE.locked,
|
||||
)
|
||||
self.STATE.docs.append(new_state)
|
||||
item_widget = FileItemWidget(new_state)
|
||||
item_widget.request_remove.connect(self._remove_row)
|
||||
self.STATE.child_modules.append(item_widget)
|
||||
|
||||
# embed in QListWidget
|
||||
list_item = QListWidgetItem(self.file_list_widget)
|
||||
# min size to avoid cropping of text
|
||||
size_hint = item_widget.sizeHint()
|
||||
size_hint.setHeight(size_hint.height() + 4)
|
||||
list_item.setSizeHint(size_hint)
|
||||
|
||||
self.file_list_widget.addItem(list_item)
|
||||
self.file_list_widget.setItemWidget(list_item, item_widget)
|
||||
|
||||
self.STATE.doc_count = self.file_list_widget.count()
|
||||
self.count_changed.emit(self.file_list_widget.count())
|
||||
|
||||
def _remove_row(
|
||||
self,
|
||||
widget: QWidget,
|
||||
):
|
||||
# search and delete row
|
||||
for i in range(self.file_list_widget.count()):
|
||||
item = self.file_list_widget.item(i)
|
||||
if self.file_list_widget.itemWidget(item) == widget:
|
||||
assert isinstance(widget, FileItemWidget)
|
||||
self.file_list_widget.takeItem(i)
|
||||
self.STATE.child_modules.remove(widget)
|
||||
break
|
||||
# do not remove the corresponding state since it is tracked
|
||||
# to be post-processed when the database saving operation is triggered
|
||||
self.STATE.
|
||||
self.count_changed.emit())
|
||||
|
||||
def get_state(self) -> Page_InitRec_DocumentSection_State:
|
||||
self._sync_GUI_to_state()
|
||||
logger.debug(
|
||||
"[Module -- DocumentSelection] Current state of document selection widget:\n%s",
|
||||
pformat(self.STATE),
|
||||
)
|
||||
|
||||
return self.STATE
|
||||
|
||||
# TODO change to protocol/interface with state
|
||||
def get_all_documents_data(self):
|
||||
"""Methode zum Auslesen aller angehängten Dateien inkl. Metadaten für das Speichern."""
|
||||
documents = []
|
||||
for i in range(self.file_list_widget.count()):
|
||||
item = self.file_list_widget.item(i)
|
||||
widget = self.file_list_widget.itemWidget(item)
|
||||
if isinstance(widget, FileItemWidget):
|
||||
documents.append(widget.get_state())
|
||||
return documents
|
||||
|
||||
|
||||
class _MainWindow(QMainWindow):
|
||||
"""main window for internal test only, not the this module's component"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Prototyp: Dokumente & Anhänge")
|
||||
self.resize(650, 500)
|
||||
|
||||
central_widget = QWidget()
|
||||
layout = QVBoxLayout(central_widget)
|
||||
|
||||
# simulate custom widget in form
|
||||
self.header_label = QLabel("<b>📎 Dokumente & Anhänge (0)</b>")
|
||||
self.header_label.setStyleSheet("font-size: 14px; color: #333333;")
|
||||
layout.addWidget(self.header_label)
|
||||
|
||||
# custom widget
|
||||
self.SESSION = Session(user_id=42, user_name="TEST-USER")
|
||||
state = Page_InitRec_DocumentSection_State(
|
||||
session=self.SESSION, pers_id=None, locked=False
|
||||
)
|
||||
self.doc_section = DocumentSectionWidget(state)
|
||||
self.doc_section.count_changed.connect(self.update_header)
|
||||
layout.addWidget(self.doc_section)
|
||||
|
||||
# test button to trigger data gathering (simulation for saving procedure and
|
||||
# backend interaction)
|
||||
self.btn_save = QPushButton("Erfassung speichern (Daten auslesen)")
|
||||
self.btn_save.setStyleSheet(
|
||||
"background-color: #0078D4; color: white; padding: 8px; font-weight: bold;"
|
||||
)
|
||||
self.btn_save.clicked.connect(self.print_backend_data)
|
||||
layout.addWidget(self.btn_save)
|
||||
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
def update_header(self, count):
|
||||
"""update counter in corresponding label, must be mapped to section heading in form"""
|
||||
self.header_label.setText(f"<b>📎 Dokumente & Anhänge ({count})</b>")
|
||||
|
||||
def print_backend_data(self):
|
||||
data = self.doc_section.get_all_documents_data()
|
||||
print("\n--- DATEN FÜR SPEICHERVORGANG BEREIT ---")
|
||||
for i, doc in enumerate(data, 1):
|
||||
print(f"Datei {i}:")
|
||||
print(f" Pfad: {doc['file_path']}")
|
||||
print(f" Datum: {doc['created_at']}")
|
||||
print(f" Kategorie: {doc['category']}")
|
||||
print(f" Anmerkung: {doc['note']}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
app = QApplication(sys.argv)
|
||||
window = _MainWindow()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# enable static type checking for the protocol
|
||||
_m1: type[Module] = DocumentSectionWidget
|
||||
_m2: type[Module] = FileItemWidget
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user