Back to Blog home

QML格式化工具新特性

Published on 星期五 七月 25, 2025 by Semih Yavuz in Dev Loop QML | Comments

Qt自带的QML文件格式工具qmlformat的最新更新带来了两项实用的改进:可配置的换行设置和自动导入排序功能。这些功能提高了代码的可读性和可维护性,是值得关注的新增功能。

指定长度后换行

冗长的代码行可能导致难以阅读和浏览,尤其是在处理具有众多属性的复杂QML组件时尤为明显。

从 Qt 6.9 起,qmlformat  可以自动对超过指定字符长度的代码行进行换行 。

选项 说明
-W,--column-width< width > 对超过指定宽度的行进行换行。使用-1可禁用换行(默认)。

实际换行示例

您可以指定一个最大行长,qmlformat会自动将超过此限制的行进行换行处理。例如:

// main.qml 
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Templates as T

T.ItemDelegate {
id: control highlighted: control.pressed || control.hovered || (control.ListView.view && control.ListView.view.currentIndex === index && control.ListView.view.highlightFollowsCurrentItem && control.enabled }

如果在使用qmlformat时未启用换行选项,会降低代码的可读性:

import QtQuick 
import QtQuick.Controls.Material
import QtQuick.Templates as T

T.ItemDelegate {
id: control highlighted: control.pressed || control.hovered || (control.ListView.view && control.ListView.view.currentIndex === index && control.ListView.view.highlightFollowsCurrentItem && control.enabled
}

使用qmlformat-W 80 main.qml生成的结果更具可读性:

import QtQuick 
import QtQuick.Controls.Material
import QtQuick.Templates as T

T.ItemDelegate {
id: control
highlighted: control.pressed ||control.hovered ||(
control.ListView.view
&& control.ListView.view.currentIndex
=== index
&& control.ListView.view.highlightFollowsCurrentItem
&& control.enabled

导入语句排序

管理杂乱无章或排序不一致的导入语句是一项挑战,当多个协作者修改同一文件时,往往会导致不必要的合并冲突。

为了解决这些问题,Qt 6.10在qmlformat中引入了--sort-imports选项。排序后的导入语句能清楚地显示使用了哪些模块,从而提高可读性,减少合并冲突带来的无效差异 ,并在所有 QML 文件中强制保持一致的结构。

选项 说明
-S,--sort-imports 按字母顺序对导入语句进行排序(警告:如果同一类型名在多个模块中存在,排序可能会改变语义!)。

 

⚠️警告:使用--sort-imports按字母顺序对导入语句进行排序,如果多个模块中存在相同的类型名,可能会改变QML文件的语义。请务必检查排序结果,以确保应用程序的行为符合预期。

导入语句排序示例

// Original main.qml 
import QtQuick.Templates
import MyCustomModule
Button {
}

运行qmlformat -S main.qml之后

import MyCustomModule 
import QtQuick.Templates

Button {
}

在本示例中,原始的main.qml使用了MyCustomModule众的Button。使用--sort-imports格式化后,语义就变成了使用QtQuick.Templates中的非可视化Button。

即将推出:可定制的分号规则

从Qt 6.10开始,我们引入了一个新的-semicolon-rule选项,让用户可以更方便地控制在格式化过程中如何处理 JavaScript 分号。新标志支持两种模式:always,即会在所有JavaScript语句添加分号;essential,即删除分号,除非因JavaScript的自动分号插入 (ASI) 机制,删除分号会导致歧义。

模式 说明
always 在所有JavaScript语句中添加分号(默认)
essential 删除分号,除非ASI(自动分号插入)机制可能引发歧义

 

注意:该选项只影响JavaScript语句末尾的分号。QML元素末尾的分号总是会被qmlformat移除,与-semicolon-rule设置无关。

作为分号规则改进的一部分,对EmptyStatements(空语句)的处理也进行了优化:在没有函数体的控制结构(如 if、for 或 while)后面的分号现在会直接出现在右结尾括号之后,而不是在新的缩进行上。此外,多个连续的空语句被合并为一个分号,使结果更简洁。


import QtQuick 
Item {
Component.onCompleted:{
for(;;);;
;;
if (true);;;


while (true);
;
;;
var a = [1, 2, 3];;;;
for (var i in a);;;;
}
}
// qmlformat output prior to 6.10
import QtQuick

Item {
Component.onCompleted:{
for (; ; )
;
;
;
;
if (true)
;
;
;

;
while (true)
;
;
;
;
var a = [1, 2, 3];
;
;
;
for (var i in a)
;
;
;
;
}
}
// qmlformat output as of 6.10
import QtQuick
Item{
Component.onCompleted:{
for (; ; );
if (true);
while (true)
var a = [1, 2, 3];
for (var i in a);
}
}

可定制分号规则示例

// original test.js 
let v = a;;;;;;
[1, 2, 3].forEach(x => console.log(x));;;;;
;;;
;;;
let x = v;;;
x = 10;;;
;
;;;
;
let y = x;;;
(function() { })();;
let a = b
+ c
;
let xx = y;
-x;

qmlformat --semicolon-rule=always test.js在JS语句末尾添加分号。

let v = a; 
[1, 2, 3].
forEach(x => console.log(x));
let x = v;
x = 10;
let y = x;
(function () {})();
let a = b + c;
let xx = y;
-x;

qmlformat --semicolon-rule=essential test.js删除所有分号,仅保留在删除可能引发歧义的情况下必须保留的分号。 

例如,let v = a; 后面的分号不会被删除,因为下面一行是以[ 符号开头的:

let v = a; 
[1, 2, 3].forEach(x => console.log(x))
let x = v
x = 10
let y = x;
(function () {})()
let a = b + c
let xx = y;
-x

在JavaScript中,某些以特定符号开头的行(如 (、[、+ 或 -)可能会导致自动分号插入(ASI)机制无法按预期工作。为了避免改变代码的语义,qmlformat 在这些情况下会保留分号。

Qt Creator 17中的qmlformat集成

我们很高兴地宣布,qmlformat现已直接集成至Qt Creator 17中,从而使QML 代码格式化在各项目之间更易访问且保持一致。

当您在Preference>Qt Quick>Code Style中选择qmlformat作为QML代码格式器时,Qt Creator会自动检测您系统上可用的最新qmlformat版本,并通过执行命令qmlformat--write-defaults生成默认配置。可以根据自己的格式化偏好自定义该配置;更多信息请点击此处。全局配置文件将存储于路径 QStandardPaths::GenericConfigLocation/.qmlformat.ini 中

了解qmlformat如何定位其配置设置非常重要。在格式化过程中,它首先会在源文件的目录中查找.qmlformat.ini文件。若未找到本地配置文件,则会逐级向上搜索目录层级,直至找到有效配置文件或抵达根目录。 若仍未发现本地配置,则最终回退到存储在通用配置位置的全局配置。

 以下流程图说明了qmlformat搜索配置文件的逻辑

[Start: QML file directory]

[Is .qmlformat.ini here?] → Yes → [Use it]
↓ No
[Go to parent directory]

[Repeat until root]

[Still not found?][Use global config in GenericConfigLocation]

 如果您遇到格式化结果与在 Qt Creator 中设置的格式规则不一致的情况,请检查您的项目目录树(directory tree)中是否存在 .qmlformat.ini 文件。此类文件的优先级高于全局配置,可能会覆盖您预期的样式化配置。

Qt Creator 17中的qmlformat实践

在Qt Creator 17中,我们新增了一个用于配置 QML 格式化选项的偏好设置板块 。要在QML文件中使用 qmlformat,您需要明确将其设置为首选格式化器 。

pref

在Qt Creator中设置qmlformat

  1. 导航至Preference>Qt Quick>Code Sytle
  2. Formatter Selection下拉菜单中,选择QmlFormat[LSP]
  3. Global qmlformat configuration中,根据您的偏好调整列宽或导入排序等其他选项

自定义全局qml格式配置后,右侧的预览窗口将实时展示格式化后的 QML 文件效果。

在QML文件中使用qmlformat

配置完成后,对QML文件进行格式化操作就非常简单了:

  1. 在编辑器中打开任意QML文件
  2. 右键点击打开上下文菜单
  3. 选择Reformat Document

或者,您也可以通过Tools>QQml/JS>Reformat Document进行重格式化操作。

请注意:如果您此前使用的是Qt Creator自带的内置格式化器 ,切换到qmlformat可能会产生不同的格式化结果。这是因为两者使用了不同的格式化算法和代码风格规则。建议在首次格式化后检查代码,以确保其符合您的预期。

使用其他版本的qmlformat


如果想使用特定版本的qmlformat或自己的分支版本,可以选择 Custom Formatter(自定义格式化器)选项 。需要在参数部分设置路径并指定选项。有关所有可用选项,请参阅qmlformat官方文档

您还可以使用shell脚本对qmlformat进行封装,从而加快工作流程。以下是一个简单的shell包装示例:

#!/bin/sh
# qmlformat-wrapper.sh

/path/to/your/qmlformat --indent-width 12 "$@"

chmod +x qmlformat-wrapper.sh

然后您可以将该脚本路径设置为 Custom Formatter,参数区域可以留空,或者根据需要添加额外选项。您也可以直接将Custom Formatter path指向qmlformat可执行文件,并在参数区域填写相关选项:

 

customformatter

注意:Qt Creator目前仍默认使用旧版格式化器,以保持兼容性和用户已有的使用习惯。由于旧版格式化器与 qmlformat 的输出并不完全一致,因此在Qt Creator中默认使用qmlformat可能会导致用户的代码风格造成意外影响 ,尤其是在已有项目或多人协作的项目中。在qmlformat的集成更加成熟和统一之前,Qt Creator 将选择权交由用户自行决定 。

下一步是什么?

 随着qmlformat集成进Qt Creator 17,QML开发工作变得更加轻松。 新功能(如自动换行和导入排序)有助于保持代码的整洁、可读性并且风格统一。而且格式化操作就集成在Qt Creator中,仅需点击几下即可完成。

接下来我们将致力于开发对整个项目进行批量格式化的功能。

欢迎您亲自体验这些新功能,并反馈您的使用感受。您的意见将帮助我们进一步优化QML工具,为所有开发者带来更好的体验。

感谢您成为Qt社区的一员--祝您在使用Qt Creator 17和qmlformat时编码愉快!

Subscribe to Our Blog

Stay up to date with the latest marketing, sales and service tips and news.