Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Image (Canvas) Rückgängig Funktion (https://www.delphipraxis.net/159237-image-canvas-rueckgaengig-funktion.html)

Dunkelbunt27 19. Mär 2011 11:07

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:
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;
- Inhalt des Bitmaps auf das Canvas kopieren um den letzten Zustand wiederherzustellen
Delphi-Quellcode:
procedure ZustandLaden(Image: TImage; bmp: TBitmap);
begin
  BitBlt(Image.Canvas.Handle,0,0,Image.Width,Image.Height,bmp.Canvas.Handle,0,0,SRCCOPY);
end;
Noch funktioniert aber gar nichts...
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

Bummi 19. Mär 2011 12:36

AW: Image (Canvas) Rückgängig Funktion
 
geht schon, wenn die Bitmapgröße passt und Du nach dem wiederherstellen Invalidate aufrufst...

Delphi-Quellcode:
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;
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...

Dunkelbunt27 19. Mär 2011 12:52

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

Bummi 19. Mär 2011 12:57

AW: Image (Canvas) Rückgängig Funktion
 
Sorry, ich habe nicht verstanden was Du meinst...

Dunkelbunt27 19. Mär 2011 13:07

AW: Image (Canvas) Rückgängig Funktion
 
Okay,
zum Array-Rückgängigmachen probiere ich ersteinmal selber bis ich eine konkrete Frage habe.

Zitat:

Arbeitschritte auch in einer Liste speichern
Wie funktioniert das?
Zitat:

Image.Invalidate;
Warum ist das unbedingt nötig?

Delphi-Quellcode:
procedure ZustandSpeichern(var DestArray: [B]array of array of TBeispielarray[/B]; SrcArray: array of array of TBeispielarray);
begin
  DestArray := SrcArray;
end;
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.

FG Dunkelbunt

EDIT: Ich möchte den Inhalt meines (dynamischen) Arrays zwischenspeichern.
Wäre DestArray := SrcArray; überhaupt möglich? Delphi zeigt inkompatible Typen an.

Bummi 19. Mär 2011 13:21

AW: Image (Canvas) Rückgängig Funktion
 
Zitat:

Warum ist das unbedingt nötig?
weil Du an der VCL vorbei auf das Canvas zugreifst.

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:
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
etc...
Ob das für Deine Zwecke brauchbar ist kann ich nicht beurteilen, da ich nicht weiß was Du machst...

Dunkelbunt27 19. Mär 2011 14:48

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:
type
TBeispiel = record
               Zeichen : char;
               attribut : byte;
             end;

var
buchstabe      : array of array of TBeispiel;
am Besten auf ein Speicherarray kopiere.

mkinzler 19. Mär 2011 14:52

AW: Image (Canvas) Rückgängig Funktion
 
Was meinst du mit Speicherarray?

Dunkelbunt27 19. Mär 2011 14:55

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.

mkinzler 19. Mär 2011 14:58

AW: Image (Canvas) Rückgängig Funktion
 
Merk dir doch einfach den höchsten Index, dann ist eine Kopie überflüssig.

Dunkelbunt27 19. Mär 2011 15:03

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:
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;
Vor dem Zeichnen bzw verändern von Werten in den Feldern wird ZustandSpeichern ausgeführt.
Delphi-Quellcode:
procedure TForm1.UndoClick(Sender: TObject);
begin
   ZustandLaden;
end;
Das sollte funktionieren, tut es aber nicht...

mkinzler 19. Mär 2011 15:08

AW: Image (Canvas) Rückgängig Funktion
 
Achtung: dynamische Arrays beginnen bei Index 0

Dunkelbunt27 19. Mär 2011 15:10

AW: Image (Canvas) Rückgängig Funktion
 
Meins nicht =)
Ich habe alles um 1 nach rechts verschoben, damit es später logischer nachzuvollziehen ist.

mkinzler 19. Mär 2011 15:13

AW: Image (Canvas) Rückgängig Funktion
 
Du belegst vielleicht den 1. Index nichtnicht.
Hast du den Array dann entsprechend größer gemacht?

Dunkelbunt27 19. Mär 2011 15:16

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);

mkinzler 19. Mär 2011 15:23

AW: Image (Canvas) Rückgängig Funktion
 
Was macht dein Code bzw. nicht?

Dunkelbunt27 19. Mär 2011 15:31

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?

Bummi 19. Mär 2011 19:29

AW: Image (Canvas) Rückgängig Funktion
 
Delphi-Quellcode:
type
TChronologieElement = record
                    Zeichen : char;
                    attribut : byte;
                    X : Integer;
                    Y : Integer;
             end;

var
TChronologieArray : array of TChronologieElement;
Bei jeder neue Zuweisung
Delphi-Quellcode:
SetLength(TChronologieArray ,High(TChronologieArray ) + 2);
i := High(TChronologieArray);
TChronologieArray[i].Zeichen := ...
..etc
damit hast Du alle Informationen um jeden Zeitpunkt wieder herzustellen, bei minimalem Speicheraufwand...

Dunkelbunt27 20. Mär 2011 15:31

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:
SetLength(TChronologieArray ,High(TChronologieArray ) + 2);
i := High(TChronologieArray);
TChronologieArray[i].Zeichen := Beispiel[i].Zeichen;
TChronologieArray[i].attribut  := Beispiel[i].attribut;
..etc
Ist das beides denn überhaupt "kompatibel", denn das TChronologieArray ist ja eindimensional.
Zitat:

TChronologieArray : array of TChronologieElement;
Und das BeispielArray zweidimensional
Zitat:

Beispiel: array of array of TBeispiel;
Wie erfolgt dann das eigentliche rückgängigmachen?
Funktioniert das auch mit SetLenght nur von ChronologieArray auf BeispielArray?

FG Dunkelbunt

Bummi 20. Mär 2011 18:31

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....

Dunkelbunt27 20. Mär 2011 19:03

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)

Bummi 20. Mär 2011 19:39

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...

Dunkelbunt27 20. Mär 2011 19:49

AW: Image (Canvas) Rückgängig Funktion
 
Zitat:

100 Schritten zu Schritt 98 zurückkehren will, leerst Du Dein Grundarray und spielst die Aufzeichnungen bis Schritt 98 wieder ein
Interessant, jetzt versteh ich erst richtig wie diese Idee anzuwenden ist!
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

Dunkelbunt27 22. Mär 2011 17:18

AW: Image (Canvas) Rückgängig Funktion
 
Hallo nochmal,

ich bin noch etwas unsicher und frage lieber nochmal nach. Entschuldigt die dumme Frage.
Zitat:

leerst Du Dein Grundarray und spielst die Aufzeichnungen bis Schritt 98 wieder ein
Mein Array kann ich doch indem ich den Ausgangszustand herstelle, nicht?
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:
var Chronozaehler : integer = 0;
Bei jeder Veränderung:
Delphi-Quellcode:
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;
Auf Druck der Undo-Taste:
Delphi-Quellcode:
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;
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:
aus Hallo[] wird _Hall[]. ([] = Cursor)
Beim zweiten mal ist es dann so wie es soll.
Aus _Hall[] wird _Hal[] ...


FG Dunkelbunt

Bummi 23. Mär 2011 22:38

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:
for i := 0 to AnzahlZeilen do
    for n := 0 to AnzahlSpalten do
        buchstabe[i,n]....
bei z.B. 5 Zeilen greift Du zu von 0..5 also auf 6 Elemente.

Ich habe einen kleinen Anhang erstellt, mit Ersatzweise einem Stringgrid und String statt Zeichen. Das Prinzip bleibt ähnlich.

Dunkelbunt27 24. Mär 2011 17:25

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 10:26 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz