cs_barbichette
Messages postés220Date d'inscriptionlundi 30 octobre 2000StatutMembreDernière intervention15 juillet 2013
-
24 août 2009 à 15:11
Caribensila
Messages postés2527Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention16 octobre 2019
-
26 août 2009 à 00:10
Bonjour lecteurs de ce poste
J'ai un petit souci avec les threads...
J'ai un thread plus que classique avec un traitement d'une image assez long. D'où l'utilisation d'un thread.
Je dessin dans un Tbitmap local au thread, puis, une fois terminé le traitement, je colle le résultat dans un autre Tbitmap appartenant à ma fenêtre principale.
Et sur cette dernière, un composant TPaintBox est censé redessiner le résultat.
Et là, rien, un joli rectangle blanc...
Et pourquoi????
Merci..
je vous donne quand même le code :
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TToto=class(tthread)
private
bmp:tbitmap;
procedure synchro;
protected
procedure Execute; override;
end;
TForm1 = class(TForm)
PaintBox1: TPaintBox;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure PaintBox1Paint(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
bmpglobal:tbitmap;
end;
var
Form1: TForm1;
toto:TToto;
implementation
{$R *.dfm}
procedure TToto.synchro;
begin
form1.bmpglobal.canvas.Draw(0,0,bmp);
end;
procedure TToto.Execute;
var i,j,n:integer;
begin
bmp:=tbitmap.Create;
bmp.width:=form1.bmpglobal.Width;
bmp.Height:=form1.bmpglobal.Height;
while not Terminated do
begin
n:=random(500);
for i:=0 to bmp.Width-1 do
for j:=0 to bmp.Height-1 do
bmp.canvas.Pixels[i,j]:=i*j*n;
synchronize(synchro);
form1.PaintBox1.Repaint;
end;
bmp.free;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered:=true;
bmpglobal:=tbitmap.Create;
bmpglobal.Width:=paintbox1.Width;
bmpglobal.height:=paintbox1.height;
toto:=TToto.Create(false);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
toto.Terminate;
toto.WaitFor;
bmpglobal.Free;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
paintbox1.Canvas.Draw(0,0,bmpglobal);
caption:=inttostr(gettickcount);
end;
end.
Bacterius
Messages postés3792Date d'inscriptionsamedi 22 décembre 2007StatutMembreDernière intervention 3 juin 201610 24 août 2009 à 17:31
Tu peux pas accéder aux objets de la VCL en mode thread, il faut que tu synchronises. Crée une procédure "CentralControl" avec les traitements dedans, et appelle là depuis Execute avec "Synchronize(CentralControl)".
cs_barbichette
Messages postés220Date d'inscriptionlundi 30 octobre 2000StatutMembreDernière intervention15 juillet 2013 24 août 2009 à 19:55
Merci Bacterius et Cantador.
Je viens de faire un essai. En effet, ça marche bien avec le chargement d'un bitmap, mais c'est pas vraiment ça que je veux faire..
Pour le scanline, bien sur que je fais un scanline, mais c'était pour l'exemple (et pour que le traitement soit volontairement long).
Bacterius : Ma procedure synchro est déjà appelé avec la methode synchronize.
Mettre à jour le titre de la fenêtre ou un tlabel, aucun souci. J'arrive encore à faire ça.
Mais je ne comprend pas pourquoi mon bitmap local au thread, une fois dessiné, ne s'affiche pas dans le paintbox.
J'ai fais deux petites choses :
1) j'ai retiré l'appel à paintbox1.repaint et je dessin direct dans le canvas. Pour ne pas encombré la file d'attente des messages.
2) en faisant l'essai de mettre la boucle de traitement (le remplissage bourrin des pixels) dans la procédure synchronisée, ça marche nickel... Mais ça bloque le programme principal. Logique...
Bref, j'ai l'impression que les bitmaps et les threads, dans Delphi, c'est pas fait pour marcher ensemble...
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 24 août 2009 à 22:55
Salut,
ça ne fonctionne pas tout simplement parce que le Bitmap de Form1 n'est pas mis à jour après modification de BMP ... !!!
Mais en dehors de ça créer un Bitmap dans la procédure Execute d'un thread c'est pas une bonne idée .. si vraiment tu as besoin d'un bitmap crée un constructeur et surcharge le destructeur pour créer et détruire le Bitmap ... mais une seule fois !!!
sinon voici un bout de code fonctionnel:
[hr]unit Unit1;
procedure TToto.Execute;
[b]begin
while not /bTerminated [b]do
/bsynchronize(synchro);
end;
procedure TToto.synchro;
var i, j, n: integer;
[b]begin
with /bform1.bmpglobal [b]do
begin
/bn : = random(500);
for i : = 0 to Width - 1 [b]do
for /bj := 0 to Height - 1 [b]do
/bcanvas.Pixels[i, j] : = i * j * n;
end;
form1.PaintBox1.Repaint;
end;
cs_barbichette
Messages postés220Date d'inscriptionlundi 30 octobre 2000StatutMembreDernière intervention15 juillet 2013 25 août 2009 à 13:15
Bon, j'ai trouvé la solution.
Il faut encadrer la modification du bitmap local au thread d'un Lock et Unlock...
Sinon, il ne dessine pas vraiment dans le canvas et l'image final est bien un rectangle blanc, vide.
Je vais chercher une utilisation simple et mettre une source en ligne
Caribensila
Messages postés2527Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention16 octobre 201918 25 août 2009 à 13:28
... Fais bien attention à ce que tu mets entre Lock et Unlock !
Cela ne doit concerner QUE du dessin effectif sur le Canvas (essaie de mettre les précalculs en dehors par exemple). Sinon ça risque de ralentir l'application.
Ceci dit, Lock/Unlock n'est pas obligatoire et un synchronize comme le montre Cirec est tout aussi efficace, je pense.
cs_barbichette
Messages postés220Date d'inscriptionlundi 30 octobre 2000StatutMembreDernière intervention15 juillet 2013 25 août 2009 à 13:41
Je m'explique un peu mieux.
j'ai un thread avec un bitmap local (bmplocal), sur lequel je fais un traitement long.
J'ai par ailleurs un paintbox sur une fenêtre dans lequel je veux faire apparaitre le résultat du traitement.
L'utilisateur peu modifier les paramètres de traitement en direct mais le rendu n'est pas instantané.
Pour ce faire, j'ai un bitmap temporaire bmptmp dans lequel je colle le résultat. Ainsi, à chaque évènement OnPaint, je suis capable de redessiner le dernier résultat de mon thread.
A la fin de mon traitement, dans mon thread, je copie le résultat de bmplocal dans bmptmp, et je met à jour le paintbox.
Le problème était qu'il n'y avait rien dans bmplocal.
Pour résoudre le problème, je dois mettre un bmplocal.Lock et bmplocal.unlock autour de ma boucle de traitement, sinon, pas d'image dans bmplocal.
Il ne bloque rien puisque c'est un bitmap local au thread.
Je ne sais pas si c'est pareil chez les autres...
Enfin, je fais faire un bout de code pour être plus clair..
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 25 août 2009 à 19:49
si en plus personne ne lit mes réponses
non c'est faux ... tu n'avais rien à l'écran tout simplement parce que tu dessines sur le Bitmap B et tu affiches le Bitmap A qui lui n'est jamais mis à jour ...
donc forcément le résultat est blanc !!!!
lit et regarde et essaye le code (le deuxième) c'est exactement tes variables et le résultat est présent à l'écran .... pas besoin de Canvas.Lock dans ce cas de figure !!!
et attention à ta procedure Execute ... il doit y avoir un minimum et aucun cas du code faisant appel à des composants qui doivent être dessinés à l'écran ...
cs_barbichette
Messages postés220Date d'inscriptionlundi 30 octobre 2000StatutMembreDernière intervention15 juillet 2013 25 août 2009 à 22:34
cirec: j'ai bien lu ton code. Le problème, c'est que le traitement (remplissage à la c...) est dans la procédure synchro. Donc pendant tout le temps de traitement, l'application se fige. Donc, aucun intérêt de faire un thread.
De plus, et je ne sais pas pourquoi, moi aussi ça marche quand je remplis le bitmap buffer dans la procédure synchro, mais ça marche plus quand je la sors de cette procédure et que je la recolle dans le 'Execute'. Pourtant, pour le traitement, il n'y a aucun appel à un objet de ma fenêtre principale, mais juste du remplissage d'un bitmap local.
Et dans ce dernier cas, il remplis rien du tout alors qu'en entourant le traitement d'un lock, unlock, ça marche.
Je posterai le code que j'ai fait, avec ce que je veux faire, et on pourra en rediscuter après le code.
Caribensila
Messages postés2527Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention16 octobre 201918 26 août 2009 à 00:10
Pour moi non plus, c'est pas clair c't'affaire... :)
Mais, dans un sens, j'ai rien pu tester non plus :s
En principe, un Lock/Unlock c'est fait pour du multi-threading. Càd quand plusieurs threads travaillent sur le même canvas.
Sinon, je ne vois pas l'intérêt de locker un Canvas qui ne risque pas d'être modifié par un autre thread.
On va peut-être découvrir un nouveau 'truc', là...
Vas-y barbichette !
Pour une fois, je suis impatient.