Utilisation des threads avec des progressbar, tlabel et showmessage

cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011 - 26 juin 2008 à 12:57
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011 - 1 juil. 2008 à 10:03
bonjour a tous,
je viens de me farcir quasiment tout les tutos concernant les thread, mais je n ai visiblement pas trouve la bonne solution, voila mon soucis :
J ai une fiche qui comporte un TLabel, un progressbar et un TTimer. le ttimer gere le progressbar qui , une fois arrive a 100%, verifie la presence d un fichier " hostX.ini ". Si ce fichier existe :
- le ttimer se met en pause
- le tlabel.caption change 
- un composant style "showmessage + progressbar" apparait pour informer que le fichier est en cours de transfert.
- le fichier .ini est envoye au client grace a un composant ICS. Ce dernier le recoit et le traite, le modifie, et le renvoie au serveur.
- le pseudo-showmessage se ferme.
- le progress bar se remet en route quand le .ini est receptionne, et le tlabel.caption rechange.

Donc tout ceci prend un certain temps, mais le truc c est que toute l application est figee pendant cette duree, le tlabel ne change pas, le showmessage apparait mais le plus souvent tout blanc, et sans la bar de progression qui s y trouve, etc...

J ai bien compris qu il faut pour tout ca utiliser les thread, mais les exemples que j ai pu trouver ne fonctionnent pas, ou plutot ne correspondent pas vraiment a ce que je cherche.
Si quelqu un a une idee, n hesitez pas et je vous donnerais peut etre plus de details sur certains bouts de code.
J ajoute aussi que mon soft a une 15zaine de fiches differentes, et que l envoie du fichier .ini implique l appel de bouts de codes dans plusieurs fiches, je ne peux donc absolument pas mettre tout le code de traitement et d envoie dans mon thread (enfin j espere ne pas avoir a en arriver la!)
Merci a tous en tout cas, ciaooo

miMim

17 réponses

l0sth34d2 Messages postés 76 Date d'inscription mercredi 21 mars 2007 Statut Membre Dernière intervention 13 novembre 2008 1
26 juin 2008 à 13:07
Pour ce qui est du problème de l'application qui fige.. tu doit faire passer la procedure Application.ProcessMessages pendant la progression.
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
26 juin 2008 à 13:16
heu, j ai lu un peu partout qu il etait fortement deconseille d utiliser cette commande afin d eviter les crash ou je ne sais trop quoi, mais plutot utiliser les thread a la place.


En tout cas merci pour ta rapidite , je viens de poster ya 15mn :)


 
0
l0sth34d2 Messages postés 76 Date d'inscription mercredi 21 mars 2007 Statut Membre Dernière intervention 13 novembre 2008 1
26 juin 2008 à 13:20
Pas de problème.

Si tu utilise Application.ProcessMessage un peu partout, ton application risque fort bien de crasher effectivement. Cette procedure permet à l'application de passer à la prochaine action, de faire un repaint, de minimizer, maximizer, etc.. durant une très grosse action. Utiliser cette procedure dans un evenement OnProgress (par exemple) sera sécuritaire et permettera à ta fiche de se rafraichir constamment durant la progression.
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
26 juin 2008 à 18:12
arghh, le probleme c est que je ne vois pas ou je peux inserer cette fonction, car a partir du moment ou le fichier .ini se trouve dans le repertoire, il se passe plusieurs evenements, le showmessage avec sa barre de progression qui boucle tant que le fichier n est pas revenu du client, un envoie du fichier en sendStr(), et tout ce ci dans des fiches differentes... c est un peu complique a expliquer simplement car le soft est assez complexe, et ca serait laborieux et difficile d expliquer le cheminement.
En fait, tout ce que je souhaite, c est de pouvoir eviter par n importe quel moyen le freeze, ou les problemes de rafraichissement des fenetres, tlabel et autre barprogress de mon application.
Aussi ajouter que tout ceci ne se passe pas sur la fiche principale, ,mais sur une fiche ouverte en ShowModal.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
26 juin 2008 à 19:23
Salut,

Je vois que tu n'as pas choisi le programme le plus simple pour commencer à utiliser des threads !

En tout cas, l'idée c'est de faire faire le traitement dans ton thread séparé (procédure Execute tu TThread) et l'affichage dans le thread principal (celui où tu es par défaut).

Pour cela, il va falloir que tu déplaces tout le code qui attend, puis récupère ton message, le modifie et le renvoie dans la procédure Execute et appeler une procédure de synchronisation pour mettre à jour l'affichage.

Par exemple, si ton Thread s'apelle TMyThread, tu auras:
unit UnitThread;

interface

uses
  Classes;

type
  TMyThread = class(TThread)
  private
    FStatus: Integer;     // on dira que 0=RAS - 1=erreur
    FProgress: Integer;   // Avancement en pourcentage.
    FMsg: string;         // Message à donner.
    FEvent: TNotifyEvent; // Evènement de progression à appeler régulièrement
    procedure AttenteMessage;
    procedure RecupereMessage;
    procedure ModifieMessage;
    procedure RenvoitMessage;
  protected
    procedure Execute; override;
    procedure DoProgress;
  public
    property Progress: Integer read FProgress;
    property Msg: string read FMsg;
    property OnProgress: TNotifyEvent read FEvent write FEvent;
  end;

var
  MyThread: TMyThread;

implementation

procedure TMyThread.Execute;
begin
  {>> Exécute les actions les unes à la suite des autres }
  FStatus := 0;
  AttenteMessage;
  if FStatus = 0 then
    RecupereMessage;
  if FStatus = 0 then
    ModifieMessage;
  if FStatus = 0 then
    RenvoitMessage;

  {>> Indique que tout est fini }
  FProgress := 100;
  FMsg := 'Tout est fini';
  Synchronize(DoProgress);
end;

procedure TMyThread.DoProgress;
begin
  if Assigned(FEvent) then
    FEVent(Self);
end;

procedure TMyThread.AttenteMessage;
begin
  FMsg := 'Attente de réception du message';
  FProgress := 0;
  while not Terminated do
  begin
    // attente ici.
    // Synchronize(DoProgress); a appeler de temps en temps comme Application.ProcessMessages
  end;
end;

procedure TMyThread.RecupereMessage;
begin
  FMsg := 'Réception du message';
  FProgress := 25;
  // récupération ici
   // Synchronize(DoProgress); a appeler de temps en temps
end;

procedure TMyThread.ModifieMessage;
begin
  FMsg := 'Modification du message';
  FProgress := 50;
  // modification ici
   // Synchronize(DoProgress); a appeler de temps en temps
end;

procedure TMyThread.RenvoitMessage;
begin
  FMsg := 'Renvoi du message';
  FProgress := 75;
  // envoi ici
  // Synchronize(DoProgress); a appeler de temps en temps
end;

end.
[copier-coller dans Delphi pour la colorisation]

Tu devrais déjà comprendre quelques trucs avec ça.

A utiliser comme suit :
uses
 
UnitThread;

[...]

MyThread := TMyThread.Create(True);

MyThread.FreeOnTerminate := True;

MyThread.OnProgress := MAJAffichage;
MyThread.Resume;

MAJAffichage étant une procédure dans ta fiche modale qui met à jor l'affichage bien entendu

Si quelque chose te bloque dnas ce code, dis le moi et je t'expliquerais.
Tu comprends bien que je ne peux pas tout t'expliquer en détails, c'est un peu long.

A+
Flo

Ressources Delphi, sources, tutoriaux, actu, ...: www.mx-dev.nethttp://te%3C/body
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
27 juin 2008 à 05:39
WOW!!! un grand merci a toi pour ton code et tes explications, je vais essayer d adapter tout ca dans mon appli, et te tiens au courant si j ai un soucis (ou si ca marche aussi bien entendu :)
a++
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
27 juin 2008 à 07:38
bon, ben j ai des galeres, comme je pouvais l imaginer!! j ai pu avoir un rafraichissement du tlabel, mais apres ca update la fenetre bizarement et le thread ne s arrete pas.
Je viens de faire une version simplifie du code que j utilise, peut etre que ca te donnera une meilleure idee :

// cette fiche est ouverte en ShowModal
procedure TDispatcherStart.FormShow(Sender: TObject);
begin
  RenderFarmIndex       := 0;    // index des differentes pools a verifier
  ProgressBar.position  := 0;    // barre de progression
  TimerChecking.Enabled := true; // timer avec interval=60
end;




procedure TDispatcherStart.TimerCheckingTimer(Sender: TObject);
begin
  TitleLabel.caption   := 'Checking Pool '+RenderFarmName[RenderFarmIndex];
  ProgressBar.position := ProgressBar.position+1;



  if ProgressBar.position=100 then
    begin
    // verifie la presence du fichier 'Host_PoolName.ini'
    if FileExists(RenderFarmName[RenderFarmIndex]+'.ini') then
       begin
         TitleLabel.caption         := 'Updating Pool...';
         TimerChecking.Enabled      := false;   // mise en pause du timer le temps de traiter le fichier
         TransferToHost(scenename,id,hostname); // procedure de traitement (variables recuperees du fichier .ini)
       end;



    ProgressBar.position:=0;
    RenderFarmIndex:=RenderFarmIndex+1;
    if RenderFarmIndex>RenderFarmCount then RenderFarmIndex:=0;
    end;
end;




procedure TDispatcherStart.TransferToHost(scenename,id,hostname:string);
begin
  // a cet endroit du code, le TitleLabel.caption n a pas pu se rafraichir,
  // il est toujours affiche en 'Checking Pool '+RenderFarmName[RenderFarmIndex]



  AClient := TTcpSrvClient(OPServer1.Client[strToInt(id)]);
  AClient.SendStr(Host_PoolNameIni.Strings.Text);    // envoie du fichier Ini au client



  TransferDialog.Instruction := 'Transfer '+scenename+' to '+hostname;  fileRcv :false;        // variable permettant de fermer ce ShowMessage sitrue
  TransferDialog.Execute;  // Composant genre ShowMessage avec ProgressBar en boucle, le tps du traitement
                           // il a un evenement OnProgress ki permet de mettre a jour son progressBar
                           // et verifie aussi si fileRcv=true;
                           // Ceci me permet d attendre le retour du fichier modifie par le client
                          // mais le message n apparait pas et le progressbar non plus


  // une fois le fichier modifie recupere du client par la procedure suivante, on peut remettre le timer en route
  TimerChecking.Enabled := true;
end;






procedure OPServerOnClientDataAvailable(Sender : TObject; Error  : Word);
begin
  // recuperation du fichier .ini par le client
  with Sender as TTcpSrvClient do
    begin
    RcvdLine               := ReceiveStr;
    OPServer.IniLines.Text := RcvdLine;
    end;
  OPServer.IniLines.saveToFile(RenderFarmName[RenderFarmIndex]+'.ini');



  fileRcv := true; // fermeture du TransferDialog
end;




Voila j ai essaye de simplifier au maximum le code, car en realite il est un peu plus complexe que ca.
Mais c est une bonne base pour me dire a quel endroit je peux inserer la creation du thread et quelles fonctions
je dois mettre en .Execute
J espere ne pas prendre trop de ton temps, mais je te remercie encore mille fois pour ton aide :)
a+


miMim
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
27 juin 2008 à 12:21
C clair que je suis loin d avoir tout compris aux Thread, mais ca commence a rentrer... 
Donc pour la progressBar, c volontaire, car je dois laisser environ 5 secondes d interval entre la verification des pools. Cette progressBar est juste "visuelle" pour ne pas laisser l appli sans rien faire pendant 5 secondes. 
Par contre, apres mon dernier post, j ai essaye un truc : J ai creer un autre progressbar ki tourne en boucle, et que j ai mis dans un Thread en me basant sur ton code (ca fonctionne nikel). Dans le FormShow, j ai mis les lignes pour creer le thread et le demarrer. Donc des que la fenetre s ouvre, je vois bien mon 2eme progressbar ki tourne en boucle dans son nouveau thread.
Le soucis, c est qu il se fige sitot que la procedure  OPServerOnClientDataAvailable est appelee. (cette procedure intervient quand le client renvoie le fichier .ini au serveur). Je pige pas car cette procedure se trouve dans une fiche de la form principale, et par consequent sur le thread principal. Cette procedure est assez longue a traiter, et donc le progressbar se fige tant que la procedure n est pas finie. Et ce que je comprend encore moins, c est que j ai mis volontairement un Label.caption:='toto'; en toute 1ere ligne de cette procedure, ce qui voudrait dire qu au moment ou elle est executee, le label devrait passer en 'toto', ben non, il passe en 'toto' une fois la procedure terminee :(
Ca me depasse un peu la programmation parfois mais bon je ne me decourage pas, et j te remerci encore pour tes infos, j ai mis a jour mon code avec ton nouveau post, mais j ai toujours le meme pbm... je vais jeter un coup d oeil a ton soft... a+ tard
0
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
27 juin 2008 à 13:21
"Je pige pas car cette procedure se trouve dans une fiche de la form principale, et par consequent sur le thread principal"

=> Archi-faux !
On s'est tous déjà fait avoir au moins une fois à ce truc là (moi le premier ), mais au moins, tu vas comprendre maintenant !

Tout se qui se déroule dans la procédure Execute du TThread est dans un thread séparé du thread principal et peut donc s'écouler en parallèle.
Il en va de même pour toutes les procédures qui sont appelées, directement ou indirectement par le code que tu places dedans !
Par conséquent, si tu appelles MAJAffichage depuis la méthode Execute, MAJAffichage se déroule dans le thread que tu auras crée ! Et c'est justement ce qu'il ne fauit pas faire (je rapelle que le but est de séparer Affichage et Traitements)

Donc, pour cela, on va stocker les données à transmettre dans l'objet du thread (dans mon exemple c'est FMsg et FProgress) puis on va synchroniser*** les deux threads: c'est le rôle de l'appel à Synchronize(). Tu lui transmets en paramètre la procédure à appeler (dans l'exemple c'est DoProgress) qui ne doit pas avoir de paramètres (c'est pour cela qu'on est obligé de stoker les données à transmettre dans l'objet).

Cette procédure "synchronisée" est exécutée dans le thread principal, ça tombe bien, c'est ce qu'on voulait ! On peut donc appeler sans soucis MAJAffichage et tout ira bien.
A noter que, pendant que MAJAffichage (ici appelée indirectement via un évènement FEvent) s'éxécute, l'exécution du thread est suspendue. Cela signifie que MAJAffichage devra être une procédure très rapide (quelques lignes) pour ne pas ralentir le thread. Mais normalement, les procédures de mise à jour de l'affichage le sont.
Deuxièmement, il ne faut pas appeler Synchronize() trop souvent, car chaque appel demande un délai de synchronisation non négligeable. Si c'est indispensable, il existe d'autres méthodes plus pratiques (PostMessage et WM_COPYDATA par exemple)

***: en fait, la synchronisation est un processus assez compliqué. Le nouveau thread attend que le thread principal retourne dans sa boucle d'éxécution (la boucle des messages windows) et fait exécuter sa méthode à ce moment là. Car on ne peut pas injecter du code comme ça au milieu de l'éxécution d'un thread !
D'où le temps de latence.

Voila, c'était le petit cours de théorie.

Ressources Delphi, sources, tutoriaux, actu, ...: www.mx-dev.nethttp://te%3C/body
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
30 juin 2008 à 05:25
ok, je commence a piger un peu mieux le fonctionnement, mais ca n a pas resolu mon probleme...


J ai teste un code simple avec creation d un thread, et la procedure MAJAffichage, ca fonctionne bien, aucun soucis, et mes label et progressbar se reactualisent... Par contre ca bloque toujours quand la procedure OnDataAvailable est appelee pour recevoir le fichier du client, et ca prend quelques secondes a chaque fois...


Je vais continuer de chercher, ou essayer d utiliser un autre composant qu ICS pour la communication entre les machines, mais bon ca ne m arrange pas trop :S


a plus tard...


en tout cas merci pour le ptit cours theorique!


 
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
30 juin 2008 à 06:32
Je viens de tester quelquechose dans l evenement OnDataAvailable :
Jusqu a maintenant, le thread etait bloque tant que le contenu de l evenement OnDataAvailable n etait pas arrive au bout. Du coup la procedure MAJAffichage s executait qu a la fin de l evenement, donc totalement inutile.
J ai donc teste 2 codes que j ai mis AU DEBUT de l evenement OnDataAvailable :
      1-  Sleep(5000);
resultat : l application se fige 5 secondes, puis poursuit le code jusqu au bout mais SANS METTRE A JOUR l affichage.
      2- ShowMessage('toto');
resultat : l application affiche le message 'toto' et MET A JOUR l affichage des label et progressbar.

Donc la 2eme solution fonctionne, sauf que je voudrais eviter de devoir afficher un message a chaque fois.

... on y est presque :)
0
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
30 juin 2008 à 10:18
Ahh... tes composants ICS sont en mode bloquant !
Normalement, tu peux les configurer en mode non bloquant.
C'est un peu différent à utiliser mais ça ne bloque pas le thread pendant 5 secondes: tout se passe avec des évènements.
Mais, n'étant pas pro avec ICS je ne peux t'en dire plus...
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
30 juin 2008 à 13:03
arghhh, pourtant je me suis tape toute la doc et les faq concernant le composant TWSocket, et d apres ce qu ils disent, le transfer et la reception des donnees ne bloquent jamais l application... si tu veux jeter un oeil, j ai lu ca ici :
http://users.pandora.be/sonal.nv/ics/faq/TWSocket.html#MultithreadedC4
car je voulais voir si par hasard je devais utiliser l option multithread du composant, mais apprement cela n est pas necessaire.
En tout cas, ils disent bien que le composant est non bloquant, et je ne vois donc pas pourquoi lorsque j affiche un showmessage au tout debut de l evenement ca ne bloque pas le thread.
...
0
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
30 juin 2008 à 16:43
Bizarre alors!
Poste donc ici tout ton code actuel qui concerne le thread et l'affichage et je regarde.
Sinon, ça va pas être évident de trouver une solution.http://www.mx-dev.net
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
1 juil. 2008 à 05:05
procedure TAdmin.OPServerOnClientDataAvailable(Sender : TObject; Error  : Word);
begin


MyThreadRcvMsg := ThreadRcvMsg.Create(True);
MyThreadRcvMsg.FreeOnTerminate := True;
MyThreadRcvMsg.OnProgress := UpdateDisplay;
MyThreadRcvMsg.Resume;



              //  showmessage('toto');         // quand je laisse le showmessage apparaitre, cela n entraine pas le freeze du thread
with Sender as TTcpSrvClient do
        begin
        FromClientHost  := IPAddrToHostName(GetPeerAddr);
        FromClientIP    := GetPeerAddr;
        RcvdLine := ReceiveStr;
        ... 
        ... pas mal de fonctions et traitements se trouvent ici...
        end;

MyThreadRcvMsg.Terminate;
end;

procedure TAdmin.UpdateDisplay(Sender: TObject);
begin
DispatcherStart.AdvProgress1.Position:=round(MyThreadRcvMsg.Progress/1000);
DispatcherStart.TitleLabel.caption:=MyThreadRcvMsg.Msg;
end;

unit UnitThreadRcvMsg;
...
...
procedure ThreadRcvMsg.DoUpdate;
begin
  if Assigned(FEvent) then
    FEvent(Self);
end;

procedure ThreadRcvMsg.Execute;
begin
  FMsg:='Updating...';
  FProgress:=0;
  RcvMsg;
end;

procedure ThreadRcvMsg.RcvMsg;
begin
  FProgress := 0;
  while not Terminated do
    begin
    inc(FProgress);
    if FProgress>100000 then
        FProgress:=0;
    Synchronize(DoUpdate);
    end;


end;

voila le code, j espere que ca te donnera une idee, moi j en ai plus!!!
merci encore :)
0
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
1 juil. 2008 à 09:34
Rahhh mais tu ne fais pas ce que je te dis !

Le code est dans le thread séparé, l'affichage dans le thread principal.
TOUT le code du serveur (donc tout ce qui touche de près ou de loin à OPServer - les appels de procédure, les évènements) sont à placer dans la méthode Execute du thread !

Ce qui veut bien sûr dire que ton composant OPServer n'est plus sur ta fiche, mais déclaré dans la section "private" du thread et crée dynamiquement.

Ne désespères pas, mais là tu n'es pas du tout parti du bon côté. Je te recommande une fois de plus de laisser ce projet de côté et de faire un exemple fonctionnel (pour de vrai) en utilisant un thread.

Ressources Delphi, sources, tutoriaux, actu, ...: www.mx-dev.nethttp://te%3C/body
0
cs_mimimou Messages postés 48 Date d'inscription vendredi 17 janvier 2003 Statut Membre Dernière intervention 31 août 2011
1 juil. 2008 à 10:03
wowww, ca complique les choses la... j avais essaye de mettre tout le code de l evenement OnDataAvailable dans un thread, mais ca me fou en l air l application, car les donnees recues arrivent en pagaille et n ont pas le temps d etre traitees jusqu au bout avant la reception d une autre, ou je sais pas mais ce qui est sur c est que ca m a tout nique!!
Bon, je crois que je vais mettre de cote le probleme, et voir un peu plus tard en testant ca dans une nouvelle appli plus simple...
Mais merci pour tout, et je te tiens au courant quand ca marche :)
0
Rejoignez-nous