5

我的应用程序有一个简单的 GUI = 我必须为我的数学函数设置一些输入参数,然后单击“计算”按钮,然后函数启动。数学函数是用纯 C 编写的,所以没有对象,只有函数。

它看起来像这样:

#include "mymath.h"

class myMath : public QMainWindow
{
    Q_OBJECT

    // ...
    void compute();
};

void myMath::compute()
{
   //get parameters from gui
   call_C_fun();
   // save results to GUI
}

这段代码的主要问题是,当我单击“计算”时(它进行了大量计算,最多需要 5 分钟左右)它会挂起我的 GUI,所以我不能做任何其他事情(我什至看不到我的 GUI,窗口在计算运行时“冻结”。函数完成后,它在 QLabels 上打印结果,GUI 再次“活着”。我该如何解决这个问题?我不想要我的当计算需要时间时,GUI 将被“冻结”。有什么想法吗?我想过QThread- 但我在线程方面有点新意,所以请为新手提供简单易懂的答案:)

4

5 回答 5

6

将计算分解到另一个线程,如下所示: -

// This is the object that will run the computation on a different thread
class Computation : public QObject
{
signals:
    void Finished();
public slots:
    void Compute();
};

然后在主窗口类中创建另一个线程并开始运行:-

class MyMath : public QMainWindow
{
    public:
        void StartComputation();
};


MyMath::StartComputation()
{
    QThread* pThread = new QThread;
    Computation* pComputation = new Computation;

    // move computation to the new thread
    pCompuation->moveToThread(pThread);

    // Note Qt5 connection style    

    // ensure the computation starts when the thread starts
    connect(pThread, &QThread::started, pComputation, &Computation::Compute); 
    // when computation is finished, quit the thread
    connect(pComputation, &Compute::Finished, pThread, &QThread::quit);
    // have the thread tidy up when finished
    connect(pThread, &QThread::finished, pThread, &QThread::deleteLater);
    // start it!
    pThread->start();
}
于 2013-09-30T14:26:15.427 回答
3

尝试使用内置的 QtConcurrent命名空间,而不是滚动您自己的线程。它更容易、优化且不易出错。无需担心将内容移动到其他线程并使用互斥锁执行显式同步。

QtConcurrent::run使用内部线程池来运行您的任务并返回一个QFuture,您可以使用QFutureWatcher和信号和插槽与之交互。名称可能听起来很复杂,但代码非常简单:

class MyMath : public QObject 
{
    Q_OBJECT
public:

    explicit MyMath(QObject* parent) : QObject(parent) {
        this->watcher = new QFutureWatcher(this);
        connect(this->watcher, SIGNAL(finished()),
                this, SLOT(computationFinished()));
    }

public slots:

    void compute() {

        // This one is called by the user. Retrieve
        // the computation arguments (whatever they
        // may be), start the computation using 
        // QtConcurrent::run and prepare to receive
        // the results. 
        // 
        // Maybe update the UI to tell the user that 
        // the computationis being performed?

        Args args = getSomeArgs();
        QFuture<Result> res = QtConcurrent::run(this, &MyMath::internalCompute, args);
        this->watcher->setFuture(res);
    }

    void computationFinished() {
        // The computation is done. Update the GUI
        // to display the results. Done.
    }

private:

    QFutureWatcher<Result>* watcher;

    Args getSomeArgs() const {
        // return arguments needed by compute.
    }

    Result internalCompute(Args args) const {
        // perform very expensive math.
    }
};

更新。在 Merlin069 的评论之后,我认为建议使用 QtConcurrent 提供的更复杂的操作是不明智的。

于 2013-09-30T14:51:38.680 回答
2

Qt GUI 数学应用程序在计算时挂起 GUI

我的应用程序有一个简单的 GUI ......

这段代码的主要问题是,当我单击“计算”时(它进行了大量计算,最多需要 5 分钟左右)它会挂起我的 GUI,所以我不能做任何其他事情(我什至看不到我的 GUI,窗口在计算运行时“冻结”。函数完成后,它在 QLabels 上打印结果,GUI 再次“活着”。我该如何解决这个问题?我不想要我的当计算需要时间时,GUI 将被“冻结”。有什么想法吗?我想过 QThread - 但我在线程方面有点新意,所以请为新手提供简单易懂的答案:)

看来您的程序(GUI)永远不会挂起。它使用“compute()”函数的“call_C_fun()”继续处理。但是 GUI 不是响应式的。请参阅http://doc.qt.digia.com/qq/qq27-responsive-guis.html上的保持 GUI 响应的参考。GUI 在长时间操作中反复出现的冻结问题可以通过多种方法进行处理。但最简单的方法是手动事件处理

要使用手动事件处理,必须在循环内定期调用QCoreApplication::processEvents() 。

以下示例显示了如何工作:

call_C_fun(){
....
while(Your_loop_condition) {
    ....
    QCoreApplication::processEvents();
    if (!your_compute_button->isChecked()) {
        //"Aborted"
        return;
    }
}
....
}
于 2013-10-08T08:46:59.493 回答
2

在您的情况下,线程将是一种解决方案。您的计算将在一个单独的线程中运行,然后允许您的 GUI 在计算您需要的任何内容时做出响应。

根据我的经验,这对于 GUI 来说是很常见的。GUI 有自己的线程,您的逻辑使用自己的线程。

您可以使用非常简单的模式来处理这个问题,来自 GUI 的信号启动工作人员,来自工作人员的信号触发 GUI 中的更新。

您需要注意通常的线程安全问题。我强烈建议你学习一些关于线程的知识,以便更好地掌握你在做什么。它还可以帮助您了解在使用线程代码时可能会观察到的奇怪行为(这对于不习惯它的人来说并不总是微不足道的)。

我还建议您阅读QThread 文档,该文档很好地解释了如何在 Qt 中实现线程代码(不过,比起 Qthread 的子类,更喜欢单独的工作线程)。看看QMutexQMutexLocker,你可能需要它们。

于 2013-09-30T13:50:19.867 回答
1

作为完整QThread解决方案的替代方案,您还可以使用QEventLoop创建一个单独的事件循环来运行您的计算,以保持主事件循环(托管 UI)免费。它没有那么灵活,QThread但它们的实施速度非常快。

于 2013-09-30T14:25:17.270 回答