How to Develop a Desktop GUI Barcode Reader with Qt and C/C++
Dynamsoft Barcode Reader provides a robust C++ SDK for barcode detection across Windows, Linux, and macOS. In this tutorial, we will walk you through building a desktop barcode reader application with a graphical user interface (GUI) using Qt and C++. By combining the cross-platform capabilities of the Qt C++ SDK with the powerful barcode scanning capabilities of Dynamsoft’s C++ SDK, you’ll learn how to develop a clean, functional barcode reader for desktop environments.
This article is Part 7 in a 16-Part Series.
- Part 1 - Building a C/C++ Barcode & QR Code Reader for Raspberry Pi with Dynamsoft SDK
- Part 2 - CMake: Build C++ Project for Windows, Linux and macOS
- Part 3 - How to Port Visual Studio C++ Project to Linux with CMake
- Part 4 - Insight Into Dynamsoft Barcode SDK Decoding Performance
- Part 5 - Building ARM64 Barcode and QR Code Scanner on Nvidia Jetson Nano
- Part 6 - How to Decode QR Code on Mac with Apple Silicon
- Part 7 - How to Develop a Desktop GUI Barcode Reader with Qt and C/C++
- Part 8 - How to Build a Desktop Barcode Scanner with Webcam Support Using Qt QCamera
- Part 9 - Building Command-line Barcode and QR Code Reader in C++
- Part 10 - How to Build Linux ARM32 and Aarch64 Barcode QR Scanner in Docker Container
- Part 11 - How to Link MSVC DLLs with MinGW GCC in Windows
- Part 12 - Transforming Raspberry Pi 4 into a Barcode Scanner with a C++ App, USB Camera, and OLED Display
- Part 13 - Building Windows Desktop Barcode Reader with Win32 API and Dynamsoft C++ Barcode SDK
- Part 14 - How to Build a Command-Line Barcode Reader with Rust and C++ Barcode SDK
- Part 15 - How to Decode Barcode and QR Code from WebP Images in C++ and Python
- Part 16 - Building a Desktop C++ Barcode Scanner with Slimmed-Down OpenCV and Webcam
Prerequisites
-
Qt Installation
-
Windows Visit Qt Downloads to download the Qt installer for Windows. The installer will automatically install Qt and its required dependencies.
To make Qt work with MinGW, you need to add
Qt\5.15.2\mingw81_64\bin
andQt\Tools\mingw1310_64\bin
to thePATH
environment variable. One more thing is to create a system environment variableQt5_DIR
and set the value toC:\Qt\5.15.2
. -
Linux
Installing Qt on Linux is simpler than on Windows:
sudo apt-get install qt5-default
-
CMake Configuration for Windows and Linux
We will use Qt Creator
to quickly set up the basic structure of our project.
-
Choose the template
Qt Widgets Application
template. -
Select
CMake
as the build system.
Linking Dynamsoft Barcode Libraries
We use CMAKE_HOST_WIN32
and CMAKE_HOST_UNIX
to distinguish between Windows and Linux environments in the build script.
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()
Building the Project
To compile the project, run the following commands:
mkdir build
cd build
###############################
# Windows with MinGW
cmake -G "MinGW Makefiles" ..
# Linux
cmake ..
###############################
cmake --build .
Solving Undefined Reference Errors
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 the Desktop GUI Barcode Reader
We use Qt Creator
to design the graphical user interface for our application.
Connecting Qt UI with Slot Functions
After designing the UI, we connect the UI elements to slot functions in the 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()));
}
Implementing Slot Functions
Here’s the implementation 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); }
-
Handle the list widget click event.
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); } }
Running the Application
Below is a screenshot of the GUI barcode reader application in action. The intuitive interface and seamless barcode detection make it easy to use for scanning and decoding barcodes.
Source Code
https://github.com/yushulx/cmake-cpp-barcode-qrcode-mrz/tree/main/examples/9.x/qt