繁体   English   中英

如何从非Qt C ++库类向Qt GUI提供反馈?

[英]How can I provide feedback from a non-Qt C++ library class to a Qt GUI?

我正在为一些计算密集型任务(机器视觉)开发C ++类库。

// 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工作时(工作人员当前正忙于对它的调用)在GUI中出现一条消息。做东西())。

我希望该库保持与Qt无关,因此我不愿意向Cruncher添加信号和插槽。 是否有其他方法可以使它在不是Qt类时向GUI提供反馈以报告其工作?

我正在考虑创建一个QTimer,它会在Worker运行时以固定的时间间隔轮询Cruncher的某些“状态”和“ errorMsg”成员,但这似乎非常不理想。

我发布自己的答案,因为尽管我接受了@Nim的建议,但我希望答案更加冗长,因此如果有人应该遇到相同的问题,则答案会更有用。

我在库中创建了消息调度程序的框架:

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

接下来,我将此对象的句柄添加到Cruncher,并通过偶尔调用message()来添加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);
};

最后,当Worker创建Cruncher的实例时,它还会为它提供一个指向正常工作的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()
    }
};

使用回调函数(类)等,并在构造过程中将其传递。 您需要报告的事情,通过该回调报告。

您可以安全地从run()方法发出信号,我认为这是将信息从工作线程传递到主线程的最佳方法。 只需将信号添加到QThread子类中即可(如果完全不确定QThread线程的工作方式,请避免添加插槽)。

最好使来自这些信号的连接明确地排队,以避免出现问题。 尽管默认的自动连接类型也应该起作用,并且会发出排队信号,但是在这种情况下,最好将其明确。 实际上,直接信号也应该这样工作,但是您必须自己照顾线程安全,而不是让Qt为您处理它,并且您无法连接到使用仅在主线程中工作的任何QtGui类的插槽。线程,因此最好坚持排队的连接。

要将简单的信息传递给run()方法,并且如果不需要立即QAtomicInt反应,则可以使用一些共享的QAtomicInt变量或类似的标记作为标志,辅助线程在方便时进行检查。 仍然需要轮询的稍微复杂一点的方法是,使用互斥锁保护共享的数据结构。 与该方向通信的更复杂方式将涉及某种消息队列(就像Qt在向该方向发出信号时在主线程的事件循环中使用的那样)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM