当前位置: 首页 > news >正文

Qt表格组件封装与远程数据库连接:从数据展示到交互体验

文章目录

    • 效果图
    • 一、整体架构设计:基于MVC模式的分层实现
    • 二、核心组件解析
      • 1. 自定义委托:`GeneralDelegate`实现单元格交互
      • 2. 分页导航控件:`PageNavigator`的状态管理
      • 3. 表格页面封装:`QTablePages`的整合能力
      • 4. 远程数据模型:`RemoteTableModel`的数据处理(核心)
    • 三、实践经验与优化方向
    • 四、组件协作关系图
    • 五、总结

  • 上篇文章中使用到了QSqlTableModel来管理与处理数据库的操作,但这个模型有一个很大的局限,就是它只能处理本地数据库,不能处理远程数据库,所以便开发了下述远程模式的model。

效果图

在这里插入图片描述

一、整体架构设计:基于MVC模式的分层实现

本次分享的表格系统严格遵循Qt的MVC(Model-View-Controller)设计模式,各组件职责清晰:

  • 模型(Model)RemoteTableModel 负责数据的获取、存储和处理,与后端数据源交互
  • 视图(View)ToolTipTableView 负责数据的可视化展示
  • 委托(Delegate)GeneralDelegate 处理单元格的自定义渲染和用户交互
  • 控制器(Controller)QTablePages 整合上述组件,协调数据流转与用户操作

此外,PageNavigator 作为独立的分页控件,负责页码管理;LogManagement 则承担数据模型的统一初始化与管理。这种分层设计让各组件可独立维护、灵活复用。

二、核心组件解析

1. 自定义委托:GeneralDelegate实现单元格交互

表格中常需要在单元格中嵌入按钮等交互元素,GeneralDelegate 通过重写Qt委托的关键方法实现这一功能:

// 重写paint方法绘制删除按钮
void GeneralDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{int currentColumn = index.column();int columnCount = index.model()->columnCount();bool isLastColumn = (currentColumn == columnCount - 1);if (isLastColumn) {QRect buttonRect = option.rect.adjusted(2, 2, -2, -2);QStyleOptionButton buttonOption;buttonOption.rect = buttonRect;buttonOption.state |= QStyle::State_Enabled;buttonOption.palette.setBrush(QPalette::Button, QColor(Qt::red));buttonOption.text = "删除";painter->save();painter->setClipRect(buttonRect);QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter);painter->restore();} else {// 普通单元格渲染QStyleOptionViewItem options = option;initStyleOption(&options, index);options.displayAlignment = Qt::AlignCenter;QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &options, painter);}
}

同时,通过editorEvent处理按钮点击事件,并通过信号将交互传递给上层:

bool GeneralDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{if (event->type() == QEvent::MouseButtonRelease && isLastColumn) {QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);QRect buttonRect = option.rect.adjusted(2, 2, -2, -2);if (buttonRect.contains(mouseEvent->pos())) {emit deleteRequested(index);  // 发送删除信号return true;}}return QStyledItemDelegate::editorEvent(event, model, option, index);
}

这种实现方式既保持了Qt委托机制的灵活性,又能自定义复杂的单元格交互。

2. 分页导航控件:PageNavigator的状态管理

分页是大数据表格的必备功能

3. 表格页面封装:QTablePages的整合能力

QTablePages作为上层封装类,将表格视图、数据模型和分页控件有机结合:

void QTablePages::InitTableForm(RemoteTableModel *dataModel, int pageLines)
{m_dataModel = dataModel;m_pageLines = pageLines;// 设置表格视图m_tableView->setModel(m_dataModel);m_tableView->setItemDelegate(new GeneralDelegate(this));// 连接分页信号connect(m_pageNavBar, &PageNavigator::SigCurrentPageChanged, this, &QTablePages::OnPageChanged);// 连接删除信号connect(m_tableView->itemDelegate(), &GeneralDelegate::deleteRequested,this, &QTablePages::onDeleteButtonClicked);
}

通过refreshData方法实现带筛选条件的刷新,支持日期范围、关键词等多条件筛选,极大提升了表格的实用性。

4. 远程数据模型:RemoteTableModel的数据处理(核心)

RemoteTableModel作为数据核心,实现了与远程数据源的交互和本地数据管理:

  • 异步数据加载:通过loadPage方法异步获取数据,避免UI阻塞

    void RemoteTableModel::loadPage(int page)
    {if (m_loading) return;m_loading = true;beginResetModel();m_records.clear();m_client->ExecuteQuery(m_filter.toStdString(),[this](const std::vector<Record> &records) {// 数据处理逻辑beginResetModel();m_records = newData;endResetModel();m_loading = false;emit loadFinished(newData);});
    }
    
  • 数据标准化:在handleDataLoaded中统一处理数据格式,确保视图展示一致性

    for (const auto &record : records) {QVariantMap normalizedRecord;for (auto it = record.begin(); it != record.end(); ++it) {QString englishKey = it.key().toLower();  // 统一键名格式if (englishKey == "total_count") {m_totalCount = it.value().toInt();    // 提取总记录数continue;}if (COLUMN_MAPPING.contains(englishKey)) {normalizedRecord[englishKey] = it.value();}}m_records.push_back(normalizedRecord);
    }
    
  • 完整CRUD支持:提供insertRowupdateRowremoveRow等方法,封装数据操作逻辑

  • 远程数据操作最核心的挑战是避免 UI 阻塞,RemoteTableModel通过异步回调 + 信号槽机制解决这一问题

void RemoteTableModel::loadPage(int page)
{if (m_loading) return;  // 防止重复请求m_loading = true;beginResetModel();      // 通知视图即将重置数据m_records.clear();      // 清空本地缓存// 异步执行远程查询m_client->ExecuteQuery(m_filter.toStdString(),  // 传递筛选条件[this](const std::vector<Record> &records) {  // 查询结果回调// 转换远程数据为本地QVariantMap格式QVector<QVariantMap> newData;for (const auto &record : records) {QVariantMap vm;auto dataMap = record.at("data");for (auto it = dataMap.begin(); it != dataMap.end(); ++it) {vm[QString::fromStdString(it->first)] = QString::fromStdString(it->second);}newData.append(vm);}beginResetModel();   // 通知视图数据开始更新m_records = newData; // 更新本地缓存endResetModel();     // 通知视图数据更新完成m_loading = false;emit loadFinished(newData);  // 触发数据处理流程});
}
  • 状态锁控制:m_loading标记防止并发请求,确保数据一致性
  • 视图通知机制:beginResetModel()和endResetModel()成对调用,告知视图重新加载数据,避免界面错乱

三、实践经验与优化方向

  1. 性能优化

    • 采用异步加载避免UI卡顿
    • 数据分页减少一次性加载压力
    • 模型重置时使用beginResetModelendResetModel确保视图高效更新
  2. 可扩展性设计

    • LogManagement中通过统一方法初始化不同业务模型,便于新增表格类型
    • 委托类与业务逻辑分离,可根据需求定制不同单元格样式
  3. 用户体验提升

    • 分页按钮状态动态反馈
    • 单元格tooltip提示完整信息
    • 支持数据导出CSV功能
  4. 未来优化方向

    • 增加数据缓存机制减少重复请求
    • 实现表格列的动态显示/隐藏
    • 加入单元格编辑功能的统一处理

四、组件协作关系图

┌─────────────────┐      ┌─────────────────┐      ┌─────────────────┐
│   LogManagement │──┬──▶│ RemoteTableModel│◀────▶│DBClientThreadHandler│
└─────────────────┘  │   └────────┬────────┘      └─────────────────┘│            ││            ▼│   ┌─────────────────┐└──▶│   QTablePages   │└────────┬────────┘│┌───────────────┼───────────────┐▼               ▼               ▼┌─────────────┐  ┌─────────────┐  ┌─────────────┐│ToolTipTableView│  │PageNavigator│  │GeneralDelegate│└─────────────┘  └─────────────┘  └─────────────┘

五、总结

  • 本文介绍的表格组件套装通过合理的架构设计和组件封装,实现了数据展示、分页导航、自定义交互等核心功能。基于Qt的MVC模式和信号槽机制,各组件既相互协作又保持独立,既保证了功能的完整性,又具备良好的可维护性和可扩展性。
  • 代码中grpc通信模块没做展示,因为grpc通信模块比较简单,且不是本文的重点,所以没做展示。
  • 核心代码
http://www.dtcms.com/a/352813.html

相关文章:

  • 阿里云——应用交付与负载均衡
  • 用户体验设计 | 从UX到AX:人工智能如何重构交互范式?
  • 阿里云轻量应用服务器与ECS对比
  • 4步用代码拆解数学建模中的TOPSIS评价决策! ! !
  • 树的常见算法及Java实现
  • LeetCode算法日记 - Day 23: 外观数列、数青蛙
  • 欧洲数字化养殖平台 Herdwatch 借力 Iceberg + StarRocks 提升分析能力
  • 【Matplotlib学习】驾驭画布:Matplotlib 布局方式从入门到精通完全指南
  • 【RabbitWQ】基于 Java 实现轻量级消息队列(二)
  • 医疗AI时代的生物医学Go编程:高性能计算与精准医疗的案例分析(一)
  • 【重学 MySQL】九十、Linux下MySQL的安装与卸载指南
  • 如何保证DDC楼宇自控系统与IBMS集成管理系统的稳定性和可靠性?
  • 深入解析 Flink Function
  • 【Datawhale之Happy-LLM】Encoder-only模型篇 task05精华~
  • 【雅思021】I’m sorry, I love you Ⅱ
  • 如何使用PyTorch搭建一个基础的神经网络并进行训练?
  • skywalking 原理
  • H20 性能表现之 gpt-oss-120b
  • 软考-系统架构设计师 管理信息系统(MIS)详细讲解
  • React内网开发代理配置详解
  • C++ 力扣 704.二分查找 基础二分查找 题解 每日一题
  • Https之(四)国密GMTLS
  • 【Redis#8】Redis 数据结构 -- Zset 类型
  • 改造thinkphp6的命令行工具和分批次导出大量数据
  • GTCB:引领金融革命,打造数字经济时代标杆
  • 【js】加密库sha.js 严重漏洞速查
  • UTXO 模型及扩展模型
  • 香港数字资产交易市场蓬勃发展,监管与创新并驾齐驱
  • 完整实验命令解析:从集群搭建到负载均衡配置(2)
  • 记录使用ruoyi-flowable开发部署中出现的问题以及解决方法(二)