【问题标题】:How to use threads in Qt GUI如何在 Qt GUI 中使用线程
【发布时间】:2016-05-24 07:04:20
【问题描述】:

我有一个应用程序,它不断地从相机读取图像并将其显示给用户。用户可以调整曝光和阈值等不同的滑块来实时修改图像。之后我还在这张图片上做了一堆计算,这有时会使 GUI 没有响应,所以我决定使用线程来划分工作负载。

但是,我无法让它正常工作,有时我会遇到分段错误和一堆“assertion ` GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed”当滑块时的警告值已更改,或者如果我尝试保存图像(我的 GUI 中的保存按钮),GUI 有时会停止更新图像或冻结,因此您无法移动滑块或按下任何按钮。

我试图做的是在 C++ 中使用标准的 std::thread,并通过插槽将其连接到开始按钮。

QObject::connect(btnStart, SIGNAL(clicked()), this, SLOT(RunStartThread()));

void MainMenu::RunStartThread(){
std::thread t1;
t1= std::thread(&MainMenu::Start,this);
t1.detach();
}

void MainMenu::Start() {
run = true;    
    window->mngr->ReadCalibration();
    window->mngr->InitializeCameras();

    while (run) {

        window->mngr->CaptureImage();
        window->mngr->ProcessImages();            
        UpdateLabels();
      }  

window->mngr->Stop();
}

当用户更改滑块值时,他们会更改我的管理器(上面的 mngr)中 captureImage 和 ProcessImages 使用的变量。当要访问变量时,我尝试使用 std::mutex 锁定/解锁,但它没有改变任何东西。我试图在网上找到如何执行此操作的示例,但尚未找到具有连续 while 循环的示例。

我是线程方面的新手,所以请告诉我我是否以错误的方式处理这个问题。

【问题讨论】:

  • 也许你最好用QThread?
  • 希望您的 mngr 实例的功能不会直接访问任何 QT 小部件。如果您想从第二个线程(UpdateLabels()?)中实现 GUI 中的任何更改,您也必须通过信号/插槽来完成此操作!
  • 滑块的更新通过信号/槽进行管理,管理器根本无法访问 Qt 小部件。 GUI 与管理器有连接,但反之则不然。 UpdateLabels 只是从管理器中获取一些图像信息并更新 GUI 中的标签。很抱歉没有澄清。我试过研究 Qthread,但我没有找到任何关于如何做到这一点的好例子。
  • "UpdateLabels 只是从管理器获取一些图像信息并在 GUI 中更新标签" - 通过信号/插槽?
  • 不,不是通过信号/插槽。只是,当图像处理完毕后,我调用 UpdateLabels 来更改标签文本。 UpdateLabels 与滑块无关,不会引起任何问题。

标签: c++ multithreading qt


【解决方案1】:

首先对于线程间通信使用信号和槽。默认情况下,Qt 连接会在线程之间进行很好的线程跳跃,从而避免复杂的同步。

其次,您有三种使用线程的方式:QThread、QRunnable、QtConcurrent::Run(我最喜欢,因为需要最少的代码)。

如果QThread 请不要继承它!是common design mistake。

例子:

SomeClass::~SomeClass()
{
    SignalStop();
    future.result();
}

void SomeClass::RunStartThread(){
     future = QtConcurrent::run(this, &SomeClass::DoOnThread);
}

void SomeClass::DoOnThread()
{
    while (ShouldContinueToRun()) {
        QImage im1 = CaptureImage();
        emit ImageCaptured(im1);
        QImage im2 = ProcessImages(im1);            
        emit ImageProcessed(im2);
    }
    emit JobCompleted();
}

请注意QObject::connect 的最后一个参数定义了在涉及不同线程时如何执行插槽调用。请参阅用于此参数的 documentation of enumeration。
所以默认情况下,Qt 会检测是否需要线程跳跃。仔细阅读QObject::moveToThread 也应该有助于理解问题(请注意,如果对象有父对象,则不能将对象移动到不同的线程)。

【讨论】:

  • 按照您的链接,那么在您的示例中,SomeClass 是否必须从 QThreadPool 继承?宁愿期望doOnThread 是静态的并且有一个参数SomeClass* 并调用run(&doOnThread, this);。我错过了什么?
  • 如果使用opencv的imshow显示图像,我真的需要发出图像已被捕获的信号吗?确定处理,因为 updatelabels 取决于此。但是我如何将该信号连接到 updateLabels() 函数/插槽?与 JobCompleted 相同,如何将此信号连接到我的函数?
  • @Aconcagua 见this part of the docs。
  • 我的例子是第三个选项QtConcurrent::Run 所以你不需要继承任何东西(就像我写的代码简单)。在第二个选项中,您必须继承 QRunnable。 QThreadPool 是运行QRunnable 的工具,不应扩展!
  • 如果您能回答我的评论,我会将您的回答标记为已接受。如果我决定像您的示例那样在线程内发出信号,如何连接信号?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
  • 2012-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多