mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-40468: Factor out class ExtPage in idlelib.configdialog (GH-26618)
(cherry picked from commit 5571cabf1b)
Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
			
			
This commit is contained in:
		
							parent
							
								
									c03f0ab259
								
							
						
					
					
						commit
						c8353239ed
					
				
					 2 changed files with 216 additions and 201 deletions
				
			
		|  | @ -112,11 +112,11 @@ def create_widgets(self): | |||
|         self.frame = frame = Frame(self, padding="5px") | ||||
|         self.frame.grid(sticky="nwes") | ||||
|         self.note = note = Notebook(frame) | ||||
|         self.highpage = HighPage(note) | ||||
|         self.extpage = ExtPage(note) | ||||
|         self.highpage = HighPage(note, self.extpage) | ||||
|         self.fontpage = FontPage(note, self.highpage) | ||||
|         self.keyspage = KeysPage(note) | ||||
|         self.keyspage = KeysPage(note, self.extpage) | ||||
|         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 ') | ||||
|  | @ -244,198 +244,6 @@ def activate_config_changes(self): | |||
|         for klass in reloadables: | ||||
|             klass.reload() | ||||
| 
 | ||||
|     def create_page_extensions(self): | ||||
|         """Part of the config dialog used for configuring IDLE extensions. | ||||
| 
 | ||||
|         This code is generic - it works for any and all IDLE extensions. | ||||
| 
 | ||||
|         IDLE extensions save their configuration options using idleConf. | ||||
|         This code reads the current configuration using idleConf, supplies a | ||||
|         GUI interface to change the configuration values, and saves the | ||||
|         changes using idleConf. | ||||
| 
 | ||||
|         Not all changes take effect immediately - some may require restarting IDLE. | ||||
|         This depends on each extension's implementation. | ||||
| 
 | ||||
|         All values are treated as text, and it is up to the user to supply | ||||
|         reasonable values. The only exception to this are the 'enable*' options, | ||||
|         which are boolean, and can be toggled with a True/False button. | ||||
| 
 | ||||
|         Methods: | ||||
|             load_extensions: | ||||
|             extension_selected: Handle selection from list. | ||||
|             create_extension_frame: Hold widgets for one extension. | ||||
|             set_extension_value: Set in userCfg['extensions']. | ||||
|             save_all_changed_extensions: Call extension page Save(). | ||||
|         """ | ||||
|         self.ext_defaultCfg = idleConf.defaultCfg['extensions'] | ||||
|         self.ext_userCfg = idleConf.userCfg['extensions'] | ||||
|         self.is_int = self.register(is_int) | ||||
|         self.load_extensions() | ||||
|         # Create widgets - a listbox shows all available extensions, with the | ||||
|         # controls for the extension selected in the listbox to the right. | ||||
|         self.extension_names = StringVar(self) | ||||
|         frame = Frame(self.note) | ||||
|         frame_ext = LabelFrame(frame, borderwidth=2, relief=GROOVE, | ||||
|                                text=' Feature Extensions ') | ||||
|         frame_ext.rowconfigure(0, weight=1) | ||||
|         frame_ext.columnconfigure(2, weight=1) | ||||
|         self.extension_list = Listbox(frame_ext, listvariable=self.extension_names, | ||||
|                                       selectmode='browse') | ||||
|         self.extension_list.bind('<<ListboxSelect>>', self.extension_selected) | ||||
|         scroll = Scrollbar(frame_ext, command=self.extension_list.yview) | ||||
|         self.extension_list.yscrollcommand=scroll.set | ||||
|         self.details_frame = LabelFrame(frame_ext, width=250, height=250) | ||||
|         self.extension_list.grid(column=0, row=0, sticky='nws') | ||||
|         scroll.grid(column=1, row=0, sticky='ns') | ||||
|         self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0]) | ||||
|         frame_ext.configure(padding=10) | ||||
|         self.config_frame = {} | ||||
|         self.current_extension = None | ||||
| 
 | ||||
|         self.outerframe = self                      # TEMPORARY | ||||
|         self.tabbed_page_set = self.extension_list  # TEMPORARY | ||||
| 
 | ||||
|         # Create the frame holding controls for each extension. | ||||
|         ext_names = '' | ||||
|         for ext_name in sorted(self.extensions): | ||||
|             self.create_extension_frame(ext_name) | ||||
|             ext_names = ext_names + '{' + ext_name + '} ' | ||||
|         self.extension_names.set(ext_names) | ||||
|         self.extension_list.selection_set(0) | ||||
|         self.extension_selected(None) | ||||
| 
 | ||||
| 
 | ||||
|         self.frame_help = HelpFrame(frame, borderwidth=2, relief=GROOVE, | ||||
|                                     text=' Help Menu Extensions ') | ||||
|         frame_ext.grid(row=0, column=0, sticky='nsew') | ||||
|         Label(frame).grid(row=1, column=0) | ||||
|         self.frame_help.grid(row=2, column=0, sticky='sew') | ||||
| 
 | ||||
|         return frame | ||||
| 
 | ||||
|     def load_extensions(self): | ||||
|         "Fill self.extensions with data from the default and user configs." | ||||
|         self.extensions = {} | ||||
|         for ext_name in idleConf.GetExtensions(active_only=False): | ||||
|             # Former built-in extensions are already filtered out. | ||||
|             self.extensions[ext_name] = [] | ||||
| 
 | ||||
|         for ext_name in self.extensions: | ||||
|             opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) | ||||
| 
 | ||||
|             # Bring 'enable' options to the beginning of the list. | ||||
|             enables = [opt_name for opt_name in opt_list | ||||
|                        if opt_name.startswith('enable')] | ||||
|             for opt_name in enables: | ||||
|                 opt_list.remove(opt_name) | ||||
|             opt_list = enables + opt_list | ||||
| 
 | ||||
|             for opt_name in opt_list: | ||||
|                 def_str = self.ext_defaultCfg.Get( | ||||
|                         ext_name, opt_name, raw=True) | ||||
|                 try: | ||||
|                     def_obj = {'True':True, 'False':False}[def_str] | ||||
|                     opt_type = 'bool' | ||||
|                 except KeyError: | ||||
|                     try: | ||||
|                         def_obj = int(def_str) | ||||
|                         opt_type = 'int' | ||||
|                     except ValueError: | ||||
|                         def_obj = def_str | ||||
|                         opt_type = None | ||||
|                 try: | ||||
|                     value = self.ext_userCfg.Get( | ||||
|                             ext_name, opt_name, type=opt_type, raw=True, | ||||
|                             default=def_obj) | ||||
|                 except ValueError:  # Need this until .Get fixed. | ||||
|                     value = def_obj  # Bad values overwritten by entry. | ||||
|                 var = StringVar(self) | ||||
|                 var.set(str(value)) | ||||
| 
 | ||||
|                 self.extensions[ext_name].append({'name': opt_name, | ||||
|                                                   'type': opt_type, | ||||
|                                                   'default': def_str, | ||||
|                                                   'value': value, | ||||
|                                                   'var': var, | ||||
|                                                  }) | ||||
| 
 | ||||
|     def extension_selected(self, event): | ||||
|         "Handle selection of an extension from the list." | ||||
|         newsel = self.extension_list.curselection() | ||||
|         if newsel: | ||||
|             newsel = self.extension_list.get(newsel) | ||||
|         if newsel is None or newsel != self.current_extension: | ||||
|             if self.current_extension: | ||||
|                 self.details_frame.config(text='') | ||||
|                 self.config_frame[self.current_extension].grid_forget() | ||||
|                 self.current_extension = None | ||||
|         if newsel: | ||||
|             self.details_frame.config(text=newsel) | ||||
|             self.config_frame[newsel].grid(column=0, row=0, sticky='nsew') | ||||
|             self.current_extension = newsel | ||||
| 
 | ||||
|     def create_extension_frame(self, ext_name): | ||||
|         """Create a frame holding the widgets to configure one extension""" | ||||
|         f = VerticalScrolledFrame(self.details_frame, height=250, width=250) | ||||
|         self.config_frame[ext_name] = f | ||||
|         entry_area = f.interior | ||||
|         # Create an entry for each configuration option. | ||||
|         for row, opt in enumerate(self.extensions[ext_name]): | ||||
|             # Create a row with a label and entry/checkbutton. | ||||
|             label = Label(entry_area, text=opt['name']) | ||||
|             label.grid(row=row, column=0, sticky=NW) | ||||
|             var = opt['var'] | ||||
|             if opt['type'] == 'bool': | ||||
|                 Checkbutton(entry_area, variable=var, | ||||
|                             onvalue='True', offvalue='False', width=8 | ||||
|                             ).grid(row=row, column=1, sticky=W, padx=7) | ||||
|             elif opt['type'] == 'int': | ||||
|                 Entry(entry_area, textvariable=var, validate='key', | ||||
|                       validatecommand=(self.is_int, '%P'), width=10 | ||||
|                       ).grid(row=row, column=1, sticky=NSEW, padx=7) | ||||
| 
 | ||||
|             else:  # type == 'str' | ||||
|                 # Limit size to fit non-expanding space with larger font. | ||||
|                 Entry(entry_area, textvariable=var, width=15 | ||||
|                       ).grid(row=row, column=1, sticky=NSEW, padx=7) | ||||
|         return | ||||
| 
 | ||||
|     def set_extension_value(self, section, opt): | ||||
|         """Return True if the configuration was added or changed. | ||||
| 
 | ||||
|         If the value is the same as the default, then remove it | ||||
|         from user config file. | ||||
|         """ | ||||
|         name = opt['name'] | ||||
|         default = opt['default'] | ||||
|         value = opt['var'].get().strip() or default | ||||
|         opt['var'].set(value) | ||||
|         # if self.defaultCfg.has_section(section): | ||||
|         # Currently, always true; if not, indent to return. | ||||
|         if (value == default): | ||||
|             return self.ext_userCfg.RemoveOption(section, name) | ||||
|         # Set the option. | ||||
|         return self.ext_userCfg.SetOption(section, name, value) | ||||
| 
 | ||||
|     def save_all_changed_extensions(self): | ||||
|         """Save configuration changes to the user config file. | ||||
| 
 | ||||
|         Attributes accessed: | ||||
|             extensions | ||||
| 
 | ||||
|         Methods: | ||||
|             set_extension_value | ||||
|         """ | ||||
|         has_changes = False | ||||
|         for ext_name in self.extensions: | ||||
|             options = self.extensions[ext_name] | ||||
|             for opt in options: | ||||
|                 if self.set_extension_value(ext_name, opt): | ||||
|                     has_changes = True | ||||
|         if has_changes: | ||||
|             self.ext_userCfg.Save() | ||||
| 
 | ||||
| 
 | ||||
| # class TabPage(Frame):  # A template for Page classes. | ||||
| #     def __init__(self, master): | ||||
|  | @ -695,8 +503,9 @@ def var_changed_space_num(self, *params): | |||
| 
 | ||||
| class HighPage(Frame): | ||||
| 
 | ||||
|     def __init__(self, master): | ||||
|     def __init__(self, master, extpage): | ||||
|         super().__init__(master) | ||||
|         self.extpage = extpage | ||||
|         self.cd = master.winfo_toplevel() | ||||
|         self.style = Style(master) | ||||
|         self.create_page_highlight() | ||||
|  | @ -1347,15 +1156,16 @@ def delete_custom(self): | |||
|         self.builtin_name.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) | ||||
|         # User can't back out of these changes, they must be applied now. | ||||
|         changes.save_all() | ||||
|         self.cd.save_all_changed_extensions() | ||||
|         self.extpage.save_all_changed_extensions() | ||||
|         self.cd.activate_config_changes() | ||||
|         self.set_theme_type() | ||||
| 
 | ||||
| 
 | ||||
| class KeysPage(Frame): | ||||
| 
 | ||||
|     def __init__(self, master): | ||||
|     def __init__(self, master, extpage): | ||||
|         super().__init__(master) | ||||
|         self.extpage = extpage | ||||
|         self.cd = master.winfo_toplevel() | ||||
|         self.create_page_keys() | ||||
|         self.load_key_cfg() | ||||
|  | @ -1779,7 +1589,7 @@ def delete_custom_keys(self): | |||
|                               or idleConf.default_keys()) | ||||
|         # User can't back out of these changes, they must be applied now. | ||||
|         changes.save_all() | ||||
|         self.cd.save_all_changed_extensions() | ||||
|         self.extpage.save_all_changed_extensions() | ||||
|         self.cd.activate_config_changes() | ||||
|         self.set_keys_type() | ||||
| 
 | ||||
|  | @ -2094,6 +1904,201 @@ def load_shelled_cfg(self): | |||
|                 'main', 'PyShell', 'auto-squeeze-min-lines', type='int')) | ||||
| 
 | ||||
| 
 | ||||
| class ExtPage(Frame): | ||||
|     def __init__(self, master): | ||||
|         super().__init__(master) | ||||
|         self.ext_defaultCfg = idleConf.defaultCfg['extensions'] | ||||
|         self.ext_userCfg = idleConf.userCfg['extensions'] | ||||
|         self.is_int = self.register(is_int) | ||||
|         self.load_extensions() | ||||
|         self.create_page_extensions()  # Requires extension names. | ||||
| 
 | ||||
|     def create_page_extensions(self): | ||||
|         """Configure IDLE feature extensions and help menu extensions. | ||||
| 
 | ||||
|         List the feature extensions and a configuration box for the | ||||
|         selected extension.  Help menu extensions are in a HelpFrame. | ||||
| 
 | ||||
|         This code reads the current configuration using idleConf, | ||||
|         supplies a GUI interface to change the configuration values, | ||||
|         and saves the changes using idleConf. | ||||
| 
 | ||||
|         Some changes may require restarting IDLE.  This depends on each | ||||
|         extension's implementation. | ||||
| 
 | ||||
|         All values are treated as text, and it is up to the user to | ||||
|         supply reasonable values. The only exception to this are the | ||||
|         'enable*' options, which are boolean, and can be toggled with a | ||||
|         True/False button. | ||||
| 
 | ||||
|         Methods: | ||||
|             extension_selected: Handle selection from list. | ||||
|             create_extension_frame: Hold widgets for one extension. | ||||
|             set_extension_value: Set in userCfg['extensions']. | ||||
|             save_all_changed_extensions: Call extension page Save(). | ||||
|         """ | ||||
|         self.extension_names = StringVar(self) | ||||
| 
 | ||||
|         frame_ext = LabelFrame(self, borderwidth=2, relief=GROOVE, | ||||
|                                text=' Feature Extensions ') | ||||
|         self.frame_help = HelpFrame(self, borderwidth=2, relief=GROOVE, | ||||
|                                text=' Help Menu Extensions ') | ||||
| 
 | ||||
|         frame_ext.rowconfigure(0, weight=1) | ||||
|         frame_ext.columnconfigure(2, weight=1) | ||||
|         self.extension_list = Listbox(frame_ext, listvariable=self.extension_names, | ||||
|                                       selectmode='browse') | ||||
|         self.extension_list.bind('<<ListboxSelect>>', self.extension_selected) | ||||
|         scroll = Scrollbar(frame_ext, command=self.extension_list.yview) | ||||
|         self.extension_list.yscrollcommand=scroll.set | ||||
|         self.details_frame = LabelFrame(frame_ext, width=250, height=250) | ||||
|         self.extension_list.grid(column=0, row=0, sticky='nws') | ||||
|         scroll.grid(column=1, row=0, sticky='ns') | ||||
|         self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0]) | ||||
|         frame_ext.configure(padding=10) | ||||
|         self.config_frame = {} | ||||
|         self.current_extension = None | ||||
| 
 | ||||
|         self.outerframe = self                      # TEMPORARY | ||||
|         self.tabbed_page_set = self.extension_list  # TEMPORARY | ||||
| 
 | ||||
|         # Create the frame holding controls for each extension. | ||||
|         ext_names = '' | ||||
|         for ext_name in sorted(self.extensions): | ||||
|             self.create_extension_frame(ext_name) | ||||
|             ext_names = ext_names + '{' + ext_name + '} ' | ||||
|         self.extension_names.set(ext_names) | ||||
|         self.extension_list.selection_set(0) | ||||
|         self.extension_selected(None) | ||||
| 
 | ||||
| 
 | ||||
|         frame_ext.grid(row=0, column=0, sticky='nsew') | ||||
|         Label(self).grid(row=1, column=0)  # Spacer.  Replace with config? | ||||
|         self.frame_help.grid(row=2, column=0, sticky='sew') | ||||
| 
 | ||||
|     def load_extensions(self): | ||||
|         "Fill self.extensions with data from the default and user configs." | ||||
|         self.extensions = {} | ||||
|         for ext_name in idleConf.GetExtensions(active_only=False): | ||||
|             # Former built-in extensions are already filtered out. | ||||
|             self.extensions[ext_name] = [] | ||||
| 
 | ||||
|         for ext_name in self.extensions: | ||||
|             opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) | ||||
| 
 | ||||
|             # Bring 'enable' options to the beginning of the list. | ||||
|             enables = [opt_name for opt_name in opt_list | ||||
|                        if opt_name.startswith('enable')] | ||||
|             for opt_name in enables: | ||||
|                 opt_list.remove(opt_name) | ||||
|             opt_list = enables + opt_list | ||||
| 
 | ||||
|             for opt_name in opt_list: | ||||
|                 def_str = self.ext_defaultCfg.Get( | ||||
|                         ext_name, opt_name, raw=True) | ||||
|                 try: | ||||
|                     def_obj = {'True':True, 'False':False}[def_str] | ||||
|                     opt_type = 'bool' | ||||
|                 except KeyError: | ||||
|                     try: | ||||
|                         def_obj = int(def_str) | ||||
|                         opt_type = 'int' | ||||
|                     except ValueError: | ||||
|                         def_obj = def_str | ||||
|                         opt_type = None | ||||
|                 try: | ||||
|                     value = self.ext_userCfg.Get( | ||||
|                             ext_name, opt_name, type=opt_type, raw=True, | ||||
|                             default=def_obj) | ||||
|                 except ValueError:  # Need this until .Get fixed. | ||||
|                     value = def_obj  # Bad values overwritten by entry. | ||||
|                 var = StringVar(self) | ||||
|                 var.set(str(value)) | ||||
| 
 | ||||
|                 self.extensions[ext_name].append({'name': opt_name, | ||||
|                                                   'type': opt_type, | ||||
|                                                   'default': def_str, | ||||
|                                                   'value': value, | ||||
|                                                   'var': var, | ||||
|                                                  }) | ||||
| 
 | ||||
|     def extension_selected(self, event): | ||||
|         "Handle selection of an extension from the list." | ||||
|         newsel = self.extension_list.curselection() | ||||
|         if newsel: | ||||
|             newsel = self.extension_list.get(newsel) | ||||
|         if newsel is None or newsel != self.current_extension: | ||||
|             if self.current_extension: | ||||
|                 self.details_frame.config(text='') | ||||
|                 self.config_frame[self.current_extension].grid_forget() | ||||
|                 self.current_extension = None | ||||
|         if newsel: | ||||
|             self.details_frame.config(text=newsel) | ||||
|             self.config_frame[newsel].grid(column=0, row=0, sticky='nsew') | ||||
|             self.current_extension = newsel | ||||
| 
 | ||||
|     def create_extension_frame(self, ext_name): | ||||
|         """Create a frame holding the widgets to configure one extension""" | ||||
|         f = VerticalScrolledFrame(self.details_frame, height=250, width=250) | ||||
|         self.config_frame[ext_name] = f | ||||
|         entry_area = f.interior | ||||
|         # Create an entry for each configuration option. | ||||
|         for row, opt in enumerate(self.extensions[ext_name]): | ||||
|             # Create a row with a label and entry/checkbutton. | ||||
|             label = Label(entry_area, text=opt['name']) | ||||
|             label.grid(row=row, column=0, sticky=NW) | ||||
|             var = opt['var'] | ||||
|             if opt['type'] == 'bool': | ||||
|                 Checkbutton(entry_area, variable=var, | ||||
|                             onvalue='True', offvalue='False', width=8 | ||||
|                             ).grid(row=row, column=1, sticky=W, padx=7) | ||||
|             elif opt['type'] == 'int': | ||||
|                 Entry(entry_area, textvariable=var, validate='key', | ||||
|                       validatecommand=(self.is_int, '%P'), width=10 | ||||
|                       ).grid(row=row, column=1, sticky=NSEW, padx=7) | ||||
| 
 | ||||
|             else:  # type == 'str' | ||||
|                 # Limit size to fit non-expanding space with larger font. | ||||
|                 Entry(entry_area, textvariable=var, width=15 | ||||
|                       ).grid(row=row, column=1, sticky=NSEW, padx=7) | ||||
|         return | ||||
| 
 | ||||
|     def set_extension_value(self, section, opt): | ||||
|         """Return True if the configuration was added or changed. | ||||
| 
 | ||||
|         If the value is the same as the default, then remove it | ||||
|         from user config file. | ||||
|         """ | ||||
|         name = opt['name'] | ||||
|         default = opt['default'] | ||||
|         value = opt['var'].get().strip() or default | ||||
|         opt['var'].set(value) | ||||
|         # if self.defaultCfg.has_section(section): | ||||
|         # Currently, always true; if not, indent to return. | ||||
|         if (value == default): | ||||
|             return self.ext_userCfg.RemoveOption(section, name) | ||||
|         # Set the option. | ||||
|         return self.ext_userCfg.SetOption(section, name, value) | ||||
| 
 | ||||
|     def save_all_changed_extensions(self): | ||||
|         """Save configuration changes to the user config file. | ||||
| 
 | ||||
|         Attributes accessed: | ||||
|             extensions | ||||
| 
 | ||||
|         Methods: | ||||
|             set_extension_value | ||||
|         """ | ||||
|         has_changes = False | ||||
|         for ext_name in self.extensions: | ||||
|             options = self.extensions[ext_name] | ||||
|             for opt in options: | ||||
|                 if self.set_extension_value(ext_name, opt): | ||||
|                     has_changes = True | ||||
|         if has_changes: | ||||
|             self.ext_userCfg.Save() | ||||
| 
 | ||||
| 
 | ||||
| class HelpFrame(LabelFrame): | ||||
| 
 | ||||
|     def __init__(self, master, **cfg): | ||||
|  |  | |||
|  | @ -1292,12 +1292,22 @@ def test_context(self): | |||
|         self.assertEqual(extpage, {'CodeContext': {'maxlines': '1'}}) | ||||
| 
 | ||||
| 
 | ||||
| #unittest.skip("Nothing here yet TODO") | ||||
| class ExtPageTest(unittest.TestCase): | ||||
|     """Test that the help source list works correctly.""" | ||||
|     @classmethod | ||||
|     def setUpClass(cls): | ||||
|         page = dialog.extpage | ||||
|         dialog.note.select(page) | ||||
| 
 | ||||
| 
 | ||||
| class HelpSourceTest(unittest.TestCase): | ||||
|     """Test that the help source list works correctly.""" | ||||
|     @classmethod | ||||
|     def setUpClass(cls): | ||||
|         dialog.note.select(dialog.extpage) | ||||
|         frame = cls.frame = dialog.frame_help | ||||
|         page = dialog.extpage | ||||
|         dialog.note.select(page) | ||||
|         frame = cls.frame = page.frame_help | ||||
|         frame.set = frame.set_add_delete_state = Func() | ||||
|         frame.upc = frame.update_help_changes = Func() | ||||
|         frame.update() | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Miss Islington (bot)
						Miss Islington (bot)