![]() |
Image (Canvas) Rückgängig Funktion
Hallo Delphi-PRAXIS,
ich möchte eine Rückgängig und Wiederholen Funktion für mein Image programmieren. Ich habe mir das so gedacht: - Inhalt des Canvas auf einem Bitmap vor jeder Änderung(zeichnen) speichern, sodass es immer eine Kopie vom letzten Schritt gibt
Delphi-Quellcode:
- Inhalt des Bitmaps auf das Canvas kopieren um den letzten Zustand wiederherzustellen
procedure ZustandSpeichern(Image: TImage; var bmp: TBitmap);
begin BitBlt(bmp.Canvas.Handle,0,0,bmp.Width,bmp.Height,Image.Canvas.Handle,0,0,SRCCOPY); end;
Delphi-Quellcode:
Noch funktioniert aber gar nichts...
procedure ZustandLaden(Image: TImage; bmp: TBitmap);
begin BitBlt(Image.Canvas.Handle,0,0,Image.Width,Image.Height,bmp.Canvas.Handle,0,0,SRCCOPY); end; Was mache ich falsch? Letztendlich wird dann eh nur 1 Schritt zurück funktionieren, das ist schade. Hat jemand vielleicht eine bessere Idee? FG Dunkelbunt |
AW: Image (Canvas) Rückgängig Funktion
geht schon, wenn die Bitmapgröße passt und Du nach dem wiederherstellen Invalidate aufrufst...
Delphi-Quellcode:
je nachdem was auf Deinem Bitmap gemalt wird könntest Du die Arbeitschritte auch in einer Liste speichern und gegf. alle Schritte auf einem leeren Image bis zu gewünschten Zustand wiederholen...
procedure TForm1.Button1Click(Sender: TObject);
begin if not Assigned(Fbmp) then Fbmp := TBitmap.create; FBMP.Width := Image.Width; Fbmp.Height := Image.Height; ZustandSpeichern(Image,FBMP); end; procedure TForm1.Button2Click(Sender: TObject); begin ZustandLaden(Image,FBMP); Image.Invalidate; end; |
AW: Image (Canvas) Rückgängig Funktion
Okay, danke, das funktioniert schonmal.
Jetzt habe ich nurnoch das Problem, dass ich den Zustand meines Arrays speichern muss, da jedem Punkt ein Zeichen zugewiesen ist und bei jedem neuzeichnen das rückgängig gemachte Zeichen wieder erscheint. ... Im Prinzip ist es sogar unsinnig das Canvas rückgängig zu machen... wenn ich einfach das Array einen Schritt zurücksetze und dann neuzeichne kommt es aufs selbe.... Edit: Wie würde es denn mit der Liste funktionieren? Warum genau muss Invalidate benutzt werden? FG Dunkelbunt |
AW: Image (Canvas) Rückgängig Funktion
Sorry, ich habe nicht verstanden was Du meinst...
|
AW: Image (Canvas) Rückgängig Funktion
Okay,
zum Array-Rückgängigmachen probiere ich ersteinmal selber bis ich eine konkrete Frage habe. Zitat:
Zitat:
Delphi-Quellcode:
Wie löst man das von der Schreibung her, wenn es ein mehrdimensionales Array ist? Delphi möchte nur ein eindimensionales Array in meinem Prozedurkopf sehen, also z.B. FestArray: array of TBeipsielarray.
procedure ZustandSpeichern(var DestArray: [B]array of array of TBeispielarray[/B]; SrcArray: array of array of TBeispielarray);
begin DestArray := SrcArray; end; FG Dunkelbunt EDIT: Ich möchte den Inhalt meines (dynamischen) Arrays zwischenspeichern. Wäre DestArray := SrcArray; überhaupt möglich? Delphi zeigt inkompatible Typen an. |
AW: Image (Canvas) Rückgängig Funktion
Zitat:
Mit dem Zustandsarray haben wir uns missverstanden. Wenn Du alle Zwischenstände als Bitmaps speichern möchtest nimm ein Objektliste mit Bitmaps. Meine Idee war eher die Arbeitsschritte wie auch immer Codiert in eine Liste speichern, quasi ein Macrorecorder.
Delphi-Quellcode:
etc...
image.canvas.Pixels[10,10]:= clred; //>> P,10,10,$ff
image.canvas.MoveTo(30,30);//>>M,30,30 image1.canvas.Ellipse(10,10,100,100);// E,10,10,100,100 Ob das für Deine Zwecke brauchbar ist kann ich nicht beurteilen, da ich nicht weiß was Du machst... |
AW: Image (Canvas) Rückgängig Funktion
Danke,
mein Fehler, ich war am Anfang auf dem falschen Weg. Für meine Zwecke ist es denke ich sinnvoller den Inhalt meines Arrays zu speichern. Meine Frage ist jetzt wie ich das Array:
Delphi-Quellcode:
am Besten auf ein Speicherarray kopiere.
type
TBeispiel = record Zeichen : char; attribut : byte; end; var buchstabe : array of array of TBeispiel; |
AW: Image (Canvas) Rückgängig Funktion
Was meinst du mit Speicherarray?
|
AW: Image (Canvas) Rückgängig Funktion
Ich hab mir gedacht einfach ein gleichgroßes Array zu benutzen, etwa so:
Speicherarray : array of array of TBeispiel; Wenn das funktionieren sollte. |
AW: Image (Canvas) Rückgängig Funktion
Merk dir doch einfach den höchsten Index, dann ist eine Kopie überflüssig.
|
AW: Image (Canvas) Rückgängig Funktion
Das funktioniert in meinem Fall nicht. Es ist jeder Platz belegt. Ich habe praktisch ein Raster, dessen Werte und Eigenschaften der Felder in dem dynamischen, mehrdimensionalen Array gespeichert sind.
Ich hab es jetzt so:
Delphi-Quellcode:
Vor dem Zeichnen bzw verändern von Werten in den Feldern wird ZustandSpeichern ausgeführt.
procedure ZustandSpeichern;
var i,n : integer; begin for i := 1 to MaxZeilen do for n := 1 to MaxSpalten do begin SpeicherArray[i,n].zeichen := buchstabe[i,n].zeichen; SpeicherArray[i,n].attribut := buchstabe[i,n].attribut; end; end; procedure ZustandLaden; var i,n : integer; begin for i := 1 to MaxZeilen do for n := 1 to MaxSpalten do begin buchstabe[i,n].zeichen := SpeicherArray[i,n].zeichen; buchstabe[i,n].attribut := SpeicherArray[i,n].attribut; end; end;
Delphi-Quellcode:
Das sollte funktionieren, tut es aber nicht...
procedure TForm1.UndoClick(Sender: TObject);
begin ZustandLaden; end; |
AW: Image (Canvas) Rückgängig Funktion
Achtung: dynamische Arrays beginnen bei Index 0
|
AW: Image (Canvas) Rückgängig Funktion
Meins nicht =)
Ich habe alles um 1 nach rechts verschoben, damit es später logischer nachzuvollziehen ist. |
AW: Image (Canvas) Rückgängig Funktion
Du belegst vielleicht den 1. Index nichtnicht.
Hast du den Array dann entsprechend größer gemacht? |
AW: Image (Canvas) Rückgängig Funktion
Ja, das mit dem Array ist alles richtig, da bin ich mir sicher.
Delphi-Quellcode:
SetLength(Beispiel,MaxZeilen+1,MaxSpalten+1);
|
AW: Image (Canvas) Rückgängig Funktion
Was macht dein Code bzw. nicht?
|
AW: Image (Canvas) Rückgängig Funktion
Also kurz zur Erklärung:
Ich klicke ein Feld an, tippe einen Buchstaben und der belegt dann den dazugehörigen Platz im Array. Wenn ich dazu noch dem Feld eine Eigenschaft zuweise, dann wird im Array die Eigenschaft vermerkt (Bsp: feld[1,1].zeichen := 'A'; feld[1,1].attribut := clblue; ). Ich möchte bevor auf Tastendruck dem eigentlichen Array "feld" das zeichen zugewiesen wird, das Array erst kopieren in "Speicherarray" und dann erst das "feld" Array beschreiben. Dann möchte ich auf Tastendruck die Werte des "feld" Arrays mit denen des "Speicherarrays" überschreiben um den Zustand vor der letzten Taste herzustellen. Schreiben, Eigenschaften und alles andere funktioniert. Was nicht funktioniert ist die gesuchte Undo-Funktion. EDIT: Mit Copy(Array) kann man ein Array kopieren. Dies geht aber nur mit eindimensionalen Arrays, ich möchte ein zweidimensionales Array kopieren. Kann mir da wer helfen? |
AW: Image (Canvas) Rückgängig Funktion
Delphi-Quellcode:
Bei jeder neue Zuweisung
type
TChronologieElement = record Zeichen : char; attribut : byte; X : Integer; Y : Integer; end; var TChronologieArray : array of TChronologieElement;
Delphi-Quellcode:
damit hast Du alle Informationen um jeden Zeitpunkt wieder herzustellen, bei minimalem Speicheraufwand...
SetLength(TChronologieArray ,High(TChronologieArray ) + 2);
i := High(TChronologieArray); TChronologieArray[i].Zeichen := ... ..etc |
AW: Image (Canvas) Rückgängig Funktion
Hallo Bummi,
danke, das klingt super. Wenn ich immer nur den höchsten Wert kopieren, werden dann immer alle vorherigen Werte mitkopiert? Würde das kopieren dann so aussehen:
Delphi-Quellcode:
Ist das beides denn überhaupt "kompatibel", denn das TChronologieArray ist ja eindimensional.
SetLength(TChronologieArray ,High(TChronologieArray ) + 2);
i := High(TChronologieArray); TChronologieArray[i].Zeichen := Beispiel[i].Zeichen; TChronologieArray[i].attribut := Beispiel[i].attribut; ..etc Zitat:
Zitat:
Funktioniert das auch mit SetLenght nur von ChronologieArray auf BeispielArray? FG Dunkelbunt |
AW: Image (Canvas) Rückgängig Funktion
Irgendwie denken und reden wir aneinander vorbei, ich hatte Dich so verstanden du Du nacheinander Änderungen an "Zellen" vornimmst (Char und Farbe).
Wenn Du jede Änderung mitprotokollierst und Deinen Ausgangszustand kennst kannst Du entweder beim Rückgängig machen vom Ausgangszustand alle Schritte bis zu diesem gewünschten Punkt "abspielen" oder von Hinten jeweils den "Vorgängerzustand" der zurückzunehmenden Zelle suchen und diesen wiederherstellen. Möglicherweise habe ich Dich aber auch ganz falsch verstanden.... |
AW: Image (Canvas) Rückgängig Funktion
Also, das mit den Zellen ist richtig, man kann es auch einfach so sehen, die Zellen bilden ein Raster und du schreibst praktisch einen Text, indem in ejde Zelle ein Buchstabe kommt. Mit der Rückgängig funktion wollte ich versuchen, den aktuellen zustand vor jeder Änderung abzuspeichern, bestenfalls so, dass man mehrere Schritte abspeichert.
Da in einem einzigen mehrdimensionalen Array die Daten für alle Zellen gespeichert sind, müsste ich eigentlich nur wissen, wie ich ein Array verdoppeln kann. Die Copy(Array) funktion funktioniert nur bei eindimensionalen Arrays, leider. Ich möchte also einfach ein komplettes Array auf ein anderes Übertragen: Bsp: ArraySpeicher := ArrayA -ArrayA wird verändert - Die Veränderung gefällt nicht => ArrayA := ArraySpeicher nur geht das ja nicht so einfach mit Gleichsetzen. Es geht also nicht nur eine einzige Zelle rückgängig zu machen, da es wie in einem zusammenhängenden Text nichts bringt nur den ertsen Buchstaben rückgängig zu machen, denn dort stand ja vor dem schreiben nichts. (Ich hoffe ich verwirre nicht zu sehr) |
AW: Image (Canvas) Rückgängig Funktion
Du kannst freilich Array's kopieren
Delphi-Quellcode:
// Fixe Array's völlig unproblematisch
procedure TForm1.Button1Click(Sender: TObject); type r=Record c:Char; f:Byte; end; TArr=array[0..9,0..9] of r; var a,b:TArr; i,j:Integer; begin for I := 0 to 9 do for j := 0 to 9 do begin a[i,j].c := Char(Ord('0') + i*10 + j) ; a[i,j].f := i*10 + j; end; b := a; Caption := b[2,2].c +'-'+ IntToStr(b[2,2].f) end; // Asymetrische Array, eben Iterativ procedure TForm1.Button2Click(Sender: TObject); type r=Record c:Char; f:Byte; end; TArr = Array of Array of r; var a,b:TArr; i,j:Integer; Function CopyMyArray(a:TArr):TArr; var x,y:Integer; begin SetLength(Result,High(a) + 1); for x := Low(a) to High(a) do begin SetLength(Result[x],High(a[x])+1); for y := Low(a[x]) to High(a[x]) do Result[x,y] := a[x,y]; end; end; begin SetLength(a,10); for I := Low(a) to High(A) do begin SetLength(a[i],i); for j := Low(a[i]) to High(a[i]) do begin a[i,j].c := Char(Ord('0') + i*10 + j) ; a[i,j].f := i*j; end; end; b := CopyMyArray(a); if (a[4,2].c=b[4,2].c) and (a[4,2].f=b[4,2].f) then Showmessage('Passt scho...') end; Trotzdem bin ich sicher dass Dir meine eigentlich Struktur langt und den Speicherbedarf minimiert. Wenn jedes Zeichen erfasst wird und der Benutzer nach 100 Schritten zu Schritt 98 zurückkehren will, leerst Du Dein Grundarray und spielst die Aufzeichnungen bis Schritt 98 wieder ein ... und so weiter... |
AW: Image (Canvas) Rückgängig Funktion
Zitat:
Wenn man das also z.B: wie in jedem Texteditor (als Beispiel) mit einer Rückgängigtaste auslöst, dann leere ich wie du egsagt hast das Array und spiele die Schritte wieder auf... Das ist wenn man genau drübernachdenkt richtig raffiniert... =D Danke auch für die ArrayKopier funktionen. Ich denke damit komme ich weiter! Vielen Dank nochmal und FG Dunkelbunt |
AW: Image (Canvas) Rückgängig Funktion
Hallo nochmal,
ich bin noch etwas unsicher und frage lieber nochmal nach. Entschuldigt die dumme Frage. Zitat:
Wie spiele ich dann die Schritte 1-98 wieder auf? EDIT: (Ich komme da total durcheinander, weil ich ja X und Y Werte für die Buchstaben habe... aber ich denke Du oder jemand anders kann mir da weiterhelfen =D) Ich hab es so gemacht, doch es ist irgendwo was falsch:
Delphi-Quellcode:
Bei jeder Veränderung:
var Chronozaehler : integer = 0;
Delphi-Quellcode:
Auf Druck der Undo-Taste:
SetLength(TChronologieArray ,High(TChronologieArray )+2); //warum eigentlich +2? Nicht +1?
Chronozaehler := High(TChronologieArray); TChronologieArray[Chronozaehler].attribut := buchstabe[PositionZeile,PositionSpalte].attribut; TChronologieArray[Chronozaehler].X := PositionZeile; TChronologieArray[Chronozaehler].Y := PositionSpalte;
Delphi-Quellcode:
Ich finde es funktioniert auch recht gut, aber unerklärlicherweise rutscht bei ersten Undo einfach alles eine Spalte nach rechts und der letzte Buchstabe verschwindet, also:
if Chronozaehler > 0 then
Chronozaehler := Chronozaehler-1; for i := 0 to AnzahlZeilen do for n := 0 to AnzahlSpalten do begin buchstabe[i,n].Zeichen := ' '; buchstabe[i,n].attribut := 0; end; for i := Low(TChronologieArray) to Chronozaehler do buchstabe[TChronologieArray[i].X,TChronologieArray[i].Y].Zeichen := TChronologieArray[i].Zeichen; PositionZeile := TChronologieArray[Chronozaehler].X; PositionSpalte := TChronologieArray[Chronozaehler].Y; aus Hallo[] wird _Hall[]. ([] = Cursor) Beim zweiten mal ist es dann so wie es soll. Aus _Hall[] wird _Hal[] ... FG Dunkelbunt |
AW: Image (Canvas) Rückgängig Funktion
Liste der Anhänge anzeigen (Anzahl: 1)
Hi, möglicherweise beginnen Deine Indizes bei 1 statt bei 0, der folgende Teil müsste in jedem Fall Ärger machen
Delphi-Quellcode:
bei z.B. 5 Zeilen greift Du zu von 0..5 also auf 6 Elemente.
for i := 0 to AnzahlZeilen do
for n := 0 to AnzahlSpalten do buchstabe[i,n].... Ich habe einen kleinen Anhang erstellt, mit Ersatzweise einem Stringgrid und String statt Zeichen. Das Prinzip bleibt ähnlich. |
AW: Image (Canvas) Rückgängig Funktion
Danke für das Beispiel Bummi.
Ich habe es probiert, doch es gibt ständig Komplikationen. Ich hab noch eine andere Idee für die Rückgängigfunktion und lass diese Idee erstmal auf der Strecke. Trotzdem ist es ziemlich nützlich zu wissen wie man eine solche Anlegen kann unabhängig von der Wirkung die man erreichen will. FG Dunkelbunt |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:34 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 by Thomas Breitkreuz