2012年11月25日星期日

Qt QListWidget


在 QListWidget 內增加一個新的 QListWidgetItem 時我會用 addItem(), 但刪除時卻要用 findItems() 及 delete, 例如:

QList search_result = list->findItems(targettext, Qt::MatchExactly);
int total = search_result.count();
for(int i=0; i delete search_result.at(i);
}

QT QThread


當我們使用 QT 中的 QThread class 時, 目的就是將一部份工作放到另一條 thread 上. 當完成編程後並執行程序時會發生 Qt 回報要用的 object 不在此 thread 上.
經過網上"爬文"及多次實作後, 我的見解是 QT 中的 QThread class 的作用好像網絡上的 switch 一樣:

1.
   - 啟動一條新 thread (現在我稱為 "子thread")
   - 作為產生 QThread 的 thread (現在我稱為 "主thread") 與 "子thread" 的資料交換介面
 
   例如:
   class testThread : public QThread {
       Q_OBJECT
     
   private:
       MyObject       *a, *b;
         
   protected:
  void run();
 
   public:
       explicit testThread(QObject *parent = 0);
     
       void startthread();
       int getValue();
   }
 
   int testThread::getValue() {
       return this->a->value;
   }
 
   void testThread::startthread() {
        // start a new thread
        this->start();
   }
 
   void testThread::run() {
        this->b = new MyObject();
       
        while(1) {
            this->a->value = this->b->value;
            b->inc();
        }
   }
 
   void main()  
   {
       testThread  test;
     
       test.startthread();
       .....
       cout << test.getValue();
   }
 
   在以上的程序中, 你會發現在 run() 中會出以下代碼:
   this->a->value = this->b->value;
 
   這是因為 object "this->b" 和 "this->a" 是在不同的 thread 內:
   - "this->b" 在 "子thread" (沒錯, 所有在 run() 內所產生的物件都是在 "子thread")
   - "this->a" 在 "主thread"
 
   所以,對於外部讀取資料要求時,我們實作時需要間接的方式來讀取"子thread"的物件, 否則會出現報警.
 
2. 對於讀取資料, 在 Qt 內我們會經常用 Signal and Slot 功能. 但我們要留意 "子thread" 有自己的 event loop. 所以我認為做法是:
   - 準備 "子thread" 的 event loop class (sub-class QEventLoop)
   - 在 run() 中產生 "子thread" 的 event loop 物件
   - 將 "子thread" 的 event loop 的 Signal 接到 QThread 的 Slot (main event loop)
   - 再將 QThread 的 Signal 接到其他的 Slot
 
   簡單來說 QThread 的 Signal 及 Slot 的作用就如網絡上的 switch/hub, 將 "子thread" 和 "主thread" 連在一起.
 
   例如:
   class threadEventLoop : public QEventLoop {
       Q_OBJECT
       .....
   private:
       int cnt;
     
   signals:
       void newValue(int value);
 
   public slots:
       void time_out();  
   }
 
   void threadEvent::time_out() {      
       cnt++;
       emit newValue(cnt);
   }
 
   class testThread : public QThread {
       Q_OBJECT
       ....
   protect:
       void run();
     
   signals:
       void newValue(int value);
     
   public slots:
       void receive_newValue(int value);
     
   }
     
   void testThread::run() {
       int b = 0;
     
       threadEventLoop loop;
       QTimer *timer = new QTimer();
     
       QObject::connect(timer, SIGNAL(timeout()), &loop, SLOT(time_out()));
       // 將 "子thread" 的 event loop 的 Signal 接到 QThread 的 Slot
       QObject::connect(&loop, SIGNAL(newValue(int)), this, SLOT(receive_newValue(int)));
     
       timer->start(1000);
     
       // start event loop
       loop.exec();      
   }
 
   void testThread::receive_newValue(int value) {
       // 將 "子thread" 的資料傳給其他 thread
       emit newValue(value);
   }

Qt MetaType


在 Signal and Slot 功能中, 如需要傳送自行定義的資料結構 (enum, struct, 等等), 在建立 connection 前我們先需要用 qRegisterMetaType 來註冊有關資料結構

Qt Drap and Drop


當我們在 Drap and Drop 中用到 QMimeData 時, QMimeData 的 formats() 傳回的值需要和 QAbstractItemModel 的 mimeTypes() 傳回的值一致