I have a program that takes a long action, I run this function on a different thread. Periodically I need to update the information for the user, so I send a signal to the GUI thread. But sometimes I need the user to make a choice, I need to display the QDialog on the GUI thread and pause the slow thread while the user selects an option, and when the user completes the selection, return the value to the slow thread and continue it
it should look something like this:
But I don’t know how to stop and continue the thread and whether it should be done this way.
Header:
class Example:public QObject
{
//...
Q_OBJECT
void mainLoop();
Example();
signals:
void updateGUI(const QString &message);
void sendQuestion(const QString &message);
void continueMainLoop(const QString &answer);
private slots:
void updatuGUIslot(const QString &message);
void showQuestionDialog(const QString &message);
};
Source:
Example::Example()
{
connect(this,&Example::updateGUI,this,&Example::updatuGUIslot);
connect(this,&Example::sendQuestion,this,&Example::showQuestionDialog);
std::thread t(&Example::mainLoop,this);
t.detach();
// in the project it is not in the constructor
}
void Example::mainLoop()
{
while(some condition1)
{
// slow action
if(some condition2)
emit updateGUI("message");
if(some condition3)
{
QString result;
ThreadPtr th = this_thread(); // pseudocode
connect(this,&Example::continueMainLoop,this,[&](const QString &answer)
{
result = answer;
th.continue(); // pseudocode
});
emit sendQuestion("question");
th.wait(); // pseudocode
}
// slow action
}
}
void Example::showQuestionDialog(const QString &message)
{
// show dialog with question
emit continueMainLoop("answer");
}
void Example::updatuGUIslot(const QString &message)
{
// update GUI
}
you need to invoke the method with BlockingQueuedConnection before condition3 for checking which option that selected by the user.
bool updateGui ;
QMetaObject::invokeMethod(this, "showDialog",Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, updateGui));
if(updateGui)
{
//update GUI
}
Related
I want to wirte a funtion CH1_Hard_Soft to process data, which accepts two arguments from two different function.
double MainWindow::getdata_CH1(double time)
{
...
double CH1_data=0;
switch (CH1.Source) {
case 0: //software-hard
CH1_data = CH1_Hard_Soft(time);
....
}
The function CH1_Hard_Soft need to accept an argument time from getdata_CH1 and accept a QVector from other thread. And the function CH1_Hard_Soft will process these data and then return a QVector to getdata_CH1(double time). I don't know how to do this. Please give me some suggestions on how to do this.THANKS!!!
You can use a Function Object: create a new class with two attributes (one per parameter). Create setter for each parameter (or redefine the operator () to be closer to the behavior of a real function).
Each setter should check if the others are setted also. In that case, call you algorithm and send the result with a signal.
For example:
A simple worker executed in another thread. It will send fake data after 3 seconds
class Worker: public QObject
{
Q_OBJECT
public:
Worker(): QObject()
{
}
void timerEvent(QTimerEvent* ev)
{
qDebug() << Q_FUNC_INFO;
emit getVector(QVector<int>() << 2 << 4 << 6 << 8);
killTimer(timerId);
}
public slots:
void run()
{
timerId = startTimer(3000);
}
signals:
void getVector(QVector<int> const& vec);
private:
int timerId;
};
The Function Object: it will accept two param (a double and a vector)
// For convenience. Define a value and a flag to check if the value is well set
template<typename T> struct Param
{
T value;
bool isInit;
Param(): isInit(false)
{}
void setValue(T const& v)
{
value = v;
isInit = true;
}
};
// The processor
class Processor: public QObject
{
Q_OBJECT
public:
Processor(QObject* parent=nullptr): QObject(parent)
{}
void operator()(QVector<int> const& vector)
{
values.setValue(vector);
if (time.isInit)
process();
}
void operator()(double t)
{
time.setValue(t);
if (values.isInit)
process();
}
signals:
void done(double result);
private:
// Will be called as soon as all the parameters are set
void process()
{
// DO something
qDebug() << Q_FUNC_INFO;
emit done(time.value * values.value.length());
}
Param<QVector<int> > values;
Param<double> time;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// Run the thread
Worker* worker = new Worker();
QThread* th = new QThread();
worker->moveToThread(th);
QObject::connect(th, &QThread::started, worker, &Worker::run);
Processor CH1_Hard_Soft;
// Will be called when the CH1_Hard_Soft will send its result
QObject::connect(&CH1_Hard_Soft, &Processor::done, [=](double result) { qDebug() << "RESULT" << result; });
// Set the param vector
QObject::connect(worker, &Worker::getVector, [&](QVector<int> const& vec) { CH1_Hard_Soft(vec); });
// Call CH1_Hard_Soft with the first param
double time = 12.6;
CH1_Hard_Soft(time);
th->start();
return app.exec();
I've a thread that read datas
class MyThread: QThread
{
...
}
void MyThread::run ()
{
uint8_t* buffer; // in my real code, it's a ring, so there is not read during write
// ...
while (true)
{
if (isInterruptionRequested())
return;
USB_READ(buffer);
emit newData(buffer);
}
}
In my UI Class I have:
connect(this, &UIClass::newData, m_thread, &MyThread::newData);
// ...
void newData(uint8_t* data)
{
// Process data
}
void UIClass::closeEvent(QCloseEvent *event)
{
disconnect(this, &UIClass::newData, m_thread, &MyThread::newData);
m_thread->requestInterruption();
m_thread->wait();
}
The problem with that if, when I click on "close", the thread is destroyed that cause the pointer data to be invalid. The signal newData is sometimes called that cause my function to work with invalid pointer and segfault. How to be sure that is not gonna happend ?
For now, I use a std::this_thread::sleep_for() with an arbitrary delay, it works, but I not find this very beautiful
That I have in my mind :
- disconnect the signal
- wait for the pendings signals to be executed
- exit
The problem is that you send a pointer from one thread to another without ensuring the pointer stays valid.
You have multiple choices to solve this. Either use QSharedPointer (or similar utilities from the stl) to hold your data, doing so will ensure your pointer will remain valid (or provide you a way to detect when the pointer becomes invalid if you also use QWeakPointer). Or you could make use of QByteArray to pass the data, but this will make a copy.
Example 1
void MyThread::run ()
{
QSharedPointer<uint8_t> buffer (new uint8_t[N]()); // Do not delete[], QSharedPointer will handle it
...
emit newData(buffer);
}
void newData(QSharedPointer<uint8_t> data)
{
// data is always valid
// Process data
}
Example 2
void MyThread::run ()
{
QSharedPointer<uint8_t> buffer (new uint8_t[N]());
...
emit newData(buffer);
}
void newData(QWeakPointer<uint8_t> data)
{
// data might not be valid but we can check
QSharedPointer<uint8_t> buffer (data);
if (!buffer)
return;
// Process data
}
Example 3
void MyThread::run ()
{
uint8_t[N] buffer;
...
emit newData(QByteArray(buffer, size));
}
void newData(QByteArray data)
{
// data is valid
// Process data
}
All you need to do is for the thread to outlive the user interface. That's rather easy:
class MyThread : public QThread
{
Q_OBJECT
RingBuffer buffer;
public:
void run() override;
~MyThread() {
requestInterruption();
quit();
wait();
}
Q_SIGNAL newData(RingBuffer *);
};
int main(int argc, char **argv) {
QApplication app{argc, argv};
MyThread thread;
thread.start();
UIClass ui;
connect(&thread, &MyThread::newData, &ui, &UIClass::newData);
return app.exec();
}
I need to retrieve some html from a QWebEnginePage. I found in the documentation the method toHtml but it always returns an empty string. I tried toPlainText
and it works, but this is not what I need.
MyClass::MyClass(QObject *parent) : QObject(parent)
{
_wp = new QWebEnginePage();
_wp->settings()->setAttribute(QWebEngineSettings::AutoLoadImages, false);
_wp->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
connect(_wp, SIGNAL(loadFinished(bool)), this, SLOT(wpLoadFinished(bool)));
}
void MyClass::start()
{
_wp->load(QUrl("http://google.com/"));
}
void MyClass::wpLoadFinished(bool s)
{
_wp->toHtml(
[] (const QString &result) {
qDebug()<<"html:";
qDebug()<<result;
}); // return empty string
/*_wp->toPlainText(
[] (const QString &result) {
qDebug()<<"txt:";
qDebug()<<result;
});*/ //works perfectly
}
What am I doing wrong?
I am getting my head around QWebEngine. It is very cool. I have got the following to work.
The lambada capture needs to be all that is "=", or "this" in the case of signal being emitted. You would also need "mutable" to modify the captured copies. toHtml() is however asynchronous so even if you capture the html it is unlikely it would be available directly after the call to toHtml() in SomeFunction. You can overcome this by using a signal and slot.
protected slots:
void handleHtml(QString sHtml);
signals:
void html(QString sHtml);
void MainWindow::SomeFunction()
{
connect(this, SIGNAL(html(QString)), this, SLOT(handleHtml(QString)));
view->page()->toHtml([this](const QString& result) mutable {emit html(result);});
}
void MainWindow::handleHtml(QString sHtml)
{
qDebug()<<"myhtml"<< sHtml;
}
I think that the problem is more a connection problem. Your code works fine on my appli :
connect(page, SIGNAL(loadFinished(bool)), this, SLOT(pageLoadFinished(bool)));
...
page->load(QUrl("http://google.com/"));
...loading time...
void MaClasse :: pageLoadFinished(bool s){
page->toHtml([this](const QString &result){
qDebug()<<"html:";
qDebug()<<result;
item->setHtml(result);});
}
I have the following code that implements a Signal/Slot + Concurrency in Qt and was wondering if I can convert this to Boost/Threads and Boost/signal2
void MyClass::Func1()
{
emit ImplementingFunc1();
//Do the stuff here
Func1Implementation()
QFuture<void> future = QtConcurrent::run(this, &MyClass::WaitForFunc1Finish());
}
void MyClass::WaitForFunc1Finish()
{
int result = GetResponse();
emit Func1HasFinished();
}
How can I implement the emit functions (in the above, these are slots in MyClass) and the pipeline using Boost?
Thanks in advance for the help
You could implement your requirements using boost. However, the signals are different because boost does not give you an event loop to dispatch signals to slots.
It means that slot connected to a boost signal called a in thread will be executed in that thread !.
Roughly:
MyClass.h
typedef boost::signals2::signal<void ()> FinishedSig;
typedef boost::shared_ptr<FinishedSig> FinishedSigPtr;
typedef boost::lock_guard<boost::mutex> LockGuard;
class MyClass
{
public:
// Signal
FinishedSig& finished() { return *m_sig; }
void Func1();
void WaitForFunc1Finish();
void WaitForFunc1FinishSlot();
private:
FinishedSigPtr m_sig;
boost::mutex m_mutex;
boost::thread m_thread;
}
MyClass.cpp
// Signal connection
this->finished().connect(boost::bind(&MyClass::Func1HasFinishedSlot, this));
void MyClass::Func1()
{
//Do the stuff here
Func1Implementation()
m_thread = boost::thread(&MyClass::WaitForFunc1Finish, this);
}
void MyClass::WaitForFunc1Finish()
{
LockGuard l(m_mutex);
// Variables are guarded against concurrent access here
int result = GetResponse();
(*m_sig)(); // emit finished sig
}
void MyClass::Func1HasFinishedSlot()
{
// This will be executed in the calling thread
LockGuard l(m_mutex);
// Variables are guarded against concurrent access here
// do stuff
}
I've started a form in new thread, because of some GUI lagging (form become non responsive) problems. This thread starts when a function (some_function()) is called. Such as...
/*========some_function=========*/
void some_function()
{
System::Threading::Thread^ t1;
System::Threading::ThreadStart^ ts = gcnew System::Threading::ThreadStart(&ThreadProc);
t1 = gcnew System::Threading::Thread(ts);
t1->Start();
while(condition)
{
Form1^ f1=gcnew Form1();
//some coding
//to change the values of a different form (Form1)
}
}
/*======ThreadProc=========*/
void ThreadProc()
{
Form1^ f1=gcnew Form1();
f1->Show(); //OR Application::Run(Form1());
}
Now the problem is about changing values of the form (Form1), such as label text, progress bar etc., within the "while" loop. Is tehre any way to change values of form, which is open in different thread ?
Check the Control::Invoke to throw a method into a safe thread to change a control. To show the form of your example:
public delegate void SwapControlVisibleDelegate(Control^ target);
public ref class Form1 : public System::Windows::Forms::Form
{
/*Ctor and InitializeComponents for Form1*/
/*...*/
protected :
virtual void OnShown(EventArgs^ e) override
{
__super::OnShown(e);
some_function();
}
void some_function()
{
System::Threading::Thread^ t1;
System::Threading::ThreadStart^ ts = gcnew ystem::Threading::ThreadStart(this, &Form1::ThreadProc);
t1 = gcnew System::Threading::Thread(ts);
t1->Start();
}
void ThreadProc()
{
Threading::Thread::Sleep(2000);
for each(Control^ c in this->Controls)
{
SwapVisible(c);
}
}
void SwapVisible(Control^ c)
{
if(c->InvokeRequired) // If this is not a safe thread...
{
c->Invoke(gcnew SwapControlVisibleDelegate(this, &Form1::SwapVisible), (Object^)c);
}else{
c->Visible ^= true;
}
}
}
This is how to call a method control into the a safe thread for doing changes.
Right now I have read your comment for the question. Take a look on BackgroundWorker component, it is perfect to run asynchronous task with cancellation support and also it implements events to receive notifications about progress and end of the tasks.