今回は tkinter で「タイピングアプリ」を作成していきたいと思います!
簡単なアプリですが、下記のような要素を含んでおり、実際に作ってみることで多くのことを学ぶことができると思います!
- キーのタイピングイベントを受け付ける(キーボードのキー押下)
- タイピングされた文字を取得する
- 特定の文字のみ見た目を変更する
- ウィジェットの見た目を一瞬だけ変化させる
Contents
作成する「タイピングアプリ」
今回作成する「タイピングアプリ」は下のアニメーションのようなものになります。
画面に表示している英語の文字列をタイピング(入力)していくアプリになります。タイピングの練習にも活用できるアプリになっています。
“次にタイピングする文字” をピンク背景・赤文字で表しており、この文字をキーボードで押下することで次の文字のタイピングに移動します。
これを、表示されている文字列の最後の文字まで繰り返すアプリになります。
また、間違ったキーがタイピングされた時は、背景色を一瞬黄色にすることで警告表示しています。
画面の下側には、最初の文字のタイピングからの経過時間を表示しており、タイピングにどれくらい時間がかかっているかを把握できるようにしています。
この経過時間の表示の仕方については下記ページの「ストップウォッチ」アプリの作成方法の解説で説明していますので、このページでは経過時間の表示についての解説は省略させていただきます。
【Python】tkinter で「ストップウォッチ」アプリを作成タイピングアプリの作り方
では、ここまで紹介してきたタイピングアプリの作り方を解説していきたいと思います。
ポイントになるのは下記の3つです。
- 次にタイピングする文字を示す
- タイピングされた文字に応じた処理を行う
- 間違ったキーがタイピングされた時に警告表示する
この節ではこのポイント3点の概要のみを解説したいと思います。
スクリプト全体は次のサンプルスクリプトで紹介し、その次のスクリプトの解説で詳細な解説をしていきたいと思います。
スポンサーリンク
次にタイピングする文字を示す
「タイピングアプリ」を作る時のポイントの一つ目が「次にタイピングする文字を示す」ことです。
今回紹介するサンプルでは、文字の背景をピンク色・文字の色を赤色にすることで、次にタイピングする文字を示すようにしています。
で、これを実現するために今回はタイピングする文字列をラベルではなく、「キャンバスウィジェット」で表示するようにしています。
より具体的にはキャンバスウィジェットに文字を描画して入力文字列を表しています。
この理由は、キャンバスウィジェットに描画した文字列は、キャンバスクラスの select_from
と select_to
メソッドにより “選択する” ことができるからです。
さらに、キャンバスクラスのコンストラクタで “選択した” 範囲の背景色や文字の色を設定することもできます。
ですので、キャンバスウィジェット作成時(キャンバスクラスのコンストラクタ実行時)に、選択した範囲の背景色と文字の色をそれぞれピンク色と赤色に設定し、さらに “次にタイピングする文字のみ” を select_from
と select_to
メソッドで選択するようにすれば、次にタイピングする文字のみを他の文字と異なる見た目で表示することができます。
タイピングされた文字に応じた処理を行う
2つ目のポイントが「タイピングされた文字に応じた処理を行う」ところです。
これを行うためにはまず「キーボードのキーの押下(タイピング)」をイベントとして受付するようにする必要があります。
イベントをご存知でない方は、下記ページで解説していますのでこちらを参照していただければと思います。
Tkinterの使い方:イベント処理を行うそして、イベント発生時に実行されるイベントハンドラでは、「タイピングされた文字」を取得し、それに応じた処理を行うようにします。
イベントハンドラの引数は、これも上記ページで解説しているとおり tkinter.Event
クラスのインスタンスです。
このインスタンスの属性 char
は、そのイベントを発生したときに押されたキーの文字が設定されていますので、この char
からタイピングされた文字を取得することが可能です。
さらに、このタイピングされた文字に応じて、下記のような処理を行います
- “次に入力する文字” と “タイピングされた文字” が同じ場合は、”次に入力する文字” を右に1つずらす
- “次に入力する文字” と “タイピングされた文字” が異なる場合は、タイピングが間違ったことを警告表示する
次にタイピングする文字を示すで説明したように、”次に入力する文字” は select_from
と select_to
メソッドで選択中の文字になります。
なので、前者の場合は、”次に入力する文字” を右に1つずらすために以下の処理を行います。
- 一旦選択を解除する(
select_clear
メソッド) - 次の文字(1つ右の文字)を選択する(
select_from
とselect_to
メソッド)
間違ったキーがタイピングされた時に警告表示する
3つ目のポイントは上記の後者の場合の処理です。
異なったキーがタイピングされたことをユーザーに示すために、キャンバスの背景色を一瞬黄色に変化させることで警告表示するようにします。
より具体的には 100
ms の間だけ黄色に変化させています。
ウィジェットの背景色などの設定はウィジェット作成時(コンストラクタ実行時)に引数で指定することができますが、ウィジェット作成後でも config
メソッドを利用して設定を変更し直すことが可能です。
ですので、「タイピングアプリ」では間違ったキーがタイピングされた場合に、一旦 config
メソッドでキャンバスの背景色を黄色に設定するようにしています。
さらに、その 100
ms 後に config
メソッドでキャンバスの背景色を元々の背景色である白色に設定しなおします。
こんな感じで「指定した時間の後に特定の処理を実行したい」場合は after
メソッドが便利です。
after
メソッドを実行することで、第1引数に指定した時間(ms 単位)が経過した後に、第2引数に指定した関数やメソッドが自動的に実行されるようになります。
after
メソッドについては下記ページで詳しく解説していますので、是非こちらを参考にしていただければと思います。
スポンサーリンク
サンプルスクリプト
ここまで解説してきた内容を踏まえて作成した「タイピングアプリ」のサンプルスクリプトを紹介していきたいと思います。
スクリプト
下記がそのサンプルスクリプトになります。
# -*- coding:utf-8 -*-
import tkinter
import time
# タイピング文字列
TEXT = "It was in the spring of the year 1894 that all London was interested, and the fashionable world dismayed, by the murder of the Honourable Ronald Adair under most unusual and inexplicable circumstances."
# キャンバスのサイズ
CANVAS_WIDTH = 500
CANVAS_HEIGHT = 300
# ラベルを更新する間隔[ms]
INTERVAL = 10
class Typing():
def __init__(self, master):
'''コンストラクタ'''
# アプリのメインウィンドウ設定
self.master = master
# 入力受付文字列設定
self.text = TEXT
# キャンバスのサイズ設定
self.canvas_width = CANVAS_WIDTH
self.canvas_height = CANVAS_HEIGHT
# 各種内部変数の初期化
self.next = 0 # 次にタイピングする文字の位置
self.start_time = None # 計測開始時間
self.timer = None # afterのID
self.is_finish = False # 終了フラグ
# ウィジェットの作成と配置を実行
self.createWidgets(master)
# キャンバス上に入力受付文字列を描画
self.drawText()
# イベントの受付設定
self.setEvents()
def createWidgets(self, master):
'''ウィジェットの作成と配置を行う'''
# 入力を受け付ける文字列表示用のキャンバス作成
self.canvas = tkinter.Canvas(
master,
bg="white",
width=self.canvas_width,
height=self.canvas_height,
selectforeground="red", # 選択された文字の色
selectbackground="pink", # 選択された文字の背景色
selectborderwidth=0 # 選択範囲の囲い線の太さ
)
self.canvas.pack()
# 計測時間表示用のラベル作成
self.label = tkinter.Label(
master,
text="0.00", # 時間の計測開始までは0.00を表示
font=("", 30)
)
self.label.pack()
def drawText(self):
'''キャンバスに文字を描画する'''
# 入力を受け付ける文字列を描画
self.canvas.create_text(
10, 10, # 座標 (0,0) から描画
anchor=tkinter.NW, # 左上寄せ
text=TEXT, # 描画する文字列は "タイピング文字列"
font=("", 30, "bold"),
width=self.canvas_width - 20, # 文字列の折り返し幅
tag="text" # 描画オブジェクト操作ようにタグ付け
)
# 次にタイピングする文字(つまり最初の文字)を選択
self.canvas.select_from("text", 0)
self.canvas.select_to("text", 0)
def setEvents(self):
'''イベントの受付設定を行う'''
# キー押下イベントを全て受付
self.master.bind("<KeyPress>", self.keyPush)
def keyPush(self, event):
'''タイピングされた時の処理を行う'''
if self.is_finish:
# すでに全文字タイピング済みなので何もしない
return
# タイピングされたキーの文字を取得
key = event.char
if key == '':
# 文字でない場合は終了
return
print(key)
if key == self.text[self.next]:
# タイピングされた文字が合っていた場合
if self.next == 0:
# 最初の文字のタイピング時は時間計測開始する
self.start_time = time.time()
self.timer = self.master.after(INTERVAL, self.update_time)
# 次にタイピングする文字を右にずらす
self.next += 1
# 一旦文字の選択を解除
self.canvas.select_clear()
if self.next == len(self.text):
# 全文字タイピングされた場合は終了処理
# 次のタイピングを受け付けないようにフラグセット
self.is_finish = True
# 時間計測を終了
self.master.after_cancel(self.timer)
# 次にタイピングする文字を選択
self.canvas.select_from("text", self.next)
self.canvas.select_to("text", self.next)
else:
# 間違った文字がタイピングされた場合
# キャンバスの色を黄色にして警告表示
self.colorWarning()
def colorWarning(self):
'''キャンバスの色を黄色に設定'''
# キャンバスの色を黄色にして警告表示
self.canvas.config(
bg="yellow"
)
# キャンバスの色を元に戻すために100ms後にcolorNormalを実行
self.master.after(100, self.colorNormal)
def colorNormal(self):
''' キャンバスの色を白色に設定'''
# キャンバスの色を白色に戻す
self.canvas.config(
bg="white"
)
def update_time(self):
'''計測時間表示を更新する'''
# update_timeを再度INTERVAL[ms]後に実行(定期的実行用)
self.timer = self.master.after(INTERVAL, self.update_time)
# 現在の時刻を取得
now_time = time.time()
# 最初の文字入力からの経過時間を計算
elapsed_time = now_time - self.start_time
# 小数点第2位までに変換
elapsed_time_str = '{:.2f}'.format(elapsed_time)
# 計測時間を表示
self.label.config(text=elapsed_time_str)
# スクリプトここから開始
app = tkinter.Tk()
typing = Typing(app)
app.mainloop()
スクリプトの設定
スクリプトの先頭付近で、下記の3つを設定できるようにしています。
TEXT
:タイピング文字列CANVAS_WIDTH
:キャンバスの幅CANVAS_HEIGHT
:キャンバスの高さ
TEXT
がキャンバスに描画する文字列、すなわちタイピングする文字列になりますので、この文字列を変更することで好きな文字列をタイピングすることができるようになります。
ただし、TEXT
の文字数が長すぎるとキャンバスに収まりきらない可能性があるので注意してください。適宜 CANVAS_WIDTH
と CANVAS_HEIGHT
を設定してキャンバスのサイズを調整していただければと思います。
スポンサーリンク
スクリプトの解説
スクリプトでは、メインウィンドウの作成とメインループ実行以外は Typing
クラスで処理を行うようにしています。
# スクリプト実行ここから開始
app = tkinter.Tk()
typing = Typing(app)
app.mainloop()
Typing
クラスでは、コンストラクタが実行されると下記の処理を行うようにしています。
- 各種初期設定
- ウィジェットの作成と配置(
createWidgets
) - キャンバスへの文字列
TEXT
の描画(drawText
) - イベントの受付設定(
setEvents
)
コンストラクタ実行後はアプリはメインループで待機し続けます。
そして、タイピングが行われると、イベント受付設定で指定したイベントハンドラ keyPush
が実行され、タイピングされた文字に応じた処理を行なっています。
ポイントになるのは各メソッドだと思いますので、下記の4つのメソッドについて解説していきたいと思います。
createWidgets
drawText
setEvents
keyPush
createWidgets
(ウィジェットの作成と配置)
createWidgets
へウィジェットの作成と配置を行うメソッドです。
作成するウィジェットは、文字列を描画するための “キャンバス” と、経過時間を表示するための “ラベル” の2つです。
今回作成するタイピングアプリにおいてポイントになるのは、キャンバス作成時の設定(Canvas
クラスのコンストラクタ実行時の引数)です。
self.canvas = tkinter.Canvas(
master,
bg="white",
width=self.canvas_width,
height=self.canvas_height,
selectforeground="red", # 選択された文字の色
selectbackground="pink", # 選択された文字の背景色
selectborderwidth=0 # 選択範囲の囲い線の太さ
)
特にポイントになるのが次にタイピングする文字を示すで説明した内容を実現するために selectforeground
・selectbackground
・selectborderwidth
の3つの引数を指定してるところです。。
これらは、キャンバスに描画された文字列の “選択範囲に対する設定” を指定する引数であり、それぞれ下記を指定するものになります。
selectforeground
:選択された範囲の文字の色selectbackground
:選択された範囲の背景の色selectborderwidth
:選択された範囲の囲い線の幅
つまり、これらを設定して文字の選択を行えば、その選択した範囲だけ他の文字とは異なった特別な見た目にすることができます。
このアプリではこれを利用し、”次にタイピングする文字” のみを選択することで、次にタイピングするべき文字をユーザーに示すことを実現しています。
実際に文字を選択する処理は、後述する drawText
と keyPush
メソッドで実施しています。
Canvas
クラスのコンストラクタで設定できる項目はたくさんあります。どのような項目が設定できるかは是非下記ページでご確認いただければと思います。
drawText
(キャンバスへの文字列描画)
drawText
は、キャンバスへの文字列描画を行うメソッドです。
また、キャンバスへの文字列描画後に、描画した文字列の先頭を “選択する” 処理も行っています。
文字列の描画は Canvas
クラスの create_text
メソッドにより実行することが可能です。
self.canvas.create_text(
10, 10, # 座標 (0,0) から描画
anchor=tkinter.NW, # 左上寄せ
text=TEXT, # 描画する文字列は "タイピング文字列"
font=("", 30, "bold"),
width=self.canvas_width - 20, # 文字列の折り返し幅
tag="text" # 描画オブジェクト操作ようにタグ付け
)
各引数の詳細は下記ページで解説していますのでこちらを参照していただければと思います。
Tkinterの使い方:Canvasクラスで図形を描画するポイントは tag
です。tag
にタグ名を設定しておくことで、描画したオブジェクトに対し、そのタグ名を指定して後から操作を行うことができます。
どんな操作ができるかは下記ページで解説していますので、興味のある方は読んでみてください。このタイピングアプリで使用する select_from
や select_to
についても解説しています。
このアプリでは、描画した文字列の特定の範囲のみを選択する操作を行うために、上記で指定した tag
を利用しています。
drawText
メソッドの下記部分が実際にその特定の範囲の選択を行なっている処理になります。
self.canvas.select_from("text", 0)
self.canvas.select_to("text", 0)
select_from
の第2引数で文字列の “何文字目から” 選択し、select_to
の第2引数で文字列の “何文字目まで” 選択するかをそれぞれ指定することで、指定した範囲の文字が選択されることになります。
この select_from
と select_to
の両方を実行しないと選択が行われないので注意してください。
で、どの文字列に対して選択を行うのかが第1引数で指定するタグ名になります。
create_text
メソッド実行時に描画する文字列のタグ名を "text"
に設定していますので、select_from
と select_to
の第1引数にもそれぞれ "text"
を設定しています。
さらに上記では、両メソッドの第2引数に 0
を設定しているので、描画した文字の先頭のみが選択されることになります。
そして、Canvas
のコンストラクタ実行時の設定により、選択した文字のみが他の文字とは異なった見た目になり、最初にタイピングすべき文字をユーザーに示すことができます。
スポンサーリンク
setEvents
(イベントの受付設定)
setEvents
はイベントの受付設定を行うメソッドです。
要は bind
メソッドを実行してイベントの受付の設定を行います。
今回はキーボードの全てのキーの押下に対するイベントを受け付けるようにしたいので、下記のように bind
メソッドを実行しています。
self.master.bind("<KeyPress>", self.keyPush)
これにより、キーボードのキーが押下された時に、次に説明する keyPush
メソッドが自動的に実行されるようになります。
keyPush
(タイピングに対するイベントハンドラ)
keyPush
はユーザーがタイピングした時(キーボードのキーが押下された時)に実行されるイベントハンドラになります。
基本的にタイピングアプリの作り方で紹介した下記の3つのポイントを実現しているのはこのメソッドになります(1つ目に関しては drawText
メソッドでも行なっていますが)。
keyPush
メソッドでは、まずタイピングされた文字の取得を行います。この文字はタイピングされた文字に応じた処理を行うでも説明した通り、keyPush
メソッドの引数 event
の char
属性から取得することができます。
key = event.char
で、この文字 key
と、次にタイピングする文字の比較を行い、この比較結果に応じて処理を切り替えるようにしています。
Typing
クラスでは属性 next
で、”次にタイピングする文字の位置”(何文字目かの情報)を管理するようにしていますので、上記の比較は次のように行うことができます。
if key == self.text[self.next]:
もし、上記が成り立つ場合は、”次にタイピングすべき文字” と実際にユーザーがタイピングした文字が一致していることになります。
ですので、下記で次にタイピングする文字の位置を示す next
をインクリメントし、
self.next += 1
さらに下記でその next
の位置の文字を選択し直すようにしています。
# 一旦文字の選択を解除
self.canvas.select_clear()
# 略
# 次にタイピングする文字を選択
self.canvas.select_from("text", self.next)
self.canvas.select_to("text", self.next)/code>
この “文字の選択” を行うのは次にタイピングする文字を示すで説明した内容を実現するためです。
上記により、次の文字だけが赤色(背景はピンク色)に変化するので、ユーザーに次にタイピングすべき文字を示すことができます。
上記の # 略
の部分では、最後の文字がタイピングされた時の処理も記述していますが、コメントで説明もしていますのでここでの解説は省略させていただきます
また、下記が成立しない場合は、ユーザーが間違った文字をタイピングしてしまったことになります。
if key == self.text[self.next]:
この場合は、間違ったキーがタイピングされた時に警告表示するで説明したように一瞬だけ(100
ms だけ)キャンバスの背景を黄色に変化させてユーザーに警告します。
これを実際に行っているのが、上記の if
文に対する else
節から実行している colorWarning
メソッドになります。
def colorWarning(self):
'''キャンバスの色を黄色に設定'''
# キャンバスの色を黄色にして警告表示
self.canvas.config(
bg="yellow"
)
# キャンバスの色を元に戻すために100ms後にcolorNormalを実行
self.master.after(100, self.colorNormal)
この colorWarning
メソッドでは、キャンバスに対して config
メソッドで背景色 bg
を "yellow"
に設定しています。ですので、これによりキャンバスの背景色が黄色に変化します。
さらに続いて after
メソッドを実行し、100
ms 後に colorNormal
メソッドを実行するように設定しています。
で、この 100
ms 後に実行される colorNormal
メソッドは下記のようになります。
def colorNormal(self):
''' キャンバスの色を白色に設定'''
# キャンバスの色を白色に戻す
self.canvas.config(
bg="white"
)
ここでキャンバスに対して config
メソッドで背景色 bg
を "white"
に設定し直していますので、100
ms 後に背景色が黄色から元々の白色に戻ることになります。
以上が「タイピングアプリ」のスクリプトの説明になります。
まとめ
このページでは Python で tkinter を利用した「タイピングアプリ」の作り方について解説しました。
スクリプトとしてはコメント込みで170行程度の小規模なものになりますが、それでも下記のような要素があって学べる点も多いのではないかと思います。
- キーのタイピングイベントを受け付ける(キーボードのキー押下)
- タイピングされた文字を取得する
- 特定の文字のみ見た目を変更する
- ウィジェットの見た目を一瞬だけ変化させる
こんな感じで、簡単そうなアプリでも、実際に作ってみると多くのことを学ぶことができます。
身近なアプリを真似して作るだけでも良い勉強になりますので、是非皆さんもアプリ開発に挑戦してみてください!