2

QML provides a date type that appears to be freely convertible to and from both the C++ QDate class and the QML Date type. The Date type is an extension of the JS Date type.

Calling the set methods on a date property is apparently permissible, because no error (e.g. "method missing") is thrown:

// in a QML object declaration
property date myDate: new Date()
// in a slot somewhere in the object
date.setYear(2002)

However, the date data is not changed; printing the date before and after calling setYear (or any other set method) causes exact same date string to be printed twice.

The QML documentation doesn't seem to say much about date type in the link provided above.

Kyle Strand
  • 15,941
  • 8
  • 72
  • 167

2 Answers2

1

Let me explain to you what is wrong with the commented following exemple:

class.hpp

#ifndef CLASS_HPP
#define CLASS_HPP

#include <QObject>
#include <QDateTime>

class DateCpp : public QObject
{
    Q_OBJECT
    public:
        DateCpp();

    protected:
        // QML dates are QDateTime in C++, not QDate.
        Q_PROPERTY(QDateTime fooDate
                   READ getDate
                   WRITE setDate
                   NOTIFY fooDateChanged)
        QDateTime date;
        QDateTime getDate() const;
        void setDate(const QDateTime & d);

    signals:
        void dateChanged();
};

#endif

class.cpp

#include "class.hpp"

DateCpp::DateCpp() : QObject(), date() {}

QDateTime DateCpp::getDate() const
{
    return this->date;
}

void DateCpp::setDate(const QDateTime & newDate)
{
    this->date = newDate;

    /*
     * You have to tell QML that the property value has changed.
     * For this, use the NOTIFY signal of the property. If you do not emit it,
     * the property will not by updated on the QML side.
     */
    emit fooDateChanged();
}

main.cpp

#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "class.hpp"

int main(int argc, char ** argv)
{
    QApplication a(argc, argv);

    // Do not forget to declare your object to QML. ;-)
    qmlRegisterType<DateCpp>("Foobar", 3, 14, "DateQML");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return a.exec();
}

main.qml

import QtQuick 2.6
import Foobar 3.14

Rectangle {
    id: rect

    // A QML date (treated as a QDateTime by the engine).
    property date rectDate

    /*
     * Binded to myDate.fooDate, but variables are at different memory spaces.
     * Will be changed together nontheless.
     */
    property date rd2: myDate.fooDate

    /*
     * Just like rd2 but it is the same object in memory, a kind of pointer
     * on DateCpp::date.
     */
    property alias rd3: myDate.fooDate

    // No "=" but ":" for property declarations.
    property date aDateProp: Date.now()

    DateQML {
        id: myDate

        // It is a kind of slot connected to the fooDate Q_PROPERTY's NOTIFY signal.
        onFooDateChanged: {
            console.log("The foodate is: " + myDate.fooDate);

            // Will trigger a signal and "onRectDateChanged" will be executed.
            rect.rectDate = myDate.fooDate;
        }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            /*
             * It is JavaScript. "date d" is consequently wrong here.
             * d will be a QML date (treated as a QDateTime by the engine).
             */
            var d = Date.now();
            d.setYear(2002);

            // Calls the fooDate Q_PROPERTY's WRITE method, i.e. DateCpp::setDate();.
            myDate.fooDate = d;

            /*
             * If DateCpp::setDate(); is not called here, you can still copy 
             * the property to modify what you want:
             *
             * var d2 = myDate.fooDate;
             * d2.setYear(2025);
             * myDate.fooDate = d2;
             */
            myDate.fooDate.setYear(2025);
        }
    }

    onRectDateChanged: {
        console.log("The QML property has changed: " + rect.rectDate);
    }
}

TL;DR:

  • You are dealing with QDateTimes.
  • Use the NOTIFY signal to tell the system that the value has changed.
  • No date d to declare dates in JavaScript.
  • There are not any "=" in QML properties declaration but ":".
air-dex
  • 4,130
  • 2
  • 25
  • 25
  • Thanks for giving a complete buildable/runnable example. For your first TL;DR point, are you saying that the QML documentation is wrong? It states, "When integrating with C++, note that any QDate value passed into QML from C++ is automatically converted into a date value, and vice-versa." There is no mention of `QDateTime` on the page. For the third TL;DR point, I realize I didn't correctly indicate the contexts for my two lines of code; I've fixed it to indicate that the `date` declaration is actually a QML property declaration, while the `setYear` call is inside a JS context. – Kyle Strand May 17 '16 at 00:16
  • http://doc.qt.io/qt-5/qtqml-cppintegration-data.html says: "*The QML engine provides automatic type conversion between QDateTime values and JavaScript Date objects.*" My bad. QML basic type `date` is `QDate`. It has nothing to do with JS dates. However there are implicit conversions with QML `Date`s. Those dates are JS dates, so they are `QDateTime`s. Personally I always work with `QDateTime` properties on the C++ side and `date` properties on the QML side, and I have never had issues about it as far as I remember. – air-dex May 17 '16 at 01:20
  • There is a mistake in your edit. Your date myDate was a QML property, but you cannot use "`=`" in QML properties but "`:`". See my edit. ;-) – air-dex May 17 '16 at 01:22
  • Bleargh, you're right, I should have just copied-and-pasted directly from my actual project code. – Kyle Strand May 17 '16 at 15:47
  • So, it looks like you only actually call the `setDate` method on a `var` declared inside a JS context. Similarly, I noticed that I could get my app to work by declaring the property as a `var` instead of a `date`. So is there no way to directly modify a `date` object without simply overwriting it using `=`? – Kyle Strand May 17 '16 at 17:46
1

It appears (and air-dex's answer seems to confirm) that the date type simply can't be directly modified in this way. This is bizarre, because there are no errors triggered by attempts to call the methods.

So the solution is to simply declare date-objects that must be modified later as var rather than date.

Kyle Strand
  • 15,941
  • 8
  • 72
  • 167