ロボ団泉大津校のホームページにようこそ!

Pythonで簡単なGUIアプリを作ろう〜おみくじ編③

難読 Python

Pythonで簡単なデスクトップアプリを作ってみよう!シリーズ。(3回目まできました!)

(*このブログで紹介しているプログラミングの記事は、教室で取り上げたものが中心なので、プログラミングに興味がある小学生の高学年くらいから中学生が理解できるような内容のつもりで書いています。そのため、Pythonのクラスなど、理解が難しいものは出来るだけ使わないようにしており、プログラムが少々、冗長になっている部分はご容赦ください^^。)

今回は、おみくじアプリ①②で作ったプログラムを応用したクイズ形式のアプリを作っていきます!
おみくじアプリ①(おみくじ)はこちらから
おみくじアプリ②(星座占い)はこちらから

作るのは、難しい読み方の地名をあてる『難読地名クイズ』!<問題>と<答え>の部分を変えれば、他のクイズにできますよー。

おみくじアプリ①②と大きく異なるところは、次の二つです。

  1. 出題される<問題>に対応する<答え>を辞書に格納する。
  2. ウィジェットの置き方にフレームを利用する。

前回と同じく、Tkinterを使いますが、ウインドウの作り方、ラベルやボタンの置き方は<おみくじ編①②>で説明したので、今回は省略して、上のポイントのみに絞って解説します。

コードの全体像

はじめにコードの全体像です。内容は次から説明していきますね!

import random
import tkinter as tk


#問題と答えのリスト
dic_nandoku = {"亜細亜":"アジア","欧羅巴":"ヨーロッパ","阿弗利加":"アフリカ",
"亜米利加":"アメリカ","英吉利":"イギリス","仏蘭西":"フランス","独逸":"ドイツ",
"伊太利亜":"イタリア","西班牙":"スペイン","葡萄牙":"ポルトガル",
"露西亜":"ロシア","瑞西":"スイス","丁抹":"デンマーク","波蘭":"ポーランド",
"倫敦":"ロンドン","巴里":"パリ","華盛頓":"ワシントン","伯林":"ベルリン",
"羅馬":"ローマ","寿府":"ジュネーブ", "紐育":"ニューヨーク","聖林":"ハリウッド",
"桑港":"サンフランシスコ","布哇":"ハワイ","剣橋":"ケンブリッジ"}

#現在のキー<問題>
g_cr_key = " "  

#--問題Noをランダムに選択し、ラベルに問題を表示
def changeText1():
    #グローバル変数
    global g_cr_key
    #ランダムなキーを取得
    g_cr_key = random.choice(list(dic_nandoku.keys())) 
    #ラベルのテキストをキーに置き換える
    lb_value['text'] = g_cr_key
    #ラベルのテキストのカラーを緑にする
    lb_value['fg'] = "green"
    #答えボタンを選択可にする
    bt_ans.config(state="active")
    
    
#--選択されている問題の答えをラベルに表示
def changeText2():
    #ラベルのテキストをキーの値に置き換える
    lb_value['text'] = dic_nandoku[g_cr_key]
    #ラベルのテキストのカラーを赤にする
    lb_value['fg'] = "red"
    #答えボタンを選択不可に戻す
    bt_ans.config(state="disable")
#--

root = tk.Tk()
root.title("難読地名クイズ")
root.geometry("450x150")  #幅450,縦:150
#フレーム作成
frame1=tk.Frame(root)
frame2=tk.Frame(root)
#ボタン作成
bt_disp=tk.Button(frame1,text="問題",fg="green", command=changeText1)
bt_ans=tk.Button(frame1,text="答え",activeforeground="red", command=changeText2)
#答えボタンを選択不可にする
bt_ans.config(state="disable")
#ラベル作成
lb_title=tk.Label(frame2,text="この漢字は何と読む?",pady=10)
lb_value=tk.Label(frame2,text="")  
#ボタン配置
bt_disp.pack(side=tk.LEFT,anchor=tk.CENTER)
bt_ans.pack(side=tk.LEFT,anchor=tk.CENTER)
#ラベル配置
lb_title.pack()
lb_value.pack()
#フレーム配置
frame1.pack()
frame2.pack()

root.mainloop()

動かしてみると、以下のような感じになります。問題と答えを変えることで他のクイズに応用できます。

<問題>と<答え>の辞書をつくる

辞書を利用すると、<問題>に対応する<答え>をセットで格納しておくことができます。辞書は、キーと値の組を格納できるデータ構造です。今回は、キーに<問題>を、値に<答え>を入れておくことで、出題された<問題>に対応する<答え>を簡単に取得することができます。
辞書を作成するには、{ }の中に、「キー:値」を組で入れます。それぞれの組はカンマ, で区切ります。

dic_nandoku = {"亜細亜":"アジア","欧羅巴":"ヨーロッパ","阿弗利加":"アフリカ",
"亜米利加":"アメリカ","英吉利":"イギリス","仏蘭西":"フランス","独逸":"ドイツ",
"伊太利亜":"イタリア","西班牙":"スペイン","葡萄牙":"ポルトガル",
"露西亜":"ロシア","瑞西":"スイス","丁抹":"デンマーク","波蘭":"ポーランド",
"倫敦":"ロンドン","巴里":"パリ","華盛頓":"ワシントン","伯林":"ベルリン",
"羅馬":"ローマ","寿府":"ジュネーブ", "紐育":"ニューヨーク","聖林":"ハリウッド",
"桑港":"サンフランシスコ","布哇":"ハワイ","剣橋":"ケンブリッジ"}

フレームを使って、ボタンとラベルを表示する

これまでは、直接、ウインドウにボタンやラベルのウィジェットを置いてきました。ウィジェットが複数になってくると、関連性のあるウィジェットはひとまとまりにして、フレームにいれ、そのフレームをウィンドウにおくと配置がしやすくなります。
フレームのイメージ↓

今回は、上のように、フレーム1に、問題と答えのボタンウィジットと、フレーム2に問題文を表示するラベルと答えを表示するラベルをおきます。
フレーム1は、横方向にボタンを配置、フレーム2は縦方向にラベルを配置します。

フレーム1の作成
フレームは、フレーム名 = tk.Frame(root) で作成し、第一引数に親ウィジット(root)を指定します。

問題と答えのボタンを作成します。tk.Button()の第一引数に、frame1を指定することで、フレームにボタンが置かれます。packメソッドでウィジェットを配置する際、指定がなければ、上から下方向に配置されますが、sideで配置する方向を指定することもできます。フレーム1は、横方向にボタンを配置したいので、side=tk.LEFTを引数に設定しています。また、ボタンウィジェットをフレームの中央に寄せるために、anchor=tk.CENTERを引数に設定しています。

frame1=tk.Frame(root)
 #ボタン作成
bt_disp=tk.Button(frame1,text="問題",fg="green", command=changeText1)  
bt_ans=tk.Button(frame1,text="答え",activeforeground="red", command=changeText2)  

#ボタンを左方向から配置し、中央に寄せる。
bt_disp.pack(side=tk.LEFT,anchor=tk.CENTER)  
bt_ans.pack(side=tk.LEFT,anchor=tk.CENTER)  

#フレーム配置
frame1.pack()

答えのボタンは、問題のボタンが押された時に、アクティブ状態にします。configメソッドを使って、デフォルトはボタンを押せない状態にしておきました。

フレーム2の作成

フレーム2には、問題文のラベルと答えを表示するラベルを配置します。上から下方向にpackメソッドで配置します。オプションを設定することで、ウィジットの上下左右に隙間を作ることが出来ます。padyはウィジットの外側の縦方向に隙間を設定します。pady = 10 としました。横方向に隙間を設定する場合は、padxになります。

frame2=tk.Frame(root)
#ラベル作成
lb_title=tk.Label(frame2,text="この漢字は何と読む?",pady=10)  
lb_value=tk.Label(frame2,text="")  

#ラベル配置
lb_title.pack()  
lb_value.pack()  

#フレーム配置
frame2.pack()

実行ボタンの関数をつくる

<問題>ボタンと<答え>ボタンを押した時に実行される関数を作ります。<問題>ボタンにはchangeText1関数(command=changeText1)、<答え>ボタンにはchangeText2関数(command=changeText2)を指定しています。それぞれのボタンを押すとcommandで設定した関数が呼び出されます。

changeText1関数の作成
changeText1関数は<問題>の部分になります。

#現在のキー<問題>
g_cr_key = " "  

#--問題Noをランダムに選択し、ラベルに問題を表示
def changeText1():
    #グローバル変数
    global g_cr_key
    #ランダムなキーを取得
    g_cr_key = random.choice(list(dic_nandoku.keys())) 
    #ラベルのテキストをキーに置き換える
    lb_value['text'] = g_cr_key
    #ラベルのテキストのカラーを緑にする
    lb_value['fg'] = "green"
    #答えボタンを選択可にする
    bt_ans.config(state="active")

関数の外で、キーの初期値をg_cr_key = ” ” で設定します。初めは何も入っていない状態です。
changeText1()関数の中で、 global g_cr_key と書いていますが、これは、global文といいグローバル変数を作成しています。グローバル変数は関数の内側でも外側でも使える変数です。pythonでは、関数の内側で変数に値を代入すると、ローカル変数になり、その変数は関数の内側でのみ有効となります。しかし、今回のg_cr_key は、changeText1()の関数外でも使用するため、グローバル変数にする必要があります。(はじめに関数外でg_cr_key = ” ” を設定しているので、g_cr_key は元々はグローバル変数ですが、changeText1()関数内で値を代入した時点でローカル変数になるため、global文が必要になります。)

random.choiceで辞書(dic_nandoku)の中からランダムにキーを一つ取り出します。辞書からランダムにキーを取り出すには、ランダム関数をインポートして下のように記載します。

import random
random.choice(list(辞書名.keys())) 

あとは、取得したキー<問題>をラベルに表示し、ラベルのテキストカラーを緑にし、答えボタンのステータスをactive(選択可)にします。

changeText2関数の作成
changeText2関数は<答え>の部分になります。

#--選択されている問題の答えをラベルに表示
def changeText2():
    #ラベルのテキストをキーの値に置き換える
    lb_value['text'] = dic_nandoku[g_cr_key]
    #ラベルのテキストのカラーを赤にする
    lb_value['fg'] = "red"
    #答えボタンを選択不可に戻す
    bt_ans.config(state="disable")
#--

changeText1()で、辞書dic_nandokuのキーをランダムに取得しました。辞書に格納されている値を取り出すには、辞書[キー]と記述します。dic_nandoku[g_cr_key]で、キー<問題>に組になっている値<答え>を取得します。後は、ラベルのテキストをキーの値に置き換え、テキストのカラーを赤にし、答えボタンのステータスを選択不可にします。<答え>を出したら、次の<問題>が出されるまで、<答え>は選択できない状態となります。

以上で完成です!