使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用

Dynamsoft Barcode Reader SDK一款多功能的条码读取控件,只需要几行代码就可以将条码读取功能嵌入到Web或桌面应用程序。这可以节省数月的开发时间和成本。能支持多种图像文件格式以及从摄像机或扫描仪获取的DIB格式。使用Dynamsoft Barcode Reader SDK,你可以创建强大且实用的条形码扫描仪软件,以满足你的业务需求。

慧都网免费下载Dynamsoft Barcode Reader最新版

Electron是最流行的跨平台桌面应用程序框架之一,已在众多著名应用程序中使用。借助网络技术,开发人员可以快速构建适用于Windows,macOS和Linux的产品。在本文中,我们将使用Electron通过Dynamsoft Barcode Reader构建条形码解码和扫描应用程序。

安装

要创建我们的应用,我们需要以下软件/软件包/工具。

  • Dynamsoft条码阅读器
  • 电子
  • npm
  • Node.js
  • 节点gyp
  • 电子重建
  • GCC / Clang /其他C ++编译器

Node.js是电子和节点程序包管理的基础。在继续开发工作之前,必须在主机中安装Node.js。

Dynamsoft条码读取器是行业领先的条码解码SDK。它支持各种格式的条形码,并且可以在所有主流平台上运行。

我们将为我们的项目导入一个C ++插件。node-gyp是用于将C ++程序编译为Node.js插件的配置工具。

创建项目
初始化项目
我们使用官方的快速入门示例开始我们的工作。

首先,让我们将示例项目克隆到我们的主机。

git clone https://github.com/electron/electron-quick-start.git

然后,安装依赖项并启动项目以测试我们是否可以正确运行Electron项目。

 

cd electron-quick-start
npm install
npm start

如果没有错误,您将看到一个弹出窗口,显示“ Hello World!”。

Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用

电子应用程序成功运行

在您的终端中,输入以下命令以安装所需的依赖项。

npm install --save-dev electron-rebuild
npm install -g node-gyp

启用Node.js集成

默认情况下,Node.js集成处于禁用状态。我们将其更改为true以便require在导入模块时使用。在main.js中,我们在中指定此值BrowserWindow。

const mainWindow = new BrowserWindow({
    width: 1280,
    height: 1024,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: true // Change to true to use Node API
    }
  })

导入Node.js C ++插件

Dynamsoft条形码阅读器提供JavaScript和C / C ++条形码SDK。为了获得Electron应用程序的更好性能,我们可以用C ++编写Node.js条形码插件。我们可以从Github获取插件源代码。

我们libs在项目根目录下创建一个名为文件夹,然后克隆节点条形码存储库。

mkdir libs
cd libs
git clone https://github.com/Dynamsoft/nodejs-barcode

克隆过程完成后,我们将使用node-gyp更改目录并为我们的项目构建库。

cd nodejs-barcode
../../node_modules/.bin/electron-rebuild

该命令electron-rebuild将检测构建环境并获取相应的标头以完成构建。

Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用

通过电子重建来重建模块

构建完成后,将创建包含动态链接库的几个目录。

Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用

标头和已编译的二进制文件将放在三个文件夹中

由于该库已正确引用了二进制库,因此我们只需要index.js在项目中导入文件即可。

返回项目根目录,并使用编辑器打开main.js文件。main.js是整个应用程序的起点。我们导入条形码附件以进行快速测试。

const dbr = require('dbr')
Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用

需要nodejs-barcode库后未生成任何错误

从文件解码条形码

在开始编码之前,让我们看一下API。

Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用

Node.js-条形码库的API

  • decodeBufferAsync:从原始RGBA数据读取和解码。
  • decodeFileAsync:通过读取图像文件进行读取和解码。
  • decodeFileStreamAsync:通过读取文件流来读取和解码。
  • decodeBase64Async:图像数据作为base64字符串传递
  • decodeYUYVAsync:图像数据是RGBA以外的YUV格式

创建解码服务

我们创建两个脚本文件:background-services.js和foreground-services.js。

Web部件使用了前台服务.js,而后台服务.js调用了C ++附加API并提供了解码服务。

在background-services.js中,我们导入Node.js条码插件。

const { ipcMain } = require('electron')
const dbr = require('./libs/nodejs-barcode/index')
const barcodeTypes = dbr.barcodeTypes
  
function decodeFileAsync(evt, filepath) {
}
  
function decodeBase64Async(evt, base64Str) {
}
  
function decodeBufferAsync(evt, imgData, width, height) {
}
  
function register() {
  ipcMain.on('decodeFileAsync', decodeFileAsync)
  ipcMain.on('decodeBase64Async', decodeBase64Async)
  ipcMain.on('decodeBufferAsync', decodeBufferAsync)
}
  
module.exports = {
  register
}

由于Node.js使用事件模型,因此我们使用事件侦听器进行进程间通信。要在主进程上注册侦听器,我们使用ipcMain.on添加事件名称和相应的处理程序。我们将注册过程放在函数中,register以控制何时注册这些事件。

在中foreground-services.js,我们现在可以使用这些服务。

 

const { ipcRenderer } = require('electron')
  
const DEBUG = false
  
ipcRenderer.setMaxListeners(30)
 
const resultBuffer = {
  lastUpdate: null,
  results: []
}
 
const frameBuffer = {
  lastUpdate: null,
  imgData: Uint8ClampedArray.from([]),
  width: 0,
  height: 0,
  channel: 0,
  decoding: false
}
 
function setFrameBuffer(img, width, height, channel) {
  console.log('frame buffer to update')
  frameBuffer.imgData = img
  frameBuffer.width = width
  frameBuffer.height = height
  frameBuffer.channel = channel
  frameBuffer.lastUpdate = Date.now()
}
 
function startVideoDecode() {
  frameBuffer.decoding = true
  videoDecode()
}
 
function stopVideoDecode() {
  frameBuffer.decoding = false
}
 
function videoDecode() {
  ipcRenderer.send('videoDecode', frameBuffer.imgData, frameBuffer.width, frameBuffer.height)
}
 
ipcRenderer.on('videoDecode-next', (evt, msg) => {
  updateResultBuffer(msg)
  if (frameBuffer.decoding)
    videoDecode()
})
 
function decodeFileAsync(filepath) {
  if (DEBUG)
    console.log('sending decodeFileAsync from renderer process with args: ' + filepath)
  ipcRenderer.send('decodeFileAsync', filepath)
}
 
function decodeBase64Async(base64Str) {
  if (DEBUG)
    console.log('sending decodeBase64Async from renderer process')
  ipcRenderer.send('decodeBase64Async', base64Str)
}
 
function decodeBufferAsync(imgData, width, height) {
  if (DEBUG)
    console.log('sending decodeBufferAsync from renderer process')
  ipcRenderer.send('decodeBufferAsync', imgData, width, height )
}
 
function updateResultBuffer(msg) {
  resultBuffer.lastUpdate = Date.now()
  resultBuffer.results = msg
}
 
ipcRenderer.on('decodeBufferAsync-done', (evt, msg) => {
  updateResultBuffer(msg)
})
 
ipcRenderer.on('decodeFileAsync-done', (evt, msg) => {
  updateResultBuffer(msg)
})
 
ipcRenderer.on('decodeBase64Async-done', (evt, msg) => {
  updateResultBuffer(msg)
})
 
  module.exports = {
    decodeFileAsync,
    decodeBase64Async,
    decodeBufferAsync,
    setFrameBuffer,
    startVideoDecode,
    stopVideoDecode,
    resultBuffer
  }

消费服务

我们已经准备了服务。现在,我们将注意力转向“前端”。

要在Web应用程序中选择文件,通常使用HTML输入元素。

 

<input id="file-selector" type="file">

 

要在选择文件后将文件路径发送到解码服务,我们可以注册该onchange 事件。不要忘记同时注册onclick ,否则它将不会再次解码同一张图像。

 

document.getElementById('file-selector').onchange = handleFileChange
document.getElementById('file-selector').onclick = evt => {
  evt.target.value = ''
}
  
async function handleFileChange(evt) {
  const file = evt.target.files[0]
  const results = await services.decodeFileAsync(file.path)
  updateResults(results)
}
async function updateResults(results) {
  // Remove existing results
  const container = document.getElementById('result-box')
  container.innerHTML = ''
  const nodes = []
  results.forEach(result => {
    nodes.push(`<div class="result-card"> \
                  <p>Format: ${result.format}</p> \
                  <p>Text: ${result.value}</p> \
                </div>`
              )

 

Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用

显示解码结果

相机实时解码
存取相机
显然,我们的第一步是访问摄像机。类似于在浏览器中激活相机,我们用于navigator.mediaDevices.getUserMedia申请相机访问。

function initCamera() {
  const video = document.querySelector('video') || document.createElement("video")
  const navigator = window.navigator
  const stream = navigator.mediaDevices.getUserMedia({
    video: { facingMode: 'user', width: 1280, height: 720 }
  })
  stream.then(stream => {
    video.srcObject = stream
  })
}

将getUserMedia返回一个承诺,其将与视频流来解决。我们添加随后的语句以将指定srcObject 为视频元素的解析流。

在中index.html,我们添加一个按钮,并使用指定onclick 事件处理程序initCamera。

<button id="video-capture-btn" class="btn-secondary">Capture</button>
document.getElementById('video-capture-btn').onclick = initCamera

检索图像数据

我们使用画布来获取图像数据。

function getFrame(videoElement) {
  const cvs = new OffscreenCanvas(640, 360)
  const ctx = cvs.getContext('2d')
  ctx.drawImage(videoElement, 0, 0, cvs.width, cvs.height)
  const imgData = ctx.getImageData(0, 0, cvs.width, cvs.height)
  decodeFromFrame(imgData)
}

画布可以接受视频元素,并使用视频帧绘制图形。只需指定视频元素和要绘制的区域,我们便可以随后获取帧数据。

每次检索视频帧时,都会将其分发到服务以进行解码。

async function decodeFromFrame(frame) {
  const res = await services.decodeBufferAsync(frame.data, frame.width, frame.height)
  updateResults(res.data)
}

最后,解码过程需要触发一个事件。在中initCamera,我们onplay使用间隔动作注册处理程序,该动作定期执行该getFrame功能以读取和解码图像数据。

派送图像数据

有了图像数据后,我们可以将其发送到解码服务。的ipcMain和ipcRenderer分别是用于主处理和渲染过程的流程间通信的对象。通过注册事件侦听器,每个进程都可以侦听特定的消息。我们注册以下事件及其相应的完成消息协议。

发件人 接收者 活动名称 讯息通讯协定

渲染器 主要 encodeFileAsync 文件路径
主要 渲染器 encodeFileAsync-done 结果
渲染器 主要 encodeBufferAsync 图像数据,宽度,高度
主要 渲染器 encodeBufferAsync-done 结果
渲染器 主要 encodeBase64Async Base64字符串
主要 渲染器 encodeBase64Async-done 结果
主要 渲染器 解码视频 图像数据,宽度,高度
渲染器 主要 下一视频 结果
在后台服务中,我们注册以下侦听器,

ipcMain.on('decodeFileAsync', decodeFileAsync)
ipcMain.on('decodeBase64Async', decodeBase64Async)
ipcMain.on('decodeBufferAsync', decodeBufferAsync)
ipcMain.on('videoDecode', videoDecode)
function videoDecode(evt, imgData, width, height) {
  if (DEBUG)
    console.log(`${new Date().toLocaleString()}/real-time decoding for video stream: ${imgData.length/height}, ${width}`)
  dbr.decodeBufferAsync(imgData, width, height, width*4, barcodeTypes, (err, msg) => {
    if (err)
      console.log(err)
    let results = [];
    for (index in msg) {
      let result = Object()
      let res = msg[index];
      result.format = res['format']
      result.value = res['value']
      results.push(result)
    }
    evt.reply('videoDecode-next', results)
    if (DEBUG)
      console.log('ipcMain: replied with ' + JSON.stringify(results))
  })
}
 
function decodeFileAsync(evt, filepath) {
  if (DEBUG)
    console.log('ipcMain: decodeFileAsync invoked: ' + filepath)
    dbr.decodeFileAsync(filepath, barcodeTypes, (err, msg) => {
      if (err)
        console.log(err)
      let results = [];
      for (index in msg) {
        let result = Object()
        let res = msg[index];
        result.format = res['format']
        result.value = res['value']
        results.push(result)
      }
      evt.reply('decodeFileAsync-done', results)
      if (DEBUG)
        console.log('ipcMain: replied with ' + JSON.stringify(results))
    })
}
 
function decodeBase64Async(evt, base64Str) {
  if (DEBUG)
    console.log('ipcMain: decodeBase64Async is invoked')
  dbr.decodeBase64Async(base64Str, barcodeTypes, (err, msg) => {
    if (err)
      console.error(err)
    let results = [];
    for (index in msg) {
      let result = Object()
      let res = msg[index];
      result.format = res['format']
      result.value = res['value']
      results.push(result)
    }
    evt.reply('decodeBase64Async-done', results)
    if (DEBUG)
        console.log('ipcMain: replied with ' + JSON.stringify(results))
  })
}
 
function decodeBufferAsync(evt, imgData, width, height) {
  if (DEBUG)
    console.log('ipcMain: decodeBufferAsync is invoked')
  console.log(imgData)
  dbr.decodeBufferAsync(imgData, width, height, width*4, barcodeTypes, (err, msg) => {
    if (err)
      console.error(err)
    let results = [];
    for (index in msg) {
      let result = Object()
      let res = msg[index];
      result.format = res['format']
      result.value = res['value']
      results.push(result)
    }
    evt.reply('decodeBufferAsync-done', results)
    if (DEBUG)
        console.log('ipcMain: replied with ' + JSON.stringify(results))
  })
}

阅读条形码

我们已经实现了我们想要的功能。现在该运行并测试我们的项目了。转到项目根路径,npm start在终端中键入以启动电子条形码读取器。

请注意,如果Electron或平台的版本发生更改,则必须每次编译nodejs-barcode库。

Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用

启动应用程序后的第一页

Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用

解码摄像机流并显示结果
本文章转载自【慧都科技】evget欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,尊重他人劳动成果
posted @ 2020-11-12 17:10  roffey  阅读(367)  评论(0编辑  收藏  举报