bpo-31002: IDLE: Add tests for configdialog keys tab (#2996)

Patch by Cheryl Sabella.
This commit is contained in:
Cheryl Sabella 2017-08-14 21:21:43 -04:00 committed by Terry Jan Reedy
parent f978405b3f
commit 2f89646349
3 changed files with 610 additions and 188 deletions

View File

@ -251,25 +251,24 @@ class ConfigDialog(Toplevel):
delete_custom_theme: Ativate default [button_delete_custom_theme].
save_new_theme: Save to userCfg['theme'] (is function).
Widget Structure: (*) widgets bound to self
frame
frame_custom: LabelFrame
(*)highlight_sample: Text
(*)frame_color_set: Frame
button_set_color: Button
(*)opt_menu_highlight_target: DynOptionMenu - highlight_target
frame_fg_bg_toggle: Frame
(*)radio_fg: Radiobutton - fg_bg_toggle
(*)radio_bg: Radiobutton - fg_bg_toggle
button_save_custom_theme: Button
frame_theme: LabelFrame
theme_type_title: Label
(*)radio_theme_builtin: Radiobutton - is_builtin_theme
(*)radio_theme_custom: Radiobutton - is_builtin_theme
(*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
(*)opt_menu_theme_custom: DynOptionMenu - custom_theme
(*)button_delete_custom_theme: Button
(*)new_custom_theme: Label
Widgets of highlights page frame: (*) widgets bound to self
frame_custom: LabelFrame
(*)highlight_sample: Text
(*)frame_color_set: Frame
button_set_color: Button
(*)opt_menu_highlight_target: DynOptionMenu - highlight_target
frame_fg_bg_toggle: Frame
(*)radio_fg: Radiobutton - fg_bg_toggle
(*)radio_bg: Radiobutton - fg_bg_toggle
button_save_custom_theme: Button
frame_theme: LabelFrame
theme_type_title: Label
(*)radio_theme_builtin: Radiobutton - is_builtin_theme
(*)radio_theme_custom: Radiobutton - is_builtin_theme
(*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
(*)opt_menu_theme_custom: DynOptionMenu - custom_theme
(*)button_delete_custom_theme: Button
(*)new_custom_theme: Label
"""
self.theme_elements={
'Normal Text': ('normal', '00'),
@ -796,52 +795,92 @@ class ConfigDialog(Toplevel):
def create_page_keys(self):
"""Return frame of widgets for Keys tab.
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_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.
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'].
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.
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.
Tk Variables:
builtin_keys: Menu variable for built-in keybindings.
custom_keys: Menu variable for custom keybindings.
are_keys_builtin: Selector for built-in or custom keybindings.
keybinding: Action/key bindings.
Methods:
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.
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.
Widget Structure: (*) widgets bound to self
frame
frame_custom: LabelFrame
frame_target: Frame
target_title: Label
scroll_target_y: Scrollbar
scroll_target_x: Scrollbar
(*)list_bindings: ListBox
(*)button_new_keys: Button
frame_key_sets: LabelFrame
frames[0]: Frame
(*)radio_keys_builtin: Radiobutton - are_keys_builtin
(*)radio_keys_custom: Radiobutton - are_keys_builtin
(*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys
(*)opt_menu_keys_custom: DynOptionMenu - custom_keys
(*)new_custom_keys: Label
frames[1]: Frame
(*)button_delete_custom_keys: Button
button_save_custom_keys: Button
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
"""
parent = self.parent
self.builtin_keys = tracers.add(
StringVar(parent), self.var_changed_builtin_keys)
self.custom_keys = tracers.add(
StringVar(parent), self.var_changed_custom_keys)
self.are_keys_builtin = tracers.add(
BooleanVar(parent), self.var_changed_are_keys_builtin)
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)
@ -858,36 +897,37 @@ class ConfigDialog(Toplevel):
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(
self.bindingslist = 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.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.radio_keys_builtin = Radiobutton(
frames[0], variable=self.are_keys_builtin, value=1,
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.radio_keys_custom = Radiobutton(
frames[0], variable=self.are_keys_builtin, value=0,
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.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.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)
button_save_custom_keys = Button(
self.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)
self.keys_message = Label(frames[0], bd=2)
##widget packing
#body
@ -900,17 +940,17 @@ class ConfigDialog(Toplevel):
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)
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.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.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)
button_save_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
@ -918,35 +958,35 @@ class ConfigDialog(Toplevel):
def load_key_cfg(self):
"Load current configuration settings for the keybinding options."
# Set current keys type radiobutton.
self.are_keys_builtin.set(idleConf.GetOption(
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.are_keys_builtin.get(): # Default theme selected.
if self.keyset_source.get(): # Default theme selected.
item_list = idleConf.GetSectionList('default', 'keys')
item_list.sort()
self.opt_menu_keys_builtin.SetMenu(item_list, current_option)
self.builtinlist.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 -')
self.custom_keyset_on['state'] = DISABLED
self.custom_name.set('- no custom keys -')
else:
self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
self.customlist.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)
self.customlist.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.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_keys(self, *params):
def var_changed_builtin_name(self, *params):
"Process selection of builtin key set."
old_keys = (
'IDLE Classic Windows',
@ -954,40 +994,41 @@ class ConfigDialog(Toplevel):
'IDLE Classic Mac',
'IDLE Classic OSX',
)
value = self.builtin_keys.get()
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.new_custom_keys.config(text='New key set, see Help',
fg='#500000')
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.new_custom_keys.config(text='', fg='black')
self.keys_message['text'] = ''
self.keys_message['fg'] = 'black'
self.load_keys_list(value)
def var_changed_custom_keys(self, *params):
def var_changed_custom_name(self, *params):
"Process selection of custom key set."
value = self.custom_keys.get()
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_are_keys_builtin(self, *params):
def var_changed_keyset_source(self, *params):
"Process toggle between builtin key set and custom key set."
value = self.are_keys_builtin.get()
value = self.keyset_source.get()
changes.add_option('main', 'Keys', 'default', value)
if value:
self.var_changed_builtin_keys()
self.var_changed_builtin_name()
else:
self.var_changed_custom_keys()
self.var_changed_custom_name()
def var_changed_keybinding(self, *params):
"Store change to a keybinding."
value = self.keybinding.get()
key_set = self.custom_keys.get()
event = self.list_bindings.get(ANCHOR).split()[0]
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.
@ -997,14 +1038,14 @@ class ConfigDialog(Toplevel):
def set_keys_type(self):
"Set available screen options based on builtin or custom key set."
if self.are_keys_builtin.get():
self.opt_menu_keys_builtin['state'] = NORMAL
self.opt_menu_keys_custom['state'] = DISABLED
if self.keyset_source.get():
self.builtinlist['state'] = NORMAL
self.customlist['state'] = DISABLED
self.button_delete_custom_keys['state'] = DISABLED
else:
self.opt_menu_keys_builtin['state'] = DISABLED
self.radio_keys_custom['state'] = NORMAL
self.opt_menu_keys_custom['state'] = NORMAL
self.builtinlist['state'] = DISABLED
self.custom_keyset_on['state'] = NORMAL
self.customlist['state'] = NORMAL
self.button_delete_custom_keys['state'] = NORMAL
def get_new_keys(self):
@ -1016,13 +1057,13 @@ class ConfigDialog(Toplevel):
changed, then a name for a custom key set needs to be
entered for the change to be applied.
"""
list_index = self.list_bindings.index(ANCHOR)
binding = self.list_bindings.get(list_index)
list_index = self.bindingslist.index(ANCHOR)
binding = self.bindingslist.get(list_index)
bind_name = binding.split()[0]
if self.are_keys_builtin.get():
current_key_set_name = self.builtin_keys.get()
if self.keyset_source.get():
current_key_set_name = self.builtin_name.get()
else:
current_key_set_name = self.custom_keys.get()
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]
@ -1032,24 +1073,24 @@ class ConfigDialog(Toplevel):
new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
current_key_sequences).result
if new_keys:
if self.are_keys_builtin.get(): # Current key set is a built-in.
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.list_bindings.select_set(list_index)
self.list_bindings.select_anchor(list_index)
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.list_bindings.delete(list_index)
self.list_bindings.insert(list_index, bind_name+' - '+new_keys)
self.list_bindings.select_set(list_index)
self.list_bindings.select_anchor(list_index)
self.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.list_bindings.select_set(list_index)
self.list_bindings.select_anchor(list_index)
self.bindingslist.select_set(list_index)
self.bindingslist.select_anchor(list_index)
def get_new_keys_name(self, message):
"Return new key set name from query popup."
@ -1065,21 +1106,20 @@ class ConfigDialog(Toplevel):
if new_keys_name:
self.create_new_key_set(new_keys_name)
def keybinding_selected(self, event):
def on_bindingslist_select(self, event):
"Activate button to assign new keys to selected action."
self.button_new_keys['state'] = NORMAL
def create_new_key_set(self, new_key_set_name):
"""Create a new custom key set with the given name.
Create the new key set based on the previously active set
with the current changes applied. Once it is saved, then
activate the new key set.
Copy the bindings/keys from the previously active keyset
to the new keyset and activate the new custom keyset.
"""
if self.are_keys_builtin.get():
prev_key_set_name = self.builtin_keys.get()
if self.keyset_source.get():
prev_key_set_name = self.builtin_name.get()
else:
prev_key_set_name = self.custom_keys.get()
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.
@ -1096,8 +1136,8 @@ class ConfigDialog(Toplevel):
# Change GUI over to the new key set.
custom_key_list = idleConf.GetSectionList('user', 'keys')
custom_key_list.sort()
self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name)
self.are_keys_builtin.set(0)
self.customlist.SetMenu(custom_key_list, new_key_set_name)
self.keyset_source.set(0)
self.set_keys_type()
def load_keys_list(self, keyset_name):
@ -1105,14 +1145,14 @@ class ConfigDialog(Toplevel):
An action/key binding can be selected to change the key binding.
"""
reselect = 0
if self.list_bindings.curselection():
reselect = 1
list_index = self.list_bindings.index(ANCHOR)
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.list_bindings.delete(0, END)
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.
@ -1120,17 +1160,21 @@ class ConfigDialog(Toplevel):
# Handle any unsaved changes to this key set.
if bind_name in changes['keys'][keyset_name]:
key = changes['keys'][keyset_name][bind_name]
self.list_bindings.insert(END, bind_name+' - '+key)
self.bindingslist.insert(END, bind_name+' - '+key)
if reselect:
self.list_bindings.see(list_index)
self.list_bindings.select_set(list_index)
self.list_bindings.select_anchor(list_index)
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 key set
keyset - dictionary containing the new keybindings
"""
if not idleConf.userCfg['keys'].has_section(keyset_name):
idleConf.userCfg['keys'].add_section(keyset_name)
@ -1145,7 +1189,7 @@ class ConfigDialog(Toplevel):
reverts to the default. The custom key set is permanently
deleted from the config file.
"""
keyset_name=self.custom_keys.get()
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):
@ -1157,14 +1201,14 @@ class ConfigDialog(Toplevel):
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 -')
self.custom_keyset_on['state'] = DISABLED
self.customlist.SetMenu(item_list, '- no custom keys -')
else:
self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
self.customlist.SetMenu(item_list, item_list[0])
# Revert to default key set.
self.are_keys_builtin.set(idleConf.defaultCfg['main']
self.keyset_source.set(idleConf.defaultCfg['main']
.Get('Keys', 'default'))
self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
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()
@ -1438,22 +1482,21 @@ class FontPage(Frame):
which invokes the default callback to add an entry to
changes. Load_tab_cfg initializes space_num to default.
Widget Structure: (*) widgets bound to self
frame (of tab_pages)
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 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
"""
self.font_name = tracers.add(StringVar(self), self.var_changed_font)
self.font_size = tracers.add(StringVar(self), self.var_changed_font)
@ -1633,30 +1676,29 @@ class GenPage(Frame):
set_add_delete_state. All but load call update_help_changes to
rewrite changes['main']['HelpFiles'].
Widget Structure: (*) widgets bound to self
frame
frame_run: LabelFrame
startup_title: Label
(*)startup_editor_on: Radiobutton - startup_edit
(*)startup_shell_on: Radiobutton - startup_edit
frame_save: LabelFrame
run_save_title: Label
(*)save_ask_on: Radiobutton - autosave
(*)save_auto_on: Radiobutton - autosave
frame_win_size: LabelFrame
win_size_title: Label
win_width_title: Label
(*)win_width_int: Entry - win_width
win_height_title: Label
(*)win_height_int: Entry - win_height
frame_help: LabelFrame
frame_helplist: Frame
frame_helplist_buttons: Frame
(*)button_helplist_edit
(*)button_helplist_add
(*)button_helplist_remove
(*)helplist: ListBox
scroll_helplist: Scrollbar
Widgets for GenPage(Frame): (*) widgets bound to self
frame_run: LabelFrame
startup_title: Label
(*)startup_editor_on: Radiobutton - startup_edit
(*)startup_shell_on: Radiobutton - startup_edit
frame_save: LabelFrame
run_save_title: Label
(*)save_ask_on: Radiobutton - autosave
(*)save_auto_on: Radiobutton - autosave
frame_win_size: LabelFrame
win_size_title: Label
win_width_title: Label
(*)win_width_int: Entry - win_width
win_height_title: Label
(*)win_height_int: Entry - win_height
frame_help: LabelFrame
frame_helplist: Frame
frame_helplist_buttons: Frame
(*)button_helplist_edit
(*)button_helplist_add
(*)button_helplist_remove
(*)helplist: ListBox
scroll_helplist: Scrollbar
"""
self.startup_edit = tracers.add(
IntVar(self), ('main', 'General', 'editor-on-startup'))

View File

@ -1,7 +1,7 @@
"""Test idlelib.configdialog.
Half the class creates dialog, half works with user customizations.
Coverage: 63%.
Coverage: 81%.
"""
from idlelib import configdialog
from test.support import requires
@ -29,6 +29,7 @@ dialog = None
mainpage = changes['main']
highpage = changes['highlight']
keyspage = changes['keys']
extpage = changes['extensions']
def setUpModule():
global root, dialog
@ -59,8 +60,6 @@ class FontPageTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
page = cls.page = dialog.fontpage
#dialog.note.insert(0, page, text='copy')
#dialog.note.add(page, text='copyfgfg')
dialog.note.select(page)
page.set_samples = Func() # Mask instance method.
@ -120,7 +119,7 @@ class FontPageTest(unittest.TestCase):
# Click on item should select that item.
d = self.page
if d.fontlist.size() < 2:
cls.skipTest('need at least 2 fonts')
self.skipTest('need at least 2 fonts')
fontlist = d.fontlist
fontlist.activate(0)
@ -233,10 +232,390 @@ class HighlightTest(unittest.TestCase):
changes.clear()
class KeysTest(unittest.TestCase):
class KeyTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
d = dialog
dialog.note.select(d.keyspage)
d.set_keys_type = Func()
d.load_keys_list = Func()
@classmethod
def tearDownClass(cls):
d = dialog
del d.set_keys_type, d.load_keys_list
def setUp(self):
d = dialog
# The following is needed for test_load_key_cfg, _delete_custom_keys.
# This may indicate a defect in some test or function.
for section in idleConf.GetSectionList('user', 'keys'):
idleConf.userCfg['keys'].remove_section(section)
changes.clear()
d.set_keys_type.called = 0
d.load_keys_list.called = 0
def test_load_key_cfg(self):
tracers.detach()
d = dialog
eq = self.assertEqual
# Use builtin keyset with no user keysets created.
idleConf.CurrentKeys = mock.Mock(return_value='IDLE Classic OSX')
d.load_key_cfg()
self.assertTrue(d.keyset_source.get())
# builtinlist sets variable builtin_name to the CurrentKeys default.
eq(d.builtin_name.get(), 'IDLE Classic OSX')
eq(d.custom_name.get(), '- no custom keys -')
eq(d.custom_keyset_on['state'], DISABLED)
eq(d.set_keys_type.called, 1)
eq(d.load_keys_list.called, 1)
eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
# Builtin keyset with non-empty user keyset list.
idleConf.SetOption('keys', 'test1', 'option', 'value')
idleConf.SetOption('keys', 'test2', 'option2', 'value2')
d.load_key_cfg()
eq(d.builtin_name.get(), 'IDLE Classic OSX')
eq(d.custom_name.get(), 'test1')
eq(d.set_keys_type.called, 2)
eq(d.load_keys_list.called, 2)
eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
# Use custom keyset.
idleConf.CurrentKeys = mock.Mock(return_value='test2')
idleConf.default_keys = mock.Mock(return_value='IDLE Modern Unix')
idleConf.SetOption('main', 'Keys', 'default', '0')
d.load_key_cfg()
self.assertFalse(d.keyset_source.get())
eq(d.builtin_name.get(), 'IDLE Modern Unix')
eq(d.custom_name.get(), 'test2')
eq(d.set_keys_type.called, 3)
eq(d.load_keys_list.called, 3)
eq(d.load_keys_list.args, ('test2', ))
del idleConf.CurrentKeys, idleConf.default_keys
tracers.attach()
def test_keyset_source(self):
eq = self.assertEqual
d = dialog
# Test these separately.
d.var_changed_builtin_name = Func()
d.var_changed_custom_name = Func()
# Builtin selected.
d.builtin_keyset_on.invoke()
eq(mainpage, {'Keys': {'default': 'True'}})
eq(d.var_changed_builtin_name.called, 1)
eq(d.var_changed_custom_name.called, 0)
changes.clear()
# Custom selected.
d.custom_keyset_on['state'] = NORMAL
d.custom_keyset_on.invoke()
self.assertEqual(mainpage, {'Keys': {'default': 'False'}})
eq(d.var_changed_builtin_name.called, 1)
eq(d.var_changed_custom_name.called, 1)
del d.var_changed_builtin_name, d.var_changed_custom_name
def test_builtin_name(self):
eq = self.assertEqual
d = dialog
idleConf.userCfg['main'].remove_section('Keys')
item_list = ['IDLE Classic Windows', 'IDLE Classic OSX',
'IDLE Modern UNIX']
# Not in old_keys, defaults name to first item.
d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
eq(mainpage, {'Keys': {'name': 'IDLE Classic Windows',
'name2': 'IDLE Modern UNIX'}})
eq(d.keys_message['text'], 'New key set, see Help')
eq(d.load_keys_list.called, 1)
eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
# Not in old keys - uses name2.
changes.clear()
idleConf.SetOption('main', 'Keys', 'name', 'IDLE Classic Unix')
d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
eq(mainpage, {'Keys': {'name2': 'IDLE Modern UNIX'}})
eq(d.keys_message['text'], 'New key set, see Help')
eq(d.load_keys_list.called, 2)
eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
# Builtin name in old_keys.
changes.clear()
d.builtinlist.SetMenu(item_list, 'IDLE Classic OSX')
eq(mainpage, {'Keys': {'name': 'IDLE Classic OSX', 'name2': ''}})
eq(d.keys_message['text'], '')
eq(d.load_keys_list.called, 3)
eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
def test_custom_name(self):
d = dialog
# If no selections, doesn't get added.
d.customlist.SetMenu([], '- no custom keys -')
self.assertNotIn('Keys', mainpage)
self.assertEqual(d.load_keys_list.called, 0)
# Custom name selected.
changes.clear()
d.customlist.SetMenu(['a', 'b', 'c'], 'c')
self.assertEqual(mainpage, {'Keys': {'name': 'c'}})
self.assertEqual(d.load_keys_list.called, 1)
def test_keybinding(self):
d = dialog
d.custom_name.set('my custom keys')
d.bindingslist.delete(0, 'end')
d.bindingslist.insert(0, 'copy')
d.bindingslist.insert(1, 'expand-word')
d.bindingslist.selection_set(0)
d.bindingslist.selection_anchor(0)
# Core binding - adds to keys.
d.keybinding.set('<Key-F11>')
self.assertEqual(keyspage,
{'my custom keys': {'copy': '<Key-F11>'}})
# Not a core binding - adds to extensions.
d.bindingslist.selection_set(1)
d.bindingslist.selection_anchor(1)
d.keybinding.set('<Key-F11>')
self.assertEqual(extpage,
{'AutoExpand_cfgBindings': {'expand-word': '<Key-F11>'}})
def test_set_keys_type(self):
eq = self.assertEqual
d = dialog
del d.set_keys_type
# Builtin keyset selected.
d.keyset_source.set(True)
d.set_keys_type()
eq(d.builtinlist['state'], NORMAL)
eq(d.customlist['state'], DISABLED)
eq(d.button_delete_custom_keys['state'], DISABLED)
# Custom keyset selected.
d.keyset_source.set(False)
d.set_keys_type()
eq(d.builtinlist['state'], DISABLED)
eq(d.custom_keyset_on['state'], NORMAL)
eq(d.customlist['state'], NORMAL)
eq(d.button_delete_custom_keys['state'], NORMAL)
d.set_keys_type = Func()
def test_get_new_keys(self):
eq = self.assertEqual
d = dialog
orig_getkeysdialog = configdialog.GetKeysDialog
gkd = configdialog.GetKeysDialog = Func(return_self=True)
gnkn = d.get_new_keys_name = Func()
d.button_new_keys['state'] = NORMAL
d.bindingslist.delete(0, 'end')
d.bindingslist.insert(0, 'copy - <Control-Shift-Key-C>')
d.bindingslist.selection_set(0)
d.bindingslist.selection_anchor(0)
d.keybinding.set('Key-a')
d.keyset_source.set(True) # Default keyset.
# Default keyset; no change to binding.
gkd.result = ''
d.button_new_keys.invoke()
eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
# Keybinding isn't changed when there isn't a change entered.
eq(d.keybinding.get(), 'Key-a')
# Default keyset; binding changed.
gkd.result = '<Key-F11>'
# No keyset name selected therefore binding not saved.
gnkn.result = ''
d.button_new_keys.invoke()
eq(gnkn.called, 1)
eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
# Keyset name selected.
gnkn.result = 'My New Key Set'
d.button_new_keys.invoke()
eq(d.custom_name.get(), gnkn.result)
eq(d.bindingslist.get('anchor'), 'copy - <Key-F11>')
eq(d.keybinding.get(), '<Key-F11>')
# User keyset; binding changed.
d.keyset_source.set(False) # Custom keyset.
gnkn.called = 0
gkd.result = '<Key-p>'
d.button_new_keys.invoke()
eq(gnkn.called, 0)
eq(d.bindingslist.get('anchor'), 'copy - <Key-p>')
eq(d.keybinding.get(), '<Key-p>')
del d.get_new_keys_name
configdialog.GetKeysDialog = orig_getkeysdialog
def test_get_new_keys_name(self):
orig_sectionname = configdialog.SectionName
sn = configdialog.SectionName = Func(return_self=True)
d = dialog
sn.result = 'New Keys'
self.assertEqual(d.get_new_keys_name(''), 'New Keys')
configdialog.SectionName = orig_sectionname
def test_save_as_new_key_set(self):
d = dialog
gnkn = d.get_new_keys_name = Func()
d.keyset_source.set(True)
# No name entered.
gnkn.result = ''
d.button_save_custom_keys.invoke()
# Name entered.
gnkn.result = 'my new key set'
gnkn.called = 0
self.assertNotIn(gnkn.result, idleConf.userCfg['keys'])
d.button_save_custom_keys.invoke()
self.assertIn(gnkn.result, idleConf.userCfg['keys'])
del d.get_new_keys_name
def test_on_bindingslist_select(self):
d = dialog
b = d.bindingslist
b.delete(0, 'end')
b.insert(0, 'copy')
b.insert(1, 'find')
b.activate(0)
b.focus_force()
b.see(1)
b.update()
x, y, dx, dy = b.bbox(1)
x += dx // 2
y += dy // 2
b.event_generate('<Enter>', x=0, y=0)
b.event_generate('<Motion>', x=x, y=y)
b.event_generate('<Button-1>', x=x, y=y)
b.event_generate('<ButtonRelease-1>', x=x, y=y)
self.assertEqual(b.get('anchor'), 'find')
self.assertEqual(d.button_new_keys['state'], NORMAL)
def test_create_new_key_set_and_save_new_key_set(self):
eq = self.assertEqual
d = dialog
# Use default as previously active keyset.
d.keyset_source.set(True)
d.builtin_name.set('IDLE Classic Windows')
first_new = 'my new custom key set'
second_new = 'my second custom keyset'
# No changes, so keysets are an exact copy.
self.assertNotIn(first_new, idleConf.userCfg)
d.create_new_key_set(first_new)
eq(idleConf.GetSectionList('user', 'keys'), [first_new])
eq(idleConf.GetKeySet('IDLE Classic Windows'),
idleConf.GetKeySet(first_new))
eq(d.custom_name.get(), first_new)
self.assertFalse(d.keyset_source.get()) # Use custom set.
eq(d.set_keys_type.called, 1)
# Test that changed keybindings are in new keyset.
changes.add_option('keys', first_new, 'copy', '<Key-F11>')
self.assertNotIn(second_new, idleConf.userCfg)
d.create_new_key_set(second_new)
eq(idleConf.GetSectionList('user', 'keys'), [first_new, second_new])
self.assertNotEqual(idleConf.GetKeySet(first_new),
idleConf.GetKeySet(second_new))
# Check that difference in keysets was in option `copy` from `changes`.
idleConf.SetOption('keys', first_new, 'copy', '<Key-F11>')
eq(idleConf.GetKeySet(first_new), idleConf.GetKeySet(second_new))
def test_load_keys_list(self):
eq = self.assertEqual
d = dialog
gks = idleConf.GetKeySet = Func()
del d.load_keys_list
b = d.bindingslist
b.delete(0, 'end')
b.insert(0, '<<find>>')
b.insert(1, '<<help>>')
gks.result = {'<<copy>>': ['<Control-Key-c>', '<Control-Key-C>'],
'<<force-open-completions>>': ['<Control-Key-space>'],
'<<spam>>': ['<Key-F11>']}
changes.add_option('keys', 'my keys', 'spam', '<Shift-Key-a>')
expected = ('copy - <Control-Key-c> <Control-Key-C>',
'force-open-completions - <Control-Key-space>',
'spam - <Shift-Key-a>')
# No current selection.
d.load_keys_list('my keys')
eq(b.get(0, 'end'), expected)
eq(b.get('anchor'), '')
eq(b.curselection(), ())
# Check selection.
b.selection_set(1)
b.selection_anchor(1)
d.load_keys_list('my keys')
eq(b.get(0, 'end'), expected)
eq(b.get('anchor'), 'force-open-completions - <Control-Key-space>')
eq(b.curselection(), (1, ))
# Change selection.
b.selection_set(2)
b.selection_anchor(2)
d.load_keys_list('my keys')
eq(b.get(0, 'end'), expected)
eq(b.get('anchor'), 'spam - <Shift-Key-a>')
eq(b.curselection(), (2, ))
d.load_keys_list = Func()
del idleConf.GetKeySet
def test_delete_custom_keys(self):
eq = self.assertEqual
d = dialog
d.button_delete_custom_keys['state'] = NORMAL
yesno = configdialog.tkMessageBox.askyesno = Func()
d.deactivate_current_config = Func()
d.activate_config_changes = Func()
keyset_name = 'spam key set'
idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value')
keyspage[keyset_name] = {'option': 'True'}
# Force custom keyset.
d.keyset_source.set(False)
d.custom_name.set(keyset_name)
# Cancel deletion.
yesno.result = False
d.button_delete_custom_keys.invoke()
eq(yesno.called, 1)
eq(keyspage[keyset_name], {'option': 'True'})
eq(idleConf.GetSectionList('user', 'keys'), ['spam key set'])
eq(d.deactivate_current_config.called, 0)
eq(d.activate_config_changes.called, 0)
eq(d.set_keys_type.called, 0)
# Confirm deletion.
yesno.result = True
d.button_delete_custom_keys.invoke()
eq(yesno.called, 2)
self.assertNotIn(keyset_name, keyspage)
eq(idleConf.GetSectionList('user', 'keys'), [])
eq(d.custom_keyset_on['state'], DISABLED)
eq(d.custom_name.get(), '- no custom keys -')
eq(d.deactivate_current_config.called, 1)
eq(d.activate_config_changes.called, 1)
eq(d.set_keys_type.called, 1)
del d.activate_config_changes, d.deactivate_current_config
del configdialog.tkMessageBox.askyesno
class GenPageTest(unittest.TestCase):

View File

@ -0,0 +1 @@
Add tests for configdialog keys tab. Patch by Cheryl Sabella.