Files
haha/gui_models.py

130 lines
4.4 KiB
Python
Raw Normal View History

2026-01-31 10:42:28 +08:00
# -*- coding: utf-8 -*-
"""表格数据模型与代理ConfigTableModel、TableActionDelegate。"""
from PyQt5.QtWidgets import (
QApplication,
QStyle,
QStyledItemDelegate,
QStyleOptionProgressBar,
QStyleOptionButton,
)
from PyQt5.QtCore import Qt, QAbstractTableModel, QModelIndex
from gui_constants import COLUMN_KEY_MAPPING, COLUMN_KEY_MAPPING_EDIT, MODEL_VIEW_HEADERS
class ConfigTableModel(QAbstractTableModel):
"""大数据量表格模型"""
def __init__(self, configs, headers, parent=None):
super().__init__(parent)
self._configs = configs
self._headers = headers if headers is not None else MODEL_VIEW_HEADERS
def rowCount(self, parent=QModelIndex()):
return len(self._configs)
def columnCount(self, parent=QModelIndex()):
return len(self._headers)
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
row = index.row()
col = index.column()
config = self._configs[row]
key = COLUMN_KEY_MAPPING.get(col, "")
if role == Qt.DisplayRole:
return str(config.get(key, "")) if key else ""
elif role == Qt.TextAlignmentRole:
return Qt.AlignCenter
elif role == Qt.ToolTipRole:
if col in (5, 8) and key:
return str(config.get(key, ""))
return None
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role != Qt.DisplayRole:
return None
if orientation == Qt.Horizontal:
return self._headers[section]
return section + 1
def flags(self, index):
if not index.isValid():
return Qt.NoItemFlags
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
def setData(self, index, value, role=Qt.EditRole):
if role != Qt.EditRole or not index.isValid():
return False
row = index.row()
col = index.column()
key = COLUMN_KEY_MAPPING_EDIT.get(col, "")
if not key:
return False
self._configs[row][key] = str(value)
self.dataChanged.emit(index, index, [Qt.DisplayRole])
return True
def removeRows(self, row, count, parent=QModelIndex()):
if row < 0 or row + count > len(self._configs):
return False
self.beginRemoveRows(parent, row, row + count - 1)
del self._configs[row:row + count]
self.endRemoveRows()
return True
def update_data(self, configs):
"""更新内部数据并刷新视图"""
self.beginResetModel()
self._configs = configs
self.endResetModel()
class TableActionDelegate(QStyledItemDelegate):
"""Model/View 操作列 + 进度列 delegate"""
def __init__(self, parent, on_delete):
super().__init__(parent)
self.on_delete = on_delete
def paint(self, painter, option, index):
if index.column() == 9:
status_text = index.sibling(index.row(), 7).data() or ""
value = 0
if "完成" in status_text or "成功" in status_text:
value = 100
elif "执行中" in status_text or "进行" in status_text:
value = 60
elif "" in status_text:
value = 10
bar = QStyleOptionProgressBar()
bar.rect = option.rect.adjusted(6, option.rect.height() // 3, -6, -option.rect.height() // 3)
bar.minimum = 0
bar.maximum = 100
bar.progress = value
bar.textVisible = False
QApplication.style().drawControl(QStyle.CE_ProgressBar, bar, painter)
return
if index.column() == 10:
rect = option.rect
delete_rect = rect.adjusted(6, 4, -6, -4)
delete_btn = QStyleOptionButton()
delete_btn.rect = delete_rect
delete_btn.text = "删除"
QApplication.style().drawControl(QStyle.CE_PushButton, delete_btn, painter)
return
super().paint(painter, option, index)
def editorEvent(self, event, model, option, index):
if index.column() != 10:
return super().editorEvent(event, model, option, index)
if event.type() == event.MouseButtonRelease:
rect = option.rect
delete_rect = rect.adjusted(6, 4, -6, -4)
if delete_rect.contains(event.pos()):
self.on_delete(index)
return True
return False