130 lines
4.4 KiB
Python
130 lines
4.4 KiB
Python
# -*- 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
|