A external component has a callback which executes in its internal thread start with std::thread, I want to create a qt component(not UI) in this thread, connect other qt components signal to this qt component, and let the slot function execute in this internal thread.
I expect to execute to run the event loop once callback triggerd to process pending slot functions invoking.
// call in a thread start with std::thread in ExternalComponent, this method invoke periodically.
void ExternalComponent::InternalProcessing() {
//do other thing...
//invoke callback
callback();
}
void CustomQtComponent::Init() {
externalComponent.SetCallback([]() {
// first time, create a Worker
if (worker_ == nullptr) {
worker_ = new Worker();
}
// process pending signals(invoke worker_ slot methods) in this thread
// ...
// do other things.
});
}
// call by ui thread
void CustomQtComponent::DoSomething() {
// do xxxx
// ...
// emit a signal, to let something process in callback threads
// emit cutstomSignalWhichConnectToWokerSlots();
}
Because external threads not start by QThread, so though we can get the QThread object in its thread(in callback), but it has no event loop.
Could I construct a QEventLoop in callback thread, and let it receive and process signals sending to worker_?
Working example with trivial classes:
...{
std::thread([this]()
{
qDebug() << "current thread is" << QThread::currentThread();
auto recv = new Worker;
connect(this, &MainWin::call, recv, &Worker::callMe);
/* Note that recv's slots will be called in the thread where
the recv object lives */
QEventLoop eventLoop;
// Do not forget to setup exit mechanism
connect(qApp, &QApplication::aboutToQuit,
&eventLoop, &QEventLoop::quit);
// Do not forget to start the loop but only after connections
eventLoop.exec();
}).detach();
// After start the thread we can trigger the signal
QThread::msleep(1000); // make sure won't be called earlier than connect()
// Check the threads are different
qDebug() << "current thread is" << QThread::currentThread();
emit call();
}
Where
void Worker::callMe()
{
qDebug() << "callMe invoked";
qDebug() << "current thread is" << QThread::currentThread();
}
See also the list of useful links about objects, signals-slots and threading in Qt, in the answer: https://stackoverflow.com/a/60755238/4149835
Related
Struggling emitting a signal from main/QML thread to another thread with a QList< QStringList > parameter. Variations I'v tried:
Q_DECLARATIVE_METATYPE in and out
Using EventExport vs. const EventExport& in signal and slot profiles
Sending empty EventExport in prepareExport() so emit has no/low data amount
Checking connect statement (always returns true)
Having qDebug() in prepareExport() and signal always appears to be emitted
Calling emit right after connect as a test (Works! Think you're going to tell me the main thread or cryoUtility objects don't exist but they do!)
Tried qRegisterMetaType with () and ("EventExport")...some say use text for typedef types
Any thoughts much appreciated!
sqlquery_model.h (not certain I need Q_DECLARATIVE_METATYPE but tried with and without...no change)
typedef QList<QStringList> EventExport;
Q_DECLARE_METATYPE(EventExport);
Q_INVOKABLE void prepareExport();
signals:
void updateEventListDataSig(const EventExport&);
sqlquery_model.cpp (this is connected to a qml page using TableView model...this emit does not seem to work)
void SqlQueryModel::prepareExport() {
if (this->rowCount() > 0) {
EventExport eventsList;
for(int i=0; i<this->rowCount(); ++i) {
QStringList eventInfo;
eventInfo.append(this->record().value(0).toString());
eventInfo.append(this->record().value(1).toString());
eventInfo.append(this->record().value(2).toString());
eventInfo.append(this->record().value(3).toString());
eventInfo.append(this->record().value(4).toString());
eventsList.append(eventInfo);
}
emit updateEventListDataSig(eventsList);
qDebug() << "Emit updatedEventListData" << eventsList.count();
}
}
main.cpp (includes sqlquery_model.h, need this as cryoUtility is a separate thread using Qt::QueuedConnection)
// Use string if using typedef method
qRegisterMetaType<EventExport>("EventExport");
mediator.h
void updateEventListDataSig(const EventExport&);
mediator.cpp (connects mainly live here, this test event works)
bool ret = connect(this, SIGNAL(updateEventListDataSig(const EventExport&)), cryoUtility, SLOT(updateEventListData(const EventExport&)), Qt::QueuedConnection);
EventExport ed;
emit updateEventListDataSig(ed);
qDebug() << "Event list CONN: " << ret;
utilities.h
void updateEventListData(const EventExport&);
utilities.cpp (this is the slot, trigger once on test call)
void Utilities::updateEventListData(const EventExport& el) {
qDebug() << "Load event list: ";// << el.count();
//eventList = el;
}
So, after more study, sqlmodelquery connection would have to occur in its constructor as it isn't active yet until its QML page loads.
I have made new QTimer object in my class's ctor. When the application starts the timer also starts with the specified time. This timer keeps a track of all the process IDs and if finds that a particular process-id is not active(killed from task manager) then it restarts that particular process. Now the issue is my application lags a-bit due to this QTimer object and only after the timeout, the application becomes smooth. But after 5 seconds the timer starts again and the application starts lagging again. What is the best alternative to start a QTimer object apart from ctor of a class?
qTimer = new QTimer(0);
connect(qTimer, SIGNAL(timeout()), this, SLOT(RestartStoppedProcess()), Qt::DirectConnection);
connect(qTimer, SIGNAL(destroyed()), qTimer, SLOT(deleteLater()), Qt::DirectConnection);
qTimer->setTimerType(Qt::VeryCoarseTimer);
qTimer->start(5000);
void ProcessMonitor::RestartStoppedProcess()
{
try
{
QProcess *objMonitorProcess = new QProcess(this);
connect(objMonitorProcess, SIGNAL(finished(int,QProcess::ExitStatus)), objMonitorProcess, SLOT(deleteLater()));
for(int i = 0; i < ui->twShowProcess->rowCount(); i++)
{
#ifdef Q_WS_LIN
// objMonitorProcess->start("pidof", QStringList() << (ui->twShowProcess->item(i, 0)->text()), QIODevice::ReadOnly);
objMonitorProcess->start("pidof "+ (ui->twShowProcess->item(i, 0)->text()), QIODevice::ReadOnly);
#elif Q_WS_WIN
QString cmd("wmic process where ");
QString qszFilters("\"processid=\'");
QString qszSubFilters = qszFilters.append(ui->twShowProcess->item(i, 1)->text().append("\'").append("\"").append(" get name"));
cmd.append(qszSubFilters);
objMonitorProcess->start(cmd.toStdString().c_str(), QIODevice::ReadOnly);
#else
QString qszProcessName = ui->twShowProcess->item(i, 0)->text().split(".").takeFirst();
objMonitorProcess->start("pgrep", QStringList() << qszProcessName, QIODevice::ReadOnly);
#endif
if((objMonitorProcess->waitForStarted(4000) == true) && (objMonitorProcess->state() == QProcess::Running))
{
if(objMonitorProcess->waitForFinished(3000) == false)
continue;
else
{
QString qszProcessStatus = ui->twShowProcess->item(i, 3)->text();
if(qszProcessStatus.compare("Running") == 0)
{
QString qszStandardOutput, qszStandardError;
qszStandardOutput.clear(); qszStandardError.clear();
qszStandardOutput = QString(objMonitorProcess->readAllStandardOutput());
qszStandardError = QString(objMonitorProcess->readAllStandardError());
if(qszStandardError.simplified().compare("No Instance(s) Available.", Qt::CaseInsensitive) == 0 && qszStandardOutput.simplified().isEmpty() == true)
{
ui->twShowProcess->selectRow(i);
if(ui->twShowProcess->selectedItems().at(0)->text().compare("LTtagdb.exe", Qt::CaseInsensitive) == 0 ||
ui->twShowProcess->selectedItems().at(0)->text().compare("LTmessagebroker.exe", Qt::CaseInsensitive) == 0)
this->StopAllApplicationsForcefully();
else
this->on_actionStop_triggered();
if(this->ptrobjContextMenu->isVisible())
this->ptrobjContextMenu->close();
ui->twShowProcess->clearSelection();
if(mobj_ProcessLogMap.mobj_ProcessAndLogMapping.contains(ui->twShowProcess->item(i, 8)->text()) == true)
{
mobj_ProcessLogMap.mobj_ProcessAndLogMapping.remove(ui->twShowProcess->item(i, 8)->text());
mobj_ProcessLogMap.mobj_ProcessAndLogMapping.insert(ui->twShowProcess->item(i, 8)->text(), QString(""));
}
else
this->mobj_ProcessLogMap.mobj_ProcessAndLogMapping.insert(ui->twShowProcess->item(i, 8)->text(), QString(""));
}
if(ui->twShowProcess->item(i, 0)->text() == "LThistdb.exe")
{
if(IsServiceRunning(QString("LThistdb.exe")) == false)
this->on_actionStopPostgreSQL_DB_Server_triggered();
}
qszStandardOutput.clear();
}
}
}
else
break;
#ifdef Q_WS_WIN
cmd.clear();
qszFilters.clear();
qszSubFilters.clear();
#endif
}
}
catch(QProcess::ProcessError pError)
{
QMessageBox objCrashExit;
objCrashExit.information(this, "Crashed", "Process error returned code: " + QString::number(pError));
}
catch(...)
{
QMessageBox objCrashExit;
objCrashExit.information(this, "Unknown Crash", "Unknown crash exit-child process may not be running correctly!");
}
}
QTimer runs in the UI thread so it blocks your UI if it takes a long time to execute. A simple solution is to just run your "processing" stuff in another background thread, the easiest way to do that in Qt might be using QtConcurrent::run like this from your timeout slot called from the QTimer:
QtConcurrent::run(yourProcessingFunction);
You can obviously create your own Thread and use the build in QTimer from QObject::startTimer(...), but that is more work usually.
The problem is likely due to the amount of work being done by RestartStoppedProcess.
As the documentation for QTimer states, when discussing the called slot:
It should be written in such a way that it always returns quickly (typically after processing one data item) so that Qt can deliver events to the user interface and stop the timer as soon as it has done all its work.
As you've witnessed with your application, a heavy workload with a QTimer on the main thread can cause lag in the Gui. The Qt documentation goes on to state:
This is the traditional way of implementing heavy work in GUI applications, but as multithreading is nowadays becoming available on more and more platforms, we expect that zero-millisecond QTimer objects will gradually be replaced by QThreads.
With this in mind, I suggest refactoring your code so that RestartStoppedProcess functions in a separate thread. There's a great tutorial on "How to Really Truly Use QThread" here.
I'm trying to write simple video recorder, based on gstreamer framework for my own customboard with ARM processor and Wayland+Qt as window subsystem.
I've created class RecordBin with rec_start slot and rec_stop public method:
void RecordBin::rec_start ()
{
/* Set pipeline to the PLAYING state */
gst_element_set_state (record_pipeline, GST_STATE_PLAYING);
g_print ("setting playing state\n");
/* Start main loop context */
g_main_loop_run (rec_loop);
}
void RecordBin::rec_stop ()
{
/* Set pipeline to the NULL state */
change_ret = gst_element_set_state (record_pipeline, GST_STATE_NULL);
/* Quit from main loop context */
g_main_loop_quit (rec_loop);
}
This is my slot in mainwindow for play/stop button clicking:
void MainWindow::on_recordButton_clicked ()
{
if (!is_recording) {
if (window_is_opened == false) {
cout << "start recording" << endl;
/* Start recording */
window_is_opened = true;
emit start_recording ();
} else {
cout << "playing window already opened" << endl;
}
} else {
cout << "recording reset" << endl;
/* Stop recording */
record_bin->rec_stop ();
window_is_opened = false;
}
}
RecordBin class is working in a separate thread (it's realized via QThread), so that glib mainloop context don't blocking Qt main window.
I can't use rec_stop method as slot, because rec_loop blocks the message handling, and when recording starts, it can't be stopped via signal.
But direct call rec_stop is thread unsafe.
Can anybody help me with two questions:
1. How I should change pipeline state from another thread?
2. Is it correct to stop recording via changing pipeline state to NULL? Probably I should to send EOS signal on the bus and handling it?
gst_element_set_state() is marked as MT safe, so it can be stopped from any thread. I had a problem with setting gst_element_set_state() to NULL: filesink at the end of pipeline wasn't completing the output files correctly (had to use EOS and catch another event that was confirming that the filesink has got that EOS). But NULL works fine for players.
Overall, I'd get rid of the glib mainloop. You can use gst_bus_set_sync_handler() to set a callback. In the callback, emit a Qt signal to RecordBin. The RecordBin will be a QObject that lives in the main Qt thread. Process the arriving GstMessage* in its slot.
In my Qt application after pressing button I want to hide that button and start quite long process. While this process is running PushButton shouldn't be visible but it seems to be waiting for process being executed and after that hide button. It looks like QWidget is refreshing after end of PushButton slot function. Here's my simplified code:
void MainWindow::on_pushButton_clicked()
{
ui->progressBar->setVisible(true);
ui->pushButton->setVisible(false);
while(x<1000000) x++; //e.g of my long time function
}
When that function (on_pushButton_clicked() -> generated by mouse->go to slot) ends up my "view" is updated and button dissappear.
Is there any function to refresh my widget or maybe I forgot about sth?
Thanks in advance
Changes to the gui aren't shown until the program has a chance to redraw itself which won't happen until you return.
you'll need to defer the execution of the code somehow:
void MainWindow::on_pushButton_clicked()
{
ui->progressBar->setVisible(true);
ui->pushButton->setVisible(false);
QMetaObject::invokeMethod(this, &MainWindow::longFunction, Qt::QueuedConnection);
}
void MainWindow::longFunction()
{
while(x<1000000) x++; //e.g of my long time function
}
This returns to the event loop and then runs the longFunction but it will still block on and the progress bar won't show any updates until it is done.
To fix that you will either need to move the execution to a new thread or split the function up in shorter parts and invoke them in sequence with QMetaObject::invokeMethod and a QueuedConnection.
In order for the button to change state, it needs to return to process events in the event loop.
You could call QApplication::processEvents before the while loop in order to fix this, though it would be better to return to the event loop naturally, before you start the long-time function, by invoking the function as a QueuedConnection.
Alternatively, the better method would be to run the function in a separate thread, which will enable your GUI to remain active during the processing of the 'long function'
Start by creating an object to encapsulate the function that will do the work:-
class Worker : public QObject {
Q_OBJECT
public:
Worker();
~Worker();
public slots:
void process(); // This is where your long function will process
signals:
void finished();
void error(QString err);
};
void Worker::process()
{
while(x<1000000) x++; //e.g of my long time function
emit finished();
}
Create a new thread and start it when the button is clicked
void MainWindow::on_pushButton_clicked()
{
// change button visibility
ui->progressBar->setVisible(true);
ui->pushButton->setVisible(false);
// create the new thread and start the long function
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()));
//ensure the objects are cleared up when the work is done
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
//start the thread and the long function processing
thread->start();
}
My gui have a two button's, one to start a server & other to stop a server.
Brief :---
Once server is started, on every new client request i will create a new thread & this will handle communication with the client.
Detail :--
When start button is pressed, I am creating an object 'tcpserverobjectWrapper' which creates an another object 'tcpserverobject' this object creats an Qtcpserver.
Now this Qtcpserver is listing for new connection. And when a new connection comes i create an 'TcpSocketThreadWrapperObject' object which
creates a thread & this thread handles communication with client . Also 'tcpserverobject' keeps the list of new client request objects created
'QList<TcpSocketThreadWrapperObject *> TcpSocketThreadWrapperObjectList;' .
I am able to connect to server from telnet clients & it creates new thread for each client & works fine.
When stop button pressed i am able to stop server & client threads.
But i have two problems here :---
1> Everytime client send some data to server. I get this kind of QsocketNotifier. What is this ?
QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread
2> If i press stop button on GUI i am able to stop the threads succesfully.
But how to stop the threads & delete the objects created for every client when client send 'STOP command' to server or closes the connection with server ?
I will also have to delete the following objects created on each client request ?
client request --> TcpSocketThreadWrapperObject -- creates --> TcpSocketThreadObject -- creates --> TcpSocketThreadObject
Can someone suggest how to solve above two problems ? Reply on this will be appreciated.
Here is the code :---
================= start & stop buttons handler =====
void MainWindow::on_actionStop_triggered()
{
if(b_threadAlreadyStarted)
{
/* ------------------ Tcp server object ------------------------*/
b_threadAlreadyStarted = false;
delete p_tcpserverobjectWrapper;
/* ------------------ Tcp server object ------------------------*/
}
}
void MainWindow::on_actionStart_triggered()
{
if(!b_threadAlreadyStarted)
{
/* ------------------ Tcp server object ------------------------*/
b_threadAlreadyStarted =true;
p_tcpserverobjectWrapper = new tcpserverobjectWrapper(this,modelCANalyzer);
qDebug() << " \n start ";
/* ------------------ Tcp server object ------------------------*/
}
}
======== tcpserverobjectWrapper class ===============
// Main server object wrapper
class tcpserverobjectWrapper : public QObject
{
Q_OBJECT
public:
explicit tcpserverobjectWrapper(QMainWindow *ptrWidget, QStandardItemModel *modelCANalyzer, QObject *parent=0);
~tcpserverobjectWrapper();
//Device thread object
tcpserverobject *m_tcpserverobject;
};
tcpserverobjectWrapper::tcpserverobjectWrapper(QMainWindow *ptrWidget , QStandardItemModel *modelCANalyzer,QObject *parent) :
QObject(parent)
{
m_tcpserverobject = new tcpserverobject ;
//save model
m_tcpserverobject->modeltable = modelCANalyzer;
m_tcpserverobject->ptrmainwindow = ptrWidget;
qDebug() << "\n tcp server thread started";
}
tcpserverobjectWrapper::~tcpserverobjectWrapper()
{
qDebug() << " \n called delete later on tcpserverobjectWrapper .. !!";
m_tcpserverobject->deleteLater(); // ---------------------> change it to - delete m_tcpserverobject
qDebug() << " \n tcp server object successfully quited .. !! ";
}
========== tcpserverobject object ==================
class tcpserverobject : public QObject
{
Q_OBJECT
public:
explicit tcpserverobject(QObject *parent = 0);
~tcpserverobject();
/*!
Pointer to QStandardItemModel to be used inside - canTableView
*/
QStandardItemModel *modeltable;
//mainwindow pointer
QMainWindow *ptrmainwindow;
// Create list of new -- socket thread wrapper objects
QList<TcpSocketThreadWrapperObject *> TcpSocketThreadWrapperObjectList;
private:
QTcpServer *tcpServer;
signals:
public slots:
void on_newConnection();
};
tcpserverobject::tcpserverobject(QObject *parent) :
QObject(parent), tcpServer(0)
{
tcpServer = new QTcpServer;
// Connect slot of the server
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(on_newConnection()));
//lisen on socket
if (!tcpServer->listen(QHostAddress::LocalHost, SERVER_PORT )) {
qDebug() << "\n returning from server listning error .. !!! ";
return;
}
qDebug() << "\n server listning";
}
tcpserverobject::~tcpserverobject()
{
// to do
while (!TcpSocketThreadWrapperObjectList.isEmpty())
delete TcpSocketThreadWrapperObjectList.takeFirst();
}
void tcpserverobject::on_newConnection()
{
QByteArray block;
block.append(" \n Hello from server .. !!!") ;
QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()),
clientConnection, SLOT(deleteLater()));
// Create new thread for this .. client request ..!!
qDebug() << "\n New connection request ..!!!";
qDebug() << "\n New client from:" << clientConnection->peerAddress().toString();
clientConnection->write(block);
clientConnection->flush();
// create new tcp object
TcpSocketThreadWrapperObject* TcpSocketThreadWrapperObjectPtr = new TcpSocketThreadWrapperObject(clientConnection);
// Append object to the list
TcpSocketThreadWrapperObjectList.append(TcpSocketThreadWrapperObjectPtr);
}
============ TcpSocketThreadWrapperObject ==============
// Main device thread object
class TcpSocketThreadWrapperObject : public QObject
{
Q_OBJECT
public:
explicit TcpSocketThreadWrapperObject(QTcpSocket *m_pTcpSocket , QObject *parent = 0);
~TcpSocketThreadWrapperObject();
/*!
pointer for write thread
*/
QThread m_TcpSocketRWThread;
/// pointer to the socketthread object
class TcpSocketThreadObject *m_pTcpSocketThreadObject;
signals:
public slots:
};
// constructor for the deviceThreadObject
TcpSocketThreadWrapperObject::TcpSocketThreadWrapperObject(QTcpSocket *m_pTcpSocket , QObject *parent) :
QObject(parent)
{
m_pTcpSocketThreadObject = new TcpSocketThreadObject(m_pTcpSocket);
//set flag for event loop -- make while(1)
m_pTcpSocketThreadObject->m_bQuit = false;
// connect the signal & slot
connect(&m_TcpSocketRWThread,SIGNAL(started()),m_pTcpSocketThreadObject,SLOT(dowork_socket()));
// Move thread to object
m_pTcpSocketThreadObject->moveToThread(&m_TcpSocketRWThread);
//Start the thread
m_TcpSocketRWThread.start();
}
TcpSocketThreadWrapperObject::~TcpSocketThreadWrapperObject()
{
//set flag for event loop -- make while(0)
m_pTcpSocketThreadObject->m_bQuit = false;
// Wait for the thread to terminate
m_TcpSocketRWThread.quit();
m_TcpSocketRWThread.wait();
// Delete the object
m_pTcpSocketThreadObject->deleteLater();
qDebug() << "\n deleted - TcpSocketThreadWrapperObject";
}
======== TcpSocketThreadObject object ========
class TcpSocketThreadObject : public QObject
{
Q_OBJECT
public:
explicit TcpSocketThreadObject(QTcpSocket *m_pTcpSocketTemp , QObject *parent = 0);
~TcpSocketThreadObject();
/*!
Pointer to TCP socket -- created by the server
*/
QTcpSocket *m_pclientConnectionSocket;
/*!
Termination control main thread
*/
volatile bool m_bQuit;
signals:
public slots:
void dowork_socket();
};
// constructor for the deviceThreadObject
TcpSocketThreadObject::TcpSocketThreadObject(QTcpSocket *m_pTcpSocketTemp , QObject *parent) :
QObject(parent)
{
m_pclientConnectionSocket = m_pTcpSocketTemp;
// todo
}
TcpSocketThreadObject::~TcpSocketThreadObject()
{
// todo
}
void TcpSocketThreadObject::dowork_socket()
{
QByteArray block;
block.append(" \n hi again .. !!!") ;
// Write to socket
m_pclientConnectionSocket->write(block);
m_pclientConnectionSocket->flush();
// Close socket
m_pclientConnectionSocket->disconnectFromHost();
qDebug() << "\n entring loop of socket thread ..!!!";
while(!m_bQuit)
{
// while loop --> send/rx command from client
}
}
======= output of one client connected to server =====
New connection request ..!!!
New client from: "127.0.0.1"
QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread
entring loop of socket thread ..!!!
I think the simplest solution to all of this is to stop using QThread.
QTcpSocket and QTcpServer are both asynchronous, so when you receive a connection, you only need to create a class to wrap the QTcpSocket and this class handles the reading of the data, having connected the QTcpSocket signals to the class's slots.
If you're getting a lot of connections, then you'll be creating lots of threads. More threads than processor cores is just a waste of time.
If each connection is requesting a lot of work to be done by the server, you can then create separate worker objects and move those to a different thread, but overall, I recommend that you do not use separate threads for QTcpServer and QTcpSockets.