From 0fe43be4e1abcd24bb503bf4ee150b08e02e4322 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 10 Jun 2016 18:19:21 -0400 Subject: Issue *24750: Switch all scrollbars in IDLE to ttk versions. Where needed, add minimal tests to cover changes. --- Lib/idlelib/configdialog.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib/idlelib/configdialog.py') diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index b58806e38c..1d7b4179b0 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -10,6 +10,7 @@ Refer to comments in EditorWindow autoindent code for details. """ from tkinter import * +from tkinter.ttk import Scrollbar import tkinter.messagebox as tkMessageBox import tkinter.colorchooser as tkColorChooser import tkinter.font as tkFont -- cgit v1.2.1 From cfab8ab54ed5d20f88e22bab6e2b6bca3f0ffb6f Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 14 Jun 2016 00:53:30 -0400 Subject: Issue #27245: temporary rename for merge. --- Lib/idlelib/configdialog.py | 1435 ------------------------------------------- 1 file changed, 1435 deletions(-) delete mode 100644 Lib/idlelib/configdialog.py (limited to 'Lib/idlelib/configdialog.py') diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py deleted file mode 100644 index 1d7b4179b0..0000000000 --- a/Lib/idlelib/configdialog.py +++ /dev/null @@ -1,1435 +0,0 @@ -"""IDLE Configuration Dialog: support user customization of IDLE by GUI - -Customize font faces, sizes, and colorization attributes. Set indentation -defaults. Customize keybindings. Colorization and keybindings can be -saved as user defined sets. Select startup options including shell/editor -and default window size. Define additional help sources. - -Note that tab width in IDLE is currently fixed at eight due to Tk issues. -Refer to comments in EditorWindow autoindent code for details. - -""" -from tkinter import * -from tkinter.ttk import Scrollbar -import tkinter.messagebox as tkMessageBox -import tkinter.colorchooser as tkColorChooser -import tkinter.font as tkFont - -from idlelib.config import idleConf -from idlelib.dynoption import DynOptionMenu -from idlelib.config_key import GetKeysDialog -from idlelib.config_sec import GetCfgSectionNameDialog -from idlelib.config_help import GetHelpSourceDialog -from idlelib.tabbedpages import TabbedPageSet -from idlelib.textview import view_text -from idlelib import macosx - -class ConfigDialog(Toplevel): - - def __init__(self, parent, title='', _htest=False, _utest=False): - """ - _htest - bool, change box location when running htest - _utest - bool, don't wait_window when running unittest - """ - Toplevel.__init__(self, parent) - self.parent = parent - if _htest: - parent.instance_dict = {} - self.wm_withdraw() - - self.configure(borderwidth=5) - self.title(title or 'IDLE Preferences') - self.geometry( - "+%d+%d" % (parent.winfo_rootx() + 20, - parent.winfo_rooty() + (30 if not _htest else 150))) - #Theme Elements. Each theme element key is its display name. - #The first value of the tuple is the sample area tag name. - #The second value is the display name list sort index. - self.themeElements={ - 'Normal Text': ('normal', '00'), - 'Python Keywords': ('keyword', '01'), - 'Python Definitions': ('definition', '02'), - 'Python Builtins': ('builtin', '03'), - 'Python Comments': ('comment', '04'), - 'Python Strings': ('string', '05'), - 'Selected Text': ('hilite', '06'), - 'Found Text': ('hit', '07'), - 'Cursor': ('cursor', '08'), - 'Editor Breakpoint': ('break', '09'), - 'Shell Normal Text': ('console', '10'), - 'Shell Error Text': ('error', '11'), - 'Shell Stdout Text': ('stdout', '12'), - 'Shell Stderr Text': ('stderr', '13'), - } - self.ResetChangedItems() #load initial values in changed items dict - self.CreateWidgets() - self.resizable(height=FALSE, width=FALSE) - self.transient(parent) - self.grab_set() - self.protocol("WM_DELETE_WINDOW", self.Cancel) - self.tabPages.focus_set() - #key bindings for this dialog - #self.bind('', self.Cancel) #dismiss dialog, no save - #self.bind('', self.Apply) #apply changes, save - #self.bind('', self.Help) #context help - self.LoadConfigs() - self.AttachVarCallbacks() #avoid callbacks during LoadConfigs - - if not _utest: - self.wm_deiconify() - self.wait_window() - - def CreateWidgets(self): - self.tabPages = TabbedPageSet(self, - page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General', - 'Extensions']) - self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH) - self.CreatePageFontTab() - self.CreatePageHighlight() - self.CreatePageKeys() - self.CreatePageGeneral() - self.CreatePageExtensions() - self.create_action_buttons().pack(side=BOTTOM) - - def create_action_buttons(self): - if macosx.isAquaTk(): - # Changing the default padding on OSX results in unreadable - # text in the buttons - paddingArgs = {} - else: - paddingArgs = {'padx':6, 'pady':3} - outer = Frame(self, pady=2) - buttons = Frame(outer, pady=2) - for txt, cmd in ( - ('Ok', self.Ok), - ('Apply', self.Apply), - ('Cancel', self.Cancel), - ('Help', self.Help)): - Button(buttons, text=txt, command=cmd, takefocus=FALSE, - **paddingArgs).pack(side=LEFT, padx=5) - # 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) - self.fontBold = BooleanVar(parent) - self.fontName = StringVar(parent) - self.spaceNum = IntVar(parent) - self.editFont = tkFont.Font(parent, ('courier', 10, 'normal')) - - ##widget creation - #body frame - frame = self.tabPages.pages['Fonts/Tabs'].frame - #body section frames - frameFont = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ') - frameIndent = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ') - #frameFont - frameFontName = Frame(frameFont) - frameFontParam = Frame(frameFont) - labelFontNameTitle = Label( - frameFontName, justify=LEFT, text='Font Face :') - self.listFontName = Listbox( - frameFontName, height=5, takefocus=FALSE, exportselection=FALSE) - self.listFontName.bind( - '', self.OnListFontButtonRelease) - scrollFont = Scrollbar(frameFontName) - scrollFont.config(command=self.listFontName.yview) - self.listFontName.config(yscrollcommand=scrollFont.set) - labelFontSizeTitle = Label(frameFontParam, text='Size :') - self.optMenuFontSize = DynOptionMenu( - frameFontParam, self.fontSize, None, command=self.SetFontSample) - checkFontBold = Checkbutton( - frameFontParam, variable=self.fontBold, onvalue=1, - offvalue=0, text='Bold', command=self.SetFontSample) - frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1) - self.labelFontSample = Label( - frameFontSample, justify=LEFT, font=self.editFont, - text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]') - #frameIndent - frameIndentSize = Frame(frameIndent) - labelSpaceNumTitle = Label( - frameIndentSize, justify=LEFT, - text='Python Standard: 4 Spaces!') - self.scaleSpaceNum = Scale( - frameIndentSize, variable=self.spaceNum, - orient='horizontal', tickinterval=2, from_=2, to=16) - - #widget packing - #body - frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y) - #frameFont - frameFontName.pack(side=TOP, padx=5, pady=5, fill=X) - frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X) - labelFontNameTitle.pack(side=TOP, anchor=W) - self.listFontName.pack(side=LEFT, expand=TRUE, fill=X) - scrollFont.pack(side=LEFT, fill=Y) - labelFontSizeTitle.pack(side=LEFT, anchor=W) - self.optMenuFontSize.pack(side=LEFT, anchor=W) - checkFontBold.pack(side=LEFT, anchor=W, padx=20) - frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - self.labelFontSample.pack(expand=TRUE, fill=BOTH) - #frameIndent - frameIndentSize.pack(side=TOP, fill=X) - labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5) - self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X) - return frame - - def CreatePageHighlight(self): - parent = self.parent - self.builtinTheme = StringVar(parent) - self.customTheme = StringVar(parent) - self.fgHilite = BooleanVar(parent) - self.colour = StringVar(parent) - self.fontName = StringVar(parent) - self.themeIsBuiltin = BooleanVar(parent) - self.highlightTarget = StringVar(parent) - - ##widget creation - #body frame - frame = self.tabPages.pages['Highlighting'].frame - #body section frames - frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Custom Highlighting ') - frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Highlighting Theme ') - #frameCustom - self.textHighlightSample=Text( - frameCustom, relief=SOLID, borderwidth=1, - font=('courier', 12, ''), cursor='hand2', width=21, height=11, - takefocus=FALSE, highlightthickness=0, wrap=NONE) - text=self.textHighlightSample - text.bind('', lambda e: 'break') - text.bind('', lambda e: 'break') - textAndTags=( - ('#you can click here', 'comment'), ('\n', 'normal'), - ('#to choose items', 'comment'), ('\n', 'normal'), - ('def', 'keyword'), (' ', 'normal'), - ('func', 'definition'), ('(param):\n ', 'normal'), - ('"""string"""', 'string'), ('\n var0 = ', 'normal'), - ("'string'", 'string'), ('\n var1 = ', 'normal'), - ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), - ("'found'", 'hit'), ('\n var3 = ', 'normal'), - ('list', 'builtin'), ('(', 'normal'), - ('None', 'keyword'), (')\n', 'normal'), - (' breakpoint("line")', 'break'), ('\n\n', 'normal'), - (' error ', 'error'), (' ', 'normal'), - ('cursor |', 'cursor'), ('\n ', 'normal'), - ('shell', 'console'), (' ', 'normal'), - ('stdout', 'stdout'), (' ', 'normal'), - ('stderr', 'stderr'), ('\n', 'normal')) - for txTa in textAndTags: - text.insert(END, txTa[0], txTa[1]) - for element in self.themeElements: - def tem(event, elem=element): - event.widget.winfo_toplevel().highlightTarget.set(elem) - text.tag_bind( - self.themeElements[element][0], '', tem) - text.config(state=DISABLED) - self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1) - frameFgBg = Frame(frameCustom) - buttonSetColour = Button( - self.frameColourSet, text='Choose Colour for :', - command=self.GetColour, highlightthickness=0) - self.optMenuHighlightTarget = DynOptionMenu( - self.frameColourSet, self.highlightTarget, None, - highlightthickness=0) #, command=self.SetHighlightTargetBinding - self.radioFg = Radiobutton( - frameFgBg, variable=self.fgHilite, value=1, - text='Foreground', command=self.SetColourSampleBinding) - self.radioBg=Radiobutton( - frameFgBg, variable=self.fgHilite, value=0, - text='Background', command=self.SetColourSampleBinding) - self.fgHilite.set(1) - buttonSaveCustomTheme = Button( - frameCustom, text='Save as New Custom Theme', - command=self.SaveAsNewTheme) - #frameTheme - labelTypeTitle = Label(frameTheme, text='Select : ') - self.radioThemeBuiltin = Radiobutton( - frameTheme, variable=self.themeIsBuiltin, value=1, - command=self.SetThemeType, text='a Built-in Theme') - self.radioThemeCustom = Radiobutton( - frameTheme, variable=self.themeIsBuiltin, value=0, - command=self.SetThemeType, text='a Custom Theme') - self.optMenuThemeBuiltin = DynOptionMenu( - frameTheme, self.builtinTheme, None, command=None) - self.optMenuThemeCustom=DynOptionMenu( - frameTheme, self.customTheme, None, command=None) - self.buttonDeleteCustomTheme=Button( - frameTheme, text='Delete Custom Theme', - command=self.DeleteCustomTheme) - self.new_custom_theme = Label(frameTheme, bd=2) - - ##widget packing - #body - frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y) - #frameCustom - self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) - frameFgBg.pack(side=TOP, padx=5, pady=0) - self.textHighlightSample.pack( - side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) - self.optMenuHighlightTarget.pack( - side=TOP, expand=TRUE, fill=X, padx=8, pady=3) - self.radioFg.pack(side=LEFT, anchor=E) - self.radioBg.pack(side=RIGHT, anchor=W) - buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5) - #frameTheme - labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5) - self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5) - self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2) - self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) - self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) - self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5) - self.new_custom_theme.pack(side=TOP, fill=X, pady=5) - return frame - - def CreatePageKeys(self): - parent = self.parent - self.bindingTarget = StringVar(parent) - self.builtinKeys = StringVar(parent) - self.customKeys = StringVar(parent) - self.keysAreBuiltin = BooleanVar(parent) - self.keyBinding = StringVar(parent) - - ##widget creation - #body frame - frame = self.tabPages.pages['Keys'].frame - #body section frames - frameCustom = LabelFrame( - frame, borderwidth=2, relief=GROOVE, - text=' Custom Key Bindings ') - frameKeySets = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Key Set ') - #frameCustom - frameTarget = Frame(frameCustom) - labelTargetTitle = Label(frameTarget, text='Action - Key(s)') - scrollTargetY = Scrollbar(frameTarget) - scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL) - self.listBindings = Listbox( - frameTarget, takefocus=FALSE, exportselection=FALSE) - self.listBindings.bind('', self.KeyBindingSelected) - scrollTargetY.config(command=self.listBindings.yview) - scrollTargetX.config(command=self.listBindings.xview) - self.listBindings.config(yscrollcommand=scrollTargetY.set) - self.listBindings.config(xscrollcommand=scrollTargetX.set) - self.buttonNewKeys = Button( - frameCustom, text='Get New Keys for Selection', - command=self.GetNewKeys, state=DISABLED) - #frameKeySets - frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0) - for i in range(2)] - self.radioKeysBuiltin = Radiobutton( - frames[0], variable=self.keysAreBuiltin, value=1, - command=self.SetKeysType, text='Use a Built-in Key Set') - self.radioKeysCustom = Radiobutton( - frames[0], variable=self.keysAreBuiltin, value=0, - command=self.SetKeysType, text='Use a Custom Key Set') - self.optMenuKeysBuiltin = DynOptionMenu( - frames[0], self.builtinKeys, None, command=None) - self.optMenuKeysCustom = DynOptionMenu( - frames[0], self.customKeys, None, command=None) - self.buttonDeleteCustomKeys = Button( - frames[1], text='Delete Custom Key Set', - command=self.DeleteCustomKeys) - buttonSaveCustomKeys = Button( - frames[1], text='Save as New Custom Key Set', - command=self.SaveAsNewKeySet) - - ##widget packing - #body - frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) - frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) - #frameCustom - self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5) - frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - #frame target - frameTarget.columnconfigure(0, weight=1) - frameTarget.rowconfigure(1, weight=1) - labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W) - self.listBindings.grid(row=1, column=0, sticky=NSEW) - scrollTargetY.grid(row=1, column=1, sticky=NS) - scrollTargetX.grid(row=2, column=0, sticky=EW) - #frameKeySets - self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS) - self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) - self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) - self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) - self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) - buttonSaveCustomKeys.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 CreatePageGeneral(self): - parent = self.parent - self.winWidth = StringVar(parent) - self.winHeight = StringVar(parent) - self.startupEdit = IntVar(parent) - self.autoSave = IntVar(parent) - self.encoding = StringVar(parent) - self.userHelpBrowser = BooleanVar(parent) - self.helpBrowser = StringVar(parent) - - #widget creation - #body - frame = self.tabPages.pages['General'].frame - #body section frames - frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Startup Preferences ') - frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Autosave Preferences ') - frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE) - frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Additional Help Sources ') - #frameRun - labelRunChoiceTitle = Label(frameRun, text='At Startup') - radioStartupEdit = Radiobutton( - frameRun, variable=self.startupEdit, value=1, - command=self.SetKeysType, text="Open Edit Window") - radioStartupShell = Radiobutton( - frameRun, variable=self.startupEdit, value=0, - command=self.SetKeysType, text='Open Shell Window') - #frameSave - labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ') - radioSaveAsk = Radiobutton( - frameSave, variable=self.autoSave, value=0, - command=self.SetKeysType, text="Prompt to Save") - radioSaveAuto = Radiobutton( - frameSave, variable=self.autoSave, value=1, - command=self.SetKeysType, text='No Prompt') - #frameWinSize - labelWinSizeTitle = Label( - frameWinSize, text='Initial Window Size (in characters)') - labelWinWidthTitle = Label(frameWinSize, text='Width') - entryWinWidth = Entry( - frameWinSize, textvariable=self.winWidth, width=3) - labelWinHeightTitle = Label(frameWinSize, text='Height') - entryWinHeight = Entry( - frameWinSize, textvariable=self.winHeight, width=3) - #frameHelp - frameHelpList = Frame(frameHelp) - frameHelpListButtons = Frame(frameHelpList) - scrollHelpList = Scrollbar(frameHelpList) - self.listHelp = Listbox( - frameHelpList, height=5, takefocus=FALSE, - exportselection=FALSE) - scrollHelpList.config(command=self.listHelp.yview) - self.listHelp.config(yscrollcommand=scrollHelpList.set) - self.listHelp.bind('', self.HelpSourceSelected) - self.buttonHelpListEdit = Button( - frameHelpListButtons, text='Edit', state=DISABLED, - width=8, command=self.HelpListItemEdit) - self.buttonHelpListAdd = Button( - frameHelpListButtons, text='Add', - width=8, command=self.HelpListItemAdd) - self.buttonHelpListRemove = Button( - frameHelpListButtons, text='Remove', state=DISABLED, - width=8, command=self.HelpListItemRemove) - - #widget packing - #body - frameRun.pack(side=TOP, padx=5, pady=5, fill=X) - frameSave.pack(side=TOP, padx=5, pady=5, fill=X) - frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X) - frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - #frameRun - labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5) - radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5) - #frameSave - labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5) - radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5) - #frameWinSize - labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5) - labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5) - entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5) - labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5) - #frameHelp - frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y) - frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y) - self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) - self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5) - self.buttonHelpListAdd.pack(side=TOP, anchor=W) - self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5) - return frame - - def AttachVarCallbacks(self): - self.fontSize.trace_variable('w', self.VarChanged_font) - self.fontName.trace_variable('w', self.VarChanged_font) - self.fontBold.trace_variable('w', self.VarChanged_font) - self.spaceNum.trace_variable('w', self.VarChanged_spaceNum) - self.colour.trace_variable('w', self.VarChanged_colour) - self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme) - self.customTheme.trace_variable('w', self.VarChanged_customTheme) - self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin) - self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget) - self.keyBinding.trace_variable('w', self.VarChanged_keyBinding) - self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys) - self.customKeys.trace_variable('w', self.VarChanged_customKeys) - self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin) - self.winWidth.trace_variable('w', self.VarChanged_winWidth) - self.winHeight.trace_variable('w', self.VarChanged_winHeight) - self.startupEdit.trace_variable('w', self.VarChanged_startupEdit) - self.autoSave.trace_variable('w', self.VarChanged_autoSave) - self.encoding.trace_variable('w', self.VarChanged_encoding) - - def remove_var_callbacks(self): - "Remove callbacks to prevent memory leaks." - for var in ( - self.fontSize, self.fontName, self.fontBold, - self.spaceNum, self.colour, self.builtinTheme, - self.customTheme, self.themeIsBuiltin, self.highlightTarget, - self.keyBinding, self.builtinKeys, self.customKeys, - self.keysAreBuiltin, self.winWidth, self.winHeight, - self.startupEdit, self.autoSave, self.encoding,): - var.trace_vdelete('w', var.trace_vinfo()[0][1]) - - def VarChanged_font(self, *params): - '''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.fontName.get() - self.AddChangedItem('main', 'EditorWindow', 'font', value) - value = self.fontSize.get() - self.AddChangedItem('main', 'EditorWindow', 'font-size', value) - value = self.fontBold.get() - self.AddChangedItem('main', 'EditorWindow', 'font-bold', value) - - def VarChanged_spaceNum(self, *params): - value = self.spaceNum.get() - self.AddChangedItem('main', 'Indent', 'num-spaces', value) - - def VarChanged_colour(self, *params): - self.OnNewColourSet() - - def VarChanged_builtinTheme(self, *params): - value = self.builtinTheme.get() - if value == 'IDLE Dark': - if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New': - self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic') - self.AddChangedItem('main', 'Theme', 'name2', value) - self.new_custom_theme.config(text='New theme, see Help', - fg='#500000') - else: - self.AddChangedItem('main', 'Theme', 'name', value) - self.AddChangedItem('main', 'Theme', 'name2', '') - self.new_custom_theme.config(text='', fg='black') - self.PaintThemeSample() - - def VarChanged_customTheme(self, *params): - value = self.customTheme.get() - if value != '- no custom themes -': - self.AddChangedItem('main', 'Theme', 'name', value) - self.PaintThemeSample() - - def VarChanged_themeIsBuiltin(self, *params): - value = self.themeIsBuiltin.get() - self.AddChangedItem('main', 'Theme', 'default', value) - if value: - self.VarChanged_builtinTheme() - else: - self.VarChanged_customTheme() - - def VarChanged_highlightTarget(self, *params): - self.SetHighlightTarget() - - def VarChanged_keyBinding(self, *params): - value = self.keyBinding.get() - keySet = self.customKeys.get() - event = self.listBindings.get(ANCHOR).split()[0] - if idleConf.IsCoreBinding(event): - #this is a core keybinding - self.AddChangedItem('keys', keySet, event, value) - else: #this is an extension key binding - extName = idleConf.GetExtnNameForEvent(event) - extKeybindSection = extName + '_cfgBindings' - self.AddChangedItem('extensions', extKeybindSection, event, value) - - def VarChanged_builtinKeys(self, *params): - value = self.builtinKeys.get() - self.AddChangedItem('main', 'Keys', 'name', value) - self.LoadKeysList(value) - - def VarChanged_customKeys(self, *params): - value = self.customKeys.get() - if value != '- no custom keys -': - self.AddChangedItem('main', 'Keys', 'name', value) - self.LoadKeysList(value) - - def VarChanged_keysAreBuiltin(self, *params): - value = self.keysAreBuiltin.get() - self.AddChangedItem('main', 'Keys', 'default', value) - if value: - self.VarChanged_builtinKeys() - else: - self.VarChanged_customKeys() - - def VarChanged_winWidth(self, *params): - value = self.winWidth.get() - self.AddChangedItem('main', 'EditorWindow', 'width', value) - - def VarChanged_winHeight(self, *params): - value = self.winHeight.get() - self.AddChangedItem('main', 'EditorWindow', 'height', value) - - def VarChanged_startupEdit(self, *params): - value = self.startupEdit.get() - self.AddChangedItem('main', 'General', 'editor-on-startup', value) - - def VarChanged_autoSave(self, *params): - value = self.autoSave.get() - self.AddChangedItem('main', 'General', 'autosave', value) - - def VarChanged_encoding(self, *params): - value = self.encoding.get() - self.AddChangedItem('main', 'EditorWindow', 'encoding', value) - - def ResetChangedItems(self): - #When any config item is changed in this dialog, an entry - #should be made in the relevant section (config type) of this - #dictionary. The key should be the config file section name and the - #value a dictionary, whose key:value pairs are item=value pairs for - #that config file section. - self.changedItems = {'main':{}, 'highlight':{}, 'keys':{}, - 'extensions':{}} - - def AddChangedItem(self, typ, section, item, value): - value = str(value) #make sure we use a string - if section not in self.changedItems[typ]: - self.changedItems[typ][section] = {} - self.changedItems[typ][section][item] = value - - def GetDefaultItems(self): - dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} - for configType in dItems: - sections = idleConf.GetSectionList('default', configType) - for section in sections: - dItems[configType][section] = {} - options = idleConf.defaultCfg[configType].GetOptionList(section) - for option in options: - dItems[configType][section][option] = ( - idleConf.defaultCfg[configType].Get(section, option)) - return dItems - - def SetThemeType(self): - if self.themeIsBuiltin.get(): - self.optMenuThemeBuiltin.config(state=NORMAL) - self.optMenuThemeCustom.config(state=DISABLED) - self.buttonDeleteCustomTheme.config(state=DISABLED) - else: - self.optMenuThemeBuiltin.config(state=DISABLED) - self.radioThemeCustom.config(state=NORMAL) - self.optMenuThemeCustom.config(state=NORMAL) - self.buttonDeleteCustomTheme.config(state=NORMAL) - - def SetKeysType(self): - if self.keysAreBuiltin.get(): - self.optMenuKeysBuiltin.config(state=NORMAL) - self.optMenuKeysCustom.config(state=DISABLED) - self.buttonDeleteCustomKeys.config(state=DISABLED) - else: - self.optMenuKeysBuiltin.config(state=DISABLED) - self.radioKeysCustom.config(state=NORMAL) - self.optMenuKeysCustom.config(state=NORMAL) - self.buttonDeleteCustomKeys.config(state=NORMAL) - - def GetNewKeys(self): - listIndex = self.listBindings.index(ANCHOR) - binding = self.listBindings.get(listIndex) - bindName = binding.split()[0] #first part, up to first space - if self.keysAreBuiltin.get(): - currentKeySetName = self.builtinKeys.get() - else: - currentKeySetName = self.customKeys.get() - currentBindings = idleConf.GetCurrentKeySet() - if currentKeySetName in self.changedItems['keys']: #unsaved changes - keySetChanges = self.changedItems['keys'][currentKeySetName] - for event in keySetChanges: - currentBindings[event] = keySetChanges[event].split() - currentKeySequences = list(currentBindings.values()) - newKeys = GetKeysDialog(self, 'Get New Keys', bindName, - currentKeySequences).result - if newKeys: #new keys were specified - if self.keysAreBuiltin.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.') - newKeySet = self.GetNewKeysName(message) - if not newKeySet: #user cancelled custom key set creation - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - return - else: #create new custom key set based on previously active key set - self.CreateNewKeySet(newKeySet) - self.listBindings.delete(listIndex) - self.listBindings.insert(listIndex, bindName+' - '+newKeys) - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - self.keyBinding.set(newKeys) - else: - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - - def GetNewKeysName(self, message): - usedNames = (idleConf.GetSectionList('user', 'keys') + - idleConf.GetSectionList('default', 'keys')) - newKeySet = GetCfgSectionNameDialog( - self, 'New Custom Key Set', message, usedNames).result - return newKeySet - - def SaveAsNewKeySet(self): - newKeysName = self.GetNewKeysName('New Key Set Name:') - if newKeysName: - self.CreateNewKeySet(newKeysName) - - def KeyBindingSelected(self, event): - self.buttonNewKeys.config(state=NORMAL) - - def CreateNewKeySet(self, newKeySetName): - #creates new custom key set based on the previously active key set, - #and makes the new key set active - if self.keysAreBuiltin.get(): - prevKeySetName = self.builtinKeys.get() - else: - prevKeySetName = self.customKeys.get() - prevKeys = idleConf.GetCoreKeys(prevKeySetName) - newKeys = {} - for event in prevKeys: #add key set to changed items - eventName = event[2:-2] #trim off the angle brackets - binding = ' '.join(prevKeys[event]) - newKeys[eventName] = binding - #handle any unsaved changes to prev key set - if prevKeySetName in self.changedItems['keys']: - keySetChanges = self.changedItems['keys'][prevKeySetName] - for event in keySetChanges: - newKeys[event] = keySetChanges[event] - #save the new theme - self.SaveNewKeySet(newKeySetName, newKeys) - #change gui over to the new key set - customKeyList = idleConf.GetSectionList('user', 'keys') - customKeyList.sort() - self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName) - self.keysAreBuiltin.set(0) - self.SetKeysType() - - def LoadKeysList(self, keySetName): - reselect = 0 - newKeySet = 0 - if self.listBindings.curselection(): - reselect = 1 - listIndex = self.listBindings.index(ANCHOR) - keySet = idleConf.GetKeySet(keySetName) - bindNames = list(keySet.keys()) - bindNames.sort() - self.listBindings.delete(0, END) - for bindName in bindNames: - key = ' '.join(keySet[bindName]) #make key(s) into a string - bindName = bindName[2:-2] #trim off the angle brackets - if keySetName in self.changedItems['keys']: - #handle any unsaved changes to this key set - if bindName in self.changedItems['keys'][keySetName]: - key = self.changedItems['keys'][keySetName][bindName] - self.listBindings.insert(END, bindName+' - '+key) - if reselect: - self.listBindings.see(listIndex) - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - - def DeleteCustomKeys(self): - keySetName=self.customKeys.get() - delmsg = 'Are you sure you wish to delete the key set %r ?' - if not tkMessageBox.askyesno( - 'Delete Key Set', delmsg % keySetName, parent=self): - return - #remove key set from config - idleConf.userCfg['keys'].remove_section(keySetName) - if keySetName in self.changedItems['keys']: - del(self.changedItems['keys'][keySetName]) - #write changes - idleConf.userCfg['keys'].Save() - #reload user key set list - itemList = idleConf.GetSectionList('user', 'keys') - itemList.sort() - if not itemList: - self.radioKeysCustom.config(state=DISABLED) - self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -') - else: - self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) - #revert to default key set - self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default')) - self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) - #user can't back out of these changes, they must be applied now - self.Apply() - self.SetKeysType() - - def DeleteCustomTheme(self): - themeName = self.customTheme.get() - delmsg = 'Are you sure you wish to delete the theme %r ?' - if not tkMessageBox.askyesno( - 'Delete Theme', delmsg % themeName, parent=self): - return - #remove theme from config - idleConf.userCfg['highlight'].remove_section(themeName) - if themeName in self.changedItems['highlight']: - del(self.changedItems['highlight'][themeName]) - #write changes - idleConf.userCfg['highlight'].Save() - #reload user theme list - itemList = idleConf.GetSectionList('user', 'highlight') - itemList.sort() - if not itemList: - self.radioThemeCustom.config(state=DISABLED) - self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -') - else: - self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) - #revert to default theme - self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) - self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) - #user can't back out of these changes, they must be applied now - self.Apply() - self.SetThemeType() - - def GetColour(self): - target = self.highlightTarget.get() - prevColour = self.frameColourSet.cget('bg') - rgbTuplet, colourString = tkColorChooser.askcolor( - parent=self, title='Pick new colour for : '+target, - initialcolor=prevColour) - if colourString and (colourString != prevColour): - #user didn't cancel, and they chose a new colour - if self.themeIsBuiltin.get(): #current theme is a built-in - message = ('Your changes will be saved as a new Custom Theme. ' - 'Enter a name for your new Custom Theme below.') - newTheme = self.GetNewThemeName(message) - if not newTheme: #user cancelled custom theme creation - return - else: #create new custom theme based on previously active theme - self.CreateNewTheme(newTheme) - self.colour.set(colourString) - else: #current theme is user defined - self.colour.set(colourString) - - def OnNewColourSet(self): - newColour=self.colour.get() - self.frameColourSet.config(bg=newColour) #set sample - plane ='foreground' if self.fgHilite.get() else 'background' - sampleElement = self.themeElements[self.highlightTarget.get()][0] - self.textHighlightSample.tag_config(sampleElement, **{plane:newColour}) - theme = self.customTheme.get() - themeElement = sampleElement + '-' + plane - self.AddChangedItem('highlight', theme, themeElement, newColour) - - def GetNewThemeName(self, message): - usedNames = (idleConf.GetSectionList('user', 'highlight') + - idleConf.GetSectionList('default', 'highlight')) - newTheme = GetCfgSectionNameDialog( - self, 'New Custom Theme', message, usedNames).result - return newTheme - - def SaveAsNewTheme(self): - newThemeName = self.GetNewThemeName('New Theme Name:') - if newThemeName: - self.CreateNewTheme(newThemeName) - - def CreateNewTheme(self, newThemeName): - #creates new custom theme based on the previously active theme, - #and makes the new theme active - if self.themeIsBuiltin.get(): - themeType = 'default' - themeName = self.builtinTheme.get() - else: - themeType = 'user' - themeName = self.customTheme.get() - newTheme = idleConf.GetThemeDict(themeType, themeName) - #apply any of the old theme's unsaved changes to the new theme - if themeName in self.changedItems['highlight']: - themeChanges = self.changedItems['highlight'][themeName] - for element in themeChanges: - newTheme[element] = themeChanges[element] - #save the new theme - self.SaveNewTheme(newThemeName, newTheme) - #change gui over to the new theme - customThemeList = idleConf.GetSectionList('user', 'highlight') - customThemeList.sort() - self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName) - self.themeIsBuiltin.set(0) - self.SetThemeType() - - def OnListFontButtonRelease(self, event): - font = self.listFontName.get(ANCHOR) - self.fontName.set(font.lower()) - self.SetFontSample() - - def SetFontSample(self, event=None): - fontName = self.fontName.get() - fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL - newFont = (fontName, self.fontSize.get(), fontWeight) - self.labelFontSample.config(font=newFont) - self.textHighlightSample.configure(font=newFont) - - def SetHighlightTarget(self): - if self.highlightTarget.get() == 'Cursor': #bg not possible - self.radioFg.config(state=DISABLED) - self.radioBg.config(state=DISABLED) - self.fgHilite.set(1) - else: #both fg and bg can be set - self.radioFg.config(state=NORMAL) - self.radioBg.config(state=NORMAL) - self.fgHilite.set(1) - self.SetColourSample() - - def SetColourSampleBinding(self, *args): - self.SetColourSample() - - def SetColourSample(self): - #set the colour smaple area - tag = self.themeElements[self.highlightTarget.get()][0] - plane = 'foreground' if self.fgHilite.get() else 'background' - colour = self.textHighlightSample.tag_cget(tag, plane) - self.frameColourSet.config(bg=colour) - - def PaintThemeSample(self): - if self.themeIsBuiltin.get(): #a default theme - theme = self.builtinTheme.get() - else: #a user theme - theme = self.customTheme.get() - for elementTitle in self.themeElements: - element = self.themeElements[elementTitle][0] - colours = idleConf.GetHighlight(theme, element) - if element == 'cursor': #cursor sample needs special painting - colours['background'] = idleConf.GetHighlight( - theme, 'normal', fgBg='bg') - #handle any unsaved changes to this theme - if theme in self.changedItems['highlight']: - themeDict = self.changedItems['highlight'][theme] - if element + '-foreground' in themeDict: - colours['foreground'] = themeDict[element + '-foreground'] - if element + '-background' in themeDict: - colours['background'] = themeDict[element + '-background'] - self.textHighlightSample.tag_config(element, **colours) - self.SetColourSample() - - def HelpSourceSelected(self, event): - self.SetHelpListButtonStates() - - def SetHelpListButtonStates(self): - if self.listHelp.size() < 1: #no entries in list - self.buttonHelpListEdit.config(state=DISABLED) - self.buttonHelpListRemove.config(state=DISABLED) - else: #there are some entries - if self.listHelp.curselection(): #there currently is a selection - self.buttonHelpListEdit.config(state=NORMAL) - self.buttonHelpListRemove.config(state=NORMAL) - else: #there currently is not a selection - self.buttonHelpListEdit.config(state=DISABLED) - self.buttonHelpListRemove.config(state=DISABLED) - - def HelpListItemAdd(self): - helpSource = GetHelpSourceDialog(self, 'New Help Source').result - if helpSource: - self.userHelpList.append((helpSource[0], helpSource[1])) - self.listHelp.insert(END, helpSource[0]) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() - - def HelpListItemEdit(self): - itemIndex = self.listHelp.index(ANCHOR) - helpSource = self.userHelpList[itemIndex] - newHelpSource = GetHelpSourceDialog( - self, 'Edit Help Source', menuItem=helpSource[0], - filePath=helpSource[1]).result - if (not newHelpSource) or (newHelpSource == helpSource): - return #no changes - self.userHelpList[itemIndex] = newHelpSource - self.listHelp.delete(itemIndex) - self.listHelp.insert(itemIndex, newHelpSource[0]) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() - - def HelpListItemRemove(self): - itemIndex = self.listHelp.index(ANCHOR) - del(self.userHelpList[itemIndex]) - self.listHelp.delete(itemIndex) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() - - def UpdateUserHelpChangedItems(self): - "Clear and rebuild the HelpFiles section in self.changedItems" - self.changedItems['main']['HelpFiles'] = {} - for num in range(1, len(self.userHelpList) + 1): - self.AddChangedItem( - 'main', 'HelpFiles', str(num), - ';'.join(self.userHelpList[num-1][:2])) - - def LoadFontCfg(self): - ##base editor font selection list - fonts = list(tkFont.families(self)) - fonts.sort() - for font in fonts: - self.listFontName.insert(END, font) - configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow') - fontName = configuredFont[0].lower() - fontSize = configuredFont[1] - fontBold = configuredFont[2]=='bold' - self.fontName.set(fontName) - lc_fonts = [s.lower() for s in fonts] - try: - currentFontIndex = lc_fonts.index(fontName) - self.listFontName.see(currentFontIndex) - self.listFontName.select_set(currentFontIndex) - self.listFontName.select_anchor(currentFontIndex) - except ValueError: - pass - ##font size dropdown - self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', - '14', '16', '18', '20', '22'), fontSize ) - ##fontWeight - self.fontBold.set(fontBold) - ##font sample - self.SetFontSample() - - def LoadTabCfg(self): - ##indent sizes - spaceNum = idleConf.GetOption( - 'main', 'Indent', 'num-spaces', default=4, type='int') - self.spaceNum.set(spaceNum) - - def LoadThemeCfg(self): - ##current theme type radiobutton - self.themeIsBuiltin.set(idleConf.GetOption( - 'main', 'Theme', 'default', type='bool', default=1)) - ##currently set theme - currentOption = idleConf.CurrentTheme() - ##load available theme option menus - if self.themeIsBuiltin.get(): #default theme selected - itemList = idleConf.GetSectionList('default', 'highlight') - itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('user', 'highlight') - itemList.sort() - if not itemList: - self.radioThemeCustom.config(state=DISABLED) - self.customTheme.set('- no custom themes -') - else: - self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) - else: #user theme selected - itemList = idleConf.GetSectionList('user', 'highlight') - itemList.sort() - self.optMenuThemeCustom.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('default', 'highlight') - itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) - self.SetThemeType() - ##load theme element option menu - themeNames = list(self.themeElements.keys()) - themeNames.sort(key=lambda x: self.themeElements[x][1]) - self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0]) - self.PaintThemeSample() - self.SetHighlightTarget() - - def LoadKeyCfg(self): - ##current keys type radiobutton - self.keysAreBuiltin.set(idleConf.GetOption( - 'main', 'Keys', 'default', type='bool', default=1)) - ##currently set keys - currentOption = idleConf.CurrentKeys() - ##load available keyset option menus - if self.keysAreBuiltin.get(): #default theme selected - itemList = idleConf.GetSectionList('default', 'keys') - itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('user', 'keys') - itemList.sort() - if not itemList: - self.radioKeysCustom.config(state=DISABLED) - self.customKeys.set('- no custom keys -') - else: - self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) - else: #user key set selected - itemList = idleConf.GetSectionList('user', 'keys') - itemList.sort() - self.optMenuKeysCustom.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('default', 'keys') - itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0]) - self.SetKeysType() - ##load keyset element list - keySetName = idleConf.CurrentKeys() - self.LoadKeysList(keySetName) - - def LoadGeneralCfg(self): - #startup state - self.startupEdit.set(idleConf.GetOption( - 'main', 'General', 'editor-on-startup', default=1, type='bool')) - #autosave state - self.autoSave.set(idleConf.GetOption( - 'main', 'General', 'autosave', default=0, type='bool')) - #initial window size - self.winWidth.set(idleConf.GetOption( - 'main', 'EditorWindow', 'width', type='int')) - self.winHeight.set(idleConf.GetOption( - 'main', 'EditorWindow', 'height', type='int')) - # default source encoding - self.encoding.set(idleConf.GetOption( - 'main', 'EditorWindow', 'encoding', default='none')) - # additional help sources - self.userHelpList = idleConf.GetAllExtraHelpSourcesList() - for helpItem in self.userHelpList: - self.listHelp.insert(END, helpItem[0]) - self.SetHelpListButtonStates() - - def LoadConfigs(self): - """ - load configuration from default and user config files and populate - the widgets on the config dialog pages. - """ - ### fonts / tabs page - self.LoadFontCfg() - self.LoadTabCfg() - ### highlighting page - self.LoadThemeCfg() - ### keys page - self.LoadKeyCfg() - ### general page - self.LoadGeneralCfg() - # note: extension page handled separately - - def SaveNewKeySet(self, keySetName, keySet): - """ - save a newly created core key set. - keySetName - string, the name of the new key set - keySet - dictionary containing the new key set - """ - if not idleConf.userCfg['keys'].has_section(keySetName): - idleConf.userCfg['keys'].add_section(keySetName) - for event in keySet: - value = keySet[event] - idleConf.userCfg['keys'].SetOption(keySetName, event, value) - - def SaveNewTheme(self, themeName, theme): - """ - save a newly created theme. - themeName - string, the name of the new theme - theme - dictionary containing the new theme - """ - if not idleConf.userCfg['highlight'].has_section(themeName): - idleConf.userCfg['highlight'].add_section(themeName) - for element in theme: - value = theme[element] - idleConf.userCfg['highlight'].SetOption(themeName, element, value) - - def SetUserValue(self, configType, section, item, value): - if idleConf.defaultCfg[configType].has_option(section, item): - if idleConf.defaultCfg[configType].Get(section, item) == value: - #the setting equals a default setting, remove it from user cfg - return idleConf.userCfg[configType].RemoveOption(section, item) - #if we got here set the option - return idleConf.userCfg[configType].SetOption(section, item, value) - - def SaveAllChangedConfigs(self): - "Save configuration changes to the user config file." - idleConf.userCfg['main'].Save() - for configType in self.changedItems: - cfgTypeHasChanges = False - for section in self.changedItems[configType]: - if section == 'HelpFiles': - #this section gets completely replaced - idleConf.userCfg['main'].remove_section('HelpFiles') - cfgTypeHasChanges = True - for item in self.changedItems[configType][section]: - value = self.changedItems[configType][section][item] - if self.SetUserValue(configType, section, item, value): - cfgTypeHasChanges = True - if cfgTypeHasChanges: - idleConf.userCfg[configType].Save() - for configType in ['keys', 'highlight']: - # save these even if unchanged! - idleConf.userCfg[configType].Save() - self.ResetChangedItems() #clear the changed items dict - self.save_all_changed_extensions() # uses a different mechanism - - def DeactivateCurrentConfig(self): - #Before a config is saved, some cleanup of current - #config must be done - remove the previous keybindings - winInstances = self.parent.instance_dict.keys() - for instance in winInstances: - instance.RemoveKeybindings() - - def ActivateConfigChanges(self): - "Dynamically apply configuration changes" - winInstances = self.parent.instance_dict.keys() - for instance in winInstances: - instance.ResetColorizer() - instance.ResetFont() - instance.set_notabs_indentwidth() - instance.ApplyKeybindings() - instance.reset_help_menu_entries() - - def Cancel(self): - self.destroy() - - def Ok(self): - self.Apply() - self.destroy() - - def Apply(self): - self.DeactivateCurrentConfig() - self.SaveAllChangedConfigs() - self.ActivateConfigChanges() - - def Help(self): - page = self.tabPages._current_page - view_text(self, title='Help for IDLE preferences', - text=help_common+help_pages.get(page, '')) - - def CreatePageExtensions(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. - """ - parent = self.parent - frame = self.tabPages.pages['Extensions'].frame - 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('<>', 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) - - 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.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): - 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, 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 - - def set_extension_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.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.""" - 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() - - -help_common = '''\ -When you click either the Apply or Ok buttons, settings in this -dialog that are different from IDLE's default are saved in -a .idlerc directory in your home directory. Except as noted, -these changes apply to all versions of IDLE installed on this -machine. Some do not take affect until IDLE is restarted. -[Cancel] only cancels changes made since the last save. -''' -help_pages = { - 'Highlighting':''' -Highlighting: -The IDLE Dark color theme is new in October 2015. It can only -be used with older IDLE releases if it is saved as a custom -theme, with a different name. -''' -} - - -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 - - -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, width=240) - 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) - interior.bind('', _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_canvas) - - return - - -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) -- cgit v1.2.1 From 045c6ff433dd1684b0d233488e5496bdafa1642b Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 14 Jun 2016 00:55:09 -0400 Subject: Issue #27245: revert temporary rename --- Lib/idlelib/configdialog.py | 1439 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1439 insertions(+) create mode 100644 Lib/idlelib/configdialog.py (limited to 'Lib/idlelib/configdialog.py') diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py new file mode 100644 index 0000000000..d19749a07e --- /dev/null +++ b/Lib/idlelib/configdialog.py @@ -0,0 +1,1439 @@ +"""IDLE Configuration Dialog: support user customization of IDLE by GUI + +Customize font faces, sizes, and colorization attributes. Set indentation +defaults. Customize keybindings. Colorization and keybindings can be +saved as user defined sets. Select startup options including shell/editor +and default window size. Define additional help sources. + +Note that tab width in IDLE is currently fixed at eight due to Tk issues. +Refer to comments in EditorWindow autoindent code for details. + +""" +from tkinter import * +from tkinter.ttk import Scrollbar +import tkinter.messagebox as tkMessageBox +import tkinter.colorchooser as tkColorChooser +import tkinter.font as tkFont + +from idlelib.config import idleConf +from idlelib.dynoption import DynOptionMenu +from idlelib.config_key import GetKeysDialog +from idlelib.config_sec import GetCfgSectionNameDialog +from idlelib.config_help import GetHelpSourceDialog +from idlelib.tabbedpages import TabbedPageSet +from idlelib.textview import view_text +from idlelib import macosx + +class ConfigDialog(Toplevel): + + def __init__(self, parent, title='', _htest=False, _utest=False): + """ + _htest - bool, change box location when running htest + _utest - bool, don't wait_window when running unittest + """ + Toplevel.__init__(self, parent) + self.parent = parent + if _htest: + parent.instance_dict = {} + self.wm_withdraw() + + self.configure(borderwidth=5) + self.title(title or 'IDLE Preferences') + self.geometry( + "+%d+%d" % (parent.winfo_rootx() + 20, + parent.winfo_rooty() + (30 if not _htest else 150))) + #Theme Elements. Each theme element key is its display name. + #The first value of the tuple is the sample area tag name. + #The second value is the display name list sort index. + self.themeElements={ + 'Normal Text': ('normal', '00'), + 'Python Keywords': ('keyword', '01'), + 'Python Definitions': ('definition', '02'), + 'Python Builtins': ('builtin', '03'), + 'Python Comments': ('comment', '04'), + 'Python Strings': ('string', '05'), + 'Selected Text': ('hilite', '06'), + 'Found Text': ('hit', '07'), + 'Cursor': ('cursor', '08'), + 'Editor Breakpoint': ('break', '09'), + 'Shell Normal Text': ('console', '10'), + 'Shell Error Text': ('error', '11'), + 'Shell Stdout Text': ('stdout', '12'), + 'Shell Stderr Text': ('stderr', '13'), + } + self.ResetChangedItems() #load initial values in changed items dict + self.CreateWidgets() + self.resizable(height=FALSE, width=FALSE) + self.transient(parent) + self.grab_set() + self.protocol("WM_DELETE_WINDOW", self.Cancel) + self.tabPages.focus_set() + #key bindings for this dialog + #self.bind('', self.Cancel) #dismiss dialog, no save + #self.bind('', self.Apply) #apply changes, save + #self.bind('', self.Help) #context help + self.LoadConfigs() + self.AttachVarCallbacks() #avoid callbacks during LoadConfigs + + if not _utest: + self.wm_deiconify() + self.wait_window() + + def CreateWidgets(self): + self.tabPages = TabbedPageSet(self, + page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General', + 'Extensions']) + self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH) + self.CreatePageFontTab() + self.CreatePageHighlight() + self.CreatePageKeys() + self.CreatePageGeneral() + self.CreatePageExtensions() + self.create_action_buttons().pack(side=BOTTOM) + + def create_action_buttons(self): + if macosx.isAquaTk(): + # Changing the default padding on OSX results in unreadable + # text in the buttons + paddingArgs = {} + else: + paddingArgs = {'padx':6, 'pady':3} + outer = Frame(self, pady=2) + buttons = Frame(outer, pady=2) + for txt, cmd in ( + ('Ok', self.Ok), + ('Apply', self.Apply), + ('Cancel', self.Cancel), + ('Help', self.Help)): + Button(buttons, text=txt, command=cmd, takefocus=FALSE, + **paddingArgs).pack(side=LEFT, padx=5) + # 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) + self.fontBold = BooleanVar(parent) + self.fontName = StringVar(parent) + self.spaceNum = IntVar(parent) + self.editFont = tkFont.Font(parent, ('courier', 10, 'normal')) + + ##widget creation + #body frame + frame = self.tabPages.pages['Fonts/Tabs'].frame + #body section frames + frameFont = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ') + frameIndent = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ') + #frameFont + frameFontName = Frame(frameFont) + frameFontParam = Frame(frameFont) + labelFontNameTitle = Label( + frameFontName, justify=LEFT, text='Font Face :') + self.listFontName = Listbox( + frameFontName, height=5, takefocus=FALSE, exportselection=FALSE) + self.listFontName.bind( + '', self.OnListFontButtonRelease) + scrollFont = Scrollbar(frameFontName) + scrollFont.config(command=self.listFontName.yview) + self.listFontName.config(yscrollcommand=scrollFont.set) + labelFontSizeTitle = Label(frameFontParam, text='Size :') + self.optMenuFontSize = DynOptionMenu( + frameFontParam, self.fontSize, None, command=self.SetFontSample) + checkFontBold = Checkbutton( + frameFontParam, variable=self.fontBold, onvalue=1, + offvalue=0, text='Bold', command=self.SetFontSample) + frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1) + self.labelFontSample = Label( + frameFontSample, justify=LEFT, font=self.editFont, + text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]') + #frameIndent + frameIndentSize = Frame(frameIndent) + labelSpaceNumTitle = Label( + frameIndentSize, justify=LEFT, + text='Python Standard: 4 Spaces!') + self.scaleSpaceNum = Scale( + frameIndentSize, variable=self.spaceNum, + orient='horizontal', tickinterval=2, from_=2, to=16) + + #widget packing + #body + frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y) + #frameFont + frameFontName.pack(side=TOP, padx=5, pady=5, fill=X) + frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X) + labelFontNameTitle.pack(side=TOP, anchor=W) + self.listFontName.pack(side=LEFT, expand=TRUE, fill=X) + scrollFont.pack(side=LEFT, fill=Y) + labelFontSizeTitle.pack(side=LEFT, anchor=W) + self.optMenuFontSize.pack(side=LEFT, anchor=W) + checkFontBold.pack(side=LEFT, anchor=W, padx=20) + frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + self.labelFontSample.pack(expand=TRUE, fill=BOTH) + #frameIndent + frameIndentSize.pack(side=TOP, fill=X) + labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5) + self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X) + return frame + + def CreatePageHighlight(self): + parent = self.parent + self.builtinTheme = StringVar(parent) + self.customTheme = StringVar(parent) + self.fgHilite = BooleanVar(parent) + self.colour = StringVar(parent) + self.fontName = StringVar(parent) + self.themeIsBuiltin = BooleanVar(parent) + self.highlightTarget = StringVar(parent) + + ##widget creation + #body frame + frame = self.tabPages.pages['Highlighting'].frame + #body section frames + frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Custom Highlighting ') + frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Highlighting Theme ') + #frameCustom + self.textHighlightSample=Text( + frameCustom, relief=SOLID, borderwidth=1, + font=('courier', 12, ''), cursor='hand2', width=21, height=11, + takefocus=FALSE, highlightthickness=0, wrap=NONE) + text=self.textHighlightSample + text.bind('', lambda e: 'break') + text.bind('', lambda e: 'break') + textAndTags=( + ('#you can click here', 'comment'), ('\n', 'normal'), + ('#to choose items', 'comment'), ('\n', 'normal'), + ('def', 'keyword'), (' ', 'normal'), + ('func', 'definition'), ('(param):\n ', 'normal'), + ('"""string"""', 'string'), ('\n var0 = ', 'normal'), + ("'string'", 'string'), ('\n var1 = ', 'normal'), + ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), + ("'found'", 'hit'), ('\n var3 = ', 'normal'), + ('list', 'builtin'), ('(', 'normal'), + ('None', 'keyword'), (')\n', 'normal'), + (' breakpoint("line")', 'break'), ('\n\n', 'normal'), + (' error ', 'error'), (' ', 'normal'), + ('cursor |', 'cursor'), ('\n ', 'normal'), + ('shell', 'console'), (' ', 'normal'), + ('stdout', 'stdout'), (' ', 'normal'), + ('stderr', 'stderr'), ('\n', 'normal')) + for txTa in textAndTags: + text.insert(END, txTa[0], txTa[1]) + for element in self.themeElements: + def tem(event, elem=element): + event.widget.winfo_toplevel().highlightTarget.set(elem) + text.tag_bind( + self.themeElements[element][0], '', tem) + text.config(state=DISABLED) + self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1) + frameFgBg = Frame(frameCustom) + buttonSetColour = Button( + self.frameColourSet, text='Choose Colour for :', + command=self.GetColour, highlightthickness=0) + self.optMenuHighlightTarget = DynOptionMenu( + self.frameColourSet, self.highlightTarget, None, + highlightthickness=0) #, command=self.SetHighlightTargetBinding + self.radioFg = Radiobutton( + frameFgBg, variable=self.fgHilite, value=1, + text='Foreground', command=self.SetColourSampleBinding) + self.radioBg=Radiobutton( + frameFgBg, variable=self.fgHilite, value=0, + text='Background', command=self.SetColourSampleBinding) + self.fgHilite.set(1) + buttonSaveCustomTheme = Button( + frameCustom, text='Save as New Custom Theme', + command=self.SaveAsNewTheme) + #frameTheme + labelTypeTitle = Label(frameTheme, text='Select : ') + self.radioThemeBuiltin = Radiobutton( + frameTheme, variable=self.themeIsBuiltin, value=1, + command=self.SetThemeType, text='a Built-in Theme') + self.radioThemeCustom = Radiobutton( + frameTheme, variable=self.themeIsBuiltin, value=0, + command=self.SetThemeType, text='a Custom Theme') + self.optMenuThemeBuiltin = DynOptionMenu( + frameTheme, self.builtinTheme, None, command=None) + self.optMenuThemeCustom=DynOptionMenu( + frameTheme, self.customTheme, None, command=None) + self.buttonDeleteCustomTheme=Button( + frameTheme, text='Delete Custom Theme', + command=self.DeleteCustomTheme) + self.new_custom_theme = Label(frameTheme, bd=2) + + ##widget packing + #body + frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y) + #frameCustom + self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) + frameFgBg.pack(side=TOP, padx=5, pady=0) + self.textHighlightSample.pack( + side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) + self.optMenuHighlightTarget.pack( + side=TOP, expand=TRUE, fill=X, padx=8, pady=3) + self.radioFg.pack(side=LEFT, anchor=E) + self.radioBg.pack(side=RIGHT, anchor=W) + buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5) + #frameTheme + labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5) + self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5) + self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2) + self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) + self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) + self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5) + self.new_custom_theme.pack(side=TOP, fill=X, pady=5) + return frame + + def CreatePageKeys(self): + parent = self.parent + self.bindingTarget = StringVar(parent) + self.builtinKeys = StringVar(parent) + self.customKeys = StringVar(parent) + self.keysAreBuiltin = BooleanVar(parent) + self.keyBinding = StringVar(parent) + + ##widget creation + #body frame + frame = self.tabPages.pages['Keys'].frame + #body section frames + frameCustom = LabelFrame( + frame, borderwidth=2, relief=GROOVE, + text=' Custom Key Bindings ') + frameKeySets = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Key Set ') + #frameCustom + frameTarget = Frame(frameCustom) + labelTargetTitle = Label(frameTarget, text='Action - Key(s)') + scrollTargetY = Scrollbar(frameTarget) + scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL) + self.listBindings = Listbox( + frameTarget, takefocus=FALSE, exportselection=FALSE) + self.listBindings.bind('', self.KeyBindingSelected) + scrollTargetY.config(command=self.listBindings.yview) + scrollTargetX.config(command=self.listBindings.xview) + self.listBindings.config(yscrollcommand=scrollTargetY.set) + self.listBindings.config(xscrollcommand=scrollTargetX.set) + self.buttonNewKeys = Button( + frameCustom, text='Get New Keys for Selection', + command=self.GetNewKeys, state=DISABLED) + #frameKeySets + frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0) + for i in range(2)] + self.radioKeysBuiltin = Radiobutton( + frames[0], variable=self.keysAreBuiltin, value=1, + command=self.SetKeysType, text='Use a Built-in Key Set') + self.radioKeysCustom = Radiobutton( + frames[0], variable=self.keysAreBuiltin, value=0, + command=self.SetKeysType, text='Use a Custom Key Set') + self.optMenuKeysBuiltin = DynOptionMenu( + frames[0], self.builtinKeys, None, command=None) + self.optMenuKeysCustom = DynOptionMenu( + frames[0], self.customKeys, None, command=None) + self.buttonDeleteCustomKeys = Button( + frames[1], text='Delete Custom Key Set', + command=self.DeleteCustomKeys) + buttonSaveCustomKeys = Button( + frames[1], text='Save as New Custom Key Set', + command=self.SaveAsNewKeySet) + + ##widget packing + #body + frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) + #frameCustom + self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5) + frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + #frame target + frameTarget.columnconfigure(0, weight=1) + frameTarget.rowconfigure(1, weight=1) + labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W) + self.listBindings.grid(row=1, column=0, sticky=NSEW) + scrollTargetY.grid(row=1, column=1, sticky=NS) + scrollTargetX.grid(row=2, column=0, sticky=EW) + #frameKeySets + self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS) + self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) + self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) + self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) + self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) + buttonSaveCustomKeys.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 CreatePageGeneral(self): + parent = self.parent + self.winWidth = StringVar(parent) + self.winHeight = StringVar(parent) + self.startupEdit = IntVar(parent) + self.autoSave = IntVar(parent) + self.encoding = StringVar(parent) + self.userHelpBrowser = BooleanVar(parent) + self.helpBrowser = StringVar(parent) + + #widget creation + #body + frame = self.tabPages.pages['General'].frame + #body section frames + frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Startup Preferences ') + frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Autosave Preferences ') + frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE) + frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Additional Help Sources ') + #frameRun + labelRunChoiceTitle = Label(frameRun, text='At Startup') + radioStartupEdit = Radiobutton( + frameRun, variable=self.startupEdit, value=1, + command=self.SetKeysType, text="Open Edit Window") + radioStartupShell = Radiobutton( + frameRun, variable=self.startupEdit, value=0, + command=self.SetKeysType, text='Open Shell Window') + #frameSave + labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ') + radioSaveAsk = Radiobutton( + frameSave, variable=self.autoSave, value=0, + command=self.SetKeysType, text="Prompt to Save") + radioSaveAuto = Radiobutton( + frameSave, variable=self.autoSave, value=1, + command=self.SetKeysType, text='No Prompt') + #frameWinSize + labelWinSizeTitle = Label( + frameWinSize, text='Initial Window Size (in characters)') + labelWinWidthTitle = Label(frameWinSize, text='Width') + entryWinWidth = Entry( + frameWinSize, textvariable=self.winWidth, width=3) + labelWinHeightTitle = Label(frameWinSize, text='Height') + entryWinHeight = Entry( + frameWinSize, textvariable=self.winHeight, width=3) + #frameHelp + frameHelpList = Frame(frameHelp) + frameHelpListButtons = Frame(frameHelpList) + scrollHelpList = Scrollbar(frameHelpList) + self.listHelp = Listbox( + frameHelpList, height=5, takefocus=FALSE, + exportselection=FALSE) + scrollHelpList.config(command=self.listHelp.yview) + self.listHelp.config(yscrollcommand=scrollHelpList.set) + self.listHelp.bind('', self.HelpSourceSelected) + self.buttonHelpListEdit = Button( + frameHelpListButtons, text='Edit', state=DISABLED, + width=8, command=self.HelpListItemEdit) + self.buttonHelpListAdd = Button( + frameHelpListButtons, text='Add', + width=8, command=self.HelpListItemAdd) + self.buttonHelpListRemove = Button( + frameHelpListButtons, text='Remove', state=DISABLED, + width=8, command=self.HelpListItemRemove) + + #widget packing + #body + frameRun.pack(side=TOP, padx=5, pady=5, fill=X) + frameSave.pack(side=TOP, padx=5, pady=5, fill=X) + frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X) + frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + #frameRun + labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5) + radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5) + #frameSave + labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5) + radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5) + #frameWinSize + labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5) + labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5) + entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5) + labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5) + #frameHelp + frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y) + frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y) + self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) + self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5) + self.buttonHelpListAdd.pack(side=TOP, anchor=W) + self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5) + return frame + + def AttachVarCallbacks(self): + self.fontSize.trace_variable('w', self.VarChanged_font) + self.fontName.trace_variable('w', self.VarChanged_font) + self.fontBold.trace_variable('w', self.VarChanged_font) + self.spaceNum.trace_variable('w', self.VarChanged_spaceNum) + self.colour.trace_variable('w', self.VarChanged_colour) + self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme) + self.customTheme.trace_variable('w', self.VarChanged_customTheme) + self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin) + self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget) + self.keyBinding.trace_variable('w', self.VarChanged_keyBinding) + self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys) + self.customKeys.trace_variable('w', self.VarChanged_customKeys) + self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin) + self.winWidth.trace_variable('w', self.VarChanged_winWidth) + self.winHeight.trace_variable('w', self.VarChanged_winHeight) + self.startupEdit.trace_variable('w', self.VarChanged_startupEdit) + self.autoSave.trace_variable('w', self.VarChanged_autoSave) + self.encoding.trace_variable('w', self.VarChanged_encoding) + + def remove_var_callbacks(self): + "Remove callbacks to prevent memory leaks." + for var in ( + self.fontSize, self.fontName, self.fontBold, + self.spaceNum, self.colour, self.builtinTheme, + self.customTheme, self.themeIsBuiltin, self.highlightTarget, + self.keyBinding, self.builtinKeys, self.customKeys, + self.keysAreBuiltin, self.winWidth, self.winHeight, + self.startupEdit, self.autoSave, self.encoding,): + var.trace_vdelete('w', var.trace_vinfo()[0][1]) + + def VarChanged_font(self, *params): + '''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.fontName.get() + self.AddChangedItem('main', 'EditorWindow', 'font', value) + value = self.fontSize.get() + self.AddChangedItem('main', 'EditorWindow', 'font-size', value) + value = self.fontBold.get() + self.AddChangedItem('main', 'EditorWindow', 'font-bold', value) + + def VarChanged_spaceNum(self, *params): + value = self.spaceNum.get() + self.AddChangedItem('main', 'Indent', 'num-spaces', value) + + def VarChanged_colour(self, *params): + self.OnNewColourSet() + + def VarChanged_builtinTheme(self, *params): + value = self.builtinTheme.get() + if value == 'IDLE Dark': + if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New': + self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic') + self.AddChangedItem('main', 'Theme', 'name2', value) + self.new_custom_theme.config(text='New theme, see Help', + fg='#500000') + else: + self.AddChangedItem('main', 'Theme', 'name', value) + self.AddChangedItem('main', 'Theme', 'name2', '') + self.new_custom_theme.config(text='', fg='black') + self.PaintThemeSample() + + def VarChanged_customTheme(self, *params): + value = self.customTheme.get() + if value != '- no custom themes -': + self.AddChangedItem('main', 'Theme', 'name', value) + self.PaintThemeSample() + + def VarChanged_themeIsBuiltin(self, *params): + value = self.themeIsBuiltin.get() + self.AddChangedItem('main', 'Theme', 'default', value) + if value: + self.VarChanged_builtinTheme() + else: + self.VarChanged_customTheme() + + def VarChanged_highlightTarget(self, *params): + self.SetHighlightTarget() + + def VarChanged_keyBinding(self, *params): + value = self.keyBinding.get() + keySet = self.customKeys.get() + event = self.listBindings.get(ANCHOR).split()[0] + if idleConf.IsCoreBinding(event): + #this is a core keybinding + self.AddChangedItem('keys', keySet, event, value) + else: #this is an extension key binding + extName = idleConf.GetExtnNameForEvent(event) + extKeybindSection = extName + '_cfgBindings' + self.AddChangedItem('extensions', extKeybindSection, event, value) + + def VarChanged_builtinKeys(self, *params): + value = self.builtinKeys.get() + self.AddChangedItem('main', 'Keys', 'name', value) + self.LoadKeysList(value) + + def VarChanged_customKeys(self, *params): + value = self.customKeys.get() + if value != '- no custom keys -': + self.AddChangedItem('main', 'Keys', 'name', value) + self.LoadKeysList(value) + + def VarChanged_keysAreBuiltin(self, *params): + value = self.keysAreBuiltin.get() + self.AddChangedItem('main', 'Keys', 'default', value) + if value: + self.VarChanged_builtinKeys() + else: + self.VarChanged_customKeys() + + def VarChanged_winWidth(self, *params): + value = self.winWidth.get() + self.AddChangedItem('main', 'EditorWindow', 'width', value) + + def VarChanged_winHeight(self, *params): + value = self.winHeight.get() + self.AddChangedItem('main', 'EditorWindow', 'height', value) + + def VarChanged_startupEdit(self, *params): + value = self.startupEdit.get() + self.AddChangedItem('main', 'General', 'editor-on-startup', value) + + def VarChanged_autoSave(self, *params): + value = self.autoSave.get() + self.AddChangedItem('main', 'General', 'autosave', value) + + def VarChanged_encoding(self, *params): + value = self.encoding.get() + self.AddChangedItem('main', 'EditorWindow', 'encoding', value) + + def ResetChangedItems(self): + #When any config item is changed in this dialog, an entry + #should be made in the relevant section (config type) of this + #dictionary. The key should be the config file section name and the + #value a dictionary, whose key:value pairs are item=value pairs for + #that config file section. + self.changedItems = {'main':{}, 'highlight':{}, 'keys':{}, + 'extensions':{}} + + def AddChangedItem(self, typ, section, item, value): + value = str(value) #make sure we use a string + if section not in self.changedItems[typ]: + self.changedItems[typ][section] = {} + self.changedItems[typ][section][item] = value + + def GetDefaultItems(self): + dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} + for configType in dItems: + sections = idleConf.GetSectionList('default', configType) + for section in sections: + dItems[configType][section] = {} + options = idleConf.defaultCfg[configType].GetOptionList(section) + for option in options: + dItems[configType][section][option] = ( + idleConf.defaultCfg[configType].Get(section, option)) + return dItems + + def SetThemeType(self): + if self.themeIsBuiltin.get(): + self.optMenuThemeBuiltin.config(state=NORMAL) + self.optMenuThemeCustom.config(state=DISABLED) + self.buttonDeleteCustomTheme.config(state=DISABLED) + else: + self.optMenuThemeBuiltin.config(state=DISABLED) + self.radioThemeCustom.config(state=NORMAL) + self.optMenuThemeCustom.config(state=NORMAL) + self.buttonDeleteCustomTheme.config(state=NORMAL) + + def SetKeysType(self): + if self.keysAreBuiltin.get(): + self.optMenuKeysBuiltin.config(state=NORMAL) + self.optMenuKeysCustom.config(state=DISABLED) + self.buttonDeleteCustomKeys.config(state=DISABLED) + else: + self.optMenuKeysBuiltin.config(state=DISABLED) + self.radioKeysCustom.config(state=NORMAL) + self.optMenuKeysCustom.config(state=NORMAL) + self.buttonDeleteCustomKeys.config(state=NORMAL) + + def GetNewKeys(self): + listIndex = self.listBindings.index(ANCHOR) + binding = self.listBindings.get(listIndex) + bindName = binding.split()[0] #first part, up to first space + if self.keysAreBuiltin.get(): + currentKeySetName = self.builtinKeys.get() + else: + currentKeySetName = self.customKeys.get() + currentBindings = idleConf.GetCurrentKeySet() + if currentKeySetName in self.changedItems['keys']: #unsaved changes + keySetChanges = self.changedItems['keys'][currentKeySetName] + for event in keySetChanges: + currentBindings[event] = keySetChanges[event].split() + currentKeySequences = list(currentBindings.values()) + newKeys = GetKeysDialog(self, 'Get New Keys', bindName, + currentKeySequences).result + if newKeys: #new keys were specified + if self.keysAreBuiltin.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.') + newKeySet = self.GetNewKeysName(message) + if not newKeySet: #user cancelled custom key set creation + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + return + else: #create new custom key set based on previously active key set + self.CreateNewKeySet(newKeySet) + self.listBindings.delete(listIndex) + self.listBindings.insert(listIndex, bindName+' - '+newKeys) + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + self.keyBinding.set(newKeys) + else: + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + + def GetNewKeysName(self, message): + usedNames = (idleConf.GetSectionList('user', 'keys') + + idleConf.GetSectionList('default', 'keys')) + newKeySet = GetCfgSectionNameDialog( + self, 'New Custom Key Set', message, usedNames).result + return newKeySet + + def SaveAsNewKeySet(self): + newKeysName = self.GetNewKeysName('New Key Set Name:') + if newKeysName: + self.CreateNewKeySet(newKeysName) + + def KeyBindingSelected(self, event): + self.buttonNewKeys.config(state=NORMAL) + + def CreateNewKeySet(self, newKeySetName): + #creates new custom key set based on the previously active key set, + #and makes the new key set active + if self.keysAreBuiltin.get(): + prevKeySetName = self.builtinKeys.get() + else: + prevKeySetName = self.customKeys.get() + prevKeys = idleConf.GetCoreKeys(prevKeySetName) + newKeys = {} + for event in prevKeys: #add key set to changed items + eventName = event[2:-2] #trim off the angle brackets + binding = ' '.join(prevKeys[event]) + newKeys[eventName] = binding + #handle any unsaved changes to prev key set + if prevKeySetName in self.changedItems['keys']: + keySetChanges = self.changedItems['keys'][prevKeySetName] + for event in keySetChanges: + newKeys[event] = keySetChanges[event] + #save the new theme + self.SaveNewKeySet(newKeySetName, newKeys) + #change gui over to the new key set + customKeyList = idleConf.GetSectionList('user', 'keys') + customKeyList.sort() + self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName) + self.keysAreBuiltin.set(0) + self.SetKeysType() + + def LoadKeysList(self, keySetName): + reselect = 0 + newKeySet = 0 + if self.listBindings.curselection(): + reselect = 1 + listIndex = self.listBindings.index(ANCHOR) + keySet = idleConf.GetKeySet(keySetName) + bindNames = list(keySet.keys()) + bindNames.sort() + self.listBindings.delete(0, END) + for bindName in bindNames: + key = ' '.join(keySet[bindName]) #make key(s) into a string + bindName = bindName[2:-2] #trim off the angle brackets + if keySetName in self.changedItems['keys']: + #handle any unsaved changes to this key set + if bindName in self.changedItems['keys'][keySetName]: + key = self.changedItems['keys'][keySetName][bindName] + self.listBindings.insert(END, bindName+' - '+key) + if reselect: + self.listBindings.see(listIndex) + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + + def DeleteCustomKeys(self): + keySetName=self.customKeys.get() + delmsg = 'Are you sure you wish to delete the key set %r ?' + if not tkMessageBox.askyesno( + 'Delete Key Set', delmsg % keySetName, parent=self): + return + self.DeactivateCurrentConfig() + #remove key set from config + idleConf.userCfg['keys'].remove_section(keySetName) + if keySetName in self.changedItems['keys']: + del(self.changedItems['keys'][keySetName]) + #write changes + idleConf.userCfg['keys'].Save() + #reload user key set list + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + if not itemList: + self.radioKeysCustom.config(state=DISABLED) + self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -') + else: + self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) + #revert to default key set + self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default')) + self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) + #user can't back out of these changes, they must be applied now + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + self.SetKeysType() + + def DeleteCustomTheme(self): + themeName = self.customTheme.get() + delmsg = 'Are you sure you wish to delete the theme %r ?' + if not tkMessageBox.askyesno( + 'Delete Theme', delmsg % themeName, parent=self): + return + self.DeactivateCurrentConfig() + #remove theme from config + idleConf.userCfg['highlight'].remove_section(themeName) + if themeName in self.changedItems['highlight']: + del(self.changedItems['highlight'][themeName]) + #write changes + idleConf.userCfg['highlight'].Save() + #reload user theme list + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) + self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -') + else: + self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + #revert to default theme + self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) + self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) + #user can't back out of these changes, they must be applied now + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + self.SetThemeType() + + def GetColour(self): + target = self.highlightTarget.get() + prevColour = self.frameColourSet.cget('bg') + rgbTuplet, colourString = tkColorChooser.askcolor( + parent=self, title='Pick new colour for : '+target, + initialcolor=prevColour) + if colourString and (colourString != prevColour): + #user didn't cancel, and they chose a new colour + if self.themeIsBuiltin.get(): #current theme is a built-in + message = ('Your changes will be saved as a new Custom Theme. ' + 'Enter a name for your new Custom Theme below.') + newTheme = self.GetNewThemeName(message) + if not newTheme: #user cancelled custom theme creation + return + else: #create new custom theme based on previously active theme + self.CreateNewTheme(newTheme) + self.colour.set(colourString) + else: #current theme is user defined + self.colour.set(colourString) + + def OnNewColourSet(self): + newColour=self.colour.get() + self.frameColourSet.config(bg=newColour) #set sample + plane ='foreground' if self.fgHilite.get() else 'background' + sampleElement = self.themeElements[self.highlightTarget.get()][0] + self.textHighlightSample.tag_config(sampleElement, **{plane:newColour}) + theme = self.customTheme.get() + themeElement = sampleElement + '-' + plane + self.AddChangedItem('highlight', theme, themeElement, newColour) + + def GetNewThemeName(self, message): + usedNames = (idleConf.GetSectionList('user', 'highlight') + + idleConf.GetSectionList('default', 'highlight')) + newTheme = GetCfgSectionNameDialog( + self, 'New Custom Theme', message, usedNames).result + return newTheme + + def SaveAsNewTheme(self): + newThemeName = self.GetNewThemeName('New Theme Name:') + if newThemeName: + self.CreateNewTheme(newThemeName) + + def CreateNewTheme(self, newThemeName): + #creates new custom theme based on the previously active theme, + #and makes the new theme active + if self.themeIsBuiltin.get(): + themeType = 'default' + themeName = self.builtinTheme.get() + else: + themeType = 'user' + themeName = self.customTheme.get() + newTheme = idleConf.GetThemeDict(themeType, themeName) + #apply any of the old theme's unsaved changes to the new theme + if themeName in self.changedItems['highlight']: + themeChanges = self.changedItems['highlight'][themeName] + for element in themeChanges: + newTheme[element] = themeChanges[element] + #save the new theme + self.SaveNewTheme(newThemeName, newTheme) + #change gui over to the new theme + customThemeList = idleConf.GetSectionList('user', 'highlight') + customThemeList.sort() + self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName) + self.themeIsBuiltin.set(0) + self.SetThemeType() + + def OnListFontButtonRelease(self, event): + font = self.listFontName.get(ANCHOR) + self.fontName.set(font.lower()) + self.SetFontSample() + + def SetFontSample(self, event=None): + fontName = self.fontName.get() + fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL + newFont = (fontName, self.fontSize.get(), fontWeight) + self.labelFontSample.config(font=newFont) + self.textHighlightSample.configure(font=newFont) + + def SetHighlightTarget(self): + if self.highlightTarget.get() == 'Cursor': #bg not possible + self.radioFg.config(state=DISABLED) + self.radioBg.config(state=DISABLED) + self.fgHilite.set(1) + else: #both fg and bg can be set + self.radioFg.config(state=NORMAL) + self.radioBg.config(state=NORMAL) + self.fgHilite.set(1) + self.SetColourSample() + + def SetColourSampleBinding(self, *args): + self.SetColourSample() + + def SetColourSample(self): + #set the colour smaple area + tag = self.themeElements[self.highlightTarget.get()][0] + plane = 'foreground' if self.fgHilite.get() else 'background' + colour = self.textHighlightSample.tag_cget(tag, plane) + self.frameColourSet.config(bg=colour) + + def PaintThemeSample(self): + if self.themeIsBuiltin.get(): #a default theme + theme = self.builtinTheme.get() + else: #a user theme + theme = self.customTheme.get() + for elementTitle in self.themeElements: + element = self.themeElements[elementTitle][0] + colours = idleConf.GetHighlight(theme, element) + if element == 'cursor': #cursor sample needs special painting + colours['background'] = idleConf.GetHighlight( + theme, 'normal', fgBg='bg') + #handle any unsaved changes to this theme + if theme in self.changedItems['highlight']: + themeDict = self.changedItems['highlight'][theme] + if element + '-foreground' in themeDict: + colours['foreground'] = themeDict[element + '-foreground'] + if element + '-background' in themeDict: + colours['background'] = themeDict[element + '-background'] + self.textHighlightSample.tag_config(element, **colours) + self.SetColourSample() + + def HelpSourceSelected(self, event): + self.SetHelpListButtonStates() + + def SetHelpListButtonStates(self): + if self.listHelp.size() < 1: #no entries in list + self.buttonHelpListEdit.config(state=DISABLED) + self.buttonHelpListRemove.config(state=DISABLED) + else: #there are some entries + if self.listHelp.curselection(): #there currently is a selection + self.buttonHelpListEdit.config(state=NORMAL) + self.buttonHelpListRemove.config(state=NORMAL) + else: #there currently is not a selection + self.buttonHelpListEdit.config(state=DISABLED) + self.buttonHelpListRemove.config(state=DISABLED) + + def HelpListItemAdd(self): + helpSource = GetHelpSourceDialog(self, 'New Help Source').result + if helpSource: + self.userHelpList.append((helpSource[0], helpSource[1])) + self.listHelp.insert(END, helpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def HelpListItemEdit(self): + itemIndex = self.listHelp.index(ANCHOR) + helpSource = self.userHelpList[itemIndex] + newHelpSource = GetHelpSourceDialog( + self, 'Edit Help Source', menuItem=helpSource[0], + filePath=helpSource[1]).result + if (not newHelpSource) or (newHelpSource == helpSource): + return #no changes + self.userHelpList[itemIndex] = newHelpSource + self.listHelp.delete(itemIndex) + self.listHelp.insert(itemIndex, newHelpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def HelpListItemRemove(self): + itemIndex = self.listHelp.index(ANCHOR) + del(self.userHelpList[itemIndex]) + self.listHelp.delete(itemIndex) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def UpdateUserHelpChangedItems(self): + "Clear and rebuild the HelpFiles section in self.changedItems" + self.changedItems['main']['HelpFiles'] = {} + for num in range(1, len(self.userHelpList) + 1): + self.AddChangedItem( + 'main', 'HelpFiles', str(num), + ';'.join(self.userHelpList[num-1][:2])) + + def LoadFontCfg(self): + ##base editor font selection list + fonts = list(tkFont.families(self)) + fonts.sort() + for font in fonts: + self.listFontName.insert(END, font) + configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow') + fontName = configuredFont[0].lower() + fontSize = configuredFont[1] + fontBold = configuredFont[2]=='bold' + self.fontName.set(fontName) + lc_fonts = [s.lower() for s in fonts] + try: + currentFontIndex = lc_fonts.index(fontName) + self.listFontName.see(currentFontIndex) + self.listFontName.select_set(currentFontIndex) + self.listFontName.select_anchor(currentFontIndex) + except ValueError: + pass + ##font size dropdown + self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', + '14', '16', '18', '20', '22'), fontSize ) + ##fontWeight + self.fontBold.set(fontBold) + ##font sample + self.SetFontSample() + + def LoadTabCfg(self): + ##indent sizes + spaceNum = idleConf.GetOption( + 'main', 'Indent', 'num-spaces', default=4, type='int') + self.spaceNum.set(spaceNum) + + def LoadThemeCfg(self): + ##current theme type radiobutton + self.themeIsBuiltin.set(idleConf.GetOption( + 'main', 'Theme', 'default', type='bool', default=1)) + ##currently set theme + currentOption = idleConf.CurrentTheme() + ##load available theme option menus + if self.themeIsBuiltin.get(): #default theme selected + itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() + self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) + self.customTheme.set('- no custom themes -') + else: + self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + else: #user theme selected + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + self.optMenuThemeCustom.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() + self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) + self.SetThemeType() + ##load theme element option menu + themeNames = list(self.themeElements.keys()) + themeNames.sort(key=lambda x: self.themeElements[x][1]) + self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0]) + self.PaintThemeSample() + self.SetHighlightTarget() + + def LoadKeyCfg(self): + ##current keys type radiobutton + self.keysAreBuiltin.set(idleConf.GetOption( + 'main', 'Keys', 'default', type='bool', default=1)) + ##currently set keys + currentOption = idleConf.CurrentKeys() + ##load available keyset option menus + if self.keysAreBuiltin.get(): #default theme selected + itemList = idleConf.GetSectionList('default', 'keys') + itemList.sort() + self.optMenuKeysBuiltin.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + if not itemList: + self.radioKeysCustom.config(state=DISABLED) + self.customKeys.set('- no custom keys -') + else: + self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) + else: #user key set selected + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + self.optMenuKeysCustom.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('default', 'keys') + itemList.sort() + self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0]) + self.SetKeysType() + ##load keyset element list + keySetName = idleConf.CurrentKeys() + self.LoadKeysList(keySetName) + + def LoadGeneralCfg(self): + #startup state + self.startupEdit.set(idleConf.GetOption( + 'main', 'General', 'editor-on-startup', default=1, type='bool')) + #autosave state + self.autoSave.set(idleConf.GetOption( + 'main', 'General', 'autosave', default=0, type='bool')) + #initial window size + self.winWidth.set(idleConf.GetOption( + 'main', 'EditorWindow', 'width', type='int')) + self.winHeight.set(idleConf.GetOption( + 'main', 'EditorWindow', 'height', type='int')) + # default source encoding + self.encoding.set(idleConf.GetOption( + 'main', 'EditorWindow', 'encoding', default='none')) + # additional help sources + self.userHelpList = idleConf.GetAllExtraHelpSourcesList() + for helpItem in self.userHelpList: + self.listHelp.insert(END, helpItem[0]) + self.SetHelpListButtonStates() + + def LoadConfigs(self): + """ + load configuration from default and user config files and populate + the widgets on the config dialog pages. + """ + ### fonts / tabs page + self.LoadFontCfg() + self.LoadTabCfg() + ### highlighting page + self.LoadThemeCfg() + ### keys page + self.LoadKeyCfg() + ### general page + self.LoadGeneralCfg() + # note: extension page handled separately + + def SaveNewKeySet(self, keySetName, keySet): + """ + save a newly created core key set. + keySetName - string, the name of the new key set + keySet - dictionary containing the new key set + """ + if not idleConf.userCfg['keys'].has_section(keySetName): + idleConf.userCfg['keys'].add_section(keySetName) + for event in keySet: + value = keySet[event] + idleConf.userCfg['keys'].SetOption(keySetName, event, value) + + def SaveNewTheme(self, themeName, theme): + """ + save a newly created theme. + themeName - string, the name of the new theme + theme - dictionary containing the new theme + """ + if not idleConf.userCfg['highlight'].has_section(themeName): + idleConf.userCfg['highlight'].add_section(themeName) + for element in theme: + value = theme[element] + idleConf.userCfg['highlight'].SetOption(themeName, element, value) + + def SetUserValue(self, configType, section, item, value): + if idleConf.defaultCfg[configType].has_option(section, item): + if idleConf.defaultCfg[configType].Get(section, item) == value: + #the setting equals a default setting, remove it from user cfg + return idleConf.userCfg[configType].RemoveOption(section, item) + #if we got here set the option + return idleConf.userCfg[configType].SetOption(section, item, value) + + def SaveAllChangedConfigs(self): + "Save configuration changes to the user config file." + idleConf.userCfg['main'].Save() + for configType in self.changedItems: + cfgTypeHasChanges = False + for section in self.changedItems[configType]: + if section == 'HelpFiles': + #this section gets completely replaced + idleConf.userCfg['main'].remove_section('HelpFiles') + cfgTypeHasChanges = True + for item in self.changedItems[configType][section]: + value = self.changedItems[configType][section][item] + if self.SetUserValue(configType, section, item, value): + cfgTypeHasChanges = True + if cfgTypeHasChanges: + idleConf.userCfg[configType].Save() + for configType in ['keys', 'highlight']: + # save these even if unchanged! + idleConf.userCfg[configType].Save() + self.ResetChangedItems() #clear the changed items dict + self.save_all_changed_extensions() # uses a different mechanism + + def DeactivateCurrentConfig(self): + #Before a config is saved, some cleanup of current + #config must be done - remove the previous keybindings + winInstances = self.parent.instance_dict.keys() + for instance in winInstances: + instance.RemoveKeybindings() + + def ActivateConfigChanges(self): + "Dynamically apply configuration changes" + winInstances = self.parent.instance_dict.keys() + for instance in winInstances: + instance.ResetColorizer() + instance.ResetFont() + instance.set_notabs_indentwidth() + instance.ApplyKeybindings() + instance.reset_help_menu_entries() + + def Cancel(self): + self.destroy() + + def Ok(self): + self.Apply() + self.destroy() + + def Apply(self): + self.DeactivateCurrentConfig() + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + + def Help(self): + page = self.tabPages._current_page + view_text(self, title='Help for IDLE preferences', + text=help_common+help_pages.get(page, '')) + + def CreatePageExtensions(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. + """ + parent = self.parent + frame = self.tabPages.pages['Extensions'].frame + 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('<>', 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) + + 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.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): + 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, 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 + + def set_extension_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.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.""" + 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() + + +help_common = '''\ +When you click either the Apply or Ok buttons, settings in this +dialog that are different from IDLE's default are saved in +a .idlerc directory in your home directory. Except as noted, +these changes apply to all versions of IDLE installed on this +machine. Some do not take affect until IDLE is restarted. +[Cancel] only cancels changes made since the last save. +''' +help_pages = { + 'Highlighting':''' +Highlighting: +The IDLE Dark color theme is new in October 2015. It can only +be used with older IDLE releases if it is saved as a custom +theme, with a different name. +''' +} + + +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 + + +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, width=240) + 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) + interior.bind('', _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_canvas) + + return + + +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) -- cgit v1.2.1 From 079c5ddae80a617bec3bf391c3fbce996fd48fa4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 26 Jun 2016 09:46:57 +0300 Subject: Issue #22115: Added methods trace_add, trace_remove and trace_info in the tkinter.Variable class. They replace old methods trace_variable, trace, trace_vdelete and trace_vinfo that use obsolete Tcl commands and might not work in future versions of Tcl. --- Lib/idlelib/configdialog.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'Lib/idlelib/configdialog.py') diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index d19749a07e..f57c9a1adf 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -465,24 +465,24 @@ class ConfigDialog(Toplevel): return frame def AttachVarCallbacks(self): - self.fontSize.trace_variable('w', self.VarChanged_font) - self.fontName.trace_variable('w', self.VarChanged_font) - self.fontBold.trace_variable('w', self.VarChanged_font) - self.spaceNum.trace_variable('w', self.VarChanged_spaceNum) - self.colour.trace_variable('w', self.VarChanged_colour) - self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme) - self.customTheme.trace_variable('w', self.VarChanged_customTheme) - self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin) - self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget) - self.keyBinding.trace_variable('w', self.VarChanged_keyBinding) - self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys) - self.customKeys.trace_variable('w', self.VarChanged_customKeys) - self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin) - self.winWidth.trace_variable('w', self.VarChanged_winWidth) - self.winHeight.trace_variable('w', self.VarChanged_winHeight) - self.startupEdit.trace_variable('w', self.VarChanged_startupEdit) - self.autoSave.trace_variable('w', self.VarChanged_autoSave) - self.encoding.trace_variable('w', self.VarChanged_encoding) + self.fontSize.trace_add('write', self.VarChanged_font) + self.fontName.trace_add('write', self.VarChanged_font) + self.fontBold.trace_add('write', self.VarChanged_font) + self.spaceNum.trace_add('write', self.VarChanged_spaceNum) + self.colour.trace_add('write', self.VarChanged_colour) + self.builtinTheme.trace_add('write', self.VarChanged_builtinTheme) + self.customTheme.trace_add('write', self.VarChanged_customTheme) + self.themeIsBuiltin.trace_add('write', self.VarChanged_themeIsBuiltin) + self.highlightTarget.trace_add('write', self.VarChanged_highlightTarget) + self.keyBinding.trace_add('write', self.VarChanged_keyBinding) + self.builtinKeys.trace_add('write', self.VarChanged_builtinKeys) + self.customKeys.trace_add('write', self.VarChanged_customKeys) + self.keysAreBuiltin.trace_add('write', self.VarChanged_keysAreBuiltin) + self.winWidth.trace_add('write', self.VarChanged_winWidth) + self.winHeight.trace_add('write', self.VarChanged_winHeight) + self.startupEdit.trace_add('write', self.VarChanged_startupEdit) + self.autoSave.trace_add('write', self.VarChanged_autoSave) + self.encoding.trace_add('write', self.VarChanged_encoding) def remove_var_callbacks(self): "Remove callbacks to prevent memory leaks." @@ -493,7 +493,7 @@ class ConfigDialog(Toplevel): self.keyBinding, self.builtinKeys, self.customKeys, self.keysAreBuiltin, self.winWidth, self.winHeight, self.startupEdit, self.autoSave, self.encoding,): - var.trace_vdelete('w', var.trace_vinfo()[0][1]) + var.trace_remove('write', var.trace_info()[0][1]) def VarChanged_font(self, *params): '''When one font attribute changes, save them all, as they are -- cgit v1.2.1 From 74ef5828da00935196f4dfc5d4cafb4a9eaba467 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 26 Jun 2016 22:05:10 -0400 Subject: Issue #27380: IDLE: add base Query dialog, with ttk widgets and subclass SectionName. These split class GetCfgSectionNameDialog from configSectionNameDialog.py, temporarily renamed config_sec.py in 3.7.9a2. More Query subclasses are planned. --- Lib/idlelib/configdialog.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib/idlelib/configdialog.py') diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index f57c9a1adf..6629d70ec6 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -18,7 +18,7 @@ import tkinter.font as tkFont from idlelib.config import idleConf from idlelib.dynoption import DynOptionMenu from idlelib.config_key import GetKeysDialog -from idlelib.config_sec import GetCfgSectionNameDialog +from idlelib.query import SectionName from idlelib.config_help import GetHelpSourceDialog from idlelib.tabbedpages import TabbedPageSet from idlelib.textview import view_text @@ -684,7 +684,7 @@ class ConfigDialog(Toplevel): def GetNewKeysName(self, message): usedNames = (idleConf.GetSectionList('user', 'keys') + idleConf.GetSectionList('default', 'keys')) - newKeySet = GetCfgSectionNameDialog( + newKeySet = SectionName( self, 'New Custom Key Set', message, usedNames).result return newKeySet @@ -837,7 +837,7 @@ class ConfigDialog(Toplevel): def GetNewThemeName(self, message): usedNames = (idleConf.GetSectionList('user', 'highlight') + idleConf.GetSectionList('default', 'highlight')) - newTheme = GetCfgSectionNameDialog( + newTheme = SectionName( self, 'New Custom Theme', message, usedNames).result return newTheme -- cgit v1.2.1 From 61e4ac554cf110c33ad4a3c19734724c1199db06 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 8 Jul 2016 00:22:50 -0400 Subject: Issue #27380: IDLE: add query.HelpSource class and tests. Remove modules that are combined in new module. --- Lib/idlelib/configdialog.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'Lib/idlelib/configdialog.py') diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 6629d70ec6..388b48f088 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -18,8 +18,7 @@ import tkinter.font as tkFont from idlelib.config import idleConf from idlelib.dynoption import DynOptionMenu from idlelib.config_key import GetKeysDialog -from idlelib.query import SectionName -from idlelib.config_help import GetHelpSourceDialog +from idlelib.query import SectionName, HelpSource from idlelib.tabbedpages import TabbedPageSet from idlelib.textview import view_text from idlelib import macosx @@ -940,7 +939,8 @@ class ConfigDialog(Toplevel): self.buttonHelpListRemove.config(state=DISABLED) def HelpListItemAdd(self): - helpSource = GetHelpSourceDialog(self, 'New Help Source').result + helpSource = HelpSource(self, 'New Help Source', + ).result if helpSource: self.userHelpList.append((helpSource[0], helpSource[1])) self.listHelp.insert(END, helpSource[0]) @@ -950,16 +950,17 @@ class ConfigDialog(Toplevel): def HelpListItemEdit(self): itemIndex = self.listHelp.index(ANCHOR) helpSource = self.userHelpList[itemIndex] - newHelpSource = GetHelpSourceDialog( - self, 'Edit Help Source', menuItem=helpSource[0], - filePath=helpSource[1]).result - if (not newHelpSource) or (newHelpSource == helpSource): - return #no changes - self.userHelpList[itemIndex] = newHelpSource - self.listHelp.delete(itemIndex) - self.listHelp.insert(itemIndex, newHelpSource[0]) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() + newHelpSource = HelpSource( + self, 'Edit Help Source', + menuitem=helpSource[0], + filepath=helpSource[1], + ).result + if newHelpSource and newHelpSource != helpSource: + self.userHelpList[itemIndex] = newHelpSource + self.listHelp.delete(itemIndex) + self.listHelp.insert(itemIndex, newHelpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() def HelpListItemRemove(self): itemIndex = self.listHelp.index(ANCHOR) -- cgit v1.2.1 From fc6e39842812db71ae9b63797be6e38370624bf7 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 10 Jul 2016 13:46:34 -0400 Subject: Issue #27173: Add 'IDLE Modern Unix' to the built-in key sets. Make the default key set depend on the platform. Add tests for changes to the config module. --- Lib/idlelib/configdialog.py | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) (limited to 'Lib/idlelib/configdialog.py') diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 388b48f088..fda655f5d7 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -341,6 +341,7 @@ class ConfigDialog(Toplevel): buttonSaveCustomKeys = Button( frames[1], text='Save as New Custom Key Set', command=self.SaveAsNewKeySet) + self.new_custom_keys = Label(frames[0], bd=2) ##widget packing #body @@ -361,6 +362,7 @@ class ConfigDialog(Toplevel): self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) + self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5) self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) frames[0].pack(side=TOP, fill=BOTH, expand=True) @@ -514,10 +516,11 @@ class ConfigDialog(Toplevel): self.OnNewColourSet() def VarChanged_builtinTheme(self, *params): + oldthemes = ('IDLE Classic', 'IDLE New') value = self.builtinTheme.get() - if value == 'IDLE Dark': - if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New': - self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic') + if value not in oldthemes: + if idleConf.GetOption('main', 'Theme', 'name') not in oldthemes: + self.AddChangedItem('main', 'Theme', 'name', oldthemes[0]) self.AddChangedItem('main', 'Theme', 'name2', value) self.new_custom_theme.config(text='New theme, see Help', fg='#500000') @@ -557,8 +560,23 @@ class ConfigDialog(Toplevel): self.AddChangedItem('extensions', extKeybindSection, event, value) def VarChanged_builtinKeys(self, *params): + oldkeys = ( + 'IDLE Classic Windows', + 'IDLE Classic Unix', + 'IDLE Classic Mac', + 'IDLE Classic OSX', + ) value = self.builtinKeys.get() - self.AddChangedItem('main', 'Keys', 'name', value) + if value not in oldkeys: + if idleConf.GetOption('main', 'Keys', 'name') not in oldkeys: + self.AddChangedItem('main', 'Keys', 'name', oldkeys[0]) + self.AddChangedItem('main', 'Keys', 'name2', value) + self.new_custom_keys.config(text='New key set, see Help', + fg='#500000') + else: + self.AddChangedItem('main', 'Keys', 'name', value) + self.AddChangedItem('main', 'Keys', 'name2', '') + self.new_custom_keys.config(text='', fg='black') self.LoadKeysList(value) def VarChanged_customKeys(self, *params): @@ -767,8 +785,10 @@ class ConfigDialog(Toplevel): else: self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) #revert to default key set - self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default')) - self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) + self.keysAreBuiltin.set(idleConf.defaultCfg['main'] + .Get('Keys', 'default')) + self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name') + or idleConf.default_keys()) #user can't back out of these changes, they must be applied now self.SaveAllChangedConfigs() self.ActivateConfigChanges() @@ -1067,7 +1087,7 @@ class ConfigDialog(Toplevel): self.optMenuKeysCustom.SetMenu(itemList, currentOption) itemList = idleConf.GetSectionList('default', 'keys') itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0]) + self.optMenuKeysBuiltin.SetMenu(itemList, idleConf.default_keys()) self.SetKeysType() ##load keyset element list keySetName = idleConf.CurrentKeys() @@ -1369,12 +1389,18 @@ machine. Some do not take affect until IDLE is restarted. [Cancel] only cancels changes made since the last save. ''' help_pages = { - 'Highlighting':''' + 'Highlighting': ''' Highlighting: The IDLE Dark color theme is new in October 2015. It can only be used with older IDLE releases if it is saved as a custom theme, with a different name. -''' +''', + 'Keys': ''' +Keys: +The IDLE Modern Unix key set is new in June 2016. It can only +be used with older IDLE releases if it is saved as a custom +key set, with a different name. +''', } -- cgit v1.2.1