概述

本文将详细介绍如何使用Qt开发一个功能完整的表格数据处理应用。我们将重点讲解QStandardItemModel和QTableView的使用方法,并通过实际案例展示如何实现数据的导入、编辑、保存以及格式设置等功能。

在这里插入图片描述

应用功能概览

本应用主要实现以下功能:

  • 文本数据文件的导入和保存
  • 表格数据的添加、插入和删除
  • 数据预览功能
  • 单元格格式设置(居左、居中、居右、粗体)
  • 状态栏实时显示当前单元格信息

界面设计

应用界面主要包含以下控件:

  • 一个TableView用于显示和编辑表格数据
  • 一个PlainTextEdit用于预览数据
  • 两个GroupBox分别容纳上述控件
  • 工具栏包含多个功能按钮

核心控件详解

1. QStandardItemModel标准项模型

QStandardItemModel是Qt中一个强大的数据模型类,它提供了项(item)的标准模型,可以用于存储自定义数据。与QTableView结合使用,可以创建功能丰富的表格应用程序。

关键特性:
  • 支持行列操作
  • 支持项的选择和编辑
  • 可以存储任意数据
  • 支持复选框等控件
初始化代码:
// 在MainWindow构造函数中初始化模型
colCount = 6; // 设置表格列数
model = new QStandardItemModel(2, colCount, this); // 创建模型,初始2行6列

2. QTableView使用详解

QTableView是Qt中用于显示和编辑表格数据的视图组件,它需要与数据模型(如QStandardItemModel)配合使用。

关键配置:
// 设置表格视图的选择模式和选择行为
ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);

功能实现详解

1. 数据导入功能

void MainWindow::on_actionOpen_triggered()
{
    // 使用文件对话框获取文件路径
    QString fileName = QFileDialog::getOpenFileName(
                       this,
                       "打开一个文件",
                       QCoreApplication::applicationDirPath(),
                       "文本数据文件(*.txt);;所有文件(*.*)");
    
    // 检查文件是否有效
    if(fileName.isEmpty()) {
        return;
    }
    
    // 打开文件并读取内容
    QFile qFile(fileName);
    if(!qFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return;
    }
    
    // 使用文本流读取文件内容
    QTextStream stream(&qFile);
    QStringList fileContent;
    
    // 逐行读取文件内容
    while(!stream.atEnd()) {
        QString line = stream.readLine();
        fileContent.append(line);
        ui->plainTextEdit->appendPlainText(line); // 在文本编辑器中显示
    }
    qFile.close(); // 关闭文件
    
    // 使用读取的内容初始化数据模型
    initModel(fileContent);
}

2. 数据模型初始化

void MainWindow::initModel(QStringList content)
{
    // 读取表头行
    QString headLine = content[0];
    
    // 使用正则表达式分割表头(匹配一个或多个空白字符)
    QStringList headList = headLine.split(QRegExp("\\s+"), QString::SkipEmptyParts);
    model->setHorizontalHeaderLabels(headList); // 设置表头
    
    // 处理数据行
    int rows = content.count();
    for(int i = 1; i < rows; i++) { // 从第1行开始(第0行是表头)
        QString line = content[i];
        QStringList fieldList = line.split(QRegExp("\\s+"), QString::SkipEmptyParts);
        
        // 处理前5列普通数据
        for(int j = 0; j < colCount - 1; j++) {
            QStandardItem *item = new QStandardItem(fieldList[j]);
            model->setItem(i - 1, j, item); // 设置单元格数据
        }
        
        // 处理最后一列(婚否列),设置为复选框
        QStandardItem *item = new QStandardItem(headList[colCount - 1]);
        item->setCheckable(true); // 设置为可勾选
        
        // 根据文本内容设置复选框状态
        if(fieldList[colCount - 1] == "否") {
            item->setCheckState(Qt::Unchecked);
        } else {
            item->setCheckState(Qt::Checked);
        }
        model->setItem(i - 1, colCount - 1, item);
    }
}

3. 状态栏显示优化

void MainWindow::on_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
    if(current.isValid()) {
        auto item = model->itemFromIndex(current);
        QString content;
        
        // 特殊处理婚否列(最后一列)
        if(current.column() == colCount - 1) {
            // 根据复选框状态显示"是"或"否"
            content = (item->checkState() == Qt::Checked) ? "是" : "否";
        } else {
            // 其他列直接显示文本内容
            content = item->text();
        }
        
        // 更新状态栏信息
        labelInfo->setText(QString::asprintf("当前单元格: %d行, %d列",
                           current.row(), current.column()) +
                           "      单元格内容: " + content);
        
        // 更新粗体按钮状态
        ui->actionBold->setChecked(item->font().bold());
    }
}

完整代码实现

mainwindow.h 头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    // 文件操作槽函数
    void on_actionOpen_triggered();  // 打开文件
    void on_actionSave_triggered();  // 保存文件
    
    // 数据操作槽函数
    void on_actionAppend_triggered(); // 尾部添加行
    void on_actionInsert_triggered(); // 插入行
    void on_actionDel_triggered();    // 删除行
    void on_actionSee_triggered();    // 预览数据
    
    // 格式设置槽函数
    void on_actionAlignRight_triggered(); // 右对齐
    void on_actionMiddle_triggered();     // 居中对齐
    void on_actionLeft_triggered();       // 左对齐
    void on_actionBold_triggered(bool checked); // 粗体设置
    
    // 当前单元格变化槽函数
    void on_currentChanged(const QModelIndex &current, const QModelIndex &previous);

private:
    Ui::MainWindow *ui; // UI对象指针

    QStandardItemModel *model;         // 数据模型
    QItemSelectionModel *selectionModel; // 选择模型

    int colCount;     // 表格列数
    QLabel *labelInfo; // 状态栏信息标签

    // 初始化数据模型
    void initModel(QStringList content);
};

#endif // MAINWINDOW_H

mainwindow.cpp 实现文件

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QTextStream>

// 构造函数
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this); // 设置UI

    // 初始化状态栏信息标签
    labelInfo = new QLabel(this);
    statusBar()->addWidget(labelInfo);

    // 初始化数据模型和选择模型
    colCount = 6; // 设置表格列数
    model = new QStandardItemModel(2, colCount, this); // 创建数据模型
    selectionModel = new QItemSelectionModel(model);   // 创建选择模型

    // 设置表格视图的模型和选择模型
    ui->tableView->setModel(model);
    ui->tableView->setSelectionModel(selectionModel);

    // 配置表格视图的选择模式和行为
    ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection); // 扩展选择模式
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);   // 选择单元格

    // 连接当前单元格变化信号到槽函数
    connect(selectionModel,
            SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
            this, SLOT(on_currentChanged(const QModelIndex &, const QModelIndex &)));
}

// 析构函数
MainWindow::~MainWindow()
{
    delete ui; // 释放UI对象
}

// 打开文件槽函数
void MainWindow::on_actionOpen_triggered()
{
    // 使用文件对话框获取文件路径
    QString fileName = QFileDialog::getOpenFileName(
                       this,
                       "打开一个文件",
                       QCoreApplication::applicationDirPath(),
                       "文本数据文件(*.txt);;所有文件(*.*)");
    
    // 检查文件是否有效
    if(fileName.isEmpty()) {
        return;
    }
    
    // 打开文件并读取内容
    QFile qFile(fileName);
    if(!qFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return;
    }
    
    // 使用文本流读取文件内容
    QTextStream stream(&qFile);
    QStringList fileContent;
    
    // 逐行读取文件内容
    while(!stream.atEnd()) {
        QString line = stream.readLine();
        fileContent.append(line);
        ui->plainTextEdit->appendPlainText(line); // 在文本编辑器中显示
    }
    qFile.close(); // 关闭文件
    
    // 使用读取的内容初始化数据模型
    initModel(fileContent);
}

// 初始化数据模型
void MainWindow::initModel(QStringList content)
{
    // 读取表头行
    QString headLine = content[0];
    
    // 使用正则表达式分割表头(匹配一个或多个空白字符)
    QStringList headList = headLine.split(QRegExp("\\s+"), QString::SkipEmptyParts);
    model->setHorizontalHeaderLabels(headList); // 设置表头
    
    // 处理数据行
    int rows = content.count();
    for(int i = 1; i < rows; i++) { // 从第1行开始(第0行是表头)
        QString line = content[i];
        QStringList fieldList = line.split(QRegExp("\\s+"), QString::SkipEmptyParts);
        
        // 处理前5列普通数据
        for(int j = 0; j < colCount - 1; j++) {
            QStandardItem *item = new QStandardItem(fieldList[j]);
            model->setItem(i - 1, j, item); // 设置单元格数据
        }
        
        // 处理最后一列(婚否列),设置为复选框
        QStandardItem *item = new QStandardItem(headList[colCount - 1]);
        item->setCheckable(true); // 设置为可勾选
        
        // 根据文本内容设置复选框状态
        if(fieldList[colCount - 1] == "否") {
            item->setCheckState(Qt::Unchecked);
        } else {
            item->setCheckState(Qt::Checked);
        }
        model->setItem(i - 1, colCount - 1, item);
    }
}

// 尾部添加行槽函数
void MainWindow::on_actionAppend_triggered()
{
    // 初始化新行的默认值
    QStringList initValue = {"无名", "男", "市场部", "销售", "5000", "婚否"};
    QList<QStandardItem*> itemList;

    // 创建新行的所有单元格
    for(int i = 0; i < colCount; i++) {
         QStandardItem *item = new QStandardItem(initValue[i]);
         itemList.push_back(item);
    }

    // 设置最后一列为复选框
    itemList[colCount - 1]->setCheckable(true);
    
    // 在模型末尾插入新行
    model->insertRow(model->rowCount(), itemList);

    // 清除选择并选中新行
    selectionModel->clearSelection();
    QModelIndex index = model->index(model->rowCount() - 1, 0);
    selectionModel->setCurrentIndex(index, QItemSelectionModel::Select);
}

// 插入行槽函数
void MainWindow::on_actionInsert_triggered()
{
    // 初始化新行的默认值
    QStringList initValue = {"无名", "男", "市场部", "销售", "5000", "婚否"};
    QList<QStandardItem*> itemList;

    // 创建新行的所有单元格
    for(int i = 0; i < colCount; i++) {
         QStandardItem *item = new QStandardItem(initValue[i]);
         itemList.push_back(item);
    }

    // 设置最后一列为复选框
    itemList[colCount - 1]->setCheckable(true);

    // 获取当前选中位置并在该位置插入新行
    QModelIndex index = selectionModel->currentIndex();
    model->insertRow(index.row(), itemList);

    // 清除选择并选中新插入的行
    selectionModel->clearSelection();
    selectionModel->setCurrentIndex(index, QItemSelectionModel::Select);
}

// 删除行槽函数
void MainWindow::on_actionDel_triggered()
{
    // 获取当前选中位置
    QModelIndex index = selectionModel->currentIndex();

    // 如果是最后一行,直接删除
    if(index.row() == model->rowCount() - 1) {
        model->removeRow(index.row());
    } else {
        // 删除行并保持选中状态
        model->removeRow(index.row());
        selectionModel->setCurrentIndex(index, QItemSelectionModel::Select);
    }
}

// 预览数据槽函数
void MainWindow::on_actionSee_triggered()
{
    // 清空文本编辑器
    ui->plainTextEdit->clear();

    // 获取表头
    QString str;
    for(int i = 0; i < model->columnCount(); i++) {
        QStandardItem *item = model->horizontalHeaderItem(i);
        str += item->text() + "\t";
    }
    ui->plainTextEdit->appendPlainText(str); // 添加表头

    // 获取每一行数据
    for(int i = 0; i < model->rowCount(); i++) {
        str = "";
        for(int j = 0; j < model->columnCount(); j++) {
            QStandardItem *item = model->item(i, j);
            str += item->text() + "\t";
        }

        // 特殊处理婚否列
        if(model->item(i, model->columnCount() - 1)->checkState() == Qt::Checked) {
            str += "是";
        } else {
            str += "否";
        }

        ui->plainTextEdit->appendPlainText(str); // 添加数据行
    }
}

// 保存文件槽函数
void MainWindow::on_actionSave_triggered()
{
    // 使用文件对话框获取保存路径
    QString fileName = QFileDialog::getSaveFileName(this, "保存文件",
                                 QCoreApplication::applicationDirPath());
    if(fileName.isEmpty()) {
        return;
    }

    // 打开文件
    QFile qFile(fileName);
    if (!qFile.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
        return;
    }

    // 使用文本流写入文件
    QTextStream stream(&qFile);

    // 写入表头
    QString str;
    for(int i = 0; i < model->columnCount(); i++) {
        auto item = model->horizontalHeaderItem(i);
        str += item->text() + "\t\t";
    }
    stream << str << "\n"; // 写入表头

    // 写入数据行
    for(int i = 0; i < model->rowCount(); i++) {
        str = "";
        for(int j = 0; j < model->columnCount(); j++) {
            QStandardItem *item = model->item(i, j);
            str += item->text() + "\t";
        }

        // 特殊处理婚否列
        if(model->item(i, model->columnCount() - 1)->checkState() == Qt::Checked) {
            str += "是";
        } else {
            str += "否";
        }

        stream << str << "\n"; // 写入数据行
    }

    qFile.close(); // 关闭文件
}

// 右对齐槽函数
void MainWindow::on_actionAlignRight_triggered()
{
    // 检查是否有选中项
    if(!selectionModel->hasSelection()) {
        return;
    }

    // 获取所有选中项并设置右对齐
    QModelIndexList indexList = selectionModel->selectedIndexes();
    for(int i = 0; i < indexList.count(); i++) {
        auto item = model->itemFromIndex(indexList[i]);
        item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
    }
}

// 居中对齐槽函数
void MainWindow::on_actionMiddle_triggered()
{
    // 检查是否有选中项
    if(!selectionModel->hasSelection()) {
        return;
    }

    // 获取所有选中项并设置居中对齐
    QModelIndexList indexList = selectionModel->selectedIndexes();
    for(int i = 0; i < indexList.count(); i++) {
        auto item = model->itemFromIndex(indexList[i]);
        item->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
    }
}

// 左对齐槽函数
void MainWindow::on_actionLeft_triggered()
{
    // 检查是否有选中项
    if(!selectionModel->hasSelection()) {
        return;
    }

    // 获取所有选中项并设置左对齐
    QModelIndexList indexList = selectionModel->selectedIndexes();
    for(int i = 0; i < indexList.count(); i++) {
        auto item = model->itemFromIndex(indexList[i]);
        item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    }
}

// 粗体设置槽函数
void MainWindow::on_actionBold_triggered(bool checked)
{
    // 检查是否有选中项
    if(!selectionModel->hasSelection()) {
        return;
    }

    // 获取所有选中项并设置粗体
    QModelIndexList indexList = selectionModel->selectedIndexes();
    for(int i = 0; i < indexList.count(); i++) {
        auto item = model->itemFromIndex(indexList[i]);
        QFont font = item->font();
        font.setBold(checked);
        item->setFont(font);
    }
}

// 当前单元格变化槽函数
void MainWindow::on_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
    if(current.isValid()) {
        auto item = model->itemFromIndex(current);
        QString content;
        
        // 特殊处理婚否列(最后一列)
        if(current.column() == colCount - 1) {
            // 根据复选框状态显示"是"或"否"
            content = (item->checkState() == Qt::Checked) ? "是" : "否";
        } else {
            // 其他列直接显示文本内容
            content = item->text();
        }
        
        // 更新状态栏信息
        labelInfo->setText(QString::asprintf("当前单元格: %d行, %d列",
                           current.row(), current.column()) +
                           "      单元格内容: " + content);
        
        // 更新粗体按钮状态
        ui->actionBold->setChecked(item->font().bold());
    }
}

总结

通过本文的详细介绍,我们学习了如何使用Qt的QStandardItemModel和QTableView开发一个功能完整的表格数据处理应用。关键点包括:

  1. 数据模型的使用:QStandardItemModel提供了灵活的数据存储和管理能力
  2. 表格视图的配置:通过设置选择模式和行为,实现丰富的交互体验
  3. 文件操作:实现了数据的导入和保存功能
  4. 数据操作:支持添加、插入和删除行操作
  5. 格式设置:实现了单元格的对齐和字体样式设置
  6. 状态栏显示:优化了婚否列的显示方式
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐