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"
Related
I'm trying to create my own worker thread for I/O operations. As far as I know the low-level management of the serial port is already done in a separate thread, but I want to put my code in another one to handle timeouts and other middleware stuff.
Here the complete code of my class. Please note it's my first attempt with Qt's multi-threading.
EDIT
The code is updated without sub-classing QThread
#ifndef WORKERSERIAL_H
#define WORKERSERIAL_H
#include <QObject>
#include <QThread>
#include <QQueue>
#include <QMutex>
#include <QTimer>
#include <QDebug>
#include "myserial.h"
class WorkerSerial : public QObject
{
Q_OBJECT
public:
explicit WorkerSerial(QObject *parent = 0) : QObject(parent)
{
m_serial = new MySerial(this);
connect(m_serial, &MySerial::lineReceived, this, &WorkerSerial::serial_LineReceived);
m_stop = false;
}
bool open(QString port, quint32 baudrate) { return m_serial->open(port, baudrate); }
bool isOpen() { return m_serial->serial()->isOpen(); }
QStringList ports() { return m_serial->ports(); }
private:
MySerial *m_serial;
QQueue<QString> m_queue;
QMutex m_mutex;
bool m_stop;
private slots:
void serial_LineReceived(QByteArray line)
{
emit lineReceived(line);
}
signals:
void lineReceived(QByteArray line);
public slots:
void close() { m_serial->close(); }
void send(QString data) { m_mutex.lock(); m_queue.enqueue(data); m_mutex.unlock(); }
void stop() { m_mutex.lock(); m_stop = true; m_mutex.unlock(); }
void run() {
forever {
m_mutex.lock();
if (m_stop)
{
qDebug() << "Closing...";
QTimer::singleShot(0, this, SLOT(close()));
while(m_serial->serial()->isOpen());
m_mutex.unlock();
return;
}
if (!m_queue.isEmpty())
{
QString data = m_queue.dequeue();
qDebug() << m_serial->sendMessage(data.toLocal8Bit(), true);
}
m_mutex.unlock();
}
}
};
#endif // WORKERSERIAL_H
The MySerial class it's a convenient wrapper for QSerialPort. Here the relevant functions:
bool MySerial::open(QString port, quint32 baudrate) {
m_serial->setPortName(port);
if (!m_serial->open(QIODevice::ReadWrite)) return false;
m_serial->setDataBits(QSerialPort::Data7);
m_serial->setParity(QSerialPort::EvenParity);
m_serial->setStopBits(QSerialPort::TwoStop);
m_serial->setBaudRate(baudrate);
m_serial->setFlowControl(QSerialPort::NoFlowControl);
return true;
}
QByteArray MySerial::sendMessage(QByteArray data) {
m_serial->write(data);
return data;
}
Finally, here how I start, use and close the worker:
QThread *workerThread = new QThread;
m_worker = new WorkerSerial;
m_worker->moveToThread(workerThread);
workerThread->start();
QTimer::singleShot(0, m_workerDMX, SLOT(run()));
// ...
m_worker->open("COM1", 250000));
// ...
m_worker->send("Hello World!");
// ...
m_worker->stop();
workerThread->quit();
workerThread->wait(1000);
// Here the application ends
This is the output:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x19882160), parent's thread is QThread(0x187fe598), current thread is WorkerSerial(0x19d4c9b8)
[this error appears when it calls the SendMessage() function]
"Hello World!"
Closing...
QMutex: destroying locked mutex
QThread: Destroyed while thread is still running
[the two errors above are due the singleSlot() isn't executed and thus the serial won't close]
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
It seems clear I've messed up something!
Would you help me to understand my mistakes?
Here is (an incomplete) implementation, which should give you an idea of how to simplify your code. Some additional notes are below:
class WorkerSerial : public QObject
{
Q_OBJECT
public:
explicit WorkerSerial(QObject *parent = 0) : QObject(parent)
{
// no need for slot -- connect directly to our signal
connect(&m_serial, &MySerial::lineReceived, this, &WorkerSerial::lineReceived);
}
bool open(QString port, quint32 baudrate) { return m_serial.open(port, baudrate); }
bool isOpen() { return m_serial.serial().isOpen(); }
QStringList ports() { return m_serial.ports(); }
private:
// doesn't need to be a pointer
MySerial m_serial;
signals:
void lineReceived(const QByteArray& line);
public slots:
void close() { m_serial.close(); }
void send(const QByteArray& data)
{
// "lengthy" function call
qDebug() << m_serial.sendMessage(data, true);
}
};
class Sender : public QObject
{
Q_OBJECT
public:
void send(const QByteArray& data) { emit sig_send(data); }
signals:
void sig_send(const QByteArray& data);
}
...
WorkerSerial m_worker;
m_worker.open("COM1", 250000));
QThread m_thread;
m_worker.moveToThread(&m_thread);
m_thread.start();
Sender m_sender;
QObject::connect(&m_sender, &Sender::sig_send, &m_worker, &WorkerSerial::send, Qt::QueuedConnection);
m_sender.send("Hello World!");
m_thread.quit();
m_thread.wait();
m_worker.close();
Additional notes:
I've added a Sender class that queues calls to WorkerSerial::send() on a different thread. If you have a signal in your code, which triggers calls to WorkerSerial::send() you don't need this class.
Once you move m_worker to another thread, it is unsafe to access any of its methods, unless (a) they are guaranteed to be thread-safe; or (b) you serialize (protect) access to m_serial with a mutex.
NOTE: this is not a fully working solution (yet). But I think it's better to post it as an answer than add another piece of code in the original question.
I managed to a different approach. Perhaps it looks weird... but it should have some advantages:
it guarantees the order of the messages to be transmitted
using a QQueue I can know the queue size or other information about
the "variable shadowing" mechanism should allow to safely communicate with the thread
Here the code, below the two last issues:
#ifndef WORKERSERIAL_H
#define WORKERSERIAL_H
#include <QObject>
#include <QThread>
#include <QQueue>
#include <QMutex>
#include <QDebug>
#include "myserial.h"
class WorkerSerial : public QThread
{
Q_OBJECT
public:
explicit WorkerSerial(QObject *parent = 0, int timeout = 0) : QThread(parent), timeout(timeout)
{
quit = false;
command = None;
}
~WorkerSerial()
{
mutex.lock();
quit = true;
mutex.unlock();
wait();
}
void open(QString port, quint32 baudrate)
{
mutex.lock();
this->port = port;
this->baudrate = baudrate;
command = Open;
mutex.unlock();
}
void close()
{
mutex.lock();
command = Close;
mutex.unlock();
}
void stop()
{
mutex.lock();
command = Quit;
mutex.unlock();
}
private:
enum Commands
{
None,
Open,
Close,
Quit
};
QQueue<QString> m_queue;
QMutex mutex;
int timeout;
int command;
QString port;
int baudrate;
QString request;
bool quit;
signals:
void portStateChanged(bool isOpen);
void lineReceived(QByteArray line);
public slots:
void send(QString data)
{
mutex.lock();
m_queue.enqueue(data);
mutex.unlock();
}
void run()
{
MySerial serial;
QString _port;
int _baudrate;
int _timeout;
QString _request = "";
int _command = None;
connect(&serial, &MySerial::lineReceived, this, &WorkerSerial::lineReceived);
mutex.lock();
_timeout = timeout;
mutex.unlock();
forever {
mutex.lock();
_command = command;
command = None;
if (!m_queue.isEmpty()) _request = m_queue.dequeue();
mutex.unlock();
switch (_command) {
case Open:
mutex.lock();
_port = port;
_baudrate = baudrate;
mutex.unlock();
if (serial.open(_port, _baudrate)) emit portStateChanged(true);
else portStateChanged(false);
break;
case Close:
serial.close();
emit portStateChanged(serial.serial()->isOpen());
break;
case Quit:
serial.close();
return;
break;
default:
break;
}
if (!_request.isEmpty())
{
qDebug() << serial.sendMessage(_request.toLocal8Bit(), true);
_request = "";
}
msleep(1);
}
}
};
#endif // WORKERSERIAL_H
Here how to use it:
WorkerSerial *worker = new WorkerSerial(this);
connect(worker , &WorkerSerial::lineReceived, this, &MainWindow::lineReceived);
connect(worker , &WorkerSerial::portStateChanged, this, &MainWindow::portStateChanged);
worker->start();
// ..
worker->open("COM2", 250000);
worker->send("Hello World");
// ..
worker->stop();
worker->wait(1000);
It "works" but there are two main issues:
the serialport is opened but the data are not actually transmitted. The MySerial class is working because if I do the following, bypassing the WorkerSerial:
MySerial serial;
serial.open("COM2", 250000);
serial.sendMessage("Hello World!", true);
the data is sent! Hence there is still something wrong in my WorkerSerial.
due to the forever loop, I need to insert a small delay, otherwise the CPU will run up to 100% doing nothing.
I am using QLocalSocket and QLocalServer for inter process communication on Windows 7 using VS 2010 and Qt 5.5.1.
After sending over 256 messages to the other process the destructor in CIPSocket freezes. I traced the problem to a signal-slot problem in qtbase\src\corelib\ioqwinoverlappedionotifier.cpp where the emitted signal _q_notify() in notify(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped) does not result in a call of _q_notified(). Therefore the Semaphore hSemaphore exceeds its max-count, resulting in the deadlock in the destructor.
What could be the reason for the signal-slot not working? I could not find any disconnects or block signals.
Thanks in advance.
main.cpp:
#include "main.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <iostream>
int main(int argc, char *argv[])
{
printf("Server (0) or Socket (1)?\n");
char c = getchar();
if (c == '0') {
QCoreApplication app(argc, argv);
CIPServer server;
app.exec();
}
else if (c == '1') {
CIPSocket socket;
for (unsigned int i = 0; i <= 256; ++i) {
socket.update(i);
QThread::msleep(10);
}
}
}
/*--------------------------------------------------------------------------
CIPSocket
----------------------------------------------------------------------------*/
CIPSocket::CIPSocket()
: m_bIsReady(false)
{
m_pSocket = new QLocalSocket(this);
m_stream.setDevice(m_pSocket);
connect(m_pSocket, SIGNAL(connected()), this, SLOT(connectionReady()));
connect(m_pSocket, SIGNAL(disconnected()), this, SLOT(connectionLost()));
m_pSocket->connectToServer("DemoServer");
}
CIPSocket::~CIPSocket()
{
delete m_pSocket;
m_pSocket = NULL;
}
void CIPSocket::update(int i)
{
if (m_bIsReady)
m_stream << i;
}
void CIPSocket::connectionReady()
{ m_bIsReady = true; }
void CIPSocket::connectionLost()
{ m_bIsReady = false; }
/*--------------------------------------------------------------------------
CIPServer
----------------------------------------------------------------------------*/
CIPServer::CIPServer(QObject* parent)
: QLocalServer(parent)
{
if (!listen("DemoServer")) {
throw ("Could not connect to 'DemoServer'");
}
connect(this, SIGNAL(newConnection()), this, SLOT(socketConnected()));
}
CIPServer::~CIPServer()
{}
void CIPServer::socketConnected()
{
qDebug() << "Connected";
m_pConnection = nextPendingConnection();
m_stream.setDevice(m_pConnection);
connect(m_pConnection, SIGNAL(disconnected()), m_pConnection, SLOT(deleteLater()));
connect(m_pConnection, SIGNAL(readyRead()), this, SLOT(update()));
}
void CIPServer::update()
{
if (m_pConnection->bytesAvailable() >= 4) {
int i;
m_stream >> i;
qDebug() << i;
}
}
main.h:
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include <QtCore/QDataStream>
#include <QtCore/QThread>
/// \brief Creates a socket for inter-process communication
class CIPSocket
: public QObject
{
Q_OBJECT;
public:
/// Constructor
CIPSocket();
/// Destructor
virtual ~CIPSocket();
/// Send the data
void update(int i);
public slots:
/// Enables updating
void connectionReady();
/// Disables updating
void connectionLost();
private:
/// The target stream
QDataStream m_stream;
/// The socket connecting to server
QLocalSocket* m_pSocket;
/// Indicates if the socket is connected
bool m_bIsReady;
};
/// \brief Creates a server for inter-process communication
class CIPServer
: public QLocalServer
{
Q_OBJECT;
public:
/// Constructor
CIPServer(QObject* parent = NULL);
/// Destructor
virtual ~CIPServer();
/// Starts the server
void start();
private slots:
/// Connects the socket to the stream and to the update function
void socketConnected();
/// Reads the data from the stream and emits a the results
void update();
private:
/// The currently connected socket
QLocalSocket* m_pConnection;
/// The incoming stream
QDataStream m_stream;
};
demo.pro:
CONFIG += qt debug
QT += network
HEADERS += main.h
SOURCES += main.cpp
CONFIG += console
The error occurs due to the event loop not running. Starting QCoreApplication starts the event loop, but waits for the application to quit. Therefore the sending has to be done in another thread.
Attached code shows the correct usage.
main.cpp:
#include "main.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
printf("Server (0) or Socket (1)?\n");
char c = getchar();
if (c == '0') {
CIPServer server;
QCoreApplication::exec();
}
else if (c == '1') {
CIPSocket socket;
CSender sender(500);
QObject::connect(&sender, SIGNAL(sendMessage(int)), &socket, SLOT(update(int)));
QObject::connect(&sender, SIGNAL(allMessagesSent()), &socket, SLOT(close()));
sender.start();
QCoreApplication::exec();
}
}
/*--------------------------------------------------------------------------
CIPSocket
----------------------------------------------------------------------------*/
CIPSocket::CIPSocket()
: m_bIsReady(false)
{
m_pSocket = new QLocalSocket(this);
m_stream.setDevice(m_pSocket);
connect(m_pSocket, SIGNAL(connected()), this, SLOT(connectionReady()));
connect(m_pSocket, SIGNAL(disconnected()), this, SLOT(connectionLost()));
m_pSocket->connectToServer("DemoServer");
}
CIPSocket::~CIPSocket()
{
delete m_pSocket;
m_pSocket = NULL;
}
void CIPSocket::update(int i)
{
if (m_bIsReady)
m_stream << i;
}
void CIPSocket::connectionReady()
{ m_bIsReady = true; }
void CIPSocket::connectionLost()
{ m_bIsReady = false; }
void CIPSocket::close()
{ QCoreApplication::exit(); }
/*--------------------------------------------------------------------------
CIPServer
----------------------------------------------------------------------------*/
CIPServer::CIPServer(QObject* parent)
: QLocalServer(parent)
{
if (!listen("DemoServer")) {
throw ("Could not connect to 'DemoServer'");
}
connect(this, SIGNAL(newConnection()), this, SLOT(socketConnected()));
}
CIPServer::~CIPServer()
{}
void CIPServer::socketConnected()
{
qDebug() << "Connected";
m_pConnection = nextPendingConnection();
m_stream.setDevice(m_pConnection);
connect(m_pConnection, SIGNAL(disconnected()), m_pConnection, SLOT(deleteLater()));
connect(m_pConnection, SIGNAL(readyRead()), this, SLOT(update()));
connect(m_pConnection, SIGNAL(disconnected()), this, SLOT(close()));
}
void CIPServer::update()
{
if (m_pConnection->bytesAvailable() >= 4) {
int i;
m_stream >> i;
qDebug() << i;
}
}
void CIPServer::close()
{ QCoreApplication::exit(); }
/*--------------------------------------------------------------------------
CSender
----------------------------------------------------------------------------*/
CSender::CSender(int iNumMessages)
: m_iNumMessages(iNumMessages)
{}
CSender::~CSender()
{}
void CSender::run()
{
while (m_iNumMessages > 0) {
emit sendMessage(m_iNumMessages);
msleep(10);
m_iNumMessages--;
}
emit allMessagesSent();
}
main.h:
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include <QtCore/QDataStream>
#include <QtCore/QThread>
#include <QtCore/QCoreApplication>
/// \brief Creates a socket for inter-process communication
class CIPSocket
: public QObject
{
Q_OBJECT;
public:
/// Constructor
CIPSocket();
/// Destructor
virtual ~CIPSocket();
public slots:
/// Enables updating
void connectionReady();
/// Disables updating
void connectionLost();
/// Send the data
void update(int i);
/// Close the application
void close();
private:
/// The target stream
QDataStream m_stream;
/// The socket connecting to server
QLocalSocket* m_pSocket;
/// Indicates if the socket is connected
bool m_bIsReady;
};
/// \brief Creates a server for inter-process communication
class CIPServer
: public QLocalServer
{
Q_OBJECT;
public:
/// Constructor
CIPServer(QObject* parent = NULL);
/// Destructor
virtual ~CIPServer();
private slots:
/// Connects the socket to the stream and to the update function
void socketConnected();
/// Reads the data from the stream and emits a the results
void update();
/// Close the application
void close();
private:
/// The currently connected socket
QLocalSocket* m_pConnection;
/// The incoming stream
QDataStream m_stream;
};
/// \brief Sends the messages via CIPSocket
class CSender
: public QThread
{
Q_OBJECT;
public:
/// Constructor
CSender(int iNumMessages);
/// Destructor
virtual ~CSender();
/// Sends the requestet number of messages in 10 ms steps
virtual void run();
signals:
/// Sends the message via the CIPSocket
void sendMessage(int);
/// Informs about all messages being sent
void allMessagesSent();
private:
/// The number of messages to send
int m_iNumMessages;
};
The program works well as it is programmed: try the following:
start server - start client, sends data - server receives data - stop client - start client once more, sends data - server receives data ...
The program has no deadlock, and it does not freeze! The program is waiting for events in Qt eventloop in line 13:
app.exec();
The question is: what shall it do?
I assume, you like to quit the program after work, then insert:
void CIPServer::socketConnected()
{ . . .
connect(m_pConnection, SIGNAL(disconnected()), this, SLOT(theend()));
}
void CIPServer::theend()
{
QCoreApplication::quit();
}
Try these actions:
start server - start client, sends data - server receives data - stop client – server stops also
I'm trying to send some data over serial port using QSerialPort class. Sending data from the same parent class every time works well. But when I'm trying to send data using another thread QSerialPort it doesn't. I used serial port monitor to watch the data and it showed nothing.
Code is looks like this:
#include <QSerialPort>
#include <QObject>
#include <QDebug>
#include <QCoreApplication>
#include <thread>
#include <mutex>
#include <list>
#include <memory>
class Message {
public:
bool isSent;
bool isProcessed;
int data;
Message(int d) :
data(d),
isSent(false),
isProcessed(false) {
}
};
class SerialClass : public QObject {
Q_OBJECT
public:
SerialClass(unsigned short portNumber, unsigned int baudrate, QObject *parent = 0) :
_portNumber(portNumber),
_baudrate(baudrate),
_serialPort(new QSerialPort(this)),
_messages(),
_writeThread(nullptr),
QObject(parent) {
_serialPort->setPortName(QString("COM%1").arg(_portNumber));
_serialPort->setBaudRate(baudrate);
_serialPort->setDataBits(QSerialPort::DataBits::Data8);
_serialPort->setParity(QSerialPort::Parity::NoParity);
_serialPort->setStopBits(QSerialPort::StopBits::OneStop);
}
void start() {
if (!_serialPort->open(QIODevice::ReadWrite)) {
qDebug() << "Couldn't open serial port";
return;
}
auto *m = &_messages;
auto *p = &_enableProcessing;
auto *port = _serialPort.get();
auto *mutex = &_mutex;
_serialPort->write(QString("Hello\r\n").toLocal8Bit());
_writeThread = std::make_unique<std::thread>([m, p, port, mutex]() {
qDebug() << "Work thread started";
while (*p) {
std::this_thread::sleep_for(std::chrono::milliseconds(15));
auto inactiveMessage = std::find_if(m->begin(), m->end(), [](Message &item) {
return !item.isSent;
});
if (inactiveMessage != m->end()) {
mutex->lock();
qDebug() << "Written" << port->write(QString("data %1\r\n").arg(inactiveMessage->data).toLocal8Bit());
inactiveMessage->isSent = true;
mutex->unlock();
}
}
qDebug() << "Work thread stopped";
});
}
void wait() {
if (!_writeThread) {
return;
}
_writeThread->join();
}
void addMessage(Message& msg) {
_mutex.lock();
_messages.push_back(msg);
_mutex.unlock();
}
private:
unsigned short _portNumber;
unsigned int _baudrate;
std::unique_ptr<QSerialPort> _serialPort;
std::unique_ptr<std::thread> _writeThread;
std::list<Message> _messages;
bool _enableProcessing;
std::mutex _mutex;
};
int main(int argc, char ** argv) {
QCoreApplication application(argc, argv);
SerialClass serialProcessor(2, 9600, &application);
serialProcessor.addMessage(Message(228));
serialProcessor.addMessage(Message(929));
serialProcessor.addMessage(Message(221424));
serialProcessor.start();
return application.exec();
}
#include "main.moc"
Does it means what the QSerialPort class can not transmit data from another thread? (not QSerialPort thread)
It is recommended not to mix STL and Qt solutions in the same project
QSerialPort can work in any thread
You can see an example of correct using of QThread here: https://stackoverflow.com/a/35673612/4149835
I have written a QT - webkit application. this application fires a callback when my pSeudo driver gets the character 'l'. However, the application crashes during a firecallback - it says - QObject::setParent: Cannot set parent, new parent is in a different thread. I don't know to fix this, I tried doing moveToThread, but it doesn't help. Please help me here.
#include <QtGui/QApplication>
#include <QApplication>
#include <QDebug>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>
#include <QThread>
#include <unistd.h>
#include <fcntl.h>
class DemoThread;
class MyJavaScriptOperations : public QObject {
Q_OBJECT
public:
QWebView *view;
DemoThread *m_pDemoThread;
MyJavaScriptOperations();
void firecb();
bool slot_installed;
signals:
void alert_script_signal();
public slots:
void JS_ADDED();
void loadFinished(bool);
private:
};
class DemoThread : public QThread {
public:
DemoThread( MyJavaScriptOperations *pJavascriptOp);
protected:
void run();
private :
MyJavaScriptOperations *m_pJavascriptOp;
};
DemoThread::DemoThread(MyJavaScriptOperations *pJavascriptOp):m_pJavascriptOp(pJavascriptOp)
{
}
void DemoThread:: run()
{
int filedesc = open("/dev/pSeudoDrv", O_RDONLY);
if(filedesc < 0)
{
qDebug()<<"Couldn't open Driver.";
}
unsigned char buff;
while(1)
{
read(filedesc,&buff, 1);
qDebug()<<"The code received is "<< buff;
if ( (m_pJavascriptOp->slot_installed == true) && (buff == 166))
{
m_pJavascriptOp->firecb();
}
qDebug()<<"Running Thread.";
sleep(6);
}
}
void MyJavaScriptOperations::JS_ADDED()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->addToJavaScriptWindowObject("myoperations", this);
}
void MyJavaScriptOperations::loadFinished(bool oper)
{
qDebug()<<__PRETTY_FUNCTION__<< oper;
slot_installed = true;
// firecb();
}
void MyJavaScriptOperations::firecb()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->evaluateJavaScript("JavaScript_function()");
}
MyJavaScriptOperations::MyJavaScriptOperations()
{
qDebug()<<__PRETTY_FUNCTION__;
view = new QWebView();
view->resize(400, 500);
connect(view->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(JS_ADDED()));
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
view->load(QUrl("./index.html"));
view->show();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyJavaScriptOperations *jvs = new MyJavaScriptOperations;
DemoThread *thread = new DemoThread(jvs);
jvs->moveToThread(thread);
thread->start();
return a.exec();
}
#include "main.moc"
This is the crash-error I get -
./QT_DEMO
MyJavaScriptOperations::MyJavaScriptOperations()
loaded the Generic plugin
The code received is 156
Running Thread.
The code received is 166
void MyJavaScriptOperations::firecb()
QObject::setParent: Cannot set parent, new parent is in a different thread
There are few articles on internet how to make multithreaded applications in Qt. Best explanation can be found here:
http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/
You could read also other articles:
https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong
http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Well, I got a solution for my problem. Please tell me, am I complicating the solution.
I am using signal and slot. The thread will emit the signal and the slot of other class will emit the callback to the Qtwebkit - the javascript function. IS it right?
Because, I have suggestion using event loop - exec().
#include <QtGui/QApplication>
#include <QApplication>
#include <QDebug>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>
#include <QThread>
/** for reading my driver **/
#include <unistd.h>
#include <fcntl.h>
class DemoThread;
class MyJavaScriptOperations : public QObject
{
Q_OBJECT
public:
QWebView *view;
DemoThread *m_pDemoThread;
MyJavaScriptOperations();
void firecb();
bool slot_installed;
signals:
void alert_script_signal();
public slots:
void JsAdded();
void alertReceived();
void loadFinished(bool);
private:
};
class DemoThread : public QThread
{
Q_OBJECT
private:
MyJavaScriptOperations *m_pJavascriptOp;
public:
DemoThread( MyJavaScriptOperations *pJavascriptOp);
protected:
void run();
signals:
void alertSendSignal();
};
DemoThread::DemoThread(MyJavaScriptOperations *pJavascriptOp):m_pJavascriptOp(pJavascriptOp)
{
connect(this, SIGNAL(alertSendSignal()), m_pJavascriptOp, SLOT(alertReceived()));
}
void DemoThread:: run()
{
int filedesc = open("/dev/pSeudoDrv", O_RDONLY);
if(filedesc < 0)
{
qDebug()<<"Couldn't open Driver.";
}
unsigned char buff;
while(1)
{
if( 1 != read(filedesc,&buff, 1))
{
qDebug()<<"Read Invalid Data";
}
qDebug()<<"The code received is "<< buff;
/** In my laptop, the 166 means the character 'l' **/
if ( (m_pJavascriptOp->slot_installed == true) && (buff == 166))
{
emit alertSendSignal();
}
qDebug()<<"Running Thread.";
}
}
void MyJavaScriptOperations::JsAdded()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->addToJavaScriptWindowObject("myoperations", this);
}
void MyJavaScriptOperations::loadFinished(bool oper)
{
qDebug()<<__PRETTY_FUNCTION__<< oper;
slot_installed = true;
}
void MyJavaScriptOperations::alertReceived()
{
qDebug()<<"Sending Firecallback now";
firecb();
}
void MyJavaScriptOperations::firecb()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->evaluateJavaScript("JavaScript_function()");
}
MyJavaScriptOperations::MyJavaScriptOperations()
{
qDebug()<<__PRETTY_FUNCTION__;
view = new QWebView();
view->resize(400, 500);
connect(view->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(JsAdded()));
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
view->load(QUrl("./index.html"));
view->show();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyJavaScriptOperations *jvs = new MyJavaScriptOperations;
DemoThread *thread = new DemoThread(jvs);
thread->start();
return a.exec();
}
#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();
}