前言
现在比较闲,我就把手上的项目写了两个版本:一版QtWidgets c++实现的;另一版 qml 加少量c++实现的(当然还没写完)。可能我用QtWidgets用习惯了,感觉qml少很多控件,然后很多东西需要自己写:我发现qml没有时间日期编辑器/选择器,所以我模仿了QDateTimeEdit,根据思路的不同写了两个版本。
代码和说明
QDateTimeEdit可以通过两种方式修改:一则是直接编辑修改;二呢,是通过按钮上下调。
为了直接修改,时间数据不会错,所以我加了验证器(validator)属性,这部分是通过c++ 继承QValidator实现的
//DateTimeValidator.h
#ifndef DATETIMEVALIDATOR_H
#define DATETIMEVALIDATOR_H
#include <QValidator>
#include<QDateTime>
class DateTimeValidator : public QValidator
{
Q_OBJECT
public:
DateTimeValidator();
State validate(QString& input, int& pos) const;
};
#endif // DATETIMEVALIDATOR_H
//DateTimeValidator.cpp
#include "DateTimeValidator.h"
DateTimeValidator::DateTimeValidator()
{
}
QValidator::State DateTimeValidator::validate(QString &input, int &pos) const
{
QDateTime dt = QDateTime::fromString(input, "yyyy-MM-dd HH:mm");
if (dt.isNull()) // If null, the input cannot be parsed
{
return QValidator::Invalid;
}
return QValidator::Acceptable;
}
然后在main.cpp中注册此属性
qmlRegisterType<DateTimeValidator>("my.components", 1, 0, "DateTimeValidator");
纯字符串操作版本
qml没有关于日期时间的类,挨边的只有一个日历,但是不是我想要的。所以若想改变它的值——字符串,好像可以通过直接修改对应的字符实现,所以就有了这个版本:通过光标的位置,得知需要修改的部分(年/月/日/时/分),然后加减后,再拼成字符串写入就行了。注意:月、日、时和分他们的值可能是一位(即小于10),前面要加'0';还有大小月,闰年的区分。
这个版本和JS Date版本稍微有点不同:这个版本达到极限值后,就不响应了,比如若秒达到59后将不再响应加操作,一直停留在59(当然若你想59后面从0开始也可以,一句话的事儿)。而JS版本若达到极限值后再操作(加/减),将影响到上一位。
这个属于最初版本可能看着很繁琐,可以精简,而且存在着小bug,但是我懒,就先这样吧
import QtQuick 2.0
import QtQuick.Controls 1.2
import my.components 1.0
TextField {
id:dateEdit;
text : "2020-07-30 00:00"
inputMask: "9999-99-99 99:99"
validator: DateTimeValidator {}
Rectangle{
id:upArrow;
x:dateEdit.width-13; y:2;
width: 9;
height: (dateEdit.height-8)/2;
color: "#00000000";
Image{
anchors.centerIn: parent;
width: 9;height: 5;
source: "qrc:/img/dateUp.png";
}
MouseArea{
anchors.fill: parent;
onPressed: upArrow.color="#cfcfcf";
onReleased: upArrow.color="#00000000";
onClicked:{
var pos=dateEdit.cursorPosition;
var txt=dateEdit.text;
if(pos<4)
{//年份
var year=txt.substr(0,4);
year=parseInt(year);
var other=txt.substr(4);
txt=(year+1).toString().concat(other);
}else if(pos>=4&&pos<=6)
{//月份
var month=txt.substr(5,2)
month=parseInt(month);
if(month===12)
{
return;
}
month=month+1;
if(month<10)
month=0+month.toString();
else
month=month.toString();
other=txt.substr(0,5);
var other2=txt.substr(7);
txt=other+month+other2;
}else if(pos>=7&&pos<=9)
{//天数
var day=txt.substr(8,2);
month=parseInt(txt.substr(5,2));
if(month===1||month===3||month===5||month===7||month===8
||month===10||month===12)
{
if(day==="31")
return
}else if(month===2)
{
year=parseInt(txt.substr(0,4));
if (year%100!=0&&year%4==0||year%400==0)
{//闰年
if(day==="29")
return;
}else{//平年
if(day==="28")
return
}
} else{
if(day==="30")
return;
}
day=parseInt(day)+1;
if(day<10)
day=0+day.toString();
else
day=day.toString();
other=txt.substr(0,8);
other2=txt.substr(10);
txt=other+day+other2;
}else if(pos>=10&&pos<=12)
{
var hour=txt.substr(11,2);
if(hour==="23")
return;
hour=parseInt(hour)+1;
if(hour<10)
hour=0+hour.toString();
else
hour=hour.toString();
other=txt.substr(0,11);
other2=txt.substr(13);
txt=other+hour+other2;
}else{
var min=txt.substr(14,2);
if(min==="59")
return;
min=parseInt(min)+1;
if(min<10)
min=0+min.toString();
else
min=min.toString();
other=txt.substr(0,14);
txt=other+min;
}
dateEdit.text=txt;
dateEdit.cursorPosition=pos;
}
}
}
Rectangle{
id:downArrow;
anchors.left: upArrow.left;
anchors.top: upArrow.bottom;
anchors.topMargin: 4;
width: upArrow.width;
height: upArrow.height;
Image{
anchors.centerIn: parent;
width: 9;height: 5;
source: "qrc:/img/dateDown.png";
}
MouseArea{
anchors.fill: parent;
onPressed: downArrow.color="#cfcfcf";
onReleased: downArrow.color="#00000000";
onClicked:{
var pos=dateEdit.cursorPosition;
var txt=dateEdit.text;
if(pos<4)
{//年份
var year=txt.substr(0,4);
year=parseInt(year);
var other=txt.substr(4);
txt=(year-1).toString().concat(other);
}else if(pos>=4&&pos<=6)
{//月份
var month=txt.substr(5,2)
month=parseInt(month);
if(month===1)
{
return;
}
month=month-1;
if(month<10)
month=0+month.toString();
else
month=month.toString();
other=txt.substr(0,5);
var other2=txt.substr(7);
txt=other+month+other2;
}else if(pos>=7&&pos<=9)
{//天数
var day=txt.substr(8,2);
month=parseInt(txt.substr(5,2));
if(day==="01")
return;
day=parseInt(day)-1;
if(day<10)
day=0+day.toString();
else
day=day.toString();
other=txt.substr(0,8);
other2=txt.substr(10);
txt=other+day+other2;
}else if(pos>=10&&pos<=12)
{
var hour=txt.substr(11,2);
if(hour==="00")
return;
hour=parseInt(hour)-1;
if(hour<10)
hour=0+hour.toString();
else
hour=hour.toString();
other=txt.substr(0,11);
other2=txt.substr(13);
txt=other+hour+other2;
}else{
var min=txt.substr(14,2);
if(min==="00")
return;
min=parseInt(min)-1;
if(min<10)
min=0+min.toString();
else
min=min.toString();
other=txt.substr(0,14);
txt=other+min;
}
dateEdit.text=txt;
dateEdit.cursorPosition=pos;
}
}
}
}
JS Date版本
这个版本逻辑主要是靠JS Date实现的,说实话我不太会,所以我是对着教程敲的(附赠网络地址)。这个参考了一些网上的对Date的操作(Date转字符串,字符串转Date),整体逻辑比上一个清晰很多,看着也舒服优雅一些。
import QtQuick 2.0
import QtQuick.Controls 1.2
import my.components 1.0
TextField {
id:dateEdit;
text : "2020-07-30 00:00"
inputMask: "9999-99-99 99:99"
validator: DateTimeValidator {}
Rectangle{
id:upArrow;
x:dateEdit.width-13; y:2;
width: 9;
height: (dateEdit.height-8)/2;
Image{
anchors.centerIn: parent;
width: 9;height: 5;
source: "qrc:/img/dateUp.png";
}
MouseArea{
anchors.fill: parent;
onPressed: upArrow.color="#cfcfcf";
onReleased: upArrow.color="#00000000";
onClicked:{
operateDateTime(1);
}
}
}
Rectangle{
id:downArrow;
anchors.left: upArrow.left;
anchors.top: upArrow.bottom;
anchors.topMargin: 4;
width: upArrow.width;
height: upArrow.height;
Image{
anchors.centerIn: parent;
width: 9;height: 5;
source: "qrc:/img/dateDown.png";
}
MouseArea{
anchors.fill: parent;
onPressed: downArrow.color="#cfcfcf";
onReleased: downArrow.color="#00000000";
onClicked:{
operateDateTime(-1);
}
}
}
function operateDateTime(operate)
{
var pos=dateEdit.cursorPosition;
var txt=dateEdit.text;
txt=txt+":00";
var date=convertDateFromString(txt);
var str=date.toString();
if(pos<4)
{//年份
date.setFullYear(date.getFullYear()+operate);
}else if(pos>=4&&pos<=6)
{//月份
date.setMonth(date.getMonth()+operate);
}else if(pos>=7&&pos<=9)
{//天数
date.setDate(date.getDate()+operate);
}else if(pos>=10&&pos<=12)
{//小时
date.setHours(date.getHours()+operate);
}else{//分钟
date.setMinutes(date.getMinutes()+operate);
}
str=date.toString();
txt=dateFormat(date,"yyyy-MM-dd hh:mm");
dateEdit.text=txt;
dateEdit.cursorPosition=pos;
}
function checkNull(value) {
return (!value || value == null || typeof(value) == "undefined" || value == "");
}
// 填充0
function fillZero(value) {
return value.toString().length < 2 ? ('0' + value) : value
}
//字符串转Date
function convertDateFromString(dateString) {
if (dateString) {
var arr1 = dateString.split(" ");
var sdate = arr1[0].split('-');
var stime=arr1[1].split(':');
var date = new Date(sdate[0], sdate[1]-1, sdate[2],stime[0],stime[1],stime[2]);
return date;
}
}
//Date通过格式str转为字符串
function dateFormat(d, str) {
var formatStr = checkNull(str) ? 'yyyy-MM-dd HH:mm:ss' : str;
if (checkNull(d)) { // 如果日期为空,自动获取当前日期
d = new Date();
} else if (d.constructor != Date) { // 如果参数不是一个日期对象,就认为是一个标准Long值日期
d = new Date(d);
}
return formatStr .replace("yyyy", d.getFullYear())
.replace("MM", fillZero(d.getMonth() + 1))
.replace("dd", fillZero(d.getDate()))
.replace("hh", fillZero(d.getHours()))
.replace("mm", fillZero(d.getMinutes()))
.replace("ss",fillZero(d.getSeconds()))
.replace("sss", d.getMilliseconds());
}
}
效果图
结束语
日期时间编辑器本来觉得挺简单的东西,结果折腾挺久,感觉想学好qml道阻且长啊!