feat: 优化任务表批量选择和详情入口 (#131)
This commit is contained in:
+80
-25
@@ -12,7 +12,6 @@
|
||||
并用 `remote_task_id` 作为任务身份。**不要用表格行号**——
|
||||
行号会随排序和筛选变化,拿它当身份必出错。
|
||||
- 不要为每个单元格创建常驻 `QWidget`(几万行会把内存吃光)。
|
||||
“详情”那一列用委托绘制。
|
||||
- 不要把完整的 `pdd_data` 放进表格模型,那是详情数据,
|
||||
查看详情时再从数据库读。
|
||||
- 表格数据只增不减(已完成任务永久保留),所以增量加载是必须的,
|
||||
@@ -57,7 +56,6 @@ from qfluentwidgets import (
|
||||
|
||||
PAGE_SIZE = 50
|
||||
CHECK_COLUMN = 0
|
||||
DETAIL_COLUMN = 9
|
||||
REMOTE_TASK_ID_ROLE = Qt.UserRole + 1
|
||||
|
||||
|
||||
@@ -94,7 +92,6 @@ class TaskTableModel(QAbstractTableModel):
|
||||
"数量",
|
||||
"状态",
|
||||
"更新时间",
|
||||
"操作",
|
||||
)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
@@ -136,7 +133,7 @@ class TaskTableModel(QAbstractTableModel):
|
||||
return self._display_value(task, column)
|
||||
|
||||
if role == Qt.TextAlignmentRole:
|
||||
if column in (0, 1, 7, 9):
|
||||
if column in (0, 1, 7):
|
||||
return Qt.AlignCenter
|
||||
if column in (5, 6):
|
||||
return Qt.AlignRight | Qt.AlignVCenter
|
||||
@@ -145,8 +142,6 @@ class TaskTableModel(QAbstractTableModel):
|
||||
if role == Qt.ToolTipRole:
|
||||
if column == 2:
|
||||
return task.title or "尚未获取标题"
|
||||
if column == DETAIL_COLUMN:
|
||||
return f"查看任务 {task.remote_task_id} 的详情"
|
||||
|
||||
if role == REMOTE_TASK_ID_ROLE:
|
||||
return task.remote_task_id
|
||||
@@ -295,6 +290,36 @@ class TaskTableModel(QAbstractTableModel):
|
||||
)
|
||||
self.checkedTaskIdsChanged.emit(())
|
||||
|
||||
def check_all_loaded_tasks(self) -> None:
|
||||
"""勾选当前已经加载的真实任务,不包含占位行和后续分页。"""
|
||||
|
||||
loaded_task_ids = {task.remote_task_id for task in self._rows}
|
||||
if not loaded_task_ids or loaded_task_ids.issubset(
|
||||
self._checked_task_ids
|
||||
):
|
||||
return
|
||||
self._checked_task_ids.update(loaded_task_ids)
|
||||
self._emit_loaded_check_state_changed()
|
||||
|
||||
def invert_loaded_task_checks(self) -> None:
|
||||
"""反选当前已经加载的真实任务,不改变尚未加载的勾选。"""
|
||||
|
||||
loaded_task_ids = {task.remote_task_id for task in self._rows}
|
||||
if not loaded_task_ids:
|
||||
return
|
||||
self._checked_task_ids.symmetric_difference_update(loaded_task_ids)
|
||||
self._emit_loaded_check_state_changed()
|
||||
|
||||
def _emit_loaded_check_state_changed(self) -> None:
|
||||
"""批量勾选后一次刷新复选框列和已选数量。"""
|
||||
|
||||
self.dataChanged.emit(
|
||||
self.index(0, CHECK_COLUMN),
|
||||
self.index(len(self._rows) - 1, CHECK_COLUMN),
|
||||
[Qt.CheckStateRole],
|
||||
)
|
||||
self.checkedTaskIdsChanged.emit(self.checked_task_ids())
|
||||
|
||||
def uncheck_tasks(self, remote_task_ids: Iterable[str]) -> None:
|
||||
"""取消指定任务的勾选,供批量操作成功后更新界面。"""
|
||||
|
||||
@@ -356,7 +381,7 @@ class TaskTableModel(QAbstractTableModel):
|
||||
return task.status
|
||||
if column == 8:
|
||||
return task.updated_at or "—"
|
||||
return "详情"
|
||||
return ""
|
||||
|
||||
|
||||
class PDDTaskPage(QWidget):
|
||||
@@ -423,6 +448,18 @@ class PDDTaskPage(QWidget):
|
||||
self.refreshButton = PushButton(FIF.SYNC, "刷新", self)
|
||||
self.refreshButton.setAccessibleName("刷新本地任务表格")
|
||||
|
||||
self.selectAllButton = PushButton("全选", self)
|
||||
self.selectAllButton.setAccessibleName("勾选当前已加载的全部任务")
|
||||
self.selectAllButton.setToolTip("勾选当前已加载的全部任务")
|
||||
self.selectAllButton.setEnabled(False)
|
||||
|
||||
self.invertSelectionButton = PushButton("反选", self)
|
||||
self.invertSelectionButton.setAccessibleName(
|
||||
"反选当前已加载的任务"
|
||||
)
|
||||
self.invertSelectionButton.setToolTip("反选当前已加载的任务")
|
||||
self.invertSelectionButton.setEnabled(False)
|
||||
|
||||
self.rerunButton = PushButton(FIF.UPDATE, "重新执行", self)
|
||||
self.rerunButton.setAccessibleName("重新执行当前选中的采集任务")
|
||||
self.rerunButton.setEnabled(False)
|
||||
@@ -452,7 +489,7 @@ class PDDTaskPage(QWidget):
|
||||
if compact == self._compact_commands and self.commandLayout.count():
|
||||
return
|
||||
self._compact_commands = compact
|
||||
for column in range(14):
|
||||
for column in range(16):
|
||||
self.commandLayout.setColumnStretch(column, 0)
|
||||
|
||||
if compact:
|
||||
@@ -462,16 +499,18 @@ class PDDTaskPage(QWidget):
|
||||
(self.taskTypeCombo, 0, 2, 1, 1),
|
||||
(self.statusLabelCaption, 0, 3, 1, 1),
|
||||
(self.statusCombo, 0, 4, 1, 1),
|
||||
(self.checkedCountLabel, 0, 6, 1, 1),
|
||||
(self.rerunButton, 0, 7, 1, 1),
|
||||
(self.resubmitButton, 0, 8, 1, 1),
|
||||
(self.removeButton, 0, 9, 1, 1),
|
||||
(self.keywordLabel, 1, 0, 1, 1),
|
||||
(self.keywordInput, 1, 1, 1, 5),
|
||||
(self.searchButton, 1, 6, 1, 1),
|
||||
(self.refreshButton, 1, 7, 1, 1),
|
||||
(self.selectAllButton, 2, 5, 1, 1),
|
||||
(self.invertSelectionButton, 2, 6, 1, 1),
|
||||
(self.checkedCountLabel, 2, 7, 1, 1),
|
||||
(self.rerunButton, 2, 8, 1, 1),
|
||||
(self.resubmitButton, 2, 9, 1, 1),
|
||||
(self.removeButton, 2, 10, 1, 1),
|
||||
)
|
||||
self.commandLayout.setColumnStretch(5, 1)
|
||||
self.commandLayout.setColumnStretch(4, 1)
|
||||
else:
|
||||
positions = (
|
||||
(self.autoFetchButton, 0, 0, 1, 1),
|
||||
@@ -483,10 +522,12 @@ class PDDTaskPage(QWidget):
|
||||
(self.keywordInput, 0, 6, 1, 1),
|
||||
(self.searchButton, 0, 7, 1, 1),
|
||||
(self.refreshButton, 0, 8, 1, 1),
|
||||
(self.checkedCountLabel, 0, 10, 1, 1),
|
||||
(self.rerunButton, 0, 11, 1, 1),
|
||||
(self.resubmitButton, 0, 12, 1, 1),
|
||||
(self.removeButton, 0, 13, 1, 1),
|
||||
(self.selectAllButton, 0, 10, 1, 1),
|
||||
(self.invertSelectionButton, 0, 11, 1, 1),
|
||||
(self.checkedCountLabel, 0, 12, 1, 1),
|
||||
(self.rerunButton, 0, 13, 1, 1),
|
||||
(self.resubmitButton, 0, 14, 1, 1),
|
||||
(self.removeButton, 0, 15, 1, 1),
|
||||
)
|
||||
self.commandLayout.setColumnStretch(6, 4)
|
||||
self.commandLayout.setColumnStretch(9, 1)
|
||||
@@ -500,6 +541,10 @@ class PDDTaskPage(QWidget):
|
||||
self.taskTable = TableView(self)
|
||||
self.taskTable.setModel(self.taskModel)
|
||||
self.taskTable.setAccessibleName("本机 PDD 任务列表")
|
||||
self.taskTable.setAccessibleDescription(
|
||||
"双击任务行或按 Enter 查看详情"
|
||||
)
|
||||
self.taskTable.setToolTip("双击任务行或按 Enter 查看详情")
|
||||
self.taskTable.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.taskTable.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.taskTable.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
||||
@@ -520,7 +565,6 @@ class PDDTaskPage(QWidget):
|
||||
self.taskTable.setColumnWidth(6, 70)
|
||||
self.taskTable.setColumnWidth(7, 116)
|
||||
self.taskTable.setColumnWidth(8, 156)
|
||||
self.taskTable.setColumnWidth(9, 72)
|
||||
self.taskTable.viewport().installEventFilter(self)
|
||||
|
||||
self.emptyStateCard = CardWidget(self)
|
||||
@@ -568,11 +612,16 @@ class PDDTaskPage(QWidget):
|
||||
self.searchButton.clicked.connect(self._apply_filters)
|
||||
self.keywordInput.returnPressed.connect(self._apply_filters)
|
||||
self.refreshButton.clicked.connect(self.refreshRequested.emit)
|
||||
self.selectAllButton.clicked.connect(
|
||||
self.taskModel.check_all_loaded_tasks
|
||||
)
|
||||
self.invertSelectionButton.clicked.connect(
|
||||
self.taskModel.invert_loaded_task_checks
|
||||
)
|
||||
self.rerunButton.clicked.connect(self._request_rerun)
|
||||
self.resubmitButton.clicked.connect(self._request_resubmit)
|
||||
self.removeButton.clicked.connect(self._request_remove)
|
||||
self.clearFiltersButton.clicked.connect(self.clear_filters)
|
||||
self.taskTable.clicked.connect(self._on_table_clicked)
|
||||
self.taskTable.activated.connect(self._on_table_activated)
|
||||
self.taskModel.checkedTaskIdsChanged.connect(self._update_checked_actions)
|
||||
|
||||
@@ -587,7 +636,7 @@ class PDDTaskPage(QWidget):
|
||||
def resizeEvent(self, event) -> None:
|
||||
"""按内容区宽度切换顶部命令区的单行或双行布局。"""
|
||||
|
||||
self._update_command_layout(compact=event.size().width() < 1180)
|
||||
self._update_command_layout(compact=event.size().width() < 1460)
|
||||
super().resizeEvent(event)
|
||||
|
||||
def set_tasks(self, tasks: Iterable[TaskRow]) -> None:
|
||||
@@ -596,6 +645,7 @@ class PDDTaskPage(QWidget):
|
||||
current_task_id = self.current_task_id()
|
||||
self.taskModel.set_tasks(tasks)
|
||||
self._update_empty_state()
|
||||
self._update_checked_actions(self.taskModel.checked_task_ids())
|
||||
self._restore_current_task(current_task_id)
|
||||
|
||||
def begin_task_reload(self) -> None:
|
||||
@@ -612,6 +662,7 @@ class PDDTaskPage(QWidget):
|
||||
|
||||
self.taskModel.append_tasks(tasks, has_more)
|
||||
self._update_empty_state()
|
||||
self._update_checked_actions(self.taskModel.checked_task_ids())
|
||||
task_id = getattr(self, "_task_id_before_reload", "")
|
||||
self._restore_current_task(task_id)
|
||||
self._task_id_before_reload = ""
|
||||
@@ -723,6 +774,8 @@ class PDDTaskPage(QWidget):
|
||||
self.rerunButton.setEnabled(True)
|
||||
self.resubmitButton.setEnabled(False)
|
||||
self.removeButton.setEnabled(False)
|
||||
self.selectAllButton.setEnabled(False)
|
||||
self.invertSelectionButton.setEnabled(False)
|
||||
self.autoFetchButton.setEnabled(False)
|
||||
return
|
||||
if self._rerun_state == "cancelling":
|
||||
@@ -731,13 +784,18 @@ class PDDTaskPage(QWidget):
|
||||
self.rerunButton.setEnabled(False)
|
||||
self.resubmitButton.setEnabled(False)
|
||||
self.removeButton.setEnabled(False)
|
||||
self.selectAllButton.setEnabled(False)
|
||||
self.invertSelectionButton.setEnabled(False)
|
||||
self.autoFetchButton.setEnabled(False)
|
||||
return
|
||||
|
||||
self.rerunButton.setText("重新执行")
|
||||
self.rerunButton.setAccessibleName("重新执行当前选中的采集任务")
|
||||
idle = not self._resubmit_running and not self._remove_running
|
||||
has_tasks = self.taskModel.data_row_count() > 0
|
||||
self.autoFetchButton.setEnabled(idle)
|
||||
self.selectAllButton.setEnabled(idle and has_tasks)
|
||||
self.invertSelectionButton.setEnabled(idle and has_tasks)
|
||||
self.rerunButton.setEnabled(
|
||||
idle and len(self.taskModel.checked_task_ids()) == 1
|
||||
)
|
||||
@@ -770,12 +828,9 @@ class PDDTaskPage(QWidget):
|
||||
}
|
||||
self.searchRequested.emit(filters)
|
||||
|
||||
def _on_table_clicked(self, index: QModelIndex) -> None:
|
||||
if index.column() == DETAIL_COLUMN:
|
||||
self._emit_detail(index)
|
||||
|
||||
def _on_table_activated(self, index: QModelIndex) -> None:
|
||||
self._emit_detail(index)
|
||||
if index.column() != CHECK_COLUMN:
|
||||
self._emit_detail(index)
|
||||
|
||||
def _emit_detail(self, index: QModelIndex) -> None:
|
||||
task = self.taskModel.row_at(index.row())
|
||||
|
||||
Reference in New Issue
Block a user