使用QT6创建一个可编辑的表格并导出和载入

cpp 复制代码
// mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QStandardItemModel>
#include <QTableView>
#include <QToolBar>
#include <QAction>
#include <QFileDialog>
#include <QMessageBox>
#include <QTextStream>
#include <QFile>
#include <QHeaderView>

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void addRow();
    void removeRow();
    void saveToCSV();
    void loadFromCSV();
    void clearTable();

private:
    QStandardItemModel *model;
    QTableView *tableView;
    void setupUI();
};

#endif // MAINWINDOW_H
cpp 复制代码
// mainwindow.cpp

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setupUI();
}

MainWindow::~MainWindow()
{
}

void MainWindow::setupUI()
{
    // 1. 创建模型和视图
    model = new QStandardItemModel(this);
    // 设置初始列数和表头
    model->setColumnCount(4);
    model->setHorizontalHeaderLabels({"ID", "姓名", "城市", "备注"});

    tableView = new QTableView(this);
    tableView->setModel(model);
    
    // 2. 配置视图属性
    // 启用编辑功能 (QStandardItemModel 默认即可编辑,但需确保视图未禁用)
    tableView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);
    // 列宽自适应
    tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    // 选中整行
    tableView->setSelectionBehavior(QAbstractItemView::SelectRows);

    setCentralWidget(tableView);
    setWindowTitle("Qt 可编辑表格示例");
    resize(800, 600);

    // 3. 创建工具栏和动作
    QToolBar *toolBar = addToolBar("操作栏");
    
    QAction *addAction = toolBar->addAction("添加行");
    connect(addAction, &QAction::triggered, this, &MainWindow::addRow);

    QAction *removeAction = toolBar->addAction("删除选中行");
    connect(removeAction, &QAction::triggered, this, &MainWindow::removeRow);

    QAction *clearAction = toolBar->addAction("清空表格");
    connect(clearAction, &QAction::triggered, this, &MainWindow::clearTable);

    toolBar->addSeparator();

    QAction *saveAction = toolBar->addAction("保存为CSV");
    connect(saveAction, &QAction::triggered, this, &MainWindow::saveToCSV);

    QAction *loadAction = toolBar->addAction("加载CSV");
    connect(loadAction, &QAction::triggered, this, &MainWindow::loadFromCSV);
}

void MainWindow::addRow()
{
    int row = model->rowCount();
    model->insertRow(row);
    // 初始化新行的数据,例如设置ID
    model->setData(model->index(row, 0), QString::number(row + 1));
}

void MainWindow::removeRow()
{
    // 获取选中的索引列表
    QModelIndexList selectedIndexes = tableView->selectionModel()->selectedIndexes();
    if (selectedIndexes.isEmpty()) {
        QMessageBox::warning(this, "提示", "请先选择要删除的行");
        return;
    }

    // 由于删除行会改变索引,我们需要从后往前删除,或者收集行号去重后排序删除
    // 这里采用收集行号并去重的方式
    QList<int> rowsToRemove;
    for (const QModelIndex &index : selectedIndexes) {
        if (!rowsToRemove.contains(index.row())) {
            rowsToRemove.append(index.row());
        }
    }
    
    // 降序排列,确保删除后面的行不影响前面行的索引
    std::sort(rowsToRemove.begin(), rowsToRemove.end(), std::greater<int>());

    for (int row : rowsToRemove) {
        model->removeRow(row);
    }
}

void MainWindow::clearTable()
{
    model->removeRows(0, model->rowCount());
}

void MainWindow::saveToCSV()
{
    QString fileName = QFileDialog::getSaveFileName(this, "保存文件", "", "CSV文件 (*.csv);;所有文件 (*)");
    if (fileName.isEmpty())
        return;

    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "错误", "无法打开文件进行写入");
        return;
    }

    QTextStream out(&file);
    // 如果需要处理中文乱码,可以设置编码,例如 UTF-8
    //out.setCodec("UTF-8"); 

    // 写入表头
    QStringList headers;
    for (int i = 0; i < model->columnCount(); ++i) {
        headers << model->headerData(i, Qt::Horizontal).toString();
    }
    out << headers.join(",") << "\n";

    // 写入数据
    for (int i = 0; i < model->rowCount(); ++i) {
        QStringList rowData;
        for (int j = 0; j < model->columnCount(); ++j) {
            QModelIndex index = model->index(i, j);
            // 获取显示角色的数据
            QString data = model->data(index, Qt::DisplayRole).toString();
            // 如果数据中包含逗号或换行符,需要用引号包裹并转义内部引号
            if (data.contains(",") || data.contains("\"") || data.contains("\n")) {
                data.replace("\"", "\"\"");
                data = "\"" + data + "\"";
            }
            rowData << data;
        }
        out << rowData.join(",") << "\n";
    }

    file.close();
    QMessageBox::information(this, "成功", "表格已保存至: " + fileName);
}

void MainWindow::loadFromCSV()
{
    QString fileName = QFileDialog::getOpenFileName(this, "打开文件", "", "CSV文件 (*.csv);;所有文件 (*)");
    if (fileName.isEmpty())
        return;

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "错误", "无法打开文件进行读取");
        return;
    }

    QTextStream in(&file);
    //in.setCodec("UTF-8");

    // 清空现有模型
    model->removeRows(0, model->rowCount());

    bool isFirstLine = true;
    while (!in.atEnd()) {
        QString line = in.readLine();
        if (line.isEmpty()) continue;

        // 简单的CSV解析,实际生产中建议使用更健壮的CSV解析库
        // 这里假设没有复杂的嵌套引号情况,仅做简单分割演示
        // 注意:这种简单分割无法完美处理包含逗号的字段,生产环境需优化
        QStringList fields = line.split(","); 
        
        if (isFirstLine) {
            // 设置表头
            model->setHorizontalHeaderLabels(fields);
            isFirstLine = false;
        } else {
            int row = model->rowCount();
            model->insertRow(row);
            for (int col = 0; col < fields.size() && col < model->columnCount(); ++col) {
                QString data = fields[col];
                // 去除可能存在的引号
                if (data.startsWith("\"") && data.endsWith("\"")) {
                    data = data.mid(1, data.size() - 2);
                    data.replace("\"\"", "\"");
                }
                model->setData(model->index(row, col), data);
            }
        }
    }
    file.close();
}
cpp 复制代码
//main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
cpp 复制代码
#CMakeLists.txt

cmake_minimum_required(VERSION 3.15)

project(editable_table LANGUAGES CXX)
project(editable_table LANGUAGES C CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_PREFIX_PATH "/home/rpdzkj/Qt/6.9.3/gcc_arm64/")

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 查找 Qt5 组件
find_package(Qt6 COMPONENTS Widgets REQUIRED)

set(PROJECT_SOURCES
        main.cpp
        mainwindow.cpp
        mainwindow.h
)

add_executable(editable_table ${PROJECT_SOURCES})

target_link_libraries(editable_table Qt6::Widgets)

详细代码请见:https://gitee.com/ws_22/qt6-edit-table.git

最终效果

相关推荐
天若有情6732 小时前
C++进阶:普通重载运算符 vs 隐式类型转换重载运算符,一篇讲透区别
开发语言·c++·算法
云深麋鹿2 小时前
C++ | 二叉搜索树
开发语言·c++
永远睡不够的入2 小时前
C++11新特性详解(上):从列表初始化到右值引用
开发语言·c++
c++圈来了个新人2 小时前
C++类和对象(中)
c语言·开发语言·数据结构·c++·考研·算法
思麟呀2 小时前
5种IO模型
linux·运维·服务器·c++
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【删数问题】:删数问题2
c++·算法·贪心·csp·信奥赛
SWAGGY..2 小时前
【C++初阶】:(10)vector的使用及模拟实现
开发语言·c++
SariHcr1232 小时前
Openarm机器人双臂模型仿真从零部署
c++·人工智能·python·机器人·bash·openarm
故事还在继续吗2 小时前
C++11关键特性
开发语言·c++·算法