Qt - emit a signal from a c++ thread
Asked Answered
L

4

19

I want to emit a signal from a C++ thread (std::thread) in Qt.

How can I do it?

Lapin answered 27/7, 2014 at 15:14 Comment(5)
Is there a reason you don't want to use Qt's own threads? Qt is a pretty "inclusive" library, if you use Qt you just about have to use Qt for everything.Embry
I don`t want create another class and so on. I need the thread for a small jobLapin
"I don't want to create another class" is really a bad reason. Alternative solutions would be much more complex and brittle!Pharmacognosy
I must pass a component of UI to the new class. It`s more complex!Lapin
Just to clarify: Using QThread does not imply 'creating another class'. Indeed, subclassing QThread is considered an anti-pattern by some.Bonaire
C
16

You definitely can emit a signal from a thread (QThread, std::thread or even boost::thread). Only you must be careful of your connect function's fifth parameter (Qt::ConnectionType):

If Qt::DirectConnection: The slot is invoked immediately (from the current thread), when the signal is emitted. If Qt::QueuedConnection: The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.

See ConnectionType-enum for more options.

The problem is not really from which thread you emit the signal, it's more from which thread the slot is being invoked. For instance, I think QLabel::setText must be executed from QLabel's owner thread (most likely main thread). So if you emit a signal connected to a QLabel's setText from a thread, connection must be done with Qt::AutoConnection, Qt::QueuedConnection or Qt::BlockingQueuedConnection.

Ci answered 27/7, 2014 at 15:34 Comment(3)
"I think QLabel::setText must be executed from QLabel's owner thread (most likely main thread)" - more than most likely...all widgets must live on the main (QApplication) thread. P.S. no need to sign posts on SO!Enlistee
Not all...I think QCheckBox::setChecked works from thread (Or maybe it was QWidget::setVisible or QWidget::setEnabled....but I remember calling on of those with no problem from threads). But you are right, in general purpose, all GUI must be updated from main thread....Ci
Some things may wind up seeming to work, because the platform you're on is allowing it, or it just hasn't crashed yet. You may or may not be getting a warning in the debug output you're not seeing. But the contract w/Qt you're supposed to be obeying is to use signals/slots to communicate with widgets if you are on a non-GUI thread. "...the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread."Enlistee
P
4

You probably should not emit a Qt signal from a std::thread-created thread in general without care. See Jpo38's answer : connection type matters, etc...

If the thread is running some Qt event loop, you probably could. See threads and QObject

There is a (Unix-specific probably) work-around, doing the same as for Unix signals with Qt : use a pipe from your std::thread to the main thread.

But, as commented by Joachim Pileborg, you should make your own QThread. It is the simplest, and probably the shortest (in term of source code), and you just need to copy and paste some existing example and adapt it to your need.

Beware that AFAIK only the main thread should do Qt GUI manipulations. You should not use any QWidget (etc...) outside of the main thread! (BTW, GTK has the same restriction, at least on Linux: only the main thread is supposed to use the X Windows system protocols)

Pharmacognosy answered 27/7, 2014 at 15:19 Comment(3)
Thanks for your response. what about windows?Lapin
I don't know anything about Windows (never coded for it, and my first program was on punched cards in 1974). If you can get some equivalent of pipe, (or else make a socket), you could try. But as Joachim Pileburg commented, you should go the Qt way and have a QThread.Pharmacognosy
You can emit signals from any thread, even not created/managed by Qt through QThread. In order to handle a queued invocation instead the thread must be a QThread / have an event loop spinning.Aruba
I
1

If you're keeping pointer to your QObject then you could use one of QMetaObject::invokeMethod member http://qt-project.org/doc/qt-5/qmetaobject.html#invokeMethod

Probably you will have to use Qt::QueuedConnection so your signal will be invoked at proper thread (not your std::thread). Remember that your signal won't be invoked immedietly.

Interfuse answered 28/7, 2014 at 8:30 Comment(0)
H
0
class MainForm : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainForm(QWidget *parent = nullptr);
    virtual ~MainForm();

private:
signals:
    void signalSendButtonEnable(bool);

private slots:
    void singalReceiveButtonEnable(bool);


};

MainForm::MainForm(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainForm), status_{false}, io_context_{}, timer_{io_context_}
{
    ui->setupUi(this);

    // bind SIGNAL & SLOT
    connect(this, SIGNAL(signalSendButtonEnable(bool)), this, SLOT(singalReceiveButtonEnable(bool)));
}

MainForm::~MainForm()
{
    delete ui;
}

void MainForm::singalReceiveButtonEnable(bool status){  //recv signal
    qDebug() << "singalReceiveButtonEnable";
    this->ui->btnConnect->setEnabled(status);
}

void MainForm::start(){
    std::thread t([](){
        sleep(20);
        emit signalSendButtonEnable(true);   //send signal
    });
    t.detach();
}

Hourglass answered 21/3, 2019 at 9:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.