質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1717閲覧

messageboxを閉じたときに、冒頭の処理に戻るようにしたい

退会済みユーザー

退会済みユーザー

総合スコア0

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/01/21 13:45

前提・実現したいこと

python3でクイズプログラムの作成を行っています。
1.取り込んだcsvから、問題に使う植物Aをランダムに選択
2.Aと同じ科の植物の中から別のひとつBを選択(答えとなる)
3.Aと異なる科の植物の中から3つを選択(C,D,E)
4.「Aと同じ科の植物はどれか選べ」という問題に
B,C,D,Eの選択肢を提示する
5.回答を選んで答え合わせボタンを押すと、正解不正解を表示
6.OKボタンを押すと再び1.に戻って新たな問題が表示される
というプログラムを想定しています。

みなさんのご指導のおかげで5までは実現できたのですが、
6の処理で三度行き詰ってしまいました。
1~4の処理をひとつの関数にまとめて、ボタンクリックイベントの下につけるという
発想がナンセンスなのでしょうか?連日恐縮ですがご教示ください。

該当のソースコード

python3.8.0

1import random 2import csv 3import tkinter, tkinter.messagebox 4 5# 問題のもととなるcsvファイルを読み込み 6# csvは1行目に name,family,character 7# 2行目に カブ,アブラナ科,春の七草 8# 3行目以降 キキョウ,キキョウ科,秋の七草 9# といった感じで3列の値が並ぶ 10 11with open('plant.csv', newline='') as csvfile: 12 reader = csv.DictReader(csvfile) 13 rows = [] 14 for row in reader: 15 rows.append(row) 16 17# 上記DictReaderで読み込んだ辞書リストから、問題用に、ひとつの辞書を呼び出す 18p_plant = random.choice(rows) 19print(p_plant) 20p_name = p_plant["name"] 21 22# 問題用の植物と同じ科(family)の植物をリストにして、 23# その中から問題用の植物を削除。 24# 残りからひとつを選ぶ(=その植物が答えになる) 25 26q_list = [x['name'] for x in rows if x['family'] == p_plant["family"]] 27q_list.remove(p_plant["name"]) 28answer = random.choice(q_list) 29 30# 答えとは異なる科(family)の植物をリスト化し、そこから3つ選ぶ 31 32b_list = [x['name'] for x in rows if x['family'] != p_plant["family"]] 33b_options = random.sample(b_list , 3) 34b_options.append(str(answer)) 35 36tki = tkinter.Tk() 37tki.geometry('300x400') 38tki.title('ラジオボタン') 39 40# ラジオボタンのラベルをリスト化する 41rdo_txt = random.sample(b_options , k=4) 42# ラジオボタンの状態 43rdo_var = tkinter.IntVar() 44 45# ラジオボタンを動的に作成して配置 46for i in range(len(rdo_txt)): 47 rdo = tkinter.Radiobutton(tki, value=i, variable=rdo_var, text=rdo_txt[i]) 48 rdo.place(x=50, y=30 + (i * 24)) 49 50# ボタンクリックイベント 51def btn_click(): 52 num = rdo_var.get() 53 54 if rdo_txt[num] == str(answer): 55 tkinter.messagebox.showinfo('結果' , "正解!") 56 else: 57 tkinter.messagebox.showinfo("結果" , '不正解!正解は' + answer) 58 59# ボタン作成 60btn = tkinter.Button(tki, text='答え合わせ', command=btn_click) 61btn.place(x=100, y=170) 62 63var = tkinter.StringVar() 64var.set(p_name + "と同じ科の植物はどれでしょう") 65label = tkinter.Label(tki, textvariable=var, width=100, ) 66label.pack() 67 68tki.mainloop()

試したこと

<1>
p_plant = random.choice(rows)~
以降を関数にしてボタンクリックイベントの下につけようとしましたが
3行目以降(p_nameの定義)が動きませんでした。
<2>
ボタンクリック後のshowinfoの戻り値をきっかけに
冒頭のrandom.choice(rows)に戻るループを何回かチャレンジしましたが
全くうまくいきませんでした。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

1~4の処理をひとつの関数にまとめて、ボタンクリックイベントの下につけるという

発想がナンセンスなのでしょうか?

恐らくまとめ方に問題があったのだと思います。オリジナルのコードですと、最初に配置済みのボタンやラジオボタン、ラベルの扱いにも困るでしょう。1回だけ実行すれば良い部分と、メッセージボックスに応答した後に再初期化しないといけない部分をうまく切り出せば、その方法でもいけます。
なるべく質問者さんのコードをそのまま利用し、適当に関数にまとめ、関数間でやり取りする変数はグローバル変数として使うよう修正した例が以下です。

Python3

1import random 2import csv 3import tkinter, tkinter.messagebox 4 5tki = None 6rdo = None 7rdo_txt = None 8rdo_var = None 9answer = None 10 11def init_view(tki): 12 global rdo, rdo_txt, rdo_var, answer 13 14 # 上記DictReaderで読み込んだ辞書リストから、問題用に、ひとつの辞書を呼び出す 15 p_plant = random.choice(rows) 16 print(p_plant) 17 p_name = p_plant["name"] 18 19 # 問題用の植物と同じ科(family)の植物をリストにして、 20 # その中から問題用の植物を削除。 21 # 残りからひとつを選ぶ(=その植物が答えになる) 22 23 q_list = [x['name'] for x in rows if x['family'] == p_plant["family"]] 24 q_list.remove(p_plant["name"]) 25 answer = random.choice(q_list) 26 27 # 答えとは異なる科(family)の植物をリスト化し、そこから3つ選ぶ 28 b_list = [x['name'] for x in rows if x['family'] != p_plant["family"]] 29 b_options = random.sample(b_list , 3) 30 b_options.append(str(answer)) 31 32 # 配置済みの子ウィジェットが存在すれば、削除しておく 33 children = tki.winfo_children() 34 for child in children: 35 # print("type of widget is : " + str(type(child))) 36 if str(type(child)) == "<class 'tkinter.Radiobutton'>": 37 child.destroy() 38 elif str(type(child)) == "<class 'tkinter.Button'>": 39 child.destroy() 40 elif str(type(child)) == "<class 'tkinter.Label'>": 41 child.destroy() 42 43 # ラジオボタンのラベルをリスト化する 44 rdo_txt = random.sample(b_options , k=4) 45 # ラジオボタンの状態 46 rdo_var = tkinter.IntVar() 47 48 # ラジオボタンを動的に作成して配置 49 for i in range(len(rdo_txt)): 50 rdo = tkinter.Radiobutton(tki, value=i, variable=rdo_var, text=rdo_txt[i]) 51 rdo.place(x=50, y=30 + (i * 24)) 52 53 # ボタン作成 54 btn = tkinter.Button(tki, text='答え合わせ', command=btn_click) 55 btn.place(x=100, y=170) 56 57 var = tkinter.StringVar() 58 var.set(p_name + "と同じ科の植物はどれでしょう") 59 label = tkinter.Label(tki, textvariable=var, width=100, ) 60 label.pack() 61 62# ボタンクリックイベント 63def btn_click(): 64 num = rdo_var.get() 65 66 if rdo_txt[num] == str(answer): 67 tkinter.messagebox.showinfo('結果' , "正解!") 68 else: 69 tkinter.messagebox.showinfo("結果" , '不正解!正解は' + answer) 70 71 # 表示内容の再初期化 72 init_view(tki) 73 74 75# 問題のもととなるcsvファイルを読み込み 76# csvは1行目に name,family,character 77# 2行目に カブ,アブラナ科,春の七草 78# 3行目以降 キキョウ,キキョウ科,秋の七草 79# といった感じで3列の値が並ぶ 80 81with open('plant.csv', newline='') as csvfile: 82 reader = csv.DictReader(csvfile) 83 rows = [] 84 for row in reader: 85 rows.append(row) 86 87tki = tkinter.Tk() 88tki.geometry('300x400') 89tki.title('ラジオボタン') 90init_view(tki) 91 92tki.mainloop() 93

動くということを示すために簡単に修正して示してみましたが、以下の改善事項が思いつきます。ですので、もっと良い修正方法もあると思います。

  • グローバル変数は安易な気がする。(小さいプログラムならそれでもいいでしょうけど)
  • ラジオボタンなどのウィジェットの再初期化でdestroyしているが、数が決まっているのであれば表示文字列を変えるだけで良いはず。
  • WM_DELETE_WINDOWイベントをハンドリングして、メッセージボックス終了後に再初期化した方がプログラムの流れとして適切かもしれない。(できるかは未確認ですが)

ご参考になれば。

投稿2020/01/21 18:48

dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/01/22 12:00

遅い時間に恐れ入ります。ありがとうございます。 ご指摘の通り4択問題を想定していますので、ラジオボタンはdestroyせず、ラベルのみ更新することを検討してみます。 ちなみにグローバル変数の使用を避けようとする場合は、関数間をまたいで読み込まれる関数を作らず、それぞれの関数の結果は平文(というのか、インデントのない部分)に返し、平文上で処理するのが目指す方向性となるのでしょうか?コメントのみで結構ですので、お時間あればご教示ください。
dodox86

2020/01/22 16:54

> グローバル変数の使用を避けようとする場合は についてですが、そうですね。入力は関数への引数、出力は関数からの返り値でやり取りするようなかたちになるかと思います。 そうすると自然、teburiさんの言われる「インデントの無い部分に返す」ようになるはずです。ですが、グローバル変数もその内容や用途をよくよく検討してみると、クラス化したときのインスタンス変数として扱えるものであったりしますし、クラス化していない場合、グローバル変数でないとどうしても無理な部分も出てくるでしょう。 グローバル変数をなるべく避けるのは関数間の結びつきを弱め、独立性を保つためですが、「プログラムは自分のやりたいことを叶えるひとつのツール」として考え直すと、現状で要望どおりに動いているものを無理に直すこともないかなとも思えます。(<「リファクタリング」と言って、見た目の動きは変えずに中身を積極的に直すこともあります) もし、今のものを大きく機能追加したり、別のプログラムを作る機会があったりしたときのテーマとして残しておくのでも良いかな、と少なくとも個人的には思います。
退会済みユーザー

退会済みユーザー

2020/01/23 11:50

なるほど、プログラムが大きくなってくると、予期せぬところから変数に影響を与えたりするリスクが出そうですね。いったんはお言葉に甘えて(?)今のクイズプログラムの完成を目指しますが、以後念頭において勉強していきたいと思います。ご丁寧に再度のコメントありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問