[Delphi] Zapis komponentu do JPG z niewidocznej formatki.

0

Witam. Mam taki problem. Poniższy kod działa poprawnie i zapisuje wygląd kontrolki do pliku JPG,
ale robi to poprawnie tylko jeżeli formatka jest widoczna. A ja chciałbym też mieć te możliwośc
gdy formatka jest zminimalizowana, a zrzut uaktuwnia timer. Wtedy w zapisanym pliku jpg jest
tylko fragment ekranu o wymiarach takich jak komponent do zrzutu, ale zawiera jakiś wycinek
ekranu, a nie o to chodzi. Może ktoś z Was podpowie, jak zmodyfikować poniższy kod aby w
pliku jpg uzyskać komponent. Ewentualnie jak na chwile pokazać całą formatkę, zrobić zrzut i
ją schować. Próbowałem wątku, pokazywania w nim po czym wywołania tej procedury, no a
na końcu Close. Ale plik dalej zawierał wycinek ewentualnie niekompletny zrzut komponentu,
bez narysowanych na nim Canvas.TextOut tekstów. Zrzucany komponent jest pochodaną od
ScrollBoxa, wzbogaconą o Canvas, a do tego zawiera utworzone na nim dynamiczne - jako
Parent - ALProgressBary. Zależy mi aby całość odwzorowała się jak przy widocznej formatce.

procedure TWykresForm.SaveWinControlToJpg(SourceControl: TWinControl; FileName: string);

procedure WinControlToBitmap(SourceControl: TWinControl; DestBitmap: TBitmap);
var
  Dest, Src : TRect;
begin
  Src := Rect(SourceControl.Left, SourceControl.Top, SourceControl.Left + SourceControl.Width, SourceControl.Top + SourceControl.Height);
  Dest := Rect(0, 0, SourceControl.Width, SourceControl.Height);
  DestBitmap.Width := SourceControl.Width;;
  DestBitmap.Height := SourceControl.Height;
  DestBitmap.Canvas.CopyRect(Dest, WykresForm.Canvas, Src);
end;

var
  Bmp : TBitmap;
  Jpg : TJpegImage;
begin
  if FileName = '' then
  begin
    Exit;
  end;
  Bmp := TBitmap.Create;
  Jpg := TJPEGImage.Create;
  WinControlToBitmap(SourceControl, Bmp);
  Jpg.Assign(Bmp);
  try
    Jpg.SaveToFile(FileName);
  finally
    Bmp.Free;
    Jpg.Free;
  end;
end;
0

Czesc.

Ta procedura nie jest do konca prawidlowa. TWinControl nie ma przeciez TCanvas wiec radzilbym zmienic jej nazwe bo jest mylaca. I tak przeciez chcesz zapisac swoj komponent a nie chcesz zeby Twoj komponent zapisywal inne komponenty prawda? Bo tak to teraz wyglada. No chyba, ze kladziesz swoj komponent na jakims innym komponencie i to SourceControl pelni role Parenta.

W kazdym razie nie udalo mi sie przetestowac Twojej procedury ale wydaje mi sie ze jest prosty sposob na rozwiazanie Twojego problemu.
Wyslij do swojego komponentu wiadomosc WM_PAINT a nastepnie skopiuj jego canvas. Jesli to nie pomoze, to po prostu narysuj wszystko jeszcze raz na TCanvas bitmapy ktora chcesz zapisac. Nie powinno to stanowic dla Ciebie zadnego problemu, po prostu to co teraz robisz po otrzymaniu WM_PAINT zapnij w metode, ktora bedzie przyjmowala TCanvas jako parametr i po sprawie :)

Czy pokazalbys swoj komponent w postaci skompilowanej do EXE? Sam robie cos podobnego:-)

0
Wodzu napisał(a)

No chyba, ze kladziesz swoj komponent na jakims innym komponencie
i to SourceControl pelni role Parenta.

Dokładnie tak jest.

W kazdym razie nie udalo mi sie przetestowac Twojej procedury ale wydaje mi sie ze jest prosty sposob na rozwiazanie Twojego problemu.
Wyslij do swojego komponentu wiadomosc WM_PAINT a nastepnie skopiuj jego canvas. Jesli to nie pomoze, to po prostu narysuj wszystko jeszcze raz na TCanvas bitmapy ktora chcesz zapisac. Nie powinno to stanowic dla Ciebie zadnego problemu, po prostu to co teraz robisz po otrzymaniu WM_PAINT zapnij w metode, ktora bedzie przyjmowala TCanvas jako parametr i po sprawie :)

Dziwne, powinieneś być w stanie na widocznej formatce uzyskać zrzut ekranowy do jpega dowolnego
wizualnego komponentu, czyli na przyklad jakiś przycisk albo ListBox albo Image z tym co na nim jest
nie tylko narysowane, ale nawet to co jest na nim utworzone i ma tego na przykład Image jako parent.
Co do Twojej metody, to nie do końca rozumiem, ale robiąc w ten spsoób uzyskam tylko sam Canvas
na którym rysowałem, a ja mam na kontrolce jeszcze komponenty których rodzicem jest ta kontrolka.

Czy pokazalbys swoj komponent w postaci skompilowanej do EXE? Sam robie cos podobnego:-)

Mój komponent to jest po prostu to, co opisałem w swojej odpowiedzi - w temacie pod tym adresem:
http://4programmers.net/Forum/546626#id546626 - tylko teraz widzę, że Misiekd
mi odpisał i będę musiał się zastosować do jego wskazówek, żeby mój komponent był "poprawniejszy".
Mój komponent to po prostu ScrollBox, który - jak widziałeś ma Canvas i tworzone są na nim w sposób
dybnamiczny komponenty ALProgressBar (do ściągnięcia z torry), które pełnią rolę słupków wykresu
(są w pionie). Całość tworzy może niezbyt udany, ale zawsze jakis prosty wykres. Efekt końcowy jest
dla mnie zadowalający, tylko właśnie pozostaje problem zrobienia zapisu wykresu do pliku jpg, kiedy
formatka jest schowana. Bo program docelowy ma jeszcze główną formatkę, która jest podczas jego
działania zminimalizowana do traya i można ją przywrócić aby zmienić ustawienia lub pokazać wykres.

EDIT:
Częściowo sobie poradziłem. Zrobiłem tak jak poniżej. W związku z tym że ta operacja ma
być wykonana raz na dobę o północy - nie powinno być problemów. Np chyba, że ktoś z
Wad ma inny pomysł - to proszę piszcie. Tylko jest problem ze wznawianiem aplikacji, bo
jak mamy na wierzchu uruchomioną jakąś grę pełnoekranową (testowałem z grą "Frets
on Fite") to gra odzyskuje kontrole nad oknem. Jednak jeżeli aplikacją jest - na przykład,
Opera - to zmienia ona rozmiary swojego okna. Czy można to jakoś dopasować może?

var
  H : THandle;
begin
  H := GetForeGroundWindow;
  Ustawienia1.Click;
  SetForeGroundWindow(Application.Handle);
  with WykresForm do
    begin
    LogSL.Text := OutputRichEdit.Text;
    GenerujWykres;
    Show;
    SendMessage(ScrBox.Handle, WM_PAINT, 0, 0);
    SaveWinControlToJpg(ScrBox, 'D:\test.jpg');
    Close;
    end;
  OkBtn.Click;
  SetForeGroundWindow(H);
  ShowWindow(H, SW_SHOW);
  ShowWindow(H, SW_RESTORE);
end;
0

Nie udalo mi sie przetestowac Twojej metody bo nie ulatwiasz zadania temu kto chce Ci pomoc ;)
W poscie piszesz, ze Twoj komponent posiada jakiegos parenta natomiast w przykladzie metody mamy TWykresForm sugerujacy, ze komponent jest forma. Nie wiedzialem (i w zasadzie nadal nie wiem) czy Twoj komponent nie dziedziczy od TForm, czy tez moze dziedziczy od TForm ale jest "wklejony" w inna forme czy co tam jeszcze. Lubie podawac dokladne odpowiedzi a nie oparte na gdybaniu co ktos ma na mysli dlatego tez darowalem sobie testy.

Mam kilka uwag jesli chcesz posluchac:

Jezeli SourceControl: TWinControl; jest uzywane tylko po to aby podac Parenta dla Twojego komponentu to czy nie lepiej zastapic to wlasciwoscia Parent?;-) Czyli skorzystac z odniesienia TWykresForm.Parent.

W tej linii zamiast WykresForm.Canvas daj Self.Canvas albo w ogole samo Canvas.

DestBitmap.Canvas.CopyRect(Dest, WykresForm.Canvas, Src);

Znalazlem jednak prostsze rozwiazanie Twojego problemu.
Skorzystaj z TForm.GetFormImage;
Ta metoda zwroci Ci bitmape Twojej formy, znajac pozycje Twojego komponentu mozesz spokojnie wyciac sobie to co Cie intereseuje z tej bitmapy i zapisac w pliku. Rozwiazanie to dziala rowniez jesli forma jest zminimalizowana.

Testowalem na Delphi 2007 i Windows Vista.

powodzenia.

0

Dzięki za pomoc, ale jednak funkcja GetFormImage nie zdała rezultatu. Po jej użyciu w taki spsoób:

var
  Bmp : TBitmap;
  Tmp : TBitmap;
  Jpg : TJpegImage;
begin
  Bmp := TBitmap.Create;
  Tmp := TBitmap.Create;
  Jpg := TJpegImage.Create;
  Bmp.Width := ScrBox.Width;
  Bmp.Height := ScrBox.Height;
  Tmp.Assign(ScrBox.Bitmap);
  ScrBox.Canvas.Draw(0, 0, Tmp);
  Bmp.Assign(GetFormImage);
  Jpg.Assign(Bmp);
  Jpg.SaveToFile('D:\formatka.jpg');
  Bmp.Free;
  Jpg.Free;
end;

Nawet przy aktywnej formatce masz taki plik wynikowy: http://i40.tinypic.com/29w4qv7.jpg
Po użyciu natomaist kodu:

var
  Bmp : TBitmap;
  Jpg : TJpegImage;
begin
  Bmp := TBitmap.Create;
  Jpg := TJpegImage.Create;
  Bmp.Width := ScrBox.Width;
  Bmp.Height := ScrBox.Height;
  Bmp.Assign(ScrBox.Bitmap);
  Jpg.Assign(Bmp);
  Jpg.SaveToFile('D:\kanwas.jpg');
  Bmp.Free;
  Jpg.Free;
end;

Masz sam Canvas, czyli naniesione teksty - wynik to taki obrazek: http://i43.tinypic.com/15fq3yt.jpg

Natomiast jak formatka jest pokazana nawet na chwilę aby byłą widoczna lub przy otwartej formatce
użyjemy takiego kodu do zrzutu, ktory kwestionujesz, mimo żę może dla Ciebie mylący dla mnie ok.

SaveWinControlToJpg(ScrBox, 'D:\ok.jpg');

Wynikiem jest taki obrazek: http://i42.tinypic.com/2604gtj.jpg i taki efekt mnie zadowala
w pełni, stąd moje pytanie czy da się jakoś nałóżyć na siebie dwie bitmapy aby móć uzyskać efekt
dokładnie taki sam jak na ostatnim obrazku? Prosiłbym o jakiś przykłądowy kod, bo nie znalazłem ;/
Ewentualnie jakiś inny sposób, aby GetImage chciało również pobrać to co jest na Canvasie ScrBox.

0

Sorry za double posta, ale chce odzielić od tamtego - tę wypowiedź.
Wodzu albo ktokolwiek: macie jakieś pomysły? Bo ja już testowałem różne warianty i nawet GetFormImage
nie da prawidłowych rezultatów jeżeli nie będzie aktywna formatka. Jak będzie zminimalizowana albo gdy w
ógole będzie zamknięta - to na pliku wynikowym pojawią się tylko niektóre komponenty. Albo jak ma to się
w przypadku TALProgressBar pojawią się bez wypełnienia o wartość Position. Nie pokaże się także BitBtn
i inne komponenty. Może ktoś z Was ma pomysł jeszcze jak zrobić zrzut graficzny komponentów na jakimś
innym komponencie, a do tego zeby na zrzucie uwzględnić to co zawiera Canvas, no a formatka jest w tym
czasie ukryta? Bo przy normalnej formatce nie ma problemów. Póki co się poddaje i idę spać. Good nite.
I dodam, że kod na stronie http://delphi.about.com/od/delphitips2008/qt/print_window.htm także nie działa ok.

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