当前位置: 代码网 > it编程>编程语言>C/C++ > Qt6 QML实现DateTimePicker组件的示例代码

Qt6 QML实现DateTimePicker组件的示例代码

2025年12月15日 C/C++ 我要评论
实现代码基于qt6.10// datetimepicker.qmlimport qtquickimport qtquick.controlsimport qtquick.layoutspopup {

实现代码

基于qt6.10

// datetimepicker.qml
import qtquick
import qtquick.controls
import qtquick.layouts

popup {
    id: datetimepicker

    width: 650
    height: 480

    modal: true
    closepolicy: popup.closeonescape | popup.closeonpressoutside
    padding: 0
    focus: true

    readonly property color colorbackground: "#2b2b2b"
    readonly property color colorsurface: "#1e1e1e"
    readonly property color colorsurfacevariant: "#222"
    readonly property color colorborder: "#444"
    readonly property color colorborderlight: "#555"

    readonly property color colorprimary: "#007aff"
    readonly property color colorprimaryhover: "#0066cc"

    readonly property color colortextprimary: "white"
    readonly property color colortextsecondary: "#aaa"
    readonly property color colortexttertiary: "#888"
    readonly property color colortextdisabled: "#444"

    readonly property color colorhover: "#333"
    readonly property color colorhoverlight: "#444"
    readonly property color colorhoverdark: "#555"

    readonly property color colorscrollbar: "#555"
    readonly property color colorscrollbarhover: "#666"
    readonly property color colorscrollbarpressed: "#888"

    readonly property color colordisabled: "#555"

    property string datetime: qt.formatdatetime(new date(), "yyyy-mm-dd hh:mm:ss")
    property string dateformat: "yyyy-mm-dd"
    property string timeformat: "hh:mm:ss"
    property string mindatetime: ""
    property string maxdatetime: ""

    property date selecteddate: new date()
    property int selectedhour: 0
    property int selectedminute: 0
    property int selectedsecond: 0

    property int currentyear: new date().getfullyear()
    property int currentmonth: new date().getmonth()

    property bool hasselection: false  // 选择状态标记

    // 焦点区域属性
    property int focusarea: 0  // 0: 日期, 1: 时, 2: 分, 3: 秒
    property int focuseddateindex: 0  // 日历格子索引

    signal confirmed(date datetime)

    background: rectangle {
        color: datetimepicker.colorbackground
        radius: 8

        focusscope {
            id: keyboardhandler
            anchors.fill: parent
            focus: true

            keys.onpressed: function (event) {
                if (event.key === qt.key_left) {
                    if (datetimepicker.focusarea === 0) {
                        datetimepicker.focuseddateindex = math.max(0, datetimepicker.focuseddateindex - 1)
                        datetimepicker.selecteddate = getdateforcell(datetimepicker.focuseddateindex)
                        datetimepicker.hasselection = true
                    } else if (datetimepicker.focusarea > 0) {
                        datetimepicker.focusarea = math.max(0, datetimepicker.focusarea - 1)
                    }
                    event.accepted = true
                } else if (event.key === qt.key_right) {
                    if (datetimepicker.focusarea === 0) {
                        datetimepicker.focuseddateindex = math.min(41, datetimepicker.focuseddateindex + 1)
                        datetimepicker.selecteddate = getdateforcell(datetimepicker.focuseddateindex)
                        datetimepicker.hasselection = true
                    } else if (datetimepicker.focusarea < 3) {
                        datetimepicker.focusarea = math.min(3, datetimepicker.focusarea + 1)
                    }
                    event.accepted = true
                } else if (event.key === qt.key_up) {
                    if (datetimepicker.focusarea === 0) {
                        datetimepicker.focuseddateindex = math.max(0, datetimepicker.focuseddateindex - 7)
                        datetimepicker.selecteddate = getdateforcell(datetimepicker.focuseddateindex)
                        datetimepicker.hasselection = true
                    } else {
                        if (datetimepicker.focusarea === 1) {
                            datetimepicker.selectedhour = (datetimepicker.selectedhour - 1 + 24) % 24
                        } else if (datetimepicker.focusarea === 2) {
                            datetimepicker.selectedminute = (datetimepicker.selectedminute - 1 + 60) % 60
                        } else {
                            datetimepicker.selectedsecond = (datetimepicker.selectedsecond - 1 + 60) % 60
                        }
                        datetimepicker.hasselection = true
                    }
                    event.accepted = true
                } else if (event.key === qt.key_down) {
                    if (datetimepicker.focusarea === 0) {
                        datetimepicker.focuseddateindex = math.min(41, datetimepicker.focuseddateindex + 7)
                        datetimepicker.selecteddate = getdateforcell(datetimepicker.focuseddateindex)
                        datetimepicker.hasselection = true
                    } else {
                        if (datetimepicker.focusarea === 1) {
                            datetimepicker.selectedhour = (datetimepicker.selectedhour + 1) % 24
                        } else if (datetimepicker.focusarea === 2) {
                            datetimepicker.selectedminute = (datetimepicker.selectedminute + 1) % 60
                        } else {
                            datetimepicker.selectedsecond = (datetimepicker.selectedsecond + 1) % 60
                        }
                        datetimepicker.hasselection = true
                    }
                    event.accepted = true
                } else if (event.key === qt.key_tab) {
                    datetimepicker.focusarea = (datetimepicker.focusarea + 1) % 4
                    event.accepted = true
                } else if (event.key === qt.key_backtab) {
                    datetimepicker.focusarea = (datetimepicker.focusarea - 1 + 4) % 4
                    event.accepted = true
                } else if (event.key === qt.key_return || event.key === qt.key_enter) {
                    if (datetimepicker.hasselection) {
                        let dt = new date(datetimepicker.selecteddate)
                        dt.sethours(datetimepicker.selectedhour)
                        dt.setminutes(datetimepicker.selectedminute)
                        dt.setseconds(datetimepicker.selectedsecond)
                        datetimepicker.confirmed(dt)
                        datetimepicker.close()
                    }
                    event.accepted = true
                } else if (event.key === qt.key_escape) {
                    datetimepicker.close()
                    event.accepted = true
                }
            }
        }
    }

    columnlayout {
        anchors.fill: parent
        spacing: 0

        // 顶部日期时间显示
        rectangle {
            layout.fillwidth: true
            layout.preferredheight: 50
            color: datetimepicker.colorsurface
            radius: 8

            rectangle {
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.bottom: parent.bottom
                height: 8
                color: parent.color
            }

            label {
                id: datetimelabel

                anchors.centerin: parent
                color: datetimepicker.colorprimary
                font.pixelsize: 16
                font.bold: true
                text: qt.formatdatetime(new date(selecteddate.getfullyear(), selecteddate.getmonth(), selecteddate.getdate(), selectedhour, selectedminute, selectedsecond), dateformat + " " + timeformat)
            }
        }

        // 主内容区域
        rowlayout {
            layout.fillwidth: true
            layout.fillheight: true
            layout.margins: 20
            spacing: 20

            // 左侧日历区域
            columnlayout {
                layout.fillwidth: true
                layout.fillheight: true
                spacing: 12

                // 月份导航栏
                rowlayout {
                    layout.fillwidth: true
                    spacing: 8

                    button {
                        text: "<<"
                        layout.preferredwidth: 40
                        layout.preferredheight: 32
                        onclicked: {
                            currentyear -= 1
                        }
                        background: rectangle {
                            color: parent.hovered ? datetimepicker.colorhoverdark : datetimepicker.colorborder
                            radius: 4
                        }
                        contentitem: text {
                            text: parent.text
                            color: datetimepicker.colortextprimary
                            horizontalalignment: text.alignhcenter
                            verticalalignment: text.alignvcenter
                        }
                    }
                    button {
                        text: "<"
                        layout.preferredwidth: 40
                        layout.preferredheight: 32
                        onclicked: {
                            currentmonth -= 1
                            if (currentmonth < 0) {
                                currentmonth = 11
                                currentyear -= 1
                            }
                        }
                        background: rectangle {
                            color: parent.hovered ? datetimepicker.colorhoverdark : datetimepicker.colorborder
                            radius: 4
                        }
                        contentitem: text {
                            text: parent.text
                            color: datetimepicker.colortextprimary
                            horizontalalignment: text.alignhcenter
                            verticalalignment: text.alignvcenter
                        }
                    }
                    label {
                        text: qt.formatdate(new date(currentyear, currentmonth), "yyyy年 m月")
                        color: datetimepicker.colortextprimary
                        font.pixelsize: 16
                        font.bold: true
                        layout.fillwidth: true
                        horizontalalignment: text.alignhcenter
                    }
                    button {
                        text: ">"
                        layout.preferredwidth: 40
                        layout.preferredheight: 32
                        onclicked: {
                            currentmonth += 1
                            if (currentmonth > 11) {
                                currentmonth = 0
                                currentyear += 1
                            }
                        }
                        background: rectangle {
                            color: parent.hovered ? datetimepicker.colorhoverdark : datetimepicker.colorborder
                            radius: 4
                        }
                        contentitem: text {
                            text: parent.text
                            color: datetimepicker.colortextprimary
                            horizontalalignment: text.alignhcenter
                            verticalalignment: text.alignvcenter
                        }
                    }
                    button {
                        text: ">>"
                        layout.preferredwidth: 40
                        layout.preferredheight: 32
                        onclicked: {
                            currentyear += 1
                        }
                        background: rectangle {
                            color: parent.hovered ? datetimepicker.colorhoverdark : datetimepicker.colorborder
                            radius: 4
                        }
                        contentitem: text {
                            text: parent.text
                            color: datetimepicker.colortextprimary
                            horizontalalignment: text.alignhcenter
                            verticalalignment: text.alignvcenter
                        }
                    }
                }

                // 星期标题行
                gridlayout {
                    layout.fillwidth: true
                    columns: 7
                    rowspacing: 0
                    columnspacing: 0

                    repeater {
                        model: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
                        label {
                            text: modeldata
                            color: datetimepicker.colortexttertiary
                            font.pixelsize: 12
                            horizontalalignment: text.alignhcenter
                            verticalalignment: text.alignvcenter
                            layout.fillwidth: true
                            layout.preferredheight: 30
                        }
                    }
                }

                // 日期网格
                gridlayout {
                    layout.fillwidth: true
                    layout.fillheight: true
                    columns: 7
                    rowspacing: 8
                    columnspacing: 8

                    repeater {
                        model: 42
                        rectangle {
                            layout.fillwidth: true
                            layout.fillheight: true
                            layout.minimumheight: 40
                            radius: 4

                            color: {
                                if (!isinrange) return datetimepicker.colorsurfacevariant

                                let date = getdateforcell(index)
                                if (date.getdate() === selecteddate.getdate() && date.getmonth() === selecteddate.getmonth() && date.getfullyear() === selecteddate.getfullyear()) {
                                    return datetimepicker.colorprimary
                                }
                                return datemousearea.containsmouse ? datetimepicker.colorborder : "transparent"
                            }

                            border.color: {
                                let date = celldate
                                if (date.getdate() === selecteddate.getdate() && date.getmonth() === selecteddate.getmonth() && date.getfullyear() === selecteddate.getfullyear()) {
                                    return "white"
                                }
                                return "transparent"
                            }
                            border.width: 2

                            property var celldate: getdateforcell(index)

                            property bool isinrange: {
                                let testdate = new date(celldate)
                                testdate.sethours(selectedhour, selectedminute, selectedsecond)
                                return isdatetimeinrange(testdate)
                            }

                            label {
                                anchors.centerin: parent
                                text: parent.celldate.getdate()
                                color: {
                                    if (!parent.isinrange) return datetimepicker.colortextdisabled
                                    return parent.celldate.getmonth() === currentmonth ? datetimepicker.colortextprimary : datetimepicker.colorborderlight
                                }
                                font.pixelsize: 14
                            }

                            mousearea {
                                id: datemousearea
                                anchors.fill: parent
                                hoverenabled: true
                                enabled: parent.isinrange
                                cursorshape: enabled ? qt.pointinghandcursor : qt.forbiddencursor
                                onclicked: {
                                    if (parent.isinrange) {
                                        let clickeddate = parent.celldate
                                        selecteddate = clickeddate

                                        // 如果点击的日期不在当前月,自动切换到该月
                                        if (clickeddate.getmonth() !== currentmonth || clickeddate.getfullyear() !== currentyear) {
                                            currentmonth = clickeddate.getmonth()
                                            currentyear = clickeddate.getfullyear()
                                        }

                                        datetimepicker.hasselection = true
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // 分隔线
            rectangle {
                layout.preferredwidth: 1
                layout.fillheight: true
                color: datetimepicker.colorborder
            }

            // 右侧时间选择区域
            rowlayout {
                layout.preferredwidth: 210
                layout.fillheight: true
                spacing: 8

                // 时间列组件
                repeater {
                    model: [{label: "时", value: selectedhour, max: 24}, {
                        label: "分", value: selectedminute, max: 60
                    }, {label: "秒", value: selectedsecond, max: 60}]

                    columnlayout {
                        layout.fillwidth: true
                        layout.fillheight: true
                        spacing: 8

                        property string timelabel: modeldata.label

                        label {
                            text: timelabel
                            color: datetimepicker.colortexttertiary
                            font.pixelsize: 12
                            layout.alignment: qt.alignhcenter
                        }

                        rectangle {
                            layout.fillwidth: true
                            layout.fillheight: true
                            color: datetimepicker.colorsurface
                            radius: 4
                            border.color: datetimepicker.colorborder
                            border.width: 1

                            scrollview {
                                anchors.fill: parent
                                anchors.margins: 2
                                clip: true
                                scrollbar.horizontal.policy: scrollbar.alwaysoff

                                scrollbar.vertical: scrollbar {
                                    policy: scrollbar.asneeded
                                    width: 8

                                    contentitem: rectangle {
                                        implicitwidth: 8
                                        radius: 4
                                        color: parent.pressed ? datetimepicker.colorscrollbarpressed : (parent.hovered ? datetimepicker.colorscrollbarhover : datetimepicker.colorscrollbar)
                                    }

                                    background: rectangle {
                                        implicitwidth: 6
                                        color: "transparent"
                                    }
                                }

                                listview {
                                    id: timelistview

                                    model: modeldata.max
                                    highlightfollowscurrentitem: false
                                    highlightmoveduration: 0
                                    snapmode: listview.snaptoitem

                                    readonly property string timetype: timelabel

                                    readonly property int currentvalue: {
                                        if (timetype === "时") return datetimepicker.selectedhour
                                        if (timetype === "分") return datetimepicker.selectedminute
                                        return datetimepicker.selectedsecond
                                    }

                                    function selecttime(value) {
                                        console.log("selected " + timelistview.timetype + ": " + value + ", hasselection: " + datetimepicker.hasselection)

                                        datetimepicker.hasselection = true

                                        if (timetype === "时") {
                                            datetimepicker.selectedhour = value
                                        } else if (timetype === "分") {
                                            datetimepicker.selectedminute = value
                                        } else {
                                            datetimepicker.selectedsecond = value
                                        }

                                        // 滚动到列表顶部
                                        positionviewatindex(value, listview.beginning)
                                    }

                                    delegate: rectangle {
                                        width: timelistview.width
                                        height: 44
                                        radius: 4

                                        color: {
                                            if (index === timelistview.currentvalue) return datetimepicker.colorprimary
                                            return timeitemmousearea.containsmouse ? datetimepicker.colorhover : "transparent"
                                        }

                                        label {
                                            anchors.centerin: parent
                                            text: index.tostring().padstart(2, '0')
                                            color: index === timelistview.currentvalue ? datetimepicker.colortextprimary : datetimepicker.colortextsecondary
                                            font.pixelsize: 16
                                            font.bold: index === timelistview.currentvalue
                                        }

                                        mousearea {
                                            id: timeitemmousearea
                                            anchors.fill: parent
                                            hoverenabled: true
                                            cursorshape: qt.pointinghandcursor
                                            onclicked: {
                                                timelistview.selecttime(index)
                                            }
                                        }
                                    }

                                    component.oncompleted: {
                                        positionviewatindex(modeldata.value, listview.beginning)
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        // 底部按钮区域
        rectangle {
            layout.fillwidth: true
            layout.preferredheight: 60
            color: datetimepicker.colorsurface

            rectangle {
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.top: parent.top
                height: 8
                color: parent.color
            }

            rowlayout {
                anchors.fill: parent
                anchors.margins: 15
                spacing: 10

                button {
                    text: qstr("今天")
                    layout.preferredwidth: 80
                    layout.preferredheight: 36
                    onclicked: {
                        var now = new date()
                        selecteddate = now
                        selectedhour = now.gethours()
                        selectedminute = now.getminutes()
                        selectedsecond = now.getseconds()
                        currentyear = now.getfullyear()
                        currentmonth = now.getmonth()
                        hasselection = true
                    }
                    background: rectangle {
                        color: parent.hovered ? datetimepicker.colorhoverdark : datetimepicker.colorborder
                        radius: 4
                        border.color: datetimepicker.colorborderlight
                        border.width: 1
                    }
                    contentitem: text {
                        text: parent.text
                        color: datetimepicker.colortextsecondary
                        font.pixelsize: 14
                        horizontalalignment: text.alignhcenter
                        verticalalignment: text.alignvcenter
                    }
                }

                item {
                    layout.fillwidth: true
                }

                button {
                    text: qstr("取消")
                    layout.preferredwidth: 80
                    layout.preferredheight: 36
                    onclicked: datetimepicker.close()
                    background: rectangle {
                        color: parent.hovered ? datetimepicker.colorhoverdark : datetimepicker.colorborder
                        radius: 4
                        border.color: datetimepicker.colorborderlight
                        border.width: 1
                    }
                    contentitem: text {
                        text: parent.text
                        color: datetimepicker.colortextsecondary
                        font.pixelsize: 14
                        horizontalalignment: text.alignhcenter
                        verticalalignment: text.alignvcenter
                    }
                }

                button {
                    text: qstr("确定")
                    layout.preferredwidth: 80
                    layout.preferredheight: 36
                    enabled: datetimepicker.hasselection
                    onclicked: {
                        let dt = new date(selecteddate)
                        dt.sethours(selectedhour)
                        dt.setminutes(selectedminute)
                        dt.setseconds(selectedsecond)

                        if (isdatetimeinrange(dt)) {
                            datetimepicker.datetime = formatdatetime(dt)
                            confirmed(dt)
                            close()
                        }
                    }
                    background: rectangle {
                        color: {
                            if (!parent.enabled) return datetimepicker.colordisabled
                            return parent.hovered ? datetimepicker.colorprimaryhover : datetimepicker.colorprimary
                        }
                        radius: 4
                    }
                    contentitem: text {
                        text: parent.text
                        color: parent.enabled ? datetimepicker.colortextprimary : datetimepicker.colortexttertiary
                        font.pixelsize: 14
                        font.bold: true
                        horizontalalignment: text.alignhcenter
                        verticalalignment: text.alignvcenter
                    }
                }
            }
        }
    }

    function getdateforcell(index) {
        let firstday = new date(currentyear, currentmonth, 1)
        let dayofweek = (firstday.getday() + 6) % 7
        let celldate = new date(currentyear, currentmonth, 1 - dayofweek + index)
        return celldate
    }

    function isdatetimeinrange(date) {
        if (mindatetime) {
            let mindt = new date(mindatetime)
            if (!isnan(mindt.gettime()) && date < mindt) {
                return false
            }
        }
        if (maxdatetime) {
            let maxdt = new date(maxdatetime)
            if (!isnan(maxdt.gettime()) && date > maxdt) {
                return false
            }
        }
        return true
    }

    function formatdatetime(date) {
        return qt.formatdatetime(date, dateformat + " " + timeformat)
    }

    ondatetimechanged: {
        if (!hasselection) {
            let dt = new date(datetime)
            if (!isnan(dt.gettime())) {
                selecteddate = dt
                selectedhour = dt.gethours()
                selectedminute = dt.getminutes()
                selectedsecond = dt.getseconds()
                currentyear = dt.getfullyear()
                currentmonth = dt.getmonth()
            }
        }
    }

    component.oncompleted: {
        if (datetime) {
            let dt = new date(datetime)
            if (!isnan(dt.gettime())) {
                selecteddate = dt
                selectedhour = dt.gethours()
                selectedminute = dt.getminutes()
                selectedsecond = dt.getseconds()
                currentyear = dt.getfullyear()
                currentmonth = dt.getmonth()

                hasselection = true
            }
        }

        // 初始化焦点位置
        let firstday = new date(currentyear, currentmonth, 1)
        let dayofweek = (firstday.getday() + 6) % 7
        focuseddateindex = dayofweek + selecteddate.getdate() - 1

        // 延迟设置焦点,确保 focusscope 已完全初始化
        qt.calllater(function () {
            keyboardhandler.forceactivefocus()
        })
    }
}

使用代码

datetimepicker {
    id: datetimepicker

    width: 250
    height: 40

    // 初始值为当前日期时间
    datetime: qt.formatdatetime(new date(), "yyyy-mm-dd hh:mm:ss")

    // 日期格式
    dateformat: "yyyy-mm-dd"
    // 时间格式
    timeformat: "hh:mm:ss"

    // 最小可选日期时间
    mindatetime: "2023-01-01 00:00:00"
    // 最大可选日期时间
    maxdatetime: "2024-12-31 23:59:59"

    onconfirmed: function (datetime) {
        startdatefield.text = qt.formatdatetime(datetime, "yyyy-mm-dd hh:mm")
    }
}

显示效果:

到此这篇关于qt6 qml实现datetimepicker组件的示例代码的文章就介绍到这了,更多相关qt6 qml datetimepicker内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com