我打算在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模型中更新属性,这是怎么回事!?
答案 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;} }
}