Сигналы, слоты и свои сигналы
Сигналы и слоты — сердце Qt. Виджеты сообщают о событиях сигналами, а ваш код на них отвечает.
Сигналы с данными
edit.textChanged.connect(self.on_text) # on_text(self, text)
spin.valueChanged.connect(self.on_value) # on_value(self, value)
checkbox.toggled.connect(self.on_toggle) # on_toggle(self, checked)
Слот получает данные сигнала аргументами. Если они не нужны — слот может их не принимать.
textChanged срабатывает и при наборе с клавиатуры, и при программном setText. У QLineEdit есть ещё textEdited — только для изменений, сделанных человеком, и returnPressed — нажатие Enter.
lambda в connect
Чтобы передать слоту свои данные, используют lambda:
for key in "0123456789":
button = QPushButton(key)
button.clicked.connect(lambda _=False, k=key: self.press(k))
clicked передаёт аргумент checked, поэтому первый параметр _. А k=key фиксирует текущее значение: без этого все кнопки получили бы последнее значение key.
Блокировка сигналов
Если два виджета обновляют друг друга, возникает круг: поле A меняет B, B испускает сигнал и меняет A… Разрывают круг блокировкой:
edit.blockSignals(True)
edit.setText(text) # сигнал не испускается
edit.blockSignals(False)
Не забывайте включить сигналы обратно — иначе виджет «замолчит» навсегда. Для надёжности есть QSignalBlocker(edit) — блокирует до конца блока with.
Свои сигналы
Свои классы тоже могут испускать сигналы — так данные программы сообщают окну об изменениях, ничего не зная о самом окне:
from PySide6.QtCore import QObject, Signal
class NoteStore(QObject):
changed = Signal(int) # атрибут КЛАССА; int — тип данных сигнала
def __init__(self):
super().__init__() # обязательно для QObject
self.notes = []
def add(self, text):
self.notes.append(text)
self.changed.emit(len(self.notes))
store = NoteStore()
store.changed.connect(lambda n: label.setText(f"Заметок: {n}"))
Испускайте сигнал, только когда что-то действительно изменилось, — иначе окно будет перерисовываться зря, а подписчики получат ложные уведомления.
Итог
- сигналы несут данные:
textChanged(str),valueChanged(int); lambda _=False, k=k: …— свои данные в слот;blockSignals(True/False)разрывает круги обновлений;- свой сигнал:
Signal(тип)в классе-наследникеQObject,emit(…).