Eventi Qt e segnale / slot


97

Nel mondo Qt, qual è la differenza di eventi e segnale / slot?

Uno sostituisce l'altro? Gli eventi sono un'astrazione del segnale / degli slot?

Risposte:


30

La documentazione di Qt probabilmente lo spiega meglio:

In Qt, gli eventi sono oggetti, derivati ​​dalla QEventclasse 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 una QObjectsottoclasse, 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 QObjectsottoclassi 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.


la coda differita è la stessa del loop di eventi della coda, oppure esistono due code? uno per i segnali differiti e uno per eventi?
Guillaume07

29
@ Raphael, vergogna per aver accettato questa risposta. - Il paragrafo citato non contiene nemmeno le parole "segnale" o "slot"!
Robert Siemer,

1
@neuronet: il paragrafo è citato con "[lo] spiega meglio", ma non lo fa. Affatto. Neanche un po.
Robert Siemer

@Robert quindi se quella piccola riga introduttiva fosse cambiata in "Prima consideriamo come i documenti spiegano gli eventi, prima di passare a considerare come sono correlati ai segnali" saresti d'accordo con la risposta? In tal caso, possiamo semplicemente modificare la risposta per migliorarla in quanto è un punto minore e sono d'accordo che potrebbe essere formulata meglio. [Modifica questa è una domanda genuina: poiché non sono sicuro che il resto sia molto meglio ... ma solo perché la parte citata non menziona i segnali sembra una preoccupazione superficiale se il resto è effettivamente buono, cosa che sono non assumendo .]
Eric

4
@neuronet, se rimuovi completamente la citazione migliorerà la risposta ai miei occhi. - Se rimuovi l'intera risposta, migliorerà l'intero QA.
Robert Siemer

152

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:

  • Tu " gestisci " gli eventi
  • " Ricevi una notifica di " emissioni di segnale

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:

  • Poiché abbiamo reimplementato un virtuale, la nostra implementazione è stata automaticamente incapsulata nella nostra classe. Se i progettisti di Qt avessero prodotto keyPressEventun segnale, avremmo bisogno di decidere se ereditare QPushButtono 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 keyPressEventun 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.
  • Poiché l'implementazione della classe base della funzione è disponibile, implementiamo facilmente il modello Catena di responsabilità gestendo i nostri casi speciali (tasti su e giù) e lasciando il resto alla classe base. Puoi vedere che questo sarebbe quasi impossibile se keyPressEventfosse 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:

  • se dovessimo creare una sottoclasse QPushButtonogni 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" messageboxquando 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.
  • potremmo voler collegare diversi slot a 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 QPushButtonemette 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).


Ho l'idea. L'evento è per classe, tutte le istanze di una classe reagiranno allo stesso modo, tutte chiameranno lo stesso QClassName :: evento. Ma il segnale è per oggetto, ogni oggetto può avere la sua connessione slot di segnale univoca.
炸鱼 薯条 德里克

39

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.


4
Trovo questa parte: "uno slot non ha voto se altri slot registrati a quel segnale verranno chiamati o meno" molto illuminante per la differenza tra segnali ed eventi. Tuttavia, ho una domanda correlata: cosa sono i messaggi e in che modo i messaggi sono correlati a segnali ed eventi? (se sono correlati). I messaggi sono un'astrazione per segnali ed eventi? Possono essere usati per descrivere tali interazioni tra QObjects (o altri oggetti?)
user1284631

@axeoth: In Qt non esistono i "messaggi". Bene, c'è una finestra di messaggio, ma questo è tutto.
Ripristina Monica

@ KubaOber: grazie. Intendevo "eventi".
user1284631

3
@axeoth: Allora la tua domanda non ha senso. Si legge: "cosa sono gli eventi e come sono gli eventi legati a segnali ed eventi".
Ripristina Monica il

@KubaOber: non ricordo il contesto un anno dopo. Ad ogni modo, sembra che chiedessi quasi la stessa cosa dell'OP: qual è la differenza tra gli eventi e come gli eventi sono correlati a segnali e slot. Qual era davvero il caso allora, IIRC, stavo cercando un modo per racchiudere classi Qt signal / slot driven in una sorta di classi non Qt, quindi stavo cercando un modo per comandare il primo dall'interno di quest'ultimo. Immagino che stavo valutando di inviare loro eventi, poiché ciò significava che dovevo includere solo le intestazioni Qt e non forzare le mie classi a implementare il segnale / gli slot.
user1284631

16

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 .


Questa frase mi fornisce una comprensione sintetica della differenza tra slot di segnale Qt ed eventi: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
swdev

7

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.


Non riesco a vedere come sarebbero due meccanismi usati per realizzare la stessa cosa - penso che sia una generalizzazione inutile che non aiuta nessuno.
Ripristina Monica il

@KubaOber no aiuta a evitare i dettagli di livello inferiore. Diresti che Python non è utile perché puoi fare la stessa cosa scrivendo in codice macchina? :)
eric

2
Sto dicendo che la prima frase della citazione è generalizzante al punto da essere inutile. Tecnicamente non è sbagliato, ma solo perché potresti, se lo desideri, utilizzare il passaggio di eventi per ottenere ciò che fa il meccanismo di slot del segnale e viceversa. Sarebbe molto macchinoso. Quindi no, gli slot di segnale non sono la stessa cosa degli eventi, non realizzano la stessa cosa e il fatto che entrambi esistano è fondamentale per il successo di Qt. L'esempio di pulsante citato ignora completamente la funzionalità generale del sistema di slot di segnale.
Ripristina Monica il

1
"Eventi e segnali sono due meccanismi paralleli utilizzati per eseguire la stessa operazione nell'ambito ristretto di alcuni widget / controlli dell'interfaccia utente ". La parte in grassetto manca in modo critico e rende la citazione inutile. Anche in questo caso, ci si può chiedere quanto della "stessa cosa" sia realmente realizzata: i segnali consentono di reagire agli eventi di clic senza installare un filtro eventi sul widget (o derivare dal widget). Farlo senza i segnali è molto più complicato e accoppia strettamente il codice client al controllo. Questo è un cattivo design.
Ripristina Monica il

1
Gli eventi sono un concetto onnipresente . La particolare implementazione in Qt li imposta concretamente come strutture dati passate 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.
Ripristina Monica il

5

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 eventmetodo 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.


2

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.


2
"Gli eventi sono generalmente gestiti in Qt con segnali / slot" - in realtà no ... Gli oggetti QEvent vengono sempre passati in giro tramite virtual sovraccarichi! Ad esempio, non c'è il segnale "keyDown" in QWidget - invece, keyDown è una funzione virtuale.
Stefan Monov

D'

1
@Stefan: Direi che poche app Qt hanno la precedenza su keyDown (e invece usano principalmente segnali come QAbstractButton :: cliccato e QLineEdit :: editingFinished) per giustificare "tipicamente". Ho sicuramente bloccato l'input da tastiera prima, ma non è il solito modo di gestire gli eventi.
jkerian,

dopo la tua modifica (chiarendo cosa intendi per "evento"), il tuo messaggio ora è corretto. Mi astengo dal riutilizzare parole del genere, però. Le cose sono già abbastanza fangose ​​senza chiamare i segnali "un tipo di eventi".
Stefan Monov

2

Ho trovato questa domanda durante la lettura di "Elaborazione eventi" di Leow Wee Kheng. Dice anche:

inserisci qui la descrizione dell'immagine

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.


1

Un'altra piccola considerazione pragmatica: l'emissione o la ricezione di segnali richiede l'ereditarietà QObjectmentre 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 QObjectdi essere la prima super classe, e potresti non voler riorganizzare il tuo ordine di eredità solo per essere in grado di inviare segnali.)


-1

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 QQuickItempiù in) e gestirà i mouseMovesegnali 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).


Gli eventi hanno il vantaggio di propagarsi nella QObjectgerarchia 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.
anonimo

È possibile implementare regole di propagazione simili con i segnali. Non ci sono regole. che un determinato segnale non può essere riemesso a un altro oggetto (figlio). Potresti aggiungere un acceptedparametro di riferimento a un segnale e gli slot che lo gestiscono oppure potresti avere una struttura come parametro di riferimento con un acceptedcampo. Ma Qt ha fatto le scelte di design che ha fatto e ora sono scolpite nella pietra.
user1095108

1
Fondamentalmente stai solo reimplementando gli eventi se lo fai nel modo che suggerisci.
Fabio A.

@FabioA. Questo è il punto della domanda dell'OP. Eventi e segnali possono / potrebbero sostituirsi a vicenda.
user1095108

Se i tuoi eventi di reimplementazione, non stai sostituendo gli eventi. Inoltre : i segnali vengono implementati sopra gli eventi. Hai bisogno di una sorta di "sistema di eventi" per dire ad altri loop di eventi, possibilmente in un thread che non è il tuo, che in futuro dovrà eseguire una determinata funzione da qualche parte.
Fabio A.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.