Python

Pythonで作る!QRコード生成&読み取りアプリ「QRApp」の完全解説

tyamada

はじめに

QRコードは情報のやり取りや決済などで身近になり、日常的に目にする機会が増えています。「自分でQRコードを生成したり読み取れるアプリを作りたい」と思ったことはありませんか。

今回紹介する「QRApp」は、Pythonとtkinter、OpenCV、pyzbarを組み合わせたQRコード生成&読み取りアプリです。このアプリを作ることで、PythonのGUI開発や画像処理、カメラ制御の基本を学ぶことができます。

ソースコードを全て表示
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from PIL import Image, ImageTk
import qrcode
import cv2
from pyzbar import pyzbar
import threading

class QRApp:
    def __init__(self, root):
        self.root = root
        self.root.title("QRコード生成&読取アプリ")
        self.root.geometry("900x700")
        
        # ---------------- QR生成フレーム ----------------
        frame_gen = ttk.LabelFrame(root, text="QRコード生成")
        frame_gen.pack(padx=10, pady=10, fill="x")
        
        self.text_var = tk.StringVar()
        ttk.Entry(frame_gen, textvariable=self.text_var, width=50).pack(side="left", padx=5, pady=5)
        ttk.Button(frame_gen, text="生成", command=self.generate_qr).pack(side="left", padx=5)
        ttk.Button(frame_gen, text="保存", command=self.save_qr).pack(side="left", padx=5)
        
        self.qr_canvas = tk.Canvas(frame_gen, width=200, height=200, bg="white")
        self.qr_canvas.pack(padx=10, pady=10)
        self.qr_image = None
        
        # ---------------- QR読み取りフレーム ----------------
        frame_read = ttk.LabelFrame(root, text="QRコード読取")
        frame_read.pack(padx=10, pady=10, fill="both", expand=True)
        
        # カメラ選択
        cam_frame = ttk.Frame(frame_read)
        cam_frame.pack(pady=5)
        ttk.Label(cam_frame, text="カメラ選択:").pack(side="left")
        self.camera_var = tk.StringVar()
        self.cam_combobox = ttk.Combobox(cam_frame, textvariable=self.camera_var, width=5, state="readonly")
        self.cam_combobox.pack(side="left", padx=5)
        self.detect_cameras()
        
        ttk.Button(cam_frame, text="カメラ開始", command=self.start_camera).pack(side="left", padx=5)
        ttk.Button(cam_frame, text="カメラ停止", command=self.stop_camera).pack(side="left", padx=5)
        
        ttk.Button(frame_read, text="画像ファイルから読み取る", command=self.read_from_file).pack(pady=5)
        
        self.video_label = tk.Label(frame_read)
        self.video_label.pack()
        
        self.result_listbox = tk.Listbox(frame_read)
        self.result_listbox.pack(fill="both", expand=True, padx=10, pady=10)
        
        self.cap = None
        self.running = False

    # ---------------- QR生成 ----------------
    def generate_qr(self):
        text = self.text_var.get()
        if not text:
            messagebox.showwarning("入力エラー", "テキストを入力してください")
            return
        qr = qrcode.QRCode(box_size=8, border=2)
        qr.add_data(text)
        qr.make(fit=True)
        img = qr.make_image(fill_color="black", back_color="white")
        self.qr_image = ImageTk.PhotoImage(img)
        self.qr_canvas.create_image(100, 100, image=self.qr_image)

    def save_qr(self):
        if self.qr_image is None:
            messagebox.showwarning("保存エラー", "QRコードを生成してください")
            return
        file_path = filedialog.asksaveasfilename(defaultextension=".png",
                                                 filetypes=[("PNG", "*.png"), ("All files", "*.*")])
        if file_path:
            text = self.text_var.get()
            img = qrcode.make(text)
            img.save(file_path)
            messagebox.showinfo("保存完了", f"{file_path} に保存しました")

    # ---------------- カメラ操作 ----------------
    def detect_cameras(self):
        # 最大5台まで仮にチェック
        cams = []
        for i in range(5):
            cap = cv2.VideoCapture(i)
            if cap.read()[0]:
                cams.append(str(i))
            cap.release()
        if not cams:
            cams.append("0")
        self.cam_combobox['values'] = cams
        self.cam_combobox.current(0)
    
    def start_camera(self):
        if self.running:
            return
        cam_index = int(self.camera_var.get())
        self.cap = cv2.VideoCapture(cam_index)
        self.running = True
        threading.Thread(target=self.update_frame, daemon=True).start()

    def stop_camera(self):
        self.running = False
        if self.cap:
            self.cap.release()
            self.cap = None
        self.video_label.config(image="")

    def update_frame(self):
        while self.running and self.cap.isOpened():
            ret, frame = self.cap.read()
            if not ret:
                continue
            
            decoded_objects = pyzbar.decode(frame)
            for obj in decoded_objects:
                data = obj.data.decode("utf-8")
                if data not in self.result_listbox.get(0, tk.END):
                    self.result_listbox.insert(tk.END, data)
                (x, y, w, h) = obj.rect
                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
            
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = Image.fromarray(cv2image)
            imgtk = ImageTk.PhotoImage(image=img)
            self.video_label.imgtk = imgtk
            self.video_label.config(image=imgtk)
        
        if self.cap:
            self.cap.release()

    # ---------------- 画像ファイルからQR読み取り ----------------
    def read_from_file(self):
        file_path = filedialog.askopenfilename(filetypes=[("画像ファイル", "*.png;*.jpg;*.jpeg;*.bmp")])
        if not file_path:
            return
        img = cv2.imread(file_path)
        decoded_objects = pyzbar.decode(img)
        if not decoded_objects:
            messagebox.showinfo("結果", "QRコードは見つかりませんでした")
            return
        for obj in decoded_objects:
            data = obj.data.decode("utf-8")
            if data not in self.result_listbox.get(0, tk.END):
                self.result_listbox.insert(tk.END, data)
        messagebox.showinfo("読み取り完了", "画像ファイルのQRコードを読み取りました")

if __name__ == "__main__":
    root = tk.Tk()
    app = QRApp(root)
    root.mainloop()

QRAppの概要

QRAppには大きく分けて二つの機能があります。

  1. QRコード生成
    テキストを入力するだけでQRコードを生成し、TkinterのCanvasに表示。生成したQRコードはPNG形式で保存することも可能です。
  2. QRコード読み取り
    カメラ映像からリアルタイムでQRコードを検出することができ、読み取った情報はリストに記録されます。また、画像ファイルからQRコードを読み取ることも可能です。カメラが複数ある場合は任意のデバイスを選択して読み取りできます。

コード解説

QRコード生成

まずはテキストを入力してQRコードを生成する部分です。

def generate_qr(self):
    text = self.text_var.get()
    if not text:
        messagebox.showwarning("入力エラー", "テキストを入力してください")
        return
    qr = qrcode.QRCode(box_size=8, border=2)
    qr.add_data(text)
    qr.make(fit=True)
    img = qr.make_image(fill_color="black", back_color="white")
    self.qr_image = ImageTk.PhotoImage(img)
    self.qr_canvas.create_image(100, 100, image=self.qr_image)

ここではqrcodeライブラリでQRコードを生成し、Pillowを使ってTkinterのCanvasに表示しています。ユーザーがテキストを入力していない場合には警告を表示するようにして、安全性も考慮しています。

保存機能も簡単です。

def save_qr(self):
    if self.qr_image is None:
        messagebox.showwarning("保存エラー", "QRコードを生成してください")
        return
    file_path = filedialog.asksaveasfilename(defaultextension=".png",
                                             filetypes=[("PNG", "*.png"), ("All files", "*.*")])
    if file_path:
        text = self.text_var.get()
        img = qrcode.make(text)
        img.save(file_path)
        messagebox.showinfo("保存完了", f"{file_path} に保存しました")

ここではユーザーが指定したパスにQRコードをPNG形式で保存します。Tkinterのfiledialogを使うことで直感的な操作が可能です。


カメラ選択とリアルタイム読み取り

複数カメラを検出して選択できる機能も備えています。

def detect_cameras(self):
    cams = []
    for i in range(5):
        cap = cv2.VideoCapture(i)
        if cap.read()[0]:
            cams.append(str(i))
        cap.release()
    if not cams:
        cams.append("0")
    self.cam_combobox['values'] = cams
    self.cam_combobox.current(0)

最大5台までのカメラをチェックし、使用可能なカメラをコンボボックスに表示します。

カメラ映像の取得と表示はスレッドを使って非同期処理しています。

def start_camera(self):
    if self.running:
        return
    cam_index = int(self.camera_var.get())
    self.cap = cv2.VideoCapture(cam_index)
    self.running = True
    threading.Thread(target=self.update_frame, daemon=True).start()

これにより、GUIのフリーズを防ぎながらリアルタイムで映像を表示できます。


QRコード検出

カメラ映像からQRコードを検出するコードです。

decoded_objects = pyzbar.decode(frame)
for obj in decoded_objects:
    data = obj.data.decode("utf-8")
    if data not in self.result_listbox.get(0, tk.END):
        self.result_listbox.insert(tk.END, data)
    (x, y, w, h) = obj.rect
    cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
  • pyzbar.decodeでQRコードを検出
  • 検出したデータをリストに追加
  • QRコードの位置を矩形でハイライト

画像ファイルからのQR読み取り

カメラが使えない場合でも、画像ファイルから読み取り可能です。

file_path = filedialog.askopenfilename(filetypes=[("画像ファイル", "*.png;*.jpg;*.jpeg;*.bmp")])
img = cv2.imread(file_path)
decoded_objects = pyzbar.decode(img)

選択した画像ファイルからQRコードを検出し、Listboxに追加します。


実際に使ってみて

このアプリを作ることで、PythonでのGUI開発だけでなく、画像処理やスレッド処理、外部ライブラリの組み合わせ方なども学べます。

また、快適に開発やテストを行うには、性能の高いPCがあると作業効率がぐっと上がります。もしこれからPythonで本格的にGUIアプリを作りたい方は、Python開発に適したノートPCやデスクトップPCを選ぶと、開発環境が快適になり学習効率も向上します。


まとめ

  • QRAppは、QRコード生成・カメラ読み取り・画像読み取りを一つのアプリで学べる学習用アプリ
  • tkinter + Pillow + OpenCV + pyzbar + qrcodeの組み合わせでGUIアプリ開発を学習
  • カメラや画像ファイルからQRコードを読み取り、結果をリアルタイムで確認可能
  • 高性能PCがあれば、開発やテストもより快適に

このアプリを作ることでPythonでのアプリ開発スキルが一気に向上します。QRAppを自分で動かす楽しさを味わいながら、必要に応じて快適なPC環境も整えると、より効率的に学習できます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT ME
tyamada
tyamada
普通の会社員(平)
記事URLをコピーしました