记录QT运行过程中某些控件样式突然失效问题和解决办法

问题:

程序一开始启动时窗口正常显示, 但在运行一段时间后出现窗口样式失效问题。

经排查主要原因是在非GUI线程中修改UI控件属性(设置控件内容(setText)、宽高(setWidth/setHeight)、是否禁用(setEnable)等等)导致的样式失效。尽管在子线程中修改的控件和出现问题的消息窗口完全没有关系。

通过QDialog自定义的消息窗口:

正常消息窗口:

样式失效异常消息窗口:

所以,不管任何情况下,尽量不在非GUI线程访问UI控件,如果确实有必要,使用信号槽机制,通过发消息方式让子线程和GUI线程通讯。

 

例:

有一个Dialog窗口, 想通过线程修改按钮(pushButton)的禁用/启用, 虽然可以直接在线程中访问,运行也不会报错,但是尽量避开这样做,后期等项目越做越大可能会出现莫名其妙的问题:

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    ui->setupUi(this);
    setStyleSheet("Dialog QPushButton{border: 0 solid;border-radius: 5px;background-color: #0052FF;color: white;}"
                  "Dialog #widget{background-color: white;border-radius: 4px;}"
                  );
    setWindowFlags(Qt::FramelessWindowHint|Qt::Tool);
    setAttribute(Qt::WA_TranslucentBackground, true);
    QtConcurrent::run([this]() {
        ui->pushButton->setEnabled(true);
    });
}

方法1:

可以在Dialog中声明一个信号,专门处理pushButton 启动/禁用操作:

class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog(QWidget *parent = nullptr);
    ~Dialog();
private slots:
    void on_pushButton_clicked();
    void on_pushButton_2_clicked();
signals:
    void sigPushButtonEnable(bool );
private:
    Ui::Dialog *ui;
};
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    ui->setupUi(this);
    setStyleSheet("Dialog QPushButton{border: 0 solid;border-radius: 5px;background-color: #0052FF;color: white;}"
                  "Dialog #widget{background-color: white;border-radius: 4px;}"
                  );
    setWindowFlags(Qt::FramelessWindowHint|Qt::Tool);
    setAttribute(Qt::WA_TranslucentBackground, true);
    connect(this, &Dialog::sigPushButtonEnable, this, [this](bool isEnable) {
        ui->pushButton->setEnabled(isEnable);
    });
    QtConcurrent::run([this]() {
        emit sigPushButtonEnable(true);
    });
}

方法2:

这样做不太灵活,如果后期除了想在子线程中修改pushButton的启动/禁用外,还想修改pushButton_2的启动/禁用,那就专门针对pushButton_2在写一个信号/槽,槽中写pushButton_2的setEnabled操作,如果在想修改pushButton的内容.....

可以通过C++11 标准库提供的std::function 函数对象类解决不灵活问题.

 

#include <functional>
class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog(QWidget *parent = nullptr);
    ~Dialog();
private slots:
    void on_pushButton_clicked();
    void on_pushButton_2_clicked();
signals:
    void sigPushButtonEnable(bool );
    void sigGUIExec(std::function<void (void)> std);
private:
    Ui::Dialog *ui;
};
Q_DECLARE_METATYPE(std::function<void (void)>);
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    ui->setupUi(this);
    setStyleSheet("Dialog QPushButton{border: 0 solid;border-radius: 5px;background-color: #0052FF;color: white;}"
                  "Dialog #widget{background-color: white;border-radius: 4px;}"
                  );
    setWindowFlags(Qt::FramelessWindowHint|Qt::Tool);
    setAttribute(Qt::WA_TranslucentBackground, true);
    connect(this, &Dialog::sigPushButtonEnable, this, [this](bool isEnable) {
        ui->pushButton->setEnabled(isEnable);
    });

    qRegisterMetaType<std::function<void (void)>>();
    connect(this, &Dialog::sigGUIExec, this, [this](std::function<void (void)> std) {
        std();
    });
    qDebug() << QThread::currentThread();//GUI线程ID:QThread(0xf328613a20)
    QtConcurrent::run([this]() {
        emit sigPushButtonEnable(true);
        qDebug() << QThread::currentThread();//子线程:QThread(0xf32864a800, name = "Thread (pooled)")
        emit sigGUIExec([this]() {
           ui->pushButton_2->setEnabled(true);
           ui->pushButton_2->setText("Hello");
           qDebug() << QThread::currentThread();//QThread(0xf328613a20)
        });
    });
}

在子线程中除调用pushButton_2的setEnable外,还调用了setText,如果后期需要在子线程中访问其他的控件也可以直接通过发送信号emit sigGUIExec([](){})进行安全的GUI控件操作处理。

 

posted @ 2023-02-23 14:58  耿明岩  阅读(730)  评论(0编辑  收藏  举报
希望能帮助到你,顺利解决问题! ...G(^_−)☆