From a51bca66b72639a97116a9a16972db3f12c2a562 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Tue, 30 Mar 2021 17:55:20 -0400 Subject: [PATCH] Much more up to date Demo of Autocomplete. Based on program by @bonklers --- DemoPrograms/Demo_Input_Auto_Complete.py | 153 +++++++++++------------ 1 file changed, 76 insertions(+), 77 deletions(-) diff --git a/DemoPrograms/Demo_Input_Auto_Complete.py b/DemoPrograms/Demo_Input_Auto_Complete.py index 435bb311..ab0ed90a 100644 --- a/DemoPrograms/Demo_Input_Auto_Complete.py +++ b/DemoPrograms/Demo_Input_Auto_Complete.py @@ -1,97 +1,96 @@ import PySimpleGUI as sg -import re -''' - Demo of using a borderless window to show possible matches for autocomplete feature -''' +""" + Autocomplete input + Thank you to GitHub user bonklers for supplying to basis for this demo! + There are 3 keyboard characters to be aware of: + * Arrow up - Change selected item in list + * Arrow down - Change selected item in list + * Escape - Erase the input and start over + * Return/Enter - use the current item selected from the list -def autocomplete_popup_show(text_list): - layout = [[ sg.Listbox(values=text_list, - size=(15, len(text_list)), - change_submits=True, bind_return_key=True, - key='-FLOATING-LISTBOX-', enable_events=True) ]] - return sg.Window("Borderless Window", - layout, - default_element_size=(12, 1), - auto_size_text=False, keep_on_top=True, - no_titlebar=True, grab_anywhere=True, - return_keyboard_events=True, - auto_size_buttons=False, - background_color='black', - default_button_element_size=(12, 1), - location=(1320, 622), finalize=True) + You can easily remove the ignore case option by searching for the "Irnore Case" Check box key: + '-IGNORE CASE-' -def predict_text(input, lista): - pattern = re.compile('.*' + input + '.*') - return [w for w in lista if re.match(pattern, w)] + The variable "choices" holds the list of strings your program will match against. + Even though the listbox of choices doesn't have a scrollbar visible, the list is longer than shown + and using your keyboard more of it will br shown as you scroll down with the arrow keys + The selection wraps around from the end to the start (and vicea versa). You can change this behavior to + make it stay at the beignning or the end + + Copyright 2021 PySimpleGUI +""" def main(): + # The list of choices that are going to be searched + # In this example, the PySimpleGUI Element names are used + choices = sorted([elem.__name__ for elem in sg.Element.__subclasses__()]) - choices = ['ABC' + str(i) for i in range(30)] # dummy data + input_width = 20 + num_items_to_show = 4 - layout = [[sg.Text('Your typed chars appear here:')], - [sg.Input(key='-INPUT-', size=(10, 1))], - [sg.Button('Show'), sg.Button('Exit')], ] + layout = [ + [sg.CB('Ignore Case', k='-IGNORE CASE-')], + [sg.Text('Input PySimpleGUI Element Name:')], + [sg.Input(size=(input_width, 1), enable_events=True, key='-IN-')], + [sg.pin(sg.Col([[sg.Listbox(values=[], size=(input_width, num_items_to_show), enable_events=True, key='-BOX-', + select_mode=sg.LISTBOX_SELECT_MODE_SINGLE, no_scrollbar=True)]], + key='-BOX-CONTAINER-', pad=(0, 0), visible=False))] + ] - window = sg.Window('Autocomplete Demo', layout, return_keyboard_events=True) + window = sg.Window('AutoComplete', layout, return_keyboard_events=True, finalize=True, font= ('Helvetica', 16)) - sel_item = -1 - fwindow = list_elem = values2 = None - while True: # Event Loop - event, values = window.read(timeout=500) + list_element:sg.Listbox = window.Element('-BOX-') # store listbox element for easier access and to get to docstrings + prediction_list, input_text, sel_item = [], "", 0 - if event in (sg.WIN_CLOSED, 'Exit'): + while True: # Event Loop + event, values = window.read() + # print(event, values) + if event == sg.WINDOW_CLOSED: break + # pressing down arrow will trigger event -IN- then aftewards event Down:40 + elif event.startswith('Escape'): + window['-IN-'].update('') + window['-BOX-CONTAINER-'].update(visible=False) + elif event.startswith('Down') and len(prediction_list): + sel_item = (sel_item + 1) % len(prediction_list) + list_element.update(set_to_index=sel_item, scroll_to_index=sel_item) + elif event.startswith('Up') and len(prediction_list): + sel_item = (sel_item + (len(prediction_list) - 1)) % len(prediction_list) + list_element.update(set_to_index=sel_item, scroll_to_index=sel_item) + elif event == '\r': + if len(values['-BOX-']) > 0: + window['-IN-'].update(value=values['-BOX-']) + window['-BOX-CONTAINER-'].update(visible=False) + elif event == '-IN-': + text = values['-IN-'] if not values['-IGNORE CASE-'] else values['-IN-'].lower() + if text == input_text: + continue + else: + input_text = text + prediction_list = [] + if text: + if values['-IGNORE CASE-']: + prediction_list = [item for item in choices if item.lower().startswith(text)] + else: + prediction_list = [item for item in choices if item.startswith(text)] - if event != sg.TIMEOUT_KEY: - # print(f'event1 {event}') - in_val = values['-INPUT-'] - prediction_list = predict_text(str(in_val), choices) - if prediction_list: - try: - fwindow.close() - except: - pass - fwindow = autocomplete_popup_show(prediction_list) - list_elem = fwindow['-FLOATING-LISTBOX-'] - if event == '_COMBO_': - sg.popup('Chose', values['_COMBO_']) + list_element.update(values=prediction_list) + sel_item = 0 + list_element.update(set_to_index=sel_item) - if event.startswith('Down') or event.startswith('special 16777237'): - sel_item = sel_item + (sel_item < len(prediction_list)) - list_elem.update(set_to_index=sel_item) - - elif event.startswith('Up') or event.startswith('special 16777235'): - sel_item = sel_item - (sel_item > 0) - list_elem.update(set_to_index=sel_item) - - if event == '\r' or event.startswith('special 16777220'): - chosen = values2['-FLOATING-LISTBOX-'] if values2 is not None else None - if chosen: - window['-INPUT-'].update(chosen[0], select=True) - fwindow.close() - sel_item = -1 - - if event.startswith('Escape') or event.startswith('special 16777216'): - window['-INPUT-'].update('') - - try: - event2, values2 = fwindow.read(timeout=10) - # if event2 == '-FLOATING-LISTBOX-' and skip_event and QT: - # skip_event = False - if event2 != sg.TIMEOUT_KEY and event2 is not None: - # print(f'event2 {event2}') - fwindow.close() - window['-INPUT-'].update(values2['-FLOATING-LISTBOX-'] - [0], select=True) - sel_item = -1 - fwindow = None - except: - pass + if len(prediction_list) > 0: + window['-BOX-CONTAINER-'].update(visible=True) + else: + window['-BOX-CONTAINER-'].update(visible=False) + elif event == '-BOX-': + window['-IN-'].update(value=values['-BOX-']) + window['-BOX-CONTAINER-'].update(visible=False) window.close() + if __name__ == '__main__': - main() + main() \ No newline at end of file