【Python】tkinterで「タイピングアプリ」を作成

tkinterでのタイピングアプリの作り方の解説ページアイキャッチ

今回は tkinter で「タイピングアプリ」を作成していきたいと思います!

簡単なアプリですが、下記のような要素を含んでおり、実際に作ってみることで多くのことを学ぶことができると思います!

  • キーのタイピングイベントを受け付ける(キーボードのキー押下)
  • タイピングされた文字を取得する
  • 特定の文字のみ見た目を変更する
  • ウィジェットの見た目を一瞬だけ変化させる

作成する「タイピングアプリ」

今回作成する「タイピングアプリ」は下のアニメーションのようなものになります。

typing-appli-1

画面に表示している英語の文字列をタイピング(入力)していくアプリになります。タイピングの練習にも活用できるアプリになっています。

“次にタイピングする文字” をピンク背景・赤文字で表しており、この文字をキーボードで押下することで次の文字のタイピングに移動します。

タイピングする文字が移動していく様子

これを、表示されている文字列の最後の文字まで繰り返すアプリになります。

また、間違ったキーがタイピングされた時は、背景色を一瞬黄色にすることで警告表示しています。

背景が黄色になって警告表示する様子

画面の下側には、最初の文字のタイピングからの経過時間を表示しており、タイピングにどれくらい時間がかかっているかを把握できるようにしています。

経過時間の表示

この経過時間の表示の仕方については下記ページの「ストップウォッチ」アプリの作成方法の解説で説明していますので、このページでは経過時間の表示についての解説は省略させていただきます。

ストップウォッチアプリ開発方法解説ページのアイキャッチ【Python】tkinter で「ストップウォッチ」アプリを作成

タイピングアプリの作り方

では、ここまで紹介してきたタイピングアプリの作り方を解説していきたいと思います。

ポイントになるのは下記の3つです。

  • 次にタイピングする文字を示す
  • タイピングされた文字に応じた処理を行う
  • 間違ったキーがタイピングされた時に警告表示する

この節ではこのポイント3点の概要のみを解説したいと思います。

スクリプト全体は次のサンプルスクリプトで紹介し、その次のスクリプトの解説で詳細な解説をしていきたいと思います。

スポンサーリンク

次にタイピングする文字を示す

「タイピングアプリ」を作る時のポイントの一つ目が「次にタイピングする文字を示す」ことです。

今回紹介するサンプルでは、文字の背景をピンク色・文字の色を赤色にすることで、次にタイピングする文字を示すようにしています。

次にタイピングする文字のみ見た目を変更させる様子

で、これを実現するために今回はタイピングする文字列をラベルではなく、「キャンバスウィジェット」で表示するようにしています。

より具体的にはキャンバスウィジェットに文字を描画して入力文字列を表しています。

キャンバスで文字列を描画する様子

この理由は、キャンバスウィジェットに描画した文字列は、キャンバスクラスの select_fromselect_to メソッドにより “選択する” ことができるからです。

文字列を選択する様子

さらに、キャンバスクラスのコンストラクタで “選択した” 範囲の背景色や文字の色を設定することもできます。

選択した範囲の見た目を変更する様子

ですので、キャンバスウィジェット作成時(キャンバスクラスのコンストラクタ実行時)に、選択した範囲の背景色と文字の色をそれぞれピンク色と赤色に設定し、さらに “次にタイピングする文字のみ” を select_fromselect_to メソッドで選択するようにすれば、次にタイピングする文字のみを他の文字と異なる見た目で表示することができます。

タイピングされた文字に応じた処理を行う

2つ目のポイントが「タイピングされた文字に応じた処理を行う」ところです。

これを行うためにはまず「キーボードのキーの押下(タイピング)」をイベントとして受付するようにする必要があります。

アプリがキーボードのキー押下イベントを受け付ける様子

イベントをご存知でない方は、下記ページで解説していますのでこちらを参照していただければと思います。

イベント処理解説ページのアイキャッチTkinterの使い方:イベント処理を行う

そして、イベント発生時に実行されるイベントハンドラでは、「タイピングされた文字」を取得し、それに応じた処理を行うようにします。

イベントハンドラの引数は、これも上記ページで解説しているとおり tkinter.Event クラスのインスタンスです。

このインスタンスの属性 char は、そのイベントを発生したときに押されたキーの文字が設定されていますので、この char からタイピングされた文字を取得することが可能です。

イベントハンドラで押されたキーの文字を取得する様子

さらに、このタイピングされた文字に応じて、下記のような処理を行います

  • “次に入力する文字” と “タイピングされた文字” が同じ場合は、”次に入力する文字” を右に1つずらす
  • “次に入力する文字” と “タイピングされた文字” が異なる場合は、タイピングが間違ったことを警告表示する

次にタイピングする文字を示すで説明したように、”次に入力する文字” は select_fromselect_to メソッドで選択中の文字になります。

なので、前者の場合は、”次に入力する文字” を右に1つずらすために以下の処理を行います。

  • 一旦選択を解除する(select_clear メソッド)
  • 次の文字(1つ右の文字)を選択する(select_fromselect_to メソッド)

次の文字を選択する様子

間違ったキーがタイピングされた時に警告表示する

3つ目のポイントは上記の後者の場合の処理です。

異なったキーがタイピングされたことをユーザーに示すために、キャンバスの背景色を一瞬黄色に変化させることで警告表示するようにします。

背景が黄色になって警告表示する様子

より具体的には 100 ms の間だけ黄色に変化させています。

ウィジェットの背景色などの設定はウィジェット作成時(コンストラクタ実行時)に引数で指定することができますが、ウィジェット作成後でも config メソッドを利用して設定を変更し直すことが可能です。

ですので、「タイピングアプリ」では間違ったキーがタイピングされた場合に、一旦 config メソッドでキャンバスの背景色を黄色に設定するようにしています。

さらに、その 100 ms 後に config メソッドでキャンバスの背景色を元々の背景色である白色に設定しなおします。

100ms後にキャンバスの色を元に戻す様子

こんな感じで「指定した時間の後に特定の処理を実行したい」場合は after メソッドが便利です。

after メソッドを実行することで、第1引数に指定した時間(ms 単位)が経過した後に、第2引数に指定した関数やメソッドが自動的に実行されるようになります。

after メソッドについては下記ページで詳しく解説していますので、是非こちらを参考にしていただければと思います。

Tkinterの使い方:after で処理を「遅らせて」or 処理を「定期的」に実行する

スポンサーリンク

サンプルスクリプト

ここまで解説してきた内容を踏まえて作成した「タイピングアプリ」のサンプルスクリプトを紹介していきたいと思います。

スクリプト

下記がそのサンプルスクリプトになります。

typing.py
# -*- 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_WIDTHCANVAS_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 # 選択範囲の囲い線の太さ
)

特にポイントになるのが次にタイピングする文字を示すで説明した内容を実現するために  selectforegroundselectbackgroundselectborderwidth の3つの引数を指定してるところです。。

これらは、キャンバスに描画された文字列の “選択範囲に対する設定” を指定する引数であり、それぞれ下記を指定するものになります。

  • selectforeground:選択された範囲の文字の色
  • selectbackground:選択された範囲の背景の色
  • selectborderwidth:選択された範囲の囲い線の幅

選択した範囲の見た目を変更する様子

つまり、これらを設定して文字の選択を行えば、その選択した範囲だけ他の文字とは異なった特別な見た目にすることができます。

このアプリではこれを利用し、”次にタイピングする文字” のみを選択することで、次にタイピングするべき文字をユーザーに示すことを実現しています。

実際に文字を選択する処理は、後述する drawTextkeyPush メソッドで実施しています。

Canvas クラスのコンストラクタで設定できる項目はたくさんあります。どのような項目が設定できるかは是非下記ページでご確認いただければと思います。

tkinterのキャンバスの作り方解説ページアイキャッチTkinterの使い方:キャンバスウィジェットの作り方

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キャンバスに図形を描画する方法解説ページのアイキャッチTkinterの使い方:Canvasクラスで図形を描画する

ポイントは tag です。tag にタグ名を設定しておくことで、描画したオブジェクトに対し、そのタグ名を指定して後から操作を行うことができます。

どんな操作ができるかは下記ページで解説していますので、興味のある方は読んでみてください。このタイピングアプリで使用する select_fromselect_to についても解説しています。

tkinterキャンバスの図形の操作方法解説ページのアイキャッチTkinterの使い方:Canvasクラスで描画した図形を操作する

このアプリでは、描画した文字列の特定の範囲のみを選択する操作を行うために、上記で指定した tag を利用しています。

drawText メソッドの下記部分が実際にその特定の範囲の選択を行なっている処理になります。

文字の選択
self.canvas.select_from("text", 0)
self.canvas.select_to("text", 0)

select_from の第2引数で文字列の “何文字目から” 選択し、select_to の第2引数で文字列の “何文字目まで” 選択するかをそれぞれ指定することで、指定した範囲の文字が選択されることになります。

この select_fromselect_to の両方を実行しないと選択が行われないので注意してください。

文字が選択される様子

で、どの文字列に対して選択を行うのかが第1引数で指定するタグ名になります。

create_text メソッド実行時に描画する文字列のタグ名を "text" に設定していますので、select_fromselect_to の第1引数にもそれぞれ "text" を設定しています。

さらに上記では、両メソッドの第2引数に 0 を設定しているので、描画した文字の先頭のみが選択されることになります。

そして、Canvas のコンストラクタ実行時の設定により、選択した文字のみが他の文字とは異なった見た目になり、最初にタイピングすべき文字をユーザーに示すことができます。

スポンサーリンク

setEvents(イベントの受付設定)

setEvents はイベントの受付設定を行うメソッドです。

要は bind メソッドを実行してイベントの受付の設定を行います。

今回はキーボードの全てのキーの押下に対するイベントを受け付けるようにしたいので、下記のように bind メソッドを実行しています。

イベントの受付
self.master.bind("<KeyPress>", self.keyPush)

これにより、キーボードのキーが押下された時に、次に説明する keyPush メソッドが自動的に実行されるようになります。

keyPush(タイピングに対するイベントハンドラ)

keyPush はユーザーがタイピングした時(キーボードのキーが押下された時)に実行されるイベントハンドラになります。

基本的にタイピングアプリの作り方で紹介した下記の3つのポイントを実現しているのはこのメソッドになります(1つ目に関しては drawText メソッドでも行なっていますが)。

keyPush メソッドでは、まずタイピングされた文字の取得を行います。この文字はタイピングされた文字に応じた処理を行うでも説明した通り、keyPush メソッドの引数 eventchar 属性から取得することができます。

タイピングされた文字の取得
key = event.char

で、この文字 key と、次にタイピングする文字の比較を行い、この比較結果に応じて処理を切り替えるようにしています。

Typing クラスでは属性 next で、”次にタイピングする文字の位置”(何文字目かの情報)を管理するようにしていますので、上記の比較は次のように行うことができます。

タイピングされた文字の比較
if key == self.text[self.next]:

もし、上記が成り立つ場合は、”次にタイピングすべき文字” と実際にユーザーがタイピングした文字が一致していることになります。

ですので、下記で次にタイピングする文字の位置を示す next をインクリメントし、

タイピングする文字を右に1つ移動
self.next += 1

さらに下記でその next の位置の文字を選択し直すようにしています。

選択のし直し
# 一旦文字の選択を解除
self.canvas.select_clear()

# 略

# 次にタイピングする文字を選択
self.canvas.select_from("text", self.next)
self.canvas.select_to("text", self.next)/code>

この “文字の選択” を行うのは次にタイピングする文字を示すで説明した内容を実現するためです。

上記により、次の文字だけが赤色(背景はピンク色)に変化するので、ユーザーに次にタイピングすべき文字を示すことができます。

MEMO

上記の # 略 の部分では、最後の文字がタイピングされた時の処理も記述していますが、コメントで説明もしていますのでここでの解説は省略させていただきます

また、下記が成立しない場合は、ユーザーが間違った文字をタイピングしてしまったことになります。

タイピングされた文字の比較
if key == self.text[self.next]:

この場合は、間違ったキーがタイピングされた時に警告表示するで説明したように一瞬だけ(100 ms だけ)キャンバスの背景を黄色に変化させてユーザーに警告します。

これを実際に行っているのが、上記の if 文に対する else 節から実行している colorWarning メソッドになります。

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 メソッドは下記のようになります。

colorNormal
	def colorNormal(self):
		''' キャンバスの色を白色に設定'''

		# キャンバスの色を白色に戻す
		self.canvas.config(
			bg="white"
		)

ここでキャンバスに対して config メソッドで背景色 bg"white" に設定し直していますので、100 ms 後に背景色が黄色から元々の白色に戻ることになります。

以上が「タイピングアプリ」のスクリプトの説明になります。ポイントだけの説明になりますので、他に説明が必要な点などあれば、コメントいただければ補足します。

まとめ

このページでは Python で tkinter を利用した「タイピングアプリ」の作り方について解説しました。

スクリプトとしてはコメント込みで170行程度の小規模なものになりますが、それでも下記のような要素があって学べる点も多いのではないかと思います。

  • キーのタイピングイベントを受け付ける(キーボードのキー押下)
  • タイピングされた文字を取得する
  • 特定の文字のみ見た目を変更する
  • ウィジェットの見た目を一瞬だけ変化させる

こんな感じで、簡単そうなアプリでも、実際に作ってみると多くのことを学ぶことができます。

身近なアプリを真似して作るだけでも良い勉強になりますので、是非皆さんもアプリ開発に挑戦してみてください!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です