У меня есть пункт QAction, что я инициализировать следующим образом:
QAction* action = foo->addAction(tr("Some Action"));
connect(action, SIGNAL(triggered()), this, SLOT(onSomeAction()));
А потом onSomeAction выглядит примерно так:
void MyClass::onSomeAction()
{
QAction* caller = qobject_cast<QAction*>(sender());
Q_ASSERT(caller != nullptr);
// do some stuff with caller
}
Это прекрасно работает, я получаю объект вызывающим, и я'м возможность использовать его, как ожидалось. Затем я попробовать на C++11 способ подключения объекта как такового:
connect(action, &QAction::triggered, [this]()
{
QAction* caller = qobject_cast<QAction*>(sender());
Q_ASSERT(caller != nullptr);
// do some stuff with caller
});
Но абонент
всегда имеет значение null и, следовательно, Q_ASSERT
триггеров. Как я могу использовать лямбда-выражения, чтобы получить отправителя?
Ответ прост: вы можете'т. Или, скорее, вы не'т хотите (или нужно!) чтобы использовать отправитель()
. Просто захватить и использовать "действие".
// Important!
// vvvv
connect(action, &QAction::triggered, this, [action, this]() {
// use action as you wish
...
});
Спецификация " это " в качестве контекста объект для функтора гарантирует, что функтор не будет вызван, Если либо действия или это
(а от QObject
) прекратит свое существование. В противном случае функтор будет пытаться ссылаться висячие указатели.
В общем, необходимо держать при захвате переменных контекста на функтор передается "подключиться", чтобы избежать использования висячие указатели/ссылки:
подключения(а, &а::фу, Б [А, B]{});
Сценарии, где A
и B
находятся в разных потоках требуют особого внимания. Это не может быть гарантировано, что после того, как функтор вводится, какую-нить не будет удалять любой объект.
Это фразеологический оборот, что объект является только разрушение в своей теме (), или в какую-нить если нить() == nullptr мы. Поскольку поток'цикл событий вызывает функтор, нулевая нить-не проблема для " Б " - без резьбы функтором выиграл'т быть вызван. Увы, там's нет гарантии о жизни
ав
б'ы-нить. Таким образом, безопаснее захватить необходимые государственные действия по значению вместо этого, так что
а`'ы жизни не важна.
// Безопасный авто аимя = а->имя_объекта(); подключения(а, &а::фу, б [аимя, б]{ qDebug() << имя; }); // Опасные подключения(а, &а::фу, Б [А,B]{ qDebug() << а->имя_объекта(); });
статические ч; авто P = &с; подключения(..., [п]{});
статический D Д; подключения(..., [&ампер;D]{});
с std::shared_ptr<е> е { новый e }; QSharedPointer<Ф> Ф { новый F; } подключения(..., [Е,F]{});
Объект QObject живет в том же потоке может быть захвачен
QPointer`; его значение должно быть проверено перед использованием в функтор.QPointer<объект QObject> Г { это->родитель(); } подключения(..., [г]{ Если (г) ... });
класс I : общественные объект QObject { ... ~Я() { setparent осуществляет(и nullptr); } };
с std::shared_ptr&л;я> Я { я }; подключения(..., [я]{ ... });
с std::weak_ptr, на<и> к { я }; подключения(..., [Дж]{ авто ДЖП = Дж.заблокировать(); если (СП) { ... } });
Используя лямбда-выражения в качестве слотов проста (например, для события из QSpinbox):
connect(spinboxObject, &QSpinBox::editingFinished, this, [this]() {<do something>});
Но это работает только если сигнал не перегружается (это означает, что есть несколько сигналов с одинаковыми именами, но разными аргументами).
connect(spinboxObject, &QSpinBox::valueChange, this, [this]() {<do something>});
дает ошибку при компиляции, потому что существуют две перегруженные сигналы: valueChanged(int) и valueChanged(сопи в QString&) Так надо квалифицировать, какая версия должна использоваться:
connect(spinboxObject, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this](int newValue){ });
Немного короче (или лучше читается) является использование QOverload:
connect(spinboxObject, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int newValue) { });
Без "эта" в контексте, например, от главного():
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel lbl{"Hello World!"};
QPushButton btn;
btn.show();
lbl.show();
QObject::connect(&btn, &QPushButton::clicked, [&lbl](){lbl.setText("Button clicked");});
return a.exec();
}