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

Langage Delphi Discussion :

Correction/amélioration d'un dialogue avec progression


Sujet :

Langage Delphi

  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    707
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 707
    Points : 777
    Points
    777
    Par défaut Correction/amélioration d'un dialogue avec progression
    Bonjour,

    J'essaye de programmer une boîte de dialogue qui doit servir à afficher la progression de longs calculs, avec possibilité de les interrompre.
    J'ai commencé à écrire quelque chose mais je ne suis pas satisfait du concept... j'ai besoin de votre aide pour améliorer et simplifier son fonctionnement (en particulier la partie TThread qui ne va pas, devrait être intégrée à la boîte de dialogue; je sais que je m'y prends mal, je bloque sur ces concepts abstraits !)

    Je mets en PJ ce que j'ai écrit, avec un projet de démo compilé, merci d'avance à qui pourra m'aider !

  2. #2
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 173
    Points
    4 173
    Par défaut
    Personnellement je traite ce genre de problème de la façon suivante :
    - J'ai un traitement qui sera long à s'exécuter. En général, ce dernier ce trouve dans un objet. Je définis un événement OnProgress sur l'objet, qui me permet d'indiquer un message, un pourcentage de progression... un peu comme tes événements OnStep.
    - Périodiquement au cours du traitement, l'objet déclenche l'événement OnProgess pour dire où il en est.

    Ensuite, pour afficher une barre de progression, un log de l'état d'avancement..., il suffit de connecter une boîte de dialogue quelconque sur l'évémenent OnProgress, sur le principe du DP de l'Observer.
    Lorsque l'événement déclenche, la boîte de dialogue ajoute le message à la fin d'un memo, fait avancer une barre de progression... ou va lire d'autre info de l'objet, le tout sans oublier d'appeler Application.ProcessMessages pour rafraichir l'écran.

    Si tu veux en plus que la boite de dialogue permette d'annuler le traitement, il suffit que l'événement ait un code de retour pour dire s'il faut continuer où arrêter (ou que ton objet ait une méthode pour indiquer qu'il faut arrêter le traitement).
    Comme j'appelle ProcessMessages lors de chaque déclenchement de l'événement, ça permet également à l'utilisateur de cliquer sur un bouton pour positionner un flag qui sera retourné par l'événement.

    Après il suffit d'afficher la boîte de dialogue avec un Show avant le début du traitement, puis de la fermer à la fin.

    Il n'y a pas besoin de multi-threader l'appli. Bien sûr, c'est faisable en multitreading et ça permet d'avoir une plus grande fluidité sur l'interface, mais c'est beaucoup plus compliqué, il faut synchroniser l'IHM et le thread qui s'execute. Si tu n'as qu'un seul traitement en tâche de fond et que l'utilisateur ne peut rien faire d'autre pendant ce temps, ce n'est pas la peine de se prendre la tête pour ça...

  3. #3
    Membre expérimenté

    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    685
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 685
    Points : 1 608
    Points
    1 608
    Par défaut
    Je n'ai pas le temps d'analyser en détails ton code, mais il parait avoir "une bonne tête", quoi qu'un peu long.. Par rapport à ce que dit Frank, même si j'utilise aussi parfois ProcessMessages par paresse pour éviter la création de threads, ce n'est pas comme ca que Windows doit être programmé : on doit réagir à des évènements et ne pas introduire des "boucles de scrutation" comme l'appel à cette fonction. Dans le cas ou tu le fais, il faut faire attention à ne pas traiter un évènement qui déclenche le relancement de la procédure, sinon tu rentreras dans une boucle infinie... Tout ca pour dire que d'un point de vue personnel, je conseille l'approche multi-thread que tu as choisie, sauf si ca n'en vaut *vraiment* pas la peine.

  4. #4
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 173
    Points
    4 173
    Par défaut
    C'est vrai qu'avec ProcessMessages il faut faire attention à ce qu'on fait.

    Cependant, multi-threader l'appli pose les mêmes problèmes que ProcessMessages :
    Tu vas avoir un thread principal qui gère l'IHM, et un autre qui exécute le traitement. Pendant que le traitement s'exécute, le thread principal, il fait quoi ? Il continue à traiter les messages Windows, exactement comme on le fait avec ProcessMessages, avec les mêmes problèmes. Rien n'interdit à l'utilisateur de relancer le traitement déjà en cours d'exécution...
    Donc au final, tu as les mêmes problèmes plus ceux propres au multi-threading.

  5. #5
    Membre expérimenté

    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    685
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 685
    Points : 1 608
    Points
    1 608
    Par défaut
    Donc au final, tu as les mêmes problèmes plus ceux propres au multi-threading.
    Non, ce n'est pas ce que je voulais dire. Le fait d'appeller ProcessMessages peut amener à entrer en boucle infinie *sans que ce ne soit l'utilisateur qui en soit la cause* :

    Cf ce thread http://www.developpez.net/forums/sho...d.php?t=299066

  6. #6
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    707
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 707
    Points : 777
    Points
    777
    Par défaut
    Merci de votre intervention, je commençais à désespérer

    Franck: les threads me permettent, comme tu le soulignes, d'avoir un traitement plus fluide. Il faut bien voir que dans ma démo, les calculs sont très rapides, mais je destine cette boîte de dialogue a des traitements qui seront beaucoup plus longs (jusqu'à plusieurs heures voire plus), et beaucoup plus compliqués. Et du coup, difficile de placer un ProcessMessages à tel ou tel endroit en espérant ne pas trop ralentir l'interface (je dois garder un bouton "Annuler" réactif), et exécuter tout ça dans un thread permet aussi de déplacer la boîte de dialogue à l'écran ce qui peut aussi avoir son utilité. Ensuite comme ce dialogue est modal, l'utilisateur n'a pas la possibilité de relancer le traitement sans l'avoir annulé auparavant.

    Reisubar: ce qui ne me plaît pas trop dans mon code, c'est que je dois dériver à chaque fois mon objet de traitement de la classe TProgressThread et réimplémenter la méthode Execute. Je préfèrerais avoir une procédure classique que je pourrais affecter à un thread de la boîte de dialogue. D'autre part, ma façon de faire complique aussi les choses si je veux décomposer mon traitement principal en plusieurs sous-procédures comme montré dans l'exemple avec la procédure Etape1 (il faut passer le TProgressThread en paramètre).

    Je me demande aussi si il vaut mieux faire comme ça (avec des évenements) ou si l'utilisation de messages ne serait pas plus pratique ? Ou peut-être encore faire à la façon d'un TaskDialog ? (à base de timer je crois: d'après ce que tu dis Reisubar, ce n'est pas terrible cette méthode de scrutation)

  7. #7
    Membre expérimenté

    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    685
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 685
    Points : 1 608
    Points
    1 608
    Par défaut
    ce qui ne me plaît pas trop dans mon code, c'est que je dois dériver à chaque fois mon objet de traitement de la classe TProgressThread
    Ca s'appelle la programmation orientée objet

    Je préfèrerais avoir une procédure classique que je pourrais affecter à un thread de la boîte de dialogue
    Comme tu l'as fait jusqu'ici, rien ne t'empêches de surcharger le constructeur de ta fiche pour lui passer une référence sur une méthode de "callback" qui sera appellée par Execute. Dans cette optique, ce serait la fiche qui créerait le thread dans son constructeur. C'est plus conforme aux principes OO d'encapsulation.

    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
    type
      TMyThreadedMethod = procedure (AThread: TExecuteThread) of object;
     
    constructor TProgressForm.Create(AOwner: TComponent; ACallback: TMyThreadedMethod);
    begin
      ...
      FProgressThread := TProgressThread.Create(ACallback);
    end;
     
    constructor TProgressThread.Create(ACallback: TMyThreadedMethod);
    begin
      inherited Create(True); 
      FCallback := ACallback;
      Resume(); //Si on suppose que tu veux lancer ton thread dès sa création...
    end;
     
    procedure TProgressThread.Execute()
    begin
      ...
      if Assigned(FCallback)
        FCallback(Self);
      ...
    end;
    Après, reste à voir la communication entre la procédure déportée et tes évènements, d'où le fait que je passe le thread en paramètre du callback. J'espère que tu saisis le principe.

    Une autre solution propre de ne pas du tout encapsuler le thread dans ta fenêtre, mais de les faire communiquer à coup de callbacks et de Synchronize().

    si l'utilisation de messages ne serait pas plus pratique ?
    L'utilisation de messages ne permet pas de threader "magiquement" le process. A la limite, ils peuvent servir de moyen de communication inter objets, néanmoins.

    à base de timer je crois
    Idem, cela ne threade pas l'application. La boucle de message continue à être celle de l'application, et si un traitement long est en cours, le message timer ne sera plus reçu d'où le "lag" visible de l'appli.

  8. #8
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 173
    Points
    4 173
    Par défaut
    à base de timer je crois
    Idem, cela ne threade pas l'application. La boucle de message continue à être celle de l'application, et si un traitement long est en cours, le message timer ne sera plus reçu d'où le "lag" visible de l'appli.
    La technique avec un timer est différente. Tu as bien deux threads : Le thread principal qui traite les messages windows et gère l'IHM et un thread secondaire qui effectue son traitement tout seul dans son coin.
    Le timer sur le thread principal sert simplement à dire à la form qu'elle doit se rafraichir, à ce moment elle va lire différente infos sur l'état du traitement en cours pour mettre à jour son affichage.
    C'est assez simple à mettre en oeuvre. Le principal intérêt de cette technique c'est que ça permet d'avoir un traitement totalement indépendant de la fenêtre qui affiche sa progression, sans avoir à se préoccuper d'envoyer les informations sur la progression au thread principal, c'est lui qui va lire ce dont il a besoin.
    Mais ça nécessite quand même de bien protéger les "variables d'états" du traitement contre les accès simultanés.
    L'autre inconvénient c'est que l'écran ne sera mis à jour qu'à la fréquence du timer. Donc ça peut ne pas être très fluide à l'écran.

    Une autre solution propre de ne pas du tout encapsuler le thread dans ta fenêtre, mais de les faire communiquer à coup de callbacks et de Synchronize().
    Là je ne comprends pas. Que le thread soit encapsulé dans la fenêtre ou pas, seul le thread principal doit toucher à l'IHM. Donc dans tous les cas, le thread devra communiquer avec la fenêtre à coup de Message ou de Synchronize.

  9. #9
    Membre expérimenté

    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    685
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 685
    Points : 1 608
    Points
    1 608
    Par défaut
    La technique avec un timer est différente. Tu as bien deux threads : Le thread principal qui traite les messages windows et gère l'IHM et un thread secondaire qui effectue son traitement tout seul dans son coin.
    OK, j'ai mal compris... Effectivement, je connais et j'utilise ceci

    Là je ne comprends pas. Que le thread soit encapsulé dans la fenêtre ou pas, seul le thread principal doit toucher à l'IHM. Donc dans tous les cas, le thread devra communiquer avec la fenêtre à coup de Message ou de Synchronize.
    Oui, je me suis mal exprimé : plutôt que d'utiliser des callbacks comme il a fait dans son code (qui pourront mener à l'appel de synchronize), directement appeler Synchronize() sur l'instance globale de la fenêtre (i.e. Synchronize(MyForm.UpdateBidule()) depuis le thread. Moins souple, puisque cela "fige" des références à des variables globales alors que les pointeurs de fonctions peuvent être assignés à des procédures d'objets différents en fonction du contexte.

    Ceci dit, plus j'y pense, plus j'aboutis au fait que par rapport à la question, la solution "timer" est moins volumineuse en code (bien qu'à mes yeux moins élégante...)

  10. #10
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    707
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 707
    Points : 777
    Points
    777
    Par défaut
    Ah, je vois que ça a continué à discuter assez tard, désolé mais je me suis couché plus tôt que vous !

    Je vais me repencher sur la solution du timer alors, c'est vrai que ça permet de mieux séparer les parties traitement et affichage, et finalement on gère plus précisément le taux de rafraichissement. Avec les événements ou les messages, on "pulse" à fréquence variable, ça dépend énormément de la vitesse de traitement la machine donc c'est moins bien maîtrisé, il faut faire des compromis comme dans mon exemple ou je déclenche un événement tous les 1%.

  11. #11
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 173
    Points
    4 173
    Par défaut
    Avec les événements ou les messages, on "pulse" à fréquence variable, ça dépend énormément de la vitesse de traitement la machine
    Pas exactement, si tu mes en place un mécanisme de notification entre le thread secondaire et le thread principal, ça permet au thread principal de mettre à jour l'affichage dès qu'il s'est passé quelque chose dans le thread secondaire qui nécessite un rafraichissement. C'est ce qui permet d'avoir un retour écran le plus réactif possible. Tu rafraichis l'affichage chaque fois que ce dernier a besoin d'être rafraichit, et uniquement lorsqu'il a besoin d'être rafraichit.
    Sur un traitement lent, qui ne provoque pas beaucoup de rafraichissement de l'affichage, cette solution est la plus élégante car elle évite de rafraichir l'écran pour rien.

    Par contre, sur un traitement rapide qui déclenche beaucoup de notifications, le thread principal va passer son temps à se redessiner et ne pourra peut-être pas tenir la cadence...

    Avec le timer, tu rafraichis l'affichage chaque fois que le timer déclenche. Donc tu as un temps de latence entre une modification du traitement et le rafraichissement de l'affichage.
    De plus, si le traitement bouge assez peu, tu va régulièrement rafraichir l'écran à l'identique. Donc tu vas faire des traitements pour rien...

    Par contre, si le traitement est rapide et génère beaucoup d'événements à afficher à l'écran (par exemple, le traitement génère une trace décrivant ce qu'il est en train de faire et tu affiches ces logs dans un memos), le timer permet de ne pas rafraichir l'écran à chaque fois, et ainsi de regrouper les rafraichissement en une seule opération, à interval de temps régulier...

    Donc au final, tout dépend de ce que tu fais...

  12. #12
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    707
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 707
    Points : 777
    Points
    777
    Par défaut
    Dans les traitements très compliqués, c'est parfois difficile de trouver où déclencher les événements de progression pour avoir un retour visuel satisfaisant (je me trouve plutôt dans ce cas). Avec les timers, on n'a pas trop à se prendre la tête, on ne s'occupe que du traitement et on sait que l'information sera affichée régulièrement, qu'il y ait eu du changement ou pas. Après, à la limite, les intervalles sont ajustables selon la situation.

    Maintenant, hum... si quelqu'un avait un exemple d'utilisation d'un TaskDialog (idéalement TMS) avec un traitement parrallèle long, ça m'intéresse

Discussions similaires

  1. recherche composant pour dialoguer avec modem
    Par newbie qui galere dans le forum Bases de données
    Réponses: 1
    Dernier message: 15/10/2004, 23h20
  2. [C#] Lancer et dialoguer avec une console ?
    Par Selenite dans le forum Windows Forms
    Réponses: 4
    Dernier message: 12/09/2004, 19h48
  3. boîte de dialogue avec image de fond + texte
    Par Eugénie dans le forum MFC
    Réponses: 13
    Dernier message: 31/08/2004, 13h32
  4. Boite de dialogue avec opengl
    Par inddzen dans le forum OpenGL
    Réponses: 3
    Dernier message: 23/04/2004, 20h25
  5. dialoguer avec un serveur RADIUS
    Par jypee76 dans le forum Développement
    Réponses: 4
    Dernier message: 12/08/2003, 10h06

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