今日も見に来てくださって、ありがとうございます。石川さんです。
先日、おしごとでバーコードを出力することがあって、ちょっと苦労しました。code128のバーコードを出力しなきゃいけなかったのですけど、このコード、スタートコードがあって、データ部分があって、チェックディジットのコードがあって、ストップコードがある、という風な構造になっていました。これだけ調べて出し方がわかるまでにも結構時間がかかりましたよ~。バーコード、難しいですねぇ。
もっと簡単にできる方法を調査すべく、Python tkinterではどうやってバーコードを出すのかな、ということで調べました。まったくやり方がわからないので、Python barcodeでグーグル先生に聞いてみました。便利な世の中ですねぇ、すぐにPython-barcodeを使えばよいことがわかりました。
出力結果を出すまで紆余曲折ありましたが、なんとか出力することができるようになりました。
できあがりイメージ

ソースコード
ソースコードは以下の通りです。
import barcode
from barcode.writer import ImageWriter
from PIL import ImageTk
import tkinter as tk
import tkinter.filedialog as fd
import tkinter.messagebox as mb
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("Tkinter barcode test")
self.label = tk.Label(self, text="Input code128:")
self.label.grid(row=0, column=0)
self.barcode = tk.StringVar()
self.barcode.set('Python tkiner')
self.entry = tk.Entry(self, textvariable=self.barcode,width=50)
self.entry.grid(row=0, column=1, columnspan=2)
self.show_button = tk.Button(self, text="Show a barcode")
self.show_button.bind("<Button-1>", self.create_image)
self.show_button.grid(row=1,column=1)
self.save_button = tk.Button(self, text="Save a barcode image")
self.save_button.bind("<Button-1>", self.save_image)
self.save_button.grid(row=1,column=0)
self.canvas = tk.Canvas(self, background="white")
self.canvas.grid(row=2,columnspan=3,sticky=tk.NSEW)
self.update()
self.create_image(None)
def create_image(self, event):
data = self.barcode.get()
if not data:
return
try:
code128 = barcode.get('code128', data, writer=ImageWriter())
except barcode.errors.IllegalCharacterError as e:
mb.showerror("Error", e)
return
image = code128.render()
photo = ImageTk.PhotoImage(image)
self.canvas.delete(tk.ALL)
self.canvas.create_image(self.canvas.winfo_width()//2,self.canvas.winfo_height()//2,image=photo)
self.image = photo
def save_image(self, event):
data = self.barcode.get()
if not data:
return
try:
code128 = barcode.get('code128', data, writer=ImageWriter())
except barcode.errors.IllegalCharacterError as e:
mb.showerror("Error", e)
self.update_idletasks()
return
filename = fd.asksaveasfilename()
if filename:
code128.save(filename)
if __name__ == '__main__':
application = Application()
application.mainloop()
詳細
Python-barcordは標準モジュールではありませんので、インストールが必要でした。ぼくの環境はAnacondaですので、通常はcondaを使ってインストールするのですが、condaで管理しているところにはセットアップされていなかったので、インストラクションどおり、pipを利用しました。
pip install python-barcode
インストールは、上記のコマンド一行、これだけです。簡単ですねぇ。一瞬で終了しました。
インストールが完了しましたので、インストラクションどおりに動作するかどうか確認してみました。よくわからないことにチャレンジするときは、ひとつひとつ動作確認していきます。まずは、説明通り動作するか、書かれてあることを試してみます。そして、自分が実現したいことで利用可能なのかどうか、ちょっとずつ、試していくことになります。地道な作業です。
今回は、ちょっとずつ試したことは割愛して、ソースコードの説明をしたいと思います。
まずは、入力されたコードをもとに、バーコードを出力できるようにしたいと思います。バーコード出力先には、キャンバスを使うことにしました。ラベルやボタンでもイメージをセットできるようです。__init__では、これらのtkitnerのウィジェットを組み合わせ枠組みを組み立てています。今回の主ポイントではないので割愛します。
バーコードのデータは35行目のcode128 = barcode.get('code128', data, writer=ImageWriter())で作成しています。第一引数の’code128’はcode128形式のバーコードを作ることを指定しています。dataは作成されるバーコードのもととなるデータで、これはEntry()で入力されたデータです。15行目で定義したStringVar()のインスタンスを17行目のEntry()インスタンス作成時のパラメータtextvariableへセットすることで、Entry()の内容が変わると自動的にStringVar()インスタンスの値も更新されるようになります。writerを指定しない場合は、svg形式のイメージができるようです。今回はtkinterで利用可能なpngへ変更するためにImageWriter()を指定しました。
39行目のrender()で作成したイメージデータを出力しています。40行目のphoto = ImageTk.PhotoImage(image)この部分ですが、ImageTK.PhotoImage()は、tkinterで使えるようイメージ変換してくれます。
で、ポイントは43行目、キャンバスの中央にイメージを作成しています。create_image()メソッドです。指定する座標は作成するイメージの中心点ということだったので、キャンバスの幅と高さを取得してその半分を座標にしました。やっとイメージがキャンバス上に表示されるようになりました。しかし!動かしてみるとわかりますが、これだけではイメージ出力されません。その次の44行目がミソです!イメージをクラスインスタンスに代入しています。これがないと、ローカル変数の値は、ガーベージコレクト、要するにメモリのお掃除対象になってしまって、処理が終わったら消えてしまうのです。最初はこれがなくて、バーコード表示されなかったのですよねぇ。
まとめ
いや~、今回はがんばりました!バーコード作成は、需要があると思うのですけど、日本語の情報が少ないのですよね。誰かのお役に立つと、うれしいです。

