Qt 表格导出数据为 excel html csv

Qt 表格导出数据为 excel html csv

  • 示例
  • 使用WPS导出出错问题

参考:

从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 <<""<< strEndline;//表格标题 stream <<""<< strEndline; stream <<""<< strEndline; stream <<""<< strEndline;//创建表格时间 stream <<""<< strEndline; stream <<"
" << QString(tr("Table titile")) << "
" << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm") << "
"
<< strEndline; //表头 颜色 stream << ""bgcolor=\"#ffffff\" align=\"center\" height=\"40\">"<< strEndline; stream <<""<< strEndline;for(int col =0; col < colNum;++col){ stream <<""<< strEndline;} stream <<""<< strEndline;//数据 交替显示颜色 一行有颜色,下一行无颜色bool bHasColor =false;for(int row =0; row < rowNum;++row){ stream <<""<< strEndline;for(int col =0; col < colNum;++col){ stream <<""<< strEndline;} stream <<""<< strEndline; bHasColor =!bHasColor;} stream <<"
<< strColor << ">" << m_model->headerData(col, Qt::Horizontal).toString() << "
; if (bHasColor) stream << strColor; stream << ">" << m_model->data(m_model->index(row, col)).toString() << "
"
<< 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)); }

使用WPS导出出错问题

安装 office 时导出 excel 正常,换成 wps 后发现会崩溃,原来软件以管理员模式运行,换成普通模式运行后正常
(管理员账户登录,有管理员权限的普通账户,控制面板中用户账户控制设置为从不通知,软件需要管理员权限,此时软件虽可运行,但访问注册表时需要权限)

解决:
1、管理员账户登录,软件改为不需要管理员权限运行,正常使用
2、Administrator 账户登录,软件仍以管理员权限运行(或者无需管理员权限运行),可正常使用
3、管理员账号登录,软件仍以管理员权限运行(或者无需管理员权限运行),本地组策略编辑器中修改UAC,
以管理员批准模式运行所有管理员 设置为禁用,重启生效后,可正常运行
Qt 表格导出数据为 excel html csv_第1张图片

设置禁用后,控制面板处用户账户控制设置会变成从不通知
Qt 表格导出数据为 excel html csv_第2张图片

注意:之前控制面板设置从不通知,但本地组策略编辑器以管理员批准模式运行所有管理员 设置为已启用 时不能使用,要改为禁用才能正常运行,
此时需要管理员权限的程序无盾牌标志,不受UAC控制

分析之前环境无法正常使用原因:
根据解决方案第二点,Administrator 账户登录,软件仍以管理员权限运行(或者无需管理员权限运行),可正常使用,
此时将本地组策略编辑器用于内置管理员账户的管理员批准模式以管理员批准模式运行所有管理员 均设置为已启用
即运行需要管理员权限的程序时也会弹框需要提权,重启生效后运行需要管理员权限的软件,此时软件有盾牌,无法正常导出表格,第一次运行时会弹出一个访问注册表需要提权的对话框,
并打开注册表中WPS表格COM组件(KET.Application)位置Qt 表格导出数据为 excel html csv_第3张图片

查看其他参数:
Qt 表格导出数据为 excel html csv_第4张图片
Qt 表格导出数据为 excel html csv_第5张图片

虽然选择提权,但仍不能正常导出表格,且后面再次运行软件不会弹出注册表提权的对话框

猜测原因:
管理员批准模式运行时,在提升到管理员权限并完成当前操作后,当前用户的操作权仍然会恢复到标准操作权限,即使当前用户管理员用户(管理员组的普通用户),
而软件以管理员权限运行那一刻提权后恢复普通用户权限,而软件中有些操作需要管理员权限时,就会出错,不明白为什么需要权限时不会弹框要求提权

你可能感兴趣的:(Qt,笔记,qt)