Building a Document Scanning and Barcode Recognition Application with Qt Python

A few months ago, I published a cross-platform desktop barcode reading application built with Qt Python and Dynamsoft Barcode Reader. The supported input sources include real-time camera stream, image files, and screenshots. In this article, I will set document scanner as the input source. The SDK used for document scanning is Dynamic Web TWAIN, which is a cross-platform JavaScript library and supports Windows, macOS, and Linux.

Pre-requisites

  • SDK
pip install dbr
pip install PyQt5

Steps to Build a Cross-platform Document Scanning and Barcode Recognition Application

We are going to create a hybrid application that combines HTML5 and Python code.

1. Create Qt application with Qt widgets

Here are the required Qt Widgets:

  • QPushButton: one for acquiring images, and the other for decoding barcodes.
  • QTextEdit: show the barcode results.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWebChannel import QWebChannel

app = QApplication([])
win = QWidget()
win.setWindowTitle('Dynamic Web TWAIN and Dynamsoft Barcode Reader')
win.show()
app.exec_()
class WebView(QWebEngineView):
def __init__(self):
QWebEngineView.__init__(self)

layout = QVBoxLayout()
win.setLayout(layout)

view = WebView()
bt_scan = QPushButton('Scan Barcode Documents')
bt_read = QPushButton('Read Barcode')
text_area = QTextEdit()

layout.addWidget(view)
layout.addWidget(bt_scan)
layout.addWidget(bt_read)
layout.addWidget(text_area)
def keyPressEvent(event):
if event.key() == Qt.Key.Key_Q:
win.close()
elif event.key() == Qt.Key.Key_R:
refresh_page()

win.keyPressEvent = keyPressEvent
python app.py

2. Load HTML and JavaScript code in Qt application

Dynamic Web TWAIN SDK provides a variety of sample code located in the Dynamic Web TWAIN SDK <version>\Samples\ folder. We copy Samples\Scan\SourceList.html and Samples\Scan\Resources to the root of our Python project.

import os

class WebView(QWebEngineView):
def __init__(self):
QWebEngineView.__init__(self)

# Load web page and resource files to QWebEngineView
file_path = os.path.abspath(os.path.join(
os.path.dirname(__file__), "index.html"))
local_url = QUrl.fromLocalFile(file_path)
self.load(local_url)

3. Establish the communication between Python and JavaScript

The communication between Python and JavaScript is the key part of the application.

function AcquireImage() {
if (DWObject) {
var OnAcquireImageSuccess, OnAcquireImageFailure;
OnAcquireImageSuccess = OnAcquireImageFailure = function () {
DWObject.CloseSource();
};

DWObject.SelectSourceByIndex(document.getElementById("source").selectedIndex); //Use method SelectSourceByIndex to avoid the 'Select Source' dialog
DWObject.OpenSource();
DWObject.IfDisableSourceAfterAcquire = true; // Scanner source will be disabled/closed automatically after the scan.
DWObject.AcquireImage(OnAcquireImageSuccess, OnAcquireImageFailure);
}
}
def read_barcode():
frame = view.page()
frame.runJavaScript('AcquireImage();')

bt_scan.clicked.connect(acquire_image)
function getCurrentImage() {
if (DWObject) {
DWObject.ConvertToBase64(
[DWObject.CurrentImageIndexInBuffer],
Dynamsoft.DWT.EnumDWT_ImageType.IT_JPG,
function (result, indices, type) {
// TODO:
},
function (errorCode, errorString) {
console.log(errorString);
}
);
}
}
var backend;
new QWebChannel(qt.webChannelTransport, function (channel) {
backend = channel.objects.backend;
});

function getCurrentImage() {
if (DWObject) {
DWObject.ConvertToBase64(
[DWObject.CurrentImageIndexInBuffer],
Dynamsoft.DWT.EnumDWT_ImageType.IT_JPG,
function (result, indices, type) {
backend.onDataReady(result.getData(0, result.getLength()))
},
function (errorCode, errorString) {
console.log(errorString);
}
);
}
}
class Backend(QObject):
@pyqtSlot(str)
def onDataReady(self, base64img):

class WebView(QWebEngineView):
def __init__(self):
QWebEngineView.__init__(self)

# Load web page and resource files to QWebEngineView
file_path = os.path.abspath(os.path.join(
os.path.dirname(__file__), "index.html"))
local_url = QUrl.fromLocalFile(file_path)
self.load(local_url)
self.backend = Backend(self)
self.channel = QWebChannel(self.page())
self.channel.registerObject('backend', self.backend)
self.page().setWebChannel(self.channel)

def read_barcode():
frame = view.page()
frame.runJavaScript('getCurrentImage();')

bt_read.clicked.connect(read_barcode)

4. Decode barcodes from scanned documents

Finally, we can use Dynamsoft Barcode Reader to decode barcodes from base64 string and display the result on the text area.

from dbr import *
import base64

# Initialize Dynamsoft Barcode Reader
reader = BarcodeReader()
# Apply for a trial license https://www.dynamsoft.com/customer/license/trialLicense?product=dbr
reader.init_license('LICENSE-KEY')

class Backend(QObject):
@pyqtSlot(str)
def onDataReady(self, base64img):
imgdata = base64.b64decode(base64img)

try:
text_results = reader.decode_file_stream(bytearray(imgdata), '')
if text_results != None:
out = ''
for text_result in text_results:
out += "Barcode Format : "
out += text_result.barcode_format_string + '\n'
out += "Barcode Text : "
out += text_result.barcode_text + '\n'
out += "-------------------------------------------------" + '\n'

text_area.setText(out)
except BarcodeReaderError as bre:
print(bre)

Source Code

https://github.com/yushulx/Qt-scan-document-read-barcode

Manager of Dynamsoft Open Source Projects | Tech Lover