반응형

QT qml로 현재 시간 정보를 가져와 아날로그 시계로 보여주기 

이번에는 qml로 현재시간 정보를 실시간으로 가져와 아날로그 시계로 표시하도록 구현해보겠습니다. 

 

아날로그 시계를 만들기 위한 구성요소는 아래와 같은데, 하나 하나씩 만들어 보겠습니다. 

1. 큰 원

2. 시간을 표시하는 12개의 작은 원

3. 시간을 표시하는 1~12까지의 숫자

4. 시계의 중앙에 위치한 원

5. 시침, 분침, 초침

6. 타이머를 이용한 현재 시간 정보를 가져오기

 

1. 큰 원(Rectangle)

원을 그리기 위해서는 Rectangle 을 사용합니다. 

radius 속성에 원의 반지름 값을 넣어주면 원이 만들어 집니다. 

    Rectangle{
        id: circle
        anchors.centerIn: parent
        height: Math.min(parent.width, parent.height)
        width: height
        radius: width/2
        border.color: "black"
    }

 

2. 시간을 표시하는 12개의 작은 원, 3. 시간을 표시하는 1~12까지의 숫자

12개를 반복적으로 수행하기 위해 Repeater를 사용합니다. 

x: 정중앙, y:0으로 부터 시작하는데, height는 원의 지름으로 하여 30도씩 회전하면서 작은 원과 숫자를 표시합니다. 

숫자는 0부터 시작하는데, 0이면 12를 표시하도록 하였습니다. 

그런데, Item이 30도씩 회전하면서 숫자도 그에 맞게 회전하기 때문에, 기울이지 않고 똑바로 보이게 하려면 Text에서 rotation을 이용하여, 똑바로 보이게 합니다. Item의 rotation과 Text의 rotation의 합이 360이 되면 똑바로 보이겠죠?

Repeater{
        model: 12
        Item{
            id: hourContainer

            property int hour: index
            height:circle.height
            rotation: index*30
            x: circle.width/2
            y:0

            Rectangle{
                height: circle.height*0.05
                width: height
                radius: width/2
                color: "black"
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.top: parent.top
            }

            Text{
                x: -15
                y: circle.height*0.05
                rotation: 360 - index*30
                text: hourContainer.hour == 0 ?12 : hourContainer.hour
                font.pixelSize: circle.height*0.05
            }
        }
    }

 

4. 시계의 중앙에 위치한 원

이건 간단합니다. 

height와 width는 최대한 작게 표시하도록 0.05를 곱하였고, radius에 반지름을 넣습니다. 

그리고 anchors.centerIn에 parent를 넣어, 화면의 중앙에 표시하도록 하였습니다. 

Rectangle{
        id: center_rect
        anchors.centerIn: parent
        height: circle.height*0.05
        width: height
        radius: width/2
        color: "black"
    }

 

5. 시침, 분침, 초침

시침, 분침, 초침 모두 Rectangle로 긴 막대기처럼 표시하도록 만들었습니다.

여기서 중요한건 현재 시간에 따라 시침, 분침, 초침의 rotation을 설정하여, 회전하여 보여지도록 하는 것입니다.

transform을 이용하여, 큰 원의 중앙점을 기준으로 회전하도록 하였습니다.

초침과 분침은 은 분,초마다 360/60=6도씩 회전하도록 하였고,

시침은 360/12=30도씩 회전하도록 하고,  현재 분의 상태에 조금 움직여야 하기 때문에,

30*(minutes/60)로 움직이도록 하였습니다.

 

시침은 아래 식대로 현재 시에 30도를 곱한 값에 현재 분에 따른 추가 각도를 더하였습니다.

hourItem.value * hourItem.angle_hour + 30*(minutes/60)  

Item{
        id:hourItem
        property int value: hours
        property int angle_hour : 360/12
        Rectangle{
            width:6
            x: window.height/2
            y: circle.height * 0.21
            height: window.height*0.35
            color: "blue"
            antialiasing: true
        }
        transform: Rotation
        {
          origin.x: window.height/2
          origin.y: window.height/2
          angle: hourItem.value * hourItem.angle_hour + 30*(minutes/60)
        }
        antialiasing: true
    }

    Item{
        id:minuteItem
        property int value: minutes
        property int angle_minute : 360/60
        Rectangle{
            width:4
            x: window.height/2
            y:circle.height *0.12
            height: window.height*0.45
            color: "black"
            antialiasing: true
        }
        transform: Rotation
        {
          origin.x: window.height/2
          origin.y: window.height/2
          angle: minuteItem.value * minuteItem.angle_minute
        }
        antialiasing: true
    }

    Item{
        id:secondItem
        property int value: seconds
        property int angle_second: 360/60
        Rectangle{
            width:2
            x: window.height/2
            y:circle.height *0.08
            height: window.height*0.5
            color: "red"
            antialiasing: true
        }
        transform: Rotation
        {
          origin.x: window.height/2
          origin.y: window.height/2
          angle: secondItem.value * secondItem.angle_second
        }
        antialiasing: true
    }

6. 타이머를 이용한 현재 시간 정보를 가져오기

현재 시간을 가져오기 위해 Date() 객체를 이용하였습니다.

Timer로 1초가 지날때마다 현재 시간을 가져와 갱신하도록 하고, 로그로 남기도록 하였습니다. 

이때, 시간, 분, 초에 대한 변수의 값은 시침, 분침, 초침이 보여질 부분에 대해 영향을 주게 됩니다 

property date curentTime: new Date()
property int hour: curentTime.getHours()
property int minutes: curentTime.getMinutes()
property int seconds: curentTime.getSeconds()

Timer{
        id: timer
        repeat: true
        interval: 1000
        running: true
        onTriggered: {
            curentTime = new Date()
            print(curentTime.getHours(), ":", curentTime.getMinutes(), ":", curentTime.getSeconds());
        }

    }

 

전체 코드

각각의 구성 요소들의 위치가 약간 안맞는 세세한 부분은 신경을 쓰지 않았습니다. 

그점 양해부탁 드리고 봐주시면 될 듯 합니다. 

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12

Window {
    id: window
    width: 500
    height: 500
    visible: true

    property date curentTime: new Date()
    property int hour: curentTime.getHours()
    property int minutes: curentTime.getMinutes()
    property int seconds: curentTime.getSeconds()


    Rectangle{
        id: circle
        anchors.centerIn: parent
        height: Math.min(parent.width, parent.height)
        width: height
        radius: width/2
        border.color: "black"
    }

    Repeater{
        model: 12
        Item{
            id: hourContainer

            property int hour: index
            height:circle.height
            rotation: index*30
            x: circle.width/2
            y:0

            Rectangle{
                height: circle.height*0.05
                width: height
                radius: width/2
                color: "black"
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.top: parent.top
            }

            Text{
                x: -15
                y: circle.height*0.05
                rotation: 360 - index*30
                text: hourContainer.hour == 0 ?12 : hourContainer.hour
                font.pixelSize: circle.height*0.05
            }
        }
    }

    Rectangle{
        id: center_rect
        anchors.centerIn: parent
        height: circle.height*0.05
        width: height
        radius: width/2
        color: "black"
    }

    Item{
        id:hourItem
        property int value: hours
        property int angle_hour : 360/12
        Rectangle{
            width:6
            x: window.height/2
            y: circle.height * 0.21
            height: window.height*0.35
            color: "blue"
            antialiasing: true
        }
        transform: Rotation
        {
          origin.x: window.height/2
          origin.y: window.height/2
          angle: hourItem.value * hourItem.angle_hour + 30*(minutes/60)
        }
        antialiasing: true
    }

    Item{
        id:minuteItem
        property int value: minutes
        property int angle_minute : 360/60
        Rectangle{
            width:4
            x: window.height/2
            y:circle.height *0.12
            height: window.height*0.45
            color: "black"
            antialiasing: true
        }
        transform: Rotation
        {
          origin.x: window.height/2
          origin.y: window.height/2
          angle: minuteItem.value * minuteItem.angle_minute
        }
        antialiasing: true
    }

    Item{
        id:secondItem
        property int value: seconds
        property int angle_second: 360/60
        Rectangle{
            width:2
            x: window.height/2
            y:circle.height *0.08
            height: window.height*0.5
            color: "red"
            antialiasing: true
        }
        transform: Rotation
        {
          origin.x: window.height/2
          origin.y: window.height/2
          angle: secondItem.value * secondItem.angle_second
        }
        antialiasing: true
    }



    Timer{
        id: timer
        repeat: true
        interval: 1000
        running: true
        onTriggered: {
            curentTime = new Date()
            print(curentTime.getHours(), ":", curentTime.getMinutes(), ":", curentTime.getSeconds());
        }

    }

}
반응형

+ Recent posts