I have a simple Qt/C++ program which gathers a webcam image out of one of my LAN devices using a cv::VideoCapture
object.
The application is being built using Qt Quick and has an Image
QML Item which is being served with a picture every 500
milliseconds via a custom QQuickImageProvider implementation:
WebcamImageProvider::WebcamImageProvider()
: QQuickImageProvider(ImageType::Image)
{
connect(&_timer, &QTimer::timeout, this, &WebcamImageProvider::updateImage);
// refresh our picture every .5 seconds
_timer.start(500);
}
// overridden function from base class
QImage WebcamImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
Q_UNUSED(id)
Q_UNUSED(size)
Q_UNUSED(requestedSize)
return _img;
}
My main function looks like this:
// Qt Quick
#include <QQuickItem>
#include <QQuickWindow>
// QML + GUI
#include <QQmlContext>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
// webcam stuff
#include "webcamimageprovider.h"
/*
* Here we go
*/
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
// we're adding our self-made image provider instance here
WebcamImageProvider wip;
engine.addImageProvider("webcam", &wip);
engine.rootContext()->setContextProperty("webcamImageProvider", &wip);
engine.load(url);
return app.exec();
The code of my main.qml file should not be important here.
Inside my image provider class, I have provided the updateImage
slot as follows:
void WebcamImageProvider::updateImage()
{
/*
* Webcam image grapping via https://raspberrypi.stackexchange.com/a/85510/132942
*/
if (!_cap.open(_videoUrl)) {
qDebug() << "Error opening stream.";
}
if (!_cap.read(_mat)) {
qDebug() << "No video capture or webcam error.";
}
// via https://stackoverflow.com/a/12312326/4217759
_img = QImage(
static_cast<uchar*>(_mat.data),
_mat.cols,
_mat.rows,
static_cast<int>(_mat.step),
QImage::Format_BGR888
);
emit imageChanged(_img);
}
My problem here is that when my device is not reachable via network, the application will freeze completely as it gets stuck in the _cap.open()
function. Therefore, I am trying to outsource this function to a future so the image will be loaded asynchronously. Since I have literally zero idea of threading and futures, I randomly gave it a shot with using QFutures:
void WebcamImageProvider::updateImage()
{
/*
* Webcam image grapping via https://raspberrypi.stackexchange.com/a/85510/132942
*/
QFuture<void> future = QtConcurrent::run([&](){ _cap.open(_videoUrl); });
if (!future.isRunning()) {
qDebug() << "Error opening stream.";
}
if (future.isFinished()) {
if (!_cap.read(_mat)) {
qDebug() << "No video capture or webcam error.";
}
// via https://stackoverflow.com/a/12312326/4217759
_img = QImage(
static_cast<uchar*>(_mat.data),
_mat.cols,
_mat.rows,
static_cast<int>(_mat.step),
QImage::Format_BGR888
);
emit imageChanged(_img);
}
}
However, I won't get any image output from that.
Can someone help me on how to structure the code correctly so I can load my image asynchronously here?
Thanks a lot in advance.
1- you should know something , create setter for _img and emit changed in that ! because it is possible one day you need set Image in another functions and if you don't do this , you should duplicate emit fooChanged().
2- when you check these and is's not responsible for working in your method , you should throw an exception then handle them and if you wanted use qDebug().
3- my suggest is that (if I understood completely your job) create an thread that working in loop and always getting new images then create a queue worker and create another thread (worker) for process your scenario(here updateImage method).