history.py 3.12 KB
"""HistoryBridge — 历史记录 tab 的列表 + 详情桥。

桥层暴露 HistoryListModel 给 QML ListView 直接用(已经是 QAbstractListModel)。
QML 通过 history.refresh() 主动刷新;新生成图片完成时由 ImageGenBridge 串起来调
itemAdded(task #16 wiring 时再补)。

详情查看走 getItem(timestamp) → dict,避免 QML 直接持有 HistoryItem dataclass。
"""
import logging
from typing import Any, Dict

from PySide6.QtCore import Property, QObject, Signal, Slot

from core.history import HistoryListModel
from ._icons import build_placeholder_icon


class HistoryBridge(QObject):
    countChanged = Signal()
    itemAdded = Signal(str)    # timestamp
    itemRemoved = Signal(str)  # timestamp

    def __init__(self, history_manager, parent=None):
        super().__init__(parent)
        self._logger = logging.getLogger(__name__)
        self._history = history_manager
        self._model = HistoryListModel(
            history_manager=history_manager,
            build_placeholder_icon=build_placeholder_icon,
            logger=self._logger,
            parent=self,
        )

    # ---- Properties -----------------------------------------------------

    @Property(QObject, constant=True)
    def model(self) -> HistoryListModel:
        """暴露给 QML ListView 的 model(QAbstractListModel)。"""
        return self._model

    @Property(int, notify=countChanged)
    def count(self) -> int:
        return self._model.rowCount()

    # ---- Slots ----------------------------------------------------------

    @Slot()
    def refresh(self) -> None:
        """从磁盘重读 index.json,整体 reset model。"""
        items = self._history.load_history_index()
        self._model.reset_timestamps([item.timestamp for item in items])
        self.countChanged.emit()
        self._logger.info(f"history refresh: {len(items)} 条")

    @Slot(str)
    def deleteItem(self, timestamp: str) -> None:
        ok = self._history.delete_history_item(timestamp)
        if ok:
            self._model.remove_timestamp(timestamp)
            self.itemRemoved.emit(timestamp)
            self.countChanged.emit()

    @Slot(str)
    def addNew(self, timestamp: str) -> None:
        """新生成完成时由 ImageGenBridge / 主线程调用,把新 timestamp 插到顶部。"""
        self._model.prepend_timestamp(timestamp)
        self.itemAdded.emit(timestamp)
        self.countChanged.emit()

    @Slot(str, result="QVariant")
    def getItem(self, timestamp: str) -> Dict[str, Any]:
        """返回单条历史的 QML 友好 dict(路径 → str / Path 不暴露)。"""
        item = self._history.load_history_item_fast(timestamp)
        if item is None:
            return {}
        return {
            "timestamp": item.timestamp,
            "prompt": item.prompt,
            "generatedImagePath": str(item.generated_image_path),
            "referenceImagePaths": [str(p) for p in item.reference_image_paths],
            "aspectRatio": item.aspect_ratio,
            "imageSize": item.image_size,
            "model": item.model,
            "createdAt": item.created_at.strftime("%Y-%m-%d %H:%M:%S"),
        }