У меня есть этот класс, который я намеревался использовать в контексте движка 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 не является обязательным. Если он определен, он должен указывать один существующий сигнал в этом классе, который излучается всякий раз, когда значение свойства изменяется. Сигналы NOTIFY для переменных MEMBER должны принимать ноль или один параметр, который должен быть того же типа, что и свойство. Параметр примет новое значение свойства. Сигнал NOTIFY должен выдаваться только тогда, когда свойство действительно было изменено, чтобы избежать, например, ненужной переоценки привязок в QML. Qt автоматически генерирует этот сигнал, когда это необходимо для свойств MEMBER, у которых нет явного установщика
Но всякий раз, когда я вызываю слот, сигналы никогда не вызываются, а свойство не обновляется в модели qml, что не так !?
закомментированные геттеры (которые не включены в макросы) все еще не работают, но спасибо





Как вы можете видеть в статье это:
New keyword in Q_PROPERTY: MEMBER let you bind a property to a class member without requiring to have a getter or a 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();
};
Удаление геттеров не будет иметь никакого значения, поскольку они не упоминаются в макросах с READ или WRITE, также это НЕ то, что я хочу, испускание сигнала ручного изменения работает хорошо (я пробовал перед публикацией), НО, как я понимаю (если я не неправильно) использование MEMBER автоматически подаст сигнал, ФАКТ НЕ БУДЕТ, и проблема здесь
Если вы измените свойство из qml, он автоматически подаст сигнал. Но если вы измените значение свойства на стороне С ++, он не будет излучать никакого сигнала.
Хорошо, я понял, что мне нужно использовать setProperty () для автоматической генерации сигнала в C++, но если я изменю его с помощью qml, он не излучает сигнал, почему?
Нет, извините, ОН излучает сигнал, но он НЕ обновляет переменную-член, мне совсем не понятно поведение этой системы свойств, даже если я внимательно прочитал документацию ...
ОК. Если я правильно понял: 1) вы используете `` setProperty ("current_view", 1) `` `вместо` `this-> m_current_view = 1; испустить sCurrentViewChanged (); `` в C++ 2) вы перемещаете экземпляр своего объекта в qml и используете его в qml как возможное решение: попробуйте унаследовать свой класс от QQuickItem, зарегистрируйте его и импортируйте в qml. После этого создайте экземпляр в qml и попробуйте использовать это свойство
Каждый Q_PROPERTY должен иметь общедоступный метод READ и общедоступный слот WRITE, также сигнал никогда не будет генерироваться автоматически, вы должны испускать его при изменении MEMBER.
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;
}
Чтобы дать более точный технически ответ:
MEMBER в Q_PROPERTY сообщит moc (компилятору метаобъектов), что при доступе к свойству через мета-объект он должен использовать член напрямую вместо метода получения или установки. Таким образом, moc будет генерировать метод установки внутри себя, который устанавливает член а также, излучающий сигнал - в основном он просто выполняет работу по написанию геттеров / сеттеров за вас. Поскольку при изменении члена необходимо подавать сигнал изменения, это происходит автоматически, когда свойство записывается из мета-объектная система. Итак, звоню:
backend->setProperty("future_view", future_view);
будет правильно излучать измененный сигнал. Это единственная гарантия, которая дается при использовании MEMBER. Изменения, которые выполняются через свойство meta, вызовут сигнал изменения. Это означает, что если вы установите future_view из QML напрямую, без метода 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)
это действительно проясняет, скажем, сторона C++ в порядке, но в QML, если я привяжу свойство current_view, например: "property int current_view: backend.current_view", и QML редактирует это свойство, обратно в C++ я получаю только сигнал onCurrentViewChanged, но переменная делает не обновляйся !!
QML-привязки однонаправлены. Изменения в backend.current_view обновят свойство QML, но не наоборот.
Потратив некоторое время, я заставил его работать так, как мне нужно, хотя это ЧРЕЗВЫЧАЙНО УЖЕ и БЕЗ ДЕКЛАРАТИВНЫХ СРЕДСТВ Я думаю, что это единственный способ добиться двойной привязки, и, возможно, это будет полезно для других людей, которые подходят к проблеме. Обратите внимание, что это не принятый ответ, я все еще надеюсь, что кто-нибудь где-нибудь, возможно, в будущем, если и когда Qt будет обновлен, сможет предложить гораздо более элегантное решение.
Основные недостатки: более длинный и подробный код, везде приходится использовать сеттеры и (постарайтесь не забыть)
В конце концов, система свойств ничего не делает автоматически, а только указывает, какие свойства выставляются, а какие являются их геттерами / сеттерами и сигналами изменения.
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;} }
}
попробуйте удалить геттеры. Ключевое слово MEMBER автоматически создает геттер и сеттер для свойства