QT programming, QtConcurrent run function - multithreading

I have a program which has class MainWindow that inherited from QMainWindow.
I want to run a specific function in a new thread.
Since I can't inherit from QThread so the best solution is to use QtConcurrent.
I stored QtConcurrent result in a QFuture but as mentioned in the QT documentation, the result of QtConcurrent can not be canceled by QFuture::cancel.
I see some questions like mine but the answers are to create a class which inherit from QThread that I can't.
So what is the solution?
Here is my codes:
mainwindow.h:
using namespace std;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
...
private:
Ui::MainWindow *ui;
...
QFuture<void> future;
QFutureWatcher< void > futureWatcher;
QPushButton *stop_pushButton_=new QPushButton;
private slots:
...
void on_graph_pushButton_clicked();
void generateGraph();
void stopPushButtonClicked();
};
mainwindow.cpp:
void MainWindow::stopPushButtonClicked()
{
futureWatcher.setFuture(future);
futureWatcher.cancel();
futureWatcher.waitForFinished();
}
...
void MainWindow::on_graph_pushButton_clicked()
{
loadingOn();
future = QtConcurrent::run(this,&MainWindow::generateGraph);
}
void MainWindow::generateGraph()
{
InsertLogs::getInstance()->printStatus(USER_GENERATE_GRAPH);
GenGraph* gr=new GenGraph;
connect(this,&MainWindow::loadingOffSignal,this,&MainWindow::loadingOff);
graphType_=gr->generateGraph(persons_comboBox_->currentText(),
ui->graphResource1_comboBox->currentText(),
ui->graphResource2_comboBox->currentText(),
ui->graphType_comboBox->currentText());
InsertLogs::getInstance()->printSuccess(SYSTEM_GENERATE_GRAPH);
emit loadingOffSignal();
}
When I click on graph_pushButton, generatedGraph is called in a new thread, but when I click on stop_pushButton the future does not stop and the generatedGraph function executes completely.

Related

One thread with multiple worker processes?

I'm wondering whats the procedure for having one thread and belonging to that thread one worker which has multiple processes. If that doesn't make too much sense hopefully the code below should explain:
// WORKER.H //
class Worker : public QObject
{
Q_OBJECT
Public:
Worker(QObject *parent = nullptr);
Public Slots:
void process1(int x);
void process2(int y);
void process3(int z);
signals:
void sendResult(int result);
}
// CONTROLLER.H //
class controller : public QObject
{
Q_OBJECT
public:
controller(QObject *parent = nullptr);
signals:
operate();
public slots:
void storeResults(const int);
private:
QThread myThread;
}
// CONTROLLER.CPP // I shall only give the constructor
Controller::Controller(QObject *parent) : QObject(parent)
{
Worker *worker = new Worker;
worker->moveToThread(&myThread);
//connections
connect(&myThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::process1);
connect(this, &Controller::operate, worker, &Worker::process2);
connect(this, &Controller::operate, worker, &Worker::process3);
connect(worker, &Worker::sendResult, this, &Controller::storeResult);
myThread.start();
}
What I'm hoping to achieve here is a system where the three processes slots in the worker class will receive data from signals from the GUI. these signals will be constantly being received throughout the life of the program. Once a given signal is received, it shall be processed, and the result of the processing will be emitted into the store result slot.
Would this code achieve what I expect or is there something I'm overlooking?
Many Thanks

Correct way to creating multiple QThreads

I am developing an application with Qt 5.9.4 and i have a class derived on QThread
class LogOperations : public QThread
{
Q_OBJECT
public:
LogOperations(QString path,QStringList args,int threadID);
~LogOperations();
void setFileName(QString path);
void setArguments(QStringList ar);
void run();
};
and there is a mainwindow which i create and start those threads in it.
First, i create a QList that holds a pointer to that QThread Class
QList<LogOperations *> threadList;
Then i am simply creating the threads like this
for(int i=0;i<ls.count();i++)
{
threadList.append(new LogOperations(ls.at(i),args,i));
threadList.at(i)->start();
}
But if there are 5-10 threads it's working fine but if there are to many (like 20 or above) threads, the application crashes, how can i handle these threads? any thought?

How to properly stop QThread run() function?

I have one thread, producer and class consumer. Consumer is registered in QML, and producer is connected with signals to consumer. Producer send data to consumer, so consumer can update the model and GUI.
Code looks something like this:
Main function:
int main(int argc, char ** argv)
{
QGuiApplication app(argc, argv);
Producer producer;
Consumer consumer;
/* Connect signals between producer and consumer */
...
...
QQuickView view;
/* Set root context */
QQmlContext *ctxt = view.rootContext();
producer.start();
ctxt->setContextProperty("consumer", &consumer);
/*Connect signals between consumer and QML*/
...
view.show();
return app.exec();
}
Producer:
class Producer : public QThread
{
Q_OBJECT
protected:
void run()
{
while(true) {
if (someFlag == true)
{
// do some work
// emit signal with data to consumer
}
}
}
signals:
// Signals for sending data
};
The question is how to properly stop thread Producer when exit in application is pressed?
Instead of while(true) check for a "should end" condition, e.g. isInterruptionRequested().
Then, in main(), before returning, you tell the thread to stop, e.g. with QThread::requestInterruption and then wait on the thread
view.show();
const int ret = app.exec();
producer.requestInterruption();
producer.wait();
return ret;
I have a suggestion:
My way of doing is, Register the windows close event and do any sort of cleaning activities in it.
pseudo code below. try to fit it in your application.
Close filter event:
class CloseEventFilter : public QObject
{
Q_OBJECT
public:
CloseEventFilter(QObject *parent) : QObject(parent) {}
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Close)
{
// Destroy your thread here.
producer->exit();//Make your thread available
//... may be global....producer is derived from QThread.
}
return QObject::eventFilter(obj, event)
}
}
};
Registering to windows
//Registering window
QGuiApplication *app = new QGuiApplication(argc,argv);
QWindow* qtwindow = new QWindow();
CloseEventFilter *closeFilter = new CloseEventFilter(window);
qtwindow >installEventFilter(closeFilter);
Why thread exit funtion (from documentation)
void QThread::exit(int returnCode = 0)
Tells the thread's event loop to exit with a return code.
After calling this function, the thread leaves the event loop and returns from the call to QEventLoop::exec(). The QEventLoop::exec() function returns returnCode.
By convention, a returnCode of 0 means success, any non-zero value indicates an error.
Note that unlike the C library function of the same name, this function does return to the caller -- it is event processing that stops.
No QEventLoops will be started anymore in this thread until QThread::exec() has been called again. If the eventloop in QThread::exec() is not running then the next call to QThread::exec() will also return immediately.
See also quit() and QEventLoop.
The while (true) if (someFlag) loop will peg the CPU at 100% when there's no work to be done. You don't want that!
Alas, there's no need to make the Producer be a QThread. It can be a plain old QObject. There are different ways of doing it.
You can use a zero-duration timer to keep the event loop active in the thread:
class Producer : public QObject
{
Q_OBJECT
QBasicTimer m_timer;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() == m_timer.timerId())
doWorkChunk();
}
void doWorkChunk() {
...
emit hasData(...);
}
public:
Producer(QObject * parent = nullptr) : QObject{parent} {
connect(this, &Producer::start, this, [this]{ m_timer.start(this, 0); });
connect(this, &Producer::stop, this, [this]{ m_timer.stop(); });
}
Q_SIGNAL void start();
Q_SIGNAL void stop();
Q_SIGNAL void hasData(const DataType &);
};
Instead of setting/clearing the flag, call start() and stop(). Then you can move the producer to any thread and it will work just fine, doing the right thing on exit (wrapping things up cleanly):
class Thread : public QThread {
using QThread::run;
public:
~Thread() { quit(); wait(); }
};
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
Producer producer;
Thread thread; // must come after producer above, the order has meaning!
producer.moveToThread(&thread);
thread.start();
return app.exec();
}
You can use the Qt Concurrent framework, too:
class Producer : public QObject {
{
Q_OBJECT
volatile bool m_active = false;
void doWorkChunk() {
...
emit hasData();
}
void doWork() {
while (m_active) doWorkChunk();
}
public:
using QObject::QObject;
~Producer() { stop(); }
void start() {
m_active = true;
QtConcurrent::run(this, &Producer::doWork);
}
void stop() {
m_active = false;
}
Q_SIGNAL void hasData(const DataType &);
};

How to call a slot, that belongs to a QThread, within that same QThread?

I have a QThread that starts an external Linux binary. I use connect() to call a slot(), which is part of the QThread, whenever I get an output from that external Linux binary. This seems not to work for me. Can anyone tell me what is wrong in the below code?
class ThreadPowerSubSystem : public QThread
{
public:
ThreadPowerSubSystem(){ }
private slots:
void checkForTwoSecPress()
{
qWarning("button pressed");
}
private:
void run()
{
QProcess *powerProcess = new QProcess();
powerProcess->start("/home/testApp/button");
connect(powerProcess, SIGNAL(readyRead()), this, SLOT(checkForTwoSecPress()));
exec();
}
};
Also I need to call a function to display a dialog inside that slot. Can anyone show me an example on how to do it?

Image not getting displayed/update in QLabel using SIGNAL-SLOT in ROS node

I am implementing the Qt code in ROS node. I have a header file in which i have defined all the members, Q_SIGNAL and Q_SLOTS. In my .cpp file i want to display an image when i press a button(assignButton). But when i press the button, nothing shows up.
To test whether the connect function is working properly or not, i tried to display an image in the imageLabel which is stored in my laptop..and it worked.
PROBLEM:- I am taking the images from simulator in ROS through the
void SelectionInterface::imageCallback(const sensor_msgs::ImageConstPtr& msg) and i want to display those images in imageLabel by SIGNAL-SLOT..but its not getting displayed..no error
My code is following:-
1. Header file-- SelectionInterface.h
class SelectionInterface : public QMainWindow
{
Q_OBJECT
public:
SelectionInterface(ros::NodeHandle *nh, RosThread *rt, QMainWindow *parent = 0);
~SelectionInterface();
private:
RosThread *rosThread;
ros::NodeHandle *nodeHandle;
// ROS Subscribers
image_transport::Subscriber image_sub;
void imageCallback(const sensor_msgs::ImageConstPtr& msg);
// More Memberfunctions and Variables
QWidget *newCentralWidget;
QPushButton *quitButton;
QPushButton *assignButton;
QVBoxLayout *layout;
QLabel *imageLabel;
QImage image;
// ...
protected:
void closeEvent(QCloseEvent *event);
Q_SIGNALS:
void windowClosedSignal();
private Q_SLOTS:
void quitInterface();
void assignImage();//QImage
};
2. .cpp file
#include "SelectionInterface.h"
#include <iostream>
SelectionInterface::SelectionInterface(ros::NodeHandle *nh, RosThread *rt, QMainWindow *parent): QMainWindow (parent)
{
rosThread = rt;
nodeHandle = nh;
// Subscribing and Publishing
image_transport::ImageTransport it(*nh);
// Setup user interface here
newCentralWidget = new QWidget;
quitButton = new QPushButton("Quit");
assignButton = new QPushButton("Assign Image");
layout = new QVBoxLayout;
imageLabel = new QLabel;
imageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
layout->addWidget(imageLabel);
layout->addWidget(quitButton);
layout->addWidget(assignButton);
newCentralWidget->setLayout(layout);
this->setCentralWidget(newCentralWidget);
// Signal-Slot Connections
connect(this, SIGNAL(windowClosedSignal()), this, SLOT(quitInterface()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));
connect(assignButton, SIGNAL(clicked()), this, SLOT(assignImage()));
// ...
}
void SelectionInterface::imageCallback(const sensor_msgs::ImageConstPtr& msg)
{
QImage image(&(msg->data[0]), msg->width, msg->height, QImage::Format_RGB888);
}
SelectionInterface::~SelectionInterface()
{
//destructer (leave empty)
}
void SelectionInterface::closeEvent(QCloseEvent *event)
{
Q_EMIT windowClosedSignal();
}
void SelectionInterface::quitInterface()
{
rosThread->stop();
rosThread->wait();
std::cout << "Good bye.\n";
}
void SelectionInterface::assignImage()
{
imageLabel->setPixmap(QPixmap::fromImage(image));
}
I doubt QImage can replace ~ to your home path. Try to specify full path e.g. '/home/user/Pictures/wallpapers/buddha.jpg'.

Resources