Files
safe-marks-client/components/main_interface.py
Julian Freeman 516d9e4df3 add filter
2025-10-18 22:40:07 -04:00

163 lines
5.6 KiB
Python

from datetime import datetime, timezone
from zoneinfo import ZoneInfo
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QAbstractItemView
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt, QSortFilterProxyModel
from qfluentwidgets import (
PushButton, TableView, FluentIcon as Fi, PillPushButton
)
from common.utils import SAFE_MAP_ICON, SAFE_MAP
# dict[str, str | int] 就是
# {
# "ID": "xxx",
# "NAME": "zzz",
# "SAFE": -2,
# "UPDATE_DATA": "aaa",
# "NOTES": ""
# }
class ExtensionsDataTable(QAbstractTableModel):
def __init__(self, ext_data: list[dict[str, str | int]], parent=None):
super().__init__(parent)
self.ext_data = ext_data or []
self.headers = ["ID", "名称", "安全性", "更新日期", "备注"]
self.col_map = {
0: "ID",
1: "NAME",
2: "SAFE",
3: "UPDATE_DATE",
4: "NOTES",
}
def rowCount(self, /, parent = ...):
return len(self.ext_data)
def columnCount(self, /, parent = ...):
return len(self.headers)
def headerData(self, section: int, orientation: Qt.Orientation, /, role: int = ...):
if orientation == Qt.Orientation.Horizontal:
if role == Qt.ItemDataRole.DisplayRole:
return self.headers[section]
return None
def data(self, index: QModelIndex, /, role: int = ...):
row = index.row()
col = index.column()
ext = self.ext_data[row]
col_val = ext[self.col_map[col]]
if role == Qt.ItemDataRole.DisplayRole:
if col == 2:
return SAFE_MAP[col_val]
elif col == 3:
return self.iso2custom(col_val)
else:
return col_val
if role == Qt.ItemDataRole.DecorationRole:
if col == 2:
return SAFE_MAP_ICON[col_val]
# 一行的每一个列都返回所有数据,这样不管双击一行的哪一个位置,都可以获取到所有数据
if role == Qt.ItemDataRole.UserRole:
return ext
return None
def update_data(self, ext_data: list[dict[str, str | int]]):
self.beginResetModel()
self.ext_data.clear()
self.ext_data.extend(ext_data)
self.endResetModel()
@staticmethod
def iso2custom(iso_time: str) -> str:
dt = datetime.fromisoformat(iso_time)
dt = dt.replace(tzinfo=timezone.utc)
dt = dt.astimezone(ZoneInfo("America/New_York"))
custom_format = "%Y-%m-%d %H:%M:%S"
return dt.strftime(custom_format)
class SafeFilterProxyModel(QSortFilterProxyModel):
def __init__(self, parent=None):
super().__init__(parent)
self.accepted_status = set()
def set_accepted_status(self, status: list[int]):
self.accepted_status = set(status)
self.invalidateFilter()
def filterAcceptsRow(self, source_row: int, source_parent, /):
index = self.sourceModel().index(source_row, 2, source_parent)
ext_d: dict[str, str | int] = self.sourceModel().data(index, Qt.ItemDataRole.UserRole)
return ext_d["SAFE"] in self.accepted_status
class MainInterface(QWidget):
def __init__(self, ext_data: list[dict[str, str | int]] = None, parent=None):
super().__init__(parent)
self.setObjectName("main")
self.vly_m = QVBoxLayout()
self.setLayout(self.vly_m)
self.hly_top = QHBoxLayout()
self.pbn_refresh = PushButton(Fi.SYNC, "刷新", self)
self.pbn_add = PushButton(Fi.ADD, "添加", self)
self.pbn_delete = PushButton(Fi.DELETE, "删除", self)
safe_checks = [
("安全", 1), ("未知", 0), ("不安全", -1), ("未记录", -2),
]
self.safe_switches: list[PillPushButton] = []
for text, m in safe_checks:
c = PillPushButton(self)
c.setText(text)
c.setIcon(SAFE_MAP_ICON[m])
c.setProperty("mark", m)
c.toggled.connect(self.update_filter)
self.safe_switches.append(c)
self.hly_top.addWidget(self.pbn_refresh)
self.hly_top.addWidget(self.pbn_add)
self.hly_top.addWidget(self.pbn_delete)
self.hly_top.addStretch(1)
for c in self.safe_switches:
self.hly_top.addWidget(c)
self.vly_m.addLayout(self.hly_top)
self.tbv_m = TableView(self)
self.ext_model = ExtensionsDataTable(ext_data, self)
self.fil_model = SafeFilterProxyModel(self)
self.fil_model.setSourceModel(self.ext_model)
self.tbv_m.setModel(self.fil_model)
self.tbv_m.setSortingEnabled(True)
self.tbv_m.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
self.tbv_m.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) # 整行选中
self.tbv_m.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) # 只允许单选
self.tbv_m.verticalHeader().hide()
self.tbv_m.setBorderVisible(True)
self.tbv_m.setBorderRadius(4)
self.tbv_m.horizontalHeader().setStretchLastSection(True)
self.tbv_m.setColumnWidth(0, 250)
self.tbv_m.setColumnWidth(1, 200)
self.tbv_m.setColumnWidth(3, 180)
self.vly_m.addWidget(self.tbv_m)
# 放到最后
for c in self.safe_switches:
c.setChecked(True)
def update_filter(self, checked: bool):
accepted_status = []
for c in self.safe_switches:
if c.isChecked():
accepted_status.append(c.property("mark"))
self.fil_model.set_accepted_status(accepted_status)