Saya memiliki QAction item yang saya menginisialisasi seperti berikut:
QAction* action = foo->addAction(tr("Some Action"));
connect(action, SIGNAL(triggered()), this, SLOT(onSomeAction()));
Dan kemudian onSomeAction mirip seperti:
void MyClass::onSomeAction()
{
QAction* caller = qobject_cast<QAction*>(sender());
Q_ASSERT(caller != nullptr);
// do some stuff with caller
}
Ini bekerja baik-baik saja, saya mendapatkan pemanggil
kembali benda dan I'm dapat menggunakannya seperti yang diharapkan. Kemudian saya mencoba C++11 cara untuk menghubungkan objek seperti:
connect(action, &QAction::triggered, [this]()
{
QAction* caller = qobject_cast<QAction*>(sender());
Q_ASSERT(caller != nullptr);
// do some stuff with caller
});
Tapi pemanggil
adalah selalu nol dan dengan demikian Q_ASSERT
pemicu. Bagaimana saya bisa menggunakan lambdas untuk mendapatkan pengirim?
Jawaban yang sederhana adalah: anda dapat't. Atau, lebih tepatnya, anda don't ingin (atau perlu!) untuk menggunakan pengirim()
. Hanya menangkap dan menggunakan action
.
// Important!
// vvvv
connect(action, &QAction::triggered, this, [action, this]() {
// use action as you wish
...
});
Spesifikasi ini
sebagai objek konteks untuk functor memastikan bahwa functor tidak akan dipanggil jika salah tindakan atau ini
(a QObject
) tidak ada lagi. Jika tidak, functor akan mencoba untuk referensi dangling pointer.
Secara umum, berikut ini harus terus ketika menangkap konteks variabel untuk sebuah functor berlalu untuk connect
, dalam rangka untuk menghindari penggunaan dangling pointer/referensi:
connect
dapat ditangkap oleh nilai, seperti di atas. Itu dijamin bahwa jika functor dipanggil, kedua ujung koneksi yang ada. menghubungkan(a, &A::foo, b, [a, b]{});
Skenario di mana a
dan b
berada di benang yang berbeda memerlukan perhatian khusus. Hal ini tidak dapat dijamin bahwa setelah functor dimasukkan, beberapa benang tidak akan menghapus objek.
Itu adalah ungkapan bahwa sebuah objek hanya hancur dalam thread()
, atau di thread apapun jika benang() == nullptr
. Sejak thread's acara loop memanggil functor, null thread ini pernah ada masalah selama b
- tanpa benang functor won't akan dipanggil. Sayangnya, ada's tidak ada jaminan tentang masa a
b
's benang. Hal ini dengan demikian lebih aman untuk menangkap diperlukan negara dari tindakan dengan nilai sebaliknya, sehingga a
's seumur hidup tidak perhatian.
// AMAN auto anama = a->objectName(); menghubungkan(a, &A::foo, b, [nama, b]{ qDebug() << nama; }); // Tidak AMAN menghubungkan(a, &A::foo, b, [a,b]{ qDebug() << a>objectName(); });
statis C c; auto p = &c; menghubungkan(..., [p]{});
statis D d; menghubungkan(..., [&d]{});
QObject
harus ditangkap melalui mereka bersama pointer dengan nilai.std::shared_ptr
QObject tinggal di thread yang sama dapat ditangkap oleh
QPointer`; nilainya harus diperiksa sebelum digunakan dalam functor.QPointer
kelas I : umum QObject { ... ~Saya() { setParent(nullptr); } };
std::shared_ptr i { I }; menghubungkan(..., [i]{ ... });
std::weak_ptr j { aku }; menghubungkan(..., [j]{ auto jp = j.kunci(); jika (jp) { ... } });
Menggunakan lambdas sebagai slot adalah langsung (misalnya untuk sebuah acara dari QSpinbox):
connect(spinboxObject, &QSpinBox::editingFinished, this, [this]() {<do something>});
Tapi ini hanya bekerja jika sinyal tidak kelebihan beban (yang berarti ada beberapa sinyal dengan nama yang sama tetapi berbeda argumen).
connect(spinboxObject, &QSpinBox::valueChange, this, [this]() {<do something>});
memberikan mengkompilasi kesalahan, karena tidak ada dua kelebihan beban sinyal: valueChanged(int) dan valueChanged(const QString&) Jadi itu perlu untuk memenuhi syarat versi mana yang harus digunakan:
connect(spinboxObject, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this](int newValue){ });
Sedikit lebih pendek (atau lebih baik dibaca) adalah penggunaan QOverload:
connect(spinboxObject, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int newValue) { });
Tanpa "" konteks, misalnya dari main():
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();
}