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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

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

Q&A

解決済

1回答

2236閲覧

Python3 Tkinter ボタンについて

person

総合スコア223

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

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

0グッド

0クリップ

投稿2020/08/08 08:58

tkinter.Button()ttk.Button()について

それぞれでボタンを押したときに、押したままの状態にしたいです。
(ボタンの外観が押したままであればそれでいいです。)

Python 入門 TkinterによるGUIプログラミング

上記のサイトでは ttk.Button()command を使ったやり方で紹介されていますが、tkinter.Button()bind() で実現することは可能ですか?

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

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

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

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

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

guest

回答1

0

ベストアンサー

tkinter.Button() や bind() で実現することは可能ですか?

How to make button in Python Tkinter stay pressed until another one is pressedを参考にすると以下のように書けます。
ただし最初に押したときだけ状態が遷移しないようです。

Python

1import tkinter as tk 2 3def on_button(e): 4 relief = btn['relief'] 5 if relief == tk.RAISED: 6 relief = tk.SUNKEN 7 else: 8 relief = tk.RAISED 9 btn.config(relief=relief) 10 11root = tk.Tk() 12btn = tk.Button(root, text='push') 13btn.bind('<ButtonPress>', on_button) 14btn.pack() 15root.mainloop()

投稿2020/08/08 09:39

can110

総合スコア38266

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

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

person

2020/08/09 05:00

tkでもcommandだとできました。 bindでは正常に(少なくともWindowsでは)動作させることは無理なんでしょうかね・・・。 # commandで実装(tk.Button()) import tkinter as tk def b1_pushed(): global b1 if b1["relief"] == "raised": b1["relief"] = "sunken" else: b1["relief"] = "raised" win = tk.Tk() b1 = tk.Button(win, text="b1", width=10, command=b1_pushed) b1.grid() win.mainloop()
can110

2020/08/09 05:06

> tkでもcommandだとできました。 こちらのWindows環境でもできました。 なぜbindでは初回うまくいかないのか理由が分かりませんが。
teamikl

2020/08/09 06:51 編集

bindの場合は、b1_pushed() の最後で return "break" すると、初回も期待通りに動くはずです。 「初回のみ」の理由をうまく説明できませんが…。なにかデフォルトのイベントの影響でしょうか、 relief を変更した後、b1.update(); time.sleep(5) と意図的に止めてみると 枠が変更されてるのは確認できます。その後に sleep が切れた後、通常の枠に戻ります。 あまり気にしたことが無かったけど、bind と command では呼ばれるタイミングが違うようですね。 command に登録した関数は、ボタンを押した後・枠線を変更した後に呼ばれるようです。
can110

2020/08/09 10:31

コメントありがとうございます。 tkinterのイベントモデルは全く分からないのですが、bindとcommandで挙動が異なるようですね。 勉強になりました。
person

2020/08/09 13:18

自分のほうでも確認しました。 バインドが<Button-1>のときにreturn "break"つけたら動作しますね。 (ちなみにバインドを<ButtonRelease>にしたらダメなようです) 自分の中でちょっとわからないのはreturn "break"の意味なのですが、これって 単純に "break"という文字列を戻り値にするのとは異なるのでしょうか?
teamikl

2020/08/09 18:22 編集

"break" という文字列を戻り値にするのは、tkinter 側の独自の仕様です。 tkinter <=> tcl/tk のやり取りはTCL言語のコード(Python側では文字列)で行っているのですが、 関数を bind() するときに、その関数の戻り値が "break" という文字列であれば、 tcl/tk 側で break コマンド (TCL言語ではコマンドと呼びます) を呼ぶコードが追加されています。 そして、tcl/tk 側では、break が呼ばれるとイベントの伝搬がそこでキャンセルされるようです。 https://docs.python.org/ja/3/library/tkinter.html ソースコード tkinter/__init__.py のリンク → "def bind" を検索 該当箇所は、 cmd = ('%sif {"[%s %s]" == "break"} break\n' TCL言語では [関数 引数...] == 戻り値との比較、となってます。 ==== 参考 因みに、ドキュメントの「バインドとイベント」の項目に "break" の事は、記載されてませんでした。 https://docs.python.org/ja/3/library/tkinter.html#bindings-and-events Tcl/Tk のマニュアル MULTIPLE MATCHES の項目に記載有り (英語) https://www.tcl.tk/man/tcl8.6/TkCmd/bind.htm button.tcl ボタン クラスの実装 ButtonUp/ButtonDown (TCL言語) https://core.tcl-lang.org/tk/artifact/74d573e9b383577d ButtonUp を見ると、relief を設定してからコマンド呼び出しされるのが見て取れます。 ---- 解決策ではありませんが、Python 側で実験。クラスのイベント解除 デフォルトのイベントを解除すると、枠の変更がなくなるので return "break" なしでも初回のみ枠が変わらない挙動は発生しない。 b1.unbind_class("Button", "<Button-1>") ※全てのボタンに影響がある為、これは解決策ではありません。 ボタンのデフォルトのイベントが影響してるという事を確認するための実験です 以下のように ButtonRelease-1 イベントを解除すると、command が呼ばれなくなる。 b1.unbind_class("Button", "<ButtonRelease-1>")
teamikl

2020/08/10 04:30 編集

「初回のみ~」の原因がわかりました。 デフォルトの<Enter>イベントが鍵でした。 ボタンのデフォルトイベントの振る舞いですが 1. Enter ... ButtonEnter 以前の relief 情報を内部変数へ保存 2. (ここでbindしたユーザ定義の関数) 3. Button-1 ... ButtonDown config -relief sunken 4. ButtonRelease-1 ... ButtonUp config -relief 前のreliefに復帰 5. (commandの場合はここで呼ばれる) つまり、自分で relief を設定しても、その後 デフォルトのButtonDown/Up イベントにより再設定されているのが 初回のみ Enter で設定された値が適応されてしまった訳です。 2回目以降は ButtonDown がユーザ関数で設定した relief 情報を復帰用に保存します。 マウスカーソルをボタン上から一旦離して、 もう一度ボタンを押すと同じように「初回のみ」の現象が再発するはずです。 return "break" は bind() を用いる場合の回避策ですが、 relief を変更する場合は、command の方が適切な位置だと思います。 (可能なら URL で紹介されてるように ttk で stateを使った方が、 より良いのですが)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問