Qt : How to start , stop and delete thread? - qt

=====================OUTDATE=======================
I have a vector which contains threads, each thread is doing a while loop, for some reason, i need to delete the thread in the vector, to do this, i referenced this blog and write a test demo online. But when i write pthread_cancel() in Qt, it reports error: use of undeclared identifier 'pthread_cancel'. I have added LIBS += -lpthread in my .pro file and added #include <thread> in my .cpp file, this error still exists.
=====================UPDATE=====================
Afterwards, i trid to use QThread to achieve my goal. In brief, to start a qthread, i create a Worker class, then instantiate it and move it to QThread, connect &QThread::started with &Worker::process, process is the time consuming method, finnaly call thread->start();; to stop a qthread, i use a bool flag, and i store it with thread id in QMap, if the bool flag is set to false, qthread will break while loop, and emit a finished() signal and this signal will trigger &QThread::quit; to delete the qthread, i connect finished() signal with &Worker::deleteLater and &QThread::deleteLater.
The code are:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h"
#include <QThread>
#include <QMap>
#include <QDebug>
QMap<int, bool> stateController;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
qDebug()<<"Main UI thread id is"<<(int)QThread::currentThreadId();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_add_clicked()
{
int row = ui->tableWidget->rowCount();
ui->tableWidget->insertRow(row);
ui->tableWidget->setItem(row,0,new QTableWidgetItem());//hold a place for blank item, in order to uniformly determine whether its text is empty afterwards
QTableWidgetItem *ckx = new QTableWidgetItem;
ckx->setCheckState(Qt::Unchecked);
ui->tableWidget->setItem(row,1,ckx);
}
void MainWindow::on_tableWidget_cellClicked(int row, int column)
{
if(column == 1){
bool state = ui->tableWidget->item(row,1)->checkState();
if(state){
if(ui->tableWidget->item(row,0)->text().isEmpty()){//only if there is not thread id, then create the thread
QThread* thread = new QThread();
qDebug()<<"New created thread id is"<<(int)thread->currentThreadId(); //I found this thread id will equal to main ui thread id, so i use a sigal/slot to update thread id
Worker* worker = new Worker(row);
worker->moveToThread(thread);
connect( worker, &Worker::updateQThreadIDSignal, this, &MainWindow::updateQThreadID);
connect( thread, &QThread::started, worker, &Worker::process);
connect( worker, &Worker::finished, thread, &QThread::quit);
// automatically delete thread and worker object when work is done:
connect( worker, &Worker::finished, worker, &Worker::deleteLater);
connect( thread, &QThread::finished, thread, &QThread::deleteLater);
//start the thread
thread->start();
}
}else{
if(!ui->tableWidget->item(row,0)->text().isEmpty()){//only if there is already the thread, then stop it
int thread_id = ui->tableWidget->item(row,0)->text().toInt();
// qDebug()<<"get thread id is"<<thread_id;
QMutexLocker locker(&mx);
stateController[thread_id] = false;
ui->tableWidget->setItem(row,0,new QTableWidgetItem());//Because thread will be delete later, so clear its thread id
}
}
}
}
void MainWindow::on_pushButton_delete_clicked()
{
QItemSelectionModel* selectionModel = ui->tableWidget->selectionModel();
QModelIndex index = selectionModel->currentIndex();
if(index.isValid()){
int row = index.row();
if(!ui->tableWidget->item(row,0)->text().isEmpty()){//Before stop the thread, ensure it exist
int thread_id = ui->tableWidget->item(row,0)->text().toInt();
if(ui->tableWidget->item(row,1)->checkState() == Qt::Checked){
//If not stopped yet, firstly stop it
QMutexLocker locker(&mx);
stateController[thread_id] = false;
}
stateController.remove(thread_id);
}
ui->tableWidget->removeRow(row);
}
}
void MainWindow::updateQThreadID(int row, QString id)
{
ui->tableWidget->setItem(row,0,new QTableWidgetItem(QString::number(id.toInt())));
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMutex>
#include <vector>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_add_clicked();
void on_tableWidget_cellClicked(int row, int column);
void on_pushButton_delete_clicked();
public slots:
void updateQThreadID(int row, QString id);
private:
Ui::MainWindow *ui;
QMutex mx;
};
#endif // MAINWINDOW_H
worker.cpp
#include "worker.h"
#include <QThread>
#include <QDebug>
#include <QMutexLocker>
extern QMap<int, bool> stateController;
Worker::Worker(int row) : row_index(row) {
qDebug()<<"Create new worker at thread "<<(int)QThread::currentThreadId();
}
Worker::~Worker() {
qDebug()<<"Destroy the worker instance";
}
void Worker::process() {
thread_id = (int)QThread::currentThreadId();
stateController.insert(thread_id, true);
// qDebug()<<"set thread id is"<<thread_id;
emit updateQThreadIDSignal(row_index,QString("%1").arg(thread_id)); //execute once, because if delete row happen, the row index will change
while(true)
{
count++;
QThread::sleep(2);//simulate time consuming operations
qDebug()<<thread_id<<"processing"<<count;
{//life scope of locker
QMutexLocker locker(&mx);
if(!stateController[thread_id])//In every loop, determine whether it can run, if not, exit the loop
{
return;
}
}
}
emit finished();
}
worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QMutex>
class Worker : public QObject {
Q_OBJECT
public:
Worker(int row);
~Worker();
public slots:
void process();
signals:
void finished();
void updateQThreadIDSignal(int, QString);
private:
int thread_id;
int row_index;
QMutex mx;
int count = 0;
};
#endif // WORKER_H
To watch the demo video, click here.
If you have any advice, please let me know, thanks !
Even though the codes seems to work fine, but i still have a question : Why i wrote &Worker::deleteLater, but I never see the destructor of Worker is called ?

Related

How to properly stop QTimer from another thread

I see that such topic was discussed many times, but can't find clear answer to simple situation. I have Worker class running in the own thread where I create timer and want to stop it due to some condition.
But I am getting the error:
Timers cannot be stopped from another thread
I fill that am missing some core logic in thread working in Qt. Can someone please explain how to resolve that? Thank you.
Here is the main.cpp
#include <QCoreApplication>
#include <QObject>
#include <QtDebug>
#include "worker.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Worker w;
return a.exec();
}
Worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QtDebug>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
private:
QThread t;
QTimer *timer;
int count = 1;
public slots:
void dataTimerFunction();
void onStopTimer(QTimer *t);
signals:
void stopTimer(QTimer *t);
};
#endif // WORKER_H
Worker.cpp
#include "worker.h"
Worker::Worker(QObject *parent) : QObject(parent)
{
this->moveToThread(&t);
QObject::connect(&t, &QThread::finished, this, &QObject::deleteLater);
t.start();
// are we in the new thread from this point, right?
timer = new QTimer();
QObject::connect(timer, &QTimer::timeout, this, &Worker::dataTimerFunction);
QObject::connect(this, &Worker::stopTimer, this, &Worker::onStopTimer);
// QObject::connect(this, &Worker::stopTimer, this, &Worker::onStopTimer, Qt::QueuedConnection); doesn't work as well
timer->start(200);
}
void Worker::dataTimerFunction()
{
qDebug()<<count;
count++;
if (count>5){
emit stopTimer(timer);
//timer->stop();
}
}
void Worker::onStopTimer(QTimer *t)
{
t->stop();
}
The problem is indeed that the timer is not moved to your thread.
Here is a modified worker class, that does what you might want to achieve. It moves the timer also to the thread and then starts and stops it by using signals / slots.
worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QtDebug>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
private:
QThread t;
QTimer *timer;
int count = 1;
signals:
void stopTimer();
void startTimer(int msec);
public slots:
void dataTimerFunction();
};
#endif // WORKER_H
worker.cpp
#include "worker.h"
Worker::Worker(QObject *parent) : QObject(parent)
{
// are we in the new thread from this point, right?
timer = new QTimer(nullptr);
this->moveToThread(&t);
timer->moveToThread(&t);
QObject::connect(&t, &QThread::finished, this, &QObject::deleteLater);
t.start();
QObject::connect(timer, &QTimer::timeout, this, &Worker::dataTimerFunction);
QObject::connect(this, &Worker::stopTimer, timer, &QTimer::stop, Qt::QueuedConnection);
QObject::connect(this, &Worker::startTimer, timer, static_cast<void (QTimer::*)(int)>(&QTimer::start), Qt::QueuedConnection);
startTimer(200);
}
void Worker::dataTimerFunction()
{
qDebug()<<count;
count++;
if (count>5){
stopTimer();
}
}
You should new your timer in your task object slot, not in the task construtor. Stop the timer when your task object is deleted.
My task object header: Worker.h
#pragma once
#include <QObject>
class QTimer;
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
~Worker();
public slots:
void WorkerTaskStartSlot(void);
void TaskFinished(void);
private slots:
void TimerOutToDoSomethingSlot(void);
signals:
void WorkertResultSig(void);
private:
QTimer *m_pTimer = nullptr;
};
Worker.cpp:
#include "Worker.h"
#include <QDebug>
#include <QThread>
#include <QTimer>
Worker::Worker(QObject *parent) : QObject(parent)
{
qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();
}
Worker::~Worker()
{
TaskFinished();
qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();
}
void Worker::WorkerTaskStartSlot(void)
{
qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();
emit WorkertResultSig();
m_pTimer = new QTimer(this);
connect(m_pTimer,&QTimer::timeout,this,&Worker::TimerOutToDoSomethingSlot);
m_pTimer->start(1000);
}
void Worker::TaskFinished(void)
{
qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();
if(m_pTimer)
{
if(m_pTimer->isActive())
{
m_pTimer->stop();
qDebug()<<__FUNCTION__<<"stop timer"<< QThread::currentThreadId();
}
delete m_pTimer;
m_pTimer = nullptr;
}
}
void Worker::TimerOutToDoSomethingSlot(void)
{
qDebug()<<__FUNCTION__<<"threadid"<< QThread::currentThreadId();
}
The controller header: MainWindow.h
#pragma once
#include <QMainWindow>
class QPushButton;
class QWidget;
class Worker;
class QThread;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
void InitCtrl(void);
private slots:
void StartTaskBtnSlot(const bool &checked);
void WorkertResultSlot(void);
private:
QPushButton *m_pStartTaskBtn = nullptr;
QWidget *m_pCenterWidget = nullptr;
Worker *m_pWorker = nullptr;
QThread *m_pWorkerThread = nullptr;
};
MainWindow.cpp
#include "MainWindow.h"
#include <QVBoxLayout>
#include <QPushButton>
#include <QWidget>
#include <QDebug>
#include <QThread>
#include "Worker.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
InitCtrl();
qDebug()<<__FUNCTION__<<"mainwindow thread id"<< QThread::currentThreadId();
}
MainWindow::~MainWindow()
{
}
void MainWindow::InitCtrl(void)
{
m_pCenterWidget = new QWidget(this);
m_pStartTaskBtn = new QPushButton(QStringLiteral("Start"),this);
QVBoxLayout *pvertlayout = new QVBoxLayout();
pvertlayout->addWidget(m_pStartTaskBtn);
m_pCenterWidget->setLayout(pvertlayout);
setCentralWidget(m_pCenterWidget);
m_pStartTaskBtn->setCheckable(true);
connect(m_pStartTaskBtn,&QPushButton::clicked,this,&MainWindow::StartTaskBtnSlot);
}
void MainWindow::StartTaskBtnSlot(const bool &checked)
{
if(checked)
{
m_pStartTaskBtn->setText(QStringLiteral("Close"));
m_pWorkerThread = new QThread();
m_pWorker = new Worker();
// move the task object to the thread BEFORE connecting any signal/slots
m_pWorker->moveToThread(m_pWorkerThread);
connect(m_pWorkerThread, SIGNAL(started()), m_pWorker, SLOT(WorkerTaskStartSlot()));
connect(m_pWorker, SIGNAL(WorkertResultSig()), this, SLOT(WorkertResultSlot()));
// automatically delete thread and task object when work is done:
connect(m_pWorkerThread, SIGNAL(finished()), m_pWorker, SLOT(deleteLater()));
connect(m_pWorkerThread, SIGNAL(finished()), m_pWorkerThread, SLOT(deleteLater()));
m_pWorkerThread->start();
}
else
{
m_pStartTaskBtn->setText(QStringLiteral("Start"));
m_pWorkerThread->quit();
m_pWorkerThread->wait();
}
}
void MainWindow::WorkertResultSlot(void)
{
qDebug()<<__FUNCTION__<<"threadid"<<QThread::currentThreadId();
}
Finally, it will output result like this:
MainWindow::MainWindow mainwindow thread id 0x2bf0
Worker::Worker threadid 0x2bf0
Worker::WorkerTaskStartSlot threadid 0x4af0
MainWindow::WorkertResultSlot threadid 0x2bf0
Worker::TimerOutToDoSomethingSlot threadid 0x4af0
Worker::TimerOutToDoSomethingSlot threadid 0x4af0
Worker::TimerOutToDoSomethingSlot threadid 0x4af0
Worker::TimerOutToDoSomethingSlot threadid 0x4af0
Worker::TaskFinished threadid 0x4af0
Worker::TaskFinished stop timer 0x4af0
Worker::~Worker threadid 0x4af0

Get duration of QMediaPlaylist object

I create a player for audiobooks - when you open a folder with mp3 file, whole list of them is added to playlist and List View. And i have a Label, which suppose to show duration of the whole book. But player->duration returns only a duration of current track, and if i go through the loop and do playlist->next() every step, player->duration returns 0. I know about Phonon and file metadata, but i need to do this without using it.
I am attaching a source code of a working project, you can use. When the player changes the file, the duration is changed and printed out. To loop within files, there is a need to wait till the decoder completes reading the media file. See the code below and the comments.
This is mainwindow.cpp
#include "mainwindow.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
bool done =false;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
player = new QMediaPlayer(this);
playlist = new QMediaPlaylist(player);
playlist->setPlaybackMode(QMediaPlaylist::Sequential);
player->setPlaylist(playlist);
connect(player, &QMediaPlayer::durationChanged, this, &MainWindow::on_durationchanged);
//connect(player,&QMediaPlayer::)
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
playlist->addMedia(QUrl::fromLocalFile("Ar_today.mp3"));
playlist->addMedia(QUrl::fromLocalFile("Ar_sunday.mp3"));
playlist->setCurrentIndex(0); //set the first file
while (done == false) //wait till the duration is read
{
QApplication::processEvents();
}
done = false; playlist->setCurrentIndex(1); //change to the second file
while (done == false) //wait till the duration is read
{
QApplication::processEvents();
} //this way you can loop through files
player->setVolume(80);
player->play();
qDebug() << player->errorString();
}
void MainWindow::on_pushButton_2_clicked()
{
player->stop();
}
void MainWindow::on_durationchanged(qint64 duration)
{
done = true;
qDebug() << "duration = " << player->duration();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMediaPlayer>
#include <QMediaPlaylist>
#include <QDebug>
extern bool done;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_durationchanged(qint64 duration);
private:
Ui::MainWindow *ui;
QMediaPlayer* player;
QMediaPlaylist* playlist;
};
#endif // MAINWINDOW_H
In the form, create 2 buttons, one called pushbutton to play and the other is pushButton_2 to stop

'QMessageBox::critical' : none of the 4 overloads could convert all the argument types

I want to display an error message whenever my independent thread encounters the word "alert1" in a specific .txt file. But I get the above error inside the monitorForAlerts() inside mythread.cpp file. The line expectedly executes if I were to place it inside dialog.cpp. So I guess this is due to non-inheritance of this object. Can you please advise me how to solve this error for the given code?
Here is the code:
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QtCore>
#include "mythread.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
public slots:
private:
Ui::Dialog *ui;
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
};
#endif // DIALOG_H
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QtCore>
#include <QDebug>
#include <QFile>
#include <Windows.h>
#include <QMessageBox>
#include <QTimer>
#define ALERTS_MESSAGE_STORAGE_PATH "E:\\QT1\\simpleGUIThread2\\simpleGUIThread2\\usbAlert.txt"
#define TIMER_VALUE 500
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
void run();
QString name;
void monitorForAlerts();
int exec();
public slots:
signals:
void testSignal(QString message);
public slots:
};
#endif // MYTHREAD_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_pushButton_clicked()
{
ui->label->show();
}
void Dialog::on_pushButton_2_clicked()
{
ui->label->hide();
}
mythread.cpp
#include "mythread.h"
#include "dialog.h"
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
}
void MyThread::run()
{
exec();
}
int MyThread::exec()
{
while(1)
{
monitorForAlerts();
emit(testSignal("hello world!!"));
sleep(1);
}
}
void MyThread::monitorForAlerts()
{
QString response = ALERTS_MESSAGE_STORAGE_PATH;
QFile resp(response);
resp.open(QIODevice::WriteOnly);
resp.close();
QFile resp1(response);
char buf[121];
char buf1[] = "alert1";
char buf2[] = "alert2";
resp1.open(QIODevice::ReadOnly);
while(resp1.size() == 0)
{
Sleep(3000);
}
qint64 lineLength = resp1.readLine(buf, sizeof(buf));
resp1.close();
if(strcmp(buf,buf1) == 0)
{
QFile::remove(ALERTS_MESSAGE_STORAGE_PATH);
qDebug()<<"warning 1!!";
QMessageBox::critical(this,tr("ERROR"),tr("Large change in illumination.\nPlease re-capture reference image.\n"));
}
if(strcmp(buf,buf2) == 0)
{
QFile::remove(ALERTS_MESSAGE_STORAGE_PATH);
qDebug()<<"warning 2!!";
QMessageBox::critical(this,tr("ERROR"),tr("The camera position has been moved or an object is obscuring its view.\nPlease check the device.\n"));
}
}
main.cpp
#include "dialog.h"
#include <QApplication>
#include "mythread.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyThread mThread1;
mThread1.name = "mThread1";
mThread1.start();
Dialog w;
w.show();
return a.exec();
}
LATEST UPDATE*********************************************************************
Hi Zlatomir,
I choose to take your 1st advice. I have created a signal that the thread will emit and connect it to a slot for QDialog. Please let me know if my understanding is correct, because I do not know where to implement the connect(), since the signal is declared in mythread.h and the slot in dialog.h. The connection type argument for connect is Qt::QueuedConnection, so that gui elements from another thread different than main-thread.
are NOT created. Is this statement correct? and where do I place this?
connect( mThread, SIGNAL(alertSignal(QString)), this, SLOT(alertSlot(QString)), Qt::QueuedConnection);
mythread.h
//....
signals:
void alertSignal(QString message);
//....
dialog.h
//....
public slots:
void alertSlot(QString message);
//....
mythread.cpp
//....
if(strcmp(buf,buf1) == 0)
{
QFile::remove(ALERTS_MESSAGE_STORAGE_PATH);
qDebug()<<"warning 1!!";
emit(alertSignal("alert1"));
}
else if(strcmp(buf,buf2) == 0)
{
QFile::remove(ALERTS_MESSAGE_STORAGE_PATH);
qDebug()<<"warning 2!!";
emit(alertSignal("alert2"));
}
dialog.cpp
void Dialog::alertSlot(QString message)
{
if(strcmp(message, "alert1"))
QMessageBox::critical(this,tr("ERROR"),tr("Large change in illumination.\nPlease re-capture reference image.\n"));
else if(strcmp(message, "alert2"))
QMessageBox::critical(this,tr("ERROR"),tr("The camera position has been moved or an object is obscuring its view.\nPlease check the device.\n"));
}
Now if this were correct, how do i implement the connect() and in which file?
The first argument is the problem, in your case this is not a good argument, because there this is a pointer to a MyThread instance, and MyThread is not a QWidget (is not derived from QWidget).
To solve this you can show the QMessageBox::critical from a slot in mainwindow (the Dialog class in your code, there you pass the instance of main-window that is a QWidget) and connect that slot with a signal that you emit from your thread, make sure that the connection type argument for connect is Qt::QueuedConnection, so that you don't try to create gui elements from another thread different than main-thread.
Another option would be to validate the data before you start the second thread and to tell
the user that he needs to provide the right files.
LE: Also check the QThread's documentation for the recommended way to use the class, now it's recommended not to derive from QThread.
LE2 - answer to the update
That connect can be made where ever you can have the two instances that you want to connect, in your case main.cpp is a good place to connect those (don't forget to fully qualify the name for connect: QObject::connect):
//...
MyThread mThread1;
mThread1.name = "mThread1";
mThread1.start();
Dialog w;
QObject::connect( &mThread1, SIGNAL(alertSignal(QString)), &w, SLOT(alertSlot(QString)), Qt::QueuedConnection);
w.show();
//...

Cannot read output using readyReadStandardOutput in Qt

I am using Qt and I am unable to get output of a exe file using readyReadStandardOutput.
Here is my code.
mainwindow.cpp
void MainWindow::on_pushButton_24_clicked()
{
myprocess = new QProcess(this);
myprocess->start("files\\helloworld.exe");
connect(myprocess, SIGNAL(readyReadStandardOutput ()), this, SLOT(outlog()));
}
void MainWindow::outlog()
{
QString abc;
abc = myprocess->readAllStandardOutput();
emit outlogtext(abc);
ui->lineEdit_4->setText(abc);
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtGui>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QProcess *myprocess;
signals:
void outlogtext(QString ver);
private slots:
void outlog();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
The helloworld.exe will just give a output "Hello world" but I cannot see it in the textEdit, whats wrong with my code? I am very new to Qt. Thank you
I got the program working. Below is the code.
mainwindow.hpp
#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
#include <QtGui>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void outlogtext(QString ver);
private slots:
void outlog();
void on_pushButton_24_clicked();
private:
QPushButton* pushButton_24;
QLineEdit* lineEdit_4;
QProcess *myprocess;
};
#endif // MAINWINDOW_HPP
main.cpp
#include <QtCore>
#include <QtGui>
#include <QDebug>
#include "mainwindow.hpp"
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
pushButton_24 = new QPushButton;
connect(pushButton_24, SIGNAL(clicked()),
this, SLOT(on_pushButton_24_clicked()));
lineEdit_4 = new QLineEdit;
QWidget* central = new QWidget;
QLayout* layout = new QVBoxLayout();
layout->addWidget(pushButton_24);
layout->addWidget(lineEdit_4);
central->setLayout(layout);
setCentralWidget(central);
}
MainWindow::~MainWindow()
{
}
void MainWindow::on_pushButton_24_clicked()
{
myprocess = new QProcess(this);
connect(myprocess, SIGNAL(readyReadStandardOutput()),
this, SLOT(outlog()));
myprocess->start("./helloworld.exe");
// For debugging: Wait until the process has finished.
myprocess->waitForFinished();
qDebug() << "myprocess error code:" << myprocess->error();
}
void MainWindow::outlog()
{
QString abc = myprocess->readAllStandardOutput();
emit outlogtext(abc);
lineEdit_4->setText(abc);
}
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow win;
win.show();
return app.exec();
}
helloworld.cpp
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
}
Some things I changed:
After constructing an object, I always connect signals and slots before performing the
actual operation on the object, which might be calling show() for widgets or calling
start() for threads. So I can be sure that I do not miss a signal like started(),
for example.
I ran the program on Linux. There I had to make sure that helloworld.exe was on my
path and I changed the command to ./helloworld.exe. I didn't create the sub-directory
called files as in your example.
The character to separate directories in Qt is the slash /. There are special functions to convert between the Qt-style and the native-style, when you want to display something to the user. Internally always make use of slashes. This even works for Windows programs (many console commands can cope with a slash instead of a backslash, too).
Adding debug output is really, really valuable during development. If the Makefile is
not set up correctly or something breaks, the helloworld.exe might end up in a directory, where it is not expected. Thus, I added code to wait for some time until the process has finished. This does not hurt because helloworld.exe needs just some milliseconds to run. Afterwards, I print the error code of QProcess just to be sure that the program has been found and could be executed. So I can be sure that the executable is on my path, that the executable flag is set, that I have permissions for executing the file etc.
I don't know exactly what causes the problem on your machine. However, comparing your solution to mine, having a look at the error code of QProcess and setting break points inside the slots should help you finding the error.

How to repaint another Qt class

I'm a new bit in Qt...
I have a Qt GUI application (written by me), let's call it QtAPP.exe
When QtAPP.exe running, I will use a QThread and QProcess to execute some external file,
such as player.exe (written in native C).
Here's my question:
In QtAPP.exe, there are 2 classes,
1. QMainWindow - Core of QtAPP.exe
2. QThread - A thread class to execute external things
For now, if I got a finished() signal in that QThread,
how do I to force the QMainWindow to repaint itself ?
Hope somebody can show me some tips, maybe sample code :)
Any suggestion are welcome~
One solution would be to simply connect the finished() signal to a slot in MainWindow whose implementation calls update(). Note that delivery of this signal will be asynchronous because the sender and receiver objects are in different threads.
Here is a working example:
main.cpp
#include <QtGui/QApplication>
#include "stuff.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
stuff.h
#ifndef STUFF_H
#define STUFF_H
#include <QtGui/QMainWindow>
#include <QtCore/QThread>
class QLabel;
class Thread : public QThread
{
Q_OBJECT
public:
Thread(QObject *parent);
void run();
private:
void startWork();
signals:
void workFinished();
};
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow();
public slots:
void startWork();
void workFinished();
private:
QLabel* m_label;
Thread* m_thread;
};
#endif
stuff.cpp
#include <QtCore/QTimer>
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtGui/QVBoxLayout>
#include <QtGui/QPushButton>
#include <QtGui/QLabel>
#include "stuff.h"
#include <QDebug>
// Global variables used for ITC
QWaitCondition buttonPressed;
QMutex mutex;
Thread::Thread(QObject *parent)
: QThread(parent)
{
}
void Thread::run()
{
qDebug() << "Thread::run" << QThread::currentThreadId();
while (1) {
mutex.lock();
buttonPressed.wait(&mutex);
mutex.unlock();
startWork();
}
}
void Thread::startWork()
{
qDebug() << "Thread::startWork" << QThread::currentThreadId();
// Simulate some long-running task
sleep(3);
// Emit a signal, which will be received in the main thread
emit workFinished();
}
MainWindow::MainWindow()
: m_label(new QLabel(this))
, m_thread(new Thread(this))
{
QPushButton *button = new QPushButton("Start", this);
connect(button, SIGNAL(pressed()), this, SLOT(startWork()));
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(button);
layout->addWidget(m_label);
setLayout(layout);
// Create connection across thread boundary
connect(m_thread, SIGNAL(workFinished()), this, SLOT(workFinished()));
m_thread->start();
}
void MainWindow::startWork()
{
// Signal the thread to tell it that the button has been pressed
mutex.lock();
m_label->setText("Started");
buttonPressed.wakeAll();
mutex.unlock();
}
void MainWindow::workFinished()
{
qDebug() << "MainWindow::workFinished" << QThread::currentThreadId();
m_label->setText("Finished");
}

Resources