用户在输入框里选择 .engine 或 .trt 文件时,路径检查可以提前发现空输入、目录、错误后缀和打不开的文件。它不能证明文件内容是有效推理引擎,也不能代替后续加载器对格式、版本和运行环境的检查。
原来的流程直接 new YOLO(...),再通过 modelState 显示成功。重复加载时,这容易遗漏旧对象释放和失败后的状态重置。应先定义状态与所有权,再将文件检查和实际引擎加载分开。
分清三个阶段
跳转到“分清三个阶段”| 阶段 | 可以确认什么 | 仍需处理的事情 |
|---|---|---|
| 路径与元数据检查 | 输入非空、当前存在、指向普通文件、后缀符合策略 | 文件可能在随后发生变化 |
| 尝试打开 | 当前进程此时能够以指定模式打开 | 内容可能为空、损坏或不受支持 |
| 引擎加载 | 由实际加载器确认格式与兼容性 | 失败原因、资源释放和界面状态 |
QFileInfo::suffix() 返回最后一个点后面的后缀。下面显式采用后缀大小写不敏感的产品规则,因此 MODEL.ENGINE 可以通过;这与文件系统查找路径时的大小写规则是两回事。QFileInfo 后缀
检查与打开之间可能有变化,exists() 或 isReadable() 的结果也不能保证下一次打开成功。实际执行 QFile::open() 并处理错误;实际加载器重新打开同一路径时,仍必须处理它自己的失败。QFile 打开文件
完整例子:检查器与临时文件
跳转到“完整例子:检查器与临时文件”这段程序只验证文件选择条件,依赖 Qt 6 的 Qt6::Core 和 C++17。以开启断言的 Debug 配置运行测试。测试文件故意只是普通文字,用于证明“通过路径检查”不等于“引擎加载成功”。这里不依赖或伪造 YOLO 的接口。
#include <QByteArray>#include <QDir>#include <QFile>#include <QFileInfo>#include <QString>#include <QTemporaryDir>#include <cassert>
enum class FileIssue { None, EmptyPath, NotFound, NotFile, WrongSuffix, CannotOpen };
struct FileCheck { FileIssue issue; QString absolutePath; QString detail; bool accepted() const { return issue == FileIssue::None; }};
FileCheck checkEngineFile(const QString& input) { // 这里保留用户选中的文件名,不擅自删除有意义的前后空格。 if (input.isEmpty()) return {FileIssue::EmptyPath, {}, QStringLiteral("请选择模型文件")}; const QFileInfo info(input); if (!info.exists()) return {FileIssue::NotFound, {}, QStringLiteral("文件不存在")}; if (!info.isFile()) return {FileIssue::NotFile, {}, QStringLiteral("请选择文件而非目录")}; const QString suffix = info.suffix().toLower(); if (suffix != "engine" && suffix != "trt") return {FileIssue::WrongSuffix, {}, QStringLiteral("需要 .engine 或 .trt 文件")};
const QString absolutePath = info.absoluteFilePath(); QFile file(absolutePath); if (!file.open(QIODevice::ReadOnly)) return {FileIssue::CannotOpen, {}, file.errorString()}; return {FileIssue::None, absolutePath, {}};}
void writeFixture(const QString& path) { QFile file(path); assert(file.open(QIODevice::WriteOnly)); const QByteArray data("not a real inference engine"); assert(file.write(data) == data.size()); assert(file.flush());}
int main() { QTemporaryDir directory; assert(directory.isValid()); const QDir root(directory.path()); const QString model = root.filePath("model.engine"); const QString uppercase = root.filePath("MODEL.ENGINE"); const QString trt = root.filePath("model.trt"); const QString wrong = root.filePath("model.txt"); writeFixture(model); writeFixture(uppercase); writeFixture(trt); writeFixture(wrong); assert(root.mkdir("folder.engine"));
assert(checkEngineFile({}).issue == FileIssue::EmptyPath); assert(checkEngineFile(root.filePath("missing.engine")).issue == FileIssue::NotFound); assert(checkEngineFile(root.filePath("folder.engine")).issue == FileIssue::NotFile); assert(checkEngineFile(wrong).issue == FileIssue::WrongSuffix); const FileCheck result = checkEngineFile(model); assert(result.accepted() && QFileInfo(result.absolutePath).isAbsolute()); assert(checkEngineFile(uppercase).accepted()); assert(checkEngineFile(trt).accepted()); assert(QFile::remove(model)); assert(checkEngineFile(model).issue == FileIssue::NotFound);}程序覆盖真实临时文件、大小写后缀、目录伪装、删除后重查和缺失路径。打开失败分支保留实际错误信息,但没有用这个测试声称所有操作系统权限失败都已覆盖。
接回模型加载界面
跳转到“接回模型加载界面”每次点击加载,先进入“正在加载”状态,禁用依赖该模型的操作,并清理上一次错误。检查不通过时在 QTextBrowser 等控件显示原因;检查通过后再显示 Loading engine file... 并调用实际加载器。
只有加载器明确返回有效模型,才显示成功并启用相关功能。失败时要保持禁用状态,不能沿用上次成功留下的布尔值。模型由 RAII 所有者管理,例如合适删除器的智能指针;加载新模型采用临时所有者,成功后再替换原对象。如果业务选择“失败时继续使用旧模型”,界面必须明确显示仍在使用旧模型,而不是显示新模型加载成功。
原例中的 modelFilePath.toStdString() 也需要结合加载器要求:Qt 的字符串表示、UTF-8 字节路径和 Windows 宽字符文件接口不能混为一谈。保持 QString 到适配边界,再按真实引擎 API 转换。耗时加载则应按引擎线程约束安排后台工作,并让界面更新回到 GUI 线程。