163 lines
5.6 KiB
Python
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)
|