技术标签: QT
这里不说如何打开或生成excel,着重说说如何快速读取excel。
网上搜到用Qt操作excel的方法,读取都是使用类似下面这种方法进行:
QVariant ExcelBase::read(int row, int col)
{
QVariant ret;
if (this->sheet != NULL && ! this->sheet->isNull())
{
QAxObject* range = this->sheet->querySubObject("Cells(int, int)", row, col);
//ret = range->property("Value");
ret = range->dynamicCall("Value()");
delete range;
}
return ret;
}
读取慢的根源就在于sheet->querySubObject("Cells(int, int)", row, col)
试想有10000个单元就得调用10000次querySubObject
,网络上90%的教程都没说这个querySubObject
产生的QAxObject*
最好进行手动删除,虽然在它的父级QAxObject
会管理它的内存,但父级不析构,子对象也不会析构,若调用10000次,就会产生10000个QAxObject
对象 得益于QT快速读取数据量很大的Excel文件此文,下面总结如何快速读写excel
原则是一次调用querySubObject
把所有数据读取到内存中
VBA中可以使用UsedRange
把所有用到的单元格范围返回,并使用属性Value
把这些单元格的所有值获取。
这时,获取到的值是一个table,但Qt把它变为一个变量QVariant来储存,其实实际是一个QList<QList<QVariant> >
,此时要操作里面的内容,需要把这个QVariant
转换为QList<QList<QVariant> >
先看看获取整个单元格的函数示意(这里ExcelBase是一个读写excel的类封装):
QVariant ExcelBase::readAll()
{
QVariant var;
if (this->sheet != NULL && ! this->sheet->isNull())
{
QAxObject *usedRange = this->sheet->querySubObject("UsedRange");
if(NULL == usedRange || usedRange->isNull())
{
return var;
}
var = usedRange->dynamicCall("Value");
delete usedRange;
}
return var;
}
代码中this->sheet
是已经打开的一个sheet,再获取内容时使用this->sheet->querySubObject("UsedRange");
即可把所有范围都获取。
下面这个castVariant2ListListVariant函数把QVariant
转换为QList<QList<QVariant> >
///
/// \brief 把QVariant转为QList<QList<QVariant> >
/// \param var
/// \param res
///
void ExcelBase::castVariant2ListListVariant(const QVariant &var, QList<QList<QVariant> > &res)
{
QVariantList varRows = var.toList();
if(varRows.isEmpty())
{
return;
}
const int rowCount = varRows.size();
QVariantList rowData;
for(int i=0;i<rowCount;++i)
{
rowData = varRows[i].toList();
res.push_back(rowData);
}
}
这样excel的所有内容都转换为QList<QList<QVariant>>
保存,其中QList<QList<QVariant> >
中QList<QVariant>
为每行的内容,行按顺序放入最外围的QList中。
同理,能通过QAxObject *usedRange = this->sheet->querySubObject("UsedRange");
实现快速读取,也可以实现快速写入
快速写入前需要些获取写入单元格的范围:Range(const QString&)
如excel的A1为第一行第一列,那么A1:B2就是从第一行第一列到第二行第二列的范围。
要写入这个范围,同样也是通过一个与之对应的QList<QList<QVariant> >
,具体见下面代码:
///
/// \brief 写入一个表格内容
/// \param cells
/// \return 成功写入返回true
/// \see readAllSheet
///
bool ExcelBase::writeCurrentSheet(const QList<QList<QVariant> > &cells)
{
if(cells.size() <= 0)
return false;
if(NULL == this->sheet || this->sheet->isNull())
return false;
int row = cells.size();
int col = cells.at(0).size();
QString rangStr;
convertToColName(col,rangStr);
rangStr += QString::number(row);
rangStr = "A1:" + rangStr;
qDebug()<<rangStr;
QAxObject *range = this->sheet->querySubObject("Range(const QString&)",rangStr);
if(NULL == range || range->isNull())
{
return false;
}
bool succ = false;
QVariant var;
castListListVariant2Variant(cells,var);
succ = range->setProperty("Value", var);
delete range;
return succ;
}
此函数是把数据从A1开始写
函数中的convertToColName
为把列数,转换为excel中用字母表示的列数,这个函数是用递归来实现的:
///
/// \brief 把列数转换为excel的字母列号
/// \param data 大于0的数
/// \return 字母列号,如1->A 26->Z 27 AA
///
void ExcelBase::convertToColName(int data, QString &res)
{
Q_ASSERT(data>0 && data<65535);
int tempData = data / 26;
if(tempData > 0)
{
int mode = data % 26;
convertToColName(mode,res);
convertToColName(tempData,res);
}
else
{
res=(to26AlphabetString(data)+res);
}
}
///
/// \brief 数字转换为26字母
///
/// 1->A 26->Z
/// \param data
/// \return
///
QString ExcelBase::to26AlphabetString(int data)
{
QChar ch = data + 0x40;//A对应0x41
return QString(ch);
}
void CMainWindow::openExcel(QString fileName)
{
QAxObject excel("Excel.Application");
excel.setProperty("Visible", false);
QAxObject *work_books = excel.querySubObject("WorkBooks");
work_books->dynamicCall("Open(const QString&)", fileName);
QAxObject *work_book = excel.querySubObject("ActiveWorkBook");
QAxObject *work_sheets = work_book->querySubObject("Sheets"); //Sheets也可换用WorkSheets
int sheet_count = work_sheets->property("Count").toInt(); //获取工作表数目
if (sheet_count > 0)
{
QAxObject *work_sheet = work_book->querySubObject("Sheets(int)", 1);
ui.label->setText("文件数据读取中...");
QVariant var = readAll(work_sheet);
castVariant2ListListVariant(var);
}
work_book->dynamicCall("Close(Boolean)", false); //关闭文件
excel.dynamicCall("Quit(void)"); //退出
}
QVariant CMainWindow::readAll(QAxObject *sheet)
{
QVariant var;
if (sheet != NULL && !sheet->isNull())
{
QAxObject *usedRange = sheet->querySubObject("UsedRange");
if (NULL == usedRange || usedRange->isNull())
{
return var;
}
var = usedRange->dynamicCall("Value");
delete usedRange;
}
return var;
}
void CMainWindow::castVariant2ListListVariant(const QVariant &var)
{
QVariantList varRows = var.toList();
if (varRows.isEmpty())
{
return;
}
const int rowCount = varRows.size();
QVariantList rowData;
for (int i = 0; i < rowCount; ++i)
{
rowData = varRows[i].toList();
if (i == 0)
{
QStringList headers;
for each (auto item in rowData)
{
QString value = item.toString();
headers.append(value);
}
ui.tableWidget->setColumnCount(headers.size()); //设置列数
ui.tableWidget->setHorizontalHeaderLabels(headers);
}
else
{
int row = ui.tableWidget->rowCount();
ui.tableWidget->setRowCount(row + 1);
for (int j = 0; j < rowData.size(); j++)
{
QString value = rowData[j].toString();
QTableWidgetItem *item = new QTableWidgetItem(value);
ui.tableWidget->setItem(row, j, item);
}
}
}
}
文章浏览阅读7.1k次。转载自: http://www.vchome.net/tech/datastruct/datasf5.htmDES(数据加密标准)在1977年被美国国家标准技术协会认可成为均衡加密算法的标准,用于加密非机密的信息.des广泛应用于各个行业的加密领域,如银行业.这麽样一种古老的加密算法,到今天还有人在用,真是让人想不明白.这种按照摩尔定律早该淘汰的东西,怎么可能会没有办法破解呢??以
文章浏览阅读1.4k次。tracepath tracepath指令可以追踪数据到达目标主机的路由信息,同时还能够发现MTU值。它跟踪路径到目的地,沿着这条路径发现MTU。它使用UDP端口或一些随机端口。它类似于Traceroute,只是不需要超级用户特权,并且没有花哨的选项。tracepath 6很好地替代了tracerout 6和Linux错误队列应用程序的典型示例。tracepath的情况更糟,因为商用IP路由器在ICMP错误消息中没有返回足够的信息。很可能,当它们被更新的时候,它会改变 此命令的适用..._tracepath gateway
文章浏览阅读92次。uojdescription给出\(n\)个变换,第\(i\)个变换是将区间中\(l_i,r_i\)的数\(x\)变成\((a_ix+b_i)\mod m\)。每次会新增一个变换,或者查询询问如果进行编号\([s,t]\)的操作,第\(k\)个数会变成多少。\(n\le10^5,q\le6\times10^5\)sol二进制分组。按顺序把变化插入线段树,如果线段树的某个满了..._清华集训玄学
文章浏览阅读2.4k次。Platform: RK3368OS: Android 6.0Kernel: 3.10.0rk3368的Chip ID是从芯片eFuse中读取出来的。然后赋值给system_serial_low和system_serial_high./proc/cpuinfo中的Serial字段就是用的这两个值。arch/arm64/boot/dts/rk3368.dtsi efuse_256@ff..._rk3568 chip id
文章浏览阅读1.2k次。关于Guava-Retry重试工具的使用1 guava-retry的简介2 guava-retry的使用1 导入maven依赖2 添加一个重试方法3 添加测试类3 总结官网地址:https://github.com/rholder/guava-retryinghttps://codechina.csdn.net/mirrors/rholder/guava-retrying?utm_source=csdn_github_accelerator1 guava-retry的简介在日常的一些场景中, 很多_guava retry demo
文章浏览阅读7.5k次,点赞10次,收藏45次。The Leaky Integrate-and-Fire (LIF) Neuron Mode基础知识_leaky integrate-and-fire
文章浏览阅读376次。文章目录第二周 Python基本图形绘制2.1 深入理解python语言2.2 实例2:python蟒蛇绘制2.3 模块1:turtle库的使用2.4 turtle程序语法元素分析2.1 深入理解python语言2.2 实例2:python蟒蛇绘制2.3 模块1:turtle库的使用2.4 turtle语法元素分析turtle八边形绘制turtle八角形绘制第二周 Python基本图形绘制2...._海龟编辑器绘制八角形
文章浏览阅读711次。习题5.3-5 从n^3个数字中进行放回抽样,共抽nci_假设一个n个记录中每个的关键字都介于1到k之间。说明如何修改计数排序,使得可以在
文章浏览阅读3.3k次,点赞2次,收藏6次。1.Command初始化:Agent、CATDialogState、Transition实现状态机Agent:CATPathElementAgent可获取元素;CATIndicationAgent可实现鼠标单击双击行为。CATDialogState状态机AddTransition状态机跳转条件:IsLastModifiedAgentCondition只要Agent变化就触发_islastmodifiedagentcondition
文章浏览阅读6.6k次,点赞13次,收藏36次。打算做一个个人网站,新建umi项目的时候发现了ant-design-pro这个项目模板,打开一看貌似挺有用,记录一下一步步探索的开发攻略,包括如何修改主题内容、添加路由新页面,以及将md文档文件插入页面(用于博客网站制作)。_ant+design+项目
文章浏览阅读521次。【VSCode】Windows 无MSVC 安装 MinGW Clang | Clangdllvm-mingw-clang_msys2安装clang
文章浏览阅读765次。各种排序算法时间复杂度各种排序算法比较 各种常用排序算法 类别 排序方法 时间复杂度 空间复杂度 稳定性 复杂性 特点 最好 平均 最坏 辅..._排序算法 平均时间复杂度怎么计算