读取已有数据的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_OBJECTpublic: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转换为QByteArrayexceldata[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):-4152range->setProperty("HorizontalAlignment", -4108);//xlCenterrange->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);//xlCentercell->setProperty("VerticalAlignment", -4108);//xlCenter}//列的表头for(i=0;i<rowcount;i++){//读取QString rowName;//修改内容cell=worksheet->querySubObject("Cells(int,int)", i+3, 1);//确定操作单元格,将数据保存到哪儿列的表头,需要从第三行开始,所以+3rowName=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);//xlCentercell->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()");//关闭exceldelete 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