2

Consider these classes:

Class A : public QObject {
    ...
    Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)

    virtual int value() { return m_value; }
    void setValue(int v) { m_value = v; Q_EMIT valueChanged();}
    ...

};

Class B : public A {
    ...

    int value() { return m_value * 2; }

    ...
};

When property value is accessed, the Class A method is called directly instead of Class B's.

So far, to workaround this apparent limitation I've replicated the property code and connected the signals from each class.

Is this the best solution?

Does anyone see potential problems (due to the properties having the same name)?

Silgaer22
  • 77
  • 3
  • 7

2 Answers2

6

From the Qt documentation:

The READ, WRITE, and RESET functions can be inherited. They can also be virtual. When they are inherited in classes where multiple inheritance is used, they must come from the first inherited class.

Just make the accessors virtual, and they will be invoked from the vtable, so you will get the right function for every different subtype.

dtech
  • 47,916
  • 17
  • 112
  • 190
  • Maybe I'm missing something, but that's what I'm doing on the example I've provided. – Silgaer22 Apr 17 '15 at 10:16
  • Well.. I guess I was... :) I've retested and this time everything worked. Apparently, on QML the onValueChanged method is not called on value init when setup like this: `B { value: 2 onValueChanged: console.log("value " + value) }` – Silgaer22 Apr 17 '15 at 10:45
  • 1
    You probably needed to clean up the project and rebuild. Qt often has problems keeping everything up to date as you make changes. – dtech Apr 17 '15 at 10:47
1
Q_PROPERTY ... // the same
signals:
    void valueChanged(int value);
public:
int value() const { return m_value; }
virtual void setValue(int newValue) { 
    m_value = newValue; 
    emit valueChanged( value() ); 
}


class B : public A {
public:
    void setValue(int newValue) { A::setValue(newValue *2); }
}

variant 2 (little bit better):

Class A {
Q_PROPERTY
signals:
    void valueChanged(int value);
public:
    int value(...
    void setValue(int value) {
        changeValue(value); // to call virtual changeValue
        emit valueChanged( m_value );
    }
protected:
    virtual void changeValue(int newValue) {
        m_value = newValue;
    }
...
}

// We dont touch public methods, but protected:
Class B : public A {
protected:
    /*virtual*/ void changeValue(int newValue) {
        A::changeValue(newValue *2);
    }
}
Alexander Chernin
  • 446
  • 2
  • 8
  • 17
  • I liked your ingenuous solution, but as the simple "thing" works I'll stick to it! – Silgaer22 Apr 17 '15 at 10:49
  • 1
    BTW **ALWAYS** check if the new value is not equal to the existing value before setting and emitting the signal. This way you avoid connection and binding loops. – dtech Apr 17 '15 at 10:54
  • Solution 2 doesn't work when setting the value on a B instance, as the notify signal is not triggered in that case. The emit would need to move to the changeValue implementation, and of course should only trigger if the value actually changes. – André Feb 08 '18 at 07:24