So my task is this - I have a GUI thread with sliders of HSV values (among other things), and a worker thread that does all the OpenCV work and sends processed video images back to GUI thread.
Like it usually is, the OpenCV work is inside of an endless loop. The thing is, half the work is transforming the current video frame according to HSV values sent from GUI sliders. If sent before the loop starts, it works. But not while it's going on, and I need it to work on the fly.
Is there any good way to communicate with that thread and change the HSV values the OpenCV loop is using, or is it a fool's errand? I can think of two solutions, one of which is probably highly inefficient (involves saving values to a file). I'm fairly new to Qt, and I could've easly missed something in the documentation and tutorials.
edit:
Here's how my app works - in GUI thread, user picks a file. A signal with an url is sent to the worker thread, which starts working away. When the user changes HSV values, a signal is sent to change the values from another thread. If the loop hasn't been started, they're received and QDebug shows me that.
edit2:
I might've been thinking about it all wrong. Is there a way for the thread to pull values from the other one? Instead of waiting for them to be sent?
edit3:
kalibracja.cpp, for Micka.
int hueMin=0;
int hueMax=180;
int satMin=0;
int satMax=255;
int valMin=15;
int valMax=255;
int satMinDua=133; //tests
HSV::HSV(QObject * parent) : QObject(parent)
{
hsvThread = new QThread;
hsvThread ->start();
moveToThread( hsvThread );
}
HSV::~HSV() //destruktor
{
hsvThread ->exit(0);
hsvThread ->wait();
delete hsvThread ;
}
void HSV::processFrames(QString kalibracja) {
while(1) {
cv::VideoCapture kalibrowanyPlik;
kalibrowanyPlik.open(kalibracja.toStdString());
int maxFrames = kalibrowanyPlik.get(CV_CAP_PROP_FRAME_COUNT);
for(int i=0; i<maxFrames; i++)
{
cv::Mat frame;
cv::Mat gray;
//satMin=kontenerHsv->satMin;
qDebug() << "kalibracja satMin - " << satMin;
qDebug() << "fdfdf - " << satMinDua;
kalibrowanyPlik.read(frame);
cv::cvtColor(frame, gray, CV_BGR2GRAY);
QImage image(cvMatToQImage(frame));
QImage processedImage(cvMatToQImage(gray));
emit progressChanged(image, processedImage);
QThread::msleep(750); //so I can read qDebug() messages easly
}
}
}
void MainWindow::onProgressChagned(QImage image, QImage processedImage) {
QPixmap processed = QPixmap::fromImage(processedImage);
processed = processed.scaledToHeight(379);
ui->labelHsv->clear();
ui->labelHsv->setPixmap(processed);
QPixmap original = QPixmap::fromImage(image);
original = original.scaledToHeight(379);
ui->labelKalibracja->clear();
ui->labelKalibracja->setPixmap(original);
}
void HSV::updateHsv(QString hmin, QString hmax, QString smin, QString smax, QString vmin, QString vmax){
satMinDua=smin.toInt();
}
mainwindow.cpp connection
HSV *hsv = new HSV;
(.... all kinds of things ....)
void MainWindow::updateHsvValues() {
QMetaObject::invokeMethod(hsv, "updateHsv", Qt::QueuedConnection,
Q_ARG(QString, hmin),
Q_ARG(QString, hmax),
Q_ARG(QString, smin),
Q_ARG(QString, smax),
Q_ARG(QString, vmin),
Q_ARG(QString, vmax));
}
It is certainly possible, but you need to be careful.
One of the ways to achieve this would be:
Create an object that stores the "current" HSV values to be used
Give a reference (or pointer) to this object to both the GUI thread and the OpenCV thread
When the GUI wants to "tell" the processing thread to use new values, it published them to that object
When the processing thread is ready to move on the the next frame (start of loop body), it fetches the values from that object.
You only need to make sure that the set and get methods on that shared object are synchronized, using a mutex for example, to prevent the processing thread from reading half-written values (data races lead to undefined behavior in C++).
If you use QThread in the "wrong" way (by subclassing QThread and using ::run , compare to https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ ), signal-slot parameter change works in endless loops too:
This is a small sample thread for testing:
void MyThread::run()
{
// start an infinite loop and test whether the sliderchange changes my used parameters
std::cout << "start infinite loop" << std::endl;
while(true)
{
unsigned long long bigVal = 0;
int lastVal = mValue;
std::cout << "start internal processing loop " << std::endl;
for(unsigned long long i=0; i<1000000000; ++i)
{
bigVal += mValue;
if(lastVal != mValue)
{
std::cout << "changed value: " << mValue << std::endl;
lastVal = mValue;
}
}
std::cout << "end internal processing loop: " << bigVal << std::endl;
}
std::cout << "stop infinite loop" << std::endl;
}
with this SLOT, which is connected to the main window slider SIGNAL
void MyThread::changeValue(int newVal)
{
// change a paramter. This is a slot which will be called by a signal.
// TODO: make this call thread-safe, e.g. by atomic operations, mutual exclusions, RW-Lock, producer-consumer etc...
std::cout << "change value: " << newVal << std::endl;
mValue = newVal;
}
giving me this result after using the slider:
this is how the slot was connected:
QObject::connect(mSlider, SIGNAL(valueChanged(int)), mTestThread, SLOT(changeValue(int)) );
if the infinite loop is performed as some kind of workerObject method which was moved to the thread with moveToThread, you can either change the way how the slot is called:
QObject::connect(mSlider, SIGNAL(valueChanged(int)), mTestThread, SLOT(changeValue(int)), Qt::DirectConnection );
Never used, but I guess the same should work for invoke:
QMetaObject::invokeMethod(hsv, "updateHsv", Qt::DirectConnection, ...
(the main thread will call changeValue then so the worker thread doesnt need to stop processing to change values => value access should be thread safe!
or you have to process the event queue of that thread:
while(true)
{
[processing]
QApplication::processEvents();
}
I think the simplest solution here may be to take advantage of the fact that Qt Signals/Slots work across threads.
Setup the appropriate slots in the processing thread and then signal them from the GUI thread.
There are all sorts of interesting questions about whether you signal for every user input, or whether you batch up changes for a moment on the GUI side...
There is some ideas for thread sync in the docs: http://doc.qt.io/qt-5/threads-synchronizing.html
Related
I am writing a simple Qt C++ GUI window for the user to input certain values for a USRP device to record (i.e. input start frequency, stop frequency, etc..). Once the user inputted the values, the "EXECUTE" button is clicked and the execute_run_usrp() function is called in its own thread (so not to block the GUI). Then the STOP button should be able to be clicked at any time to terminate the thread running the function that runs the USRP, execute_run_usrp(), thus terminating the USRP recording process.
The function run_usrp(x,y,z,etc) is defined in another *.cpp file in the Project.
The problem that I am having that the STOP button when clicked only seems to "pause" the function...doesn't actually kill it (like with CTRL-C, which works great here)
Here is my code from MainWindow.cpp for the EXECUTE button click:
// run the following when EXECUTE button is clicked
void MainWindow::on_button_EXECUTE_clicked()
{
if ( ui->calculated_StartTime->text() == "" )
{
QMessageBox messageBox;
messageBox.critical(0,"Error","Hit the \"CALCULATE SCHEDULE\" button first above!!");
messageBox.setFixedSize(500,200);
return;
}
ui->button_STOP->setVisible(true);
ui->button_EXECUTE->setVisible(false);
auto function = std::bind([this]{execute_run_usrp();});
QThread* temp = QThread::create(function);
temp->start();
connect( ui->button_STOP, SIGNAL(clicked()), temp, SLOT( terminate() ));
}
Here is the execute_run_usrp() function:
void MainWindow::execute_run_usrp()
{
float startFreq = ui->input_startFreq->text().toFloat();
float stopFreq = ui->input_stopFreq->text().toFloat();
float stepFreq = ui->input_stepFreq->text().toFloat();
int nRepeats = ui->input_numRepeats->text().toInt();
float ipp = ui->input_IPP->text().toFloat();
int sweepCadence = ui->calculated_sweepCadence->text().toInt();
int numSweeps = ui->input_numSweeps->text().toInt();
std::string schedule_run = ui->calculated_StartTime->text().toStdString();
std::cout << startFreq << std::endl;
std::cout << stopFreq << std::endl;
std::cout << stepFreq<< std::endl;
std::cout << nRepeats << std::endl;
std::cout << ipp << std::endl;
std::cout << sweepCadence << std::endl;
std::cout << numSweeps << std::endl;
run_usrp(startFreq, stopFreq, stepFreq, nRepeats, ipp, sweepCadence, numSweeps, schedule_run);
}
And here is the STOP button code:
void MainWindow::on_button_STOP_clicked()
{
ui->button_STOP->setVisible(false);
ui->button_EXECUTE->setVisible(true);
}
Clicking the STOP button only seems to pause the function, doesn't actually kill it like doing CTRL-C with the keyboard. I think the UHD library (that runs the USRPs) spwans its own thread for running.
Question: How do I 100% terminate the entire function (and including any spwaned children from UHD) when I hit the STOP button ?
Thank you very much!
How do I 100% terminate the entire function (and including any spwaned children from UHD) when I hit the STOP button ?
From the code you've shown there's no easy answer imho.
The shortest one is: by incorporating some inter-thread communication inside the USRP function, provided you want some sort of graceful exit.
As for terminate, QT's docs state:
This function is dangerous and its use is discouraged. The thread can be terminated at any point in its code path. Threads can be terminated while modifying data. There is no chance for the thread to clean up after itself, unlock any held mutexes, etc. In short, use this function only if absolutely necessary.
Alternatively, the whole USRP function can be run inside a separate process, that is then killed. Again, plain killing of the process is far from graceful, still it seems easier and safer than doing it to a thread. You may want to check QProcess for reference.
I'm using QtConcurrent::run to execute some functions in background and not hang the GUI thread. In one function, I read logs from local SQlite database and send them to server by TCP socket.
Now I want to delay the execution after each log so the server has time to save it (TCP response is read in different thread). I'm stuck with Qt4.8 due to implementation limitations (many embeded devices - no chance to upgrade QT on them) and I can't use QThread::sleep(2) because it is protected in 4.8.
Is it possible to somehow pause the execution of thread inside QtConcurrent::run method or should I redesign it to implement my own class inheriting QThread?
void MainWindow::ReportFinishedHUs(bool asyncWork)
{
if(asyncWork == false)
{
QMutexLocker locker(&localDBmutex);
QList<QSqlRecord> HUsToReport = localDB->getHUsForBook();
qDebug() << "HUs to report" << HUsToReport.count();
if(!HUsToReport.isEmpty())
{
Cls_log::WriteDebugLog("HUs to report: " + QString::number(HUsToReport.count()));
foreach (QSqlRecord record, HUsToReport)
{
int _hu = record.indexOf("hu");
int _logTime = record.indexOf("logTime");
QString logTimeString = record.value(_logTime).toString();
QString hu = record.value(_hu).toString();
qDebug() << hu << logTimeString;
// creating message here ...
qDebug() << message;
emit sig_SendTCPMessage(message);
// this is where I need to wait for 2 seconds
QThread::sleep(2);
}
}
}
else
{
QtConcurrent::run(this, &MainWindow::ReportFinishedHUs, false);
}
}
EDIT:
Solved by usleep(2000000) which I somehow discarded for being platform specific... but hey, half of my aplication is platform specific and I only use it in embeded device with constant OS.
Keeping the question open if anyone can suggest more elegand solution using Qt methods. I like to get inspired.
I have a multi-threading application written in C++ with Qt5.7 and OpenNI. It has a main thread that starts a second thread which capture frame from a .oni recording file (asus xtion pro live) does some processing and through the Qt signal-slot mechanism pass the frame to the main thread, which display it using imshow().
What I want to do is to implement a pause key, so pressing for example 'p' the processing pause. I am thinking of something like this:
void Camera::run(){
while(!cameraStop && this->device.isValid())
{
try {
if (!buttonPause) {
getFrame();
process();
emit sigFrameImageReady(frame);
if (cv::waitKey(1)==112){
setButtonPause(!(getButtonPause()));
}
}
}
catch(std::exception &ex) {
std::cerr << "getFrame()" << ex.what() << std::endl;
}
}
}
In this way it doesn't work, I think that's because the frame is displayed by another thread (the main one), the waitKey() here simply blocks the entire process, but if I put it in the main thread, just after imshow() in this way:
void Process::FrameImageReady(cv::Mat FrameImage)
{
if (modedebug)
cv::imshow("bgr", FrameImage);
if (cv::waitKey(1)==112){
cam->setButtonPause(!(getButtonPause()));
}
}
waitkey seems to be ignored (image displaying works fine).. any idea?
EDIT
The GUI part is only for debugging purpose.
You should implement thread safe FIFO bufer or circular buffer in your displaying thread. Signal from the camera thread would be pushing images to this buffer and the displaying thread would be taking them out and display them in a separate loop. Only that way you separate the camera event loop from the display thread.
Maybe it's a late answer but I would like to present what I have done in my recent project, in case anyone is in the similar situation and want to use the same techniques.
Basically my project is a command line program and uses QT for multi threading & inter-thread communications, while we also want some minimal UI to display images, and handle user key events, so OpenCV Highgui module is pretty much enough for me. And here is what I did to make them work together:
(Important) you have to build OpenCV with QT support to make the
following code work, that is, check WITH_QT option in cmake-gui when building OpenCV. Or if you use vcpkg, use:
vcpkg install opencv[qt]:x64-windows
First create the class for working thread, I use this thread to constantly retrieve image frames from the camera buffer and notify the main thread every time the image is ready:
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread() : mAbort(false)
{
}
~MyThread()
{
mMutex.lock();
mAbort = true; // make the thread out of the loop
mMutex.unlock();
wait(); // wait until the run() function returns
cout << "thread terminated" << endl;
}
protected:
void run() override
{
while(!mAbort) {
cv::Mat frame;
if (myCamera->grab(frame) && !frame.empty()) {
emit imageReady(frame);
}
else {
cout << "failed to grab" << endl;
}
}
}
signals:
void imageReady(const cv::Mat& frame);
private:
bool mAbort;
QMutex mMutex;
};
Then create the controller class to handle the signal from working thread, I use it to display the frame. Note it is running in the main thread:
class MyController : public QObject
{
Q_OBJECT
public:
MyController(MyThread* pThread) : mThread(pThread)
{
// this is needed as the argument is passed through queued connection
qRegisterMetaType<cv::Mat>("CvMat");
connect(mThread, &MyThread::imageReady, this, &MyController::onImageReady);
connect(mThread, &MyThread::finished, mThread, &QObject::deleteLater);
mThread->start();
}
public slots:
void onImageReady(const cv::Mat& frame) {
cv::imshow("camera live view", frame);
}
private:
MyThread* mThread;
};
Now in the main function, we can start the thread, and handle key events using cv::waitKey(), note it will also start the QT main event loop internally.
Q_DECLARE_METATYPE(cv::Mat)
...
int main(int argc, char* argv[])
{
...
// start working thread
MyThread thread = new MyThread();
MyController controller(thread);
// handle key events
while (true) {
int key = cv::waitKey();
cout << "key = " << key << endl;
// press "ESC" to exit
if (key == 27) {
break;
}
}
// clean up
...
delete thread;
delete myCamera;
return 0;
}
I use the following code to talk to a USB-serial port device:
#include "masterthread.h"
#include <QtSerialPort/QSerialPort>
#include <QTime>
#include "Windows.h"
#include "Psapi.h"
#include <QDebug>
QT_USE_NAMESPACE
MasterThread::MasterThread(QObject *parent)
: QThread(parent), waitTimeout(0), quit(false)
{
}
MasterThread::~MasterThread()
{
mutex.lock();
quit = true;
cond.wakeOne();
mutex.unlock();
wait();
}
void MasterThread::run()
{
bool currentPortNameChanged = false;
QSerialPort serial;
serial.setPortName("COM3");
serial.setBaudRate(57600);
serial.setStopBits(static_cast<QSerialPort::StopBits>(1));
serial.setDataBits(static_cast<QSerialPort::DataBits>(8));
serial.setParity(static_cast<QSerialPort::Parity>(0));
serial.open(QIODevice::ReadWrite);
//Tell the serial port connected device to start talking
//--------------------------------------
const char init[] = { 0x0d, 0x0d, 0x0d };
serial.write(init, sizeof(init));
const char* cmd = "mavlink stop\n";
serial.write(cmd, strlen(cmd));
serial.write(init, 2);
cmd = "uorb start";
serial.write(cmd, strlen(cmd));
serial.write(init, 2);
cmd = "sh /etc/init.d/rc.usb\n";
serial.write(cmd, strlen(cmd));
serial.write(init, 4);
serial.waitForBytesWritten(100);
int i = 0;
int j = 0;
forever
{
//Write test data out
//-----------------------------
QByteArray test(2000, 't');
serial.write(test);
bool check = serial.waitForBytesWritten(100);
if (!check)
{
qDebug() << "FAIL: " << j++;
}
if (serial.waitForReadyRead(20))
{
QByteArray responseData = serial.readAll();
while (serial.waitForReadyRead(10))
responseData += serial.readAll();
QString response(responseData);
qDebug() << response;
}
QThread::msleep(20);
//Print memory usage
//---------------------------------------------------
if (i++ % 10 == 0)
{
PROCESS_MEMORY_COUNTERS memcount;
if (!GetProcessMemoryInfo(GetCurrentProcess(), &memcount, sizeof(memcount))) return;
qDebug()<<"----------------------------" << memcount.WorkingSetSize / 1024 << "KB memory used";
}
} // end foever
qDebug() << "Exiting forever loop";
}
with a simple main.cpp as:
#include <QApplication>
#include "masterthread.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MasterThread thread;
thread.start();
return app.exec();
}
But the memory usage keeps increasing, like 5~10MB per hour as if there are some leakage.
The device is suppose to be connected for days and weeks...
What am I doing wrong here? I am on Qt5.6 windows7 debug
Many Qt Components have an implicit dependency on its event loop.
While you are starting the main threads event loop with the call to app.exec(); you are not handling events generated by the QObjects created in the QThread MasterThread thread;. The details and nuances of Event handling in Qt are very well described on this page: https://wiki.qt.io/Threads_Events_QObjects#Threads_and_QObjects
But the solution boils down to: if you want to be able to process queued up Qt events in a thread where you are processing some long-running task you should call QCoreApplication::processEvents(); from time to time. This will prevent Qt events from endlessly queueing up.
EDITED after looking on the code Qt 5.7,5.6,5.5 and reading docs.
As an answer is already accepted, I would just add some thoughts here as it's too long for comments.
Keep things short - an answer you accepted is wrong..
There are two sides of the story. And as SO answers often taken 'as it is as long as they work' I'd like to explain myself...
If you look on a code provided - there is nothing wrong with it. All objects are properly stack allocated and should be destroyed automatically.
Point is that QtSerial uses deleteLater() and then a question - how to delete those allocations properly.
If any module/object/code uses deleteLater() it requires an event loop, if deleteLater() called on a thread without event loop, object will be deleted after thread is terminated. As long as there is no event loop running for code above, processEvents will no work.. actually processEvents() is not something which is used for this, because a whole idea to return from the context which is called deleteLater() and have a next run, and that's checked in the Qt Source Code, so calling processEvent() straight after without incrementing loop count will do nothing at all, that's why answer you accepted is totally wrong.
Conclusion:
If any object requires event loop running it should be EXPLICITELY stated in the documentation as there is nothing wrong in using QIODevice in sync mode outside event loop.
So at my opinion,point is - its a bug in the QT Serial itself which I suggest you report.
In general it's really wrong practice for Qt to run never-ending loops..
It's much much better and cleaner to use QObject Worker tactic which is pushed to the thread, have proper even loop running etc.
For small 'threaded' tasks it's much better to use QtConcurrent.
Proper Workaround:
you will have a thread with properly running event loop and a timer firing at 20ms to do your things
// main thread:
class Worker: public QObject {
public:
Worker();
public slots:
onInit() {
// initialize everything
startTimer(20);
}
protected:
void timerEvent(..) {
// do your things every 20ms
}
}
...
QThread * pWorkerThread = new QThread();
pWorkerThread->setObjectName(QString("Serial"));
Worker * pWorker = new Worker();
Worker->setObjectName(QString("Common Storage Impl"));
Worker->moveToThread(WorkerThread);
connect(pWorkerThread, SIGNAL(started()), pWorker, SLOT(onInit()));
connect(pWorkerThread, SIGNAL(finished()), pWorker, SLOT(deleteLater()));
connect(pWorkerThread, SIGNAL(finished()), pWorkerThread, SLOT(deleteLater()));
pWorkerThread->start();
...
I have some issues with this:
First I create my object and move it to a thread:
FileUploader *fileUploader = new FileUploader(fileList_, start, (offset == 0 ? (fileList_.count() - start) : offset));
QThread *fileUploaderThread = new QThread;
fileUploader->moveToThread(fileUploaderThread);
fileUploaderThreads_.append(fileUploaderThread);
fileUploaders_.append(fileUploader); // contains pointers to the objects
connect(fileUploader, SIGNAL(progressChangedAt(int)), model_, SLOT(reportProgressChanged(int)), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(statusChangedAt(int)), model_, SLOT(reportStatusChanged(int)), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), fileUploaderThread, SLOT(quit()), Qt::QueuedConnection);
connect(fileUploaderThread, SIGNAL(finished()), this, SLOT(checkIfFinished()), Qt::QueuedConnection);
The in the slot checkIfFinished() I wanna go though all the threads and see if they quit.
qDebug() << "one thread done";
foreach(QThread *thread, fileUploaderThreads_) { // or FileUploader* fileuploader, fileUploaders_ ?
if(thread && !thread->isFinished()) {
qDebug() << "not finished " << thread->currentThreadId();
return; // not done
}
}
When this is printed out, I only get the main thread Id, not the threads. I tried to print the threads id's out but with no luck (after they are started ofc).
The reason why I'm doing this I because the person writing "Advanced Qt Programming - Mark S" did a similar thing with QThreads that he placed in a list and checked if they where finished. The only thing thats working now is the connection which kills the thread when the fileUploader is done.
Also, how do I store pointers for the threads? I wounder how I will be able to delete them all if they don't seem to point to the right threads.
Edit:
I tried to store the QObjects in a list instead and do this:
QThread *senderx = qobject_cast<QThread*>(sender());
qDebug() << "one thread done" << senderx;
foreach(FileUploader *fileUploader, fileUploaders_) {
if(fileUploader && !fileUploader->thread()->isFinished()) {
qDebug() << "not finished " << fileUploader->thread();
return; // not done
}
}
//done
qDebug() << "done";
setButtonState(false);
And my outprint on the last call is :
one thread done QThread(0x43ee180)
not finished QThread(0x43ee180)
How is this possible? It is done, but the method says otherwise.
you want QObject::thread(), not QThread::currentThreadId() - the second one returns the thread that the function was called in.
Once you fix that, your pointers will work fine.