質問: random.choice(rows)~ 以降を関数にしてボタンクリックイベントの下につけようとしましたが 3行目以降(p_nameの定義)が動きませんでした。 <2> ボタンクリック後のshowinfoの戻り値をきっかけに 冒頭のrandom.choice(rows)に戻るループを何回かチャレンジしましたが 全くうまくいきませんでした。 ### 補足情報(FW/ツールのバージョンなど) ここにより詳細な情報を記載してください。
回答: > > 1~4の処理をひとつの関数にまとめて、ボタンクリックイベントの下につけるという > > 発想がナンセンスなのでしょうか? > > > 恐らくまとめ方に問題があったのだと思います。オリジナルのコードですと、最初に配置済みのボタンやラジオボタン、ラベルの扱いにも困るでしょう。1回だけ実行すれば良い部分と、メッセージボックスに応答した後に再初期化しないといけない部分をうまく切り出せば、その方法でもいけます。 なるべく質問者さんのコードをそのまま利用し、適当に関数にまとめ、関数間でやり取りする変数はグローバル変数として使うよう修正した例が以下です。 ``` import random import csv import tkinter, tkinter.messagebox tki = None rdo = None rdo_txt = None rdo_var = None answer = None def init_view(tki): global rdo, rdo_txt, rdo_var, answer # 上記DictReaderで読み込んだ辞書リストから、問題用に、ひとつの辞書を呼び出す p_plant = random.choice(rows) print(p_plant) p_name = p_plant["name"] # 問題用の植物と同じ科(family)の植物をリストにして、 # その中から問題用の植物を削除。 # 残りからひとつを選ぶ(=その植物が答えになる) q_list = [x['name'] for x in rows if x['family'] == p_plant["family"]] q_list.remove(p_plant["name"]) answer = random.choice(q_list) # 答えとは異なる科(family)の植物をリスト化し、そこから3つ選ぶ b_list = [x['name'] for x in rows if x['family'] != p_plant["family"]] b_options = random.sample(b_list , 3) b_options.append(str(answer)) # 配置済みの子ウィジェットが存在すれば、削除しておく children = tki.winfo_children() for child in children: # print("type of widget is : " + str(type(child))) if str(type(child)) == "": child.destroy() elif str(type(child)) == "": child.destroy() elif str(type(child)) == "": child.destroy() # ラジオボタンのラベルをリスト化する rdo_txt = random.sample(b_options , k=4) # ラジオボタンの状態 rdo_var = tkinter.IntVar() # ラジオボタンを動的に作成して配置 for i in range(len(rdo_txt)): rdo = tkinter.Radiobutton(tki, value=i, variable=rdo_var, text=rdo_txt[i]) rdo.place(x=50, y=30 + (i * 24)) # ボタン作成 btn = tkinter.Button(tki, text='答え合わせ', command=btn_click) btn.place(x=100, y=170) var = tkinter.StringVar() var.set(p_name + "と同じ科の植物はどれでしょう") label = tkinter.Label(tki, textvariable=var, width=100, ) label.pack() # ボタンクリックイベント def btn_click(): num = rdo_var.get() if rdo_txt[num] == str(answer): tkinter.messagebox.showinfo('結果' , "正解!") else: tkinter.messagebox.showinfo("結果" , '不正解!正解は' + answer) # 表示内容の再初期化 init_view(tki) # 問題のもととなるcsvファイルを読み込み # csvは1行目に name,family,character # 2行目に カブ,アブラナ科,春の七草 # 3行目以降 キキョウ,キキョウ科,秋の七草 # といった感じで3列の値が並ぶ with open('plant.csv', newline='') as csvfile: reader = csv.DictReader(csvfile) rows = [] for row in reader: rows.append(row) tki = tkinter.Tk() tki.geometry('300x400') tki.title('ラジオボタン') init_view(tki) tki.mainloop() ``` 動くということを示すために簡単に修正して示してみましたが、以下の改善事項が思いつきます。ですので、もっと良い修正方法もあると思います。 * グローバル変数は安易な気がする。(小さいプログラムならそれでもいいでしょうけど) * ラジオボタンなどのウィジェットの再初期化で`destroy`しているが、数が決まっているのであれば表示文字列を変えるだけで良いはず。 * `WM_DELETE_WINDOW`イベントをハンドリングして、メッセージボックス終了後に再初期化した方がプログラムの流れとして適切かもしれない。(できるかは未確認ですが) ご参考になれば。