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

Xiao Ling - Aug 9 '21 - - Dev Community

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.

Prerequisites

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.

Qt MinGW

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
Enter fullscreen mode Exit fullscreen mode

CMake Configuration for Windows and Linux

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

  1. Choose the template Qt Widgets Application.

    Qt new project

  2. Select CMake as the build system.

    Qt new project

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")   

Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

To build the project, run:

mkdir build
cd build

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

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

cmake --build .
Enter fullscreen mode Exit fullscreen mode

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

MinGW link error

Replacing the code with C API can solve the issue:

reader = DBR_CreateInstance();
Enter fullscreen mode Exit fullscreen mode

Building C++ GUI Barcode Reader for Desktop

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

Qt UI

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()));
}
Enter fullscreen mode Exit fullscreen mode

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.

Qt C++ GUI barcode reader

Source Code

https://github.com/yushulx/cmake-cpp-barcode-qrcode/tree/main/examples/9.x/qt

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player