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.

Prerequisites

  • Dynamsoft Capture Vision Trial License

  • 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 and Qt\Tools\mingw1310_64\bin to the PATH environment variable. One more thing is to create a system environment variable Qt5_DIR and set the value to C:\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.

  1. Choose the template Qt Widgets Application template.

    Qt new project

  2. Select CMake as the build system.

    Qt new project

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:

MinGW link error

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.

Qt UI

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.

Qt C++ GUI barcode reader

Source Code

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