note: Notebook
highpage: self.create_page_highlight
fontpage: FontPage
- keyspage: self.create_page_keys
+ keyspage: KeysPage
genpage: GenPage
- extpageL self.create_page_extensions
+ extpage: self.create_page_extensions
Methods:
create_action_buttons
self.note = note = Notebook(self, width=450, height=450)
self.highpage = self.create_page_highlight()
self.fontpage = FontPage(note, self.highpage)
- self.keyspage = self.create_page_keys()
+ self.keyspage = KeysPage(note)
self.genpage = GenPage(note)
self.extpage = self.create_page_extensions()
note.add(self.fontpage, text='Fonts/Tabs')
#self.load_font_cfg()
#self.load_tab_cfg()
self.load_theme_cfg()
- self.load_key_cfg()
+ # self.load_key_cfg()
# self.load_general_cfg()
# note: extension page handled separately
self.activate_config_changes()
self.set_theme_type()
+ def deactivate_current_config(self):
+ """Remove current key bindings.
- def create_page_keys(self):
- """Return frame of widgets for Keys tab.
+ Iterate over window instances defined in parent and remove
+ 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()
- Enable users to provisionally change both individual and sets of
- keybindings (shortcut keys). Except for features implemented as
- extensions, keybindings are stored in complete sets called
- keysets. Built-in keysets in idlelib/config-keys.def are fixed
- as far as the dialog is concerned. Any keyset can be used as the
- base for a new custom keyset, stored in .idlerc/config-keys.cfg.
+ def activate_config_changes(self):
+ """Apply configuration changes to current windows.
- Function load_key_cfg() initializes tk variables and keyset
- lists and calls load_keys_list for the current keyset.
- Radiobuttons builtin_keyset_on and custom_keyset_on toggle var
- keyset_source, which controls if the current set of keybindings
- are from a builtin or custom keyset. DynOptionMenus builtinlist
- and customlist contain lists of the builtin and custom keysets,
- respectively, and the current item from each list is stored in
- vars builtin_name and custom_name.
+ Dynamically update the current parent window instances
+ with some of the configuration changes.
+ """
+ win_instances = self.parent.instance_dict.keys()
+ for instance in win_instances:
+ instance.ResetColorizer()
+ instance.ResetFont()
+ instance.set_notabs_indentwidth()
+ instance.ApplyKeybindings()
+ instance.reset_help_menu_entries()
- Button delete_custom_keys invokes delete_custom_keys() to delete
- a custom keyset from idleConf.userCfg['keys'] and changes. Button
- save_custom_keys invokes save_as_new_key_set() which calls
- get_new_keys_name() and create_new_key_set() to save a custom keyset
- and its keybindings to idleConf.userCfg['keys'].
+ def create_page_extensions(self):
+ """Part of the config dialog used for configuring IDLE extensions.
- Listbox bindingslist contains all of the keybindings for the
- selected keyset. The keybindings are loaded in load_keys_list()
- and are pairs of (event, [keys]) where keys can be a list
- of one or more key combinations to bind to the same event.
- Mouse button 1 click invokes on_bindingslist_select(), which
- allows button_new_keys to be clicked.
+ This code is generic - it works for any and all IDLE extensions.
- So, an item is selected in listbindings, which activates
- button_new_keys, and clicking button_new_keys calls function
- get_new_keys(). Function get_new_keys() gets the key mappings from the
- current keyset for the binding event item that was selected. The
- function then displays another dialog, GetKeysDialog, with the
- selected binding event and current keys and always new key sequences
- to be entered for that binding event. If the keys aren't
- changed, nothing happens. If the keys are changed and the keyset
- is a builtin, function get_new_keys_name() will be called
- for input of a custom keyset name. If no name is given, then the
- change to the keybinding will abort and no updates will be made. If
- a custom name is entered in the prompt or if the current keyset was
- already custom (and thus didn't require a prompt), then
- idleConf.userCfg['keys'] is updated in function create_new_key_set()
- with the change to the event binding. The item listing in bindingslist
- is updated with the new keys. Var keybinding is also set which invokes
- the callback function, var_changed_keybinding, to add the change to
- the 'keys' or 'extensions' changes tracker based on the binding type.
+ IDLE extensions save their configuration options using idleConf.
+ This code reads the current configuration using idleConf, supplies a
+ GUI interface to change the configuration values, and saves the
+ changes using idleConf.
- Tk Variables:
- keybinding: Action/key bindings.
+ Not all changes take effect immediately - some may require restarting IDLE.
+ This depends on each extension's implementation.
- Methods:
- load_keys_list: Reload active set.
- create_new_key_set: Combine active keyset and changes.
- set_keys_type: Command for keyset_source.
- save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
- deactivate_current_config: Remove keys bindings in editors.
+ All values are treated as text, and it is up to the user to supply
+ reasonable values. The only exception to this are the 'enable*' options,
+ which are boolean, and can be toggled with a True/False button.
- Widgets for keys page frame: (*) widgets bound to self
- frame_key_sets: LabelFrame
- frames[0]: Frame
- (*)builtin_keyset_on: Radiobutton - var keyset_source
- (*)custom_keyset_on: Radiobutton - var keyset_source
- (*)builtinlist: DynOptionMenu - var builtin_name,
- func keybinding_selected
- (*)customlist: DynOptionMenu - var custom_name,
- func keybinding_selected
- (*)keys_message: Label
- frames[1]: Frame
- (*)button_delete_custom_keys: Button - delete_custom_keys
- (*)button_save_custom_keys: Button - save_as_new_key_set
- frame_custom: LabelFrame
- frame_target: Frame
- target_title: Label
- scroll_target_y: Scrollbar
- scroll_target_x: Scrollbar
- (*)bindingslist: ListBox - on_bindingslist_select
- (*)button_new_keys: Button - get_new_keys & ..._name
+ Methods:
+ load_extensions:
+ extension_selected: Handle selection from list.
+ create_extension_frame: Hold widgets for one extension.
+ set_extension_value: Set in userCfg['extensions'].
+ save_all_changed_extensions: Call extension page Save().
"""
parent = self.parent
- self.builtin_name = tracers.add(
- StringVar(parent), self.var_changed_builtin_name)
- self.custom_name = tracers.add(
- StringVar(parent), self.var_changed_custom_name)
- self.keyset_source = tracers.add(
- BooleanVar(parent), self.var_changed_keyset_source)
- self.keybinding = tracers.add(
- StringVar(parent), self.var_changed_keybinding)
-
- # Widget creation:
- # body and section frames.
frame = Frame(self.note)
- 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.bindingslist = Listbox(
- frame_target, takefocus=FALSE, exportselection=FALSE)
- self.bindingslist.bind('<ButtonRelease-1>',
- self.on_bindingslist_select)
- scroll_target_y['command'] = self.bindingslist.yview
- scroll_target_x['command'] = self.bindingslist.xview
- self.bindingslist['yscrollcommand'] = scroll_target_y.set
- self.bindingslist['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.builtin_keyset_on = Radiobutton(
- frames[0], variable=self.keyset_source, value=1,
- command=self.set_keys_type, text='Use a Built-in Key Set')
- self.custom_keyset_on = Radiobutton(
- frames[0], variable=self.keyset_source, value=0,
- command=self.set_keys_type, text='Use a Custom Key Set')
- self.builtinlist = DynOptionMenu(
- frames[0], self.builtin_name, None, command=None)
- self.customlist = DynOptionMenu(
- frames[0], self.custom_name, None, command=None)
- self.button_delete_custom_keys = Button(
- frames[1], text='Delete Custom Key Set',
- command=self.delete_custom_keys)
- self.button_save_custom_keys = Button(
- frames[1], text='Save as New Custom Key Set',
- command=self.save_as_new_key_set)
- self.keys_message = Label(frames[0], bd=2)
+ self.ext_defaultCfg = idleConf.defaultCfg['extensions']
+ self.ext_userCfg = idleConf.userCfg['extensions']
+ self.is_int = self.register(is_int)
+ self.load_extensions()
+ # Create widgets - a listbox shows all available extensions, with the
+ # controls for the extension selected in the listbox to the right.
+ self.extension_names = StringVar(self)
+ frame.rowconfigure(0, weight=1)
+ frame.columnconfigure(2, weight=1)
+ self.extension_list = Listbox(frame, listvariable=self.extension_names,
+ selectmode='browse')
+ self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
+ scroll = Scrollbar(frame, command=self.extension_list.yview)
+ self.extension_list.yscrollcommand=scroll.set
+ self.details_frame = LabelFrame(frame, width=250, height=250)
+ self.extension_list.grid(column=0, row=0, sticky='nws')
+ scroll.grid(column=1, row=0, sticky='ns')
+ self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
+ frame.configure(padx=10, pady=10)
+ self.config_frame = {}
+ self.current_extension = None
+
+ self.outerframe = self # TEMPORARY
+ self.tabbed_page_set = self.extension_list # TEMPORARY
+
+ # Create the frame holding controls for each extension.
+ ext_names = ''
+ for ext_name in sorted(self.extensions):
+ self.create_extension_frame(ext_name)
+ ext_names = ext_names + '{' + ext_name + '} '
+ self.extension_names.set(ext_names)
+ self.extension_list.selection_set(0)
+ self.extension_selected(None)
- ##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.bindingslist.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.builtin_keyset_on.grid(row=0, column=0, sticky=W+NS)
- self.custom_keyset_on.grid(row=1, column=0, sticky=W+NS)
- self.builtinlist.grid(row=0, column=1, sticky=NSEW)
- self.customlist.grid(row=1, column=1, sticky=NSEW)
- self.keys_message.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)
- self.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.keyset_source.set(idleConf.GetOption(
- 'main', 'Keys', 'default', type='bool', default=1))
- # Set current keys.
- current_option = idleConf.CurrentKeys()
- # Load available keyset option menus.
- if self.keyset_source.get(): # Default theme selected.
- item_list = idleConf.GetSectionList('default', 'keys')
- item_list.sort()
- self.builtinlist.SetMenu(item_list, current_option)
- item_list = idleConf.GetSectionList('user', 'keys')
- item_list.sort()
- if not item_list:
- self.custom_keyset_on['state'] = DISABLED
- self.custom_name.set('- no custom keys -')
- else:
- self.customlist.SetMenu(item_list, item_list[0])
- else: # User key set selected.
- item_list = idleConf.GetSectionList('user', 'keys')
- item_list.sort()
- self.customlist.SetMenu(item_list, current_option)
- item_list = idleConf.GetSectionList('default', 'keys')
- item_list.sort()
- self.builtinlist.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_extensions(self):
+ "Fill self.extensions with data from the default and user configs."
+ self.extensions = {}
+ for ext_name in idleConf.GetExtensions(active_only=False):
+ self.extensions[ext_name] = []
- def var_changed_builtin_name(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_name.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.keys_message['text'] = 'New key set, see Help'
- self.keys_message['fg'] = '#500000'
- else:
- changes.add_option('main', 'Keys', 'name', value)
- changes.add_option('main', 'Keys', 'name2', '')
- self.keys_message['text'] = ''
- self.keys_message['fg'] = 'black'
- self.load_keys_list(value)
+ for ext_name in self.extensions:
+ opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
- def var_changed_custom_name(self, *params):
- "Process selection of custom key set."
- value = self.custom_name.get()
- if value != '- no custom keys -':
- changes.add_option('main', 'Keys', 'name', value)
- self.load_keys_list(value)
+ # Bring 'enable' options to the beginning of the list.
+ enables = [opt_name for opt_name in opt_list
+ if opt_name.startswith('enable')]
+ for opt_name in enables:
+ opt_list.remove(opt_name)
+ opt_list = enables + opt_list
- def var_changed_keyset_source(self, *params):
- "Process toggle between builtin key set and custom key set."
- value = self.keyset_source.get()
- changes.add_option('main', 'Keys', 'default', value)
- if value:
- self.var_changed_builtin_name()
- else:
- self.var_changed_custom_name()
+ for opt_name in opt_list:
+ def_str = self.ext_defaultCfg.Get(
+ ext_name, opt_name, raw=True)
+ try:
+ def_obj = {'True':True, 'False':False}[def_str]
+ opt_type = 'bool'
+ except KeyError:
+ try:
+ def_obj = int(def_str)
+ opt_type = 'int'
+ except ValueError:
+ def_obj = def_str
+ opt_type = None
+ try:
+ value = self.ext_userCfg.Get(
+ ext_name, opt_name, type=opt_type, raw=True,
+ default=def_obj)
+ except ValueError: # Need this until .Get fixed.
+ value = def_obj # Bad values overwritten by entry.
+ var = StringVar(self)
+ var.set(str(value))
- def var_changed_keybinding(self, *params):
- "Store change to a keybinding."
- value = self.keybinding.get()
- key_set = self.custom_name.get()
- event = self.bindingslist.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)
+ self.extensions[ext_name].append({'name': opt_name,
+ 'type': opt_type,
+ 'default': def_str,
+ 'value': value,
+ 'var': var,
+ })
- def set_keys_type(self):
- "Set available screen options based on builtin or custom key set."
- if self.keyset_source.get():
- self.builtinlist['state'] = NORMAL
- self.customlist['state'] = DISABLED
- self.button_delete_custom_keys['state'] = DISABLED
- else:
- self.builtinlist['state'] = DISABLED
- self.custom_keyset_on['state'] = NORMAL
- self.customlist['state'] = NORMAL
- self.button_delete_custom_keys['state'] = NORMAL
+ def extension_selected(self, event):
+ "Handle selection of an extension from the list."
+ newsel = self.extension_list.curselection()
+ if newsel:
+ newsel = self.extension_list.get(newsel)
+ if newsel is None or newsel != self.current_extension:
+ if self.current_extension:
+ self.details_frame.config(text='')
+ self.config_frame[self.current_extension].grid_forget()
+ self.current_extension = None
+ if newsel:
+ self.details_frame.config(text=newsel)
+ self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
+ self.current_extension = newsel
- def get_new_keys(self):
- """Handle event to change key binding for selected line.
+ def create_extension_frame(self, ext_name):
+ """Create a frame holding the widgets to configure one extension"""
+ f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
+ self.config_frame[ext_name] = f
+ entry_area = f.interior
+ # Create an entry for each configuration option.
+ for row, opt in enumerate(self.extensions[ext_name]):
+ # Create a row with a label and entry/checkbutton.
+ label = Label(entry_area, text=opt['name'])
+ label.grid(row=row, column=0, sticky=NW)
+ var = opt['var']
+ if opt['type'] == 'bool':
+ Checkbutton(entry_area, textvariable=var, variable=var,
+ onvalue='True', offvalue='False',
+ indicatoron=FALSE, selectcolor='', width=8
+ ).grid(row=row, column=1, sticky=W, padx=7)
+ elif opt['type'] == 'int':
+ Entry(entry_area, textvariable=var, validate='key',
+ validatecommand=(self.is_int, '%P')
+ ).grid(row=row, column=1, sticky=NSEW, padx=7)
- 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.
- """
- list_index = self.bindingslist.index(ANCHOR)
- binding = self.bindingslist.get(list_index)
- bind_name = binding.split()[0]
- if self.keyset_source.get():
- current_key_set_name = self.builtin_name.get()
- else:
- current_key_set_name = self.custom_name.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.keyset_source.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.bindingslist.select_set(list_index)
- self.bindingslist.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.bindingslist.delete(list_index)
- self.bindingslist.insert(list_index, bind_name+' - '+new_keys)
- self.bindingslist.select_set(list_index)
- self.bindingslist.select_anchor(list_index)
- self.keybinding.set(new_keys)
- else:
- self.bindingslist.select_set(list_index)
- self.bindingslist.select_anchor(list_index)
+ else:
+ Entry(entry_area, textvariable=var
+ ).grid(row=row, column=1, sticky=NSEW, padx=7)
+ return
- 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_extension_value(self, section, opt):
+ """Return True if the configuration was added or changed.
- 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)
+ If the value is the same as the default, then remove it
+ from user config file.
+ """
+ name = opt['name']
+ default = opt['default']
+ value = opt['var'].get().strip() or default
+ opt['var'].set(value)
+ # if self.defaultCfg.has_section(section):
+ # Currently, always true; if not, indent to return.
+ if (value == default):
+ return self.ext_userCfg.RemoveOption(section, name)
+ # Set the option.
+ return self.ext_userCfg.SetOption(section, name, value)
- def on_bindingslist_select(self, event):
- "Activate button to assign new keys to selected action."
- self.button_new_keys['state'] = NORMAL
+ def save_all_changed_extensions(self):
+ """Save configuration changes to the user config file.
- def create_new_key_set(self, new_key_set_name):
- """Create a new custom key set with the given name.
+ Attributes accessed:
+ extensions
- Copy the bindings/keys from the previously active keyset
- to the new keyset and activate the new custom keyset.
+ Methods:
+ set_extension_value
"""
- if self.keyset_source.get():
- prev_key_set_name = self.builtin_name.get()
- else:
- prev_key_set_name = self.custom_name.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.customlist.SetMenu(custom_key_list, new_key_set_name)
- self.keyset_source.set(0)
- self.set_keys_type()
+ has_changes = False
+ for ext_name in self.extensions:
+ options = self.extensions[ext_name]
+ for opt in options:
+ if self.set_extension_value(ext_name, opt):
+ has_changes = True
+ if has_changes:
+ self.ext_userCfg.Save()
- def load_keys_list(self, keyset_name):
- """Reload the list of action/key binding pairs for the active key set.
- An action/key binding can be selected to change the key binding.
- """
- reselect = False
- if self.bindingslist.curselection():
- reselect = True
- list_index = self.bindingslist.index(ANCHOR)
- keyset = idleConf.GetKeySet(keyset_name)
- bind_names = list(keyset.keys())
- bind_names.sort()
- self.bindingslist.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.bindingslist.insert(END, bind_name+' - '+key)
- if reselect:
- self.bindingslist.see(list_index)
- self.bindingslist.select_set(list_index)
- self.bindingslist.select_anchor(list_index)
-
- def save_new_key_set(self, keyset_name, keyset):
- """Save a newly created core key set.
-
- Add keyset to idleConf.userCfg['keys'], not to disk.
- If the keyset doesn't exist, it is created. The
- binding/keys are taken from the keyset argument.
-
- keyset_name - string, the name of the new key set
- keyset - dictionary containing the new keybindings
- """
- 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)
+# class TabPage(Frame): # A template for Page classes.
+# def __init__(self, master):
+# super().__init__(master)
+# self.create_page_tab()
+# self.load_tab_cfg()
+# def create_page_tab(self):
+# # Define tk vars and register var and callback with tracers.
+# # Create subframes and widgets.
+# # Pack widgets.
+# def load_tab_cfg(self):
+# # Initialize widgets with data from idleConf.
+# def var_changed_var_name():
+# # For each tk var that needs other than default callback.
+# def other_methods():
+# # Define tab-specific behavior.
- 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_name.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.custom_keyset_on['state'] = DISABLED
- self.customlist.SetMenu(item_list, '- no custom keys -')
- else:
- self.customlist.SetMenu(item_list, item_list[0])
- # Revert to default key set.
- self.keyset_source.set(idleConf.defaultCfg['main']
- .Get('Keys', 'default'))
- self.builtin_name.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()
+class FontPage(Frame):
- def deactivate_current_config(self):
- """Remove current key bindings.
+ def __init__(self, master, highpage):
+ super().__init__(master)
+ self.highlight_sample = highpage.highlight_sample
+ self.create_page_font_tab()
+ self.load_font_cfg()
+ self.load_tab_cfg()
- Iterate over window instances defined in parent and remove
- 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()
+ def create_page_font_tab(self):
+ """Return frame of widgets for Font/Tabs tab.
- def activate_config_changes(self):
- """Apply configuration changes to current windows.
+ Fonts: Enable users to provisionally change font face, size, or
+ boldness and to see the consequence of proposed choices. Each
+ action set 3 options in changes structuree and changes the
+ corresponding aspect of the font sample on this page and
+ highlight sample on highlight page.
- Dynamically update the current parent window instances
- with some of the configuration changes.
- """
- win_instances = self.parent.instance_dict.keys()
- for instance in win_instances:
- instance.ResetColorizer()
- instance.ResetFont()
- instance.set_notabs_indentwidth()
- instance.ApplyKeybindings()
- instance.reset_help_menu_entries()
+ Function load_font_cfg initializes font vars and widgets from
+ idleConf entries and tk.
- def create_page_extensions(self):
- """Part of the config dialog used for configuring IDLE extensions.
+ Fontlist: mouse button 1 click or up or down key invoke
+ on_fontlist_select(), which sets var font_name.
- This code is generic - it works for any and all IDLE extensions.
+ Sizelist: clicking the menubutton opens the dropdown menu. A
+ mouse button 1 click or return key sets var font_size.
- IDLE extensions save their configuration options using idleConf.
- This code reads the current configuration using idleConf, supplies a
- GUI interface to change the configuration values, and saves the
- changes using idleConf.
+ Bold_toggle: clicking the box toggles var font_bold.
- Not all changes take effect immediately - some may require restarting IDLE.
- This depends on each extension's implementation.
+ Changing any of the font vars invokes var_changed_font, which
+ adds all 3 font options to changes and calls set_samples.
+ Set_samples applies a new font constructed from the font vars to
+ font_sample and to highlight_sample on the hightlight page.
- All values are treated as text, and it is up to the user to supply
- reasonable values. The only exception to this are the 'enable*' options,
- which are boolean, and can be toggled with a True/False button.
+ Tabs: Enable users to change spaces entered for indent tabs.
+ Changing indent_scale value with the mouse sets Var space_num,
+ which invokes the default callback to add an entry to
+ changes. Load_tab_cfg initializes space_num to default.
- Methods:
- load_extensions:
- extension_selected: Handle selection from list.
- create_extension_frame: Hold widgets for one extension.
- set_extension_value: Set in userCfg['extensions'].
- save_all_changed_extensions: Call extension page Save().
+ Widgets for FontPage(Frame): (*) widgets bound to self
+ frame_font: LabelFrame
+ frame_font_name: Frame
+ font_name_title: Label
+ (*)fontlist: ListBox - font_name
+ scroll_font: Scrollbar
+ frame_font_param: Frame
+ font_size_title: Label
+ (*)sizelist: DynOptionMenu - font_size
+ (*)bold_toggle: Checkbutton - font_bold
+ frame_font_sample: Frame
+ (*)font_sample: Label
+ frame_indent: LabelFrame
+ indent_title: Label
+ (*)indent_scale: Scale - space_num
"""
- parent = self.parent
- frame = Frame(self.note)
- self.ext_defaultCfg = idleConf.defaultCfg['extensions']
- self.ext_userCfg = idleConf.userCfg['extensions']
- self.is_int = self.register(is_int)
- self.load_extensions()
- # Create widgets - a listbox shows all available extensions, with the
- # controls for the extension selected in the listbox to the right.
- self.extension_names = StringVar(self)
- frame.rowconfigure(0, weight=1)
- frame.columnconfigure(2, weight=1)
- self.extension_list = Listbox(frame, listvariable=self.extension_names,
- selectmode='browse')
- self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
- scroll = Scrollbar(frame, command=self.extension_list.yview)
- self.extension_list.yscrollcommand=scroll.set
- self.details_frame = LabelFrame(frame, width=250, height=250)
- self.extension_list.grid(column=0, row=0, sticky='nws')
- scroll.grid(column=1, row=0, sticky='ns')
- self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
- frame.configure(padx=10, pady=10)
- self.config_frame = {}
- self.current_extension = None
+ self.font_name = tracers.add(StringVar(self), self.var_changed_font)
+ self.font_size = tracers.add(StringVar(self), self.var_changed_font)
+ self.font_bold = tracers.add(BooleanVar(self), self.var_changed_font)
+ self.space_num = tracers.add(IntVar(self), ('main', 'Indent', 'num-spaces'))
- self.outerframe = self # TEMPORARY
- self.tabbed_page_set = self.extension_list # TEMPORARY
+ # Create widgets:
+ # body and body section frames.
+ frame_font = LabelFrame(
+ self, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
+ frame_indent = LabelFrame(
+ self, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
+ # frame_font.
+ frame_font_name = Frame(frame_font)
+ frame_font_param = Frame(frame_font)
+ font_name_title = Label(
+ frame_font_name, justify=LEFT, text='Font Face :')
+ self.fontlist = Listbox(frame_font_name, height=5,
+ takefocus=True, exportselection=FALSE)
+ self.fontlist.bind('<ButtonRelease-1>', self.on_fontlist_select)
+ self.fontlist.bind('<KeyRelease-Up>', self.on_fontlist_select)
+ self.fontlist.bind('<KeyRelease-Down>', self.on_fontlist_select)
+ scroll_font = Scrollbar(frame_font_name)
+ scroll_font.config(command=self.fontlist.yview)
+ self.fontlist.config(yscrollcommand=scroll_font.set)
+ font_size_title = Label(frame_font_param, text='Size :')
+ self.sizelist = DynOptionMenu(frame_font_param, self.font_size, None)
+ self.bold_toggle = Checkbutton(
+ frame_font_param, variable=self.font_bold,
+ onvalue=1, offvalue=0, text='Bold')
+ frame_font_sample = Frame(frame_font, relief=SOLID, borderwidth=1)
+ temp_font = tkFont.Font(self, ('courier', 10, 'normal'))
+ self.font_sample = Label(
+ frame_font_sample, justify=LEFT, font=temp_font,
+ text='AaBbCcDdEe\nFfGgHhIiJj\n1234567890\n#:+=(){}[]')
+ # frame_indent.
+ indent_title = Label(
+ frame_indent, justify=LEFT,
+ text='Python Standard: 4 Spaces!')
+ self.indent_scale = Scale(
+ frame_indent, variable=self.space_num,
+ orient='horizontal', tickinterval=2, from_=2, to=16)
- # Create the frame holding controls for each extension.
- ext_names = ''
- for ext_name in sorted(self.extensions):
- self.create_extension_frame(ext_name)
- ext_names = ext_names + '{' + ext_name + '} '
- self.extension_names.set(ext_names)
- self.extension_list.selection_set(0)
- self.extension_selected(None)
+ # Pack widgets:
+ # body.
+ frame_font.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ frame_indent.pack(side=LEFT, padx=5, pady=5, fill=Y)
+ # frame_font.
+ frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
+ frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
+ font_name_title.pack(side=TOP, anchor=W)
+ self.fontlist.pack(side=LEFT, expand=TRUE, fill=X)
+ scroll_font.pack(side=LEFT, fill=Y)
+ font_size_title.pack(side=LEFT, anchor=W)
+ self.sizelist.pack(side=LEFT, anchor=W)
+ self.bold_toggle.pack(side=LEFT, anchor=W, padx=20)
+ frame_font_sample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ self.font_sample.pack(expand=TRUE, fill=BOTH)
+ # frame_indent.
+ frame_indent.pack(side=TOP, fill=X)
+ indent_title.pack(side=TOP, anchor=W, padx=5)
+ self.indent_scale.pack(side=TOP, padx=5, fill=X)
- return frame
+ def load_font_cfg(self):
+ """Load current configuration settings for the font options.
- def load_extensions(self):
- "Fill self.extensions with data from the default and user configs."
- self.extensions = {}
- for ext_name in idleConf.GetExtensions(active_only=False):
- self.extensions[ext_name] = []
+ Retrieve current font with idleConf.GetFont and font families
+ from tk. Setup fontlist and set font_name. Setup sizelist,
+ which sets font_size. Set font_bold. Call set_samples.
+ """
+ configured_font = idleConf.GetFont(self, 'main', 'EditorWindow')
+ font_name = configured_font[0].lower()
+ font_size = configured_font[1]
+ font_bold = configured_font[2]=='bold'
- for ext_name in self.extensions:
- opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
-
- # Bring 'enable' options to the beginning of the list.
- enables = [opt_name for opt_name in opt_list
- if opt_name.startswith('enable')]
- for opt_name in enables:
- opt_list.remove(opt_name)
- opt_list = enables + opt_list
-
- for opt_name in opt_list:
- def_str = self.ext_defaultCfg.Get(
- ext_name, opt_name, raw=True)
- try:
- def_obj = {'True':True, 'False':False}[def_str]
- opt_type = 'bool'
- except KeyError:
- try:
- def_obj = int(def_str)
- opt_type = 'int'
- except ValueError:
- def_obj = def_str
- opt_type = None
- try:
- value = self.ext_userCfg.Get(
- ext_name, opt_name, type=opt_type, raw=True,
- default=def_obj)
- except ValueError: # Need this until .Get fixed.
- value = def_obj # Bad values overwritten by entry.
- var = StringVar(self)
- var.set(str(value))
+ # Set editor font selection list and font_name.
+ fonts = list(tkFont.families(self))
+ fonts.sort()
+ for font in fonts:
+ self.fontlist.insert(END, font)
+ self.font_name.set(font_name)
+ lc_fonts = [s.lower() for s in fonts]
+ try:
+ current_font_index = lc_fonts.index(font_name)
+ self.fontlist.see(current_font_index)
+ self.fontlist.select_set(current_font_index)
+ self.fontlist.select_anchor(current_font_index)
+ self.fontlist.activate(current_font_index)
+ except ValueError:
+ pass
+ # Set font size dropdown.
+ self.sizelist.SetMenu(('7', '8', '9', '10', '11', '12', '13', '14',
+ '16', '18', '20', '22', '25', '29', '34', '40'),
+ font_size)
+ # Set font weight.
+ self.font_bold.set(font_bold)
+ self.set_samples()
- self.extensions[ext_name].append({'name': opt_name,
- 'type': opt_type,
- 'default': def_str,
- 'value': value,
- 'var': var,
- })
+ def var_changed_font(self, *params):
+ """Store changes to font attributes.
- def extension_selected(self, event):
- "Handle selection of an extension from the list."
- newsel = self.extension_list.curselection()
- if newsel:
- newsel = self.extension_list.get(newsel)
- if newsel is None or newsel != self.current_extension:
- if self.current_extension:
- self.details_frame.config(text='')
- self.config_frame[self.current_extension].grid_forget()
- self.current_extension = None
- if newsel:
- self.details_frame.config(text=newsel)
- self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
- self.current_extension = newsel
+ When one font attribute changes, save them all, as they are
+ not independent from each other. In particular, when we are
+ overriding the default font, we need to write out everything.
+ """
+ value = self.font_name.get()
+ changes.add_option('main', 'EditorWindow', 'font', value)
+ value = self.font_size.get()
+ changes.add_option('main', 'EditorWindow', 'font-size', value)
+ value = self.font_bold.get()
+ changes.add_option('main', 'EditorWindow', 'font-bold', value)
+ self.set_samples()
- def create_extension_frame(self, ext_name):
- """Create a frame holding the widgets to configure one extension"""
- f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
- self.config_frame[ext_name] = f
- entry_area = f.interior
- # Create an entry for each configuration option.
- for row, opt in enumerate(self.extensions[ext_name]):
- # Create a row with a label and entry/checkbutton.
- label = Label(entry_area, text=opt['name'])
- label.grid(row=row, column=0, sticky=NW)
- var = opt['var']
- if opt['type'] == 'bool':
- Checkbutton(entry_area, textvariable=var, variable=var,
- onvalue='True', offvalue='False',
- indicatoron=FALSE, selectcolor='', width=8
- ).grid(row=row, column=1, sticky=W, padx=7)
- elif opt['type'] == 'int':
- Entry(entry_area, textvariable=var, validate='key',
- validatecommand=(self.is_int, '%P')
- ).grid(row=row, column=1, sticky=NSEW, padx=7)
+ def on_fontlist_select(self, event):
+ """Handle selecting a font from the list.
- else:
- Entry(entry_area, textvariable=var
- ).grid(row=row, column=1, sticky=NSEW, padx=7)
- return
+ 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_extension_value(self, section, opt):
- """Return True if the configuration was added or changed.
+ def set_samples(self, event=None):
+ """Update update both screen samples with the font settings.
- If the value is the same as the default, then remove it
- from user config file.
+ Called on font initialization and change events.
+ Accesses font_name, font_size, and font_bold Variables.
+ Updates font_sample and hightlight page highlight_sample.
"""
- name = opt['name']
- default = opt['default']
- value = opt['var'].get().strip() or default
- opt['var'].set(value)
- # if self.defaultCfg.has_section(section):
- # Currently, always true; if not, indent to return.
- if (value == default):
- return self.ext_userCfg.RemoveOption(section, name)
- # Set the option.
- return self.ext_userCfg.SetOption(section, name, value)
-
- def save_all_changed_extensions(self):
- """Save configuration changes to the user config file.
+ font_name = self.font_name.get()
+ font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL
+ new_font = (font_name, self.font_size.get(), font_weight)
+ self.font_sample['font'] = new_font
+ self.highlight_sample['font'] = new_font
- Attributes accessed:
- extensions
+ def load_tab_cfg(self):
+ """Load current configuration settings for the tab options.
- Methods:
- set_extension_value
+ Attributes updated:
+ space_num: Set to value from idleConf.
"""
- has_changes = False
- for ext_name in self.extensions:
- options = self.extensions[ext_name]
- for opt in options:
- if self.set_extension_value(ext_name, opt):
- has_changes = True
- if has_changes:
- self.ext_userCfg.Save()
-
+ # Set indent sizes.
+ space_num = idleConf.GetOption(
+ 'main', 'Indent', 'num-spaces', default=4, type='int')
+ self.space_num.set(space_num)
-# class TabPage(Frame): # A template for Page classes.
-# def __init__(self, master):
-# super().__init__(master)
-# self.create_page_tab()
-# self.load_tab_cfg()
-# def create_page_tab(self):
-# # Define tk vars and register var and callback with tracers.
-# # Create subframes and widgets.
-# # Pack widgets.
-# def load_tab_cfg(self):
-# # Initialize widgets with data from idleConf.
-# def var_changed_var_name():
-# # For each tk var that needs other than default callback.
-# def other_methods():
-# # Define tab-specific behavior.
+ def var_changed_space_num(self, *params):
+ "Store change to indentation size."
+ value = self.space_num.get()
+ changes.add_option('main', 'Indent', 'num-spaces', value)
-class FontPage(Frame):
+class KeysPage(Frame):
- def __init__(self, master, highpage):
+ def __init__(self, master):
super().__init__(master)
- self.highlight_sample = highpage.highlight_sample
- self.create_page_font_tab()
- self.load_font_cfg()
- self.load_tab_cfg()
+ self.cd = master.master
+ self.create_page_keys()
+ self.load_key_cfg()
- def create_page_font_tab(self):
- """Return frame of widgets for Font/Tabs tab.
+ def create_page_keys(self):
+ """Return frame of widgets for Keys tab.
- Fonts: Enable users to provisionally change font face, size, or
- boldness and to see the consequence of proposed choices. Each
- action set 3 options in changes structuree and changes the
- corresponding aspect of the font sample on this page and
- highlight sample on highlight page.
+ Enable users to provisionally change both individual and sets of
+ keybindings (shortcut keys). Except for features implemented as
+ extensions, keybindings are stored in complete sets called
+ keysets. Built-in keysets in idlelib/config-keys.def are fixed
+ as far as the dialog is concerned. Any keyset can be used as the
+ base for a new custom keyset, stored in .idlerc/config-keys.cfg.
- Function load_font_cfg initializes font vars and widgets from
- idleConf entries and tk.
+ Function load_key_cfg() initializes tk variables and keyset
+ lists and calls load_keys_list for the current keyset.
+ Radiobuttons builtin_keyset_on and custom_keyset_on toggle var
+ keyset_source, which controls if the current set of keybindings
+ are from a builtin or custom keyset. DynOptionMenus builtinlist
+ and customlist contain lists of the builtin and custom keysets,
+ respectively, and the current item from each list is stored in
+ vars builtin_name and custom_name.
- Fontlist: mouse button 1 click or up or down key invoke
- on_fontlist_select(), which sets var font_name.
+ Button delete_custom_keys invokes delete_custom_keys() to delete
+ a custom keyset from idleConf.userCfg['keys'] and changes. Button
+ save_custom_keys invokes save_as_new_key_set() which calls
+ get_new_keys_name() and create_new_key_set() to save a custom keyset
+ and its keybindings to idleConf.userCfg['keys'].
- Sizelist: clicking the menubutton opens the dropdown menu. A
- mouse button 1 click or return key sets var font_size.
+ Listbox bindingslist contains all of the keybindings for the
+ selected keyset. The keybindings are loaded in load_keys_list()
+ and are pairs of (event, [keys]) where keys can be a list
+ of one or more key combinations to bind to the same event.
+ Mouse button 1 click invokes on_bindingslist_select(), which
+ allows button_new_keys to be clicked.
- Bold_toggle: clicking the box toggles var font_bold.
+ So, an item is selected in listbindings, which activates
+ button_new_keys, and clicking button_new_keys calls function
+ get_new_keys(). Function get_new_keys() gets the key mappings from the
+ current keyset for the binding event item that was selected. The
+ function then displays another dialog, GetKeysDialog, with the
+ selected binding event and current keys and always new key sequences
+ to be entered for that binding event. If the keys aren't
+ changed, nothing happens. If the keys are changed and the keyset
+ is a builtin, function get_new_keys_name() will be called
+ for input of a custom keyset name. If no name is given, then the
+ change to the keybinding will abort and no updates will be made. If
+ a custom name is entered in the prompt or if the current keyset was
+ already custom (and thus didn't require a prompt), then
+ idleConf.userCfg['keys'] is updated in function create_new_key_set()
+ with the change to the event binding. The item listing in bindingslist
+ is updated with the new keys. Var keybinding is also set which invokes
+ the callback function, var_changed_keybinding, to add the change to
+ the 'keys' or 'extensions' changes tracker based on the binding type.
- Changing any of the font vars invokes var_changed_font, which
- adds all 3 font options to changes and calls set_samples.
- Set_samples applies a new font constructed from the font vars to
- font_sample and to highlight_sample on the hightlight page.
+ Tk Variables:
+ keybinding: Action/key bindings.
- Tabs: Enable users to change spaces entered for indent tabs.
- Changing indent_scale value with the mouse sets Var space_num,
- which invokes the default callback to add an entry to
- changes. Load_tab_cfg initializes space_num to default.
+ Methods:
+ load_keys_list: Reload active set.
+ create_new_key_set: Combine active keyset and changes.
+ set_keys_type: Command for keyset_source.
+ save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
+ deactivate_current_config: Remove keys bindings in editors.
- Widgets for FontPage(Frame): (*) widgets bound to self
- frame_font: LabelFrame
- frame_font_name: Frame
- font_name_title: Label
- (*)fontlist: ListBox - font_name
- scroll_font: Scrollbar
- frame_font_param: Frame
- font_size_title: Label
- (*)sizelist: DynOptionMenu - font_size
- (*)bold_toggle: Checkbutton - font_bold
- frame_font_sample: Frame
- (*)font_sample: Label
- frame_indent: LabelFrame
- indent_title: Label
- (*)indent_scale: Scale - space_num
+ Widgets for KeysPage(frame): (*) widgets bound to self
+ frame_key_sets: LabelFrame
+ frames[0]: Frame
+ (*)builtin_keyset_on: Radiobutton - var keyset_source
+ (*)custom_keyset_on: Radiobutton - var keyset_source
+ (*)builtinlist: DynOptionMenu - var builtin_name,
+ func keybinding_selected
+ (*)customlist: DynOptionMenu - var custom_name,
+ func keybinding_selected
+ (*)keys_message: Label
+ frames[1]: Frame
+ (*)button_delete_custom_keys: Button - delete_custom_keys
+ (*)button_save_custom_keys: Button - save_as_new_key_set
+ frame_custom: LabelFrame
+ frame_target: Frame
+ target_title: Label
+ scroll_target_y: Scrollbar
+ scroll_target_x: Scrollbar
+ (*)bindingslist: ListBox - on_bindingslist_select
+ (*)button_new_keys: Button - get_new_keys & ..._name
"""
- self.font_name = tracers.add(StringVar(self), self.var_changed_font)
- self.font_size = tracers.add(StringVar(self), self.var_changed_font)
- self.font_bold = tracers.add(BooleanVar(self), self.var_changed_font)
- self.space_num = tracers.add(IntVar(self), ('main', 'Indent', 'num-spaces'))
+ self.builtin_name = tracers.add(
+ StringVar(self), self.var_changed_builtin_name)
+ self.custom_name = tracers.add(
+ StringVar(self), self.var_changed_custom_name)
+ self.keyset_source = tracers.add(
+ BooleanVar(self), self.var_changed_keyset_source)
+ self.keybinding = tracers.add(
+ StringVar(self), self.var_changed_keybinding)
# Create widgets:
- # body and body section frames.
- frame_font = LabelFrame(
- self, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
- frame_indent = LabelFrame(
- self, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
- # frame_font.
- frame_font_name = Frame(frame_font)
- frame_font_param = Frame(frame_font)
- font_name_title = Label(
- frame_font_name, justify=LEFT, text='Font Face :')
- self.fontlist = Listbox(frame_font_name, height=5,
- takefocus=True, exportselection=FALSE)
- self.fontlist.bind('<ButtonRelease-1>', self.on_fontlist_select)
- self.fontlist.bind('<KeyRelease-Up>', self.on_fontlist_select)
- self.fontlist.bind('<KeyRelease-Down>', self.on_fontlist_select)
- scroll_font = Scrollbar(frame_font_name)
- scroll_font.config(command=self.fontlist.yview)
- self.fontlist.config(yscrollcommand=scroll_font.set)
- font_size_title = Label(frame_font_param, text='Size :')
- self.sizelist = DynOptionMenu(frame_font_param, self.font_size, None)
- self.bold_toggle = Checkbutton(
- frame_font_param, variable=self.font_bold,
- onvalue=1, offvalue=0, text='Bold')
- frame_font_sample = Frame(frame_font, relief=SOLID, borderwidth=1)
- temp_font = tkFont.Font(self, ('courier', 10, 'normal'))
- self.font_sample = Label(
- frame_font_sample, justify=LEFT, font=temp_font,
- text='AaBbCcDdEe\nFfGgHhIiJj\n1234567890\n#:+=(){}[]')
- # frame_indent.
- indent_title = Label(
- frame_indent, justify=LEFT,
- text='Python Standard: 4 Spaces!')
- self.indent_scale = Scale(
- frame_indent, variable=self.space_num,
- orient='horizontal', tickinterval=2, from_=2, to=16)
+ # body and section frames.
+ frame_custom = LabelFrame(
+ self, borderwidth=2, relief=GROOVE,
+ text=' Custom Key Bindings ')
+ frame_key_sets = LabelFrame(
+ self, 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.bindingslist = Listbox(
+ frame_target, takefocus=FALSE, exportselection=FALSE)
+ self.bindingslist.bind('<ButtonRelease-1>',
+ self.on_bindingslist_select)
+ scroll_target_y['command'] = self.bindingslist.yview
+ scroll_target_x['command'] = self.bindingslist.xview
+ self.bindingslist['yscrollcommand'] = scroll_target_y.set
+ self.bindingslist['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.builtin_keyset_on = Radiobutton(
+ frames[0], variable=self.keyset_source, value=1,
+ command=self.set_keys_type, text='Use a Built-in Key Set')
+ self.custom_keyset_on = Radiobutton(
+ frames[0], variable=self.keyset_source, value=0,
+ command=self.set_keys_type, text='Use a Custom Key Set')
+ self.builtinlist = DynOptionMenu(
+ frames[0], self.builtin_name, None, command=None)
+ self.customlist = DynOptionMenu(
+ frames[0], self.custom_name, None, command=None)
+ self.button_delete_custom_keys = Button(
+ frames[1], text='Delete Custom Key Set',
+ command=self.delete_custom_keys)
+ self.button_save_custom_keys = Button(
+ frames[1], text='Save as New Custom Key Set',
+ command=self.save_as_new_key_set)
+ self.keys_message = Label(frames[0], bd=2)
+
+ # Pack widgets:
+ # 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.bindingslist.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.builtin_keyset_on.grid(row=0, column=0, sticky=W+NS)
+ self.custom_keyset_on.grid(row=1, column=0, sticky=W+NS)
+ self.builtinlist.grid(row=0, column=1, sticky=NSEW)
+ self.customlist.grid(row=1, column=1, sticky=NSEW)
+ self.keys_message.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)
+ self.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)
+
+ def load_key_cfg(self):
+ "Load current configuration settings for the keybinding options."
+ # Set current keys type radiobutton.
+ self.keyset_source.set(idleConf.GetOption(
+ 'main', 'Keys', 'default', type='bool', default=1))
+ # Set current keys.
+ current_option = idleConf.CurrentKeys()
+ # Load available keyset option menus.
+ if self.keyset_source.get(): # Default theme selected.
+ item_list = idleConf.GetSectionList('default', 'keys')
+ item_list.sort()
+ self.builtinlist.SetMenu(item_list, current_option)
+ item_list = idleConf.GetSectionList('user', 'keys')
+ item_list.sort()
+ if not item_list:
+ self.custom_keyset_on['state'] = DISABLED
+ self.custom_name.set('- no custom keys -')
+ else:
+ self.customlist.SetMenu(item_list, item_list[0])
+ else: # User key set selected.
+ item_list = idleConf.GetSectionList('user', 'keys')
+ item_list.sort()
+ self.customlist.SetMenu(item_list, current_option)
+ item_list = idleConf.GetSectionList('default', 'keys')
+ item_list.sort()
+ self.builtinlist.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 var_changed_builtin_name(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_name.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.keys_message['text'] = 'New key set, see Help'
+ self.keys_message['fg'] = '#500000'
+ else:
+ changes.add_option('main', 'Keys', 'name', value)
+ changes.add_option('main', 'Keys', 'name2', '')
+ self.keys_message['text'] = ''
+ self.keys_message['fg'] = 'black'
+ self.load_keys_list(value)
+
+ def var_changed_custom_name(self, *params):
+ "Process selection of custom key set."
+ value = self.custom_name.get()
+ if value != '- no custom keys -':
+ changes.add_option('main', 'Keys', 'name', value)
+ self.load_keys_list(value)
+
+ def var_changed_keyset_source(self, *params):
+ "Process toggle between builtin key set and custom key set."
+ value = self.keyset_source.get()
+ changes.add_option('main', 'Keys', 'default', value)
+ if value:
+ self.var_changed_builtin_name()
+ else:
+ self.var_changed_custom_name()
+
+ def var_changed_keybinding(self, *params):
+ "Store change to a keybinding."
+ value = self.keybinding.get()
+ key_set = self.custom_name.get()
+ event = self.bindingslist.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)
- # Pack widgets:
- # body.
- frame_font.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
- frame_indent.pack(side=LEFT, padx=5, pady=5, fill=Y)
- # frame_font.
- frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
- frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
- font_name_title.pack(side=TOP, anchor=W)
- self.fontlist.pack(side=LEFT, expand=TRUE, fill=X)
- scroll_font.pack(side=LEFT, fill=Y)
- font_size_title.pack(side=LEFT, anchor=W)
- self.sizelist.pack(side=LEFT, anchor=W)
- self.bold_toggle.pack(side=LEFT, anchor=W, padx=20)
- frame_font_sample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
- self.font_sample.pack(expand=TRUE, fill=BOTH)
- # frame_indent.
- frame_indent.pack(side=TOP, fill=X)
- indent_title.pack(side=TOP, anchor=W, padx=5)
- self.indent_scale.pack(side=TOP, padx=5, fill=X)
+ def set_keys_type(self):
+ "Set available screen options based on builtin or custom key set."
+ if self.keyset_source.get():
+ self.builtinlist['state'] = NORMAL
+ self.customlist['state'] = DISABLED
+ self.button_delete_custom_keys['state'] = DISABLED
+ else:
+ self.builtinlist['state'] = DISABLED
+ self.custom_keyset_on['state'] = NORMAL
+ self.customlist['state'] = NORMAL
+ self.button_delete_custom_keys['state'] = NORMAL
- def load_font_cfg(self):
- """Load current configuration settings for the font options.
+ def get_new_keys(self):
+ """Handle event to change key binding for selected line.
- Retrieve current font with idleConf.GetFont and font families
- from tk. Setup fontlist and set font_name. Setup sizelist,
- which sets font_size. Set font_bold. Call set_samples.
+ 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.
"""
- configured_font = idleConf.GetFont(self, 'main', 'EditorWindow')
- font_name = configured_font[0].lower()
- font_size = configured_font[1]
- font_bold = configured_font[2]=='bold'
+ list_index = self.bindingslist.index(ANCHOR)
+ binding = self.bindingslist.get(list_index)
+ bind_name = binding.split()[0]
+ if self.keyset_source.get():
+ current_key_set_name = self.builtin_name.get()
+ else:
+ current_key_set_name = self.custom_name.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.keyset_source.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.bindingslist.select_set(list_index)
+ self.bindingslist.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.bindingslist.delete(list_index)
+ self.bindingslist.insert(list_index, bind_name+' - '+new_keys)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
+ self.keybinding.set(new_keys)
+ else:
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
- # Set editor font selection list and font_name.
- fonts = list(tkFont.families(self))
- fonts.sort()
- for font in fonts:
- self.fontlist.insert(END, font)
- self.font_name.set(font_name)
- lc_fonts = [s.lower() for s in fonts]
- try:
- current_font_index = lc_fonts.index(font_name)
- self.fontlist.see(current_font_index)
- self.fontlist.select_set(current_font_index)
- self.fontlist.select_anchor(current_font_index)
- self.fontlist.activate(current_font_index)
- except ValueError:
- pass
- # Set font size dropdown.
- self.sizelist.SetMenu(('7', '8', '9', '10', '11', '12', '13', '14',
- '16', '18', '20', '22', '25', '29', '34', '40'),
- font_size)
- # Set font weight.
- self.font_bold.set(font_bold)
- self.set_samples()
+ 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 var_changed_font(self, *params):
- """Store changes to font attributes.
+ 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)
- When one font attribute changes, save them all, as they are
- not independent from each other. In particular, when we are
- overriding the default font, we need to write out everything.
- """
- value = self.font_name.get()
- changes.add_option('main', 'EditorWindow', 'font', value)
- value = self.font_size.get()
- changes.add_option('main', 'EditorWindow', 'font-size', value)
- value = self.font_bold.get()
- changes.add_option('main', 'EditorWindow', 'font-bold', value)
- self.set_samples()
+ def on_bindingslist_select(self, event):
+ "Activate button to assign new keys to selected action."
+ self.button_new_keys['state'] = NORMAL
- def on_fontlist_select(self, event):
- """Handle selecting a font from the list.
+ def create_new_key_set(self, new_key_set_name):
+ """Create a new custom key set with the given name.
- Event can result from either mouse click or Up or Down key.
- Set font_name and example displays to selection.
+ Copy the bindings/keys from the previously active keyset
+ to the new keyset and activate the new custom keyset.
"""
- font = self.fontlist.get(
- ACTIVE if event.type.name == 'KeyRelease' else ANCHOR)
- self.font_name.set(font.lower())
+ if self.keyset_source.get():
+ prev_key_set_name = self.builtin_name.get()
+ else:
+ prev_key_set_name = self.custom_name.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.customlist.SetMenu(custom_key_list, new_key_set_name)
+ self.keyset_source.set(0)
+ self.set_keys_type()
- def set_samples(self, event=None):
- """Update update both screen samples with the font settings.
+ def load_keys_list(self, keyset_name):
+ """Reload the list of action/key binding pairs for the active key set.
- Called on font initialization and change events.
- Accesses font_name, font_size, and font_bold Variables.
- Updates font_sample and hightlight page highlight_sample.
+ An action/key binding can be selected to change the key binding.
"""
- font_name = self.font_name.get()
- font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL
- new_font = (font_name, self.font_size.get(), font_weight)
- self.font_sample['font'] = new_font
- self.highlight_sample['font'] = new_font
+ reselect = False
+ if self.bindingslist.curselection():
+ reselect = True
+ list_index = self.bindingslist.index(ANCHOR)
+ keyset = idleConf.GetKeySet(keyset_name)
+ bind_names = list(keyset.keys())
+ bind_names.sort()
+ self.bindingslist.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.bindingslist.insert(END, bind_name+' - '+key)
+ if reselect:
+ self.bindingslist.see(list_index)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
- def load_tab_cfg(self):
- """Load current configuration settings for the tab options.
+ @staticmethod
+ def save_new_key_set(keyset_name, keyset):
+ """Save a newly created core key set.
- Attributes updated:
- space_num: Set to value from idleConf.
+ Add keyset to idleConf.userCfg['keys'], not to disk.
+ If the keyset doesn't exist, it is created. The
+ binding/keys are taken from the keyset argument.
+
+ keyset_name - string, the name of the new key set
+ keyset - dictionary containing the new keybindings
"""
- # Set indent sizes.
- space_num = idleConf.GetOption(
- 'main', 'Indent', 'num-spaces', default=4, type='int')
- self.space_num.set(space_num)
+ 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 var_changed_space_num(self, *params):
- "Store change to indentation size."
- value = self.space_num.get()
- changes.add_option('main', 'Indent', 'num-spaces', 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
+ deleted from the config file.
+ """
+ keyset_name = self.custom_name.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.cd.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.custom_keyset_on['state'] = DISABLED
+ self.customlist.SetMenu(item_list, '- no custom keys -')
+ else:
+ self.customlist.SetMenu(item_list, item_list[0])
+ # Revert to default key set.
+ self.keyset_source.set(idleConf.defaultCfg['main']
+ .Get('Keys', 'default'))
+ self.builtin_name.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.cd.save_all_changed_extensions()
+ self.cd.activate_config_changes()
+ self.set_keys_type()
class GenPage(Frame):