Qt dünyasında, olayların ve sinyal / slotların farkı nedir?
Biri diğerinin yerini alır mı? Olaylar, sinyal / yuvaların bir soyutlaması mı?
Yanıtlar:
Qt dokümantasyon muhtemelen en iyi bu açıklar:
Qt'de olaylar, soyut
QEvent
sınıftan türetilen ve bir uygulama içinde veya uygulamanın bilmesi gereken dış etkinliğin bir sonucu olarak meydana gelen olayları temsil eden nesnelerdir. Olaylar, birQObject
alt sınıfın herhangi bir örneği tarafından alınabilir ve işlenebilir , ancak bunlar özellikle widget'larla ilgilidir. Bu belge, olayların tipik bir uygulamada nasıl iletildiğini ve işlendiğini açıklar.
Yani olaylar ve sinyal / slotlar, aynı şeyleri başaran iki paralel mekanizmadır. Genel olarak, bir olay bir dış varlık (örneğin, klavye veya fare tekerleği) tarafından üretilecek ve olay döngüsünde iletilecektir QApplication
. Genel olarak, kodu ayarlamadığınız sürece, olay üretmeyeceksiniz. QObject::installEventFilter()
Uygun işlevleri geçersiz kılarak, alt sınıflandırılmış nesnedeki olayları filtreleyebilir veya işleyebilirsiniz.
Sinyaller ve Yuvalar oluşturmak ve almak çok daha kolaydır ve herhangi iki QObject
alt sınıfı bağlayabilirsiniz . Metaclass aracılığıyla işlenirler (daha fazlası için moc_classname.cpp dosyanıza bakın), ancak üreteceğiniz sınıflar arası iletişimin çoğu muhtemelen sinyalleri ve yuvaları kullanacaktır. Sinyaller hemen teslim edilebilir veya bir kuyruk aracılığıyla ertelenebilir (iş parçacığı kullanıyorsanız).
Bir sinyal üretilebilir.
Qt'de sinyaller ve olayların her ikisi de Observer modelinin uygulamalarıdır . Farklı durumlarda kullanılırlar çünkü farklı güçlü ve zayıf yönleri vardır.
Her şeyden önce, 'Qt olayı' ile ne demek istediğimizi tam olarak tanımlayalım: olayı işlemek istiyorsanız, kendi temel sınıfınızda yeniden uygulamanız beklenen, Qt sınıfında sanal bir işlev. İle ilgili olduğunu Şablon Yöntemi desen .
" Sap " kelimesini nasıl kullandığıma dikkat edin . Aslında, sinyallerin ve olayların amacı arasındaki temel bir fark:
Aradaki fark, olayı "idare ettiğinizde", sınıf dışında yararlı olan bir davranışla "karşılık verme" sorumluluğunu üstlenmenizdir. Örneğin, üzerinde numara olan bir düğme bulunan bir uygulamayı düşünün. Uygulamanın, kullanıcının düğmeye odaklanmasına ve "yukarı" ve "aşağı" klavye tuşlarına basarak numarayı değiştirmesine izin vermesi gerekir. Aksi takdirde düğme normal gibi çalışmalıdır QPushButton
(tıklanabilir, vb.). Qt'de bu, yeniden QPushButton
uygulayan kendi küçük yeniden kullanılabilir "bileşeninizi" (alt sınıfı ) oluşturarak yapılır QWidget::keyPressEvent
. Sözde kod:
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)
Görmek? Bu kod yeni bir soyutlama sunar: düğme gibi davranan, ancak bazı ekstra işlevlere sahip bir pencere öğesi. Bu işlevi çok rahat bir şekilde ekledik:
keyPressEvent
bir sinyal vermiş olsaydı, sinyali miras alıp almayacağına QPushButton
veya sadece harici olarak bağlanıp bağlanmayacağına karar vermemiz gerekirdi . Ancak bu aptalca olurdu, çünkü Qt'de özel bir davranışa sahip bir pencere öğesi yazarken her zaman miras almanız beklenir (iyi bir nedenle - yeniden kullanılabilirlik / modülerlik). Yani keyPressEvent
bir etkinlik yaparak keyPressEvent
, işlevselliğin temel yapı taşı olan niyetlerini iletirler . Bir sinyal olsaydı, amaçlanmadığı halde kullanıcıya dönük bir şey gibi görünürdü.keyPressEvent
Bir sinyal olsaydı , bunun neredeyse imkansız olacağını görebilirsiniz .Qt'nin tasarımı iyi düşünülmüş - doğru şeyi yapmayı kolaylaştırarak ve yanlış şeyi yapmayı zorlaştırarak (keyPressEvent'i bir olay haline getirerek) bizi başarı çukuruna düşürdüler.
Öte yandan, en basit kullanımını düşünün QPushButton
- sadece onu örneklemek ve tıklandığında bildirim almak :
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
Bu açıkça sınıfın kullanıcısı tarafından yapılmalıdır :
QPushButton
Her seferinde bir düğmenin bize bir tıklama bildirmesini istediğimizde alt sınıfa ayırmak zorunda kalsaydık, bu, iyi bir neden olmaksızın çok sayıda alt sınıf gerektirirdi ! messagebox
Tıklandığında her zaman "Merhaba dünya" gösteren bir widget yalnızca tek bir durumda kullanışlıdır - bu nedenle tamamen yeniden kullanılamaz. Yine, doğru şeyi yapmaktan başka seçeneğimiz yok - ona dışarıdan bağlanarak.clicked()
bağlamak - veya birkaç sinyali bağlamak isteyebiliriz sayHello()
. Sinyallerle karışıklık olmaz. Alt sınıflandırma ile oturup uygun bir tasarıma karar verene kadar bazı sınıf diyagramları üzerinde düşünmeniz gerekir.Yerler o biri Not QPushButton
yayar clicked()
onun içindedir mousePressEvent()
uygulanması. Anlamına gelmez clicked()
ve mousePressEvent()
değiştirilebilir vardır - onlar bağımız sadece o.
Dolayısıyla, sinyallerin ve olayların farklı amaçları vardır (ancak ikisi de bir şey olduğuna dair bir bildirime "abone olmanıza" izin vermesi bakımından birbiriyle ilişkilidir).
Şimdiye kadar cevapları beğenmedim. - Sorunun bu kısmına odaklanmama izin verin:
Olaylar, sinyal / yuvaların bir soyutlaması mı?
Kısa cevap: hayır. Uzun cevap "daha iyi" bir soruyu gündeme getiriyor : Sinyaller ve olaylar nasıl ilişkilidir?
Boşta olan bir ana döngü (örneğin Qt'ler) genellikle işletim sisteminin bir select () çağrısında "sıkışır". Bu çağrı, uygulamayı bir dizi soket veya dosya ya da çekirdeğe soran her şeyi iletirken "uyku" durumuna sokar: eğer bunlarda bir değişiklik olursa, select () çağrısının geri dönmesine izin verin. - Ve çekirdek, dünyanın efendisi olarak bunun ne zaman olacağını bilir.
Bu select () çağrısının sonucu şunlar olabilir: X11'e bağlanan soketteki yeni veriler, dinlediğimiz bir UDP bağlantı noktasına bir paket geldi, vb. - Bu şey ne bir Qt sinyali, ne de bir Qt olayı ve Qt ana döngüsü, yeni veriyi birine mi diğerine mi çevireceğine veya yok sayacağına kendisi karar verir.
Qt, keyPressEvent () gibi bir yöntemi (veya birkaçını) çağırarak onu etkili bir şekilde Qt olayına dönüştürebilir. Veya Qt, gerçekte o sinyal için kayıtlı tüm fonksiyonları arayan ve bunları birbiri ardına çağıran bir sinyal yayar.
Bu iki kavramın bir farkı burada görülebilir: bir slotun o sinyale kayıtlı diğer slotların çağrılıp çağrılmayacağı konusunda bir oyu yoktur. - Olaylar daha çok bir zincir gibidir ve olay işleyicisi bu zinciri kesintiye uğratıp kesmeyeceğine karar verir. Sinyaller bu açıdan yıldız veya ağaç gibi görünür.
Bir olay tetikleyebilir veya tamamen bir sinyale dönüştürülebilir (sadece bir tane yayınlayın ve "super ()" olarak adlandırmayın). Bir sinyal bir olaya dönüştürülebilir (bir olay işleyicisini çağırın).
Neyin özetli olduğu duruma bağlıdır: tıklanan () - sinyal fare olaylarını özetlemektedir (bir düğme, çok fazla hareket etmeden aşağı ve tekrar yukarı hareket eder). Klavye olayları daha düşük seviyelerden soyutlamalardır (果 veya é gibi şeyler sistemimdeki birkaç tuş vuruşudur).
Belki focusInEvent () bunun tam tersine bir örnektir: clicked () sinyali kullanabilir (ve dolayısıyla soyut), ancak gerçekten kullanıp kullanmadığını bilmiyorum.
Olaylar, olay döngüsü tarafından gönderilir. Qt, Win32 veya başka herhangi bir GUI kitaplığını kullanarak Windows veya Linux ne yazarsanız yazın her GUI programının bir olay döngüsüne ihtiyacı vardır. Ayrıca her iş parçacığının kendi olay döngüsü vardır. Qt'de "GUI Olay Döngüsü" (tüm Qt uygulamalarının ana döngüsüdür) gizlidir, ancak şunu çağırmaya başlarsınız:
QApplication a(argc, argv);
return a.exec();
Programınıza gönderilen mesajlar işletim sistemi ve diğer uygulamalar olay olarak gönderilir.
Sinyaller ve yuvalar Qt mekanizmalarıdır. Moc (meta-nesne derleyicisi) kullanan derleme sürecinde, geri çağırma işlevlerine dönüştürülürler.
Olayın, onu göndermesi gereken bir alıcısı olmalıdır. Bu olayı başka kimse anlamamalı.
Yayılan sinyale bağlı tüm yuvalar yürütülecektir.
Sinyalleri olay olarak düşünmemelisiniz çünkü Qt belgelerinde okuyabileceğiniz gibi:
Bir sinyal gönderildiğinde, ona bağlı slotlar, normal bir fonksiyon çağrısı gibi genellikle hemen çalıştırılır. Bu olduğunda, sinyaller ve yuvalar mekanizması herhangi bir GUI olay döngüsünden tamamen bağımsızdır.
Bir olay gönderdiğinizde, olay döngüsü daha önce gelen tüm olayları gönderene kadar bir süre beklemesi gerekir. Bu nedenle olay veya sinyal gönderildikten sonra kodun çalıştırılması farklıdır. Bir olay gönderildikten sonraki kod hemen çalıştırılacaktır. Sinyaller ve yuva mekanizmaları ile bağlantı türüne bağlıdır. Normalde tüm slotlardan sonra çalıştırılacaktır. Qt :: QueuedConnection kullanıldığında, olaylar gibi hemen çalıştırılacaktır. Qt belgelerindeki tüm bağlantı türlerini kontrol edin .
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
Olay işlemeyi biraz ayrıntılı olarak tartışan bir makale var: http://www.packtpub.com/article/events-and-signals
Burada olaylar ve sinyaller arasındaki farkı tartışıyor:
Olaylar ve sinyaller, aynı şeyi gerçekleştirmek için kullanılan iki paralel mekanizmadır. Genel bir fark olarak, sinyaller bir pencere öğesi kullanılırken yararlıdır, oysa olaylar pencere öğesi uygulanırken yararlıdır. Örneğin, QPushButton gibi bir widget kullandığımızda, sinyalin yayılmasına neden olan düşük seviyeli fareye basma veya tuşa basma olaylarından daha çok clicked () sinyaliyle ilgileniyoruz. Ancak QPushButton sınıfını uyguluyorsak, fare ve anahtar olayları için kodun uygulanmasıyla daha çok ilgileniyoruz. Ayrıca, genellikle olayları ele alırız, ancak sinyal emisyonları tarafından bilgilendiriliriz.
Kabul edilen cevap aynı cümlelerin bazılarını kullandığından, bu konu hakkında konuşmanın yaygın bir yolu gibi görünüyor.
Unutmayın, Kuba Ober'in bu yanıtıyla ilgili aşağıdaki faydalı yorumlara bakın, bu biraz basit olabilir mi diye merak etmeme neden oluyor.
event
. Sinyaller ve yuvalar somut olarak yöntemlerdir, bağlantı mekanizması ise sinyalin kendisine bağlı olarak listelenen bir veya daha fazla yuvayı çağırmasına izin veren bir veri yapısıdır. Umarım sinyal / slotlardan olayların bir "alt küme" veya "varyantı" olarak bahsetmenin saçma olduğunu ve bunun tersini görüyorsunuz. Onlar gerçekten farklı şeylerdir gerçekleşmesi bazı widget'lar bağlamında benzer uçlara kullanılacak. Bu kadar. Ne kadar çok genelleme yaparsanız, o kadar az yardımcı olursunuz IMHO.
TL; DR: Sinyaller ve yuvalar dolaylı yöntem çağrılarıdır. Olaylar, veri yapılarıdır. Bu yüzden oldukça farklı hayvanlar.
Bir araya geldikleri tek zaman, iş parçacığı sınırlarının ötesinde yuva çağrılarının yapıldığı zamandır. Yuva çağrısı argümanları bir veri yapısında paketlenir ve alıcı iş parçacığının olay kuyruğuna bir olay olarak gönderilir. Alıcı iş parçacığında, QObject::event
yöntem bağımsız değişkenleri çözer, çağrıyı yürütür ve bir bağlantı engelliyse muhtemelen sonucu döndürür.
Unutulmaya genellemek istiyorsak, olayları hedef nesnenin event
yöntemini çağırmanın bir yolu olarak düşünebiliriz . Bu, bir modadan sonra dolaylı bir yöntem çağrısı - ancak doğru bir ifade olsa bile bunun hakkında düşünmenin yararlı bir yolu olduğunu düşünmüyorum.
Olaylar (genel bir kullanıcı / ağ etkileşimi anlamında) tipik olarak Qt'de sinyaller / yuvalar ile işlenir, ancak sinyaller / yuvalar birçok başka şey yapabilir.
QEvent ve alt sınıfları, temelde çerçevenin kodunuzla iletişim kurması için çok az standartlaştırılmış veri paketleridir. Fareye bir şekilde dikkat etmek istiyorsanız, yalnızca QMouseEvent API'ye bakmanız gerekir ve kütüphane tasarımcılarının, farenin bir köşesinde ne yaptığını anlamanız gerektiğinde her seferinde tekerleği yeniden icat etmek zorunda kalmazsınız. Qt API.
Bir çeşit olayları (yine genel durumda) bekliyorsanız, slotunuzun neredeyse kesinlikle bir QEvent alt sınıfını argüman olarak kabul edeceği doğrudur.
Bununla birlikte, sinyaller ve yuvalar kesinlikle QEvents olmadan kullanılabilir, ancak bir sinyali etkinleştirmek için orijinal itici gücün genellikle bir tür kullanıcı etkileşimi veya diğer eşzamansız faaliyetler olduğunu göreceksiniz. Ancak bazen kodunuz, belirli bir sinyali ateşlemenin yapılacak doğru şey olacağı bir noktaya ulaşır. Örneğin, uzun bir süreç sırasında bir ilerleme çubuğuna bağlı bir sinyali ateşlemek , o noktaya kadar bir QEvent içermez.
Bu soruyu Leow Wee Kheng'in 'Olay işleme' yazısını okurken buldum . Ayrıca şöyle diyor:
Jasmine Blanchette diyor ki:
Standart işlev çağrıları veya sinyaller ve yuvalar yerine olayları kullanmanızın ana nedeni, olayların hem eşzamanlı hem de eşzamansız olarak (sendEvent () veya postEvents () çağırmanıza bağlı olarak), bir işlevi çağırırken veya çağırırken kullanılabilmesidir. bir yuva her zaman eşzamanlıdır. Olayların bir başka avantajı da filtrelenebilmeleridir.
Bir başka küçük pragmatik değerlendirme: sinyal yaymak veya almak miras almayı gerektirirken QObject
, herhangi bir miras nesnesi bir olay gönderebilir veya gönderebilir (çünkü çağırdığınızdan QCoreApplication.sendEvent()
veya postEvent()
) Bu genellikle bir sorun değildir, ancak: PyQt sinyallerini garip bir şekilde kullanmak QObject
için birinci süper sınıf olması gerekir, ve sadece sinyal gönderebilmek için miras sıranızı yeniden düzenlemek istemeyebilirsiniz.)
Bana göre olaylar tamamen gereksizdir ve atılabilir. Qt'nin halihazırda olduğu gibi ayarlanmış olması dışında, sinyallerin olaylar veya olaylar tarafından sinyallerle değiştirilememesinin bir nedeni yoktur. Kuyruğa alınan sinyaller olaylar tarafından sarılır ve olaylar muhtemelen sinyallerle sarılabilir, örneğin:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
İçinde mouseMoveEvent()
bulunan QWidget
(ancak QQuickItem
artık bulunmayan) kolaylık işlevinin yerini alacak ve mouseMove
bir sahne yöneticisinin öğe için yayacağı sinyalleri işleyecektir . Sinyalin öğe adına bazı dış varlık tarafından yayılması önemsizdir ve sözde izin verilmese bile Qt bileşenleri dünyasında oldukça sık görülür (Qt bileşenleri bu kuralı genellikle atlatır). Ancak Qt, birçok farklı tasarım kararının bir araya geldiği bir gruptur ve eski kodu kırma korkusuyla neredeyse tamamen taşa dökülür (ki bu zaten yeterince sık olur).
QObject
üst-alt hiyerarşiyi yayma avantajına sahiptir . Sinyal / yuva bağlantıları, belirli bir koşul karşılandığında doğrudan veya dolaylı olarak bir işlevi çağırma vaadidir. Sinyaller ve yuvalarla ilişkili bir işleme hiyerarşisi yoktur.
accepted
Bir sinyale ve onu işleyen yuvalara bir referans parametresi ekleyebilir veya bir accepted
alan içeren bir referans parametresi olarak bir yapıya sahip olabilirsiniz . Ancak Qt, yaptığı tasarım seçimlerini yaptı ve artık değişmez.