2014-06-10 13 views
14

In alcuni esempi di Qt, vedo che usano QTimer::singleShot(0, this , SLOT(funcA())), perché non chiamare direttamente lo slot funcA? anche la stessa domanda per l'utilizzo di QMetaMethod::invoke per chiamare la funzione con i parametri.QTimer :: singleShot e QMetaMethod :: invoke

risposta

20

Le seguenti linee sono tutti funzionalmente equivalenti:

QTimer::singleShot(0, object, &Class::funcA); // Qt 5 
QTimer::singleShot(0, object, SLOT(funcA())); // Qt 4 
QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection); 

Come è ora evidente, l'intento è quello di eseguire la chiamata entro il ciclo degli eventi. La chiamata in coda risulta nella pubblicazione di un numero QMetaCallEvent in object. Questo evento è gestito da QObject::event e genera la chiamata del metodo desiderato. Così, i seguenti sono esattamente equivalente, anche se quest'ultimo è un dettaglio di implementazione privata - avermi permesso di saltare the details of instantiating the event:

QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection); 
QCoreApplication::postEvent(object, new QMetaCallEvent{...}); 

Questo è particolarmente utile in varie situazioni. Per esempio:

  • di eseguire del codice eventi dopo tutto finora pubblicati sono stati gestiti.

  • Da eseguire solo dopo l'avvio del ciclo degli eventi.

  • Per chiamare un metodo invokable che non è accessibile a causa dei modificatori di accesso C++. I metodi invokable sono: segnali, slot e metodi dichiarati Q_INVOKABLE.

  • La chiamata diretta non è sicura (leggi: un errore!) Quando uno QObject si trova in un altro thread, a meno che non si chiami esplicitamente un metodo documentato come thread-safe.

La chiamata in coda è una necessità se si vuole garantire che un ciclo di eventi si chiude immediatamente: un quit() chiamata diretta è un no-op, se il ciclo non è ancora in esecuzione.

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    app.quit(); // this is a no-op since the event loop isn't running yet 
    return app.exec(); // will not quit as desired 
} 

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); 
    return app.exec(); // will return immediately 
} 

Idealmente, utilizza postToThread da this answer, offre il modo più basso costo di chiamare metodi in altri thread:

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    postToThread([]{ qApp->quit(); }); 
} 

Un modo alternativo per farlo sta usando una QObject come sorgente del segnale:

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    { 
    QObject src; 
    src.connect(&src, &QObject::destroyed, &app, &QCoreApplication::quit, 
       Qt::QueuedConnection); 
    } 
    return app.exec(); // will return immediately 
} 

un altro modo sarebbe quello di utilizzare un evento personalizzato e agire in suo distruttore:

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    struct QuitEvent : QEvent { 
    QuitEvent() : QEvent(QEvent::None) {} 
    ~QuitEvent() { qApp->quit(); } 
    }; 
    QCoreApplication::postEvent(&app, new QuitEvent); 
    return app.exec(); // will return immediately 
} 
+0

Ricevo 'QObject :: startTimer: I timer possono essere utilizzati solo con i thread avviati con QThread' quando si utilizza QTimer :: singleShot in una pura funzione C++ (non all'interno di un QTthread) mentre' QMetaObject :: invokeMethod' non ha questo problema. C'è un modo per usare QTimer :: singleShot (o qualche metodo equivalente) che supporta il controllo in fase di compilazione? – Isaac

+0

@Isaac Sì. Vedi la modifica. Il modo più efficiente e infallibile di farlo è dato nel TL; DR di [questa risposta] (http://stackoverflow.com/questions/21646467/how-to-execute-a-functor-in-a-given- thread-in-qt-MCD-style/21653558 # 21653558). –

3

Ogni sistema ha un ciclo di eventi in cui vengono elaborati gli eventi. Dire come

application::processEvents() 
{ 
// process event list.. 
} 

ora il luogo in cui si scrive QTimer::singleShot(0, this, SLOT(doSomething())); potrebbe essere all'interno di un evento di trasformazione. Al termine di questo ciclo, processEvents verrà richiamato e verrà eseguito doSomething().

Quindi questo è come chiamare doSomething nel prossimo ciclo degli eventi, piuttosto che chiamarlo immediatamente. Spero che tu abbia avuto l'idea.

+0

Una volta immesso 'QCoreApplication :: exec', il codice del thread del gui è ** per definizione ** eseguito a causa di un evento (o un meccanismo specifico della piattaforma simile al segnale Unix asincrono). Quindi è davvero offuscare le cose da dire "potrebbe essere all'interno di un evento di elaborazione". Una volta che 'app.exec()' è chiamato da 'main()', gli eventi sono l'unica ragione per * qualsiasi * codice UI da eseguire, praticamente. –

+0

Quando si elaborano eventi inviati (al contrario di inviati) in Qt, il dispatcher di eventi del loop eventi si trova nello stack di chiamate da qualche parte sotto la chiamata finale a 'Foo :: event (QEvent *)'. Al termine del codice, il controllo ritorna al dispatcher e il dispatcher preleva un altro evento e richiama di nuovo il metodo 'evento (QEvent *)' dell'oggetto di destinazione o va in sospensione in attesa di altri eventi. –

+0

"gli eventi sono l'unica ragione per cui qualsiasi codice UI esegue" disclaimer: nonostante i segnali Unix e le porte di completamento di I/O di Windows. –

0

Questi metodi possono anche essere utilizzati per invocare membri protetti e privati ​​di una classe (se definiti come slot) da un ambito che altrimenti richiederebbe l'accesso pubblico.