参考:
从QTableView中导出数据到excel(一)
qt QTableWidget&&QTableView 导出数据到excel
Qt导出Excel的简单实现
QT调用Excel时,设置自动调整列宽和所有单元格居中
QT操作Excel封装类(包含高级功能:合并单元格,文本及单元格格式设定等)
Qt QAxObject 以文本方式保存excel
AutoFilter.Range 属性 (Excel)
Borders 对象 (Excel)
COM 介绍:
在 Windows-Based 程序中使用 COM
Component Object Model (COM)
初始化 COM 库
//头文件中部分成员
class Widget: public QDialog
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
void init(void);
void taskOver(void);
void exportData(void);
void createExcelFile(const QString &strFileName, const int rowNum, const int colNum);
void createHTMLFile(const QString &strFileName, const int rowNum, const int colNum);
void createCSVFile(const QString &strFileName, const int rowNum, const int colNum);
private:
QStandardItemModel *m_model;
QFutureWatcher<void> m_watcher;
enum FileType
{
TYPE_EXCEL,
TYPE_HTML,
TYPE_CSV
} m_fileType; //导出的文件类型
}
//初始化 一些数据
void Widget::init()
{
m_model = new QStandardItemModel(this);
connect(&m_watcher, SIGNAL(finished()), this, SLOT(taskOver()));
QFuture<void> future = QtConcurrent::run(this, &DataStatistics::initData);
m_watcher.setFuture(future);
}
//初始化表格,获取数据
void Widget::initData()
{
}
void Widget::exportData()
{
//获取表格的行列
int rowNum = m_model->rowCount();
int colNum = m_model->columnCount();
if (rowNum < 1 || colNum < 1)
return;
//获取保存文件的路径,想要将导出的文件保存的路径
QString strPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
auto getFileName = [&]() -> bool
{
strFileName = QFileDialog::getSaveFileName(this, tr("Save File"),strPath, strFileType);
if (strFileName.isEmpty())
{
//文件名为空,提示
return false;
}
return true;
};
QFuture<void> future;
switch (m_fileType)
{
case TYPE_EXCEL:
{
if (!getFileName())
return;
future = QtConcurrent::run(this, &Widget::createExcelFile, tr("Excel File (*.xls *.xlsx)"), rowNum, colNum);
break;
}
case TYPE_HTML:
{
if (!getFileName())
return;
future = QtConcurrent::run(this, &Widget::createHTMLFile, tr("HTML File(*.htm *.html)"), rowNum, colNum);
break;
}
case TYPE_CSV:
{
if (!getFileName())
return;
future = QtConcurrent::run(this, &Widget::createCSVFile, tr("CSV File(*.csv)"), rowNum, colNum);
break;
}
default:
break;
}
m_watcher.setFuture(future);
}
//导出 excel
void Widget::createExcelFile(const QString &strFileName, const int rowNum, const int colNum)
{
if (strFileName.isEmpty())
return;
//调用 COM 库之前初始化库
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr))
{
//失败提示
return;
}
//新建COM对象 excel
QAxObject *excel = new QAxObject();
if (excel == nullptr)
{
//失败提示
return;
}
//连接excel控件 Office 为 Excel.Application,WPS为 KET.Application,使用时WPS用Excel.Application也能找到
if (excel->setControl("Excel.Application")) //连接excel控件
{
}
else if (excel->setControl("KET.Application")) //WPS 表格
{
}
else
{
//初始化失败提示
return;
}
excel->dynamicCall("SetVisible(bool)", false); //true可以显示窗体,看见创建数据过程
excel->setProperty("DisplayAlerts", false); //不显示警告信息
QAxObject *workbooks = excel->querySubObject("Workbooks"); //Application对象(excel)的子对象为工作簿集合
if (workbooks == nullptr)
{
//获取Workbooks失败提示
return;
}
workbooks->dynamicCall("Add"); //新建一个工作簿
QAxObject *workbook = excel->querySubObject("ActiveWorkBook"); //获取当前工作簿
//当前工作簿子对象为Worksheets(集合),这里设置所用为第一个 Worksheet
QAxObject *worksheet = workbook->querySubObject("Worksheets(int)", 1);
//表格标题 第一行 设置标题名字,字号,加粗
QAxObject *cells;
cells = worksheet->querySubObject("Cells(int, int)", 1, 1);
cells->dynamicCall("SetValue(const QString&)", tr("Table title");
cells->querySubObject("Font")->setProperty("Size", 20);
cells->querySubObject("Font")->setProperty("Bold", true);
//标题行高
worksheet->querySubObject("Range(const QString&)", "1:1")->setProperty("RowHeight", 35);
//合并标题列
QString strCellTitle("A1:");
strCellTitle.append(QChar(colNum-1 + 'A'));
strCellTitle.append(QString::number(1));
QAxObject *range = worksheet->querySubObject("Range(const QString&)", strCellTitle);
range->setProperty("WrapText", true);
range->setProperty("MergeCells", true);
//使用的全部范围 属性设置 要在写入数据前设置文本格式为纯文本,否则写入 2/3 会变成 2月3号 日期形式
QString strRange("A1:");
strRange.append(colNum-1 + 'A');
strRange.append(QString::number(rowNum+2));
range = worksheet->querySubObject("Range(const QString&)", strRange);
range->setProperty("NumberFormatLocal", "@"); //文字格式为纯文本,要在写入数据前设置
//表格数据写入
//表头 导出表格第二行
for (int col = 0; col < colNum; ++col)
{
cells = worksheet->querySubObject("Cells(int, int)", 2, col+1);
cells->dynamicCall("SetValue(const QString&)", m_model->headerData(col, Qt::Horizontal).toString(););
}
//表格数据 从第三行起
for (int row = 0; row < rowNum; ++row)
{
for (int col = 0; col < colNum; ++col)
{
QString strValue = m_model->data(m_model->index(row, col)).toString(); //每项的数据
cells = worksheet->querySubObject("Cells(int,int)",row+3,col+1);
cells->dynamicCall("SetValue(const QString&)",strValue);
}
}
//调整表头行高 , 颜色 字体加粗
strRange = QString("A2:");
strRange.append(colNum-1 + 'A');
strRange.append(QString::number(2));
range = worksheet->querySubObject("Range(const QString&)", strRange);
range->setProperty("RowHeight", 30);
range->querySubObject("Interior")->setProperty("Color", QColor(127, 255, 212));
range->querySubObject("Font")->setProperty("Bold", true);
//调整数据区行高
strRange = QString("3:");
strRange.append(QString::number(rowNum + 2));
range = worksheet->querySubObject("Range(const QString&)", strRange);
range->setProperty("RowHeight", 25);
//调整表格列宽
range = worksheet->querySubObject("UsedRange");
cells = range->querySubObject("Columns");
cells->dynamicCall("AutoFit"); //自适应列宽
//文字居中显示
range->setProperty("HorizontalAlignment", -4108);
range->setProperty("VerticalAlignment", -4108); //居中 -4108 为 xlHAlignCenter
//表格数据区外画框,范围没问题,但最后表格会多出很多列有边框
range->querySubObject("Borders")->setProperty("LineStyle", QString::number(1));
range->querySubObject("Borders")->setProperty("Color", QColor(0, 0, 0));
//保存到excel
worksheet->dynamicCall("SaveAs(const QString&)", QDir::toNativeSeparators(strFileName));
//关闭
worksheet->dynamicCall("Close()");
excel->dynamicCall("Quit()");
FREE_PTR(excel)
//打开文件
if (!strFileName.isEmpty())
QDesktopServices::openUrl("file:///" + QDir::toNativeSeparators(strFileName));
CoUninitialize();
return;
}
//导出 html 文件
void Widget::createHTMLFile(const QString &strFileName, const int rowNum, const int colNum)
{
if (strFileName.isEmpty())
return;
QFile file(strFileName);
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate))
return;
QString strTime = QDateTime::currentDateTime().toString(DATE_TIME_FORMAT);
QString strColor = "bgcolor=\"#7FFFD4\"";
QString strEndline = "\n";
QTextStream stream(&file);
stream << "" << "\n";
stream << "" << strEndline;
stream << "" << strEndline;
stream << "" << strEndline;
//标题
stream << "" << strEndline;
stream << "" << strEndline;
stream << "" << QString(tr("Table titile")) << " " << strEndline; //表格标题
stream << " " << strEndline;
stream << "" << strEndline;
stream << ""
<< QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")
<< " " << strEndline; //创建表格时间
stream << " " << strEndline;
stream << "
" << strEndline;
//表头 颜色
stream << "
"bgcolor=\"#ffffff\" align=\"center\" height=\"40\">"
<< strEndline;
stream << "" << strEndline;
for (int col = 0; col < colNum; ++col)
{
stream << " << strColor << ">"
<< m_model->headerData(col, Qt::Horizontal).toString()
<< " " << strEndline;
}
stream << " " << strEndline;
//数据 交替显示颜色 一行有颜色,下一行无颜色
bool bHasColor = false;
for (int row = 0; row < rowNum; ++row)
{
stream << "" << strEndline;
for (int col = 0; col < colNum; ++col)
{
stream << ";
if (bHasColor)
stream << strColor;
stream << ">" << m_model->data(m_model->index(row, col)).toString()
<< " " << strEndline;
}
stream << " " << strEndline;
bHasColor = !bHasColor;
}
stream << "
" << strEndline;
stream << "" << strEndline;
stream << "" << strEndline;
stream << "" << strEndline;
file.close();
//打开文件
if (!strFileName.isEmpty())
QDesktopServices::openUrl("file:///" + QDir::toNativeSeparators(strFileName));
}
//导出 CSV 文件
void Widget::createCSVFile(const QString &strFileName, const int rowNum, const int colNum)
{
if (strFileName.isEmpty())
return;
QFile file(strFileName);
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate))
return;
QTextStream stream(&file);
stream.setCodec("ANSI"); //UTF-8格式excel打开乱码
QString strContents("");
auto addNewLine = [&strContents]()
{
if (strContents.size() > 0)
strContents = strContents.replace(strContents.size()-1, 1, "\n");
};
//标题
strContents = QString(tr("Table title\n"));
//表头
for (int col = 0; col < colNum; ++col)
{
strContents += m_model->headerData(col, Qt::Horizontal).toString() + ",";
}
addNewLine();
//数据
for (int row = 0; row < rowNum; ++row)
{
for (int col = 0; col < colNum; ++col)
{
QString strData = m_model->data(m_model->index(row, col)).toString();
strContents += strData + ",";
}
addNewLine();
}
stream << strContents;
file.close();
//打开文件
if (!strFileName.isEmpty())
QDesktopServices::openUrl("file:///" + QDir::toNativeSeparators(strFileName));
}
安装 office 时导出 excel 正常,换成 wps 后发现会崩溃,原来软件以管理员模式运行,换成普通模式运行后正常
(管理员账户登录,有管理员权限的普通账户,控制面板中用户账户控制设置为从不通知
,软件需要管理员权限,此时软件虽可运行,但访问注册表时需要权限)
解决:
1、管理员账户登录,软件改为不需要管理员权限运行,正常使用
2、Administrator
账户登录,软件仍以管理员权限运行(或者无需管理员权限运行),可正常使用
3、管理员账号登录,软件仍以管理员权限运行(或者无需管理员权限运行),本地组策略编辑器
中修改UAC,
将 以管理员批准模式运行所有管理员
设置为禁用,重启生效后,可正常运行
注意:之前控制面板设置从不通知
,但本地组策略编辑器
中以管理员批准模式运行所有管理员
设置为已启用
时不能使用,要改为禁用
才能正常运行,
此时需要管理员权限的程序无盾牌标志,不受UAC控制
分析之前环境无法正常使用原因:
根据解决方案第二点,Administrator
账户登录,软件仍以管理员权限运行(或者无需管理员权限运行),可正常使用,
此时将本地组策略编辑器
中用于内置管理员账户的管理员批准模式
和 以管理员批准模式运行所有管理员
均设置为已启用
,
即运行需要管理员权限的程序时也会弹框需要提权,重启生效后运行需要管理员权限的软件,此时软件有盾牌,无法正常导出表格,第一次运行时会弹出一个访问注册表需要提权的对话框,
并打开注册表中WPS表格COM组件(KET.Application)位置
:
虽然选择提权,但仍不能正常导出表格,且后面再次运行软件不会弹出注册表提权的对话框
猜测原因:
管理员批准模式运行时,在提升到管理员权限并完成当前操作后,当前用户的操作权仍然会恢复到标准操作权限,即使当前用户管理员用户(管理员组的普通用户),
而软件以管理员权限运行那一刻提权后恢复普通用户权限,而软件中有些操作需要管理员权限时,就会出错,不明白为什么需要权限时不会弹框要求提权