IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

GTK+ avec C & C++ Discussion :

boucle de fond bloquée par app->run(*p_main_window)


Sujet :

GTK+ avec C & C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2012
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2012
    Messages : 114
    Points : 53
    Points
    53
    Par défaut boucle de fond bloquée par app->run(*p_main_window)
    Bien le bonjour,
    je bosse sur raspbian stretch installé depuis noobs sur raspberry pi 3B.
    Je développe une appli avec IHM glade+gtk3mm, en c++.
    Je ne suis pas très à l'aise avec les termes à utiliser et suis plutôt débutant dans ces technologies.

    J'ai déjà développé une appli dont le main se contentait, après qq initialisations, de lancer l'interface graphique. Puis toutes les actions étaient provoquées par des interruptions, de type timer ou de type hardware. Le main ressemblait à cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
     
    /**************/
    /* main         */
    /**************/
    int main (int argc, char *argv[])
    {
      //auto app = Gtk::Application::create(argc, argv/*, "org.gtkmm.example"*/);
      auto app = Gtk::Application::create();
     
      //Load the GtkBuilder file and instantiate its widgets:
      auto refBuilder = Gtk::Builder::create();
      try{
        refBuilder->add_from_file("ihm.glade");
      }
      catch(const Glib::FileError& ex){
        std::cerr << "FileError: " << ex.what() << std::endl;
        return 1;
      }
      catch(const Glib::MarkupError& ex)
      {
        std::cerr << "MarkupError: " << ex.what() << std::endl;
        return 1;
      }
      catch(const Gtk::BuilderError& ex){
        std::cerr << "BuilderError: " << ex.what() << std::endl;
        return 1;
      }
     
      refBuilder->get_widget("main_window", p_main_window);
     
    if(p_main_window){
        refBuilder->get_widget("button_Test", p_button_Test);
     
        refBuilder->get_widget("label_Connexion_reseau", p_label_Connexion_reseau);
        if(p_button_Test) p_button_Test->signal_clicked().connect( sigc::ptr_fun(on_button_test_clicked) );
     
        p_main_window->set_size_request(800,480);
        p_main_window->set_title("xx");
     //   p_graph_drawing_area->set_size_request(500,300);
     
      initPin(); //init les gpio
      initIsrIo(); //init ninterruption sur gpio
      signal(SIGALRM, sig_handler);  // init signal interruption sur timer
     
      p_date_label->set_label (horodatageDate());
      g_idle_add(IhmDateRefresh,0);
     
      g_timeout_add(1000, timer_1_s, 0);
      g_idle_add(initAll,0);
      ouvertureLogSession();
     
      app->run(*p_main_window);
     
      }
      }
     
      delete p_main_window;
      return 0;
    }
    Pour cette nouvelle appli, je suis très embêté du fait que l'appel est bloquant. En effet, je voudrais cette fois, après qq initialisations, lancer l'interface graphique puis passer au déroulé de mon programme, sans nécessité d'attendre une interruption pour le déclenchement.
    Je ne sais pas comment m'y prendre. J'ai bien penser à lancer le run dans un autre thread, mais j'ai peur que ça complique beaucoup (en plus de lancer dans un autre thread, complique des mises à jour de l'interface depuis thread du main par exemple).

    Il doit exister une façon de faire, propre, assez accessible, dont vous pourriez me donner le secret ?

    J'espère avoir été clair.

    Bonne journée

  2. #2
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Bonjour,

    tu ne peux rien faire au fait que run soit bloquant. Le principe même d'une interface graphique c'est de réagir à des événements, et pour réagir à ces événéments, il faut pouvoir les recevoir, les traiter puis attendre de nouveaux événements. C'est la partie "attendre de nouveaux événements" qui fait que cela ne peut qu'êre bloquant. C'est le principe même d'une pompe à événements.

    Il te faut donc au lieu d'adapter GTK+ (ou n'importe quelle interface grapqhique d'ailleurs) à ton usage, adapter ton usage au fonctionnement d'une interface graphique. Cela veut dire dans le cas présent:
    • pas de signaux C ANSI (man 2 signal), qui font mauvais ménage avec GTK+
    • utiliser g_idle_add de préférence ou g_timeout_add_seconds ou g_timeout_add pour demander à GTK+ d'appeler ta callback pour vérifier un état du matériel.


    Il y a sans doute mieux, notamment pour éviter de faire du polling, mais c'est un premier pas.
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2012
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2012
    Messages : 114
    Points : 53
    Points
    53
    Par défaut
    Bonjour Liberforce, et merci,

    utiliser g_idle_add de préférence ou g_timeout_add_seconds ou g_timeout_add pour demander à GTK+ d'appeler ta callback pour vérifier un état du matériel.
    Dans ma précédente appli, j'utilisais g_idle_add et g_idle_timeout_add essentiellement lorsque je cherchais à rafraichir l'interface graphique (j'avais compris que ça permettait de patienter que l'interface soit "libre" pour y accéder, avec mes mots à moi). Je faisais ces appels depuis des fonctions qui étaient exécutées du fait d'interruptions.

    Mais je ne vois pas bien comment ça va fonctionner dans cette situation. Je vais lancer la boucle de fond, avant de faire le run, via un g_idle? Peux-tu me prèciser l'ordre des choses dans le code et dans le temps?

    Merci

  4. #4
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    g_idle_add, g_timeout_add et g_timeout_add seconds sont des GSource. Ce sont des sources d'événements. Elles envoient des événements dans la pompe à événements (appelée "main loop" dasn la doc GTK+). Cette pompe traite ensuite les événements qui lui arrivent au fil de l'eau. Tu n'as pas mis le code de tes callbacks, et ne dis pas ce que ton interface graphique doit faire en réponse aux changements des tes I/O, alors c'est difficile de te dire exactement quoi faire. Mais le principe est le suivant:

    1. appeler g_idle_add en lui passant une callback
    2. dans cette callback:
    2.1. tu vérifies l'état de tes I/O
    2.2. en fonction des changements d'états, tu déclenche des actions sur tes widgets (changement d'état d'un bouton, ou autre)
    2.3. tu sors de ta callback le plus rapidement possible pour que la pompe a événements puisse continuer à traiter les événements
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2012
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2012
    Messages : 114
    Points : 53
    Points
    53
    Par défaut
    Bonjour,
    je vais essayer de réexpliquer mieux ce que je cherche à faire
    je voudrai cette fois, après qq initialisations, lancer l'interface graphique, puis passer au déroulé de mon programme, sans nécessité d'attendre une interruption pour le déclenchement
    je ne cherche pas seulement à mettre à jour une l'interface graphique au gré des évènements des I/O, cela je savais le faire dans ma première apllication, de ces 2 façons:
    • interruption sur I/O -> g_idle_add vers mise à jour de l'interface
    • g_idle_time.... -> lecture des I/O périodique pour mise à jour interface

    je veux:
    • préparer l'interface graphique
    • lancer l'interface graphique (run)
    • dérouler un certain nombre de fonctions, dont je nomme l'ensemble "boucle de fond", le coeur du programme, qui peut durer plusieurs minutes (avec des mises à jour de l'interface graphique via des g_idle_add)

    mais le "run" m'empêche d'aller jusqu'au 3° point. Logique.
    Dois-je alors lancer cette boucle de fond via un g_idle_add (ou g_idle_time)? Je ne crois pas, puisque cette boucle est très longue elle ne libérera pas de sitôt la pompe à évènement.

    Me fais-je mieux comprendre?

  6. #6
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Dans ce cas je vois 2 solutions:
    1. 1 thread à part qui fait tes traitements longs et fait les mises à jour graphiques via g_idle_add
    2. ou bien une machine d'état qui découperait tes traitements en petits morceaux, suffisamment courts pour ne pas bloquer la pompe à événements. Voici un exemple avec la technique du lazy loading.


    Si tu sais déjà comment envoyer au thread principal les actions de mises à jour via g_idle_add, c'est sans doute la soluition la plus simple. Je crois que tu peux utiliser un GTask pour ça.
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2012
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2012
    Messages : 114
    Points : 53
    Points
    53
    Par défaut
    bien bien,
    • 1 thread à part qui fait tes traitements longs et fait les mises à jour graphiques via g_idle_add


    Ca me disait bien à froid, jusqu'à ce que je réalise que le passage des données d'un thread à l'autre risque d'être compliqué.
    Car j'imagine que si je lance l'interface dans le thread principal, appelle un second thread pour ma boucle de fond, et lance depuis celeui-ci des g_idle_add, ben ça va posé des problèmes. Tu confirmes?
    Je pourrai aussi juste mettre à jour des variables d'affichage dans ce second thread, et avoir un g_timeout_add dans le premier thread pour rafraichir l'interface toutes les 0.5 secondes par exemple (même si il n'y aucun changement d'affichage. Ca bouffe de la ressource pour rien dans ce cas, pas optimum dans le fonctionnement)
    Et c'est peut-être pour éviter cela que tu me branche sur GTask. J'ai attaqué la doc, et je me retrouve dans un autre pb trop souvent rencontré: je ne la comprends pas. C'est difficile pour moi d'attaquer des doc de ce type avec un vocabulaire très spécifique, en anglais. Je crois comprendre que ça permet, entre autre, le partage entre thread, masi j'ai du mal à approfondir. Mais je crains la mise en oeuvre.



    • 2 ou bien une machine d'état qui découperait tes traitements en petits morceaux, suffisamment courts pour ne pas bloquer la pompe à événements. Voici un exemple avec la technique du lazy loading.


    Intéressant également. Je regarde si je peux découper ma boucle de fond en un certain nombre d'états, élémentaires. Si je comprends bien, l'idée est de ne pas empiler trop d'évènement pendant l'exécution du morceau. Par exemple:
    1. avant le "run" je lance un g_idle_add vers une routine (dite gestionnaire de routine) qui lance la routine de l'état courant
    2. je fais le "run"
    3. la routine de l'état courant est lancée, s'exécute, lance un g-idle_add vers mise à jour interface, positionne le nouvel état, lance le g_idle_add vers le gestionnaire de routine, se termine.
    4. g-idle_add vers mise à jour interface s'exécute automatiquement dés que possible
    5. puis retour à 3 automatiquement dés que possible

    ça ressemblerait à ça pour ce qui est d'une solution machine d'état si je me souviens bien. Lazy loading à l'air d'être fait pour cela aussi, mais à nouveau cela me semble fastidieux de rentrer dedans.

    peux-tu me dire, si tu comprends mes mots, si mes 2 formulations te paraissent justes?

    Merci beaucoup pour ton aide. J'espère ne pas t'exaspérer avec mes difficultés à rentrer dans des nouveautés, si je peux faire autrement (certes moins dans l'art du métier). Pour tout te dire, mes projets sont plutôt accés électronique, avec qq fois un contrôle/commande depuis un poste raspberrypi, c++. D'où ma non spécialité en la matière.

  8. #8
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Citation Envoyé par niconol Voir le message
    Ca me disait bien à froid, jusqu'à ce que je réalise que le passage des données d'un thread à l'autre risque d'être compliqué.
    C'est pour ça qu'on déconseille en général l'usage de threads, car GTK+ (comme beaucoup de toolkits) n'est pas thread-safe. Il faut faire attention à n'appeler aucune fonction GTK+ dans ton thread.

    Citation Envoyé par niconol Voir le message
    Car j'imagine que si je lance l'interface dans le thread principal, appelle un second thread pour ma boucle de fond, et lance depuis celeui-ci des g_idle_add, ben ça va posé des problèmes. Tu confirmes?
    Non, ça devrait passer tant que dans ton thread tu ne fais que donner des indications au thread principal ("tel entrée a changé d'état", etc.). Le principe est donc d'appeler g_idle_add dans ton thread, avec des données permettant au thread principal de savoir le travail à effectuer. La callback que tu passes à g_idle_add sera alors appelée par la main loop, dans le thread principal. C'est dans celui ci que tous les appels à GTK+ doivent se faire.

    Citation Envoyé par niconol Voir le message
    Je pourrai aussi juste mettre à jour des variables d'affichage dans ce second thread, et avoir un g_timeout_add dans le premier thread pour rafraichir l'interface toutes les 0.5 secondes par exemple (même si il n'y aucun changement d'affichage. Ca bouffe de la ressource pour rien dans ce cas, pas optimum dans le fonctionnement)
    Et c'est peut-être pour éviter cela que tu me branche sur GTask. J'ai attaqué la doc, et je me retrouve dans un autre pb trop souvent rencontré: je ne la comprends pas. C'est difficile pour moi d'attaquer des doc de ce type avec un vocabulaire très spécifique, en anglais. Je crois comprendre que ça permet, entre autre, le partage entre thread, masi j'ai du mal à approfondir. Mais je crains la mise en oeuvre.
    Je vais avoir du mal à t'aider, je sais que GThread est obsolète et qu'il faut utiliser GTask à présent mais je n'ai pas eu l'occasion de l'utiliser.

    Citation Envoyé par niconol Voir le message
    Intéressant également. Je regarde si je peux découper ma boucle de fond en un certain nombre d'états, élémentaires. Si je comprends bien, l'idée est de ne pas empiler trop d'évènement pendant l'exécution du morceau. Par exemple:
    1. avant le "run" je lance un g_idle_add vers une routine (dite gestionnaire de routine) qui lance la routine de l'état courant
    2. je fais le "run"
    3. la routine de l'état courant est lancée, s'exécute, lance un g-idle_add vers mise à jour interface, positionne le nouvel état, lance le g_idle_add vers le gestionnaire de routine, se termine.
    4. g-idle_add vers mise à jour interface s'exécute automatiquement dés que possible
    5. puis retour à 3 automatiquement dés que possible

    ça ressemblerait à ça pour ce qui est d'une solution machine d'état si je me souviens bien. Lazy loading à l'air d'être fait pour cela aussi, mais à nouveau cela me semble fastidieux de rentrer dedans.
    Pas besoin de chainer les g_idle_add, tu as la valeur de retour de ta callback qui dit s'il faut continuer à l'appeler. S'il faut continuer à l'appeler, tu renvoies G_SOURCE_CONTINUE, sinon G_SOURCE_REMOVE.
    En gros tu te retrouves dans ta callback avec du code du genre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    if (state == ACTION1)
    {
        /* faire ton action 1 */
        ...
     
        /* changer d'état si on a fini */
        state = ACTION2;
    }
    else if (state == ACTION2)
    {
        /* faire ton action 2 */
        ...
     
        /* changer d'état si on a fini */
        state = ACTION3;
    }
    else if (state == ACTION3)
    {
        /* faire ton action 2 */
        ...
     
        /* changer d'état si on a fini */
        state = END;
    }
     
    if (state == END)
    {
        return G_SOURCE_REMOVE;
    }
    else
    {
        return G_SOURCE_CONTINUE;
    }
    Citation Envoyé par niconol Voir le message
    Merci beaucoup pour ton aide. J'espère ne pas t'exaspérer avec mes difficultés à rentrer dans des nouveautés, si je peux faire autrement (certes moins dans l'art du métier). Pour tout te dire, mes projets sont plutôt accés électronique, avec qq fois un contrôle/commande depuis un poste raspberrypi, c++. D'où ma non spécialité en la matière.
    Pas de soucis, j'ai fait de l'électronique avant aussi, je comprends la problématique
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2012
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2012
    Messages : 114
    Points : 53
    Points
    53
    Par défaut
    j'ai un peu oublié cette discussion, tête dans le guidon,
    mais je n'ai pas oublié les bons conseils de liberforce, une machine d'état résout bien mes problèmes,
    merci pour tout

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 02/04/2013, 12h49
  2. [Timer] timer bloqué par une boucle for?
    Par Jidefix dans le forum Interfaces Graphiques en Java
    Réponses: 3
    Dernier message: 18/09/2006, 17h12
  3. bloqué par cookie
    Par kidu dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 19/08/2005, 09h59
  4. Site sur CD - javascript bloqué par SP2
    Par loutente dans le forum Général Conception Web
    Réponses: 16
    Dernier message: 17/05/2005, 14h22
  5. Select qui boucle ou se termine par un Ora-01460
    Par PatriceP dans le forum Oracle
    Réponses: 4
    Dernier message: 29/10/2004, 08h53

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo