I updated my question. My project is so simple. I want to create 3 thread. Each thread have different operations. Finally, they should communicate using signal&slot.
I got errors:
-debug/main.o:main.cpp:(.rdata$.refptr._ZTV6mclass[.refptr._ZTV6mclass]+0x0): undefined reference to `vtable for mclass'
-debug/main.o:main.cpp:(.rdata$.refptr._ZN6mclass16staticMetaObjectE[.refptr._ZN6mclass16staticMetaObjectE]+0x0): undefined reference to `mclass::staticMetaObject'
-debug/main.o:main.cpp:(.rdata$.refptr._ZN6mclass7sender2Ev[.refptr._ZN6mclass7sender2Ev]+0x0): undefined reference to `mclass::sender2()'
-debug/main.o:main.cpp:(.rdata$.refptr._ZN6mclass7sender1Ei[.refptr._ZN6mclass7sender1Ei]+0x0): undefined reference to `mclass::sender1(int)'
-debug/mclass.o: in function `mclass::start()':
-\qtProjects\triple\mclass.cpp:20: error: undefined reference to `mclass::sender1(int)
-\qtProjects\build-triple-Desktop_Qt_6_3_2_MinGW_64_bit-Debug/../triple/mclass.cpp:22: undefined reference to `mclass::sender2()'
-collect2.exe: error: ld returned 1 exit status
-[Makefile.Debug:71: debug/triple.exe] Error 1
I change : public QObject to QThread and I got same errors again.
Thanks for helping.
tthread.h file
#ifndef TTHREAD_H
#define TTHREAD_H
#include <QThread>
#include <QObject>
#include <QDebug>
class tthread : public QObject
{
Q_OBJECT
public:
explicit tthread(QObject *parent = nullptr);
void secondd();
void start();
signals:
void sec(int value);
void minu(int value);
public slots:
void minutee(int value);
void hourr(int value);
};
#endif // TTHREAD_H
main.cpp file
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QObject>
#include "tthread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread t1;
QThread t2;
QThread t3;
tthread thread1;
tthread thread2;
tthread thread3;
thread1.moveToThread(&t1);
thread2.moveToThread(&t2);
thread3.moveToThread(&t3);
QObject::connect(&thread1, &tthread::sec, &thread2, &tthread::minutee);
QObject::connect(&thread2, &tthread::minu, &thread3, &tthread::hourr);
thread1.start();
thread2.start();
thread3.start();
return a.exec();
}
tthread.cpp file
#include "tthread.h"
tthread::tthread(QObject *parent) : QObject(parent)
{
}
void tthread::start()
{
qDebug() << "Start" << QThread::currentThread();
}
void tthread::secondd()
{
int counter = 0;
int sign = 1;
while(1)
{
counter++;
qDebug() << "Secondd Counter: " << counter << QThread::currentThread();
if(counter == 5)
{
emit sec(sign);
counter = 0;
qDebug() << "Signal from sec to min " << QThread::currentThread();
}
}
}
void tthread::minutee(int value)
{
int counter2 = 0;
int sign2 = 1;
while(1)
{
qDebug() << "Secondd Value: " << value ;
counter2++;
qDebug() << "Secondd Counter: " << counter2 ;
if(counter2 == 5)
{
emit minu(sign2);
counter2 = 0;
qDebug() << "Signal from sec to min ";
}
}
}
void tthread::hourr(int value)
{
int counter3 = 0;
while(1)
{
qDebug() << "hourr Value: " << value ;
counter3++;
qDebug() << "hourr Counter: " << counter3 ;
if(counter3 == 5)
{
counter3 = 0;
qDebug() << "Signal from min to hourr ";
}
}
}
convert class tthread : public QObject to class tthread : public QThread at first.
Then remove the code with QThread t1, QThread t2, QThread t3. You do not need them since tthread class is a QThread. Start threads with the code below.
->start(QThread::Priority::HighestPriority);
Then override the Run method of QThread
protected:
void run() override;
Move the content inside secondd method to the Run method, and delete secondd method. Probably you do not start the secondd method. That's why nothing works. Run method is automatically called when you call the start method of a QThread.
By the way, you have infinite loops. You need to break the loop when you want to close the application. Do not use while(1). Instead, use while (boolean_param). In the close event of the application, call some signals&slots to set the boolean_param to false to finish the job in the thread.
Related
How are you solving signals/slots mechanism between two classes, if you do not make an object from class(only inherit from class) etc? QTimer, QSerialPort, which is the source of SIGNAL, and in the second class you make connection?
Is such an approach even possible?
In my case. I have two classes usb2can_driver and canview. The usb2can_driver inherit from QSerialPort, which inherit QIODevice contained SIGNAL(readyRead()). This is used in the canview for connection with handler subroutine read_u2c()
I know, in the code is a lot of garbage from testing.
In the canview.cpp void CANview::on_connectPort_released() is made connection, and in the usb2can_driver.cpp int USB2CAN_driver::connectToPort(QString portName) is part of inherit from QSerialPort.
I will be pleasure for every answer. If you think, that question is posed incorrectly, please give me feedback.
usb2can_driver.h
#ifndef USB2CAN_DRIVER_H
#define USB2CAN_DRIVER_H
#include "QSerialPort"
#include "QTimer"
#include "QObject"
#include "QSignalSpy"
class USB2CAN_driver : public QSerialPort
{
Q_OBJECT;
public:
USB2CAN_driver();
//virtual ~USB2CAN_driver();
//QSerialPort *port_USB2CAN = new QSerialPort();
int temporary_init_Counter = 0;
int init();
void USB_LoopBack();
void Boot_Mode();
void Config_Mode();
void Normal_Mode();
void LoopBack_Mode();
QByteArray Get_Mode();
void WriteReg(QByteArray regAdress, QByteArray value[]);
QByteArray WriteCMD(QByteArray CMD_name, QByteArray value);
QByteArray ReadReg(QByteArray regAdress);
QString portName;
int connectToPort(QString portName);
int disconnectedFromPort();
QTimer *tim;
int tim_counter = 0;
public: signals:
void readyRead();
private slots:
QByteArray read_USB2CAN();
void initSend();
//void timEvent();
};
#endif // USB2CAN_DRIVER_H
usb2can_driver.cpp
#include "USB2CAN_define.h"
#include "QSerialPort"
#include "QSerialPort"
#include "QObject"
#include "QDebug"
#include <QSignalSpy>
USB2CAN_driver::USB2CAN_driver()
{
//USB2CAN_driver:: = new QSerialPort();
//USB2CAN_driver::Baud9600;
//USB2CAN_driver::AllDirections;
//qDebug() << "Open port" << USB2CAN_driver::open(QIODevice::ReadWrite);
}
/*
USB2CAN_driver::~USB2CAN_driver(){
QObject::disconnect(USB2CAN_driver::,SIGNAL(readyRead()),USB2CAN_driver::,SLOT(QByteArray read_USB2CAN()));
}
*/
int USB2CAN_driver::connectToPort(QString portName){
//port_USB2CAN.setPortName(portName);
USB2CAN_driver::setPortName(portName);
USB2CAN_driver::setBaudRate(QSerialPort::Baud9600,QSerialPort::AllDirections);
USB2CAN_driver::setPortName(portName);
//Reimplemented separately as signal of driver. !!!
//qDebug() << "connect S&S in the driver, status: " << QObject::connect(this,SIGNAL(readyRead),this,SLOT(read_USB2CAN));
//qDebug() << "connect S&S in the driver, status: " << connect(this,SIGNAL(readyRead()),this,SLOT(read_USB2CAN()));
//QSignalSpy spy(this, SIGNAL(readyRead()));
//qDebug() << "from driver" << spy.wait(5000) << "----" << spy.signal();
tim = new QTimer;
return USB2CAN_driver::open(QIODevice::ReadWrite);
}
/*
void USB2CAN_driver::timEvent(){
qDebug() << "Tim" << tim_counter++;
if(tim_counter >= 5){
tim_counter = 0;
tim->stop();
}
}
*/
int USB2CAN_driver::disconnectedFromPort(){
//QObject::disconnect(this,SIGNAL(readyRead()),this,SLOT(read_USB2CAN()));
USB2CAN_driver::close();
if(USB2CAN_driver::isOpen()){
return 1;
}
else{
qDebug() << "------------------Port is diconected-----------------";
return 0;
}
}
void USB2CAN_driver::USB_LoopBack(){
}
void USB2CAN_driver::Boot_Mode(){
}
void USB2CAN_driver::Config_Mode(){
}
void USB2CAN_driver::Normal_Mode(){
}
void USB2CAN_driver::LoopBack_Mode(){
}
QByteArray USB2CAN_driver::Get_Mode(){
while(!USB2CAN_driver::waitForBytesWritten(300)){
USB2CAN_driver::write(getMode);
}
return USB2CAN_driver::readAll(); //In progress...
}
void USB2CAN_driver::WriteReg(QByteArray regAdress, QByteArray value[]){
int length = regAdress.length() + value->length();
QByteArray len;
len.setNum(length);
QByteArray sendVal[] = { writeReg, len, regAdress, *value };
QByteArray sendData;
sendData.fromRawData(*sendVal,sizeof (sendVal));
while(!USB2CAN_driver::waitForBytesWritten(300)){
USB2CAN_driver::write(sendData);
}
}
QByteArray USB2CAN_driver::WriteCMD(QByteArray CMD_name, QByteArray value){
}
QByteArray USB2CAN_driver::ReadReg(QByteArray regAdress){
}
int USB2CAN_driver::init(){
}
QByteArray USB2CAN_driver::read_USB2CAN(){
qDebug() <<"From driver RX" << USB2CAN_driver::readAll();
return USB2CAN_driver::readAll();
}
void USB2CAN_driver::initSend(){
}
canview.h
#define CANVIEW_H
#include <QDialog>
#include <usb2can_driver.h>
namespace Ui {
class CANview;
}
class CANview : public QDialog
{
Q_OBJECT
public:
explicit CANview(QWidget *parent = nullptr);
~CANview();
USB2CAN_driver *u2c;
QTimer *time;
private: signals:
friend void USB2CAN_driver::readyRead();
private slots:
void on_connectPort_released();
void on_pushButton_released();
QByteArray read_u2c();
void timerSubrutine();
private:
Ui::CANview *ui;
};
#endif // CANVIEW_H
canview.cpp
#include "ui_canview.h"
#include "QSignalSpy"
CANview::CANview(QWidget *parent) : QDialog(parent),ui(new Ui::CANview)
{
ui->setupUi(this);
u2c = new USB2CAN_driver;
}
CANview::~CANview()
{
delete ui;
}
//connect fcn
void CANview::on_connectPort_released()
{
if(u2c->isOpen()){
u2c->disconnectedFromPort();
}
else{
u2c->connectToPort(ui->inputNamePort->text());
qDebug() << "Connect rx task, status: " << connect(???,SIGNAL(readyRead()),this,SLOT(read_u2c()));
connect(u2c->tim,SIGNAL(timeout()),this,SLOT(timerSubrutine()));
u2c->tim->start(800);
}
//Controll of opened/close port
if(u2c->isOpen()){
ui->connectPort->setCheckState(Qt::CheckState::Checked);
}
else{
ui->connectPort->setCheckState(Qt::CheckState::Unchecked);
}
}
//Send function
void CANview::on_pushButton_released()
{
u2c->write(ui->TX_textBrowser->toPlainText().toLatin1(),static_cast<int>(ui->TX_textBrowser->toPlainText().length()));
qDebug() << "Send: " << static_cast<int> (ui->TX_textBrowser->toPlainText().length());
QSignalSpy spy(u2c,SIGNAL(readyRead()));
qDebug() << spy.signal() << spy.signalsBlocked() << spy.isValid();
}
QByteArray CANview::read_u2c(){
qDebug() << "RX:" << u2c->readAll();
ui->RX_textBrowser_2->setPlainText(u2c->readAll());
return u2c->readAll();
}
void CANview::timerSubrutine(){
qDebug() << "TimerEvent" << u2c->tim_counter++;
if(u2c->tim_counter >= 5){
u2c->tim->stop();
}
}```
It is impossible to connect classes as mentioned by #Scheff's Cat.
The solution is do not use inheritance from QSerialPort in the usb2can_driver. If I want connect signal of QSerialPort with slot (which is part of second class), I had to create a object from QSerialPort in the constructor of USB2CAN_driver.
This object to allow use signal/slot mechanism.
So in short: USB2CAN_driver:: was replaced by object port_USB2CAN
For the connection in the second class (canview), i used this syntax:
connect(u2c->port_USB2CAN,SIGNAL(readyRead()),this,SLOT(read_u2c()));
Thank to Scheff's Cat, your comment was helpfully. This solution is working, but if somebody see the non-standard syntax please warning me.
I have been trying to get this simple example using threads activated by pushbuttons to work. It is based off of the solution in the question below:
How to implement frequent start/stop of a thread (QThread)
The main differences between the example solution above and my code below are:
I used a QWidget instead of MainWindow
I changed the name of signals for clarity
My code contains debugging information
I experimented with eliminating the signals created by worker as the didn't appear to do anything
It appears that the start/stop signals are not triggering their corresponding slots, but I am not experienced enough to troubleshoot why.
Additionally, I am unsure of the purpose of the signal:
SignalToObj_mainThreadGUI()
Is that just something that could be used and is not?
I have been trying to get this code to work for some time, so any help would be greatly appreciated.
main.cpp
#include "threadtest.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ThreadTest w;
w.show();
return a.exec();
}
threadtest.h
#include <QWidget>
#include <QThread>
#include "worker.h"
namespace Ui
{
class ThreadTest;
}
class ThreadTest : public QWidget
{
Q_OBJECT
public:
explicit ThreadTest(QWidget *parent = 0);
~ThreadTest();
signals:
void startWorkSignal();
void stopWorkSignal();
private slots:
void on_startButton_clicked();
void on_stopButton_clicked();
private:
Ui::ThreadTest *ui;
worker *myWorker;
QThread *WorkerThread;
};
threadtest.cpp
#include "threadtest.h"
#include "ui_threadtest.h"
ThreadTest::ThreadTest(QWidget *parent) :
QWidget(parent),
ui(new Ui::ThreadTest)
{
ui->setupUi(this);
myWorker = new worker;
WorkerThread = new QThread;
myWorker->moveToThread(WorkerThread);
connect(this,
SIGNAL(startWorkSignal()),
myWorker,
SLOT(StartWork())
);
connect(this,
SIGNAL(stopWorkSignal()),
myWorker,
SLOT(StopWork())
);
//Debug
this->dumpObjectInfo();
myWorker->dumpObjectInfo();
}
ThreadTest::~ThreadTest()
{
delete ui;
}
void ThreadTest::on_startButton_clicked()
{
qDebug() << "startwork signal emmitted";
emit startWorkSignal();
}
void ThreadTest::on_stopButton_clicked()
{
qDebug() << "stopwork signal emmitted";
emit stopWorkSignal();
}
worker.h
#include <QObject>
#include <QDebug>
class worker : public QObject {
Q_OBJECT
public:
explicit worker(QObject *parent = 0);
~worker();
signals:
void SignalToObj_mainThreadGUI();
//void running();
//void stopped();
public slots:
void StopWork();
void StartWork();
private slots:
void do_Work();
private:
volatile bool running, stopped;
};
worker.cpp
#include "worker.h"
worker::worker(QObject *parent) : QObject(parent), stopped(false),
running(false)
{
qDebug() << "running: " << running;
qDebug() << "stopped: " << stopped;
}
worker::~worker() {}
void worker::do_Work()
{
qDebug() << "inside do Work";
emit SignalToObj_mainThreadGUI();
if (!running || stopped) return;
// actual work here
/*
for (int i = 0; i < 100; i++)
{
qDebug() << "count: " + i;
}
*/
QMetaObject::invokeMethod(this, "do_Work", Qt::QueuedConnection);
}
void worker::StopWork()
{
qDebug() << "inside StopWork";
stopped = true;
running = false;
//emit stopped();
}
void worker::StartWork()
{
qDebug() << "inside StartWork";
stopped = false;
running = true;
//emit running();
do_Work();
}
You should write
WorkerThread->start();
Or you can use the thread of the ThreadTest object instead the WorkerThread (in this case the WorkerThread is needless):
myWorker->moveToThread(thread()); // this->thread
The slots are not triggered, because you have moved myWork to the thread WorkerThread, but didnot run an event loop in that thread. In threadtest.cpp, add
WorkerThread .start();
after
myWorker = new worker;
WorkerThread = new QThread;
myWorker->moveToThread(WorkerThread);
I am trying to implement a simple state machine within a console application. The signals that are supposed to trigger the state transitions are being emitted, however, the state machine is not reacting to those signals.
This state machine works perfectly when running within a QApplication (i.e. a GUI application), however I am wanting to develop a console application. I suspect there is an issue in the way I have implemented the event loop, as the QStateMachine is not emitting the started() signal.
What is the correct way to execute the application in order for the state machine to function correctly?
main.cpp:
#include <QCoreApplication>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test test;
QMetaObject::invokeMethod( &test, "Run", Qt::QueuedConnection );
return a.exec();
}
test.h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QStateMachine>
class Test : public QObject
{
Q_OBJECT
public:
explicit Test(QObject *parent = 0) : QObject(parent) {}
public slots:
void Run();
signals:
void stateChanged();
void debugSignal();
private:
void buildStateMachine();
QStateMachine machine;
private slots:
void runS1();
void runS2();
void runS3();
void debugSlot();
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <QDebug>
void Test::Run()
{
buildStateMachine();
QTextStream qin(stdin);
while (true)
{
QString line = qin.readLine();
qDebug() << "line: " << line;
if (line == "A")
{
qDebug() << "emit stateChanged signal";
emit stateChanged();
}
else if (line == "B")
{
qDebug() << "emit debugSignal";
emit debugSignal();
}
}
}
void Test::buildStateMachine()
{
connect(&machine, SIGNAL(started()), this, SLOT(debugSlot())); // doesn't seem to get triggered... (why is machine not starting?)
connect(this, SIGNAL(debugSignal()), this, SLOT(debugSlot())); // works as expected
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
QState *s3 = new QState(&machine);
s1->addTransition(this, SIGNAL(stateChanged()), s2);
s2->addTransition(this, SIGNAL(stateChanged()), s3);
s3->addTransition(this, SIGNAL(stateChanged()), s1);
connect(s1, SIGNAL(entered()), this, SLOT(runS1())); // these are never triggered
connect(s2, SIGNAL(entered()), this, SLOT(runS2()));
connect(s3, SIGNAL(entered()), this, SLOT(runS3()));
s1->assignProperty(&machine, "state", 1);
s2->assignProperty(&machine, "state", 2);
s3->assignProperty(&machine, "state", 3);
machine.setInitialState(s1);
machine.start();
}
void Test::runS1()
{
qDebug() << "entered state S1";
}
void Test::runS2()
{
qDebug() << "entered state S2";
}
void Test::runS3()
{
qDebug() << "entered state S3";
}
void Test::debugSlot()
{
qDebug() << "slot was triggered!";
}
Solved my problem. The issue was caused by the infinite while loop. The slot can only be called once the Run function ends, which obviously never occurs.
Here is a working solution. The change to the Qt 5 style signals and slots syntax is optional. If main.cpp is kept as-is from the version shown in the question above, the application will not quit correctly.
main.cpp:
#include <QCoreApplication>
#include <QTimer>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test test;
QObject::connect(&test, &Test::finished, &a, &QCoreApplication::quit, Qt::QueuedConnection);
QTimer::singleShot(0, &test, &Test::Run);
return a.exec();
}
test.h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QStateMachine>
class Test : public QObject
{
Q_OBJECT
public:
explicit Test(QObject *parent = 0);
signals:
void next_state();
void finished();
private:
void buildStateMachine();
QStateMachine machine;
public slots:
void Run();
void runS1();
void runS2();
void runS3();
void debugSlot();
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <iostream>
#include <QTextStream>
Test::Test(QObject * parent) : QObject(parent)
{
buildStateMachine();
}
void Test::Run()
{
QTextStream qin(stdin);
std::cout << "line: ";
QString line = qin.readLine();
if (line == "A")
{
std::cout << "emit stateChanged signal" << std::endl;
emit next_state();
}
else if (line == "q")
{
emit finished();
}
else
{
Run();
}
}
void Test::buildStateMachine()
{
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
QState *s3 = new QState(&machine);
s1->addTransition(this, SIGNAL(next_state()), s2);
s2->addTransition(this, SIGNAL(next_state()), s3);
s3->addTransition(this, SIGNAL(next_state()), s1);
connect(s1, &QState::entered, this, &Test::runS1);
connect(s2, &QState::entered, this, &Test::runS2);
connect(s3, &QState::entered, this, &Test::runS3);
machine.setInitialState(s1);
machine.start();
}
void Test::runS1()
{
std::cout << "entered state S1" << std::endl;
Run();
}
void Test::runS2()
{
std::cout << "entered state S2" << std::endl;
Run();
}
void Test::runS3()
{
std::cout << "entered state S3" << std::endl;
Run();
}
void Test::debugSlot()
{
std::cout << "debug slot was triggered!" << std::endl;
Run();
}
Sorry for basic question. I'm trying to show json in QPlainTextWidget. I have api function which have console output and contains all needed data. Looks like that:
int iperf_run_server(struct iperf_test *test)
{
int result, s, streams_accepted;
fd_set read_set, write_set;
struct iperf_stream *sp;
struct timeval now;
struct timeval* timeout;
......
if (test->json_output)
if (iperf_json_start(test) < 0)
return -1;
if (test->json_output) {
cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
} else if (test->verbose) {
iprintf(test, "%s\n", version);
iprintf(test, "%s", "");
fflush(stdout);
printf("%s\n", get_system_info());
}
.....
cleanup_server(test);
if (test->json_output) {
if (iperf_json_finish(test) < 0)
return -1;
}
....
return 0;
}
For now I have first thread with my gui, and second thread, contains class which run this function on a signal. All things works normally, but i'm not fully understand, how I can "stop" iperf_run_server for "reading/buffering" output, without any changes in api.
The simplest thing to do would be to collect each message in a string, and emit a signal from the object running in the second thread. You can connect that signal to a slot in an object in the GUI thread.A zero-timeout timer is invoked each time the event loop is done processing other events - it is a useful mechanism to leverage to run things "continuously".
For example:
#include <QApplication>
#include <QPlainTextEdit>
#include <QThread>
#include <QBasicTimer>
#include <QTextStream>
//! A thread that's always safe to destruct.
class Thread : public QThread {
private:
// This is a final class.
using QThread::run;
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() {
quit();
wait();
}
};
class IperfTester : public QObject {
Q_OBJECT
struct Test { int n; Test(int n_) : n(n_) {} };
QList<Test> m_tests;
QBasicTimer m_timer;
public:
IperfTester(QObject * parent = 0) : QObject(parent) {
for (int i = 0; i < 50; ++i) m_tests << Test(i+1);
}
//! Run the tests. This function is thread-safe.
Q_SLOT void runTests() {
QMetaObject::invokeMethod(this, "runTestsImpl");
}
Q_SIGNAL void message(const QString &);
private:
Q_INVOKABLE void runTestsImpl() {
m_timer.start(0, this);
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
if (m_tests.isEmpty()) {
m_timer.stop();
return;
}
runTest(m_tests.first());
m_tests.removeFirst();
}
void runTest(Test & test) {
// do the work
QString msg;
QTextStream s(&msg);
s << "Version:" << "3.11" << "\n";
s << "Number:" << test.n << "\n";
emit message(msg);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPlainTextEdit log;
// This order is important: the thread must be defined after the object
// to be moved into the thread.
IperfTester tester;
Thread thread;
tester.moveToThread(&thread);
thread.start();
log.connect(&tester, SIGNAL(message(QString)), SLOT(appendPlainText(QString)));
log.show();
tester.runTests();
return a.exec();
// Here, the thread is stopped and destructed first, following by a now threadless
// tester. It would be an error if the tester object was destructed while its
// thread existed (even if it was stopped!).
}
#include "main.moc"
This problem is bothering me because it should work, but sadly it does not.
What i try to achieve is to read the standard output of a certain process and make another process handle it i.e. print it out.
The process that produces output looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int main() {
for (int i = 0; i < 100; i++) {
printf("yes %d\n",i);
fflush(stdout);
sleep(1);
}
return 0;
}
The process is started in another application like this:
#include <QProcess>
...
QProcess * process = new QProcess;
SomeClass * someClass = new SomeClass(process);
connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyRead()));
process->start("../Test/Test",QStringList());
if (!process->waitForStarted(4000)) {
qDebug() << "Process did not start.";
}
...
void SomeClass::onReadyRead() {
qDebug() << "Reading:" << process->readAllStdOutput();
}
My expected output would be:
Reading: yes 0
Reading: yes 1
...
Reading: yes 99
However i get no output at all.
And when i use QCoreApplication i get all the output but not through the signal/slot but directly in the console.
I dont understand because it works in another application that uses Qt 4.8.
My question is, is anyone experiencing the same problem or does anyone know how i can get the expected behaviour?
Your problem in the answer you provide lies in misunderstanding how the reading works. It simply returns whatever data you've got there, whether there are line endings or not. By spawning a thread and sleeping between lines, you're effectively sending the inter-process data in line-sized chunks since the pipe is flushed when you wait long enough.
So, your answer, while working, is not really how one should do it. You need to use readLine() to chop the incoming data into lines. Below is an example with following qualities:
There's just one executable :)
Only Qt apis are used. This reduces the runtime memory consumption.
Both processes cleanly terminate.
The amount of code is as minimal as practicable.
// https://github.com/KubaO/stackoverflown/tree/master/questions/process-17856897
#include <QtCore>
QTextStream out{stdout};
class Slave : public QObject {
QBasicTimer m_timer;
int m_iter = 0;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() == m_timer.timerId()) {
out << "iteration " << m_iter++ << endl;
if (m_iter > 35) qApp->quit();
}
}
public:
Slave(QObject *parent = nullptr) : QObject(parent) {
m_timer.start(100, this);
}
};
class Master : public QObject {
Q_OBJECT
QProcess m_proc{this};
Q_SLOT void read() {
while (m_proc.canReadLine()) {
out << "read: " << m_proc.readLine();
out.flush(); // endl implicitly flushes, so we must do the same
}
}
Q_SLOT void started() {
out << "started" << endl;
}
Q_SLOT void finished() {
out << "finished" << endl;
qApp->quit();
}
public:
Master(QObject *parent = nullptr) : QObject(parent) {
connect(&m_proc, SIGNAL(readyRead()), SLOT(read()));
connect(&m_proc, SIGNAL(started()), SLOT(started()));
connect(&m_proc, SIGNAL(finished(int)), SLOT(finished()));
m_proc.start(qApp->applicationFilePath(), {"dummy"});
}
};
int main(int argc, char *argv[])
{
QCoreApplication app{argc, argv};
if (app.arguments().length() > 1)
new Slave{&app}; // called with an argument, this is the slave process
else
new Master{&app}; // no arguments, this is the master
return app.exec();
}
#include "main.moc"
Based on the code you've posted, you're connecting to the class slot with this: -
connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyReadStdOutput()));
But the function in the class is declared like this: -
void SomeClass::onReadyRead();
If you're expecting onReadyRead to be called, then you should be calling it in the SLOT, rather than onReadyReadStdOutput. So change your connection to: -
connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyRead()));
Well i solved my problem.
If the process is started with startDetached() it will not receive the signals from readyRead(), readyReadStandardOutput() and readyReadStandardError().
So just starting it with start() solved the problem.
However i noticed that if i start and do the while loop and prints in main() it will read everything at once even if it ends with \n. So i started the while loop in a thread and that problem was also solved. Everything prints as expected.
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
public:
explicit Thread(QObject *parent = 0) : QThread(parent) {}
protected:
void run() {
for (int i = 0; i < 100; i++) {
std::cout << "yes" << i << std::endl;
msleep(200);
}
exit(0);
}
};
int main(int argc, char ** argv) {
QCoreApplication app(argc,argv);
Thread * t = new Thread();
t->start();
return app.exec();
}
TestP main.cpp
#include <QProcess>
#include <iostream>
class Controller : public QObject
{
Q_OBJECT
private:
QProcess * process;
public:
Controller(QObject *parent = 0) :
QObject(parent), process(new QProcess) {}
void init(const QString &program) {
connect(process,SIGNAL(readyRead()),this,SLOT(readStdOut()));
connect(process,SIGNAL(started()),this,SLOT(onStarted()));
connect(process,SIGNAL(finished(int)),this,SLOT(onFinished(int)));
process->start(program);
}
private slots:
void readStdOut() {
std::cout << "YES " << QString(process->readAllStandardOutput()).toUtf8().constData() << std::endl;
}
void onStarted(){
std::cout << "Process started" << std::endl;
}
void onFinished(int) {
std::cout << "Process finished: " << signal << std::endl;
}
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
Controller c;
c.init("../Test/Test");
return a.exec();
}