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

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

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

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

Tkinter

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

Python

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

Q&A

解決済

1回答

2673閲覧

かいけつ[Tkinter]Notebookのタブに実装した×ボタンのデザインをカーソルを合わせた時に変更したい

meoto2408

総合スコア52

Python 3.x

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

Tkinter

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

Python

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

1グッド

0クリップ

投稿2021/10/23 07:00

編集2021/10/24 07:01

タブに閉じるボタンを実装する方法が思いつかず調べているとStackflowでこのようなコードを見つけました。[参考元]

py

1import tkinter as tk 2from tkinter import ttk 3 4class CustomNotebook(ttk.Notebook): 5 """A ttk Notebook with close buttons on each tab""" 6 7 __initialized = False 8 9 def __init__(self, *args, **kwargs): 10 if not self.__initialized: 11 self.__initialize_custom_style() 12 self.__inititialized = True 13 14 kwargs["style"] = "CustomNotebook" 15 ttk.Notebook.__init__(self, *args, **kwargs) 16 17 self._active = None 18 19 self.bind("<ButtonPress-1>", self.on_close_press, True) 20 self.bind("<ButtonRelease-1>", self.on_close_release) 21 22 def on_close_press(self, event): 23 """Called when the button is pressed over the close button""" 24 25 element = self.identify(event.x, event.y) 26 27 if "close" in element: 28 index = self.index("@%d,%d" % (event.x, event.y)) 29 self.state(['pressed']) 30 self._active = index 31 return "break" 32 33 def on_close_release(self, event): 34 """Called when the button is released""" 35 if not self.instate(['pressed']): 36 return 37 38 element = self.identify(event.x, event.y) 39 if "close" not in element: 40 # user moved the mouse off of the close button 41 return 42 43 index = self.index("@%d,%d" % (event.x, event.y)) 44 45 if self._active == index: 46 self.forget(index) 47 self.event_generate("<<NotebookTabClosed>>") 48 49 self.state(["!pressed"]) 50 self._active = None 51 52 def __initialize_custom_style(self): 53 style = ttk.Style() 54 self.images = ( 55 tk.PhotoImage("img_close", data=''' 56 R0lGODlhCAAIAMIBAAAAADs7O4+Pj9nZ2Ts7Ozs7Ozs7Ozs7OyH+EUNyZWF0ZWQg 57 d2l0aCBHSU1QACH5BAEKAAQALAAAAAAIAAgAAAMVGDBEA0qNJyGw7AmxmuaZhWEU 58 5kEJADs= 59 '''), 60 tk.PhotoImage("img_closeactive", data=''' 61 R0lGODlhCAAIAMIEAAAAAP/SAP/bNNnZ2cbGxsbGxsbGxsbGxiH5BAEKAAQALAAA 62 AAAIAAgAAAMVGDBEA0qNJyGw7AmxmuaZhWEU5kEJADs= 63 '''), 64 tk.PhotoImage("img_closepressed", data=''' 65 R0lGODlhCAAIAMIEAAAAAOUqKv9mZtnZ2Ts7Ozs7Ozs7Ozs7OyH+EUNyZWF0ZWQg 66 d2l0aCBHSU1QACH5BAEKAAQALAAAAAAIAAgAAAMVGDBEA0qNJyGw7AmxmuaZhWEU 67 5kEJADs= 68 ''') 69 ) 70 71 style.element_create("close", "image", "img_close", 72 ("active", "pressed", "!disabled", "img_closepressed"), 73 ("active", "!disabled", "img_closeactive"), border=8, sticky='') 74 style.layout("CustomNotebook", [("CustomNotebook.client", {"sticky": "nswe"})]) 75 style.layout("CustomNotebook.Tab", [ 76 ("CustomNotebook.tab", { 77 "sticky": "nswe", 78 "children": [ 79 ("CustomNotebook.padding", { 80 "side": "top", 81 "sticky": "nswe", 82 "children": [ 83 ("CustomNotebook.focus", { 84 "side": "top", 85 "sticky": "nswe", 86 "children": [ 87 ("CustomNotebook.label", {"side": "left", "sticky": ''}), 88 ("CustomNotebook.close", {"side": "left", "sticky": ''}), 89 ] 90 }) 91 ] 92 }) 93 ] 94 }) 95 ]) 96 97if __name__ == "__main__": 98 root = tk.Tk() 99 100 notebook = CustomNotebook(width=200, height=200) 101 notebook.pack(side="top", fill="both", expand=True) 102 103 for color in ("red", "orange", "green", "blue", "violet"): 104 frame = tk.Frame(notebook, background=color) 105 notebook.add(frame, text=color) 106 107 root.mainloop()

import部分がそのままだと動かないためそこだけ編集しましたがそれ以外はそのままで動かしたところ×ボタンにカーソルを合わせたらデザインが変わるのではなく,タブにカーソルを合わしたらデザインが変わるようなものでした。

確かにCustomNotebook.focusの子要素にstyle.element_createで追加したclose"active", "!disabled", "img_closeactive"とNotebookのStateでデザインを変えているのは理解できてはいるのですが,×ボタンにカーソルを合わせたらデザインが変えるようにするためにはどのような方法があるのかがわかりませんでした。

そもそもStyleよりもthemeを使うべきなのかな?とか調べていて思ったりしたのですが何かヒントをいただけるとありがたいです。

追記 2021/10/24 01:14

実装したい細かいこと
0. カーソルを×ボタンに合わせると黄色い×になる。

  1. カーソルを×ボタンに合わせながらクリックすると赤いボタンになる。
  2. 通常時は黒い×である。

追記(解決コード)

一部変更点ありますが,以下のコードで実装出来ました!

py

1class CustomNotebook(ttk.Notebook): 2 """A ttk Notebook with close buttons on each tab""" 3 4 __initialized = False 5 6 def __init__(self, *args, **kwargs): 7 if not self.__initialized: 8 self.__initialize_custom_style() 9 self.__inititialized = True 10 11 kwargs["style"] = "CustomNotebook" 12 ttk.Notebook.__init__(self, *args, **kwargs) 13 14 self._active = None 15 16 self.bind("<ButtonPress-1>", self.on_close_press, True) 17 self.bind("<ButtonRelease-1>", self.on_close_release) 18 self.bind("<Motion>", self.on_close_motion) 19 20 def on_close_motion(self, event): 21 element = self.identify(event.x, event.y) 22 if "close" in element: 23 self.state(['!pressed']) 24 self.state(['hover']) 25 else: 26 self.state(['!pressed']) 27 self.state(['!hover']) 28 29 def on_close_press(self, event): 30 """Called when the button is pressed over the close button""" 31 32 element = self.identify(event.x, event.y) 33 34 if "close" in element: 35 index = self.index("@%d,%d" % (event.x, event.y)) 36 self.state(['pressed']) 37 self.state(['hover']) 38 self._active = index 39 return "break" 40 41 def on_close_release(self, event): 42 """Called when the button is released""" 43 if not self.instate(['pressed']) and not self.instate(['hover']): 44 print("bbb") 45 return 46 47 element = self.identify(event.x, event.y) 48 if "close" not in element: 49 # user moved the mouse off of the close button 50 self.state(['!pressed']) 51 self.state(['!hover']) 52 print("aaa") 53 return 54 55 index = self.index("@%d,%d" % (event.x, event.y)) 56 57 if self._active == index: 58 close_file(index) 59 60 self.state(["!pressed"]) 61 self.state(['!hover']) 62 self._active = None 63 64 def __initialize_custom_style(self): 65 img_close = image_file_to_base64(settings.resource_path("icon/tab/closeTabButton.gif")) 66 img_closehover = image_file_to_base64(settings.resource_path("icon/tab/closeTabButton_hover.gif")) 67 img_closepressed = image_file_to_base64(settings.resource_path("icon/tab/closeTabButton_push.gif")) 68 img_closeintact = image_file_to_base64(settings.resource_path("icon/tab/closeTabButton_inact.gif")) 69 70 style = ttk.Style() 71 self.images = ( 72 # 73 tk.PhotoImage("img_close", data=img_close), 74 tk.PhotoImage("img_closehover", data=img_closehover), 75 tk.PhotoImage("img_closepressed", data=img_closepressed), 76 tk.PhotoImage("img_closeintact", data=img_closeintact) 77 ) 78 79 style.element_create( 80 "close", "image", "img_close", 81 ("!active", "!selected", "img_closeintact"), 82 ("active", "!pressed", "hover", "!disabled", "img_closehover"), 83 ("active", "pressed", "hover", "!disabled", "img_closepressed"), 84 ("active", "!pressed", "!hover", "!disabled", "img_close"), 85 border=8, sticky='' 86 ) 87 style.layout("CustomNotebook", [("CustomNotebook.client", {"sticky": "nswe"})]) 88 style.layout("CustomNotebook.Tab", [ 89 ("CustomNotebook.tab", { 90 "sticky": "nswe", 91 "children": [ 92 ("CustomNotebook.padding", { 93 "side": "top", 94 "sticky": "nswe", 95 "children": [ 96 ("CustomNotebook.focus", { 97 "side": "top", 98 "sticky": "nswe", 99 "children": [ 100 ("CustomNotebook.label", {"side": "left", "sticky": ''}), 101 ("CustomNotebook.close", {"side": "left", "sticky": ''}), 102 ] 103 }) 104 ] 105 }) 106 ] 107 }) 108 ])
teamikl👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

×ボタンにカーソルを合わせたらデザインが変えるようにする

ButtonPress, ButtonRelease イベントを使っていますが、
カーソルが載っている間のイベントは Motion, Enter, Leave 辺りを用います。

実装方法は、既に実装されてる on_close_press を参考に、
xボタンにカーソルが当たっているかどうかは、
identifyメソッドでマウス座標にある要素で判別し、
state を切り替える事で対応した画像を表示します。

python

1 2 ...3 4 5 # self.bind("<ButtonPress-1>", self.on_close_press, True) 6 # self.bind("<ButtonRelease-1>", self.on_close_release) 7 self.bind("<Motion>", self.on_close_motion) 8 9 def on_close_motion(self, event): 10 element = self.identify(event.x, event.y) 11 12 if "close" in element: 13 self.state(['pressed']) 14 else: 15 self.state(['!pressed'])

追記: 2021/10/24 15:26

python

1 2# 変更箇所1: カーソルが載った時のイベントに <Motion> を追加 3 4 self.bind("<ButtonPress-1>", self.on_close_press, True) 5 self.bind("<ButtonRelease-1>", self.on_close_release) 6 self.bind("<Motion>", self.on_close_motion) 7 8 9# 変更箇所2: CustomNotebook クラスへ on_clsoe_motion メソッドを追加 10 11 def on_close_motion(self, event): 12 element = self.identify(event.x, event.y) 13 14 if "close" in element: 15 self.state(["hover", "!pressed"]) 16 else: 17 self.state(["!hover", "!pressed"]) 18 19 20# 変更箇所3: スタイル要素の変更 (※ Theme の影響を受けるかもしれません) 21 22 style.element_create("close", "image", "img_close", 23 ("active", "pressed", "!disabled", "img_closepressed"), 24 ("active", "focus", "hover", "!disabled", "img_closeactive"), 25 border=8, sticky='')

対応済みの問題:

  • カーソルを載せているタブと違うタブの画像が変わる。

 → style.element_create で登録の際に active を指定する、等。

  • 閉じるボタンを押したまま、タブ外に移動してでクリックを離すと

 Notebook に bind した ButtonRelease イベントは発生しません。
→ 状態が解除されず pressed 状態のままになるので、Motion イベントで !pressed を指定

未確認

  • 非アクティブのタブの閉じるボタンにカーソルを載せた時の挙動

 スタイル側で、「カーソルの乗った非アクティブのタブ」を識別する方法が、恐らくない。(未確認)

テストは十分に行ってないので、他にも対応漏れの挙動はあるかもしれません。

  • focus, hover, active, pressed 等の状態、
  • ButtonPress, ButtonRelease, Motion 等のイベント (今回は利用してませんが、Enter, Leave)

の組み合わせで、目的の挙動を組み立てることになります。

投稿2021/10/23 12:06

編集2021/10/24 06:38
teamikl

総合スコア8664

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

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

meoto2408

2021/10/23 16:26 編集

そのコードだとタブ自体にカーソルを合わせると黄色い×になって,×にカーソルを合わせると赤い×になります。 タブに合わせた時にも一切通常の状態と変化しないようにするのって可能なのでしょうか? 細かい実装したことも追記したので確認いただけると嬉しいです...
teamikl

2021/10/23 16:35 編集

>一切通常の状態と変化しないようにする 可能ですが、「通常の状態」の意図が 「カーソルが載っていない状態と同じ」なのか 「スタイルオプションを指定しない標準の挙動と状態と同じ」で異なります 変更の必要がある場合は、style.map メソッドで "CustomNotebook.Tab" の background の "active"色を変更します。 黄色い x については style.element_create の ("active", "!disabled", "img_closeactive"), の部分を省くことで可能です。
meoto2408

2021/10/24 03:57

「通常の状態」の意図は「カーソルが載っていない状態と同じ」ですね。 クリックしたときの挙動というのは難しいということであっていますか? イメージはNotePad++に近いのですが... https://i.gyazo.com/45523f62637555b2e64753353df67f73.mp4
teamikl

2021/10/24 04:28 編集

クリックした時の挙動も必要なのであれば 既に実装済みの、ButtonPress, ButtonRelease を用います。 画像の色までは把握してませんが、黄・赤が逆なのであれば Motion で変更する state を active, !active Press, Release で変更する state を pressed, !pressed とします
meoto2408

2021/10/24 04:42 編集

クリックされたときの挙動に関しては!pressedで判定しているため("active", "!disabled", "img_closeactive"), を削除すると使えなくなります。 かと言って元に戻せば×ボタン以外のタブの範囲にマウスカーソルを合わせると×ボタンの色が変わってしまうんですよね... 「×ボタン以外のタブの範囲にマウスカーソルを合わせた状態」と「どこにもカーソルを合わせていない時」の条件を一致させることができたら改善するのでしょうが...
teamikl

2021/10/24 04:55 編集

一度情報を整理した方が良さそうですね、 私の最初の回答はクリックではなく、 カーソルを載せた時に変更のみだと思っていったので、 - style.element_create は最初の質問のコードのまま  黄色・赤色の閉じるボタン画像をスタイル要素として登録 - マウス操作イベントで ButtonPress, ButtonRelease はクリック時  カーソルが載った時は Motion (必要に応じて Enter, Leave) - イベントハンドラの関数内で、close ボタン位置にあるかどうかは  identify メソッドで判別する。 - 閉じるボタンの画像変更は、必要に応じて  state メソッドで active, !active, pressed, !pressed に変更。 追加で、恐らく Theme により挙動が変わる部分ですが、 タブの閉じるボタン以外の部分にカーソルを載せた時、 タブの背景色も変わります。 標準の挙動ですが、「カーソルが載っていない状態と同じ」ではないので 対応が必要であれば、style.map で active 時の背景色を変更します。
teamikl

2021/10/24 05:54 編集

訂正: style.element_create は最初の質問のコードのまま → 挙動に応じた修正が必要でした カーソルが載った時のスタイルとしては、active, pressed の代わりに focus, hover といった状態を指定できます。 - create_element でデフォルトの挙動を登録し、 - ButtonPress, ButtonRelease, Motion 等のイベントでは  閉じるボタン以外の場合、デフォルトの状態を解除する といった流れになります。 さらに細かい点を挙げると、active, focus, hover の組み合わせにより、 タブがアクティブ時にカーソルが乗った状態 タブが非アクティブ時にカーソルが乗った状態 等も変わってくるようです。 問題になる例: 設定次第では Tab3 がアクティブ状態で、Tab4 の閉じるボタンにカーソルを乗せると Tab3 の閉じるボタンが変更されてしまう。
meoto2408

2021/10/24 06:46

> カーソルが載った時のスタイルとしては、active, pressed の代わりに focus, hover といった状態を指定できます。 なるほど pressedしかないから無理かなと思っていましたがhoverも使えるならどうにかできそうです 少し試行錯誤してみます。
meoto2408

2021/10/24 07:02

無事解決しました ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問