最直接方法是用 QDomDocument 构建树状 XML,以 objectName() 为 ID 存 widget 的 geometry、parent 关系、isVisible()、QDockWidget/QSplitter 状态等,并按父子深度顺序恢复,注意 UTF-8 编码与 Qt 版本兼容性。

Qt 里用 QDomDocument 保存 widget 位置尺寸最直接
Qt 是 C++ 图形界面最常用框架,原生不带布局序列化功能,得自己把 geometry()、state()(如 QDockWidget 折叠状态)等手动写进 XML。别碰 QSettings 的 ini 格式——它不支持嵌套结构,存复杂布局会很快失控。
常见错误是只存 x/y/width/height,结果主窗口缩放后子控件错位。必须同时存 parent 关系和是否隐藏(isVisible()),否则恢复时控件可能被父容器 layout 忽略。
- 用
QDomDocument构建树状结构,每个 widget 对应一个<widget>节点,用objectName()当唯一 ID(务必提前 set) - 对
QSplitter要额外存saveState()返回的QByteArray,转成 hex 字符串再写入 XML,不能直接当文本存 - 避免在
resizeEvent()里实时写文件——太频繁,改用QTimer::singleShot(300, this, &MyWidget::saveLayout)防抖
读取 XML 时必须按 parent-child 顺序重建 widget 状态
XML 里节点顺序 ≠ 实际父子依赖顺序。如果先恢复子 widget 的 geometry,但它的 parent 还没 resize 或还没 show,坐标会计算错误,最终全部堆在左上角。
典型现象:配置文件里 DockWidget 的 state 恢复后位置偏移,或者 QTabWidget 的当前页签没切回去。
立即学习“C++免费学习笔记(深入)”;
- 先递归遍历 XML,收集所有
objectName()到 map,不立即设置属性 - 再按 widget 树深度排序(用
QObject::findChildren<QWidget*>()+parent()层数判断),从顶层窗口开始恢复 -
QMainWindow要最后调restoreState(),且必须在所有addDockWidget()和setCentralWidget()完成之后 - 对
QCheckBox或QComboBox这类状态控件,别只存 checked/index,要连同isEnabled()一起存——用户可能禁用了某组选项
XML 文件路径和编码容易被忽略的坑
Windows 下用 QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) 存配置,别硬写 "./config.xml"。前者自动处理权限、UAC、多用户隔离;后者在安装版程序里大概率写失败,还静默忽略错误。
另一个高频问题:QDomDocument::setContent() 默认按系统 locale 解码,中文路径或含中文字段时直接解析失败,报错 "not well-formed (invalid token)"。
- 写入前用
doc.toString().toUtf8(),再用QFile::write()保存为 UTF-8(无 BOM) - 读取时显式传
QByteArray给setContent(),并指定编码:doc.setContent(data, true)(第二个参数true表示自动检测编码) - 配置文件名别用空格或特殊符号,
"my-app-config.xml"比"My App Config.xml"更稳妥,某些旧版 Qt 在路径含空格时QFile打开失败不报错
跨 Qt 版本兼容性比想象中脆弱
Qt 5.15 和 Qt 6.2 对 QDockWidget::saveState() 返回的二进制格式不兼容。把 Qt 5 生成的 state 字符串直接喂给 Qt 6 的 restoreState(),会静默失败——控件还在,但停靠位置、浮动状态全丢。
更隐蔽的是 QSplitter::saveState(),Qt 6.5 开始内部加了版本标记,老版本读新数据会崩溃。
- 在 XML 根节点加
qt_version="6.5"属性,读取时先校验再分支处理 - 不要长期依赖
saveState()的原始字节流;对关键布局(如编辑器分栏),拆成list<int>存各栏宽度比例,恢复时用setSizes() - 测试阶段务必在目标部署环境(特别是 Windows Server 或精简版 Linux)跑一次读写全流程,GUI 环境变量(如
QT_QPA_PLATFORM)不同可能导致geometry()返回值异常
真正麻烦的不是怎么写进去,而是怎么让“写进去的东西”在三个月后、换了一台电脑、升级了 Qt 小版本之后,还能让界面看起来和用户离开时一模一样——这要求每一步都带校验、留退路、不假设环境干净。



















