thread dinamik form Synchronize

Delphi'de kod yazma ile ilgili sorularınızı bu foruma yazabilirsiniz.
Cevapla
Kullanıcı avatarı
safak
Şafak EBESEK
Mesajlar: 165
Kayıt: 05 Ağu 2003 04:39
Konum: Istanbul
İletişim:

thread dinamik form Synchronize

Mesaj gönderen safak »

Arkadaşlar,

Ana formun üstünde bir buton var. Basılınca 2. form açılıyor. 2. formun üzerinde bir button var. Basılınca thread çalışıyor ve Label1.Caption'a saati yazıyor.

Eğer 2. form AutoCreated olarak bırakılırsa sistem çalışıyor. Fakat dinamik olarak yaratılırsa (Ana formdaki buttona her basışta), 2. formdaki Label güncellenmiyor.

Aşağıda kodu aktarıyorum.

Kod: Tümünü seç

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Unit2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation


{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
//var   Form2:TForm2;
begin
  //Form2:=TForm2.Create(nil);
  Form2.Show;
end;

end.

Kod: Tümünü seç

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm2 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  Txxx = class(TThread)
  private
    { Private declarations }
  public
    procedure Execute; override;
    procedure yaz;
  end;


var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action:=caFree;
end;


procedure TForm2.Button1Click(Sender: TObject);
var xxx : Txxx;
begin
  xxx:= Txxx.Create(False);
end;

procedure Txxx.Execute;
var  i:integer;
begin
  for i := 1 to 10 do begin
    Synchronize(yaz);
    sleep(500);
  end;
end;

procedure Txxx.yaz;
begin
  Form2.Label1.Caption:=DateTimeToStr(now);
end;

end.
Teşekkürler.
t-hex
Kıdemli Üye
Mesajlar: 531
Kayıt: 18 Mar 2005 02:45
Konum: İstanbul/Antalya
İletişim:

Mesaj gönderen t-hex »

Merhaba,

Kod: Tümünü seç

 //Form2:=TForm2.Create(nil); 
  Form2.Show; 
bu kodu

Kod: Tümünü seç

 Application.CreateForm(TForm2,Form2);
 Form2.Show;
olarak değiştirip denerseniz olur sanırım. Bana göre formun owner'ının nil olmasından kaynaklanıyor.

Ayrıca Form2'nin onclose olayında closeaction'i caFree olarak belirleyin ki memory leak olmasın.
Kullanıcı avatarı
safak
Şafak EBESEK
Mesajlar: 165
Kayıt: 05 Ağu 2003 04:39
Konum: Istanbul
İletişim:

thread dinamik form Synchronize çözüm

Mesaj gönderen safak »

Arkadaşlar,
Öncelikle aşağıdaki konu başlığına bir göz atılabilir.
viewtopic.php?t=1626&highlight=form+%2Alist%2A
Bir süre sonra çözümü buldum.
Aşağıda kodu aktarıyorum.
Uzun kod blokları göndermek çok sevimli olmasa da, örnek olabilir / geliştirilebilir düşüncesiyle aktarıyorum.
Kolay Gelsin,

Kod: Tümünü seç

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Contnrs, Unit2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  FormList : TObjectList;

implementation


{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  frm :TForm2;
  i:integer;
begin
  frm:=TForm2.Create(Application);
  i:=FormList.Add(frm);
  frm.Tag:=i;
  frm.Caption:=IntToStr(i);
  frm.Show;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FormList:=TObjectList.Create(True);
end;

end.

Kod: Tümünü seç

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm2 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  Txxx = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
    procedure yaz;
  public
    FormNo : integer;
  end;

implementation

uses Unit1;

{$R *.dfm}

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action:=caFree;
end;

procedure TForm2.Button1Click(Sender: TObject);
var xxx:Txxx;
begin
  xxx:= Txxx.Create(True);
  xxx.FormNo:=Tag;
  xxx.FreeOnTerminate:=True;
  xxx.Resume;
end;

procedure Txxx.Execute;
var  i:integer;
begin
  for i := 1 to 10 do begin
    Synchronize(yaz);
    sleep(500);
  end;
end;

procedure Txxx.yaz;
begin
  TForm2(FormList[FormNo]).Label1.Caption:= DateTimeToStr(now);
end;

end.
fduman
Moderator
Mesajlar: 2749
Kayıt: 17 Ara 2004 12:02
Konum: Ankara

Mesaj gönderen fduman »

Altı üstü bir form'da label update edeceksiniz. Form'u bir objectlist'e atmayı gereksiz buluyorum. T-Hex'in bahsettiği türden bir sorun var gibi duruyor. Form2 variable tanımsız kaldığından olabilir.

Çözümünüzde neye rastladınız? Sorun ne imiş?
Kullanıcı avatarı
safak
Şafak EBESEK
Mesajlar: 165
Kayıt: 05 Ağu 2003 04:39
Konum: Istanbul
İletişim:

Mesaj gönderen safak »

Görüşleriniz için teşekkür ederim. Ben de sizin gibi düşünmüş ve yazmıştım. Fakat çalışmadı. Belki sabaha karşı beşte her zaman yaptığımdan farklı bir şey yapmışımdır. Belki de tekrar tekra aynı körlüğü yaşıyorum.

Yaklaşımım şöyleydi; Bir ana pencere olsun. Bu penceredeki bir düğmeye
Her basışta yeni bir pencere açılsın. Açılan her yeni pencerenin kodu içerisinde bir thread olsun. Ve istenildiğinde kendi yaratıldığı form üzerinde işlem yapsın.

İlk bakışta anlaşıldığı gibi, thread ayrı bir unit içerisnde değil. Ve dinamik olarak yaratılan bir pencerenin içeriindeki nesnelere ve onların özelliklerine erişip üzerlerinde işlem yapması gerekiyor.

Tanımlanan bu talep modeli için çok hoş başka çözümler olsa de ObjectList'in nasıl kullanıldığını gösteren bir kod parçasının (belki) forumda araştırma yapanlara faydası olabilir.

Eğer denemek içi vaktiniz olur ve önerdiğiniz gibi çalıştıırabilirseniz (ben gene çalıştıramadım) gerçekten biraz tatil yapabilirim.
fduman
Moderator
Mesajlar: 2749
Kayıt: 17 Ara 2004 12:02
Konum: Ankara

Mesaj gönderen fduman »

Şimdi daha da merak ettim. :) Ufak bir App. oluşturup deneyeceğim. Sonuçlarını buraya yazarım.

Kolay gelsin.
fduman
Moderator
Mesajlar: 2749
Kayıt: 17 Ara 2004 12:02
Konum: Ankara

Mesaj gönderen fduman »

Evet ufak bir app. ile denedim. Bende sorunsuz çalıştı. Örnek kodu gönderiyorum.

Main form: (üzerinde bir Button var)

Kod: Tümünü seç

unit Main;

interface

uses Forms, Classes, Controls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Child;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(Form2) then
    Form2:= TForm2.Create(nil);
  Form2.Show;
end;

end.
Thread form (Auto create form olmamalı, üzerinde bir button ve 1 label var):

Kod: Tümünü seç

unit Child;

interface

uses Forms, Controls, StdCtrls, Classes;

type
  TForm2 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  end;

  TTestThread = class(TThread)
  private
    procedure UpdateLabel;
  protected
    procedure Execute; override;
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

uses Windows, SysUtils;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action:= caFree;
  Form2:= nil;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  // Thread çalışırken formu kapatmanız halinde gerekli olan
  // ve thread i free edecek kod eklenmemiştir!!!!!
  with TTestThread.Create(True) do
  begin
    FreeOnTerminate:= True;
    Resume;
  end;
end;

{ TThread }

procedure TTestThread.Execute;
var
  I: Integer;
begin
  for I:= 1 to 10 do
  try
    if Terminated then Exit;
    Synchronize(UpdateLabel);
    Sleep(1000);
  except
    // Dummy
  end;
end;

procedure TTestThread.UpdateLabel;
begin
  if Assigned(Form2) then
    Form2.Label1.Caption:= DateTimeToStr(Now);
end;

end.
Dediğim gibi bu kod mükemmelen çalışıyor ve saatin saniye sayışını görebiliyorum. Sende neden çalışmadığını halen anlayamadım. Senin kodu kopyalayıp deneyeceğim.
fduman
Moderator
Mesajlar: 2749
Kayıt: 17 Ara 2004 12:02
Konum: Ankara

Mesaj gönderen fduman »

Baktım da çalıştırmaya bile gerek yok. Senin kod ile benimki arasında pek bir fark yok. Sadece şunu söyleyeyim Execute method'unun protected member olması yapısal olarak daha iyi olurdu. Yaz methodunu da private veya protected yapman hataları azaltır. Gerçi bu thread'i senden başkası kullanmayacak ama. :)

Bir de benim kodu dener misin? Eğer çalışmazsa Delphi'nde ilginç bir problem mevcut. Delphi sürümün kaçtı? Ben bu kodu Delphi 7 Enterprise Update Pack 1.1 ile denedim.
Kullanıcı avatarı
safak
Şafak EBESEK
Mesajlar: 165
Kayıt: 05 Ağu 2003 04:39
Konum: Istanbul
İletişim:

Mesaj gönderen safak »

Tekrar teşekkürler.
İkinci gönderdiğim çözüm kodunda thread metodları protected di. Siz de form kapanışı için olan bölümü eklemişsiniz. Daha hoş bir örnek oldu. Kodunuzu çalıştırdım. Main formdaki button tıklanınca bir child form açılıyor. Child formdaki buttom tıklanınca thread çalışıyor. Benim kurgumda, Main formdaki buttona her basışta yeni bir child from açılıyor. Ve her child formun zamanı gösteren threadı , her child formun kendi üzerindeki buttona basılarak çalıştırılıyor. Bu tanım için kodu nasıl tasarlardınız?
Teşekkürler
fduman
Moderator
Mesajlar: 2749
Kayıt: 17 Ara 2004 12:02
Konum: Ankara

Mesaj gönderen fduman »

Tamam şimdi olayı anladım. :) Sadece dinamik yaratılıyor ve 1 form için diye düşünüyorum hep. :)

Yapmak istediğin şey için Thread'e Form2 pointer'ını geçmelisin. Şöyle yapabilirsin:

Kod: Tümünü seç

TTestThread = class(TThread)
private
  FForm: TForm;
protected
  procedure UpdateLabel;
.......
......
public
  constructor Create(CreateSuspended: Boolean; AForm: TForm); override;
end;

constructor TTestThread.Create(CreateSuspended: Boolean; AForm: TForm); override;
begin
    inherited Create(CreateSuspended);
  FForm:= AForm;
end;
  
procedure TTestThread.UpdateLabel;
begin
   // Constructor'da aldığımız Form pointer'ını kullandık.
   TForm2( AForm).Label1.Caption:= DateTimeToStr(Now);
end;
Bu örnekte Thread constructor'ına bir parametre daha gönderiliyor, o da yarattığın Form'un pointer değeri. Thread bu pointer değerine sahip class'ın Label1.Caption'unu güncelliyor.

Kullanırken de formunu dinamik yaratıp yaratılan Form pointer'ını thread'e göndermelisin.

Form sınıfının free edilmesi ile uğraşmamak için Form'unun ownerını Ana form'a veya application a bağlaman yerinde olur. Bu sayede program kapanırken, henüz kapatılmayan formlar da bellekten otomatik free edilir ve uğraşmana gerek kalmaz.

Yaratırken:

Kod: Tümünü seç

  AForm:= TForm2.Create(Application);
  AForm.Show;
  with TTestThread.Create(True, AForm) do
  begin
     FreeOnTerminate:= True;
      Resume;
  end;


gibi bir kod işini görecektir.
fduman
Moderator
Mesajlar: 2749
Kayıt: 17 Ara 2004 12:02
Konum: Ankara

Mesaj gönderen fduman »

Pardon bir hata yapmışım. Child form üzerindeki butona basılınca thread in çalışması için biraz cambazlık gerekiyor ve senin TObjectList örneğin daha mantıklı hale geliyor. Aslında kasınca bunu da kullanmaya gerek yok ama kasmaya gerek de yok. Kendi örneğin güzel iş görecektir. Yanlış anladığım ve seni de uğraştırdığım için kusura bakma.

İyi çalışmalar.
Kullanıcı avatarı
safak
Şafak EBESEK
Mesajlar: 165
Kayıt: 05 Ağu 2003 04:39
Konum: Istanbul
İletişim:

Mesaj gönderen safak »

Katkınız ve katılımınız için teşekkürler.
Cevapla