diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt index bc3d978f43f1ad..19ae531a57afad 100644 --- a/Lib/idlelib/README.txt +++ b/Lib/idlelib/README.txt @@ -41,7 +41,6 @@ config.py # Load, fetch, and save configuration (nim). configdialog.py # Display user configuration dialogs. config_help.py # Specify help source in configdialog. config_key.py # Change keybindings. -dynoption.py # Define mutable OptionMenu widget (nim). debugobj.py # Define class used in stackviewer. debugobj_r.py # Communicate objects between processes with rpc (nim). debugger.py # Debug code run from shell or editor; show window. diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 82596498d34611..07b0c2a00ddf5f 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -16,15 +16,14 @@ TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE, NONE, BOTH, X, Y, W, E, EW, NS, NSEW, NW, HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END) -from tkinter.ttk import (Frame, LabelFrame, Button, Checkbutton, Entry, Label, - OptionMenu, Notebook, Radiobutton, Scrollbar, Style) +from tkinter.ttk import (Button, Checkbutton, Entry, Frame, Label, LabelFrame, + Notebook, Radiobutton, Scrollbar, Style, Combobox) import tkinter.colorchooser as tkColorChooser import tkinter.font as tkFont from tkinter import messagebox from idlelib.config import idleConf, ConfigChanges from idlelib.config_key import GetKeysDialog -from idlelib.dynoption import DynOptionMenu from idlelib import macosx from idlelib.query import SectionName, HelpSource from idlelib.textview import view_text @@ -520,7 +519,7 @@ def create_page_font_tab(self): scroll_font: Scrollbar frame_font_param: Frame font_size_title: Label - (*)sizelist: DynOptionMenu - font_size + (*)sizelist: Combobox - font_size (*)bold_toggle: Checkbutton - font_bold frame_sample: LabelFrame (*)font_sample: Label @@ -555,7 +554,10 @@ def create_page_font_tab(self): scroll_font.config(command=self.fontlist.yview) self.fontlist.config(yscrollcommand=scroll_font.set) font_size_title = Label(frame_font_param, text='Size :') - self.sizelist = DynOptionMenu(frame_font_param, self.font_size, None) + self.sizelist = Combobox(frame_font_param, textvariable=self.font_size, + width=3, state='readonly') + self.sizelist.bind('<>', + lambda e: self.sizelist.selection_clear()) self.bold_toggle = Checkbutton( frame_font_param, variable=self.font_bold, onvalue=1, offvalue=0, text='Bold') @@ -621,9 +623,10 @@ def load_font_cfg(self): except ValueError: pass # Set font size dropdown. - self.sizelist.SetMenu(('7', '8', '9', '10', '11', '12', '13', '14', - '16', '18', '20', '22', '25', '29', '34', '40'), - font_size) + self.sizelist['values'] = ('7', '8', '9', '10', '11', '12', '13', '14', + '16', '18', '20', '22', '25', '29', '34', '40') + self.sizelist.set(font_size) + # Set font weight. self.font_bold.set(font_bold) self.set_samples() @@ -707,7 +710,7 @@ def create_page_highlight(self): for the current theme. Radiobuttons builtin_theme_on and custom_theme_on toggle var theme_source, which controls if the current set of colors are from a builtin or custom theme. - DynOptionMenus builtinlist and customlist contain lists of the + Comboboxes builtinlist and customlist contain lists of the builtin and custom themes, respectively, and the current item from each list is stored in vars builtin_name and custom_name. @@ -737,7 +740,7 @@ def create_page_highlight(self): if the current selected color for a tag is for the foreground or background. - DynOptionMenu targetlist contains a readable description of the + Combobox targetlist contains a readable description of the tags applied to Python source within IDLE. Selecting one of the tags from this list populates highlight_target, which has a callback function set_highlight_target(). @@ -795,7 +798,7 @@ def create_page_highlight(self): (*)highlight_sample: Text (*)frame_color_set: Frame (*)button_set_color: Button - (*)targetlist: DynOptionMenu - highlight_target + (*)targetlist: Combobox - highlight_target frame_fg_bg_toggle: Frame (*)fg_on: Radiobutton - fg_bg_toggle (*)bg_on: Radiobutton - fg_bg_toggle @@ -804,8 +807,8 @@ def create_page_highlight(self): theme_type_title: Label (*)builtin_theme_on: Radiobutton - theme_source (*)custom_theme_on: Radiobutton - theme_source - (*)builtinlist: DynOptionMenu - builtin_name - (*)customlist: DynOptionMenu - custom_name + (*)builtinlist: Combobox - builtin_name + (*)customlist: Combobox - custom_name (*)button_delete_custom: Button (*)theme_message: Label """ @@ -894,9 +897,10 @@ def tem(event, elem=element): self.button_set_color = Button( self.frame_color_set, text='Choose Color for :', command=self.get_color) - self.targetlist = DynOptionMenu( - self.frame_color_set, self.highlight_target, None, - highlightthickness=0) #, command=self.set_highlight_targetBinding + self.targetlist = Combobox(self.frame_color_set, + textvariable=self.highlight_target, state='readonly') + self.targetlist.bind('<>', + lambda e: self.targetlist.selection_clear()) self.fg_on = Radiobutton( frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=1, text='Foreground', command=self.set_color_sample_binding) @@ -915,10 +919,15 @@ def tem(event, elem=element): self.custom_theme_on = Radiobutton( frame_theme, variable=self.theme_source, value=0, command=self.set_theme_type, text='a Custom Theme') - self.builtinlist = DynOptionMenu( - frame_theme, self.builtin_name, None, command=None) - self.customlist = DynOptionMenu( - frame_theme, self.custom_name, None, command=None) + self.builtinlist = Combobox(frame_theme, + textvariable=self.builtin_name, state='readonly') + self.builtinlist.bind('<>', + lambda e: self.builtinlist.selection_clear()) + self.customlist = Combobox(frame_theme, + textvariable=self.custom_name, state='readonly') + self.customlist.bind('<>', + lambda e: self.customlist.selection_clear()) + self.button_delete_custom = Button( frame_theme, text='Delete Custom Theme', command=self.delete_custom) @@ -975,26 +984,31 @@ def load_theme_cfg(self): if self.theme_source.get(): # Default theme selected. item_list = idleConf.GetSectionList('default', 'highlight') item_list.sort() - self.builtinlist.SetMenu(item_list, current_option) + self.builtinlist['values'] = item_list + self.builtinlist.set(current_option) item_list = idleConf.GetSectionList('user', 'highlight') item_list.sort() if not item_list: self.custom_theme_on.state(('disabled',)) self.custom_name.set('- no custom themes -') else: - self.customlist.SetMenu(item_list, item_list[0]) + self.customlist['values'] = item_list + self.customlist.set(item_list[0]) else: # User theme selected. item_list = idleConf.GetSectionList('user', 'highlight') item_list.sort() - self.customlist.SetMenu(item_list, current_option) + self.customlist['values'] = item_list + self.customlist.set(current_option) item_list = idleConf.GetSectionList('default', 'highlight') item_list.sort() - self.builtinlist.SetMenu(item_list, item_list[0]) + self.builtinlist['values'] = item_list + self.builtinlist.set(item_list[0]) self.set_theme_type() # Load theme element option menu. theme_names = list(self.theme_elements.keys()) theme_names.sort(key=lambda x: self.theme_elements[x][1]) - self.targetlist.SetMenu(theme_names, theme_names[0]) + self.targetlist['values'] = theme_names + self.targetlist.set(theme_names[0]) self.paint_theme_sample() self.set_highlight_target() @@ -1068,13 +1082,13 @@ def set_theme_type(self): load_theme_cfg """ if self.theme_source.get(): - self.builtinlist['state'] = 'normal' - self.customlist['state'] = 'disabled' + self.builtinlist.state(('!disabled',)) + self.customlist.state(('disabled',)) self.button_delete_custom.state(('disabled',)) else: - self.builtinlist['state'] = 'disabled' + self.builtinlist.state(('disabled',)) self.custom_theme_on.state(('!disabled',)) - self.customlist['state'] = 'normal' + self.customlist.state(('!disabled',)) self.button_delete_custom.state(('!disabled',)) def get_color(self): @@ -1181,7 +1195,8 @@ def create_new(self, new_theme_name): # Change GUI over to the new theme. custom_theme_list = idleConf.GetSectionList('user', 'highlight') custom_theme_list.sort() - self.customlist.SetMenu(custom_theme_list, new_theme_name) + self.customlist['values'] = custom_theme_list + self.customlist.set(new_theme_name) self.theme_source.set(0) self.set_theme_type() @@ -1329,9 +1344,11 @@ def delete_custom(self): item_list.sort() if not item_list: self.custom_theme_on.state(('disabled',)) - self.customlist.SetMenu(item_list, '- no custom themes -') + self.customlist['values'] = item_list + self.customlist.set('- no custom themes -') else: - self.customlist.SetMenu(item_list, item_list[0]) + self.customlist['values'] = item_list + self.customlist.set(item_list[0]) # Revert to default theme. self.theme_source.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) self.builtin_name.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) @@ -1364,7 +1381,7 @@ def create_page_keys(self): lists and calls load_keys_list for the current keyset. Radiobuttons builtin_keyset_on and custom_keyset_on toggle var keyset_source, which controls if the current set of keybindings - are from a builtin or custom keyset. DynOptionMenus builtinlist + are from a builtin or custom keyset. Comboboxes builtinlist and customlist contain lists of the builtin and custom keysets, respectively, and the current item from each list is stored in vars builtin_name and custom_name. @@ -1416,9 +1433,9 @@ def create_page_keys(self): frames[0]: Frame (*)builtin_keyset_on: Radiobutton - var keyset_source (*)custom_keyset_on: Radiobutton - var keyset_source - (*)builtinlist: DynOptionMenu - var builtin_name, + (*)builtinlist: Combobox - var builtin_name, func keybinding_selected - (*)customlist: DynOptionMenu - var custom_name, + (*)customlist: Combobox - var custom_name, func keybinding_selected (*)keys_message: Label frames[1]: Frame @@ -1473,10 +1490,14 @@ def create_page_keys(self): self.custom_keyset_on = Radiobutton( frames[0], variable=self.keyset_source, value=0, command=self.set_keys_type, text='Use a Custom Key Set') - self.builtinlist = DynOptionMenu( - frames[0], self.builtin_name, None, command=None) - self.customlist = DynOptionMenu( - frames[0], self.custom_name, None, command=None) + self.builtinlist = Combobox(frames[0], + textvariable=self.builtin_name, state='readonly') + self.builtinlist.bind('<>', + lambda e: self.builtinlist.selection_clear()) + self.customlist = Combobox(frames[0], + textvariable=self.custom_name, state='readonly') + self.customlist.bind('<>', + lambda e: self.customlist.selection_clear()) self.button_delete_custom_keys = Button( frames[1], text='Delete Custom Key Set', command=self.delete_custom_keys) @@ -1521,21 +1542,25 @@ def load_key_cfg(self): if self.keyset_source.get(): # Default theme selected. item_list = idleConf.GetSectionList('default', 'keys') item_list.sort() - self.builtinlist.SetMenu(item_list, current_option) + self.builtinlist['values'] = item_list + self.builtinlist.set(current_option) item_list = idleConf.GetSectionList('user', 'keys') item_list.sort() if not item_list: self.custom_keyset_on.state(('disabled',)) self.custom_name.set('- no custom keys -') else: - self.customlist.SetMenu(item_list, item_list[0]) + self.customlist['values'] = item_list + self.customlist.set(item_list[0]) else: # User key set selected. item_list = idleConf.GetSectionList('user', 'keys') item_list.sort() - self.customlist.SetMenu(item_list, current_option) + self.customlist['values'] = item_list + self.customlist.set(current_option) item_list = idleConf.GetSectionList('default', 'keys') item_list.sort() - self.builtinlist.SetMenu(item_list, idleConf.default_keys()) + self.builtinlist['values'] = item_list + self.builtinlist.set(idleConf.default_keys()) self.set_keys_type() # Load keyset element list. keyset_name = idleConf.CurrentKeys() @@ -1592,13 +1617,13 @@ def var_changed_keybinding(self, *params): def set_keys_type(self): "Set available screen options based on builtin or custom key set." if self.keyset_source.get(): - self.builtinlist['state'] = 'normal' - self.customlist['state'] = 'disabled' + self.builtinlist.state(('!disabled',)) + self.customlist.state(('disabled',)) self.button_delete_custom_keys.state(('disabled',)) else: - self.builtinlist['state'] = 'disabled' + self.builtinlist.state(('disabled',)) self.custom_keyset_on.state(('!disabled',)) - self.customlist['state'] = 'normal' + self.customlist.state(('!disabled',)) self.button_delete_custom_keys.state(('!disabled',)) def get_new_keys(self): @@ -1689,7 +1714,8 @@ def create_new_key_set(self, new_key_set_name): # Change GUI over to the new key set. custom_key_list = idleConf.GetSectionList('user', 'keys') custom_key_list.sort() - self.customlist.SetMenu(custom_key_list, new_key_set_name) + self.customlist['values'] = custom_key_list + self.customlist.set(new_key_set_name) self.keyset_source.set(0) self.set_keys_type() @@ -1759,9 +1785,11 @@ def delete_custom_keys(self): item_list.sort() if not item_list: self.custom_keyset_on.state(('disabled',)) - self.customlist.SetMenu(item_list, '- no custom keys -') + self.customlist['values'] = item_list + self.customlist.set('- no custom keys -') else: - self.customlist.SetMenu(item_list, item_list[0]) + self.customlist['values'] = item_list + self.customlist.set(item_list[0]) # Revert to default key set. self.keyset_source.set(idleConf.defaultCfg['main'] .Get('Keys', 'default')) @@ -1829,7 +1857,7 @@ def create_page_general(self): (*)auto_wait_int: Entry - autocomplete_wait frame_paren1: Frame paren_style_title: Label - (*)paren_style_type: OptionMenu - paren_style + (*)paren_style_type: Combobox - paren_style frame_paren2: Frame paren_time_title: Label (*)paren_flash_time: Entry - flash_delay @@ -1942,9 +1970,12 @@ def create_page_general(self): frame_paren1 = Frame(frame_window, borderwidth=0) paren_style_title = Label(frame_paren1, text='Paren Match Style') - self.paren_style_type = OptionMenu( - frame_paren1, self.paren_style, 'expression', - "opener","parens","expression") + self.paren_style_type = Combobox(frame_paren1, + textvariable=self.paren_style, state='readonly', width=10, + values=("opener","parens","expression")) + self.paren_style_type.set('expression') + self.paren_style_type.bind('<>', + lambda e: self.paren_style_type.selection_clear()) frame_paren2 = Frame(frame_window, borderwidth=0) paren_time_title = Label( frame_paren2, text='Time Match Displayed (milliseconds)\n' diff --git a/Lib/idlelib/dynoption.py b/Lib/idlelib/dynoption.py deleted file mode 100644 index 9c6ffa435a1089..00000000000000 --- a/Lib/idlelib/dynoption.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -OptionMenu widget modified to allow dynamic menu reconfiguration -and setting of highlightthickness -""" -import copy - -from tkinter import OptionMenu, _setit, StringVar, Button - -class DynOptionMenu(OptionMenu): - """ - unlike OptionMenu, our kwargs can include highlightthickness - """ - 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') - - 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 _dyn_option_menu(parent): # htest # - from tkinter import Toplevel # + StringVar, Button - - top = Toplevel(parent) - top.title("Tets dynamic option menu") - x, y = map(int, parent.geometry().split('+')[1:]) - top.geometry("200x100+%d+%d" % (x + 250, y + 175)) - top.focus_set() - - var = StringVar(top) - var.set("Old option set") #Set the default value - dyn = DynOptionMenu(top,var, "old1","old2","old3","old4") - dyn.pack() - - def update(): - dyn.SetMenu(["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) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 1fea6d41df811c..adbbe3ca848ded 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -194,7 +194,7 @@ def test_fontlist_mouse(self): def test_sizelist(self): # Click on number should select that number d = self.page - d.sizelist.variable.set(40) + d.sizelist.set(40) self.assertEqual(d.font_size.get(), '40') def test_bold_toggle(self): @@ -385,7 +385,8 @@ def test_builtin_name(self): # Not in old_themes, defaults name to first item. idleConf.SetOption('main', 'Theme', 'name', 'spam') - d.builtinlist.SetMenu(item_list, 'IDLE Dark') + d.builtinlist['values'] = item_list + d.builtinlist.set('IDLE Dark') eq(mainpage, {'Theme': {'name': 'IDLE Classic', 'name2': 'IDLE Dark'}}) eq(d.theme_message['text'], 'New theme, see Help') @@ -394,14 +395,16 @@ def test_builtin_name(self): # Not in old themes - uses name2. changes.clear() idleConf.SetOption('main', 'Theme', 'name', 'IDLE New') - d.builtinlist.SetMenu(item_list, 'IDLE Dark') + d.builtinlist['values'] = item_list + d.builtinlist.set('IDLE Dark') eq(mainpage, {'Theme': {'name2': 'IDLE Dark'}}) eq(d.theme_message['text'], 'New theme, see Help') eq(d.paint_theme_sample.called, 2) # Builtin name in old_themes. changes.clear() - d.builtinlist.SetMenu(item_list, 'IDLE Classic') + d.builtinlist['values'] = item_list + d.builtinlist.set('IDLE Classic') eq(mainpage, {'Theme': {'name': 'IDLE Classic', 'name2': ''}}) eq(d.theme_message['text'], '') eq(d.paint_theme_sample.called, 3) @@ -410,13 +413,15 @@ def test_custom_name(self): d = self.page # If no selections, doesn't get added. - d.customlist.SetMenu([], '- no custom themes -') + d.customlist['values'] = [] + d.customlist.set('- no custom themes -') self.assertNotIn('Theme', mainpage) self.assertEqual(d.paint_theme_sample.called, 0) # Custom name selected. changes.clear() - d.customlist.SetMenu(['a', 'b', 'c'], 'c') + d.customlist['values'] = ['a', 'b', 'c'] + d.customlist.set('c') self.assertEqual(mainpage, {'Theme': {'name': 'c'}}) self.assertEqual(d.paint_theme_sample.called, 1) @@ -433,7 +438,8 @@ def test_highlight_target_list_mouse(self): eq = self.assertEqual d = self.page - d.targetlist.SetMenu(['a', 'b', 'c'], 'c') + d.targetlist['values'] = ['a', 'b', 'c'] + d.targetlist.set('c') eq(d.highlight_target.get(), 'c') eq(d.set_highlight_target.called, 1) @@ -524,16 +530,16 @@ def test_set_theme_type(self): # Builtin theme selected. d.theme_source.set(True) d.set_theme_type() - eq(d.builtinlist['state'], NORMAL) - eq(d.customlist['state'], DISABLED) + eq(d.builtinlist.instate(('disabled',)), False) + eq(d.customlist.instate(('disabled',)), True) eq(d.button_delete_custom.state(), ('disabled',)) # Custom theme selected. d.theme_source.set(False) d.set_theme_type() - eq(d.builtinlist['state'], DISABLED) + eq(d.builtinlist.instate(('disabled',)), True) eq(d.custom_theme_on.state(), ('selected',)) - eq(d.customlist['state'], NORMAL) + eq(d.customlist.instate(('disabled',)), False) eq(d.button_delete_custom.state(), ()) d.set_theme_type = Func() @@ -892,7 +898,8 @@ def test_builtin_name(self): 'IDLE Modern UNIX'] # Not in old_keys, defaults name to first item. - d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX') + d.builtinlist['values'] = item_list + d.builtinlist.set('IDLE Modern UNIX') eq(mainpage, {'Keys': {'name': 'IDLE Classic Windows', 'name2': 'IDLE Modern UNIX'}}) eq(d.keys_message['text'], 'New key set, see Help') @@ -902,7 +909,8 @@ def test_builtin_name(self): # Not in old keys - uses name2. changes.clear() idleConf.SetOption('main', 'Keys', 'name', 'IDLE Classic Unix') - d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX') + d.builtinlist['values'] = item_list + d.builtinlist.set('IDLE Modern UNIX') eq(mainpage, {'Keys': {'name2': 'IDLE Modern UNIX'}}) eq(d.keys_message['text'], 'New key set, see Help') eq(d.load_keys_list.called, 2) @@ -910,7 +918,8 @@ def test_builtin_name(self): # Builtin name in old_keys. changes.clear() - d.builtinlist.SetMenu(item_list, 'IDLE Classic OSX') + d.builtinlist['values'] = item_list + d.builtinlist.set('IDLE Classic OSX') eq(mainpage, {'Keys': {'name': 'IDLE Classic OSX', 'name2': ''}}) eq(d.keys_message['text'], '') eq(d.load_keys_list.called, 3) @@ -920,13 +929,15 @@ def test_custom_name(self): d = self.page # If no selections, doesn't get added. - d.customlist.SetMenu([], '- no custom keys -') + d.customlist['values'] = [] + d.customlist.set('- no custom keys -') self.assertNotIn('Keys', mainpage) self.assertEqual(d.load_keys_list.called, 0) # Custom name selected. changes.clear() - d.customlist.SetMenu(['a', 'b', 'c'], 'c') + d.customlist['values'] = ['a', 'b', 'c'] + d.customlist.set('c') self.assertEqual(mainpage, {'Keys': {'name': 'c'}}) self.assertEqual(d.load_keys_list.called, 1) @@ -959,16 +970,16 @@ def test_set_keys_type(self): # Builtin keyset selected. d.keyset_source.set(True) d.set_keys_type() - eq(d.builtinlist['state'], NORMAL) - eq(d.customlist['state'], DISABLED) + eq(d.builtinlist.instate(('disabled',)), False) + eq(d.customlist.instate(('disabled',)), True) eq(d.button_delete_custom_keys.state(), ('disabled',)) # Custom keyset selected. d.keyset_source.set(False) d.set_keys_type() - eq(d.builtinlist['state'], DISABLED) + eq(d.builtinlist.instate(('disabled',)), True) eq(d.custom_keyset_on.state(), ('selected',)) - eq(d.customlist['state'], NORMAL) + eq(d.customlist.instate(('disabled',)), False) eq(d.button_delete_custom_keys.state(), ()) d.set_keys_type = Func() @@ -1279,7 +1290,7 @@ def test_autocomplete_wait(self): def test_parenmatch(self): d = self.page eq = self.assertEqual - d.paren_style_type['menu'].invoke(0) + d.paren_style_type.set('opener') eq(extpage, {'ParenMatch': {'style': 'opener'}}) changes.clear() d.paren_flash_time.delete(0, 'end')