diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 82596498d34611..bb4fcbf72fd608 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -41,6 +41,14 @@ Squeezer) +def init_validators(page): + 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 + return (page.register(is_digits_or_empty), '%P',) + + class ConfigDialog(Toplevel): """Config dialog for IDLE. """ @@ -101,6 +109,7 @@ def create_widgets(self): highpage: HighPage fontpage: FontPage keyspage: KeysPage + editpage: EditPage genpage: GenPage extpage: self.create_page_extensions @@ -113,11 +122,13 @@ def create_widgets(self): self.highpage = HighPage(note) self.fontpage = FontPage(note, self.highpage) self.keyspage = KeysPage(note) + self.editpage = EditPage(note) self.genpage = GenPage(note) self.extpage = self.create_page_extensions() note.add(self.fontpage, text='Fonts/Tabs') note.add(self.highpage, text='Highlights') note.add(self.keyspage, text=' Keys ') + note.add(self.editpage, text=' Editor/Shell ') note.add(self.genpage, text=' General ') note.add(self.extpage, text='Extensions') note.enable_traversal() @@ -1774,30 +1785,167 @@ def delete_custom_keys(self): self.set_keys_type() -class GenPage(Frame): +class EditPage(Frame): def __init__(self, master): super().__init__(master) + self.digits_only = init_validators(self) + self.create_page_editor() + self.load_editor_cfg() + + def create_page_editor(self): + """Return frame of widgets for Editor tab. + + Enable users to provisionally change editor options. Function + load_editor_cfg intializes tk variables using idleConf. + Radiobuttons save_ask_on and save_auto_on set var autosave. + Entry boxes format_width_int and context_int set var + format_width and context_lines. Setting var_name invokes + the default callback that adds option to changes. + + Widgets for EditPage(Frame): (*) widgets bound to self + 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 + 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 + """ + 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( + 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: + # Section frames. + frame_editor = LabelFrame(self, borderwidth=2, relief=GROOVE, + text=' Editor Preferences') + frame_shell = LabelFrame(self, borderwidth=2, relief=GROOVE, + text=' Shell Preferences') + + # 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, + text='Format Paragraph Max Width') + self.format_width_int = Entry( + frame_format, textvariable=self.format_width, width=4, + 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: + # Body. + 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_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.pack(side=TOP, padx=5, pady=0, fill=X) + format_width_title.pack(side=LEFT, anchor=W, padx=5, 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_editor_cfg(self): + "Load current configuration settings for the editor options." + # Set variables for editor windows. + self.autosave.set(idleConf.GetOption( + '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( + 'main', 'EditorWindow', 'line-numbers-default', type='bool')) + self.context_lines.set(idleConf.GetOption( + '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')) + - self.init_validators() +class GenPage(Frame): + + def __init__(self, master): + super().__init__(master) + self.digits_only = init_validators(self) self.create_page_general() self.load_general_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_general(self): """Return frame of widgets for General tab. Enable users to provisionally change general options. Function load_general_cfg initializes tk variables and helplist using idleConf. Radiobuttons startup_shell_on and startup_editor_on - set var startup_edit. Radiobuttons save_ask_on and save_auto_on - set var autosave. Entry boxes win_width_int and win_height_int + 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. @@ -1834,24 +1982,6 @@ def create_page_general(self): paren_time_title: Label (*)paren_flash_time: Entry - flash_delay (*)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 - 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 - 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_help: LabelFrame frame_helplist: Frame frame_helplist_buttons: Frame @@ -1879,27 +2009,10 @@ def create_page_general(self): self.paren_bell = tracers.add( 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( - 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: # Section frames. frame_window = LabelFrame(self, borderwidth=2, relief=GROOVE, 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_help = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Additional Help Sources ') # Frame_window. @@ -1954,49 +2067,6 @@ def create_page_general(self): self.bell_on = Checkbutton( frame_paren2, text="Bell on Mismatch", variable=self.paren_bell) - # 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, - text='Format Paragraph Max Width') - self.format_width_int = Entry( - frame_format, textvariable=self.format_width, width=4, - 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', - ) - # frame_help. frame_helplist = Frame(frame_help) frame_helplist_buttons = Frame(frame_helplist) @@ -2020,8 +2090,6 @@ def create_page_general(self): # Pack widgets: # Body. 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_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) # frame_run. frame_run.pack(side=TOP, padx=5, pady=0, fill=X) @@ -2052,29 +2120,6 @@ def create_page_general(self): 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) - # 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.pack(side=TOP, padx=5, pady=0, fill=X) - format_width_title.pack(side=LEFT, anchor=W, padx=5, 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) - # frame_help. frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y) frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) @@ -2104,20 +2149,6 @@ def load_general_cfg(self): self.paren_bell.set(idleConf.GetOption( 'extensions', 'ParenMatch', 'bell')) - # Set variables for editor windows. - self.autosave.set(idleConf.GetOption( - '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( - 'main', 'EditorWindow', 'line-numbers-default', type='bool')) - self.context_lines.set(idleConf.GetOption( - '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')) - # Set additional help sources. self.user_helplist = idleConf.GetAllExtraHelpSourcesList() self.helplist.delete(0, 'end') diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 1fea6d41df811c..8a66402a99a685 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -1203,6 +1203,71 @@ def test_delete_custom_keys(self): del d.askyesno +class EditPageTest(unittest.TestCase): + """Test that edtior tab widgets enable users to make changes. + + Test that widget actions set vars, that var changes add + options to changes and that helplist works correctly. + """ + @classmethod + def setUpClass(cls): + page = cls.page = dialog.editpage + dialog.note.select(page) + + @classmethod + def tearDownClass(cls): + page = cls.page + + def setUp(self): + changes.clear() + + def test_load_editor_cfg(self): + # Set to wrong values, load, check right values. + eq = self.assertEqual + d = self.page + d.autosave.set(1) + d.format_width.set(1) + d.context_lines.set(1) + d.line_numbers_default.set(True) + d.auto_squeeze_min_lines.set(1) + d.load_editor_cfg() + eq(d.autosave.get(), 0) + eq(d.format_width.get(), '72') + eq(d.context_lines.get(), '15') + eq(d.line_numbers_default.get(), False) + eq(d.auto_squeeze_min_lines.get(), '50') + + def test_autosave(self): + d = self.page + d.save_auto_on.invoke() + self.assertEqual(mainpage, {'General': {'autosave': '1'}}) + d.save_ask_on.invoke() + 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): + self.page.context_int.delete(0, 'end') + self.page.context_int.insert(0, '1') + self.assertEqual(extpage, {'CodeContext': {'maxlines': '1'}}) + + def test_line_numbers(self): + d = self.page + d.line_numbers_default.set(True) + d.line_numbers_default_bool.invoke() + self.assertEqual(mainpage, {'EditorWindow': {'line-numbers-default': 'False'}}) + d.line_numbers_default_bool.invoke() + self.assertEqual(mainpage, {'EditorWindow': {'line-numbers-default': 'True'}}) + + def test_auto_squeeze(self): + self.page.auto_squeeze_min_lines_int.delete(0, 'end') + self.page.auto_squeeze_min_lines_int.insert(0, '99') + self.assertEqual(mainpage, {'PyShell': {'auto-squeeze-min-lines': '99'}}) + + class GenPageTest(unittest.TestCase): """Test that general tab widgets enable users to make changes. @@ -1233,7 +1298,6 @@ def test_load_general_cfg(self): eq = self.assertEqual d = self.page d.startup_edit.set(1) - d.autosave.set(1) d.win_width.set(1) d.win_height.set(1) d.helplist.insert('end', 'bad') @@ -1241,7 +1305,6 @@ def test_load_general_cfg(self): idleConf.SetOption('main', 'HelpFiles', '1', 'name;file') d.load_general_cfg() eq(d.startup_edit.get(), 0) - eq(d.autosave.get(), 0) eq(d.win_width.get(), '80') eq(d.win_height.get(), '40') eq(d.helplist.get(0, 'end'), ('name',)) @@ -1289,23 +1352,6 @@ def test_parenmatch(self): d.bell_on.invoke() eq(extpage, {'ParenMatch': {'bell': 'False'}}) - def test_autosave(self): - d = self.page - d.save_auto_on.invoke() - self.assertEqual(mainpage, {'General': {'autosave': '1'}}) - d.save_ask_on.invoke() - 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): - self.page.context_int.delete(0, 'end') - self.page.context_int.insert(0, '1') - self.assertEqual(extpage, {'CodeContext': {'maxlines': '1'}}) - def test_source_selected(self): d = self.page d.set = d.set_add_delete_state diff --git a/Misc/NEWS.d/next/IDLE/2018-03-11-15-29-02.bpo-33051.4tKnde.rst b/Misc/NEWS.d/next/IDLE/2018-03-11-15-29-02.bpo-33051.4tKnde.rst new file mode 100644 index 00000000000000..1a970c124f8417 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-03-11-15-29-02.bpo-33051.4tKnde.rst @@ -0,0 +1 @@ +Separate editor options from the general tab in config dialog.