2013-02-07 23 views
2

我正在爲一些計算密集型任務(機器視覺)開發C++類庫。如何從非Qt C++庫類向Qt GUI提供反饋?

// I am a part of a Qt-agnostic library 
class Cruncher 
{ 
    /* ... */ 
public: 
    void doStuff(); 
}; 

然後有一個使用該庫的Qt GUI。我創建一個工作線程從庫中調用繁重工作程序:

// I am a part of a Qt-based GUI which utilizes the library 
class Worker : public QThread 
{ 
    /* ... */ 
protected: 
    virtual void run() 
    { 
     /* ... */ 
     Cruncher c; 
     for (int i = 0; i < count; ++i) 
      c.doStuff(); // takes some time, and while it's working 
         // it should communicate status changes which should 
         // become visible in the GUI 
    } 
}; 

現在裏面doStuff()很多情況,我想提供一些反饋,對正在發生的事情,而無需等待用戶爲doStuff()返回。首先,可能需要一些更好的進度報告,而不是在每次調用doStuff()之後增加一個步驟。此外,doStuff()可能會遇到非關鍵性故障,因此它可以繼續執行部分工作,但是當Cruncher正在工作時(並且Worker正在忙於進行調用),我希望在GUI中出現一條消息做東西())。

我想庫保持Qt獨立,所以我不願意爲Cruncher添加信號和插槽。任何其他方式使它能夠向GUI提供反饋,以在其不是Qt類時報告其工作?

我正在考慮創建一個QTimer,它會在Worker正在運行時以固定的時間間隔輪詢Cruncher的某些「狀態」和「errorMsg」成員,但這看起來非常不理想。

+0

你已經擁有的QThread那裏,所以它不是很Qt的獨立... – hyde

+0

的QThread的是GUI,而不是在圖書館。我希望圖書館(一個可以用於其他非互動,非GUI程序的獨立項目)保持Qt獨立。 – neuviemeporte

回答

3

我發佈了我自己的答案,因爲儘管我拿@尼姆的建議,我想讓答案稍微冗長一點,因此如果有人應該有同樣的問題更有用。

我創建在庫中的消息調度的骨架:

// doesn't need to know about Qt 
class MessagePort 
{ 
public: 
    virtual void message(std::string msg) = 0; 
}; 

接着,我添加了一個手柄到該對象到計算器和與到消息)偶爾調用(五香doStuff():

// now with Super Cow powers! 
class Cruncher 
{ 
protected: 
    MessagePort *msgPort_; 

public: 
    Cruncher(MessagePort *msgPort) : msgPort_(msgPort) {} 
    void doStuff() 
    { 
     while(...) 
     { 
      /*...*/ 
      msgPort_->message("Foo caused an overload in Bar!"); 
     } 
    } 
}; 

最後,我製作使用所有必要的Qt善良GUI內MessagePort的實現:

class CruncherMsgCallback : public QObject, public MessagePort 
{ 
    Q_OBJECT 

public: 
    CruncherMsgCallback() : QObject(), MessagePort() 
    { 
     connect(this, SIGNAL(messageSignal(const QString &)), 
       GUI, SLOT(messageShow(const QString &)), 
       Qt::QueuedConnection); 
    } 

    virtual void message(std::string msg) 
    { 
     emit messageSignal(QString::fromStdString(msg)); 
    } 

signals: 
    void messageSignal(const QString &msg); 
}; 

最後,當勞動者創造排排坐的實例,這也給了它一個指向工作MessagePort:

class Worker 
{ 
protected: 
    virtual void run() 
    { 
     CruncherMsgCallback msgC; 
     Cruncher c(&msgC); // &msgC works as a pointer to a 
          // generic MessagePort by upcasting 
     c.doStuff(); // Cruncher can send messages to the GUI 
        // from inside doStuff() 
    } 
}; 
+1

+1,我也有一份日常工作.. :)如果它工作,並且清楚地分離了組件,並且意圖很明確,這是一個很好的解決方案... – Nim

+0

@Nim:感謝這個想法,試着自己找出想法。 :) – neuviemeporte

1

使用回調函數(類)等,並在建設過程中通過。你需要報告的事情,通過回調報告。

+0

好的,但是回調應該是GUI還是庫的一部分?如果是前者,我該如何避免在庫中包含#包括GUI原型?如果是後者,如何在不知道任何關於GUI的情況下與GUI進行通信?你能否提供一個(僞)代碼示例? – neuviemeporte

1

您可以安全地從run()方法發出信號,我認爲這是將信息從工作線程傳遞到主線程的最佳方式。只需將信號添加到QThread子類(避免添加插槽,如果您完全不確定QThread線程是如何工作的)。

更好地使這些信號的連接明確排隊,以避免問題。儘管默認的自動連接類型也應該工作,並且排隊信號發出,但我認爲最好在這種情況下明確表示。實際上,直接信號也應該如此工作,但是您必須自己處理線程安全問題,而不是讓Qt爲您處理它,而且您無法連接到使用任何只能在主服務器上運行的QtGui類的插槽線程,所以最好堅持排隊連接。

要將簡單的信息傳遞給run()方法,並且如果不需要立即反應,可以使用幾個共享的QAtomicInt變量或類似標記的東西,工作線程在方便時進行檢查。稍微複雜一些的方法,仍然需要輪詢,是共享數據結構,你用互斥鎖保護。更復雜的通信方式將涉及某種消息隊列(就像Qt在主線程的事件循環中使用,當您向該方向發送信號時)。

+0

問題是,run()方法被doStuff()調用阻塞,並且在doStuff()的當前調用終止之前不會變爲空閒狀態。我需要在doStuff()運行時發送消息。 – neuviemeporte

+0

@neuviemeporte好吧,doStuff()如何獲取消息?那裏有哪些線程間通信操作/ funcionts /庫?在你的問題中指定,也許。 – hyde

+0

doStuff()是消息的來源;當它決定一些不可能的事情時,它會跳過一些工作。它的一部分庫可以訪問所有libstC++和任何其他庫,而不是Qt。抱歉不清楚。 – neuviemeporte