Q_PROPERTY系统无法正常工作

时间:2018-06-20 12:57:26

标签: c++ qt qobject

我打算在qml引擎的上下文中使用此类,因此为了使用属性绑定,我设置了这些Q_PROPERY宏。我想使用MEMBER关键字,并自动发出通知信号。

class InterfaceBackend : public QObject
{
    Q_OBJECT
    Q_PROPERTY(quint8 current_view MEMBER m_current_view NOTIFY sCurrentViewChanged)
    Q_PROPERTY(quint8 future_view MEMBER m_future_view NOTIFY sFutureViewChanged)

public:
    explicit InterfaceBackend(QObject *parent = 0);
    ~InterfaceBackend();

    quint8 getCurrentView() { return this->m_current_view; }
    quint8 getFutureView() { return this->m_future_view; }

private:
    quint8 m_current_view;
    quint8 m_future_view;
    QByteArray m_selected_language;

public slots:
    void onLanguageSelected(QByteArray language);

private slots:


signals:
    void sCurrentViewChanged(quint8 current_view);
    void sFutureViewChanged(quint8 future_view);

};


InterfaceBackend::InterfaceBackend(QObject *parent) : QObject(parent)
{
    this->setObjectName("backend");
    QObject::connect(this, &InterfaceBackend::sFutureViewChanged, []() {qDebug() << "sFutureViewChanged";});
    this->m_current_view=1;
    this->m_future_view=1;
}

InterfaceBackend::~InterfaceBackend()
{

}

void InterfaceBackend::onLanguageSelected(QByteArray language)
{
    this->m_selected_language=language;
    this->m_future_view=2;
}

qt文档说:

NOTIFY信号是可选的。如果定义,则应在该类中指定一个现有信号,只要该属性的值发生更改,该信号就会发出。 MEMBER变量的NOTIFY信号必须采用零或一个参数,该参数必须与属性具有相同的类型。该参数将采用属性的新值。例如,仅当属性确实已更改时,才应发出NOTIFY信号,以避免不必要地在QML中重新评估绑定。对于没有显式设置器的MEMBER属性,Qt会自动发出该信号。

但是,每当我调用插槽时,信号就不会被调用,也不会在qml模型中更新属性,这是怎么回事!?

4 个答案:

答案 0 :(得分:1)

给出一个在技术上更准确的答案:

MEMBER中的Q_PROPERTY将告诉Moc(元对象编译器),当通过元对象访问属性时,应直接使用成员而不是getter。或二传手方法。因此,moc将在内部生成setter方法,该方法设置成员 并发出信号-基本上,这只是为您编写getter / setter方法的工作。由于更改成员需要发出更改信号,因此当从元对象系统写入属性时,将自动完成此操作。因此,致电:

backend->setProperty("future_view", future_view);

将正确发出更改的信号。这是使用MEMBER时提供的唯一保证。通过meta属性完成的更改将触发更改信号。这意味着,如果您直接从QML设置future_view,而无需使用onLanguageSelected方法,则它实际上可以工作。

但是,在您的示例中,您直接使用特殊方法将值写入成员-这不会自动触发信号! (我的意思是,Qt应该怎么知道您这样做了)。因此,您需要做的就是每当您更改会员的价值时就需要自己发出更改信号:

void onLanguageSelected(QByteArray language)
{
    this->m_selected_language=language;
    this->m_future_view=2;
    emit sFutureViewChanged();
}

编辑::如果您试图阻止直接从QML编写属性,则无法使用MEMBER!请改用吸气剂,并仅在属性中注册该吸气剂。使用与上面相同的代码来编写和更改属性:

    Q_PROPERTY(quint8 future_view READ futureView NOTIFY sFutureViewChanged)

答案 1 :(得分:-1)

如您在this文章中所见:

  

Q_PROPERTY中的新关键字:MEMBER使您可以将属性绑定到类成员,而无需使用getter或setter。

因此,您应该删除吸气剂,结果代码将如下所示

class InterfaceBackend : public QObject
{
    Q_OBJECT
    Q_PROPERTY(quint8 current_view MEMBER m_current_view NOTIFY sCurrentViewChanged)
    Q_PROPERTY(quint8 future_view MEMBER m_future_view NOTIFY sFutureViewChanged)

public:
    explicit InterfaceBackend(QObject *parent = 0)
        : QObject(parent)
    {
        this->setObjectName("backend");
        QObject::connect(this, &InterfaceBackend::sFutureViewChanged, []() { qDebug() << "sFutureViewChanged";});
        this->m_current_view=1;
        emit sCurrentViewChanged();
        this->m_future_view=1;
        emit sFutureViewChanged();
    }
    ~InterfaceBackend() = default;

private:
    quint8 m_current_view;
    quint8 m_future_view;
    QByteArray m_selected_language;

public slots:
    void onLanguageSelected(QByteArray language) {
        this->m_selected_language=language;
        this->m_future_view=2;
        emit sFutureViewChanged();
    }

signals:
    void sCurrentViewChanged();
    void sFutureViewChanged();

};

答案 2 :(得分:-1)

每个Q_PROPERTY必须具有READ公共方法和WRITE公共插槽,并且该信号永远不会自动发出,只要成员发生更改,就应该发出该信号。

class InterfaceBackend : public QObject
{
    Q_OBJECT
    Q_PROPERTY(quint8 current_view MEMBER m_current_view READ currentView WRITE setCurrentView NOTIFY sCurrentViewChanged)
    Q_PROPERTY(quint8 future_view MEMBER m_future_view READ futureView WRITE setFutureView NOTIFY sFutureViewChanged)

public:
    explicit InterfaceBackend(QObject *parent = 0);
    ~InterfaceBackend();

    quint8 currentView() const
    {
        return m_current_view;
    }
    quint8 futureView() const
    {
        return m_future_view;
    }

private:
    quint8 m_current_view;
    quint8 m_future_view;
    QByteArray m_selected_language;

public slots:
    void onLanguageSelected(QByteArray language);

    void setCurrentView(quint8 current_view)
    {
        if (m_current_view == current_view)
            return;

        m_current_view = current_view;
        emit sCurrentViewChanged(m_current_view);
    }

    void setFutureView(quint8 future_view)
    {
        if (m_future_view == future_view)
            return;

        m_future_view = future_view;
        emit sFutureViewChanged(m_future_view);
    }

private slots:


signals:
    void sCurrentViewChanged(quint8 current_view);
    void sFutureViewChanged(quint8 future_view);

};


InterfaceBackend::InterfaceBackend(QObject *parent) : QObject(parent)
{
    this->setObjectName("backend");
    QObject::connect(this, &InterfaceBackend::sFutureViewChanged, []() {qDebug() << "sFutureViewChanged";});
    this->m_current_view=1;
    this->m_future_view=1;
}

InterfaceBackend::~InterfaceBackend()
{

}

void InterfaceBackend::onLanguageSelected(QByteArray language)
{
    this->m_selected_language=language;
    this->m_future_view=2;
}

答案 3 :(得分:-1)

花一些时间,我可以根据需要进行工作,尽管这是非常丑陋的,并且绝不言弃,我认为这是实现双重绑定的唯一方法,也许对其他解决此问题的人会有帮助。请注意,这不是一个可以接受的答案,我仍然希望某个地方的某个人,也许在将来,如果Qt将在何时以及何时更新,可以提出一个更为优雅的解决方案。

主要缺点:代码冗长且冗长,必须在各处使用setter,并且(请不要忘记)

属性系统最终不会自动执行任何操作,只是声明哪些属性是公开的,哪些属性是它们的获取器/设置器和更改信号

c ++

class InterfaceController : public QObject
{
    Q_OBJECT
    Q_PROPERTY(quint8 current_view READ getCurrentView WRITE setCurrentView NOTIFY sCurrentViewChanged)
    Q_PROPERTY(quint8 future_view READ getFutureView WRITE setFutureView NOTIFY sFutureViewChanged)

public:
    explicit InterfaceController(QObject *parent = 0);

    //getters
    quint8 getCurrentView() { return this->m_current_view; }
    quint8 getFutureView() { return this->m_future_view; }

    //setters
    Q_INVOKABLE void setCurrentView(quint8 current_view) { if(this->m_current_view!=current_view) {this->m_current_view=current_view; emit sCurrentViewChanged(this->m_current_view);} }
    Q_INVOKABLE void setFutureView(quint8 future_view) { if(this->m_future_view!=future_view) {this->m_future_view=future_view; emit sFutureViewChanged(this->m_future_view);} }

private:
    quint8 m_current_view;
    quint8 m_future_view;

    QByteArray m_selected_language;

public slots:
    void onLanguageSelected(QByteArray language);

private slots:


signals:
    void sCurrentViewChanged(quint8 current_view);
    void sFutureViewChanged(quint8 future_view);

};

InterfaceController::InterfaceController(QObject *parent) : QObject(parent)
{
    this->m_current_view=1;
    this->m_future_view=1;
}

void InterfaceController::onLanguageSelected(QByteArray language)
{
    this->m_selected_language=language;
    this->setFutureView(2);
}

QML

id: root
property int current_view: 1
property int future_view: 1

Connections {
    target: root
    onCurrent_viewChanged: { backend.setCurrentView(current_view); }
    onFuture_viewChanged: { backend.setFutureView(future_view); }
}

Connections {
    target: backend
    onSCurrentViewChanged: { if(root.current_view!=current_view) {root.current_view=current_view;} }
    onSFutureViewChanged: { if(root.future_view!=future_view) {root.future_view=future_view;} }
}