Display a QMessageBox from a QThread - c++

I want to display a message box from a separate thread, however, I get this error:
QThread: Destroyed while thread is still running
Can anyone explain how to display a message box from a thread?

Emit a signal. Since you cannot do UI stuff in a Qthread, instead send your message as an argument of your signal.
signal decalaration in your qthread:
signals:
void write2SysStatus(QString theMessage);
emitting the signal from the qthread:
emit write2SysStatus("Some status");
slot declaration/definition in QMainWindow:
public slots:
void eWriteLine ( QString theMessage ){
//this is where you use you message box.
}
connecting of the slot and signal:
connect(pFPSengine, SIGNAL(write2SysStatus(QString)), this,SLOT(eWriteLine(QString)));

Related

Qt - does a QTimer::timeout() signal result in a QEvent?

Qt doc:
If no event loop is running, events won't be delivered to the object.
For example, if you create a QTimer object in a thread but never call
exec(), the QTimer will never emit its timeout() signal. Calling
deleteLater() won't work either. (These restrictions apply to the main
thread as well.)
Does this mean that signal void QTimer::timeout() will also issue a QEvent?
If so, where does the Qt doc state this?
where does the Qt doc state this?
Nowhere, because it shouldn't matter to the user of QTimer. The timer event is an implementation detail. It is delivered to the timer object itself, so you'd really have to go out of your way to intercept it. Here's how QTimer works:
class QTimer : public QObject {
Q_TIMER
QBasicTimer m_timer;
protected:
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() == m_timer.timerId())
emit timeout();
}
/*...*/
};
If you think about it, there's no way of emitting any signals without running the code that emits the signals, and the only way to safely run such code that emits things asynchronously is to code for run-to-completion chunks that cede control to the event loop at every opportunity. The event loop is notified by the platform that a timer has timed out, and emits a signal right then. You'd be in deep trouble if Qt issued signals such as timer timeouts from intrusive asynchronous callbacks like Unix signals: just read about how few things you can do while in a signal handler - it'd be no different than an interrupt handler.

Qt - emit a signal from a c++ thread

I want to emit a signal from a C++ thread (std::thread) in Qt.
How can I do it?
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.
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)
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.
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();
}

Proper use of progress bar in Qt?

I must send a file through serial port, so I use a separate thread, named serialWorker.
In the main thread I have this:
ui->progressBar->setMaximum(file.size());
QTextStream in(&file);
while(!in.atEnd()){
serialWorker.send(line);
ui->progressBar->setValue(in.pos());
}
The problem is that the progress bar is not "in real time", namely, as serialWorker doesn't block main thread, the loop runs without waiting. How I should approach this better?
You are updating progress bar in a loop which blocks main GUI thread thus your progressbar does not get updated.
You should use a signal in your serialWorker to notify the progress bar in your user interface of the value for the progress. In your send function you should emit the signal with the appropriate value. The signal is like:
void progressChanged(int val);
You should also connect the progressChanged(int) signal to the setValue(int value) slot of the QProgressBar.
And the last point is that you should not call send directly when it is in an other thread. The correct way is defining send as a slot and connecting a signal to that slot and emitting the signal when you want to call send.

How to Avoid Qt Access Violation Errors When Using QThread?

I am working on an application where I use QThread to capture frames of camera (OpenCV). I followed the approach described here and moved a worker to the QThread:
m_CameraCaptureThread= new QThread();
m_ProcessingThread= new QThread();
m_CameraCapture= new CCameraCapture();
//Move camera capture object to thread
m_CameraCapture->moveToThread(m_CameraCaptureThread);
//Connect error signal
QObject::connect(m_CameraCapture, SIGNAL(error(QString,QString)), this, SLOT(reportError(QString,QString)));
//Connect the finished signal of the worker class to the thread for quitting the loop
QObject::connect(m_CameraCapture, SIGNAL(finished()), m_CameraCaptureThread, SLOT(quit()));
//This connections guarantees that the *m_CVideoCapture is automatically deleted if the event loop of the thread is terminated. Therefore, m_CVideoCapture does not need to be released manually if the capturing process is stopped.
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));
QObject::connect(m_CameraCapture, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));
QObject::connect(this, SIGNAL(exitThreads()), m_CameraCapture, SLOT(exitThread()));
This code is part of the constructor of my camera handler class. If the main application is closed I want to exit all threads. Therefore, the destructor of my CCameraHandler is:
CCameraHandler::~CCameraHandler(void)
{
emit exitThreads();
qDebug() << "CCameraHandler deleted";
}
The exit Slot in my camera capture which is called by the signal exitThreads() is:
void CCameraCapture::exitThread(){
//Stop grabbing
stopGrabbing();
//Emit finished signal which should be connected to quit() of QThread and deleteLate of this class;
emit finished();
}
As one can see from the connection setup the emitted finished() signal will quit the event loop of the thread and call deleteLater() of the Worker and the Thread. The destructor of the worker which is called looks like:
CCameraCapture::~CCameraCapture(void)
{
qDebug() << "CCameraCapture deleted";
}
The result is that the Destructor of CCameraCapture is called correctly - it appears only one time in the QDebug stream but at the end of CCameraCapture::~CCameraCapture(void) scope. I get an access violation error from OpenCVs opencv_highgui249d.dll. As I am only using:
cv::VideoCapture m_Cap;
in the class definition of CCameraCapture, the destruction of m_Cap must cause this error. At the moment I really do not know how to solve this issue. Any ideas?
Edit:
The application should close when the main window is closed using
this->setAttribute(Qt::WA_DeleteOnClose);
and
CMainWindow::~CMainWindow(){
m_CameraHandler->deleteLater();
m_ImageWidget->deleteLater();
m_ProcessedImageWidget->deleteLater();
emit windowClosed();
qDebug() << "CMainWindow deleted";
}
If the main application is closed I want to exit all threads.
Without debugging this myself, it looks like a problem here is the emit in the destructor of CCameraHandler.
One reason this is problematic is that if the user closes the application and it quits the main event loop, (started with QApplication's call to exec), any objects that have had deleteLater called may not actually be deleted. In this case, I'm specifically looking at m_CameraCaptureThread.
If we walk through the event handling of signals / slots: -
QApplication::processEvents...
CCameraCapture::exitThread()
emit finished
QThread::quit
QThread::deleteLater
By calling deleteLater, an event is placed in the current thread's event queue to process the delete after the slot function has exited. This occurs when the event loop next processes events.
However, the application is going to quit, so the event loop does not run again and the call to deleteLater is not serviced.
If all objects are running in the same thread, then signal / slot connections are direct, which would be less of an issue. However, with multiple threads, the connections are queued.
I suggest changing the destructor so that you clean up without using an emit signal here and see if the problem still persists.
Finally solved the problem: The application terminated before the threads left their eventloop. The point that a camera capture thread usually never terminates makes it necessary to exit the capturing loop at some point. If this exit is triggered by closing the application one needs to exit the threads before the application closes. I follow this example (see above). However, as the loop never terminates one needs emit a signal from the main thread to terminate. If this is done on close of the application, the signal will not arrive in time. Therefore, I connected the finished() signal of QThread to the deleteLater() of the worker
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));
The finished signal will be emitted on exit of the event loop and will delete the QThread and the worker. In the destructor of the class which sets up the QThread and the worker I now use
CCameraHandler::~CCameraHandler(void)
{
emit stopGrabbing();
m_CameraCaptureThread->exit();
m_CameraCaptureThread->wait();
qDebug() << "CCameraHandler deleted";
}
At first I left out the wait and the application still crashed. For me this solved the problem. Thanks for the help to figure out this solution.

How emit objectNameChanged signal under Qt by the QWidget?

I have this code:
QWidget *w = qobject_cast<QWidget *>(d->m_object);
w->setObjectName("test");
it's can not emit objectNameChanged signal, so I want to manual emit signal,
w->objectNameChanged("test",QWidget::QPrivateSignal)); but report error QWidget::QPrivateSignal is private). How can I emit QWidget::objectNameChanged() signals?
From the Documentation (5.7):
Note: This is a private signal. It can be used in signal connections
but cannot be emitted by the user.
Anyway, the signal should be emitted, when you change the name, there should be a mistake somewhere else.