diff -r 61c4dbec2e2c Lib/idlelib/configdialog.py --- a/Lib/idlelib/configdialog.py Sat Aug 13 05:38:18 2016 +0300 +++ b/Lib/idlelib/configdialog.py Sun Aug 14 03:36:22 2016 +0800 @@ -16,7 +16,7 @@ import tkinter.font as tkFont from idlelib.config import idleConf -from idlelib.dynoption import DynOptionMenu +from idlelib.dynoption import DynCombobox from idlelib.config_key import GetKeysDialog from idlelib.query import SectionName, HelpSource from idlelib.tabbedpages import TabbedPageSet @@ -140,7 +140,7 @@ scrollFont.config(command=self.listFontName.yview) self.listFontName.config(yscrollcommand=scrollFont.set) labelFontSizeTitle = Label(frameFontParam, text='Size :') - self.optMenuFontSize = DynOptionMenu( + self.comboFontSize = DynCombobox( frameFontParam, self.fontSize, None, command=self.SetFontSample) checkFontBold = Checkbutton( frameFontParam, variable=self.fontBold, onvalue=1, @@ -169,7 +169,7 @@ 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) + self.comboFontSize.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) @@ -235,9 +235,8 @@ 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.comboHighlightTarget = DynCombobox( + self.frameColourSet, self.highlightTarget, None) self.radioFg = Radiobutton( frameFgBg, variable=self.fgHilite, value=1, text='Foreground', command=self.SetColourSampleBinding) @@ -256,9 +255,9 @@ self.radioThemeCustom = Radiobutton( frameTheme, variable=self.themeIsBuiltin, value=0, command=self.SetThemeType, text='a Custom Theme') - self.optMenuThemeBuiltin = DynOptionMenu( + self.comboThemeBuiltin = DynCombobox( frameTheme, self.builtinTheme, None, command=None) - self.optMenuThemeCustom=DynOptionMenu( + self.comboThemeCustom = DynCombobox( frameTheme, self.customTheme, None, command=None) self.buttonDeleteCustomTheme=Button( frameTheme, text='Delete Custom Theme', @@ -275,7 +274,7 @@ 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( + self.comboHighlightTarget.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) @@ -284,8 +283,8 @@ 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.comboThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) + self.comboThemeCustom.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 @@ -331,9 +330,9 @@ self.radioKeysCustom = Radiobutton( frames[0], variable=self.keysAreBuiltin, value=0, command=self.SetKeysType, text='Use a Custom Key Set') - self.optMenuKeysBuiltin = DynOptionMenu( + self.comboKeysBuiltin = DynCombobox( frames[0], self.builtinKeys, None, command=None) - self.optMenuKeysCustom = DynOptionMenu( + self.comboKeysCustom = DynCombobox( frames[0], self.customKeys, None, command=None) self.buttonDeleteCustomKeys = Button( frames[1], text='Delete Custom Key Set', @@ -360,8 +359,8 @@ #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.comboKeysBuiltin.grid(row=0, column=1, sticky=NSEW) + self.comboKeysCustom.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) @@ -642,24 +641,24 @@ def SetThemeType(self): if self.themeIsBuiltin.get(): - self.optMenuThemeBuiltin.config(state=NORMAL) - self.optMenuThemeCustom.config(state=DISABLED) + self.comboThemeBuiltin.enable() + self.comboThemeCustom.config(state=DISABLED) self.buttonDeleteCustomTheme.config(state=DISABLED) else: - self.optMenuThemeBuiltin.config(state=DISABLED) + self.comboThemeBuiltin.disable() self.radioThemeCustom.config(state=NORMAL) - self.optMenuThemeCustom.config(state=NORMAL) + self.comboThemeCustom.enable() self.buttonDeleteCustomTheme.config(state=NORMAL) def SetKeysType(self): if self.keysAreBuiltin.get(): - self.optMenuKeysBuiltin.config(state=NORMAL) - self.optMenuKeysCustom.config(state=DISABLED) + self.comboKeysBuiltin.enable() + self.comboKeysCustom.disable() self.buttonDeleteCustomKeys.config(state=DISABLED) else: - self.optMenuKeysBuiltin.config(state=DISABLED) + self.comboKeysBuiltin.disable() self.radioKeysCustom.config(state=NORMAL) - self.optMenuKeysCustom.config(state=NORMAL) + self.comboKeysCustom.enable() self.buttonDeleteCustomKeys.config(state=NORMAL) def GetNewKeys(self): @@ -736,7 +735,7 @@ #change gui over to the new key set customKeyList = idleConf.GetSectionList('user', 'keys') customKeyList.sort() - self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName) + self.comboKeysCustom.reset_values(customKeyList, newKeySetName) self.keysAreBuiltin.set(0) self.SetKeysType() @@ -781,9 +780,9 @@ itemList.sort() if not itemList: self.radioKeysCustom.config(state=DISABLED) - self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -') + self.comboKeysCustom.reset_values(itemList, '- no custom keys -') else: - self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) + self.comboKeysCustom.reset_values(itemList, itemList[0]) #revert to default key set self.keysAreBuiltin.set(idleConf.defaultCfg['main'] .Get('Keys', 'default')) @@ -812,9 +811,9 @@ itemList.sort() if not itemList: self.radioThemeCustom.config(state=DISABLED) - self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -') + self.comboThemeCustom.reset_values(itemList, '- no custom themes -') else: - self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + self.comboThemeCustom.reset_values(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')) @@ -885,7 +884,7 @@ #change gui over to the new theme customThemeList = idleConf.GetSectionList('user', 'highlight') customThemeList.sort() - self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName) + self.comboThemeCustom.reset_values(customThemeList, newThemeName) self.themeIsBuiltin.set(0) self.SetThemeType() @@ -1017,7 +1016,7 @@ except ValueError: pass ##font size dropdown - self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', + self.comboFontSize.reset_values(('7', '8', '9', '10', '11', '12', '13', '14', '16', '18', '20', '22'), fontSize ) ##fontWeight self.fontBold.set(fontBold) @@ -1040,26 +1039,26 @@ if self.themeIsBuiltin.get(): #default theme selected itemList = idleConf.GetSectionList('default', 'highlight') itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) + self.comboThemeBuiltin.reset_values(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]) + self.comboThemeCustom.reset_values(itemList, itemList[0]) else: #user theme selected itemList = idleConf.GetSectionList('user', 'highlight') itemList.sort() - self.optMenuThemeCustom.SetMenu(itemList, currentOption) + self.comboThemeCustom.reset_values(itemList, currentOption) itemList = idleConf.GetSectionList('default', 'highlight') itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) + self.comboThemeBuiltin.reset_values(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.comboHighlightTarget.reset_values(themeNames, themeNames[0]) self.PaintThemeSample() self.SetHighlightTarget() @@ -1073,21 +1072,21 @@ if self.keysAreBuiltin.get(): #default theme selected itemList = idleConf.GetSectionList('default', 'keys') itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, currentOption) + self.comboKeysBuiltin.reset_values(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]) + self.comboKeysCustom.reset_values(itemList, itemList[0]) else: #user key set selected itemList = idleConf.GetSectionList('user', 'keys') itemList.sort() - self.optMenuKeysCustom.SetMenu(itemList, currentOption) + self.comboKeysCustom.reset_values(itemList, currentOption) itemList = idleConf.GetSectionList('default', 'keys') itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, idleConf.default_keys()) + self.comboKeysBuiltin.reset_values(itemList, idleConf.default_keys()) self.SetKeysType() ##load keyset element list keySetName = idleConf.CurrentKeys() diff -r 61c4dbec2e2c Lib/idlelib/dynoption.py --- a/Lib/idlelib/dynoption.py Sat Aug 13 05:38:18 2016 +0300 +++ b/Lib/idlelib/dynoption.py Sun Aug 14 03:36:22 2016 +0800 @@ -1,40 +1,41 @@ -""" -OptionMenu widget modified to allow dynamic menu reconfiguration -and setting of highlightthickness -""" -import copy -from tkinter import OptionMenu, _setit, StringVar, Button +"""Combobox widget with dynamic item reconfiguration and a callback""" -class DynOptionMenu(OptionMenu): - """ - unlike OptionMenu, our kwargs can include highlightthickness - """ +from tkinter.constants import DISABLED, READONLY +from tkinter.ttk import Combobox + + +class DynCombobox(Combobox): + def __init__(self, master, variable, value, *values, **kwargs): - # TODO copy value instead of whole dict - kwargsCopy=copy.copy(kwargs) - if 'highlightthickness' in list(kwargs.keys()): - del(kwargs['highlightthickness']) - OptionMenu.__init__(self, master, variable, value, *values, **kwargs) - self.config(highlightthickness=kwargsCopy.get('highlightthickness')) - #self.menu=self['menu'] - self.variable=variable - self.command=kwargs.get('command') + self.command = kwargs.pop('command', None) + super().__init__(master, **kwargs) + self['values'] = (value,) + tuple(values) + self.set(variable.get()) + self.variable = variable + self.bind('<>', self.on_select) + self.enable() - def SetMenu(self,valueList,value=None): - """ - clear and reload the menu with a new set of options. - valueList - list of new options - value - initial value to set the optionmenu's menubutton to - """ - self['menu'].delete(0,'end') - for item in valueList: - self['menu'].add_command(label=item, - command=_setit(self.variable,item,self.command)) - if value: - self.variable.set(value) + def enable(self): + self.config(state=READONLY) -def _dyn_option_menu(parent): # htest # - from tkinter import Toplevel # + StringVar, Button + def disable(self): + self.config(state=DISABLED) + + def on_select(self, event): + self.variable.set(self.get()) + if self.command: + self.command(event) + + def reset_values(self, values, selection=None): + """Reloads the combo box with a new set of options""" + self['values'] = values + if selection: + self.set(selection) + self.variable.set(selection) + + +def _dyn_combobox(parent): # htest # + from tkinter import Button, StringVar, Toplevel top = Toplevel(parent) top.title("Tets dynamic option menu") @@ -43,15 +44,16 @@ top.focus_set() var = StringVar(top) - var.set("Old option set") #Set the default value - dyn = DynOptionMenu(top,var, "old1","old2","old3","old4") + var.set("Old option set") # Set the default value + dyn = DynCombobox(top, var, "old1", "old2", "old3", "old4") dyn.pack() def update(): - dyn.SetMenu(["new1","new2","new3","new4"], value="new option set") + dyn.reset_values(["new1", "new2", "new3", "new4"], + value="new option set") button = Button(top, text="Change option set", command=update) button.pack() if __name__ == '__main__': from idlelib.idle_test.htest import run - run(_dyn_option_menu) + run(_dyn_combobox) diff -r 61c4dbec2e2c Lib/tkinter/constants.py --- a/Lib/tkinter/constants.py Sat Aug 13 05:38:18 2016 +0300 +++ b/Lib/tkinter/constants.py Sun Aug 14 03:36:22 2016 +0800 @@ -70,6 +70,7 @@ NORMAL='normal' DISABLED='disabled' ACTIVE='active' +READONLY='readonly' # Canvas state HIDDEN='hidden'