[Qt入門篇]7 Qt的屬性系統——NOTIFY和RESET
阿新 • • 發佈:2018-11-04
接上一篇,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呢?我們定義如下程式碼:
宣告:
[cpp] view plain copy
- #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函式:
[cpp] view plain copy
- 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中增加一個新的屬性:
[cpp] view plain copy
- #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;
- };
修改實現檔案如下:
[cpp] view plain copy
- #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程式碼,訊號仍舊被觸發了。