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

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

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

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

ラジオボタン

ラジオボタンはフォームに使われる要素のひとつであり、ユーザに限られた選択肢からひとつの答えを選んでもらうというものです。

Python

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

Q&A

解決済

1回答

454閲覧

Python, Tkinter, ラジオボタンを選択した後の返し値が取得できない。

ykasaf7

総合スコア10

Tkinter

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

ラジオボタン

ラジオボタンはフォームに使われる要素のひとつであり、ユーザに限られた選択肢からひとつの答えを選んでもらうというものです。

Python

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

1グッド

1クリップ

投稿2024/07/12 08:26

編集2024/07/18 05:28

実現したいこと

メッセージボックス内のラジオボタンからどれか1つを選択して、「OK」ボタンを押すとラジオボタンに設定した値を返し値として取得したい。

発生している問題・分からないこと

ラジオボタンを設定したメッセージボックスは要求通りに表示されますが、
ラジオボタンはどれか1つしか選択できないと理解しているのですが、
1番上以外の項目が選択されています。

現状、表示しているメッセージボックスの画面shotを共有します。
イメージ説明

表示されたメッセージボックスから1つラジオボタンをクリックすると、クリックしていないラジオボタンはOFFになりますが、OKボタンを押した後の返し値はありません。
printコマンドで返し値を調べましたが、Noneや""空欄などの応答もありません。

該当のソースコード

import tkinter as tk from tkinter import messagebox as Msgbox from tkinter import simpledialog def CautionMessage(): #プログラム実行前のメッセージ a = "プログラムを実行します。" msg_front() res = Msgbox.askyesno('確認',a) if res == False: msg_front() sys.exit() #messageboxを最前面に表示 def msg_front(): root = tk.Tk() root.attributes('-topmost', True) root.withdraw() root.lift() root.focus_force() def Secret(): global answer item = ["1","2","3","4","5","6"] #root = tk.Tk() root = tk.Toplevel() #root = tk.Tk()から変更 root.geometry("300x200") root.title("選択画面") val = tk.IntVar() #ラジオボタン機構、itemのどれか1つしか選択できない。 for i in range(len(item)): tk.Radiobutton(root, text = item[i], value = i, variable = val).pack(anchor = tk.W) def choice(): global answer ch = val.get() answer = ch #print(item[ch]) root.destroy() root.quit() button = tk.Button(root, text = "OK", command = choice).pack() root.mainloop() return answer CautionMessage() print(Selection())

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

ネット上のコードをそのまま試しましたが、返し値を取得できませんでした。
(2024.07.18変更)
前回はラジオボタンに関する部分のみを抜粋しましたが、
前回の抜粋部分は正常に動作していることを確認できました。
他のプログラムが干渉しているみたいですので、再確認した結果
上記コードだとラジオボタンが正常に動作できませんでした。
(2024.07.18)
「root.quit()」をコメント通りの位置に追加しました。
Selection関数の返し値がありましたが、全て「0」が返ってきます。
また、ラジオボタンの機能も正常に動作していませんでした。

補足

tatsu99👍を押しています

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

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

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

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

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

TakaiY

2024/07/12 09:32

コードをコピペして動かしてみましたが、変数Resには選択したボタンのインデックスの値が入っていました。 質問にある「返し値を取得できませんでした。」というのはどのような状況ですか?
ykasaf7

2024/07/13 11:05

print("answer : ", res)を実行しましたが、resの中身だけではなく、その前のanswerの文字列も表示されません。 関数化せずに実行すると、ラジオボタンが正常などれかひとつしか選択できない機能になるのですが、関数化すると質問のような内容になってしまいます。
TakaiY

2024/07/13 11:55

「実行」「関数化せずに実行」はそれぞれどのように実行しているのでしょう?
hiroki-o

2024/07/13 13:53

>ネット上のコードをそのまま試しましたが URLを教えてください。 ・Windows 11、Python 3.12.4 ・提示されたソースの前にimport tkinter as tk、後ろにprint(Res)を足しただけ で確認しましたが、 ・ラジオボタンは1個のみ選択 ・Resは、TakaiYさんのコメント通り、選択したラジオボタンのインデックス でした。
ykasaf7

2024/07/18 01:32

返答が遅くなりすみません。ラジオボタンの動作がおかしいので、ラジオボタンに関するプログラムのみを公開しましたが、公開したプログラムは正常に動作していることを確認できました。他のプログラムが干渉しているみたいです。引き続き確認した結果、以下のコートで挙動がどうしても要求通りに動作できません。 CautionMessage関数は正しくメッセージを表示しますが、 その後に実行するSelection関数が正しく動作できません。 ====================================================== import tkinter as tk from tkinter import messagebox as Msgbox from tkinter import simpledialog def CautionMessage(): #プログラム実行前のメッセージ s0 = "\n" a11 = "プログラムを実行します。" + s0 a = a11 msg_front() res = Msgbox.askyesno('確認',a) if res == False: msg_front() sys.exit() def msg_front(): root = tk.Tk() root.attributes('-topmost', True) root.withdraw() root.lift() root.focus_force() def Selection(): global answer item = ["1","2","3","4","5","6"] root = tk.Tk() root.geometry("300x200") root.title("選択画面") val = tk.IntVar() #ラジオボタン機構、itemのどれか1つしか選択できない。 for i in range(len(item)): tk.Radiobutton(root, text = item[i], value = i, variable = val).pack(anchor = tk.W) def choice(): global answer ch = val.get() answer = ch #print(item[ch]) root.destroy() button = tk.Button(root, text = "OK", command = choice).pack() #msg_front() root.mainloop() return answer CautionMessage() print(Selection())
ykasaf7

2024/07/18 01:39

import tkinter as tk from tkinter import messagebox as Msgbox from tkinter import simpledialog def CautionMessage(): #プログラム実行前のメッセージ a = "プログラムを実行します。" msg_front() res = Msgbox.askyesno('確認',a) if res == False: msg_front() sys.exit() #メッセージボックスを最前面に表示する def msg_front(): root = tk.Tk() root.attributes('-topmost', True) root.withdraw() root.lift() root.focus_force() def Selection(): global answer item = ["1","2","3","4","5","6"] root = tk.Tk() root.geometry("300x200") root.title("選択画面") val = tk.IntVar() #ラジオボタン機構、itemのどれか1つしか選択できない for i in range(len(item)): tk.Radiobutton(root, text = item[i], value = i, variable = val).pack(anchor = tk.W) def choice(): global answer ch = val.get() answer = ch root.destroy() button = tk.Button(root, text = "OK", command = choice).pack() root.mainloop() return answer CautionMessage() print(Selection())
melian

2024/07/18 01:41

コメント欄にコードを載せるとインデントが判らなくなってしまいますので、質問文を編集してください。
ykasaf7

2024/07/18 01:47

melianさん コメントありがとうございます。質問文を編集しました。 確認頂けると幸いです。
melian

2024/07/18 03:59 編集

最小限の変更で済ませるのであれば以下の様にすると良いかと思います。 def Selection():  global answer  item = ["1","2","3","4","5","6"]  # root = tk.Tk()  root = tk.Toplevel() # top level window に変更 それから、root.destroy() の後に root.quit() が必要かと思います。 def choice():  global answer  ch = val.get()  answer = ch  #print(item[ch])  root.destroy()  root.quit() # 追加
ykasaf7

2024/07/18 04:22

コメントありがとうございます。root.quit()の追加を試しましたがまだ解決できていません。 結論から言いますと、Selection関数の返し値が取得できるようになりましたが、 ラジオボタンが正常に動作しないため、返し値が全て「0」になります。
melian

2024/07/18 04:24

tk.Tk() から tk.Toplevel() への変更は試しましたでしょうか?
ykasaf7

2024/07/18 05:30

melianさん 「tk.Tk() から tk.Toplevel()」の部分を失念しており、 再確認した結果、正常な動作を確認できました。コメントありがとうございます。
guest

回答1

0

ベストアンサー

予想だけど、IntVar の生存期間の問題かな?

IntVar を格納する変数が 関数スコープ だと、関数を抜けた後にGCにより val は破棄され、
ラジオボタンが複数選択される等、挙動がおかしくなる事があります。

暫定的な回避策: IntVar インスタンスを格納する変数 val を global 変数にしておく。
⇒ global スコープに置く、global 宣言する、等。


追記2: ラジオボタンが複数選択される挙動についての再現コード例
(戻り値の問題とは別)

python

1import tkinter as tk 2 3CHOICES = ["A", "B", "C", "D", "E"] 4root = tk.Tk() 5 6def create_widgets(parent, items): 7 # XXX: 8 # global val 9 val = tk.IntVar() 10 11 for idx, text in enumerate(items): 12 radio = tk.Radiobutton(parent, text=text, variable=val, value=idx) 13 radio.pack() 14 15create_widgets(root, CHOICES) 16 17root.mainloop()

追記: 質問に現在提示されているコードでは、冒頭にimport tkinter as tk を挿入し実行したところ、
実行は正常にできて、answer は正常に戻し値になっていました。齟齬があるとすれば、

  • answer = ch ⇒ もしかして anwer = item[ch] ch だと文字列ではなく、数値。
  • コメントに掛かれているコード print("answer : ", res) 質問に提示のコードでは Res = Selection() Rが大文字

追加分のコードに関する回答追記

問題点は、複数の tkinter.Tk を生成している点 & インスタンス生成時に親を省略している点

説明補足すると

  • tkinter.Tk() は、イベントループと メインのウィンドウ(Toplevel) を持つ。
  • tkinter.Toplevel() は、ウィンドウのみ。
  • ウィジェットやIntVar 等のインスタンスを作成する際に、
    引数を省略すると、デフォルトの root (一番最初に生成した tkinter.Tk) が親となる。

ラジオボタンは2つ目の root で使われているが、intVar は最初の root の所属となっているため
(原因は関数スコープとGC ではありませんでいたが)
無効な IntVar を指定してる点が、ラジオボタンのおかしな挙動の原因です。

暫定的な回避策は、IntVar の親の明示的な指定

val = tk.IntVar(root)

後々の事を考えると、
「イベントループ」が複数という状況自体を避けた方が良いので、
プログラムの構造自体を root = tk.Tk() は一度で済むような構成を割と強くお勧めします。
分量的に回答で示すことは難しいですが、方針として tkinter でのダイアログの作成方法等を調べてみてください。

投稿2024/07/17 08:46

編集2024/07/18 05:22
teamikl

総合スコア8681

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

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

ykasaf7

2024/07/18 01:55

多数の検討をありがとうございます。 前回のコードは正常に動作していることを確認しました。 「ラジオボタンの挙動がおかしい」ということだけで視野が狭まってしまい、 前回の抜粋したコードでは不足していました。 質問に影響していると思われるコードを追加しましたので、 お手数をおかけしますが、確認して頂けると幸いです。
TakaiY

2024/07/18 02:31

横から失礼します。 追加されたコードを見ましたが、この回答で指摘されているとおり、「IntVar を格納する変数が 関数スコープ だと、関数を抜けた後にGCにより val は破棄され、ラジオボタンが複数選択される等、挙動がおかしくなる」という現象です。 直すとしたら小手先でなく、プログラムの構造を見直したほうがいいと思います。 tkinterのルートウィンドウは、メインルーチンで保持するところあたりから。
ykasaf7

2024/07/18 04:19

TakaiYさん 理解が足りていなくて申し訳ないのですが、 最後の2行を入れ替えて、「print(Selection())」→「CautionMessage()」とすると、 ラジオボタンが正常に動作できるのですが、 >IntVar を格納する変数が 関数スコープ だと、関数を抜けた後にGCにより val は破棄され、・・・ の部分は解決されていると見て良いのでしょうか。
teamikl

2024/07/18 04:35 編集

質問に掲載されていたコード(※ コメントにある追加分に関しては未確認)では、 IntVar の生存期間の問題は解決されてると思いますよ。 関数内で mainloop が実装されているため。そして、返り値も正常に返ってます。 この回答は、スクリーンショットにあるような ラジオボタンのおかしな挙動に関しての説明です。
teamikl

2024/07/18 05:03 編集

追加分のコードを確認。 現在掲載中のコードからの(暫定的な)回避策は、 val = tk.IntVar(root) と、IntVar の所属する root を指定することで、問題の現象の回避は出来るはずです。 (問題自体は回答で説明したとおりですが、原因箇所は関数スコープではなく、  後から追加された分のコード、Tk() を複数回使い&IntVar を親指定なしで生成した点) Tk() のインスタンスを複数作っていると問題を引き起こしやすくなります。 間違ったコードではないが、コードを書く上で注意点が多くなる為、出来れば避けた方が良いです。 複数の Tk() を生成する場合、IntVar 等を生成する際に引数を省略すると、 デフォルトの root (一番最初に作った Tk()) に割り当てられます。 IntVar が使われているのは、2つめの root なので、その辺りが挙動がおかしくなった原因。 推奨する解決策 Tkinter では、tkinter.Tk (イベントループとメインウィンドウ) を受け持つので、 root = tk.Tk() はプログラムの中で最初の一回のみとして、 複数のウィンドウが必要な場合は、以降のウィンドウの生成には tkinter.Toplevel を用いるような構成をお勧めします。
ykasaf7

2024/07/18 05:25

teamiklさん 回答ありがとうございます。 提案頂きました解決策通りに試したところ問題が解決しました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問