Di dunia Qt, apa perbedaan peristiwa dan sinyal / slot?
Apakah yang satu menggantikan yang lain? Apakah peristiwa merupakan abstraksi dari sinyal / slot?
Jawaban:
The dokumentasi Qt mungkin menjelaskan itu yang terbaik:
Dalam Qt, kejadian adalah objek, diturunkan dari
QEvent
kelas abstrak , yang merepresentasikan hal-hal yang telah terjadi baik di dalam aplikasi atau sebagai hasil dari aktivitas luar yang perlu diketahui aplikasi. Peristiwa dapat diterima dan ditangani oleh setiapQObject
subkelas, tetapi sangat relevan dengan widget. Dokumen ini menjelaskan bagaimana peristiwa disampaikan dan ditangani dalam aplikasi khusus.
Jadi peristiwa dan sinyal / slot adalah dua mekanisme paralel yang mencapai hal yang sama. Secara umum, acara akan dibuat oleh entitas luar (misalnya, papan tombol atau roda mouse) dan akan disampaikan melalui putaran acara di QApplication
. Secara umum, kecuali Anda menyiapkan kode, Anda tidak akan membuat acara. Anda dapat memfilternya QObject::installEventFilter()
atau menangani peristiwa di objek subclass dengan mengganti fungsi yang sesuai.
Sinyal dan Slot jauh lebih mudah untuk dibuat dan diterima dan Anda dapat menghubungkan dua QObject
subkelas mana pun . Mereka ditangani melalui Metaclass (lihat file moc_classname.cpp Anda lebih lanjut), tetapi sebagian besar komunikasi antar kelas yang akan Anda hasilkan mungkin akan menggunakan sinyal dan slot. Sinyal dapat dikirim segera atau ditangguhkan melalui antrian (jika Anda menggunakan utas).
Sinyal dapat dihasilkan.
Dalam Qt, sinyal dan kejadian keduanya merupakan implementasi dari pola Observer . Mereka digunakan dalam situasi yang berbeda karena mereka memiliki kekuatan dan kelemahan yang berbeda.
Pertama-tama mari kita definisikan apa yang kita maksud dengan 'acara Qt' dengan tepat: fungsi virtual dalam kelas Qt, yang Anda diharapkan untuk diterapkan kembali di kelas dasar Anda jika Anda ingin menangani acara tersebut. Ini terkait dengan pola Metode Template .
Perhatikan bagaimana saya menggunakan kata " pegangan ". Memang, inilah perbedaan mendasar antara maksud dari sinyal dan peristiwa:
Perbedaannya adalah ketika Anda "menangani" acara tersebut, Anda mengambil tanggung jawab untuk "menanggapi" dengan perilaku yang berguna di luar kelas. Misalnya, pertimbangkan aplikasi yang memiliki tombol dengan angka di atasnya. Aplikasi harus membiarkan pengguna memfokuskan tombol dan mengubah nomor dengan menekan tombol keyboard "atas" dan "bawah". Jika tidak, tombol akan berfungsi seperti biasa QPushButton
(dapat diklik, dll.). Dalam Qt ini dilakukan dengan membuat "komponen" kecil Anda sendiri (subkelas dari QPushButton
), yang akan diimplementasikan kembali QWidget::keyPressEvent
. Pseudocode:
class NumericButton extends QPushButton
private void addToNumber(int value):
// ...
reimplement base.keyPressEvent(QKeyEvent event):
if(event.key == up)
this.addToNumber(1)
else if(event.key == down)
this.addToNumber(-1)
else
base.keyPressEvent(event)
Lihat? Kode ini menyajikan abstraksi baru: widget yang berfungsi seperti tombol, tetapi dengan beberapa fungsi tambahan. Kami menambahkan fungsi ini dengan sangat nyaman:
keyPressEvent
sinyal, kita perlu memutuskan apakah akan mewarisi QPushButton
atau hanya terhubung secara eksternal ke sinyal. Tetapi itu akan menjadi bodoh, karena di Qt Anda selalu diharapkan untuk mewarisi saat menulis widget dengan perilaku khusus (untuk alasan yang bagus - usabilitas / modularitas). Jadi dengan membuat keyPressEvent
acara, mereka menyampaikan maksud mereka yang keyPressEvent
hanya merupakan blok bangunan dasar fungsionalitas. Jika itu adalah sinyal, itu akan terlihat seperti hal yang dihadapi pengguna, padahal tidak dimaksudkan.keyPressEvent
itu adalah sinyal.Desain Qt dipikirkan dengan baik - mereka membuat kami jatuh ke lubang kesuksesan dengan membuatnya mudah untuk melakukan hal yang benar dan sulit untuk melakukan hal yang salah (dengan menjadikan keyPressEvent sebagai acara).
Di sisi lain, pertimbangkan penggunaan yang paling sederhana QPushButton
- cukup buat instance dan dapatkan pemberitahuan saat diklik :
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
Ini jelas dimaksudkan untuk dilakukan oleh pengguna kelas:
QPushButton
setiap kali kami ingin beberapa tombol untuk memberi tahu kami tentang sebuah klik, itu akan membutuhkan banyak subkelas tanpa alasan yang jelas! Widget yang selalu menampilkan "Halo dunia" messagebox
saat diklik hanya berguna dalam satu kasus - jadi sama sekali tidak dapat digunakan kembali. Sekali lagi, kita tidak punya pilihan selain melakukan hal yang benar - dengan menghubungkannya secara eksternal.clicked()
- atau menghubungkan beberapa sinyal sayHello()
. Dengan sinyal tidak ada keributan. Dengan subclassing Anda harus duduk dan merenungkan beberapa diagram kelas sampai Anda memutuskan desain yang sesuai.Perhatikan bahwa salah satu tempat yang QPushButton
dipancarkan clicked()
adalah dalam mousePressEvent()
implementasinya. Itu tidak berarti clicked()
danmousePressEvent()
dapat dipertukarkan - hanya saja mereka terkait.
Jadi, sinyal dan peristiwa memiliki tujuan yang berbeda (tetapi terkait sehingga memungkinkan Anda "berlangganan" ke pemberitahuan tentang sesuatu yang terjadi).
Saya tidak suka jawabannya sejauh ini. - Biarkan saya berkonsentrasi pada bagian pertanyaan ini:
Apakah peristiwa merupakan abstraksi dari sinyal / slot?
Jawaban singkatnya: tidak. Jawaban panjangnya menimbulkan pertanyaan "lebih baik": Bagaimana sinyal dan peristiwa terkait?
Loop utama yang menganggur (misalnya Qt) biasanya "macet" dalam panggilan pilih () dari sistem operasi. Panggilan itu membuat aplikasi "tidur", ketika ia melewati banyak soket atau file atau apa pun yang diminta kernel: jika ada perubahan pada ini, biarkan panggilan select () kembali. - Dan kernel, sebagai penguasa dunia, tahu kapan itu terjadi.
Hasil dari panggilan select () itu bisa berupa: data baru pada soket yang terhubung ke X11, paket ke port UDP yang kita dengarkan masuk, dll. - Hal itu bukanlah sinyal Qt, atau peristiwa Qt, dan Perulangan utama Qt memutuskan sendiri jika ia mengubah data baru menjadi yang satu, yang lain, atau mengabaikannya.
Qt dapat memanggil metode (atau beberapa) seperti keyPressEvent (), yang secara efektif mengubahnya menjadi acara Qt. Atau Qt memancarkan sinyal, yang pada dasarnya mencari semua fungsi yang terdaftar untuk sinyal itu, dan memanggilnya satu demi satu.
Satu perbedaan dari kedua konsep tersebut terlihat di sini: sebuah slot tidak memiliki suara apakah slot lain yang terdaftar ke sinyal itu akan dipanggil atau tidak. - Acara lebih seperti sebuah rantai, dan pengendali kejadian memutuskan apakah itu mengganggu rantai itu atau tidak. Dalam hal ini, sinyal terlihat seperti bintang atau pohon.
Suatu peristiwa dapat memicu atau sepenuhnya diubah menjadi sinyal (cukup pancarkan saja, dan jangan sebut "super ()"). Sinyal bisa diubah menjadi event (sebut event handler).
Apa yang mengabstraksikan apa yang bergantung pada kasusnya: clicked () - signal mengabstraksi peristiwa mouse (sebuah tombol turun dan naik lagi tanpa terlalu banyak bergerak). Acara keyboard adalah abstraksi dari level yang lebih rendah (hal-hal seperti 果 atau é adalah beberapa penekanan tombol di sistem saya).
Mungkin focusInEvent () adalah contoh kebalikannya: ia dapat menggunakan (dan dengan demikian mengabstraksikan) sinyal yang diklik (), tetapi saya tidak tahu apakah sebenarnya demikian.
Acara dikirim oleh loop acara. Setiap program GUI membutuhkan event loop, apapun yang Anda tulis di Windows atau Linux, menggunakan Qt, Win32 atau library GUI lainnya. Selain itu, setiap utas memiliki loop acara sendiri. Dalam Qt "GUI Event Loop" (yang merupakan loop utama dari semua aplikasi Qt) tersembunyi, tetapi Anda memulainya dengan memanggil:
QApplication a(argc, argv);
return a.exec();
Pesan OS dan aplikasi lain yang dikirim ke program Anda dikirim sebagai peristiwa.
Sinyal dan slot adalah mekanisme Qt. Dalam proses kompilasi menggunakan moc (meta-object compiler), mereka diubah menjadi fungsi callback.
Acara harus memiliki satu penerima, yang harus mengirimkannya. Tidak ada orang lain yang mendapatkan acara itu.
Semua slot yang terhubung ke sinyal yang dipancarkan akan dijalankan.
Anda tidak boleh menganggap Sinyal sebagai peristiwa, karena seperti yang Anda baca di dokumentasi Qt:
Ketika sinyal dipancarkan, slot yang terhubung dengannya biasanya segera dijalankan, seperti pemanggilan fungsi normal. Ketika ini terjadi, sinyal dan mekanisme slot benar-benar tidak bergantung pada loop peristiwa GUI.
Saat Anda mengirim acara, itu harus menunggu beberapa saat hingga loop acara mengirimkan semua acara yang datang lebih awal. Karena itu, eksekusi kode setelah peristiwa atau sinyal pengiriman berbeda. Kode setelah pengiriman acara akan segera dijalankan. Dengan mekanisme sinyal dan slot, itu tergantung pada jenis koneksi. Biasanya ini akan dijalankan setelah semua slot. Menggunakan Qt :: QueuedConnection, ini akan segera dijalankan, seperti acara. Periksa semua jenis koneksi dalam dokumentasi Qt .
When you send an event, it must wait for time when event loop dispatch all events that came earlier. Because of this, execution of the cod after sending event or signal is different
Ada artikel yang membahas pemrosesan acara secara detail: http://www.packtpub.com/article/events-and-signals
Ini membahas perbedaan antara peristiwa dan sinyal di sini:
Peristiwa dan sinyal adalah dua mekanisme paralel yang digunakan untuk mencapai hal yang sama. Sebagai perbedaan umum, sinyal berguna saat menggunakan widget, sedangkan peristiwa berguna saat mengimplementasikan widget. Sebagai contoh, ketika kita menggunakan widget seperti QPushButton, kita lebih tertarik dengan sinyal clicked () nya daripada pada penekanan mouse tingkat rendah atau kejadian penekanan tombol yang menyebabkan sinyal dipancarkan. Tetapi jika kita mengimplementasikan kelas QPushButton, kita lebih tertarik pada implementasi kode untuk mouse dan event kunci. Selain itu, kami biasanya menangani acara tetapi mendapat pemberitahuan melalui emisi sinyal.
Ini tampaknya menjadi cara umum untuk membicarakannya, karena jawaban yang diterima menggunakan beberapa frasa yang sama.
Catatan, silakan lihat komentar bermanfaat di bawah tentang jawaban dari Kuba Ober ini, yang membuat saya bertanya-tanya apakah ini mungkin sedikit sederhana.
event
. Sinyal dan slot, secara konkret , adalah metode, sedangkan mekanisme koneksi adalah struktur data yang memungkinkan sinyal memanggil satu atau lebih slot yang terdaftar sebagai terhubung dengannya. Saya harap Anda melihat bahwa membicarakan sinyal / slot sebagai "subset" atau "varian" dari peristiwa adalah omong kosong, dan sebaliknya. Mereka benar-benar hal yang berbeda yang terjadi akan digunakan untuk tujuan yang sama dalam konteks beberapa widget. Itu dia. Semakin Anda menggeneralisasi, semakin kurang membantu Anda menjadi IMHO.
TL; DR: Sinyal dan slot adalah panggilan metode tidak langsung. Peristiwa adalah struktur data. Jadi mereka adalah hewan yang sangat berbeda.
Satu-satunya saat ketika mereka berkumpul adalah ketika panggilan slot dilakukan melintasi batas utas. Argumen panggilan slot dikemas dalam struktur data dan dikirim sebagai peristiwa ke antrean peristiwa thread penerima. Di thread penerima, QObject::event
metode ini akan membongkar argumen, menjalankan panggilan, dan mungkin mengembalikan hasilnya jika itu adalah koneksi yang memblokir.
Jika kita ingin menggeneralisasi untuk dilupakan, orang dapat menganggap peristiwa sebagai cara untuk memanggil metode objek target event
. Ini adalah panggilan metode tidak langsung, setelah mode - tapi menurut saya ini bukan cara berpikir yang membantu, bahkan jika itu pernyataan yang benar.
Peristiwa (dalam pengertian umum interaksi pengguna / jaringan) biasanya ditangani dalam Qt dengan sinyal / slot, tetapi sinyal / slot dapat melakukan banyak hal lain.
QEvent dan subkelasnya pada dasarnya hanyalah paket data standar kecil untuk kerangka kerja untuk berkomunikasi dengan kode Anda. Jika Anda ingin memperhatikan mouse dengan cara tertentu, Anda hanya perlu melihat API QMouseEvent, dan perancang perpustakaan tidak perlu menemukan kembali roda setiap kali Anda perlu mencari tahu apa yang dilakukan mouse di beberapa sudut API Qt.
Memang benar bahwa jika Anda menunggu acara (sekali lagi dalam kasus umum), slot Anda hampir pasti akan menerima subclass QEvent sebagai argumen.
Dengan demikian, sinyal dan slot pasti dapat digunakan tanpa QEvents, meskipun Anda akan menemukan bahwa dorongan asli untuk mengaktifkan sinyal sering kali berupa semacam interaksi pengguna atau aktivitas asinkron lainnya. Terkadang, bagaimanapun, kode Anda hanya akan mencapai titik di mana menembakkan sinyal tertentu adalah hal yang tepat untuk dilakukan. Misalnya, menembakkan sinyal yang terhubung ke bilah kemajuan selama proses yang lama tidak melibatkan QEvent hingga saat itu.
Saya menemukan pertanyaan ini saat membaca 'Pemrosesan acara' oleh Leow Wee Kheng. Ia juga mengatakan:
Jasmine Blanchette mengatakan:
Alasan utama mengapa Anda akan menggunakan peristiwa daripada panggilan fungsi standar, atau sinyal dan slot, adalah bahwa peristiwa dapat digunakan baik secara sinkron maupun asinkron (bergantung pada apakah Anda memanggil sendEvent () atau postEvents ()), saat memanggil fungsi atau memanggil slot selalu sinkron. Keuntungan lain dari acara adalah dapat difilter.
Pertimbangan pragmatis kecil lainnya: memancarkan atau menerima sinyal membutuhkan QObject
pewarisan sedangkan objek warisan apa pun dapat memposting atau mengirim acara (karena Anda memanggil QCoreApplication.sendEvent()
atau postEvent()
) Ini biasanya bukan masalah tetapi: untuk menggunakan sinyal, anehnya PyQt memerlukan QObject
untuk menjadi kelas super pertama, dan Anda mungkin tidak ingin mengatur ulang pesanan warisan Anda hanya untuk dapat mengirim sinyal.)
Menurut pendapat saya, acara benar-benar mubazir dan bisa dibuang. Tidak ada alasan mengapa sinyal tidak bisa digantikan oleh kejadian atau kejadian oleh sinyal, kecuali Qt sudah diatur apa adanya. Sinyal antrean dibungkus oleh peristiwa dan peristiwa dapat dibayangkan dibungkus oleh sinyal, misalnya:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
Akan menggantikan mouseMoveEvent()
fungsi kenyamanan yang ditemukan di QWidget
(tetapi tidak QQuickItem
lagi) dan akan menangani mouseMove
sinyal yang akan dipancarkan oleh manajer adegan untuk item tersebut. Fakta bahwa sinyal dipancarkan atas nama item oleh beberapa entitas luar tidak penting dan cukup sering terjadi di dunia komponen Qt, meskipun seharusnya tidak diizinkan (komponen Qt sering mengelak aturan ini). Tapi Qt adalah konglomerat dari banyak keputusan desain yang berbeda dan cukup banyak dilemparkan ke batu karena takut melanggar kode lama (yang sebenarnya cukup sering terjadi).
QObject
hierarki induk-anak, bila perlu. Koneksi sinyal / slot hanyalah janji untuk memanggil suatu fungsi, secara langsung atau tidak langsung, ketika kondisi tertentu terpenuhi. Tidak ada hierarki penanganan yang terkait dengan sinyal dan slot.
accepted
parameter referensi ke sinyal dan slot yang menanganinya atau Anda dapat memiliki struktur sebagai parameter referensi dengan kolom accepted
. Tapi Qt membuat pilihan desain yang dibuatnya dan sekarang semuanya telah ditetapkan.