Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
7a16207c
Commit
7a16207c
authored
Oct 22, 2014
by
Terry Jan Reedy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue #3068: Add Idle extension configuration dialog to Options menu.
Original patch by Tal Einat.
parent
ed91883b
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
278 additions
and
20 deletions
+278
-20
Lib/idlelib/Bindings.py
Lib/idlelib/Bindings.py
+2
-1
Lib/idlelib/EditorWindow.py
Lib/idlelib/EditorWindow.py
+4
-0
Lib/idlelib/configDialog.py
Lib/idlelib/configDialog.py
+258
-15
Lib/idlelib/configHandler.py
Lib/idlelib/configHandler.py
+3
-0
Lib/idlelib/idle_test/htest.py
Lib/idlelib/idle_test/htest.py
+9
-0
Lib/idlelib/macosxSupport.py
Lib/idlelib/macosxSupport.py
+2
-4
No files found.
Lib/idlelib/Bindings.py
View file @
7a16207c
...
...
@@ -75,7 +75,8 @@ menudefs = [
(
'!_Auto-open Stack Viewer'
,
'<<toggle-jit-stack-viewer>>'
),
]),
(
'options'
,
[
(
'_Configure IDLE...'
,
'<<open-config-dialog>>'
),
(
'Configure _IDLE'
,
'<<open-config-dialog>>'
),
(
'Configure _Extensions'
,
'<<open-config-extensions-dialog>>'
),
None
,
]),
(
'help'
,
[
...
...
Lib/idlelib/EditorWindow.py
View file @
7a16207c
...
...
@@ -216,6 +216,8 @@ class EditorWindow(object):
text
.
bind
(
"<<python-docs>>"
,
self
.
python_docs
)
text
.
bind
(
"<<about-idle>>"
,
self
.
about_dialog
)
text
.
bind
(
"<<open-config-dialog>>"
,
self
.
config_dialog
)
text
.
bind
(
"<<open-config-extensions-dialog>>"
,
self
.
config_extensions_dialog
)
text
.
bind
(
"<<open-module>>"
,
self
.
open_module
)
text
.
bind
(
"<<do-nothing>>"
,
lambda
event
:
"break"
)
text
.
bind
(
"<<select-all>>"
,
self
.
select_all
)
...
...
@@ -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
)
def
help_dialog
(
self
,
event
=
None
):
if
self
.
root
:
...
...
Lib/idlelib/configDialog.py
View file @
7a16207c
...
...
@@ -18,8 +18,8 @@ from idlelib.tabbedpages import TabbedPageSet
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
)
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
)
outer
=
Frame
(
self
,
pady
=
2
)
buttons
=
Frame
(
outer
,
pady
=
2
)
self
.
buttonOk
=
Button
(
frame
,
text
=
'Ok'
,
command
=
self
.
Ok
,
buttons
,
text
=
'Ok'
,
command
=
self
.
Ok
,
takefocus
=
FALSE
,
**
paddingArgs
)
self
.
buttonApply
=
Button
(
frame
,
text
=
'Apply'
,
command
=
self
.
Apply
,
buttons
,
text
=
'Apply'
,
command
=
self
.
Apply
,
takefocus
=
FALSE
,
**
paddingArgs
)
self
.
buttonCancel
=
Button
(
frame
,
text
=
'Cancel'
,
command
=
self
.
Cancel
,
buttons
,
text
=
'Cancel'
,
command
=
self
.
Cancel
,
takefocus
=
FALSE
,
**
paddingArgs
)
self
.
buttonOk
.
pack
(
side
=
LEFT
,
padx
=
5
)
self
.
buttonApply
.
pack
(
side
=
LEFT
,
padx
=
5
)
self
.
buttonCancel
.
pack
(
side
=
LEFT
,
padx
=
5
)
# Comment out Help button creation and packing until implement self.Help
## self.buttonHelp = Button(
##
frame
, text='Help', command=self.Help,
##
buttons
, 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
# add space above buttons
Frame
(
outer
,
height
=
2
,
borderwidth
=
0
).
pack
(
side
=
TOP
)
buttons
.
pack
(
side
=
BOTTOM
)
return
outer
def
CreatePageFontTab
(
self
):
parent
=
self
.
parent
self
.
fontSize
=
StringVar
(
parent
)
...
...
@@ -1205,10 +1206,252 @@ class ConfigDialog(Toplevel):
def
Help
(
self
):
pass
class
VerticalScrolledFrame
(
Frame
):
"""A pure Tkinter vertically scrollable frame.
* Use the 'interior' attribute to place widgets inside the scrollable frame
* Construct and pack/place/grid normally
* This frame only allows vertical scrolling
"""
def
__init__
(
self
,
parent
,
*
args
,
**
kw
):
Frame
.
__init__
(
self
,
parent
,
*
args
,
**
kw
)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar
=
Scrollbar
(
self
,
orient
=
VERTICAL
)
vscrollbar
.
pack
(
fill
=
Y
,
side
=
RIGHT
,
expand
=
FALSE
)
canvas
=
Canvas
(
self
,
bd
=
0
,
highlightthickness
=
0
,
yscrollcommand
=
vscrollbar
.
set
)
canvas
.
pack
(
side
=
LEFT
,
fill
=
BOTH
,
expand
=
TRUE
)
vscrollbar
.
config
(
command
=
canvas
.
yview
)
# reset the view
canvas
.
xview_moveto
(
0
)
canvas
.
yview_moveto
(
0
)
# create a frame inside the canvas which will be scrolled with it
self
.
interior
=
interior
=
Frame
(
canvas
)
interior_id
=
canvas
.
create_window
(
0
,
0
,
window
=
interior
,
anchor
=
NW
)
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def
_configure_interior
(
event
):
# update the scrollbars to match the size of the inner frame
size
=
(
interior
.
winfo_reqwidth
(),
interior
.
winfo_reqheight
())
canvas
.
config
(
scrollregion
=
"0 0 %s %s"
%
size
)
if
interior
.
winfo_reqwidth
()
!=
canvas
.
winfo_width
():
# update the canvas's width to fit the inner frame
canvas
.
config
(
width
=
interior
.
winfo_reqwidth
())
interior
.
bind
(
'<Configure>'
,
_configure_interior
)
def
_configure_canvas
(
event
):
if
interior
.
winfo_reqwidth
()
!=
canvas
.
winfo_width
():
# update the inner frame's width to fill the canvas
canvas
.
itemconfigure
(
interior_id
,
width
=
canvas
.
winfo_width
())
canvas
.
bind
(
'<Configure>'
,
_configure_canvas
)
return
def
is_int
(
s
):
"Return 's is blank or represents an int'"
if
not
s
:
return
True
try
:
int
(
s
)
return
True
except
ValueError
:
return
False
# TODO:
# * Revert to default(s)? Per option or per extension?
# * List options in their original order (possible??)
class
ConfigExtensionsDialog
(
Toplevel
):
"""A dialog for configuring IDLE extensions.
This dialog is generic - it works for any and all IDLE extensions.
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
)
self
.
wm_withdraw
()
self
.
configure
(
borderwidth
=
5
)
self
.
geometry
(
"+%d+%d"
%
(
parent
.
winfo_rootx
()
+
20
,
parent
.
winfo_rooty
()
+
(
30
if
not
_htest
else
150
)))
self
.
wm_title
(
title
or
'IDLE Extensions Configuration'
)
self
.
defaultCfg
=
idleConf
.
defaultCfg
[
'extensions'
]
self
.
userCfg
=
idleConf
.
userCfg
[
'extensions'
]
self
.
is_int
=
self
.
register
(
is_int
)
self
.
load_extensions
()
self
.
create_widgets
()
self
.
resizable
(
height
=
FALSE
,
width
=
FALSE
)
# don't allow resizing yet
self
.
transient
(
parent
)
self
.
protocol
(
"WM_DELETE_WINDOW"
,
self
.
Cancel
)
self
.
tabbed_page_set
.
focus_set
()
# wait for window to be generated
self
.
update
()
# set current width as the minimum width
self
.
wm_minsize
(
self
.
winfo_width
(),
1
)
# now allow resizing
self
.
resizable
(
height
=
TRUE
,
width
=
TRUE
)
self
.
wm_deiconify
()
if
not
_htest
:
self
.
grab_set
()
self
.
wait_window
()
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
]
=
[]
for
ext_name
in
self
.
extensions
:
opt_list
=
sorted
(
self
.
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
.
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
.
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
create_widgets
(
self
):
"""Create the dialog's widgets."""
self
.
rowconfigure
(
0
,
weight
=
1
)
self
.
rowconfigure
(
1
,
weight
=
0
)
self
.
columnconfigure
(
0
,
weight
=
1
)
# create the tabbed pages
self
.
tabbed_page_set
=
TabbedPageSet
(
self
,
page_names
=
self
.
extensions
.
keys
(),
n_rows
=
None
,
max_tabs_per_row
=
5
,
page_class
=
TabbedPageSet
.
PageRemove
)
self
.
tabbed_page_set
.
grid
(
row
=
0
,
column
=
0
,
sticky
=
NSEW
)
for
ext_name
in
self
.
extensions
:
self
.
create_tab_page
(
ext_name
)
self
.
create_action_buttons
().
grid
(
row
=
1
)
create_action_buttons
=
ConfigDialog
.
create_action_buttons
.
im_func
def
create_tab_page
(
self
,
ext_name
):
"""Create the page for an extension."""
page
=
LabelFrame
(
self
.
tabbed_page_set
.
pages
[
ext_name
].
frame
,
border
=
2
,
padx
=
2
,
relief
=
GROOVE
,
text
=
' %s '
%
ext_name
)
page
.
pack
(
fill
=
BOTH
,
expand
=
True
,
padx
=
12
,
pady
=
2
)
# create the scrollable frame which will contain the entries
scrolled_frame
=
VerticalScrolledFrame
(
page
,
pady
=
2
,
height
=
250
)
scrolled_frame
.
pack
(
side
=
BOTTOM
,
fill
=
BOTH
,
expand
=
TRUE
)
entry_area
=
scrolled_frame
.
interior
entry_area
.
columnconfigure
(
0
,
weight
=
0
)
entry_area
.
columnconfigure
(
1
,
weight
=
1
)
# 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
)
else
:
Entry
(
entry_area
,
textvariable
=
var
).
grid
(
row
=
row
,
column
=
1
,
sticky
=
NSEW
,
padx
=
7
)
return
Ok
=
ConfigDialog
.
Ok
.
im_func
def
Apply
(
self
):
self
.
save_all_changed_configs
()
pass
Cancel
=
ConfigDialog
.
Cancel
.
im_func
def
Help
(
self
):
pass
def
set_user_value
(
self
,
section
,
opt
):
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
.
userCfg
.
RemoveOption
(
section
,
name
)
# set the option
return
self
.
userCfg
.
SetOption
(
section
,
name
,
value
)
def
save_all_changed_configs
(
self
):
"""Save configuration changes to the user config file."""
has_changes
=
False
for
ext_name
in
self
.
extensions
:
options
=
self
.
extensions
[
ext_name
]
for
opt
in
options
:
if
self
.
set_user_value
(
ext_name
,
opt
):
has_changes
=
True
if
has_changes
:
self
.
userCfg
.
Save
()
if
__name__
==
'__main__'
:
import
unittest
unittest
.
main
(
'idlelib.idle_test.test_configdialog'
,
verbosity
=
2
,
exit
=
False
)
from
idlelib.idle_test.htest
import
run
run
(
ConfigDialog
)
run
(
ConfigDialog
,
ConfigExtensionsDialog
)
Lib/idlelib/configHandler.py
View file @
7a16207c
...
...
@@ -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
# Should also print Warning(file, section, option).
# Currently may raise ValueError
if
not
self
.
has_option
(
section
,
option
):
return
default
if
type
==
'bool'
:
...
...
Lib/idlelib/idle_test/htest.py
View file @
7a16207c
...
...
@@ -93,6 +93,15 @@ _class_browser_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
,},
'msg'
:
"IDLE extensions dialog.
\
n
"
"
\
n
[Ok] to close the dialog.[Apply] to apply the settings and "
"and [Cancel] to revert all changes.
\
n
Re-run the test to ensure "
"changes made have persisted."
}
_color_delegator_spec
=
{
'file'
:
'ColorDelegator'
,
...
...
Lib/idlelib/macosxSupport.py
View file @
7a16207c
...
...
@@ -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
]
# Remove the 'Configure' entry from the options menu, it is in the
# Remove the 'Configure Idle' entry from the options menu, it is in the
# application menu as 'Preferences'
del
Bindings
.
menudefs
[
-
2
][
1
][
0
:
2
]
del
Bindings
.
menudefs
[
-
2
][
1
][
0
]
menubar
=
Menu
(
root
)
root
.
configure
(
menu
=
menubar
)
menudict
=
{}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment