QML学习——Qt Quick Extras Examples 1.4(八)

Qt Quick Extras Examples

阅读官方的源码然后尝试做了下

01 A car dashboard
  • 样例演示:

  • 说明:

    • ValueSource组件控制数值相关的动画,例如图中数值的变化;
    • TurnIndicator组件是控制左右方向灯的闪烁和背景,里面使用了Canvas来绘制样式;
    • IconGaugeStyle是对DashboardGaugeStyle的封装,里面绘制了带图标仪表盘的样式,用于左边油量表和水温表的样式;
    • DashboardGaugeStyle是对CircularGaugeStyle的封装,用于中间的速度仪表的样式;
    • TachometerStyle是对DashboardGaugeStyle的封装,用于右边转速表的样式;

具体代码:

TestExtrasDashboard.qml

js 复制代码
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4

import "./Component"

Rectangle {
    id: root
    width: 1024
    height: 600

    color: "#161616"

    ValueSource {
        id: valueSource
    }

    // Dashboards are typically in a landscape orientation, so we need to ensure
    // our height is never greater than our width.
    Item {
        id: container
        width: root.width
        height: Math.min(root.width, root.height)
        anchors.centerIn: parent

        Row {
            id: gaugeRow
            spacing: container.width * 0.02
            anchors.centerIn: parent

            TurnIndicator {
                id: leftIndicator
                anchors.verticalCenter: parent.verticalCenter
                width: height
                height: container.height * 0.1 - gaugeRow.spacing

                direction: Qt.LeftArrow
                on: valueSource.turnSignal === Qt.LeftArrow
            }

            Item {
                width: height
                height: container.height * 0.25 - gaugeRow.spacing
                anchors.verticalCenter: parent.verticalCenter

                CircularGauge {
                    id: fuelGauge
                    value: valueSource.fuel
                    maximumValue: 1
                    y: parent.height / 2 - height / 2 - container.height * 0.01
                    width: parent.width
                    height: parent.height * 0.7

                    style: IconGaugeStyle {
                        id: fuelGaugeStyle

                        icon: "qrc:/Icons/Used_Images/Icons/02_10-fuel-icon.png"
                        minWarningColor: Qt.rgba(0.5, 0, 0, 1)

                        tickmarkLabel: Text {
                            color: "white"
                            visible: styleData.value === 0 || styleData.value === 1
                            font.pixelSize: fuelGaugeStyle.toPixels(0.225)
                            text: styleData.value === 0 ? "E" : (styleData.value === 1 ? "F" : "")
                        }
                    }
                }

                CircularGauge {
                    value: valueSource.temperature
                    maximumValue: 1
                    width: parent.width
                    height: parent.height * 0.7
                    y: parent.height / 2 + container.height * 0.01

                    style: IconGaugeStyle {
                        id: tempGaugeStyle

                        icon: "qrc:/Icons/Used_Images/Icons/03_10-temperature-icon.png"
                        maxWarningColor: Qt.rgba(0.5, 0, 0, 1)

                        tickmarkLabel: Text {
                            color: "white"
                            visible: styleData.value === 0 || styleData.value === 1
                            font.pixelSize: tempGaugeStyle.toPixels(0.225)
                            text: styleData.value === 0 ? "C" : (styleData.value === 1 ? "H" : "")
                        }
                    }
                }
            }

            CircularGauge {
                id: speedometer
                value: valueSource.kph
                anchors.verticalCenter: parent.verticalCenter
                maximumValue: 280
                // We set the width to the height, because the height will always be
                // the more limited factor. Also, all circular controls letterbox
                // their contents to ensure that they remain circular. However, we
                // don't want to extra space on the left and right of our gauges,
                // because they're laid out horizontally, and that would create
                // large horizontal gaps between gauges on wide screens.
                width: height
                height: container.height * 0.5

                style: DashboardGaugeStyle {}
            }

            CircularGauge {
                id: tachometer
                width: height
                height: container.height * 0.25 - gaugeRow.spacing
                value: valueSource.rpm
                maximumValue: 8
                anchors.verticalCenter: parent.verticalCenter

                style: TachometerStyle {}
            }

            TurnIndicator {
                id: rightIndicator
                anchors.verticalCenter: parent.verticalCenter
                width: height
                height: container.height * 0.1 - gaugeRow.spacing

                direction: Qt.RightArrow
                on: valueSource.turnSignal === Qt.RightArrow
            }
        }
    }
}

DashboardGaugeStyle.qml

js 复制代码
import QtQuick 2.2
import QtQuick.Controls.Styles 1.4

CircularGaugeStyle {
    tickmarkInset: toPixels(0.04)
    minorTickmarkInset: tickmarkInset
    labelStepSize: 20
    labelInset: toPixels(0.23)

    property real xCenter: outerRadius
    property real yCenter: outerRadius
    property real needleLength: outerRadius - tickmarkInset * 1.25
    property real needleTipWidth: toPixels(0.02)
    property real needleBaseWidth: toPixels(0.06)
    property bool halfGauge: false

    function toPixels(percentage) {
        return percentage * outerRadius;
    }

    function degToRad(degrees) {
        return degrees * (Math.PI / 180);
    }

    function radToDeg(radians) {
        return radians * (180 / Math.PI);
    }

    function paintBackground(ctx) {
        if (halfGauge) {
            ctx.beginPath();
            ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height / 2);
            ctx.clip();
        }

        ctx.beginPath();
        ctx.fillStyle = "black";
        ctx.ellipse(0, 0, ctx.canvas.width, ctx.canvas.height);
        ctx.fill();

        ctx.beginPath();
        ctx.lineWidth = tickmarkInset;
        ctx.strokeStyle = "black";
        ctx.arc(xCenter, yCenter, outerRadius - ctx.lineWidth / 2, outerRadius - ctx.lineWidth / 2, 0, Math.PI * 2);
        ctx.stroke();

        ctx.beginPath();
        ctx.lineWidth = tickmarkInset / 2;
        ctx.strokeStyle = "#222";
        ctx.arc(xCenter, yCenter, outerRadius - ctx.lineWidth / 2, outerRadius - ctx.lineWidth / 2, 0, Math.PI * 2);
        ctx.stroke();

        ctx.beginPath();
        var gradient = ctx.createRadialGradient(xCenter, yCenter, 0, xCenter, yCenter, outerRadius * 1.5);
        gradient.addColorStop(0, Qt.rgba(1, 1, 1, 0));
        gradient.addColorStop(0.7, Qt.rgba(1, 1, 1, 0.13));
        gradient.addColorStop(1, Qt.rgba(1, 1, 1, 1));
        ctx.fillStyle = gradient;
        ctx.arc(xCenter, yCenter, outerRadius - tickmarkInset, outerRadius - tickmarkInset, 0, Math.PI * 2);
        ctx.fill();
    }

    background: Canvas {
        onPaint: {
            var ctx = getContext("2d");
            ctx.reset();
            paintBackground(ctx);
        }

        Text {
            id: speedText
            font.pixelSize: toPixels(0.3)
            text: kphInt
            color: "white"
            horizontalAlignment: Text.AlignRight
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top: parent.verticalCenter
            anchors.topMargin: toPixels(0.1)

            readonly property int kphInt: control.value
        }
        Text {
            text: "km/h"
            color: "white"
            font.pixelSize: toPixels(0.09)
            anchors.top: speedText.bottom
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

    needle: Canvas {
        implicitWidth: needleBaseWidth
        implicitHeight: needleLength

        property real xCenter: width / 2
        property real yCenter: height / 2

        onPaint: {
            var ctx = getContext("2d");
            ctx.reset();

            ctx.beginPath();
            ctx.moveTo(xCenter, height);
            ctx.lineTo(xCenter - needleBaseWidth / 2, height - needleBaseWidth / 2);
            ctx.lineTo(xCenter - needleTipWidth / 2, 0);
            ctx.lineTo(xCenter, yCenter - needleLength);
            ctx.lineTo(xCenter, 0);
            ctx.closePath();
            ctx.fillStyle = Qt.rgba(0.66, 0, 0, 0.66);
            ctx.fill();

            ctx.beginPath();
            ctx.moveTo(xCenter, height)
            ctx.lineTo(width, height - needleBaseWidth / 2);
            ctx.lineTo(xCenter + needleTipWidth / 2, 0);
            ctx.lineTo(xCenter, 0);
            ctx.closePath();
            ctx.fillStyle = Qt.lighter(Qt.rgba(0.66, 0, 0, 0.66));
            ctx.fill();
        }
    }

    foreground: null
}

IconGaugeStyle.qml

js 复制代码
import QtQuick 2.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4

DashboardGaugeStyle {
    id: fuelGaugeStyle
    minimumValueAngle: -60
    maximumValueAngle: 60
    tickmarkStepSize: 1
    labelStepSize: 1
    labelInset: toPixels(-0.25)
    minorTickmarkCount: 3

    needleLength: toPixels(0.85)
    needleBaseWidth: toPixels(0.08)
    needleTipWidth: toPixels(0.03)

    halfGauge: true

    property string icon: ""
    property color minWarningColor: "transparent"
    property color maxWarningColor: "transparent"
    readonly property real minWarningStartAngle: minimumValueAngle - 90
    readonly property real maxWarningStartAngle: maximumValueAngle - 90

    tickmark: Rectangle {
        implicitWidth: toPixels(0.06)
        antialiasing: true
        implicitHeight: toPixels(0.2)
        color: "#c8c8c8"
    }

    minorTickmark: Rectangle {
        implicitWidth: toPixels(0.03)
        antialiasing: true
        implicitHeight: toPixels(0.15)
        color: "#c8c8c8"
    }

    background: Item {
        Canvas {
            anchors.fill: parent
            onPaint: {
                var ctx = getContext("2d");
                ctx.reset();

                paintBackground(ctx);

                if (minWarningColor != "transparent") {
                    ctx.beginPath();
                    ctx.lineWidth = fuelGaugeStyle.toPixels(0.08);
                    ctx.strokeStyle = minWarningColor;
                    ctx.arc(outerRadius, outerRadius,
                        // Start the line in from the decorations, and account for the width of the line itself.
                        outerRadius - tickmarkInset - ctx.lineWidth / 2,
                        degToRad(minWarningStartAngle),
                        degToRad(minWarningStartAngle + angleRange / (minorTickmarkCount + 1)), false);
                    ctx.stroke();
                }
                if (maxWarningColor != "transparent") {
                    ctx.beginPath();
                    ctx.lineWidth = fuelGaugeStyle.toPixels(0.08);
                    ctx.strokeStyle = maxWarningColor;
                    ctx.arc(outerRadius, outerRadius,
                        // Start the line in from the decorations, and account for the width of the line itself.
                        outerRadius - tickmarkInset - ctx.lineWidth / 2,
                        degToRad(maxWarningStartAngle - angleRange / (minorTickmarkCount + 1)),
                        degToRad(maxWarningStartAngle), false);
                    ctx.stroke();
                }
            }
        }

        Image {
            source: icon
            anchors.bottom: parent.verticalCenter
            anchors.bottomMargin: toPixels(0.3)
            anchors.horizontalCenter: parent.horizontalCenter
            width: toPixels(0.3)
            height: width
            fillMode: Image.PreserveAspectFit
        }
    }
}

TachometerStyle.qml

js 复制代码
import QtQuick 2.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4

DashboardGaugeStyle {
    id: tachometerStyle
    tickmarkStepSize: 1
    labelStepSize: 1
    needleLength: toPixels(0.85)
    needleBaseWidth: toPixels(0.08)
    needleTipWidth: toPixels(0.03)

    tickmark: Rectangle {
        implicitWidth: toPixels(0.03)
        antialiasing: true
        implicitHeight: toPixels(0.08)
        color: styleData.index === 7 || styleData.index === 8 ? Qt.rgba(0.5, 0, 0, 1) : "#c8c8c8"
    }

    minorTickmark: null

    tickmarkLabel: Text {
        font.pixelSize: Math.max(6, toPixels(0.12))
        text: styleData.value
        color: styleData.index === 7 || styleData.index === 8 ? Qt.rgba(0.5, 0, 0, 1) : "#c8c8c8"
        antialiasing: true
    }

    background: Canvas {
        onPaint: {
            var ctx = getContext("2d");
            ctx.reset();
            paintBackground(ctx);

            ctx.beginPath();
            ctx.lineWidth = tachometerStyle.toPixels(0.08);
            ctx.strokeStyle = Qt.rgba(0.5, 0, 0, 1);
            var warningCircumference = maximumValueAngle - minimumValueAngle * 0.1;
            var startAngle = maximumValueAngle - 90;
            ctx.arc(outerRadius, outerRadius,
                // Start the line in from the decorations, and account for the width of the line itself.
                outerRadius - tickmarkInset - ctx.lineWidth / 2,
                degToRad(startAngle - angleRange / 8 + angleRange * 0.015),
                degToRad(startAngle - angleRange * 0.015), false);
            ctx.stroke();
        }

        Text {
            id: rpmText
            font.pixelSize: tachometerStyle.toPixels(0.3)
            text: rpmInt
            color: "white"
            horizontalAlignment: Text.AlignRight
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top: parent.verticalCenter
            anchors.topMargin: 20

            readonly property int rpmInt: valueSource.rpm
        }
        Text {
            text: "x1000"
            color: "white"
            font.pixelSize: tachometerStyle.toPixels(0.1)
            anchors.top: parent.top
            anchors.topMargin: parent.height / 4
            anchors.horizontalCenter: parent.horizontalCenter
        }
        Text {
            text: "RPM"
            color: "white"
            font.pixelSize: tachometerStyle.toPixels(0.1)
            anchors.top: rpmText.bottom
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }
}

TurnIndicator.qml

js 复制代码
import QtQuick 2.2

Item {
    // This enum is actually keyboard-related, but it serves its purpose
    // as an indication of direction for us.
    property int direction: Qt.LeftArrow
    property bool on: false

    property bool flashing: false

    scale: direction === Qt.LeftArrow ? 1 : -1
//! [1]
    Timer {
        id: flashTimer
        interval: 500
        running: on
        repeat: true
        onTriggered: flashing = !flashing
    }
//! [1]
//! [2]
    function paintOutlinePath(ctx) {
        ctx.beginPath();
        ctx.moveTo(0, height * 0.5);
        ctx.lineTo(0.6 * width, 0);
        ctx.lineTo(0.6 * width, height * 0.28);
        ctx.lineTo(width, height * 0.28);
        ctx.lineTo(width, height * 0.72);
        ctx.lineTo(0.6 * width, height * 0.72);
        ctx.lineTo(0.6 * width, height);
        ctx.lineTo(0, height * 0.5);
    }
//! [2]
    Canvas {
        id: backgroundCanvas
        anchors.fill: parent

        onPaint: {
            var ctx = getContext("2d");
            ctx.reset();

            paintOutlinePath(ctx);

            ctx.lineWidth = 1;
            ctx.strokeStyle = "pink";
            ctx.stroke();
        }
    }
//! [3]
    Canvas {
        id: foregroundCanvas
        anchors.fill: parent
        visible: on && flashing

        onPaint: {
            var ctx = getContext("2d");
            ctx.reset();

            paintOutlinePath(ctx);

            ctx.fillStyle = "green";
            ctx.fill();
        }
    }
//! [3]
}

ValueSource.qml

js 复制代码
import QtQuick 2.2
//! [0]
Item {
    id: valueSource
    property real kph: 0
    property real rpm: 1
    property real fuel: 0.85
    property string gear: {
        var g;
        if (kph == 0) {
            return "P";
        }
        if (kph < 30) {
            return "1";
        }
        if (kph < 50) {
            return "2";
        }
        if (kph < 80) {
            return "3";
        }
        if (kph < 120) {
            return "4";
        }
        if (kph < 160) {
            return "5";
        }
    }
    property int turnSignal: gear == "P" && !start ? randomDirection() : -1
    property real temperature: 0.6
    property bool start: true
//! [0]

    function randomDirection() {
        return Math.random() > 0.5 ? Qt.LeftArrow : Qt.RightArrow;
    }

    SequentialAnimation {
        running: true
        loops: 1

        // We want a small pause at the beginning, but we only want it to happen once.
        PauseAnimation {
            duration: 1000
        }

        PropertyAction {
            target: valueSource
            property: "start"
            value: false
        }

        SequentialAnimation {
            loops: Animation.Infinite
//! [1]
            ParallelAnimation {
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    from: 0
                    to: 30
                    duration: 3000
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    from: 1
                    to: 6.1
                    duration: 3000
                }
            }
//! [1]
            ParallelAnimation {
                // We changed gears so we lost a bit of speed.
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    from: 30
                    to: 26
                    duration: 600
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    from: 6
                    to: 2.4
                    duration: 600
                }
            }
            ParallelAnimation {
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    to: 60
                    duration: 3000
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    to: 5.6
                    duration: 3000
                }
            }
            ParallelAnimation {
                // We changed gears so we lost a bit of speed.
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    to: 56
                    duration: 600
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    to: 2.3
                    duration: 600
                }
            }
            ParallelAnimation {
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    to: 100
                    duration: 3000
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    to: 5.1
                    duration: 3000
                }
            }
            ParallelAnimation {
                // We changed gears so we lost a bit of speed.
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    to: 96
                    duration: 600
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    to: 2.2
                    duration: 600
                }
            }

            ParallelAnimation {
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    to: 140
                    duration: 3000
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    to: 6.2
                    duration: 3000
                }
            }

            // Start downshifting.

            // Fifth to fourth gear.
            ParallelAnimation {
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.Linear
                    to: 100
                    duration: 5000
                }

                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    to: 3.1
                    duration: 5000
                }
            }

            // Fourth to third gear.
            NumberAnimation {
                target: valueSource
                property: "rpm"
                easing.type: Easing.InOutSine
                to: 5.5
                duration: 600
            }

            ParallelAnimation {
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    to: 60
                    duration: 5000
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    to: 2.6
                    duration: 5000
                }
            }

            // Third to second gear.
            NumberAnimation {
                target: valueSource
                property: "rpm"
                easing.type: Easing.InOutSine
                to: 6.3
                duration: 600
            }

            ParallelAnimation {
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    to: 30
                    duration: 5000
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    to: 2.6
                    duration: 5000
                }
            }

            NumberAnimation {
                target: valueSource
                property: "rpm"
                easing.type: Easing.InOutSine
                to: 6.5
                duration: 600
            }

            // Second to first gear.
            ParallelAnimation {
                NumberAnimation {
                    target: valueSource
                    property: "kph"
                    easing.type: Easing.InOutSine
                    to: 0
                    duration: 5000
                }
                NumberAnimation {
                    target: valueSource
                    property: "rpm"
                    easing.type: Easing.InOutSine
                    to: 1
                    duration: 4500
                }
            }

            PauseAnimation {
                duration: 5000
            }
        }
    }
}
02 Flat Style

目前已经没有QtQuick.Controls.Styles.Flat,该例子没有实验成功;

main.cpp

c++ 复制代码
#include <QtGui/QGuiApplication>
#include <QtQml/QQmlApplicationEngine>
#include <QtGui/QFontDatabase>
#include <QtCore/QDir>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    if (qgetenv("QT_QUICK_CONTROLS_1_STYLE").isEmpty()) {
#ifdef QT_STATIC
        // Need a full path to find the style when built statically
        qputenv("QT_QUICK_CONTROLS_1_STYLE", ":/ExtrasImports/QtQuick/Controls/Styles/Flat");
#else
        qputenv("QT_QUICK_CONTROLS_1_STYLE", "Flat");
#endif
    }
    QQmlApplicationEngine engine;
    engine.load(QUrl("qrc:/main.qml"));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();
}

main.qml

js 复制代码
import QtQml 2.14 as Qml
import QtQuick 2.4
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles.Flat 1.0 as Flat
import QtQuick.Extras 1.4
import QtQuick.Extras.Private 1.0

ApplicationWindow {
    id: window
    width: 480
    height: 860
    title: "Flat Example"
    visible: true

    readonly property bool contentLoaded: contentLoader.item
    readonly property alias anchorItem: controlsMenu
    property int currentMenu: -1
    readonly property int textMargins: Math.round(32 * Flat.FlatStyle.scaleFactor)
    readonly property int menuMargins: Math.round(13 * Flat.FlatStyle.scaleFactor)
    readonly property int menuWidth: Math.min(window.width, window.height) * 0.75

    onCurrentMenuChanged: {
        xBehavior.enabled = true;
        anchorCurrentMenu();
    }

    onMenuWidthChanged: anchorCurrentMenu()

    function anchorCurrentMenu() {
        switch (currentMenu) {
        case -1:
            anchorItem.x = -menuWidth;
            break;
        case 0:
            anchorItem.x = 0;
            break;
        case 1:
            anchorItem.x = -menuWidth * 2;
            break;
        }
    }

    Item {
        id: container
        anchors.fill: parent

        Item {
            id: loadingScreen
            anchors.fill: parent
            visible: !contentLoaded

            Timer {
                running: true
                interval: 100
                // TODO: Find a way to know when the loading screen has been rendered instead
                // of using an abritrary amount of time.
                onTriggered: contentLoader.sourceComponent = Qt.createComponent("Content.qml")
            }

            Column {
                anchors.centerIn: parent
                spacing: Math.round(10 * Flat.FlatStyle.scaleFactor)

                BusyIndicator {
                    anchors.horizontalCenter: parent.horizontalCenter
                }

                Label {
                    text: "Loading Light Flat UI..."
                    width: Math.min(loadingScreen.width, loadingScreen.height) * 0.8
                    height: font.pixelSize
                    anchors.horizontalCenter: parent.horizontalCenter
                    renderType: Text.QtRendering
                    font.pixelSize: Math.round(26 * Flat.FlatStyle.scaleFactor)
                    horizontalAlignment: Text.AlignHCenter
                    fontSizeMode: Text.Fit
                    font.family: Flat.FlatStyle.fontFamily
                    font.weight: Font.Light
                }
            }
        }

        Rectangle {
            id: controlsMenu
            x: container.x - width
            z: contentContainer.z + 1
            width: menuWidth
            height: parent.height

            // Don't let the menus become visible when resizing the window
            Qml.Binding {
                target: controlsMenu
                property: "x"
                value: container.x - controlsMenu.width
                when: !xBehavior.enabled && !xNumberAnimation.running && currentMenu == -1
                restoreMode: Binding.RestoreBinding
            }

            Behavior on x {
                id: xBehavior
                enabled: false
                NumberAnimation {
                    id: xNumberAnimation
                    easing.type: Easing.OutExpo
                    duration: 500
                    onRunningChanged: xBehavior.enabled = false
                }
            }

            Rectangle {
                id: controlsTitleBar
                width: parent.width
                height: toolBar.height
                color: Flat.FlatStyle.defaultTextColor

                Label {
                    text: "Controls"
                    font.family: Flat.FlatStyle.fontFamily
                    font.pixelSize: Math.round(16 * Flat.FlatStyle.scaleFactor)
                    renderType: Text.QtRendering
                    color: "white"
                    anchors.left: parent.left
                    anchors.leftMargin: menuMargins
                    anchors.verticalCenter: parent.verticalCenter
                }
            }

            ListView {
                id: controlView
                width: parent.width
                anchors.top: controlsTitleBar.bottom
                anchors.bottom: parent.bottom
                clip: true
                currentIndex: 0
                model: contentLoaded ? contentLoader.item.componentModel : null
                delegate: MouseArea {
                    id: delegateItem
                    width: parent.width
                    height: 64 * Flat.FlatStyle.scaleFactor
                    onClicked: {
                        if (controlView.currentIndex != index)
                            controlView.currentIndex = index;

                        currentMenu = -1;
                    }

                    Rectangle {
                        width: parent.width
                        height: Flat.FlatStyle.onePixel
                        anchors.bottom: parent.bottom
                        color: Flat.FlatStyle.lightFrameColor
                    }

                    Label {
                        text: delegateItem.ListView.view.model[index].name
                        font.pixelSize: Math.round(15 * Flat.FlatStyle.scaleFactor)
                        font.family: Flat.FlatStyle.fontFamily
                        renderType: Text.QtRendering
                        color: delegateItem.ListView.isCurrentItem ? Flat.FlatStyle.styleColor : Flat.FlatStyle.defaultTextColor
                        anchors.left: parent.left
                        anchors.leftMargin: menuMargins
                        anchors.verticalCenter: parent.verticalCenter
                    }
                }

                Rectangle {
                    width: parent.height
                    height: 8 * Flat.FlatStyle.scaleFactor
                    rotation: 90
                    anchors.left: parent.right
                    transformOrigin: Item.TopLeft

                    gradient: Gradient {
                        GradientStop {
                            color: Qt.rgba(0, 0, 0, 0.15)
                            position: 0
                        }
                        GradientStop {
                            color: Qt.rgba(0, 0, 0, 0.05)
                            position: 0.5
                        }
                        GradientStop {
                            color: Qt.rgba(0, 0, 0, 0)
                            position: 1
                        }
                    }
                }
            }
        }

        Item {
            id: contentContainer
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.left: controlsMenu.right
            width: parent.width

            ToolBar {
                id: toolBar
                visible: !loadingScreen.visible
                width: parent.width
                height: 54 * Flat.FlatStyle.scaleFactor
                z: contentLoader.z + 1
                style: Flat.ToolBarStyle {
                    padding.left: 0
                    padding.right: 0
                }

                RowLayout {
                    anchors.fill: parent

                    MouseArea {
                        id: controlsButton
                        Layout.preferredWidth: toolBar.height + textMargins
                        Layout.preferredHeight: toolBar.height
                        onClicked: currentMenu = currentMenu == 0 ? -1 : 0

                        Column {
                            id: controlsIcon
                            anchors.left: parent.left
                            anchors.leftMargin: textMargins
                            anchors.verticalCenter: parent.verticalCenter
                            spacing: Math.round(2 * Flat.FlatStyle.scaleFactor)

                            Repeater {
                                model: 3

                                Rectangle {
                                    width: Math.round(4 * Flat.FlatStyle.scaleFactor)
                                    height: width
                                    radius: width / 2
                                    color: Flat.FlatStyle.defaultTextColor
                                }
                            }
                        }
                    }

                    Text {
                        text: "Light Flat UI Demo"
                        font.family: Flat.FlatStyle.fontFamily
                        font.pixelSize: Math.round(16 * Flat.FlatStyle.scaleFactor)
                        horizontalAlignment: Text.AlignHCenter
                        color: "#666666"
                        Layout.fillWidth: true
                    }

                    MouseArea {
                        id: settingsButton
                        Layout.preferredWidth: controlsButton.Layout.preferredWidth
                        Layout.preferredHeight: controlsButton.Layout.preferredHeight
                        onClicked: currentMenu = currentMenu == 1 ? -1 : 1

                        SettingsIcon {
                            width: Math.round(32 * Flat.FlatStyle.scaleFactor)
                            height: Math.round(32 * Flat.FlatStyle.scaleFactor)
                            anchors.verticalCenter: parent.verticalCenter
                            anchors.right: parent.right
                            anchors.rightMargin: textMargins - Math.round(8 * Flat.FlatStyle.scaleFactor)
                        }
                    }
                }
            }

            Loader {
                id: contentLoader
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.top: toolBar.bottom
                anchors.bottom: parent.bottom

                property QtObject settingsData: QtObject {
                    readonly property bool checks: disableSingleItemsAction.checked
                    readonly property bool frames: !greyBackgroundAction.checked
                    readonly property bool allDisabled: disableAllAction.checked
                }
                property QtObject controlData: QtObject {
                    readonly property int componentIndex: controlView.currentIndex
                    readonly property int textMargins: window.textMargins
                }

                MouseArea {
                    enabled: currentMenu != -1
                    // We would be able to just set this to true here, if it weren't for QTBUG-43083.
                    hoverEnabled: enabled
                    anchors.fill: parent
                    preventStealing: true
                    onClicked: currentMenu = -1
                    focus: enabled
                    z: 1000
                }
            }
        }

        Rectangle {
            id: settingsMenu
            z: contentContainer.z + 1
            width: menuWidth
            height: parent.height
            anchors.left: contentContainer.right

            Rectangle {
                id: optionsTitleBar
                width: parent.width
                height: toolBar.height
                color: Flat.FlatStyle.defaultTextColor

                Label {
                    text: "Options"
                    font.family: Flat.FlatStyle.fontFamily
                    font.pixelSize: Math.round(16 * Flat.FlatStyle.scaleFactor)
                    renderType: Text.QtRendering
                    color: "white"
                    anchors.left: parent.left
                    anchors.leftMargin: menuMargins
                    anchors.verticalCenter: parent.verticalCenter
                }
            }

            Action {
                id: disableAllAction
                checkable: true
                checked: false
            }

            Action {
                id: disableSingleItemsAction
                checkable: true
                checked: false
            }

            Action {
                id: greyBackgroundAction
                checkable: true
                checked: false
            }

            ListView {
                id: optionsListView
                width: parent.width
                anchors.top: optionsTitleBar.bottom
                anchors.bottom: parent.bottom
                clip: true
                interactive: delegateHeight * count > height

                readonly property int delegateHeight: 64 * Flat.FlatStyle.scaleFactor

                model: [
                    { name: "Disable all", action: disableAllAction },
                    { name: "Disable single items", action: disableSingleItemsAction },
                    { name: "Grey background", action: greyBackgroundAction },
                    { name: "Exit", action: null }
                ]
                delegate: Rectangle {
                    id: optionDelegateItem
                    width: parent.width
                    height: optionsListView.delegateHeight

                    Rectangle {
                        width: parent.width
                        height: Flat.FlatStyle.onePixel
                        anchors.bottom: parent.bottom
                        color: Flat.FlatStyle.lightFrameColor
                    }

                    Loader {
                        sourceComponent: optionText !== "Exit"
                            ? optionsListView.checkBoxComponent : optionsListView.exitComponent
                        anchors.fill: parent
                        anchors.leftMargin: menuMargins

                        property string optionText: optionsListView.model[index].name
                        property int optionIndex: index
                    }
                }

                property Component checkBoxComponent: Item {
                    Label {
                        text: optionText
                        font.family: Flat.FlatStyle.fontFamily
                        font.pixelSize: Math.round(15 * Flat.FlatStyle.scaleFactor)
                        fontSizeMode: Text.Fit
                        renderType: Text.QtRendering
                        verticalAlignment: Text.AlignVCenter
                        color: Flat.FlatStyle.defaultTextColor
                        height: parent.height
                        anchors.left: parent.left
                        anchors.right: checkBox.left
                        anchors.rightMargin: Flat.FlatStyle.twoPixels
                    }

                    CheckBox {
                        id: checkBox
                        checked: optionsListView.model[optionIndex].action.checked
                        anchors.right: parent.right
                        anchors.rightMargin: menuMargins
                        anchors.verticalCenter: parent.verticalCenter
                        onCheckedChanged: optionsListView.model[optionIndex].action.checked = checkBox.checked
                    }
                }

                property Component exitComponent: MouseArea {
                    anchors.fill: parent
                    onClicked: Qt.quit()

                    Label {
                        text: optionText
                        font.family: Flat.FlatStyle.fontFamily
                        font.pixelSize: Math.round(15 * Flat.FlatStyle.scaleFactor)
                        renderType: Text.QtRendering
                        color: Flat.FlatStyle.defaultTextColor
                        anchors.verticalCenter: parent.verticalCenter
                    }
                }

                Rectangle {
                    width: parent.height
                    height: 8 * Flat.FlatStyle.scaleFactor
                    rotation: -90
                    anchors.right: parent.left
                    transformOrigin: Item.TopRight

                    gradient: Gradient {
                        GradientStop {
                            color: Qt.rgba(0, 0, 0, 0.15)
                            position: 0
                        }
                        GradientStop {
                            color: Qt.rgba(0, 0, 0, 0.05)
                            position: 0.5
                        }
                        GradientStop {
                            color: Qt.rgba(0, 0, 0, 0)
                            position: 1
                        }
                    }
                }
            }
        }
    }
}
Point
  • 当需要有连续的model时:

    • 可以等model创建完成后再添加item

    js 复制代码
    model: ListModel {
        //等ListModel创建出来再添加
        Component.onCompleted: {
            for (var i = 2000; i < 2100; ++i) {
                append({value: i.toString()});
            }
        }
    }
  • 需要用不同字体时,可以在root组件创建一个空字体,然后在对应的组件来根据root字体来按比例缩放:

    • root中最外层的字体:

    js 复制代码
    Text {
        id: textSingleton
    }
    //or:
    FontMetrics {
        id: fontMetrics
        font.family: "Arial"
    }
    • 要使用时:

    js 复制代码
    Xxxx{
        Label {
            font.pixelSize: textSingleton.font.pixelSize * 1.25
            font.family: fontMetrics.font.family
        }
    }
    • 如果是外部字体,也可以导入使用,例如:

    js 复制代码
    FontLoader {
    	id: openSans
        source: "qrc:/fonts/OpenSans-Regular.ttf"
    }
    //use:
    Label {
        font.pixelSize: textSingleton.font.pixelSize * 1.25
    	font.family: openSans.name //use name!
    }
  • 当有组件类似功能时,可以使用同一种Control来管理;

源码
主界面
  • TestAllExtraGallery.qml
js 复制代码
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.15

import "./Viewer/"
import "./Customizer/"
import "./Style/"

Rectangle {
    id: root
    width: 480
    height: 600
    color: "#161616"
    clip: true

    function toPixels(percentage) {
        return percentage * Math.min(root.width, root.height);
    }

    property bool isScreenPortrait: height > width
    property color lightFontColor: "#222"                   //亮色模式字体统一颜色
    property color darkFontColor: "#e7e7e7"                 //暗色模式字体颜色
    readonly property color lightBackgroundColor: "#cccccc" //亮色背景颜色
    readonly property color darkBackgroundColor: "#161616"  //暗色背景颜色
    property real customizerPropertySpacing: 10             //设置中自定义控制组件的间隔
    property real colorPickerRowSpacing: 8


    //01 CircularGauge仪表盘组件,由CircularGaugeView单独管理
    property Component circularGauge: CircularGaugeView {}

    //02 DelayButton延迟按钮组件,由ControlView统一管理
    property Component delayButton: ControlView {
        darkBackground: false

        //中间延迟按钮区域组件,无自定组件区域
        control: ColumnLayout { //加个布局方便随主界面大小变化而变化
            // width: parent.width
            // height: (delayButtonColumn.height - delayButtonColumn.spacing) / 2
            // spacing: height * 0.025
            width: controlBounds.width
            height: controlBounds.height - spacing
            spacing: root.toPixels(0.05)

            DelayButton {
                id: delayButton
                Layout.alignment: Qt.AlignCenter
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: "Alarm"
                //Quick Contral 1 通过这种方式更改按钮中字体的大小:
                style: DelayButtonStyle {
                    label: Text {
                        renderType: Text.NativeRendering
                        verticalAlignment: Text.AlignVCenter
                        horizontalAlignment: Text.AlignHCenter
                        font.pointSize: 20
                        font.bold: true
                        font.family: "Source Code Pro"
                        color: "blue"
                        text: delayButton.text
                    }
                }
            }
        }
    }

    //03 Dial旋钮组件,由ControlView统一管理
    property Component dial: ControlView {
        darkBackground: false

        //中间控制旋钮区域
        control: Column {
            id: dialColumn
            width: controlBounds.width
            height: controlBounds.height - spacing
            spacing: root.toPixels(0.05)

            //第一个旋钮
            ColumnLayout {
                id: volumeColumn
                width: parent.width
                height: (dialColumn.height - dialColumn.spacing) / 2
                spacing: height * 0.025

                //上面的旋钮
                Dial {
                    id: volumeDial
                    Layout.alignment: Qt.AlignCenter
                    Layout.fillWidth: true
                    Layout.fillHeight: true
                    onPressedChanged: console.log("current value:" + value);

                    /*!
                        Determines whether the dial animates its rotation to the new value when
                        a single click or touch is received on the dial.
                    */
                    //当animate按钮打开时,单击Dial会使用属性动画,而按住拖拉则不会
                    property bool animate: customizerItem.animate

                    Behavior on value {
                        enabled: volumeDial.animate && !volumeDial.pressed
                        NumberAnimation {
                            duration: 300
                            easing.type: Easing.OutSine
                        }
                    }
                }

                //旋钮文字
                ControlLabel {
                    id: volumeText
                    text: "Volume"
                    Layout.alignment: Qt.AlignCenter
                }
            }

            //第二个旋钮
            ColumnLayout {
                id: trebleColumn
                width: parent.width
                height: (dialColumn.height - dialColumn.spacing) / 2
                spacing: height * 0.025

                Dial {
                    id: dial2
                    Layout.alignment: Qt.AlignCenter
                    Layout.fillWidth: true
                    Layout.fillHeight: true

                    //设置显示外面的刻度
                    stepSize: 1
                    maximumValue: 10

                    style: DialStyle {
                        //以像素为单位的从表盘(外半径)外部到值标记文本中心的距离。
                        labelInset: outerRadius * 0
                    }
                }

                ControlLabel {
                    id: trebleText
                    text: "Treble"
                    Layout.alignment: Qt.AlignCenter
                }
            }
        }

        //设置中的自定义控制区
        customizer: Column {
            spacing: customizerPropertySpacing

            property alias animate: animateCheckBox.checked

            CustomizerLabel {
                text: "Animate" + (animateCheckBox.checked ? " On" : " Off")
            }

            CustomizerSwitch {
                id: animateCheckBox
            }
        }
    }

    //04 Gauge刻度计组件,由ControlView统一管理
    property Component gauge: ControlView {
        //刻度计没有使用Style,所以默认暗色背景显示
        // darkBackground: false

        //中间刻度计区域
        control: Gauge {
            id: gauge
            //根据设置的方向判断长和宽
            width: orientation === Qt.Vertical ? implicitWidth : controlBounds.width
            height: orientation === Qt.Vertical ? controlBounds.height : implicitHeight
            anchors.centerIn: parent

            minimumValue: 0
            value: customizerItem.value
            maximumValue: 100
            //根据 设置中的自定义控制区中 手动设定的方向 来控制 中间刻度计区域的方向
            orientation: customizerItem.orientationFlag ? Qt.Vertical : Qt.Horizontal
            tickmarkAlignment: orientation === Qt.Vertical
                               ? (customizerItem.alignFlag ? Qt.AlignLeft : Qt.AlignRight)
                               : (customizerItem.alignFlag ? Qt.AlignTop : Qt.AlignBottom)
        }

        //设置中的自定义控制区
        customizer: Column {
            spacing: customizerPropertySpacing

            //方便ControlView的其他组件使用
            property alias value: valueSlider.value
            property alias orientationFlag: orientationCheckBox.checked
            property alias alignFlag: alignCheckBox.checked

            CustomizerLabel {
                text: "Value"
            }

            CustomizerSlider {
                id: valueSlider
                minimumValue: 0
                value: 50
                maximumValue: 100
            }

            CustomizerLabel {
                text: "Vertical orientation"
            }

            CustomizerSwitch {
                id: orientationCheckBox
                checked: true
            }

            CustomizerLabel {
                text: controlItem.orientation === Qt.Vertical ? "Left align" : "Top align"
            }

            CustomizerSwitch {
                id: alignCheckBox
                checked: true
            }
        }
    }

    //05 PieMenu圆盘形菜单组件,由PieMenuControlView单独管理
    property Component pieMenu: PieMenuControlView {}

    //06 StatusIndicator状态指示灯组件,由ControlView统一管理
    property Component statusIndicator: ControlView {
        id: statusIndicatorView
        darkBackground: false

        //中间指示灯区域组件,无自定组件区域
        control: ColumnLayout { //使得两个状态指示灯能随主窗口拉伸而拉伸
            id: indicatorLayout
            width: statusIndicatorView.controlBounds.width * 0.25
            height: statusIndicatorView.controlBounds.height * 0.75
            anchors.centerIn: parent

            //闪烁间隔时间
            Timer {
                id: recordingFlashTimer
                running: true
                repeat: true
                interval: 1000
            }

            Repeater {
                model: ListModel {
                    id: indicatorModel
                    ListElement {
                        name: "Power"
                        indicatorColor: "#35e02f"
                    }
                    ListElement {
                        name: "Recording"
                        indicatorColor: "red"
                    }
                }

                ColumnLayout {
                    //设置单个指示灯宽度,可随拉伸而拉伸
                    Layout.preferredWidth: indicatorLayout.width
                    spacing: 0

                    StatusIndicator {
                        id: indicator
                        color: indicatorColor
                        Layout.preferredWidth: statusIndicatorView.controlBounds.width * 0.07
                        Layout.preferredHeight: Layout.preferredWidth
                        Layout.alignment: Qt.AlignHCenter
                        on: true

                        //当定时器出发时改变状态
                        Connections {
                            target: recordingFlashTimer
                            function onTriggered() {
                                if (name == "Recording")
                                    indicator.active = !indicator.active
                            }
                        }
                    }
                    ControlLabel {
                        id: indicatorLabel
                        text: name
                        Layout.alignment: Qt.AlignHCenter
                        Layout.maximumWidth: parent.width
                        horizontalAlignment: Text.AlignHCenter
                    }
                }
            }
        }
    }

    //07 ToggleButton状态按钮组件,由ControlView统一管理
    property Component toggleButton: ControlView {
        id: toggleButtonView
        darkBackground: false

        //中间控制区域,无自定组件区域
        control: ColumnLayout {
            id: toggleButtonLayout
            width: controlBounds.width
            height: controlBounds.height - spacing
            spacing: root.toPixels(0.05)

            ToggleButton {
                Layout.alignment: Qt.AlignCenter
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: checked ? "On" : "Off"
            }
        }
    }

    //08 Tumbler滚动选择器组件,由ControlView统一管理
    property Component tumbler: ControlView {
        id: tumblerView
        darkBackground: false

        //中间控制区域,无自定组件区域
        control: Tumbler { //注意,这里是Extra1.4的Tumbler,不是QuickControls2的Tumbler
            id: tumbler
            anchors.centerIn: parent

            // TODO: Use FontMetrics with 5.4
            //需要有这个字符度规(衡量标准)不然会ReferenceError: characterMetrics is not defined
            Label {
                id: characterMetrics
                font.bold: true
                font.pixelSize: textSingleton.font.pixelSize * 1.25
                font.family: openSans.name
                visible: false
                text: "D"
            }

            readonly property real delegateTextMargins: characterMetrics.width * 1.5
            readonly property var days: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

            //日期的滚筒选择器
            TumblerColumn {
                id: tumblerDayColumn
                // model: ListModel {}

                function updateModel() {
                    console.log("tumblerDayColumn-->updateModel()");
                    var previousIndex = tumblerDayColumn.currentIndex;
                    var newDays = tumbler.days[monthColumn.currentIndex];

                    //模型不存在则增加
                    if (!model) {
                        var array = [];
                        for (var i = 0; i < newDays; ++i) {
                            array.push(i + 1);
                        }
                        model = array;
                    } else {
                        // If we've already got days in the model, just add or remove
                        // the minimum amount necessary to make spinning the month column fast.
                        var difference = model.length - newDays;
                        if (model.length > newDays) {
                            //有bug,删除数组这里的初始坐标应该加上difference,原例子没加
                            console.log("diff:" + difference);
                            var deleteArr = model.splice(model.length - difference, difference);
                            console.log("deleteArr:" + deleteArr + " new:" + model);
                        } else {
                            var lastDay = model[model.length - 1];
                            //这里difference是负的,所以是减difference,原例子是+,有bug
                            for (i = lastDay; i < lastDay - difference; ++i) {
                                model.push(i + 1);
                            }
                        }
                        //同步一下
                        tumblerDayColumn.model = model;
                    }
                    //月份改变时,如果之前的日期比当前月份大,则设为当前月份的最后一天
                    //curentIndex是只读属性
                    // tumblerDayColumn.currentIndex = Math.min(newDays - 1, previousIndex);
                    tumbler.setCurrentIndexAt(0, Math.min(newDays - 1, previousIndex));
                }
            }
            //月份的滚筒选择器
            TumblerColumn {
                id: monthColumn
                width: characterMetrics.width * 3 + tumbler.delegateTextMargins
                model: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
                //月份的值改变时,更新日期滚筒选择器
                onCurrentIndexChanged: {
                    console.log("monthTumblerColumn: onCurrentIndexChanged(): currentIndex:" + currentIndex + " role:" + role + " value:" + model[currentIndex]);
                    tumblerDayColumn.updateModel();
                }
            }
            //年份的滚筒选择器
            TumblerColumn {
                width: characterMetrics.width * 4 + tumbler.delegateTextMargins
                model: ListModel {
                    //等ListModel创建出来再添加
                    Component.onCompleted: {
                        for (var i = 2000; i < 2100; ++i) {
                            append({value: i.toString()});
                        }
                    }
                }
            }
        }
    }

    //组件集合
    property var componentMap: {
        "CircularGauge": circularGauge,
        "DelayButton": delayButton,
        "Dial": dial,
        "Gauge": gauge,
        "PieMenu": pieMenu,
        "StatusIndicator": statusIndicator,
        "ToggleButton": toggleButton,
        "Tumbler": tumbler
    }


    Text {
        id: textSingleton
    }

    //字体加载器
    FontLoader {
        id: openSans
        source: "qrc:/Others/Used_Others/01_11used_OpenSans-Regular.ttf"
    }

    //所有组件列表
    StackView {
        id: stackView
        anchors.fill: parent

        initialItem: ListView {
            model: ListModel {
                ListElement {
                    title: "CircularGauge"
                }
                ListElement {
                    title: "DelayButton"
                }
                ListElement {
                    title: "Dial"
                }
                ListElement {
                    title: "Gauge"
                }
                ListElement {
                    title: "PieMenu"
                }
                ListElement {
                    title: "StatusIndicator"
                }
                ListElement {
                    title: "ToggleButton"
                }
                ListElement {
                    title: "Tumbler"
                }
            }

            delegate: Button {
                width: stackView.width
                height: root.height * 0.125
                text: title

                style: BlackButtonStyle {
                    fontColor: root.darkFontColor
                    rightAlignedIconSource: "qrc:/Icons/Used_Images/Icons/09_11used_PieMenuControlView_go.png"
                }

                onClicked: {
                    if (stackView.depth == 1) {
                        // Only push the control view if we haven't already pushed it...
                        stackView.push({item: componentMap[title]});
                        stackView.currentItem.forceActiveFocus();
                    }
                }
            }
        }
    }
}
View组件界面
  • Viewer

    • CircularGaugeView.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls 1.4
    import QtQuick.Extras 1.4
    import "../Style/"
    import "../Customizer/"
    
    ControlView {
        id: controlView
        darkBackground: customizerItem.currentStyleDark
    
        property color fontColor: darkBackground ? "white" : "black"
    
        property bool accelerating: false
    
        Keys.onSpacePressed: accelerating = true
        Keys.onReleased: {
            if (event.key === Qt.Key_Space) {
                accelerating = false;
                event.accepted = true;
            }
        }
    
        Button {
            id: accelerate
            text: "Accelerate"
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.bottom: parent.bottom
            height: root.height * 0.125
    
            onPressedChanged: accelerating = pressed
    
            style: BlackButtonStyle {
                background: BlackButtonBackground {
                    pressed: control.pressed
                }
                label: Text {
                    text: control.text
                    color: "white"
                    font.pixelSize: Math.max(textSingleton.font.pixelSize, root.toPixels(0.04))
                    font.family: openSans.name
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                }
            }
        }
    
        //中央控制区域为CircularGauge
        control: CircularGauge {
            id: gauge
            minimumValue: customizerItem.minimumValue
            maximumValue: customizerItem.maximumValue
            width: controlBounds.width
            height: controlBounds.height
    
            value: accelerating ? maximumValue : 0
            style: styleMap[customizerItem.currentStylePath]
    
            // This stops the styles being recreated when a new one is chosen.
            property var styleMap: {
                var styles = {};
                for (var i = 0; i < customizerItem.allStylePaths.length; ++i) {
                    var path = customizerItem.allStylePaths[i];
                    styles[path] = Qt.createComponent(path, gauge);
                }
                styles;
            }
    
            // Called to update the style after the user has edited a property.
            Connections {
                target: customizerItem
                function onMinimumValueAngleChanged() { __style.minimumValueAngle = customizerItem.minimumValueAngle }
                function onMaximumValueAngleChanged() { __style.maximumValueAngle = customizerItem.maximumValueAngle }
                function onLabelStepSizeChanged() { __style.tickmarkStepSize = __style.labelStepSize = customizerItem.labelStepSize }
            }
    
            Behavior on value {
                NumberAnimation {
                    easing.type: Easing.OutCubic
                    duration: 6000
                }
            }
        }
    
        //自定义组件区域
        customizer: Column {
            readonly property var allStylePaths: {
                var paths = [];
                for (var i = 0; i < stylePicker.model.count; ++i) {
                    paths.push(stylePicker.model.get(i).path);
                }
                paths;
            }
            property alias currentStylePath: stylePicker.currentStylePath
            property alias currentStyleDark: stylePicker.currentStyleDark
            property alias minimumValue: minimumValueSlider.value
            property alias maximumValue: maximumValueSlider.value
            property alias minimumValueAngle: minimumAngleSlider.value
            property alias maximumValueAngle: maximumAngleSlider.value
            property alias labelStepSize: labelStepSizeSlider.value
    
            id: circularGaugeColumn
            spacing: customizerPropertySpacing
    
            readonly property bool isDefaultStyle: stylePicker.model.get(stylePicker.currentIndex).name === "Default"
    
            Item {
                id: stylePickerBottomSpacing
                width: parent.width
                height: stylePicker.height + textSingleton.implicitHeight
    
                StylePicker {
                    id: stylePicker
                    currentIndex: 1
    
                    model: ListModel {
                        ListElement {
                            name: "Default"
                            path: "../Style/CircularGaugeDefaultStyle.qml"
                            dark: true
                        }
                        ListElement {
                            name: "Dark"
                            path: "../Style/CircularGaugeDarkStyle.qml"
                            dark: true
                        }
                        ListElement {
                            name: "Light"
                            path: "../Style/CircularGaugeLightStyle.qml"
                            dark: false
                        }
                    }
                }
            }
    
            CustomizerLabel {
                text: "Minimum angle"
            }
    
            CustomizerSlider {
                id: minimumAngleSlider
                minimumValue: -180
                value: -145
                maximumValue: 180
                width: parent.width
            }
    
            CustomizerLabel {
                text: "Maximum angle"
            }
    
            CustomizerSlider {
                id: maximumAngleSlider
                minimumValue: -180
                value: 145
                maximumValue: 180
            }
    
            CustomizerLabel {
                text: "Minimum value"
            }
    
            CustomizerSlider {
                id: minimumValueSlider
                minimumValue: 0
                value: 0
                maximumValue: 360
                stepSize: 1
            }
    
            CustomizerLabel {
                text: "Maximum value"
            }
    
            CustomizerSlider {
                id: maximumValueSlider
                minimumValue: 0
                value: 240
                maximumValue: 300
                stepSize: 1
            }
    
            CustomizerLabel {
                text: "Label step size"
            }
    
            CustomizerSlider {
                id: labelStepSizeSlider
                minimumValue: 10
                value: 20
                maximumValue: 100
                stepSize: 20
            }
        }
    }
    • ControlLabel.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Extras 1.4
    
    Text {
        font.pixelSize: Math.max(textSingleton.font.pixelSize, Math.min(32, root.toPixels(0.045)))
        color: "#4e4e4e"
        styleColor: "#ffffff"
        style: Text.Raised
    }
    • ControlView.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls 1.4
    
    ///!所有控制区域组件的实体化
    Rectangle {
        id: view
        color: darkBackground ? "transparent" : lightBackgroundColor
    
        Keys.onReleased: {
            if (event.key === Qt.Key_Back) {
                stackView.pop();
                event.accepted = true;
            }
        }
    
        //是否开启暗色主题默认为true
        property bool darkBackground: true
    
        //控制区域的组件,提供给上层来定义
        property Component control
        //设置中自定义组件区域,提供给上层来定义
        property Component customizer
    
        property alias controlItem: controlLoader.item
        property alias customizerItem: customizerLoader.item
    
        property bool isCustomizerVisible: false
    
        property real margin: root.toPixels(0.05)
    
        property rect controlBounds: Qt.rect(largestControlItem.x + controlBoundsItem.x,
            largestControlItem.y + controlBoundsItem.y, controlBoundsItem.width, controlBoundsItem.height)
    
        Item {
            id: largestControlItem
            x: margin
            y: margin
            width: isCustomizerVisible ? widthWhenCustomizing : widthWhenNotCustomizing
            height: isCustomizerVisible ? heightWhenCustomizing : heightWhenNotCustomizing
    
            readonly property real widthWhenCustomizing: (!isScreenPortrait ? parent.width / 2 : parent.width) - margin * 2
            readonly property real heightWhenCustomizing: (isScreenPortrait ? parent.height / 2 : parent.height - toolbar.height) - margin * 2
            readonly property real widthWhenNotCustomizing: parent.width - margin * 2
            readonly property real heightWhenNotCustomizing: parent.height - toolbar.height - margin * 2
    
            Item {
                id: controlBoundsItem
                x: parent.width / 2 - controlBoundsItem.width / 2
                y: customizer && customizerItem.visible ? 0 : (isScreenPortrait ? (parent.height / 2) - (controlBoundsItem.height / 2) : 0)
                width: Math.min(parent.widthWhenCustomizing, parent.widthWhenNotCustomizing)
                height: Math.min(parent.heightWhenCustomizing, parent.heightWhenNotCustomizing)
    
                Behavior on x {
                    id: controlXBehavior
                    enabled: false
                    NumberAnimation {}
                }
    
                Behavior on y {
                    id: controlYBehavior
                    enabled: false
                    NumberAnimation {}
                }
    
                Loader {
                    id: controlLoader
                    sourceComponent: control
                    anchors.centerIn: parent
    
                    property alias view: view
                }
            }
        }
    
        Flickable {
            id: flickable
            // Hide the customizer on the right of the screen if it's not visible.
            x: (isScreenPortrait ? 0 : (isCustomizerVisible ? largestControlItem.x + largestControlItem.width + margin : view.width)) + margin
            y: (isScreenPortrait ? largestControlItem.y + largestControlItem.height : 0) + margin
            width: largestControlItem.width
            height: parent.height - y - toolbar.height - margin
            anchors.leftMargin: margin
            anchors.rightMargin: margin
            visible: customizerLoader.opacity > 0
    
            flickableDirection: Flickable.VerticalFlick
    
            clip: true
            contentWidth: width
            contentHeight: customizerLoader.height
    
            Behavior on x {
                id: flickableXBehavior
                enabled: false
                NumberAnimation {}
            }
    
            Behavior on y {
                id: flickableYBehavior
                enabled: false
                NumberAnimation {}
            }
    
            Loader {
                id: customizerLoader
                sourceComponent: customizer
                opacity: 0
                width: flickable.width
    
                property alias view: view
    
                Behavior on opacity {
                    NumberAnimation {
                        duration: 300
                    }
                }
            }
        }
    
        ControlViewToolbar {
            id: toolbar
    
            onCustomizeClicked: {
                controlXBehavior.enabled = !isScreenPortrait;
                controlYBehavior.enabled = isScreenPortrait;
    
                isCustomizerVisible = !isCustomizerVisible;
    
                if (isScreenPortrait) {
                    flickableXBehavior.enabled = false;
                    flickableYBehavior.enabled = true;
                } else {
                    flickableXBehavior.enabled = true;
                    flickableYBehavior.enabled = false;
                }
    
                customizerLoader.opacity = isCustomizerVisible ? 1 : 0;
            }
        }
    
        FlickableMoreIndicator {
            flickable: flickable
            atTop: true
            gradientColor: view.darkBackground ? darkBackgroundColor : lightBackgroundColor
        }
    
        FlickableMoreIndicator {
            flickable: flickable
            atTop: false
            gradientColor: view.darkBackground ? darkBackgroundColor : lightBackgroundColor
        }
    }
    • ControlViewToolbar.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls 1.4
    import "../Style/"
    
    BlackButtonBackground {
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        height: root.height * 0.125
    
        signal customizeClicked
    
        gradient: Gradient {
            GradientStop {
                color: "#333"
                position: 0
            }
            GradientStop {
                color: "#222"
                position: 1
            }
        }
    
        Button {
            id: back
            width: parent.height
            height: parent.height
            anchors.left: parent.left
            anchors.bottom: parent.bottom
            iconSource: "qrc:/Icons/Used_Images/Icons/07_11used_PieMenuControlView_back.png"
            onClicked: stackView.pop()
    
            style: BlackButtonStyle {
            }
    
        }
    
        Button {
            id: customize
            width: parent.height
            height: parent.height
            anchors.right: parent.right
            anchors.bottom: parent.bottom
            iconSource: "qrc:/Icons/Used_Images/Icons/08_11used_PieMenuControlView_settings.png"
            visible: customizer
    
            style: BlackButtonStyle {
            }
    
            onClicked: customizeClicked()
        }
    }
    • FlickableMoreIndicator.qml

    js 复制代码
    import QtQuick 2.15
    
    Rectangle {
        anchors.top: atTop ? flickable.top : undefined
        anchors.bottom: atTop ? undefined : flickable.bottom
        anchors.left: isScreenPortrait ? parent.left : parent.horizontalCenter
        anchors.right: parent.right
        height: 30
        visible: flickable.visible
        opacity: atTop
            ? (flickable.contentY > showDistance ? 1 : 0)
            : (flickable.contentY < flickable.contentHeight - showDistance ? 1 : 0)
        scale: atTop ? 1 : -1
    
        readonly property real showDistance: 0
        property Flickable flickable
        property color gradientColor
        /*! \c true if this indicator is at the top of the item */
        property bool atTop
    
        Behavior on opacity {
            NumberAnimation {
            }
        }
    
        gradient: Gradient {
            GradientStop {
                position: 0.0
                color: gradientColor
            }
            GradientStop {
                position: 1.0
                color: "transparent"
            }
        }
    }
    • PieMenuControlView.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls 1.4
    import QtQuick.Extras 1.4
    import "../Style/"
    
    Rectangle {
        id: view
        color: customizerItem.currentStyleDark ? "#111" : "#555"
    
        Behavior on color {
            ColorAnimation {}
        }
    
        Keys.onReleased: {
            if (event.key === Qt.Key_Back) {
                stackView.pop();
                event.accepted = true;
            }
        }
    
        property bool darkBackground: true
    
        property Component mouseArea
    
        property Component customizer: Item {
            property alias currentStylePath: stylePicker.currentStylePath
            property alias currentStyleDark: stylePicker.currentStyleDark
    
            StylePicker {
                id: stylePicker
                currentIndex: 0
                width: Math.round(Math.max(textSingleton.implicitHeight * 6 * 2, parent.width * 0.5))
                anchors.centerIn: parent
    
                model: ListModel {
                    ListElement {
                        name: "Default"
                        path: "../Style/PieMenuDefaultStyle.qml"
                        dark: false
                    }
                    ListElement {
                        name: "Dark"
                        path: "../Style/PieMenuDarkStyle.qml"
                        dark: true
                    }
                }
            }
        }
    
        property alias controlItem: pieMenu
        property alias customizerItem: customizerLoader.item
    
        Item {
            id: controlBoundsItem
            width: parent.width
            height: parent.height - toolbar.height
            visible: customizerLoader.opacity === 0
    
            Image {
                id: bgImage
                anchors.centerIn: parent
                height: 48
                Text {
                    id: bgLabel
                    anchors.top: parent.bottom
                    anchors.topMargin: 20
                    anchors.horizontalCenter: parent.horizontalCenter
                    text: "Tap to open"
                    color: "#999"
                    font.pointSize: 20
                }
            }
    
            MouseArea {
                id: touchArea
                anchors.fill: parent
                onClicked: pieMenu.popup(touchArea.mouseX, touchArea.mouseY)
            }
    
            PieMenu {
                id: pieMenu
                triggerMode: TriggerMode.TriggerOnClick
                width: Math.min(controlBoundsItem.width, controlBoundsItem.height) * 0.5
                height: width
    
                style: Qt.createComponent(customizerItem.currentStylePath)
    
                MenuItem {
                    text: "Zoom In"
                    onTriggered: {
                        bgImage.source = iconSource
                        bgLabel.text = text + " selected"
                    }
                    iconSource: "qrc:/Icons/Used_Images/Icons/04_11used_PieMenuControlView_zoomIn.png"
                }
                MenuItem {
                    text: "Zoom Out"
                    onTriggered: {
                        bgImage.source = iconSource
                        bgLabel.text = text + " selected"
                    }
                    iconSource: "qrc:/Icons/Used_Images/Icons/05_11used_PieMenuControlView_zoomOut.png"
                }
                MenuItem {
                    text: "Info"
                    onTriggered: {
                        bgImage.source = iconSource
                        bgLabel.text = text + " selected"
                    }
                    iconSource: "qrc:/Icons/Used_Images/Icons/06_11used_PieMenuControlView_info.png"
                }
            }
        }
        Loader {
            id: customizerLoader
            sourceComponent: customizer
            opacity: 0
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.leftMargin: 30
            anchors.rightMargin: 30
            y: parent.height / 2 - height / 2 - toolbar.height
            visible: customizerLoader.opacity > 0
    
            property alias view: view
    
            Behavior on y {
                NumberAnimation {
                    duration: 300
                }
            }
    
            Behavior on opacity {
                NumberAnimation {
                    duration: 300
                }
            }
        }
    
        ControlViewToolbar {
            id: toolbar
    
            onCustomizeClicked: {
                customizerLoader.opacity = customizerLoader.opacity == 0 ? 1 : 0;
            }
        }
    }
Style样式
  • Style

    • BlackButtonBackground.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls 1.4
    import QtQuick.Controls.Styles 1.4
    
    Rectangle {
        property bool pressed: false
    
        gradient: Gradient {
            GradientStop {
                color: pressed ? "#222" : "#333"
                position: 0
            }
            GradientStop {
                color: "#222"
                position: 1
            }
        }
        Rectangle {
            height: 1
            width: parent.width
            anchors.top: parent.top
            color: "#444"
            visible: !pressed
        }
        Rectangle {
            height: 1
            width: parent.width
            anchors.bottom: parent.bottom
            color: "#000"
        }
    }
    • BlackButtonStyle.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls 1.4
    import QtQuick.Controls.Styles 1.4
    
    ButtonStyle {
        property color fontColor
    
        property url rightAlignedIconSource
    
        background: BlackButtonBackground {
            pressed: control.pressed
        }
        label: Item {
            implicitWidth: row.implicitWidth
            implicitHeight: row.implicitHeight
            baselineOffset: row.y + text.y + text.baselineOffset
    
            Row {
                id: row
                anchors.left: control.text.length === 0 ? undefined : parent.left
                anchors.leftMargin: control.text.length === 0 ? 0 : textSingleton.implicitHeight
                anchors.verticalCenter: parent.verticalCenter
                anchors.horizontalCenter: control.text.length === 0 ? parent.horizontalCenter : undefined
    
                Image {
                    source: control.iconSource
                    width: Math.min(sourceSize.width, height)
                    height: text.implicitHeight
                    fillMode: Image.PreserveAspectFit
                }
                Text {
                    id: text
                    text: control.text
                    color: fontColor
                    font.pixelSize: control.height * 0.25
                    font.family: openSans.name
                    horizontalAlignment: Text.AlignLeft
                    verticalAlignment: Text.AlignVCenter
                    anchors.verticalCenter: parent.verticalCenter
                }
            }
    
            Loader {
                active: rightAlignedIconSource.toString().length !== 0
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.right
                anchors.rightMargin: textSingleton.implicitHeight
    
                sourceComponent: Image {
                    width: Math.min(sourceSize.width, height)
                    height: text.implicitHeight
                    fillMode: Image.PreserveAspectFit
                    source: rightAlignedIconSource
                }
            }
        }
    }
    • CircularGaugeDarkStyle.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls.Styles 1.4
    
    CircularGaugeStyle {
        id: root
        tickmarkStepSize: 10
        minorTickmarkCount: 1
        labelStepSize: 20
        tickmarkInset: outerRadius * 0.06
        minorTickmarkInset: tickmarkInset
        labelInset: outerRadius * 0.23
    
        background: Image {
            source: "qrc:/StylesPic/Used_Images/Pictures/02_11used_CircularGaugeDarkStyle_background.png"
        }
    
        needle: Image {
            id: needleImage
            transformOrigin: Item.Bottom
            source: "qrc:/StylesPic/Used_Images/Pictures/03_11used_CircularGaugeDarkStyle_needle.png"
            scale: {
                var distanceFromLabelToRadius = labelInset / 2;
                var idealHeight = outerRadius - distanceFromLabelToRadius;
                var originalImageHeight = needleImage.sourceSize.height;
                idealHeight / originalImageHeight;
            }
        }
    
        foreground: Item {
            Image {
                anchors.centerIn: parent
                source: "qrc:/StylesPic/Used_Images/Pictures/04_11used_CircularGaugeDarkStyle_center.png"
                scale: (outerRadius * 0.25) / sourceSize.height
            }
        }
    
        tickmark: Rectangle {
            implicitWidth: outerRadius * 0.02
            antialiasing: true
            implicitHeight: outerRadius * 0.05
            color: "#888"
        }
    
        minorTickmark: Rectangle {
            implicitWidth: outerRadius * 0.01
            antialiasing: true
            implicitHeight: outerRadius * 0.02
            color: "#444"
        }
    
        tickmarkLabel: Text {
            font.pixelSize: Math.max(6, outerRadius * 0.1)
            text: styleData.value
            color: "white"
        }
    }
    • CircularGaugeDefaultStyle.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls.Styles 1.4
    
    CircularGaugeStyle {
        labelStepSize: 20
    }
    • CircularGaugeLightStyle.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls.Styles 1.4
    
    CircularGaugeStyle {
        id: root
        tickmarkStepSize: 10
        minorTickmarkCount: 2
        labelStepSize: 40
        tickmarkInset: outerRadius * 0.06
        minorTickmarkInset: tickmarkInset
        labelInset: outerRadius * 0.23
    
        background: Image {
            source: "qrc:/StylesPic/Used_Images/Pictures/05_11used_CircularGaugeDarkStyle_background-light.png"
        }
    
        needle: Image {
            id: needleImage
            source: "qrc:/StylesPic/Used_Images/Pictures/06_11used_CircularGaugeDarkStyle_needle-light.png"
            transformOrigin: Item.Bottom
            scale: {
                var distanceFromLabelToRadius = labelInset / 2;
                var idealHeight = outerRadius - distanceFromLabelToRadius;
                var originalImageHeight = needleImage.sourceSize.height;
                idealHeight / originalImageHeight;
            }
        }
    
        foreground: Item {
            Image {
                anchors.centerIn: parent
                source: "qrc:/StylesPic/Used_Images/Pictures/07_11used_CircularGaugeDarkStyle_center-light.png"
                scale: (outerRadius * 0.25) / sourceSize.height
            }
        }
    
        tickmark: Rectangle {
            implicitWidth: outerRadius * 0.01
            antialiasing: true
            implicitHeight: outerRadius * 0.04
            color: "#999"
        }
    
        minorTickmark: Rectangle {
            implicitWidth: outerRadius * 0.01
            antialiasing: true
            implicitHeight: outerRadius * 0.02
            color: "#bbb"
        }
    
        tickmarkLabel: Text {
            font.family: "qrc:/Others/Used_Others/01_11used_OpenSans-Regular.ttf"
            font.pixelSize: Math.max(6, outerRadius * 0.1)
            text: styleData.value
            color: "#333"
        }
    }
    • PieMenuDarkStyle.qml

    js 复制代码
    import QtQuick.Controls.Styles 1.4
    
    PieMenuStyle {
        backgroundColor: "#222"
        shadowColor: Qt.rgba(1, 1, 1, 0.26)
    }
    • PieMenuDefaultStyle.qml

    js 复制代码
    import QtQuick.Controls.Styles 1.4
    
    PieMenuStyle {
    }
    • StylePicker.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls 1.4
    import QtQuick.Controls.Styles 1.4
    import QtQuick.Extras 1.4
    
    ListView {
        id: stylePicker
        width: parent.width
        height: root.height * 0.06
        interactive: false
        spacing: -1
    
        orientation: ListView.Horizontal
    
        readonly property string currentStylePath: stylePicker.model.get(stylePicker.currentIndex).path
        readonly property bool currentStyleDark: stylePicker.model.get(stylePicker.currentIndex).dark !== undefined
            ? stylePicker.model.get(stylePicker.currentIndex).dark
            : true
    
        ExclusiveGroup {
            id: styleExclusiveGroup
        }
    
        delegate: Button {
            width: Math.round(stylePicker.width / stylePicker.model.count)
            height: stylePicker.height
            checkable: true
            checked: index === ListView.view.currentIndex
            exclusiveGroup: styleExclusiveGroup
    
            onCheckedChanged: {
                if (checked) {
                    ListView.view.currentIndex = index;
                }
            }
    
            style: ButtonStyle {
                background: Rectangle {
                    readonly property color checkedColor: currentStyleDark ? "#444" : "#777"
                    readonly property color uncheckedColor: currentStyleDark ? "#222" : "#bbb"
                    color: checked ? checkedColor : uncheckedColor
                    border.color: checkedColor
                    border.width: 1
                    radius: 1
                }
    
                label: Text {
                    text: name
                    color: currentStyleDark ? "white" : (checked ? "white" : "black")
                    font.pixelSize: root.toPixels(0.04)
                    font.family: openSans.name
                    anchors.centerIn: parent
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                }
            }
        }
    }
Customizer自定义组件
  • Customizer

    • CustomizerLabel.qml

    js 复制代码
    import QtQuick 2.15
    
    Text {
        color: darkBackground ? root.darkFontColor : root.lightFontColor
        font.pixelSize: root.toPixels(0.04)
        font.family: openSans.name
        anchors.horizontalCenter: parent.horizontalCenter
    }
    • CustomizerSlider.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls 1.4
    import QtQuick.Controls.Styles 1.4
    
    Slider {
        id: slider
        width: parent.width
        height: root.toPixels(0.1)
    
        style: SliderStyle {
            handle: Rectangle {
                height: root.toPixels(0.06)
                width: height
                radius: width/2
                color: "#fff"
            }
    
            groove: Rectangle {
                implicitHeight: root.toPixels(0.015)
                implicitWidth: 100
                radius: height/2
                border.color: "#333"
                color: "#222"
                Rectangle {
                    height: parent.height
                    width: styleData.handlePosition
                    implicitHeight: 6
                    implicitWidth: 100
                    radius: height/2
                    color: "#555"
                }
            }
        }
    }
    • CustomizerSwitch.qml

    js 复制代码
    import QtQuick 2.15
    import QtQuick.Controls 1.4
    
    Switch {
        anchors.horizontalCenter: parent.horizontalCenter
    }

以上就是所有关于Extras的官方例子;


相关推荐
Felix_One5 天前
Qt 串口通信避坑指南:QSerialPort 的 5 个常见问题
qt
blasit8 天前
笔记:Qt C++建立子线程做一个socket TCP常连接通信
c++·qt·tcp/ip
西岸行者13 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意13 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码13 天前
嵌入式学习路线
学习
毛小茛13 天前
计算机系统概论——校验码
学习
babe小鑫13 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
范特西.i13 天前
QT聊天项目(8)
开发语言·qt
winfreedoms13 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下13 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs