Commit b1660800 authored by Terry Jan Reedy's avatar Terry Jan Reedy Committed by GitHub

bpo-31060: IDLE: Finish regrouping ConfigDialog methods (#2908)

Finish resorting the 72 ConfigDialog methods into 7 groups that represent the dialog, action buttons, and font, highlight, keys, general, and extension pages.  This will help with continuing to add tests and improve the pages. It will enable splitting ConfigDialog into 6 or 7 more comprehensible classes. 
parent 12953ffe
...@@ -30,7 +30,6 @@ from idlelib.textview import view_text ...@@ -30,7 +30,6 @@ from idlelib.textview import view_text
changes = ConfigChanges() changes = ConfigChanges()
class ConfigDialog(Toplevel): class ConfigDialog(Toplevel):
"""Config dialog for IDLE. """Config dialog for IDLE.
""" """
...@@ -112,6 +111,58 @@ class ConfigDialog(Toplevel): ...@@ -112,6 +111,58 @@ class ConfigDialog(Toplevel):
self.create_page_extensions() self.create_page_extensions()
self.create_action_buttons().pack(side=BOTTOM) 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): def create_action_buttons(self):
"""Return frame of action buttons for dialog. """Return frame of action buttons for dialog.
...@@ -150,6 +201,50 @@ class ConfigDialog(Toplevel): ...@@ -150,6 +201,50 @@ class ConfigDialog(Toplevel):
buttons.pack(side=BOTTOM) buttons.pack(side=BOTTOM)
return outer 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): def create_page_font_tab(self):
"""Return frame of widgets for Font/Tabs tab. """Return frame of widgets for Font/Tabs tab.
...@@ -299,16 +394,6 @@ class ConfigDialog(Toplevel): ...@@ -299,16 +394,6 @@ class ConfigDialog(Toplevel):
# Set font weight. # Set font weight.
self.font_bold.set(font_bold) 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): def var_changed_font(self, *params):
"""Store changes to font attributes. """Store changes to font attributes.
...@@ -324,6 +409,16 @@ class ConfigDialog(Toplevel): ...@@ -324,6 +409,16 @@ class ConfigDialog(Toplevel):
changes.add_option('main', 'EditorWindow', 'font-bold', value) changes.add_option('main', 'EditorWindow', 'font-bold', value)
self.set_samples() 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): def set_samples(self, event=None):
"""Update update both screen samples with the font settings. """Update update both screen samples with the font settings.
...@@ -531,466 +626,551 @@ class ConfigDialog(Toplevel): ...@@ -531,466 +626,551 @@ class ConfigDialog(Toplevel):
self.new_custom_theme.pack(side=TOP, fill=X, pady=5) self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
return frame return frame
def create_page_keys(self): def load_theme_cfg(self):
"""Return frame of widgets for Keys tab. """Load current configuration settings for the theme options.
Tk Variables: Based on the is_builtin_theme toggle, the theme is set as
builtin_keys: Menu variable for built-in keybindings. either builtin or custom and the initial widget values
custom_keys: Menu variable for custom keybindings. reflect the current settings from idleConf.
are_keys_builtin: Selector for built-in or custom keybindings.
keybinding: Action/key bindings.
Methods: Attributes updated:
load_key_config: Set table. is_builtin_theme: Set from idleConf.
load_keys_list: Reload active set. opt_menu_theme_builtin: List of default themes from idleConf.
keybinding_selected: Bound to list_bindings button release. opt_menu_theme_custom: List of custom themes from idleConf.
get_new_keys: Command for button_new_keys. radio_theme_custom: Disabled if there are no custom themes.
get_new_keys_name: Call popup. custom_theme: Message with additional information.
create_new_key_set: Combine active keyset and changes. opt_menu_highlight_target: Create menu from self.theme_elements.
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.
Widget Structure: (*) widgets bound to self Methods:
frame set_theme_type
frame_custom: LabelFrame paint_theme_sample
frame_target: Frame set_highlight_target
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 # Set current theme type radiobutton.
self.builtin_keys = StringVar(parent) self.is_builtin_theme.set(idleConf.GetOption(
self.custom_keys = StringVar(parent) 'main', 'Theme', 'default', type='bool', default=1))
self.are_keys_builtin = BooleanVar(parent) # Set current theme.
self.keybinding = StringVar(parent) 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 def var_changed_builtin_theme(self, *params):
#body frame """Process new builtin theme selection.
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('<ButtonRelease-1>', 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 Add the changed theme's name to the changed_items and recreate
#body the sample with the values from the selected theme.
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) old_themes = ('IDLE Classic', 'IDLE New')
#frame_custom value = self.builtin_theme.get()
self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5) if value not in old_themes:
frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
#frame target changes.add_option('main', 'Theme', 'name', old_themes[0])
frame_target.columnconfigure(0, weight=1) changes.add_option('main', 'Theme', 'name2', value)
frame_target.rowconfigure(1, weight=1) self.new_custom_theme.config(text='New theme, see Help',
target_title.grid(row=0, column=0, columnspan=2, sticky=W) fg='#500000')
self.list_bindings.grid(row=1, column=0, sticky=NSEW) else:
scroll_target_y.grid(row=1, column=1, sticky=NS) changes.add_option('main', 'Theme', 'name', value)
scroll_target_x.grid(row=2, column=0, sticky=EW) changes.add_option('main', 'Theme', 'name2', '')
#frame_key_sets self.new_custom_theme.config(text='', fg='black')
self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS) self.paint_theme_sample()
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_custom_theme(self, *params):
"""Process new custom theme selection.
def create_page_general(self): If a new custom theme is selected, add the name to the
"""Return frame of widgets for General tab. 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()
Enable users to provisionally change general options. Function def var_changed_is_builtin_theme(self, *params):
load_general_cfg intializes tk variables and helplist using """Process toggle between builtin and custom theme.
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 Update the default toggle value and apply the newly
name, position pairs and copies names to listbox helplist. selected theme type.
Clicking a name invokes help_source selected. Clicking """
button_helplist_name invokes helplist_item_name, which also value = self.is_builtin_theme.get()
changes user_helplist. These functions all call changes.add_option('main', 'Theme', 'default', value)
set_add_delete_state. All but load call update_help_changes to if value:
rewrite changes['main']['HelpFiles']. self.var_changed_builtin_theme()
else:
self.var_changed_custom_theme()
Widget Structure: (*) widgets bound to self def var_changed_color(self, *params):
frame "Process change to color choice."
frame_run: LabelFrame self.on_new_color_set()
startup_title: Label
(*)startup_editor_on: Radiobutton - startup_edit def var_changed_highlight_target(self, *params):
(*)startup_shell_on: Radiobutton - startup_edit "Process selection of new target tag for highlighting."
frame_save: LabelFrame self.set_highlight_target()
run_save_title: Label
(*)save_ask_on: Radiobutton - autosave def set_theme_type(self):
(*)save_auto_on: Radiobutton - autosave """Set available screen options based on builtin or custom theme.
frame_win_size: LabelFrame
win_size_title: Label Attributes accessed:
win_width_title: Label is_builtin_theme
(*)win_width_int: Entry - win_width
win_height_title: Label Attributes updated:
(*)win_height_int: Entry - win_height opt_menu_theme_builtin
frame_help: LabelFrame opt_menu_theme_custom
frame_helplist: Frame button_delete_custom_theme
frame_helplist_buttons: Frame radio_theme_custom
(*)button_helplist_edit
(*)button_helplist_add Called from:
(*)button_helplist_remove handler for radio_theme_builtin and radio_theme_custom
(*)helplist: ListBox delete_custom_theme
scroll_helplist: Scrollbar create_new_theme
load_theme_cfg
""" """
parent = self.parent if self.is_builtin_theme.get():
self.startup_edit = IntVar(parent) self.opt_menu_theme_builtin['state'] = NORMAL
self.autosave = IntVar(parent) self.opt_menu_theme_custom['state'] = DISABLED
self.win_width = StringVar(parent) self.button_delete_custom_theme['state'] = DISABLED
self.win_height = StringVar(parent) 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
# Create widgets: def get_color(self):
# body. """Handle button to select a new color for the target tag.
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('<ButtonRelease-1>', 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: If a new color is selected while using a builtin theme, a
# body. name must be supplied to create a custom theme.
frame_run.pack(side=TOP, padx=5, pady=5, fill=X)
frame_save.pack(side=TOP, padx=5, pady=5, fill=X) Attributes accessed:
frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X) highlight_target
frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_color_set
# frame_run. is_builtin_theme
startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) Attributes updated:
self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) color
# frame_save.
run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5) Methods:
self.save_auto_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) get_new_theme_name
self.save_ask_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) create_new_theme
# frame_win_size. """
win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5) target = self.highlight_target.get()
self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) prev_color = self.frame_color_set.cget('bg')
win_height_title.pack(side=RIGHT, anchor=E, pady=5) rgbTuplet, color_string = tkColorChooser.askcolor(
self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) parent=self, title='Pick new color for : '+target,
win_width_title.pack(side=RIGHT, anchor=E, pady=5) initialcolor=prev_color)
# frame_help. if color_string and (color_string != prev_color):
frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y) # User didn't cancel and they chose a new color.
frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) if self.is_builtin_theme.get(): # Current theme is a built-in.
scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y) message = ('Your changes will be saved as a new Custom Theme. '
self.helplist.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) 'Enter a name for your new Custom Theme below.')
self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5) new_theme = self.get_new_theme_name(message)
self.button_helplist_add.pack(side=TOP, anchor=W) if not new_theme: # User cancelled custom theme creation.
self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5) 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 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 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 save_as_new_theme(self):
"""Prompt for new theme name and create the theme.
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.
return frame 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 load_general_cfg(self): Attributes accessed:
"Load current configuration settings for the general options." builtin_theme
# Set startup state. custom_theme
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): Attributes updated:
"Store change to toggle for starting IDLE in the editor or shell." opt_menu_theme_custom
value = self.startup_edit.get() is_builtin_theme
changes.add_option('main', 'General', 'editor-on-startup', value)
def var_changed_autosave(self, *params): Method:
"Store change to autosave." save_new_theme
value = self.autosave.get() set_theme_type
changes.add_option('main', 'General', 'autosave', value) """
if self.is_builtin_theme.get():
theme_type = 'default'
theme_name = self.builtin_theme.get()
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()
def var_changed_win_width(self, *params): def set_highlight_target(self):
"Store change to window width." """Set fg/bg toggle and color based on highlight tag target.
value = self.win_width.get()
changes.add_option('main', 'EditorWindow', 'width', value)
def var_changed_win_height(self, *params): Instance variables accessed:
"Store change to window height." highlight_target
value = self.win_height.get()
changes.add_option('main', 'EditorWindow', 'height', value)
def help_source_selected(self, event): Attributes updated:
"Handle event for selecting additional help." radio_fg
self.set_add_delete_state() radio_bg
fg_bg_toggle
def set_add_delete_state(self): Methods:
"Toggle the state for the help list buttons based on list entries." set_color_sample
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): Called from:
"""Handle add button for the help list. var_changed_highlight_target
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()
Query for name and location of new help sources and add def set_color_sample_binding(self, *args):
them to the list. """Change color sample based on foreground/background toggle.
Methods:
set_color_sample
""" """
help_source = HelpSource(self, 'New Help Source').result self.set_color_sample()
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): def set_color_sample(self):
"""Handle edit button for the help list. """Set the color of the frame background to reflect the selected target.
Query with existing help source information and update Instance variables accessed:
config if the values are changed. theme_elements
highlight_target
fg_bg_toggle
highlight_sample
Attributes updated:
frame_color_set
""" """
item_index = self.helplist.index(ANCHOR) # Set the color sample area.
help_source = self.user_helplist[item_index] tag = self.theme_elements[self.highlight_target.get()][0]
new_help_source = HelpSource( plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
self, 'Edit Help Source', color = self.highlight_sample.tag_cget(tag, plane)
menuitem=help_source[0], self.frame_color_set.config(bg=color)
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): def paint_theme_sample(self):
"""Handle remove button for the help list. """Apply the theme colors to each element tag in the sample text.
Delete the help list item from config. Instance attributes accessed:
theme_elements
is_builtin_theme
builtin_theme
custom_theme
Attributes updated:
highlight_sample: Set the tag elements to the theme.
Methods:
set_color_sample
Called from:
var_changed_builtin_theme
var_changed_custom_theme
load_theme_cfg
""" """
item_index = self.helplist.index(ANCHOR) if self.is_builtin_theme.get(): # Default theme
del(self.user_helplist[item_index]) theme = self.builtin_theme.get()
self.helplist.delete(item_index) else: # User theme
self.update_help_changes() theme = self.custom_theme.get()
self.set_add_delete_state() 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 update_help_changes(self): def save_new_theme(self, theme_name, theme):
"Clear and rebuild the HelpFiles section in changes" """Save a newly created theme to idleConf.
changes['main']['HelpFiles'] = {}
for num in range(1, len(self.user_helplist) + 1): theme_name - string, the name of the new theme
changes.add_option( theme - dictionary containing the new theme
'main', 'HelpFiles', str(num), """
';'.join(self.user_helplist[num-1][:2])) 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 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 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): def create_page_keys(self):
"Remove callbacks to prevent memory leaks." """Return frame of widgets for Keys tab.
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): Tk Variables:
"Process change to color choice." builtin_keys: Menu variable for built-in keybindings.
self.on_new_color_set() custom_keys: Menu variable for custom keybindings.
are_keys_builtin: Selector for built-in or custom keybindings.
keybinding: Action/key bindings.
def var_changed_builtin_theme(self, *params): Methods:
"""Process new builtin theme selection. 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.
Add the changed theme's name to the changed_items and recreate Widget Structure: (*) widgets bound to self
the sample with the values from the selected theme. 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
""" """
old_themes = ('IDLE Classic', 'IDLE New') parent = self.parent
value = self.builtin_theme.get() self.builtin_keys = StringVar(parent)
if value not in old_themes: self.custom_keys = StringVar(parent)
if idleConf.GetOption('main', 'Theme', 'name') not in old_themes: self.are_keys_builtin = BooleanVar(parent)
changes.add_option('main', 'Theme', 'name', old_themes[0]) self.keybinding = StringVar(parent)
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 var_changed_custom_theme(self, *params): ##widget creation
"""Process new custom theme selection. #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('<ButtonRelease-1>', 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)
If a new custom theme is selected, add the name to the ##widget packing
changed_items and apply the theme to the sample. #body
""" frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
value = self.custom_theme.get() frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
if value != '- no custom themes -': #frame_custom
changes.add_option('main', 'Theme', 'name', value) self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
self.paint_theme_sample() 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_is_builtin_theme(self, *params): def load_key_cfg(self):
"""Process toggle between builtin and custom theme. "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)
Update the default toggle value and apply the newly
selected theme type.
"""
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()
def var_changed_highlight_target(self, *params):
"Process selection of new target tag for highlighting."
self.set_highlight_target()
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)
def var_changed_builtin_keys(self, *params): def var_changed_builtin_keys(self, *params):
"Process selection of builtin key set." "Process selection of builtin key set."
...@@ -1016,46 +1196,30 @@ class ConfigDialog(Toplevel): ...@@ -1016,46 +1196,30 @@ class ConfigDialog(Toplevel):
def var_changed_custom_keys(self, *params): def var_changed_custom_keys(self, *params):
"Process selection of custom key set." "Process selection of custom key set."
value = self.custom_keys.get() value = self.custom_keys.get()
if value != '- no custom keys -': if value != '- no custom keys -':
changes.add_option('main', 'Keys', 'name', value) changes.add_option('main', 'Keys', 'name', value)
self.load_keys_list(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: def var_changed_are_keys_builtin(self, *params):
handler for radio_theme_builtin and radio_theme_custom "Process toggle between builtin key set and custom key set."
delete_custom_theme value = self.are_keys_builtin.get()
create_new_theme changes.add_option('main', 'Keys', 'default', value)
load_theme_cfg if value:
""" self.var_changed_builtin_keys()
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: else:
self.opt_menu_theme_builtin['state'] = DISABLED self.var_changed_custom_keys()
self.radio_theme_custom['state'] = NORMAL
self.opt_menu_theme_custom['state'] = NORMAL def var_changed_keybinding(self, *params):
self.button_delete_custom_theme['state'] = NORMAL "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)
def set_keys_type(self): def set_keys_type(self):
"Set available screen options based on builtin or custom key set." "Set available screen options based on builtin or custom key set."
...@@ -1183,489 +1347,329 @@ class ConfigDialog(Toplevel): ...@@ -1183,489 +1347,329 @@ class ConfigDialog(Toplevel):
if bind_name in changes['keys'][keyset_name]: if bind_name in changes['keys'][keyset_name]:
key = changes['keys'][keyset_name][bind_name] key = changes['keys'][keyset_name][bind_name]
self.list_bindings.insert(END, bind_name+' - '+key) self.list_bindings.insert(END, bind_name+' - '+key)
if reselect: if reselect:
self.list_bindings.see(list_index) self.list_bindings.see(list_index)
self.list_bindings.select_set(list_index) self.list_bindings.select_set(list_index)
self.list_bindings.select_anchor(list_index) self.list_bindings.select_anchor(list_index)
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
deleted from the config file.
"""
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.
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.
If a new color is selected while using a builtin theme, a
name must be supplied to create a custom theme.
Attributes accessed:
highlight_target
frame_color_set
is_builtin_theme
Attributes updated:
color
Methods:
get_new_theme_name
create_new_theme
"""
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 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 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 save_as_new_theme(self):
"""Prompt for new theme name and create the theme.
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.
Create the new theme based on the previously active theme
with the current changes applied. Once it is saved, then
activate the new theme.
Attributes accessed:
builtin_theme
custom_theme
Attributes updated:
opt_menu_theme_custom
is_builtin_theme
Method:
save_new_theme
set_theme_type
"""
if self.is_builtin_theme.get():
theme_type = 'default'
theme_name = self.builtin_theme.get()
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()
def set_highlight_target(self):
"""Set fg/bg toggle and color based on highlight tag target.
Instance variables accessed:
highlight_target
Attributes updated:
radio_fg
radio_bg
fg_bg_toggle
Methods:
set_color_sample
Called from:
var_changed_highlight_target
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): def save_new_key_set(self, keyset_name, keyset):
"""Change color sample based on foreground/background toggle. """Save a newly created core key set.
Methods: keyset_name - string, the name of the new key set
set_color_sample keyset - dictionary containing the new key set
""" """
self.set_color_sample() if not idleConf.userCfg['keys'].has_section(keyset_name):
idleConf.userCfg['keys'].add_section(keyset_name)
def set_color_sample(self): for event in keyset:
"""Set the color of the frame background to reflect the selected target. value = keyset[event]
idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
Instance variables accessed: def delete_custom_keys(self):
theme_elements """Handle event to delete a custom key set.
highlight_target
fg_bg_toggle
highlight_sample
Attributes updated: Applying the delete deactivates the current configuration and
frame_color_set reverts to the default. The custom key set is permanently
deleted from the config file.
""" """
# Set the color sample area. keyset_name=self.custom_keys.get()
tag = self.theme_elements[self.highlight_target.get()][0] delmsg = 'Are you sure you wish to delete the key set %r ?'
plane = 'foreground' if self.fg_bg_toggle.get() else 'background' if not tkMessageBox.askyesno(
color = self.highlight_sample.tag_cget(tag, plane) 'Delete Key Set', delmsg % keyset_name, parent=self):
self.frame_color_set.config(bg=color) return
self.deactivate_current_config()
def paint_theme_sample(self): # Remove key set from changes, config, and file.
"""Apply the theme colors to each element tag in the sample text. 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()
Instance attributes accessed: def deactivate_current_config(self):
theme_elements """Remove current key bindings.
is_builtin_theme
builtin_theme
custom_theme
Attributes updated: Iterate over window instances defined in parent and remove
highlight_sample: Set the tag elements to the theme. the keybindings.
"""
# Before a config is saved, some cleanup of current
# config must be done - remove the previous keybindings.
win_instances = self.parent.instance_dict.keys()
for instance in win_instances:
instance.RemoveKeybindings()
Methods: def activate_config_changes(self):
set_color_sample """Apply configuration changes to current windows.
Called from: Dynamically update the current parent window instances
var_changed_builtin_theme with some of the configuration changes.
var_changed_custom_theme
load_theme_cfg
""" """
if self.is_builtin_theme.get(): # Default theme win_instances = self.parent.instance_dict.keys()
theme = self.builtin_theme.get() for instance in win_instances:
else: # User theme instance.ResetColorizer()
theme = self.custom_theme.get() instance.ResetFont()
for element_title in self.theme_elements: instance.set_notabs_indentwidth()
element = self.theme_elements[element_title][0] instance.ApplyKeybindings()
colors = idleConf.GetHighlight(theme, element) instance.reset_help_menu_entries()
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 load_theme_cfg(self):
"""Load current configuration settings for the theme options.
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.
Attributes updated: def create_page_general(self):
is_builtin_theme: Set from idleConf. """Return frame of widgets for General tab.
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.
Methods: Enable users to provisionally change general options. Function
set_theme_type load_general_cfg intializes tk variables and helplist using
paint_theme_sample idleConf. Radiobuttons startup_shell_on and startup_editor_on
set_highlight_target 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 current theme type radiobutton. set var win_width and win_height. Setting var_name invokes the
self.is_builtin_theme.set(idleConf.GetOption( var_changed_var_name callback that adds option to changes.
'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): Helplist: load_general_cfg loads list user_helplist with
"Load current configuration settings for the keybinding options." name, position pairs and copies names to listbox helplist.
# Set current keys type radiobutton. Clicking a name invokes help_source selected. Clicking
self.are_keys_builtin.set(idleConf.GetOption( button_helplist_name invokes helplist_item_name, which also
'main', 'Keys', 'default', type='bool', default=1)) changes user_helplist. These functions all call
# Set current keys. set_add_delete_state. All but load call update_help_changes to
current_option = idleConf.CurrentKeys() rewrite changes['main']['HelpFiles'].
# 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 load_configs(self): Widget Structure: (*) widgets bound to self
"""Load configuration for each page. 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)
Load configuration from default and user config files and populate # Create widgets:
the widgets on the config dialog pages. # 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('<ButtonRelease-1>', 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: # Pack widgets:
load_font_cfg # body.
load_tab_cfg frame_run.pack(side=TOP, padx=5, pady=5, fill=X)
load_theme_cfg frame_save.pack(side=TOP, padx=5, pady=5, fill=X)
load_key_cfg frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X)
load_general_cfg frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
""" # frame_run.
self.load_font_cfg() startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
self.load_tab_cfg() self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
self.load_theme_cfg() self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
self.load_key_cfg() # frame_save.
self.load_general_cfg() run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
# note: extension page handled separately 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)
def save_new_key_set(self, keyset_name, keyset): return frame
"""Save a newly created core key set.
keyset_name - string, the name of the new key set def load_general_cfg(self):
keyset - dictionary containing the new key set "Load current configuration settings for the general options."
""" # Set startup state.
if not idleConf.userCfg['keys'].has_section(keyset_name): self.startup_edit.set(idleConf.GetOption(
idleConf.userCfg['keys'].add_section(keyset_name) 'main', 'General', 'editor-on-startup', default=0, type='bool'))
for event in keyset: # Set autosave state.
value = keyset[event] self.autosave.set(idleConf.GetOption(
idleConf.userCfg['keys'].SetOption(keyset_name, event, value) '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 save_new_theme(self, theme_name, theme): def var_changed_startup_edit(self, *params):
"""Save a newly created theme to idleConf. "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)
theme_name - string, the name of the new theme def var_changed_autosave(self, *params):
theme - dictionary containing the new theme "Store change to autosave."
""" value = self.autosave.get()
if not idleConf.userCfg['highlight'].has_section(theme_name): changes.add_option('main', 'General', 'autosave', value)
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): def var_changed_win_width(self, *params):
"""Remove current key bindings. "Store change to window width."
value = self.win_width.get()
changes.add_option('main', 'EditorWindow', 'width', value)
Iterate over window instances defined in parent and remove def var_changed_win_height(self, *params):
the keybindings. "Store change to window height."
""" value = self.win_height.get()
# Before a config is saved, some cleanup of current changes.add_option('main', 'EditorWindow', 'height', value)
# config must be done - remove the previous keybindings.
win_instances = self.parent.instance_dict.keys()
for instance in win_instances:
instance.RemoveKeybindings()
def activate_config_changes(self): def help_source_selected(self, event):
"""Apply configuration changes to current windows. "Handle event for selecting additional help."
self.set_add_delete_state()
Dynamically update the current parent window instances def set_add_delete_state(self):
with some of the configuration changes. "Toggle the state for the help list buttons based on list entries."
""" if self.helplist.size() < 1: # No entries in list.
win_instances = self.parent.instance_dict.keys() self.button_helplist_edit['state'] = DISABLED
for instance in win_instances: self.button_helplist_remove['state'] = DISABLED
instance.ResetColorizer() else: # Some entries.
instance.ResetFont() if self.helplist.curselection(): # There currently is a selection.
instance.set_notabs_indentwidth() self.button_helplist_edit['state'] = NORMAL
instance.ApplyKeybindings() self.button_helplist_remove['state'] = NORMAL
instance.reset_help_menu_entries() else: # There currently is not a selection.
self.button_helplist_edit['state'] = DISABLED
self.button_helplist_remove['state'] = DISABLED
def cancel(self): def helplist_item_add(self):
"""Dismiss config dialog. """Handle add button for the help list.
Methods: Query for name and location of new help sources and add
destroy: inherited them to the list.
""" """
self.destroy() 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 ok(self): def helplist_item_edit(self):
"""Apply config changes, then dismiss dialog. """Handle edit button for the help list.
Methods: Query with existing help source information and update
apply config if the values are changed.
destroy: inherited
""" """
self.apply() item_index = self.helplist.index(ANCHOR)
self.destroy() 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 apply(self): def helplist_item_remove(self):
"""Apply config changes and leave dialog open. """Handle remove button for the help list.
Methods: Delete the help list item from config.
deactivate_current_config
save_all_changed_extensions
activate_config_changes
""" """
self.deactivate_current_config() item_index = self.helplist.index(ANCHOR)
changes.save_all() del(self.user_helplist[item_index])
self.save_all_changed_extensions() self.helplist.delete(item_index)
self.activate_config_changes() self.update_help_changes()
self.set_add_delete_state()
def help(self):
"""Create textview for config dialog help.
Attrbutes accessed: def update_help_changes(self):
tab_pages "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:
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_extensions(self): def create_page_extensions(self):
"""Part of the config dialog used for configuring IDLE extensions. """Part of the config dialog used for configuring IDLE extensions.
......
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.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment