Qt 및 C/C++를 사용하여 데스크탑용 GUI 바코드 리더를 개발하는 방법

Qt 및 C/C++를 사용하여 데스크탑용 GUI 바코드 리더를 개발하는 방법

2022-10-02 last update

46 minutes reading qrcode barcode cpp qt
어떤 에디션Dynamsoft Barcode SDK을 사용하든 기본 알고리즘 인터페이스는 모두 C/C++로 구현됩니다. Dynamsoft 바코드 SDK를 정확하게 평가하려면 C++ 라이브러리를 시작하는 것이 좋습니다. 그러나 SDK 패키지에는 명령줄 샘플만 있습니다. 평범한 샘플 코드는 조작이 편하지 않아서 지겹다. 따라서 Qt를 사용하여 멋진 GUI 바코드 리더기를 작성하기로 결정했습니다. 이 기사에서는 Qt, MinGW, CMake 및 Dynamsoft C++ 바코드 SDK를 사용하여 Windows 및 Linux용 GUI 바코드 리더 애플리케이션을 개발하는 방법을 보여줍니다.

바코드 SDK 다운로드



Dynamsoft C++ SDK v8.6

Qt 설치




Qt Downloads를 방문하여 Windows용 Qt 설치 프로그램을 다운로드하십시오. 설치 프로그램은 Qt와 그 종속성을 자동으로 설치합니다.

Qt에는 MinGW 개발 환경이 포함되어 있지만 Visual Studio는 내 환경에서 자동 감지되지 않습니다.



저는 Windows에서 CMake 및 msvc를 사용하여 C++ 응용 프로그램을 빌드하는 데 익숙합니다. 하지만 그것은 중요하지 않습니다. Qt 프로젝트에 msvc를 사용하는 방법을 알아내지 않겠습니다. 대신 Windows 응용 프로그램을 빌드하기 위해 GCC를 사용하는 것이 좋습니다. MinGW에서 제공하는 GCC 컴파일러를 사용하려면 Qt\6.1.2\mingw81_64\bin 환경 변수에 Qt\Tools\mingw810_64\bin, PATH를 추가해야 합니다.

리눅스

Linux에 Qt를 설치하는 것은 Windows보다 훨씬 쉽습니다.

sudo apt-get install qt5-default


Windows 및 Linux용 CMake 구성


Qt Creator를 사용하여 프로젝트의 골격을 빠르게 설정합니다.

  • 템플릿Qt Widgets Application을 선택합니다.



  • 빌드 시스템으로 CMake를 선택하십시오.



  • 한 가지 주의할 점은 MinGW가 .lib 및 .dll 파일 연결을 지원한다는 것입니다. 다음과 같이 링크 라이브러리를 추가할 수 있습니다.

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

    CMAKE_HOST_WIN32CMAKE_HOST_UNIX를 사용하여 Windows와 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()
    


    프로젝트를 빌드하려면 다음을 실행합니다.

    mkdir build
    cd build
    
    ###############################
    # Windows with MinGW
    cmake -G "MinGW Makefiles" ..
    
    # Linux
    cmake ..
    ###############################
    
    cmake --build .
    


    C++ 클래스CBarcodeReader를 사용하는 경우 Windows에서 프로젝트를 빌드할 때 undefined reference 오류가 발생할 수 있습니다.



    코드를 C API로 교체하면 문제를 해결할 수 있습니다.

    reader = DBR_CreateInstance();
    


    데스크탑용 C++ GUI 바코드 리더 빌드



    먼저 Qt Creator를 사용하여 사용자 인터페이스를 디자인합니다.



    그런 다음 Qt UI 위젯을 MainWindow 생성자의 슬롯 기능에 연결합니다.

    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()));
    }
    


    다음은 슬롯 기능의 기본 구현입니다.

  • 이미지 파일을 로드합니다.

    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);
    }
    


  • 폴더를 열고 폴더에 있는 모든 이미지 파일을 로드합니다.

    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);
    }
    


  • 목록 위젯의 클릭 이벤트를 트리거합니다.

    void MainWindow::listWidgetClicked(QListWidgetItem *item)
    {
        ui->statusbar->showMessage(QString(item->text()));
    
        // Load the image file to QImage
        QImage image(item->text());
    }
    


  • 바코드 이미지를 디코딩하여 결과를 표시합니다.

    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);
        }
    }
    


  • 템플릿 파일을 로드하고 내보냅니다.

    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);
        }
    }
    


  • 다음은 GUI 바코드 리더 애플리케이션을 실행하는 스크린샷입니다.



    소스 코드



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