首页 > 解决方案 > 如何从 std::thread 更改 GUI?

问题描述

首先,我尝试使用setVisible()fromthread

有一个事件:

void MainWindow::OnShow(){
    // Start OnShow actions
    ui->LoadingBox->setVisible(true);
    std::thread dThread(OnShow_threaded, ui, &(this->settingsMap));
    dThread.join();
}

有一个功能OnShow_threaded

void OnShow_threaded(Ui::MainWindow *ui, std::unordered_map<QString,QString> *settingsMap){

    // Connect to server
    bool hasInternet = false;
   
    // If app doesn't have Internet access -> show offline mode
    if (!hasInternet) {
        ui->SettingsLabel->setVisible(true);
    }
}

编译带有错误的静态程序集时程序崩溃:

QCoreApplication::sendEvent 中的 ASSERT 失败:“无法向其他线程拥有的对象发送事件。当前线程 0x0x36c56540。在线程 0x0x341c2fa0 中创建了接收器 'WarningMsg'(类型为 'QGroupBox')”,文件 kernel\qcoreapplication.cpp,行558

在线上:ui->SettingsLabel->setVisible(true);

同时,动态链接时也不会出现这样的错误。

你可以在GitHub 上找到完整的项目


其次,我尝试使用事件。

有一个功能OnShow_threaded

void OnShow_threaded(MainWindow* mw, Ui::MainWindow *ui, std::unordered_map<QString,QString> *settingsMap){
    // Connect to server
    bool hasInternet = false;

    // If app doesn't have Internet access -> show offline mode
    if (!hasInternet) {
        MyEvent* event = new MyEvent(EventTypes::InternetConnectionError);
        QCoreApplication::postEvent(mw, event);
        //delete event;
        //delete receiver;
    }
}

有一个事件类:

#ifndef EVENTS_HPP
#define EVENTS_HPP

#include <QEvent>
#include <QString>

enum EventTypes {
    InternetConnectionError,
    Unknown
};

class MyEvent : public QEvent
{
public:
  MyEvent(const EventTypes _type) : QEvent(QEvent::User) {_localType = _type;}
 ~MyEvent() {}

  auto localType() const {return _localType;}


private:
  int _localType;
};

#endif // EVENTS_HPP

有一个事件处理程序:

void MainWindow::events(QEvent *event)
{
    if (event->type() == QEvent::User)
      {
        MyEvent* postedEvent = static_cast<MyEvent*>(event);

        if (postedEvent->localType() == EventTypes::InternetConnectionError){
            ui->WarningMsg->setVisible(true);
            ui->SettingsLabel->setVisible(true);
        }
    }
}

传递参数:

void MainWindow::OnShow(){
    // Start OnShow actions
    ui->LoadingBox->setVisible(true);
    std::thread dThread(OnShow_threaded, this, ui, &(this->settingsMap));
    dThread.detach();
}

有一个 mainwindows hpp 文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include <QMovie>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QObject>
#include <QMessageBox>
#include <QStandardPaths>
#include <QDir>
#include <QFile>
#include <QCoreApplication>
#include <QSaveFile>
#include <QProcess>

#include <thread>
#include <chrono>
#include <unordered_map>
#include <iostream>
#include <fstream>
#include <cstdlib>

#include "settings.hpp"
#include "events.hpp"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    void OnShow();

private slots:
    void SettingsLabelPressed();

    void on_CloseMsgButton_clicked();

    void on_Settings_SaveButton_clicked();

    void on_Settings_UseTranslation_stateChanged(int arg1);

protected:

    void events(QEvent* event);

private:
    Ui::MainWindow *ui;
    std::unordered_map<QString,QString> settingsMap;
};

void OnShow_threaded(MainWindow* mw, Ui::MainWindow *ui, std::unordered_map<QString,QString> *settingsMap);

#endif // MAINWINDOW_H

但事件不执行。
我做错什么了?
以及如何从另一个线程正确更改 GUI?


З.Ы. 对不起我的英语,我来自俄罗斯......


标签: c++qtuser-interfaceqt5c++17

解决方案


在 Qt 中,与许多其他 GUI 框架一样,GUI 只能从主线程更新。这意味着如果您想从另一个线程更新 GUI,您必须将其传达给主线程,而主线程又会更新 GUI。

有关更多详细信息,请参阅这些文章:


其他语言的附加资源:Правильная работа с потоками в Qt


推荐阅读