How to Develop GUI Barcode Reader for Desktop with Qt and C/C++

Xiao Ling
6 min readAug 5, 2021

No matter which edition of Dynamsoft Barcode SDK you use, the underlying algorithm interfaces are all implemented in C/C++. To evaluate Dynamsoft barcode SDK precisely, you’d better get started with the C++ libraries. However, there is only a command-line sample available in the SDK package. I have been tired of the plain sample code because it is not convenient for operation. Therefore, I decide to write a fancy GUI barcode reader using Qt. This article shows how to develop a GUI barcode reader application for Windows and Linux with Qt, MinGW, CMake, and Dynamsoft C++ barcode SDK.

Barcode SDK Download

Dynamsoft C++ SDK v8.6

Qt Installation

Windows Visit Qt Downloads and download the Qt installer for Windows. The installer will automatically install Qt and its dependencies.

Qt contains MinGW development environment, whereas Visual Studio is not auto-detected in my environment.

I am used to build C++ applications with CMake and msvc on Windows. But it doesn't matter. I am not going to figure out the way of using msvc for Qt project. Instead, using GCC for building Windows application is a nice try. To use the GCC compiler provided by MinGW, you need to add Qt\6.1.2\mingw81_64\bin and Qt\Tools\mingw810_64\bin to the PATH environment variable.

Linux

Installing Qt on Linux is much easier than Windows:

sudo apt-get install qt5-default

CMake Configuration for Windows and Linux

We use Qt Creator to quickly set up the skeleton of our project.

Choose the template Qt Widgets Application.

Select CMake as the build system.

One thing to note is that MinGW supports linking .lib and .dll files. You can add link libraries as follows:

# DBRx64.lib
target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets "DBRx64")

# Or DynamsoftBarcodeReaderx64.dll
target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets "DynamsoftBarcodeReaderx64")

We use CMAKE_HOST_WIN32 and CMAKE_HOST_UNIX to distinguish Windows and Linux.

if (CMAKE_HOST_WIN32)
link_directories("${PROJECT_SOURCE_DIR}/platform/windows/bin/")
elseif(CMAKE_HOST_UNIX)
link_directories("${PROJECT_SOURCE_DIR}/platform/linux/")
endif()

if (CMAKE_HOST_WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets "DynamsoftBarcodeReaderx64")
elseif(CMAKE_HOST_UNIX)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets "DynamsoftBarcodeReader")
endif()

To build the project, run:

mkdir build
cd build

###############################
# Windows with MinGW
cmake -G "MinGW Makefiles" ..

# Linux
cmake ..
###############################

cmake --build .

If you use C++ class CBarcodeReader, you may meet the undefined reference error when building the project on Windows:

Replacing the code with C API can solve the issue:

reader = DBR_CreateInstance();

Building C++ GUI Barcode Reader for Desktop

First, we use Qt Creator to design the user interface.

Then, connect the Qt UI widgets to slot functions in MainWindow constructor:

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();

protected:
void closeEvent(QCloseEvent *);

private:
Ui::MainWindow *ui;
void *reader;
void showImage(const QImage &image, QString fileName);
void showMessageBox(QString title, QString content);

private slots:
void openFile();
void openFolder();
void listWidgetClicked(QListWidgetItem *item);
void exportTemplate();
void about();
void setLicense();
void loadTemplate();
};

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);

// Dynamsoft Barcode Reader
reader = DBR_CreateInstance();

// Open a file.
connect(ui->actionOpen_File, SIGNAL(triggered()), this, SLOT(openFile()));

// Open a folder.
connect(ui->actionOpen_Folder, SIGNAL(triggered()), this, SLOT(openFolder()));

// List widget event.
connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(listWidgetClicked(QListWidgetItem*)));

// Export template.
connect(ui->actionExport_template, SIGNAL(triggered()), this, SLOT(exportTemplate()));

// About dialog.
connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about()));

// Set license.
connect(ui->actionEnter_License_Key, SIGNAL(triggered()), this, SLOT(setLicense()));

// Template load button
connect(ui->pushButton_template, SIGNAL(clicked()), this, SLOT(loadTemplate()));

// Template export button
connect(ui->pushButton_export_template, SIGNAL(clicked()), this, SLOT(exportTemplate()));
}

Here are the primary implementations of the slot functions:

Load an image file.

QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "", tr("Barcode images (*)"));
if (!fileName.isEmpty()) {
// Add to list
ui->listWidget->addItem(fileName);
ui->statusbar->showMessage(fileName);

// Load the image file to QImage
QImage image(fileName);
}

Open a folder and load all the image files in the folder.

QString folderName = QFileDialog::getExistingDirectory(this, tr("Open Folder"), "", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (!folderName.isEmpty()) {
// Get all files in the folder
QStringList fileNames = QDir(folderName).entryList(QDir::Files | QDir::NoDotAndDotDot);
// Add to list
for (int i = 0; i < fileNames.size(); i++) {
ui->listWidget->addItem(QDir::cleanPath(folderName + QDir::separator() + fileNames.at(i)));
}

ui->statusbar->showMessage(folderName);
}

Trigger the click event of list widget.

void MainWindow::listWidgetClicked(QListWidgetItem *item)
{
ui->statusbar->showMessage(QString(item->text()));

// Load the image file to QImage
QImage image(item->text());
}

Decode a barcode image and display the result.

void MainWindow::showImage(const QImage &image, QString fileName)
{
ui->textEdit_results->setText("");
if (!image.isNull()) {
QPixmap pm = QPixmap::fromImage(image);
QPainter painter(&pm);
painter.setPen(Qt::red);

/************************
* Barcode detection.
************************/

// Get the template content and initialize the runtime settings.
QString content = ui->textEdit_results->toPlainText();
char errorMessage[256];
DBR_InitRuntimeSettingsWithString(reader, content.toStdString().c_str(), CM_OVERWRITE, errorMessage, 256);

// Set barcode types.
int types = 0, types2 = 0;
if (ui->checkBox_code39->isChecked()) {types |= BF_CODE_39;}
if (ui->checkBox_code93->isChecked()) {types |= BF_CODE_93;}
if (ui->checkBox_code128->isChecked()){ types |= BF_CODE_128;}
if (ui->checkBox_codabar->isChecked()){ types |= BF_CODABAR;}
if (ui->checkBox_itf->isChecked()){ types |= BF_ITF;}
if (ui->checkBox_ean13->isChecked()){ types |= BF_EAN_13;}
if (ui->checkBox_ean8->isChecked()){ types |= BF_EAN_8;}
if (ui->checkBox_upca->isChecked()){ types |= BF_UPC_A;}
if (ui->checkBox_upce->isChecked()){ types |= BF_UPC_E;}
if (ui->checkBox_industrial25->isChecked()){ types |= BF_INDUSTRIAL_25;}
if (ui->checkBox_qrcode->isChecked()){ types |= BF_QR_CODE;}
if (ui->checkBox_pdf417->isChecked()){ types |= BF_PDF417;}
if (ui->checkBox_aztec->isChecked()){ types |= BF_AZTEC;}
if (ui->checkBox_maxicode->isChecked()){ types |= BF_MAXICODE;}
if (ui->checkBox_datamatrix->isChecked()){ types |= BF_DATAMATRIX;}
if (ui->checkBox_gs1->isChecked()){ types |= BF_GS1_COMPOSITE;}
if (ui->checkBox_patchcode->isChecked()){ types |= BF_PATCHCODE;}
if (ui->checkBox_dotcode->isChecked()){ types2 |= BF2_DOTCODE;}
if (ui->checkBox_postalcode->isChecked()){ types2 |= BF2_POSTALCODE;}

PublicRuntimeSettings settings;
DBR_GetRuntimeSettings(reader, &settings);
settings.barcodeFormatIds = types;
settings.barcodeFormatIds_2 = types2;
DBR_UpdateRuntimeSettings(reader, &settings, errorMessage, 256);

int errorCode = DBR_DecodeFile(reader, fileName.toStdString().c_str(), "");
TextResultArray *handler = NULL;
DBR_GetAllTextResults(reader, &handler);

if (handler->resultsCount == 0)
{
ui->textEdit_results->setText("No barcode found.\n");
DBR_FreeTextResults(&handler);
return;
}

QString out = "";
TextResult **results = handler->results;
for (int index = 0; index < handler->resultsCount; index++)
{
LocalizationResult* localizationResult = results[index]->localizationResult;
out += "Index: " + QString::number(index) + "\n";
out += "Barcode format: " + QString(results[index]->barcodeFormatString) + "\n";
out += "Barcode value: " + QString(results[index]->barcodeText) + "\n";
out += "Bounding box: (" + QString::number(localizationResult->x1) + ", " + QString::number(localizationResult->y1) + ") "
+ "(" + QString::number(localizationResult->x2) + ", " + QString::number(localizationResult->y2) + ") "
+ "(" + QString::number(localizationResult->x3) + ", " + QString::number(localizationResult->y3) + ") "
+ "(" + QString::number(localizationResult->x4) + ", " + QString::number(localizationResult->y4) + ")\n";
out += "----------------------------------------------------------------------------------------\n";

painter.drawLine(localizationResult->x1, localizationResult->y1, localizationResult->x2, localizationResult->y2);
painter.drawLine(localizationResult->x2, localizationResult->y2, localizationResult->x3, localizationResult->y3);
painter.drawLine(localizationResult->x3, localizationResult->y3, localizationResult->x4, localizationResult->y4);
painter.drawLine(localizationResult->x4, localizationResult->y4, localizationResult->x1, localizationResult->y1);
}

DBR_FreeTextResults(&handler);

painter.end();
ui->label->setPixmap(pm.scaled(ui->label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));

ui->textEdit_results->setText(out);
}
}

Load and export the template file.

void MainWindow::loadTemplate()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "", tr("Barcode Template (*.json)"));
QFile file(fileName);
if (file.open(QIODevice::ReadOnly)) {
QTextStream stream(&file);
QString content = stream.readAll();
// DBR_LoadSettingsFromStringPtr(reader, content.toStdString().c_str());
ui->textEdit_template->setText(content);
}
}

void MainWindow::exportTemplate()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "", tr("Barcode Template (*.json)"));
QFile file(fileName);
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);

char* pContent = NULL;
DBR_OutputSettingsToStringPtr(reader, &pContent, "currentRuntimeSettings");
stream << QString(pContent);
DBR_FreeSettingsString(&pContent);
}
}

Here is the screenshot of running the GUI barcode reader application.

Source Code

https://github.com/yushulx/Qt-desktop-barcode-reader

Originally published at https://www.dynamsoft.com on August 5, 2021.

--

--

Xiao Ling

Manager of Dynamsoft Open Source Projects | Tech Lover