mirror of https://github.com/python/cpython
bpo-40468: Factor out class ExtPage in idlelib.configdialog (GH-26618)
This commit is contained in:
parent
9fd21f649d
commit
5571cabf1b
|
@ -112,11 +112,11 @@ class ConfigDialog(Toplevel):
|
|||
self.frame = frame = Frame(self, padding="5px")
|
||||
self.frame.grid(sticky="nwes")
|
||||
self.note = note = Notebook(frame)
|
||||
self.highpage = HighPage(note)
|
||||
self.extpage = ExtPage(note)
|
||||
self.highpage = HighPage(note, self.extpage)
|
||||
self.fontpage = FontPage(note, self.highpage)
|
||||
self.keyspage = KeysPage(note)
|
||||
self.keyspage = KeysPage(note, self.extpage)
|
||||
self.genpage = GenPage(note)
|
||||
self.extpage = self.create_page_extensions()
|
||||
note.add(self.fontpage, text='Fonts/Tabs')
|
||||
note.add(self.highpage, text='Highlights')
|
||||
note.add(self.keyspage, text=' Keys ')
|
||||
|
@ -244,198 +244,6 @@ class ConfigDialog(Toplevel):
|
|||
for klass in reloadables:
|
||||
klass.reload()
|
||||
|
||||
def create_page_extensions(self):
|
||||
"""Part of the config dialog used for configuring IDLE extensions.
|
||||
|
||||
This code is generic - it works for any and all IDLE extensions.
|
||||
|
||||
IDLE extensions save their configuration options using idleConf.
|
||||
This code reads the current configuration using idleConf, supplies a
|
||||
GUI interface to change the configuration values, and saves the
|
||||
changes using idleConf.
|
||||
|
||||
Not all changes take effect immediately - some may require restarting IDLE.
|
||||
This depends on each extension's implementation.
|
||||
|
||||
All values are treated as text, and it is up to the user to supply
|
||||
reasonable values. The only exception to this are the 'enable*' options,
|
||||
which are boolean, and can be toggled with a True/False button.
|
||||
|
||||
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().
|
||||
"""
|
||||
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 = Frame(self.note)
|
||||
frame_ext = LabelFrame(frame, borderwidth=2, relief=GROOVE,
|
||||
text=' Feature Extensions ')
|
||||
frame_ext.rowconfigure(0, weight=1)
|
||||
frame_ext.columnconfigure(2, weight=1)
|
||||
self.extension_list = Listbox(frame_ext, listvariable=self.extension_names,
|
||||
selectmode='browse')
|
||||
self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
|
||||
scroll = Scrollbar(frame_ext, command=self.extension_list.yview)
|
||||
self.extension_list.yscrollcommand=scroll.set
|
||||
self.details_frame = LabelFrame(frame_ext, 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_ext.configure(padding=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)
|
||||
|
||||
|
||||
self.frame_help = HelpFrame(frame, borderwidth=2, relief=GROOVE,
|
||||
text=' Help Menu Extensions ')
|
||||
frame_ext.grid(row=0, column=0, sticky='nsew')
|
||||
Label(frame).grid(row=1, column=0)
|
||||
self.frame_help.grid(row=2, column=0, sticky='sew')
|
||||
|
||||
return frame
|
||||
|
||||
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):
|
||||
# Former built-in extensions are already filtered out.
|
||||
self.extensions[ext_name] = []
|
||||
|
||||
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))
|
||||
|
||||
self.extensions[ext_name].append({'name': opt_name,
|
||||
'type': opt_type,
|
||||
'default': def_str,
|
||||
'value': value,
|
||||
'var': var,
|
||||
})
|
||||
|
||||
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 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, variable=var,
|
||||
onvalue='True', offvalue='False', 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'), width=10
|
||||
).grid(row=row, column=1, sticky=NSEW, padx=7)
|
||||
|
||||
else: # type == 'str'
|
||||
# Limit size to fit non-expanding space with larger font.
|
||||
Entry(entry_area, textvariable=var, width=15
|
||||
).grid(row=row, column=1, sticky=NSEW, padx=7)
|
||||
return
|
||||
|
||||
def set_extension_value(self, section, opt):
|
||||
"""Return True if the configuration was added or changed.
|
||||
|
||||
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 save_all_changed_extensions(self):
|
||||
"""Save configuration changes to the user config file.
|
||||
|
||||
Attributes accessed:
|
||||
extensions
|
||||
|
||||
Methods:
|
||||
set_extension_value
|
||||
"""
|
||||
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()
|
||||
|
||||
|
||||
# class TabPage(Frame): # A template for Page classes.
|
||||
# def __init__(self, master):
|
||||
|
@ -695,8 +503,9 @@ class FontPage(Frame):
|
|||
|
||||
class HighPage(Frame):
|
||||
|
||||
def __init__(self, master):
|
||||
def __init__(self, master, extpage):
|
||||
super().__init__(master)
|
||||
self.extpage = extpage
|
||||
self.cd = master.winfo_toplevel()
|
||||
self.style = Style(master)
|
||||
self.create_page_highlight()
|
||||
|
@ -1347,15 +1156,16 @@ class HighPage(Frame):
|
|||
self.builtin_name.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
|
||||
# User can't back out of these changes, they must be applied now.
|
||||
changes.save_all()
|
||||
self.cd.save_all_changed_extensions()
|
||||
self.extpage.save_all_changed_extensions()
|
||||
self.cd.activate_config_changes()
|
||||
self.set_theme_type()
|
||||
|
||||
|
||||
class KeysPage(Frame):
|
||||
|
||||
def __init__(self, master):
|
||||
def __init__(self, master, extpage):
|
||||
super().__init__(master)
|
||||
self.extpage = extpage
|
||||
self.cd = master.winfo_toplevel()
|
||||
self.create_page_keys()
|
||||
self.load_key_cfg()
|
||||
|
@ -1779,7 +1589,7 @@ class KeysPage(Frame):
|
|||
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.extpage.save_all_changed_extensions()
|
||||
self.cd.activate_config_changes()
|
||||
self.set_keys_type()
|
||||
|
||||
|
@ -2094,6 +1904,201 @@ class GenPage(Frame):
|
|||
'main', 'PyShell', 'auto-squeeze-min-lines', type='int'))
|
||||
|
||||
|
||||
class ExtPage(Frame):
|
||||
def __init__(self, master):
|
||||
super().__init__(master)
|
||||
self.ext_defaultCfg = idleConf.defaultCfg['extensions']
|
||||
self.ext_userCfg = idleConf.userCfg['extensions']
|
||||
self.is_int = self.register(is_int)
|
||||
self.load_extensions()
|
||||
self.create_page_extensions() # Requires extension names.
|
||||
|
||||
def create_page_extensions(self):
|
||||
"""Configure IDLE feature extensions and help menu extensions.
|
||||
|
||||
List the feature extensions and a configuration box for the
|
||||
selected extension. Help menu extensions are in a HelpFrame.
|
||||
|
||||
This code reads the current configuration using idleConf,
|
||||
supplies a GUI interface to change the configuration values,
|
||||
and saves the changes using idleConf.
|
||||
|
||||
Some changes may require restarting IDLE. This depends on each
|
||||
extension's implementation.
|
||||
|
||||
All values are treated as text, and it is up to the user to
|
||||
supply reasonable values. The only exception to this are the
|
||||
'enable*' options, which are boolean, and can be toggled with a
|
||||
True/False button.
|
||||
|
||||
Methods:
|
||||
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().
|
||||
"""
|
||||
self.extension_names = StringVar(self)
|
||||
|
||||
frame_ext = LabelFrame(self, borderwidth=2, relief=GROOVE,
|
||||
text=' Feature Extensions ')
|
||||
self.frame_help = HelpFrame(self, borderwidth=2, relief=GROOVE,
|
||||
text=' Help Menu Extensions ')
|
||||
|
||||
frame_ext.rowconfigure(0, weight=1)
|
||||
frame_ext.columnconfigure(2, weight=1)
|
||||
self.extension_list = Listbox(frame_ext, listvariable=self.extension_names,
|
||||
selectmode='browse')
|
||||
self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
|
||||
scroll = Scrollbar(frame_ext, command=self.extension_list.yview)
|
||||
self.extension_list.yscrollcommand=scroll.set
|
||||
self.details_frame = LabelFrame(frame_ext, 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_ext.configure(padding=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)
|
||||
|
||||
|
||||
frame_ext.grid(row=0, column=0, sticky='nsew')
|
||||
Label(self).grid(row=1, column=0) # Spacer. Replace with config?
|
||||
self.frame_help.grid(row=2, column=0, sticky='sew')
|
||||
|
||||
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):
|
||||
# Former built-in extensions are already filtered out.
|
||||
self.extensions[ext_name] = []
|
||||
|
||||
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))
|
||||
|
||||
self.extensions[ext_name].append({'name': opt_name,
|
||||
'type': opt_type,
|
||||
'default': def_str,
|
||||
'value': value,
|
||||
'var': var,
|
||||
})
|
||||
|
||||
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 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, variable=var,
|
||||
onvalue='True', offvalue='False', 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'), width=10
|
||||
).grid(row=row, column=1, sticky=NSEW, padx=7)
|
||||
|
||||
else: # type == 'str'
|
||||
# Limit size to fit non-expanding space with larger font.
|
||||
Entry(entry_area, textvariable=var, width=15
|
||||
).grid(row=row, column=1, sticky=NSEW, padx=7)
|
||||
return
|
||||
|
||||
def set_extension_value(self, section, opt):
|
||||
"""Return True if the configuration was added or changed.
|
||||
|
||||
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 save_all_changed_extensions(self):
|
||||
"""Save configuration changes to the user config file.
|
||||
|
||||
Attributes accessed:
|
||||
extensions
|
||||
|
||||
Methods:
|
||||
set_extension_value
|
||||
"""
|
||||
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()
|
||||
|
||||
|
||||
class HelpFrame(LabelFrame):
|
||||
|
||||
def __init__(self, master, **cfg):
|
||||
|
|
|
@ -1292,12 +1292,22 @@ class GenPageTest(unittest.TestCase):
|
|||
self.assertEqual(extpage, {'CodeContext': {'maxlines': '1'}})
|
||||
|
||||
|
||||
#unittest.skip("Nothing here yet TODO")
|
||||
class ExtPageTest(unittest.TestCase):
|
||||
"""Test that the help source list works correctly."""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
page = dialog.extpage
|
||||
dialog.note.select(page)
|
||||
|
||||
|
||||
class HelpSourceTest(unittest.TestCase):
|
||||
"""Test that the help source list works correctly."""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
dialog.note.select(dialog.extpage)
|
||||
frame = cls.frame = dialog.frame_help
|
||||
page = dialog.extpage
|
||||
dialog.note.select(page)
|
||||
frame = cls.frame = page.frame_help
|
||||
frame.set = frame.set_add_delete_state = Func()
|
||||
frame.upc = frame.update_help_changes = Func()
|
||||
frame.update()
|
||||
|
|
Loading…
Reference in New Issue