QML —— Canvas重绘钟表组件(附完整源码)

示例效果]

在这里插入图片描述

源码

     main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}


     main.qml

import QtQuick 2.12
import QtQuick.Window 2.12

import QtQuick.Layouts 1.12

Window
{
    id: root
    visible: true
    width: 800
    height: 600
    title: qsTr("Hello World")

    ColumnLayout
    {
        anchors.top: parent.top
        Layout.topMargin: 10
        spacing: 5

        RowLayout
        {
            Layout.alignment:Qt.AlignHCenter
            Text
            {
                text: qsTr("当前时间:")
                font.family: "微软雅黑"
                font.pixelSize: 20
            }
            Text
            {
                id: currentTimeID
                text: qsTr("--")
                color: "pink"
                font.family: "Courier New"
                font.pixelSize: 40
                font.bold: true
            }
        }

        MyClock
        {
            Layout.alignment:Qt.AlignHCenter
            id:rootClock
            onCurrentPaintTime:{currentTimeID.text = timeString}
        }
    }
}


     MyClock.qml

import QtQuick 2.0
import QtQuick.Window 2.12

Item
{
    id: rootClock
    visible: true
//    width: 740
//    height: 680

    signal currentPaintTime(var timeString)

    Timer
    {
        interval: 980
        running: true
        repeat: true
        onTriggered:{canvasClock.requestPaint()}
    }

    Canvas
    {
        id: canvasClock
        anchors.fill: parent

        onPaint:
        {
            // 获取绘制2d画笔对象
            let painter2D = getContext("2d")

            painter2D.clearRect(0,0,width,height)

            // 绘制背景相关
            drawBackground(painter2D)

            // 绘制中心点
            drawCenterPoint(painter2D)

            // 绘制时分秒
            var date = new Date();
            var hours =  date.getHours()
            var minutes =  date.getMinutes()
            var seconds = date.getSeconds();
            drawTime_hour(painter2D,hours%12)
            drawTime_minute(painter2D,minutes)
            drawTime_second(painter2D,seconds)

            // 信号传递出绘制的时间
            currentPaintTime(hours+":"+minutes+":"+seconds);
        }
    }

    // 背景圆半径取窗口宽高最小值的某个系数
    property int windowMinValue : Math.min(root.width,root.height)
    property int backgroundRadius : windowMinValue*0.4

    function drawBackground(painter2D)
    {
        // 保存当前绘制环境
        painter2D.save()

        // 将当前路径重置为新路径
        painter2D.beginPath()

        // 变换坐标起始点
        painter2D.translate(root.width/2,root.height/2)

        // 像素清除为透明黑色
        painter2D.clearRect(-backgroundRadius,-backgroundRadius,backgroundRadius*2,backgroundRadius*2)

        /******************** 绘制背景圆 ********************/
        // 绘制外侧大圆
        painter2D.arc(0,0,backgroundRadius,0,360)
        painter2D.fillStyle = "#edeef7"
        painter2D.fill()

        // 使用当前笔划样式笔划子路径
        painter2D.stroke()
        /**************************************************/

        /******************** 写背景文字 ********************/
        let backgroundText = [12,1,2,3,4,5,6,7,8,9,10,11]
        painter2D.font = "bold " + (windowMinValue/20)+"px \"Courier New\""
        painter2D.textAlign = "center"
        painter2D.textBaseline = "middle"
        painter2D.fillStyle = "black";
        for(var indexText=0;indexText<12;++indexText)
        {
            let rotatoAngle = (indexText * 30)      // 30°
            let realAngle = 90- rotatoAngle;
            let x = Math.floor(backgroundRadius*0.85 * (Math.cos(realAngle*Math.PI/180)))
            let y = Math.floor(backgroundRadius*0.85 * (Math.sin(realAngle*Math.PI/180)))
            painter2D.fillText(backgroundText[indexText],x,-y)   // 文字
        }
        /**************************************************/

        /******************** 画背景刻度 ********************/
        for(var indexMark=0;indexMark<60;++indexMark)
        {
            let rotatoMarkAngle = (indexMark * 6)*Math.PI/180   // 6°
            painter2D.save()
            painter2D.beginPath()
            painter2D.rotate(rotatoMarkAngle)                   // 旋转
            painter2D.moveTo(0,backgroundRadius)
            if(indexMark%5==0){painter2D.lineWidth=backgroundRadius*0.03;painter2D.lineTo(0,backgroundRadius*0.95)}
            else{painter2D.lineTo(0,backgroundRadius*0.95)}
            painter2D.stroke();
            painter2D.restore()
        }
        /**************************************************/

        // 释放当前绘制环境
        painter2D.restore()
    }

    function drawCenterPoint(painter2D)
    {
        painter2D.save()
        painter2D.beginPath()           // 必须加入
        painter2D.translate(root.width/2,root.height/2)
        painter2D.arc(0,0,backgroundRadius*0.02,0,360);
        painter2D.fillStyle="#000000";  // 填充色
        painter2D.fill();               // 进行填充
        painter2D.stroke()
        painter2D.restore()
    }

    function drawTime_second(painter2D,seconds)
    {
        let rotatoSecondAngle = (seconds * 6)*Math.PI/180   // 360°/60s=6°/s

        painter2D.save()
        painter2D.beginPath()                               // 必须加入
        painter2D.translate(root.width/2,root.height/2)     // 变换坐标起始点
        painter2D.rotate(rotatoSecondAngle)                 // 旋转
        painter2D.lineWidth=backgroundRadius*0.01;
        painter2D.moveTo(0,backgroundRadius*0.15)
        painter2D.lineTo(0,-backgroundRadius*0.8)
        painter2D.stroke()
        painter2D.restore()
    }

    function drawTime_minute(painter2D,minute)
    {
        let rotatoMinuteAngle = (minute * 6)*Math.PI/180    // 360°/60m=6°/m

        painter2D.save()
        painter2D.beginPath()                               // 必须加入
        painter2D.translate(root.width/2,root.height/2)     // 变换坐标起始点
        painter2D.rotate(rotatoMinuteAngle)                 // 旋转
        painter2D.lineWidth=backgroundRadius*0.02;
        painter2D.moveTo(0,backgroundRadius*0.05)
        painter2D.lineTo(0,-backgroundRadius*0.5)
        painter2D.stroke()
        painter2D.restore()
    }

    function drawTime_hour(painter2D,hour)
    {
        let rotatoHourAngle = (hour * 30)*Math.PI/180       // 360°/12h=30°/h

        painter2D.save()
        painter2D.beginPath()                               // 必须加入
        painter2D.translate(root.width/2,root.height/2)     // 变换坐标起始点
        painter2D.rotate(rotatoHourAngle)                   // 旋转
        painter2D.lineWidth=backgroundRadius*0.03;
        painter2D.moveTo(0,backgroundRadius*0.05)
        painter2D.lineTo(0,-backgroundRadius*0.3)
        painter2D.stroke()
        painter2D.restore()
    }
}

关注

笔者 - jxd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

信必诺

嗨,支持下哥们呗。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值