![]() |
Durch ein Bild "hindurchklicken"? Darunterliegende
Hallo.
Ich habe mehrere Images, die dynamisch angelegt wurden auf einem Form. Ich weiß nicht, welches der Images vorne und welches hinten liegt. Klickt der Benutzer ein Image an, soll ein Ereigniss statt finden, dass alle darunterliegende Images zusätzlich betrifft. Wie mache ich das? Alle Bilder haben natürlich das gleiche OnMouseDown(x, y) Ereigniss. Am schönsten wäre es, wenn ich herausfinden könnte, welche Bilder an der Position (x, y) zu finden seien. Wäre das gar nicht möglich, könnte ich alle Bilder in einen Array legen und diese dann alle nacheinander prüfen, ob diese die Koordinate beinhalten und dann ein künstliches OnMouseDown(x, y) erzeugen. Wäre aber nicht so eine dolle Lösung, oder? Gruß blackdrake |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
ich denke du wirst nicht drumherumkommen, ein array zu verwenden, wenn du unbedingt TImages verwenden willst. Du kannst natürlich die bilder auch selbst zu malen, die Abfrage wäre aber ähnlich...
Gruß Frank |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
also die unperformante variante (für die mich hier auch viele schlagen werden, weil FindComponent im Spiel ist ^^) wäre:
wenn ein OnClick auftritt, anfangen alle images nacheinander über findcomponent suchen und schaun über X und Y und GetCursorPos usw. ob das image darunter liegt...Dann das event für die auch auslösen...andere variante: array anlegen mit referenzen auf alle Images, dann kannst du statt findcomponent auch den array durchlaufen (imho besser). Hope that helps. MfG |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Hallo.
Es ist scheinbar ein größeres Problem als ich dachte. Ich habe jetzt ersteinmal die unperformante Variante verwendet, aber statt den Array einfach mal Components[] auf alle TImages durchgeprüft. Doch nun habe ich ein weiteres Problem erkannt: Ich kann die Z-Reihenfolge zwar festlegen, aber nicht lesen. Wie kann ich es realisieren, dass der Klick durch ein Image hindurchgeht (das Event soll an das nächst Untere Element gelangen), wenn ein Kriterium nicht erfüllt ist? Kann ich da irgendwas mit einer eigenen Komponente bewirken? Gruß blackdrake |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
die z-ordner müsste die gleiche reihenfolge sein, wie es in dem Components-array steht...ich habs mal getested...die komponenten werden in dieser reihenfolge erstellt/gezeichnet. also das erste Element (Index 0) ist das unterste.
einfacher Test mit Panels und TImages...bei allen controls die obere Event-Methode...
Delphi-Quellcode:
zusätzlich ein button, welcher das Components-array anzeigt. die reihenfolge wird da drin auch geändert.
procedure TForm1.Panel1Click(Sender: TObject);
begin (Sender as TControl).BringtoFront; end; procedure TForm1.Button1Click(Sender: TObject); var i:integer; begin memo1.clear; for i:=0 to ControlCount-1 do memo1.lines.Add(Controls[i].Name); end; HTH Frank |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Hallo.
Vielen Dank für den Hinweis! Ich werde es nochmal testen, wie es sich bei dynamisch angelegten Komponenten verhält, aber es sieht ja so richtig gut aus. Hätte ich nicht gedacht, dass Delphi intern den Array nach der Z-Order neu berechnet. Gruß blackdrake |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Hallo.
Ich habe gestern versucht, die Components[] Reihenfolge selbst zu testen. Leider repräsentiert Components[] nicht der Z-Order! Ändere ich die Z-Reihenfolge zur Entwurfszeit, ist Components[] geordnet. Ändere ich die Reihenfolge in der Laufzeit durch BringToFront, wird die Components[] nicht neu geordnet. Gruß blackdrake |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
nicht Components[i] sondern Controls[i].
sichtbare Komponenten sind TControls, von daher nimm mal das...siehe auch mein Beispiel oben. bei mir hats die Reihenfolge in der Liste beim ändern der Z-Order mit geändert. Gruß Frank |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Hallo.
Entschuldige, ich hatte den Beitrag falsch im Gedächtnis, als ich getestet habe. Controls[] habe ich ausführlich getestet und es repräsentiert die Z-Order exakt. Ich bin derzeit dabei, eine VCL zu schreiben, die vom TImage abgeleitet ist und eine Klick-Maske darstellt (sichtbar oder unsichtbar). Klickt der Benutzer ins Transparente, wird der Klick/Drag/Drop automatisch an die unterliegenden Elemente weitergeleitet (ganz schön viel arbeit...). Dazu natürlich frei definierbare Curser (pl. Cursor), die natürlich ebenfalls nach oben hin durchgeleitet werden müssen. Ich bin bei ca. 80% angelangt. Nun habe ich ein etwas kleineres Problem: Ich fände es echt klasse, wenn ein Click-Ereigniss nicht nur an darunterliegende Click-Masken, sondern auch auf X-beliebige Controls durchgeleitet werden könnte. Das selbe für Drag/Drop-Ereignisse. Nun das Problem: "Click" ist zwar bereits im TControl, aber protected. Ohne Angabe eines konkreten Derivats, das "Click" freischaltet, kann ich nicht drauf zurückgreifen. Bedeutet: TControl(darunterliegend).Click; geht nicht. Habe ich damit keine Change, das zu realisieren? Gruß blackdrake |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
ich verstehe zwar nicht genau, was du realisieren willst, aber evtl ist es einfacher, mit regions zu arbeiten.
mittels regions kannst du controls ausschneiden. wenn du das z.b. auf TPanel anwendest, kannst du bestimmte bereiche des TPanels durchklickbar machen. schau dir z.b. mal den quellcode von TCoolform an, da wird ein TForm mittels Maskenbitmap zugeschnitten...das gleiche mit TPanels ist evtl das, was du suchst. HTH Frank |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Hallo.
Ich habe nichts zu dem Thema "Regions" außer einer kostenpflichtigen VCL bei Torry gefunden. Wo finde ich weitere Informationen zu Panels, die eine Maske besitzen? Ich werde mir mal das Coolform anschauen, doch ich befürchte, solche Masken gelten nur für Forms ( ![]() Gruß blackdrake |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Hi blackdrake,
mit regions kannst du die Transparenten Teile deiner Controls "durchklickbar" machen. Das gilt nicht nur für Forms. Klappt bei jedem WinControl. Such mal hier nach regions und du wirst ne Menge Beiträge dazu finden. Gruß oki |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
ein Beispiel für bitmap-gesteuerte regions:
![]() HTH Frank |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Hallo.
Vielen Dank für den Hinweis. Ich habe ein paar Informationen gefunden, jedoch ist es mit den Regions ziemlich umständlich. Ich habe jetzt folgende Komponente, die jedoch extrem unperformant ist. Die Pixels werden abgeglichen und ggf. zu langgezogenen Rects zusammengefasst, die dann mit CombineRgn vereinigt werden. Das ist aber bereits bei meiner Beispielgrafik mit 300x200 Pixeln bei 7 Sekunden Berechnungszeit inakzeptabel. Gibt es irgendeine Lösung dafür?
Delphi-Quellcode:
Gruß
type
TTransClickMask = class(TCustomControl) private FImage: TImage; FPicture: TPicture; procedure SetPicture(Value: TPicture); procedure MakeTransparent; // procedure DestroyTransparency; procedure PictureChanged(Sender: TObject); protected public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override; published property Picture: TPicture read FPicture write SetPicture; end; { TTransClickMask ... BETA } constructor TTransClickMask.Create(AOwner: TComponent); begin inherited; FImage := TImage.Create(Self); FImage.Parent := Self; FImage.AutoSize := true; FPicture := TPicture.Create; FPicture.OnChange := PictureChanged; FImage.Picture.Assign(FPicture); end; destructor TTransClickMask.Destroy; begin FPicture.Free; FImage.Free; inherited; end; (* procedure TTransClickMask.DestroyTransparency; var Rgn: THandle; begin Rgn := CreateRectRgn(0, 0, Width, Height); SetWindowRgn(Handle, Rgn, true); DeleteObject(Rgn); end; *) procedure TTransClickMask.MakeTransparent; var x,y : integer; rgn1, rgn2 : hrgn; startx,endx : integer; begin // Code entnommen aus der Demo von TCoolForm, mit einem Bugfix // for every line do... rgn1 := 0; for y := 0 to FImage.Picture.BitMap.Height-1 do begin x := -1; repeat // look for the beginning of a stretch of non-transparent pixels while (FImage.Picture.bitmap.canvas.pixels[x,y] = $00FFFFFF) and (x = FImage.Picture.BitMap.width) do begin inc(x); end; startx := x; // look for the end of a stretch of non-transparent pixels inc(x); while (FImage.Picture.bitmap.canvas.pixels[x,y]<>$00FFFFFF) and (x<(*=*)FImage.Picture.BitMap.width) do begin inc(x); end; endx := x; // do we have some pixels? if startx <> FImage.Picture.BitMap.Width then begin // do we have a region already? if rgn1 = 0 then begin // Create a region to start with rgn1 := CreateRectRgn(startx+1,y,endx,y+1); end else begin // Add to the existing region rgn2 := CreateRectRgn(startx+1, y, endx, y+1); if rgn2 <> 0 then CombineRgn(rgn1, rgn1, rgn2, RGN_OR); DeleteObject(rgn2); end; end; until x >= FImage.Picture.BitMap.width - 1; end; SetWindowRgn(Handle, Rgn1, true); DeleteObject(Rgn1); end; procedure TTransClickMask.PictureChanged(Sender: TObject); begin // DestroyTransparency; FImage.Picture.Assign(FPicture); Width := FPicture.Width; Height := FPicture.Height; MakeTransparent; end; procedure TTransClickMask.SetBounds(ALeft, ATop, AWidth, AHeight: Integer); begin if AWidth <> FPicture.Width then AWidth := FPicture.Width; if AHeight <> FPicture.Height then AHeight := FPicture.Height; inherited; end; procedure TTransClickMask.SetPicture(Value: TPicture); begin FPicture.Assign(Value); end; blackdrake |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Liste der Anhänge anzeigen (Anzahl: 1)
Hi,
ich find den Thread im Moment nicht, aber ich hatte mir den code seinerzeit auf die Platte geholt. Hier mal für dich zum Testen. ich hatte auch noch ein Beispiel mit "Pixelauswertung". ich such noch mal. Gruß oki |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Liste der Anhänge anzeigen (Anzahl: 1)
Auch hier hab ich aktuell nur den Code gefunden. Vielleicht finde ich auch noch den Thread.
Gruß oki |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo.
Vielen Dank für die Beispiele. Bei dem FormDemo habe ich noch einen Bugfix machen müssen, damit auch 1-Pixel-Breite Linien dargestellt werden können:
Delphi-Quellcode:
Bei dem FrmDemo, das mit ScanLines arbeitet, geht es jetzt schon viel schneller. Natürlich ist es je nach Grafik unterschiedlich. Hier mein aktueller Testlauf:
Excl := CreateRectRGN(StartX, Y, X (** Bugfix: hier war +1 **), Y + 1);
Testcomputer: 500 MHz; 2 Durchläufe innerhalb Debugger, gemessen mit Now+TimeToStr+ShowMessage. Referenz-Testgrafik siehe Anhang. Modifizierter TCoolForm Code (siehe oben) --> 23 Sekunden FrmDemo --> 4 Sekunden Tranform --> (hat ja gar nichts mit BMP-Masken zu tun) * 4 Sekunden sind hingegen trotzdem ein bisschen lahm, auch wenn ich zugeben muss, dass meine Referenzgrafik darauf optimiert ist, den Code auszubremsen. Gibt es denn keine andere Möglichkeit als CreateRectRgn+CombineRgn z.B. mit Assembler oder API-Varianten, bei denen man die BMP direkt übergibt? Was ich bei den Scanlines aber noch nicht ganz verstehe, ist die Implementierung im FrmDemo. In der Delphi-Hilfe steht im Beispiel PByteArray. Dieser ist in Delphi definiert als:
Delphi-Quellcode:
In deinem Beispiel ist die Verwendung:
type
PByteArray = ^TByteArray; TByteArray = array[0..32767] of Byte; var P: PByteArray; begin P := Bmp.ScanLine[i]; end;
Delphi-Quellcode:
* Seltsam:
type
PRGBTriple = ^TRGBTriple; {$EXTERNALSYM tagRGBTRIPLE} tagRGBTRIPLE = packed record rgbtBlue: Byte; rgbtGreen: Byte; rgbtRed: Byte; end; TRGBTriple = tagRGBTRIPLE type TRGBArray = array[0..32767] of TRGBTriple; PRGBArray = ^TRGBArray; var P: PRGBArray; begin P := Bmp.ScanLine[i]; end; - Im Delphi-Beispiel wird ScanLine[] in ein 32767x1 Byte großen Typ gepackt - Im FormDemo-Beispiel wird ScanLine[] in ein 32767x3 Byte großen packed Typ gepackt Wird es bei letzterem Probleme geben, weil der Typ 3fach so groß ist? Komisch, dabei ist 32768 (Länge des Arrays) nicht mal durch 3 teilbar... Welche Werte repräsentieren dann die Elemente? Wäre die Reihenfolge RGBRGBRGB... wäre es doch ein Vielfaches von 3. * Wo die Zahl 32767 (2^15-1) herkommt bzw. wer dieses Limit vorschreibt, ist mir ebenfalls rätselhaft und ich weiß nicht, ob es dabei zu Limitationen beim Einlesen von großen BMPs kommen könnte. Gruß blackdrake |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Zitat:
und zwar gibts noch die möglichkeit sich selbst ein canvas zu basteln und anhand diesem die region auszuschneiden. ich hab das damals mal für ein OnScreenDisplay genommen, wo der dargestellte Text ausgeschnitten wurde :) zu finden ist ein Beispielprojekt als OSD.zip auf ![]() Zitat:
Gruß Frank |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
Zitat:
Zitat:
Gruß blackdrake |
Re: Durch ein Bild "hindurchklicken"? Darunterlieg
ich denke mal, das mit dem array of record ist eine reine dimensionierungsgeschichte...
soweit ich mich erinnere, holt man sich ja scanline nur den poiter pro zeile im bitmap, und holt sich nicht einen Pointer, um das ganze Bitmap durchzugehen... d.h. es wären maximal 32767 pixel (bei 3 byte-record) in horizontaler Ausdehnung möglich. sollte reichen, oder? willst du letzteres erreichen (1 Pointer pro bitmap), wäre evtl. ein dynamisches Array sinnvoll, weis aber nicht, ob das mit dem Pointer-Typ damit funktioniert. Gruß Frank |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:28 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz