从后台工作线程修改 Qt GUI

2024-10-21 09:14:00
admin
原创
318
摘要:问题描述:我在 Qt 中工作,当我按下按钮 GO 时,我需要不断地向网络发送包并使用收到的信息修改界面。问题是我while(1)在按钮中有一个,所以按钮永远不会完成,所以界面永远不会更新。我想在按钮中创建一个线程并将代码放在while(){}那里。我的问题是如何从线程修改界面?(例如,如何从线程修改文本框?解...

问题描述:

我在 Qt 中工作,当我按下按钮 GO 时,我需要不断地向网络发送包并使用收到的信息修改界面。

问题是我while(1)在按钮中有一个,所以按钮永远不会完成,所以界面永远不会更新。我想在按钮中创建一个线程并将代码放在while(){}那里。

我的问题是如何从线程修改界面?(例如,如何从线程修改文本框?


解决方案 1:

关于 Qt 的重要一点是您必须仅从 GUI 线程(即主线程)使用 Qt GUI。

这就是为什么正确的方法是从工作线程通知主线程,主线程中的代码实际上会更新文本框、进度条或其他东西。

我认为,实现此目的的最佳方法是使用 QThread 而不是 posix 线程,并使用 Qt信号在线程之间进行通信。这将是您的工作程序,是以下程序的替代品thread_func

class WorkerThread : public QThread {
    void run() {
        while(1) {
             // ... hard work
             // Now want to notify main thread:
             emit progressChanged("Some info");
        }
    }
    // Define signal:
    signals:
    void progressChanged(QString info);
};

在您的小部件中,定义一个与.h 中的信号具有相同原型的插槽:

class MyWidget : public QWidget {
    // Your gui code

    // Define slot:
    public slots:
    void onProgressChanged(QString info);
};

在.cpp中实现该函数:

void MyWidget::onProgressChanged(QString info) {
    // Processing code
    textBox->setText("Latest info: " + info);
}

现在,在您想要生成线程的地方(单击按钮):

void MyWidget::startWorkInAThread() {
    // Create an instance of your woker
    WorkerThread *workerThread = new WorkerThread;
    // Connect our signal and slot
    connect(workerThread, SIGNAL(progressChanged(QString)),
                          SLOT(onProgressChanged(QString)));
    // Setup callback for cleanup when it finishes
    connect(workerThread, SIGNAL(finished()),
            workerThread, SLOT(deleteLater()));
    // Run, Forest, run!
    workerThread->start(); // This invokes WorkerThread::run in a new thread
}

连接信号和槽后,emit progressChanged(...)在工作线程中发射槽将向主线程发送消息,然后主线程将调用连接到该信号的槽onProgressChanged

我还没有测试过代码,所以如果我哪里错了,请随时提出修改建议

解决方案 2:

因此,该机制是您无法从线程内部修改小部件,否则应用程序将崩溃并出现以下错误:

QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
Segmentation fault

为了解决这个问题,您需要将线程工作封装在一个类中,例如:

class RunThread:public QThread{
  Q_OBJECT
 public:
  void run();

 signals:
  void resultReady(QString Input);
};

其中 run() 包含您想要做的所有工作。

在您的父类中,您将有一个生成数据的调用函数和一个 QT 小部件更新函数:

class DevTab:public QWidget{
public:
  void ThreadedRunCommand();
  void DisplayData(QString Input);
...
}

然后调用线程,你将连接一些插槽,这

void DevTab::ThreadedRunCommand(){
  RunThread *workerThread = new RunThread();
  connect(workerThread, &RunThread::resultReady, this, &DevTab::UpdateScreen);
  connect(workerThread, &RunThread::finished, workerThread, &QObject::deleteLater);
  workerThread->start();  
}

连接函数有4个参数,参数1为原因类,参数2为该类中的信号。参数3为回调函数的类,参数4为该类中的回调函数。

然后你将在子线程中有一个函数来生成数据:

void RunThread::run(){
  QString Output="Hello world";
  while(1){
    emit resultReady(Output);
    sleep(5);
  }
}

然后,您将在父函数中有一个回调来更新小部件:

void DevTab::UpdateScreen(QString Input){
  DevTab::OutputLogs->append(Input);
}

然后,当您运行它时,父级中的小部件将在线程中每次调用 emit 宏时更新。如果连接函数配置正确,它将自动获取发出的参数,并将其存储到回调函数的输入参数中。

工作原理:

  1. 我们初始化课程

  2. 我们设置了插槽来处理线程完成时发生的事件以及如何处理“返回”的emit数据,因为我们无法以通常的方式从线程返回数据

  3. 然后我们通过调用来运行该线程(该调用被硬编码到 QThread 中),QT在类中->start()查找硬编码名称memberfunction.run()

  4. 每次emit在子线程中调用 resultReady 宏时,它都会将 QString 数据存放到线程间某个共享数据区域中

  5. QT 检测到 resultReady 已触发,并向您的函数 UpdateScreen(QString) 发出信号,以接受从 run() 发出的 QString 作为父线程中的实际函数参数。

  6. 每次触发 emit 关键字时都会重复此操作。

本质上,这些connect()函数是子线程和父线程之间的接口,以便数据可以来回传输。

注意: resultReady() 不需要定义。可以将其视为 QT 内部存在的宏。

解决方案 3:

您可以使用invokeMethod()或信号和槽机制,基本上有很多例子,比如如何发出信号以及如何在槽中接收信号。但是,InvokeMethod看起来很有趣。

下面是一个示例,展示了如何从线程更改标签的文本:

//文件1.cpp

QObject *obj = NULL; //global 
QLabel *label = new QLabel("test");
obj = label;   //Keep this as global and assign this once in constructor.

接下来您可以在 WorkerThread 中执行以下操作:

//file2.cpp(即线程)

extern QObject *obj;
void workerThread::run()
{
     for(int i = 0; i<10 ;i++
     {
         QMetaObject::invokeMethod(obj, "setText",
                                Q_ARG(QString,QString::number(i)));
     }
     emit finished();
}

解决方案 4:

你启动线程并将一些指针传递给线程函数(在 posix 中,线程函数具有签名void (thread_func)(void),在 windows 下也是如此) - 并且你完全可以自由地将指针发送到你自己的数据(结构或其他东西)并从线程函数中使用它(将指针转换为适当的类型)。好吧,内存管理应该是经过深思熟虑的(这样你就不会泄漏内存也不会使用线程中已经释放的内存),但这是一个不同的问题

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2079  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1459  
  建筑行业正处于数字化转型的关键时期,建筑产品生命周期管理(PLM)系统的实施对于提升项目效率、质量和协同性至关重要。特别是在 2025 年,基于建筑信息模型(BIM)的项目进度优化工具成为众多建筑企业关注的焦点。这些工具不仅能够整合项目全生命周期的数据,还能通过精准的分析和模拟,为项目进度管理提供强大支持。BIM 与建...
plm是什么软件   0  
  PLM系统开发的重要性与现状PLM(产品生命周期管理)系统在现代企业的产品研发、生产与管理过程中扮演着至关重要的角色。它贯穿产品从概念设计到退役的整个生命周期,整合了产品数据、流程以及人员等多方面的资源,极大地提高了企业的协同效率和创新能力。通过PLM系统,企业能够实现产品信息的集中管理与共享,不同部门之间可以实时获取...
国产plm软件   0  
  PLM(产品生命周期管理)系统在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和技术的飞速发展,企业对PLM系统的迭代周期优化需求日益迫切。2025年敏捷认证对项目管理提出了新的要求,其中燃尽图作为一种强大的可视化工具,在PLM系统迭代周期优化中有着广泛且重要的应用。深入探讨这些应用,对于提升企业的项...
plm系统主要干什么的   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用