# -*- 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