From 3a5834c94d3d52a5b4fb79b8fa9d17cf9f5f7151 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 27 Jul 2017 03:18:01 -0400 Subject: [PATCH 1/5] News blurb --- Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst diff --git a/Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst b/Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst new file mode 100644 index 000000000000000..8cf11875c3c06d4 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst @@ -0,0 +1 @@ +IDLE - Add tests for ConfigDialog highlight tab. From c6de7b3b5051534c2eae33964569f6843ecb0814 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 27 Jul 2017 03:43:13 -0400 Subject: [PATCH 2/5] Move highlight functions under create_page_highlight. --- Lib/idlelib/configdialog.py | 1873 ++++++++++++++++++----------------- 1 file changed, 957 insertions(+), 916 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index daaa34459e2fa64..afb54f122cd7c5f 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -531,1027 +531,1068 @@ def tem(event, elem=element): self.new_custom_theme.pack(side=TOP, fill=X, pady=5) return frame - def create_page_keys(self): - """Return frame of widgets for Keys tab. + def load_theme_cfg(self): + """Load current configuration settings for the theme options. - Tk Variables: - builtin_keys: Menu variable for built-in keybindings. - custom_keys: Menu variable for custom keybindings. - are_keys_builtin: Selector for built-in or custom keybindings. - keybinding: Action/key bindings. + Based on the is_builtin_theme toggle, the theme is set as + either builtin or custom and the initial widget values + reflect the current settings from idleConf. - Methods: - load_key_config: Set table. - load_keys_list: Reload active set. - keybinding_selected: Bound to list_bindings button release. - get_new_keys: Command for button_new_keys. - get_new_keys_name: Call popup. - create_new_key_set: Combine active keyset and changes. - set_keys_type: Command for are_keys_builtin. - delete_custom_keys: Command for button_delete_custom_keys. - save_as_new_key_set: Command for button_save_custom_keys. - save_new_key_set: Save to idleConf.userCfg['keys'] (is function). - deactivate_current_config: Remove keys bindings in editors. + Attributes updated: + is_builtin_theme: Set from idleConf. + opt_menu_theme_builtin: List of default themes from idleConf. + opt_menu_theme_custom: List of custom themes from idleConf. + radio_theme_custom: Disabled if there are no custom themes. + custom_theme: Message with additional information. + opt_menu_highlight_target: Create menu from self.theme_elements. - Widget Structure: (*) widgets bound to self - frame - frame_custom: LabelFrame - frame_target: Frame - target_title: Label - scroll_target_y: Scrollbar - scroll_target_x: Scrollbar - (*)list_bindings: ListBox - (*)button_new_keys: Button - frame_key_sets: LabelFrame - frames[0]: Frame - (*)radio_keys_builtin: Radiobutton - are_keys_builtin - (*)radio_keys_custom: Radiobutton - are_keys_builtin - (*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys - (*)opt_menu_keys_custom: DynOptionMenu - custom_keys - (*)new_custom_keys: Label - frames[1]: Frame - (*)button_delete_custom_keys: Button - button_save_custom_keys: Button + Methods: + set_theme_type + paint_theme_sample + set_highlight_target """ - parent = self.parent - self.builtin_keys = StringVar(parent) - self.custom_keys = StringVar(parent) - self.are_keys_builtin = BooleanVar(parent) - self.keybinding = StringVar(parent) + # Set current theme type radiobutton. + self.is_builtin_theme.set(idleConf.GetOption( + 'main', 'Theme', 'default', type='bool', default=1)) + # Set current theme. + current_option = idleConf.CurrentTheme() + # Load available theme option menus. + if self.is_builtin_theme.get(): # Default theme selected. + item_list = idleConf.GetSectionList('default', 'highlight') + item_list.sort() + self.opt_menu_theme_builtin.SetMenu(item_list, current_option) + item_list = idleConf.GetSectionList('user', 'highlight') + item_list.sort() + if not item_list: + self.radio_theme_custom['state'] = DISABLED + self.custom_theme.set('- no custom themes -') + else: + self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) + else: # User theme selected. + item_list = idleConf.GetSectionList('user', 'highlight') + item_list.sort() + self.opt_menu_theme_custom.SetMenu(item_list, current_option) + item_list = idleConf.GetSectionList('default', 'highlight') + item_list.sort() + self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0]) + self.set_theme_type() + # Load theme element option menu. + theme_names = list(self.theme_elements.keys()) + theme_names.sort(key=lambda x: self.theme_elements[x][1]) + self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0]) + self.paint_theme_sample() + self.set_highlight_target() - ##widget creation - #body frame - frame = self.tab_pages.pages['Keys'].frame - #body section frames - frame_custom = LabelFrame( - frame, borderwidth=2, relief=GROOVE, - text=' Custom Key Bindings ') - frame_key_sets = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Key Set ') - #frame_custom - frame_target = Frame(frame_custom) - target_title = Label(frame_target, text='Action - Key(s)') - scroll_target_y = Scrollbar(frame_target) - scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL) - self.list_bindings = Listbox( - frame_target, takefocus=FALSE, exportselection=FALSE) - self.list_bindings.bind('', self.keybinding_selected) - scroll_target_y.config(command=self.list_bindings.yview) - scroll_target_x.config(command=self.list_bindings.xview) - self.list_bindings.config(yscrollcommand=scroll_target_y.set) - self.list_bindings.config(xscrollcommand=scroll_target_x.set) - self.button_new_keys = Button( - frame_custom, text='Get New Keys for Selection', - command=self.get_new_keys, state=DISABLED) - #frame_key_sets - frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0) - for i in range(2)] - self.radio_keys_builtin = Radiobutton( - frames[0], variable=self.are_keys_builtin, value=1, - command=self.set_keys_type, text='Use a Built-in Key Set') - self.radio_keys_custom = Radiobutton( - frames[0], variable=self.are_keys_builtin, value=0, - command=self.set_keys_type, text='Use a Custom Key Set') - self.opt_menu_keys_builtin = DynOptionMenu( - frames[0], self.builtin_keys, None, command=None) - self.opt_menu_keys_custom = DynOptionMenu( - frames[0], self.custom_keys, None, command=None) - self.button_delete_custom_keys = Button( - frames[1], text='Delete Custom Key Set', - command=self.delete_custom_keys) - button_save_custom_keys = Button( - frames[1], text='Save as New Custom Key Set', - command=self.save_as_new_key_set) - self.new_custom_keys = Label(frames[0], bd=2) + def var_changed_color(self, *params): + "Process change to color choice." + self.on_new_color_set() - ##widget packing - #body - frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) - frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) - #frame_custom - self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5) - frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - #frame target - frame_target.columnconfigure(0, weight=1) - frame_target.rowconfigure(1, weight=1) - target_title.grid(row=0, column=0, columnspan=2, sticky=W) - self.list_bindings.grid(row=1, column=0, sticky=NSEW) - scroll_target_y.grid(row=1, column=1, sticky=NS) - scroll_target_x.grid(row=2, column=0, sticky=EW) - #frame_key_sets - self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS) - self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS) - self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW) - self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW) - self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5) - self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2) - button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2) - frames[0].pack(side=TOP, fill=BOTH, expand=True) - frames[1].pack(side=TOP, fill=X, expand=True, pady=2) - return frame + def var_changed_builtin_theme(self, *params): + """Process new builtin theme selection. + Add the changed theme's name to the changed_items and recreate + the sample with the values from the selected theme. + """ + old_themes = ('IDLE Classic', 'IDLE New') + value = self.builtin_theme.get() + if value not in old_themes: + if idleConf.GetOption('main', 'Theme', 'name') not in old_themes: + changes.add_option('main', 'Theme', 'name', old_themes[0]) + changes.add_option('main', 'Theme', 'name2', value) + self.new_custom_theme.config(text='New theme, see Help', + fg='#500000') + else: + changes.add_option('main', 'Theme', 'name', value) + changes.add_option('main', 'Theme', 'name2', '') + self.new_custom_theme.config(text='', fg='black') + self.paint_theme_sample() - def create_page_general(self): - """Return frame of widgets for General tab. + def var_changed_custom_theme(self, *params): + """Process new custom theme selection. - Enable users to provisionally change general options. Function - load_general_cfg intializes 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 win_width and win_height. Setting var_name invokes the - var_changed_var_name callback that adds option to changes. + If a new custom theme is selected, add the name to the + changed_items and apply the theme to the sample. + """ + value = self.custom_theme.get() + if value != '- no custom themes -': + changes.add_option('main', 'Theme', 'name', value) + self.paint_theme_sample() - Helplist: load_general_cfg loads list user_helplist with - 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']. + def var_changed_is_builtin_theme(self, *params): + """Process toggle between builtin and custom theme. - Widget Structure: (*) widgets bound to self - frame - frame_run: LabelFrame - startup_title: Label - (*)startup_editor_on: Radiobutton - startup_edit - (*)startup_shell_on: Radiobutton - startup_edit - frame_save: LabelFrame - run_save_title: Label - (*)save_ask_on: Radiobutton - autosave - (*)save_auto_on: Radiobutton - autosave - frame_win_size: LabelFrame - win_size_title: Label - win_width_title: Label - (*)win_width_int: Entry - win_width - win_height_title: Label - (*)win_height_int: Entry - win_height - frame_help: LabelFrame - frame_helplist: Frame - frame_helplist_buttons: Frame - (*)button_helplist_edit - (*)button_helplist_add - (*)button_helplist_remove - (*)helplist: ListBox - scroll_helplist: Scrollbar + Update the default toggle value and apply the newly + selected theme type. """ - parent = self.parent - self.startup_edit = IntVar(parent) - self.autosave = IntVar(parent) - self.win_width = StringVar(parent) - self.win_height = StringVar(parent) + value = self.is_builtin_theme.get() + changes.add_option('main', 'Theme', 'default', value) + if value: + self.var_changed_builtin_theme() + else: + self.var_changed_custom_theme() - # Create widgets: - # body. - frame = self.tab_pages.pages['General'].frame - # body section frames. - frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Startup Preferences ') - frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' autosave Preferences ') - frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE) - frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Additional Help Sources ') - # frame_run. - startup_title = Label(frame_run, text='At Startup') - self.startup_editor_on = Radiobutton( - frame_run, variable=self.startup_edit, value=1, - text="Open Edit Window") - self.startup_shell_on = Radiobutton( - frame_run, variable=self.startup_edit, value=0, - text='Open Shell Window') - # frame_save. - 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_win_size. - win_size_title = Label( - frame_win_size, text='Initial Window Size (in characters)') - win_width_title = Label(frame_win_size, text='Width') - self.win_width_int = Entry( - frame_win_size, textvariable=self.win_width, width=3) - win_height_title = Label(frame_win_size, text='Height') - self.win_height_int = Entry( - frame_win_size, textvariable=self.win_height, width=3) - # frame_help. - frame_helplist = Frame(frame_help) - frame_helplist_buttons = Frame(frame_helplist) - self.helplist = Listbox( - frame_helplist, height=5, takefocus=FALSE, - exportselection=FALSE) - scroll_helplist = Scrollbar(frame_helplist) - scroll_helplist['command'] = self.helplist.yview - self.helplist['yscrollcommand'] = scroll_helplist.set - self.helplist.bind('', self.help_source_selected) - self.button_helplist_edit = Button( - frame_helplist_buttons, text='Edit', state=DISABLED, - width=8, command=self.helplist_item_edit) - self.button_helplist_add = Button( - frame_helplist_buttons, text='Add', - width=8, command=self.helplist_item_add) - self.button_helplist_remove = Button( - frame_helplist_buttons, text='Remove', state=DISABLED, - width=8, command=self.helplist_item_remove) - - # Pack widgets: - # body. - frame_run.pack(side=TOP, padx=5, pady=5, fill=X) - frame_save.pack(side=TOP, padx=5, pady=5, fill=X) - frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X) - frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - # frame_run. - startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) - self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - # frame_save. - 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_win_size. - win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5) - self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) - 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_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) - scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y) - self.helplist.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) - self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5) - self.button_helplist_add.pack(side=TOP, anchor=W) - self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5) - - return frame - - def load_general_cfg(self): - "Load current configuration settings for the general options." - # Set startup state. - self.startup_edit.set(idleConf.GetOption( - 'main', 'General', 'editor-on-startup', default=0, type='bool')) - # Set autosave state. - self.autosave.set(idleConf.GetOption( - 'main', 'General', 'autosave', default=0, type='bool')) - # Set initial window size. - self.win_width.set(idleConf.GetOption( - 'main', 'EditorWindow', 'width', type='int')) - self.win_height.set(idleConf.GetOption( - 'main', 'EditorWindow', 'height', type='int')) - # Set additional help sources. - self.user_helplist = idleConf.GetAllExtraHelpSourcesList() - self.helplist.delete(0, 'end') - for help_item in self.user_helplist: - self.helplist.insert(END, help_item[0]) - self.set_add_delete_state() - - def var_changed_startup_edit(self, *params): - "Store change to toggle for starting IDLE in the editor or shell." - value = self.startup_edit.get() - changes.add_option('main', 'General', 'editor-on-startup', value) + def var_changed_highlight_target(self, *params): + "Process selection of new target tag for highlighting." + self.set_highlight_target() - def var_changed_autosave(self, *params): - "Store change to autosave." - value = self.autosave.get() - changes.add_option('main', 'General', 'autosave', value) + def set_theme_type(self): + """Set available screen options based on builtin or custom theme. - def var_changed_win_width(self, *params): - "Store change to window width." - value = self.win_width.get() - changes.add_option('main', 'EditorWindow', 'width', value) + Attributes accessed: + is_builtin_theme - def var_changed_win_height(self, *params): - "Store change to window height." - value = self.win_height.get() - changes.add_option('main', 'EditorWindow', 'height', value) + Attributes updated: + opt_menu_theme_builtin + opt_menu_theme_custom + button_delete_custom_theme + radio_theme_custom - def help_source_selected(self, event): - "Handle event for selecting additional help." - self.set_add_delete_state() + Called from: + handler for radio_theme_builtin and radio_theme_custom + delete_custom_theme + create_new_theme + load_theme_cfg + """ + if self.is_builtin_theme.get(): + self.opt_menu_theme_builtin['state'] = NORMAL + self.opt_menu_theme_custom['state'] = DISABLED + self.button_delete_custom_theme['state'] = DISABLED + else: + self.opt_menu_theme_builtin['state'] = DISABLED + self.radio_theme_custom['state'] = NORMAL + self.opt_menu_theme_custom['state'] = NORMAL + self.button_delete_custom_theme['state'] = NORMAL - def set_add_delete_state(self): - "Toggle the state for the help list buttons based on list entries." - if self.helplist.size() < 1: # No entries in list. - self.button_helplist_edit['state'] = DISABLED - self.button_helplist_remove['state'] = DISABLED - else: # Some entries. - if self.helplist.curselection(): # There currently is a selection. - self.button_helplist_edit['state'] = NORMAL - self.button_helplist_remove['state'] = NORMAL - else: # There currently is not a selection. - self.button_helplist_edit['state'] = DISABLED - self.button_helplist_remove['state'] = DISABLED + def delete_custom_theme(self): + """Handle event to delete custom theme. - def helplist_item_add(self): - """Handle add button for the help list. + The current theme is deactivated and the default theme is + activated. The custom theme is permanently removed from + the config file. - Query for name and location of new help sources and add - them to the list. - """ - help_source = HelpSource(self, 'New Help Source').result - if help_source: - self.user_helplist.append(help_source) - self.helplist.insert(END, help_source[0]) - self.update_help_changes() + Attributes accessed: + custom_theme - def helplist_item_edit(self): - """Handle edit button for the help list. + Attributes updated: + radio_theme_custom + opt_menu_theme_custom + is_builtin_theme + builtin_theme - Query with existing help source information and update - config if the values are changed. + Methods: + deactivate_current_config + save_all_changed_extensions + activate_config_changes + set_theme_type """ - item_index = self.helplist.index(ANCHOR) - help_source = self.user_helplist[item_index] - new_help_source = HelpSource( - self, 'Edit Help Source', - menuitem=help_source[0], - filepath=help_source[1], - ).result - if new_help_source and new_help_source != help_source: - self.user_helplist[item_index] = new_help_source - self.helplist.delete(item_index) - self.helplist.insert(item_index, new_help_source[0]) - self.update_help_changes() - self.set_add_delete_state() # Selected will be un-selected + theme_name = self.custom_theme.get() + delmsg = 'Are you sure you wish to delete the theme %r ?' + if not tkMessageBox.askyesno( + 'Delete Theme', delmsg % theme_name, parent=self): + return + self.deactivate_current_config() + # Remove theme from changes, config, and file. + changes.delete_section('highlight', theme_name) + # Reload user theme list. + item_list = idleConf.GetSectionList('user', 'highlight') + item_list.sort() + if not item_list: + self.radio_theme_custom['state'] = DISABLED + self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -') + else: + self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) + # Revert to default theme. + self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) + self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) + # User can't back out of these changes, they must be applied now. + changes.save_all() + self.save_all_changed_extensions() + self.activate_config_changes() + self.set_theme_type() - def helplist_item_remove(self): - """Handle remove button for the help list. + def get_color(self): + """Handle button to select a new color for the target tag. - Delete the help list item from config. - """ - item_index = self.helplist.index(ANCHOR) - del(self.user_helplist[item_index]) - self.helplist.delete(item_index) - self.update_help_changes() - self.set_add_delete_state() + If a new color is selected while using a builtin theme, a + name must be supplied to create a custom theme. - def update_help_changes(self): - "Clear and rebuild the HelpFiles section in changes" - changes['main']['HelpFiles'] = {} - for num in range(1, len(self.user_helplist) + 1): - changes.add_option( - 'main', 'HelpFiles', str(num), - ';'.join(self.user_helplist[num-1][:2])) + Attributes accessed: + highlight_target + frame_color_set + is_builtin_theme + Attributes updated: + color - def attach_var_callbacks(self): - "Attach callbacks to variables that can be changed." - self.font_size.trace_add('write', self.var_changed_font) - self.font_name.trace_add('write', self.var_changed_font) - self.font_bold.trace_add('write', self.var_changed_font) - self.space_num.trace_add('write', self.var_changed_space_num) - self.color.trace_add('write', self.var_changed_color) - self.builtin_theme.trace_add('write', self.var_changed_builtin_theme) - self.custom_theme.trace_add('write', self.var_changed_custom_theme) - self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme) - self.highlight_target.trace_add('write', self.var_changed_highlight_target) - self.keybinding.trace_add('write', self.var_changed_keybinding) - self.builtin_keys.trace_add('write', self.var_changed_builtin_keys) - self.custom_keys.trace_add('write', self.var_changed_custom_keys) - self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin) - self.win_width.trace_add('write', self.var_changed_win_width) - self.win_height.trace_add('write', self.var_changed_win_height) - self.startup_edit.trace_add('write', self.var_changed_startup_edit) - self.autosave.trace_add('write', self.var_changed_autosave) - - def remove_var_callbacks(self): - "Remove callbacks to prevent memory leaks." - for var in ( - self.font_size, self.font_name, self.font_bold, - self.space_num, self.color, self.builtin_theme, - self.custom_theme, self.is_builtin_theme, self.highlight_target, - self.keybinding, self.builtin_keys, self.custom_keys, - self.are_keys_builtin, self.win_width, self.win_height, - self.startup_edit, self.autosave,): - var.trace_remove('write', var.trace_info()[0][1]) - - def var_changed_color(self, *params): - "Process change to color choice." - self.on_new_color_set() - - def var_changed_builtin_theme(self, *params): - """Process new builtin theme selection. - - Add the changed theme's name to the changed_items and recreate - the sample with the values from the selected theme. + Methods: + get_new_theme_name + create_new_theme """ - old_themes = ('IDLE Classic', 'IDLE New') - value = self.builtin_theme.get() - if value not in old_themes: - if idleConf.GetOption('main', 'Theme', 'name') not in old_themes: - changes.add_option('main', 'Theme', 'name', old_themes[0]) - changes.add_option('main', 'Theme', 'name2', value) - self.new_custom_theme.config(text='New theme, see Help', - fg='#500000') - else: - changes.add_option('main', 'Theme', 'name', value) - changes.add_option('main', 'Theme', 'name2', '') - self.new_custom_theme.config(text='', fg='black') - self.paint_theme_sample() + target = self.highlight_target.get() + prev_color = self.frame_color_set.cget('bg') + rgbTuplet, color_string = tkColorChooser.askcolor( + parent=self, title='Pick new color for : '+target, + initialcolor=prev_color) + if color_string and (color_string != prev_color): + # User didn't cancel and they chose a new color. + if self.is_builtin_theme.get(): # Current theme is a built-in. + message = ('Your changes will be saved as a new Custom Theme. ' + 'Enter a name for your new Custom Theme below.') + new_theme = self.get_new_theme_name(message) + if not new_theme: # User cancelled custom theme creation. + return + else: # Create new custom theme based on previously active theme. + self.create_new_theme(new_theme) + self.color.set(color_string) + else: # Current theme is user defined. + self.color.set(color_string) - def var_changed_custom_theme(self, *params): - """Process new custom theme selection. + def on_new_color_set(self): + "Display sample of new color selection on the dialog." + new_color=self.color.get() + self.frame_color_set.config(bg=new_color) # Set sample. + plane ='foreground' if self.fg_bg_toggle.get() else 'background' + sample_element = self.theme_elements[self.highlight_target.get()][0] + self.highlight_sample.tag_config(sample_element, **{plane:new_color}) + theme = self.custom_theme.get() + theme_element = sample_element + '-' + plane + changes.add_option('highlight', theme, theme_element, new_color) - If a new custom theme is selected, add the name to the - changed_items and apply the theme to the sample. - """ - value = self.custom_theme.get() - if value != '- no custom themes -': - changes.add_option('main', 'Theme', 'name', value) - self.paint_theme_sample() + def get_new_theme_name(self, message): + "Return name of new theme from query popup." + used_names = (idleConf.GetSectionList('user', 'highlight') + + idleConf.GetSectionList('default', 'highlight')) + new_theme = SectionName( + self, 'New Custom Theme', message, used_names).result + return new_theme - def var_changed_is_builtin_theme(self, *params): - """Process toggle between builtin and custom theme. + def save_as_new_theme(self): + """Prompt for new theme name and create the theme. - Update the default toggle value and apply the newly - selected theme type. + Methods: + get_new_theme_name + create_new_theme """ - value = self.is_builtin_theme.get() - changes.add_option('main', 'Theme', 'default', value) - if value: - self.var_changed_builtin_theme() - else: - self.var_changed_custom_theme() + new_theme_name = self.get_new_theme_name('New Theme Name:') + if new_theme_name: + self.create_new_theme(new_theme_name) - def var_changed_highlight_target(self, *params): - "Process selection of new target tag for highlighting." - self.set_highlight_target() + def create_new_theme(self, new_theme_name): + """Create a new custom theme with the given name. - def var_changed_keybinding(self, *params): - "Store change to a keybinding." - value = self.keybinding.get() - key_set = self.custom_keys.get() - event = self.list_bindings.get(ANCHOR).split()[0] - if idleConf.IsCoreBinding(event): - changes.add_option('keys', key_set, event, value) - else: # Event is an extension binding. - ext_name = idleConf.GetExtnNameForEvent(event) - ext_keybind_section = ext_name + '_cfgBindings' - changes.add_option('extensions', ext_keybind_section, event, value) + Create the new theme based on the previously active theme + with the current changes applied. Once it is saved, then + activate the new theme. - def var_changed_builtin_keys(self, *params): - "Process selection of builtin key set." - old_keys = ( - 'IDLE Classic Windows', - 'IDLE Classic Unix', - 'IDLE Classic Mac', - 'IDLE Classic OSX', - ) - value = self.builtin_keys.get() - if value not in old_keys: - if idleConf.GetOption('main', 'Keys', 'name') not in old_keys: - changes.add_option('main', 'Keys', 'name', old_keys[0]) - changes.add_option('main', 'Keys', 'name2', value) - self.new_custom_keys.config(text='New key set, see Help', - fg='#500000') - else: - changes.add_option('main', 'Keys', 'name', value) - changes.add_option('main', 'Keys', 'name2', '') - self.new_custom_keys.config(text='', fg='black') - self.load_keys_list(value) + Attributes accessed: + builtin_theme + custom_theme - def var_changed_custom_keys(self, *params): - "Process selection of custom key set." - value = self.custom_keys.get() - if value != '- no custom keys -': - changes.add_option('main', 'Keys', 'name', value) - self.load_keys_list(value) + Attributes updated: + opt_menu_theme_custom + is_builtin_theme - def var_changed_are_keys_builtin(self, *params): - "Process toggle between builtin key set and custom key set." - value = self.are_keys_builtin.get() - changes.add_option('main', 'Keys', 'default', value) - if value: - self.var_changed_builtin_keys() + Method: + save_new_theme + set_theme_type + """ + if self.is_builtin_theme.get(): + theme_type = 'default' + theme_name = self.builtin_theme.get() else: - self.var_changed_custom_keys() + theme_type = 'user' + theme_name = self.custom_theme.get() + new_theme = idleConf.GetThemeDict(theme_type, theme_name) + # Apply any of the old theme's unsaved changes to the new theme. + if theme_name in changes['highlight']: + theme_changes = changes['highlight'][theme_name] + for element in theme_changes: + new_theme[element] = theme_changes[element] + # Save the new theme. + self.save_new_theme(new_theme_name, new_theme) + # Change GUI over to the new theme. + custom_theme_list = idleConf.GetSectionList('user', 'highlight') + custom_theme_list.sort() + self.opt_menu_theme_custom.SetMenu(custom_theme_list, new_theme_name) + self.is_builtin_theme.set(0) + self.set_theme_type() - def set_theme_type(self): - """Set available screen options based on builtin or custom theme. + def set_highlight_target(self): + """Set fg/bg toggle and color based on highlight tag target. - Attributes accessed: - is_builtin_theme + Instance variables accessed: + highlight_target Attributes updated: - opt_menu_theme_builtin - opt_menu_theme_custom - button_delete_custom_theme - radio_theme_custom + radio_fg + radio_bg + fg_bg_toggle + + Methods: + set_color_sample Called from: - handler for radio_theme_builtin and radio_theme_custom - delete_custom_theme - create_new_theme + var_changed_highlight_target load_theme_cfg """ - if self.is_builtin_theme.get(): - self.opt_menu_theme_builtin['state'] = NORMAL - self.opt_menu_theme_custom['state'] = DISABLED - self.button_delete_custom_theme['state'] = DISABLED - else: - self.opt_menu_theme_builtin['state'] = DISABLED - self.radio_theme_custom['state'] = NORMAL - self.opt_menu_theme_custom['state'] = NORMAL - self.button_delete_custom_theme['state'] = NORMAL - - def set_keys_type(self): - "Set available screen options based on builtin or custom key set." - if self.are_keys_builtin.get(): - self.opt_menu_keys_builtin['state'] = NORMAL - self.opt_menu_keys_custom['state'] = DISABLED - self.button_delete_custom_keys['state'] = DISABLED - else: - self.opt_menu_keys_builtin['state'] = DISABLED - self.radio_keys_custom['state'] = NORMAL - self.opt_menu_keys_custom['state'] = NORMAL - self.button_delete_custom_keys['state'] = NORMAL + if self.highlight_target.get() == 'Cursor': # bg not possible + self.radio_fg['state'] = DISABLED + self.radio_bg['state'] = DISABLED + self.fg_bg_toggle.set(1) + else: # Both fg and bg can be set. + self.radio_fg['state'] = NORMAL + self.radio_bg['state'] = NORMAL + self.fg_bg_toggle.set(1) + self.set_color_sample() - def get_new_keys(self): - """Handle event to change key binding for selected line. + def set_color_sample_binding(self, *args): + """Change color sample based on foreground/background toggle. - A selection of a key/binding in the list of current - bindings pops up a dialog to enter a new binding. If - the current key set is builtin and a binding has - changed, then a name for a custom key set needs to be - entered for the change to be applied. + Methods: + set_color_sample """ - list_index = self.list_bindings.index(ANCHOR) - binding = self.list_bindings.get(list_index) - bind_name = binding.split()[0] - if self.are_keys_builtin.get(): - current_key_set_name = self.builtin_keys.get() - else: - current_key_set_name = self.custom_keys.get() - current_bindings = idleConf.GetCurrentKeySet() - if current_key_set_name in changes['keys']: # unsaved changes - key_set_changes = changes['keys'][current_key_set_name] - for event in key_set_changes: - current_bindings[event] = key_set_changes[event].split() - current_key_sequences = list(current_bindings.values()) - new_keys = GetKeysDialog(self, 'Get New Keys', bind_name, - current_key_sequences).result - if new_keys: - if self.are_keys_builtin.get(): # Current key set is a built-in. - message = ('Your changes will be saved as a new Custom Key Set.' - ' Enter a name for your new Custom Key Set below.') - new_keyset = self.get_new_keys_name(message) - if not new_keyset: # User cancelled custom key set creation. - self.list_bindings.select_set(list_index) - self.list_bindings.select_anchor(list_index) - return - else: # Create new custom key set based on previously active key set. - self.create_new_key_set(new_keyset) - self.list_bindings.delete(list_index) - self.list_bindings.insert(list_index, bind_name+' - '+new_keys) - self.list_bindings.select_set(list_index) - self.list_bindings.select_anchor(list_index) - self.keybinding.set(new_keys) - else: - self.list_bindings.select_set(list_index) - self.list_bindings.select_anchor(list_index) + self.set_color_sample() - def get_new_keys_name(self, message): - "Return new key set name from query popup." - used_names = (idleConf.GetSectionList('user', 'keys') + - idleConf.GetSectionList('default', 'keys')) - new_keyset = SectionName( - self, 'New Custom Key Set', message, used_names).result - return new_keyset + def set_color_sample(self): + """Set the color of the frame background to reflect the selected target. - def save_as_new_key_set(self): - "Prompt for name of new key set and save changes using that name." - new_keys_name = self.get_new_keys_name('New Key Set Name:') - if new_keys_name: - self.create_new_key_set(new_keys_name) + Instance variables accessed: + theme_elements + highlight_target + fg_bg_toggle + highlight_sample - def keybinding_selected(self, event): - "Activate button to assign new keys to selected action." - self.button_new_keys['state'] = NORMAL + Attributes updated: + frame_color_set + """ + # Set the color sample area. + tag = self.theme_elements[self.highlight_target.get()][0] + plane = 'foreground' if self.fg_bg_toggle.get() else 'background' + color = self.highlight_sample.tag_cget(tag, plane) + self.frame_color_set.config(bg=color) - def create_new_key_set(self, new_key_set_name): - """Create a new custom key set with the given name. + def paint_theme_sample(self): + """Apply the theme colors to each element tag in the sample text. - Create the new key set based on the previously active set - with the current changes applied. Once it is saved, then - activate the new key set. - """ - if self.are_keys_builtin.get(): - prev_key_set_name = self.builtin_keys.get() - else: - prev_key_set_name = self.custom_keys.get() - prev_keys = idleConf.GetCoreKeys(prev_key_set_name) - new_keys = {} - for event in prev_keys: # Add key set to changed items. - event_name = event[2:-2] # Trim off the angle brackets. - binding = ' '.join(prev_keys[event]) - new_keys[event_name] = binding - # Handle any unsaved changes to prev key set. - if prev_key_set_name in changes['keys']: - key_set_changes = changes['keys'][prev_key_set_name] - for event in key_set_changes: - new_keys[event] = key_set_changes[event] - # Save the new key set. - self.save_new_key_set(new_key_set_name, new_keys) - # Change GUI over to the new key set. - custom_key_list = idleConf.GetSectionList('user', 'keys') - custom_key_list.sort() - self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name) - self.are_keys_builtin.set(0) - self.set_keys_type() + Instance attributes accessed: + theme_elements + is_builtin_theme + builtin_theme + custom_theme - def load_keys_list(self, keyset_name): - """Reload the list of action/key binding pairs for the active key set. + Attributes updated: + highlight_sample: Set the tag elements to the theme. - An action/key binding can be selected to change the key binding. + Methods: + set_color_sample + + Called from: + var_changed_builtin_theme + var_changed_custom_theme + load_theme_cfg """ - reselect = 0 - if self.list_bindings.curselection(): - reselect = 1 - list_index = self.list_bindings.index(ANCHOR) - keyset = idleConf.GetKeySet(keyset_name) - bind_names = list(keyset.keys()) - bind_names.sort() - self.list_bindings.delete(0, END) - for bind_name in bind_names: - key = ' '.join(keyset[bind_name]) - bind_name = bind_name[2:-2] # Trim off the angle brackets. - if keyset_name in changes['keys']: - # Handle any unsaved changes to this key set. - if bind_name in changes['keys'][keyset_name]: - key = changes['keys'][keyset_name][bind_name] - self.list_bindings.insert(END, bind_name+' - '+key) - if reselect: - self.list_bindings.see(list_index) - self.list_bindings.select_set(list_index) - self.list_bindings.select_anchor(list_index) + if self.is_builtin_theme.get(): # Default theme + theme = self.builtin_theme.get() + else: # User theme + theme = self.custom_theme.get() + for element_title in self.theme_elements: + element = self.theme_elements[element_title][0] + colors = idleConf.GetHighlight(theme, element) + if element == 'cursor': # Cursor sample needs special painting. + colors['background'] = idleConf.GetHighlight( + theme, 'normal', fgBg='bg') + # Handle any unsaved changes to this theme. + if theme in changes['highlight']: + theme_dict = changes['highlight'][theme] + if element + '-foreground' in theme_dict: + colors['foreground'] = theme_dict[element + '-foreground'] + if element + '-background' in theme_dict: + colors['background'] = theme_dict[element + '-background'] + self.highlight_sample.tag_config(element, **colors) + self.set_color_sample() - def delete_custom_keys(self): - """Handle event to delete a custom key set. + def save_new_theme(self, theme_name, theme): + """Save a newly created theme to idleConf. - Applying the delete deactivates the current configuration and - reverts to the default. The custom key set is permanently - deleted from the config file. + theme_name - string, the name of the new theme + theme - dictionary containing the new theme """ - keyset_name=self.custom_keys.get() - delmsg = 'Are you sure you wish to delete the key set %r ?' - if not tkMessageBox.askyesno( - 'Delete Key Set', delmsg % keyset_name, parent=self): - return - self.deactivate_current_config() - # Remove key set from changes, config, and file. - changes.delete_section('keys', keyset_name) - # Reload user key set list. - item_list = idleConf.GetSectionList('user', 'keys') - item_list.sort() - if not item_list: - self.radio_keys_custom['state'] = DISABLED - self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -') - else: - self.opt_menu_keys_custom.SetMenu(item_list, item_list[0]) - # Revert to default key set. - self.are_keys_builtin.set(idleConf.defaultCfg['main'] - .Get('Keys', 'default')) - self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name') - or idleConf.default_keys()) - # User can't back out of these changes, they must be applied now. - changes.save_all() - self.save_all_changed_extensions() - self.activate_config_changes() - self.set_keys_type() - - def delete_custom_theme(self): - """Handle event to delete custom theme. + if not idleConf.userCfg['highlight'].has_section(theme_name): + idleConf.userCfg['highlight'].add_section(theme_name) + for element in theme: + value = theme[element] + idleConf.userCfg['highlight'].SetOption(theme_name, element, value) - The current theme is deactivated and the default theme is - activated. The custom theme is permanently removed from - the config file. - Attributes accessed: - custom_theme + def create_page_keys(self): + """Return frame of widgets for Keys tab. - Attributes updated: - radio_theme_custom - opt_menu_theme_custom - is_builtin_theme - builtin_theme + Tk Variables: + builtin_keys: Menu variable for built-in keybindings. + custom_keys: Menu variable for custom keybindings. + are_keys_builtin: Selector for built-in or custom keybindings. + keybinding: Action/key bindings. Methods: - deactivate_current_config - save_all_changed_extensions - activate_config_changes - set_theme_type - """ - theme_name = self.custom_theme.get() - delmsg = 'Are you sure you wish to delete the theme %r ?' - if not tkMessageBox.askyesno( - 'Delete Theme', delmsg % theme_name, parent=self): - return - self.deactivate_current_config() - # Remove theme from changes, config, and file. - changes.delete_section('highlight', theme_name) - # Reload user theme list. - item_list = idleConf.GetSectionList('user', 'highlight') - item_list.sort() - if not item_list: - self.radio_theme_custom['state'] = DISABLED - self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -') - else: - self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) - # Revert to default theme. - self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) - self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) - # User can't back out of these changes, they must be applied now. - changes.save_all() - self.save_all_changed_extensions() - self.activate_config_changes() - self.set_theme_type() - - def get_color(self): - """Handle button to select a new color for the target tag. - - If a new color is selected while using a builtin theme, a - name must be supplied to create a custom theme. + load_key_config: Set table. + load_keys_list: Reload active set. + keybinding_selected: Bound to list_bindings button release. + get_new_keys: Command for button_new_keys. + get_new_keys_name: Call popup. + create_new_key_set: Combine active keyset and changes. + set_keys_type: Command for are_keys_builtin. + delete_custom_keys: Command for button_delete_custom_keys. + save_as_new_key_set: Command for button_save_custom_keys. + save_new_key_set: Save to idleConf.userCfg['keys'] (is function). + deactivate_current_config: Remove keys bindings in editors. - Attributes accessed: - highlight_target - frame_color_set - is_builtin_theme + Widget Structure: (*) widgets bound to self + frame + frame_custom: LabelFrame + frame_target: Frame + target_title: Label + scroll_target_y: Scrollbar + scroll_target_x: Scrollbar + (*)list_bindings: ListBox + (*)button_new_keys: Button + frame_key_sets: LabelFrame + frames[0]: Frame + (*)radio_keys_builtin: Radiobutton - are_keys_builtin + (*)radio_keys_custom: Radiobutton - are_keys_builtin + (*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys + (*)opt_menu_keys_custom: DynOptionMenu - custom_keys + (*)new_custom_keys: Label + frames[1]: Frame + (*)button_delete_custom_keys: Button + button_save_custom_keys: Button + """ + parent = self.parent + self.builtin_keys = StringVar(parent) + self.custom_keys = StringVar(parent) + self.are_keys_builtin = BooleanVar(parent) + self.keybinding = StringVar(parent) + + ##widget creation + #body frame + frame = self.tab_pages.pages['Keys'].frame + #body section frames + frame_custom = LabelFrame( + frame, borderwidth=2, relief=GROOVE, + text=' Custom Key Bindings ') + frame_key_sets = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Key Set ') + #frame_custom + frame_target = Frame(frame_custom) + target_title = Label(frame_target, text='Action - Key(s)') + scroll_target_y = Scrollbar(frame_target) + scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL) + self.list_bindings = Listbox( + frame_target, takefocus=FALSE, exportselection=FALSE) + self.list_bindings.bind('', self.keybinding_selected) + scroll_target_y.config(command=self.list_bindings.yview) + scroll_target_x.config(command=self.list_bindings.xview) + self.list_bindings.config(yscrollcommand=scroll_target_y.set) + self.list_bindings.config(xscrollcommand=scroll_target_x.set) + self.button_new_keys = Button( + frame_custom, text='Get New Keys for Selection', + command=self.get_new_keys, state=DISABLED) + #frame_key_sets + frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0) + for i in range(2)] + self.radio_keys_builtin = Radiobutton( + frames[0], variable=self.are_keys_builtin, value=1, + command=self.set_keys_type, text='Use a Built-in Key Set') + self.radio_keys_custom = Radiobutton( + frames[0], variable=self.are_keys_builtin, value=0, + command=self.set_keys_type, text='Use a Custom Key Set') + self.opt_menu_keys_builtin = DynOptionMenu( + frames[0], self.builtin_keys, None, command=None) + self.opt_menu_keys_custom = DynOptionMenu( + frames[0], self.custom_keys, None, command=None) + self.button_delete_custom_keys = Button( + frames[1], text='Delete Custom Key Set', + command=self.delete_custom_keys) + button_save_custom_keys = Button( + frames[1], text='Save as New Custom Key Set', + command=self.save_as_new_key_set) + self.new_custom_keys = Label(frames[0], bd=2) + + ##widget packing + #body + frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) + frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) + #frame_custom + self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5) + frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + #frame target + frame_target.columnconfigure(0, weight=1) + frame_target.rowconfigure(1, weight=1) + target_title.grid(row=0, column=0, columnspan=2, sticky=W) + self.list_bindings.grid(row=1, column=0, sticky=NSEW) + scroll_target_y.grid(row=1, column=1, sticky=NS) + scroll_target_x.grid(row=2, column=0, sticky=EW) + #frame_key_sets + self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS) + self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS) + self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW) + self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW) + self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5) + self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2) + button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2) + frames[0].pack(side=TOP, fill=BOTH, expand=True) + frames[1].pack(side=TOP, fill=X, expand=True, pady=2) + return frame + + def load_key_cfg(self): + "Load current configuration settings for the keybinding options." + # Set current keys type radiobutton. + self.are_keys_builtin.set(idleConf.GetOption( + 'main', 'Keys', 'default', type='bool', default=1)) + # Set current keys. + current_option = idleConf.CurrentKeys() + # Load available keyset option menus. + if self.are_keys_builtin.get(): # Default theme selected. + item_list = idleConf.GetSectionList('default', 'keys') + item_list.sort() + self.opt_menu_keys_builtin.SetMenu(item_list, current_option) + item_list = idleConf.GetSectionList('user', 'keys') + item_list.sort() + if not item_list: + self.radio_keys_custom['state'] = DISABLED + self.custom_keys.set('- no custom keys -') + else: + self.opt_menu_keys_custom.SetMenu(item_list, item_list[0]) + else: # User key set selected. + item_list = idleConf.GetSectionList('user', 'keys') + item_list.sort() + self.opt_menu_keys_custom.SetMenu(item_list, current_option) + item_list = idleConf.GetSectionList('default', 'keys') + item_list.sort() + self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys()) + self.set_keys_type() + # Load keyset element list. + keyset_name = idleConf.CurrentKeys() + self.load_keys_list(keyset_name) + + + def create_page_general(self): + """Return frame of widgets for General tab. + + Enable users to provisionally change general options. Function + load_general_cfg intializes 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 win_width and win_height. Setting var_name invokes the + var_changed_var_name callback that adds option to changes. + + Helplist: load_general_cfg loads list user_helplist with + 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']. + + Widget Structure: (*) widgets bound to self + frame + frame_run: LabelFrame + startup_title: Label + (*)startup_editor_on: Radiobutton - startup_edit + (*)startup_shell_on: Radiobutton - startup_edit + frame_save: LabelFrame + run_save_title: Label + (*)save_ask_on: Radiobutton - autosave + (*)save_auto_on: Radiobutton - autosave + frame_win_size: LabelFrame + win_size_title: Label + win_width_title: Label + (*)win_width_int: Entry - win_width + win_height_title: Label + (*)win_height_int: Entry - win_height + frame_help: LabelFrame + frame_helplist: Frame + frame_helplist_buttons: Frame + (*)button_helplist_edit + (*)button_helplist_add + (*)button_helplist_remove + (*)helplist: ListBox + scroll_helplist: Scrollbar + """ + parent = self.parent + self.startup_edit = IntVar(parent) + self.autosave = IntVar(parent) + self.win_width = StringVar(parent) + self.win_height = StringVar(parent) + + # Create widgets: + # body. + frame = self.tab_pages.pages['General'].frame + # body section frames. + frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Startup Preferences ') + frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' autosave Preferences ') + frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE) + frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Additional Help Sources ') + # frame_run. + startup_title = Label(frame_run, text='At Startup') + self.startup_editor_on = Radiobutton( + frame_run, variable=self.startup_edit, value=1, + text="Open Edit Window") + self.startup_shell_on = Radiobutton( + frame_run, variable=self.startup_edit, value=0, + text='Open Shell Window') + # frame_save. + 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_win_size. + win_size_title = Label( + frame_win_size, text='Initial Window Size (in characters)') + win_width_title = Label(frame_win_size, text='Width') + self.win_width_int = Entry( + frame_win_size, textvariable=self.win_width, width=3) + win_height_title = Label(frame_win_size, text='Height') + self.win_height_int = Entry( + frame_win_size, textvariable=self.win_height, width=3) + # frame_help. + frame_helplist = Frame(frame_help) + frame_helplist_buttons = Frame(frame_helplist) + self.helplist = Listbox( + frame_helplist, height=5, takefocus=FALSE, + exportselection=FALSE) + scroll_helplist = Scrollbar(frame_helplist) + scroll_helplist['command'] = self.helplist.yview + self.helplist['yscrollcommand'] = scroll_helplist.set + self.helplist.bind('', self.help_source_selected) + self.button_helplist_edit = Button( + frame_helplist_buttons, text='Edit', state=DISABLED, + width=8, command=self.helplist_item_edit) + self.button_helplist_add = Button( + frame_helplist_buttons, text='Add', + width=8, command=self.helplist_item_add) + self.button_helplist_remove = Button( + frame_helplist_buttons, text='Remove', state=DISABLED, + width=8, command=self.helplist_item_remove) + + # Pack widgets: + # body. + frame_run.pack(side=TOP, padx=5, pady=5, fill=X) + frame_save.pack(side=TOP, padx=5, pady=5, fill=X) + frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X) + frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + # frame_run. + startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + # frame_save. + 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_win_size. + win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) + 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_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) + scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y) + self.helplist.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) + self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5) + self.button_helplist_add.pack(side=TOP, anchor=W) + self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5) + + return frame + + def load_general_cfg(self): + "Load current configuration settings for the general options." + # Set startup state. + self.startup_edit.set(idleConf.GetOption( + 'main', 'General', 'editor-on-startup', default=0, type='bool')) + # Set autosave state. + self.autosave.set(idleConf.GetOption( + 'main', 'General', 'autosave', default=0, type='bool')) + # Set initial window size. + self.win_width.set(idleConf.GetOption( + 'main', 'EditorWindow', 'width', type='int')) + self.win_height.set(idleConf.GetOption( + 'main', 'EditorWindow', 'height', type='int')) + # Set additional help sources. + self.user_helplist = idleConf.GetAllExtraHelpSourcesList() + self.helplist.delete(0, 'end') + for help_item in self.user_helplist: + self.helplist.insert(END, help_item[0]) + self.set_add_delete_state() + + def var_changed_startup_edit(self, *params): + "Store change to toggle for starting IDLE in the editor or shell." + value = self.startup_edit.get() + changes.add_option('main', 'General', 'editor-on-startup', value) + + def var_changed_autosave(self, *params): + "Store change to autosave." + value = self.autosave.get() + changes.add_option('main', 'General', 'autosave', value) + + def var_changed_win_width(self, *params): + "Store change to window width." + value = self.win_width.get() + changes.add_option('main', 'EditorWindow', 'width', value) + + def var_changed_win_height(self, *params): + "Store change to window height." + value = self.win_height.get() + changes.add_option('main', 'EditorWindow', 'height', value) + + def help_source_selected(self, event): + "Handle event for selecting additional help." + self.set_add_delete_state() + + def set_add_delete_state(self): + "Toggle the state for the help list buttons based on list entries." + if self.helplist.size() < 1: # No entries in list. + self.button_helplist_edit['state'] = DISABLED + self.button_helplist_remove['state'] = DISABLED + else: # Some entries. + if self.helplist.curselection(): # There currently is a selection. + self.button_helplist_edit['state'] = NORMAL + self.button_helplist_remove['state'] = NORMAL + else: # There currently is not a selection. + self.button_helplist_edit['state'] = DISABLED + self.button_helplist_remove['state'] = DISABLED + + def helplist_item_add(self): + """Handle add button for the help list. + + Query for name and location of new help sources and add + them to the list. + """ + help_source = HelpSource(self, 'New Help Source').result + if help_source: + self.user_helplist.append(help_source) + self.helplist.insert(END, help_source[0]) + self.update_help_changes() - Attributes updated: - color + def helplist_item_edit(self): + """Handle edit button for the help list. - Methods: - get_new_theme_name - create_new_theme + Query with existing help source information and update + config if the values are changed. """ - target = self.highlight_target.get() - prev_color = self.frame_color_set.cget('bg') - rgbTuplet, color_string = tkColorChooser.askcolor( - parent=self, title='Pick new color for : '+target, - initialcolor=prev_color) - if color_string and (color_string != prev_color): - # User didn't cancel and they chose a new color. - if self.is_builtin_theme.get(): # Current theme is a built-in. - message = ('Your changes will be saved as a new Custom Theme. ' - 'Enter a name for your new Custom Theme below.') - new_theme = self.get_new_theme_name(message) - if not new_theme: # User cancelled custom theme creation. - return - else: # Create new custom theme based on previously active theme. - self.create_new_theme(new_theme) - self.color.set(color_string) - else: # Current theme is user defined. - self.color.set(color_string) + item_index = self.helplist.index(ANCHOR) + help_source = self.user_helplist[item_index] + new_help_source = HelpSource( + self, 'Edit Help Source', + menuitem=help_source[0], + filepath=help_source[1], + ).result + if new_help_source and new_help_source != help_source: + self.user_helplist[item_index] = new_help_source + self.helplist.delete(item_index) + self.helplist.insert(item_index, new_help_source[0]) + self.update_help_changes() + self.set_add_delete_state() # Selected will be un-selected - def on_new_color_set(self): - "Display sample of new color selection on the dialog." - new_color=self.color.get() - self.frame_color_set.config(bg=new_color) # Set sample. - plane ='foreground' if self.fg_bg_toggle.get() else 'background' - sample_element = self.theme_elements[self.highlight_target.get()][0] - self.highlight_sample.tag_config(sample_element, **{plane:new_color}) - theme = self.custom_theme.get() - theme_element = sample_element + '-' + plane - changes.add_option('highlight', theme, theme_element, new_color) + def helplist_item_remove(self): + """Handle remove button for the help list. - def get_new_theme_name(self, message): - "Return name of new theme from query popup." - used_names = (idleConf.GetSectionList('user', 'highlight') + - idleConf.GetSectionList('default', 'highlight')) - new_theme = SectionName( - self, 'New Custom Theme', message, used_names).result - return new_theme + Delete the help list item from config. + """ + item_index = self.helplist.index(ANCHOR) + del(self.user_helplist[item_index]) + self.helplist.delete(item_index) + self.update_help_changes() + self.set_add_delete_state() - def save_as_new_theme(self): - """Prompt for new theme name and create the theme. + def update_help_changes(self): + "Clear and rebuild the HelpFiles section in changes" + changes['main']['HelpFiles'] = {} + for num in range(1, len(self.user_helplist) + 1): + changes.add_option( + 'main', 'HelpFiles', str(num), + ';'.join(self.user_helplist[num-1][:2])) - Methods: - get_new_theme_name - create_new_theme - """ - new_theme_name = self.get_new_theme_name('New Theme Name:') - if new_theme_name: - self.create_new_theme(new_theme_name) - def create_new_theme(self, new_theme_name): - """Create a new custom theme with the given name. + def attach_var_callbacks(self): + "Attach callbacks to variables that can be changed." + self.font_size.trace_add('write', self.var_changed_font) + self.font_name.trace_add('write', self.var_changed_font) + self.font_bold.trace_add('write', self.var_changed_font) + self.space_num.trace_add('write', self.var_changed_space_num) + self.color.trace_add('write', self.var_changed_color) + self.builtin_theme.trace_add('write', self.var_changed_builtin_theme) + self.custom_theme.trace_add('write', self.var_changed_custom_theme) + self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme) + self.highlight_target.trace_add('write', self.var_changed_highlight_target) + self.keybinding.trace_add('write', self.var_changed_keybinding) + self.builtin_keys.trace_add('write', self.var_changed_builtin_keys) + self.custom_keys.trace_add('write', self.var_changed_custom_keys) + self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin) + self.win_width.trace_add('write', self.var_changed_win_width) + self.win_height.trace_add('write', self.var_changed_win_height) + self.startup_edit.trace_add('write', self.var_changed_startup_edit) + self.autosave.trace_add('write', self.var_changed_autosave) - Create the new theme based on the previously active theme - with the current changes applied. Once it is saved, then - activate the new theme. + def remove_var_callbacks(self): + "Remove callbacks to prevent memory leaks." + for var in ( + self.font_size, self.font_name, self.font_bold, + self.space_num, self.color, self.builtin_theme, + self.custom_theme, self.is_builtin_theme, self.highlight_target, + self.keybinding, self.builtin_keys, self.custom_keys, + self.are_keys_builtin, self.win_width, self.win_height, + self.startup_edit, self.autosave,): + var.trace_remove('write', var.trace_info()[0][1]) - Attributes accessed: - builtin_theme - custom_theme + def var_changed_keybinding(self, *params): + "Store change to a keybinding." + value = self.keybinding.get() + key_set = self.custom_keys.get() + event = self.list_bindings.get(ANCHOR).split()[0] + if idleConf.IsCoreBinding(event): + changes.add_option('keys', key_set, event, value) + else: # Event is an extension binding. + ext_name = idleConf.GetExtnNameForEvent(event) + ext_keybind_section = ext_name + '_cfgBindings' + changes.add_option('extensions', ext_keybind_section, event, value) - Attributes updated: - opt_menu_theme_custom - is_builtin_theme + def var_changed_builtin_keys(self, *params): + "Process selection of builtin key set." + old_keys = ( + 'IDLE Classic Windows', + 'IDLE Classic Unix', + 'IDLE Classic Mac', + 'IDLE Classic OSX', + ) + value = self.builtin_keys.get() + if value not in old_keys: + if idleConf.GetOption('main', 'Keys', 'name') not in old_keys: + changes.add_option('main', 'Keys', 'name', old_keys[0]) + changes.add_option('main', 'Keys', 'name2', value) + self.new_custom_keys.config(text='New key set, see Help', + fg='#500000') + else: + changes.add_option('main', 'Keys', 'name', value) + changes.add_option('main', 'Keys', 'name2', '') + self.new_custom_keys.config(text='', fg='black') + self.load_keys_list(value) - Method: - save_new_theme - set_theme_type - """ - if self.is_builtin_theme.get(): - theme_type = 'default' - theme_name = self.builtin_theme.get() + def var_changed_custom_keys(self, *params): + "Process selection of custom key set." + value = self.custom_keys.get() + if value != '- no custom keys -': + changes.add_option('main', 'Keys', 'name', value) + self.load_keys_list(value) + + def var_changed_are_keys_builtin(self, *params): + "Process toggle between builtin key set and custom key set." + value = self.are_keys_builtin.get() + changes.add_option('main', 'Keys', 'default', value) + if value: + self.var_changed_builtin_keys() else: - theme_type = 'user' - theme_name = self.custom_theme.get() - new_theme = idleConf.GetThemeDict(theme_type, theme_name) - # Apply any of the old theme's unsaved changes to the new theme. - if theme_name in changes['highlight']: - theme_changes = changes['highlight'][theme_name] - for element in theme_changes: - new_theme[element] = theme_changes[element] - # Save the new theme. - self.save_new_theme(new_theme_name, new_theme) - # Change GUI over to the new theme. - custom_theme_list = idleConf.GetSectionList('user', 'highlight') - custom_theme_list.sort() - self.opt_menu_theme_custom.SetMenu(custom_theme_list, new_theme_name) - self.is_builtin_theme.set(0) - self.set_theme_type() + self.var_changed_custom_keys() - def set_highlight_target(self): - """Set fg/bg toggle and color based on highlight tag target. + def set_theme_type(self): + """Set available screen options based on builtin or custom theme. - Instance variables accessed: - highlight_target + Attributes accessed: + is_builtin_theme Attributes updated: - radio_fg - radio_bg - fg_bg_toggle - - Methods: - set_color_sample + opt_menu_theme_builtin + opt_menu_theme_custom + button_delete_custom_theme + radio_theme_custom Called from: - var_changed_highlight_target + handler for radio_theme_builtin and radio_theme_custom + delete_custom_theme + create_new_theme load_theme_cfg """ - if self.highlight_target.get() == 'Cursor': # bg not possible - self.radio_fg['state'] = DISABLED - self.radio_bg['state'] = DISABLED - self.fg_bg_toggle.set(1) - else: # Both fg and bg can be set. - self.radio_fg['state'] = NORMAL - self.radio_bg['state'] = NORMAL - self.fg_bg_toggle.set(1) - self.set_color_sample() - - def set_color_sample_binding(self, *args): - """Change color sample based on foreground/background toggle. - - Methods: - set_color_sample - """ - self.set_color_sample() + if self.is_builtin_theme.get(): + self.opt_menu_theme_builtin['state'] = NORMAL + self.opt_menu_theme_custom['state'] = DISABLED + self.button_delete_custom_theme['state'] = DISABLED + else: + self.opt_menu_theme_builtin['state'] = DISABLED + self.radio_theme_custom['state'] = NORMAL + self.opt_menu_theme_custom['state'] = NORMAL + self.button_delete_custom_theme['state'] = NORMAL - def set_color_sample(self): - """Set the color of the frame background to reflect the selected target. + def set_keys_type(self): + "Set available screen options based on builtin or custom key set." + if self.are_keys_builtin.get(): + self.opt_menu_keys_builtin['state'] = NORMAL + self.opt_menu_keys_custom['state'] = DISABLED + self.button_delete_custom_keys['state'] = DISABLED + else: + self.opt_menu_keys_builtin['state'] = DISABLED + self.radio_keys_custom['state'] = NORMAL + self.opt_menu_keys_custom['state'] = NORMAL + self.button_delete_custom_keys['state'] = NORMAL - Instance variables accessed: - theme_elements - highlight_target - fg_bg_toggle - highlight_sample + def get_new_keys(self): + """Handle event to change key binding for selected line. - Attributes updated: - frame_color_set + A selection of a key/binding in the list of current + bindings pops up a dialog to enter a new binding. If + the current key set is builtin and a binding has + changed, then a name for a custom key set needs to be + entered for the change to be applied. """ - # Set the color sample area. - tag = self.theme_elements[self.highlight_target.get()][0] - plane = 'foreground' if self.fg_bg_toggle.get() else 'background' - color = self.highlight_sample.tag_cget(tag, plane) - self.frame_color_set.config(bg=color) + list_index = self.list_bindings.index(ANCHOR) + binding = self.list_bindings.get(list_index) + bind_name = binding.split()[0] + if self.are_keys_builtin.get(): + current_key_set_name = self.builtin_keys.get() + else: + current_key_set_name = self.custom_keys.get() + current_bindings = idleConf.GetCurrentKeySet() + if current_key_set_name in changes['keys']: # unsaved changes + key_set_changes = changes['keys'][current_key_set_name] + for event in key_set_changes: + current_bindings[event] = key_set_changes[event].split() + current_key_sequences = list(current_bindings.values()) + new_keys = GetKeysDialog(self, 'Get New Keys', bind_name, + current_key_sequences).result + if new_keys: + if self.are_keys_builtin.get(): # Current key set is a built-in. + message = ('Your changes will be saved as a new Custom Key Set.' + ' Enter a name for your new Custom Key Set below.') + new_keyset = self.get_new_keys_name(message) + if not new_keyset: # User cancelled custom key set creation. + self.list_bindings.select_set(list_index) + self.list_bindings.select_anchor(list_index) + return + else: # Create new custom key set based on previously active key set. + self.create_new_key_set(new_keyset) + self.list_bindings.delete(list_index) + self.list_bindings.insert(list_index, bind_name+' - '+new_keys) + self.list_bindings.select_set(list_index) + self.list_bindings.select_anchor(list_index) + self.keybinding.set(new_keys) + else: + self.list_bindings.select_set(list_index) + self.list_bindings.select_anchor(list_index) - def paint_theme_sample(self): - """Apply the theme colors to each element tag in the sample text. + def get_new_keys_name(self, message): + "Return new key set name from query popup." + used_names = (idleConf.GetSectionList('user', 'keys') + + idleConf.GetSectionList('default', 'keys')) + new_keyset = SectionName( + self, 'New Custom Key Set', message, used_names).result + return new_keyset - Instance attributes accessed: - theme_elements - is_builtin_theme - builtin_theme - custom_theme + def save_as_new_key_set(self): + "Prompt for name of new key set and save changes using that name." + new_keys_name = self.get_new_keys_name('New Key Set Name:') + if new_keys_name: + self.create_new_key_set(new_keys_name) - Attributes updated: - highlight_sample: Set the tag elements to the theme. + def keybinding_selected(self, event): + "Activate button to assign new keys to selected action." + self.button_new_keys['state'] = NORMAL - Methods: - set_color_sample + def create_new_key_set(self, new_key_set_name): + """Create a new custom key set with the given name. - Called from: - var_changed_builtin_theme - var_changed_custom_theme - load_theme_cfg + Create the new key set based on the previously active set + with the current changes applied. Once it is saved, then + activate the new key set. """ - if self.is_builtin_theme.get(): # Default theme - theme = self.builtin_theme.get() - else: # User theme - theme = self.custom_theme.get() - for element_title in self.theme_elements: - element = self.theme_elements[element_title][0] - colors = idleConf.GetHighlight(theme, element) - if element == 'cursor': # Cursor sample needs special painting. - colors['background'] = idleConf.GetHighlight( - theme, 'normal', fgBg='bg') - # Handle any unsaved changes to this theme. - if theme in changes['highlight']: - theme_dict = changes['highlight'][theme] - if element + '-foreground' in theme_dict: - colors['foreground'] = theme_dict[element + '-foreground'] - if element + '-background' in theme_dict: - colors['background'] = theme_dict[element + '-background'] - self.highlight_sample.tag_config(element, **colors) - self.set_color_sample() + if self.are_keys_builtin.get(): + prev_key_set_name = self.builtin_keys.get() + else: + prev_key_set_name = self.custom_keys.get() + prev_keys = idleConf.GetCoreKeys(prev_key_set_name) + new_keys = {} + for event in prev_keys: # Add key set to changed items. + event_name = event[2:-2] # Trim off the angle brackets. + binding = ' '.join(prev_keys[event]) + new_keys[event_name] = binding + # Handle any unsaved changes to prev key set. + if prev_key_set_name in changes['keys']: + key_set_changes = changes['keys'][prev_key_set_name] + for event in key_set_changes: + new_keys[event] = key_set_changes[event] + # Save the new key set. + self.save_new_key_set(new_key_set_name, new_keys) + # Change GUI over to the new key set. + custom_key_list = idleConf.GetSectionList('user', 'keys') + custom_key_list.sort() + self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name) + self.are_keys_builtin.set(0) + self.set_keys_type() - def load_theme_cfg(self): - """Load current configuration settings for the theme options. + def load_keys_list(self, keyset_name): + """Reload the list of action/key binding pairs for the active key set. - Based on the is_builtin_theme toggle, the theme is set as - either builtin or custom and the initial widget values - reflect the current settings from idleConf. + An action/key binding can be selected to change the key binding. + """ + reselect = 0 + if self.list_bindings.curselection(): + reselect = 1 + list_index = self.list_bindings.index(ANCHOR) + keyset = idleConf.GetKeySet(keyset_name) + bind_names = list(keyset.keys()) + bind_names.sort() + self.list_bindings.delete(0, END) + for bind_name in bind_names: + key = ' '.join(keyset[bind_name]) + bind_name = bind_name[2:-2] # Trim off the angle brackets. + if keyset_name in changes['keys']: + # Handle any unsaved changes to this key set. + if bind_name in changes['keys'][keyset_name]: + key = changes['keys'][keyset_name][bind_name] + self.list_bindings.insert(END, bind_name+' - '+key) + if reselect: + self.list_bindings.see(list_index) + self.list_bindings.select_set(list_index) + self.list_bindings.select_anchor(list_index) - Attributes updated: - is_builtin_theme: Set from idleConf. - opt_menu_theme_builtin: List of default themes from idleConf. - opt_menu_theme_custom: List of custom themes from idleConf. - radio_theme_custom: Disabled if there are no custom themes. - custom_theme: Message with additional information. - opt_menu_highlight_target: Create menu from self.theme_elements. + def delete_custom_keys(self): + """Handle event to delete a custom key set. - Methods: - set_theme_type - paint_theme_sample - set_highlight_target + Applying the delete deactivates the current configuration and + reverts to the default. The custom key set is permanently + deleted from the config file. """ - # Set current theme type radiobutton. - self.is_builtin_theme.set(idleConf.GetOption( - 'main', 'Theme', 'default', type='bool', default=1)) - # Set current theme. - current_option = idleConf.CurrentTheme() - # Load available theme option menus. - if self.is_builtin_theme.get(): # Default theme selected. - item_list = idleConf.GetSectionList('default', 'highlight') - item_list.sort() - self.opt_menu_theme_builtin.SetMenu(item_list, current_option) - item_list = idleConf.GetSectionList('user', 'highlight') - item_list.sort() - if not item_list: - self.radio_theme_custom['state'] = DISABLED - self.custom_theme.set('- no custom themes -') - else: - self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) - else: # User theme selected. - item_list = idleConf.GetSectionList('user', 'highlight') - item_list.sort() - self.opt_menu_theme_custom.SetMenu(item_list, current_option) - item_list = idleConf.GetSectionList('default', 'highlight') - item_list.sort() - self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0]) - self.set_theme_type() - # Load theme element option menu. - theme_names = list(self.theme_elements.keys()) - theme_names.sort(key=lambda x: self.theme_elements[x][1]) - self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0]) - self.paint_theme_sample() - self.set_highlight_target() - - def load_key_cfg(self): - "Load current configuration settings for the keybinding options." - # Set current keys type radiobutton. - self.are_keys_builtin.set(idleConf.GetOption( - 'main', 'Keys', 'default', type='bool', default=1)) - # Set current keys. - current_option = idleConf.CurrentKeys() - # Load available keyset option menus. - if self.are_keys_builtin.get(): # Default theme selected. - item_list = idleConf.GetSectionList('default', 'keys') - item_list.sort() - self.opt_menu_keys_builtin.SetMenu(item_list, current_option) - item_list = idleConf.GetSectionList('user', 'keys') - item_list.sort() - if not item_list: - self.radio_keys_custom['state'] = DISABLED - self.custom_keys.set('- no custom keys -') - else: - self.opt_menu_keys_custom.SetMenu(item_list, item_list[0]) - else: # User key set selected. - item_list = idleConf.GetSectionList('user', 'keys') - item_list.sort() - self.opt_menu_keys_custom.SetMenu(item_list, current_option) - item_list = idleConf.GetSectionList('default', 'keys') - item_list.sort() - self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys()) + keyset_name=self.custom_keys.get() + delmsg = 'Are you sure you wish to delete the key set %r ?' + if not tkMessageBox.askyesno( + 'Delete Key Set', delmsg % keyset_name, parent=self): + return + self.deactivate_current_config() + # Remove key set from changes, config, and file. + changes.delete_section('keys', keyset_name) + # Reload user key set list. + item_list = idleConf.GetSectionList('user', 'keys') + item_list.sort() + if not item_list: + self.radio_keys_custom['state'] = DISABLED + self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -') + else: + self.opt_menu_keys_custom.SetMenu(item_list, item_list[0]) + # Revert to default key set. + self.are_keys_builtin.set(idleConf.defaultCfg['main'] + .Get('Keys', 'default')) + self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name') + or idleConf.default_keys()) + # User can't back out of these changes, they must be applied now. + changes.save_all() + self.save_all_changed_extensions() + self.activate_config_changes() self.set_keys_type() - # Load keyset element list. - keyset_name = idleConf.CurrentKeys() - self.load_keys_list(keyset_name) def load_configs(self): """Load configuration for each page. From 1087f476ff07742bc0b7ebccb822eac6fae0bafc Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 27 Jul 2017 14:51:11 -0400 Subject: [PATCH 3/5] Replace News blurb. --- Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst | 1 - Misc/NEWS.d/next/IDLE/2017-07-27-14-48-42.bpo-31060.GdY_VY.rst | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst create mode 100644 Misc/NEWS.d/next/IDLE/2017-07-27-14-48-42.bpo-31060.GdY_VY.rst diff --git a/Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst b/Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst deleted file mode 100644 index 8cf11875c3c06d4..000000000000000 --- a/Misc/NEWS.d/next/IDLE/2017-07-27-03-17-42.bpo-31001.HkSQAV.rst +++ /dev/null @@ -1 +0,0 @@ -IDLE - Add tests for ConfigDialog highlight tab. diff --git a/Misc/NEWS.d/next/IDLE/2017-07-27-14-48-42.bpo-31060.GdY_VY.rst b/Misc/NEWS.d/next/IDLE/2017-07-27-14-48-42.bpo-31060.GdY_VY.rst new file mode 100644 index 000000000000000..1d202c7fa2c5fe8 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2017-07-27-14-48-42.bpo-31060.GdY_VY.rst @@ -0,0 +1,3 @@ +IDLE - Finish rearranging methods of ConfigDialog Grouping methods +pertaining to each tab and the buttons will aid writing tests and improving +the tabs and will enable splitting the groups into classes. From 55b787a9e5ebd5e7f74412838cebeddb5866a2ea Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 27 Jul 2017 15:33:53 -0400 Subject: [PATCH 4/5] Move dialog methods under create_widgets. --- Lib/idlelib/configdialog.py | 108 +++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index afb54f122cd7c5f..55423623825165f 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -30,7 +30,6 @@ changes = ConfigChanges() - class ConfigDialog(Toplevel): """Config dialog for IDLE. """ @@ -112,6 +111,62 @@ def create_widgets(self): self.create_page_extensions() self.create_action_buttons().pack(side=BOTTOM) + def load_configs(self): + """Load configuration for each page. + + Load configuration from default and user config files and populate + the widgets on the config dialog pages. + + Methods: + load_font_cfg + load_tab_cfg + load_theme_cfg + load_key_cfg + load_general_cfg + """ + self.load_font_cfg() + self.load_tab_cfg() + self.load_theme_cfg() + self.load_key_cfg() + self.load_general_cfg() + # note: extension page handled separately + + def attach_var_callbacks(self): + "Attach callbacks to variables that can be changed." + self.font_size.trace_add('write', self.var_changed_font) + self.font_name.trace_add('write', self.var_changed_font) + self.font_bold.trace_add('write', self.var_changed_font) + self.space_num.trace_add('write', self.var_changed_space_num) + self.color.trace_add('write', self.var_changed_color) + self.builtin_theme.trace_add('write', self.var_changed_builtin_theme) + self.custom_theme.trace_add('write', self.var_changed_custom_theme) + self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme) + self.highlight_target.trace_add('write', self.var_changed_highlight_target) + self.keybinding.trace_add('write', self.var_changed_keybinding) + self.builtin_keys.trace_add('write', self.var_changed_builtin_keys) + self.custom_keys.trace_add('write', self.var_changed_custom_keys) + self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin) + self.win_width.trace_add('write', self.var_changed_win_width) + self.win_height.trace_add('write', self.var_changed_win_height) + self.startup_edit.trace_add('write', self.var_changed_startup_edit) + self.autosave.trace_add('write', self.var_changed_autosave) + + def remove_var_callbacks(self): + "Remove callbacks to prevent memory leaks." + for var in ( + self.font_size, self.font_name, self.font_bold, + self.space_num, self.color, self.builtin_theme, + self.custom_theme, self.is_builtin_theme, self.highlight_target, + self.keybinding, self.builtin_keys, self.custom_keys, + self.are_keys_builtin, self.win_width, self.win_height, + self.startup_edit, self.autosave,): + var.trace_remove('write', var.trace_info()[0][1]) + + + + + + def create_action_buttons(self): """Return frame of action buttons for dialog. @@ -1321,37 +1376,6 @@ def update_help_changes(self): ';'.join(self.user_helplist[num-1][:2])) - def attach_var_callbacks(self): - "Attach callbacks to variables that can be changed." - self.font_size.trace_add('write', self.var_changed_font) - self.font_name.trace_add('write', self.var_changed_font) - self.font_bold.trace_add('write', self.var_changed_font) - self.space_num.trace_add('write', self.var_changed_space_num) - self.color.trace_add('write', self.var_changed_color) - self.builtin_theme.trace_add('write', self.var_changed_builtin_theme) - self.custom_theme.trace_add('write', self.var_changed_custom_theme) - self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme) - self.highlight_target.trace_add('write', self.var_changed_highlight_target) - self.keybinding.trace_add('write', self.var_changed_keybinding) - self.builtin_keys.trace_add('write', self.var_changed_builtin_keys) - self.custom_keys.trace_add('write', self.var_changed_custom_keys) - self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin) - self.win_width.trace_add('write', self.var_changed_win_width) - self.win_height.trace_add('write', self.var_changed_win_height) - self.startup_edit.trace_add('write', self.var_changed_startup_edit) - self.autosave.trace_add('write', self.var_changed_autosave) - - def remove_var_callbacks(self): - "Remove callbacks to prevent memory leaks." - for var in ( - self.font_size, self.font_name, self.font_bold, - self.space_num, self.color, self.builtin_theme, - self.custom_theme, self.is_builtin_theme, self.highlight_target, - self.keybinding, self.builtin_keys, self.custom_keys, - self.are_keys_builtin, self.win_width, self.win_height, - self.startup_edit, self.autosave,): - var.trace_remove('write', var.trace_info()[0][1]) - def var_changed_keybinding(self, *params): "Store change to a keybinding." value = self.keybinding.get() @@ -1594,26 +1618,6 @@ def delete_custom_keys(self): self.activate_config_changes() self.set_keys_type() - def load_configs(self): - """Load configuration for each page. - - Load configuration from default and user config files and populate - the widgets on the config dialog pages. - - Methods: - load_font_cfg - load_tab_cfg - load_theme_cfg - load_key_cfg - load_general_cfg - """ - self.load_font_cfg() - self.load_tab_cfg() - self.load_theme_cfg() - self.load_key_cfg() - self.load_general_cfg() - # note: extension page handled separately - def save_new_key_set(self, keyset_name, keyset): """Save a newly created core key set. From e029233250cc562b7ac67f8d4d9735b18a552e2f Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 27 Jul 2017 16:24:30 -0400 Subject: [PATCH 5/5] Move dialog and action button methods, leaving keys methods together. Do some rearrangements within groups. Check grep of 'def ' against re-arranged version of same grep before movements. --- Lib/idlelib/configdialog.py | 817 +++++++++++++++++------------------- 1 file changed, 388 insertions(+), 429 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 55423623825165f..e359ec24cd3a5bb 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -163,10 +163,6 @@ def remove_var_callbacks(self): var.trace_remove('write', var.trace_info()[0][1]) - - - - def create_action_buttons(self): """Return frame of action buttons for dialog. @@ -205,6 +201,50 @@ def create_action_buttons(self): buttons.pack(side=BOTTOM) return outer + def ok(self): + """Apply config changes, then dismiss dialog. + + Methods: + apply + destroy: inherited + """ + 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 + """ + self.deactivate_current_config() + changes.save_all() + self.save_all_changed_extensions() + self.activate_config_changes() + + def cancel(self): + """Dismiss config dialog. + + Methods: + destroy: inherited + """ + self.destroy() + + def help(self): + """Create textview for config dialog help. + + Attrbutes accessed: + tab_pages + + Methods: + view_text: Method from textview module. + """ + page = self.tab_pages._current_page + view_text(self, title='Help for IDLE preferences', + text=help_common+help_pages.get(page, '')) + def create_page_font_tab(self): """Return frame of widgets for Font/Tabs tab. @@ -354,16 +394,6 @@ def load_font_cfg(self): # Set font weight. self.font_bold.set(font_bold) - def on_fontlist_select(self, event): - """Handle selecting a font from the list. - - Event can result from either mouse click or Up or Down key. - Set font_name and example displays to selection. - """ - font = self.fontlist.get( - ACTIVE if event.type.name == 'KeyRelease' else ANCHOR) - self.font_name.set(font.lower()) - def var_changed_font(self, *params): """Store changes to font attributes. @@ -379,6 +409,16 @@ def var_changed_font(self, *params): changes.add_option('main', 'EditorWindow', 'font-bold', value) self.set_samples() + def on_fontlist_select(self, event): + """Handle selecting a font from the list. + + Event can result from either mouse click or Up or Down key. + Set font_name and example displays to selection. + """ + font = self.fontlist.get( + ACTIVE if event.type.name == 'KeyRelease' else ANCHOR) + self.font_name.set(font.lower()) + def set_samples(self, event=None): """Update update both screen samples with the font settings. @@ -638,10 +678,6 @@ def load_theme_cfg(self): self.paint_theme_sample() self.set_highlight_target() - def var_changed_color(self, *params): - "Process change to color choice." - self.on_new_color_set() - def var_changed_builtin_theme(self, *params): """Process new builtin theme selection. @@ -686,6 +722,10 @@ def var_changed_is_builtin_theme(self, *params): else: self.var_changed_custom_theme() + def var_changed_color(self, *params): + "Process change to color choice." + self.on_new_color_set() + def var_changed_highlight_target(self, *params): "Process selection of new target tag for highlighting." self.set_highlight_target() @@ -718,53 +758,6 @@ def set_theme_type(self): self.opt_menu_theme_custom['state'] = NORMAL self.button_delete_custom_theme['state'] = NORMAL - def delete_custom_theme(self): - """Handle event to delete custom theme. - - The current theme is deactivated and the default theme is - activated. The custom theme is permanently removed from - the config file. - - Attributes accessed: - custom_theme - - Attributes updated: - radio_theme_custom - opt_menu_theme_custom - is_builtin_theme - builtin_theme - - Methods: - deactivate_current_config - save_all_changed_extensions - activate_config_changes - set_theme_type - """ - theme_name = self.custom_theme.get() - delmsg = 'Are you sure you wish to delete the theme %r ?' - if not tkMessageBox.askyesno( - 'Delete Theme', delmsg % theme_name, parent=self): - return - self.deactivate_current_config() - # Remove theme from changes, config, and file. - changes.delete_section('highlight', theme_name) - # Reload user theme list. - item_list = idleConf.GetSectionList('user', 'highlight') - item_list.sort() - if not item_list: - self.radio_theme_custom['state'] = DISABLED - self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -') - else: - self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) - # Revert to default theme. - self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) - self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) - # User can't back out of these changes, they must be applied now. - changes.save_all() - self.save_all_changed_extensions() - self.activate_config_changes() - self.set_theme_type() - def get_color(self): """Handle button to select a new color for the target tag. @@ -978,6 +971,53 @@ def save_new_theme(self, theme_name, theme): value = theme[element] idleConf.userCfg['highlight'].SetOption(theme_name, element, value) + def delete_custom_theme(self): + """Handle event to delete custom theme. + + The current theme is deactivated and the default theme is + activated. The custom theme is permanently removed from + the config file. + + Attributes accessed: + custom_theme + + Attributes updated: + radio_theme_custom + opt_menu_theme_custom + is_builtin_theme + builtin_theme + + Methods: + deactivate_current_config + save_all_changed_extensions + activate_config_changes + set_theme_type + """ + theme_name = self.custom_theme.get() + delmsg = 'Are you sure you wish to delete the theme %r ?' + if not tkMessageBox.askyesno( + 'Delete Theme', delmsg % theme_name, parent=self): + return + self.deactivate_current_config() + # Remove theme from changes, config, and file. + changes.delete_section('highlight', theme_name) + # Reload user theme list. + item_list = idleConf.GetSectionList('user', 'highlight') + item_list.sort() + if not item_list: + self.radio_theme_custom['state'] = DISABLED + self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -') + else: + self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) + # Revert to default theme. + self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) + self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) + # User can't back out of these changes, they must be applied now. + changes.save_all() + self.save_all_changed_extensions() + self.activate_config_changes() + self.set_theme_type() + def create_page_keys(self): """Return frame of widgets for Keys tab. @@ -1130,251 +1170,44 @@ def load_key_cfg(self): self.load_keys_list(keyset_name) - def create_page_general(self): - """Return frame of widgets for General tab. - Enable users to provisionally change general options. Function - load_general_cfg intializes 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 win_width and win_height. Setting var_name invokes the - var_changed_var_name callback that adds option to changes. - Helplist: load_general_cfg loads list user_helplist with - 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']. + def var_changed_builtin_keys(self, *params): + "Process selection of builtin key set." + old_keys = ( + 'IDLE Classic Windows', + 'IDLE Classic Unix', + 'IDLE Classic Mac', + 'IDLE Classic OSX', + ) + value = self.builtin_keys.get() + if value not in old_keys: + if idleConf.GetOption('main', 'Keys', 'name') not in old_keys: + changes.add_option('main', 'Keys', 'name', old_keys[0]) + changes.add_option('main', 'Keys', 'name2', value) + self.new_custom_keys.config(text='New key set, see Help', + fg='#500000') + else: + changes.add_option('main', 'Keys', 'name', value) + changes.add_option('main', 'Keys', 'name2', '') + self.new_custom_keys.config(text='', fg='black') + self.load_keys_list(value) - Widget Structure: (*) widgets bound to self - frame - frame_run: LabelFrame - startup_title: Label - (*)startup_editor_on: Radiobutton - startup_edit - (*)startup_shell_on: Radiobutton - startup_edit - frame_save: LabelFrame - run_save_title: Label - (*)save_ask_on: Radiobutton - autosave - (*)save_auto_on: Radiobutton - autosave - frame_win_size: LabelFrame - win_size_title: Label - win_width_title: Label - (*)win_width_int: Entry - win_width - win_height_title: Label - (*)win_height_int: Entry - win_height - frame_help: LabelFrame - frame_helplist: Frame - frame_helplist_buttons: Frame - (*)button_helplist_edit - (*)button_helplist_add - (*)button_helplist_remove - (*)helplist: ListBox - scroll_helplist: Scrollbar - """ - parent = self.parent - self.startup_edit = IntVar(parent) - self.autosave = IntVar(parent) - self.win_width = StringVar(parent) - self.win_height = StringVar(parent) - - # Create widgets: - # body. - frame = self.tab_pages.pages['General'].frame - # body section frames. - frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Startup Preferences ') - frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' autosave Preferences ') - frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE) - frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Additional Help Sources ') - # frame_run. - startup_title = Label(frame_run, text='At Startup') - self.startup_editor_on = Radiobutton( - frame_run, variable=self.startup_edit, value=1, - text="Open Edit Window") - self.startup_shell_on = Radiobutton( - frame_run, variable=self.startup_edit, value=0, - text='Open Shell Window') - # frame_save. - 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_win_size. - win_size_title = Label( - frame_win_size, text='Initial Window Size (in characters)') - win_width_title = Label(frame_win_size, text='Width') - self.win_width_int = Entry( - frame_win_size, textvariable=self.win_width, width=3) - win_height_title = Label(frame_win_size, text='Height') - self.win_height_int = Entry( - frame_win_size, textvariable=self.win_height, width=3) - # frame_help. - frame_helplist = Frame(frame_help) - frame_helplist_buttons = Frame(frame_helplist) - self.helplist = Listbox( - frame_helplist, height=5, takefocus=FALSE, - exportselection=FALSE) - scroll_helplist = Scrollbar(frame_helplist) - scroll_helplist['command'] = self.helplist.yview - self.helplist['yscrollcommand'] = scroll_helplist.set - self.helplist.bind('', self.help_source_selected) - self.button_helplist_edit = Button( - frame_helplist_buttons, text='Edit', state=DISABLED, - width=8, command=self.helplist_item_edit) - self.button_helplist_add = Button( - frame_helplist_buttons, text='Add', - width=8, command=self.helplist_item_add) - self.button_helplist_remove = Button( - frame_helplist_buttons, text='Remove', state=DISABLED, - width=8, command=self.helplist_item_remove) - - # Pack widgets: - # body. - frame_run.pack(side=TOP, padx=5, pady=5, fill=X) - frame_save.pack(side=TOP, padx=5, pady=5, fill=X) - frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X) - frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - # frame_run. - startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) - self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - # frame_save. - 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_win_size. - win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5) - self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) - 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_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) - scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y) - self.helplist.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) - self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5) - self.button_helplist_add.pack(side=TOP, anchor=W) - self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5) - - return frame - - def load_general_cfg(self): - "Load current configuration settings for the general options." - # Set startup state. - self.startup_edit.set(idleConf.GetOption( - 'main', 'General', 'editor-on-startup', default=0, type='bool')) - # Set autosave state. - self.autosave.set(idleConf.GetOption( - 'main', 'General', 'autosave', default=0, type='bool')) - # Set initial window size. - self.win_width.set(idleConf.GetOption( - 'main', 'EditorWindow', 'width', type='int')) - self.win_height.set(idleConf.GetOption( - 'main', 'EditorWindow', 'height', type='int')) - # Set additional help sources. - self.user_helplist = idleConf.GetAllExtraHelpSourcesList() - self.helplist.delete(0, 'end') - for help_item in self.user_helplist: - self.helplist.insert(END, help_item[0]) - self.set_add_delete_state() - - def var_changed_startup_edit(self, *params): - "Store change to toggle for starting IDLE in the editor or shell." - value = self.startup_edit.get() - changes.add_option('main', 'General', 'editor-on-startup', value) - - def var_changed_autosave(self, *params): - "Store change to autosave." - value = self.autosave.get() - changes.add_option('main', 'General', 'autosave', value) - - def var_changed_win_width(self, *params): - "Store change to window width." - value = self.win_width.get() - changes.add_option('main', 'EditorWindow', 'width', value) - - def var_changed_win_height(self, *params): - "Store change to window height." - value = self.win_height.get() - changes.add_option('main', 'EditorWindow', 'height', value) - - def help_source_selected(self, event): - "Handle event for selecting additional help." - self.set_add_delete_state() - - def set_add_delete_state(self): - "Toggle the state for the help list buttons based on list entries." - if self.helplist.size() < 1: # No entries in list. - self.button_helplist_edit['state'] = DISABLED - self.button_helplist_remove['state'] = DISABLED - else: # Some entries. - if self.helplist.curselection(): # There currently is a selection. - self.button_helplist_edit['state'] = NORMAL - self.button_helplist_remove['state'] = NORMAL - else: # There currently is not a selection. - self.button_helplist_edit['state'] = DISABLED - self.button_helplist_remove['state'] = DISABLED - - def helplist_item_add(self): - """Handle add button for the help list. - - Query for name and location of new help sources and add - them to the list. - """ - help_source = HelpSource(self, 'New Help Source').result - if help_source: - self.user_helplist.append(help_source) - self.helplist.insert(END, help_source[0]) - self.update_help_changes() - - def helplist_item_edit(self): - """Handle edit button for the help list. - - Query with existing help source information and update - config if the values are changed. - """ - item_index = self.helplist.index(ANCHOR) - help_source = self.user_helplist[item_index] - new_help_source = HelpSource( - self, 'Edit Help Source', - menuitem=help_source[0], - filepath=help_source[1], - ).result - if new_help_source and new_help_source != help_source: - self.user_helplist[item_index] = new_help_source - self.helplist.delete(item_index) - self.helplist.insert(item_index, new_help_source[0]) - self.update_help_changes() - self.set_add_delete_state() # Selected will be un-selected - - def helplist_item_remove(self): - """Handle remove button for the help list. - - Delete the help list item from config. - """ - item_index = self.helplist.index(ANCHOR) - del(self.user_helplist[item_index]) - self.helplist.delete(item_index) - self.update_help_changes() - self.set_add_delete_state() - - def update_help_changes(self): - "Clear and rebuild the HelpFiles section in changes" - changes['main']['HelpFiles'] = {} - for num in range(1, len(self.user_helplist) + 1): - changes.add_option( - 'main', 'HelpFiles', str(num), - ';'.join(self.user_helplist[num-1][:2])) + def var_changed_custom_keys(self, *params): + "Process selection of custom key set." + value = self.custom_keys.get() + if value != '- no custom keys -': + changes.add_option('main', 'Keys', 'name', value) + self.load_keys_list(value) + def var_changed_are_keys_builtin(self, *params): + "Process toggle between builtin key set and custom key set." + value = self.are_keys_builtin.get() + changes.add_option('main', 'Keys', 'default', value) + if value: + self.var_changed_builtin_keys() + else: + self.var_changed_custom_keys() def var_changed_keybinding(self, *params): "Store change to a keybinding." @@ -1388,71 +1221,6 @@ def var_changed_keybinding(self, *params): ext_keybind_section = ext_name + '_cfgBindings' changes.add_option('extensions', ext_keybind_section, event, value) - def var_changed_builtin_keys(self, *params): - "Process selection of builtin key set." - old_keys = ( - 'IDLE Classic Windows', - 'IDLE Classic Unix', - 'IDLE Classic Mac', - 'IDLE Classic OSX', - ) - value = self.builtin_keys.get() - if value not in old_keys: - if idleConf.GetOption('main', 'Keys', 'name') not in old_keys: - changes.add_option('main', 'Keys', 'name', old_keys[0]) - changes.add_option('main', 'Keys', 'name2', value) - self.new_custom_keys.config(text='New key set, see Help', - fg='#500000') - else: - changes.add_option('main', 'Keys', 'name', value) - changes.add_option('main', 'Keys', 'name2', '') - self.new_custom_keys.config(text='', fg='black') - self.load_keys_list(value) - - def var_changed_custom_keys(self, *params): - "Process selection of custom key set." - value = self.custom_keys.get() - if value != '- no custom keys -': - changes.add_option('main', 'Keys', 'name', value) - self.load_keys_list(value) - - def var_changed_are_keys_builtin(self, *params): - "Process toggle between builtin key set and custom key set." - value = self.are_keys_builtin.get() - changes.add_option('main', 'Keys', 'default', value) - if value: - self.var_changed_builtin_keys() - else: - self.var_changed_custom_keys() - - def set_theme_type(self): - """Set available screen options based on builtin or custom theme. - - Attributes accessed: - is_builtin_theme - - Attributes updated: - opt_menu_theme_builtin - opt_menu_theme_custom - button_delete_custom_theme - radio_theme_custom - - Called from: - handler for radio_theme_builtin and radio_theme_custom - delete_custom_theme - create_new_theme - load_theme_cfg - """ - if self.is_builtin_theme.get(): - self.opt_menu_theme_builtin['state'] = NORMAL - self.opt_menu_theme_custom['state'] = DISABLED - self.button_delete_custom_theme['state'] = DISABLED - else: - self.opt_menu_theme_builtin['state'] = DISABLED - self.radio_theme_custom['state'] = NORMAL - self.opt_menu_theme_custom['state'] = NORMAL - self.button_delete_custom_theme['state'] = NORMAL - def set_keys_type(self): "Set available screen options based on builtin or custom key set." if self.are_keys_builtin.get(): @@ -1584,8 +1352,20 @@ def load_keys_list(self, keyset_name): self.list_bindings.select_set(list_index) self.list_bindings.select_anchor(list_index) - def delete_custom_keys(self): - """Handle event to delete a custom key set. + def save_new_key_set(self, keyset_name, keyset): + """Save a newly created core key set. + + keyset_name - string, the name of the new key set + keyset - dictionary containing the new key set + """ + if not idleConf.userCfg['keys'].has_section(keyset_name): + idleConf.userCfg['keys'].add_section(keyset_name) + for event in keyset: + value = keyset[event] + idleConf.userCfg['keys'].SetOption(keyset_name, event, value) + + def delete_custom_keys(self): + """Handle event to delete a custom key set. Applying the delete deactivates the current configuration and reverts to the default. The custom key set is permanently @@ -1618,30 +1398,6 @@ def delete_custom_keys(self): self.activate_config_changes() self.set_keys_type() - def save_new_key_set(self, keyset_name, keyset): - """Save a newly created core key set. - - keyset_name - string, the name of the new key set - keyset - dictionary containing the new key set - """ - if not idleConf.userCfg['keys'].has_section(keyset_name): - idleConf.userCfg['keys'].add_section(keyset_name) - for event in keyset: - value = keyset[event] - idleConf.userCfg['keys'].SetOption(keyset_name, event, value) - - def save_new_theme(self, theme_name, theme): - """Save a newly created theme to idleConf. - - theme_name - string, the name of the new theme - theme - dictionary containing the new theme - """ - if not idleConf.userCfg['highlight'].has_section(theme_name): - idleConf.userCfg['highlight'].add_section(theme_name) - for element in theme: - value = theme[element] - idleConf.userCfg['highlight'].SetOption(theme_name, element, value) - def deactivate_current_config(self): """Remove current key bindings. @@ -1668,49 +1424,252 @@ def activate_config_changes(self): instance.ApplyKeybindings() instance.reset_help_menu_entries() - def cancel(self): - """Dismiss config dialog. - Methods: - destroy: inherited - """ - self.destroy() + def create_page_general(self): + """Return frame of widgets for General tab. - def ok(self): - """Apply config changes, then dismiss dialog. + Enable users to provisionally change general options. Function + load_general_cfg intializes 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 win_width and win_height. Setting var_name invokes the + var_changed_var_name callback that adds option to changes. - Methods: - apply - destroy: inherited + Helplist: load_general_cfg loads list user_helplist with + 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']. + + Widget Structure: (*) widgets bound to self + frame + frame_run: LabelFrame + startup_title: Label + (*)startup_editor_on: Radiobutton - startup_edit + (*)startup_shell_on: Radiobutton - startup_edit + frame_save: LabelFrame + run_save_title: Label + (*)save_ask_on: Radiobutton - autosave + (*)save_auto_on: Radiobutton - autosave + frame_win_size: LabelFrame + win_size_title: Label + win_width_title: Label + (*)win_width_int: Entry - win_width + win_height_title: Label + (*)win_height_int: Entry - win_height + frame_help: LabelFrame + frame_helplist: Frame + frame_helplist_buttons: Frame + (*)button_helplist_edit + (*)button_helplist_add + (*)button_helplist_remove + (*)helplist: ListBox + scroll_helplist: Scrollbar """ - self.apply() - self.destroy() + parent = self.parent + self.startup_edit = IntVar(parent) + self.autosave = IntVar(parent) + self.win_width = StringVar(parent) + self.win_height = StringVar(parent) - def apply(self): - """Apply config changes and leave dialog open. + # Create widgets: + # body. + frame = self.tab_pages.pages['General'].frame + # body section frames. + frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Startup Preferences ') + frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' autosave Preferences ') + frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE) + frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Additional Help Sources ') + # frame_run. + startup_title = Label(frame_run, text='At Startup') + self.startup_editor_on = Radiobutton( + frame_run, variable=self.startup_edit, value=1, + text="Open Edit Window") + self.startup_shell_on = Radiobutton( + frame_run, variable=self.startup_edit, value=0, + text='Open Shell Window') + # frame_save. + 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_win_size. + win_size_title = Label( + frame_win_size, text='Initial Window Size (in characters)') + win_width_title = Label(frame_win_size, text='Width') + self.win_width_int = Entry( + frame_win_size, textvariable=self.win_width, width=3) + win_height_title = Label(frame_win_size, text='Height') + self.win_height_int = Entry( + frame_win_size, textvariable=self.win_height, width=3) + # frame_help. + frame_helplist = Frame(frame_help) + frame_helplist_buttons = Frame(frame_helplist) + self.helplist = Listbox( + frame_helplist, height=5, takefocus=FALSE, + exportselection=FALSE) + scroll_helplist = Scrollbar(frame_helplist) + scroll_helplist['command'] = self.helplist.yview + self.helplist['yscrollcommand'] = scroll_helplist.set + self.helplist.bind('', self.help_source_selected) + self.button_helplist_edit = Button( + frame_helplist_buttons, text='Edit', state=DISABLED, + width=8, command=self.helplist_item_edit) + self.button_helplist_add = Button( + frame_helplist_buttons, text='Add', + width=8, command=self.helplist_item_add) + self.button_helplist_remove = Button( + frame_helplist_buttons, text='Remove', state=DISABLED, + width=8, command=self.helplist_item_remove) - Methods: - deactivate_current_config - save_all_changed_extensions - activate_config_changes + # Pack widgets: + # body. + frame_run.pack(side=TOP, padx=5, pady=5, fill=X) + frame_save.pack(side=TOP, padx=5, pady=5, fill=X) + frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X) + frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + # frame_run. + startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + # frame_save. + 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_win_size. + win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) + 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_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) + scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y) + self.helplist.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) + self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5) + self.button_helplist_add.pack(side=TOP, anchor=W) + self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5) + + return frame + + def load_general_cfg(self): + "Load current configuration settings for the general options." + # Set startup state. + self.startup_edit.set(idleConf.GetOption( + 'main', 'General', 'editor-on-startup', default=0, type='bool')) + # Set autosave state. + self.autosave.set(idleConf.GetOption( + 'main', 'General', 'autosave', default=0, type='bool')) + # Set initial window size. + self.win_width.set(idleConf.GetOption( + 'main', 'EditorWindow', 'width', type='int')) + self.win_height.set(idleConf.GetOption( + 'main', 'EditorWindow', 'height', type='int')) + # Set additional help sources. + self.user_helplist = idleConf.GetAllExtraHelpSourcesList() + self.helplist.delete(0, 'end') + for help_item in self.user_helplist: + self.helplist.insert(END, help_item[0]) + self.set_add_delete_state() + + def var_changed_startup_edit(self, *params): + "Store change to toggle for starting IDLE in the editor or shell." + value = self.startup_edit.get() + changes.add_option('main', 'General', 'editor-on-startup', value) + + def var_changed_autosave(self, *params): + "Store change to autosave." + value = self.autosave.get() + changes.add_option('main', 'General', 'autosave', value) + + def var_changed_win_width(self, *params): + "Store change to window width." + value = self.win_width.get() + changes.add_option('main', 'EditorWindow', 'width', value) + + def var_changed_win_height(self, *params): + "Store change to window height." + value = self.win_height.get() + changes.add_option('main', 'EditorWindow', 'height', value) + + def help_source_selected(self, event): + "Handle event for selecting additional help." + self.set_add_delete_state() + + def set_add_delete_state(self): + "Toggle the state for the help list buttons based on list entries." + if self.helplist.size() < 1: # No entries in list. + self.button_helplist_edit['state'] = DISABLED + self.button_helplist_remove['state'] = DISABLED + else: # Some entries. + if self.helplist.curselection(): # There currently is a selection. + self.button_helplist_edit['state'] = NORMAL + self.button_helplist_remove['state'] = NORMAL + else: # There currently is not a selection. + self.button_helplist_edit['state'] = DISABLED + self.button_helplist_remove['state'] = DISABLED + + def helplist_item_add(self): + """Handle add button for the help list. + + Query for name and location of new help sources and add + them to the list. """ - self.deactivate_current_config() - changes.save_all() - self.save_all_changed_extensions() - self.activate_config_changes() + help_source = HelpSource(self, 'New Help Source').result + if help_source: + self.user_helplist.append(help_source) + self.helplist.insert(END, help_source[0]) + self.update_help_changes() - def help(self): - """Create textview for config dialog help. + def helplist_item_edit(self): + """Handle edit button for the help list. - Attrbutes accessed: - tab_pages + Query with existing help source information and update + config if the values are changed. + """ + item_index = self.helplist.index(ANCHOR) + help_source = self.user_helplist[item_index] + new_help_source = HelpSource( + self, 'Edit Help Source', + menuitem=help_source[0], + filepath=help_source[1], + ).result + if new_help_source and new_help_source != help_source: + self.user_helplist[item_index] = new_help_source + self.helplist.delete(item_index) + self.helplist.insert(item_index, new_help_source[0]) + self.update_help_changes() + self.set_add_delete_state() # Selected will be un-selected - Methods: - view_text: Method from textview module. + def helplist_item_remove(self): + """Handle remove button for the help list. + + Delete the help list item from config. """ - page = self.tab_pages._current_page - view_text(self, title='Help for IDLE preferences', - text=help_common+help_pages.get(page, '')) + item_index = self.helplist.index(ANCHOR) + del(self.user_helplist[item_index]) + self.helplist.delete(item_index) + self.update_help_changes() + self.set_add_delete_state() + + def update_help_changes(self): + "Clear and rebuild the HelpFiles section in changes" + changes['main']['HelpFiles'] = {} + for num in range(1, len(self.user_helplist) + 1): + changes.add_option( + 'main', 'HelpFiles', str(num), + ';'.join(self.user_helplist[num-1][:2])) + def create_page_extensions(self): """Part of the config dialog used for configuring IDLE extensions.