[Python-checkins] bpo-33962: Use ttk spinbox for IDLE indent space config (GH-22954)

terryjreedy webhook-mailer at python.org
Thu Jun 10 15:14:19 EDT 2021


https://github.com/python/cpython/commit/42d5a4fc3b35e45cdd237d56a04e98894d0a31f5
commit: 42d5a4fc3b35e45cdd237d56a04e98894d0a31f5
branch: main
author: Mark Roseman <mark at markroseman.com>
committer: terryjreedy <tjreedy at udel.edu>
date: 2021-06-10T15:13:55-04:00
summary:

bpo-33962: Use ttk spinbox for IDLE indent space config (GH-22954)

If ttk.Spinbox is not available (Tk < 8.5.9) use readonly ttk.Combobox.

Co-authored-by: Terry Jan Reedy <tjreedy at udel.edu>

files:
A Misc/NEWS.d/next/IDLE/2021-06-10-00-50-02.bpo-33962.ikAUNg.rst
M Lib/idlelib/configdialog.py
M Lib/idlelib/idle_test/test_configdialog.py

diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py
index e3fa34f2090e4..6d0893680274b 100644
--- a/Lib/idlelib/configdialog.py
+++ b/Lib/idlelib/configdialog.py
@@ -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
@@ -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
@@ -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):
@@ -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
@@ -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
@@ -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)
@@ -385,13 +363,6 @@ 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)
@@ -399,7 +370,6 @@ def create_page_font_tab(self):
         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)
@@ -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.
@@ -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):
 
@@ -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
@@ -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(
@@ -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(
@@ -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')
@@ -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)
@@ -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)
@@ -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(
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
index f71d1b1bc09a0..3005ce08c9bf4 100644
--- a/Lib/idlelib/idle_test/test_configdialog.py
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -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):
@@ -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.
 
@@ -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'}})
@@ -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
diff --git a/Misc/NEWS.d/next/IDLE/2021-06-10-00-50-02.bpo-33962.ikAUNg.rst b/Misc/NEWS.d/next/IDLE/2021-06-10-00-50-02.bpo-33962.ikAUNg.rst
new file mode 100644
index 0000000000000..b15fa8f184792
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2021-06-10-00-50-02.bpo-33962.ikAUNg.rst
@@ -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.



More information about the Python-checkins mailing list