Excel(MS-Office)には2013あたりから標準でバーコードを作成するライブラリがあります。このライブラリでシートに入力された値をQRコードにできます。さわったことがあるかたはご存じかと思いますが、このバーコード作成ライブラリ(Microsoft bar code control)はイマイチ使い勝手がよろしくないのです。
VBAには荷が重いQRコード作成
少量を作る場合なら、このライブラリでも問題にはなりません。が、大量に必要な場合間違いなく心が折れます。VBAでループを使って生成することも可能かと思われますが、何しろ出力がシート上のコントロールというトンデモ仕様なので、取りまわしがすこぶる悪いです。何よりShapeをはじめとした、セル外に存在する物体が私は大嫌いです。
そもそも大量にQRコードが必要な場面というのが、ニッチな気もしないでもないですが、CordovaなどでQRコードを読み取るスマホアプリを簡単に作成できるご時世ですので、例えば職場の備品管理で、備品に貼ったQRコードをスマホでバンバン読んでいくだけで、どれがあってどれがないというのが一覧になる、なんてのはそんなに難しいことをしなくても実現できます。PCに外付けでリーダーを取り付ければExcel-VBAで処理することも容易です。
実際、私も似たようなことをやろうとして「ExcelでQRコード作成問題」にぶち当たったわけです。
Pythonには余裕のQRコード作成
さて、どうしようかということでPythonにQRコードを扱うモジュールがないわけがないと調べてみると、当然あるわけで、じゃあ、ExcelシートをPythonでQRコードにしちゃえば良くね?となり、今回のツールが完成しました。
QRコードを作成するWEBサービスは多々ありますが、大量生成は想定されていません。本ツールはExcelをベースにすることで、管理番号などを連番で大量にQRコードにしたい場合、オートフィル、コピペ、関数といったExcel上の機能を総動員して簡単に作成できます。すでに管理台帳がExcelで整備されていれば、そのデータを丸ごと流用できます。
Python標準モジュール以外に、Excelファイルの扱いは神ツールOpenPyXLで、QRコード生成にはqrcodeモジュール、画像処理はpillowモジュールを使っています。
使用方法
1.このページ下部「アプリを入手する」からzipファイルをダウンロードします。
展開すると次のようなアプリ実行ファイルとExcelファイルがあります。
Excelファイルはこのアプリ専用にデザインされているので、セルの追加削除等をしないでください。Excelファイルには次のようなシートがあります。
2.データを入力します。
- コード列にはQRコードに変換する文字列(重複不可、途中に空白行不可、必須)
- 名称列には任意の識別名(30文字以下、省略化)
あらかじめ次のようなデータが入力されているので、そのままお試しいただけます。名称はオプション設定(後述)により、出力画像に表示したりファイル名にしたりできます。
実際に使用するときは、これを書き換えてお使いください。
3.Excelファイルを保存終了してアプリにドラッグ&ドロップします。
Windowsからセキュリティの警告がでることがありますが、自己責任で実行許可してください。
一瞬DOS窓が表示されますが、こいつの仕業です。データ数により時間がかかることがあります。
4.処理した結果がフォルダとして出力されます。
フォルダの中身はシートへ入力したデータをQRコード画像へ変換したPNGファイルになっています。
画像は用途に応じてご自由にお使いください。
※ファイル名に禁則文字が含まれている場合は_(アンダーバー)に置き換わっています。
オプション設定
シートのオプション設定により出力するQRコードにさまざまな効果を適用できます。
オプション設定の意味は次の通りです。
オプション | 設定値 | 説明 | 既定値 |
---|---|---|---|
名称表示 | する or しない | QRコードの左上に名称列の値を表示するか | しない |
文字サイズ | 数値 | 名称表示の文字のサイズ | 14 |
フォントパス | 文字列 | 名称表示の文字のフォントファイルのパス | meiryo.ttc |
前景色 | セル色(RGB) | QRコードのドット部分の色 | 0,0,0 |
背景色 | セル色(RGB) | QRコードの背景の色 | 255,255,255 |
余白サイズ | 数値 | QRコードの枠部分の太さ | 4 |
ファイル名 | 名称 or コード | ファイル名をコードにするか名称にするか | 名称 |
だいたいは見たままなのでわかるかと思われますが、色変更は(あまりオススメしませんが)方法が特殊なので補足します。
通常のメニューからセル色を変更すると色は相対的に設定されます。詳細は省略しますがExcelが色をテーマで管理して一括変更を可能にするために、色そのものではなく色のインデックスがつけられているだけの状態です。これだとPythonで色として取得することが(たぶん)できないので絶対値で指定する必要があります。
セルの書式設定 → 塗りつぶし → その他の色 → ユーザー設定タブ とすると次のような設定画面になります。
ここで、カラーモデルをRGBにしてそれぞれ数値で設定するか、カラーピッカーで選んで前景色、背景色の設定セルを塗りつぶしてください。
オプションを適用して出力したQRコード画像のサンプルは次のようになります。
左から
「名称表示する・他デフォルト」
「名称表示する・文字サイズ増・余白サイズ増・前景色変更」
「名称表示しない・前景色変更・背景色変更」
となっております。
枠の太さ(余白サイズ)はQRコードに収めるデータのサイズによって設定通りにならない場合があります。QRコードがデータを表現できないサイズになると余白側へ拡張されます。
ソースコード
スクリプトは次の通りです。得体の知れないサイトの実行ファイルなんて使えねーというかたはソースコードをコピペしてpyファイルにしてご利用ください。
import re
import os
import sys
import qrcode
import openpyxl as excel
from PIL import ImageFont, ImageDraw, ImageColor
try:
excel_file_path = sys.argv[1]
wb = excel.open(excel_file_path, data_only=True)
ws = wb.active
# シートの設定値取得
name_visible = ws['c2'].value == 'する'
file_name_is_code = ws['c13'].value == 'コード'
font_size = ws['c4'].value if type(ws['c4'].value) is int else 14
qr_border = ws['c11'].value if type(ws['c11'].value) is int else 4
font_path = ws['d4'].value if os.path.exists(ws['d4'].value) \
else r'C:\Windows\Fonts\meiryo.ttc'
# 色はRGBで設定している場合のみ有効
qr_fc = ws['c7'].fill.fgColor
qr_bc = ws['c9'].fill.fgColor
if qr_fc.type == 'rgb':
fill_color = ImageColor.getcolor('#' + qr_fc.rgb[2:], 'RGB')
else:
fill_color = 'black'
if qr_bc.type == 'rgb':
back_color = ImageColor.getcolor('#' + qr_bc.rgb[2:], 'RGB')
else:
back_color = 'white'
font = ImageFont.truetype(font_path, font_size)
p = sys.executable if getattr(sys, 'frozen', False) else __file__
cd = os.path.dirname(os.path.abspath(p))
dst_dir = os.path.join(cd, 'qr_code_result')
if not os.path.exists(dst_dir):
os.mkdir(dst_dir)
# QRコード生成保存
for row in ws.iter_rows(min_row=2):
if row[0].value is None:
break
code_str = str(row[0].value)
name = row[1].value
qr = qrcode.QRCode(border=qr_border)
qr.add_data(code_str)
qr.make()
img = qr.make_image(fill_color=fill_color, back_color=back_color)
if name_visible and name is not None:
draw = ImageDraw.Draw(img)
draw.text((10, 10), str(name)[:30], font=font, fill=fill_color)
if file_name_is_code:
s = code_str
else:
s = name
fn = re.sub(r'[\\/:*?"<>|]+', '_', s)
img.save(os.path.join(dst_dir, fn + '.png'), 'PNG')
except:
print('エラーのため中断しました。')
input()
エラーは握り潰すので、必要に応じてエラーハンドラーを追加してください。
アプリを入手する
このアプリに含まれるOpenPyXLモジュールはデフォルトではXMLの脆弱性を利用した攻撃を防ぐことはできません。信頼できないExcelファイルを処理しないでください。
おわり。