Routines dépendantes simultanées dans une application d'interface graphique

Sep 15 2020

L'idée est que, dans ma demande que j'ai 5 routines nommées long_process_1, long_process_2, long_process_3, long_process_4, long_process_5. Ce sont chacun de longs calculs qui accrocheraient l'interface graphique et ne peuvent être appelés que dans cet ordre. Lorsque chaque résultat est prêt, l'utilisateur doit saisir le résultat. Nous appelons donc ask_user_1, ask_user_2, ask_user_3, ask_user_4, ask_user_5qui doit évidemment fonctionner dans le fil de l' interface principale. Ainsi, le tout ne peut pas être exécuté dans un thread séparé dans son intégralité. Voici comment je l'ai fait:

void MainWindow::on_button_click() // starts with a button click
{
    QEventLoop eventLoop;
    QFutureWatcher<int> watch;

    // Watcher intercepts eventLoop when the computation is finished
    connect(&watch, &decltype(watch)::finished, &eventLoop, &decltype(eventLoop)::quit);

    auto const res1 = QtConcurrent::run(long_process_1);
    watch.setFuture(res1);
    eventLoop.exec(); // Handles GUI during computation
    auto const user_in1 = ask_user_1(res1);

    auto const res2 = QtConcurrent::run(long_process_2, user_in1);
    watch.setFuture(res2);
    eventLoop.exec();
    auto const user_in2 = ask_user_2(res2);

    auto const res3 = QtConcurrent::run(long_process_3, user_in2);
    watch.setFuture(res3);
    eventLoop.exec();
    auto const user_in3 = ask_user_3(res3);

    auto const res4 = QtConcurrent::run(long_process_4, user_in3);
    watch.setFuture(res4);
    eventLoop.exec();
    auto const user_in4 = ask_user_4(res4);

    auto res5 = QtConcurrent::run(long_process_5, user_in4);
    watch.setFuture(res5);
    eventLoop.exec();
    (void)ask_user_5(res5); // result is not used
}

Cela fonctionne bien. Je trouve cela beaucoup plus simple que d'exécuter le tout via des slots de signal "finis". Cela simplifie également la propriété des variables locales / résultats de fonction car ils sont tous dans une seule fonction. Les longues routines sont clairement découplées de la fonction qui les appelle. Cela semble un peu étrange dans l'ensemble. J'ai eu l'idée de très vieux documents Qt . Tous les commentaires sont les bienvenus.

Réponses

1 Noname Sep 15 2020 at 22:51

L'exécution de trucs non liés à l'interface graphique à l'aide de la distribution d'événements principale (gui-thread) est une sorte de pratique anti-interface graphique.

Si le premier processus doit être appelé, puis le deuxième processus, il s'agit donc essentiellement de longs appels synchrones (avec une invite utilisateur entre chacun)

Il est probable (bonne pratique) d'exécuter le premier long processus à l'aide d'un thread (généralement en dehors de event-dispatch / gui-thread).

Pendant ce temps, le long processus pourrait informer l'interface graphique du processus en envoyant des événements de progression (en supposant que l'interface graphique fonctionne comme un écouteur de progression de processus ici).

Maintenant, comme le long processus est exécuté par un thread, l'interface graphique est fonctionnelle et ne se fige pas, donc l'utilisateur peut voir la progression, ou contrôler la progression (par exemple, abandonner, etc.)

Une fois le long thread terminé, demandez au gui-thread / event-dispatch une invite utilisateur, et si le démarrage du deuxième long processus est une chose, allez-y comme le premier en utilisant un thread.

Global

Essayez de ne pas effectuer de trucs non-gui excessifs en utilisant gui-thread / event-dispatch. C'est en fait une bonne pratique.

Essayez de ne pas effectuer de tâches liées à l'interface graphique (telles que la mise à jour d'une valeur / d'un statut de barre de progression) en utilisant des threads non-GUI (hors de la portée du widget / de la distribution). Cela peut ne pas être valable pour tous les widgets, mais généralement (possible) tout changement d'interface graphique (ici comme le changement de barre de progression) semble être ignoré.
Mais en réalité, ce thread hors de portée ne pouvait pas forcer le thread main-gui à effectuer une actualisation / repeindre le composant cible.

En supposant que le fil de discussion-gui / event-dispatch est le gars qui est assis à côté de la porte du widget et qui attend toutes les requêtes liées à l'interface graphique.
Donc, si une demande de mise à jour de la barre de progression est effectuée correctement par la routine attendue, ce type repeindra la barre de progression cible car il le doit.

Mais que se passe-t-il lorsque la demande provient de l'extérieur de la portée de l'envoi d'événements? C'est plus comme entrer dans la pièce par les fenêtres, plutôt que par la porte et le gars à côté.
Donc, techniquement, la barre de progression sera mise à jour, mais pourrait ne pas être mise à jour immédiatement (ou pas du tout), car le responsable de la mise à jour de l'interface utilisateur n'a pas réalisé qu'il y avait une demande qui oblige les composants à se repeindre.