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

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

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

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

Python

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

Q&A

解決済

1回答

2750閲覧

TkInter: Treeviewの行選択されたノード(階層)をしりたい、または最上位階層は 選択させないようにしたい

saya24

総合スコア222

Tkinter

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

Python

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

1グッド

0クリップ

投稿2021/10/21 14:10

編集2021/10/22 06:26

当初有効化されていないボタンを ツリービューの行選択をうけて、有効化させることを達成しようとしています。

最上位(店舗一覧・取扱品目・納入実績)は 行選択できないことが望ましいのですが それは諦めていて....
ボタン有効化=行選択のコールバック関数側で 選択された階層を判断し 有効化の必要性を判断できたらな、と考えています。
ということで、以下が質問です。

① ツリービュで行選択されたノード=階層をしるには 如何したらよろしいですか?
② 行選択の反対操作として Shiftキーを押しながら ツリービュ内の選択済み行を選ぶと 行解放がなされます。この際、先のボタンを非活性にできたらと考えています。ツリービュのウィジェットにBindさせる行解放操作のキーがございましたらご教示ください。

以下コードは Pythonの環境さえあればどなたでも実行可能と思われます。
立ち上がったメニューでは上位の入出力定義のボタンを押下してください。

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

Python

1# tkinterのインポート 2import tkinter as tk 3import tkinter.ttk as ttk 4import math 5import os 6from tkinter import filedialog 7import tkinter.messagebox as tkmb 8import openpyxl 9################################################################################# 10# # 11# 一般関数 # 12# # 13# # 14################################################################################# 15# フレーム切替え=画面遷移 16def change_frame(frame): 17 frame.tkraise() 18 19 20# 入力ブック情報取得 21def get_bookinfo(dict_Book, tree): 22 23 for p in dict_Book: # キーを親要素に設定 24 parent = tree.insert("","end",text=p,) 25 First = True 26 for m in dict_Book[p]: # 要素を子要素に設定 27 if (First): 28 tree.insert(parent,"end",text="===固定値挿入===",) 29 First = False 30 child = tree.insert(parent,"end",text=m,) 31 32################################################################################# 33# # 34# 画面生成 # 35# # 36# # 37################################################################################# 38def resource_path(relative_path): 39 try: 40 # PyInstaller creates a temp folder and stores path in _MEIPASS 41 base_path = sys._MEIPASS 42 except Exception: 43 base_path = os.path.abspath(".") 44 45 return os.path.join(base_path, relative_path) 46 47 48 49def adjust_windowsize(root): 50 ww = root.winfo_screenwidth() 51 wh = root.winfo_screenheight() 52 53 lw = math.ceil(ww * 0.208) 54 lh = math.ceil(wh * 0.277) 55 56 root.geometry(str(lw)+"x"+str(lh)+"+"+str(int(ww/2-lw/2))+"+"+str(int(wh/2-lh/2))) 57################################################################################# 58# # 59# フレーム生成 # 60# # 61# # 62################################################################################# 63def generate_frame(root): 64 65 #★選択された階層次第でボタンの有効化実施を判断、現在無条件に有効化 #★ 66 def on_tree_select(event): 67 btn_AddColumn["state"] = "active" 68 69 ############################################################################# 70 # # 71 # STYLE # 72 # # 73 ############################################################################# 74 style = ttk.Style() 75 style.theme_use('winnative') 76 77 style.configure("Treeview", background="black", foreground="white", fieldbackground="black") 78 style.map("Treeview",background=[("selected", "silver")]) 79 style.map("Treeview",foreground=[("selected", "red")]) 80 ############################################################################# 81 # # 82 # メインメニュー用フレーム設置 # 83 # # 84 ############################################################################# 85 frmMain = ttk.Frame(root) 86 frmMain.grid(row=0, column=0, sticky=tk.E + tk.W + tk.N + tk.S) 87 88 # メインメニューにボタン配置 89 btn_SettingMenu = tk.Button(frmMain, text = "入出力定義", font=("",0,"normal","roman","normal"), command=lambda: change_frame(frmIOMenu)) 90 btn_SettingMenu.pack(fill = tk.BOTH, expand=True) 91 92 btn_SettingMenu = tk.Button(frmMain, text = "終了", font=("",0,"normal","roman","normal"), command=root.destroy) 93 btn_SettingMenu.pack(fill = tk.BOTH, expand=True) 94 ############################################################################# 95 # # 96 # 入出力定義メニュー用フレーム設置 # 97 # # 98 ############################################################################# 99 frmIOMenu = ttk.Frame(root) 100 frmIOMenu.grid_rowconfigure([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], weight=1) 101 frmIOMenu.grid_columnconfigure([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], weight=1, minsize=40) 102 frmIOMenu.grid(row=0, column=0, sticky=tk.E + tk.W + tk.N + tk.S) 103 104 105 106 # 3 シート構成・列構成ツリー 107 tree1 = ttk.Treeview(frmIOMenu, show="tree", height=4) 108 tree1.grid(row=3, column=0, rowspan=4, columnspan=5, sticky=tk.E + tk.W + tk.N + tk.S) 109 tree1.bind("<<TreeviewSelect>>", on_tree_select) #★質問の部分★、選択解除のキーも知りたい.... 110 111 112 113 114 115 # 3 転記ボタン 116 btn_AddColumn = tk.Button(frmIOMenu, text = "→", state="disabled") 117 btn_AddColumn.grid(row=3, column=5, sticky=tk.E + tk.W + tk.N + tk.S) 118 119 # 3 転記済列の表 120 tree2 = ttk.Treeview(frmIOMenu, show="headings", height=4) 121 tree2.grid(row=3, column=6, rowspan=4, columnspan=4, sticky=tk.E + tk.W + tk.N + tk.S) 122 123 124 # 1 入力ファイル指定ボタン 125 dict_Book = {'店舗一覧': ['県CD [A1]', '市町村CD [B1]', '店舗CD [C1]', '担当者CD [D1]'], 126 '取扱品目': ['品目CD [A1]', '品目名称 [B1]', '販売価格 [C1]', '販売開始月 [D1]'], 127 '納入実績': ['県CD [A1]', '品目CD [B1]', '年月 [C1]', '金額 [D1]']} 128 129 btn_AssignInputFile = tk.Button(frmIOMenu, text = "入力ファイル指定", command=get_bookinfo(dict_Book, tree1)) 130 btn_AssignInputFile.grid(row=1, column=0, columnspan=3, sticky=tk.E + tk.W + tk.N + tk.S) 131 132 133 # 1 指定ファイルパス格納テキストボックス 134 ent_InputPath = tk.Entry(frmIOMenu, state="disabled") 135 ent_InputPath.grid(row=1, column=3, columnspan=7, sticky=tk.E + tk.W + tk.N + tk.S) 136 137 # 2 出力ファイル指定ボタン 138 btn_AssignOutputFile = tk.Button(frmIOMenu, text = "出力ファイル指定") 139 btn_AssignOutputFile.grid(row=2, column=0, columnspan=3, sticky=tk.E + tk.W + tk.N + tk.S) 140 141 # 2 指定ファイルパス格納テキストボックス 142 ent_OutputPath = tk.Entry(frmIOMenu, state="disabled") 143 ent_OutputPath.grid(row=2, column=3, columnspan=7, sticky=tk.E + tk.W + tk.N + tk.S) 144 145 # 4 出力一上昇ボタン 146 btn_Up = tk.Button(frmIOMenu, text = "↑", state="disabled") 147 btn_Up.grid(row=4, column=5, sticky=tk.E + tk.W + tk.N + tk.S) 148 149 # 5 出力一下降ボタン 150 btn_Down = tk.Button(frmIOMenu, text = "↓", state="disabled") 151 btn_Down.grid(row=5, column=5, sticky=tk.E + tk.W + tk.N + tk.S) 152 153 # 6 転記中止ボタン 154 btn_Down = tk.Button(frmIOMenu, text = "←", state="disabled") 155 btn_Down.grid(row=6, column=5, sticky=tk.E + tk.W + tk.N+ tk.S) 156 157 # 11 メニューへボタン 158 btn_ReturnMenu = tk.Button(frmIOMenu, text = "閉じる", command=lambda: change_frame(frmMain)) 159 btn_ReturnMenu.grid(row=11, column=8, columnspan=2, sticky=tk.E + tk.W + tk.N + tk.S) 160 161 # 起動時メインのフレームを前面に 162 frmMain.tkraise() 163 164 165 166if __name__ == "__main__": 167 # ウインドウの作成 168 root = tk.Tk() 169 170 #フォームサイズを実行端末から導き、ド真中に配置表示 171 adjust_windowsize(root) 172 173 #タイトルを指定 174 root.title("TkInterの勉強") 175 176 #フレーム切替え達成の上で とても重要、ルートのグリッド定義 177 root.grid_rowconfigure(0, weight=1) 178 root.grid_columnconfigure(0, weight=1) 179 180 181 #タイトル左隅のアイコン 182 #iconfile = resource_path("images\favicon.ico") 183 #root.iconbitmap(default=iconfile) 184 185 #フォームの最大化、×ボタン操作を無効化 186 root.resizable(0,0) 187 root.protocol('WM_DELETE_WINDOW', (lambda: 'pass')()) 188 189 # カーソル変更 190 root["cursor"] = "hand2" 191 192 generate_frame(root) 193 194 root.mainloop()

15:21 理解できず...再確認

イメージ説明

python

1 def on_tree_select(event): 2 selected_items = tree1.selection() 3 print (selected_items) 4 btn_AddColumn["state"] = "active"
teamikl👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

ツリービュで行選択されたノード=階層をしるには 如何したらよろしいですか?

Treeviewのparent()メソッドで、親ノードを知ることができます。
insertする時の第一引数と同じで、ルートノードは空文字 "" です。

ツリービュのウィジェットにBindさせる行解放操作のキーがございましたらご教示ください。

既存のイベントにはありません。
ttk.Treeview で実装されている仮想イベントは以下の3つのみです。

event_generate メソッドで独自イベントを追加することはできます。
→ 回答下コードで実装サンプルを提示。

※ 但し、Tkinterの制限として独自イベントに任意のデータを付随させることはできません。
例えば、「選択解除されたデータをイベントの引数に含めたい」等は出来ない。
Treeview のメソッドを使い、改めてマウス座標から対象のアイテム・選択中かどうかを問い合わせる必要があります。

最上位(店舗一覧・取扱品目・納入実績)は 行選択できないことが望ましいのですが それは諦めていて....

強制的に選択解除することは可能ですが、
キーボード操作で子ノードを開きたい場合、選択できないと困りませんか?


問題個所の切り分け
質問のコード全文に追加すると、問題の該当箇所が解りにくく成るので
以下のコードは Treeview の挙動のみに絞ります。

(2) の条件は、複数選択の場合が恐らく考慮されてないので、
質問の要件とは異なるかもしれませんが、良くあるような状況を想定し、
リストの選択状態によってボタンの状態を変更するデモ。

python

1import tkinter as tk 2from tkinter import ttk 3from functools import partial 4 5SAMPLE_DATA = { 6 "A": [1, 2, 3], 7 "B": [4, 5, 6], 8 "C": [7, 8, 9], 9} 10 11root = tk.Tk() 12tree = ttk.Treeview(root) 13buttonA = ttk.Button(root, text="A") 14buttonB = ttk.Button(root, text="B") 15 16# サンプルデータをツリービューに追加 17for key, val in SAMPLE_DATA.items(): 18 parent = tree.insert("", tk.END, text=key) 19 for text in val: 20 tree.insert(parent, tk.END, text=text) 21 22def on_tree_state(state, event): 23 """状態に応じて、ボタンのstateを変更 24 """ 25 if state: 26 buttonA.config(state=tk.DISABLED) 27 buttonB.config(state=tk.NORMAL) 28 else: 29 buttonA.config(state=tk.NORMAL) 30 buttonB.config(state=tk.DISABLED) 31 32def get_tree_selection_children(tree): 33 """選択中の子要素のリストを返す 34 """ 35 for item in tree.selection(): 36 if tree.parent(item): 37 yield item 38 39def on_tree_select(event): 40 """ 41 """ 42 tree = event.widget 43 items = list(get_tree_selection_children(tree)) 44 45 if items: 46 # 子要素が一つ以上選択されている場合 47 tree.event_generate("<<StateSelect>>") 48 else: 49 # 子要素が一つも選択されていない場合 50 tree.event_generate("<<StateDeselect>>") 51 52tree.bind("<<StateSelect>>", partial(on_tree_state, True)) 53tree.bind("<<StateDeselect>>", partial(on_tree_state, False)) 54tree.bind("<<TreeviewSelect>>", on_tree_select) 55 56tree.pack() 57buttonA.pack() 58buttonB.pack() 59root.mainloop() 60 61

投稿2021/10/22 03:21

teamikl

総合スコア8664

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

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

saya24

2021/10/22 06:30

teamiklさん 引き続きのご支援誠にありがとうございます。 想像以上に理解不測で parent()メソッドの理由方法の部分で 既に躓いております。 本文に追記を施しましたが、SELECTIONのPARENTということですよね? 一先ずSELECTIONの戻りのリストに何が入っているか確認してみたのですが。 選択された行が最上位か否かを把握したいのですが どういう手続きになるのでしょうか
teamikl

2021/10/22 06:49

parent() の使い方は、私の回答のコードの get_tree_selection_children関数が利用例です。 selection() は選択された各アイテムのID(文字列)のリストを返すので、 個々のアイテムに対して parent メソッドで、親ノードがあるかどうかを問い合わせます。 ---- > SELECTIONのPARENTということですよね? Treeview ウィジェットの parent メソッドです。 https://docs.python.org/ja/3/library/tkinter.ttk.html#tkinter.ttk.Treeview parent(item)¶ item の親の識別子を、 item が階層の最上位にいた場合 '' を返します。
saya24

2021/10/22 07:01

ありがとうございます、提示頂いたコードを選択行の開放用と思い、そちらの理解をせず 自分でボタンの有効化・非有効化に着手してしまい、問合せを追記してしまいました。 至れる尽くせりのコード提示だったんですね、すみません。 これから解釈して 自分なりに適用させて頂きます。まずはお礼まで
saya24

2021/10/22 07:03

>selection() は選択された各アイテムのID(文字列)のリストを返すので、個々のアイテムに対して parent メソッドで、親ノードがあるかどうかを問い合わせます。 そういうことか!!
saya24

2021/10/22 07:27

teamiklさん 神ってる.... 無事適用できました、っていうかほとんど そのまんま。 重ねてお礼申し上げます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問