网格菜单重构、大文件异步加载与内存/解析优化、修复应用图标
网格右键菜单: - 菜单顺序改为: 网格 N / Y 轴最大值 / Y 轴最小值 / 选区 Y 轴自适应 / --分隔线-- / Y 轴自适应 / 时间轴自适应 / 全部自适应 (恢复自适应移到分隔线以下、时间轴自适应上方) - Y 轴最大值/最小值改为菜单内联输入框:点击就地编辑、回车提交,菜单不关闭, 处理方式与拾色器的 RGB 内联编辑一致;提交后按真实夹取结果回写菜单显示值, 并同步刷新自适应项的文案/对勾/可用状态 - 新增"选区 Y 轴自适应":只按当前显示时间段内的数据做幅值自适应 (时间单调时二分定位区间,非单调时全扫并按区间过滤) 大文件加载: - 加载改为工作线程执行(解析 + 按列重组),主线程抽送消息并播放过渡动画 (旋转弧线 + 进度条 + 文件名 + 百分比 + 按 Esc 取消),界面不再整块卡死 - 加载完成后主线程只做一次 O(1) 的移动入队,不会产生可见卡顿 - 新增 DataModel::BuildDataFile / DataSet::AddPreparedFile:不依赖 DataSet, 因此可以安全地在线程上运行 内存与解析效率: - CsvTable 由"行优先二维数组"改为"列优先":百万行文件原先会产生上百万次独立堆分配 (每行还有 vector 控制块与容量对齐开销),现在每列一整块连续内存 - 解析改为流式分块读取(4MB),不再把整个文件读入内存,也没有体积上限 (原先超过 512MB 直接拒绝);实测 51MB 文件峰值内存约 126MB(数据本身 113MB) - BuildDataFile 直接把整列移动进 SignalData(零拷贝),不再逐元素复制 - TryParseDouble 增加十进制快速路径:ucrt strtod 实测约 335ns/次,手写路径约 13ns (快 25 倍);非十进制写法(十六进制浮点、inf/nan、带尾部杂字符)原样交回 strtod, 输入接受语义完全不变(实测与 strtod 的差异不超过 1 ULP) → 640MB 测试文件加载时间由约 40s 降到 2s 以内 交互流畅性: - 拖拽(平移/框选/拖动信号)期间关闭波形抗锯齿并压缩顶点预算(2 像素一列包络), 松开后立即恢复全质量绘制;实测 BIG_DATA 10 信号全显示的帧耗时由 105~168ms 降到 75~90ms(其中折线绘制 55ms→23ms) - 折线包络抽稀改为"列边界单调推进"的二分,避免每列在全区间二分造成缓存缺失 - 未对波形构建引入多线程:构建已由几何键缓存消除,且拖拽降级后仅约 1~2ms/帧, 并行化收益不足以抵消线程与同步的复杂度(加载路径的多线程收益明确,故只在该处引入) 图标: - 窗口类与 WM_SETICON 原先使用系统默认图标 IDI_APPLICATION,导致标题栏、 任务栏与 Alt+Tab 缩略图都显示成通用应用图标;改为按 SM_CXICON/SM_CXSMICON 从应用的 IDI_APP_ICON 资源加载(多尺寸 ICO 会挑选最接近的一帧) 其他: - PopupMenu 新增 EDIT 项类型(菜单内联输入框)与 UpdateItemValue/UpdateItemLabel
This commit is contained in:
1 parent
d6a81839bb
commit
45b44eae57
11 files changed
+1637
-222
No files matched your search
@@ -2,6 +2,7 @@
|
||||
#define __CSV_TABLE_HPP__
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -9,15 +10,22 @@ namespace jdi
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief CSV 文件的解析结果,保存列名与数值矩阵。
|
||||
* @brief CSV 文件的解析结果,保存列名与**列优先**的数值矩阵。
|
||||
*
|
||||
* 采用列优先(每条信号一整块连续内存)而不是行优先的二维数组,原因有二:
|
||||
* 1. 大型文件(百万行级)若按行存储会产生上百万次独立堆分配,
|
||||
* 每行还要额外承担 vector 的控制块与容量对齐开销,实测内存放大接近 1.5 倍;
|
||||
* 2. 后续构建信号时可以直接把整列**移动**进 SignalData,不再逐元素拷贝。
|
||||
*/
|
||||
struct CsvTable
|
||||
{
|
||||
std::vector<std::wstring> headers; /**< 列名(已去除引号与首尾空白)。 */
|
||||
std::vector<std::vector<double>> rows; /**< 数值矩阵,行优先,每行长度与 headers 一致。 */
|
||||
wchar_t delimiter = L','; /**< 实际使用的分隔符。 */
|
||||
std::size_t sourceRowCount = 0; /**< 文件中有效数据行数(不含空行与注释行)。 */
|
||||
std::size_t skippedRowCount = 0; /**< 因格式异常被置为缺失值的行数。 */
|
||||
std::vector<std::wstring> headers; /**< 列名(已去除引号与首尾空白)。 */
|
||||
std::vector<std::vector<double>> columns; /**< 列优先数值矩阵:columns[列][行],缺失处为 NaN。 */
|
||||
std::size_t rowCount = 0; /**< 有效数据行数。 */
|
||||
wchar_t delimiter = L','; /**< 实际使用的分隔符。 */
|
||||
std::size_t sourceRowCount = 0; /**< 文件中有效数据行数(不含空行与注释行)。 */
|
||||
std::size_t skippedRowCount = 0; /**< 因格式异常被置为缺失值的行数。 */
|
||||
unsigned long long fileBytes = 0; /**< 源文件字节数(用于按比例预留容量与进度显示)。 */
|
||||
|
||||
/**
|
||||
* @brief 判断解析结果是否包含可用的表格数据。
|
||||
@@ -25,18 +33,32 @@ namespace jdi
|
||||
*/
|
||||
bool HasData() const
|
||||
{
|
||||
return !headers.empty() && !rows.empty();
|
||||
return !headers.empty() && (rowCount > 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 解析进度回调。
|
||||
*
|
||||
* 在解析过程中按块回调,ratio 为 0.0-1.0 的完成比例;
|
||||
* 返回 false 表示请求取消,解析会尽快停止并返回失败。
|
||||
*/
|
||||
using CsvProgress = std::function<bool(double ratio)>;
|
||||
|
||||
/**
|
||||
* @brief 读取并解析一个 CSV 文件。
|
||||
*
|
||||
* 文件以分块流式方式读取(不把整个文件一次性读入内存),
|
||||
* 因此内存占用只与解析结果相关,不会因文件体积成倍放大,也没有体积上限。
|
||||
*
|
||||
* @param [IN] filePath CSV 文件完整路径(宽字符,支持中文路径)。
|
||||
* @param [OUT] table 解析结果输出。
|
||||
* @param [OUT] error 失败原因描述(成功时清空)。
|
||||
* @param [IN] progress 解析进度回调,可为空。
|
||||
* @return 解析成功返回 true,否则返回 false。
|
||||
*/
|
||||
bool LoadCsvTable(const std::wstring& filePath, CsvTable& table, std::wstring& error);
|
||||
bool LoadCsvTable(const std::wstring& filePath, CsvTable& table, std::wstring& error,
|
||||
const CsvProgress& progress = CsvProgress());
|
||||
|
||||
} // namespace jdi
|
||||
|
||||
|
||||
Reference in new issue
Block a user