PythonでQRコードを生成

事務作業効率化

PythonでQRコードを簡単に生成!qrcodeライブラリの基本的な使い方

QRコードは、URLやテキスト情報を簡単に共有できる便利な仕組みです。Pythonには「qrcode」というライブラリがあり、わずか数行のコードでQRコードを生成できます。本記事では、インストールから基本的な使い方、応用例までを解説します。

インストール方法

まずはライブラリをインストールしましょう。ターミナルやコマンドプロンプトで以下を実行します。

pip install qrcode[pil]

[pil]オプションを付けることで、画像処理ライブラリPillowも一緒にインストールされ、PNG画像として保存できるようになります。

基本的なQRコード生成

最もシンプルな使い方は以下の通りです。

import qrcode

# QRコードに埋め込むデータ
data = "https://techpara-blog.com"

# QRコード生成
img = qrcode.make(data)

# 画像を保存
img.save("qrcode_basic.png")

これだけで、指定したURLを含むQRコード画像が生成されます。

詳細設定を使ったQRコード生成

もう少し細かく制御したい場合は、QRCodeクラスを使います。

import qrcode

qr = qrcode.QRCode(
    version=1,  # QRコードのサイズ(1〜40)
    error_correction=qrcode.constants.ERROR_CORRECT_H,  # 誤り訂正レベル
    box_size=10,  # 1マスのピクセル数
    border=4,  # 外枠のマス数
)

qr.add_data("Hello, TechPara Blog!")
qr.make(fit=True)

img = qr.make_image(fill_color="black", back_color="white")
img.save("qrcode_custom.png")

主なパラメータ

  • version: QRコードのサイズ。大きいほど多くのデータを格納可能。
  • error_correction: 誤り訂正レベル(L/M/Q/H)。Hは最も強力で、汚れや欠損に強い。
  • box_size: 1セルのピクセル数。大きくすると画像が拡大される。
  • border: 外枠のセル数。通常は4以上が推奨

Pythonのqrcodeライブラリを使えば、数行のコードでQRコードを生成できます。基本的な使い方から詳細設定まで理解すれば、ビジネスや趣味の場面で幅広く活用可能です。

「まずはURLをQRコード化してみる」ことから始めて、徐々にカスタマイズや応用に挑戦してみましょう!

応用編 ExcelのデータをQRコードに書き出す

 製造業においては生産計画などExcelで管理したり出力したりすることもあるかと思います。Excelファイル内に列挙されたデータを一括でQRコードとして出力するためのPythonスクリプトを作ってみました。GUIで操作できるので現場でも受け入れてもらえるかもしれません。

スクリプトで使用する他のライブラリをインストール

pip3 install qrcode openpyxl

スクリプト全体

こちらのスクリプトをメモ帳などのコードエディタにコピペして実行してみましょう。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Requirements:
  pip install qrcode[pil] openpyxl

Description:
  - Select an Excel file (.xlsx/.xls) via a file dialog
  - Read values from column A (first worksheet)
  - Generate QR codes for each non-empty cell
  - Output format selectable: SVG, PNG, JPG
  - Scale selectable: 1–10 (box size)
  - Saves files into an 'qrcodes_<format>_scale<scale>' folder next to the Excel file
"""

import os
import re
import tkinter as tk
from tkinter import ttk, filedialog, messagebox

# QR code
import qrcode
from qrcode.image.svg import SvgImage, SvgPathImage, SvgFragmentImage

# Excel
from openpyxl import load_workbook

# -----------------------------
# Helpers
# -----------------------------

def sanitize_filename(s: str, fallback: str) -> str:
    """Make a safe filename from a string; use fallback if empty."""
    if s is None:
        return fallback
    # Convert to string and strip
    s = str(s).strip()
    if not s:
        return fallback
    # Remove unsafe characters
    s = re.sub(r'[\\/:*?"<>|]', '_', s)
    # Limit length to avoid OS issues
    return s[:150] if s else fallback

def read_column_a_values(excel_path: str):
    """Read non-empty values from column A in the active worksheet."""
    wb = load_wb(excel_path)
    ws = wb.active
    values = []
    for row in ws.iter_rows(min_row=1, max_col=1, values_only=True):
        val = row[0]
        if val is None:
            continue
        text = str(val).strip()
        if text:
            values.append(text)
    return values

def load_wb(excel_path: str):
    # openpyxl loads .xlsx; .xls is not supported. Warn if needed.
    try:
        return load_workbook(excel_path, data_only=True)
    except Exception as e:
        raise RuntimeError(f"Excelの読み込みに失敗しました: {e}")

def ensure_output_dir(excel_path: str, fmt: str, scale: int) -> str:
    base_dir = os.path.dirname(os.path.abspath(excel_path))
    out_dir = os.path.join(base_dir, f"qrcodes_{fmt.lower()}_scale{scale}")
    os.makedirs(out_dir, exist_ok=True)
    return out_dir

def make_qr(text: str, fmt: str, scale: int):
    """
    Create a QRCode object for given text and format.
    scale maps to box_size.
    """
    # Common QR settings
    qr = qrcode.QRCode(
        version=None,               # auto size
        error_correction=qrcode.constants.ERROR_CORRECT_M,
        box_size=max(1, int(scale)),
        border=4
    )
    qr.add_data(text)
    qr.make(fit=True)

    if fmt.lower() == "svg":
        # Use path-based SVG (smaller, cleaner)
        img = qr.make_image(image_factory=SvgPathImage)
        return img
    else:
        # PIL image for PNG/JPG
        img = qr.make_image(fill_color="black", back_color="white")
        return img

def save_qr(img, out_dir: str, base_name: str, fmt: str):
    ext = fmt.lower()
    filename = os.path.join(out_dir, f"{base_name}.{ext}")
    # For JPG, enforce RGB (no alpha)
    if ext == "jpg":
        # PIL image path; convert if needed
        try:
            img = img.convert("RGB")
        except Exception:
            # SVG does not support convert; but we only call this branch for PIL images.
            pass
    img.save(filename)
    return filename

# -----------------------------
# GUI Application
# -----------------------------

class QRApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Excel A列 → QRコード生成")
        self.geometry("640x400")
        self.resizable(True, True)

        self.excel_path = tk.StringVar(value="")
        self.format_var = tk.StringVar(value="SVG")
        self.scale_var = tk.IntVar(value=6)

        self.create_widgets()

    def create_widgets(self):
        # Top frame: file selection
        top = ttk.Frame(self, padding=(12, 12))
        top.pack(fill="x")

        ttk.Label(top, text="Excelファイル (.xlsx) を選択:").grid(row=0, column=0, sticky="w")
        path_entry = ttk.Entry(top, textvariable=self.excel_path)
        path_entry.grid(row=1, column=0, columnspan=2, sticky="ew", padx=(0, 8))
        top.columnconfigure(0, weight=1)

        select_btn = ttk.Button(top, text="参照...", command=self.select_excel)
        select_btn.grid(row=1, column=2, sticky="e")

        # Options frame
        opts = ttk.LabelFrame(self, text="出力設定", padding=(12, 12))
        opts.pack(fill="x", padx=12, pady=8)

        ttk.Label(opts, text="フォーマット:").grid(row=0, column=0, sticky="w")
        fmt_combo = ttk.Combobox(opts, textvariable=self.format_var, values=["SVG", "PNG", "JPG"], state="readonly", width=8)
        fmt_combo.grid(row=0, column=1, sticky="w", padx=(8, 16))

        ttk.Label(opts, text="スケール (1–10):").grid(row=0, column=2, sticky="w")
        scale_spin = ttk.Spinbox(opts, from_=1, to=10, textvariable=self.scale_var, width=5)
        scale_spin.grid(row=0, column=3, sticky="w", padx=(8, 0))

        # Action buttons
        actions = ttk.Frame(self, padding=(12, 0))
        actions.pack(fill="x")

        convert_btn = ttk.Button(actions, text="変換開始", command=self.convert)
        convert_btn.pack(side="left")

        # Log area
        log_frame = ttk.LabelFrame(self, text="ログ", padding=(8, 8))
        log_frame.pack(fill="both", expand=True, padx=12, pady=12)

        self.log = tk.Text(log_frame, height=12, wrap="word")
        self.log.pack(fill="both", expand=True)

    def select_excel(self):
        path = filedialog.askopenfilename(
            title="Excelファイルを選択",
            filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")]
        )
        if path:
            self.excel_path.set(path)
            self.append_log(f"選択: {path}")

    def convert(self):
        path = self.excel_path.get().strip()
        fmt = self.format_var.get().strip().upper()
        scale = int(self.scale_var.get())

        if not path:
            messagebox.showwarning("警告", "Excelファイルを選択してください。")
            return

        if not os.path.exists(path):
            messagebox.showerror("エラー", "指定されたファイルが存在しません。")
            return

        if fmt not in {"SVG", "PNG", "JPG"}:
            messagebox.showerror("エラー", "フォーマットが不正です。SVG/PNG/JPG から選択してください。")
            return

        try:
            values = read_column_a_values(path)
        except Exception as e:
            messagebox.showerror("エラー", str(e))
            self.append_log(str(e))
            return

        if not values:
            messagebox.showinfo("情報", "A列に有効なデータが見つかりませんでした。")
            self.append_log("A列にデータがありません。")
            return

        out_dir = ensure_output_dir(path, fmt, scale)
        self.append_log(f"出力先: {out_dir}")
        self.append_log(f"件数: {len(values)} / フォーマット: {fmt} / スケール: {scale}")

        success = 0
        for idx, text in enumerate(values, start=1):
            try:
                img = make_qr(text, fmt, scale)
                base_name = sanitize_filename(text, fallback=f"row{idx}")
                saved = save_qr(img, out_dir, base_name, fmt)
                self.append_log(f"[OK] {idx}: {saved}")
                success += 1
            except Exception as e:
                self.append_log(f"[NG] {idx}: {e}")

        messagebox.showinfo("完了", f"変換完了: {success}/{len(values)} 件")
        self.append_log("変換を完了しました。")

    def append_log(self, msg: str):
        self.log.insert("end", msg + "\n")
        self.log.see("end")

# -----------------------------
# Entry point
# -----------------------------

def main():
    app = QRApp()
    app.mainloop()

if __name__ == "__main__":
    main()

コメント

タイトルとURLをコピーしました