New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] Multiline keyboard navigation (tab specifically) #5131
Comments
According tkinter reference
Note: It looks like only for sg.Multiline/tk.Text now, not for sg.Input/tk.Entry. |
Perhaps it's possible to bind to the tab key for the Multiline and get an event that you'll use to move to the next focus? I normally have not seen this behavior in GUIs, but I do notice for example there on this very Issue as I type the comment if I enter a tab, it being to traverse focus areas of this window. It's honestly the first time I've seen this in a user interface. |
I'm interested in this, is it actually possible? |
Here's a bind example that manually moves from the Multiline to the Input element. import PySimpleGUI as sg
layout = [ [sg.Text('My Window')],
[sg.Multiline(key='-MLINE-')],
[sg.Input(size=(12,1), key='-IN-')],
[sg.Button('Go'), sg.Button('Exit')] ]
window = sg.Window('Window Title', layout, finalize=True)
window['-MLINE-'].bind('<Tab>', '+TAB')
while True:
event, values = window.read()
print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event.endswith('+TAB'):
window['-IN-'].set_focus()
window.close() |
Wouldn't it be easier to link the TAB key to a TAB+CTRL keystroke in order to avoid the binding to every element and not create switching methods for every input and multiline? |
I've not looked at chaining a binding before to see if that's possible. Nor, as I said, had I dug into getting the focus order from tkinter to figure out what would normally be next. I was just responding to your request for the method I suggested which was to intercept the Tab and then take the appropriate action within your event loop. I've not done much with focus order manipulation before is all. |
Following code show the way to bind class to all sg.Multiline/tk.Text, then you can use Tab key to traverse focus to next. import PySimpleGUI as sg
def callback1(event):
event.widget.tk_focusNext().focus()
return 'break'
def callback2(event):
event.widget.tk_focusNext().focus()
layout = [
[sg.Input(key=(0, 1))],
[sg.Input()],
[sg.Multiline(size=(10, 5), expand_x=True, key='Multiline')],
[sg.Listbox(['1', '2', '3'], size=(5, 3)),
sg.Combo(['a', 'b', 'c'], size=(5, 3)),
sg.Button('Send')],
]
window = sg.Window('Title', layout, finalize=True) # Window finalized is required when binding
window['Multiline'].Widget.bind("<Tab>", callback1)
#window.TKroot.bind_class('Text', "<Tab>", callback2) # Binding on all tk.Text/sg.Multiline
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
window.close() Following code won't work well, Tab key won't "break" and will still add spaces into Multiline element. import PySimpleGUI as sg
layout = [
[sg.Input(key=(0, 1))],
[sg.Input()],
[sg.Multiline(size=(10, 5), expand_x=True, key='Multiline')],
[sg.Listbox(['1', '2', '3'], size=(5, 3)),
sg.Combo(['a', 'b', 'c'], size=(5, 3)),
sg.Button('Send')],
]
window = sg.Window('Title', layout, finalize=True) # Window finalized is required when binding
window['Multiline'].bind('<Tab>', ' Tab')
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == "Multiline Tab":
window['Multiline'].Widget.tk_focusNext().focus()
window.close() |
I knew when I wrote a response that I've not yet looked at the ordering of focus that it would be minutes before Jason would post an answer where he had figured out focus ordering in tkinter. Incredible support! This is one feature where I'm unsure exactly how I would go about adding an enhancement to handle the focus order. Maybe a call at the window level to move to the next element. In the second example, where the tab is added to the Multiline, it's possible to simply strip that tab off before moving to the next element. import PySimpleGUI as sg
layout = [
[sg.Input(key=(0, 1))],
[sg.Input()],
[sg.Multiline(size=(10, 5), expand_x=True, key='Multiline')],
[sg.Listbox(['1', '2', '3'], size=(5, 3)),
sg.Combo(['a', 'b', 'c'], size=(5, 3)),
sg.Button('Send')],
]
window = sg.Window('Title', layout, finalize=True) # Window finalized is required when binding
window['Multiline'].bind('<Tab>', ' Tab')
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == "Multiline Tab":
window['Multiline'].update(values['Multiline'].replace('\t', ''))
window['Multiline'].Widget.tk_focusNext().focus()
window.close() It won't traverse backwards however as the Shift+Tab needs to be bound as well, but does remove the tab and moves to the next element. |
Added to 4.57.0.18.... I've added 2 new methods to all Elements that will help with this in the future. It's bugged me for a while that the focus ordering isn't addressed at all in the APIs. I think that these changes and this sample code will do what is being requested in this issue. Both tab and Shift+Tab now traverse the window in the way one would expect. import PySimpleGUI as sg
layout = [
[sg.Input(key=(0, 1))],
[sg.Input()],
[sg.Multiline(size=(10, 5), expand_x=True, key='Multiline', focus=True)],
[sg.Listbox(['1', '2', '3'], size=(5, 3)),
sg.Combo(['a', 'b', 'c'], size=(5, 3)),
sg.Button('Send')]]
window = sg.Window('Title', layout, finalize=True) # Window finalized is required when binding
window['Multiline'].bind('<Tab>', ' Tab')
window['Multiline'].bind('<Shift-Tab>', ' ShiftTab')
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == "Multiline Tab":
window['Multiline'].update(values['Multiline'].replace('\t', ''))
window['Multiline'].get_next_focus().set_focus()
elif event == "Multiline ShiftTab":
window['Multiline'].get_previous_focus().set_focus()
window.close() |
Here's a new version of the code that uses the new Setting import PySimpleGUI as sg
layout = [
[sg.Input(key=(0, 1))],
[sg.Input()],
[sg.Multiline(size=(10, 5), expand_x=True, key='Multiline', focus=True)],
[sg.Listbox(['1', '2', '3'], size=(5, 3)),
sg.Combo(['a', 'b', 'c'], size=(5, 3)),
sg.Button('Send')]]
window = sg.Window('Title', layout, finalize=True) # Window finalized is required when binding
window['Multiline'].bind('<Tab>', ' Tab', propagate=False)
window['Multiline'].bind('<Shift-Tab>', ' ShiftTab')
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == "Multiline Tab":
window['Multiline'].get_next_focus().set_focus()
elif event == "Multiline ShiftTab":
window['Multiline'].get_previous_focus().set_focus()
window.close() |
Type of Issue (Enhancement, Error, Bug, Question)
Question
Operating System
Linux
PySimpleGUI Port (tkinter, Qt, Wx, Web)
tkinter
Versions
Python version: 3.10.2 (main, Jan 15 2022, 19:56:27) [GCC 11.1.0]
port: tkinter
tkinter version: 8.6.12
PySimpleGUI version: 4.56.0
PySimpleGUI filename: [...]/lib/python3.10/site-packages/PySimpleGUI/PySimpleGUI.py
Your Experience In Months or Years (optional)
Years Python programming experience
10
Years Programming experience overall
30
Have used another Python GUI Framework? (tkinter, Qt, etc) (yes/no is fine)
Yes
Anything else you think would be helpful?
Troubleshooting
Detailed Description
When I add a Multiline input, tab (the key, not the UI element) can no longer be used to navigate the window. CTRL+Tab works, but that's counterintuitive.
Code To Duplicate
Watcha Makin?
Playing around, discovering PySimpleGui. The main purpose of what I'm making is a fast data entry tool. Hence the need to quickly navigate the window using tab. Multiline currently breaks this flow. I can do without tab characters in my user input. Maybe I can override the default behavior somehow? I couldn't find anything in the docs or issues for this, but searching for this problem is difficult with 'tab' also being a UI element.
The text was updated successfully, but these errors were encountered: