前言

现在比较闲,我就把手上的项目写了两个版本:一版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());
    }
}

 

效果图

QDateTime对应的java qml datetime_sed

结束语

日期时间编辑器本来觉得挺简单的东西,结果折腾挺久,感觉想学好qml道阻且长啊!