最近Pythonをかじりだした理由として、Rapsberry Piで使う目的で日常ルーチンを読み上げてくれるツールを作ったことがきっかけ。
※読み上げといってもテキスト合成ではなくて、単にwavファイルに入れておいた音声を再生するだけです。
タイトルでがっかりさせちゃってたらすみません。
イメージとしてはこんな感じで起動してきて、再生ボタンをタップするたびに次のタスクを読み上げてくれるルーチンツール。
掃除って単純なようで段取りを間違うと面倒なことになる。
そもそも普段から完璧に綺麗なら何も問題ないんだけど、物が散らかり始めると、Aを掃除中にBが気になり、Bに手を付けたら今度またAが気になりという風にデッドロックがかかる。
ので、ルーチンを設計して、それに命じられるままに掃除しようと。
コンピュータに使われる私。
まずはPythonをまじめに学習する前の私が書いたコード。思考錯誤して、なんとか動いたけど、謙遜抜きでまぁひどいコードである。
よく分からないままコピペした部分も少なからずあるので。
import os import sys import tkinter import playsound from tkinter import * from tkinter import ttk import tkinter.font as font base = os.path.dirname(os.path.abspath(__file__)) def play(filenumber): global var txt = open(base + "/wav/CleanupRoutine-" + str(filenumber) + ".txt", 'r', encoding='utf-8') var.set(txt.read()) root.update() txt.close filename=base + "/wav/CleanupRoutine-" + str(filenumber) + ".wav" playsound.playAudio(filename) root = tkinter.Tk() root.title("Tkinter test") root.attributes("-fullscreen", True) #root.geometry("800x480") frame = tkinter.Frame(root) frame.pack(fill="x") button = tkinter.Button(frame, text="Quit",bg="#00a4e4",fg="#ffffff", width=5, command=root.quit) button.pack(side="right") n = 0 def button2_clicked(event): canvas.itemconfig(imagearea, image = icon2) root.update() global n play(n) n = n + 1 if n > 14 : n = 0 canvas.itemconfig(imagearea, image = icon1) root.update() icon1 = PhotoImage(file=base + '/next.png') icon2 = PhotoImage(file=base + '/next_disabled.png') my_font = font.Font(root,family="fonts-vlgothic",size=20,weight="bold") var = tkinter.StringVar() lbl = tkinter.Label(root, textvariable=var, height=5, font=my_font) lbl.pack() canvas = tkinter.Canvas(root, width=128, height=128) imagearea = canvas.create_image(0, 0, image=icon1, anchor=tkinter.NW) canvas.bind("<Button-1>", button2_clicked) canvas.pack() root.mainloop()
色々インポートしてるけど、このうちplaysoundは私が別ファイルに書いたサウンドファイル再生用のモジュールで、それ以外は標準ライブラリ的なもの。(用語もまだあまり分かってない。)
こちらが、「やさしいPython」の「クラス」の学習まで終わってから修正したコード。
import tkinter as tk import tkinter.font as font import os import playsound class Application(tk.Frame): def __init__(self, master=None): super().__init__(master) self.master = master self.pack() self.load_resources() self.create_widgets() self.n = 0 def load_resources(self): self.base = os.path.dirname(os.path.abspath(__file__)) self.icon1 = tk.PhotoImage(file=self.base + '/next.png') self.icon2 = tk.PhotoImage(file=self.base + '/next_disabled.png') def create_widgets(self): #Root Setting self.master.title("Cleanup Routine") #self.master.attributes("-fullscreen", True) self.master.geometry("800x480") #Generate Parts frame1 = tk.Frame(master = self.master) quit_button = tk.Button( master = frame1, command = self.master.destroy, width = 5, text = "Quit", bg = "#00a4e4", fg = "#ffffff") next_button = tk.Canvas( master = self.master, width = 128, height = 128) self.imagearea = next_button.create_image( 0, 0, image=self.icon1, anchor=tk.NW) next_button.bind("<Button-1>", self.next_clicked) self.var = tk.StringVar() my_font = font.Font(root,family="Migu 1M",size=20,weight="normal") lbl = tk.Label(root, textvariable=self.var, height=5, font=my_font) #Layout frame1.pack(fill="x") quit_button.pack(side="right") lbl.pack() next_button.pack() #Attributation self.frame1 = frame1 self.quit_button = quit_button self.next_button = next_button self.lbl = lbl def play(self, filenumber): txt = open(self.base + "/wav/CleanupRoutine-" + str(filenumber) + ".txt", 'r', encoding='utf-8') self.var.set(txt.read()) root.update() txt.close filename=self.base + "/wav/CleanupRoutine-" + str(filenumber) + ".wav" playsound.playAudio(filename) def next_clicked(self, event): self.next_button.itemconfig(self.imagearea, image = self.icon2) self.master.update() n = self.n self.play(n) n = n + 1 if n > 14: n = 0 self.n = n self.next_button.itemconfig(self.imagearea, image = self.icon1) self.master.update() root = tk.Tk() app = Application(master=root) app.mainloop()
学習によって理解度も進んだので、最初のコードより幾分マシに見える。
インポート文の仕組みも分かったので余計なものをインポートするのもやめられた。
GUIは、Tkinterのサンプルコードをベースに必要なGuiパーツを肉付けしていくというやり方で作った。
実は最初のコードを書く段階ではTkinterのサンプルでクラスが使われているのでよく理解できず、クラスを使ってないサンプルを探し回って使ったんだけど、きちんと学習した後はクラスを使った方が合理的だと分かって書き直した。
ただ今度はクラスの肥大化とサウンド再生とかテキスト読み込み・出力とかがアプリケーションクラスにごちゃっと入ってるので、それはそれでメンテナンス性悪いのではという感じ。
selfの使い方も分かったけど、なんでもselfつけてとりあえずインスタンスの属性に放り込んでおけばいいや的な使い方をしてるので、「合ってるのかこの使い方?」という疑問がある。
次の課題はModel View Controllerの分離。
qiita.com