certflow.views.queue_panel 源代码

"""打印队列面板视图

以表格 + 工具栏形式展示打印队列,提供启动/暂停/继续、置顶、上移、
取消、清空等操作的 UI,并通过控制器信号实时刷新(无轮询阻塞)。

仅负责展示与交互,所有业务逻辑委托 PrintQueueController。
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from PySide6.QtWidgets import (
    QGroupBox,
    QLabel,
    QMessageBox,
    QTableWidgetItem,
    QVBoxLayout,
    QWidget,
)

from certflow.utils.logger import logger
from certflow.widgets import ActionToolbar
from certflow.widgets.enhanced_table import EnhancedTable

if TYPE_CHECKING:
    from certflow.controllers.print_queue_controller import PrintQueueController


[文档] class QueuePanel(QGroupBox): """打印队列面板 嵌入 PrintView,展示 print_jobs 队列并支持交互操作。 """ def __init__(self, controller: PrintQueueController, parent: QWidget | None = None) -> None: """初始化队列面板 Args: controller: 打印队列控制器 parent: 父控件 """ super().__init__("打印队列", parent) self._controller = controller # 传输控制条单独构建(启动/暂停/继续/置顶/上移/取消/清空已完成), # 由 PrintView 提升到顶部作为「主操作面」;QueuePanel 自身只留任务表格与状态。 self._transport_bar = self._build_transport_bar() self._build_ui() self._connect_signals() self._refresh() def _build_transport_bar(self) -> ActionToolbar: """构建传输控制条(启动/暂停/继续/置顶/上移/取消/清空已完成) 仅构建一次并返回控件;信号在此连接。由 PrintView 提升到顶部作主操作面, QueuePanel 自身不再内置该工具栏(仅保留任务表格与状态标签)。 """ toolbar = ActionToolbar() self.btn_start = toolbar.add_button("启动") self.btn_pause = toolbar.add_button("暂停") self.btn_resume = toolbar.add_button("继续") self.btn_top = toolbar.add_button("置顶") self.btn_up = toolbar.add_button("上移") self.btn_cancel = toolbar.add_button("取消") self.btn_clear = toolbar.add_button("清空已完成") for b in ( self.btn_start, self.btn_pause, self.btn_resume, self.btn_top, self.btn_up, self.btn_cancel, self.btn_clear, ): b.setMinimumHeight(26) toolbar.add_stretch() self.btn_start.clicked.connect(self._on_start) self.btn_pause.clicked.connect(self._on_pause) self.btn_resume.clicked.connect(self._on_resume) self.btn_top.clicked.connect(self._on_top) self.btn_up.clicked.connect(self._on_up) self.btn_cancel.clicked.connect(self._on_cancel) self.btn_clear.clicked.connect(self._on_clear) return toolbar @property def transport_bar(self) -> ActionToolbar: """暴露传输控制条,供 PrintView 提升到顶部主操作面。""" return self._transport_bar def _build_ui(self) -> None: """构建面板 UI(仅任务表格 + 状态;传输控制条已提升至 PrintView 顶部)""" layout = QVBoxLayout(self) layout.setContentsMargins(6, 8, 6, 6) layout.setSpacing(6) # 状态标签 self.status_label = QLabel("就绪") self.status_label.setStyleSheet("color: #666; font-size: 11px;") layout.addWidget(self.status_label) # 队列表格 self.table = EnhancedTable() self.table.setColumnCount(5) self.table.setHorizontalHeaderLabels(["ID", "SN", "状态", "优先级", "创建时间"]) for idx, field in enumerate(["id", "sn", "status", "priority", "created_at"]): self.table.set_header_field(idx, field) self.table.setColumnWidth(0, 50) self.table.setColumnWidth(1, 110) self.table.setColumnWidth(2, 70) self.table.setColumnWidth(3, 60) self.table.setColumnWidth(4, 150) self.table.setMaximumHeight(220) # 打印队列是全量本地数据,启用客户端排序 self.table.setSortingEnabled(True) layout.addWidget(self.table) def _connect_signals(self) -> None: """连接控制器信号,驱动实时刷新""" self._controller.queue_changed.connect(self._refresh) self._controller.job_started.connect(lambda _id: self._refresh()) self._controller.job_finished.connect(lambda *_a: self._refresh()) self._controller.status_changed.connect(self.status_label.setText) # ============================================================ # 刷新 # ============================================================ def _refresh(self) -> None: """从控制器拉取队列数据并渲染表格""" try: jobs = self._controller.list_jobs(include_terminal=True) except Exception as e: # noqa: BLE001 logger.warning(f"刷新队列失败: {e}") return self.table.setRowCount(len(jobs)) for row, job in enumerate(jobs): status = job["status"] self._set_cell(row, 0, str(job["id"])) self._set_cell(row, 1, job["sn"] or "—") self._set_cell( row, 2, self._controller.status_label(status), self._controller.status_color(status) ) self._set_cell(row, 3, str(job["priority"])) self._set_cell(row, 4, job["created_at"] or "—") self.table.resizeColumnsToContents() def _set_cell(self, row: int, col: int, text: str, color: str | None = None) -> None: """设置单元格文本与颜色""" item = QTableWidgetItem(text) if color: from PySide6.QtGui import QColor item.setForeground(QColor(color)) self.table.setItem(row, col, item) # ============================================================ # 操作 # ============================================================ def _selected_job_id(self) -> int | None: """返回当前选中行的任务 ID Returns: int | None: 任务 ID;无选中返回 None """ row = self.table.currentRow() if row < 0: return None item = self.table.item(row, 0) if item is None: return None try: return int(item.text()) except ValueError: return None def _on_start(self) -> None: self._controller.start() def _on_pause(self) -> None: self._controller.pause() def _on_resume(self) -> None: self._controller.resume() def _on_top(self) -> None: job_id = self._selected_job_id() if job_id is None: QMessageBox.information(self, "提示", "请先选择一行任务") return self._controller.move_top(job_id) def _on_up(self) -> None: job_id = self._selected_job_id() if job_id is None: QMessageBox.information(self, "提示", "请先选择一行任务") return self._controller.move_up(job_id) def _on_cancel(self) -> None: job_id = self._selected_job_id() if job_id is None: QMessageBox.information(self, "提示", "请先选择一行任务") return self._controller.cancel_job(job_id) def _on_clear(self) -> None: deleted = self._controller.clear_finished() self.status_label.setText(f"已清空 {deleted} 条已完成任务")