Qt5. Как временно отключить генерацию событий от дочерних виджетов?
Пишу виджет редактора параметров и возникла проблема с генерацией событий от дочерних виджетов.
Сам виджет подписывается на изменение дочерних и пересылает события выше.
Допустим, редактор содержит пару QLineEdit:
class Editor(QWidget):
paramsChanged = Signal(str, str)
def __init__(self, parent= None):
super().__init__(parent)
self.__build_ui()
def __build_ui(self):
self.__a = QLineEdit()
self.__b = QLineEdit();
box = QVBoxLayout()
box.addWidget(self.__a)
box.addWidget(self.__b)
self.__a.textChanged.connect(self.__on_paramChanged)
self.__b.textChanged.connect(self.__on_paramChanged)
self.setLayout(box)
def __on_paramChanged(self):
self.paramsChanged.emit(self.__a.text(), self.__b.text())
def setParams(self, a, b):
self.__a.setText(a)
self.__b.setText(b)
При программной установке значений дочерние виджеты генерируют события "изменено".
editor.setParams("a", "b")
Лог:
changed a
changed a b
Собственно вопрос:
Как на время загрузки параметров(Editor::setParams) отключить генерацию событий от дочерних виджетов?
Полный код на Python + PySide2
# -*- coding: utf-8 -*-
import sys
from PySide2.QtCore import *
from PySide2.QtWidgets import *
class Editor(QWidget):
paramsChanged = Signal(str, str)
def __init__(self, parent= None):
super().__init__(parent)
self.__build_ui()
def __build_ui(self):
self.__a = QLineEdit()
self.__b = QLineEdit();
box = QVBoxLayout()
box.addWidget(self.__a)
box.addWidget(self.__b)
self.__a.textChanged.connect(self.__on_paramChanged)
self.__b.textChanged.connect(self.__on_paramChanged)
self.setLayout(box)
def __on_paramChanged(self):
self.paramsChanged.emit(self.__a.text(), self.__b.text())
def setParams(self, a, b):
self.__a.setText(a)
self.__b.setText(b)
def on_change(a, b):
print("changed", a, b)
if __name__ == '__main__':
app = QApplication(sys.argv)
editor = Editor()
editor.paramsChanged.connect(on_change)
editor.show()
editor.setParams("a", "b")
sys.exit(app.exec_())
Ответы (2 шт):
Заблокировать сигналы от наследника QObject (включая виджеты):
QObject.blockSignals(True)
Разблокировать, соответственно:
QObject.blockSignals(False)
Дополнение к ответу от Alexander Chernin
Если при наложении блокировки она была уже активирована , то вызов
object->blockSignals(false); ее снимет, что может сломать вызывающий код.
Например:
// Наш класс
class MyObject: public QObject
{
Q_OBJECT
public:
void setValues(/* */);
// ...
signals:
void valueChanged();
// ...
};
void MyObject::setValues(/* */)
{
blockSignals(true);
// ...
// какой то код для установки значения в дочерние объекты
// ...
blockSignals(false); // безусловная разблокировка
emit valueCanged(); // генерация сигнала
}
Вызывающий код
constructor()
{
object = new MyObject(this);
connect(object, ...);
}
void updateObject()
{
object->blockSignals(true);
// ...
object->setValues(...);
// < тут сигналы от object уже разблокированы
// ...
object->blockSignals(false);
}
QObject::blockSignals возвращает предыдущее состояние блокировки сигналов для данного объекта, что позволяет восстанавливать предыдущее состояние.
То есть код методов MyObject::setValues и updateObject нужно модифицировать:
void MyObject::setValues(/* */)
{
auto state = blockSignals(true); // сохранение старого состояния
// ...
// какой то код для установки значения в дочерние объекты
// ...
blockSignals(state); // восстановить состояние блокировки
emit valueCanged(); // генерация сигнала
}
void updateObject()
{
auto state = object->blockSignals(true);
// ...
object->setValues(...);
// OK: сигналы от object еще заблокированы
// ...
object->blockSignals(state);
}
Для того чтобы не забывать об этом можно использовать вспомогательный класс QSignalBlocker.
Его алгоритм и метод использования напоминает lock guard
void updateObject()
{
QSignalBlocker block(object); // блокировка
// ...
object->setValues(...);
// ...
// автоматическое восстановление состояния блокировки
}
Пример для Python
def updateObject(self):
_ = QSignalBlocker(self.object);
self.object.setValues(...)