[Qt入門篇]7 Qt的屬性系統——NOTIFY和RESET

接上一篇,READ、WRITE和MEMBER能夠實現屬性讀寫,就Qt本身來講,我覺得Qt更推薦使用READ和WRITE,而不是MEMBER,這從QWidget的聲明中能夠看出來——所有屬性的讀寫都是READ和WRITE定義的,沒有使用MEMBER。這也可以理解,MEMBER將屬性關聯了成員變量,用戶修改屬性時相當於直接對成員變量操作,這樣的操作破壞了類的封閉性;而READ和WRITE則是使用類成員函數來修改成員變量,這是OOP(面向對象編程)提倡的方式,所以大家還是儘量使用WRITE和READ來定義屬性。

READ、WRITE和MEMBER修改屬性一般來說就足夠了,但是考慮到有時要觸發信號(這是Qt的特色,不能忽略)所以還需要一個NOTIFY關鍵字;要恢復屬性默認值,還需要一個RESET關鍵字。
還是QWidget例子:
Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle NOTIFY windowTitleChanged DESIGNABLE isWindow)
這個聲明包含了“NOTIFY windowTitleChanged”,表明當windowTitle屬性發生變化時,會觸發windowTitleChanged信號,相應的信號聲明在QWidget中
Q_SIGNALS:
void windowTitleChanged(const QString &title);
Q_PROPERTY(QLocale locale READ locale WRITE setLocale RESET unsetLocale)
這個聲明包含了“RESET unsetLocale”,表明QWidget有一個unsetLocale方法用於重置locale屬性,QWidget聲明中有:
public:
void unsetLocale();
NOTIFY signalfun:表明屬性變化時會觸發signalfun信號,signalfun的聲明有兩種形式,一種是帶一個參數,參數類型與屬性值相同,該參數存儲了最新的值,加不加const都可以;另一種不帶參數。
RESET fun:表明重置屬性的函數,該接口將屬性設置爲默認值,它的形式只有一種:沒有參數和返回值。
那如何來使用NOTIFY和RESET呢?我們定義如下代碼:
聲明:
#ifndef COBJ2_H
#define COBJ2_H

#include <QObject>

class CObj2 : public QObject
{
Q_OBJECT
Q_PROPERTY(qint32 test READ test WRITE setTest NOTIFY testChanged RESET unset)
Q_PROPERTY(qint32 test1 READ test WRITE setTest NOTIFY testChanged1)
public:
explicit CObj2(QObject *parent = 0);
qint32 test(void);
void setTest(qint32 t);
signals:
void testChanged(qint32 t);
void testChanged1(void);
public slots:
void OnTestChanged(qint32 t);
void OnTestChanged1(void);
void unset(void);
private:
qint32 m_test;
};

#endif // COBJ2_H
實現:
#include "cobj2.h"
#include <QDebug>

CObj2::CObj2(QObject *parent) : QObject(parent)
{
m_test = 0;
connect(this, SIGNAL(testChanged(qint32)), this, SLOT(OnTestChanged(qint32)));
connect(this, SIGNAL(testChanged1()), this, SLOT(OnTestChanged1()));
}

qint32 CObj2::test(void)
{
return (m_test);
}

void CObj2::setTest(qint32 t)
{
m_test = t;
}

void CObj2::OnTestChanged(qint32 t)
{
qDebug() << "OnTestChanged : " << t;
}

void CObj2::OnTestChanged1(void)
{
qDebug() << "OnTestChanged1";
}

void CObj2::unset(void)
{
m_test = 0;
}


COjb2中我們定義了兩個屬性:test和test1,test改變時觸發testChanged信號,test1改變時觸發testChanged1信號,其中test還有RESET方法unset。

在客戶端調用如下代碼:
CObj2 s;
s.setProperty("test", QVariant(2));
s.setProperty("test1", QVariant(3));
如果一切正常,那麼應該有字符串輸出,希望如此,點擊運行……什麼都沒有出現,這是爲什麼呢?原因是這樣的,以test屬性爲例,
Q_PROPERTY(qint32 test READ test WRITE setTest NOTIFY testChanged RESET unset)
這裏僅是告訴元對象系統test屬性改變時會發出testChanged信號,但是它不是實現,具體的實現需要我們自己來完成,這就需要修改setTest函數:
void CObj2::setTest(qint32 t)
{
m_test = t;
emit testChanged(t);
emit testChanged1();
}


在代碼中主動添加信號觸發語句,修改之後再運行,可以得到預期輸出:
OnTestChanged : 2
OnTestChanged1
OnTestChanged : 3
OnTestChanged1
這個例子可以說明兩點:
1 NOTIFY信號觸發需要在WRITE關聯的函數中加入觸發代碼;
2 NOTIFY信號關聯的成員函數可以支持帶一個參數,也可以不帶參數。
那麼如果NOTIFY信號聲明之後,不需要添加emit代碼就能觸發呢?這也是可以的,這種情況需要將NOTIFY和MEMBER搭配使用,在CObj2中增加一個新的屬性:
#ifndef COBJ2_H
#define COBJ2_H

#include <QObject>

class CObj2 : public QObject
{
Q_OBJECT
Q_PROPERTY(qint32 test READ test WRITE setTest NOTIFY testChanged RESET unset)
Q_PROPERTY(qint32 test1 READ test WRITE setTest NOTIFY testChanged1)
Q_PROPERTY(qint32 test2 MEMBER m_test NOTIFY testChanged3)
public:
explicit CObj2(QObject *parent = 0);
qint32 test(void);
void setTest(qint32 t);
signals:
void testChanged(qint32 t);
void testChanged1(void);
void testChanged3(qint32 t);
public slots:
void OnTestChanged(qint32 t);
void OnTestChanged1(void);
void unset(void);
private:
qint32 m_test;
};



修改實現文件如下:
#include "cobj2.h"
#include <QDebug>

CObj2::CObj2(QObject *parent) : QObject(parent)
{
m_test = 0;
connect(this, SIGNAL(testChanged(qint32)), this, SLOT(OnTestChanged(qint32)));
connect(this, SIGNAL(testChanged1()), this, SLOT(OnTestChanged1()));
connect(this, SIGNAL(testChanged3(qint32)), this, SLOT(OnTestChanged(qint32)));
}

qint32 CObj2::test(void)
{
return (m_test);
}

void CObj2::setTest(qint32 t)
{
m_test = t;
emit testChanged(t);
emit testChanged1();
}

void CObj2::OnTestChanged(qint32 t)
{
qDebug() << "OnTestChanged : " << t;
}

void CObj2::OnTestChanged1(void)
{
qDebug() << "OnTestChanged1";
}

void CObj2::unset(void)
{
m_test = 0;
}



客戶端調用:
CObj2 s;
s.setProperty("test2", QVariant(4));
運行後輸出:
OnTestChanged : 4
此時,沒有手工添加emit代碼,信號仍舊被觸發了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章