Naprzemienne wczytywanie obrazka

0

Witam wszystkich ponownie

Zdecydowałem się napisać ponownie ponieważ nie daję sobie rady z napisaniem kodu który ma za zadanie podczas zdarzenia On MouseMove na komponencie TMemo wczytywanie obrazka a na zdarzenie on MouseLeave wczytywanie ponownie obrazka który jest domyślie załadowany. Problem polega na tym że obrazki różnią się tylko kolorem jednej rzeczy i chciałbym żeby zamienne wczytywanie odbywało się płynnie bez "mrugania" i aby nie było śladu że w tle zamianiają się te obrazki.
Proszę o pomoc.
Próbuję to zrobić w ten oto sposób :

procedure TForm7.Memo1MouseLeave(Sender: TObject);

begin
    Obrazek.FreeImage;
    Obrazek.LoadFromFile ('człowiek - wymiary kpl_v3.bmp');
    Image3.Picture.Bitmap := Obrazek;
    Refresh;

end;

procedure TForm7.Memo1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin

  Obrazek.FreeImage;
  Obrazek.LoadFromFile ('człowiek - wymiary_2_v3.bmp');
  Image3.Picture.Bitmap := Obrazek;
  Refresh;

end;
0
Obrazek.FreeImage;
Obrazek.LoadFromFile ('człowiek - wymiary kpl_v3.bmp');

To niepotrzebne – LoadFromFile najpierw zwalnia pamięć zajmowaną przez bieżący obraz, a następnie ładuje nowy z zadanego pliku.

Image3.Picture.Bitmap := Obrazek;
Refresh;

To też niepotrzebne – po przypisaniu referencji do właściwości Bitmap, komponent automatycznie przemalowuje się. Całe te cztery linijki można by wymienić na jedną, a program i tak będzie działał tak samo:

Image3.Picture.Bitmap.LoadFromFile('człowiek - wymiary kpl_v3.bmp');

Tak samo, czyli źle, bo nieoptymalnie.


Ten sposób i tak jest do bani, ale wypadało napisać o tym, że przekombinowujesz.

Podstawowym problemem jest to, że ciągle ładujesz obrazki z plików, zamiast je raz załadować do pamięci i tylko przepisywać referencje do komponentu w odpowiednich zdarzeniach. Przydadzą się dwa pola:

private
  FPictureNormal: TBitmap;
  FPictureHover: TBitmap;

Drugi podstawowy błąd to użycie zdarzenia OnMouseMove. Ono wywoływane jest za każdym razem, jeśli pozycja kursora zmieni się choćby o piksel. Zakładając, że Twoje Memo ma 300px szerokości i przesuniesz kursor od lewej krawędzi do prawej, obrazek zostanie 300x załadowany z pamięci i wpisany do Image3, a ten z kolei zostanie 300x przemalowany. Dziwisz się, że miga? Bo ja nie.

Przydatne dwie metody – jedna tworzy instancje i ładuje obrazy z plików do pamieci, a druga zwalnia obiekty:

procedure TMyForm.LoadPictures();
begin
  FPictureNormal := TBitmap.Create();
  FPictureNormal.LoadFromFile('człowiek - wymiary kpl_v3.bmp');
  
  FPictureHover := TBitmap.Create();
  FPictureHover.LoadFromFile('człowiek - wymiary_2_v3.bmp');
end;

procedure TMyForm.UnloadPictures();
begin
  FPictureNormal.Free();
  FPictureHover.Free();
end;

Następnie do wykrycia najechania kursora na Memo użyj zdarzenia OnMouseEnter, w którym użyj obrazu Hover:

Image3.Picture.Bitmap := FPictureHover;

A do wykrycia zabrania kursora znad kontrolki, użyj zdarzenia OnMouseLeave, w którym użyj obrazu Normal:

Image3.Picture.Bitmap := FPictureNormal;

Amen. ;)

0

Witam ponownie

Dziękuję bardzo @furious programming za odpowiedz
Zastosowałem się do wskazówek ale nadal mi to mruga gdy najeżdzam na Memo i go opuszczam
Dodaję że mam takich obrazków 19 różniących się szczegółami kolorystycznymi. Niestety dalej mi to miga. Proszę o pomoc co jeszcze można by było zrobić?
Uporczywie to jest wkurzające gdy szybko przesuwam myszką po innych polach memo przypisanych do obrazków.
Tak pomyślałem a może za dużo objętości zajmują te mapy bitowe bo jedna mapa ma objętość około 1,5MB?

pozdrawiam
Arek

0
solark napisał(a):

Dodaję że mam takich obrazków 19 różniących się szczegółami kolorystycznymi.

Szczegóły kolorystycznie nie mają żadnego znaczenia, jeśli chodzi o proces renderowania kontrolki.

Niestety dalej mi to miga.

To, czyli co? Jak mamy Ci pomóc, skoro nie mamy dostępu do kodu?

Proszę o pomoc co jeszcze można by było zrobić?

Wymień wszystkie Image na PaintBox, ew. dodatkowo skorzystaj z tylnego buforowania.

Owe miganie pojawia się wtedy, gdy komponent najpierw wypełnia swoje tło jednolitym prostokątem, a dopiero po tym maluje coś na wierzchu. Jeśli namalujesz sobie wszystko na pomocniczej bitmapie i na koniec namalujesz całą bitmapę na płótnie komponentu, to nic nie mignie.

Uporczywie to jest wkurzające gdy szybko przesuwam myszką po innych polach memo przypisanych do obrazków.

Jak są one ułożone na formularzu? Zrób zrzut ekranu (a raczej samego formularza) i pokaż go tutaj.

Tak pomyślałem a może za dużo objętości zajmują te mapy bitowe bo jedna mapa ma objętość około 1,5MB?

Ile?! Strasznie duże te obrazy… Jaki jest ich rozmiar w pikselach i czemu tak dużo? :P

0

Grafika jak na załączonym obrazku
Chodzi o to że chcę zmieniać kolor wymiaru na czerwony po najechaniu na Memo w związku z tym mam 19 obrazków a na każdym inny wymiar w kolorze czerwonym
Plik zajmuje tyle bo jest to obraz BMP zapisany z obrazka wektorowego. Rozmiar w pikselach to 594 na 697 pikseli. Ile w takim razie powinien zajmować taki plik aby się to odbywało płynnie?
A jak w ogóle wczytać obrazek do tego PaintBoxa i jak zrobić to tylne buforowanie?

A oto kod który udało mi się stworzyć:

 private
    { Private declarations }
      FMaleST0 : TBitmap;
      FMaleST1 : TBitmap;
      FMaleST2 : TBitmap;
      FMaleST3 : TBitmap;
      FMaleST4 : TBitmap;
      FMaleST5 : TBitmap;
      FMaleST6 : TBitmap;
      FMaleST7 : TBitmap;
      FMaleST8 : TBitmap;
      FMaleST9 : TBitmap;
      FMaleST10 : TBitmap;
      FMaleST11 : TBitmap;
      FMaleST12 : TBitmap;
      FMaleST13 : TBitmap;
      FMaleST14 : TBitmap;
      FMaleST15 : TBitmap;
      FMaleST16 : TBitmap;
      FMaleST17 : TBitmap;
      FMaleST18 : TBitmap;
      FMaleST19 : TBitmap;

  public
    { Public declarations }


  end;

var
  Form7: TForm7;

implementation

{$R *.dfm}

uses Unit8;

 procedure TForm7.LoadPictures();
begin
  FMaleST0 := TBitmap.Create();
  FMaleST0.LoadFromFile('człowiek - wymiary kpl.bmp');

  FMaleST1 := TBitmap.Create();
  FMaleST1.LoadFromFile('człowiek - wymiary_1.bmp');

  FMaleST2 := TBitmap.Create();
  FMaleST2.LoadFromFile('człowiek - wymiary_2.bmp');

  FMaleST3 := TBitmap.Create();
  FMaleST3.LoadFromFile('człowiek - wymiary_3.bmp');

  FMaleST4 := TBitmap.Create();
  FMaleST4.LoadFromFile('człowiek - wymiary_4.bmp');

  FMaleST5 := TBitmap.Create();
  FMaleST5.LoadFromFile('człowiek - wymiary_5.bmp');

  FMaleST6 := TBitmap.Create();
  FMaleST6.LoadFromFile('człowiek - wymiary_6.bmp');

  FMaleST7 := TBitmap.Create();
  FMaleST7.LoadFromFile('człowiek - wymiary_7.bmp');

  FMaleST8 := TBitmap.Create();
  FMaleST8.LoadFromFile('człowiek - wymiary_8.bmp');

  FMaleST9 := TBitmap.Create();
  FMaleST9.LoadFromFile('człowiek - wymiary_9.bmp');

  FMaleST10 := TBitmap.Create();
  FMaleST10.LoadFromFile('człowiek - wymiary_10.bmp');

  FMaleST11 := TBitmap.Create();
  FMaleST11.LoadFromFile('człowiek - wymiary_11.bmp');

  FMaleST12 := TBitmap.Create();
  FMaleST12.LoadFromFile('człowiek - wymiary_12.bmp');

  FMaleST13 := TBitmap.Create();
  FMaleST13.LoadFromFile('człowiek - wymiary_13.bmp');

  FMaleST14 := TBitmap.Create();
  FMaleST14.LoadFromFile('człowiek - wymiary_14.bmp');

  FMaleST15 := TBitmap.Create();
  FMaleST15.LoadFromFile('człowiek - wymiary_15.bmp');

  FMaleST16 := TBitmap.Create();
  FMaleST16.LoadFromFile('człowiek - wymiary_16.bmp');

  FMaleST17 := TBitmap.Create();
  FMaleST17.LoadFromFile('człowiek - wymiary_17.bmp');

  FMaleST18 := TBitmap.Create();
  FMaleST18.LoadFromFile('człowiek - wymiary_18.bmp');

  FMaleST19 := TBitmap.Create();
  FMaleST19.LoadFromFile('człowiek - wymiary_19.bmp');


end;

procedure TForm7.UnloadPictures();
begin
  FMaleST0.Free();
  FMaleST1.Free();
  FMaleST2.Free();
  FMaleST3.Free();
  FMaleST4.Free();
  FMaleST5.Free();
  FMaleST6.Free();
  FMaleST7.Free();
  FMaleST8.Free();
  FMaleST9.Free();
  FMaleST10.Free();
  FMaleST11.Free();
  FMaleST12.Free();
  FMaleST13.Free();
  FMaleST14.Free();
  FMaleST15.Free();
  FMaleST16.Free();
  FMaleST17.Free();
  FMaleST18.Free();
  FMaleST19.Free();
end;

procedure TForm7.FormClose(Sender: TObject; var Action: TCloseAction);
begin

    UnloadPictures();


end;

procedure TForm7.FormCreate(Sender: TObject);
begin

  PageControl1.ActivePageIndex := 0;
  PageControl2.ActivePageIndex := 0;
  LoadPictures();

end;




procedure TForm7.Memo1MouseEnter(Sender: TObject);
begin
    Image3.Picture.Bitmap := FMaleST1;
end;

procedure TForm7.Memo1MouseLeave(Sender: TObject);
begin
    Image3.Picture.Bitmap := FMaleST0;
end;

procedure TForm7.Memo2MouseEnter(Sender: TObject);
begin
     Image3.Picture.Bitmap := FMaleST2;
end;

procedure TForm7.Memo2MouseLeave(Sender: TObject);
begin
     Image3.Picture.Bitmap := FMaleST0;
end;

procedure TForm7.Memo3MouseEnter(Sender: TObject);
begin
   Image3.Picture.Bitmap := FMaleST3;
end;

procedure TForm7.Memo3MouseLeave(Sender: TObject);
begin
   Image3.Picture.Bitmap := FMaleST0;
end;

end.

0
solark napisał(a):

Grafika jak na załączonym obrazku

Powinieneś ten obrazek malować w całości na PaintBox lub w oknie, dostosowanym do jego wymiarów.

Chodzi o to że chcę zmieniać kolor wymiaru na czerwony po najechaniu na Memo w związku z tym mam 19 obrazków a na każdym inny wymiar w kolorze czerwonym

Mhm. Zakładam, że te 19 obrazków różni się między sobą tylko tym, że ten podstawowy jest cały czarno-biały, a pozostałe posiadają kolorowe strzałki? Czyli wszystkie są tego samego rozmiaru, a jedynie niektóre ich fragmenty różnią się kolorami?

Jeśli dobrze rozumiem, to robisz bardzo duży błąd. Zwróć wagę na to, że odmalowanie tak wielkiego obrazu na ekranie trwa dość długo, a po drugie, taki obraz również bardzo dużo zajmuje. Dlatego też powinieneś mieć tylko jeden duży obrazek – dla tła – a pozostałe powinny zawierać wyłącznie obszar różniący się od oryginału. Czyli każdy z nich powinien zawierać tylko kolorowe strzałki, z tym co jest pod spodem.

Najprostsze rozwiązanie to malować tło na płótnie formularza. Następnie na nim położyć 18 różnych Image, załadować do nich obrazki (nie podczas działania programu – w oknie inspektora obiektów) i ułożyć je tak, aby pasowały do strzałek na tle. Wszystkim tym obrazkom ustawić Visible na False, tak aby po uruchomieniu wszystkie były niewidoczne. Na koniec wygenerować zdarzenia OnMouseEnter i OnMouseLeave dla pierwszego Memo, w nich odpowiednio pokazywać i ukrywać dany Image. Na koniec podłączyć te dwa zdarzenia pod wszystkie inne Image, tak aby nie dublować kodu.

Plik zajmuje tyle bo jest to obraz BMP zapisany z obrazka wektorowego. Rozmiar w pikselach to 594 na 697 pikseli. Ile w takim razie powinien zajmować taki plik aby się to odbywało płynnie?

Wyeksportuj grafikę źródłową (wektorową) do PNG to się przekonasz. ;)

A jak w ogóle wczytać obrazek do tego PaintBoxa i jak zrobić to tylne buforowanie?

Teraz już mniej więcej wiem co potrzebujesz zrobić, więc to co wcześniej napisałem nie jest aktualne. Ogólnie w tylnym buforowaniu chodzi o to, aby wszystkie operacje związane z malowaniem po ekranie (czyli np. bezpośrednio na płótnie formularza lub komponentu) wykonać na pomocniczej bitmapie, a dopiero po tym wszystkim namalować całą bitmapę na płótnie kontrolki. Dzięki temu obraz na ekranie zostanie odmalowany tylko raz, co skutecznie wykluczy miganie (tzw. flickering). Drugą istotną różnicą jest to, że tylne buforowanie daje solidnego kopa, dzięki czemu czas trwania procesu renderowania znacznie się skraca.

To tak w skrócie, i choć sposób ten jest bardzo prosty w implementacji, to można by pisać o nim bez końca. Przyklad back bufferingu znajdziesz w moim niedawnym poście, zajrzyj do kodu z załącznika i poczytaj – Transparent panel i zrzut części ekranu .


 private
    { Private declarations }
      FMaleST0 : TBitmap;
      FMaleST1 : TBitmap;
      FMaleST2 : TBitmap;
      FMaleST3 : TBitmap;
      FMaleST4 : TBitmap;
      FMaleST5 : TBitmap;
      FMaleST6 : TBitmap;
      FMaleST7 : TBitmap;
      FMaleST8 : TBitmap;
      FMaleST9 : TBitmap;
      FMaleST10 : TBitmap;
      FMaleST11 : TBitmap;
      FMaleST12 : TBitmap;
      FMaleST13 : TBitmap;
      FMaleST14 : TBitmap;
      FMaleST15 : TBitmap;
      FMaleST16 : TBitmap;
      FMaleST17 : TBitmap;
      FMaleST18 : TBitmap;
      FMaleST19 : TBitmap;

Słyszałeś kiedyś o tablicach, albo o listach generycznych? :P

procedure TForm7.Memo1MouseEnter(Sender: TObject);
begin
    Image3.Picture.Bitmap := FMaleST1;
end;

{..}

Nie używasz parametru Sender, a on pozwoli drastycznie skrócić kod.

0

Witam ponownie

Dzięki @furious programming za zaangażowanie.
Spróbowałem sposobu o którym mówisz tzn wyeksportowałem do png tych 19 obrazków z osobno każdą strzałką w kolorze czerwonym o rozmiarze takim jak postać,następnie ustawiłem właściwości Transparent na True a Visable na Folse ale to dalej miga - Tracę już powoli nadzieję że to się uda. Może to tylne buforowanie coś pomoże. Niestety nie wiem jak to ugryźć w moim przypadku i jak to oprogramować dla Image.
Wiem że może już się robię nudny ale proszę o pomoc

0
solark napisał(a):

Spróbowałem sposobu o którym mówisz tzn wyeksportowałem do png tych 19 obrazków z osobno każdą strzałką w kolorze czerwonym o rozmiarze takim jak postać […]

Nie, miałeś wyeksportować fragmenty obrazu źródłowego (same strzałki), do o wiele mniejszych obrazków. :P

[…] następnie ustawiłem właściwości Transparent na True […]

I tu jest problem – użycie 32-bitowych grafik w interfejsie potwornie spowalnia proces renderowania okna… Dlatego w celu zwiększenia wydajności używa się pomocniczych bitmap.

Nie pisałem Ci żebyś użył grafik przezroczystych – wszystkie powinny być 24-bitowe, a te malutkie grafiki powinny zawierać fragmenty o wymiarach strzałek, razem z tłem i czarnymi liniami. Dzięki temu wszystkie obrazy będą mogły być nieprzezroczyste, a renderowanie okna będzie bardzo szybkie.

I pamiętaj – to że najprostszym rozwiązaniem jest narobienie kupki komponentów i ich pokazywanie/ukrywanie, wcale nie oznacza, że jest to rozwiązanie dobre. Bo nie jest. Dobrym sposobem będzie malowanie tła bezpośrednio na płótnie komponentu/formularza, stosując back buffering do łącznia obrazu w całość (tło z konkretną strzałką).

Może to tylne buforowanie coś pomoże.

Na pewno pomoże.

Niestety nie wiem jak to ugryźć w moim przypadku i jak to oprogramować dla Image.

Nie nie nie – nie dla Image, bo ten komponent jest cholernie powolny. Tylny bufor ma posłużyć jako niewidoczne, pomocnicze płótno do złożenia obrazu do kupy, a następnie tak przygotowaną grafikę namalować w odpowiednim komponencie lub bezpośrednio na płótnie formularza.


Możesz dołączyć do załączników posta archiwum ze wszystkimi grafikami?

Mógłbym coś naskrobać w wolnym czasie. Najlepiej by było, gdybyś przygotował zestaw obrazków o tych samych rozmiarach (o rozmiarze głównego dla tła), gdzie jeden byłby czarno-białym tłem, a reszta posiadała dodatkowo pokolorowane strzałki, po jednej dla każdej grafiki.

0

Witam ponownie

Jeszcze raz dziękuję @furious programming za zaangażowanie i pomoc
Zgodnie z rozmową wysyłam rysunki do stworzenia przykładowej formatki
Gdyby się udało stworzyć odpowiedni kod to bardzo proszę o zamieszczenie go

pozdrawiam
Arek

0

Jeśli tymi dodatkowymi obrazkami są strzałki i odnośniki to nie lepiej mazać je bezpośrednio na canvasie tylko bawić się w ładowanie obrazków??

Można też stworzyć Shape'y ze strzałkami (trójkąt, linia, trójkąt) nad tym podkładem i tylko je ukrywać albo pokazywać :)
Trójkąt w shape-ie

0
solark napisał(a):

Zgodnie z rozmową wysyłam rysunki do stworzenia przykładowej formatki

No niezbyt, prosiłem o stworzenie obrazków o takich samych rozmiarach, gdzie jedno jest tło a pozostałe to jego kopie z pokolorowanymi strzałkami. Nieważne, przygotuję swoje grafiki do prostego testu – będzie łatwiej.

Gdyby się udało stworzyć odpowiedni kod to bardzo proszę o zamieszczenie go

Przykład przygotuję w Lazarusie, bo tylko tego używam. Mimo wszystko kod będzie się dało przenieść do Delphi.

0

Dziękuję bardzo @furious programming za zaangażowanie. Chciałem się zapytać jak tam idzie z tym przykładem?

0

Ja będę forsował swoję propozycję rozwiązania chociaż w trochę innej formie. Sprawdź jak widzisz rozwiązanie na przykładzie z załącznika :D

obraz.7z

0

Dzięki @Clarc O coś takiego chodziło. Niestety nie rozumiem tego kodu i moja mał prośba to jakbyś mógł wyjaśnić ten kod.
Druga sprawa to wcześniej nie podjąłem tematu ponieważ nie wiem jak idealnie przykryć strzałkę tym obiektem Shape bo wydaje mi się że będę potrzebował jakieś współrzędne tej strzałki do przykrycia z obrazka i właśnie skąd wziąć te współrzędne ?
A może rysować strzałkę za pomocą shapa od początku i tylko zmieniać jej kolor?
Proszę @Clarc jeszcze o wyjaśnienie kodu i sposobu który chcesz zastosować

dziękuję i pozdrawiam
Arek

0

Tak na szybko. Na formatce wstawiasz sobie Komponenty Shape. Komponentami 'przykrywasz' swoje strzałki, tak że komponent jest na całej strzałce (jeśli jesteś pedantem to możesz później poustawiać sobie top, left, width, weight co do piksela tak żeby wszystko ładnie wyglądało). Dla shape-ów zmieniasz dwa parametry:

    Brush.Color = clRed
    Pen.Mode = pmMerge

Pierwszy zmienia kolor na czerwony, drugi nakłada filtr (jeśli można to tak nazwać) ,który czarne czy ciemne elementy koloruje według pierwszego parametru (możesz posprawdzać inne opcje Mode bo może będzie jakieś inne ciekawe rozwiązanie). Na starcie ustawiasz dla shape-ów Visible na False.
Teraz są 2 procedury które przekazują przez parametr sendera czyli obiekt, który je wywołam. Procedury są podpięte pod zdarzenia w Memo: OnMouseEnter i OnMouseLeave.
W procedurach rzutujesz obiekt na klase TMemo otrzymując odwołanie do konkretnego obiektu. Później robisz coś podobnego z Shape-ami. Szukasz komponentu którego Name odpowiada schematowi 'ShapeX' gdzie 'X' to numer odpowiadający takiemu samemu schematowi w Memo, gdzie Memo mają Name 'MemoX'. Jeśli komponent typu TShape zostanie znaleziony to po pokazujesz lub ukrywasz.
Jeśli miałeś problem ze zrozumieniem możesz poczytać trochę o tym mniej więcej od tego miejsca: https://4programmers.net/Delphi/Kompendium

0

Witam ponownie
Dzięki @Clarc za wyjaśnienia

0

Jak by Ci się udało @furious programming to bardzo bym Ciebie prosił abyś umieścił taki kod
Udało mi się to odpalić ale czasami przy opuszczaniu Memo coś mignie i pojawia sie białe tło Może udałoby się to jakoś wyeliminować?

1

No dobrze, PoC gotowy.

Dla przykładu użyłem loga 4p, tak aby pokazać, że kształty i zawartość podświetlanych fragmentów nie mają większego znaczenia. Są trzy Memo, każde z nich podświetla dany fragment loga – reszta jest nieaktywna. Wszystkie Memo i Image są nazwane domyślnie, z zachowaniem indeksu – dzięki temu można łatwo się do nich odnieść. Wszystkie obrazki wbrew pozorom nie obsługują przezroczystości – są 24-bitowe, z białym tłem w szare linie. Łącznie z tłem.

O co chodzi – użyte kontrolki mają nazwy Memo0, Memo1, Memo2, Image0, Image1 oraz Image2. Dodatkowo, każde Memo ma ustawiony Tag na wartość od 0 do 2. Do tego mamy wygenerowaną parę zdarzeń – OnMouseEnter i OnMouseLeave dla Memo0 – które podpięte są w oknie inspektora obiektów do pozostałych Memo. Wyglądają tak:

procedure TForm1.Memo0MouseEnter(ASender: TObject);
var
  LMemo: TMemo absolute ASender;
begin
  UpdateImageVisibility(LMemo.Tag, True);
end;

procedure TForm1.Memo0MouseLeave(ASender: TObject);
var
  LMemo: TMemo absolute ASender;
begin
  UpdateImageVisibility(LMemo.Tag, False);
end;

Deklaracja LMemo służy do tego, aby móc traktować ASender nie jako TObject, a właśnie jako TMemo, bo trzeba się dobrać do właściwości Tag (a TObject go nie posiada). Następnie wywoływana jest metoda UpdateImageVisibility, która wygląda tak:

procedure TForm1.UpdateImageVisibility(AIndex: Integer; AVisible: Boolean);
var
  LImage: TImage;
  LImageName: String;
begin
  LImageName := Format('Image%d', [AIndex]);

  LImage := FindChildControl(LImageName) as TImage;
  LImage.Visible := AVisible;
end;

Pierwsza linijka po begin służy do określenia nazwy komponentu Image, na podstawie indeksu z parametru AIndex. Chodzi o to, aby uzyskać ciąg Image0, Image1 lub Image2, bo takie nazwy komponentów Image używamy.

Kolejna linijka służy do znalezienia komponentu o przygotowanej nazwie (trzymanej w zmiennej LImageName) i zwrócenia referencji tego komponentu. Z racji tej, że metoda FindChildControl zwraca referencję typu TControl, rzutujemy ją na TImage za pomocą operatora as. Do zmiennej LImage ląduje referencja docelowego Image.

Ostatnim krokiem jest zmiana stanu widoczności naszego obrazka, co wykonywane jest w ostatniej linijce – wartość AVisible wpisywana jest do właściwości Visible, która albo ukrywa komponent (jeśli True), albo go pokazuje (jeśli False). Wartość True przekazywana jest w zdarzeniu OnMouseEnter, bo chcemy pokazać dany Image, a False w zdarzeniu OnMouseLeave, aby go ukryć.


I to wszystko – cały kod modułu głównego okna wrzucam tutaj. Jak widać, jest go niewiele, a jeśli dojdą kolejne pary Image i Memo, to przybędzie jedynie kodu deklaracji kontrolek w klasie okna – zdarzenia będzie można podpiąć pod nowe kontrolki, nie pisząc ani jednej linijki kodu.

Zrzut z działania aplikacji jest w załączniku (filmik .webm), tak samo jak źródła projektu z plikiem .exe do potestowania. Załącznik oprócz źródeł projektu zawiera także cztery używane grafiki, więc możesz sobie popatrzeć jak one wyglądają.


PS: Niektóre rzeczy można było zapisać krócej, jednak nie chciałem zbyt skomplikować kodu. No i też trochę spieszyłem się z tym przykładem, więc nie wszystko dobrze przemyślałem. Ale jest Ok – możesz korzystać. :P

0

zamiast:

procedure TForm1.Memo0MouseEnter(ASender: TObject);
var
  LMemo: TMemo absolute ASender;
begin
  UpdateImageVisibility(LMemo.Tag, True);
end;

można pewnie, ale nie testowałem:

procedure TForm1.Memo0MouseEnter(ASender: TObject);
begin
  UpdateImageVisibility(TMemo(ASender).Tag, True);
end;
0

@lampasss: pewnie. ;)

To jest pozostałość po wcześniejszej wersji kodu, która nie używała metody UpdateImageVisibility i istnienie tej zmiennej miało sens. Uniwersalny kod wydzieliłem do osobnej metody, ale o tych zmiennych najwyraźniej zapomniałem.

Samo istnienie metody UpdateImageVisibility też nie jest konieczne, bo cały kod można sprowadzić tylko i wyłącznie do jednej linijki kodu, zastępującej zmienne itd. Czyli wymienić to:

procedure TForm1.UpdateImageVisibility(AIndex: Integer; AVisible: Boolean);
var
  LImage: TImage;
  LImageName: String;
begin
  LImageName := Format('Image%d', [AIndex]);
 
  LImage := FindChildControl(LImageName) as TImage;
  LImage.Visible := AVisible;
end;

na to:

procedure TForm1.Memo0MouseEnter(ASender: TObject);
begin
  TImage(FindChildControl(Format('Image%d', [TMemo(ASender).Tag]))).Show();
end;

procedure TForm1.Memo0MouseLeave(ASender: TObject);
begin
  TImage(FindChildControl(Format('Image%d', [TMemo(ASender).Tag]))).Hide();
end;

ale sam widzisz jak z czytelnością tego kodu. Dlatego porozbijałem instrukcje na mniejsze części, tak aby pytacz nie rwał włosów z głowy, próbując rozgryźć o co chodzi. :P

0

Witam ponownie
Niestety mam problem z odpaleniem tego kodu
Wybaczcie że może powiem coś głupiego ale wydaje mi się że definicja funkcji FindChildControl

function FindChildControl(const ControlName: string): TControl;

domyślnie przyjmuje stringa jako stałą natomiast według kodu @furious programming parametr funkcji zadeklarowany jest jako zmienna

var
  LImage: TImage;
  LImageName: String;

Mogę się mylić bo jestem początkujący dlatego proszę o pomoc

0

Poczytaj tutaj o funkcja i procedurach Procedury i funkcje

A na czym polega konkretnie problem z odpaleniem kodu?

0

Wyskakuje mi taki błąd po najechaniu na Memo jak w załączniku

0

A jaki masz kod w Memo0MouseEnter? Bo to co napisał @furious programming LMemo: TMemo absolute ASender; nie jest już chyba w Delphi obsługiwane.

0

Właśnie tak mam napisane
Jak możesz to proszę podaj jak ma być właściwie

0

@Clarc: no jak to nie jest obsługiwane… Variables – Absolute Addresses. To co może nie wspierane, jeśli o absolute chodzi, to deklarowanie zmiennej z podaniem konkretnego adresu pamięci (jako literału liczbowego):

var
  Foo: TFoo absolute $FF00;

We Free Pascalu domyślnie też nie jest obsługiwana, ale można tej konstrukcji użyć w trybie {MODE TP}, czyli w dialekcie, w którym takie rzeczy robiło się na co dzień (tyle że w formacie $segment:offset).


Deklarowanie zmiennej pod adresem innej zmiennej to konstrukcja pozwalająca ukrócić drabinki if-ów z is-ami oraz pozwalająca pozbyć się ciągłego rzutowania referencji za pomocą as lub w stylu funkcyjnym. Jest to konstrukcja bardzo przydatna i nie ma powodu, aby jej nie wspierać.

0

Witam ponownie
Dziękuję wszystkim za pomoc

Udało mi się uruchomić ten program ale bez tej linijki poniżej:

LImage := FindChildControl(LImageName) as TImage;

Ewidentnie jest z nią jakiś problem ale nie wiem jaki dlatego proszę o pomoc

0

Sprawdź czy nazwy komponentów Image są w stylu Image0, Image1, Image2 itd., następnie czy nazwy Memo są w tym samym stylu, czyli Memo0, Memo1, Memo2 itd., następnie sprawdź, czy wszystkie Memo mają zdefiniowany prawidłowy Tag, czyli 0, 1, 2 itd. Następnie sprawdź, czy zdarzenia Memo0.OnMouseEnter i Memo0.OnMouseLeave są podłączone pod wszystkie Memo.

Jeśli wszystko jest na swoim miejscu to nie ma prawa nie działać.

solark napisał(a):
LImage := FindChildControl(LImageName) as TImage;

Ewidentnie jest z nią jakiś problem ale nie wiem jaki dlatego proszę o pomoc

Problem jest taki, że kontrolki nazwane są inaczej i metoda FindChildControl nie odnajduje tej docelowej, przez co zwraca nil i kolejna instrukcja generuje wyjątek. Tak obstawiam – bo kodu nima.

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