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

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

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

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

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

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

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

#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

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
                        }
                    }
                }
            }
        }
    }
}
03 Gallery
Point
  • 当需要有连续的model时:

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

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

    • root中最外层的字体:

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

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

    • 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
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

    • 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

    • 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

    • 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

    • 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

    • 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

    • 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

    • 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

    • 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

    • 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

    • import QtQuick 2.15
      import QtQuick.Controls.Styles 1.4
      
      CircularGaugeStyle {
          labelStepSize: 20
      }
      
    • CircularGaugeLightStyle.qml

    • 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

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

    • import QtQuick.Controls.Styles 1.4
      
      PieMenuStyle {
      }
      
    • StylePicker.qml

    • 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

    • 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

    • 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

    • import QtQuick 2.15
      import QtQuick.Controls 1.4
      
      Switch {
          anchors.horizontalCenter: parent.horizontalCenter
      }
      

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/763451.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

excel修改批量一列单价的金额并保留1位小数

1.打开表格&#xff0c;要把单价金额变成现在的两倍&#xff0c;数据如下&#xff1a; 2.把单价这一列粘贴到一个新的sheet页面&#xff0c;在B2单元格输入公式&#xff1a;A2*2 然后按enter回车键,这时候吧鼠标放到B2单元格右下角&#xff0c;会出现一个黑色的小加号&#xf…

SQL 注入联合查询之为什么要 and 1=2

在 SQL 注入联合查询中&#xff0c;将 id 先置为假&#xff08;如 id-1 或其他使查询结果为空的条件&#xff09;&#xff0c;通常是为了让前面的查询语句查询不到结果&#xff0c;从而使联合查询中后面的语句结果能够显示在回显位上

【深度学习】pytorch训练中的一个大坑

使用的命令&#xff1a;iostat -x 5 可以看到 ssd的利用率已经满了。 之前在的数据集放在了 hdd上&#xff0c;训练结果特别慢。 所以我把它移动到了ssd上&#xff0c;然后训练参数用的 resume&#xff0c; 但是&#xff01;&#xff01;&#xff01;&#xff01;它把历史记住…

虚拟环境管理

虚拟环境 在使用 Python 时我们一般使用“pip install 第三方包名”来安装第三方包&#xff0c;但是由于pip的特性&#xff0c;系统只能安装每个包的一个版本。而在实际开发中&#xff0c;可能同时开发多个项目&#xff0c;如&#xff1a;上图有三个项目&#xff1b;每个项目需…

摄影后期色彩管理流程(Lightroom篇)

在摄影后期处理中&#xff0c;色彩管理是确保图像从捕捉到输出的一致性和准确性的关键。Lightroom 和 Photoshop 其实已经将这套色彩管理流程作为默认选项&#xff0c;如果实质操作时仍存在色彩偏差的问题&#xff0c;可参考以下内容。 ProPhoto RGB > Adobe RGB > sRGB …

幻兽帕鲁服务器如何安装模组安装

由于模组多数为Window版本的&#xff0c;所以本教程以服务端为Window的作为演示&#xff08;Linux服务端的也是一样的操作&#xff09;百度莱卡云开服 如果你你是Linux版本的&#xff0c;请点击跳转切换服务端教程 接下来是本地安装模组包的方法&#xff08;服务器自带&#xf…

Web3 游戏周报(6.23 - 6.29)

区块链游戏热度不减&#xff0c;你是否掌握了上周的重要动态&#xff1f; 回顾上周区块链游戏动态&#xff0c;查看 Footprint Analytics 与 ABGA 的最新数据报告。 【6.23 - 6.29】Web3 游戏行业动态&#xff1a; 继 Notcoin 之后&#xff0c;另一款 Telegram 游戏 Hamster …

React实战学习(一)_棋盘设计

需求&#xff1a; 左上侧&#xff1a;状态左下侧&#xff1a;棋盘&#xff0c;保证胜利就结束 和 下过来的不能在下右侧&#xff1a;“时光机”,保证可以回顾&#xff0c;索引 语法&#xff1a; 父子之间属性传递&#xff08;props&#xff09;子父组件传递&#xff08;写法上&…

【MySQL篇】Percona XtraBackup物理备份工具的基础理论概述(第一篇,总共五篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

​产品经理-困惑4:产品面对开发是否低人一等(4)

在互联网当中,做产品的,在面对开发是否觉得低人一等&#xff1f; 完全不会 从团队层面来看&#xff0c;任何互联网团队都是由开发、产品、视觉、运营、市场等专业人才所组成的专业团队 每人各有专攻&#xff0c;为同一个目标&#xff08;即项目成功&#xff09;而不懈努力。各工…

带安全启动—Ubuntu系统—手动安装Nvidia驱动

教程1&#xff1a;在启用安全启动的 Fedora 中安装英伟达驱动 教程2&#xff1a;UEFI安全启动模式下安装Ubuntu的NVIDIA显卡驱动 1. 搜索合适的驱动 Nvidia驱动官网 选择这个 驱动(.run)链接 2. 安装必要的软件依赖 CUDA底层用C写的&#xff0c;因此导入编译器 sudo apt i…

1-4.时间序列数据建模流程范例

文章最前&#xff1a; 我是Octopus&#xff0c;这个名字来源于我的中文名–章鱼&#xff1b;我热爱编程、热爱算法、热爱开源。所有源码在我的个人github &#xff1b;这博客是记录我学习的点点滴滴&#xff0c;如果您对 Python、Java、AI、算法有兴趣&#xff0c;可以关注我的…

已解决java.io.NotSerializableException:对象不支持序列化的正确解决方法,亲测有效!!!

已解决java.io.NotSerializableException&#xff1a;对象不支持序列化的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 出现问题的场景 示例代码 报错原因 解决思路 解决方法 1. 实现Serializable接口 修改后的Employee类 2…

递归----计算P函数

注意运算中的符号不能少&#xff01;&#xff01;&#xff01;&#xff01; * 必须体现出&#xff01;&#xff01;&#xff01;&#xff01; #include <stdio.h>double P( int n, double x );int main() {int n;double x;scanf("%d %lf", &n, &x);pri…

计算机毕业设计Python+Spark股票基金推荐与预测系统 股票基金可视化 股票基金推荐系统 股票基金可视化系统 股票基金数据分析 股票基金爬虫大数据

目 录 摘 要 Abstract 第1章 前 言 1.1 项目的背景和意义 1.2 研究现状 1.3 项目的目标和范围 1.4 论文结构简介 第2章 技术与原理 2.1 开发原理 2.2 开发工具 2.3 关键技术 第3章 需求建模 3.1 系统可行性分析 3.2 功能需求分析 3.3 非功能性…

opengl箱子的显示

VS环境配置&#xff1a; /JMC /ifcOutput "Debug\" /GS /analyze- /W3 /Zc:wchar_t /I"D:\Template\glfwtemplate\glfwtemplate\assimp" /I"D:\Template\glfwtemplate\glfwtemplate\glm" /I"D:\Template\glfwtemplate\glfwtemplate\LearnOp…

Wireshark - tshark支持iptables提供数据包

tshark现在的数据包获取方式有两种&#xff0c;分别是读文件、网口监听&#xff08;af-packet原始套接字&#xff09;。两种方式在包获取上&#xff0c;都是通过读文件的形式&#xff1b;存在文件io操作&#xff0c;在专门处理大流量的情境下&#xff0c; 我们复用wireshark去做…

小阿轩yx-案例:MySQL主从复制与读写分离

小阿轩yx-案例&#xff1a;MySQL主从复制与读写分离 案例分析 概述 实际生产环境中 如果对数据库读和写都在同一个数据库服务器中操作&#xff0c;无论在安全性、高可用性还是高并发等各个方面都完全不能满足实际需求一般都是通过主从复制&#xff08;Master-Slave&#xf…

Python tkinter: 开发一个目标检测GUI小程序

程序提供了一个用户友好的界面&#xff0c;允许用户选择图片或文件夹&#xff0c;使用行人检测模型进行处理&#xff0c;并在GUI中显示检测结果。用户可以通过点击画布上的检测结果来获取更多信息&#xff0c;并使用键盘快捷键来浏览不同的图片。 一. 基本功能介绍 界面布局&am…

C++封装

1. 封装 1.1. struct 当单一变量无法完成描述需求的时候&#xff0c;结构体类型解决了这一问题。可以将多个类型打包成一体&#xff0c;形成新的类型&#xff0c;这是c语言中的封装 但是&#xff0c;新类型并不包含&#xff0c;对数据类的操作。所有操作都是通过函数的方式进…