Skip to content

[3.10] bpo-33962: Use ttk spinbox for IDLE indent space config (GH-22954) #26647

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 62 additions & 94 deletions Lib/idlelib/configdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
StringVar, BooleanVar, IntVar, TRUE, FALSE,
TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE,
NONE, BOTH, X, Y, W, E, EW, NS, NSEW, NW,
HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END)
HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END, TclError)
from tkinter.ttk import (Frame, LabelFrame, Button, Checkbutton, Entry, Label,
OptionMenu, Notebook, Radiobutton, Scrollbar, Style)
OptionMenu, Notebook, Radiobutton, Scrollbar, Style,
Spinbox, Combobox)
from tkinter import colorchooser
import tkinter.font as tkfont
from tkinter import messagebox
Expand Down Expand Up @@ -101,8 +102,9 @@ def create_widgets(self):
highpage: HighPage
fontpage: FontPage
keyspage: KeysPage
genpage: GenPage
extpage: self.create_page_extensions
winpage: WinPage
shedpage: ShedPage
extpage: ExtPage

Methods:
create_action_buttons
Expand Down Expand Up @@ -170,26 +172,15 @@ def create_action_buttons(self):
return outer

def ok(self):
"""Apply config changes, then dismiss dialog.

Methods:
apply
destroy: inherited
"""
"""Apply config changes, then dismiss dialog."""
self.apply()
self.destroy()

def apply(self):
"""Apply config changes and leave dialog open.

Methods:
deactivate_current_config
save_all_changed_extensions
activate_config_changes
"""
"""Apply config changes and leave dialog open."""
self.deactivate_current_config()
changes.save_all()
self.save_all_changed_extensions()
self.extpage.save_all_changed_extensions()
self.activate_config_changes()

def cancel(self):
Expand Down Expand Up @@ -299,12 +290,11 @@ class FontPage(Frame):
def __init__(self, master, highpage):
super().__init__(master)
self.highlight_sample = highpage.highlight_sample
self.create_page_font_tab()
self.create_page_font()
self.load_font_cfg()
self.load_tab_cfg()

def create_page_font_tab(self):
"""Return frame of widgets for Font/Tabs tab.
def create_page_font(self):
"""Return frame of widgets for Font tab.

Fonts: Enable users to provisionally change font face, size, or
boldness and to see the consequence of proposed choices. Each
Expand All @@ -328,11 +318,6 @@ def create_page_font_tab(self):
Set_samples applies a new font constructed from the font vars to
font_sample and to highlight_sample on the highlight page.

Tabs: Enable users to change spaces entered for indent tabs.
Changing indent_scale value with the mouse sets Var space_num,
which invokes the default callback to add an entry to
changes. Load_tab_cfg initializes space_num to default.

Widgets for FontPage(Frame): (*) widgets bound to self
frame_font: LabelFrame
frame_font_name: Frame
Expand All @@ -345,23 +330,16 @@ def create_page_font_tab(self):
(*)bold_toggle: Checkbutton - font_bold
frame_sample: LabelFrame
(*)font_sample: Label
frame_indent: LabelFrame
indent_title: Label
(*)indent_scale: Scale - space_num
"""
self.font_name = tracers.add(StringVar(self), self.var_changed_font)
self.font_size = tracers.add(StringVar(self), self.var_changed_font)
self.font_bold = tracers.add(BooleanVar(self), self.var_changed_font)
self.space_num = tracers.add(IntVar(self), ('main', 'Indent', 'num-spaces'))

# Define frames and widgets.
frame_font = LabelFrame(
self, borderwidth=2, relief=GROOVE, text=' Shell/Editor Font ')
frame_sample = LabelFrame(
self, borderwidth=2, relief=GROOVE,
text=' Font Sample (Editable) ')
frame_indent = LabelFrame(
self, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
frame_font = LabelFrame(self, borderwidth=2, relief=GROOVE,
text=' Shell/Editor Font ')
frame_sample = LabelFrame(self, borderwidth=2, relief=GROOVE,
text=' Font Sample (Editable) ')
# frame_font.
frame_font_name = Frame(frame_font)
frame_font_param = Frame(frame_font)
Expand All @@ -385,21 +363,13 @@ def create_page_font_tab(self):
self.font_sample = font_sample_frame.text
self.font_sample.config(wrap=NONE, width=1, height=1)
self.font_sample.insert(END, font_sample_text)
# frame_indent.
indent_title = Label(
frame_indent, justify=LEFT,
text='Python Standard: 4 Spaces!')
self.indent_scale = Scale(
frame_indent, variable=self.space_num,
orient='horizontal', tickinterval=2, from_=2, to=16)

# Grid and pack widgets:
self.columnconfigure(1, weight=1)
self.rowconfigure(2, weight=1)
frame_font.grid(row=0, column=0, padx=5, pady=5)
frame_sample.grid(row=0, column=1, rowspan=3, padx=5, pady=5,
sticky='nsew')
frame_indent.grid(row=1, column=0, padx=5, pady=5, sticky='ew')
# frame_font.
frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
Expand All @@ -411,9 +381,6 @@ def create_page_font_tab(self):
self.bold_toggle.pack(side=LEFT, anchor=W, padx=20)
# frame_sample.
font_sample_frame.pack(expand=TRUE, fill=BOTH)
# frame_indent.
indent_title.pack(side=TOP, anchor=W, padx=5)
self.indent_scale.pack(side=TOP, padx=5, fill=X)

def load_font_cfg(self):
"""Load current configuration settings for the font options.
Expand Down Expand Up @@ -487,22 +454,6 @@ def set_samples(self, event=None):
self.font_sample['font'] = new_font
self.highlight_sample['font'] = new_font

def load_tab_cfg(self):
"""Load current configuration settings for the tab options.

Attributes updated:
space_num: Set to value from idleConf.
"""
# Set indent sizes.
space_num = idleConf.GetOption(
'main', 'Indent', 'num-spaces', default=4, type='int')
self.space_num.set(space_num)

def var_changed_space_num(self, *params):
"Store change to indentation size."
value = self.space_num.get()
changes.add_option('main', 'Indent', 'num-spaces', value)


class HighPage(Frame):

Expand All @@ -515,7 +466,7 @@ def __init__(self, master, extpage):
self.load_theme_cfg()

def create_page_highlight(self):
"""Return frame of widgets for Highlighting tab.
"""Return frame of widgets for Highlights tab.

Enable users to provisionally change foreground and background
colors applied to textual tags. Color mappings are stored in
Expand Down Expand Up @@ -1617,40 +1568,41 @@ def create_page_windows(self):
"""Return frame of widgets for Windows tab.

Enable users to provisionally change general window options.
Function load_windows_cfg initializes tk variables idleConf.
Function load_windows_cfg initializes tk variable idleConf.
Radiobuttons startup_shell_on and startup_editor_on set var
startup_edit. Entry boxes win_width_int and win_height_int set var
win_width and win_height. Setting var_name invokes the default
callback that adds option to changes.

Widgets for WinPage(Frame): (*) widgets bound to self
Widgets for WinPage(Frame): > vars, bound to self
frame_window: LabelFrame
frame_run: Frame
startup_title: Label
(*)startup_editor_on: Radiobutton - startup_edit
(*)startup_shell_on: Radiobutton - startup_edit
startup_editor_on: Radiobutton > startup_edit
startup_shell_on: Radiobutton > startup_edit
frame_win_size: Frame
win_size_title: Label
win_width_title: Label
(*)win_width_int: Entry - win_width
win_width_int: Entry > win_width
win_height_title: Label
(*)win_height_int: Entry - win_height
frame_cursor_blink: Frame
cursor_blink_title: Label
(*)cursor_blink_bool: Checkbutton - cursor_blink
win_height_int: Entry > win_height
frame_cursor: Frame
indent_title: Label
indent_chooser: Spinbox (Combobox < 8.5.9) > indent_spaces
blink_on: Checkbutton > cursor_blink
frame_autocomplete: Frame
auto_wait_title: Label
(*)auto_wait_int: Entry - autocomplete_wait
auto_wait_int: Entry > autocomplete_wait
frame_paren1: Frame
paren_style_title: Label
(*)paren_style_type: OptionMenu - paren_style
paren_style_type: OptionMenu > paren_style
frame_paren2: Frame
paren_time_title: Label
(*)paren_flash_time: Entry - flash_delay
(*)bell_on: Checkbutton - paren_bell
paren_flash_time: Entry > flash_delay
bell_on: Checkbutton > paren_bell
frame_format: Frame
format_width_title: Label
(*)format_width_int: Entry - format_width
format_width_int: Entry > format_width
"""
# Integer values need StringVar because int('') raises.
self.startup_edit = tracers.add(
Expand All @@ -1659,6 +1611,8 @@ def create_page_windows(self):
StringVar(self), ('main', 'EditorWindow', 'width'))
self.win_height = tracers.add(
StringVar(self), ('main', 'EditorWindow', 'height'))
self.indent_spaces = tracers.add(
StringVar(self), ('main', 'Indent', 'num-spaces'))
self.cursor_blink = tracers.add(
BooleanVar(self), ('main', 'EditorWindow', 'cursor-blink'))
self.autocomplete_wait = tracers.add(
Expand Down Expand Up @@ -1699,18 +1653,28 @@ def create_page_windows(self):
validatecommand=self.digits_only, validate='key',
)

frame_cursor_blink = Frame(frame_window, borderwidth=0)
cursor_blink_title = Label(frame_cursor_blink, text='Cursor Blink')
self.cursor_blink_bool = Checkbutton(frame_cursor_blink,
variable=self.cursor_blink, width=1)
frame_cursor = Frame(frame_window, borderwidth=0)
indent_title = Label(frame_cursor,
text='Indent spaces (4 is standard)')
try:
self.indent_chooser = Spinbox(
frame_cursor, textvariable=self.indent_spaces,
from_=1, to=10, width=2,
validatecommand=self.digits_only, validate='key')
except TclError:
self.indent_chooser = Combobox(
frame_cursor, textvariable=self.indent_spaces,
state="readonly", values=list(range(1,11)), width=3)
cursor_blink_title = Label(frame_cursor, text='Cursor Blink')
self.cursor_blink_bool = Checkbutton(frame_cursor, text="Cursor blink",
variable=self.cursor_blink)

frame_autocomplete = Frame(frame_window, borderwidth=0,)
auto_wait_title = Label(frame_autocomplete,
text='Completions Popup Wait (milliseconds)')
self.auto_wait_int = Entry(frame_autocomplete, width=6,
textvariable=self.autocomplete_wait,
validatecommand=self.digits_only,
validate='key')
text='Completions Popup Wait (milliseconds)')
self.auto_wait_int = Entry(
frame_autocomplete, textvariable=self.autocomplete_wait,
width=6, validatecommand=self.digits_only, validate='key')

frame_paren1 = Frame(frame_window, borderwidth=0)
paren_style_title = Label(frame_paren1, text='Paren Match Style')
Expand All @@ -1722,7 +1686,8 @@ def create_page_windows(self):
frame_paren2, text='Time Match Displayed (milliseconds)\n'
'(0 is until next input)')
self.paren_flash_time = Entry(
frame_paren2, textvariable=self.flash_delay, width=6)
frame_paren2, textvariable=self.flash_delay, width=6,
validatecommand=self.digits_only, validate='key')
self.bell_on = Checkbutton(
frame_paren2, text="Bell on Mismatch", variable=self.paren_bell)
frame_format = Frame(frame_window, borderwidth=0)
Expand All @@ -1747,10 +1712,11 @@ def create_page_windows(self):
win_height_title.pack(side=RIGHT, anchor=E, pady=5)
self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5)
win_width_title.pack(side=RIGHT, anchor=E, pady=5)
# frame_cursor_blink.
frame_cursor_blink.pack(side=TOP, padx=5, pady=0, fill=X)
cursor_blink_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.cursor_blink_bool.pack(side=LEFT, padx=5, pady=5)
# frame_cursor.
frame_cursor.pack(side=TOP, padx=5, pady=0, fill=X)
indent_title.pack(side=LEFT, anchor=W, padx=5)
self.indent_chooser.pack(side=LEFT, anchor=W, padx=10)
self.cursor_blink_bool.pack(side=RIGHT, anchor=E, padx=15, pady=5)
# frame_autocomplete.
frame_autocomplete.pack(side=TOP, padx=5, pady=0, fill=X)
auto_wait_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
Expand All @@ -1776,6 +1742,8 @@ def load_windows_cfg(self):
'main', 'EditorWindow', 'width', type='int'))
self.win_height.set(idleConf.GetOption(
'main', 'EditorWindow', 'height', type='int'))
self.indent_spaces.set(idleConf.GetOption(
'main', 'Indent', 'num-spaces', type='int'))
self.cursor_blink.set(idleConf.GetOption(
'main', 'EditorWindow', 'cursor-blink', type='bool'))
self.autocomplete_wait.set(idleConf.GetOption(
Expand Down
33 changes: 9 additions & 24 deletions Lib/idlelib/idle_test/test_configdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ def test_click_ok(self):
def test_click_apply(self):
d = dialog
deactivate = d.deactivate_current_config = mock.Mock()
save_ext = d.save_all_changed_extensions = mock.Mock()
save_ext = d.extpage.save_all_changed_extensions = mock.Mock()
activate = d.activate_config_changes = mock.Mock()
d.buttons['Apply'].invoke()
deactivate.assert_called_once()
save_ext.assert_called_once()
activate.assert_called_once()
del d.save_all_changed_extensions
del d.extpage.save_all_changed_extensions
del d.activate_config_changes, d.deactivate_current_config

def test_click_cancel(self):
Expand Down Expand Up @@ -260,27 +260,6 @@ def test_set_samples(self):
d.set_samples = Func() # Re-mask for other tests.


class IndentTest(unittest.TestCase):

@classmethod
def setUpClass(cls):
cls.page = dialog.fontpage
cls.page.update()

def test_load_tab_cfg(self):
d = self.page
d.space_num.set(16)
d.load_tab_cfg()
self.assertEqual(d.space_num.get(), 4)

def test_indent_scale(self):
d = self.page
changes.clear()
d.indent_scale.set(20)
self.assertEqual(d.space_num.get(), 16)
self.assertEqual(mainpage, {'Indent': {'num-spaces': '16'}})


class HighPageTest(unittest.TestCase):
"""Test that highlight tab widgets enable users to make changes.

Expand Down Expand Up @@ -1250,6 +1229,12 @@ def test_editor_size(self):
d.win_width_int.insert(0, '11')
self.assertEqual(mainpage, {'EditorWindow': {'width': '11'}})

def test_indent_spaces(self):
d = self.page
d.indent_chooser.set(6)
self.assertEqual(d.indent_spaces.get(), '6')
self.assertEqual(mainpage, {'Indent': {'num-spaces': '6'}})

def test_cursor_blink(self):
self.page.cursor_blink_bool.invoke()
self.assertEqual(mainpage, {'EditorWindow': {'cursor-blink': 'False'}})
Expand Down Expand Up @@ -1278,7 +1263,7 @@ def test_paragraph(self):
self.assertEqual(extpage, {'FormatParagraph': {'max-width': '11'}})


class GenPageTest(unittest.TestCase):
class ShedPageTest(unittest.TestCase):
"""Test that shed tab widgets enable users to make changes.

Test that widget actions set vars, that var changes add
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Move the indent space setting from the Font tab to the new Windows tab.
Patch by Mark Roseman and Terry Jan Reedy.