bpo-40468: Split IDLE settings General tab (GH-26621)

Replace it with Windows tab for Shell and Editor options
and Shell/Ed for options exclusive to one of them.

Create room for more options and make dialog shorter,
to better fit small windows.
(cherry picked from commit 275d5f7957)

Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
This commit is contained in:
Miss Islington (bot) 2021-06-09 13:37:56 -07:00 committed by GitHub
parent 33a7a24288
commit 664ae29e6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 184 additions and 141 deletions

View file

@ -116,11 +116,14 @@ def create_widgets(self):
self.highpage = HighPage(note, self.extpage) self.highpage = HighPage(note, self.extpage)
self.fontpage = FontPage(note, self.highpage) self.fontpage = FontPage(note, self.highpage)
self.keyspage = KeysPage(note, self.extpage) self.keyspage = KeysPage(note, self.extpage)
self.genpage = GenPage(note) self.winpage = WinPage(note)
self.shedpage = ShedPage(note)
note.add(self.fontpage, text='Fonts/Tabs') note.add(self.fontpage, text='Fonts/Tabs')
note.add(self.highpage, text='Highlights') note.add(self.highpage, text='Highlights')
note.add(self.keyspage, text=' Keys ') note.add(self.keyspage, text=' Keys ')
note.add(self.genpage, text=' General ') note.add(self.winpage, text=' Windows ')
note.add(self.shedpage, text=' Shell/Ed ')
note.add(self.extpage, text='Extensions') note.add(self.extpage, text='Extensions')
note.enable_traversal() note.enable_traversal()
note.pack(side=TOP, expand=TRUE, fill=BOTH) note.pack(side=TOP, expand=TRUE, fill=BOTH)
@ -1594,14 +1597,14 @@ def delete_custom_keys(self):
self.set_keys_type() self.set_keys_type()
class GenPage(Frame): class WinPage(Frame):
def __init__(self, master): def __init__(self, master):
super().__init__(master) super().__init__(master)
self.init_validators() self.init_validators()
self.create_page_general() self.create_page_windows()
self.load_general_cfg() self.load_windows_cfg()
def init_validators(self): def init_validators(self):
digits_or_empty_re = re.compile(r'[0-9]*') digits_or_empty_re = re.compile(r'[0-9]*')
@ -1610,26 +1613,17 @@ def is_digits_or_empty(s):
return digits_or_empty_re.fullmatch(s) is not None return digits_or_empty_re.fullmatch(s) is not None
self.digits_only = (self.register(is_digits_or_empty), '%P',) self.digits_only = (self.register(is_digits_or_empty), '%P',)
def create_page_general(self): def create_page_windows(self):
"""Return frame of widgets for General tab. """Return frame of widgets for Windows tab.
Enable users to provisionally change general options. Function Enable users to provisionally change general window options.
load_general_cfg initializes tk variables and helplist using Function load_windows_cfg initializes tk variables idleConf.
idleConf. Radiobuttons startup_shell_on and startup_editor_on Radiobuttons startup_shell_on and startup_editor_on set var
set var startup_edit. Radiobuttons save_ask_on and save_auto_on startup_edit. Entry boxes win_width_int and win_height_int set var
set var autosave. Entry boxes win_width_int and win_height_int win_width and win_height. Setting var_name invokes the default
set var win_width and win_height. Setting var_name invokes the callback that adds option to changes.
default callback that adds option to changes.
Helplist: load_general_cfg loads list user_helplist with Widgets for WinPage(Frame): (*) widgets bound to self
name, position pairs and copies names to listbox helplist.
Clicking a name invokes help_source selected. Clicking
button_helplist_name invokes helplist_item_name, which also
changes user_helplist. These functions all call
set_add_delete_state. All but load call update_help_changes to
rewrite changes['main']['HelpFiles'].
Widgets for GenPage(Frame): (*) widgets bound to self
frame_window: LabelFrame frame_window: LabelFrame
frame_run: Frame frame_run: Frame
startup_title: Label startup_title: Label
@ -1654,24 +1648,9 @@ def create_page_general(self):
paren_time_title: Label paren_time_title: Label
(*)paren_flash_time: Entry - flash_delay (*)paren_flash_time: Entry - flash_delay
(*)bell_on: Checkbutton - paren_bell (*)bell_on: Checkbutton - paren_bell
frame_editor: LabelFrame
frame_save: Frame
run_save_title: Label
(*)save_ask_on: Radiobutton - autosave
(*)save_auto_on: Radiobutton - autosave
frame_format: Frame frame_format: Frame
format_width_title: Label format_width_title: Label
(*)format_width_int: Entry - format_width (*)format_width_int: Entry - format_width
frame_line_numbers_default: Frame
line_numbers_default_title: Label
(*)line_numbers_default_bool: Checkbutton - line_numbers_default
frame_context: Frame
context_title: Label
(*)context_int: Entry - context_lines
frame_shell: LabelFrame
frame_auto_squeeze_min_lines: Frame
auto_squeeze_min_lines_title: Label
(*)auto_squeeze_min_lines_int: Entry - auto_squeeze_min_lines
""" """
# Integer values need StringVar because int('') raises. # Integer values need StringVar because int('') raises.
self.startup_edit = tracers.add( self.startup_edit = tracers.add(
@ -1690,29 +1669,13 @@ def create_page_general(self):
StringVar(self), ('extensions', 'ParenMatch', 'flash-delay')) StringVar(self), ('extensions', 'ParenMatch', 'flash-delay'))
self.paren_bell = tracers.add( self.paren_bell = tracers.add(
BooleanVar(self), ('extensions', 'ParenMatch', 'bell')) BooleanVar(self), ('extensions', 'ParenMatch', 'bell'))
self.auto_squeeze_min_lines = tracers.add(
StringVar(self), ('main', 'PyShell', 'auto-squeeze-min-lines'))
self.autosave = tracers.add(
IntVar(self), ('main', 'General', 'autosave'))
self.format_width = tracers.add( self.format_width = tracers.add(
StringVar(self), ('extensions', 'FormatParagraph', 'max-width')) StringVar(self), ('extensions', 'FormatParagraph', 'max-width'))
self.line_numbers_default = tracers.add(
BooleanVar(self),
('main', 'EditorWindow', 'line-numbers-default'))
self.context_lines = tracers.add(
StringVar(self), ('extensions', 'CodeContext', 'maxlines'))
# Create widgets: # Create widgets:
# Section frames.
frame_window = LabelFrame(self, borderwidth=2, relief=GROOVE, frame_window = LabelFrame(self, borderwidth=2, relief=GROOVE,
text=' Window Preferences') text=' Window Preferences')
frame_editor = LabelFrame(self, borderwidth=2, relief=GROOVE,
text=' Editor Preferences')
frame_shell = LabelFrame(self, borderwidth=2, relief=GROOVE,
text=' Shell Preferences')
# Frame_window.
frame_run = Frame(frame_window, borderwidth=0) frame_run = Frame(frame_window, borderwidth=0)
startup_title = Label(frame_run, text='At Startup') startup_title = Label(frame_run, text='At Startup')
self.startup_editor_on = Radiobutton( self.startup_editor_on = Radiobutton(
@ -1747,8 +1710,7 @@ def create_page_general(self):
self.auto_wait_int = Entry(frame_autocomplete, width=6, self.auto_wait_int = Entry(frame_autocomplete, width=6,
textvariable=self.autocomplete_wait, textvariable=self.autocomplete_wait,
validatecommand=self.digits_only, validatecommand=self.digits_only,
validate='key', validate='key')
)
frame_paren1 = Frame(frame_window, borderwidth=0) frame_paren1 = Frame(frame_window, borderwidth=0)
paren_style_title = Label(frame_paren1, text='Paren Match Style') paren_style_title = Label(frame_paren1, text='Paren Match Style')
@ -1763,18 +1725,7 @@ def create_page_general(self):
frame_paren2, textvariable=self.flash_delay, width=6) frame_paren2, textvariable=self.flash_delay, width=6)
self.bell_on = Checkbutton( self.bell_on = Checkbutton(
frame_paren2, text="Bell on Mismatch", variable=self.paren_bell) frame_paren2, text="Bell on Mismatch", variable=self.paren_bell)
frame_format = Frame(frame_window, borderwidth=0)
# Frame_editor.
frame_save = Frame(frame_editor, borderwidth=0)
run_save_title = Label(frame_save, text='At Start of Run (F5) ')
self.save_ask_on = Radiobutton(
frame_save, variable=self.autosave, value=0,
text="Prompt to Save")
self.save_auto_on = Radiobutton(
frame_save, variable=self.autosave, value=1,
text='No Prompt')
frame_format = Frame(frame_editor, borderwidth=0)
format_width_title = Label(frame_format, format_width_title = Label(frame_format,
text='Format Paragraph Max Width') text='Format Paragraph Max Width')
self.format_width_int = Entry( self.format_width_int = Entry(
@ -1782,36 +1733,8 @@ def create_page_general(self):
validatecommand=self.digits_only, validate='key', validatecommand=self.digits_only, validate='key',
) )
frame_line_numbers_default = Frame(frame_editor, borderwidth=0)
line_numbers_default_title = Label(
frame_line_numbers_default, text='Show line numbers in new windows')
self.line_numbers_default_bool = Checkbutton(
frame_line_numbers_default,
variable=self.line_numbers_default,
width=1)
frame_context = Frame(frame_editor, borderwidth=0)
context_title = Label(frame_context, text='Max Context Lines :')
self.context_int = Entry(
frame_context, textvariable=self.context_lines, width=3,
validatecommand=self.digits_only, validate='key',
)
# Frame_shell.
frame_auto_squeeze_min_lines = Frame(frame_shell, borderwidth=0)
auto_squeeze_min_lines_title = Label(frame_auto_squeeze_min_lines,
text='Auto-Squeeze Min. Lines:')
self.auto_squeeze_min_lines_int = Entry(
frame_auto_squeeze_min_lines, width=4,
textvariable=self.auto_squeeze_min_lines,
validatecommand=self.digits_only, validate='key',
)
# Pack widgets: # Pack widgets:
# Body.
frame_window.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_window.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
frame_editor.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
frame_shell.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
# frame_run. # frame_run.
frame_run.pack(side=TOP, padx=5, pady=0, fill=X) frame_run.pack(side=TOP, padx=5, pady=0, fill=X)
startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
@ -1840,34 +1763,10 @@ def create_page_general(self):
paren_time_title.pack(side=LEFT, anchor=W, padx=5) paren_time_title.pack(side=LEFT, anchor=W, padx=5)
self.bell_on.pack(side=RIGHT, anchor=E, padx=15, pady=5) self.bell_on.pack(side=RIGHT, anchor=E, padx=15, pady=5)
self.paren_flash_time.pack(side=TOP, anchor=W, padx=15, pady=5) self.paren_flash_time.pack(side=TOP, anchor=W, padx=15, pady=5)
# frame_save.
frame_save.pack(side=TOP, padx=5, pady=0, fill=X)
run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.save_auto_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
self.save_ask_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
# frame_format. # frame_format.
frame_format.pack(side=TOP, padx=5, pady=0, fill=X) frame_format.pack(side=TOP, padx=5, pady=0, fill=X)
format_width_title.pack(side=LEFT, anchor=W, padx=5, pady=5) format_width_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.format_width_int.pack(side=TOP, padx=10, pady=5) self.format_width_int.pack(side=TOP, padx=10, pady=5)
# frame_line_numbers_default.
frame_line_numbers_default.pack(side=TOP, padx=5, pady=0, fill=X)
line_numbers_default_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.line_numbers_default_bool.pack(side=LEFT, padx=5, pady=5)
# frame_context.
frame_context.pack(side=TOP, padx=5, pady=0, fill=X)
context_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.context_int.pack(side=TOP, padx=5, pady=5)
# frame_auto_squeeze_min_lines
frame_auto_squeeze_min_lines.pack(side=TOP, padx=5, pady=0, fill=X)
auto_squeeze_min_lines_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.auto_squeeze_min_lines_int.pack(side=TOP, padx=5, pady=5)
def load_general_cfg(self):
"Load current configuration settings for the general options."
self.load_windows_cfg()
self.load_shelled_cfg()
def load_windows_cfg(self): def load_windows_cfg(self):
# Set variables for all windows. # Set variables for all windows.
@ -1887,22 +1786,142 @@ def load_windows_cfg(self):
'extensions', 'ParenMatch', 'flash-delay', type='int')) 'extensions', 'ParenMatch', 'flash-delay', type='int'))
self.paren_bell.set(idleConf.GetOption( self.paren_bell.set(idleConf.GetOption(
'extensions', 'ParenMatch', 'bell')) 'extensions', 'ParenMatch', 'bell'))
self.format_width.set(idleConf.GetOption(
'extensions', 'FormatParagraph', 'max-width', type='int'))
class ShedPage(Frame):
def __init__(self, master):
super().__init__(master)
self.init_validators()
self.create_page_shed()
self.load_shelled_cfg()
def init_validators(self):
digits_or_empty_re = re.compile(r'[0-9]*')
def is_digits_or_empty(s):
"Return 's is blank or contains only digits'"
return digits_or_empty_re.fullmatch(s) is not None
self.digits_only = (self.register(is_digits_or_empty), '%P',)
def create_page_shed(self):
"""Return frame of widgets for Shell/Ed tab.
Enable users to provisionally change shell and editor options.
Function load_shed_cfg initializes tk variables using idleConf.
Entry box auto_squeeze_min_lines_int sets
auto_squeeze_min_lines_int. Setting var_name invokes the
default callback that adds option to changes.
Widgets for ShedPage(Frame): (*) widgets bound to self
frame_shell: LabelFrame
frame_auto_squeeze_min_lines: Frame
auto_squeeze_min_lines_title: Label
(*)auto_squeeze_min_lines_int: Entry -
auto_squeeze_min_lines
frame_editor: LabelFrame
frame_save: Frame
run_save_title: Label
(*)save_ask_on: Radiobutton - autosave
(*)save_auto_on: Radiobutton - autosave
frame_format: Frame
format_width_title: Label
(*)format_width_int: Entry - format_width
frame_line_numbers_default: Frame
line_numbers_default_title: Label
(*)line_numbers_default_bool: Checkbutton - line_numbers_default
frame_context: Frame
context_title: Label
(*)context_int: Entry - context_lines
"""
# Integer values need StringVar because int('') raises.
self.auto_squeeze_min_lines = tracers.add(
StringVar(self), ('main', 'PyShell', 'auto-squeeze-min-lines'))
self.autosave = tracers.add(
IntVar(self), ('main', 'General', 'autosave'))
self.line_numbers_default = tracers.add(
BooleanVar(self),
('main', 'EditorWindow', 'line-numbers-default'))
self.context_lines = tracers.add(
StringVar(self), ('extensions', 'CodeContext', 'maxlines'))
# Create widgets:
frame_shell = LabelFrame(self, borderwidth=2, relief=GROOVE,
text=' Shell Preferences')
frame_editor = LabelFrame(self, borderwidth=2, relief=GROOVE,
text=' Editor Preferences')
# Frame_shell.
frame_auto_squeeze_min_lines = Frame(frame_shell, borderwidth=0)
auto_squeeze_min_lines_title = Label(frame_auto_squeeze_min_lines,
text='Auto-Squeeze Min. Lines:')
self.auto_squeeze_min_lines_int = Entry(
frame_auto_squeeze_min_lines, width=4,
textvariable=self.auto_squeeze_min_lines,
validatecommand=self.digits_only, validate='key',
)
# Frame_editor.
frame_save = Frame(frame_editor, borderwidth=0)
run_save_title = Label(frame_save, text='At Start of Run (F5) ')
self.save_ask_on = Radiobutton(
frame_save, variable=self.autosave, value=0,
text="Prompt to Save")
self.save_auto_on = Radiobutton(
frame_save, variable=self.autosave, value=1,
text='No Prompt')
frame_line_numbers_default = Frame(frame_editor, borderwidth=0)
line_numbers_default_title = Label(
frame_line_numbers_default, text='Show line numbers in new windows')
self.line_numbers_default_bool = Checkbutton(
frame_line_numbers_default,
variable=self.line_numbers_default,
width=1)
frame_context = Frame(frame_editor, borderwidth=0)
context_title = Label(frame_context, text='Max Context Lines :')
self.context_int = Entry(
frame_context, textvariable=self.context_lines, width=3,
validatecommand=self.digits_only, validate='key',
)
# Pack widgets:
frame_shell.pack(side=TOP, padx=5, pady=5, fill=BOTH)
Label(self).pack() # Spacer -- better solution?
frame_editor.pack(side=TOP, padx=5, pady=5, fill=BOTH)
# frame_auto_squeeze_min_lines
frame_auto_squeeze_min_lines.pack(side=TOP, padx=5, pady=0, fill=X)
auto_squeeze_min_lines_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.auto_squeeze_min_lines_int.pack(side=TOP, padx=5, pady=5)
# frame_save.
frame_save.pack(side=TOP, padx=5, pady=0, fill=X)
run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.save_auto_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
self.save_ask_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
# frame_line_numbers_default.
frame_line_numbers_default.pack(side=TOP, padx=5, pady=0, fill=X)
line_numbers_default_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.line_numbers_default_bool.pack(side=LEFT, padx=5, pady=5)
# frame_context.
frame_context.pack(side=TOP, padx=5, pady=0, fill=X)
context_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.context_int.pack(side=TOP, padx=5, pady=5)
def load_shelled_cfg(self): def load_shelled_cfg(self):
# Set variables for shell windows.
self.auto_squeeze_min_lines.set(idleConf.GetOption(
'main', 'PyShell', 'auto-squeeze-min-lines', type='int'))
# Set variables for editor windows. # Set variables for editor windows.
self.autosave.set(idleConf.GetOption( self.autosave.set(idleConf.GetOption(
'main', 'General', 'autosave', default=0, type='bool')) 'main', 'General', 'autosave', default=0, type='bool'))
self.format_width.set(idleConf.GetOption(
'extensions', 'FormatParagraph', 'max-width', type='int'))
self.line_numbers_default.set(idleConf.GetOption( self.line_numbers_default.set(idleConf.GetOption(
'main', 'EditorWindow', 'line-numbers-default', type='bool')) 'main', 'EditorWindow', 'line-numbers-default', type='bool'))
self.context_lines.set(idleConf.GetOption( self.context_lines.set(idleConf.GetOption(
'extensions', 'CodeContext', 'maxlines', type='int')) 'extensions', 'CodeContext', 'maxlines', type='int'))
# Set variables for shell windows.
self.auto_squeeze_min_lines.set(idleConf.GetOption(
'main', 'PyShell', 'auto-squeeze-min-lines', type='int'))
class ExtPage(Frame): class ExtPage(Frame):
def __init__(self, master): def __init__(self, master):

View file

@ -1203,7 +1203,7 @@ def test_delete_custom_keys(self):
del d.askyesno del d.askyesno
class GenPageTest(unittest.TestCase): class WinPageTest(unittest.TestCase):
"""Test that general tab widgets enable users to make changes. """Test that general tab widgets enable users to make changes.
Test that widget actions set vars, that var changes add Test that widget actions set vars, that var changes add
@ -1211,24 +1211,22 @@ class GenPageTest(unittest.TestCase):
""" """
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
page = cls.page = dialog.genpage page = cls.page = dialog.winpage
dialog.note.select(page) dialog.note.select(page)
page.update() page.update()
def setUp(self): def setUp(self):
changes.clear() changes.clear()
def test_load_general_cfg(self): def test_load_windows_cfg(self):
# Set to wrong values, load, check right values. # Set to wrong values, load, check right values.
eq = self.assertEqual eq = self.assertEqual
d = self.page d = self.page
d.startup_edit.set(1) d.startup_edit.set(1)
d.autosave.set(1)
d.win_width.set(1) d.win_width.set(1)
d.win_height.set(1) d.win_height.set(1)
d.load_general_cfg() d.load_windows_cfg()
eq(d.startup_edit.get(), 0) eq(d.startup_edit.get(), 0)
eq(d.autosave.get(), 0)
eq(d.win_width.get(), '80') eq(d.win_width.get(), '80')
eq(d.win_height.get(), '40') eq(d.win_height.get(), '40')
@ -1274,6 +1272,35 @@ def test_parenmatch(self):
d.bell_on.invoke() d.bell_on.invoke()
eq(extpage, {'ParenMatch': {'bell': 'False'}}) eq(extpage, {'ParenMatch': {'bell': 'False'}})
def test_paragraph(self):
self.page.format_width_int.delete(0, 'end')
self.page.format_width_int.insert(0, '11')
self.assertEqual(extpage, {'FormatParagraph': {'max-width': '11'}})
class GenPageTest(unittest.TestCase):
"""Test that shed tab widgets enable users to make changes.
Test that widget actions set vars, that var changes add
options to changes.
"""
@classmethod
def setUpClass(cls):
page = cls.page = dialog.shedpage
dialog.note.select(page)
page.update()
def setUp(self):
changes.clear()
def test_load_shelled_cfg(self):
# Set to wrong values, load, check right values.
eq = self.assertEqual
d = self.page
d.autosave.set(1)
d.load_shelled_cfg()
eq(d.autosave.get(), 0)
def test_autosave(self): def test_autosave(self):
d = self.page d = self.page
d.save_auto_on.invoke() d.save_auto_on.invoke()
@ -1281,11 +1308,6 @@ def test_autosave(self):
d.save_ask_on.invoke() d.save_ask_on.invoke()
self.assertEqual(mainpage, {'General': {'autosave': '0'}}) self.assertEqual(mainpage, {'General': {'autosave': '0'}})
def test_paragraph(self):
self.page.format_width_int.delete(0, 'end')
self.page.format_width_int.insert(0, '11')
self.assertEqual(extpage, {'FormatParagraph': {'max-width': '11'}})
def test_context(self): def test_context(self):
self.page.context_int.delete(0, 'end') self.page.context_int.delete(0, 'end')
self.page.context_int.insert(0, '1') self.page.context_int.insert(0, '1')

View file

@ -1,2 +1,4 @@
Shorten settings dialog by moving help sources to extensions tab. This will Split the settings dialog General tab into Windows and Shell/ED tabs.
improve issues with dialog being too tall for some screens. Move help sources, which extend the Help menu, to the Extensions tab.
Make space for new options and shorten the dialog.
The latter makes the dialog better fit small screens.