为 QML 设置样式,无需手动标记要设置样式的每个属性

2022-01-19 00:00:00 qt styling qml css

我知道 QML 不像小部件那样支持 CSS 样式,并且我已经阅读了样式/主题的替代方法:

I know that QML does not support CSS styling like widgets do, and I have read up on alternative approaches to styling/theming:

  • https://qt-project.org/wiki/QmlStyling
  • http://www.slideshare.net/BurkhardSubert/practical-qml-key-navigation/34

这些方法的共同点是,它们要求开发人员指定可以设置样式的 QML 部分,方法是绑定到样式化 QML 文件/单例"中的属性,或者使用加载器加载不同的基于样式名称的 QML 组件.我想要的是类似于 CSS 中的id"选择器而不是class"选择器的东西,这样各个 QML 文件就不必知道它们以后是否会被设置样式.

Common for these approaches is that they require the developer to specify the parts of the QML that can be styled, either by binding to a property in a "styling QML file/singleton", or by using a Loader to load a different QML component based on style name. What I would like is something that works like the "id" selector in CSS instead of the "class" selector, so that the individual QML files do not have to know whether they will be styled later on or not.

我当前的方法使所有 QML 文件看起来都类似于此(使用链接 2 中的方法):

My current approach make all the QML files look similar to this (using approach in link 2):

Main.qml

Rectangle {
    Id: background
    color: g_theme.background.color 
    //g_theme is defined in root context and loaded dynamically
}

我想做的是:

Main.qml

Rectangle {
    Id: background
    color: "green" // default color
}

然后有一个样式文件定义(或类似)

And then have a styling file that defines (or similar)

Main.qml #background.color: red

目前是否有可能,或者正在为未来的 Qt 版本准备中,或者首选的样式设置方式是否会继续类似于上面链接中描述的方法?

Is this possible at the moment, or something that is in the pipeline for a future Qt version, or will the preferred way of styling continue to be something similar to the approach described in the links above?

推荐答案

首选方法不是在默认组件上应用样式,而是从这些组件派生以创建预先设置样式的自定义组件.

The preferred way isn't applying a style on default components, but deriving from these components to create pre-styled custom components.

我为我的项目做什么:

首先,我创建一个集中的主题"文件,作为 JavaScript 共享模块:

First, I create one centralized 'theme' file, as a JavaScript shared module :

// MyTheme.js
.pragma library;
var bgColor   = "steelblue";
var fgColor   = "darkred";
var lineSize  = 2;
var roundness = 6;

接下来,我创建依赖它的自定义组件:

Next, I create custom components that rely on it :

// MyRoundedRect.qml
import QtQuick 2.0;
import "MyTheme.js" as Theme;
Rectangle {
    color: Theme.bgColor;
    border {
        width: Theme.lineSize;
        color: Theme.fgColor;
    }
    radius: Theme.roundness;
}

然后,我可以通过一行代码在任何地方使用我的预样式组件:

Then, I can use my pre-styled component everywhere with a single line of code :

MyRoundedRect { }

而且这种方法有一个巨大的优势:它真的是面向对象的,而不是简单的蒙皮.

And this method has a huge advantage : it's really object-oriented, not simple skinning.

如果您愿意,您甚至可以在自定义组件中添加嵌套对象,例如文本、图像、阴影等...甚至是一些 UI 逻辑,例如鼠标悬停时的颜色变化.

If you want you can even add nested objects in your custom component, like text, image, shadow, etc... or even some UI logic, like color-change on mouse hover.

PS:是的,可以使用 QML 单例而不是 JS 模块,但它需要额外的 qmldir 文件,并且仅从 Qt 5.2 开始支持,这可能会受到限制.显然,上下文属性中的 C++ QObject 也可以工作(例如,如果您想从磁盘上的文件加载皮肤属性......).

PS : yeah one can use QML singleton instead of JS module, but it requires extra qmldir file and is supported only from Qt 5.2, which can be limiting. And obviously, a C++ QObject inside a context property would also work (e.g. if you want to load skin properties from a file on the disk...).

相关文章