history.py
3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""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"),
}
@Slot(str, result=str)
def thumbnailPath(self, timestamp: str) -> str:
"""返回该 timestamp 缩略图本地路径(按需生成)。源图缺失时返回 ""。"""
item = self._history.load_history_item_fast(timestamp)
if item is None or not item.generated_image_path.exists():
return ""
thumb = self._history.get_or_create_thumbnail(item.generated_image_path)
if thumb is None:
# 缩略图生成失败时回退到原图(QML Image 读 PNG 没问题)
return str(item.generated_image_path)
return str(thumb)