diff options
author | Terry Jan Reedy <tjreedy@udel.edu> | 2015-10-14 02:03:51 (GMT) |
---|---|---|
committer | Terry Jan Reedy <tjreedy@udel.edu> | 2015-10-14 02:03:51 (GMT) |
commit | 93f3542ae4501a8520cb6769db82038986ecf8d5 (patch) | |
tree | 1348dc3b5e27fe964029a1a46df1eead86b1ffe0 | |
parent | 5805ddeedb5624aa6677f5e03e6f505c9920495f (diff) | |
download | cpython-93f3542ae4501a8520cb6769db82038986ecf8d5.zip cpython-93f3542ae4501a8520cb6769db82038986ecf8d5.tar.gz cpython-93f3542ae4501a8520cb6769db82038986ecf8d5.tar.bz2 |
Issue #24782: Finish converting the Configure Extension dialog into a new
tab in the IDLE Preferences dialog. Code patch by Mark Roseman.
-rw-r--r-- | Doc/library/idle.rst | 21 | ||||
-rw-r--r-- | Lib/idlelib/Bindings.py | 1 | ||||
-rw-r--r-- | Lib/idlelib/EditorWindow.py | 6 | ||||
-rw-r--r-- | Lib/idlelib/configDialog.py | 300 | ||||
-rw-r--r-- | Lib/idlelib/help.html | 22 | ||||
-rw-r--r-- | Lib/idlelib/idle_test/htest.py | 12 |
6 files changed, 154 insertions, 208 deletions
diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 2797057..4384d56 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -252,17 +252,16 @@ Options menu (Shell and Editor) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Configure IDLE - Open a configuration dialog. Fonts, indentation, keybindings, and color - themes may be altered. Startup Preferences may be set, and additional - help sources can be specified. Non-default user setting are saved in a - .idlerc directory in the user's home directory. Problems caused by bad user - configuration files are solved by editing or deleting one or more of the - files in .idlerc. On OS X, open the configuration dialog by selecting - Preferences in the application menu. - -Configure Extensions - Open a configuration dialog for setting preferences for extensions - (discussed below). See note above about the location of user settings. + Open a configuration dialog and change preferences for the following: + fonts, indentation, keybindings, text color themes, startup windows and + size, additional help sources, and extensions (see below). On OS X, + open the configuration dialog by selecting Preferences in the application + menu. To use a new built-in color theme (IDLE Dark) with older IDLEs, + save it as a new custom theme. + + Non-default user settings are saved in a .idlerc directory in the user's + home directory. Problems caused by bad user configuration files are solved + by editing or deleting one or more of the files in .idlerc. Code Context (toggle)(Editor Window only) Open a pane at the top of the edit window which shows the block context diff --git a/Lib/idlelib/Bindings.py b/Lib/idlelib/Bindings.py index 226671c..ab25ff1 100644 --- a/Lib/idlelib/Bindings.py +++ b/Lib/idlelib/Bindings.py @@ -78,7 +78,6 @@ menudefs = [ ]), ('options', [ ('Configure _IDLE', '<<open-config-dialog>>'), - ('Configure _Extensions', '<<open-config-extensions-dialog>>'), None, ]), ('help', [ diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py index a634d1f..0196344 100644 --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -191,8 +191,6 @@ class EditorWindow(object): text.bind("<<python-docs>>", self.python_docs) text.bind("<<about-idle>>", self.about_dialog) text.bind("<<open-config-dialog>>", self.config_dialog) - text.bind("<<open-config-extensions-dialog>>", - self.config_extensions_dialog) text.bind("<<open-module>>", self.open_module) text.bind("<<do-nothing>>", lambda event: "break") text.bind("<<select-all>>", self.select_all) @@ -514,10 +512,6 @@ class EditorWindow(object): # Synchronize with macosxSupport.overrideRootMenu.config_dialog. configDialog.ConfigDialog(self.top,'Settings') - def config_extensions_dialog(self, event=None): - "Handle Options 'Configure Extensions' event." - configDialog.ConfigExtensionsDialog(self.top) - def help_dialog(self, event=None): "Handle Help 'IDLE Help' event." # Synchronize with macosxSupport.overrideRootMenu.help_dialog. diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py index 48cd991..3f1207b 100644 --- a/Lib/idlelib/configDialog.py +++ b/Lib/idlelib/configDialog.py @@ -80,12 +80,14 @@ class ConfigDialog(Toplevel): def CreateWidgets(self): self.tabPages = TabbedPageSet(self, - page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General']) + page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General', + 'Extensions']) self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH) self.CreatePageFontTab() self.CreatePageHighlight() self.CreatePageKeys() self.CreatePageGeneral() + self.CreatePageExtensions() self.create_action_buttons().pack(side=BOTTOM) def create_action_buttons(self): @@ -1092,6 +1094,7 @@ class ConfigDialog(Toplevel): self.LoadKeyCfg() ### general page self.LoadGeneralCfg() + # note: extension page handled separately def SaveNewKeySet(self, keySetName, keySet): """ @@ -1145,6 +1148,7 @@ class ConfigDialog(Toplevel): # save these even if unchanged! idleConf.userCfg[configType].Save() self.ResetChangedItems() #clear the changed items dict + self.save_all_changed_extensions() # uses a different mechanism def DeactivateCurrentConfig(self): #Before a config is saved, some cleanup of current @@ -1180,127 +1184,58 @@ class ConfigDialog(Toplevel): view_text(self, title='Help for IDLE preferences', text=help_common+help_pages.get(page, '')) -help_common = '''\ -When you click either the Apply or Ok buttons, settings in this -dialog that are different from IDLE's default are saved in -a .idlerc directory in your home directory. Except as noted, -hese changes apply to all versions of IDLE installed on this -machine. Some do not take affect until IDLE is restarted. -[Cancel] only cancels changes made since the last save. -''' -help_pages = { - 'Highlighting':''' -Highlighting: -The IDLE Dark color theme is new in Octover 2015. It can only -be used with older IDLE releases if it is saved as a custom -theme, with a different name. -''' -} - - -class VerticalScrolledFrame(Frame): - """A pure Tkinter vertically scrollable frame. - - * Use the 'interior' attribute to place widgets inside the scrollable frame - * Construct and pack/place/grid normally - * This frame only allows vertical scrolling - """ - def __init__(self, parent, *args, **kw): - Frame.__init__(self, parent, *args, **kw) - - # create a canvas object and a vertical scrollbar for scrolling it - vscrollbar = Scrollbar(self, orient=VERTICAL) - vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE) - canvas = Canvas(self, bd=0, highlightthickness=0, - yscrollcommand=vscrollbar.set) - canvas.pack(side=LEFT, fill=BOTH, expand=TRUE) - vscrollbar.config(command=canvas.yview) - - # reset the view - canvas.xview_moveto(0) - canvas.yview_moveto(0) - - # create a frame inside the canvas which will be scrolled with it - self.interior = interior = Frame(canvas) - interior_id = canvas.create_window(0, 0, window=interior, anchor=NW) - - # track changes to the canvas and frame width and sync them, - # also updating the scrollbar - def _configure_interior(event): - # update the scrollbars to match the size of the inner frame - size = (interior.winfo_reqwidth(), interior.winfo_reqheight()) - canvas.config(scrollregion="0 0 %s %s" % size) - interior.bind('<Configure>', _configure_interior) + def CreatePageExtensions(self): + """Part of the config dialog used for configuring IDLE extensions. - def _configure_canvas(event): - if interior.winfo_reqwidth() != canvas.winfo_width(): - # update the inner frame's width to fill the canvas - canvas.itemconfigure(interior_id, width=canvas.winfo_width()) - canvas.bind('<Configure>', _configure_canvas) - - return - -def is_int(s): - "Return 's is blank or represents an int'" - if not s: - return True - try: - int(s) - return True - except ValueError: - return False + This code is generic - it works for any and all IDLE extensions. -# TODO: -# * Revert to default(s)? Per option or per extension? -# * List options in their original order (possible??) -class ConfigExtensionsDialog(Toplevel): - """A dialog for configuring 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. - This dialog is generic - it works for any and all IDLE extensions. + Not all changes take effect immediately - some may require restarting IDLE. + This depends on each extension's implementation. - IDLE extensions save their configuration options using idleConf. - ConfigExtensionsDialog 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 an True/False button. - """ - def __init__(self, parent, title=None, _htest=False): - Toplevel.__init__(self, parent) - self.wm_withdraw() - - self.configure(borderwidth=5) - self.geometry( - "+%d+%d" % (parent.winfo_rootx() + 20, - parent.winfo_rooty() + (30 if not _htest else 150))) - self.wm_title(title or 'IDLE Extensions Configuration') - - self.defaultCfg = idleConf.defaultCfg['extensions'] - self.userCfg = idleConf.userCfg['extensions'] + 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 an True/False button. + """ + parent = self.parent + frame = self.tabPages.pages['Extensions'].frame + self.ext_defaultCfg = idleConf.defaultCfg['extensions'] + self.ext_userCfg = idleConf.userCfg['extensions'] self.is_int = self.register(is_int) self.load_extensions() - self.create_widgets() + # 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.rowconfigure(0, weight=1) + frame.columnconfigure(2, weight=1) + self.extension_list = Listbox(frame, listvariable=self.extension_names, + selectmode='browse') + self.extension_list.bind('<<ListboxSelect>>', self.extension_selected) + scroll = Scrollbar(frame, command=self.extension_list.yview) + self.extension_list.yscrollcommand=scroll.set + self.details_frame = LabelFrame(frame, 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.configure(padx=10, pady=10) + self.config_frame = {} + self.current_extension = None - self.resizable(height=FALSE, width=FALSE) # don't allow resizing yet - self.transient(parent) - self.protocol("WM_DELETE_WINDOW", self.Cancel) - self.tabbed_page_set.focus_set() - # wait for window to be generated - self.update() - # set current width as the minimum width - self.wm_minsize(self.winfo_width(), 1) - # now allow resizing - self.resizable(height=TRUE, width=TRUE) - - self.wm_deiconify() - if not _htest: - self.grab_set() - self.wait_window() + 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) def load_extensions(self): "Fill self.extensions with data from the default and user configs." @@ -1309,7 +1244,7 @@ class ConfigExtensionsDialog(Toplevel): self.extensions[ext_name] = [] for ext_name in self.extensions: - opt_list = sorted(self.defaultCfg.GetOptionList(ext_name)) + 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 @@ -1319,7 +1254,7 @@ class ConfigExtensionsDialog(Toplevel): opt_list = enables + opt_list for opt_name in opt_list: - def_str = self.defaultCfg.Get( + def_str = self.ext_defaultCfg.Get( ext_name, opt_name, raw=True) try: def_obj = {'True':True, 'False':False}[def_str] @@ -1332,7 +1267,7 @@ class ConfigExtensionsDialog(Toplevel): def_obj = def_str opt_type = None try: - value = self.userCfg.Get( + value = self.ext_userCfg.Get( ext_name, opt_name, type=opt_type, raw=True, default=def_obj) except ValueError: # Need this until .Get fixed @@ -1347,37 +1282,6 @@ class ConfigExtensionsDialog(Toplevel): 'var': var, }) - def create_widgets(self): - """Create the dialog's widgets.""" - self.extension_names = StringVar(self) - self.rowconfigure(0, weight=1) - self.columnconfigure(2, weight=1) - self.extension_list = Listbox(self, listvariable=self.extension_names, - selectmode='browse') - self.extension_list.bind('<<ListboxSelect>>', self.extension_selected) - scroll = Scrollbar(self, command=self.extension_list.yview) - self.extension_list.yscrollcommand=scroll.set - self.details_frame = LabelFrame(self, 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]) - self.configure(padx=10, pady=10) - self.config_frame = {} - self.current_extension = None - - self.outerframe = self # TEMPORARY - self.tabbed_page_set = self.extension_list # TEMPORARY - - # create the individual pages - 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.create_action_buttons().grid(row=1, columnspan=3) - def extension_selected(self, event): newsel = self.extension_list.curselection() if newsel: @@ -1392,8 +1296,6 @@ class ConfigExtensionsDialog(Toplevel): self.config_frame[newsel].grid(column=0, row=0, sticky='nsew') self.current_extension = newsel - create_action_buttons = ConfigDialog.create_action_buttons - 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) @@ -1420,19 +1322,7 @@ class ConfigExtensionsDialog(Toplevel): ).grid(row=row, column=1, sticky=NSEW, padx=7) return - - Ok = ConfigDialog.Ok - - def Apply(self): - self.save_all_changed_configs() - pass - - Cancel = ConfigDialog.Cancel - - def Help(self): - pass - - def set_user_value(self, section, opt): + def set_extension_value(self, section, opt): name = opt['name'] default = opt['default'] value = opt['var'].get().strip() or default @@ -1440,20 +1330,92 @@ class ConfigExtensionsDialog(Toplevel): # if self.defaultCfg.has_section(section): # Currently, always true; if not, indent to return if (value == default): - return self.userCfg.RemoveOption(section, name) + return self.ext_userCfg.RemoveOption(section, name) # set the option - return self.userCfg.SetOption(section, name, value) + return self.ext_userCfg.SetOption(section, name, value) - def save_all_changed_configs(self): + def save_all_changed_extensions(self): """Save configuration changes to the user config file.""" has_changes = False for ext_name in self.extensions: options = self.extensions[ext_name] for opt in options: - if self.set_user_value(ext_name, opt): + if self.set_extension_value(ext_name, opt): has_changes = True if has_changes: - self.userCfg.Save() + self.ext_userCfg.Save() + + +help_common = '''\ +When you click either the Apply or Ok buttons, settings in this +dialog that are different from IDLE's default are saved in +a .idlerc directory in your home directory. Except as noted, +hese changes apply to all versions of IDLE installed on this +machine. Some do not take affect until IDLE is restarted. +[Cancel] only cancels changes made since the last save. +''' +help_pages = { + 'Highlighting':''' +Highlighting: +The IDLE Dark color theme is new in Octover 2015. It can only +be used with older IDLE releases if it is saved as a custom +theme, with a different name. +''' +} + + +def is_int(s): + "Return 's is blank or represents an int'" + if not s: + return True + try: + int(s) + return True + except ValueError: + return False + + +class VerticalScrolledFrame(Frame): + """A pure Tkinter vertically scrollable frame. + + * Use the 'interior' attribute to place widgets inside the scrollable frame + * Construct and pack/place/grid normally + * This frame only allows vertical scrolling + """ + def __init__(self, parent, *args, **kw): + Frame.__init__(self, parent, *args, **kw) + + # create a canvas object and a vertical scrollbar for scrolling it + vscrollbar = Scrollbar(self, orient=VERTICAL) + vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE) + canvas = Canvas(self, bd=0, highlightthickness=0, + yscrollcommand=vscrollbar.set) + canvas.pack(side=LEFT, fill=BOTH, expand=TRUE) + vscrollbar.config(command=canvas.yview) + + # reset the view + canvas.xview_moveto(0) + canvas.yview_moveto(0) + + # create a frame inside the canvas which will be scrolled with it + self.interior = interior = Frame(canvas) + interior_id = canvas.create_window(0, 0, window=interior, anchor=NW) + + # track changes to the canvas and frame width and sync them, + # also updating the scrollbar + def _configure_interior(event): + # update the scrollbars to match the size of the inner frame + size = (interior.winfo_reqwidth(), interior.winfo_reqheight()) + canvas.config(scrollregion="0 0 %s %s" % size) + interior.bind('<Configure>', _configure_interior) + + def _configure_canvas(event): + if interior.winfo_reqwidth() != canvas.winfo_width(): + # update the inner frame's width to fill the canvas + canvas.itemconfigure(interior_id, width=canvas.winfo_width()) + canvas.bind('<Configure>', _configure_canvas) + + return if __name__ == '__main__': diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 379b277..2189fd4 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -266,16 +266,16 @@ access to locals and globals.</dd> <h3>25.5.1.7. Options menu (Shell and Editor)<a class="headerlink" href="#options-menu-shell-and-editor" title="Permalink to this headline">ΒΆ</a></h3> <dl class="docutils"> <dt>Configure IDLE</dt> -<dd>Open a configuration dialog. Fonts, indentation, keybindings, and color -themes may be altered. Startup Preferences may be set, and additional -help sources can be specified. Non-default user setting are saved in a -.idlerc directory in the user’s home directory. Problems caused by bad user -configuration files are solved by editing or deleting one or more of the -files in .idlerc. On OS X, open the configuration dialog by selecting -Preferences in the application menu.</dd> -<dt>Configure Extensions</dt> -<dd>Open a configuration dialog for setting preferences for extensions -(discussed below). See note above about the location of user settings.</dd> +<dd><p class="first">Open a configuration dialog and change preferences for the following: +fonts, indentation, keybindings, text color themes, startup windows and +size, additional help sources, and extensions (see below). On OS X, +open the configuration dialog by selecting Preferences in the application +menu. To use a new built-in color theme (IDLE Dark) with older IDLEs, +save it as a new custom theme.</p> +<p class="last">Non-default user settings are saved in a .idlerc directory in the user’s +home directory. Problems caused by bad user configuration files are solved +by editing or deleting one or more of the files in .idlerc.</p> +</dd> <dt>Code Context (toggle)(Editor Window only)</dt> <dd>Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window.</dd> @@ -699,7 +699,7 @@ are currently:</p> The Python Software Foundation is a non-profit corporation. <a href="https://www.python.org/psf/donations/">Please donate.</a> <br /> - Last updated on Oct 02, 2015. + Last updated on Oct 13, 2015. <a href="../bugs.html">Found a bug</a>? <br /> Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.2.3. diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 3a9885c..b0f4340 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -93,15 +93,6 @@ _class_browser_spec = { "Double clicking on items prints a traceback for an exception " "that is ignored." } -ConfigExtensionsDialog_spec = { - 'file': 'configDialog', - 'kwds': {'title': 'Test Extension Configuration', - '_htest': True,}, - 'msg': "IDLE extensions dialog.\n" - "\n[Ok] to close the dialog.[Apply] to apply the settings and " - "and [Cancel] to revert all changes.\nRe-run the test to ensure " - "changes made have persisted." - } _color_delegator_spec = { 'file': 'ColorDelegator', @@ -121,7 +112,8 @@ ConfigDialog_spec = { "font face of the text in the area below it.\nIn the " "'Highlighting' tab, try different color schemes. Clicking " "items in the sample program should update the choices above it." - "\nIn the 'Keys' and 'General' tab, test settings of interest." + "\nIn the 'Keys', 'General' and 'Extensions' tabs, test settings" + "of interest." "\n[Ok] to close the dialog.[Apply] to apply the settings and " "and [Cancel] to revert all changes.\nRe-run the test to ensure " "changes made have persisted." |