[Qgis-user] keyboard navigation in forms with multiline text fields
Ludwig Kniprath
ludwig at kni-online.de
Wed Sep 23 14:38:08 PDT 2026
Hello Tobias,
interesting problem, because in "multiline-text-fields" (Qt:
QPlainTextEdit rsp. QTextBrowser) the Tab-Key always has the function to
insert a tab, what else... :)
The workaround-script below handles edits in "multiline-text-fields", if
the user inserts a tab, it will instead switch the focus to the next
form-element. By using this, the user won't be able to insert any tabs
at all.
It has to be registered as init-code for the forms of your layer, see below.
Surprisingly complicated, perhaps there's a simpler solution...
Incidentally, Shift-Tab has no special function in multi-line text
fields and simply moves the focus to the previous element.
from qgis.PyQt.QtWidgets import
QDialog,QWidget,QPlainTextEdit,QTextBrowser,QCheckBox,QDateTimeEdit,QFrame
from qgis.PyQt.QtCore import Qt,QObject
from qgis.PyQt.QtGui import QTextCursor,QTextDocument
from qgis.gui import QgsFilterLineEdit,QgsSpinBox
def contents_change(position:int, chars_removed:int, chars_added: int):
"""triggered, if the content of the connected
form_widget.document() has changed
Will capture a single tab-insert and instead switch the focus to
the next form-element.
Args:
position (int): position where edit was done
chars_removed (int): number of removed chars
chars_added (int): number of added chars
"""
# check 1: only one char added
if chars_added == 1:
# QTextDocument, which was connected with this slot
q_txt_dcmt:QTextDocument = QObject().sender()
# check 2: and it was a tab
last_char:str = q_txt_dcmt.toPlainText()[position]
if last_char == "\t":
# undo edit by deleting last_char
cursor = QTextCursor(q_txt_dcmt)
cursor.setPosition(position)
cursor.deleteChar()
# searching for QFrame with objectName 'mAttributeEditor'
as topmost widget for the focusable form-elements
# bottom-up from QObject().sender()
att_ed_qfr:QFrame = None
qpte:QTextBrowser|QPlainTextEdit = None
parent = QObject().sender().parent()
while parent:
if parent.objectName() and parent.__class__ in
[QTextBrowser,QPlainTextEdit]:
# the triggering widget
qpte = parent
if parent.objectName() == "mAttributeEditor":
# topmost widget
att_ed_qfr = parent
break
parent = parent.parent()
if att_ed_qfr and qpte:
qpte.setPlainText(q_txt_dcmt.toPlainText())
form_widgets = []
form_idx = 0
# findChildren fortunately delivers the widgets in
tabIndex-order...
for widget in
att_ed_qfr.findChildren(QWidget,options=Qt.FindChildOption.FindChildrenRecursively):
# hope to have all QGis-used widget-classes for
attribute-forms
if widget.objectName() and widget.__class__ in
[QTextBrowser,QPlainTextEdit,QgsFilterLineEdit,QCheckBox,QgsSpinBox,QDateTimeEdit]:
if widget == qpte:
# the index of the triggering widget
form_idx = len(form_widgets)
form_widgets.append(widget)
# focus next form-element
focus_idx = form_idx + 1 if form_idx <
len(form_widgets) - 1 else 0
form_widgets[focus_idx].setFocus()
else:
raise Exception("Something went terribly wrong...")
def my_form_open(dialog:QDialog, layer, feature):
# identify the multiline form-widgets by type and objectName
(matching to field-name)
# and connect the contentsChange-signal to widget.document() (!)
all_field_names = [field.name() for field in layer.fields()]
for widget in
dialog.findChildren(QWidget,options=Qt.FindChildOption.FindChildrenRecursively):
if widget.objectName() in all_field_names and
(isinstance(widget,QPlainTextEdit) or isinstance(widget,QTextBrowser)):
widget.document().contentsChange.connect(contents_change)
Register as Attribute-form-init-function: Copy&Paste code, adopt the
other settings
Depending on your settings messages will be displayed each time you open
a form:
That gets annoying after a while and can be prevented as follows:
Settings > Options > General > Project Files
hth
Ludwig
Am 22.09.26 um 16:16 schrieb Tobias Schula via QGIS-User:
> Hello list!
>
> I've designed an input form that contains multiline text fields for a
> longer description of a feature. The users should be able to use the
> form with the keyboard, so they can navigate with "Tab", "Shift+Tab",
> accept changes with "Enter" and decline changes with "Esc".
>
> However, once I put a multiline text field in the form, the user is
> trapped in this field and cannot escape without using the mouse. This
> greatly interrupts the workflow, since the users are usually inputting
> hand written field notes at their desk.
>
> I can't seem to find any documentation on the navigation in forms, nor
> have I gotten to work any combinations of "Ctrl", "Shift", "Tab" or
> "Enter". I can only close the form with "Esc", but then all input will
> be lost.
>
> Does anyone have any idea or can someone point me to documentation? If
> there is no way to exit multiline fields, I will file a bug/feature
> request.
>
>
> Best regards
>
>
> _______________________________________________
> QGIS-User mailing list
> QGIS-User at lists.osgeo.org
> List info:https://lists.osgeo.org/mailman/listinfo/qgis-user
> Unsubscribe:https://lists.osgeo.org/mailman/listinfo/qgis-user
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osgeo.org/pipermail/qgis-user/attachments/20260923/a9bb81e1/attachment-0001.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ow5eEI1TGLU0z83k.png
Type: image/png
Size: 63084 bytes
Desc: not available
URL: <http://lists.osgeo.org/pipermail/qgis-user/attachments/20260923/a9bb81e1/attachment-0004.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: vrBExeLr9bJkcRdA.png
Type: image/png
Size: 76661 bytes
Desc: not available
URL: <http://lists.osgeo.org/pipermail/qgis-user/attachments/20260923/a9bb81e1/attachment-0005.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: EpAXgPR4SqZCucPK.png
Type: image/png
Size: 15191 bytes
Desc: not available
URL: <http://lists.osgeo.org/pipermail/qgis-user/attachments/20260923/a9bb81e1/attachment-0006.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: RuWx0RtgAgtIIgm1.png
Type: image/png
Size: 90228 bytes
Desc: not available
URL: <http://lists.osgeo.org/pipermail/qgis-user/attachments/20260923/a9bb81e1/attachment-0007.png>
More information about the QGIS-User
mailing list