4

I have the following QML object defined in c++:

class MyObj : public QQuickItem {
    Q_OBJECT
    Q_PROPERTY(QVariant func MEMBER func)

public slots:
    void callFunc(){
        //Call function pointed by "func" somehow
    }

private:
    QVariant func;
};

In QML I use MyObj as follows:

MyObj{
    func: function test(){ console.log("Hi!"); }

    Button{
        text: "Call func"
        onClicked: parent.callFunc()
    }
}

I'm getting the following error:

Unable to assign a function to a property of any type other than var.

I don't understand, isn't a QVariant property supposed to be identical as a property var? What is the correct way of doing this?

1 Answer 1

5

You can use QJSValue for this. Qt Quick Controls 2's SpinBox does something similar:

Q_PROPERTY(QJSValue textFromValue READ textFromValue WRITE setTextFromValue NOTIFY textFromValueChanged FINAL)

Its getters and setters are implemented like this:

QJSValue QQuickSpinBox::textFromValue() const
{
    Q_D(const QQuickSpinBox);
    if (!d->textFromValue.isCallable()) {
        QQmlEngine *engine = qmlEngine(this);
        if (engine)
            d->textFromValue = engine->evaluate(QStringLiteral("function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0); }"));
    }
    return d->textFromValue;
}

void QQuickSpinBox::setTextFromValue(const QJSValue &callback)
{
    Q_D(QQuickSpinBox);
    if (!callback.isCallable()) {
        qmlInfo(this) << "textFromValue must be a callable function";
        return;
    }
    d->textFromValue = callback;
    emit textFromValueChanged();
}

The getter provides a default function implementation if none was given (or the value isn't actually a function).

The function is used to allow the user to return custom text for a given input value:

text: control.textFromValue(control.value, control.locale)

Taking an example from the documentation, here's how you'd assign/override the function:

SpinBox {
    id: spinbox
    from: 0
    value: 110
    to: 100 * 100
    stepSize: 100
    anchors.centerIn: parent

    property int decimals: 2
    property real realValue: value / 100

    validator: DoubleValidator {
        bottom: Math.min(spinbox.from, spinbox.to)
        top:  Math.max(spinbox.from, spinbox.to)
    }

    textFromValue: function(value, locale) {
        return Number(value / 100).toLocaleString(locale, 'f', spinbox.decimals)
    }

    valueFromText: function(text, locale) {
        return Number.fromLocaleString(locale, text) * 100
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks this works. I actually had tried Q_PROPERTY(QJSValue func MEMBER callback) before asking the question but this somehow gives the error: invalid new-expression of abstract class type ‘QQmlPrivate::QQmlElement<MyObj>’ during compilation. I had then assumed that properties cannot be of QJSValue type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.