17db96d56Sopenharmony_ci""" 27db96d56Sopenharmony_ciDialog for building Tkinter accelerator key bindings 37db96d56Sopenharmony_ci""" 47db96d56Sopenharmony_cifrom tkinter import Toplevel, Listbox, StringVar, TclError 57db96d56Sopenharmony_cifrom tkinter.ttk import Frame, Button, Checkbutton, Entry, Label, Scrollbar 67db96d56Sopenharmony_cifrom tkinter import messagebox 77db96d56Sopenharmony_cifrom tkinter.simpledialog import _setup_dialog 87db96d56Sopenharmony_ciimport string 97db96d56Sopenharmony_ciimport sys 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_ciFUNCTION_KEYS = ('F1', 'F2' ,'F3' ,'F4' ,'F5' ,'F6', 137db96d56Sopenharmony_ci 'F7', 'F8' ,'F9' ,'F10' ,'F11' ,'F12') 147db96d56Sopenharmony_ciALPHANUM_KEYS = tuple(string.ascii_lowercase + string.digits) 157db96d56Sopenharmony_ciPUNCTUATION_KEYS = tuple('~!@#%^&*()_-+={}[]|;:,.<>/?') 167db96d56Sopenharmony_ciWHITESPACE_KEYS = ('Tab', 'Space', 'Return') 177db96d56Sopenharmony_ciEDIT_KEYS = ('BackSpace', 'Delete', 'Insert') 187db96d56Sopenharmony_ciMOVE_KEYS = ('Home', 'End', 'Page Up', 'Page Down', 'Left Arrow', 197db96d56Sopenharmony_ci 'Right Arrow', 'Up Arrow', 'Down Arrow') 207db96d56Sopenharmony_ciAVAILABLE_KEYS = (ALPHANUM_KEYS + PUNCTUATION_KEYS + FUNCTION_KEYS + 217db96d56Sopenharmony_ci WHITESPACE_KEYS + EDIT_KEYS + MOVE_KEYS) 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ci 247db96d56Sopenharmony_cidef translate_key(key, modifiers): 257db96d56Sopenharmony_ci "Translate from keycap symbol to the Tkinter keysym." 267db96d56Sopenharmony_ci mapping = {'Space':'space', 277db96d56Sopenharmony_ci '~':'asciitilde', '!':'exclam', '@':'at', '#':'numbersign', 287db96d56Sopenharmony_ci '%':'percent', '^':'asciicircum', '&':'ampersand', 297db96d56Sopenharmony_ci '*':'asterisk', '(':'parenleft', ')':'parenright', 307db96d56Sopenharmony_ci '_':'underscore', '-':'minus', '+':'plus', '=':'equal', 317db96d56Sopenharmony_ci '{':'braceleft', '}':'braceright', 327db96d56Sopenharmony_ci '[':'bracketleft', ']':'bracketright', '|':'bar', 337db96d56Sopenharmony_ci ';':'semicolon', ':':'colon', ',':'comma', '.':'period', 347db96d56Sopenharmony_ci '<':'less', '>':'greater', '/':'slash', '?':'question', 357db96d56Sopenharmony_ci 'Page Up':'Prior', 'Page Down':'Next', 367db96d56Sopenharmony_ci 'Left Arrow':'Left', 'Right Arrow':'Right', 377db96d56Sopenharmony_ci 'Up Arrow':'Up', 'Down Arrow': 'Down', 'Tab':'Tab'} 387db96d56Sopenharmony_ci key = mapping.get(key, key) 397db96d56Sopenharmony_ci if 'Shift' in modifiers and key in string.ascii_lowercase: 407db96d56Sopenharmony_ci key = key.upper() 417db96d56Sopenharmony_ci return f'Key-{key}' 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_ciclass GetKeysFrame(Frame): 457db96d56Sopenharmony_ci 467db96d56Sopenharmony_ci # Dialog title for invalid key sequence 477db96d56Sopenharmony_ci keyerror_title = 'Key Sequence Error' 487db96d56Sopenharmony_ci 497db96d56Sopenharmony_ci def __init__(self, parent, action, current_key_sequences): 507db96d56Sopenharmony_ci """ 517db96d56Sopenharmony_ci parent - parent of this dialog 527db96d56Sopenharmony_ci action - the name of the virtual event these keys will be 537db96d56Sopenharmony_ci mapped to 547db96d56Sopenharmony_ci current_key_sequences - a list of all key sequence lists 557db96d56Sopenharmony_ci currently mapped to virtual events, for overlap checking 567db96d56Sopenharmony_ci """ 577db96d56Sopenharmony_ci super().__init__(parent) 587db96d56Sopenharmony_ci self['borderwidth'] = 2 597db96d56Sopenharmony_ci self['relief'] = 'sunken' 607db96d56Sopenharmony_ci self.parent = parent 617db96d56Sopenharmony_ci self.action = action 627db96d56Sopenharmony_ci self.current_key_sequences = current_key_sequences 637db96d56Sopenharmony_ci self.result = '' 647db96d56Sopenharmony_ci self.key_string = StringVar(self) 657db96d56Sopenharmony_ci self.key_string.set('') 667db96d56Sopenharmony_ci # Set self.modifiers, self.modifier_label. 677db96d56Sopenharmony_ci self.set_modifiers_for_platform() 687db96d56Sopenharmony_ci self.modifier_vars = [] 697db96d56Sopenharmony_ci for modifier in self.modifiers: 707db96d56Sopenharmony_ci variable = StringVar(self) 717db96d56Sopenharmony_ci variable.set('') 727db96d56Sopenharmony_ci self.modifier_vars.append(variable) 737db96d56Sopenharmony_ci self.advanced = False 747db96d56Sopenharmony_ci self.create_widgets() 757db96d56Sopenharmony_ci 767db96d56Sopenharmony_ci def showerror(self, *args, **kwargs): 777db96d56Sopenharmony_ci # Make testing easier. Replace in #30751. 787db96d56Sopenharmony_ci messagebox.showerror(*args, **kwargs) 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ci def create_widgets(self): 817db96d56Sopenharmony_ci # Basic entry key sequence. 827db96d56Sopenharmony_ci self.frame_keyseq_basic = Frame(self, name='keyseq_basic') 837db96d56Sopenharmony_ci self.frame_keyseq_basic.grid(row=0, column=0, sticky='nsew', 847db96d56Sopenharmony_ci padx=5, pady=5) 857db96d56Sopenharmony_ci basic_title = Label(self.frame_keyseq_basic, 867db96d56Sopenharmony_ci text=f"New keys for '{self.action}' :") 877db96d56Sopenharmony_ci basic_title.pack(anchor='w') 887db96d56Sopenharmony_ci 897db96d56Sopenharmony_ci basic_keys = Label(self.frame_keyseq_basic, justify='left', 907db96d56Sopenharmony_ci textvariable=self.key_string, relief='groove', 917db96d56Sopenharmony_ci borderwidth=2) 927db96d56Sopenharmony_ci basic_keys.pack(ipadx=5, ipady=5, fill='x') 937db96d56Sopenharmony_ci 947db96d56Sopenharmony_ci # Basic entry controls. 957db96d56Sopenharmony_ci self.frame_controls_basic = Frame(self) 967db96d56Sopenharmony_ci self.frame_controls_basic.grid(row=1, column=0, sticky='nsew', padx=5) 977db96d56Sopenharmony_ci 987db96d56Sopenharmony_ci # Basic entry modifiers. 997db96d56Sopenharmony_ci self.modifier_checkbuttons = {} 1007db96d56Sopenharmony_ci column = 0 1017db96d56Sopenharmony_ci for modifier, variable in zip(self.modifiers, self.modifier_vars): 1027db96d56Sopenharmony_ci label = self.modifier_label.get(modifier, modifier) 1037db96d56Sopenharmony_ci check = Checkbutton(self.frame_controls_basic, 1047db96d56Sopenharmony_ci command=self.build_key_string, text=label, 1057db96d56Sopenharmony_ci variable=variable, onvalue=modifier, offvalue='') 1067db96d56Sopenharmony_ci check.grid(row=0, column=column, padx=2, sticky='w') 1077db96d56Sopenharmony_ci self.modifier_checkbuttons[modifier] = check 1087db96d56Sopenharmony_ci column += 1 1097db96d56Sopenharmony_ci 1107db96d56Sopenharmony_ci # Basic entry help text. 1117db96d56Sopenharmony_ci help_basic = Label(self.frame_controls_basic, justify='left', 1127db96d56Sopenharmony_ci text="Select the desired modifier keys\n"+ 1137db96d56Sopenharmony_ci "above, and the final key from the\n"+ 1147db96d56Sopenharmony_ci "list on the right.\n\n" + 1157db96d56Sopenharmony_ci "Use upper case Symbols when using\n" + 1167db96d56Sopenharmony_ci "the Shift modifier. (Letters will be\n" + 1177db96d56Sopenharmony_ci "converted automatically.)") 1187db96d56Sopenharmony_ci help_basic.grid(row=1, column=0, columnspan=4, padx=2, sticky='w') 1197db96d56Sopenharmony_ci 1207db96d56Sopenharmony_ci # Basic entry key list. 1217db96d56Sopenharmony_ci self.list_keys_final = Listbox(self.frame_controls_basic, width=15, 1227db96d56Sopenharmony_ci height=10, selectmode='single') 1237db96d56Sopenharmony_ci self.list_keys_final.insert('end', *AVAILABLE_KEYS) 1247db96d56Sopenharmony_ci self.list_keys_final.bind('<ButtonRelease-1>', self.final_key_selected) 1257db96d56Sopenharmony_ci self.list_keys_final.grid(row=0, column=4, rowspan=4, sticky='ns') 1267db96d56Sopenharmony_ci scroll_keys_final = Scrollbar(self.frame_controls_basic, 1277db96d56Sopenharmony_ci orient='vertical', 1287db96d56Sopenharmony_ci command=self.list_keys_final.yview) 1297db96d56Sopenharmony_ci self.list_keys_final.config(yscrollcommand=scroll_keys_final.set) 1307db96d56Sopenharmony_ci scroll_keys_final.grid(row=0, column=5, rowspan=4, sticky='ns') 1317db96d56Sopenharmony_ci self.button_clear = Button(self.frame_controls_basic, 1327db96d56Sopenharmony_ci text='Clear Keys', 1337db96d56Sopenharmony_ci command=self.clear_key_seq) 1347db96d56Sopenharmony_ci self.button_clear.grid(row=2, column=0, columnspan=4) 1357db96d56Sopenharmony_ci 1367db96d56Sopenharmony_ci # Advanced entry key sequence. 1377db96d56Sopenharmony_ci self.frame_keyseq_advanced = Frame(self, name='keyseq_advanced') 1387db96d56Sopenharmony_ci self.frame_keyseq_advanced.grid(row=0, column=0, sticky='nsew', 1397db96d56Sopenharmony_ci padx=5, pady=5) 1407db96d56Sopenharmony_ci advanced_title = Label(self.frame_keyseq_advanced, justify='left', 1417db96d56Sopenharmony_ci text=f"Enter new binding(s) for '{self.action}' :\n" + 1427db96d56Sopenharmony_ci "(These bindings will not be checked for validity!)") 1437db96d56Sopenharmony_ci advanced_title.pack(anchor='w') 1447db96d56Sopenharmony_ci self.advanced_keys = Entry(self.frame_keyseq_advanced, 1457db96d56Sopenharmony_ci textvariable=self.key_string) 1467db96d56Sopenharmony_ci self.advanced_keys.pack(fill='x') 1477db96d56Sopenharmony_ci 1487db96d56Sopenharmony_ci # Advanced entry help text. 1497db96d56Sopenharmony_ci self.frame_help_advanced = Frame(self) 1507db96d56Sopenharmony_ci self.frame_help_advanced.grid(row=1, column=0, sticky='nsew', padx=5) 1517db96d56Sopenharmony_ci help_advanced = Label(self.frame_help_advanced, justify='left', 1527db96d56Sopenharmony_ci text="Key bindings are specified using Tkinter keysyms as\n"+ 1537db96d56Sopenharmony_ci "in these samples: <Control-f>, <Shift-F2>, <F12>,\n" 1547db96d56Sopenharmony_ci "<Control-space>, <Meta-less>, <Control-Alt-Shift-X>.\n" 1557db96d56Sopenharmony_ci "Upper case is used when the Shift modifier is present!\n\n" + 1567db96d56Sopenharmony_ci "'Emacs style' multi-keystroke bindings are specified as\n" + 1577db96d56Sopenharmony_ci "follows: <Control-x><Control-y>, where the first key\n" + 1587db96d56Sopenharmony_ci "is the 'do-nothing' keybinding.\n\n" + 1597db96d56Sopenharmony_ci "Multiple separate bindings for one action should be\n"+ 1607db96d56Sopenharmony_ci "separated by a space, eg., <Alt-v> <Meta-v>." ) 1617db96d56Sopenharmony_ci help_advanced.grid(row=0, column=0, sticky='nsew') 1627db96d56Sopenharmony_ci 1637db96d56Sopenharmony_ci # Switch between basic and advanced. 1647db96d56Sopenharmony_ci self.button_level = Button(self, command=self.toggle_level, 1657db96d56Sopenharmony_ci text='<< Basic Key Binding Entry') 1667db96d56Sopenharmony_ci self.button_level.grid(row=2, column=0, stick='ew', padx=5, pady=5) 1677db96d56Sopenharmony_ci self.toggle_level() 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_ci def set_modifiers_for_platform(self): 1707db96d56Sopenharmony_ci """Determine list of names of key modifiers for this platform. 1717db96d56Sopenharmony_ci 1727db96d56Sopenharmony_ci The names are used to build Tk bindings -- it doesn't matter if the 1737db96d56Sopenharmony_ci keyboard has these keys; it matters if Tk understands them. The 1747db96d56Sopenharmony_ci order is also important: key binding equality depends on it, so 1757db96d56Sopenharmony_ci config-keys.def must use the same ordering. 1767db96d56Sopenharmony_ci """ 1777db96d56Sopenharmony_ci if sys.platform == "darwin": 1787db96d56Sopenharmony_ci self.modifiers = ['Shift', 'Control', 'Option', 'Command'] 1797db96d56Sopenharmony_ci else: 1807db96d56Sopenharmony_ci self.modifiers = ['Control', 'Alt', 'Shift'] 1817db96d56Sopenharmony_ci self.modifier_label = {'Control': 'Ctrl'} # Short name. 1827db96d56Sopenharmony_ci 1837db96d56Sopenharmony_ci def toggle_level(self): 1847db96d56Sopenharmony_ci "Toggle between basic and advanced keys." 1857db96d56Sopenharmony_ci if self.button_level.cget('text').startswith('Advanced'): 1867db96d56Sopenharmony_ci self.clear_key_seq() 1877db96d56Sopenharmony_ci self.button_level.config(text='<< Basic Key Binding Entry') 1887db96d56Sopenharmony_ci self.frame_keyseq_advanced.lift() 1897db96d56Sopenharmony_ci self.frame_help_advanced.lift() 1907db96d56Sopenharmony_ci self.advanced_keys.focus_set() 1917db96d56Sopenharmony_ci self.advanced = True 1927db96d56Sopenharmony_ci else: 1937db96d56Sopenharmony_ci self.clear_key_seq() 1947db96d56Sopenharmony_ci self.button_level.config(text='Advanced Key Binding Entry >>') 1957db96d56Sopenharmony_ci self.frame_keyseq_basic.lift() 1967db96d56Sopenharmony_ci self.frame_controls_basic.lift() 1977db96d56Sopenharmony_ci self.advanced = False 1987db96d56Sopenharmony_ci 1997db96d56Sopenharmony_ci def final_key_selected(self, event=None): 2007db96d56Sopenharmony_ci "Handler for clicking on key in basic settings list." 2017db96d56Sopenharmony_ci self.build_key_string() 2027db96d56Sopenharmony_ci 2037db96d56Sopenharmony_ci def build_key_string(self): 2047db96d56Sopenharmony_ci "Create formatted string of modifiers plus the key." 2057db96d56Sopenharmony_ci keylist = modifiers = self.get_modifiers() 2067db96d56Sopenharmony_ci final_key = self.list_keys_final.get('anchor') 2077db96d56Sopenharmony_ci if final_key: 2087db96d56Sopenharmony_ci final_key = translate_key(final_key, modifiers) 2097db96d56Sopenharmony_ci keylist.append(final_key) 2107db96d56Sopenharmony_ci self.key_string.set(f"<{'-'.join(keylist)}>") 2117db96d56Sopenharmony_ci 2127db96d56Sopenharmony_ci def get_modifiers(self): 2137db96d56Sopenharmony_ci "Return ordered list of modifiers that have been selected." 2147db96d56Sopenharmony_ci mod_list = [variable.get() for variable in self.modifier_vars] 2157db96d56Sopenharmony_ci return [mod for mod in mod_list if mod] 2167db96d56Sopenharmony_ci 2177db96d56Sopenharmony_ci def clear_key_seq(self): 2187db96d56Sopenharmony_ci "Clear modifiers and keys selection." 2197db96d56Sopenharmony_ci self.list_keys_final.select_clear(0, 'end') 2207db96d56Sopenharmony_ci self.list_keys_final.yview('moveto', '0.0') 2217db96d56Sopenharmony_ci for variable in self.modifier_vars: 2227db96d56Sopenharmony_ci variable.set('') 2237db96d56Sopenharmony_ci self.key_string.set('') 2247db96d56Sopenharmony_ci 2257db96d56Sopenharmony_ci def ok(self): 2267db96d56Sopenharmony_ci self.result = '' 2277db96d56Sopenharmony_ci keys = self.key_string.get().strip() 2287db96d56Sopenharmony_ci if not keys: 2297db96d56Sopenharmony_ci self.showerror(title=self.keyerror_title, parent=self, 2307db96d56Sopenharmony_ci message="No key specified.") 2317db96d56Sopenharmony_ci return 2327db96d56Sopenharmony_ci if (self.advanced or self.keys_ok(keys)) and self.bind_ok(keys): 2337db96d56Sopenharmony_ci self.result = keys 2347db96d56Sopenharmony_ci return 2357db96d56Sopenharmony_ci 2367db96d56Sopenharmony_ci def keys_ok(self, keys): 2377db96d56Sopenharmony_ci """Validity check on user's 'basic' keybinding selection. 2387db96d56Sopenharmony_ci 2397db96d56Sopenharmony_ci Doesn't check the string produced by the advanced dialog because 2407db96d56Sopenharmony_ci 'modifiers' isn't set. 2417db96d56Sopenharmony_ci """ 2427db96d56Sopenharmony_ci final_key = self.list_keys_final.get('anchor') 2437db96d56Sopenharmony_ci modifiers = self.get_modifiers() 2447db96d56Sopenharmony_ci title = self.keyerror_title 2457db96d56Sopenharmony_ci key_sequences = [key for keylist in self.current_key_sequences 2467db96d56Sopenharmony_ci for key in keylist] 2477db96d56Sopenharmony_ci if not keys.endswith('>'): 2487db96d56Sopenharmony_ci self.showerror(title, parent=self, 2497db96d56Sopenharmony_ci message='Missing the final Key') 2507db96d56Sopenharmony_ci elif (not modifiers 2517db96d56Sopenharmony_ci and final_key not in FUNCTION_KEYS + MOVE_KEYS): 2527db96d56Sopenharmony_ci self.showerror(title=title, parent=self, 2537db96d56Sopenharmony_ci message='No modifier key(s) specified.') 2547db96d56Sopenharmony_ci elif (modifiers == ['Shift']) \ 2557db96d56Sopenharmony_ci and (final_key not in 2567db96d56Sopenharmony_ci FUNCTION_KEYS + MOVE_KEYS + ('Tab', 'Space')): 2577db96d56Sopenharmony_ci msg = 'The shift modifier by itself may not be used with'\ 2587db96d56Sopenharmony_ci ' this key symbol.' 2597db96d56Sopenharmony_ci self.showerror(title=title, parent=self, message=msg) 2607db96d56Sopenharmony_ci elif keys in key_sequences: 2617db96d56Sopenharmony_ci msg = 'This key combination is already in use.' 2627db96d56Sopenharmony_ci self.showerror(title=title, parent=self, message=msg) 2637db96d56Sopenharmony_ci else: 2647db96d56Sopenharmony_ci return True 2657db96d56Sopenharmony_ci return False 2667db96d56Sopenharmony_ci 2677db96d56Sopenharmony_ci def bind_ok(self, keys): 2687db96d56Sopenharmony_ci "Return True if Tcl accepts the new keys else show message." 2697db96d56Sopenharmony_ci try: 2707db96d56Sopenharmony_ci binding = self.bind(keys, lambda: None) 2717db96d56Sopenharmony_ci except TclError as err: 2727db96d56Sopenharmony_ci self.showerror( 2737db96d56Sopenharmony_ci title=self.keyerror_title, parent=self, 2747db96d56Sopenharmony_ci message=(f'The entered key sequence is not accepted.\n\n' 2757db96d56Sopenharmony_ci f'Error: {err}')) 2767db96d56Sopenharmony_ci return False 2777db96d56Sopenharmony_ci else: 2787db96d56Sopenharmony_ci self.unbind(keys, binding) 2797db96d56Sopenharmony_ci return True 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_ci 2827db96d56Sopenharmony_ciclass GetKeysWindow(Toplevel): 2837db96d56Sopenharmony_ci 2847db96d56Sopenharmony_ci def __init__(self, parent, title, action, current_key_sequences, 2857db96d56Sopenharmony_ci *, _htest=False, _utest=False): 2867db96d56Sopenharmony_ci """ 2877db96d56Sopenharmony_ci parent - parent of this dialog 2887db96d56Sopenharmony_ci title - string which is the title of the popup dialog 2897db96d56Sopenharmony_ci action - string, the name of the virtual event these keys will be 2907db96d56Sopenharmony_ci mapped to 2917db96d56Sopenharmony_ci current_key_sequences - list, a list of all key sequence lists 2927db96d56Sopenharmony_ci currently mapped to virtual events, for overlap checking 2937db96d56Sopenharmony_ci _htest - bool, change box location when running htest 2947db96d56Sopenharmony_ci _utest - bool, do not wait when running unittest 2957db96d56Sopenharmony_ci """ 2967db96d56Sopenharmony_ci super().__init__(parent) 2977db96d56Sopenharmony_ci self.withdraw() # Hide while setting geometry. 2987db96d56Sopenharmony_ci self['borderwidth'] = 5 2997db96d56Sopenharmony_ci self.resizable(height=False, width=False) 3007db96d56Sopenharmony_ci # Needed for winfo_reqwidth(). 3017db96d56Sopenharmony_ci self.update_idletasks() 3027db96d56Sopenharmony_ci # Center dialog over parent (or below htest box). 3037db96d56Sopenharmony_ci x = (parent.winfo_rootx() + 3047db96d56Sopenharmony_ci (parent.winfo_width()//2 - self.winfo_reqwidth()//2)) 3057db96d56Sopenharmony_ci y = (parent.winfo_rooty() + 3067db96d56Sopenharmony_ci ((parent.winfo_height()//2 - self.winfo_reqheight()//2) 3077db96d56Sopenharmony_ci if not _htest else 150)) 3087db96d56Sopenharmony_ci self.geometry(f"+{x}+{y}") 3097db96d56Sopenharmony_ci 3107db96d56Sopenharmony_ci self.title(title) 3117db96d56Sopenharmony_ci self.frame = frame = GetKeysFrame(self, action, current_key_sequences) 3127db96d56Sopenharmony_ci self.protocol("WM_DELETE_WINDOW", self.cancel) 3137db96d56Sopenharmony_ci frame_buttons = Frame(self) 3147db96d56Sopenharmony_ci self.button_ok = Button(frame_buttons, text='OK', 3157db96d56Sopenharmony_ci width=8, command=self.ok) 3167db96d56Sopenharmony_ci self.button_cancel = Button(frame_buttons, text='Cancel', 3177db96d56Sopenharmony_ci width=8, command=self.cancel) 3187db96d56Sopenharmony_ci self.button_ok.grid(row=0, column=0, padx=5, pady=5) 3197db96d56Sopenharmony_ci self.button_cancel.grid(row=0, column=1, padx=5, pady=5) 3207db96d56Sopenharmony_ci frame.pack(side='top', expand=True, fill='both') 3217db96d56Sopenharmony_ci frame_buttons.pack(side='bottom', fill='x') 3227db96d56Sopenharmony_ci 3237db96d56Sopenharmony_ci self.transient(parent) 3247db96d56Sopenharmony_ci _setup_dialog(self) 3257db96d56Sopenharmony_ci self.grab_set() 3267db96d56Sopenharmony_ci if not _utest: 3277db96d56Sopenharmony_ci self.deiconify() # Geometry set, unhide. 3287db96d56Sopenharmony_ci self.wait_window() 3297db96d56Sopenharmony_ci 3307db96d56Sopenharmony_ci @property 3317db96d56Sopenharmony_ci def result(self): 3327db96d56Sopenharmony_ci return self.frame.result 3337db96d56Sopenharmony_ci 3347db96d56Sopenharmony_ci @result.setter 3357db96d56Sopenharmony_ci def result(self, value): 3367db96d56Sopenharmony_ci self.frame.result = value 3377db96d56Sopenharmony_ci 3387db96d56Sopenharmony_ci def ok(self, event=None): 3397db96d56Sopenharmony_ci self.frame.ok() 3407db96d56Sopenharmony_ci self.grab_release() 3417db96d56Sopenharmony_ci self.destroy() 3427db96d56Sopenharmony_ci 3437db96d56Sopenharmony_ci def cancel(self, event=None): 3447db96d56Sopenharmony_ci self.result = '' 3457db96d56Sopenharmony_ci self.grab_release() 3467db96d56Sopenharmony_ci self.destroy() 3477db96d56Sopenharmony_ci 3487db96d56Sopenharmony_ci 3497db96d56Sopenharmony_ciif __name__ == '__main__': 3507db96d56Sopenharmony_ci from unittest import main 3517db96d56Sopenharmony_ci main('idlelib.idle_test.test_config_key', verbosity=2, exit=False) 3527db96d56Sopenharmony_ci 3537db96d56Sopenharmony_ci from idlelib.idle_test.htest import run 3547db96d56Sopenharmony_ci run(GetKeysDialog) 355