最近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)
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):
self.master.title("Cleanup Routine")
self.master.geometry("800x480")
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)
frame1.pack(fill="x")
quit_button.pack(side="right")
lbl.pack()
next_button.pack()
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