今回は Python の tkinter でログイン機能付きのアプリのを作成していこうと思います。
Contents
作成するアプリ
まずは、このページで作成していくログイン機能付きアプリについて説明していきます。
アプリ起動画面
今回作成するアプリでは、起動時に下の図のようなログイン画面を表示します。

スポンサーリンク
ユーザー名とパスワード入力
このログイン画面では「ユーザー名」と「パスワード」を入力することができるようにウィジェットを配置しています。

ユーザー名とパスワードの登録
「ユーザー名」と「パスワード」を入力した後に「登録」ボタンを押せば、入力したユーザー名とパスワードが登録されます
ログイン
また、「ユーザー名」と「パスワード」を入力した後に「ログイン」ボタンを押せば、下記を満たす場合にはログインに成功します。
- 入力した「ユーザー名」がすでに登録済
- 入力した「パスワード」と登録済みの「パスワード」が一致
スポンサーリンク
ログイン失敗時の動作
ログインに失敗した場合は、下の図のような画面が表示された後に再度ログイン画面が表示されます。

ログイン成功時の動作
ログインに成功した場合は画面が遷移し、アプリのメイン機能画面が表示されます。
といっても、今回の解説では「ログイン画面」に重点を置いていますので、メイン機能画面はただ単に下の図のようにメッセージを表示するだけになります。

このメイン機能をゲームなどにすれば「ログインしないとプレイできないゲームアプリ」や「ログインユーザーに応じて所持キャラや所持アイテムを変更するゲームアプリ」などを作成することも可能です。
ユーザー名とパスワードの管理
この「ログイン」機能を提供するためには登録された「ユーザー名」と「パスワード」のユーザー情報を管理しておく必要があります。
実際にはこれらはサーバー等で管理しておき、ログイン実行時にサーバーに問い合わせて照合する方が良いのですが、サーバーを準備するのは大変です。
ですので、今回は自身の PC のみで簡単にログイン機能を実現できるように下記の二つの方法でユーザー情報を管理していきたいと思います。
- CSV ファイル
- データベース
スポンサーリンク
ログイン機能付きアプリの作り方
ではここまで解説してきたログイン機能付きアプリの作り方を解説していきたいと思います。
ログイン機能付きアプリのクラス構成
ログイン機能付きアプリのスクリプトとしては大きく分けて下記の2つのクラスを用意します。
- メイン機能を提供するクラス
- ログイン機能を提供するクラス(Loginクラス)
特に今回ポイントになるのはログイン機能を提供する Login クラスです。
このクラスではユーザーからのユーザー名やパスワードの入力を受け付け、その受け付けた情報に基づいてログイン成功・ログイン失敗を判定します。
そして、ログイン成功時にはメイン機能を提供するクラスの処理を開始するように制御するようにします。
逆にログインが成功していない状態では、メイン機能を提供するクラスの処理が開始されないようにする必要があります。
メイン機能の準備
ですので、メイン機能を提供するクラスでは、インスタンス生成時(コンストラクタ実行時)には処理を開始せず、別途処理を開始するためのメソッド(start)を用意するようにします。
そして、ログイン成功時には Login クラスからこの start メソッドを実行するようにします。

あとは、start メソッドが実行された際にメインの機能が実行されるようにクラスを作成すれば良いです。例えばメイン機能がゲームであれば、ゲームを開始する処理を start メソッドに実装すれば良いです。
今回は簡単のため、メインの機能は「ユーザー名を表示すること」だけにしています。
このメイン機能を提供するクラスの一例は下記のようになります。
class MainAppli():
	'''アプリ本体'''
	def __init__(self, master):
		'''
			コンストラクタ
			master:ログイン画面を配置するウィジェット
		'''
		self.master = master
		# ログイン完了していないのでウィジェットは作成しない
	def start(self, login_name):
		'''アプリを起動する'''
		# ログインユーザー名を表示する
		self.message = tkinter.Label(
			self.master,
			font=("",40),
			text=login_name + "でログイン中"
		)
		self.message.pack()
		# 必要に応じてウィジェット作成やイベントの設定なども行うスポンサーリンク
ウィジェットの作成と配置
ここからはログイン機能を提供する Login クラスを作成していきます。
まずは最初にログイン画面っぽくウィジェットの作成と配置を行っていきます。
今回紹介するスクリプトでは下の図のようなログイン画面を作成しますので、

下の図のようにウィジェットの作成と配置を行います。

ウィジェットはそれぞれ grid メソッドにより配置しています。
ウィジェットの配置に関しては下記ページで詳細を解説していますので、必要に応じてこちらも参照していただければと思います。
 Tkinterの使い方:ウィジェットの配置(pack・grid・place)
  Tkinterの使い方:ウィジェットの配置(pack・grid・place)  
tkinter.Entry クラスではユーザーから文字列の入力を受け付けることができます。パスワード入力する tkinter.Entry クラスのインスタンス生成時に、引数 show="*" を指定することで、入力された文字列を “*” で隠すことができます。

ウィジェットの作成と配置を行う create_widgets メソッドは下記のようになります。
def create_widgets(self):
	'''ウィジェットを作成・配置する'''
	# ユーザー名入力用のウィジェット
	self.name_label = tkinter.Label(
		self.master,
		text="ユーザー名"
	)
	self.name_label.grid(
		row=0,
		column=0
	)
	self.widgets.append(self.name_label)
	self.name_entry = tkinter.Entry(self.master)
	self.name_entry.grid(
		row=0,
		column=1
	)
	self.widgets.append(self.name_entry)
	# パスワード入力用のウィジェット
	self.pass_label = tkinter.Label(
		self.master,
		text="パスワード"
	)
	self.pass_label.grid(
		row=1,
		column=0
	)
	self.widgets.append(self.pass_label)
	self.pass_entry = tkinter.Entry(
		self.master,
		show="*"
	)
	self.pass_entry.grid(
		row=1,
		column=1
	)
	self.widgets.append(self.pass_entry)
	# ログインボタン
	self.login_button = tkinter.Button(
		self.master,
		text="ログイン",
		command=self.login
	)
	self.login_button.grid(
		row=2,
		column=0,
		columnspan=2,
	)
	self.widgets.append(self.login_button)
	# 登録ボタン
	self.register_button = tkinter.Button(
		self.master,
		text="登録",
		command=self.register
	)
	self.register_button.grid(
		row=3,
		column=0,
		columnspan=2,
	)
	self.widgets.append(self.register_button)
	# ウィジェット全てを中央寄せ
	self.master.grid_anchor(tkinter.CENTER)下記のようにインスタンスをリスト widgets に参照させるようにしていますが、これはログイン画面の非表示・再表示を行う際に各ウィジェットのインスタンスを参照するためです。
self.widgets.append(self.pass_entry)このリスト widgets は後述するログイン失敗時の処理とログイン成功時の処理で使用します。
ユーザー情報の登録
「登録」ボタンが押された時には、ユーザー情報の登録を行います。
このユーザー情報の管理方法としては、まず CSV ファイルで管理する方法を紹介していきます。
ユーザー情報の登録は、CSV ファイルにユーザー情報を紐付けて追記することで実現します。
より具体的には、Entry ウィジェットに入力された文字列(ユーザー名とパスワード)を取得し、その取得したユーザー名とパスワードを 紐づけて CSV ファイルに追記することでユーザー情報の登録を行います。

この CSV ファイルは Python 標準モジュールの csv を利用することで簡単に扱うことができます。
ユーザー情報を登録する register メソッドの例は下記のようになります。
def register(self):
	'''ユーザー名とパスワードを登録する'''
	# 入力された情報をEntryウィジェットから取得
	username = self.name_entry.get()
	password = self.pass_entry.get()
	# 取得した情報をCSVに追記
	f = open(CSV_FILE, 'a')
	csv_data = csv.writer(f)
	csv_data.writerow([username, password])
	f.close()self.name_entry はユーザー名を self.pass_entry はパスワードをそれぞれ入力するウィジェットのインスタンスで、これらのインスタンスに get メソッドを実行させることで入力された文字列を取得することができます。
さらに、この取得した情報を下記により CSV ファイルの最後に追記しています。
# 取得した情報をCSVに追記
f = open(CSV_FILE, 'a')
csv_data = csv.writer(f)
csv_data.writerow([username, password])
f.close()writerow メソッドを実行することで、csv ファイルに指定されたリストの各要素を同じ行に書き込むことができます。
CSV ファイルの最後に追記するためには open の第2要素に 'a' を指定する必要があります。
CSV ファイルがまだ存在しない場合は、自動的に作成されます。
register メソッドを実行することで、下記のような CSV ファイルを作成していくことができます。
userA, aiueo userB, kakikukeko userC, sashisuseso
各行の第 0 要素がユーザー名、第 1 要素がパスワードになります。
register メソッドが実行されるたびに CSV ファイルの最後に入力されたユーザー情報がどんどん追記されていきます。
userA, aiueo userB, kakikukeko userC, sasisuseso userD, tatituteto
ログイン実行
「ログイン」ボタンが押された時には、ログインの実行を行います。
まず Entry クラスのウィジェットに入力されたユーザー名とパスワードを取得し、ユーザー名がユーザー情報を管理している CSV ファイルに登録されているかどうかを確認します。
登録されている場合は、そのユーザー名が登録された時に一緒に登録されたパスワードと、ウィジェットから取得したパスワードが一致するかどうかを確認します。
パスワードが一致する場合はログイン成功時の処理、ユーザー名が登録されていない or パスワードが一致しない場合はログイン失敗時の処理を行います。
これらの処理を行う login メソッドの一例は下記のようになります。
def login(self):
	'''ログインを実行する'''
	# 入力された情報をEntryウィジェットから取得
	username = self.name_entry.get()
	password = self.pass_entry.get()
	# 登録済みのユーザー名とパスワードを取得
	try:
		f = open(CSV_FILE)
	except FileNotFoundError:
		self.fail()
		return
	csv_data = csv.reader(f)
	
	# 登録されているものと一致するかを確認
	for user in csv_data:
		if user[0] == username:
			if user[1] == password:
				# 登録されているユーザー名・パスワードと一致する場合
				# ログインユーザー名を設定
				self.login_name = username
				self.success()
				break
	else:
		# 入力された情報が登録されていないば場合
		self.fail()
	f.close()入力されているユーザー名やパスワードを取得するのは register メソッドと同様ですが、ログイン時にはこの取得したユーザー名やパスワードと照合するために CSV ファイルの読み込みを行う必要があります。
これは下記のように csv モジュールを行うことで行なっています。これにより CSV ファイル全体が読み込まれます。
さらに、下記のように for 文を記述することで、CSV ファイルの一行分の要素がリスト形式で順々に取得することができます。
for user in csv_data:register メソッドでは、下記のような形式で各行の第 0 要素にはユーザー名、第 1 要素にはパスワードが格納されるように CSV への追記を行っています。
userA, aiueo userB, kakikukeko userC, sashisuseso
for 文内の userもこれと同様に、第 0 要素にユーザー名、第 1 要素にパスワードとなるリストとなります。
['ユーザー名', 'パスワード']ですので、login で各行の user[0] と user[1] それぞれを入力されたユーザー名とパスワードと照合していくことで、入力されたユーザー名が既に登録されており、さらにパスワードが一致しているかどうかを確認することができます。
上記の login メソッドでは、ログインに失敗した場合(入力されたユーザー名が登録されていない場合や、パスワードが一致しない場合)は、fail メソッドによりログイン失敗時の処理を行うようにしています。
ログイン成功した場合は、success メソッドによりログイン成功時の処理を行っています。
スポンサーリンク
ログイン失敗時の処理
ログイン失敗時の処理を行う fail メソッドの一例は下記のようになります。
def fail(self):
	'''ログイン失敗時の処理を行う'''
	# 表示中のウィジェットを一旦削除
	for widget in self.widgets:
		widget.destroy()
	# "ログインできませんでした"メッセージを表示
	self.message = tkinter.Label(
		self.master,
		text="ログインに失敗しました",
		font=("",40)
	)
	self.message.place(
		x=self.master.winfo_width() // 2,
		y=self.master.winfo_height() // 2,
		anchor=tkinter.CENTER
	)
	# 少しディレイを入れてredisplayを実行
	self.master.after(1000, self.redisplay)この fail メソッドでは、主に下記のことを行っています。
- ログイン画面を非表示にする
- ログイン失敗したことをメッセージ表示する
- 一定時間後にメッセージを非表示にし、ログイン画面を再表示する
ログイン画面を非表示にする
ログイン画面の非表示は、ログイン画面を構成するウィジェットを全て削除することで実現しています。
# 表示中のウィジェットを一旦削除
for widget in self.widgets:
	widget.destroy()ウィジェットの削除は、そのインスタンスに destroy メソッドを実行することで行うことができます。
各ウィジェットのインスタンスは、ウィジェットの作成と配置で作成したリスト widgets に格納されていますので、この widgets の全要素に destroy メソッドを実行させればログイン画面を非表示にしています。
ログイン失敗したことをメッセージ表示する
ログイン画面を非表示にした後は「ログインに失敗したこと」をユーザーに伝えるメッセージを画面に表示します。
これは単純に tkinter の Label クラスのインスタンスを生成・配置することで実現することができます。
# "ログインに失敗しました"メッセージを表示
self.message = tkinter.Label(
	self.master,
	text="ログインに失敗しました",
	font=("",40)
)
self.message.place(
	x=self.master.winfo_width() // 2,
	y=self.master.winfo_height() // 2,
	anchor=tkinter.CENTER
)一定時間後にメッセージを非表示にし、ログイン画面を再表示する
ログイン失敗後は再度ログインをユーザーが試行できるように、先ほど表示したメッセージを非表示にし、再度ログイン画面を表示します。
すぐに表示するとユーザーがメッセージを読む時間もありませんので、after メソッドを利用して 1000 ms 遅らせてからメッセージの非表示とログイン画面の表示を行うようにしています。
# 少しディレイを入れてredisplayを実行
self.master.after(1000, self.redisplay)これにより after メソッド実行してから 1000 ms 経過した後に下記の redisplay メソッドが実行されます。
def redisplay(self):
	'''ログイン画面を再表示する'''
	# "ログインできませんでした"メッセージを削除
	self.message.destroy()
	# ウィジェットを再度作成・配置
	self.create_widgets()redisplay メソッドでは Label ウィジェットの削除によるメッセージの非表示と、create_widgets メソッド実行によるウィジェットの作成と配置を行っています。
after メソッドについて詳しく知りたい方は是非下記のページも読んでみてください。
 Tkinterの使い方:after で処理を「遅らせて」or 処理を「定期的」に実行する
  Tkinterの使い方:after で処理を「遅らせて」or 処理を「定期的」に実行する  
ログイン成功時の処理
ログイン失敗時の処理を行う success メソッドの一例は下記のようになります。
def success(self):
	'''ログイン成功時の処理を実行する'''
	# 表示中のウィジェットを一旦削除
	for widget in self.widgets:
		widget.destroy()
	# "ログインに成功しました"メッセージを表示
	self.message = tkinter.Label(
		self.master,
		text="ログインに成功しました",
		font=("",40)
	)
	self.message.place(
		x=self.master.winfo_width() // 2,
		y=self.master.winfo_height() // 2,
		anchor=tkinter.CENTER
	)
	# 少しディレイを入れてredisplayを実行
	self.master.after(1000, self.main_start)この success メソッドでは、主に下記のことを行っています。
- ログイン画面を非表示にする
- ログイン失敗したことをメッセージ表示する
- 一定時間後にメッセージを非表示にし、メイン機能をスタートする
上記2つに関しては fail メソッドと同様ですので説明は省略します。
一定時間後にメッセージを非表示にし、メイン機能をスタートする
これに関しても after メソッドを利用して一定時間送らせた後に処理を行う点は fail メソッドと同様です。
ただし success メソッドでは、after メソッドで下記の main_start メソッドを実行するようにしています。
def main_start(self):
	'''アプリ本体を起動する'''
	# "ログインに成功しました"メッセージを削除
	self.message.destroy()
	# アプリ本体を起動
	self.main.start(self.login_name)main は、メイン機能の準備で作成した「アプリのメイン機能を提供するクラス」のインスタンスで、start メソッドはこのクラスに用意したメイン機能をスタートするメソッドです。
ですので、ログインに成功すると success メソッドからメイン機能を提供するクラスが起動され、メイン機能が実行されることになります。
以上が、ログイン機能付きアプリの作り方になります。
ログイン機能付きアプリのサンプルスクリプト
ではここまで解説してきた内容を踏まえたログイン機能付きアプリのサンプルスクリプトを紹介していきたいと思います。
スポンサーリンク
スクリプト
ログイン機能付きアプリのサンプルスクリプトは下記のようになります。
import csv
import tkinter
# ログイン情報を管理するCSVへのファイルパス
CSV_FILE = "login.csv"
class Login():
	'''ログインを制御するクラス'''
	def __init__(self, master, main):
		'''コンストラクタ
			master:ログイン画面を配置するウィジェット
			body:アプリ本体のクラスのインスタンス
		'''
		self.master = master
		# アプリ本体のクラスのインスタンスをセット
		self.main = main
		# ログイン関連のウィジェットを管理するリスト
		self.widgets = []
		# ログイン画面のウィジェット作成
		self.create_widgets()
	def create_widgets(self):
		'''ウィジェットを作成・配置する'''
		# ユーザー名入力用のウィジェット
		self.name_label = tkinter.Label(
			self.master,
			text="ユーザー名"
		)
		self.name_label.grid(
			row=0,
			column=0
		)
		self.widgets.append(self.name_label)
		self.name_entry = tkinter.Entry(self.master)
		self.name_entry.grid(
			row=0,
			column=1
		)
		self.widgets.append(self.name_entry)
		# パスワード入力用のウィジェット
		self.pass_label = tkinter.Label(
			self.master,
			text="パスワード"
		)
		self.pass_label.grid(
			row=1,
			column=0
		)
		self.widgets.append(self.pass_label)
		self.pass_entry = tkinter.Entry(
			self.master,
			show="*"
		)
		self.pass_entry.grid(
			row=1,
			column=1
		)
		self.widgets.append(self.pass_entry)
		# ログインボタン
		self.login_button = tkinter.Button(
			self.master,
			text="ログイン",
			command=self.login
		)
		self.login_button.grid(
			row=2,
			column=0,
			columnspan=2,
		)
		self.widgets.append(self.login_button)
		# 登録ボタン
		self.register_button = tkinter.Button(
			self.master,
			text="登録",
			command=self.register
		)
		self.register_button.grid(
			row=3,
			column=0,
			columnspan=2,
		)
		self.widgets.append(self.register_button)
		# ウィジェット全てを中央寄せ
		self.master.grid_anchor(tkinter.CENTER)
	def login(self):
		'''ログインを実行する'''
		# 入力された情報をEntryウィジェットから取得
		username = self.name_entry.get()
		password = self.pass_entry.get()
		# 登録済みのユーザー名とパスワードを取得
		try:
			f = open(CSV_FILE)
		except FileNotFoundError:
			self.fail()
			return
		csv_data = csv.reader(f)
		
		# 登録されているものと一致するかを確認
		for user in csv_data:
			if user[0] == username:
				if user[1] == password:
					# 登録されているユーザー名・パスワードと一致する場合
					# ログインユーザー名を設定
					self.login_name = username
					self.success()
					break
		else:
			# 入力された情報が登録されていないば場合
			self.fail()
		f.close()
	def register(self):
		'''ユーザー名とパスワードを登録する'''
		# 入力された情報をEntryウィジェットから取得
		username = self.name_entry.get()
		password = self.pass_entry.get()
		# 取得した情報をCSVに追記
		f = open(CSV_FILE, 'a')
		csv_data = csv.writer(f)
		csv_data.writerow([username, password])
		f.close()
	def fail(self):
		'''ログイン失敗時の処理を行う'''
		# 表示中のウィジェットを一旦削除
		for widget in self.widgets:
			widget.destroy()
		# "ログインに失敗しました"メッセージを表示
		self.message = tkinter.Label(
			self.master,
			text="ログインに失敗しました",
			font=("",40)
		)
		self.message.place(
			x=self.master.winfo_width() // 2,
			y=self.master.winfo_height() // 2,
			anchor=tkinter.CENTER
		)
		# 少しディレイを入れてredisplayを実行
		self.master.after(1000, self.redisplay)
	def redisplay(self):
		'''ログイン画面を再表示する'''
		# "ログインできませんでした"メッセージを削除
		self.message.destroy()
		# ウィジェットを再度作成・配置
		self.create_widgets()
	def success(self):
		'''ログイン成功時の処理を実行する'''
		# 表示中のウィジェットを一旦削除
		for widget in self.widgets:
			widget.destroy()
		# "ログインに成功しました"メッセージを表示
		self.message = tkinter.Label(
			self.master,
			text="ログインに成功しました",
			font=("",40)
		)
		self.message.place(
			x=self.master.winfo_width() // 2,
			y=self.master.winfo_height() // 2,
			anchor=tkinter.CENTER
		)
		# 少しディレイを入れてredisplayを実行
		self.master.after(1000, self.main_start)
	def main_start(self):
		'''アプリ本体を起動する'''
		# "ログインに成功しました"メッセージを削除
		self.message.destroy()
		# アプリ本体を起動
		self.main.start(self.login_name)
		
class MainAppli():
	'''アプリ本体'''
	def __init__(self, master):
		'''
			コンストラクタ
			master:ログイン画面を配置するウィジェット
		'''
		self.master = master
		# ログイン完了していないのでウィジェットは作成しない
	def start(self, login_name):
		'''アプリを起動する'''
		# ログインユーザー名を表示する
		self.message = tkinter.Label(
			self.master,
			font=("",40),
			text=login_name + "でログイン中"
		)
		self.message.pack()
		# 必要に応じてウィジェット作成やイベントの設定なども行う
app = tkinter.Tk()
# メインウィンドウのサイズ設定
app.geometry("600x400")
# アプリ本体のインスタンス生成
main = MainAppli(app)
# ログイン管理クラスのインスタンス生成
login = Login(app, main)
app.mainloop()アプリの設定
下記で、ユーザーのユーザー名とパスワードを保存する CSV ファイルのファイルパスを設定することができます。
# ログイン情報を管理するCSVへのファイルパス
CSV_FILE = "login.csv"アプリの動作確認
スクリプトを実行すると下図のような画面のアプリが起動します。

username 欄にユーザー名、password 欄にパスワードを入力して「登路」ボタンを押すことで、ユーザー情報を登録することができます。
登録済みのユーザー名とパスワードを入力してから「ログイン」ボタンを押せば、ログインに成功して「ログインに成功しました」メッセージ表示後、下の図のようなアプリのメイン機能の画面が表示されます。

登録済みでないユーザー名やパスワードを入力して「ログイン」ボタンを押した場合は、下の図のように「ログインに失敗しました」メッセージが表示され、再度ログイン画面が表示されます。

スポンサーリンク
ユーザー情報をデータベースで管理する
ここまではユーザー情報を CSV ファイルで管理していましたが、次はユーザー情報をデータベースで管理する方法について解説していきたいと思います。
sqlite3 をインポート
Python3 においてはデータベースを利用する sqlite3 が標準モジュールとなっています。今回は簡単のために、この sqlite3 を利用してデータベースによるユーザー情報の管理を実現していきたいと思います。
前述の通り、sqlite3 は標準モジュールですので、Python3 においては単に sqlite3 をインポートするだけで sqlite3 の機能を利用することができます。
import sqlite3データベースの操作
sqlite3 でのデータベースの操作は基本的に下記の流れになります。
- connect関数でデータベースへのコネクションを生成する
- コネクションに cursorメソッドを実行させてカーソルを生成する
- カーソルに executeメソッドを実行させてデータベース操作を行う- データベースにテーブルを作成する
- テーブルに情報を追記する(ユーザー名とパスワード登録時)
- テーブルから情報を取得する(ログイン時)
 
- (必要に応じて)コネクションに commitメソッドを実行させてテーブルの情報を保存する
- コネクションに closeメソッドを実行させてデータベース接続を終了する
execute メソッドの引数には実行する SQL 文を指定する必要があります。
sqlite3 の使い方などは下記ページなどが参考になると思います。
スポンサーリンク
データベースでユーザー情報を登録するサンプルスクリプト
ログイン機能付きアプリのサンプルスクリプトで紹介したスクリプトではユーザー情報を CSV ファイルで管理しましたが、今度はデータベースでユーザー情報を管理するようにしたサンプルスクリプトを紹介したいと思います。
import csv
import tkinter
import sqlite3
# ユーザー情報を登録するDB名
DB_NAME = "user.db"
class Login():
	'''ログインを制御するクラス'''
	def __init__(self, master, main):
		'''コンストラクタ
			master:ログイン画面を配置するウィジェット
			body:アプリ本体のクラスのインスタンス
		'''
		self.master = master
		# アプリ本体のクラスのインスタンスをセット
		self.main = main
		# ログイン関連のウィジェットを管理するリスト
		self.widgets = []
		# ログイン画面のウィジェット作成
		self.create_widgets()
	def create_widgets(self):
		'''ウィジェットを作成・配置する'''
		# ユーザー名入力用のウィジェット
		self.name_label = tkinter.Label(
			self.master,
			text="ユーザー名"
		)
		self.name_label.grid(
			row=0,
			column=0
		)
		self.widgets.append(self.name_label)
		self.name_entry = tkinter.Entry(self.master)
		self.name_entry.grid(
			row=0,
			column=1
		)
		self.widgets.append(self.name_entry)
		# パスワード入力用のウィジェット
		self.pass_label = tkinter.Label(
			self.master,
			text="パスワード"
		)
		self.pass_label.grid(
			row=1,
			column=0
		)
		self.widgets.append(self.pass_label)
		self.pass_entry = tkinter.Entry(
			self.master,
			show="*"
		)
		self.pass_entry.grid(
			row=1,
			column=1
		)
		self.widgets.append(self.pass_entry)
		# ログインボタン
		self.login_button = tkinter.Button(
			self.master,
			text="ログイン",
			command=self.login
		)
		self.login_button.grid(
			row=2,
			column=0,
			columnspan=2,
		)
		self.widgets.append(self.login_button)
		# 登録ボタン
		self.register_button = tkinter.Button(
			self.master,
			text="登録",
			command=self.register
		)
		self.register_button.grid(
			row=3,
			column=0,
			columnspan=2,
		)
		self.widgets.append(self.register_button)
		# ウィジェット全てを中央寄せ
		self.master.grid_anchor(tkinter.CENTER)
	def login(self):
		'''ログインを実行する'''
		# 入力された情報をEntryウィジェットから取得
		username = self.name_entry.get()
		password = self.pass_entry.get()
		if self.check(username, password):
			# ログインユーザー名を設定
			self.login_name = username
			self.success()
		else:
			self.fail()
	def check(self, username, password):
		'''
			入力されたユーザー情報が登録済みか確認する
			username:ユーザー名
			password:パスワード
			返却値:True(登録済み),False(未登録)
		'''
		# DB接続
		self.connection = sqlite3.connect(DB_NAME)
		self.cursor = self.connection.cursor()
		# まだDBにテーブルがなければ作成
		self.cursor.execute(
			"CREATE TABLE IF NOT EXISTS user_info (username text, password text)"
		)
		# ユーザー情報を取得
		check = self.cursor.execute(
			"SELECT * FROM user_info WHERE username=? and password=?",
			[username, password]
		)
		# 取得したユーザー情報をリスト化
		user_list = check.fetchall()
		# ユーザー情報の有無をチェック
		if user_list:
			ret = True
		else:
			ret = False
		# DB接続をクローズ
		self.connection.close()
		# ユーザー情報が登録されているかどうかを返却
		return ret
	def save(self, username, password):
		'''
			入力されたユーザー情報を登録する
			username:ユーザー名
			password:パスワード
		'''
		# DB接続
		self.connection = sqlite3.connect(DB_NAME)
		self.cursor = self.connection.cursor()
		# まだDBにテーブルがなければ作成
		self.cursor.execute(
			"CREATE TABLE IF NOT EXISTS user_info (username text, password text)"
		)
		# 取得した情報をDBに追記
		self.cursor.execute("INSERT INTO user_info VALUES (?,?)", [username, password])
		# DBを保存
		self.connection.commit()
		# DB接続をクローズ
		self.connection.close()
	def register(self):
		'''ユーザー名とパスワードを登録する'''
		# 入力された情報をEntryウィジェットから取得
		username = self.name_entry.get()
		password = self.pass_entry.get()
		self.save(username, password)
	def fail(self):
		'''ログイン失敗時の処理を行う'''
		# 表示中のウィジェットを一旦削除
		for widget in self.widgets:
			widget.destroy()
		# "ログインに失敗しました"メッセージを表示
		self.message = tkinter.Label(
			self.master,
			text="ログインできませんでした",
			font=("",40)
		)
		self.message.place(
			x=self.master.winfo_width() // 2,
			y=self.master.winfo_height() // 2,
			anchor=tkinter.CENTER
		)
		# 少しディレイを入れてredisplayを実行
		self.master.after(1000, self.redisplay)
	def redisplay(self):
		'''ログイン画面を再表示する'''
		# "ログインできませんでした"メッセージを削除
		self.message.destroy()
		# ウィジェットを再度作成・配置
		self.create_widgets()
	def success(self):
		'''ログイン成功時の処理を実行する'''
		# 表示中のウィジェットを一旦削除
		for widget in self.widgets:
			widget.destroy()
		# "ログインに成功しました"メッセージを表示
		self.message = tkinter.Label(
			self.master,
			text="ログインに成功しました",
			font=("",40)
		)
		self.message.place(
			x=self.master.winfo_width() // 2,
			y=self.master.winfo_height() // 2,
			anchor=tkinter.CENTER
		)
		# 少しディレイを入れてredisplayを実行
		self.master.after(1000, self.main_start)
	def main_start(self):
		'''アプリ本体を起動する'''
		# "ログインに成功しました"メッセージを削除
		self.message.destroy()
		# アプリ本体を起動
		self.main.start(self.login_name)
		
class MainAppli():
	'''アプリ本体'''
	def __init__(self, master):
		'''コンストラクタ
			master:ログイン画面を配置するウィジェット
		'''
		self.master = master
		# ログイン完了していないのでウィジェットは作成しない
	def start(self, login_name):
		'''アプリを起動する'''
		# ログインユーザー名を表示する
		self.message = tkinter.Label(
			self.master,
			font=("",40),
			text=login_name + "でログイン中"
		)
		self.message.pack()
		# 必要に応じてウィジェット作成やイベントの設定なども行う
app = tkinter.Tk()
# メインウィンドウのサイズ設定
app.geometry("600x400")
# アプリ本体のインスタンス生成
main = MainAppli(app)
# ログイン管理クラスのインスタンス生成
login = Login(app, main)
app.mainloop()スクリプトの解説
ログイン機能付きアプリのサンプルスクリプトで紹介したスクリプトと異なるのは、register メソッドと login メソッドの2つです。
特にこれらのメソッドから実行される save メソッドと check メソッドでは前述のデータベース操作を行っています。
save メソッドでも check メソッドでも、まだデータベースにユーザー情報を管理するテーブル(user_info)が存在しない場合は、下記の execute でテーブルを作成してからデータベース操作を行うようにしています。
# まだDBにテーブルがなければ作成
self.cursor.execute(
	"CREATE TABLE IF NOT EXISTS user_info (username text, password text)"
)これにより、下の図のように username と password の2つの列を持つテーブル user_info が作成されます(既にテーブル user_info が存在する場合は何も行われないと思います)。

save メソッドではユーザーの情報をデータベースに追記するため、下記のように execute メソッドで SQL 文の INSERT を実行してデータベースの user_info テーブルに追記し、その後 commit メソッドを実行して追記内容の保存を行っています。
# 取得した情報をDBに追記
self.cursor.execute("INSERT INTO user_info VALUES (?,?)", [username, password])
# DBを保存
self.connection.commit()check メソッドではデータベースのユーザー情報の照合を行うため、下記のように execute メソッドで SQL 文の SELECT を実行してデータベースの user_info テーブルからユーザーが入力したユーザー名とパスワードの検索を行っています。
# ユーザー情報を取得
check = self.cursor.execute(
	"SELECT * FROM user_info WHERE username=? and password=?",
	[username, password]
)
# 取得したユーザー情報をリスト化
user_list = check.fetchall()最後に実行している fetchall では、直前の execute で実行した SELECT  による検索結果をリスト化するメソッドです。この fetchall の返却値 user_list が None でなければ検索結果が存在したことになるので、ログイン画面で入力されたユーザー情報がデータベースに存在したことになります。
つまり、ログインボタン押し下げ時にログイン画面に入力されていたユーザー情報は既に登録されていたことになるので、この場合は check メソッドに True を返却させ、その後 check メソッドの呼び出し元である login メソッドにログイン成功時の処理を行うようにしています。
正直私も SQL 文については詳しくないのですが、割と歴史のある命令文ですのでググるとたくさん情報が見つかると思います。やりたいことに応じて適宜検索しながら SQL 文を記述してみてください。
まとめ
このページでは「ログイン機能付きアプリ」の作成方法について解説しました。
ログイン機能を実現するポイントは下記の2つだと思います。
- ユーザー情報の管理
- メイン機能の開始
ユーザー情報の管理については、今回は「CSV ファイルで管理する方法」と「データベースで管理する方法」を紹介しました。
特に、データベースで管理する方法ではデータベースや SQL 文について学べますので是非こちらも挑戦してみていただければと思います。
WEB サーバー上でユーザー情報を管理するようにすれば、ちょっとした簡単なソシャゲアプリなんかも作れそうですね!
メイン機能の開始については、ログイン画面を非表示にしてからメイン機能を提供するクラスのメソッドを実行する方法を紹介しました。
ログイン画面を非表示にする際に destroy メソッドを利用してウィジェットを削除したり、after メソッドでちょっと遅らせて画面を遷移させるあたりはいろんなアプリ作成で利用できるテクニックですので、是非この辺りのメソッドについては理解しておくと良いと思います!


