1. 程式人生 > >[Qt入門篇]7 Qt的屬性系統——NOTIFY和RESET

[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呢?我們定義如下程式碼:
宣告:
[cpp]  view plain  copy
  1. #ifndef COBJ2_H  
  2. #define COBJ2_H  
  3.   
  4. #include <QObject>  
  5.   
  6. class CObj2 : public QObject  
  7. {  
  8. Q_OBJECT  
  9. Q_PROPERTY(qint32 test READ test WRITE setTest NOTIFY testChanged RESET unset)  
  10. Q_PROPERTY(qint32 test1 READ test WRITE setTest NOTIFY testChanged1)  
  11. public:  
  12. explicit CObj2(QObject *parent = 0);  
  13. qint32 test(void);  
  14. void setTest(qint32 t);  
  15. signals:  
  16. void testChanged(qint32 t);  
  17. void testChanged1(void);  
  18. public slots:  
  19. void OnTestChanged(qint32 t);  
  20. void OnTestChanged1(void);  
  21. void unset(void);  
  22. private:  
  23. qint32 m_test;  
  24. };  
  25.   
  26. #endif // COBJ2_H  
  27. 實現:  
  28. #include "cobj2.h"  
  29. #include <QDebug>  
  30.   
  31. CObj2::CObj2(QObject *parent) : QObject(parent)  
  32. {  
  33. m_test = 0;  
  34. connect(this, SIGNAL(testChanged(qint32)), this, SLOT(OnTestChanged(qint32)));  
  35. connect(this, SIGNAL(testChanged1()), this, SLOT(OnTestChanged1()));  
  36. }  
  37.   
  38. qint32 CObj2::test(void)  
  39. {  
  40. return (m_test);  
  41. }  
  42.   
  43. void CObj2::setTest(qint32 t)  
  44. {  
  45. m_test = t;  
  46. }  
  47.   
  48. void CObj2::OnTestChanged(qint32 t)  
  49. {  
  50. qDebug() << "OnTestChanged : " << t;  
  51. }  
  52.   
  53. void CObj2::OnTestChanged1(void)  
  54. {  
  55. qDebug() << "OnTestChanged1";  
  56. }  
  57.   
  58. void CObj2::unset(void)  
  59. {  
  60. m_test = 0;  
  61. }  


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
  1. void CObj2::setTest(qint32 t)  
  2. {  
  3. m_test = t;  
  4. emit testChanged(t);  
  5. emit testChanged1();  
  6. }  


在程式碼中主動新增訊號觸發語句,修改之後再執行,可以得到預期輸出:
OnTestChanged : 2
OnTestChanged1
OnTestChanged : 3
OnTestChanged1
這個例子可以說明兩點:
1 NOTIFY訊號觸發需要在WRITE關聯的函式中加入觸發程式碼;
2 NOTIFY訊號關聯的成員函式可以支援帶一個引數,也可以不帶引數。
那麼如果NOTIFY訊號宣告之後,不需要新增emit程式碼就能觸發呢?這也是可以的,這種情況需要將NOTIFY和MEMBER搭配使用,在CObj2中增加一個新的屬性:
[cpp]  view plain  copy
  1. #ifndef COBJ2_H  
  2. #define COBJ2_H  
  3.   
  4. #include <QObject>  
  5.   
  6. class CObj2 : public QObject  
  7. {  
  8. Q_OBJECT  
  9. Q_PROPERTY(qint32 test READ test WRITE setTest NOTIFY testChanged RESET unset)  
  10. Q_PROPERTY(qint32 test1 READ test WRITE setTest NOTIFY testChanged1)  
  11. Q_PROPERTY(qint32 test2 MEMBER m_test NOTIFY testChanged3)  
  12. public:  
  13. explicit CObj2(QObject *parent = 0);  
  14. qint32 test(void);  
  15. void setTest(qint32 t);  
  16. signals:  
  17. void testChanged(qint32 t);  
  18. void testChanged1(void);  
  19. void testChanged3(qint32 t);  
  20. public slots:  
  21. void OnTestChanged(qint32 t);  
  22. void OnTestChanged1(void);  
  23. void unset(void);  
  24. private:  
  25. qint32 m_test;  
  26. };  



修改實現檔案如下:
[cpp]  view plain  copy
  1. #include "cobj2.h"  
  2. #include <QDebug>  
  3.   
  4. CObj2::CObj2(QObject *parent) : QObject(parent)  
  5. {  
  6. m_test = 0;  
  7. connect(this, SIGNAL(testChanged(qint32)), this, SLOT(OnTestChanged(qint32)));  
  8. connect(this, SIGNAL(testChanged1()), this, SLOT(OnTestChanged1()));  
  9. connect(this, SIGNAL(testChanged3(qint32)), this, SLOT(OnTestChanged(qint32)));  
  10. }  
  11.   
  12. qint32 CObj2::test(void)  
  13. {  
  14. return (m_test);  
  15. }  
  16.   
  17. void CObj2::setTest(qint32 t)  
  18. {  
  19. m_test = t;  
  20. emit testChanged(t);  
  21. emit testChanged1();  
  22. }  
  23.   
  24. void CObj2::OnTestChanged(qint32 t)  
  25. {  
  26. qDebug() << "OnTestChanged : " << t;  
  27. }  
  28.   
  29. void CObj2::OnTestChanged1(void)  
  30. {  
  31. qDebug() << "OnTestChanged1";  
  32. }  
  33.   
  34. void CObj2::unset(void)  
  35. {  
  36. m_test = 0;  
  37. }  



客戶端呼叫:
CObj2 s;
s.setProperty("test2", QVariant(4));
執行後輸出:
OnTestChanged : 4
此時,沒有手工新增emit程式碼,訊號仍舊被觸發了。