gpt4 book ai didi

c++ - 在工作 GUI 示例中使用 Controller 和 QT Worker

转载 作者:行者123 更新时间:2023-11-30 02:16:59 25 4
gpt4 key购买 nike

我创建了一个最小的 QT GUI 示例,以根据 QThread 5.12 documentation 中推荐的方法从工作线程更新小部件。 .

QThread 5.12 documentation 中所述,Worker 类(具有可能很长的 void doWork(const QString &parameter) 方法是:

class Worker : public QObject
{
Q_OBJECT

public slots:
void doWork(const QString &parameter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}

signals:
void resultReady(const QString &result);
};

对应的Controller类是:

class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};

QThread 的子类不同,文档中显示的方法显示了使用 Controller 和扩展 QObject 而不是扩展 QThread 和重写 QThread::run 方法,但是它没有说明如何在真实示例的上下文中使用这些方法。

我需要使用 QT Worker 线程来使用计时器更新 GUI 上的小部件。

我还需要能够使用不同的参数停止和重新启动/重新启动该线程,但我在如何正确执行此操作方面遇到了一些问题。指示通过 Controller 和 Worker 执行此操作的首选方法,但连接逻辑有点困惑。

我需要帮助的地方是如何将计时器正确地集成到我的工作线程中,以及如何在当前工作线程完成或被中断并重新启动时停止并重新启动替代工作线程。

我的工作代码由以下文件组成。

Controller .h

#pragma once

// SYSTEM INCLUDES
#include <QObject>
#include <QThread>

// APPLICATION INCLUDES
#include "Worker.h"

// DEFINES
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STRUCTS
// TYPEDEFS
// FORWARD DECLARATIONS

class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller(/*MainWindow* mainWindow*/) {
auto worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &) {
// how do I update the mainWindow from here
}
signals:
void operate(int);
};

worker .h

#pragma once

// SYSTEM INCLUDES
#include <QTimer>
#include <QObject>
#include <QEventLoop>

// APPLICATION INCLUDES
#include "Worker.h"

// DEFINES
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STRUCTS
// TYPEDEFS
// FORWARD DECLARATIONS

class Worker : public QObject
{
Q_OBJECT

public slots:
void doWork(int count) {
QString result = "finished";
// Event loop allocated in workerThread
// (non-main) thread affinity (as moveToThread)
// this is important as otherwise it would occur
// on the main thread.
QEventLoop loop;
for (auto i=0; i< count; i++) {
// wait 1000 ms doing nothing...
QTimer::singleShot(1000, &loop, SLOT(quit()));
// process any signals emitted above
loop.exec();

emit progressUpdate(i);
}
emit resultReady(result);
}
signals:
void progressUpdate(int secondsLeft);
void resultReady(const QString &result);
};

MainWindow.h - 我需要在此处添加一个 Controller 成员。我还在此处添加了一个 updateValue 插槽,我希望在其中更新 GUI。不幸的是,我不知道如何让 Controller 或工作人员连接来自线程的信号以更新此插槽。

#pragma once

// SYSTEM INCLUDES
#include <memory>
#include <QMainWindow>

// APPLICATION INCLUDES
// DEFINES
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STRUCTS
// TYPEDEFS
// FORWARD DECLARATIONS
namespace Ui {
class MainWindow;
}

class Controller;

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();

private slots:
void on_pushButton_clicked();
void updateValue(int secsLeft);

private:
Ui::MainWindow *ui;
std::unique_ptr<Controller> mpController;
};

主窗口.cpp -

#include <QThread>

#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "Controller.h"

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, mpController(std::make_unique<Controller>())
{
ui->setupUi(this);
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::on_pushButton_clicked()
{
emit mpController->operate(100);
}

void MainWindow::updateValue(int secsLeft)
{
ui->secondsLeft->setText(QString::number(secsLeft));
}

最后是 main.cpp

#include "MainWindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();

return a.exec();
}

我基本上需要有关如何使用集成在我的 GUI 中的 QT 线程 Controller /工作器的帮助和解释。

最佳答案

我会尽力回答您在问题中提出的所有问题:

  1. I don't know how to get the controller or the worker to connect a signal from the thread to update this slot.

你自己几乎是对的。

您的 Worker 位于 Controller 的事件循环中:

+--GUI-thread--+ (main event loop)
| MainWindow, |
| Controller --o-----> +--QThread--+ (own event loop in ::exec())
+--------------+ | Worker |
+-----------+

Controller 和 Worker 之间的通信必须通过信号槽连接进行。在 MainWindow 和 Controller 之间,信号有助于将依赖性降至最低。

您可以将 Controller 想象成一种中继:来自 MainWindow 的命令通过 Controller 转发给 Worker。 Worker 的结果通过 Controller 转发给任何感兴趣的人。

为此,您可以简单地在 Controller 中定义信号:

class Controller : public QObject
{
//...
signals:
void SignalForwardResult(int result);
};

然后代替

    connect(worker, &Worker::resultReady, this, &Controller::handleResults);

使用新信号:

    connect(worker, &Worker::resultReady, this, &Controller::SignalForwardResult);
// Yes, you can connect a signal to another signal the same way you would connect to a slot.

并在您的 MainWindow 构造函数中:

//...
ui->setupUi(this);
connect(mpController, &Controller::SignalForwardResult, this, &MainWindow::displayResult);

同样适用于 Worker::progressUpdate() -> Controller::SignalForwardProgress() -> MainWindow::updateValue()


  1. how to stop and restart a replacement worker when the current one has either finished or been interrupted and restarted.

或者为每个任务创建一个新的工作人员或者使用可以对新任务请求使用react的持久性工作人员。

  • 您通过将任务发送给工作人员 ::doWork() 函数来启动任务。
  • 当长时间的工作完成时,任务自行结束。您通过工作人员的 resultReady 信号收到通知。
  • 只有通过干预才能取消任务
    • 如果您确实有一个 QTimer,您可以使用一个 cancel() 槽,因为它会在下一次超时之前在线程的事件循环中被调用。
    • 如果您有一个长时间运行的计算,您需要共享一些您从计算方法内部读取并从 GUI 线程设置的标记。我通常为此使用共享的 QAtomicInt 指针,但共享的 bool 通常也足够了。

请注意,当方法在线程上运行时,该线程的事件循环会被阻塞,并且在该方法完成之前不会收到任何信号。

不要使用 QCoreApplication::processEvents() 除非你真的知道你在做什么。 (并期望你不这样做!)


  1. how to properly integrate the timer in my worker thread

你不应该。

  • 我猜你使用后台线程是因为有太多的工作要做,或者你需要阻塞等待太长时间以至于阻塞 GUI,对吧? (如果没有,请考虑不使用线程,为您省去很多麻烦。)

  • 如果你需要一个计时器,让它成为Worker的成员,并将它的parentObject设置为Worker实例。这样,两者将始终具有相同的线程关联。然后,将它连接到一个插槽,如 Worker::timeoutSlot()。您可以在那里发出结束信号。

关于c++ - 在工作 GUI 示例中使用 Controller 和 QT Worker,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54172976/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com