Czy można wstawić niestandardowy kształt jako TButton lub TImage?

0

Tak na prawdę chodzi mi o to, żeby stworzyć przycisk, ale nie o standardowym kształcie, prostokąta, czy okrągu, a np. z prawej strony jest zwykła linia, z lewej u góry jest róg zaokrąglony, a na dole ten zaokrąglony róg jest połączony z dolną linią, linią skośną. Do tej pory jako przycisk wstawiałem TImage, ale po prostu był to prostokąt, więc i tak reagował jak powinien. Teraz też mógłbym zrobić jakiś plik graficzny z przezroczystością, ale TImage wstawia się jako prostokąt i tą przezroczystość traktował by jako część obrazka.

3
lucasp17 napisał(a):

Teraz też mógłbym zrobić jakiś plik graficzny z przezroczystością, ale TImage wstawia się jako prostokąt i tą przezroczystość traktował by jako część obrazka.
No to czemu tego nie zrobisz? Podstawa to funkcja tworząca region z obrazka (możesz znaleźć w Google ja w przykładzie też używam pierwszej znalezionej). Ponieważ TImage nie ma uchwytu więc nie można bezpośrednio nadać mu kształtu funkcją SetWindowRgn ale raczej nic nie stoi na przeszkodzie aby sprawdzać czy kliknięto obszar o którym nam chodzi przy zdarzeniu OnMouseDown (i ew. OnMouseUp) całość moze wyglądać np. tak:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TRGBArray = array[0..32767] of TRGBTriple;
  PRGBArray = ^TRGBArray;

  TForm1 = class(TForm)
    Image1: TImage;
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    function CreateRegion(Bmp: TBitmap): THandle;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


function TForm1.CreateRegion(Bmp: TBitmap): THandle;
var
  X, Y, StartX:Integer;
  Excl: THandle;
  Row: PRGBArray;
  TransparentColor: TRGBTriple;
begin
  // Change the format so we know how to compare
  // the colors
  Bmp.PixelFormat := pf24Bit;
    
  // Create a region of the whole bitmap 
  // later we will take the transparent   
  // bits away
  Result := CreateRectRGN(0, 0, Bmp.Width, Bmp.Height);

  // Loop down the bitmap   
  for Y := 0 to Bmp.Height - 1 do
  begin
    // Get the current row of pixels
    Row := Bmp.Scanline[Y];

    // If its the first get the transparent
    // color, it must be the top left pixel
    if Y = 0 then
    begin
      TransparentColor := Row[0];
    end;

    // Reset StartX (-1) to indicate we have
    // not found a transparent area yet
    StartX := -1;

    // Loop across the row
    for X := 0 to Bmp.Width do
    begin

      // Check for transparency by comparing the color
      if(X <> Bmp.Width) and
        (Row[X].rgbtRed = TransparentColor.rgbtRed) and
        (Row[X].rgbtGreen = TransparentColor.rgbtGreen) and
        (Row[X].rgbtBlue = TransparentColor.rgbtBlue) then
      begin
        // We have (X <> Bmp.Width) in the clause so that
        // when we go past the end of the row we we can
        // exclude the remaining transparent area (if any)
        // If its transparent and the previous wasn't
        // remember were the transparency started
        if StartX = -1 then
        begin
          StartX := X;
        end;
      end
      else
      begin
        // Its not transparent
        if StartX > -1 then
        begin
          // If previous pixels were transparent we
          // can now exclude the from the region
          Excl := CreateRectRGN(StartX, Y, X, Y + 1);
          try
            // Remove the exclusion from our original region
            CombineRGN(Result, Result, Excl, RGN_DIFF);

            // Reset StartX so we can start searching
            // for the next transparent area
            StartX := -1;
          finally
            DeleteObject(Excl);
          end;
     end;
      end;
    end;
  end;
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if PtInRegion(Image1.Tag, X, Y) then
    ShowMessage('dziala');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Image1.Tag:= CreateRegion(Image1.Picture.Bitmap);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DeleteObject(Image1.Tag);
end;

end.

Image1 to oczywiście "przycisk" i znajduje się w nim bitmapa i ma ustawiony Transparent na True.

5

Każdą kontrolkę dziedziczącą po TWinControl (posiadającą uchwyt) możesz przyciąć do dowolnego kształtu używając SetWindowRgn (niestety TImage się nie łapie). Po takiej operacji nie trzeba już sprawdzać czy się kliknęło w "narysowaną" część czy w "schowaną".

2

Dokładnie tak - każdy koponent, który dziedziczy po klasie TWinControl, czyli de facto posiada swój uchwyt, bo jego m.in. trzeba podać w funkcji SetWindowRgn; Tę funkcję można wykorzystać z dowolnymi komponentami posiadającymi uchwyt, a także z formularzami;

Niestety TImage dziedziczy z klasy TGraphicControl, a ta z TControl, więc nie ma uchwytu i zastosować wymienionej funkcji nie można; Sprawdź rozwiązanie kAzka oraz poczytaj materiały z sieci.

2
abrakadaber napisał(a):

... niestety TImage się nie łapie ...
Owszem, ale łapie się TPanel na którym umieszczono ten TImage.

0

Niestety. Z tym sobie nie radzę. Nie wiem skąd się bierze uchwyt i czym to konkretnie jest. A więc potrafię zastosować SetWindowRgn tylko do całego okna.

4

Ale czego tu można nie rozumieć? Dałem gotowca @_13th_Dragon dodał że nawet nie trzeba sprawdzać funkcją PtInRegion czy kliknięto narysowany obszar przycisku tylko wystarczy że Image będzie na Panelu (a więc Panel będzie jego rodzicem) wtedy można dostosować kształt Panelu funkcją SetWindowRgn a leżący na nim Image automatycznie będzie miał ten sam kształt. Wszystko jak w moim poprzednim kodzie podaję tylko co się zmieniło:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel1.Tag:= CreateRegion(Image1.Picture.Bitmap);
  SetWindowRgn(Panel1.Handle, Panel1.Tag, True);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DeleteObject(Panel1.Tag);
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  ShowMessage('działa');
end;
0

Panel1.Handle To było problemem. Teraz na pewno zadziała. Po prostu ja nie wiedziałem skąd mam wziąć ten uchwyt dla poszczególnych komponentów. Szukałem jakichś funkcji które by to robiły. Nie wiedziałem, że wystarczy to zrobić w ten sposób.

1

Uchwyt to nic innego jak unikalna liczba naturalna, nadawana zapewne przez systemowe mechanizmy, za pomocą której identyfikuje się okna w systemie; Pod pojęciem okien rozumie się wszelkie formularze oraz komponenty posiadające uchwyt (czyli tak jak napisałem wcześniej, dziedziczące po klasie TWinControl w VCL, a pod WinAPI wszelkie komponenty tworzone np. za pomocą CreateWindow);

Takie komponenty oraz wszelkie formularze posiadają odpowiednie właściwości, zwracające uchwyty np. typu THandle (HWND z WinAPI); Dodatkowo, systemowe biblioteki zawierają i udostępniają szereg funkcji do pobierania wszelkich uchwytów, jak np. FindWindow, GetActiveWindow czy GetDesktopWindow;

Jak widzisz jest sporo rzeczy do poznania.

0

Jest sporo i z czasem je poznaję. Za tego posta bardzo dziękuję po dużo mi wyjaśnił. Ja czytałem na temat uchwytów, ale tak na prawdę w 100% nie byłem pewien czym one są. Ale i tak największy problem miałem właśnie z jego znalezieniem.

0

Nie opowiadaj... Wpisz sobie w wyszukiwarce Wikipedii słówko uchwyt, a zobaczysz listę artykułów, w której na pierwszej pozycji (są dwie) masz artykuł Uchwyt (informatyka); Oczywiście w polskiej wersji niewiele ktoś napisał, ale już w angielskiej jest więcej i sensowniej - Handle (computing);

Jak widzisz znaleźć coś jest łatwo - trzeba tylko wiedzieć jak zapytać o to Google/Wikipedię;


A jeśli chodzi o same uchwyty, to nie służą one tylko i wyłącznie do identyfikowania okien czy komponentów; Są jeszcze np. uchwyty do bibliotek (Lazarusowy specjalny typ LibHandle), uchwyty do bitmap (HBITMAP), ikon (HICON), palet (HPALETTE), kursorów (HCURSOR), kontekstów urządzeń (HDC), plików (HFILE), fontów (HFONT) i mnóstwa, mnóstwa innych rzeczy, których uchwyty zawierają prefiks H;

To tylko liczby, więc równie dobrze mógłby być jeden typ liczb naturalnych dla ich wszystkich, ale ze względu na czytelność jest ich aż tyle (i dobrze).

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