目的
Pythonでバーコードリーダーアプリを作りたいと思った。
GUIとして画面が欲しいので、tkinterでバーコード読み取り結果を画面表示するアプリを考えた。
環境
・Windows11
・WSL2(Ubuntu-22.04)
・python 3.10.12
・usbipd
・バーコードリーダー(Tera HW0002)
公式サイト : https://tera-digital.com/products/2d-barcode-scanner-hw0002-o
Amazon : https://www.amazon.co.jp/dp/B07T9J9Q99
ポイント
・バーコードリーダー
仮想COM設定にする。
設定方法は公式サイトのマニュアルを参照。
・BarcodeScannerクラス
インスタンス生成時に受信スレッドが立つ。
データ受信時にRecieveDataHandlerを発火させる。
RecieveDataHandlerはイベントを実行する。
・Windowクラス
変数はtk.StringVar()で宣言する。
宣言した変数をWidgets(Label等)に紐づける。
変数へのアクセスはset(), get()メソッドを使う。
変数の値を更新する関数を用意し、データ受信時にcallbackしてもらうようにイベントに追加する。
課題
・Eventクラスは改良の余地あり
ソースコード
#!/usr/bin/env python3
import serial
import tkinter as tk
import BarcodeScanner as bs
class Window:
def __init__(self) -> None:
root = tk.Tk()
root.wm_title("BarcodeScanner")
self.frame = tk.Frame(root)
self.frame.pack(padx=150, pady=0)
self.lName = tk.Label(text="Scan Data:")
self.lName.pack(side="left", padx=0, pady=20)
self.lScanData = tk.Label()
self.lScanData.pack(side="left", padx=0, pady=20)
self.vScanData = tk.StringVar()
self.vScanData.set("Wait for Read")
self.lScanData["textvariable"] = self.vScanData
def DisplayScanData(self, data):
self.vScanData.set(data)
print(f"input:{self.vScanData.get()}")
if __name__ == "__main__":
path = "/dev/ttyUSB0" # change value by environment
bcs = bs.BarcodeScanner(path,
9600,
serial.EIGHTBITS,
serial.PARITY_EVEN,
serial.STOPBITS_ONE,
3)
myapp = Window()
bcs.rEvent.AddEvent(myapp.DisplayScanData)
myapp.frame.mainloop()
import serial
import threading
import Event as ev
class BarcodeScanner:
def __init__(self, path, br, bs, pr, sb, to):
self.rBuffer = self.Buffer()
self.rEvent = ev.Event()
self.Open(path, br, bs, pr, sb, to)
def Open(self, path, br, bs, pr, sb, to):
self.ser = serial.Serial(path)
self.ser.baudrate = br
self.ser.bytesize = bs
self.ser.parity = pr
self.ser.stopbits = sb
self.ser.timeout = to # sec
self.th_rd = threading.Thread(target=self.RecieveData, daemon=True)
self.th_rd.start()
def Close(self):
self.ser.close()
def SendData(self, sd):
self.ser.write(sd)
def RecieveData(self):
while True:
if self.ser.in_waiting > 0:
rd = self.ser.read(self.ser.in_waiting)
self.RecieveDataHandler(rd)
def RecieveDataHandler(self, indata:bytes):
self.rBuffer.In(indata)
buf = self.rBuffer.Read()
if b'\x0D\x0A' in buf: # CRLF
self.rEvent.ExecuteEvents(self.rBuffer.Out(endbyte=b'\x0D\x0A'))
elif b'\x0D' in buf: # CR
self.rEvent.ExecuteEvents(self.rBuffer.Out(endbyte=b'\x0D'))
elif b'\x0A' in buf: # LF
self.rEvent.ExecuteEvents(self.rBuffer.Out(endbyte=b'\x0A'))
class Buffer:
def __init__(self) -> None:
self.buffer = None
def In(self, data:bytes):
if self.buffer is None:
self.buffer = data
else:
self.buffer += data
def Out(self, endbyte) -> bytes:
if self.buffer is None:
return None
else:
data, endbyte, over = self.buffer.partition(endbyte)
self.Clear()
return data
def Read(self) -> bytes:
return self.buffer
def Clear(self):
self.buffer = None
class Event:
def __init__(self):
self.events = []
def ExecuteEvents(self, data):
if len(self.events) > 0:
for e in self.events:
e(data)
def AddEvent(self, func):
self.events.append(func)
参考
・tkinter --- Tcl/Tk の Python インターフェース — Python 3.12.5 ドキュメント : https://docs.python.org/ja/3/library/tkinter.html