Basic Widgets
The basic widgets are the leaf elements of every Qt UI: the label that displays text, the line edit that takes one line of input, the plain text edit that takes the document, the button that triggers an action, the checkbox and the radio that take a choice, the slider and the spin box that take a number, and the progress bar that shows state. Each has a small vocabulary of properties (the text, the enabled state, the focus policy) and a small set of signals (clicked, textChanged, toggled), and together they cover the majority of what a typical dialog or form is made of. This chapter introduces each with the properties and signals it actually uses, and ends with a form that composes them - the running example's settings dialog.
Labels, Line Edits, and Text Edits
from PyQt5.QtWidgets import (QLabel, QLineEdit, QPlainTextEdit,
QApplication)
import sys
app = QApplication(sys.argv)
# the label - displays text, takes no input:
label = QLabel("Name:")
label.setAlignment(Qt.AlignLeft)
# the rich text: the QLabel renders the HTML subset (the
# styled text, the link):
label.setHtml("The <b>NotePad</b> editor")
label.setTextInteractionFlags(
Qt.TextSelectableByMouse) # the selectable label
# the line edit - single-line text input:
edit = QLineEdit()
edit.setText("default")
edit.setMaxLength(60)
edit.setEchoMode(QLineEdit.Password) # the password dots
edit.setPlaceholderText("your name")
text = edit.text()
edit.textChanged.connect(on_text) # the change signal
# the plain text edit - the multi-line document (the editor):
doc = QPlainTextEdit()
doc.setPlainText("first line\nsecond line")
plain = doc.toPlainText()
doc.textChanged.connect(on_doc)
# the QPlainTextEdit is the running example's center -
# the editor's document widget (the model-view chapter
# shows the QTextEdit / the QPlainTextEdit choice)
The text widgets split by input. The QLabel displays - it has the text (the setText, the setHtml for the styled subset), the alignment, and the text interaction flags (the TextSelectableByMouse for the selectable label, the link), and no input state. The QLineEdit takes one line: the text()/setText for the value, the setMaxLength, the setEchoMode (the Password for the dots), the setPlaceholderText, and the textChanged signal on every change. The QPlainTextEdit is the multi-line document - the toPlainText/setPlainText for the whole document, the textChanged signal, and the cursor and the selection through its QTextCursor - and it is the running example's editor center (the QTextEdit is the rich-text variant, the choice the model-view chapter notes). The pattern across the three: the displayed content is a property (the text, the plainText) or the document object, and reading or writing the text goes through that property - the same uniformity the toolkit builds on.
Buttons, Checkboxes, Radios
from PyQt5.QtWidgets import (QPushButton, QCheckBox,
QRadioButton, QToolButton)
# the push button - the action trigger:
button = QPushButton("Save")
button.clicked.connect(on_save)
button.setEnabled(False) # the disabled state
button.setDefault(True) # the default (the Enter)
button.setShortcut("Ctrl+S") # the button's own shortcut
# the check box - the boolean toggle:
check = QCheckBox("Wrap long lines")
check.setChecked(True)
print(check.isChecked()) # True/False
check.toggled.connect(on_toggle) # the checked bool
# the radio buttons - mutually exclusive, grouped by parent
# (the radios under the same parent / the QButtonGroup):
r1 = QRadioButton("Light")
r2 = QRadioButton("Dark")
r3 = QRadioButton("System")
# the QButtonGroup is the explicit group (the id per button):
from PyQt5.QtWidgets import QButtonGroup
group = QButtonGroup()
group.addButton(r1, 0)
group.addButton(r2, 1)
group.addButton(r3, 2)
group.idClicked.connect(on_theme) # the clicked id
# the tool button - the icon button (the toolbar's):
tb = QToolButton()
tb.setText("Open")
# tb.setIcon(...) # the QIcon (the resource chapter)
tb.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
The choice and action widgets. The QPushButton is the action trigger - the clicked signal is the handler, the setEnabled is the disable, the setDefault marks it as the Enter-key default, and the setShortcut gives it its own shortcut. The QCheckBox is the boolean - the setChecked/isChecked for the state, the toggled signal with the checked argument. The radio buttons are the mutually-exclusive choice - the QRadioButtons under the same parent form an implicit group (one active), and the QButtonGroup is the explicit group (the addButton with the id, the idClicked/buttonToggled signals carrying the id) - the group is the data structure the exclusivity lives in, and the setExclusive controls whether the group allows the all-off state. The QToolButton is the icon button (the toolbar's, the setIcon, the toolButtonStyle for the icon-plus-text). The pattern is the one the whole chapter holds: each widget exposes its state through a getter/setter pair and its change through a signal, so reading state is a method call and reacting to change is a connection.
Sliders, Spin Boxes, Progress
from PyQt5.QtWidgets import (QSlider, QSpinBox, QDoubleSpinBox,
QProgressBar)
# the slider - the range control:
slider = QSlider(Qt.Horizontal)
slider.setRange(0, 100)
slider.setValue(40)
slider.setSingleStep(1)
slider.setPageStep(10)
slider.valueChanged.connect(on_value) # the int value
# the spin box - the numeric entry with the steppers:
spin = QSpinBox()
spin.setRange(1, 12)
spin.setValue(4)
spin.setSingleStep(1)
spin.valueChanged.connect(on_indent) # the int
# the double spin: the float, the decimals
dspin = QDoubleSpinBox()
dspin.setRange(0.0, 100.0)
dspin.setDecimals(2)
dspin.setValue(12.5)
# the progress bar - the state display:
bar = QProgressBar()
bar.setRange(0, 100)
bar.setValue(35)
bar.setFormat("%p%") # the percent format
# the indeterminate: the busy mode
bar.setRange(0, 0) # the 0/0 = the indeterminate
# the enabled state across all: the setEnabled(False)
# the keyboard: the slider / the spin respond to the
# arrows (the default focus behavior)
The numeric and state widgets. The QSlider is the range control - the setRange, the setValue, the singleStep/pageStep, and the valueChanged signal with the int value. The QSpinBox is the numeric entry with the steppers - the setRange, the setValue, the valueChanged with the int - and the QDoubleSpinBox is the float variant (the setDecimals for the precision). The QProgressBar is the state display - the setRange/setValue for the determinate (the setFormat for the %p% percent), and the setRange(0, 0) for the indeterminate busy mode. All of them share the setEnabled (the disable), and the keyboard behavior is the default (the arrows on the focused slider and spin). The pattern is the uniform one: the value through the value/setValue, the change through the valueChanged signal, and the range through the setRange - the same three operations for the numeric widgets, which is the consistency the form composition relies on.
Composing a Form: The Dialog
from PyQt5.QtWidgets import (QDialog, QDialogButtonBox,
QFormLayout, QLineEdit, QCheckBox,
QSpinBox, QApplication)
import sys
app = QApplication(sys.argv)
# the form dialog - the QFormLayout is the label-field grid:
dialog = QDialog()
dialog.setWindowTitle("Settings")
form = QFormLayout(dialog)
name = QLineEdit()
wrap = QCheckBox("Wrap long lines")
wrap.setChecked(True)
indent = QSpinBox()
indent.setRange(1, 12)
indent.setValue(4)
form.addRow("Name:", name)
form.addRow(wrap)
form.addRow("Indent:", indent)
# the button box - the standard OK/Cancel (the platform's):
buttons = QDialogButtonBox(QDialogButtonBox.Ok |
QDialogButtonBox.Cancel)
buttons.accepted.connect(dialog.accept) # the RESPONSE_Accepted
buttons.rejected.connect(dialog.reject) # the RESPONSE_Rejected
form.addRow(buttons)
# the run: the exec_ is the modal loop (the block until
# the response), the show is the modeless:
if dialog.exec_() == QDialog.Accepted:
print(name.text(), wrap.isChecked(), indent.value())
# the exec_ returns the dialog's result (the Accepted /
# the Rejected), and the accept / the reject are the slots
# the form is the composition: the QFormLayout's addRow
# (the label + the field, the field alone), the button box
# for the standard response, and the exec_ as the modal
# collection - the dialog's whole logic in the flow
The form is the composition of the basics in the dialog: the QFormLayout is the label-field grid (the addRow with the label and the field, or the field alone for the checkbox that is its own label), the QDialogButtonBox is the standard OK/Cancel (the platform's native buttons, the accepted/rejected signals connected to the dialog's accept/reject), and the exec_() is the modal loop that blocks until the response and returns the result (the QDialog.Accepted/Rejected). The read of every field happens after the exec_ returns the Accepted - the name.text(), the wrap.isChecked(), the indent.value() - which is the form's whole logic in the flow. The show() is the modeless variant (the dialog open while the app runs, the finished signal the response). The pattern is the one the dialogs chapter expands: the QDialog is the frame, the QFormLayout is the fields, the button box is the response, and the exec_ is the collection - the basic widgets supply the fields, the layout arranges them, and the dialog's result completes the interaction.
Summary
The basic widgets are the leaf vocabulary: the QLabel (the display, the setHtml for the styled subset, the selectable flag), the QLineEdit (the single-line input, the echoMode for the password, the placeholderText, the textChanged signal), the QPlainTextEdit (the multi-line document, the running example's editor), the QPushButton (the clicked action, the setDefault, the setShortcut), the QCheckBox and the QRadioButton (the checked state, the toggled signal, the QButtonGroup for the explicit exclusive group), the QSlider and the QSpinBox/QDoubleSpinBox (the value/setValue, the setRange, the valueChanged), and the QProgressBar (the determinate setValue, the setRange(0,0) indeterminate). Every widget shares the same two operations - the getter/setter for its value and the signal for its change - which is the uniformity the rest of the toolkit builds on, and the form composition (the QFormLayout fields, the QDialogButtonBox, the exec_ collection) is the pattern that assembles them into a working dialog.