Wczytywanie obrazka do TImage w wątku

Wczytywanie obrazka do TImage w wątku

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

U3
  • Rejestracja:prawie 13 lat
  • Ostatnio:prawie 2 lata
  • Postów:196
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:

Kopiuj
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:

Kopiuj
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:

Kopiuj
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:

Kopiuj
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

Kopiuj
//********* 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;
edytowany 3x, ostatnio: user322
KA
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:Gorlice
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.


Nie odpowiadam na PW w sprawie pomocy programistycznej.
Pytania zadawaj na forum, bo:
od tego ono jest ;) | celowo nie zawracasz gitary | przeczyta to więcej osób a więc większe szanse że ktoś pomoże.
U3
  • Rejestracja:prawie 13 lat
  • Ostatnio:prawie 2 lata
  • Postów:196
0

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

np.:

Kopiuj
procedure procedura1;
begin
  procedura2;
end;

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

flowCRANE
Skoro jest wywoływana w wątku, to będzie "wątkowana";
U3
@furious programming Mimo, że w kodzie jest, to podczas działania nie jest.
U3
  • Rejestracja:prawie 13 lat
  • Ostatnio:prawie 2 lata
  • Postów:196
0

Przerobiłem tak:

Kopiuj
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

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

ale bez zmian.

edytowany 1x, ostatnio: user322
U3
  • Rejestracja:prawie 13 lat
  • Ostatnio:prawie 2 lata
  • Postów:196
0

Nikt nie potrafi pomóc??

abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:8 miesięcy
  • Postów:6610
0

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


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
edytowany 1x, ostatnio: abrakadaber
U3
  • Rejestracja:prawie 13 lat
  • Ostatnio:prawie 2 lata
  • Postów:196
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.

flowCRANE
Ale o co chodzi? Bo ja nie udzielam się w sumie w tym wątku, a widzę że jedziesz po wszystkich jak po burych sukach... Rozumiem, że odpowiedzi mogą nie pasować, ale opanuj się - zawsze można grzecznie komuś coś tłumaczyć; W przeciwnym razie zrazisz użytkowników do siebie i trudniej będzie Ci otrzymać pomoc; Więc zwracam uwagę na słownictwo, bo możesz zostać wysłany na przymusowy urlop od forum;
KA
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:Gorlice
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.


Nie odpowiadam na PW w sprawie pomocy programistycznej.
Pytania zadawaj na forum, bo:
od tego ono jest ;) | celowo nie zawracasz gitary | przeczyta to więcej osób a więc większe szanse że ktoś pomoże.
edytowany 1x, ostatnio: kAzek
KA
@user322 wpadłem na taki pomysł że być może obrazek ładuje się długo przez konwersję z JPG na bitmapę spróbuj w wątku dynamicznie utworzyć TBitmap i załadować (bez synchroznize) do niego bitmapę a dopiero później (już w synchronize) załadować tą Bitmapę do TImage. Oczywiście nie mam pewności czy coś to da ale możesz sprawdzić.
U3
@kAzek od kiedy w programowaniu się czegoś nie da? :) Nie rozumiem takiego myślenia, on po prostu nie wie jak i nie umie pomóc a się odzywa, nawet nie nakieruje jak by to ugryźć. Spróbuję zrobić tak jak napisałeś, a jak nie pomoże to hm... może po zrobię kompresję obrazka na 70% przed zapisem i odczyt będzie krótszy ze względu na jego mniejszy rozmiar, ewentualnie wcześniej sprawdzę jakiś inny komponent. Dzięki.
KA
@user322 Nie napisałem że w ogóle się nie da tylko że w ten sposób się nie da (przez to synchronize) a przecież zasugerowałem rozwiązania. Jasne że wszystko się da możesz nawet napisać własny genialny system który z magiczną prędkością będzie ładował obrazki o zwykłym lepszym komponencie (np. działającym na DirectX, OpenGL lub GDI+)‎ nie wspominając. Podsumowując wszystko się da tylko jakim nakładem czasu i kosztem. Najlepiej krytykować próbujących pomóc (przecież nikt nie ma takiego obowiązku) samemu nie podając czego już próbowałeś na przyszłość na pewno ktoś Ci pomoże.
abrakadaber
abrakadaber
@user322 a ty czytać potrafisz ze zrozumieniem? Napisałem, że tak nie zadziała a nie, że się nie da. Ale dla ciebie to wszystko jedno - przecież wpadłeś na genialny pomysł to MUSI ZADZIAŁAĆ!!
M6
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 6 lat
  • Postów:220
0

a probowales usunac synchronize?


KA
Tak później się dziwić czemu wywala błędy w Delphi odwołania z wątków do VCL muszą być w Synchronize.
M6
zalezy. Czasem lepiej dac sobie spokuj z synchronize i nie ma problemow
KA
Tak czasami teoretycznie nie ma tylko dopiero w pewnych nieprzewidywalnych okolicznościach nagle się pojawiają a wtedy cały napisany kod (który był napisany w taki sposób aby nie wykorzystywać synchronize) można sobie wsadzić i pisać od nowa apkę.
abrakadaber
abrakadaber
@mca64 Następny genialny co ma głęboko w d*** zasady a potem płacze jak zacznie błędami sypać. Nie wolno się odwoływać do VCLa z innego wątku bezpośrednio. Nie i koniec. Może podczas testów zadziała ale prędzej czy później się wysypie. A wiesz dlaczego - ano dlatego, że nikt z projektujących język i środowisko nie przewidział takiej opcji.
M6
Skad wiesz co ja uzywam a czego nie. To ze tak napisalem nie znaczy ze nie ma tam byc tego synchronize. Moze za to sprawdzic czy w tym lezy problem.
M6
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 6 lat
  • Postów:220
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.


edytowany 1x, ostatnio: mca64
abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:8 miesięcy
  • Postów:6610
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...


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
U3
  • Rejestracja:prawie 13 lat
  • Ostatnio:prawie 2 lata
  • Postów:196
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.

edytowany 1x, ostatnio: user322
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:Tuchów
  • Postów:12171
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ć.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.