实现代码
基于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内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论