7. Text Widgets
While Qt buttons and the numeric widgets let the user enter binary and numerical data, text widgets support plain text and rich text input. Qt offers three text widgets:
QLineEdit- single-line plain-text editing,QTextEdit- provides multi-line richtext with Markdown and HTML[^1] support,QPlainTextEdit- multi-line plain-text with optional support for syntax coloring.
7.1 QLineEdit
QLineEdit provides a single-line plain-text editor. It supports the usual edit operations (copy, cut, paste, undo, redo) via standard keyboard shortcuts and a built-in context menu. You can restrict input length with setMaxLength(), validate input using a QValidator or input mask, and turn the widget into a password field by setting echoMode to EchoMode.Password.
Here are the QLineEdit signals you are likely to use:
| Signal | When Emitted |
|---|---|
textChanged(text) |
Any change to the text (user or programmatic) |
textEdited(text) |
Only when the user types, pastes, or deletes |
returnPressed() |
Enter/Return key is pressed |
editingFinished() |
Enter pressed or the line edit loses focus |
selectionChanged() |
Selected text changes |
inputRejected() |
A validator rejects the input |
![]() |
Suppose you need a text field that accepts only letters and warns the user about invalid input. |
Here’s how to do it:
1 # The QLineEdit widget is a one-line text editor.
2
3 import sys
4
5 from PySide6.QtCore import Slot
6 from PySide6.QtGui import QRegularExpressionValidator
7 from PySide6.QtWidgets import (QApplication,
8 QWidget, QVBoxLayout, QLineEdit, QLabel)
9
10
11 class Window(QWidget):
12
13 def __init__(self):
14
15 super().__init__()
16 self.resize(240, 20)
17
18 layout = QVBoxLayout()
19 self.setLayout(layout)
20
21 # 1 - Create a line edit widget instance
22
23 self.line_edit = QLineEdit()
24 validator = QRegularExpressionValidator('^[a-zA-Z]*$')
25 self.line_edit.setValidator(validator)
26
27 self.label = QLabel()
28
29 # 3 - Connect the signals with the slots
30
31 self.line_edit.editingFinished.connect(self.on_editing_finished)
32 self.line_edit.inputRejected.connect(self.on_input_rejected)
33
34 layout.addWidget(self.line_edit)
35 layout.addWidget(self.label)
36
37 # 2 - Create methods to handle line edit signals.
38
39 @Slot()
40 def on_editing_finished(self):
41 self.label.setText(
42 f'Editing finished: {self.line_edit.text()}')
43
44 @Slot()
45 def on_input_rejected(self):
46 self.label.setText('Only letters allowed')
47
48
49 if __name__ == '__main__':
50
51 app = QApplication(sys.argv)
52 main_window = Window()
53 main_window.show()
54 sys.exit(app.exec())
Create a
QLineEditobject, attach a regular expression validator that allows only letters, and add aQlabelto display feedback.Implement two slots: one to show the final text when editing finishes, and another to warn the user when an invalid character is rejected.
Connect the
editingFinished()andinputRejected()signal to the corresponding slots.

7.2 QTextEdit
QTextEdit is a widget for displaying and editing both plain text and rich text. It can handle large documents efficiently and can be used as an advanced WYSIWYG editor that support rich text formatting via HTML or Markdown. Internally, QtextEdit uses a QTextDocument object, which organizes content in a hierarchy of frames, blocks and fragments.
Commonly used QTextEdit signals:
| Signal | Description |
|---|---|
textChanged() |
Emitted when document’s content changes |
cursorPositionChanged() |
Emitted when cursor position changes |
copyAvailable(yes) |
Emitted when the availability of copy changes |
redoAvailable(available) |
Emitted when redo becomes available/unavailable |
undoAvailable(available) |
Emitted when undo becomes available/unavailable |
selectionChanged() |
Emitted when current selection changes |
Note: textChanged() is emitted for both user edits and programmatic changes (e.g., calling setPlainText(), setHtml(), or setMarkdown()).
![]() |
Let’s say you need to create a basic Markdown editor with side-by-side live preview. |
To do it:
1 # The QTextEdit class provides a widget that is used
2 # to edit and display both plain and rich text.
3
4 import sys
5 from PySide6.QtCore import Slot
6 from PySide6.QtGui import QFontDatabase
7 from PySide6.QtWidgets import (QApplication,
8 QWidget, QHBoxLayout, QTextEdit)
9
10
11 class Window(QWidget):
12
13 def __init__(self):
14
15 super().__init__()
16
17 layout = QHBoxLayout()
18 self.setLayout(layout)
19
20 # 1. Create the source textedit instance
21
22 self.src = QTextEdit()
23 self.src.textChanged.connect(self.update_preview)
24
25 mono = QFontDatabase.systemFont(
26 QFontDatabase.SystemFont.FixedFont)
27 mono.setPointSize(11)
28 self.src.setFont(mono)
29
30 # 2. Create the preview textedit instance
31
32 self.preview = QTextEdit()
33 self.preview.setReadOnly(True)
34 self.preview.setStyleSheet('background-color: #f0f0f0;')
35
36 layout.addWidget(self.src)
37 layout.addWidget(self.preview)
38
39 self.src.setText('### Enter Markdown Text\n\n')
40
41 # 3. Implement the slot to preview the entered text
42
43 @Slot()
44 def update_preview(self):
45 markdown_text = self.src.toPlainText()
46 self.preview.setMarkdown(markdown_text)
47
48
49 if __name__ == '__main__':
50
51 app = QApplication(sys.argv)
52 main_window = Window()
53 main_window.show()
54 sys.exit(app.exec())
Create the source editor. Instantiate
QTextEditfor editing. Use monospace font for better allignment.Create the preview widget. Instantiate a second
QTextEditthat will display the rendered Markdown. Make it read-only and give it a distinct appearance.Implement the preview slot to and connect the signal. Get the source textedit contents as plain text and use
setMarkdown()to render it as Markdown in the preview textedit. By default,setMarkdown()uses the GitHub-flavored Markdown dialect.
Note that we perform the initial setPlainText() only after connecting the textChanged() signal. This ensures the slot runs immediately and the preview is rendered with the initial text.

7.3 QPlainTextEdit
QPlainTextEdit provides a widget optimized for editing and displaying plain text. Unlike QTextEdit, it does not support HTML or Markdown.
A QPlainTextEdit document is composed of characters and blocks (paragraphs) separated by newline characters. Each block contains a sequence of characters with optional formatting.
![]() |
You are tasked with creating a basic plain-text editor that uses a monospace font and shows the current cursor line and column, as well as the total text length. To do this: |
1 # The QPlainTextEdit class provides a widget
2 # that is used to edit and display plain text.
3
4 import sys
5 from PySide6.QtCore import Slot
6 from PySide6.QtGui import QFontDatabase
7 from PySide6.QtWidgets import (QApplication,
8 QWidget, QVBoxLayout, QPlainTextEdit, QLabel)
9
10
11 class Window(QWidget):
12
13 def __init__(self):
14
15 super().__init__()
16
17 layout = QVBoxLayout()
18 self.setLayout(layout)
19
20 # 1. Create the plain text edit widget
21 # and set its font and wrap mode.
22
23 self.editor = QPlainTextEdit()
24 self.editor.setLineWrapMode(
25 QPlainTextEdit.LineWrapMode.NoWrap)
26 mono = QFontDatabase.systemFont(
27 QFontDatabase.SystemFont.FixedFont)
28 mono.setPointSize(11)
29 self.editor.setFont(mono)
30
31 # Labels displaying document stats.
32
33 self.charcount_label = QLabel()
34 self.position_label = QLabel()
35
36 layout.addWidget(self.editor)
37 layout.addWidget(self.charcount_label)
38 layout.addWidget(self.position_label)
39
40 # 3. Connect the signals to the slots
41 self.editor.textChanged.connect(self.update_char_count)
42 self.editor.cursorPositionChanged.connect(self.update_position)
43
44 self.editor.setPlainText('print("Hello PySide6")')
45
46 # 2. Get the underlying QTextDocument properties
47 # Character count is directly available.
48 # Cursor position needs to be calculated.
49
50 @Slot()
51 def update_char_count(self):
52 char_count = self.editor.document().characterCount()
53 self.charcount_label.setText(f'Char count: {char_count}')
54
55 @Slot()
56 def update_position(self):
57
58 cursor = self.editor.textCursor()
59 x = str(cursor.block().blockNumber() + 1)
60 y = str(cursor.positionInBlock() + 1)
61
62 self.position_label.setText(f'Line: {x} Column: {y}')
63
64
65 if __name__ == '__main__':
66
67 app = QApplication(sys.argv)
68 main_window = Window()
69 main_window.show()
70 sys.exit(app.exec())
Create a
QPlainTextEditobject, useQFontDatabaseto set its font to a generic monospace font, and disable line wrapping. Also add two labels to the window: one for showing character count, and the other for showing cursor position.Implement the slots to display text stats. In
update_char_count(), retrieve the character count directly from the editor’s underlyingQTextDocumentand display it in the label. Inupdate_position(), use the editor’sQTextCursorto get the current block number and the current cursor position within the block, then use these to calculate the cursor’s current line (block number + 1) and column (position within the block + 1).Connect the signals to the slots. Update the character count when
textChanged()emits, update cursor position whencursorPositionChanged()emits.

