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

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

新規登録して質問してみよう
ただいま回答率
85.35%
for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Python

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

Q&A

解決済

1回答

1395閲覧

tkinterを使って繰り返し処理による問題作成

taki.muramatsu

総合スコア7

for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Python

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

0グッド

0クリップ

投稿2021/09/22 00:31

編集2021/09/30 09:22

特定の条件の間
問題を作成して出題できるようにしたい
次の問題ボタンを押すと次のページが作成されて問題が出題されるようにしたいです

発生している問題・エラーメッセージ

一つのフレームに
複数の問題が表示されてしまいます
イメージ説明

エラーメッセージ

該当のソースコード

ソースコード import tkinter as tk import tkinter.ttk as ttk from numpy.lib.ufunclike import _fix_out_named_y from sympy import * import pandas as pd import numpy as np import random import matplotlib.pyplot as plt from sympy.external.importtools import WARN_NOT_INSTALLED from sympy.matrices import common #シンボルを定義 x = Symbol("x") y = Symbol("y") z = Symbol("z") a = Symbol("a") b = Symbol("b") c = Symbol("c") #基本問題作成 def create_question_1(Question_No,Val_No_1,Val_No_2,Val_No_3): if Question_No == 1: if Val_No_1 == 1: Integ = x ans = integrate(Integ) out_Integ = "∮"+"x"+" "+"dx" else: Integ =Val_No_1*x ans = integrate(Integ) out_Integ ="∮"+str(Val_No_1)+"x"+" "+"dx" elif Question_No == 2: if Val_No_1 and Val_No_2 == 1: Integ = x**2+x ans = integrate(Integ) out_Integ = "∮"+"x**2"+"+"+"x"+" "+"dx" else: Integ = Val_No_1*x**2+Val_No_2*x ans = integrate(Integ) out_Integ = "∮"+str(Val_No_1)+"x**2"+"+"+str(Val_No_2)+"x"+" "+"dx" else: if Val_No_1 and Val_No_2 and Val_No_3 == 1: Integ = x**3+x**2+x ans = integrate(Integ) out_Integ = "∮"+"x**3"+"+"+"x**2"+"+"+"x"+"+"+" "+"dx" else: Integ = Val_No_1*x**3+Val_No_2*x**2+Val_No_3 ans = integrate(Integ) out_Integ = "∮"+str(Val_No_1)+"x**3"+"+"+str(Val_No_2)+"x**2"+"+"+str(Val_No_3)+"x"+" "+"dx" return ans,out_Integ #ウィンドウ切り替え def change_window(frame_name): frame_name.tkraise() #フレーム作成 def frame_create(): Frame_name = ttk.Frame(root) Frame_name.grid(row=0,column=0,sticky="nsew",pady=20) return Frame_name #問題ページ作成 def create_question(frame_name,change_frame,ans,Out_Integ): label_1 = ttk.Label(frame_name,text="以下の問題を解いてください") label_2 = ttk.Label(frame_name,text="回答を表示します") label_app = ttk.Label(frame_name,text=Out_Integ) Entry_app = ttk.Entry(frame_name,width=30) button_home = ttk.Button(frame_name,text="ホームに戻る",command=lambda:change_window(change_frame)) answer_button = ttk.Button(frame_name,text="回答",command=lambda:Answer_button(Entry_app,ans,label_2)) label_1.pack() label_2.place(x=350,y=350) label_app.pack() Entry_app.place(x=300,y=300) button_home.place(x=350,y=420) answer_button.place(x=350,y=520) #回答ボタンの動き def Answer_button(Entry_name,ans,label_name): Entry_ans = Entry_name.get() str_ans = str(ans) if str_ans == Entry_ans: label_name.configure(text="正解") else: label_name.configure(text="不正解") #ラベル作成 def label_create(frame_name,text_name): label_name = ttk.Label(frame_name,text=text_name) return label_name #ボタン生成 def button_create(frame_name,text_name,button_name,change_window_name): button_frame = ttk.Button(frame_name,text=text_name,command=lambda:button_name(change_window_name)) return button_frame #ラベル配置 def label_placcement(label_name,x_coord,y_coord): label_name.place(x=x_coord,y=y_coord) #ボタン配置 def button_placcement(button_name,x_coord,y_coord): button_name.place(x=x_coord,y=y_coord) if __name__ == "__main__": root = tk.Tk() root.geometry("800x800") root.grid_rowconfigure(0,weight=1) root.grid_columnconfigure(0,weight=1) #メインフレーム作成 frame = frame_create() frame_app_1 = frame_create() frame_app_2 = frame_create() frame_app_3 = frame_create() frame_app_4 = frame_create() frame_app_5 = frame_create() frame_app_6 = frame_create() #ホームウィジェット作成 label_1 = label_create(frame,"あなたの成長する積分アプリ") label_2 = label_create(frame,"⚠️x**2はxの2乗x**3はxの3乗を表しています") label_3 = label_create(frame,"1:基本レベル") label_4 = label_create(frame,"2:定期テストレベル") label_5 = label_create(frame,"3:センター試験レベル") label_6 = label_create(frame,"4:難関私立レベル") label_6 = label_create(frame,"5:難関国公立レベル") label_7 = label_create(frame,"6:弱点レベル") #ラベル配置 label_placcement(label_1,290,50) label_placcement(label_2,290,150) label_placcement(label_3,330,250) label_placcement(label_4,330,350) label_placcement(label_5,330,450) label_placcement(label_6,330,550) label_placcement(label_7,330,650) #問題ページボタン作成 button_1 = button_create(frame,"問題へ",change_window,frame_app_1) button_2 = button_create(frame,"問題へ",change_window,frame_app_2) button_3 = button_create(frame,"問題へ",change_window,frame_app_3) button_4 = button_create(frame,"問題へ",change_window,frame_app_4) button_5 = button_create(frame,"問題へ",change_window,frame_app_5) button_6 = button_create(frame,"問題へ",change_window,frame_app_6) #ボタン配置 button_placcement(button_1,350,280) button_placcement(button_2,350,380) button_placcement(button_3,350,480) button_placcement(button_4,350,580) button_placcement(button_5,350,580) button_placcement(button_6,350,680) #問題作成 i = 0 j = 0 while i <= 10: Question_No = random.randint(1,3) Val_No_1 = random.randint(1,9) Val_No_2 = random.randint(1,9) Val_No_3 = random.randint(1,9) ans,Out_Integ = create_question_1(Question_No,Val_No_1,Val_No_2,Val_No_3) create_question(frame_app_1,ans,frame,Out_Integ) i += 1 j += 1 Frame_Name = "frame_"+str(i) Frame_Name_1 = "frame_"+str(j) Frame_Name = frame_create() Frame_Name_1 = frame_create() button = ttk.Button(frame_app_1,text="次の問題へ",command = change_window(Frame_Name)) button.place(x=350,y=670) frame.tkraise() root.mainloop()

試したこと

ここに問題に対して試したことを記載してください。
問題作成時の"次の問題へ"の処理時にcreate_questionを呼び出すよにしたが
思い通りの動きをしなかった

i = 0 j = 0 count = 0 while count < 10: count += 1 i += 1 j += 1 Question_No = random.randint(1,3) Val_No_1 = random.randint(1,9) Val_No_2 = random.randint(1,9) Val_No_3 = random.randint(1,9) ans,Out_Integ = create_question_1(Question_No,Val_No_1,Val_No_2,Val_No_3) Frame_Name = "frame_app_"+str(i) Frame_Name_1 = "frame_app_1"+str(j) Frame = frame_create() Frame_1 = frame_create() Frame_Name = Frame Frame_Name_1 = Frame_1 create_question(Frame_Name,frame,ans,Out_Integ) next_question_button = ttk.Button(Frame_Name,text="次の問題へ",command=lambda:change_window(Frame_Name_1)) next_question_button.place(x=600,y=600)

現状のループ内

コード count = 0 while count < 10: count += 1 Question_No = random.randint(1,3) Val_No_1 = random.randint(1,9) Val_No_2 = random.randint(1,9) Val_No_3 = random.randint(1,9) ans,Out_Integ = create_question_1(Question_No,Val_No_1,Val_No_2,Val_No_3) question_Frame = frame_create() create_question(frame_app_1 ,frame,ans,Out_Integ) create_question(question_Frame ,frame,ans,Out_Integ) next_question_button = ttk.Button(frame_app_1,text="次の問題へ",command=functools.partial(change_window,question_Frame)) next_question_button.place(x=600,y=600)

イメージ説明
イメージ説明

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

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

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

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

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

guest

回答1

0

ベストアンサー

問題点: 同じFrameに対して配置している為

ページ遷移の実装方法は主に2通りあり、

tkraise() を使う方法では、問題毎にFrameを作り
同じ位置に重ねて配置、tkraiseで上に来る Frame を切り替える
という手法を取ります。

同じ Frame に対して配置する場合は、
一旦前の内容を全て破棄 (destroy) して、新規に作成します。
こちらの場合は tkraise() は不要です。


追記: 問題毎にFrame は作成してるようですが、create_question に渡すところで
frame_app_1 が固定になってます。

python

1 create_question(frame_app_1,ans,frame,Out_Integ)

追記2: コメントで説明した実装例

レイアウト等は省き、ページ遷移を確かめる最小限のコードです。

python

1 2 3import tkinter as tk 4from tkinter import ttk 5 6def frame_grid_config(frame): 7 frame.grid_rowconfigure(0, weight=1) 8 frame.grid_columnconfigure(0, weight=1) 9 10def frame_create(parent): 11 # NOTE: 親ウィジェットを指定できるように変更 12 frame = ttk.Frame(parent) 13 frame.grid(row=0, column=0, sticky=tk.NSEW, pady=20) 14 return frame 15 16root = tk.Tk() 17root.geometry("600x600") 18frame_home = frame_create(root) 19frame_app_1 = frame_create(root) 20frame_app_2 = frame_create(root) 21frame_app_3 = frame_create(root) 22 23frame_grid_config(root) 24frame_grid_config(frame_app_1) 25frame_grid_config(frame_app_2) 26frame_grid_config(frame_app_3) 27 28def create_app_1(parent): 29 frames = [] 30 31 def back_home(): 32 # NOTE: 戻る時に、最初の問題を一番上に戻しておく 33 frames[0].tkraise() 34 frame_home.tkraise() 35 36 for i, _ in enumerate(range(5), start=1): 37 frame = frame_create(parent) 38 frames.append(frame) 39 label = ttk.Label(frame, text=f"問題{i}") 40 label.pack() 41 42 home_button = ttk.Button(frame, text="Home", 43 command=back_home) 44 home_button.pack() 45 46 for frame, next_frame in zip(frames, frames[1:]): 47 button = ttk.Button(frame, text="次の問題へ", 48 command=next_frame.tkraise) 49 button.pack() 50 else: 51 # 最後の問題 52 frame = frames[-1] 53 54 # 最初の問題を一番上へ 55 frames[0].tkraise() 56 57 58def create_home(parent): 59 button1 = ttk.Button(parent, text="1", 60 command=frame_app_1.tkraise) 61 button2 = ttk.Button(parent, text="2", 62 command=frame_app_2.tkraise) 63 button3 = ttk.Button(parent, text="3", 64 command=frame_app_3.tkraise) 65 66 button1.pack() 67 button2.pack() 68 button3.pack() 69 70 parent.tkraise() 71 72create_app_1(frame_app_1) 73create_home(frame_home) 74 75root.after_idle(frame_home.tkraise) 76root.mainloop()

次の問題へのボタンは、複数のリストを組み合わせる zip 関数を使い
「現在の問題」「次の問題」のループを作成しています。

python

1# zip の例 2zip([1,2,3], [4,5,6]) # => [(1,4), (2,5), (3,6)] 3 4xs = [1,2,3,4] 5 6zip(xs, xs[1:]) # => [(1,2), (2,3), (3,4)]

投稿2021/09/27 03:47

編集2021/09/30 23:27
teamikl

総合スコア8760

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

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

taki.muramatsu

2021/09/27 05:05

create_question()でframe_app_1を渡さないと最初で表示されないと思うんですがその場合 どうしたら良いですか? ループ内で作ったframeを渡すと最初の"問題へ"で表示されません
taki.muramatsu

2021/09/27 06:01

while i <= 10: i += 1 j += 1 Question_No = random.randint(1,3) Val_No_1 = random.randint(1,9) Val_No_2 = random.randint(1,9) Val_No_3 = random.randint(1,9) ans,Out_Integ = create_question_1(Question_No,Val_No_1,Val_No_2,Val_No_3) Frame_Name = "frame_app_"+str(i) Frame_Name_1 = "frame_"+str(j) Frame_Name = frame_create() Frame_Name_1 = frame_create() create_question(Frame_Name,ans,frame,Out_Integ) としたのですが最初の問題へのボタンで移動する処理のところでframe_app_1を定義してるからだと思うのですが、どうしたほうが良いですか
teamikl

2021/09/27 07:42 編集

前提に齟齬がありそうなので、確認ですが、 「難易度」と「問題」があり、最初の画面で難易度を選ぶと問題が表示され、 現状では10問一度に表示されているのを、 1問ずつ順番に表示するといった形式を想定でしょうか? 「難易度」選択の方のページ遷移は既に実装されているようなので、 それと同じ仕組みを「問題」でも実装することになります。 手順としては、 - 問題毎にframeを生成して、その上に問題を配置。 - 「次の問題へ」ボタンでそのFrameのtkraise()を呼び出す。 と、することで対応できるはずです。 現状のコードでループ内で作られる Frame は未使用になってます。 ==== 他の問題点: ボタン作成時に change_window 関数が呼ばれてしまいます。 command = change_window(Frame_Name)) ループ内だと lambda を使う際は注意が必要なので、functools モジュールをimport して command = functools.partial(change_window, Frame_Name) ※但し、ここの 変数Frame_Nameは、有効なフレームではありません。別問題。 他の問題点2: Frame_Name = "frame_"+str(i) Frame_Name_1 = "frame_"+str(j) Frame_Name = frame_create() Frame_Name_1 = frame_create() は、変数を上書きしている為、想定通りの挙動になってないはずです。確認して見て下さい。
taki.muramatsu

2021/09/28 13:56

なるほど ありがとうございます!!!
taki.muramatsu

2021/09/29 12:32

while i <= 10: i += 1 j += 1 Question_No = random.randint(1,3) Val_No_1 = random.randint(1,9) Val_No_2 = random.randint(1,9) Val_No_3 = random.randint(1,9) ans,Out_Integ = create_question_1(Question_No,Val_No_1,Val_No_2,Val_No_3) Frame_Name = "frame_app_"+str(i) Frame_Name_1 = "frame_app_1"+str(j) Frame = frame_create() Frame_1 = frame_create() Frame_Name = Frame Frame_Name_1 = Frame_1 create_question(Frame_Name_1,ans,frame,Out_Integ) button_question = button_create(Frame_Name_1,"次へ",change_window,Frame_Name) button_question.pack()
taki.muramatsu

2021/09/30 02:03 編集

各フレームに一問ずつ表示されボタン操作で次のフレームに変わり問題を表示するものを作りたいです ループ内でcreate_questionの第一引数をframe_app_1にしないと最初の難易度選択後に何も表示されない画面が出てきてしまうのでframe_app_1にすると全部の問題が出てきてうまくいかないです ループ内で最初の難易度選択後と同じ動きで、frameとボタンを別で作ってそのボタンでchange_window(Frame_Name)にしてみましたがうまくいきません command = functools.partial(change_window, Frame_Name)の使い方と使うタイミングがわかりません 教えていただけるとありがたいです ループ内で難易度選択ボタンを作ろうかと思いましたがそれだとただ最初の画面に難易度選択ボタンがでくるだけだと思いやめました create_questionの処理の中に次の問題へ変わるボタンを作成しループ内で作成したFrame_Nameを渡しましたがそれもうまくいきませんでした ボタンを押しても空白の画面が出るだけでした
teamikl

2021/09/30 04:05 編集

現在実行してるコードを質問内に編集で反映できますか。 >frame_app_1にすると全部の問題が出てきてうまくいかないです は、フレームの親子関係が整理できてないように見受けられます。 設計・構造的な問題なので、 現状のコードの一部を修正してというのは難しいです。 複数の問題があるので、整理して一つづつ解消していきましょう。 >ループ内で難易度選択ボタンを作ろうかと思いましたがそれだとただ最初の画面に難易度選択ボタンがでくるだけだと思いやめました 「難易度」選択のページ遷移は出来てるので、 それと同じことを「質問」にたいして同じような実装とするのが、順当な解決策です。 「ループ内で」というのが問題を難しくしてる要因だと思います。 例えば、ループ中には「次の問題」のフレームはまだ作成されてません。 「次の問題」ボタンをクリックした時に フレームの tkraise を呼び出すので、 その「問題」は、「次の問題」をクリックした時に呼び出されるフレーム上に作成しなければなりません ↑ ここが出来てなくて、すべて同じフレーム上に作成してるのが原因。 ==== 次の問題へでchange_windowが呼び出されるタイミング >command = functools.partial(change_window, Frame_Name)の使い方と使うタイミングがわかりません >> button = ttk.Button(frame_app_1,text="次の問題へ",command = change_window(Frame_Name)) では、次の問題「Buttonが作成されるタイミングで」change_windowが呼び出されてます。 上の方では lambda を利用してますが、ループ内での lambda の利用は意図しない挙動となるため 代替として functools.partial を使う方法を提案します。 この問題とは別に Frame_Name の使い方の問題もあるので、 この部分を修正したからと言って、すべての問題が解消するわけではありません。 change_window では Frame_Name の tkraise を呼び出すので、 「次の問題」は Frame_Name 上に作成しなければなりません。
taki.muramatsu

2021/09/30 04:40

現在のループ内を 追記しました 一度確認をお願いします
teamikl

2021/09/30 22:42

functools.partial を使う場所に関しては、合ってます。 問題点はループ内で扱う2つのフレームで 1巡目: 問題1を生成 2巡目: 問題2を生成 3巡目: 問題3を生成 ... という処理だとすると、現在のコードでは 1巡目で問題1を生成した時に、まだ生成されていない問題2のフレームを参照しなければなりません。 > create_question()でframe_app_1を渡さないと最初で表示されないと思うんですがその場合 どうしたら良いですか? > ループ内で作ったframeを渡すと最初の"問題へ"で表示されません この疑問が解消されてないのだと思いますが、ここが質問されている現象の原因なので、 ** frame_app_1 上に直接ではなく**、問題毎にフレームを用意して、その上に生成します。 root  frame_app_1   question1 フレームを作りその上に問題を生成   question2 ...   question3 ... 懸念されている点の解消方法は、各フレームの親を frame_app_1 として、 tkraise() を用いて最初の問題のフレームを一番上にしておくことで解消できるはずです。 ※ ここは、コードで説明した方が解りやすいと思うので、回答に追記します。 - 次の問題へボタンもframe_app_1 上ではなく、各問題のフレーム上に生成します。 create_question(frame_app_1 ,frame,ans,Out_Integ) ← 上述したように、frame_app_1 ではなく、問題毎にframeを作る create_question(question_Frame ,frame,ans,Out_Integ) ~~ command=functools.partial(change_window,question_Frame) question_Frame は、 恐らく次の問題を生成したいのだと思いますが、これでは2重に生成されることになるので 少し工夫が必要になります。解りやすくするために、ループを2つに分けてみてはどうでしょう ループ1: 問題を予め生成しておく。フレームをリストへ追加。 ループ2: 各問題に対して「次の問題へ」ボタンを生成する。
teamikl

2021/09/30 22:53

追記: 出来れば、ループ内だけではなくコードは実行可能な形で掲載をお願いします。 問題点を解りやすくする為、numpy, sympy, pandas matplotlib.pyplot 辺りは、今回の問題には全く関係ない部分なので省いて、 tkinter のみで、ページ遷移の部分だけで問題を再現できる形にして見て下さい。
taki.muramatsu

2021/10/04 00:09

無事に思い通りの動きになりましたありがとうございました。 が、else: # 最後の問題 frame = frames[-1 ここの部分の動きがイマイチよくわからなかったので詳しく教えて欲しいです。
teamikl

2021/10/04 02:27

> ここの部分の動きがイマイチよくわからなかったので詳しく教えて欲しいです。 for/else の else 節は、ループがbreak等で中断されなかった場合、最後に実行されます。 frameを扱うコードのインデントを揃えたかったのと、 問題のフレームに対する一連のループ処理という意図で、for文のelse節に記述しました。 挙動自体は、この場合は for 文が終わった後に書いても変わりありません。 1巡目: 次の問題へ ボタンで2問目を表示する 2巡目: 次の問題へ ボタンで3問目を表示する ... と続き、最後の問題には next_frameにあたる「次の問題」がないので、frame のみ定義してます。 用途として想定しているのは、 最後の問題の画面に何か表示させたい場合、例えば回答の内容を何処かに送る等がある場合、 次の問題ではなく最後の画面へ移る様にします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問