I am coding a fairly simple application that uses Qt with OpenCV. I have single window that contains a widget which displays a video feed captured from a webcam. The webcam video capture is running in an infinite loop in a separate thread so as not to consume the UI thread.
When I close the window (using the normal "X" button on the top right of the window - this app is being developed in Windows 7), it doesn't seem to be shutting down the program correctly. The window does close visibly, but I put a breakpoint in the destructor of the main window, and the breakpoint never gets hit. Additionally, the thread which does video capture continues to run (I know this because the thread outputs to stdout periodically). Only when I click "stop debugging" in the Qt development environment does it cause everything to completely shut down.
Here is my worker object (not subclassing from QThread):
class Worker : public QObject
{
Q_OBJECT
private:
VideoCapture *cap;
bool finished;
QMutex mutex;
public:
Worker ()
{
cap = new VideoCapture(0);
finished = false;
}
bool isFinished ()
{
QMutexLocker locker (&mutex);
return finished;
}
public slots:
void doWork ()
{
Mat frame;
while(!isFinished())
{
// ...some code that outputs to stdout deleted for clarity...
(*cap) >> frame;
emit resultReady(frame);
}
}
void setFinished (bool f)
{
QMutexLocker locker (&mutex);
finished = f;
}
signals:
void resultReady (Mat frame);
};
Here is the header file for my main window:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
CVImageWidget* imageWidget;
Worker *worker;
QThread workerThread;
public slots:
void handleResults (Mat frame);
signals:
void operate ();
void finishSignal (bool f);
private:
Ui::MainWindow *ui;
};
And the class implementation:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//Create a widget for this window.
QWidget *wdg = new QWidget(this);
QGridLayout *grid = new QGridLayout(wdg);
// ...code creating widgets deleted for clarity...
this->setCentralWidget(wdg);
//Start video capture
qRegisterMetaType<Mat>("Mat");
worker = new Worker();
worker->moveToThread(&workerThread);
connect(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(operate()), worker, SLOT(doWork()));
connect(worker, SIGNAL(resultReady(Mat)), this, SLOT(handleResults(Mat)));
connect(this, SIGNAL(finishSignal(bool)), worker, SLOT(setFinished(bool)));
workerThread.start();
emit operate();
}
MainWindow::~MainWindow()
{
emit finishSignal(true);
workerThread.quit();
workerThread.wait();
delete ui;
}
void MainWindow::handleResults(Mat frame)
{
imageWidget->showImage(frame);
}
Any help understanding why the program doesn't shut down properly would be appreciated. Thanks!
Related
We developed an image processing and display program based on Qt. We use sub-threads for image processing and the main thread as UI display. In order to display synchronization, we use the Qt::BlockingQueuedConnection parameter when connecting the signal-slot. There are other threads that may overwrite the image data, so we use locks.
code show as below:
class ProcessThread : public QThread
{
Q_OBJECT
public:
ProcessThread(QObject *parent = nullptr);
void run() override
{
int i=0;
while (1)
{
if(isQuit)break;
mutex.lock();
QString filename=QString("/home/%1.bmp").arg((i++)%2000);
data=imread(filename.toStdString());
//dosomthing...
emit processCompleted(&data);
mutex.unlock();
}
}
void setIsQuit(bool v){isQuit=v;}
signals:
void processCompleted(const Mat *data);
private:
bool isQuit=false;
Mat data;
QMutex mutex;
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr): QWidget(parent)
{
thread=new ProcessThread();
connect(thread,SIGNAL(processCompleted(const Mat *)),
this,SLOT(processCompleted(const Mat *)),Qt::BlockingQueuedConnection);
thread->start();
}
~Widget()
{
thread->setIsQuit(true);
thread->quit();
thread->wait();
delete thread;
}
void paintEvent(QPaintEvent *event) override
{
Q_UNUSED(event)
QPainter p(this);
p.drawImage(QPoint(0,0),image);
}
private slots:
void processCompleted(const Mat *data)
{
if(data!=nullptr)
{
if(data->channels()==1)
{
image=QImage(data->data,
data->cols,
data->rows,
data->step,
QImage::Format_Grayscale8);
}
else
{
image=QImage(data->data,
data->cols,
data->rows,
data->step,
QImage::Format_RGB888);
}
}
else
{
image=QImage();
}
repaint();
}
private:
QImage image;
ProcessThread *thread;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
When I exit the program, it will be stuck in thread->wait() in the ~Widget() function; it cannot continue to execute;
If I comment out thread->wait(); it will not get stuck when exiting, but it will prompt: "QMutex: destroying locked mutex" and "QThread: Destroyed while thread is still running" errors;
If the Qt::BlockingQueuedConnection parameter is not used when connecting the signal-slot, the first problem above will not occur, but the display will not be synchronized.
We need to display synchronization, how can we do that? Thanks!
I am trying to learn how to use Multi-threading in Qt and put it to use in QtWidget applications. So I have setup this simple test case.
MainWindow
This form has some buttons and each button will execute another form (a Dialog for that matter).
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->btnShowCalibrationDialog, &QPushButton::clicked,
this, [&](){
CalibrationDialog dlg;
dlg.exec();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
CalibrationDialog This dialog has a QPushButton and a QTextEdit. When the button is clicked I will run a QRunnable class (comes next!)
calibrationdialog.h (I have spared you the includes and guards!)
class CalibrationDialog : public QDialog
{
Q_OBJECT
public:
explicit CalibrationDialog(QWidget *parent = nullptr);
~CalibrationDialog();
public slots:
void onBeginWorkRequested();
void onReceiveData(int data);
private:
Ui::CalibrationDialog *ui;
};
calibrationdialog.cpp
#include "worker.h"
CalibrationDialog::CalibrationDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::CalibrationDialog)
{
ui->setupUi(this);
connect(ui->btnBegin, &QPushButton::clicked,
this, &CalibrationDialog::onBeginWorkRequested);
}
CalibrationDialog::~CalibrationDialog()
{
delete ui;
}
void CalibrationDialog::onBeginWorkRequested()
{
qDebug() << "CalibrationDialog::onBeginWorkRequested()" << "on" << QThread::currentThreadId();
Worker* worker = new Worker();
QThreadPool* pool = QThreadPool::globalInstance();
connect(worker, &Worker::reportProgress,
this, &CalibrationDialog::onReceiveData);
pool->start(worker);
pool->waitForDone();
}
void CalibrationDialog::onReceiveData(int data)
{
ui->teResults->append(QString::number(data));
ui->teResults->ensureCursorVisible();
}
Worker And this is some runnable...I want to report the progress so that is shows up in the textedit of the dialog in a reponsive manner!
worker.h
class Worker : public QObject, public QRunnable
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
void run() override;
private:
int mProgress = 0;
signals:
void reportProgress(int progress);
};
worker.cpp
Worker::Worker(QObject *parent) : QObject(parent)
{
}
void Worker::run()
{
qDebug() << "Worker is running #" << QThread::currentThreadId();
while(true) {
mProgress += 100;
emit reportProgress(mProgress);
QThread::msleep(500);
}
}
I see in the debugger that control goes in to the while loop...but the signal is not being handled by the dialog! I mean the textedit stays empty!
I tried to read documentationand search online...I came to the conclusion that my problem lies in the waste land of thread affinity....but I have no idea if that is the case and if that is, how to solve it. Please assist!
remove pool->waitForDone();, never use waitForX methods in a GUI since they are blocking preventing signals from doing their job.
My problem is the following one: I have 2 classes (mainwindow and mythread), I run the thread from the mainwindow and I would like to display some QLabel of my mainwindow from mythread :
mythread.cpp :
void mythread::run()
{
while(1)
{
this->read();
}
}
void mythread::read()
{
RF_Power_Control(&MonLecteur, TRUE, 0);
status = ISO14443_3_A_PollCard(&MonLecteur, atq, sak, uid, &uid_len);
if (status != 0){
//display Qlabel in mainwindow
}
}
mainwindow.cpp :
_thread = new mythread();
_thread->start();
You should use Qt's signal/slot mechanism. The thread will emit a signal, that new data has been read. Any interested object can connect to that signal, and perform actions depending on it.
This also works across thread-boundaries, as in your example. In Qt, it is required that only the main-thread interacts with UI elements.
Here is an outline:
// Your mainwindow:
class MyWindow : public QMainWindow {
Q_OBJECT
// as needed
private slots:
void setLabel(const QString &t) { m_label->setText(t); }
};
// Your thread
class MyThread: public QThread {
Q_OBJECT
// as needed
signals:
void statusUpdated(const QString &t);
};
// in your loop
if (status != 0) {
emit statusUpdated("New Status!");
}
// in your mainwindow
_thread = new MyThread;
connect(_thread, &MyThread::statusUpdated, this, &MyWindow::setLabel);
_thread->start();
I want to update a modal QProgressDialog from my worker thread. However, if I set the dialog to be modal, my application crashes (and the dialog did not show any progress). If I do not, everything goes fine (but the user can tinker around with the rest of the program, which may cause issues).
What am I doing wrong?
Minimum code sample follows:
filereader qfr;
QProgressDialog progress("Importing file.", "Cancel", 0, file_size);
connect(&qfr, &filereader::signalProgress, &progress, &QProgressDialog::setValue, Qt::QueuedConnection);
QThread worker_thread;
std::atomic<bool> success = false;
connect(&worker_thread, &QThread::started,
[&]() {
success = qfr.read_file(/* parameters */);
worker_thread.quit();});
worker_thread.start();
//progress.setWindowModality(Qt::WindowModal); // Works only fine when this line is commented !!
while (worker_thread.isRunning()) {
QApplication::processEvents();
QThread::sleep(0);
}
progress.close();
Your thread is pretty much pointless. It serves no real purpose. You could have as well just called QApplication::processEvents in your read_file method. But you shouldn't, calling processEvents is bad practice.
What you should do is remove that while loop, and make your progress dialog a member of your class. I don't really like how that lambda looks either. I would personally just use filereader::read_file as a slot.
Note that Qt::windowModal blocks input to the parent window. Your progress dialog has no parent. So you would either have to call progress->setModal(true), or progress.setWindowModality(Qt::ApplicationModal);. Or set a parent to it.
Here is a small example (it is not tailor made for your application, but it should point you in the right direction):
#include <QtWidgets>
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject *parent = nullptr) : QObject(parent){}
public slots:
void simulateLongProcess()
{
for(int i = 0; i < 101; i++)
{
emit progressChanged(i);
QThread::msleep(100);
}
emit finishedWorking(true);
}
signals:
void progressChanged(int progress);
void finishedWorking(bool result);
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr) : QWidget(parent)
{
setLayout(new QHBoxLayout);
progress_dialog.setModal(true);
progress_dialog.setAutoReset(false);
progress_dialog.setCancelButton(nullptr);
QThread *thread = new QThread(this);
connect(thread, &QThread::started, &worker, &Worker::simulateLongProcess);
connect(&worker, &Worker::finishedWorking, thread, &QThread::quit);
connect(&worker, &Worker::progressChanged, &progress_dialog, &QProgressDialog::setValue);
connect(&worker, &Worker::finishedWorking, &progress_dialog, &QProgressDialog::close);
connect(&worker, &Worker::finishedWorking, this, &Widget::handleResult);
QPushButton * start_button = new QPushButton("START");
connect(start_button, &QPushButton::clicked, this, [=]
{
progress_dialog.show();
thread->start();
});
layout()->addWidget(start_button);
resize(400, 300);
}
public slots:
void handleResult(bool result)
{
// do something with the result
}
private:
QProgressDialog progress_dialog;
Worker worker;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"
I try to implement this: when app is started I need to create multiple threads that would use the same QDialog window to get messages from user. When thread is started, it asks user for input and if button OK pressed, it prints the message to console. I can't figure out why but I get dialog window only once and after that it prints my one message to console and application finishes.
Here's how I describe dialog window:
#include <QtWidgets>
class MyDialog : public QDialog
{
Q_OBJECT
public:
QWaitCondition* condition;
explicit MyDialog(QWidget *parent = 0);
signals:
void got_message(QString);
public slots:
void show_message_input();
void show_message();
private:
QLabel* message_label;
QVBoxLayout* vbox;
QHBoxLayout* hbox;
QLineEdit* message_input;
QDialogButtonBox* dialog_buttons;
};
MyDialog::MyDialog(QWidget *parent) : QDialog(parent)
{
setModal(true);
message_label = new QLabel("Message");
message_input = new QLineEdit();
dialog_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
hbox = new QHBoxLayout();
hbox->addWidget(message_label);
hbox->addWidget(message_input);
vbox = new QVBoxLayout();
vbox->addLayout(hbox);
vbox->addWidget(dialog_buttons);
setLayout(vbox);
connect(dialog_buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(dialog_buttons, SIGNAL(rejected()), this, SLOT(reject()));
condition = new QWaitCondition();
}
void MyDialog::show_message_input()
{
int result = this->exec();
if (result == QDialog::Accepted)
{
emit got_message(message_input->text());
condition->wakeAll();
}
}
Here's MyThread class:
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(int id, MyDialog* window, QObject *parent = 0);
signals:
void show_input();
public slots:
void print_message(QString);
private:
static QMutex mutex;
static QMutex mutex2;
MyDialog* window;
int id;
void run();
void get_captcha_value();
};
QMutex MyThread::mutex;
QMutex MyThread::mutex2;
MyThread::MyThread(int id, MyDialog* window, QObject *parent) :
QThread(parent)
{
this->id = id;
this->window = window;
connect(this, SIGNAL(show_input()), this->window, SLOT(show_message_input()));
}
void MyThread::get_captcha_value()
{
QMutexLocker lock(&mutex);
connect(this->window, SIGNAL(got_message(QString)), SLOT(print_message(QString)));
emit show_input();
mutex2.lock();
window->condition->wait(&mutex2);
mutex2.unlock();
}
void MyThread::run()
{
mutex.lock();
qDebug() << "Starting thread " << id;
mutex.unlock();
get_captcha_value();
mutex.lock();
qDebug() << "Finishing thread " << id;
mutex.unlock();
}
void MyThread::print_message(QString message)
{
qDebug() << message;
QObject::disconnect(this, SLOT(print_message(QString)));
}
And main function:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyDialog* window = new MyDialog();
QList<MyThread*> threads;
for(int i = 0; i < 5; i++)
{
MyThread* thread = new MyThread(i, window);
threads << thread;
thread->start();
}
return a.exec();
}
The first problem you have is that you're inheriting from QThread. Unless you're wanting to re-write how Qt handles threading, you're doing it wrong!.
What you need to do is have a class that inherits from QObject and move the instances to a new thread. The main problem from inheriting QThread is that it can cause confusion about thread affinity (which thread an object is running on).
Also, creating more threads than processor cores is just a waste of resources.
I suggest you read this article on how to use Qt threading and stop inheriting from QThread.
Finally, the use of QMutex is to protect multiple threads accessing the same data simultaneously. You should be able to remove all of them in the code you've shown. Emitting signals with data from one thread to be received by a slot on another thread is the preferred method in Qt.