Much more up to date Demo of Autocomplete. Based on program by @bonklers
This commit is contained in:
parent
06579d678e
commit
a51bca66b7
|
@ -1,97 +1,96 @@
|
||||||
import PySimpleGUI as sg
|
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):
|
You can easily remove the ignore case option by searching for the "Irnore Case" Check box key:
|
||||||
layout = [[ sg.Listbox(values=text_list,
|
'-IGNORE CASE-'
|
||||||
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)
|
|
||||||
|
|
||||||
def predict_text(input, lista):
|
The variable "choices" holds the list of strings your program will match against.
|
||||||
pattern = re.compile('.*' + input + '.*')
|
Even though the listbox of choices doesn't have a scrollbar visible, the list is longer than shown
|
||||||
return [w for w in lista if re.match(pattern, w)]
|
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():
|
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:')],
|
layout = [
|
||||||
[sg.Input(key='-INPUT-', size=(10, 1))],
|
[sg.CB('Ignore Case', k='-IGNORE CASE-')],
|
||||||
[sg.Button('Show'), sg.Button('Exit')], ]
|
[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
|
list_element:sg.Listbox = window.Element('-BOX-') # store listbox element for easier access and to get to docstrings
|
||||||
fwindow = list_elem = values2 = None
|
prediction_list, input_text, sel_item = [], "", 0
|
||||||
while True: # Event Loop
|
|
||||||
event, values = window.read(timeout=500)
|
|
||||||
|
|
||||||
if event in (sg.WIN_CLOSED, 'Exit'):
|
while True: # Event Loop
|
||||||
|
event, values = window.read()
|
||||||
|
# print(event, values)
|
||||||
|
if event == sg.WINDOW_CLOSED:
|
||||||
break
|
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:
|
list_element.update(values=prediction_list)
|
||||||
# print(f'event1 {event}')
|
sel_item = 0
|
||||||
in_val = values['-INPUT-']
|
list_element.update(set_to_index=sel_item)
|
||||||
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_'])
|
|
||||||
|
|
||||||
if event.startswith('Down') or event.startswith('special 16777237'):
|
if len(prediction_list) > 0:
|
||||||
sel_item = sel_item + (sel_item < len(prediction_list))
|
window['-BOX-CONTAINER-'].update(visible=True)
|
||||||
list_elem.update(set_to_index=sel_item)
|
else:
|
||||||
|
window['-BOX-CONTAINER-'].update(visible=False)
|
||||||
elif event.startswith('Up') or event.startswith('special 16777235'):
|
elif event == '-BOX-':
|
||||||
sel_item = sel_item - (sel_item > 0)
|
window['-IN-'].update(value=values['-BOX-'])
|
||||||
list_elem.update(set_to_index=sel_item)
|
window['-BOX-CONTAINER-'].update(visible=False)
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
window.close()
|
window.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
Loading…
Reference in New Issue