Unsolved How to show data from a blocking socket in QEditText
-
I have a QDialog with a QEditText object and I want to show data (it's a log from a server) from a blocking socket, I get this data using an API call that uses a callback to get the lines from the this log, this callback basically never ends, only way to finish it is
send a signal to force a close on it (in a command line I use ctrl+c).The issue I'm having is how to implement the thread and connect the slots/signals correctly.
My main window has a menu option to just open the dialog where I will show the data, this dialog has the QEditText in the UI, I also have a button to "start capturing" the log.
this is my header:
namespace Ui { class EventMonitor; } class EventMonitor : public QDialog { Q_OBJECT public: explicit EventMonitor(QWidget *parent = nullptr); ~EventMonitor(); private slots: void on_pushButton_3_clicked(); private: Ui::EventMonitor *ui; }; class Worker : public QObject { Q_OBJECT public: Worker(); ~Worker(); public slots: void process(); signals: void finished(); void error(QString err); private: };
In my push button event I create the thread
void EventMonitor::on_pushButton_3_clicked() { QThread* thread = new QThread; Worker* worker = new Worker(); worker->moveToThread(thread); connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString))); connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, SIGNAL(finished()), thread, SLOT(quit())); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); }
then In the worker process I call the API functions
void Worker::process() { openSession(IP, USER, PASS , &hPocket); DGetLogEvents(hPocket, ReceiveLogCallback, QTextEditBOX); emit finished(); }
This last parameter (QTextEditBox) is a void that I thought I could use to send the QTextEdit pointer and write to it inside the callback, but found out that in a different thread I couldn't write to other thread objects :(
The callback is simple it will just write the log line to a char *szEvent .int AAP_API ReceiveLogCallback(char *szEvent, void *pParam, BOOL bFinal) { //pParam->setText(szEvent) -> something similar to this will never work. emit(szEvent); return 0; }
So, I was thinking that the easiest way would be emit(szEvent) to a slot that has the QTextEdit from the UI in the other thread, but I'm not sure how to do this, since the callback is not in the same class
-
Hi and welcome to devnet,
Create a QString from the data your received and then use QMetaObject::invokeMethod with
Qt::QueuedConnection
type. That way, the slot will be executed in the right thread. -
thanks for the reply, the invoke seems like a good idea, but I'm not sure how to implement this as well.. the problem is this callback is a blocking socket that never closes, so it never leaves the callback function. How can I invoke from there if I can't access the UI object?
-
Can't you pass a custom structure or data to that callback when you register it ?
-
yes, I can do something like this
void Worker::process() { QString line; openSession(IP, USER, PASS , &hPocket); DGetLogEvents(hPocket, ReceiveLogCallback, &line); emit finished(); }
then in the callback
int AAP_API ReceiveLogCallback(char *szEvent, void *pParam, BOOL bFinal)
{
QString *line = *pParam;
line = QString::fromLocal8Bit(szEvent);
return 0;
}but the issue is that the function will never reach emit finished(); , it gets stuck in the callback forever.. so I need to emit this value somewhere I guess? or is it possible to connect to this QString line created in the worker class to get the value in the UI thread? Can you explain a little bit if possible? I'm somewhat noobish in QT.
-
Pass the pointer to your widget as parameter of the callback. Then in your callback you can use invokeMethod. -
Another possibility that will be cleaner: add a signal to your worker to emit the string and pass a pointer to your worker as parameter of your callback.
This will keep things cleanly separated.
-
@SGaist said in How to show data from a blocking socket in QEditText:
pass a pointer t
thanks, I think im getting closer, but I still don't get something.
how do I send a signal from the callback to write on the gui now?
I got this so far:
eventmonitor.h
#ifndef EVENTMONITOR_H #define EVENTMONITOR_H #include "stdafx.h" #include "worker.h" #include <QTextEdit> #include <QThread> namespace Ui { class EventMonitor; } class EventMonitor : public QDialog { Q_OBJECT public: explicit EventMonitor(QWidget *parent = nullptr); ~EventMonitor(); signals: void sendLine(QString *line); private slots: void on_start_logButton_clicked(); public slots: void receive_line(QString line); private: Ui::EventMonitor *ui; Worker m_worker; }; #endif // EVENTMONITOR_H
eventmonitor.cpp
#include "eventmonitor.h" #include "ui_eventmonitor.h" #include "hsm.h" EventMonitor::EventMonitor(QWidget *parent) : QDialog(parent), ui(new Ui::EventMonitor) { ui->setupUi(this); connect(&m_worker, SIGNAL(sendLineToGui(QString)), this, SLOT(receive_line(QString))); connect(this, SIGNAL(sendLine(QString *)), &m_worker, SLOT(process(QString *))); } EventMonitor::~EventMonitor() { delete ui; } void EventMonitor::receive_line(QString logLine) { ui->textEdit->setText(logLine); } void EventMonitor::on_start_logButton_clicked() { QString line; QThread* thread = new QThread; Worker* worker = new Worker(); emit sendLine(&line); worker->moveToThread(thread); connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString))); connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, SIGNAL(finished()), thread, SLOT(quit())); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); }
worker.h
#ifndef WORKER_H #define WORKER_H #include <QObject> #include "stdafx.h" class Worker : public QObject { Q_OBJECT public: explicit Worker(QObject * parent = 0); ~Worker(); public slots: void process(QString *line); signals: void sendLineToGui(QString line); void finished(); void error(QString err); private: }; #endif // WORKER_H
worker.cpp
#include "worker.h" Worker::Worker(QObject *parent) { } Worker::~Worker() { } int AAP_API ReceiveLogCallback(char *szEvent, void *pParam, BOOL bFinal) { QString *line = (QString*)pParam; *line = QString::fromLocal8Bit(szEvent); //emit sendLineToGui return 0; } void Worker::process(QString *line) { HSM::HPOCKET hPocket; std::string sIP(CSP::GetCSPIp()); HSM::libclient::openSession(CSP::GetCSPIp(), __INTERNAL_USER_CER_database__, __INTERNAL_USER_PASS_CER_database__ , &hPocket); DGetLogEvents(hPocket, ReceiveLogCallback, line); emit finished(); }
-
thanks! managed to do it like you said before, I passed the widget pointer to the callback parameter, it feels like bad design, but it's working.
-
That's the wrong approach. As I suggested, use invokeMethod to emit the signal.