Wątek zablokowany 2014-08-16 17:16 przez furious programming.

Wczytywanie obrazka do TImage w wątku

0

Cześć,

mam taki problem:

Do procedury przekazuję polę TBlobField zawierający zapisany obrazek JPEG, oraz jako drugi parametr właściwość TPicture od komponentu TImage.

Procedura wczytująca obrazek:

procedure TfrmMenu.ShowDBPrintScreen(FieldImagen: TBlobField; Picture: TPicture);
var
  Stream: TMemoryStream;
  Jpg: TJpegImage;
begin

  Jpg := nil;
  Stream := nil;
  try
    Stream := TMemoryStream.Create;
    FieldImagen.SaveToStream(Stream);
    if Stream.Size>0 then
    begin
      Jpg := TJpegImage.Create;
      Stream.Position := 0;
      Jpg.LoadFromStream(Stream);
      Picture.Bitmap.Assign(Jpg);
    end
    else
      Picture.Bitmap.Assign(nil);
  except
    on e:exception do
    begin
      frmMenu.ShowMessageDlg('Błąd wczytywania obrazu! (ShowDBPrintScreen)#13#13<b>' + e.Message + '</b>', 'err');
      Picture.Bitmap.Assign(nil);
    end;
  end;
  jpg.Free;
  Stream.Free;
end;

Niestety problem pojawia się przy odczycie większych obrazków, a dokładniej przy przypisywaniu ich do komponentu Image czyli:

Image1.Picture.Assign(jpg);

program na nie całą sekundę się zawiesza na poczet wykonania tej procedury.

Pomyślałem, że mogę to robić w wątku więc zrobiłem to w ten sposób:

type
  TThread_ImagePreview = class(TThread)
  protected
    procedure Execute; override;
  private
    procedure ShowImagePreview;
  public
    BlobField: TBlobField;
    ImagePreview: TPicture;
  end;

procedure TThread_ImagePreview.Execute;
begin
  inherited;
  NameThreadForDebugging('ImagePreview');

  FreeOnTerminate := True;
  Synchronize(ShowImagePreview);
end;

procedure TThread_ImagePreview.ShowImagePreview;
begin
  frmMenu.ShowDBPrintScreen(BlobField, ImagePreview);
end;

Wątek wywołuję w ten sposób:

procedure button1.OnClick(Sender: TObject);
var
  Thread_ImagePreview: TThread_ImagePreview;
begin
  // tutaj otwarcie dataseta
  ....
      Thread_ImagePreview := TThread_ImagePreview.Create(True);
      Thread_ImagePreview.BlobField := (DataSet.Fields[0] as TBlobField);
      Thread_ImagePreview.ImagePreview := img_printscreen.Picture;
      Thread_ImagePreview.Resume;
end;

Ale ku mojemu zdziwieniu - zero różnicy. Tak jakby wątek nie zadziałał. Procedura wykonywana jest w dwóch miejscach prawie jednocześnie z tym, że jako parametry dostają inny obrazek.

Czy ktoś może doradzić co robię nie tak? Przyznam, że dopiero zaczynam z wątkami.

@EDITED

//********* ZMODYFIKOWANA WERSJA WĄTKU
type
  TThread_ImagePreview = class(TThread)
  protected
    procedure Execute; override;
  private
    procedure ShowImagePreview;
  public
    Table: string;
    ID: integer;
    ImagePreview: TPicture;
  end;

procedure TThread_ImagePreview.ShowImagePreview;
var
  Dataset: TIBDataset;
begin
  try
    Dataset := TIBDataSet.Create(nil);
    Dataset.BufferChunks := 10;
    Dataset.Database := frmdm.IBDatabase1;
    Dataset.Transaction := frmDM.IBTransaction1;

    Dataset.DisableControls;
    Dataset.Close;
    if Table = 'tasks' then
    begin
      Dataset.SelectSQL.Text := 'select img from tasks where id_t = :id_t and img is not null';
      Dataset.ParamByName('id_t').AsInteger := ID;
    end
    else
    begin
      Dataset.SelectSQL.Text := 'select print_screen from repository where id_rep = :id_rep';
      Dataset.ParamByName('id_rep').AsInteger := ID;
    end;
    Dataset.Open;
    Dataset.EnableControls;

    if Table = 'tasks' then
    begin
      frmMenu.img_printscreen.Picture := nil;
      if not Dataset.FieldByName('img').IsNull then
      begin
        frmMenu.Notebook_RightPanel.ActivePage := 'printscreen';
        frmMenu.ShowDBPrintScreen((DataSet.Fields[0] as TBlobField), ImagePreview);
      end
      else
        frmMenu.Notebook_RightPanel.ActivePage := 'empty';
    end
    else
    begin
      frmRepository.imgPrintScreen.Picture := nil;
      if not Dataset.FieldByName('print_screen').IsNull then
      begin
        frmRepository.Notebook1.ActivePage := 'printscreen';
        frmMenu.ShowDBPrintScreen((DataSet.Fields[0] as TBlobField), ImagePreview);
      end
      else
        frmRepository.Notebook1.ActivePage := 'empty';
    end;
    FreeAndNil(Dataset);
  except
    on e:exception do
    begin
      FreeAndNil(Dataset);
      frmMenu.ShowMessageDlg('Błąd wczytywania zrzutu ekranu instrukcji!#13#13<b>' + e.Message + '</b>', 'err');
    end;

  end;
end;

//********* Wywołanie
Thread_ImagePreview := TThread_ImagePreview.Create(True);
Thread_ImagePreview.ID := Data.ID_REP;
Thread_ImagePreview.Table := 'repository';
Thread_ImagePreview.ImagePreview := imgPrintScreen.Picture;
Thread_ImagePreview.Resume;
0

Kariery nie zrobisz bo jedyną rzeczą jaką wykonuje wątek jest procedura która jest w Synchronize w takim wypadku użycie wątku faktycznie nic nie da musiałbyś przerobić kod tak aby tylko ładowanie obrazka do TImage było w synchroinize ale nie wiem czy by to wiele dało skoro właśnie ładowanie obrazka zajmuje dużo czasu.

0

A procedury wywoływane wewnątrz Synchornize(Procedura1);

np.:

procedure procedura1;
begin
  procedura2;
end;

Mam rozumieć, że procedura "procedura2" nie będzie wątkowana?

0

Przerobiłem tak:

type
  TThread_ImagePreview = class(TThread)
  protected
    procedure Execute; override;
  private
    procedure LoadImage;
    procedure ShowImage(FieldImagen: TBlobField);
  public
    Table: string;
    ID: integer;
    ImagePreview: TPicture;
  end;

procedure TThread_ImagePreview.Execute;
begin
  inherited;
  NameThreadForDebugging('ImagePreview');

  FreeOnTerminate := True;
  Synchronize(LoadImage);
end;

procedure TThread_ImagePreview.LoadImage;
var
  Dataset: TIBDataset;
begin
  try
    Dataset := TIBDataSet.Create(nil);
    Dataset.BufferChunks := 10;
    Dataset.Database := frmdm.IBDatabase1;
    Dataset.Transaction := frmDM.IBTransaction1;

    Dataset.DisableControls;
    Dataset.Close;
    if Table = 'tasks' then
    begin
      Dataset.SelectSQL.Text := 'select img from tasks where id_t = :id_t and img is not null';
      Dataset.ParamByName('id_t').AsInteger := ID;
    end
    else
    begin
      Dataset.SelectSQL.Text := 'select print_screen from repository where id_rep = :id_rep';
      Dataset.ParamByName('id_rep').AsInteger := ID;
    end;
    Dataset.Open;
    Dataset.EnableControls;

    if Table = 'tasks' then
    begin
      frmMenu.img_printscreen.Picture := nil;
      if not Dataset.FieldByName('img').IsNull then
      begin
        frmMenu.Notebook_RightPanel.ActivePage := 'printscreen';
        ShowImage((DataSet.Fields[0] as TBlobField));
      end
      else
        frmMenu.Notebook_RightPanel.ActivePage := 'empty';
    end
    else
    begin
      frmRepository.imgPrintScreen.Picture := nil;
      if not Dataset.FieldByName('print_screen').IsNull then
      begin
        frmRepository.Notebook1.ActivePage := 'printscreen';
        ShowImage((DataSet.Fields[0] as TBlobField));
      end
      else
        frmRepository.Notebook1.ActivePage := 'empty';
    end;
    FreeAndNil(Dataset);
  except
    on e:exception do
    begin
      FreeAndNil(Dataset);
      frmMenu.ShowMessageDlg('Błąd wczytywania zrzutu ekranu instrukcji!#13#13<b>' + e.Message + '</b>', 'err');
    end;

  end;
end;

procedure TThread_ImagePreview.ShowImage(FieldImagen: TBlobField);
var
  Stream: TMemoryStream;
  Jpg: TJpegImage;
begin

  Jpg := nil;
  Stream := nil;
  try
    Stream := TMemoryStream.Create;
    FieldImagen.SaveToStream(Stream);
    if Stream.Size>0 then
    begin
      Jpg := TJpegImage.Create;
      Stream.Position := 0;
      Jpg.LoadFromStream(Stream);
      ImagePreview.Bitmap.Assign(Jpg);
    end
    else
      ImagePreview.Bitmap.Assign(nil);
  except
    on e:exception do
    begin
      frmMenu.ShowMessageDlg('Błąd wczytywania obrazu! (ShowDBPrintScreen)#13#13<b>' + e.Message + '</b>', 'err');
      ImagePreview.Bitmap.Assign(nil);
    end;
  end;
  jpg.Free;
  Stream.Free;
end;

Ale dalej to samo...

Wrzuciłem nawet zawartość procedury ShowImage do procedury Synchronize zamiast linijki

ShowImage((DataSet.Fields[0] as TBlobField));

ale bez zmian.

0

Nikt nie potrafi pomóc??

0

to po prostu tak nie zadziała. A poza tym nie masz podstawowej wiedzy o programowaniu wielowątkowym.

0

LUDZIE!!

to po prostu tak nie zadziała.

Serio?

A poza tym nie masz podstawowej wiedzy o programowaniu wielowątkowym.

Awww...

"Czy ktoś może doradzić co robię nie tak? Przyznam, że dopiero zaczynam z wątkami.

@abrakadaber naucz się czytać ze zrozumieniem i wbij sobie do głowy to, że jeśli nie jesteś w stanie pomóc to trzymaj się z dala od klawiatury w takich postach, bo nie wnosisz kompletnie nic, a tylko wkurzasz ludzi. Ciekawość mnie zjada i cisnął mi się na język miłe słowa w Twoim kierunku, ale zapytam tylko jedno. Po co w ogóle się udzielasz w tym temacie jak g**** pomagasz? Przypomnij sobie czym jest forum i może trochę regulamin bo albo masz sklerozę, albo jesteś stary i wiek daje we znaki z pamięcią, albo kochasz statystykę. Tak na prawdę nie interesuje mnie to więc nawet się tutaj już nie udzielaj.

@furious programming sorry, że tak pojechałem, ale nie mogę po prostu z takich ludzi, którzy niby chcą, niby wiedzą, ale jednak ich samoocena przewyższa ich chęci co z kolei wpływa na to, że robią co właśnie widać w powyższej wypowiedzi. Takich ludzi potrzeba "najwięcej".

Jeszcze raz zwracam się z prośbą o pomoc w rozwiązaniu problemu.

0

Ale tu się naprawdę nie da nic mądrego zrobić jak pisałem bo najdłuższa operacja jest w Synchronize. Jak jesteś taki mądry to sam wymyśl bo jak jedna osoba napisze że się nie da to nie wystarcza i się upierasz. Na to byś musiał zrobić jakąś własną metodę ładowania obrazka tak aby ten wczytywał się etapami które mógłbyś kontrolować (w pętli) i wtedy nawet zwykłe Application.ProcessMessages załatwiło by sprawę. A może istnieje jakiś gotowy szybszy komponent bo TImage jest wolny.

0

a probowales usunac synchronize?

0

jedynie co moge doradzic to, uzywaj czegos takiego

.create
try
.
.
finally
.free
end;

a jesli chcesz bezpieczny watek to synchronzie daj tylko dla Picture.Bitmap.Assign(Jpg);
wszystko inne bez tego i powinno byc ok.

0
user322 napisał(a):

LUDZIE!!

to po prostu tak nie zadziała.

Serio?

tak serio. NIE ZADZIAŁA - dotarło? Poniał? NIE ZADZIAŁA bo i tak tą operację wykonujesz w WĄTKU GŁÓWNYM! Ale co taki ignorant może wiedzieć...

A poza tym nie masz podstawowej wiedzy o programowaniu wielowątkowym.

Awww...

"Czy ktoś może doradzić co robię nie tak? Przyznam, że dopiero zaczynam z wątkami.
Zaczynać a nie mieć podstawowej wiedzy, którą zdobywa się czytając COKOLWIEK NA TEN TEMAT to ogromna różnica, no ale widać jesteś następny wszechwiedzący.

Reszty nie chce mi się komentować bo po prostu nie ma czego. Zero wiedzy, zero kultury a jak się powie, że coś nie zadziała tak jak master wymyślił to wielka obraz i zbrodnia. Żal...

0

@abrakadaber chłopie... gdybyś był kompilatorem i grałbyś mi na nerwy tak jak to robisz teraz, to bym Cię usunął razem z twardym dyskiem przez okno, weź wyjdź z tego wątku skoro nie masz pojęcia jak to ugryźć, nawet nie wykazujesz zainteresowania tematem, tylko piszesz "nie da się tak prosto, tak tego nie zrobisz, itp" takie teksty oszczędź sobie.

Zaczynać a nie mieć podstawowej wiedzy, którą zdobywa się czytając COKOLWIEK NA TEN TEMAT to ogromna różnica, no ale widać jesteś następny wszechwiedzący.

Mądralo, chyba w szkole Cię nie uczyli, że podstawową wiedzę nabywa się na początku CZYLI jak się coś zaczyna. Teraz czytaj uważnie: JA ZACZYNAM poznawać wątki, tym samym, poznaję podstawy próbując od razu pisać. Prościej się nie da wytłumaczyć, ale pewnie i tak nie pojmiesz skoro do tego czasu masz z tym problem. Skoro dla Ciebie to jest ogromna różnica to trzeba Cię "odjąć" z tego forum bo jedyną różnicę jaką ja widzę tutaj, to Twoje denne teksty i wykłócanie się. Nie potrafisz - siedź cicho, a jak wiesz to się pochwal może wtedy kontakt będzie nieco inny.

Serio?

tak serio. NIE ZADZIAŁA - dotarło? Poniał? NIE ZADZIAŁA bo i tak tą operację wykonujesz w WĄTKU GŁÓWNYM! Ale co taki ignorant może wiedzieć...

Nie wyczułeś, tzw. Sarkazm.

Żal

Gimbaza od razu pcha się na język widząc to. Chyba wiem skąd bierzesz teksty ;]

Koniec tematu.

0

Widzę, że wątek do niczego sensownego nie doprowadził, a w zamian przerodził się w prywatną wojnę - dlatego też wątek zamykam; A Ty @user322 masz ode mnie ostrzeżenie - następnym razem za takie wypowiedzi zostaniesz zbanowany; Można się kłócić, ale kulturalnie i te minimum kultury i powściągliwości trzeba jednak zachować.

1 użytkowników online, w tym zalogowanych: 0, gości: 1