目的
こちらの記事を参考に自分のPC環境でも動くものを作りたい。
html上に表示し、QRコードを読み取り、リアルタイムで状態を変化させたい。
イメージ
htmlで表示された表の状況の部分は、NGとなっています。QRコードをwebカメラで読み込むことでは、 データベースのstatusを書き換えます。すると、状況は、OKに変化します。
プログラム
手順としては、 QRコードの作成、データベースの作成、pythonのプログラムの作成になります。プログラムは、githabにあげていますので、参考にしてください。
https://github.com/yutti/study/tree/main/QR_code
QRコードの作成
今回作成した QRコードは、github上においていますので、そちらを使用してください。QRコードの作成は、文字表示付きQRコードの作成にまとめましたので、こちらを参考にしてください。
データベースの作成
github上においてあるmake_db.py
を実行するとデータベースを作成できます。データベースの作成方法について、pythonでのsqlite3データベース作成にまとめましたので、こちらを参考にしてください。
pythonのプログラムの作成
プログラムのベースは、最初に紹介した記事になります。プログラムを実行させると、webカメラが起動します。QRコードを読み取っていただくと、表の状況の部分が変わります。読み込みが終わりましたら、キーボードのqを押すと抜けることができます。表の状況を元の状態に戻したい場合は、make_db.py
を実行していただくと、戻るようにしています。
import os.path
import json
import sqlite3
import webbrowser
import cv2
import winsound
import threading
from pyzbar.pyzbar import decode
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from websocket_server import WebsocketServer
class OrenoServer:
def __init__(self):
self.HOST = 'localhost'
self.HTTP_PORT = 8080
self.WS_PORT = 8081
self.client = None
self.wss = WebsocketServer(host=self.HOST, port=self.WS_PORT)
self.wss.set_fn_new_client(self.new_client)
self.https = ThreadingHTTPServer((self.HOST, self.HTTP_PORT), HttpHandler)
self.codes = []
def start(self):
threading.Thread(target=self.wss.run_forever).start()
threading.Thread(target=self.https.serve_forever).start()
webbrowser.open(f'http://{self.HOST}:{str(self.HTTP_PORT)}')
def shutdown(self):
self.wss.shutdown()
self.https.shutdown()
def new_client(self, client, server):
if self.client is None:
self.client = client
threading.Thread(target=self.cam_capture).start()
def cam_capture(self):
cap = cv2.VideoCapture(0)
font = cv2.FONT_HERSHEY_SIMPLEX
while cap.isOpened():
ret, frame = cap.read()
if ret:
d = decode(frame)
if d:
for barcode in d:
barcode_data = barcode.data.decode('utf-8')
if barcode_data not in self.codes:
self.codes.append(barcode_data)
winsound.Beep(2000, 50)
font_color = (0, 0, 255)
self.wss.send_message(self.client, barcode_data)
else:
font_color = (0, 154, 87)
x, y, w, h = barcode.rect
cv2.rectangle(frame, (x, y), (x + w, y + h), font_color, 2)
frame = cv2.putText(frame, barcode_data, (x, y - 10),
font, .5, font_color, 2, cv2.LINE_AA)
cv2.imshow('QRCODE READER press q -> exit', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
db = OrenoDataBase()
db.set(self.codes)
db.close()
self.shutdown()
break
class OrenoDataBase:
def __init__(self):
self.conn = sqlite3.connect(r"./book_list.db", check_same_thread=False)
self.conn.row_factory = sqlite3.Row
self.cur = self.conn.cursor()
def get(self):
self.cur.execute('SELECT * FROM bookitems ORDER BY code')
rows = []
for r in self.cur.fetchall():
rows.append({'name': r['name'], 'code': r['code'], 'status': r['status']})
return rows
def set(self, codes):
place_holder = ','.join('?'*len(codes))
values = tuple(codes)
self.cur.execute(
f'UPDATE bookitems SET status = TRUE WHERE code in ({place_holder})', values)
self.conn.commit()
def close(self):
self.cur.close()
self.conn.close()
class HttpHandler(BaseHTTPRequestHandler):
def do_GET(self):
with open(r"./template.html", mode='r', encoding='utf-8') as html:
response_body = html.read()
self.send_response(200)
self.send_header('Content-type', 'text/html; charset=utf-8')
self.end_headers()
self.wfile.write(response_body.encode('utf-8'))
def do_POST(self):
db = OrenoDataBase()
rows = db.get()
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response_body = json.dumps(rows)
self.wfile.write(response_body.encode('utf-8'))
db.close()
server = OrenoServer()
server.start()
htmlは、下記のように設定しています。table classの設定が悪いのか、表の背景色を黒色に設定しても反映されなかったので、表の枠線が見えるものを用いてます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>本のリスト</title>
</head>
<body>
<div class="container">
<table class="table mt-5 table-bordered border-primary " id="tb">
</table>
</div>
<script>
const HTTP_PORT = 8080
const WS_PORT = 8081
let wss = new WebSocket('ws://localhost:' + WS_PORT)
wss.onmessage = function (e) {
$('#'+ e.data).removeClass('bg-danger').addClass('bg-success').text('OK')
}
$.ajax({
url: 'http://localhost:' + HTTP_PORT,
type: 'POST',
dataType: 'json',
}).then(
function (data) {
let elem = '<tr><th>タイトル</th><th>識別コード</th><th>状況</th></tr>'
$.each(data, function (key, item) {
let bc
let status
if(item.status === 1){
bc = 'bg-success'
status = 'OK'
}else{
bc = 'bg-danger'
status = 'NG'
}
elem += '<tr>'
elem += '<td>' + item.name + '</td>'
elem += '<td>' + item.code + '</td>'
elem += '<td class="' + bc + '" id="' + item.code +'">' + status + '</td>'
elem += '</tr>'
})
$('#tb').html(elem)
})
</script>
</body>
</html>