Interesting task of the processes in Qt - c++

Suppose I run a console application using QProcess. The application runs, displays some information, and then waits for n seconds and displays other information.
My current code is:
QProcess * p = new QProcess();
p->start("test.bat");
p->waitForStarted();
p->waitForFinished();
p->readAll();
delete p;
Currently I get all the output at the end, but what I need to do is get the output and display it as it becomes available. How do I do this?

You could connect to the readyRead() signal, so whenever there is some data to read, you will keep reading it and display without waiting for the process to finish. That means the following in terms of code:
class Foo : public QObject
{
Q_OBJECT
public:
explicit Foo::Foo(QObject parent = Q_NULLPTR)
: QObject(parent)
{
...
connect(myProcess, SIGNAL(readyRead()), SLOT(handleReadyRead()));
connect(myProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(handleFinished(int, QProcess::ExitStatus)));
connect(myProcess, SIGNAL(error(QProcess::ProcessError)), SLOT(handleError(QProcess::ProcessError)));
myProcess.start("test.bat");
...
}
public slots:
void Foo::handleReadyRead()
{
qDebug() << myProcess.readAll();
}
void Foo::handleFinished(int, QProcess::ExitStatus)
{
// Handle finished
}
void Foo::handleError(QProcess::ProcessError)
{
// Handle error
}
private:
QProcess myProcess;
}
Disclaimer: consider it as pseudo-code as I have not built it, but it should demonstrate the concept to use.

Related

How do you run a function in the background of your program (Specifically an autosave function)? QT / C++

In my code I would like to integrate an auto-save function that runs every couple seconds or so. I would like this to run in the background because I have other stuff that I am going to be running at the same time. So how would I do this?
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <fstream>
#include <QFile>
#include <QDebug>
using namespace std;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
// Setup code
ui->textEdit->setReadOnly(true);
ui->textEdit->append("Select one of the buttons on the left to pick a log");
}
MainWindow::~MainWindow() {
delete ui;
}
string lastSavedText[] = {
" ",
" "
};
QString qLastSavedTextHome, qLastSavedTextWork;
This is my first button
void MainWindow::on_homeButton_clicked() {
// Preparing text edit
ui->textEdit->setReadOnly(false);
ui->textEdit->clear();
ui->textEdit->setOverwriteMode(true);
// Loading previously saved text
QFile file { "home.apl" };
if ( !file.open(QIODevice::ReadOnly | QIODevice::Text) ) {
qDebug() << "Could not open file!";
return;
}
const auto& lastSavedText = file.readAll();
file.close();
ui->textEdit->setPlainText( lastSavedText );
}
This is my second one
void MainWindow::on_workButton_clicked() {
// Preparing text edit
ui->textEdit->setReadOnly(false);
ui->textEdit->clear();
ui->textEdit->setOverwriteMode(true);
// Loading previously saved text
QFile file2 { "work.apl" };
if ( !file2.open(QIODevice::ReadOnly | QIODevice::Text) ) {
qDebug() << "Could not open file!";
return;
}
const auto& lastSavedText = file2.readAll();
file2.close();
ui->textEdit->setPlainText( lastSavedText );
}
This is the save button I hope to eliminate with an autosave
void MainWindow::on_saveButton_clicked() {
// Converts textEdit to string
QString textEditText = ui->textEdit->toPlainText();
lastSavedText[0] = textEditText.toStdString();
// Saving files
ofstream home;
home.open("home.apl");
home << lastSavedText[0];
home.close();
ofstream work;
work.open("work.apl");
work << lastSavedText[1];
work.close();
}
There is 2 solutions.
Easy one
Use simply a timer that will execute the code of your save button. You can set the timer to execute any period of time.
QTimer
But this might cause the software to freeze if this operation takes too much time. In which case, you can put the function that saves inside a thread.
Threads
You can use threads to do that.
Thread, is basically a process that will detach from your main process and can be run at the same time, each thread doing its own work.
Note that to communicate between thread, the safest method is to use signals.
Qt Threads Documentation
Example
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();
}
You can use a QTimer with QtConcurrent::run, and then you get the simplicity with the benefit of running the saving on a different thread you don't need to manage.
Practically, try
QTimer::singleShot(time, this, Qt::TimerType::CoarseTime, QtConcurrent::run(this,&MainWindow::on_saveButton_clicked));
Here's a first approximation using a background thread (for the sake of brevity, it inherits QThread - for your real application, consider decoupling the QThread base-class from this worker thread object. That will also make it possible to give a father-object for t).
class Thread: public QThread {
Q_OBJECT
public:
Thread(QTextEdit *textEdit):textEdit(textEdit) {
QTimer *t = new QTimer;
connect(t, SIGNAL(timeout()), SLOT(saveOnce()));
t->moveToThread(this);
t->start(2000);
}
protected:
QTextEdit *textEdit;
std::string lastSavedText[2];
private slots:
QString text() const { return textEdit->toPlainText(); }
void saveOnce() {
QString textEditText;
QMetaObject::invokeMethod(this,
"text", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QString,textEditText));
lastSavedText[0] = textEditText.toStdString();
// Saving files
ofstream home;
home.open("home.apl");
home << lastSavedText[0];
home.close();
ofstream work;
work.open("work.apl");
work << lastSavedText[1];
work.close();
}
};
Care must be taken, when taking this approach with BlockingQueuedConnection, that the thread does not call invokeMethod while the main thread is waiting for it to exit - then a deadlock happens because the main-thread cannot process the text() queued call anymore.

QThreads never quit although emit Finished reached

Following scenario:
If the user press the "StartMeasure" button I start a new worker thread to avoid blocking behaviour in GUI!
There I start again n new threads for hardware communication (need parallel flow for reasons of speed) -> send/receive something and evaluate the response.
Thread model:
The problem is that the n worker threads (for HW communication) never quits!
Simplified code:
void Tool::on_btn_StartMeasure()
{
TestExecution *testExecution = new TestExecution();
QThread *workerThread = new QThread();
connect(workerThread, &QThread::started, testExecution, &TestExecution::DoTest);
connect(testExecution, &TestExecution::Finished, workerThread, &QThread::quit);
testExecution->moveToThread(workerThread);
workerThread->start();
}
TestExecution Class
class TestExecution : public QObject
{
Q_OBJECT
public:
void DoTest();
signals:
void Finished();
};
void TestExecution::DoTest()
{
std::vector<std::shared_ptr<TestFlow>> objects;
std::vector<QThreads*> workerThreads;
for(int n=0; n<numberOfDevices; n++)
{
workerThreads.push_back(new QThread());
objects.push_back(std::make_shared<TestFlow>());
connect(workerThreads.back(), &QThread::started, objects.back().get(), &TestFlow::DoWork);
connect(objects.back().get(), &TestFlow::Finished, workerThreads.back(), &QThread::quit);
objects.back()->moveToThread(workerThreads.back());
workerThreads.back()->start;
}
// wait for worker threads to finish
for(auto it=workerThreads.begin(); it!=workerThreads.end(); it++)
{
(*it)->wait(30); // wait a maximum of 30s -> for testing
}
// do something with the result <--- NEVER REACHED
emit Finished();
}
TestFlow Class
class TestFlow : public QObject
{
Q_OBJECT
public:
void DoWork();
signals:
void Finished();
};
void TestFlow::DoWork()
{
// do something
emit Finished(); <-- REACHED
}
If I reach the Finished signal in TestFlow he steps into qobject_impl.h to the method QSlotObjectBase::call(Object *r, void **a) and stops there. If I wait long enough (thread timeout of 30s) the workerThreads terminates!
Something wrong or missing here?
Thank you for any help!
UPDATE:
output of qDebug (only 1 worker thread)
Tool::on_btn_StartMeasure(), currentThread QThread(0x601b08), object thread QThread(0x601b08)
TestExecution::DoTest(), currentThread QThread(0x388bf68) , object thread QThread(0x388bf68)
TestFlow::DoWork(), currentThread QThread(0x3958690) , object thread QThread(0x3958690)
Looks ok to me?!

Qt Cannot send events to objects owned by a different thread

I have mainwindow class with such slots in it:
void MainWindow::connect_to_server(const std::string& nickname,
const std::string& ip,
int port)
{
remote_server = new Server(nickname, ip, port);
connect(remote_server, SIGNAL(error()), SLOT(connection_failed()));
auto thread = new QThread(this);
remote_server->moveToThread(thread);
connect(thread, SIGNAL(started()), remote_server, SLOT(establish_connection()));
connect(remote_server, SIGNAL(stop_thread()), thread, SLOT(quit()));
thread->start();
}
void MainWindow::action_disconnect_triggered() {
if (remote_server == nullptr) {
return;
}
remote_server->disconnect();
remote_server = nullptr;
}
And Server class:
void Server::establish_connection() {
master_socket = std::move(
std::unique_ptr<QTcpSocket>(new QTcpSocket(nullptr))
);
master_socket->connectToHost(ip.c_str(), port);
master_socket->waitForConnected(timeout*1000);
if (master_socket->state() == QAbstractSocket::UnconnectedState) {
disconnect();
emit error();
}
emit stop_thread();
}
void Server::disconnect() {
if (master_socket) {
master_socket->disconnectFromHost();
}
}
Initially, I invoke MainWindow::connect_to_server where client successfully connected to remote server. Then, I invoke MainWindow::action_disconnect_triggered and on this stage I get such error:
Btw, when I run it in OS X 10.11, error does not emerge and all works properly. What am I doing wrong and how can I fix it?
remote_server->disconnect(); might be the issue here.
you do not send the event directly, but you call the function and it gets invoked in your main thread.
try QMetaObject::invokeMethod(remote_server, "disconnect", Qt::QueuedConnection); to see if this problem still exists
cheers
You are connecting the objects before moving to thread. That way Qt cant find it in the new one. Just move to thrrqd before everything and this should work.

QTCPSocket start by timer at the same time

I have problem with synchronize(start at the same time QTCPSocket) in my application I have 10 sockets. I have to read data at the similar time to all sockets. At this moment I have something that:
///...///
if(!socket->waitForConnected(-1))
{
qDebug() << "Server not found";
emit serverNotFound();
}else if(socket->state()==QAbstractSocket::ConnectedState){
qDebug() << "Connected"
connect(timer, SIGNAL(timeout()),this,SLOT(connected()));
timer->start(1000);
}
}
On connected signal:
void SocketsClass::connected()
{
sendRequest(socket, messageToServer);
}
The problem is that when the first socket get connected the timer starts for the one.
You can invert your approach. Don't wait for the sockets to get connected. Instead, check if the socket is connected in the slot activated by the timer. In that slot, you can iterate over all sockets and send the message to each of them.
Finally, you should never use Qt's waitForXxx methods, they lead to a horrible pseudo-synchronous code that is very error prone and hard to extend and maintain. Use the signal-slot mechanism instead.
Example:
SocketManager : public QObject {
Q_OBJECT
QTcpSocket m_sockets[10];
QTimer m_timer;
public:
SocketManager(QObject * parent = 0) : QObject(parent) {
... // connect all sockets here
m_timer.start(1000);
connect(&m_timer, &QTimer::timeout, [this]{
for (auto & socket : m_sockets)
if (socket.state() == QAbstractSocket::ConnectedState)
sendRequest(socket, messageToServer);
});
}
};

I just don't understand threads in Qt

Okay, so, here's the deal.
I'm currently writing a small chat messaging simulation / project using SysV IPC, and I use Qt for my client app. What I want is a background thread that would wait on a message queue and send a signal to a GUI thread whenever a new message comes. I have attempted to write the code using QThread inheritance, but it doesn't seem to work, the messages are not shown, and I think I'm missing something here.
As for the code:
ipcEventListener.h:
class IPCEventListener : public QThread
{
Q_OBJECT
public:
IPCEventListener();
void run();
messageWrapper mw;
signals:
void sendChatMsg(MSG_CHAT_MESSAGE cm);
};
ipcEventListener.cpp
IPCEventListener::IPCEventListener()
{
}
void IPCEventListener::run()
{
mutex.lock();
int n = msgrcv(myQueueId, &mw, sizeof(mw)-sizeof(long), 0, IPC_NOWAIT);
mutex.unlock();
if (n>0)
{
snip...
else if (mw.resp.type == MESSAGE)
{
emit sendChatMsg(mw.chatMsg);
}
}
exec();
}
mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
listener = new IPCEventListener(this);
connect(this->listener, SIGNAL(sendChatMsg(MSG_CHAT_MESSAGE)), this, SLOT(message_received(MSG_CHAT_MESSAGE)));
connect(this->ui->pushButton, SIGNAL(clicked()), this, SLOT(on_pushButton_clicked()));
listener->start();
ui->comboBox->addItem("Client");
ui->comboBox->addItem("Room");
}
void MainWindow::message_received(MSG_CHAT_MESSAGE cm)
{
QString formattedMessage = "";
formattedMessage.append("[");
formattedMessage.append(cm.send_time);
formattedMessage.append("] ");
if (cm.msg_type == PRIVATE) formattedMessage.append("[PRIV:] ");
formattedMessage.append(cm.sender);
formattedMessage.append(": ");
formattedMessage.append(cm.message);
formattedMessage.append("\n");
ui->textEdit->append(formattedMessage);
}
What am I missing?
(PS: I know the code probably breaks about a hundred thousand of code conventions, but the deadline is pretty soon and I have to resort to kludges. It's just a school project, though).
You have a logical error in your code. You treat void IPCEventListener::run() as a method which is in a loop and is executing again ang again ang again but it's not. QThread::run() is method where you only initialize your thread and execute exec() function, to start event loop. It means that in current version of your application, you try to receive message just once and then your thread is just waiting for some events, without doing anything with them.
So what you need is an inifite loop in which you will try to receive messages. And don't forget to stop this loop while program closing.