Qt:读取已有数据的Excel文档,并将数据显示在通过QTableWidget绘制的表格中,之后将显示的数据保存成excel格式进行输出(包括表头等内容)

Qt 专栏收录该内容
31 篇文章 2 订阅

读取已有数据的Excel文档,并将数据显示在通过QTableWidget绘制的表格中,之后将显示的数据保存成excel格式进行输出(包括表头等内容)


  • UI展示及功能简介


  • 读取
//于Mainwindow中,on_read_clicked属于Pushbotton摁键的槽函数
void MainWindow::on_read_clicked()
{
    execel_read();//调用读取类方法,execel_read的具体内容见下文
}

 

选择【设置好内容的Excel文档】进行读取

结束后会显示读取完成。

上图为模板1.xlsx中的内容


  • 生成报表

//于Mainwindow中,on_pushButton_clicked属于Pushbotton摁键的槽函数

void MainWindow::on_pushButton_clicked()
{
    dom->datasend(exceldata);//在MainWindow中调用dialog的类方法datasend()
   //dom是属于dialog类的对象,需要在MainWindow中包含dialog头文件,之后定义指针对象dom,并分配空间
   //具体操作如下***处
    QMessageBox::warning(this,tr("生成波表情况:"),tr("报表已经生成!"),QMessageBox::Yes);
}
*********************************
//MainWindow中包含dialog的头文件

#include"dialog.h"

//MainWindow.h中声明属于dialog类的对象

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    ............
private slots:
    ............

private:
    ............
    Dialog *dom;//命名还是个大问题,保留dialog,不能在前后加数字
};
//MainWindow.cpp中分配dom指针对象空间
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    .............
    dom = new Dialog(this);
    .............
}
*********************************

 

点击后会显示报表已经生成。


  • 查看报表

void MainWindow::on_sheet_clicked()
//显示部分,关于TableWidget已经于生成报表部分绘制完成,如有需要,生成报表和查看报表可以统一为一个部分
{
    dom->exec();//保持窗口
}

 

将会显示生成报表的结果。


  • 保存

将生成的报表保存成Excel文档

保存会出现提示窗口,选择是否要打开。选择是

void MainWindow::on_pushButton_2_clicked()
{
    saveas();
}

 


以上情况不具有普世应用的意义,如何应用需要根据工程中的具体内容而定,因为程序内部未对从excel中读取到的数据进行处理,所以输出的报表中,数据和读取时选择的【模板1.xlsx】数据一样。

在实际工程中,会出现对大量excel数据进行读取(录入),并进行数据处理,之后输出显示成表格(如查看报表功能),必要的话,有可能需要将显示表格中的内容进行保存。

本文的内容主要是省去了数据处理的部分,概括性的对上述复杂问题进行一个精炼。


  • 程序介绍

读取excel

void MainWindow::execel_read()
{
            QString path = QFileDialog::getOpenFileName(this,"open",
                                                      "../","execl(*.xlsx)");
            //指定父对象(this),“open”具体操作,打开,“../”默认,之后可以添加要打开文件的格式
            if(path.isEmpty()==false)
            {
                //文件对象
                QFile file(path);
                //打开文件,默认为utf8变量,
                bool flag = file.open(QIODevice::ReadOnly);
                if(flag == true)//打开成功
                {
                    QAxObject *excel = new QAxObject(this);//建立excel操作对象
                    excel->setControl("Excel.Application");//连接Excel控件
                    excel->setProperty("Visible", false);//不显示窗体看效果
                    excel->setProperty("DisplayAlerts", false);//不显示警告看效果
                    /*********获取COM文件的一种方式************/
                    QAxObject *workbooks = excel->querySubObject("WorkBooks");
                    //获取工作簿(excel文件)集合
                    workbooks->dynamicCall("Open(const QString&)", path);//path至关重要,获取excel文件的路径
                    //打开一个excel文件
                    QAxObject *workbook = excel->querySubObject("ActiveWorkBook");
                    QAxObject *worksheet = workbook->querySubObject("WorkSheets(int)",1);//访问excel中的工作表中第一个单元格
                    QAxObject *usedRange = worksheet->querySubObject("UsedRange");//sheet的范围
                    /*********获取COM文件的一种方式************/
                    //获取打开excel的起始行数和列数和总共的行数和列数
                    int intRowStart = usedRange->property("Row").toInt();//起始行数
                    int intColStart = usedRange->property("Column").toInt(); //起始列数
                    QAxObject *rows, *columns;
                    rows = usedRange->querySubObject("Rows");//行
                    columns = usedRange->querySubObject("Columns");//列
                    int intRow = rows->property("Count").toInt();//行数
                    int intCol = columns->property("Count").toInt();//列数
                    //起始行列号
                    qDebug()<<intRowStart;
                    qDebug()<<intColStart;
                    //行数和列数
                    qDebug()<<intRow;
                    qDebug()<<intCol;
                    int a,b;
                    a=intRow-intRowStart+1,b=intCol-intColStart+1;
                    QByteArray text[a][b];
                    int coerow=0,coecol=0;
                    for (int i = intRowStart; i < intRowStart + intRow; i++,coerow++)
                        {
                            coecol=0;//务必是要恢复初值的
                            for (int j = intColStart; j < intColStart + intCol; j++,coecol++)
                            {
                                cell = excel->querySubObject("Cells(Int, Int)", i, j );
                                QVariant cellValue = cell->dynamicCall("value");
                                text[coerow][coecol]=cellValue.toByteArray();//QVariant转换为QByteArray
                                exceldata[coerow][coecol]=QString(text[coerow][coecol]);//QByteArray转换为QString
                            }
                        }
                    workbook->dynamicCall( "Close(Boolean)", false );
                    excel->dynamicCall( "Quit(void)" );
                    delete excel;
                    QMessageBox::warning(this,tr("读取情况"),tr("读取完成!"),QMessageBox::Yes);
                }
                file.close();
            }


}

QTableWidget绘制表格

void Dialog::putoutsend()
{

    table->setRowCount(6);    //设置行数
    table->setColumnCount(4); //设置列数
    table->move(20,20);
    //QTableWidget *tableWidget = new QTableWidget(10,5); //这个可以代替上两行


    table->setWindowTitle("QTableWidget & Item");
    table->resize(550, 400);

    setFixedSize(580,500);//固定窗口的大小
    //设置表格的表头
    QStringList header;
    header<<"1"<<"2"<<"3"<<"4";
    table->setHorizontalHeaderLabels(header);//设置表头(横)

    QStringList header2;
    header2<<"abc"<<"jmc"<<"Month"<<"Description"<<"Month"<<"Description";
    table->setVerticalHeaderLabels(header2);//设置表头(竖)

    //设置行列的宽和高
    /*for(int i = 0;i<10;++i)
    {
        Table_one->setRowHeight(i,40);

    }
    for(int i = 0;i<4;++i)
    {
        Table_one->setColumnWidth(i,65);

    }*/

    //设置表格内容(横坐标,纵坐标,QTableWidgetItem("输入文本"))
    //如果有要输入的需要,就是把处理过后的数据输出到报表中,即可使用这个功能

    //显示阶段
    for(int i=0;i<6;i++)
    {
        for(int j=0;j<4;j++)
        {
           table->setItem(i,j,new QTableWidgetItem(exsheet[i][j]));//注意,exsheet为QString类型的数组,只有QString类型才能显示,如果是double等数值类型,请使用QString::number转换类型
           qDebug()<<exsheet[i][j];

        }
    }

    table->setEditTriggers(QAbstractItemView::NoEditTriggers);//只读模式
   
    //表列随着表格变化而自适应变化
    table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

    //表行随着表格变化而自适应变化
    table->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);

    //将widget设置为窗口的中心控件
    //setCentralWidget(widget);


    //将读到的内容存在tableDate中
    arow=table->rowCount(),acol=table->columnCount();
    //QString tabeDate[table->rowCount()][table->columnCount()];//读取行数和列数
    for(int i=0; i<arow; i++)
    {
        for(int j=0; j<acol; j++)
        {
            senddata[i][j] = table->item(i, j)->text();//将数据读入一个全局变量中,方便之后进行保存操作
        }
    }
    table->show();
}

行列高宽的设置

自定义:

    for(int i = 0;i<10;++i)
    {
        Table_one->setRowHeight(i,40);

    }
    for(int i = 0;i<4;++i)
    {
        Table_one->setColumnWidth(i,65);

    }

其他类型:

    Table_one->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    Table_one->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);

其中的参数:ResizeMode mode的具体内容如下: 

从上到下分别为:手动调整,固定大小,根据内容分配(两种模式)。 

 

读取QTableWidget绘制的表格中的内容

void Dialog::savedata()
{
    QString filepath = QFileDialog::getSaveFileName(table, "保存",
    QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
                        "Excel 文件(*.xls *.xlsx)");
    //返回应写入文件类型的目录,返回包含用户文档文件的目录
    if (filepath!="")
    {
        QAxObject *excel = new QAxObject(this);//建立excel操作对象
        if (excel->setControl("Excel.Application")) //连接Excel控件
        {
            excel->dynamicCall("SetVisible (bool Visible)","false");//不显示窗体
            excel->setProperty("DisplayAlerts", false);
            //不显示任何警告信息。如果为true那么在关闭是会出现类似“文件已修改,是否保存”的提示
            QAxObject *workbooks = excel->querySubObject("WorkBooks");//获取工作簿集合
            workbooks->dynamicCall("Add");//新建一个工作簿
            QAxObject *workbook = excel->querySubObject("ActiveWorkBook");//获取当前工作簿
            QAxObject *worksheet = workbook->querySubObject("Worksheets(int)", 1);
            //访问excel中的工作表中第一个单元格


            //保存到execl,总的来说就是操作单元格,赋值的赋值,改格式大的改格式,遵循一点,修改颜色,行高,字体这些属性,务必是要设定一个修改范围再动手

            int i,j,colcount=table->columnCount(),rowcount=table->rowCount();//表格的列数
            QAxObject *cell,*col;
            //标题行
            cell=worksheet->querySubObject("Cells(int,int)", 1, 1);//操作单元格
            cell->dynamicCall("SetValue(const QString&)", "title");//设置标题内容,SetValue(const QString&),后面就是打印的文本内容
            cell->querySubObject("Font")->setProperty("Size", 18);
            //前半句是获取单元格字体,后半句是设置字体大小
            //cell->setProperty("Name", QStringLiteral("华文彩云"));//设置单元格字体

           // cell->setProperty("Italic", true);  //设置单元格字体斜体
            //cell->setProperty("Underline", 2);  //设置单元格下划线

            //获取这个范围内的单元格,直接调整行高
            worksheet->querySubObject("Range(const QString&)", "1:1")->setProperty("RowHeight", 60);
            //合并标题行
            QString cellTitle;

            cellTitle.append("A1:");//初始原点
            cellTitle.append(QChar(colcount  + 'A'));//终止列(一般都要总列数减去1,Qchar)

            cellTitle.append(QString::number(1));//终止行()Qstring

            //下述第一条,可以理解为对sellTitle这个范围的单元格进行操作
            QAxObject *range = worksheet->querySubObject("Range(const QString&)", cellTitle);
            range->setProperty("WrapText", true);//自动换行
            range->setProperty("MergeCells", true);//合并单元格
            //排版:居中
            //左对齐(xlLeft):-4131  居中(xlCenter):-4108  右对齐(xlRight):-4152
            range->setProperty("HorizontalAlignment", -4108);//xlCenter
            range->setProperty("VerticalAlignment", -4108);//xlCenter

            //行的表头
            for(i=0;i<colcount;i++)
            {
                //读取
                QString columnName;
              
                //修改内容
                cell=worksheet->querySubObject("Cells(int,int)", 2, i+2);//确定操作单元格,将数据保存到哪儿,行的表头,所以要从第二列开始

                columnName=table->horizontalHeaderItem(i)->text();//获取此处的文本内容,i是列号,就是第几列中的文本内容

                cell->dynamicCall("SetValue(const QString&)", columnName);//打印到excel

                //前半句是获取单元格字体,后半句是字体加粗
                cell->querySubObject("Font")->setProperty("Bold", true);

                cell->querySubObject("Interior")->setProperty("Color",QColor(191, 191, 191));
                cell->setProperty("HorizontalAlignment", -4108);//xlCenter
                cell->setProperty("VerticalAlignment", -4108);//xlCenter
            }
            //列的表头
            for(i=0;i<rowcount;i++)
            {
                //读取
                QString rowName;
               
                //修改内容
                cell=worksheet->querySubObject("Cells(int,int)", i+3, 1);//确定操作单元格,将数据保存到哪儿列的表头,需要从第三行开始,所以+3

                rowName=table->verticalHeaderItem(i)->text();
                        //horizontalHeaderItem(i)->text();//获取此处的文本内容,i是列号,就是第几列中的文本内容

                cell->dynamicCall("SetValue(const QString&)",rowName);//打印到excel

                //前半句是获取单元格字体,后半句是字体加粗
                cell->querySubObject("Font")->setProperty("Bold", true);

                cell->querySubObject("Interior")->setProperty("Color",QColor(191, 191, 191));
                cell->setProperty("HorizontalAlignment", -4108);//xlCenter
                cell->setProperty("VerticalAlignment", -4108);//xlCenter
            }


            //数据区
            for(i=0;i<table->rowCount();i++)
            {
                for (j=0;j<colcount;j++)
                {
                    QString rowdata[4];
                    rowdata[j]=table->item(i,j)->text();
                    worksheet->querySubObject("Cells(int,int)", i+3, j+2)->dynamicCall("SetValue(const QString&)",
                                                                                      rowdata[j]);
                }
            }
            //画框线
            QString lrange;
            lrange.append("A2:");//起始行列位置(原点),修改后,开始的位置会变化,A2的从A2那一行开始,A3就是第三行
            lrange.append(colcount + 'A');//终止列,-1,就是正常状态,改变colcount后面的

            lrange.append(QString::number(table->rowCount() +2));//终止行
            //这个起始的位置,相当于原点,最终给一个行列的终止位置就行

            range = worksheet->querySubObject("Range(const QString&)", lrange);
            //querySubObject("Borders")是对边框的设置,必须有
            range->querySubObject("Borders")->setProperty("LineStyle", QString::number(1));
            range->querySubObject("Borders")->setProperty("Color", QColor(0, 0, 0));//颜色的设置

            //调整数据区行高
            QString rowsName;
            rowsName.append("A2:");
            rowsName.append(colcount + 'A');

            //上面两句也可以变成
            //rowsName.append("2:");//起始列

            rowsName.append(QString::number(table->rowCount() + 2));//终止行

            range = worksheet->querySubObject("Range(const QString&)", rowsName);
            range->setProperty("RowHeight", 20);//设置行高
            range->setProperty("ColumnWidth", 60);  //设置单元格列宽

            workbook->dynamicCall("SaveAs(const QString&)",QDir::toNativeSeparators(filepath));
            //保存至filepath
            //并将'/'分隔符转换为适合底层操作系统的分隔符。
            //在Windows上,toNativeSeparators(“c:/ winnt / system32”)返回“c:\ winnt \ system32”。
            //将filepath的路径

            workbook->dynamicCall("Close()");//关闭工作簿
            excel->dynamicCall("Quit()");//关闭excel
            delete excel;
            excel=NULL;
            if (QMessageBox::question(NULL,"完成","文件已经导出,是否现在打开?",QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes)
            {
                //QDesktopServices类提供了访问常见桌面服务的方法。
                //QDesktopServices::openUrl(const QUrl &url)
                //打开指定url中的文件
                //QDir目录结构及其内容的访问。
                QDesktopServices::openUrl(QUrl("file:///" + QDir::toNativeSeparators(filepath)));
            }
        }
        else
        {
            QMessageBox::warning(NULL,"错误","未能创建 Excel 对象,请安装 Microsoft Excel。",QMessageBox::Apply);
        }
}

整体程序布局(仅供参考)

整个头文件及源文件分布如下

mainwindow.h包含以下头文件

#include <QMainWindow>
#include <QAxObject>//excel必须包含此头文件
#include<QString>

#include <QFileDialog>//文件操作必要

#include<QTableWidget>//绘制表格需要

#include"dialog.h"//dialog源文件的头文件

#include<QDebug>

dialog.h包含以下头文件

#include <QDialog>
#include<QHBoxLayout>
#include <QMainWindow>
#include<QTextEdit>
#include<QWidget>
#include<QTableWidget>
#include<QDebug>
#include<QString>
#include <QFileDialog>
#include<QAxObject>
#include<QMessageBox>
#include <QDesktopServices>

上述的读取Excel代码位于mainwindow.cpp中

其他功能的代码位于dialog.cpp中

所以在选择查看报表的时候,会另外弹出一个QDialog类型的窗口显示内容,也可将以上功能都放置于同一个cpp中。


补充:

如果现在TableWidget中删除某行,执行以下操作

   int rowIndex = table->currentRow();
   if(rowIndex == -1)
    {
        QMessageBox::warning(this,"Warning!","请选择一行再删除!",QMessageBox::Yes);

    }
    else if(rowIndex != -1)
    {
        table->removeRow(rowIndex);

    }

解释:需要写入保护程序,当TableWidget没有行被选中的时候,返回值是-1,所以当有返回值不是-1,也就是我们选中某一行的时候,再进行删除操作,否则在调用过程中,可能会有如下错误。

行数: -1
ASSERT failure in QList<T>::at: "index out of range", file F:\Qt\5.9.5\mingw53_32\include/QtCore/qlist.h, line 541
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
Error - 
RtlWerpReportException failed with status code :-1073741823. Will try to launch the process directly

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值