Risposte:
La documentazione di Qt probabilmente lo spiega meglio:
In Qt, gli eventi sono oggetti, derivati dalla
QEvent
classe astratta , che rappresentano cose che sono accadute all'interno di un'applicazione o come risultato di attività esterne di cui l'applicazione deve essere a conoscenza. Gli eventi possono essere ricevuti e gestiti da qualsiasi istanza di unaQObject
sottoclasse, ma sono particolarmente rilevanti per i widget. Questo documento descrive come gli eventi vengono consegnati e gestiti in un'applicazione tipica.
Quindi eventi e segnale / slot sono due meccanismi paralleli che realizzano le stesse cose. In generale, un evento verrà generato da un'entità esterna (ad esempio, tastiera o rotellina del mouse) e verrà consegnato tramite il loop degli eventi QApplication
. In generale, a meno che non imposti il codice, non genererai eventi. È possibile filtrarli QObject::installEventFilter()
o gestire gli eventi in oggetti sottoclasse sovrascrivendo le funzioni appropriate.
I segnali e gli slot sono molto più facili da generare e ricevere e puoi collegare due QObject
sottoclassi qualsiasi . Sono gestiti tramite la Metaclass (dai un'occhiata al tuo file moc_classname.cpp per ulteriori informazioni), ma la maggior parte della comunicazione interclasse che produrrai probabilmente utilizzerà segnali e slot. I segnali possono essere consegnati immediatamente o differiti tramite una coda (se si utilizzano thread).
È possibile generare un segnale.
In Qt, i segnali e gli eventi sono entrambi implementazioni del pattern Observer . Sono utilizzati in diverse situazioni perché hanno diversi punti di forza e di debolezza.
Prima di tutto definiamo cosa intendiamo esattamente per 'evento Qt': una funzione virtuale in una classe Qt, che ci si aspetta che venga reimplementata in una tua classe base se vuoi gestire l'evento. È correlato al pattern del metodo del modello .
Nota come ho usato la parola " maniglia ". In effetti, ecco una differenza fondamentale tra l'intento dei segnali e degli eventi:
La differenza è che quando "gestisci" l'evento, ti assumi la responsabilità di "rispondere" con un comportamento utile al di fuori della classe. Ad esempio, considera un'app che ha un pulsante con un numero sopra. L'app deve consentire all'utente di mettere a fuoco il pulsante e modificare il numero premendo i tasti "su" e "giù". Altrimenti il pulsante dovrebbe funzionare come un normale QPushButton
(può essere cliccato, ecc.). In Qt questo viene fatto creando il tuo piccolo "componente" riutilizzabile (sottoclasse di QPushButton
), che reimplementa QWidget::keyPressEvent
. Pseudocodice:
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)
Vedere? Questo codice presenta una nuova astrazione: un widget che agisce come un pulsante, ma con alcune funzionalità extra. Abbiamo aggiunto questa funzionalità molto comodamente:
keyPressEvent
un segnale, avremmo bisogno di decidere se ereditare QPushButton
o semplicemente connetterci esternamente al segnale. Ma sarebbe stupido, poiché in Qt ci si aspetta sempre che erediti quando si scrive un widget con un comportamento personalizzato (per una buona ragione - riusabilità / modularità). Quindi, realizzando keyPressEvent
un evento, trasmettono il loro intento che keyPressEvent
è solo un elemento fondamentale della funzionalità. Se fosse un segnale, sembrerebbe una cosa rivolta all'utente, quando non è destinato a esserlo.keyPressEvent
fosse un segnale.Il design di Qt è ben congegnato: ci hanno fatto cadere nella fossa del successo rendendo facile fare la cosa giusta e difficile fare la cosa sbagliata (rendendo keyPressEvent un evento).
D'altra parte, considera l'utilizzo più semplice di QPushButton
: istanziarlo e ricevere una notifica quando viene cliccato :
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
Questo è chiaramente pensato per essere fatto dall'utente della classe:
QPushButton
ogni volta che vogliamo che un pulsante ci informi di un clic, ciò richiederebbe molte sottoclassi senza una buona ragione! Un widget che mostra sempre un "Hello world" messagebox
quando cliccato è utile solo in un singolo caso, quindi non è totalmente riutilizzabile. Ancora una volta, non abbiamo altra scelta che fare la cosa giusta - connettendoci esternamente.clicked()
- o collegare diversi segnali a sayHello()
. Con i segnali non c'è confusione. Con le sottoclassi dovresti sederti e meditare su alcuni diagrammi di classe finché non decidi un disegno appropriato.Nota che uno dei luoghi che QPushButton
emette clicked()
è nella sua mousePressEvent()
implementazione. Ciò non significa clicked()
e mousePressEvent()
sono intercambiabili, solo che sono correlati.
Quindi i segnali e gli eventi hanno scopi diversi (ma sono correlati in quanto entrambi ti consentono di "iscriverti" a una notifica di qualcosa che accade).
Finora non mi piacciono le risposte. - Consentitemi di concentrarmi su questa parte della domanda:
Gli eventi sono un'astrazione del segnale / degli slot?
Risposta breve: no. La risposta lunga solleva una domanda "migliore": come sono correlati i segnali e gli eventi?
Un ciclo principale inattivo (ad esempio le Qt) è solitamente "bloccato" in una chiamata select () del sistema operativo. Quella chiamata fa "dormire" l'applicazione, mentre passa un mucchio di socket o file o qualsiasi altra cosa al kernel chiedendo: se qualcosa cambia su questi, lasciate che la chiamata select () ritorni. - E il kernel, in quanto padrone del mondo, sa quando ciò accade.
Il risultato di quella chiamata a select () potrebbe essere: nuovi dati sul socket si connettono a X11, è arrivato un pacchetto a una porta UDP su cui ascoltiamo, ecc. - Quella roba non è né un segnale Qt, né un evento Qt, e il Il ciclo principale di Qt decide se trasforma i nuovi dati in uno, nell'altro o lo ignora.
Qt potrebbe chiamare un metodo (o più) come keyPressEvent (), trasformandolo effettivamente in un evento Qt. Oppure Qt emette un segnale, che in effetti cerca tutte le funzioni registrate per quel segnale e le chiama una dopo l'altra.
Una differenza di questi due concetti è visibile qui: uno slot non può votare se altri slot registrati a quel segnale verranno chiamati o meno. - Gli eventi sono più simili a una catena e il gestore di eventi decide se interromperla o meno. Sotto questo aspetto, i segnali sembrano una stella o un albero.
Un evento può innescarsi o essere interamente trasformato in un segnale (emetterne uno e non chiamare "super ()"). Un segnale può essere trasformato in un evento (chiama un gestore di eventi).
Ciò che astrae ciò che dipende dal caso: il segnale cliccato () astrae gli eventi del mouse (un pulsante va giù e su di nuovo senza muoversi troppo). Gli eventi della tastiera sono astrazioni dai livelli inferiori (cose come 果 o é sono diversi tasti premuti nel mio sistema).
Forse il focusInEvent () è un esempio del contrario: potrebbe usare (e quindi astratto) il segnale cliccato (), ma non so se lo fa effettivamente.
Gli eventi vengono inviati dal loop degli eventi. Ogni programma GUI necessita di un ciclo di eventi, qualunque cosa tu scriva Windows o Linux, utilizzando Qt, Win32 o qualsiasi altra libreria GUI. Inoltre, ogni thread ha il proprio ciclo di eventi. In Qt "GUI Event Loop" (che è il ciclo principale di tutte le applicazioni Qt) è nascosto, ma lo si avvia chiamando:
QApplication a(argc, argv);
return a.exec();
I messaggi del sistema operativo e di altre applicazioni inviati al programma vengono inviati come eventi.
I segnali e gli slot sono meccanismi Qt. Nel processo di compilazione utilizzando moc (compilatore meta-oggetto), vengono modificati in funzioni di callback.
L'evento dovrebbe avere un ricevitore, che dovrebbe inviarlo. Nessun altro dovrebbe ricevere quell'evento.
Verranno eseguiti tutti gli slot collegati al segnale emesso.
Non dovresti pensare ai segnali come eventi, perché come puoi leggere nella documentazione di Qt:
Quando viene emesso un segnale, gli slot ad esso collegati vengono solitamente eseguiti immediatamente, proprio come una normale chiamata di funzione. Quando ciò accade, il meccanismo dei segnali e degli slot è totalmente indipendente da qualsiasi loop di eventi della GUI.
Quando si invia un evento, è necessario attendere un po 'di tempo prima che il ciclo di eventi invii tutti gli eventi precedenti. Per questo motivo, l'esecuzione del codice dopo l'invio dell'evento o del segnale è diversa. Il codice successivo all'invio di un evento verrà eseguito immediatamente. Con i segnali e i meccanismi degli slot dipende dal tipo di connessione. Normalmente verrà eseguito dopo tutti gli slot. Usando Qt :: QueuedConnection, verrà eseguito immediatamente, proprio come gli eventi. Controlla tutti i tipi di connessione nella documentazione 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
C'è un articolo che discute l'elaborazione degli eventi in dettaglio: http://www.packtpub.com/article/events-and-signals
Discute la differenza tra eventi e segnali qui:
Eventi e segnali sono due meccanismi paralleli utilizzati per realizzare la stessa cosa. Come differenza generale, i segnali sono utili quando si utilizza un widget, mentre gli eventi sono utili quando si implementa il widget. Ad esempio, quando stiamo usando un widget come QPushButton, siamo più interessati al suo segnale cliccato () che alla pressione del mouse di basso livello o agli eventi di pressione del tasto che hanno causato l'emissione del segnale. Ma se stiamo implementando la classe QPushButton, siamo più interessati all'implementazione del codice per gli eventi del mouse e dei tasti. Inoltre, di solito gestiamo gli eventi ma riceviamo notifiche dalle emissioni di segnali.
Questo sembra essere un modo comune di parlarne, poiché la risposta accettata utilizza alcune delle stesse frasi.
Nota, per favore vedi commenti utili qui sotto su questa risposta di Kuba Ober, che mi fanno chiedere se potrebbe essere un po 'semplicistico.
event
. I segnali e gli slot, sono, concretamente , metodi, mentre il meccanismo di connessione è una struttura dati che consente al segnale di richiamare uno o più slot elencati come collegati ad esso. Spero che tu capisca che parlare di segnale / slot come un "sottoinsieme" o "variante" di eventi non ha senso e viceversa. Sono davvero cose diverse che accadono ad essere utilizzati a fini simili nel contesto di alcuni widget. Questo è tutto. Più generalizzi, meno utile diventi IMHO.
TL; DR: i segnali e gli slot sono chiamate a metodi indiretti. Gli eventi sono strutture di dati. Quindi sono animali abbastanza diversi.
L'unico momento in cui si uniscono è quando le chiamate agli slot vengono effettuate oltre i confini del thread. Gli argomenti della chiamata di slot vengono impacchettati in una struttura di dati e vengono inviati come evento alla coda degli eventi del thread ricevente. Nel thread di ricezione, il fileQObject::event
metodo decomprime gli argomenti, esegue la chiamata ed eventualmente restituisce il risultato se si trattava di una connessione bloccante.
Se siamo disposti a generalizzare fino all'oblio, si potrebbe pensare agli eventi come a un modo per invocare il event
metodo dell'oggetto target . Questa è una chiamata al metodo indiretto, dopo una moda, ma non penso che sia un modo utile di pensarci, anche se è un'affermazione vera.
Gli eventi (in senso generale di interazione utente / rete) sono tipicamente gestiti in Qt con segnali / slot, ma i segnali / slot possono fare molte altre cose.
QEvent e le sue sottoclassi sono fondamentalmente solo piccoli pacchetti di dati standardizzati per il framework per comunicare con il tuo codice. Se vuoi prestare attenzione al mouse in qualche modo, devi solo guardare l'API QMouseEvent, ei progettisti della libreria non devono reinventare la rotellina ogni volta che devi capire cosa ha fatto il mouse in qualche angolo di l'API Qt.
È vero che se stai aspettando eventi (di nuovo nel caso generale) di qualche tipo, il tuo slot accetterà quasi certamente una sottoclasse QEvent come argomento.
Detto questo, i segnali e gli slot possono certamente essere utilizzati senza QEvents, anche se scoprirai che l'impulso originale per l'attivazione di un segnale sarà spesso un qualche tipo di interazione dell'utente o altre attività asincrone. A volte, tuttavia, il tuo codice raggiungerà un punto in cui sparare un certo segnale sarà la cosa giusta da fare. Ad esempio, l'emissione di un segnale collegato a una barra di avanzamento durante un lungo processo non comporta un QEvent fino a quel punto.
Ho trovato questa domanda durante la lettura di "Elaborazione eventi" di Leow Wee Kheng. Dice anche:
Jasmine Blanchette ha detto:
Il motivo principale per cui dovresti usare eventi piuttosto che chiamate di funzioni standard, o segnali e slot, è che gli eventi possono essere usati sia in modo sincrono che asincrono (a seconda che tu chiami sendEvent () o postEvents ()), mentre chiamando una funzione o invocando uno slot è sempre sincrono. Un altro vantaggio degli eventi è che possono essere filtrati.
Un'altra piccola considerazione pragmatica: l'emissione o la ricezione di segnali richiede l'ereditarietà QObject
mentre un oggetto di qualsiasi eredità può pubblicare o inviare un evento (poiché si invoca QCoreApplication.sendEvent()
o postEvent()
) Questo di solito non è un problema ma: per usare i segnali PyQt stranamente richiede QObject
di essere la prima super classe, e potresti non voler riorganizzare il tuo ordine di eredità solo per essere in grado di inviare segnali.)
Secondo me gli eventi sono completamente ridondanti e potrebbero essere buttati fuori. Non c'è motivo per cui i segnali non possano essere sostituiti da eventi o eventi da segnali, tranne che Qt è già impostato così com'è. I segnali in coda sono avvolti da eventi e gli eventi potrebbero plausibilmente essere avvolti da segnali, ad esempio:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
Sostituirebbe la mouseMoveEvent()
funzione di convenienza trovata in QWidget
(ma non QQuickItem
più in) e gestirà i mouseMove
segnali che un gestore di scene emetterebbe per l'elemento. Il fatto che il segnale sia emesso per conto dell'articolo da qualche entità esterna non è importante e accade abbastanza spesso nel mondo dei componenti Qt, anche se presumibilmente non è consentito (i componenti Qt spesso aggirano questa regola). Ma Qt è un conglomerato di molte diverse decisioni di progettazione e praticamente scolpite nella pietra per paura di rompere il vecchio codice (cosa che accade comunque abbastanza spesso).
QObject
gerarchia padre-figlio, quando necessario. Le connessioni segnale / slot sono solo una promessa di chiamare una funzione, direttamente o indirettamente, quando viene soddisfatta una determinata condizione. Non esiste una gerarchia di gestione associata con segnali e slot.
accepted
parametro di riferimento a un segnale e gli slot che lo gestiscono oppure potresti avere una struttura come parametro di riferimento con un accepted
campo. Ma Qt ha fatto le scelte di design che ha fatto e ora sono scolpite nella pietra.