Skip to content

Commit ff1522d

Browse files
authored
Merge pull request #420 from j2doll/j2doll/LargeData
LargeData example
2 parents 1203179 + 5744fc9 commit ff1522d

File tree

3 files changed

+292
-0
lines changed

3 files changed

+292
-0
lines changed

Example.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,41 @@ int main(int argc, char *argv[])
135135
![](markdown.data/copycat.png)
136136
![](markdown.data/copycat2.jpg)
137137

138+
## [LargeData](https://github.com/QtExcel/QXlsx/tree/master/LargeData)
139+
- `LargeData` example demonstrates how to efficiently generate, write, and read large Excel `.xlsx` files using `QXlsx`.
140+
- It showcases:
141+
- Writing a large dataset (hundreds of thousands of rows)
142+
- Optional cell formatting
143+
- Splitting data across multiple sheets
144+
- Time-stamped progress reporting (0.1% increments)
145+
- Verification of selected sample cells after writing
146+
- Command-line configurability (rows, columns, sheet size, etc.)
147+
- This example is useful for testing performance, benchmarking, and validating QXlsx behavior with big data workloads.
148+
149+
- Command-Line Usage
150+
- The program accepts several optional parameters:
151+
152+
| Option | Description | Default |
153+
| --------------------------- | ------------------------------------- | ---------- |
154+
| `--rows <n>` `-r <n>` | Number of rows to generate | `100000` |
155+
| `--cols <n>` `-c <n>` | Number of columns to generate | `10` |
156+
| `--use-style` `-s` | Apply simple cell formatting | *Disabled* |
157+
| `--sheet-rows <n>` `-S <n>` | Max rows per sheet (0 = single sheet) | `0` |
158+
159+
- Examples
160+
161+
```
162+
LargeData --rows 200000 --cols 20
163+
Generate 200,000 rows × 20 columns
164+
165+
LargeData -r 100000 -c 10 --use-style
166+
Apply cell formatting
167+
168+
LargeData -r 200000 -c 10 -S 50000
169+
Split into multiple sheets (50,000 rows per sheet)
170+
171+
LargeData -r 300000 -c 15 -S 60000 --use-style
172+
All options combined
173+
```
174+
138175

LargeData/CMakeLists.txt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
3+
project(LargeData LANGUAGES CXX)
4+
5+
# C++ standard configuration
6+
set(CMAKE_CXX_STANDARD 17)
7+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
8+
9+
# Qt automatic processing
10+
set(CMAKE_INCLUDE_CURRENT_DIR ON)
11+
set(CMAKE_AUTOMOC ON)
12+
set(CMAKE_AUTORCC ON)
13+
set(CMAKE_AUTOUIC OFF)
14+
15+
#
16+
# 1) Add QXlsx library
17+
# Your directory structure should be:
18+
# /QXlsx
19+
# /LargeData
20+
#
21+
# So LargeData/CMakeLists.txt runs add_subdirectory("../QXlsx")
22+
#
23+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../QXlsx QXlsx_build)
24+
25+
#
26+
# 2) Find Qt6 modules
27+
# Core is enough for console program.
28+
#
29+
find_package(Qt6 COMPONENTS Core REQUIRED)
30+
31+
#
32+
# 3) Build executable
33+
#
34+
add_executable(LargeData
35+
main.cpp
36+
)
37+
38+
#
39+
# 4) Link against QXlsx and Qt6::Core
40+
#
41+
target_link_libraries(LargeData
42+
PRIVATE
43+
Qt6::Core
44+
QXlsx::QXlsx
45+
)
46+
47+
# Build as console application
48+
set(CMAKE_WIN32_EXECUTABLE OFF)

LargeData/main.cpp

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#include <QCoreApplication>
2+
#include <QCommandLineParser>
3+
#include <QElapsedTimer>
4+
#include <QDebug>
5+
#include <QDateTime>
6+
7+
#include "xlsxdocument.h"
8+
#include "xlsxformat.h"
9+
10+
using namespace QXlsx;
11+
12+
// Generate deterministic cell content
13+
static QVariant make_cell_value(int row, int col)
14+
{
15+
// Even columns → integer, odd columns → string
16+
if (col % 2 == 0) {
17+
return row * 1000 + col;
18+
} else {
19+
return QStringLiteral("R%1C%2").arg(row).arg(col);
20+
}
21+
}
22+
23+
// Verify several sample cells inside the generated XLSX file
24+
static bool verify_sample_cells(Document &doc, int row_count, int col_count)
25+
{
26+
QList<QPair<int,int>> samples = {
27+
{1, 1},
28+
{row_count / 2, col_count / 2},
29+
{row_count, col_count}
30+
};
31+
32+
for (const auto &rc : samples) {
33+
int r = qBound(1, rc.first, row_count);
34+
int c = qBound(1, rc.second, col_count);
35+
36+
QVariant expected = make_cell_value(r, c);
37+
38+
// cellAt now returns std::shared_ptr<Cell>
39+
auto cell = doc.cellAt(r, c);
40+
if (!cell) {
41+
qWarning() << "[verify] null cell at" << r << c;
42+
return false;
43+
}
44+
45+
QVariant actual = cell->value();
46+
47+
if (actual != expected) {
48+
qWarning() << "[verify] mismatch at" << r << c
49+
<< "expected =" << expected
50+
<< "actual =" << actual;
51+
return false;
52+
}
53+
}
54+
return true;
55+
}
56+
57+
int main(int argc, char *argv[])
58+
{
59+
QCoreApplication app(argc, argv);
60+
QCoreApplication::setApplicationName("LargeData");
61+
QCoreApplication::setApplicationVersion("1.0");
62+
63+
QCommandLineParser parser;
64+
parser.setApplicationDescription(
65+
"QXlsx Large Data Example - Generates and reads a large XLSX file."
66+
);
67+
68+
parser.addHelpOption();
69+
parser.addVersionOption();
70+
71+
QCommandLineOption rows_opt({"r", "rows"},
72+
"Number of rows (default: 100000)", "rows", "100000");
73+
QCommandLineOption cols_opt({"c", "cols"},
74+
"Number of columns (default: 10)", "cols", "10");
75+
QCommandLineOption use_style_opt({"s", "use-style"},
76+
"Apply a simple cell formatting style");
77+
QCommandLineOption sheet_rows_opt({"S", "sheet-rows"},
78+
"Maximum rows per sheet (0 = single sheet)", "sheetRows", "0");
79+
80+
parser.addOption(rows_opt);
81+
parser.addOption(cols_opt);
82+
parser.addOption(use_style_opt);
83+
parser.addOption(sheet_rows_opt);
84+
parser.process(app);
85+
86+
int row_count = parser.value(rows_opt).toInt();
87+
int col_count = parser.value(cols_opt).toInt();
88+
bool use_style = parser.isSet(use_style_opt);
89+
int sheet_rows = parser.value(sheet_rows_opt).toInt();
90+
91+
qInfo() << "[LargeData] rows =" << row_count
92+
<< "cols =" << col_count
93+
<< "use_style =" << use_style
94+
<< "sheet_rows =" << sheet_rows;
95+
96+
QString file_name = QStringLiteral("large_data_%1x%2.xlsx")
97+
.arg(row_count)
98+
.arg(col_count);
99+
100+
Document xlsx;
101+
102+
// Cell formatting (optional)
103+
Format data_format;
104+
if (use_style) {
105+
data_format.setFontName("Calibri");
106+
data_format.setFontSize(10);
107+
data_format.setBorderStyle(Format::BorderThin);
108+
}
109+
110+
QElapsedTimer timer;
111+
112+
// ---------------------------------------------------------
113+
// 1) Write a large dataset
114+
// ---------------------------------------------------------
115+
qInfo() << "[LargeData] writing...";
116+
timer.start();
117+
118+
int current_sheet_index = 1;
119+
int current_sheet_row_start = 1;
120+
121+
// Manage sheet switching when a sheet row limit is provided
122+
auto ensure_sheet_for_row = [&](int row) {
123+
if (sheet_rows <= 0) {
124+
if (xlsx.currentWorksheet() == nullptr)
125+
xlsx.addSheet("Sheet1");
126+
return;
127+
}
128+
129+
if (xlsx.currentWorksheet() == nullptr) {
130+
xlsx.addSheet(QStringLiteral("Sheet%1").arg(current_sheet_index));
131+
current_sheet_row_start = 1;
132+
return;
133+
}
134+
135+
if (row - current_sheet_row_start >= sheet_rows) {
136+
++current_sheet_index;
137+
xlsx.addSheet(QStringLiteral("Sheet%1").arg(current_sheet_index));
138+
current_sheet_row_start = row;
139+
}
140+
};
141+
142+
// Progress reporting every 0.1%
143+
const double progress_step = 0.1;
144+
double next_progress = progress_step;
145+
146+
for (int row = 1; row <= row_count; ++row) {
147+
148+
ensure_sheet_for_row(row);
149+
150+
for (int col = 1; col <= col_count; ++col) {
151+
QVariant value = make_cell_value(row, col);
152+
if (use_style)
153+
xlsx.write(row - current_sheet_row_start + 1, col, value, data_format);
154+
else
155+
xlsx.write(row - current_sheet_row_start + 1, col, value);
156+
}
157+
158+
// Progress + timestamp (yyyy-MM-dd hh:mm:ss.zzz)
159+
double percent =
160+
(static_cast<double>(row) * 100.0) /
161+
static_cast<double>(row_count);
162+
163+
if (percent + 1e-9 >= next_progress) {
164+
165+
QString timestamp =
166+
QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
167+
168+
qInfo().noquote()
169+
<< "[" + timestamp + "]"
170+
<< "[LargeData] progress:"
171+
<< QString::number(percent, 'f', 1) + "%";
172+
173+
while (next_progress <= percent)
174+
next_progress += progress_step;
175+
}
176+
}
177+
178+
if (!xlsx.saveAs(file_name)) {
179+
qCritical() << "[LargeData] failed to save file:" << file_name;
180+
return 1;
181+
}
182+
183+
double write_sec = timer.elapsed() / 1000.0;
184+
qInfo() << "[LargeData] write finished:" << write_sec << "seconds";
185+
186+
// ---------------------------------------------------------
187+
// 2) Read file back and verify sample cells
188+
// ---------------------------------------------------------
189+
qInfo() << "[LargeData] reading & verifying...";
190+
timer.restart();
191+
192+
Document verify_doc(file_name);
193+
194+
if (!verify_doc.isLoadPackage()) {
195+
qCritical() << "[LargeData] failed to open file:" << file_name;
196+
return 1;
197+
}
198+
199+
bool verified = verify_sample_cells(verify_doc, row_count, col_count);
200+
double read_sec = timer.elapsed() / 1000.0;
201+
202+
qInfo() << "[LargeData] read finished:" << read_sec << "seconds";
203+
qInfo() << "[LargeData] verify result =" << (verified ? "OK" : "FAILED");
204+
qInfo() << "[LargeData] output file:" << file_name;
205+
206+
return verified ? 0 : 1;
207+
}

0 commit comments

Comments
 (0)