I need to have a computationally intensive function run in a different thread so that the GUI doesn't freeze or turn grey when it's running.
I followed this example: https://stackoverflow.com/a/16501374/2904614
But the GUI still freezes and turns grey.
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->textBrowser->setFont(QFont("Monospace",11));
ui->textBrowser->setLineWrapMode(QTextEdit::NoWrap);
updater->moveToThread(thread);
connect(updater,SIGNAL(req()), this, SLOT(getCheckSum()));
connect(thread, SIGNAL(destroyed()), updater, SLOT(deleteLater()));
thread->start();
}
When the user clicks the designated button. Since there may be a lot of files in one directory, the GUI will freeze as QDirIterator goes through all of them. I'm hoping to add a progress bar, that will show the user the program is still functioning.
void MainWindow::on_pushButton_clicked()
{
updater->getHash();
//getCheckSum();
}
EDIT
I would like to have the function MainWindow::getCheckSum() run in a different thread than the GUI. How will I implement this?
Github: https://github.com/Jyang772/PenguSniff
Given you only need to run one function i would recommend QtConcurrent. Extract form the docs:
Running a Function in a Separate Thread
To run a function in another thread, use QtConcurrent::run():
extern void aFunction();
QFuture<void> future = QtConcurrent::run(aFunction);
This will run aFunction in a separate thread obtained from the default QThreadPool. You can use the QFuture and QFutureWatcher classes to monitor the status of the function.
The problem is that you are calling the function of a class in another thread directly:
updater->getHash();
It causes undefined behavior in your application and makes it crash. The correct way is to emit a signal from your MainWindow which is connected to the getHash() slot in updater:
connect(this,SIGNAL(getHash()), updater, SLOT(getHash()));
void MainWindow::on_pushButton_clicked()
{
emit getHash();
}
This will result in a queued connection and runs the slot in the updater thread.
You call your getHash() computational function (it is computational as per your comment) in GUI thread. Bind it to clicked signal, then it will be invoked in object's thread.
connect(button, SIGNAL(clicked()), this, SLOT(getHash()));
Related
I am currently starting on QTCreator. I have been asked to use QTimers in a particular context which is this:
We have an open window,
One or more QTimers are triggered and make things appear on the screen every x msec.
When we press "Escape" the window should close and everything should be reset to 0.
But here is the problem, the timers are defined in a static way:
QTimer::singleShot(500, this, SLOT(foo());
When I call this->close() (which closes my window), the timers do not stop and continue. I tried several solutions: browse all the QTimers contained in my object, obviously there are none since they are defined in static. Instead of declaring them in static I've tried to create each time a new QTimer object like that:
QTimer *timer= new QTimer(this);
timer->setSingleShot(true);
timer->setInterval(2000);
timer->setParent(this);
timer->start();
And then call timer->stop() later, but I think it's very brutal when you have multiple Timers in the same code.
Is there a way to stop the timers when this->close is called, knowing that the timers are defined as a static one ?
Assuming you are using,
QWindow *qw = new QWindow();
QTimer *timer= new QTimer();
To solve the issue you need to connect destroyed() signal of QWindow to timer's slot stop()
So as soon as window is destroyed all registered timers will be stopped without explicit stop call. make sure you connect all timer instances. Code snippet as following,
QObject::connect(&qw, SIGNAL(destroyed()), timer, SLOT(stop()))
QObject::connect(&qw, SIGNAL(destroyed()), timer2, SLOT(stop()))
QObject::connect(&qw, SIGNAL(destroyed()), timer3, SLOT(stop()))
PS:
QTimer *timer= new QTimer(this); // here you are setting parent as 'this' already
timer->setSingleShot(true);
timer->setInterval(2000);
timer->setParent(this); // remove this, no need to set parent again.
timer->start();
I have a Qt GUI application that is doing some important real time work which must not be interrupted at all costs (Forwarding some incoming Serial traffic over LAN). At the moment the application is running flawless when there is no interaction with the GUI, but as soon as you click on a button or drag the form around, it seems like the forwarding is stopped for the time the click is being processed. Forwarding is done in a QTimer loop which I already put on a different thread than the GUI thread, but no change in the outcome.
Here's some parts of the code:
class MainWindow : public QMainWindow
{
QSerialPort serialReceiver; // This is the serial object
QTcpSocket *clientConnection;
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
// Some Initializations ...
QThread* timerthread = new QThread(this); // This is the thread that is supposed to do the forwarding
QTimer *timer = new QTimer(0);
timer->setInterval(25);
timer->moveToThread(timerthread);
connect(timer ,SIGNAL(timeout()),this,SLOT(readserialData())); // Run readserialData() each 25ms
timer->connect(timerthread, SIGNAL(started()), SLOT(start()));
timerthread->start();
}
void MainWindow::readserialData()
{
if(serialReceiver.isOpen() )
{
qint64 available = serialReceiver.bytesAvailable();
if(available > 0) // Read the serial if any data is available
{
QByteArray serialReceivedData = serialReceiver.readAll(); // This line would not be executed when there is an interaction with the GUI
if(isClientConnet)
{
int writedataNum = clientConnection->write(serialReceivedData);
}
}
}
}
As I said earlier, this code is running fine under idle circumstances without any data loss. Am I doing something wrong?
It is a good idea to run you important real time work in another thread. The GUI thread or main should do drawing and the other one should do processing.
Qt's documentation about GUI thread says:
GUI Thread and Worker Thread
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
And also when to use multithreading
Using Threads
There are basically two use cases for threads:
Make processing faster by making use of multicore processors.
Keep the GUI thread or other time critical threads responsive by offloading long lasting processing or blocking calls to other threads.
In your case running the realtime processing in separate thread will fix UI lagging issues and also will fix realtimeness issue.
I suggest you to read Threading basics from Qt's doc.
Threading basics
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();
}
I have a QDialog on my main thread and I have some logic that happens on a separate thread. When the logic begins, a signal is emitted connected to show() on the dialog. When the logic ends, a signal is emitted that is connected to hide() on the dialog. When the logic actually does work, the dialog is show/hide properly. If the logic does "nothing" and the signals are just emitted sequentially, the dialog doesn't always show/hide properly.
My connections are made similar to this:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent = 0) :
Ui(new Ui::MainWindowUi),
Transferer(new DataTransferer()),
TransferProgress(this),
TransferThread()
{
Ui->setupUi();
connect(&Transferer, SIGNAL(Begin()), &TransferProgress, SLOT(show()));
connect(&Transferer, SIGNAL(End()), &TransferProgress, SLOT(hide()));
Transferer.moveToThread(&TransferThread);
TransferThread.start();
Transferer.DoStuff(true);
}
virtual ~MainWindow()
{
TransferThread.quit();
TransferThread.wait(1000);
delete Ui;
Ui = NULL;
}
private:
Ui::MainWindowUi* Ui;
DataTransferer Transferer;
TransferProgressDialog TransferProgress;
QThread TransferThread;
}
The logic looks similar to this:
class DataTransferer : public QObject
{
Q_OBJECT
public:
DataTransferer(QObject *parent) : QObject(parent) {}
virtual ~DataTransferer() {}
void DoStuff(bool dontDoStuff)
{
emit Start();
if (!dontDoStuff)
{
QThread::sleep(1);
}
emit End();
}
}
When the DataTransferer does stuff, everything works fine. When the dialog is shown and hidden in rapid succession, I get the ghost dialog approximately every other time I call DoStuff().
I used QThread::currentThreadId() and verified that the dialog and logic are running on separate threads.
Why would my dialog not hide properly in this case? Should I just force my logic to always run for at least a few hundred milliseconds (that solution is bad)? Is there a way I can have my dialog make sure it's fully loaded before trying to hide itself? Should I handle these signals/slots differently?
EDIT: I've currently resigned to just putting a QThread::sleep(1) after I emit the signal to show() the dialog. I don't like this solution, but nothing else has seemed to work. The sleep(1) allows the dialog to come all the way up before hiding it. I was also able to get this to work with QThread::msleep(10), but that still resulted in the ghost dialog about 1 in 6 tries.
I tried using a member QMutex in the dialog logic whenever I called either show() or hide(), but this didn't work.
I changed all cross-thread connections to use Qt::BlockingQueuedConnection and Qt::QueuedConnection and neither attempt was successful.
I tried moving the slot connections from the dialog to the object that sets up the connections and then calling the slots directly, but that didn't prove successful either.
I have the same issue, show dialog and when get some signals to close it, when time less than 20ms(which means quickly hide the dialog), it will left a ghost dialog.
So, i just use
QTimer::singleShot(50, this, [this](){
hide(); //hide dialog
});
in close handler function. It seems work well.
My guess is that the problem occurs because "show" and "hide" calls interlace. To verify this, use a semaphore - lock the object until show have finished, and wait on it in hide. Also look at the top-voted answer here for another possible (perhaps better) solution: connecting signal/slot across different threads between QObjects
Use Qt::BlockingQueuedConnection to connect signals to slots. Be sure that event loop of main thread is not blocked. Also, if your worker thread uses a lot of cpu time - you may call QThread::yeldCurrentThread() call.
I try to use QThread, but I can't do it :(
My sample thread:
#include "worker.h"
#include "mainwindow.h"
#include <QDebug>
Worker::Worker() {}
Worker::~Worker() {
qDebug() << "Worker ends.";
}
void Worker::run() {
qDebug() << "Worker start.";
sleep(2);
emit finished();
}
And code on_btnStart_clicked():
Worker *worker = new Worker;
QThread *workerThread = new QThread(this);
connect(workerThread, SIGNAL(started()), worker, SLOT(start()));
connect(workerThread, SIGNAL(finished()), worker, SLOT(quit()));
worker->moveToThread(workerThread);
workerThread->start();
Worker starts, but never ends (if I close application, I get QThread: Destroyed while thread is still running.
Another problem - how I can transfer data between thread and my application? I want to use QThread for QLabel (example: timer). I can't find any good tutorial that I can complile without problems.
Anyone can help me?
And, if I can ask, how I can start thread like onCreate() for form? I want to create simple timer to count time of application running.
Regards
You should have a read through This article.
Look at the first code sample of the Qt 4.8 QThread documentation. It has many lines of boiler plate just to run some code in a thread. And the there is even a leak: the QThread is never going to quit and be destroyed.
The submitted patch has been accepted but the href link he forwards you to still shows the old code.
This patch snippet diff shows the valid approach on the right-side.
It shows both methods, sub-classing QThread and using QObject->Worker
You can use that as the sample to base your code on. Apply the approach that suit's your requirements as he mentions in the blog.