相机表格优化

1) 指定单元格“单击即可编辑”

做法(核心思想)

  • 整表默认不可编辑;
  • 仅对 ScanCode 列(COL_SCAN) 设置可编辑标志 + 自定义委托;
  • 捕获 QTableView::clicked,如果点的是 COL_SCAN,就主动调用 edit(idx) 进入编辑器。

对应代码位置

  • 初始化表格:整表先关闭编辑(NoEditTriggers),然后只给 COL_SCAN 单元格加 Qt::ItemIsEditable 标志GigeAndUsbCameraInfoBurningNew。
    • initTableView() 里:ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);随后在 fillTable() 里逐格设置:if (c == COL_SCAN) it->setFlags((it->flags() | Qt::ItemIsEditable) & ~Qt::ItemIsUserCheckable);GigeAndUsbCameraInfoBurningNew
  • 绑定单击即编辑:
    connect(ui->tableView, &QTableView::clicked, this, [=](const QModelIndex& idx) {
        if (!idx.isValid()) return;
        if (idx.column() == COL_SCAN) {
            ui->tableView->edit(idx); // 手动开启编辑,仅此一列
        }
    });
    

    代码在 initTableView() 中GigeAndUsbCameraInfoBurningNew

  • COL_SCAN 使用自定义委托 ValidatingScanDelegate,该委托只对指定列创建 QLineEdit 编辑器(createEditor 判断列号)GigeAndUsbCameraInfoBurningNewGigeAndUsbCameraInfoBurningNew。

为什么能满足“单击可编辑”

  • Qt 默认进入编辑通常需要双击或特定触发器;你把触发器关掉,改为在 clicked 信号中手动调用 edit(),从而达到单击即编辑的体验GigeAndUsbCameraInfoBurningNew。

可扩展/注意事项

  • 如需让其他列也“单击编辑”,在 clicked 槽里增加对应列判断;或把触发逻辑放到自定义委托的 editorEvent 中统一管理GigeAndUsbCameraInfoBurningNew。
  • NoEditTriggers 保证只有你定义的路径能进入编辑,避免误触。

2) 在特定单元格里按 Return/Tab 自动跳下一行

做法(核心思想)

  • ValidatingScanDelegate 的 createEditor() 返回 QLineEdit,并对其绑定:
    • returnPressed:回车时清理条码枪附带的 \r/\ncommitData → closeEditor,并自定义发出 scanNextRequested(index)
    • textChanged:兜底,如果扫码直接带了 \r/\n(没按回车),同样提交并触发跳转。
  • 主窗体连接 scanNextRequested定位到下一行同一列,清空下个单元格并进入编辑。

对应代码位置

  • 委托创建编辑器并绑定回车逻辑(节选):
    QObject::connect(ed, &QLineEdit::returnPressed, ed, [this, ed, index]() {
        QString t = ed->text();
        while (!t.isEmpty() && (t.endsWith('\n') || t.endsWith('\r'))) t.chop(1);
        { QSignalBlocker b(ed); ed->setText(t); }
        emit commitData(ed);
        emit closeEditor(ed, QAbstractItemDelegate::NoHint);
        emit scanNextRequested(index);
    });
    

    同时对 textChanged 做了带回车的兜底提交与跳转GigeAndUsbCameraInfoBurningNew。

  • 主窗体里响应跳转:
    connect(scanDelegate, &ValidatingScanDelegate::scanNextRequested, this, [=](const QModelIndex& idx) {
        const int nextRow = idx.row() + 1;
        if (nextRow >= m_model->rowCount()) return;
        const QModelIndex nextIdx = m_model->index(nextRow, COL_SCAN);
        if (!nextIdx.isValid()) return;
        m_model->setData(nextIdx, QString(), Qt::EditRole);       // 自动清空下一行
        ui->tableView->setCurrentIndex(nextIdx);
        ui->tableView->scrollTo(nextIdx, QAbstractItemView::PositionAtCenter);
        ui->tableView->edit(nextIdx);                              // 直接进入编辑
    });
    

    代码在 initTableView() 中GigeAndUsbCameraInfoBurningNew。

  • 另外,委托的 setModelData() 会把解析结果(SN/GUID/MAC/类型/是否OK)写入多种自定义 Role(RoleScanOk/RoleScanSN/...),并根据合法性把文字颜色置红或清除红色,这保证了边录入边校验GigeAndUsbCameraInfoBurningNew。

为什么能满足“回车/Tab自动跳转”

  • 你没有依赖 QAbstractItemView 的默认“导航”,而是在委托里自己定义了“提交→关闭→发跳转信号”的流程,主窗体再把焦点切到下一行 COL_SCAN 并启动编辑,形成连贯的录入链路GigeAndUsbCameraInfoBurningNew。
  • 如需 Tab 触发,同样可以在 eventFilter 或编辑器里拦截 Qt::Key_Tab,复用同一段提交/跳转逻辑。

可扩展/注意事项

  • 条码枪常带回车:你已经对 \r/\n 做了清理和兜底(returnPressed + textChanged 双路径),非常稳健GigeAndUsbCameraInfoBurningNew。
  • 跳转前做校验:你现在通过 validator 与解析函数 parseScanLine() 联动,非法则置红+tooltip,合法则写自定义 Role,后续写入前也再次校验GigeAndUsbCameraInfoBurningNew。

3) 自定义表头勾选框

做法(核心思想)

  • 使用自定义 CheckBoxHeader 作为 QTableView 的水平表头;
  • 表头被点击时,遍历每一行的第一列复选框,统一设置“选中/不选中”;
  • 行内复选框变化时,反向统计当前勾选数,把表头状态更新为两态(Checked/Unchecked)
  • 第一列单元格配合 CheckToggleDelegate,实现单击即切换(不用进入编辑器)。

对应代码位置

  • 设置表头、宽度和代理:
    • initTableView() 里创建 CheckBoxHeader 并 setHorizontalHeader(header)GigeAndUsbCameraInfoBurningNew;
    • 第一列 cell 设置 setCheckable(true)(在 fillTable())GigeAndUsbCameraInfoBurningNew;
    • COL_CHECK 列绑定 CheckToggleDelegate,在 editorEvent 里处理鼠标释放切换 Qt::CheckStateRoleGigeAndUsbCameraInfoBurningNew。
  • 点击表头 → 全选/全不选:
    connect(header, &CheckBoxHeader::checkBoxClicked, ui->tableView, [=](Qt::CheckState state) {
        m_isClearingChecksProgrammatically = true;
        Qt::CheckState target = (state == Qt::Checked) ? Qt::Checked : Qt::Unchecked;
        for (int r = 0; r < m_model->rowCount(); ++r) {
            auto* item = m_model->item(r, 0);
            if (item) item->setCheckState(target);
        }
        m_isClearingChecksProgrammatically = false;
    });
    

    GigeAndUsbCameraInfoBurningNew

  • 行勾选变化 → 更新表头,两态逻辑(不使用半选):
    • 在 initTableView() 和 fillTable() 里都对 itemChanged 做了统计并 header->setCheckState(st);你的版本特意把“半选”去掉,只保留 Checked / Unchecked(代码注释里也写了“✅ 仅两态”)GigeAndUsbCameraInfoBurningNew。
  • 防抖/防回环:
    • 用 m_isClearingChecksProgrammatically 标记程序批量改勾选的阶段,避免 itemChanged 再次触发计算/递归GigeAndUsbCameraInfoBurningNew。

为什么能满足“表头勾选”

  • 复选框不属于默认表头的一部分,必须自绘 + 自定义事件;你通过 CheckBoxHeader 承担“画 + 点击”的工作,通过两条连接完成“表头 → 行”和“行 → 表头”的状态同步,从而实现真正两态的全选/全不选GigeAndUsbCameraInfoBurningNew。
  • 行内切换由 CheckToggleDelegate 在鼠标释放时直接翻转 CheckStateRole,手感更快、更直觉GigeAndUsbCameraInfoBurningNew。

可扩展/注意事项

  • 若后续需要“全选(仅显示设备)”“全选(仅GEV)”等复杂选择,可把表头改为菜单 + 复选框组合。
  • 当前去掉了半选态,更简单;如需恢复半选,在统计勾选数时把“0 < checked < rowCount”映射为 Qt::PartiallyChecked 即可。

额外细节(和体验强相关)

  • 编辑/勾选时自动暂停“自动刷新”
    • 开始编辑 ScanCode → scanEditStarted 检测到后 autoRefreshCheckBox->setChecked(false)(触发 onStartAuto(Qt::Unchecked));
    • 行内勾选变化、或 itemChanged 触发时,也会关闭自动刷新,避免 UI 被刷新打断当前操作GigeAndUsbCameraInfoBurningNew。
    • 自动刷新开/关对应 USBDeviceUpdater/GigeDeviceUpdater 的 startAutoRefresh/stopAutoRefresh 调用GigeAndUsbCameraInfoBurningNew。
  • 录入校验即时反馈
    • setModelData() 用 parseScanLine() 解析扫码内容并写入多种 Role,再根据 ok 与否设置文字变红和 tooltip 提示,这样用户当下就能看到问题GigeAndUsbCameraInfoBurningNew。

小结(给“为什么这套组合好用”的一句话)

  • 你把“进入编辑(单击) → 输入(条码枪也稳) → 回车自动跳 → 即时校验提示”串成一条闭环,同时又把“批量选择(表头两态复选)”做成一键操作;并且在用户动手时暂停自动刷新,体验上非常贴近生产线的快速录入/批量写入需求。这三块设计互相咬合,没有互相打架,是一套非常实用的表格交互方案GigeAndUsbCameraInfoBurningNewGigeAndUsbCameraInfoBurningNew。
阅读剩余
THE END