Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Automating Excel in Qt with QAxObject for Template-Based Writes and Batch Updates

Tech 1

QAxObject enables COM automation of Microsoft Excel on Windows. When an .xlsx file is opened by Excel, a hidden lock file named ~$.xlsx is created in the same directoyr. If this lock file is present, writing via QAxObject typically fails due to the workbook being read-only. Detecting and avoiding this condition is required before automation.

A template workbook placed alongside the executable can be copied per run, then populated through QAxObject calls. The snippet below demonstrates copying a template, checking for the lock file, writing individual cells, and performing a fast batch update over a contiguous range.

#include <QApplication>
#include <QAxObject>
#include <QFileDialog>
#include <QStandardPaths>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QColor>
#include <QDebug>

// Excel columns mapping (1-based)
enum XlCol {
    ColA = 1,
    ColB,
    ColC,
    ColD,
    ColE,
    ColF,
    ColG,
    ColH,
    ColI
};

// Prompt for a destination .xlsx path
QString promptSavePath()
{
    QString selectedFilter;
    const QString desktop = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
    return QFileDialog::getSaveFileName(
        nullptr,
        QStringLiteral("Save As"),
        desktop,
        QStringLiteral("Excel (*.xlsx)"),
        &selectedFilter);
}

// Write a single cell: value + text color
void setCell(QAxObject* sheet, XlCol column, int row, const QColor& textColor, const QString& value)
{
    QAxObject* cell = sheet->querySubObject("Cells(int,int)", row, column);
    cell->setProperty("Value", value);
    QAxObject* font = cell->querySubObject("Font");
    font->setProperty("Color", textColor);
}

// Convert QVariant (2D SAFEARRAY) -> QList<QList<QVariant>> for fast reads
void to2DVariantList(const QVariant& src, QList<QList<QVariant>>& out)
{
    const QVariantList rows = src.toList();
    for (const QVariant& r : rows) {
        out.push_back(r.toList());
    }
}

// Convert QList<QList<QVariant>> -> QVariant for fast writes
void from2DVariantList(const QList<QList<QVariant>>& cells, QVariant& out)
{
    QVariantList rows;
    rows.reserve(cells.size());
    for (const QList<QVariant>& r : cells) {
        rows.append(QVariant(r));
    }
    out = QVariant(rows);
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // 1) Resolve template path and ensure existence
    QString templatePath = QStringLiteral("./template.xlsx");
    QFileInfo tplInfo(templatePath);
    if (!tplInfo.exists()) {
        qDebug() << "Missing template.xlsx";
        return 0;
    }

    templatePath = QDir::toNativeSeparators(tplInfo.absoluteFilePath());

    // 2) Choose destination and copy template
    const QString targetXlsx = QDir::toNativeSeparators(promptSavePath());
    if (targetXlsx.isEmpty()) {
        return 0;
    }

    if (!QFile::copy(templatePath, targetXlsx)) {
        qDebug() << "Failed to copy template to" << targetXlsx;
        return 0;
    }

    // 3) Detect Excel lock (~$...) indicating read-only state
    QFileInfo targetInfo(targetXlsx);
    const QString lockName = QStringLiteral("~$") + targetInfo.fileName();
    const QString lockPath = targetInfo.dir().absoluteFilePath(lockName);
    if (QFileInfo(lockPath).exists()) {
        qDebug() << "Workbook appears locked by Excel (read-only). Close the file and retry.";
        return 0;
    }

    // 4) Launch Excel via COM and open workbook
    QAxObject excel("Excel.Application");
    excel.setProperty("Visible", true);
    excel.setProperty("DisplayAlerts", false);

    QAxObject* workbooks = excel.querySubObject("Workbooks");
    QAxObject* workbook = workbooks->querySubObject("Open(const QString&)", QDir::toNativeSeparators(targetXlsx));
    excel.setProperty("Caption", "Qt Excel Automation");

    // Pick first worksheet
    QAxObject* sheet = workbook->querySubObject("Worksheets(int)", 1);

    // 5) Individual cell updates
    setCell(sheet, ColB, 2, QColor(0, 128, 255), QStringLiteral("12345"));
    setCell(sheet, ColB, 3, QColor(255, 165, 0), QStringLiteral("B3"));
    setCell(sheet, ColB, 4, QColor(200, 0, 0),   QStringLiteral("B4"));

    // 6) Batch write to contiguous range
    //    Range size: 100 rows x 9 cols (A6:I105)
    QAxObject* range = sheet->querySubObject("Range(const QString&)", "A6:I105");

    QList<QList<QVariant>> matrix;
    matrix.reserve(100);
    for (int r = 0; r < 100; ++r) {
        QList<QVariant> row;
        row.reserve(9);
        for (int c = 0; c < 9; ++c) {
            row.append(r + c); // example data
        }
        matrix.append(row);
    }

    QVariant safeArray;
    from2DVariantList(matrix, safeArray);
    range->setProperty("Value", safeArray);

    // 7) Save; optionally close workbook and quit Excel
    workbook->dynamicCall("Save()");
    // workbook->dynamicCall("Close()");
    // excel.dynamicCall("Quit()");

    return 0;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.