How to Develop GUI Barcode Reader for Desktop with Qt and C/C++
Tags: qt, cpp, barcode, qrcode
Published: 2021-08-09 01:28:59

No matter which edition of [Dynamsoft Barcode SDK](https://www.dynamsoft.com/barcode-reader/overview/) 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](https://www.dynamsoft.com/barcode-reader/downloads/)

## Qt Installation

**Windows**
Visit [Qt Downloads](https://www.qt.io/download-open-source/) 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](https://www.dynamsoft.com/codepool/img/2021/08/qt-kit-mingw.png)

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:

```bash
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.
1. Choose the template `Qt Widgets Application`.
    
    ![Qt new project](https://www.dynamsoft.com/codepool/img/2021/08/qt-project-new.png)

2. Select `CMake` as the build system.

    ![Qt new project](https://www.dynamsoft.com/codepool/img/2021/08/qt-project-cmake.png)

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

```cmake
# 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.

```cmake
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:

```bash
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:

![MinGW link error](https://www.dynamsoft.com/codepool/img/2021/08/mingw64-link-error.png)

Replacing the code with C API can solve the issue:

```c
reader = DBR_CreateInstance();
``` 

## Building C++ GUI Barcode Reader for Desktop

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

![Qt UI](https://www.dynamsoft.com/codepool/img/2021/08/qt-ui.png)

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

```cpp
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.
    
    ```cpp
    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.
    
    ```cpp
    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.

    ```cpp
    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.
    
    ```cpp
    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.
    
    ```cpp
    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](https://www.dynamsoft.com/codepool/img/2021/08/qt-desktop-barcode-reader.png)

## Source Code
[https://github.com/yushulx/Qt-desktop-barcode-reader](https://github.com/yushulx/Qt-desktop-barcode-reader)