Much more up to date Demo of Autocomplete. Based on program by @bonklers

This commit is contained in:
PySimpleGUI 2021-03-30 17:55:20 -04:00
parent 06579d678e
commit a51bca66b7
1 changed files with 76 additions and 77 deletions

View File

@ -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()