QML Clock Widget Demo Implementation Analysis

Demo Overview

The clock demo displays a horizontal list of world clocks. Users can drag the list left or right using the mouse. Clocks shown with a dark background indicate nighttime hours (7 PM to 7 AM), while clocks without the dark overlay represent daytime hours.

Source Code Aanlysis

1. Application Entry Point

The main.cpp file contains a single macro for initializing the QML engine. For detailed explanations of this macro, refer to the dedicated article on QML key macros.

2. Clock Component Implementation

The Clock.qml component handles individual clock display and time calculation:

Item {
    id: clockItem
    
    // Dynamic width calculation based on parent ListView dimensions
    width: {
        if (ListView.view && ListView.view.width >= 200)
            return ListView.view.width / Math.floor(ListView.view.width / 200.0);
        else
            return 200;
    }

    // Dynamic height calculation
    height: {
        if (ListView.view && ListView.view.height >= 240)
            return ListView.view.height;
        else
            return 240;
    }

    // Exposed properties for external assignment
    property alias cityName: cityText.text
    property int hourValue
    property int minuteValue
    property int secondValue
    property real timezoneOffset
    property bool isNightTime: false
    property bool useUTCTime: true

    // Time calculation function
    function updateTime() {
        var currentDate = new Date;
        hourValue = useUTCTime ? currentDate.getUTCHours() + Math.floor(clockItem.timezoneOffset) : currentDate.getHours()
        isNightTime = (hourValue < 7 || hourValue > 19)
        minuteValue = useUTCTime ? currentDate.getUTCMinutes() + ((clockItem.timezoneOffset % 1) * 60) : currentDate.getMinutes()
        secondValue = currentDate.getUTCSeconds();
    }

    // Periodic timer for real-time updates
    Timer {
        interval: 100
        running: true
        repeat: true
        onTriggered: clockItem.updateTime()
    }

    // Clock face container centered in parent
    Item {
        anchors.centerIn: parent
        width: 200
        height: 240
        
        // Background images - day vs night variants
        Image {
            id: dayBackground
            source: "clock.png"
            visible: clockItem.isNightTime == false
        }
        
        Image {
            source: "clock-night.png"
            visible: clockItem.isNightTime == true
        }

        // Hour hand
        Image {
            x: 92.5
            y: 27
            source: "hour.png"
            transform: Rotation {
                id: hourHandRotation
                origin.x: 7.5
                origin.y: 73
                angle: (clockItem.hourValue * 30) + (clockItem.minuteValue * 0.5)
                Behavior on angle {
                    SpringAnimation {
                        spring: 2
                        damping: 0.2
                        modulus: 360
                    }
                }
            }
        }

        // Minute hand
        Image {
            x: 93.5
            y: 17
            source: "minute.png"
            transform: Rotation {
                id: minuteHandRotation
                origin.x: 6.5
                origin.y: 83
                angle: clockItem.minuteValue * 6
                Behavior on angle {
                    SpringAnimation {
                        spring: 2
                        damping: 0.2
                        modulus: 360
                    }
                }
            }
        }

        // Second hand
        Image {
            x: 97.5
            y: 20
            source: "second.png"
            transform: Rotation {
                id: secondHandRotation
                origin.x: 2.5
                origin.y: 80
                angle: clockItem.secondValue * 6
                Behavior on angle {
                    SpringAnimation {
                        spring: 2
                        damping: 0.2
                        modulus: 360
                    }
                }
            }
        }
        
        // Center cap
        Image {
            anchors.centerIn: dayBackground
            source: "center.png"
        }

        // City name label
        Text {
            id: cityText
            y: 210
            anchors.horizontalCenter: parent.horizontalCenter
            color: "white"
            font.family: "Helvetica"
            font.bold: true
            font.pixelSize: 16
            style: Text.Raised
            styleColor: "black"
        }
    }
}

3. Clock List View

The Clocks.qml file implements a horizontal ListView that displays multiple clock widgets:

import "content" as Widgets

Rectangle {
    id: container
    width: 640
    height: 320
    color: "#646464"

    // Horizontal scrolling clock list
    ListView {
        id: clockList
        anchors.fill: parent
        orientation: ListView.Horizontal
        cacheBuffer: 2000
        snapMode: ListView.SnapOneItem
        highlightRangeMode: ListView.ApplyRange

        delegate: Widgets.Clock {
            cityName: cityLabel
            timezoneOffset: offset
        }

        // Data model with city names and UTC offsets
        model: ListModel {
            ListElement { cityLabel: "New York"; offset: -4 }
            ListElement { cityLabel: "London"; offset: 0 }
            ListElement { cityLabel: "Oslo"; offset: 1 }
            ListElement { cityLabel: "Mumbai"; offset: 5.5 }
            ListElement { cityLabel: "Tokyo"; offset: 9 }
            ListElement { cityLabel: "Brisbane"; offset: 10 }
            ListElement { cityLabel: "Los Angeles"; offset: -8 }
        }
    }

    // Left navigation arrow
    Image {
        anchors.left: parent.left
        anchors.bottom: parent.bottom
        anchors.margins: 10
        source: "content/arrow.png"
        rotation: -90
        opacity: clockList.atXBeginning ? 0 : 0.5
        Behavior on opacity {
            NumberAnimation { duration: 500 }
        }
    }

    // Right navigation arrow
    Image {
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        anchors.margins: 10
        source: "content/arrow.png"
        rotation: 90
        opacity: clockList.atXEnd ? 0 : 0.5
        Behavior on opacity {
            NumberAnimation { duration: 500 }
        }
    }
}

Key Implementation Details

  • Dynamic Sizing: The clock compoennt calculates its dimensions based on the parent ListView's available space, ensuring proper scaling across different screen sizes.
  • Time Zone Support: Each clock accepts a timezone offset property to display time for different global locations using UTC-based calculations.
  • Day/Night Detection: The isNightTime property automatically determines whether to display the dark background based on whether the hour value falls outside the 7 AM to 7 PM range.
  • Smooth Animations: Spring animasions on the Rotation transforms provide realistic hand movement with natural easing.
  • List Navigation: The snapMode and highlightRangeMode properties create a polished user experience when scrolling through the clock list.
  • Visual Feedback: Navigation arrows fade in/out based on scroll position using Behavior animations.

Tags: Qt QML gui clock-widget ListView

Posted on Thu, 29 Oct 2026 16:31:59 +0000 by aromakat