文章目录
- 
- 效果图
 - 一、整体架构设计:基于MVC模式的分层实现
 - 二、核心组件解析
 - 
- [1. 自定义委托:`GeneralDelegate`实现单元格交互](#1. 自定义委托:
GeneralDelegate实现单元格交互) - [2. 分页导航控件:`PageNavigator`的状态管理](#2. 分页导航控件:
PageNavigator的状态管理) - [3. 表格页面封装:`QTablePages`的整合能力](#3. 表格页面封装:
QTablePages的整合能力) - [4. 远程数据模型:`RemoteTableModel`的数据处理(核心)](#4. 远程数据模型:
RemoteTableModel的数据处理(核心)) 
 - [1. 自定义委托:`GeneralDelegate`实现单元格交互](#1. 自定义委托:
 - 三、实践经验与优化方向
 - 四、组件协作关系图
 - 五、总结
 
 - 
上篇文章中使用到了
QSqlTableModel来管理与处理数据库的操作,但这个模型有一个很大的局限,就是它只能处理本地数据库,不能处理远程数据库,所以便开发了下述远程模式的model。 
效果图

一、整体架构设计:基于MVC模式的分层实现
本次分享的表格系统严格遵循Qt的MVC(Model-View-Controller)设计模式,各组件职责清晰:
- 模型(Model) :
RemoteTableModel负责数据的获取、存储和处理,与后端数据源交互 - 视图(View) :
ToolTipTableView负责数据的可视化展示 - 委托(Delegate) :
GeneralDelegate处理单元格的自定义渲染和用户交互 - 控制器(Controller) :
QTablePages整合上述组件,协调数据流转与用户操作 
此外,PageNavigator 作为独立的分页控件,负责页码管理;LogManagement 则承担数据模型的统一初始化与管理。这种分层设计让各组件可独立维护、灵活复用。
二、核心组件解析
1. 自定义委托:GeneralDelegate实现单元格交互
表格中常需要在单元格中嵌入按钮等交互元素,GeneralDelegate 通过重写Qt委托的关键方法实现这一功能:
            
            
              cpp
              
              
            
          
          // 重写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处理按钮点击事件,并通过信号将交互传递给上层:
            
            
              cpp
              
              
            
          
          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作为上层封装类,将表格视图、数据模型和分页控件有机结合:
            
            
              cpp
              
              
            
          
          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阻塞cppvoid 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中统一处理数据格式,确保视图展示一致性cppfor (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支持 :提供
insertRow、updateRow、removeRow等方法,封装数据操作逻辑 - 
远程数据操作最核心的挑战是避免 UI 阻塞,RemoteTableModel通过异步回调 + 信号槽机制解决这一问题:
 
            
            
              cpp
              
              
            
          
          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()成对调用,告知视图重新加载数据,避免界面错乱
 
三、实践经验与优化方向
- 
性能优化:
- 采用异步加载避免UI卡顿
 - 数据分页减少一次性加载压力
 - 模型重置时使用
beginResetModel和endResetModel确保视图高效更新 
 - 
可扩展性设计:
LogManagement中通过统一方法初始化不同业务模型,便于新增表格类型- 委托类与业务逻辑分离,可根据需求定制不同单元格样式
 
 - 
用户体验提升:
- 分页按钮状态动态反馈
 - 单元格tooltip提示完整信息
 - 支持数据导出CSV功能
 
 - 
未来优化方向:
- 增加数据缓存机制减少重复请求
 - 实现表格列的动态显示/隐藏
 - 加入单元格编辑功能的统一处理
 
 
四、组件协作关系图
            
            
              plaintext
              
              
            
          
          ┌─────────────────┐      ┌─────────────────┐      ┌─────────────────┐
│   LogManagement │──┬──▶│ RemoteTableModel│◀────▶│DBClientThreadHandler│
└─────────────────┘  │   └────────┬────────┘      └─────────────────┘
                     │            │
                     │            ▼
                     │   ┌─────────────────┐
                     └──▶│   QTablePages   │
                         └────────┬────────┘
                                  │
                  ┌───────────────┼───────────────┐
                  ▼               ▼               ▼
         ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
         │ToolTipTableView│  │PageNavigator│  │GeneralDelegate│
         └─────────────┘  └─────────────┘  └─────────────┘
        五、总结
- 本文介绍的表格组件套装通过合理的架构设计和组件封装,实现了数据展示、分页导航、自定义交互等核心功能。基于Qt的MVC模式和信号槽机制,各组件既相互协作又保持独立,既保证了功能的完整性,又具备良好的可维护性和可扩展性。
 - 代码中grpc通信模块没做展示,因为grpc通信模块比较简单,且不是本文的重点,所以没做展示。
 - 核心代码