news 2026/10/23 3:01:49

QML controls 的6种样式与主题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
QML controls 的6种样式与主题

目录

    • 样式切换
      • 演示代码
      • 关键逻辑解析
      • 标题栏的样式名从哪来
    • 默认控件
    • 自定义控件外观
      • 演示代码
      • 关键逻辑解析
    • 换个样式,内边距也会变
    • 运行与源码说明

示例qml_styles主要讲 Qt Quick Controls 的样式系统:六种内置样式怎么切换、都是什么效果、以及如何自定义样式且不被系统样式影响。

三个页面各看一件事:

  • 样式切换:六张卡片对应六种样式,任选一个样式即可,UI会自动重启
  • 默认控件:展示对应系统样式下的控件外观
  • 自定义控件外观:自定义样式,可以观察到它们脱离了系统样式的干扰

样式切换

任选一个样式,然后切换到默认控件这张页面,可以看到组件的变化,如图所示:

演示代码

delegate: Rectangle { id: card required property string modelData Layout.fillWidth: true Layout.preferredHeight: 90 radius: 8 color: hover.hovered ? "#F0F7FF" : "#FFFFFF" border.color: card.modelData === StyleHelper.currentStyle ? "#1296FF" : "#e0e0e0" border.width: card.modelData === StyleHelper.currentStyle ? 2 : 1 // ... 省略卡片内部:样式名、"当前"标记、一句话简介 ... HoverHandler { id: hover cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: StyleHelper.restartWithStyle(card.modelData) } }

真正干活的是 C++ 里的这一小段:

voidStyleHelper::restartWithStyle(constQString&style){if(style.isEmpty()||style==QQuickStyle::name())return;// 重启时带上 -style,新进程里由 Qt 自己取走并应用QProcess::startDetached(QCoreApplication::applicationFilePath(),{QStringLiteral("-style"),style});QCoreApplication::quit();}

关键逻辑解析

点卡片以后,程序把自己重新拉起来:新进程带着样式名启动,旧进程退出。新进程里 Qt 在注册 QML 类型之前就读到了这个样式名,界面一出来就是新的样子。

要点只有三个:卡片被点、带着样式名另起一个自己、旧进程退出。卡片上的点击调restartWithStyle,这是 C++ 侧注入给 QML 的方法;它内部用QProcess::startDetached另起一个自己,参数里带 Qt 认的-style,然后让当前进程退出。

之所以非要重启,是因为样式选择发生在 QML 类型注册那一刻,注册完就换不了。Qt 文档的原话是 “It is not possible to change the style after the QML types have been registered.”。理论上还有一条路:销毁所有 QML 引擎、清掉类型注册、再重新设样式重建引擎。等于把整个界面拆了重建,内存里的状态全丢,那个函数还要求调用前必须没有任何引擎活着。重启进程反而干净。

标题栏的样式名从哪来

标题栏里那行「Controls 样式与主题:Basic」不是自己解析命令行得来的,而是直接问 Qt:

QStringStyleHelper::currentStyle()const{// QQuickStyle::name() 就是控件实际渲染用的样式名,-style / 环境变量 / 平台默认// 这几级 Qt 已经解析完;别自己去 argv 里翻,QGuiApplication 构造时就把它取走了。returnQQuickStyle::name();}

这里藏着一个坑。main.cpp里要是自己去QCoreApplication::arguments()找-style,永远拿不到:

// 错误示范:这样永远找不到 -styleQGuiApplicationapp(argc,argv);constQStringList args=QCoreApplication::arguments();intidx=args.indexOf("-style");

QGuiApplication构造时会把-style <值>从 argv 里摘走,存进内部的 styleOverride,Qt Quick Controls 随后从那里读。构造函数文档里有一句:

Note:argcandargvmight be changed as Qt removes command line arguments that it recognizes.

症状很怪:样式确实切过去了,标题栏的样式名却一直是空的。QQuickStyle::name()返回的是 Qt 按优先级解析完的结果,命令行、环境变量QT_QUICK_CONTROLS_STYLE、qtquickcontrols2.conf、平台默认,这一串它已经算过了,和控件实际渲染用的是同一份答案。

不带任何参数运行时,它返回平台默认的真实样式名,Windows 上是Windows,Linux 上是Fusion。所以标题栏里永远是个具体名字。

默认控件

同一份代码,换个样式再回来,按钮、开关、滑块的样子完全不同。这一页没有代码可讲,就是十来种常用控件摆在当前样式下:三个状态的按钮和一个圆形按钮、复选项、开关、单选项、滑块、进度条、输入框、下拉框、数字输入框。

六种样式各自的脾气:

样式特点
Basic轻量、性能最好,控件最朴素,适合完全自绘
Fusion跨平台桌面风格,跟随系统自动切换明暗,Linux 默认样式
MaterialGoogle Material Design,带明暗主题和主色、强调色
Universal微软通用设计规范,跨设备一致,带明暗主题和强调色
Imagine基于图片素材,换一套图片就能整体换装
WindowsWindows 原生观感,Windows 平台的默认样式

自定义控件外观

这一页把按钮的背景、开关的指示器、滑块的手柄全换成自己画的圆角矩形和圆点。

切换系统样式,这一页的样子不会变。前面两页的控件外观由样式决定,这一页由代码里的部件决定。

演示代码

按钮换了背景和正文:

Button { id: customButton text: "自定义按钮" implicitWidth: 130 background: Rectangle { radius: 6 color: customButton.pressed ? "#0B6FBF" : customButton.hovered ? "#2AA5FF" : "#1296FF" border.color: "#0B6FBF" } contentItem: Text { text: customButton.text color: "white" font.pointSize: 11 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } }

开关换了指示器,轨道是圆角矩形,里面套一个圆点:

Switch { id: customSwitch text: "自定义开关" indicator: Rectangle { implicitWidth: 42 implicitHeight: 20 radius: 10 color: customSwitch.checked ? "#1296FF" : "#CCCCCC" Rectangle { width: 16 height: 16 radius: 8 color: "white" y: 2 x: customSwitch.checked ? parent.width - width - 2 : 2 Behavior on x { NumberAnimation { duration: 120 } } } } contentItem: Text { text: customSwitch.text color: "#333" font.pointSize: 11 leftPadding: customSwitch.indicator.width + 8 verticalAlignment: Text.AlignVCenter } }

关键逻辑解析

换掉一个部件,等于把那部分渲染权收回到自己手里。控件本身还在管状态和交互,画成什么样由你决定。

这一页动了三处:按钮的background(正文一并换)、开关的indicator、滑块的handle。开关的轨道是个圆角矩形,里面的圆点跟着选中状态左右走,切换时补一段短动画。

代价是样式自带的视觉反馈不再参与。按压、悬停这些状态要在自己的部件里自己判断,示例里是拿控件的按压、悬停状态给颜色。

还有一类样式根本不接受换部件。Windows 这类原生样式受系统主题管理,换部件时 Qt 会在控制台报:

The current style does not support customization of this control (property: "background" ...) Please customize a non-native style (such as Basic, Fusion, Material, etc).

想看自定义效果,就切到非原生样式下。

换个样式,内边距也会变

样式不只换外观,也会换控件内部的内边距算法。标题栏原本用的是 ToolBar,切到 Imagine 之后标题直接看不见了。

Imagine 的 ToolBar 把内边距算成 SafeArea 加背景图自带的内边距:

topPadding: SafeArea.margins.top + (background ? background.topPadding : 0)

标题栏把背景换成纯色的 Rectangle 之后,Rectangle 没有 topPadding 这个属性,0 加 undefined 得到 NaN,内容区高度跟着变成 0,标题就画不出来。其余五种样式的 ToolBar 内边距是固定值,不受影响。

修法有两种:把四个方向的内边距显式写死,或者干脆别用 ToolBar。标题栏本来就是一条纯色带子,用 Rectangle 加 Text 自己画,样式爱怎么换都碰不到它。示例用的是后者。

运行与源码说明

  • Qt 6.5+,构建脚本里写的是qt_standard_project_setup(REQUIRES 6.5)
  • 六种样式各自是独立的 QML 模块,必须显式find_package并链进来,否则加载不到
  • 命令行直接指定样式:qml_styles.exe -style Material
  • Windows 样式只在 Windows 上可用,macOS、iOS 同理
  • 用 Qt Creator 打开qml_styles/CMakeLists.txt直接运行

已验证环境:

  • 我的 Qt 版本:Qt 6.12.0
  • 操作系统:Windows 11
  • GitCode:QML 示例合集V2.0 - qml_styles
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 3:01:08

PDF压缩后小字还清楚吗?用同一文件检查大小和画质

PDF压缩后文件小了&#xff0c;编号和金额还能看清吗&#xff1f;下面用同一份一页扫描PDF&#xff0c;对照默认压缩和JPEG图像质量参数&#xff0c;检查输出大小、页数与关键文字。 先保留原文件 教学输入只有图像&#xff0c;没有可提取文字。页面为595842点&#xff0c;嵌…

作者头像 李华
网站建设 2026/10/23 2:58:09

在线计算器服务系统- Flask SQLite

本项目为前几天收费帮学妹做的一个项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 采用前后端分离架构构建的在线计算器服务系统 127.0.0.1:5000 admin/admin123&…

作者头像 李华
网站建设 2026/10/23 2:57:38

Leetcode 链表,树,回溯 模板

链表/反转链表迭代 ListNode reverseList(ListNode head) {ListNode prev null, cur head;while (cur ! null) {ListNode next cur.next; // 1. 先存下一个&#xff0c;防断链cur.next prev; // 2. 反转当前指针prev cur; // 3. prev 前进cu…

作者头像 李华
网站建设 2026/10/23 2:57:32

Agent Harness 深度解析:模型决定下限,骨架决定上限

这是「近五年 AI 技术演进」系列深挖的最后一篇&#xff0c;第十三篇。前十二篇各拆了一个零件——提示词、检索、工具、协议、编排、技能、规矩、记忆、沙箱。这一篇不再拆新零件&#xff0c;而是把它们装回去&#xff1a;为什么这些零件必须长在一起&#xff0c;才叫一个 Age…

作者头像 李华