Time Picker in QML

This is the iphone style for time picking

   Rectangle {
    id:clockid
    width: frame.implicitWidth + 10
    height: frame.implicitHeight + 10
    anchors.centerIn: parent
    color: "cornsilk"
    function formatText(count, modelData) {
        var data = count === 12 ? modelData + 1 : modelData;
        return data.toString().length < 2 ? "0" + data : data;
    }
    FontMetrics {
        id: fontMetrics
        font.pixelSize: 10
    }

    Component {
        id: delegateComponent

        Label {
            text: clockid.formatText(Tumbler.tumbler.count, modelData)
            opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
            font.pixelSize: fontMetrics.font.pixelSize * 1.25
        }

    }

    Frame {
        id: frame
        padding: 0
        anchors.centerIn: parent



        Row {
            id: row_clock

            Tumbler {
                id: hoursTumbler
                model: 12
                delegate: delegateComponent
                visibleItemCount: 5
            }

            Tumbler {
                id: minutesTumbler
                model: 60
                delegate: delegateComponent
                visibleItemCount: 5
            }

            Tumbler {
                id: amPmTumbler
                model: ["AM", "PM"]
                delegate: delegateComponent
            }
        }
    }
}

i think my own time picker is good you can extend it as you like its in persian orientaion you need to swap around things a little bit or use some layout mirroring :
UButton.qml

import QtQuick 2.4
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
Button {
    id:root
    Universal.accent: Universal.Cobalt
    Universal.foreground: "white"
    highlighted: true
    font.family: "B Nazanin"
    font.pointSize: 12
}

UCard.qml

import QtQuick 2.4
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
import QtGraphicalEffects 1.0
Item{
    property alias radius : morakhasiRect.radius
    property alias color : morakhasiRect.color
    implicitWidth: 150
    implicitHeight: 150

    Rectangle{
        anchors.rightMargin: 1
        anchors.leftMargin: 1
        anchors.bottomMargin: 1
        anchors.topMargin: 1
        id:morakhasiRect
        anchors.fill: parent
        color: "#f5f5f5"
    }
    DropShadow {
        anchors.fill: morakhasiRect
        radius: 9.0
        samples: 17
        color: "#80000000"
        source: morakhasiRect
    }
}

URect.qml

Rectangle{
    color: "transparent"
    border.color: Universal.color(Universal.Cobalt)
    border.width: 1
}

UTumbler.qml

import QtQuick 2.0
import QtQuick.Controls.Universal 2.4
import QtQuick.Controls 2.4
Tumbler{
    id:hourSpin
    wrap: false
    delegate: Text{

        font.pointSize: 12
        text: modelData
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        opacity: 1.0 - Math.abs(Tumbler.displacement) / (hourSpin.visibleItemCount / 2)
    }
    Rectangle {
        anchors.horizontalCenter: hourSpin.horizontalCenter
        y: hourSpin.height * 0.4
        width: 40
        height: 1
        color: Universal.color(Universal.Cobalt)
    }

    Rectangle {
        anchors.horizontalCenter: hourSpin.horizontalCenter
        y: hourSpin.height * 0.6
        width: 40
        height: 1
        color: Universal.color(Universal.Cobalt)
    }
}

UTimeDialog

import QtQuick 2.0
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
Item{
    id:root
    property alias hour : hourSpin.currentIndex
    property alias minute : minuteSpin.currentIndex
    signal open
    signal close
    signal accepted
    signal rejected
    visible: element.opened
    onOpen: element.open()
    onClose: element.close()
    implicitWidth: 200
    implicitHeight: 200
    Dialog {
        id: element
        modal: true
        width: parent.width
        height: parent.height
        padding: 5
        margins: 5
        background: Item{

        }

        onAccepted: {
            root.accepted()
        }
        onRejected: {
            root.rejected()
        }
        contentItem: UCard{
            anchors.fill: parent
            radius: 10
        }

        Column{
            id: column
            spacing: 30
            anchors.centerIn: parent
            Row{
                id: row
                spacing: 20
                anchors.horizontalCenter: parent.horizontalCenter
                Column{
                    id: column1
                    spacing: 15
                    height: 80
                    width: 50
                    clip:true

                    UTumbler{
                        id:hourSpin
                        anchors.horizontalCenter: parent.horizontalCenter
                        anchors.verticalCenter: parent.verticalCenter
                        model: 24
                    }

                }
                Text{
                    text: ":"
                    font.pointSize: 12
                    anchors.verticalCenter: parent.verticalCenter
                    font.family: "B Nazanin"
                }
                Column{
                    id: column2
                    spacing: 15
                    height: 80
                    width: 50
                    clip:true

                    UTumbler{
                        id:minuteSpin
                        anchors.horizontalCenter: parent.horizontalCenter
                        anchors.verticalCenter: parent.verticalCenter
                        model: 60
                    }

                }

            }
            Row{
                anchors.horizontalCenter: parent.horizontalCenter
                spacing: 40
                UButton{
                    text:"select"

                    onClicked: {
                        element.reject()
                    }
                }
                UButton{
                    text: "cancel"
                    onClicked: {
                        element.accept()
                    }
                }
            }
        }

    }
}

UIcoButton.qml

import QtQuick 2.4
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
import QtGraphicalEffects 1.0
FocusScope{
    id:focusScope
    signal clicked
    property alias font : icoText.font.family
    property alias icon : icoText.text
    property alias size : icoText.font.pixelSize
    property alias caption : captionTxt.text
    property alias spacing : row.spacing
    property string colorEnter :Universal.color(Universal.Cobalt)
    property string colorExit :"#00171f"
    property alias state: root.state
    implicitWidth: captionTxt.text!= "" ? 100 : 35
    implicitHeight: 40
    Rectangle {
        id: root
        radius: 0
        anchors.fill: parent
        color: colorExit
        state: "default"
        focus: true

        onFocusChanged: {
            if(focus){
                root.border.width = 1
                root.border.color = Universal.color( Universal.Cobalt)
            }
            else{
                root.border.width = 0
                root.border.color = "transparent"
            }
        }

        Row{
            id: row
            anchors.rightMargin: 5
            anchors.leftMargin: 5
            anchors.bottomMargin: 5
            anchors.topMargin: 5
            anchors.fill: parent
            layoutDirection: Qt.RightToLeft
            spacing: 15
            Text {
                id: icoText
                text: ""
                anchors.verticalCenter: parent.verticalCenter
                font.pixelSize: 25
                font.family: "fontawesome"
                color: "white"
            }
            Text{
                id:captionTxt
                text: ""
                anchors.verticalCenter: parent.verticalCenter
                font.pixelSize: icoText.font.pixelSize * 55 /100
                font.family: "B Nazanin"
                color: "white"
                visible: text!= ""
            }

        }
        InnerShadow {
            id:shadow
            anchors.fill: row
            radius: 1.0
            samples: 17
            horizontalOffset: 1
            color: colorExit
            source: row
            visible: false
        }
        //    Glow {
        //        id:shadow
        //        anchors.fill: row
        //        radius: 6
        //        samples: 25
        //        color: "white"
        //        source: row
        //        visible: false
        //    }

        MouseArea{
            id: mouseArea
            anchors.fill: parent
            hoverEnabled: true
            onEntered: {
                if(root.state == "default")
                    root.color = colorEnter
                else{
                    icoText.color = colorEnter
                    captionTxt.color = colorEnter
                }
            }

            onExited: {
                if(root.state == "default")
                    root.color = colorExit
                else{
                    icoText.color = colorExit
                    captionTxt.color = colorExit
                }
            }

            onPressed: {
                shadow.visible = true
            }

            onReleased: {
                shadow.visible = false
            }

            onClicked: {
                focusScope.clicked()
            }
        }
        states: [
            State {
                name: "transparent"
                PropertyChanges {
                    target: root
                    color:"transparent"
                }
                PropertyChanges {
                    target: icoText
                    color:colorExit
                }
                PropertyChanges {
                    target: captionTxt
                    color:colorExit
                }
            },
            State{
                name: "default"
                PropertyChanges {
                    target: root
                    color:"#00171f"
                }
                PropertyChanges {
                    target: icoText
                    color:"white"
                }
                PropertyChanges {
                    target: captionTxt
                    color:"white"
                }
            }

        ]
    }
}

UTimePicker

import QtQuick 2.4
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
Item {
    id: scope
    clip: true
    QtObject{
        id:variables
        property var time: ({hour: 0, minute: 0})
        onTimeChanged: {
            refreshDialogTime()

        }
    }
    signal changed
    property alias caption : captionTxt.text
    property size size : Qt.size(30,70)
    property string splitter : ":"
    property alias spacing : row.spacing
    Component.onCompleted: {
        var q = new Date()
        var curtime = q.toLocaleTimeString().substring(0,5);
        if(splitter != ":"){
            curtime.replace(':',splitter)
        }
        var vars = curtime.split(':')
        setTime(vars[0],vars[1])
        refreshDialogTime()
    }
    function refreshDialogTime(){
        dialog.hour = variables.time.hour
        dialog.minute = variables.time.minute
    }

    function getTime(){
        return variables.time;
    }

    function setTimeString(time){
        textArea.text= time
    }

    function setTime(hour,minute){
        var _hour = hour
        if(_hour<10){
            _hour = "0"+hour.toString()
        }
        else{
            _hour = hour.toString()
        }
        var _minute = minute
        if(_minute <10){
            _minute = "0"+minute.toString()
        }
        else{
            _minute = minute.toString()
        }

        var time = _hour+":"+_minute
        textArea.text = time
    }
    implicitHeight: 50
    implicitWidth: 200
    Row{
        id: row
        width: parent.width
        height: parent.height
        spacing: 25
        layoutDirection: Qt.RightToLeft
        Text{
            font.bold: true
            id: captionTxt
            font.pointSize: 12
            horizontalAlignment: Text.AlignRight
            anchors.verticalCenter: parent.verticalCenter
            width: scope.size.width * scope.width /100 - scope.spacing/2
            verticalAlignment: Text.AlignVCenter
            font.family: "B Nazanin"

        }
        Item{
            id: element
            anchors.verticalCenter: parent.verticalCenter
            height: parent.height
            width: scope.size.height * scope.width /100 - scope.spacing/2
            Rectangle{
                id:backrec
                height: parent.height
                anchors.verticalCenter: parent.verticalCenter
                width: parent.width
                border.width: 1
                border.color: "black"
                TextField{
                    id:textArea
                    selectByMouse: true

                    anchors.verticalCenter: parent.verticalCenter

                    height: parent.height
                    rightPadding: 5
                    bottomPadding: 5
                    topPadding: 5
                    padding: 5
                    verticalAlignment: Text.AlignVCenter
                    onFocusChanged: {
                        if(focus){
                            captionTxt.color = Universal.color( Universal.Cobalt)
                            backrec.border.color = Universal.color( Universal.Cobalt)

                        }
                        else{
                            captionTxt.color = "black"
                            backrec.border.color = "black"
                        }
                    }
                    background: URect{

                        color: "transparent"
                        border.color: "black"
                        border.width: 0
                    }
                    onTextChanged: {
                        var _temp = text.split(splitter)
                        if(_temp.length>0){

                            variables.time.hour =_temp[0]==""?0:  _temp[0]
                            variables.time.minute = _temp[1]==""?0:_temp[1]
                        }
                        changed()
                    }


                    placeholderText : "HH:mm"
                    anchors.right: parent.right
                    anchors.left: iconBtn.right
                    font.family: "B Nazanin"
                    font.pointSize: 12
                    inputMask:  "99:99"
                    validator: RegExpValidator { regExp: /^([0-1\s]?[0-9\s]|2[0-3\s]):([0-5\s][0-9\s])$ / }
                }
                IcoButton{
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: parent.left
                    anchors.leftMargin: 2
                    id:iconBtn
                    caption: ""
                    size: 30
                    icon: "\uf017"
                    state: "transparent"
                    onClicked: {
                        textArea.focus = true
                        dialog.open()
                    }
                }
            }


        }
    }
    UTimeDialog{
        id:dialog
        x:iconBtn.x
        y:iconBtn.y+ scope.height
        onAccepted: {
            setTime(hour,minute)
        }
    }
}

Example

UTimePicker{
    x: 285
    width: 200
    spacing: 15
    size: Qt.size(35,65)
    caption: "time"
    onChanged: {
        var i =  getTime()
        console.log(i.hour)
        console.log(i.minute)
    }
}

which looks like this : ScreenShot
ScreenShot2

for mirroring :

    LayoutMirroring.enabled: true
    LayoutMirroring.childrenInherit: true

if some one intersted i could share library for this


Since Qt 5.5 the so called Qt Quick Enterprise Controls will be available also in the community edition of Qt under the name Qt Quick Extras. Among the others, the Tumbler seems a feasible solution for your requirements: you can easily setup two columns, one for the hours and one for the minutes.

If you are still interested in the circular selection (or wants to implement your own tumbler) you can take different routes such as create your own component inheriting from QQuickItem or QQuickPaintedItem or exploiting a custom view with PathView. The latter is the case I'm going to cover in this answer. Just refer to the provided links for examples about custom components creation.

Citing the documentation of PathView:

The view has a model, which defines the data to be displayed, and a delegate, which defines how the data should be displayed. The delegate is instantiated for each item on the path. The items may be flicked to move them along the path.

Hence the path defines the way items are laid out on the screen, even in a circular fashion. A path can be constructed via a Path type, i.e. a sequence of path segments of different kind. PathArc is the one we are interested in, since it provides the desired rounded shape.

The following example uses these elements to define a circular time picker. Each path is constructed by exploiting the currentIndexof the delegate: an integer is used as model for the PathViews - 12 for the hours view and 6 for the minutes view, respectively. The text of the delegates is generated by exploiting the index attached property and manipulating it to generate hours and 10-minutes interval values (see the delegates Text items). Finally, the text of the current element (i.e. the currentItem) is bound to the time label in the center of the window: as the currentIndex and currentItem change, also the label gets updated.

The overall component looks like this:

enter image description here

highlightcomponents (blue and green circles) are used to graphically representing editing of the time: when visible the time can be edited, i.e. another Item of the path can be selected. Switching between normal and editing mode occurs by clicking the time label in the center.

When in editing mode the user can simply hover the different hours/minutes values to select them. If the newly selected hour/minute is clicked the editing for that specific PathView is disabled and the corresponding highlight circle disappears.

This code is clearly just a toy example to give you a grasp of what PathView can be used for. Several improvements can be done, e.g. animations, a better number positioning, detailed minutes representation, a nice background and so on. However they are out of scope w.r.t. the question and were not considered.

import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
import QtQuick.Layouts 1.1

Window {
    visible: true
    width: 280; height: 280

    RowLayout {             // centre time label
        anchors.centerIn: parent
        Text {
            id: h
            font.pixelSize: 30
            font.bold: true
            text: outer.currentItem.text
        }
        Text {
            id: div
            font.pixelSize: 30
            font.bold: true
            text: qsTr(":")
        }
        Text {
            id: m
            font.pixelSize: 30
            font.bold: true
            text: inner.currentItem.text
        }

        MouseArea {
            anchors.fill: parent
            onClicked: outer.choiceActive = inner.choiceActive = !outer.choiceActive
        }
    }


    PathView {          // hours path
        id: outer
        property bool pressed: false
        model: 12

        interactive: false
        highlightRangeMode:  PathView.NoHighlightRange
        property bool choiceActive: false

        highlight: Rectangle {
            id: rect
            width: 30 * 1.5
            height: width
            radius: width / 2
            border.color: "darkgray"
            color: "steelblue"
            visible: outer.choiceActive
        }

        delegate: Item {
            id: del
            width: 30
            height: 30
            property bool currentItem: PathView.view.currentIndex == index
            property alias text : textHou.text
            Text {
                id: textHou
                anchors.centerIn: parent
                font.pixelSize: 24
                font.bold: currentItem
                text: index + 1
                color: currentItem ? "black" : "gray"
            }

            MouseArea {
                anchors.fill: parent
                enabled: outer.choiceActive
                onClicked: outer.choiceActive = false
                hoverEnabled: true
                onEntered: outer.currentIndex = index
            }
        }

        path: Path {
            startX: 200; startY: 40
            PathArc {
                x: 80; y: 240
                radiusX: 110; radiusY: 110
                useLargeArc: false
            }
            PathArc {
                x: 200; y: 40
                radiusX: 110; radiusY: 110
                useLargeArc: false
            }
        }
    }

    PathView {          // minutes path
        id: inner
        property bool pressed: false
        model: 6
        interactive: false
        highlightRangeMode:  PathView.NoHighlightRange
        property bool choiceActive: false

        highlight: Rectangle {
            width: 30 * 1.5
            height: width
            radius: width / 2
            border.color: "darkgray"
            color: "lightgreen"
            visible: inner.choiceActive
        }

        delegate: Item {
            width: 30
            height: 30
            property bool currentItem: PathView.view.currentIndex == index
            property alias text : textMin.text
            Text {
                id: textMin
                anchors.centerIn: parent
                font.pixelSize: 24
                font.bold: currentItem
                text: index * 10
                color: currentItem ? "black" : "gray"
            }

            MouseArea {
                anchors.fill: parent
                enabled: inner.choiceActive
                onClicked: inner.choiceActive = false
                hoverEnabled: true
                onEntered: inner.currentIndex = index
            }
        }

        path: Path {
            startX: 140; startY: 60
            PathArc {
                x: 140; y: 220
                radiusX: 40; radiusY: 40
                useLargeArc: false
            }
            PathArc {
                x: 140; y: 60
                radiusX: 40; radiusY: 40
                useLargeArc: false
            }
        }
    }

    // to set current time!
    onVisibleChanged: {
        var d = new Date();
        outer.currentIndex = d.getUTCHours() % 12
        inner.currentIndex = d.getMinutes() / 10
    }
}