Advanced GUI Python Barcode and QR Code Reader for Windows, Linux, macOS and Rasberry Pi OS

Qt Designer is a powerful tool for creating cross-platform graphical user interfaces (GUIs) using Qt widgets. In this guide, we’ll use Qt Designer to develop an advanced GUI for a Python-based barcode and QR code reader, leveraging the Dynamsoft Python Barcode SDK. This application will be compatible with Windows, Linux, macOS, and Raspberry Pi OS, and includes all the robust features of the Dynamsoft Barcode Reader.

Prerequisites

  • OpenCV

      python3 -m pip install opencv-python
    
  • PySide6

      python3 -m pip install PySide6
    
  • Dynamsoft Barcode Reader

      python3 -m pip install dbr
    
  • Dynamsoft Barcode SDK License

Creating the UI with Qt Designer

To build the GUI, you’ll need the following Qt widgets:

  • Menu Bar
    • Open file
    • Open folder
    • Save template
    • Enter license key
    • About
  • Status Bar
    • Show status information
  • List Widget
    • List loaded file names
  • Label
    • Display image or webcam frame
  • Group Box
    • Groups for camera, template and barcode type settings
  • Push Button
    • Start camera
    • Stop camera
    • Load barcode template file
    • Export barcode template file
  • Combo Box
    • Camera resolution
  • Check Box
    • Auto-stop camera view when barcode recognition is done
    • Synchronously or asynchronously display barcode results
  • Text Edit
    • Enter parameter template
    • Show barcode decoding results

Qt designer

After designing your UI in Qt Designer, save the layout as a .ui file (e.g., design.ui). To use this layout in your Python application, convert the .ui file to a Python file using the following command:

PySide6-uic design.ui -o design.py

Steps to Build a Cross-platform Python Barcode and QR Code Reader with GUI

Let’s walk through building a Python barcode reader application step by step.

Loading the UI File

With the UI code ready, you can load it into your application as follows:

'''
Usage:
   app.py <license.txt>
'''

import sys
from PySide6.QtGui import QPixmap, QImage
from PySide6.QtWidgets import QApplication, QMainWindow, QInputDialog
from PySide6.QtCore import QFile, QTimer
from PySide6.QtWidgets import *
from design import Ui_MainWindow

from barcode_manager import *
import os
import cv2
from dbr import EnumBarcodeFormat, EnumBarcodeFormat_2

class MainWindow(QMainWindow):
    def __init__(self, license):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        
def main():
    try:
        with open(sys.argv[1]) as f:
            license = f.read()
    except:
        license = ""

    app = QApplication(sys.argv)

    window = MainWindow(license)
    window.show()

    sys.exit(app.exec_())


if __name__ == '__main__':
    print(__doc__)
    main()

Setting the License Key

To use the SDK, set the license key via the BarcodeReader class:

from dbr import *

def set_license(self, key):
    return BarcodeReader.init_license(key)

Signals and Slots

Next, initialize global variables and bind widget signals to slots for various functionalities:

# Initialization
self._all_data = {}
self._results = None

# Dynamsoft Barcode Reader
self._barcodeManager = BarcodeManager()
    if license is not None and license != '':
        error = self._barcodeManager.set_license(license)
        if error[0] != EnumErrorCode.DBR_OK:
            self.showMessageBox(error[1])

# Create a timer.
self.timer = None

# Open camera
self._cap = None
# self.openCamera()

# Resolution list
self.ui.comboBox.currentTextChanged.connect(self.onComboBoxChanged)

# The current path.
self._path = os.path.dirname(os.path.realpath(__file__))

# Camera button
self.ui.pushButton_open.clicked.connect(self.openCamera)
self.ui.pushButton_stop.clicked.connect(self.stopCamera)

# Load file
self.ui.actionOpen_File.triggered.connect(self.openFile)

# Load directory
self.ui.actionOpen_Folder.triggered.connect(self.openFolder)

# Export template
self.ui.actionExport_template.triggered.connect(self.exportTemplate)

# About
self.ui.actionAbout.triggered.connect(self.about)

# Set license
self.ui.actionEnter_License_Key.triggered.connect(self.setLicense)

## List widget
self.ui.listWidget.currentItemChanged.connect(self.currentItemChanged)

## Template load button
self.ui.pushButton_template.clicked.connect(self.loadTemplate)

## Template export button
self.ui.pushButton_export_template.clicked.connect(self.exportTemplate)

File Dialog for Opening and Saving Files

Handle essential file operations such as loading individual images, batch loading from folders, and saving template files:

  • Load a file:

      def openFile(self):
          filename = QFileDialog.getOpenFileName(self, 'Open File',
                                                  self._path, "Barcode images (*)")
          if filename is None or filename[0] == '':
              return
    
          filename = filename[0]
    
  • Load files from a folder:

      def openFolder(self):
          dir = QFileDialog.getExistingDirectory(self, 'Open Folder',
                                                  self._path, QFileDialog.ShowDirsOnly)
          if dir is '':
              return
    
          files = [os.path.join(dir, f) for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]
          if len(files) == 0:
              return
    
          for filename in files:
              self.appendFile(filename)
    
    
  • Save a template:

      def exportTemplate(self):
          filename = QFileDialog.getSaveFileName(self, 'Save File',
                                                  self._path, "Barcode Template (*.json)")
          if filename is None or filename[0] == '':
              return
    
          filename = filename[0]
    

Decoding Barcodes from JPEG, PNG, TIFF, PDF and GIF Files

OpenCV supports JPEG, PNG, and TIFF formats. To decode barcodes and QR codes from these formats, use:

frame = cv2.imread(filename)
results = self._reader.decode_buffer(frame)

For additional formats like PDF and GIF, use Dynamsoft’s SDK:

results = self._reader.decode_file(filename)

To display images in the label widget, convert the intermediate image data to a NumPy array:

intermediate_results = self._reader.get_all_intermediate_results()

imageData =  intermediate_results[0].results[0]
buffer = imageData.bytes
width = imageData.width
height = imageData.height
stride = imageData.stride
format = imageData.image_pixel_format
channel = 3
if format == EnumImagePixelFormat.IPF_RGB_888:
    channel = 3
elif format == EnumImagePixelFormat.IPF_BINARY or format == EnumImagePixelFormat.IPF_GRAYSCALED or format == EnumImagePixelFormat.IPF_BINARYINVERTED:
    channel = 1

if format == EnumImagePixelFormat.IPF_BINARY or format == EnumImagePixelFormat.IPF_BINARYINVERTED:
    whiteValue = 1
    if format == EnumImagePixelFormat.IPF_BINARYINVERTED:
        whiteValue = 0
    
    binData = bytearray(len(buffer) << 3)
    count = 0
    for pos in range(len(buffer)):
        for bit in range(7, -1, -1):
            if (buffer[pos] >> bit) & 0x01 == whiteValue:
                binData[count] = 255
            else:
                binData[count] = 0
            
            count += 1

    frame = np.ndarray((height, width, channel), np.uint8, binData, 0, (stride << 3, channel, 1))
else:

    frame = np.ndarray((height, width, channel), np.uint8, buffer, 0, (stride, channel, 1))

Showing Webcam Stream

Trigger a timer to continuously update and display the webcam stream:

def openCamera(self):

    width = 640; height = 480
    self._cap = cv2.VideoCapture(0)
    self._cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
    self._cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

    if not self._cap.isOpened(): 
        self.showMessageBox('Error', "Failed to open camera.")
        return

    self.timer = QTimer()
    self.timer.timeout.connect(self.nextFrameUpdate)
    self.timer.start(1000./24)

def nextFrameUpdate(self):
    ret, frame = self._cap.read()
    frame, self._results = self._barcodeManager.decode_frame(frame)

    self.showResults(frame, self._results)

Creating a Python Process for Real-Time Barcode Scanning

Since barcode decoding is CPU-intensive and Python’s GIL limits threading performance, use a separate process to avoid blocking the UI:

def create_barcode_process(self):
    size = 1
    self.frameQueue = Queue(size)
    self.resultQueue = Queue(size)
    self.barcodeScanning = Process(target=process_barcode_frame, args=(self.frameQueue, self.resultQueue, self._template, self._types, self._types2))
    self.barcodeScanning.start()

Running the GUI Python Barcode and QR Code Reader

To run the application, execute the following command:

python3 app_advanced.py license.txt

Here are screenshots of the GUI barcode reader on different platforms:

  • Windows

    GUI barcode reader on Windows

  • Raspberry Pi OS

    GUI barcode reader on Raspberry Pi OS

Source Code

https://github.com/yushulx/python-barcode-qrcode-sdk/tree/main/examples/official/9.x/qt_gui