11import tkinter as tk
22from tkinter import ttk
3- from typing import TYPE_CHECKING , Any
3+ from typing import TYPE_CHECKING , Any , Optional
44
55from bigtree .node import node
66
1212__all__ = ["render_tree" ]
1313
1414
15+ class DragDropTree (ttk .Treeview ):
16+ def __init__ (self , master : tk .Tk , ** kwargs : Any ):
17+ super ().__init__ (master , ** kwargs )
18+ self .bind ("<ButtonPress-1>" , self .on_button_press )
19+ self .bind ("<B1-Motion>" , self .on_motion )
20+ self .bind ("<ButtonRelease-1>" , self .on_button_release )
21+ self .tag_configure ("highlight" , background = "lightblue" )
22+
23+ self ._dragging_item : Optional [str ] = None
24+ self ._drop_target : Optional [str ] = None
25+
26+ def on_button_press (self , event : TkEvent ) -> None :
27+ """Assign dragging item to pressed object"""
28+ item = self .identify_row (event .y )
29+ if item :
30+ self ._dragging_item = item
31+
32+ def on_motion (self , event : TkEvent ) -> None :
33+ """Highlight drop target"""
34+ if not self ._dragging_item :
35+ return
36+
37+ # Highlight drop target
38+ new_target = self .identify_row (event .y )
39+ if new_target != self ._drop_target :
40+ self ._clear_highlight ()
41+ if new_target and new_target != self ._dragging_item :
42+ self .item (new_target , tags = ("highlight" ,))
43+ self ._drop_target = new_target
44+
45+ def _clear_highlight (self ) -> None :
46+ """Clear highlight"""
47+ if self ._drop_target :
48+ self .item (self ._drop_target , tags = ())
49+ self ._drop_target = None
50+
51+ def on_button_release (self , event : TkEvent ) -> None :
52+ """Assign dragging item to first child of drop target"""
53+ if not self ._dragging_item :
54+ return
55+
56+ target = self .identify_row (event .y )
57+ if target and target != self ._dragging_item :
58+ self .item (target , open = True )
59+ self .move (self ._dragging_item , target , 0 )
60+
61+ self ._clear_highlight ()
62+ self ._dragging_item = None
63+ self ._drop_target = None
64+
65+
1566class TkinterTree :
1667 def __init__ (
1768 self ,
@@ -29,11 +80,10 @@ def __init__(
2980 self .counter = 0
3081
3182 root .title (title )
32-
33- tree = ttk . Treeview (root )
83+ root . minsize ( width = 400 , height = 200 )
84+ tree = DragDropTree (root )
3485 tree .pack (fill = tk .BOTH , expand = True )
3586
36- # Hidden entry for inline editing
3787 entry = tk .Entry (root )
3888 entry .bind ("<FocusOut>" , lambda e : entry .place_forget ())
3989 entry .bind ("<Return>" , self .on_return )
@@ -45,10 +95,14 @@ def __init__(
4595 # Insert nodes
4696 tree_root = tree .insert ("" , "end" , iid = self .get_iid (), text = root_name )
4797
48- # Add button
49- tk .Button (root , text = "Add Child" , command = self .on_plus ).pack ()
50- tk .Button (root , text = "Print Tree" , command = self .print_tree ).pack ()
51- tk .Button (root , text = "Export Tree" , command = self .export_tree ).pack ()
98+ # Add buttons
99+ button_frame = tk .Frame (root )
100+ button_frame .pack ()
101+ b_add = tk .Button (button_frame , text = "Add Child" , command = self .on_plus )
102+ b_print = tk .Button (button_frame , text = "Print Tree" , command = self .print_tree )
103+ b_export = tk .Button (button_frame , text = "Export Tree" , command = self .export_tree )
104+ for button in [b_add , b_print , b_export ]:
105+ button .pack (side = "left" , padx = 5 )
52106
53107 self .tree = tree
54108 self .tree_root = tree_root
@@ -97,13 +151,13 @@ def _add_child(_node: node.Node, node_iid: str) -> None:
97151
98152 def print_tree (self ) -> None :
99153 """Export tree, print tree to console. Tree can be constructed into a bigtree object using
100- bigtree.tree.construct.str_to_tree."""
154+ ` bigtree.tree.construct.str_to_tree` ."""
101155 tree = self .get_tree ()
102156 tree .show ()
103157
104158 def export_tree (self ) -> None :
105159 """Export tree, print tree dictionary to console. Tree can be constructed into a bigtree object using
106- bigtree.tree.construct.dict_to_tree"""
160+ ` bigtree.tree.construct.dict_to_tree` """
107161 from pprint import pprint
108162
109163 from bigtree .tree import export
@@ -173,7 +227,7 @@ def render_tree(
173227 title : str = "Tree Render" ,
174228 root_name : str = "Root" ,
175229) -> None :
176- """Renders tree with tkinter, exports tree to JSON file .
230+ """Renders tree in windows pop-up, powered by tkinter. Able to export tree to console .
177231
178232 Viewing Interaction:
179233
@@ -184,6 +238,7 @@ def render_tree(
184238 - Add node: Press "+" / Click "Add Child" button
185239 - Delete node: Press "Delete"
186240 - Rename node: Double click
241+ - Move node: Drag-and-drop to assign as (first) child
187242
188243 Export Interaction:
189244
@@ -195,7 +250,7 @@ def render_tree(
195250 root_name: initial root name of tree
196251
197252 Returns:
198- Tree render in window pop-up
253+ Tree rendered in window pop-up
199254 """
200255 root = tk .Tk ()
201256 TkinterTree (root , title , root_name )
0 commit comments