How to Build a QR Code Scanner for Windows and Android with Qt QML

Requirements

Setting Up Qt Environment for Windows and Android

Open Qt Creator and go to Tools > Options > Devices > Android.

Building QR Code Scanner for Windows Using Qt QML and C++

We create a simple camera view in main.qml:

import QtQuick 2.0
import QtMultimedia 5.4

Item {
width: 1280
height: 720

Camera {
id: camera
}

VideoOutput {
id: viewfinder
width: parent.width
height: parent.height
source: camera
}
}

Adding a filter to get the video frame

In main.cpp, we create a QRCodeFilter class, a QRCodeFilterRunnable class and a QRCodeFilterResult class. We get the video frame via QVideoFrame QRCodeFilterRunnable::run().

class QRCodeFilter : public QAbstractVideoFilter
{
Q_OBJECT

public:
QVideoFilterRunnable *createFilterRunnable() override;

signals:
void finished(QObject *result);

private:
friend class QRCodeFilterRunnable;
};

class QRCodeFilterRunnable : public QVideoFilterRunnable
{
public:
QRCodeFilterRunnable(QRCodeFilter *filter) : m_filter(filter)
{
}
~QRCodeFilterRunnable() {}
QVideoFrame run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, RunFlags flags) override;

private:
QRCodeFilter *m_filter;
};

class QRCodeFilterResult : public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ text)

public:
QString text() const { return m_text; }

private:
QString m_text;
friend class QRCodeFilterRunnable;
};

QVideoFilterRunnable *QRCodeFilter::createFilterRunnable()
{
return new QRCodeFilterRunnable(this);
}

QVideoFrame QRCodeFilterRunnable::run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, RunFlags flags)
{
Q_UNUSED(surfaceFormat);
Q_UNUSED(flags);
QRCodeFilterResult *result = new QRCodeFilterResult;
result->m_text = "QVideoFrame";
emit m_filter->finished(result);

return *input;
}
int main(int argc, char* argv[])
{
QGuiApplication app(argc,argv);
qmlRegisterType<QRCodeFilter>("com.dynamsoft.barcode", 1, 0, "QRCodeFilter");
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
QObject::connect(view.engine(), &QQmlEngine::quit,
qApp, &QGuiApplication::quit);
view.setSource(QUrl("qrc:///main.qml"));
view.resize(1280, 720);
view.show();
return app.exec();
}
import QtQuick 2.0
import QtMultimedia 5.4

import com.dynamsoft.barcode 1.0

Item {
width: 1280
height: 720

Camera {
id: camera
}

VideoOutput {
id: viewfinder
width: parent.width
height: parent.height
source: camera
}
}
import QtQuick 2.0
import QtMultimedia 5.4

import com.dynamsoft.barcode 1.0

Item {
width: 1280
height: 720

Camera {
id: camera
captureMode: Camera.CaptureVideo
focus {
focusMode: Camera.FocusContinuous
focusPointMode: Camera.FocusPointCenter
}
objectName: "qrcamera"
}

VideoOutput {
id: viewfinder
width: parent.width
height: parent.height
source: camera
filters: [ qrcodefilter ]
}

QRCodeFilter {
id: qrcodefilter
onFinished: {
info.text = result.text;
}
}

Column {
x: 10
y: 10
Text {
font.pointSize: 24
color: "green"
text: "Qt Demo: QR Code Scanner"
}
Text {
id: info
font.pointSize: 12
color: "green"
text: info.text
}
}
}

Integrating C++ barcode SDK into Qt project

We create directories: libs/windows and include.

  • Copy DynamsoftBarcodeReaderx64.dll and vcomp110.dll to libs/windows.
  • Copy DynamsoftCommon.h and DynamsoftBarcodeReader.h to include.
TEMPLATE=app
TARGET=qrcodescanner

QT += quick qml multimedia

SOURCES += main.cpp
RESOURCES += qrcodescanner.qrc
HEADERS = include/DynamsoftCommon.h \
include/DynamsoftBarcodeReader.h

target.path = $$PWD
INSTALLS += target

win32: LIBS += -L$$PWD/libs/windows -lDynamsoftBarcodeReaderx64
  1. Include DynamsoftBarcodeReader.h and DynamsoftCommon.h.
#include "include/DynamsoftCommon.h"
#include "include/DynamsoftBarcodeReader.h"
class QRCodeFilterRunnable : public QVideoFilterRunnable
{
public:
QRCodeFilterRunnable(QRCodeFilter *filter) : m_filter(filter)
{
reader = DBR_CreateInstance();
char errorMessage[256];
PublicRuntimeSettings settings;
DBR_GetRuntimeSettings(reader, &settings);
settings.barcodeFormatIds = BF_QR_CODE;
DBR_UpdateRuntimeSettings(reader, &settings, errorMessage, 256);
DBR_InitLicense(reader, "LICENSE-KEY");
}
~QRCodeFilterRunnable() {DBR_DestroyInstance(reader);}
QVideoFrame run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, RunFlags flags) override;

private:
QRCodeFilter *m_filter;
void *reader;
};
QVideoFrame QRCodeFilterRunnable::run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, RunFlags flags)
{
Q_UNUSED(surfaceFormat);
Q_UNUSED(flags);
QRCodeFilterResult *result = new QRCodeFilterResult;

input->map(QAbstractVideoBuffer::ReadOnly);
int ret = DBR_DecodeBuffer(reader, input->bits(), input->width(), input->height(), input->bytesPerLine(), <PixelFormat>, "");
input->unmap();
}
input->map(QAbstractVideoBuffer::ReadOnly);

int width = input->width();
int height = input->height();
int total = width * height;

unsigned char* origin = input->bits();
unsigned char* grayscale = new unsigned char[total];
for (int i = 0; i < total; i++)
{
grayscale[i] = origin[i * 2];
}
int ret = DBR_DecodeBuffer(reader, grayscale, width, height, width, IPF_GRAYSCALED, "");
delete[] grayscale;
input->unmap();
QString out = "";
TextResultArray *handler = NULL;
DBR_GetAllTextResults(reader, &handler);
TextResult **results = handler->results;
int count = handler->resultsCount;

for (int index = 0; index < count; index++)
{
out += "Index: " + QString::number(index) + ", Elapsed time: " + QString::number(start.msecsTo(end)) + "ms\n";
out += "Barcode format: " + QLatin1String(results[index]->barcodeFormatString) + "\n";
out += "Barcode value: " + QLatin1String(results[index]->barcodeText) + "\n";
}
DBR_FreeTextResults(&handler);

result->m_text = out;
emit m_filter->finished(result);

Making the Codebase Work for Android with a Bit of Change

When thinking about integrating an Android barcode SDK, you may come up with how to link *.aar package and how to call Java API firstly. It is true that Dynamsoft provides a DynamsoftBarcodeReaderAndroid.aar package for Android programming. Nevertheless, there is a trick to use the mobile barcode SDK without linking the aar file in build.gradle.

unix: LIBS += -L$$PWD/libs/android -lDynamsoftBarcodeReaderAndroid

contains(ANDROID_TARGET_ARCH,arm64-v8a) {
ANDROID_EXTRA_LIBS = $$PWD/libs/android/libDynamsoftBarcodeReaderAndroid.so
}
import QtQuick 2.0
import QtMultimedia 5.4

import com.dynamsoft.barcode 1.0

Item {
width: 1280
height: 720

Camera {
id: camera
captureMode: Camera.CaptureVideo
focus {
focusMode: Camera.FocusContinuous
focusPointMode: Camera.FocusPointCenter
}
objectName: "qrcamera"
}
}
QObject *qmlCamera = view.findChild<QObject*>("qrcamera");
QCamera *camera = qvariant_cast<QCamera *>(qmlCamera->property("mediaObject"));
QCameraViewfinderSettings viewfinderSettings = camera->viewfinderSettings();
viewfinderSettings.setResolution(640, 480);
viewfinderSettings.setMinimumFrameRate(15.0);
viewfinderSettings.setMaximumFrameRate(30.0);
viewfinderSettings.setPixelFormat(QVideoFrame::Format_NV21); // cannot work
camera->setViewfinderSettings(viewfinderSettings);
DBR_DecodeBuffer(reader, input->bits(), width, height, input->bytesPerLine(), IPF_ABGR_8888, "");
#ifdef Q_OS_ANDROID
int ret = DBR_DecodeBuffer(reader, input->bits(), width, height, input->bytesPerLine(), IPF_ABGR_8888, "");
#else
unsigned char* origin = input->bits();
unsigned char* grayscale = new unsigned char[total];
for (int i = 0; i < total; i++)
{
grayscale[i] = origin[i * 2];
}
int ret = DBR_DecodeBuffer(reader, grayscale, width, height, width, IPF_GRAYSCALED, "");
delete[] grayscale;
#endif

Source Code

https://github.com/yushulx/Qt-QML-QR-code-scanner

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Xiao Ling

Xiao Ling

Manager of Dynamsoft Open Source Projects | Tech Lover