Weird QPushButton bug

1

I'm writing a Qt-based dll for plotting. The dll contains QApplication that runs in a separate std::thread. The main widget is class QPlotsControl : public QMainWindow.

void QtPlotter::ApplicationLoop(const WorkerThreadCallbackFn& cbFn)
{
    ...
    QApplication a(argc, &argv);
    ...
    m_qPlotsControl = new QPlotsControl(cbFn, [this](){ this->ReadyCallback(); });
    m_qPlotsControl->show();
    a.exec();
    delete m_qPlotsControl;
    ...
    return;
}

QtPlotter::QtPlotter(const WorkerThreadCallbackFn& cbFn):
    m_ready{false}, m_isFinished{false}
{
    std::thread guiThread([this,cbFn] { this->ApplicationLoop(cbFn); });
    guiThread.detach();
    ...
}

So QtPlotter is invoked from the outside, it creates a thread and QApplication runs in that thread. Everything worked fine until recently. Now, there is sometimes an error QObject::~QObject(): timers cannot be stopped from another thread and access violation at 0x000000C5 when I try to close the QPlotsControl and exit the ApplicationLoop. I understand that something is being deleted from a wrong thread, but can't locate what. After some time I found the source of troubles: crash occurs if I click recently added QPushButton. If I not, nothing happens and works/exits fine. Relevant code:

QPushButton *Ui_QPlotsControl::selectInstrumentsButton;
void Ui_QPlotsControl::setupUi(QMainWindow *QPlotsControl)
{
...
    selectInstrumentsButton = new QPushButton(centralWidget);
    selectInstrumentsButton->setObjectName(QString::fromUtf8("selectInstrumentsButton"));
    horizontalLayout->addWidget(selectInstrumentsButton);
...
}

void Ui_QPlotsControl::retranslateUi(QMainWindow *QPlotsControl)
{
    selectInstrumentsButton->setText(QCoreApplication::translate("QPlotsControl", "\320\222\321\213\320\261\320\276\321\200 \320\270\320\275\321\201\321\202\321\200\321\203\320\274\320\265\320\275\321\202\320\276\320\262", nullptr));
} // retranslateUi

QPlotsControl::QPlotsControl(const WorkerThreadCallbackFn& cbFunc, const readyCallbackFn& readyCb,
    QWidget *parent) : m_Callback{ cbFunc }, m_readyCallback { readyCb },
    m_needsClosing{false},
    QMainWindow(parent)
{
    ui.setupUi(this);
    ...
}

There is literally no more references to this button in the code now. Nothing connected. Nothing overloaded. If one clicks, error occurs on close. If one doesn't, everything is ok.

I'm sorry I can't provide a reproducible example, at least immediately. I use MSVC 2017 (v141) & qt5.14.1.
What I'm looking at?
Thank you very much.


EDIT#1
I found that the message about QObject::~QObject(): timers cannot be stopped from another thread is printed somewhere from ntdll thread. There are no Qt-based threads at this point. So it does mean, I suppose, that QApplication does not release all resources and is being destroyed not from "GUI"(or qt-main) thread. I wonder what is the difference between QPushButton and its click and QTabWidget (works fine).


EDIT#2
This is a threading problem. The minimal reproducible example is extremely simple:

#include <QApplication>
#include <QPushButton>
#include <thread>

void thrRoutine()
{
    int argc = 1;
    char *argv[] = { "gui\0" };
    QApplication a(argc, argv);
    QPushButton btn("Push");
    btn.show();
    a.exec();
}

int main(int argc, char *argv[])
{
    std::thread thr(thrRoutine);
    thr.join();
}

If one closes the window without interaction, everything is fine. If any interaction occured, including simple hovering the pointer over the button, QObject::~QObject: Timers cannot be stopped from another thread message will be printed and crash will occur in more sophisticated setups.
How to deal with it?

c++
qt
qt5
asked on Stack Overflow Apr 25, 2021 by Suthiro • edited Apr 27, 2021 by Suthiro

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0