Mam na formie paintbox na którym użytkownik rysuje linie. Potrzebuję jakiejś przezroczystej kontrolki (może to być panel lub Timage lub inna) na którą nakładam siatkę linii, a cały komponent leży na paintbox'ie. I jeszcze jedno: jak w delphi otrzymać zrzut ekranu, a potem wydrukować go na drukarce?
Romlus napisał(a):
Mam na formie paintbox na którym użytkownik rysuje linie. Potrzebuję jakiejś przezroczystej kontrolki (może to być panel lub Timage lub inna) na którą nakładam siatkę linii, a cały komponent leży na paintbox'ie.
Nie. Absolutnie nie.
Funkcja malowania linii oraz tła powinna być zrealizowana w tym samym komponencie. Utwórz w pamięci bitmapę pomocniczą (back buffer), na tej bitmapie maluj to co chce użytkownik. W zdarzeniu PaintBox.OnPaint
najpierw namaluj zawartość pomocniczej bitmapy, a następnie na wierzchu siatkę linii.
Nie dość, że ograniczysz się do jednego komponentu, to samo malowanie i renderowanie kontrolki będzie dużo szybsze.
I jeszcze jedno: jak w delphi otrzymać zrzut ekranu, a potem wydrukować go na drukarce?
Możesz napisać więcej na ten temat? Chodzi mi o to że użytkownik rysuje ściany, czyli linie na paintbox'ie, a potem nakłada na ten obraz siatkę którą ma mieć możliwość przesuwać aż ustali odpowiednie położenie i wydrukuje całość.
Tu nie ma co wyjaśniać – sposób jest zbyt prosty. :]
Potrzebujesz pomocniczej bitmapy, o takim samym rozmiarze co PaintBox
. Wszystko co może malować użytkownik (czyli linie) malujesz na tej pomocniczej bitmapie, nie w OnPaint
komponentu. Dzięki temu zawsze będziesz miał dostęp do surowego rysunku. W zdarzeniu PaintBox.OnPaint
malujesz na płótnie kontrolki zawartość tej pomocniczej bitmapy, a następnie – również na płótnie komponentu – malujesz tę dodatkową siatkę.
Dzięki temu użytkownik na ekranie będzie widział swój rysunek wraz z siatką, a Ty będziesz posiadał w pamięci sam surowy rysunek. To pozwoli dowolnie przesuwać siatkę, bo jest malowana dynamicznie (czyli jest tworem wirtualnym).
Najlepiej by było napisać do tego celu własną, prostą kontrolkę i umieścić pomocniczą bitmapę wewnątrz niej (w sekcji private
). Tworzyć ją w konstruktorze komponentu, zwalniać w destruktorze, zmieniać rozmiar np. w SetBounds
i dać do niej dostęp za pomocą dodatkowej właściwości. Również całą funkcjonalność dotyczącą malowania (czyli ustawień stylu linii, klikania itd.) też należy umieścić w kodzie kontrolki. Dopisać sobie metody przetwarzające odpowiednie komunikaty (np. WM_LBUTTONDOWN
, WM_LBUTTONUP
, WM_MOUSEMOVE
) i zaimplementować w nich rysowanie.
W jaki sposób rysować w czasie rzeczywistym po bitmapie.
Tworzę bitmapę:
bitmapa:=TBitmap.Create;
bitmapa.Width:=paintbox1.Width;
bitmapa.Height:=paintbox1.Height;
Potem rysuję na płótnie:
bitmapa.canvas.Pen.Color:=clblack;
bitmapa.canvas.MoveTo(x1,y1);
bitmapa.canvas.LineTo(x3,y3);
Lecz efektu nie widać.
var
BackBuffer: TBitmap;
begin
BackBuffer := TBitmap.Create();
BackBuffer.PixelFormat := pf24bit;
BackBuffer.Width := PaintBox1.Width;
BackBuffer.Height := PaintBox1.Height;
A zdarzenie OnPaint
? Nie dziw się że nie widać nic na ekranie.
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
Self.Canvas.Draw(0, 0, BackBuffer);
end;
Zapomniałeś też o wypełnieniu tła pomocniczej bitmapy. Domyślnie – jeśli pamięć mnie nie myli – będzie czarne.
BackBuffer.Canvas.Brush.Color := clWhite;
BackBuffer.Canvas.FillRect(BackBuffer.Canvas.ClipRect);
Jak rozumiem, użytkownik w czasie działania programu rysuje na bitmapie w zdarzeniu paintbox.onmousemove?
Nie wiem czy w swoim programie masz już zaimplementowaną obsługę myszy i ogólnie całego mechanizmu malowania, ale jeśli tak, to jedyne co musisz zrobić to wymienić wszystkie linijki malujące po PaintBox.Canvas
na te malujące po BackBuffer.Canvas
.
To spowoduje, że wszystko co namaluje użytkownik, znajdzie się na pomocniczej bitmapie (a nie na ekranie), więc po namalowaniu czegokolwiek na bitmapie, wywołaj PaintBox.Invalidate
– ta metoda wywoła m.in. zdarzenie OnPaint
, dzięki czemu kontrolka na ekranie zostanie odmalowana.
To pozwoli również na podgląd rozciągania linii podczas malowania. Przykładowo, w OnMouseDown
zapamiętujesz współrzędne kliknięcia i ustawiasz flagę włączającą tryb malowania linii (zwykła zmienna logiczna), w OnMouseMove
, jeśli ta flaga jest ustawiona, zapamiętujesz bieżące współrzędne kursora i wołasz Invalidate
, a w OnPaint
(jeśli flaga jest ustawiona) malujesz zawartość bitmapy na ekranie, następnie na podstawie zapamiętanych współrzędnych malujesz dynamicznie linię, a na koniec również dynamicznie malujesz siatkę na samej górze.
Zaraz coś naskrobię – tak będzie najłatwiej. ;)
@Romlus: znalazłem chwilkę i wystrugałem małego PoC-a.
Nie wiem dokładnie jak to u Ciebie wygląda z tą siatką i liniami, więc dla testu napisałem apkę, w której użytkownik może malować prostokąty. Edytor posiada tylny bufor, na którym lądują dane naniesione przez użytkownika. W edytorze (na buforze nie) malowana jest też siatka na wierzchu. Podczas malowania prostokąta, widoczny jest w edytorze jego podgląd, a po puszczeniu klawisza myszy, ląduje w buforze.
Lewym przyciskiem myszy maluje się prostokąty (w taki sam sposób jak w zwykłym edytorze graficznym), a prawy przycisk służy do przesuwania siatki linii. Po lewej w oknie jest pole edytora, po prawej podgląd tego co znajduje się aktualnie w buforze.
Tutaj zrzut z działania: PaintBox PoC.webm
W razie gdybyś mial Lazarusa to również dołączam źródła w załączniku. Have fun. ;)
Dzięki za pomoc. Mój program działa właściwie.