mainloopは一度しか使ってはいけないようなことも見たのですが、このコードは大丈夫なのでしょうか。
エラーなしに正常実行できる、という意味では大丈夫の範疇ですが、
コードの改変等、保守する際に なぜ mainloop が複数あるコードが問題になりやすいか
を把握してないと以降の保守が大変になる傾向があります。(実際に、QAサイトで質問が多い)
→ 事例: 余計な部分のデバッグに時間が掛かり、
本来追加したかった機能がなかなか実装出来ない、等。
質問のコードでは、
root1 = tk.Tk()
root1.mainloop()
# この時点で上の mainloop は終了している
root2 = tk.Tk()
root2.mainloop()
という構造になってそれぞれが独立しているため、root が複数ある状況にはなってません。
→ 複数のrootを同時に扱うと、別の問題があります。(tkinter ライブラリ利用の作法)
ただし、mainloopを抜けて再度 tk.Tk() を作るという事は、
ウィンドウ作成の度に tkinter ライブラリ全体が終了・初期化されるという事を留意しておいてください。
効率化の為には、初期化は一度のみにして、ウィンドウが複数必要であれば Toplevel を用いるように実装します。
追記: 問題の原因
恐らく2つめのウィンドウで user.entry.get()
みたいな事をして、
以前入力された値を参照したい場合、
mainloop() が終了しているので、以前の UI 内の値を参照出来ないというのが、
mainloopが複数ある場合の気を付けなければならない点。
mainloop 終了前に値を別の場所の保存しておくのは解決策の一つですが、
ウィンドウ毎にmainloopを終了せずに、初期化・終了は1度で済ませる方法もあります。
お勧めできる解決策は、入力値を返すダイアログを使った実装方法です。
python
1from tkinter.simpledialog import askstring
2from tkinter.messagebox import showinfo
3
4if name := askstring("名前入力", "あなたの名前を入力してください"):
5 showinfo("test", f"Hello, {name}")
対話環境での入力みたいなコードは、
GUIで主に扱うイベント駆動プログラミングでは記述が難かったりします。
(出来ないわけではないけど、非同期処理や双方向ジェネレーター等を扱う話題)
対話環境のように 入力・出力を扱うには、ダイアログ・ベースにするのが簡単。
質問のコードをカスタムダイアログにした例
python
1import tkinter as tk
2from tkinter.simpledialog import _QueryDialog, Dialog, askinteger
3from dataclasses import dataclass
4
5import re
6
7def is_valid_email(email):
8 return bool(re.fullmatch(r"[^@]+@[^@]+\.[^@]+", email))
9
10@dataclass
11class UserInput: # 入力された値を纏める入れ物。辞書でもいい
12 name: str = ""
13 age: int = -1
14 email: str = ""
15
16# 質問のコードをカスタムダイアログに
17class _QueryMyStringDialog(_QueryDialog):
18 def body(self, master):
19 label1 = tk.Label(master, text=self.prompt)
20 label1.pack()
21 label2 = tk.Label(master, text="")
22 label2.pack()
23 entry = self.entry = tk.Entry(master)
24 entry.pack()
25 self.geometry("300x150")
26
27 def getresult(self):
28 return self.entry.get()
29
30
31# 入力検査のカスタマイズ例
32class _QueryEmailDialog(_QueryMyStringDialog):
33 errormessage = "無効なメールアドレス"
34
35 def getresult(self):
36 email = super().getresult()
37
38 if not is_valid_email(email):
39 raise ValueError()
40
41 return email
42
43
44def ask_string(title="", prompt=""):
45 d = _QueryMyStringDialog(title, prompt)
46 return d.result
47
48def ask_email(title="", prompt=""):
49 d = _QueryEmailDialog(title, prompt)
50 return d.result
51
52
53class OutputDialog(Dialog):
54 def __init__(self, parent, user):
55 self.user = user ## NOTE: body が __init__内で呼び出される為、事前に設定
56 super().__init__(parent)
57
58 def body(self, master):
59 user = self.user
60 lbl=tk.Label(master ,text=f"Hello, {user.name} <{user.email}>")
61 lbl.pack()
62
63 def buttonbox(self): # ボタン部分のカスタマイズ例
64 box = tk.Frame(self)
65 button = tk.Button(box, text="OK", command=self.ok)
66 button.pack()
67 box.pack()
68
69
70def main():
71 user = UserInput()
72
73 user.name = ask_string("名前入力", "あなたの名前を入力してください")
74 user.age = askinteger("年齢", "年齢を入力してください")
75 user.email = ask_email("Email", "メールアドレスを入力してください")
76
77 OutputDialog(None, user)
78
79
80if __name__ == '__main__':
81 main()
参考までに、GUI でこういったステップ入力
名前入力、次の画面で EMailアドレス入力~~と、
ユーザの入力・選択によって進む UI は 「 Wizard 」と呼ばれていて
残念ながら tkinter には用意されてませんが、探せばサンプルコードはいくつか見つかるかも。
簡易的には ユーザ入力を求めるダイアログ関数で似たような実装が出来ます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2025/02/13 01:42