Skip to content

Commit 6acf212

Browse files
gh-75733: Show IDLE's trees in a ttk.Treeview
idlelib.tree drew its rows on a canvas, with hardcoded pixel sizes that fit neither every font nor every monitor, an icon on every row, and bindings of its own for clicks and scrolling. It now wraps a ttk.Treeview, which takes its row height from the configured font and its indicators, colors and keys from ttk and the theme. Converted: the Module and Path browsers, Debug => Stack Viewer, the object browsers, and the stack, Locals and Globals panes of the Debug Control window. Rows carry more than a text now: an item fills the columns of its row with GetValues, and says with GetTags what kind of row it is, which is how the stack views show a frame as module, function, line and source, and how the Path Browser tells a directory, a package and a module apart. The stack pane of the debugger marks the frame it stopped in with a drawn arrow instead of a "> " in front of the text. The Locals and Globals panes are read-only tables, as editing them never reached the objects in the user process (gh-69184). The Module Browser shows no icons, its text saying already whether a row is a class or a function (gh-69277). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 57594aa commit 6acf212

19 files changed

Lines changed: 773 additions & 603 deletions

Lib/idlelib/Icons/README.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ https://www.python.org/psf/trademarks-faq/
4444
https://www.python.org/psf/trademarks/ # Usage.
4545

4646

47-
OTHER GIFS: These are used by browsers using idlelib.tree.
48-
At least some will not be used when tree is replaced by ttk.Treeview.
47+
OTHER GIFS: folder, openfolder and python mark the directories, the
48+
packages and the modules of the Path Browser.
4949

5050

5151
Edited 2024 August 26 by TJR.

Lib/idlelib/Icons/minusnode.gif

-75 Bytes
Binary file not shown.

Lib/idlelib/Icons/plusnode.gif

-78 Bytes
Binary file not shown.

Lib/idlelib/browser.py

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
import pyclbr
1313
import sys
1414

15-
from idlelib.config import idleConf
1615
from idlelib import pyshell
17-
from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas
16+
from idlelib.tree import TreeItem, TreeWidget
1817
from idlelib.util import py_extensions
1918
from idlelib.window import ListedToplevel
2019

@@ -96,9 +95,8 @@ def __init__(self, master, path, *, _htest=False, _utest=False):
9695
self.init()
9796

9897
def close(self, event=None):
99-
"Dismiss the window and the tree nodes."
98+
"Dismiss the window."
10099
self.top.destroy()
101-
self.node.destroy()
102100

103101
def init(self):
104102
"Create browser tkinter widgets, including the tree."
@@ -117,25 +115,23 @@ def init(self):
117115
top.geometry("+%d+%d" %
118116
(root.winfo_rootx(), root.winfo_rooty() + 200))
119117
self.settitle()
120-
top.focus_set()
121-
122-
# create scrolled canvas
123-
theme = idleConf.CurrentTheme()
124-
background = idleConf.GetHighlight(theme, 'normal')['background']
125-
sc = ScrolledCanvas(top, bg=background, highlightthickness=0,
126-
takefocus=1)
127-
sc.frame.pack(expand=1, fill="both")
128-
item = self.rootnode()
129-
self.node = node = TreeNode(sc.canvas, None, item)
118+
119+
# create the tree
120+
self.tree = tree = TreeWidget(top, self.rootnode())
121+
tree.pack(expand=True, fill="both")
122+
self.set_icons()
123+
tree.focus_set()
130124
if not self._utest:
131-
node.update()
132-
node.expand()
125+
tree.expand()
133126

134127
def settitle(self):
135128
"Set the window title."
136129
self.top.wm_title("Module Browser - " + os.path.basename(self.path))
137130
self.top.wm_iconname("Module Browser")
138131

132+
def set_icons(self):
133+
"Give the rows icons. The module browser shows none; see gh-69277."
134+
139135
def rootnode(self):
140136
"Return a ModuleBrowserTreeItem as the root of the tree."
141137
return ModuleBrowserTreeItem(self.path)
@@ -160,14 +156,14 @@ def GetText(self):
160156
"Return the module name as the text string to display."
161157
return os.path.basename(self.file)
162158

163-
def GetIconName(self):
164-
"Return the name of the icon to display."
165-
return "python"
166-
167159
def GetSubList(self):
168160
"Return ChildBrowserTreeItems for children."
169161
return [ChildBrowserTreeItem(obj) for obj in self.listchildren()]
170162

163+
def GetTags(self):
164+
"Mark the row as a module, which the Path Browser gives an icon."
165+
return ('module',)
166+
171167
def OnDoubleClick(self):
172168
"Open a module in an editor window when double clicked."
173169
if not is_browseable_extension(self.file):
@@ -213,13 +209,6 @@ def GetText(self):
213209
else:
214210
return "class " + name
215211

216-
def GetIconName(self):
217-
"Return the name of the icon to display."
218-
if self.isfunction:
219-
return "python"
220-
else:
221-
return "folder"
222-
223212
def IsExpandable(self):
224213
"Return True if self.obj has nested objects."
225214
return self.obj.children != {}
@@ -244,7 +233,7 @@ def _module_browser(parent): # htest #
244233
else:
245234
file = __file__
246235
# Add nested objects for htest.
247-
class Nested_in_func(TreeNode):
236+
class Nested_in_func(TreeItem):
248237
def nested_in_class(): pass
249238
def closure():
250239
class Nested_in_closure: pass

0 commit comments

Comments
 (0)