cpython: 1d708436831a (original) (raw)
--- a/Lib/idlelib/Bindings.py +++ b/Lib/idlelib/Bindings.py @@ -75,7 +75,8 @@ menudefs = [ ('!_Auto-open Stack Viewer', '<>'), ]), ('options', [
--- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -216,6 +216,8 @@ class EditorWindow(object): text.bind("<>", self.python_docs) text.bind("<>", self.about_dialog) text.bind("<>", self.config_dialog)
text.bind("<<open-config-extensions-dialog>>",[](#l2.7)
self.config_extensions_dialog)[](#l2.8) text.bind("<<open-module>>", self.open_module)[](#l2.9) text.bind("<<do-nothing>>", lambda event: "break")[](#l2.10) text.bind("<<select-all>>", self.select_all)[](#l2.11)
@@ -570,6 +572,8 @@ class EditorWindow(object): def config_dialog(self, event=None): configDialog.ConfigDialog(self.top,'Settings')
- def config_extensions_dialog(self, event=None):
configDialog.ConfigExtensionsDialog(self.top)[](#l2.17)
def help_dialog(self, event=None): if self.root:
--- a/Lib/idlelib/configDialog.py +++ b/Lib/idlelib/configDialog.py @@ -18,8 +18,8 @@ from idlelib.tabbedpages import TabbedPa from idlelib.keybindingDialog import GetKeysDialog from idlelib.configSectionNameDialog import GetCfgSectionNameDialog from idlelib.configHelpSourceEdit import GetHelpSourceDialog +from idlelib.tabbedpages import TabbedPageSet from idlelib import macosxSupport - class ConfigDialog(Toplevel): def init(self, parent, title='', _htest=False, _utest=False): @@ -83,8 +83,6 @@ class ConfigDialog(Toplevel): self.CreatePageKeys() self.CreatePageGeneral() self.create_action_buttons().pack(side=BOTTOM)
Frame(self, height=2, borderwidth=0).pack(side=BOTTOM)[](#l3.17)
- def create_action_buttons(self): if macosxSupport.isAquaTk(): # Changing the default padding on OSX results in unreadable @@ -92,27 +90,30 @@ class ConfigDialog(Toplevel): paddingArgs = {} else: paddingArgs = {'padx':6, 'pady':3} -
frame = Frame(self, pady=2)[](#l3.27)
outer = Frame(self, pady=2)[](#l3.28)
buttons = Frame(outer, pady=2)[](#l3.29) self.buttonOk = Button([](#l3.30)
frame, text='Ok', command=self.Ok,[](#l3.31)
buttons, text='Ok', command=self.Ok,[](#l3.32) takefocus=FALSE, **paddingArgs)[](#l3.33) self.buttonApply = Button([](#l3.34)
frame, text='Apply', command=self.Apply,[](#l3.35)
buttons, text='Apply', command=self.Apply,[](#l3.36) takefocus=FALSE, **paddingArgs)[](#l3.37) self.buttonCancel = Button([](#l3.38)
frame, text='Cancel', command=self.Cancel,[](#l3.39)
buttons, text='Cancel', command=self.Cancel,[](#l3.40) takefocus=FALSE, **paddingArgs)[](#l3.41)
-# Comment out Help button creation and packing until implement self.Help -## self.buttonHelp = Button( -## frame, text='Help', command=self.Help, -## takefocus=FALSE, **paddingArgs) -## self.buttonHelp.pack(side=RIGHT, padx=5) self.buttonOk.pack(side=LEFT, padx=5) self.buttonApply.pack(side=LEFT, padx=5) self.buttonCancel.pack(side=LEFT, padx=5)
return frame[](#l3.50)
+# Comment out Help button creation and packing until implement self.Help +## self.buttonHelp = Button( +## buttons, text='Help', command=self.Help, +## takefocus=FALSE, **paddingArgs) +## self.buttonHelp.pack(side=RIGHT, padx=5)
# add space above buttons[](#l3.57)
Frame(outer, height=2, borderwidth=0).pack(side=TOP)[](#l3.58)
buttons.pack(side=BOTTOM)[](#l3.59)
def CreatePageFontTab(self): parent = self.parent self.fontSize = StringVar(parent)return outer[](#l3.60)
@@ -1205,10 +1206,252 @@ class ConfigDialog(Toplevel): def Help(self): pass +class VerticalScrolledFrame(Frame):
# create a canvas object and a vertical scrollbar for scrolling it[](#l3.78)
vscrollbar = Scrollbar(self, orient=VERTICAL)[](#l3.79)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)[](#l3.80)
canvas = Canvas(self, bd=0, highlightthickness=0,[](#l3.81)
yscrollcommand=vscrollbar.set)[](#l3.82)
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)[](#l3.83)
vscrollbar.config(command=canvas.yview)[](#l3.84)
# reset the view[](#l3.86)
canvas.xview_moveto(0)[](#l3.87)
canvas.yview_moveto(0)[](#l3.88)
# create a frame inside the canvas which will be scrolled with it[](#l3.90)
self.interior = interior = Frame(canvas)[](#l3.91)
interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)[](#l3.92)
# track changes to the canvas and frame width and sync them,[](#l3.94)
# also updating the scrollbar[](#l3.95)
def _configure_interior(event):[](#l3.96)
# update the scrollbars to match the size of the inner frame[](#l3.97)
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())[](#l3.98)
canvas.config(scrollregion="0 0 %s %s" % size)[](#l3.99)
if interior.winfo_reqwidth() != canvas.winfo_width():[](#l3.100)
# update the canvas's width to fit the inner frame[](#l3.101)
canvas.config(width=interior.winfo_reqwidth())[](#l3.102)
interior.bind('<Configure>', _configure_interior)[](#l3.103)
def _configure_canvas(event):[](#l3.105)
if interior.winfo_reqwidth() != canvas.winfo_width():[](#l3.106)
# update the inner frame's width to fill the canvas[](#l3.107)
canvas.itemconfigure(interior_id, width=canvas.winfo_width())[](#l3.108)
canvas.bind('<Configure>', _configure_canvas)[](#l3.109)
return[](#l3.111)
- "Return 's is blank or represents an int'"
- if not s:
return True[](#l3.116)
- try:
int(s)[](#l3.118)
return True[](#l3.119)
- except ValueError:
return False[](#l3.121)
+ +# TODO: +# * Revert to default(s)? Per option or per extension? +# * List options in their original order (possible??) +class ConfigExtensionsDialog(Toplevel):
- IDLE extensions save their configuration options using idleConf.
- ConfigExtensionsDialog 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 an True/False button.
- """
- def init(self, parent, title=None, _htest=False):
Toplevel.__init__(self, parent)[](#l3.144)
self.wm_withdraw()[](#l3.145)
self.configure(borderwidth=5)[](#l3.147)
self.geometry([](#l3.148)
"+%d+%d" % (parent.winfo_rootx() + 20,[](#l3.149)
parent.winfo_rooty() + (30 if not _htest else 150)))[](#l3.150)
self.wm_title(title or 'IDLE Extensions Configuration')[](#l3.151)
self.defaultCfg = idleConf.defaultCfg['extensions'][](#l3.153)
self.userCfg = idleConf.userCfg['extensions'][](#l3.154)
self.is_int = self.register(is_int)[](#l3.155)
self.load_extensions()[](#l3.156)
self.create_widgets()[](#l3.157)
self.resizable(height=FALSE, width=FALSE) # don't allow resizing yet[](#l3.159)
self.transient(parent)[](#l3.160)
self.protocol("WM_DELETE_WINDOW", self.Cancel)[](#l3.161)
self.tabbed_page_set.focus_set()[](#l3.162)
# wait for window to be generated[](#l3.163)
self.update()[](#l3.164)
# set current width as the minimum width[](#l3.165)
self.wm_minsize(self.winfo_width(), 1)[](#l3.166)
# now allow resizing[](#l3.167)
self.resizable(height=TRUE, width=TRUE)[](#l3.168)
self.wm_deiconify()[](#l3.170)
if not _htest:[](#l3.171)
self.grab_set()[](#l3.172)
self.wait_window()[](#l3.173)
- def load_extensions(self):
"Fill self.extensions with data from the default and user configs."[](#l3.176)
self.extensions = {}[](#l3.177)
for ext_name in idleConf.GetExtensions(active_only=False):[](#l3.178)
self.extensions[ext_name] = [][](#l3.179)
for ext_name in self.extensions:[](#l3.181)
opt_list = sorted(self.defaultCfg.GetOptionList(ext_name))[](#l3.182)
# bring 'enable' options to the beginning of the list[](#l3.184)
enables = [opt_name for opt_name in opt_list[](#l3.185)
if opt_name.startswith('enable')][](#l3.186)
for opt_name in enables:[](#l3.187)
opt_list.remove(opt_name)[](#l3.188)
opt_list = enables + opt_list[](#l3.189)
for opt_name in opt_list:[](#l3.191)
def_str = self.defaultCfg.Get([](#l3.192)
ext_name, opt_name, raw=True)[](#l3.193)
try:[](#l3.194)
def_obj = {'True':True, 'False':False}[def_str][](#l3.195)
opt_type = 'bool'[](#l3.196)
except KeyError:[](#l3.197)
try:[](#l3.198)
def_obj = int(def_str)[](#l3.199)
opt_type = 'int'[](#l3.200)
except ValueError:[](#l3.201)
def_obj = def_str[](#l3.202)
opt_type = None[](#l3.203)
try:[](#l3.204)
value = self.userCfg.Get([](#l3.205)
ext_name, opt_name, type=opt_type, raw=True,[](#l3.206)
default=def_obj)[](#l3.207)
except ValueError: # Need this until .Get fixed[](#l3.208)
value = def_obj # bad values overwritten by entry[](#l3.209)
var = StringVar(self)[](#l3.210)
var.set(str(value))[](#l3.211)
self.extensions[ext_name].append({'name': opt_name,[](#l3.213)
'type': opt_type,[](#l3.214)
'default': def_str,[](#l3.215)
'value': value,[](#l3.216)
'var': var,[](#l3.217)
})[](#l3.218)
- def create_widgets(self):
"""Create the dialog's widgets."""[](#l3.221)
self.rowconfigure(0, weight=1)[](#l3.222)
self.rowconfigure(1, weight=0)[](#l3.223)
self.columnconfigure(0, weight=1)[](#l3.224)
# create the tabbed pages[](#l3.226)
self.tabbed_page_set = TabbedPageSet([](#l3.227)
self, page_names=self.extensions.keys(),[](#l3.228)
n_rows=None, max_tabs_per_row=5,[](#l3.229)
page_class=TabbedPageSet.PageRemove)[](#l3.230)
self.tabbed_page_set.grid(row=0, column=0, sticky=NSEW)[](#l3.231)
for ext_name in self.extensions:[](#l3.232)
self.create_tab_page(ext_name)[](#l3.233)
self.create_action_buttons().grid(row=1)[](#l3.235)
page = LabelFrame(self.tabbed_page_set.pages[ext_name].frame,[](#l3.242)
border=2, padx=2, relief=GROOVE,[](#l3.243)
text=' %s ' % ext_name)[](#l3.244)
page.pack(fill=BOTH, expand=True, padx=12, pady=2)[](#l3.245)
# create the scrollable frame which will contain the entries[](#l3.247)
scrolled_frame = VerticalScrolledFrame(page, pady=2, height=250)[](#l3.248)
scrolled_frame.pack(side=BOTTOM, fill=BOTH, expand=TRUE)[](#l3.249)
entry_area = scrolled_frame.interior[](#l3.250)
entry_area.columnconfigure(0, weight=0)[](#l3.251)
entry_area.columnconfigure(1, weight=1)[](#l3.252)
# create an entry for each configuration option[](#l3.254)
for row, opt in enumerate(self.extensions[ext_name]):[](#l3.255)
# create a row with a label and entry/checkbutton[](#l3.256)
label = Label(entry_area, text=opt['name'])[](#l3.257)
label.grid(row=row, column=0, sticky=NW)[](#l3.258)
var = opt['var'][](#l3.259)
if opt['type'] == 'bool':[](#l3.260)
Checkbutton(entry_area, textvariable=var, variable=var,[](#l3.261)
onvalue='True', offvalue='False',[](#l3.262)
indicatoron=FALSE, selectcolor='', width=8[](#l3.263)
).grid(row=row, column=1, sticky=W, padx=7)[](#l3.264)
elif opt['type'] == 'int':[](#l3.265)
Entry(entry_area, textvariable=var, validate='key',[](#l3.266)
validatecommand=(self.is_int, '%P')[](#l3.267)
).grid(row=row, column=1, sticky=NSEW, padx=7)[](#l3.268)
else:[](#l3.270)
Entry(entry_area, textvariable=var[](#l3.271)
).grid(row=row, column=1, sticky=NSEW, padx=7)[](#l3.272)
return[](#l3.273)
- def set_user_value(self, section, opt):
name = opt['name'][](#l3.288)
default = opt['default'][](#l3.289)
value = opt['var'].get().strip() or default[](#l3.290)
opt['var'].set(value)[](#l3.291)
# if self.defaultCfg.has_section(section):[](#l3.292)
# Currently, always true; if not, indent to return[](#l3.293)
if (value == default):[](#l3.294)
return self.userCfg.RemoveOption(section, name)[](#l3.295)
# set the option[](#l3.296)
return self.userCfg.SetOption(section, name, value)[](#l3.297)
- def save_all_changed_configs(self):
"""Save configuration changes to the user config file."""[](#l3.300)
has_changes = False[](#l3.301)
for ext_name in self.extensions:[](#l3.302)
options = self.extensions[ext_name][](#l3.303)
for opt in options:[](#l3.304)
if self.set_user_value(ext_name, opt):[](#l3.305)
has_changes = True[](#l3.306)
if has_changes:[](#l3.307)
self.userCfg.Save()[](#l3.308)
+ + if name == 'main': import unittest unittest.main('idlelib.idle_test.test_configdialog', verbosity=2, exit=False) - from idlelib.idle_test.htest import run
--- a/Lib/idlelib/configHandler.py +++ b/Lib/idlelib/configHandler.py @@ -45,6 +45,9 @@ class IdleConfParser(ConfigParser): Get an option value for given section/option or return default. If type is specified, return as type. """
# TODO Use default as fallback, at least if not None[](#l4.7)
# Should also print Warning(file, section, option).[](#l4.8)
# Currently may raise ValueError[](#l4.9) if not self.has_option(section, option):[](#l4.10) return default[](#l4.11) if type == 'bool':[](#l4.12)
--- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -93,6 +93,15 @@ AboutDialog_spec = { "Double clicking on items prints a traceback for an exception " "that is ignored." } +ConfigExtensionsDialog_spec = {
- 'file': 'configDialog',
- 'kwds': {'title': 'Test Extension Configuration',
'_htest': True,},[](#l5.10)
- 'msg': "IDLE extensions dialog.\n"
"\n[Ok] to close the dialog.[Apply] to apply the settings and "[](#l5.12)
"and [Cancel] to revert all changes.\nRe-run the test to ensure "[](#l5.13)
"changes made have persisted."[](#l5.14)
- }
_color_delegator_spec = { 'file': 'ColorDelegator',
--- a/Lib/idlelib/macosxSupport.py +++ b/Lib/idlelib/macosxSupport.py @@ -142,11 +142,9 @@ def overrideRootMenu(root, flist): # Remove the 'About' entry from the help menu, it is in the application # menu del Bindings.menudefs[-1][1][0:2] -