undo-Funktion - gelöst
undo-Funktion - gelöst
Ich denke gerade darüber nach wie ich am besten eine Rückgängig/Wiederholen- oder Zurück/Vor-Funktion realisieren könnte.
Meine beste Idee ist ein Array und Proceduren die beim erreichen einer Grenze am anderen Ende weiter lesen/schreiben.
An Variablen brächte ich wohl : AktuellePosition , MaximalVor und MaximalZurück.
Allerdings scheint es mir sehr schwierig wenn der Anwender nach zehn Schritten z.B. fünf zurück geht und dann drei neue Schritte vor macht.
Hat da jemand eine bessere Idee, einen Link oder vielleicht schon eine fertige Lösung dafür?
Ich frage lieber bevor ich mich da rein stürze. Vielleicht ist ja der Ansatz schon falsch.
Nach "FiFo" und "undo" habe ich schon erfolglos gesucht.
Meine beste Idee ist ein Array und Proceduren die beim erreichen einer Grenze am anderen Ende weiter lesen/schreiben.
An Variablen brächte ich wohl : AktuellePosition , MaximalVor und MaximalZurück.
Allerdings scheint es mir sehr schwierig wenn der Anwender nach zehn Schritten z.B. fünf zurück geht und dann drei neue Schritte vor macht.
Hat da jemand eine bessere Idee, einen Link oder vielleicht schon eine fertige Lösung dafür?
Ich frage lieber bevor ich mich da rein stürze. Vielleicht ist ja der Ansatz schon falsch.
Nach "FiFo" und "undo" habe ich schon erfolglos gesucht.
Zuletzt geändert von uweb am 25.09.2009 19:31, insgesamt 1-mal geändert.
- ts-soft
- Beiträge: 22292
- Registriert: 08.09.2004 00:57
- Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel - Wohnort: Berlin
Re: undo-Funktion
Falls es für das EditorGadget oder Scintilla sein sollte, dort ist sowas nativ schon integriert.
- Fluid Byte
- Beiträge: 3110
- Registriert: 27.09.2006 22:06
- Wohnort: Berlin, Mitte
Re: undo-Funktion
Das kommt auf die Anwendung an. Was für ein Programm bastelst du denn grad'?Hat da jemand eine bessere Idee, einen Link oder vielleicht schon eine fertige Lösung dafür?
Windows 10 Pro, 64-Bit / Outtakes | Derek
Re: undo-Funktion
Danke für die schnellen Antworten.
Im Moment stehe ich wieder ganz am Anfang und muß halt wieder kleine Brötchen backen.

Ich hätte vermutet, daß es da eine universelle Datenstruktur bzw einen universellen Algorithmus gibt.
Welche Lösungen gibt es denn? Worin unterscheiden die sich?
edit
code leicht verändert
& Ich habe eben festegestellt, daß es schon schwierig ist LastFolder.s zu pflegen wenn das Verzeichnis über das ExplorerListGadget gewechselt wird.
Code: Alles auswählen
WindowWidth = 640
WindowHeight = 480
Distance = 10
ToolBarHeight = 10
ComboBoxHeight = 20
Enumeration ; Windows & StatusBars
#myWindow
#myStatusBar
EndEnumeration
Enumeration ; Gadgets
#myComboBoxGadget
#myExplorerTreeGadget
#myExplorerListGadget
#mySplitterGadget
EndEnumeration
WindowFlags | #PB_Window_SizeGadget
WindowFlags | #PB_Window_SystemMenu
WindowFlags | #PB_Window_ScreenCentered
Macro ComboBoxY
ToolBarHeight+Distance
EndMacro
Macro BodyY
ToolBarHeight+ComboBoxHeight+2*Distance
EndMacro
Macro BodyHeight
WindowHeight(#myWindow)-(BodyY+StatusBarHeight(#myStatusBar)+Distance)
EndMacro
If OpenWindow(#myWindow, #PB_Ignore, #PB_Ignore, WindowWidth, WindowHeight, "Menü Test", WindowFlags)
CreateStatusBar(#myStatusBar, WindowID(#myWindow))
AddStatusBarField(WindowWidth/2)
AddStatusBarField(#PB_Ignore) ; Größe dieses Feldes automatisch festlegen
ComboBoxGadget(#myComboBoxGadget, 0, ComboBoxY, WindowWidth(#myWindow), ComboBoxHeight, #PB_ComboBox_Editable)
ExplorerTreeGadget(#myExplorerTreeGadget, 0, BodyY, 0, 0, "*.wf;*.wfx",#PB_Explorer_AlwaysShowSelection|#PB_Explorer_AutoSort)
ExplorerListGadget(#myExplorerListGadget, 0, BodyY, 0, 0, "C:\QMSystems\", #PB_Explorer_AlwaysShowSelection|#PB_Explorer_FullRowSelect|#PB_Explorer_MultiSelect)
SplitterGadget(#mySplitterGadget, 0, BodyY, WindowWidth(#myWindow), BodyHeight, #myExplorerTreeGadget, #myExplorerListGadget, #PB_Splitter_Vertical|#PB_Splitter_Separator)
StatusBarText(#myStatusBar, 0, "Area 0")
StatusBarText(#myStatusBar, 1, "Area 1")
LastFolder.s = GetGadgetText(#myExplorerListGadget)
SetGadgetText(#myComboBoxGadget,LastFolder.s)
SetGadgetText(#myExplorerTreeGadget,LastFolder.s)
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Gadget
Select EventGadget()
Case #myExplorerTreeGadget ; EventGadget()
Select EventType()
Case #PB_EventType_Change ; #myExplorerTreeGadget - EventType()
StatusBarText(#myStatusBar, 0, LastFolder.s)
LastFolder.s = GetGadgetText(#myExplorerTreeGadget)
StatusBarText(#myStatusBar, 1, LastFolder.s)
SetGadgetText(#myComboBoxGadget,LastFolder.s)
SetGadgetText(#myExplorerListGadget,LastFolder.s)
EndSelect
Case #myExplorerListGadget ; EventGadget()
Select EventType()
Case #PB_EventType_Change ; #myExplorerListGadget - EventType()
If LastFolder.s <> GetGadgetText(#myExplorerListGadget)
StatusBarText(#myStatusBar, 0, LastFolder.s)
LastFolder.s = GetGadgetText(#myExplorerListGadget)
StatusBarText(#myStatusBar, 1,LastFolder.s)
SetGadgetText(#myComboBoxGadget,LastFolder.s)
SetGadgetText(#myExplorerTreeGadget,LastFolder.s)
EndIf
EndSelect
EndSelect
Case #PB_Event_SizeWindow ; WindowEvent()
ResizeGadget(#mySplitterGadget, #PB_Ignore, #PB_Ignore, WindowWidth(#myWindow), BodyHeight) ; Our 'master' splitter gadget
; die beiden im SplitterGadget liegenden Gadgets benötigen kein Resize
; das erledigt das SplitterGadget automatisch.
ResizeGadget(#myComboBoxGadget, #PB_Ignore, #PB_Ignore, WindowWidth(#myWindow), #PB_Ignore)
EndSelect
Until Event = #PB_Event_CloseWindow
EndIf
End

Ich hätte vermutet, daß es da eine universelle Datenstruktur bzw einen universellen Algorithmus gibt.
Welche Lösungen gibt es denn? Worin unterscheiden die sich?
edit
code leicht verändert
& Ich habe eben festegestellt, daß es schon schwierig ist LastFolder.s zu pflegen wenn das Verzeichnis über das ExplorerListGadget gewechselt wird.
Re: undo-Funktion
FiFo (First In, First Out) beschreibt ja das Prinzip der Schlange. Ich würde hingegen eine Undo-Funktion mit Hilfe der Datenstruktur Stack (= Stapel) realisieren (Last In, First Out). Das ist eigentlich logisch: Wenn man etwas rückgängig machen möchte, braucht man zunächst wieder das, was man zuletzt gemacht hatte. Technisch kann man einen Stapel in PB in Form eines Arrays oder einer Linked List implementieren.uweb hat geschrieben:Nach "FiFo" und "undo" habe ich schon erfolglos gesucht.
Wundert mich also nicht sooooooo, dass eine Suche nach "FiFo" und "undo" nicht der Bringer ist.

Ich selbst habe sowas noch nie gemacht, aber ich finde das Thema interessant.
Für eine Redo-Funktion hatte ich keine gute Idee, aber eine Google Suche nach
brachte was:Datenstruktur undo redo
<http://bis.informatik.uni-leipzig.de/de ... mmando.pdf>, S. 6Zusätzlich zur Undo-Funktion ist es auch nicht weiter schwer, eine Redo-Funktion zu
implementieren. Dazu muss der Command Processor nur einen weiteren Stack, den
Redo-Stack, einführen. Immer wenn die „Undo“-Methode des Command Processor
aufgerufen wird, wird dann nicht einfach der oberste Befehl vom Undo-Stack
entfernt, sondern auf den Redo-Stack gelegt. Wenn die „Redo“-Methode des
Command Processor aufgerufen wird, wird die „FuehreAus“-Methode des obersten
Befehls des Redo-Stack ausgeführt und dieser Befehl wird wieder vom Redo-Stack
auf den Undo-Stack gelegt.
Klingt logisch, finde ich.

Gruß, Little John
Re: undo-Funktion
Super, Danke!
FiFo / LiFo - wusste ich doch immerhin noch, daß es da einen Fo gibt.
Zum FiFo war die Assoziation einfach größer.
Ich mache mich gleich daran wenn ich das Problem mit dem ExplorerListGadget gelöst habe.
Dazu mache ich wohl besser einen eigenen Post auf sonst geht das hier unter.
FiFo / LiFo - wusste ich doch immerhin noch, daß es da einen Fo gibt.

Zum FiFo war die Assoziation einfach größer.
Ich mache mich gleich daran wenn ich das Problem mit dem ExplorerListGadget gelöst habe.
Dazu mache ich wohl besser einen eigenen Post auf sonst geht das hier unter.
Re: undo-Funktion
Schön dass es das war, was Du suchtest!

Gruß, Li Jo
FiFo klingt für mich auch vertrauter. Könnte bei mir daran liegen, dass mein Lieblingsbrowser der FireFox ist.uweb hat geschrieben:Zum FiFo war die Assoziation einfach größer.

Gruß, Li Jo
- Froggerprogger
- Badmin
- Beiträge: 855
- Registriert: 08.09.2004 20:02
Re: undo-Funktion - gelöst
Gibt es. Solche "komplexen Standard-Konzepte" nennen sich "Design Pattern" (=> Google).Ich hätte vermutet, daß es da eine universelle Datenstruktur bzw einen universellen Algorithmus gibt.
Hab über Undo/Redo unabhängig davon auch schonmal nachgedacht: Ich würde jede Aktion in ein "invertierbares Aktionsobjekt" kapseln, also ein Aktionsobjekt, dass man anwenden kann, aber auch wieder rückgängig machen. Sofern ein solches Konzept konsequent eingebaut ist, braucht man eine Aktion nur noch an die Aktionsqueue zu schicken. Dafür genügt eine einfache Liste, in der man sich vor- und zurückbewegt, mit der speziellen Eigenschaft, dass: Sobald man mehrere Dinge Undo gemacht hat, und dann eine *neue* Aktion ausführt, wird zuvor alles, was man per Redo noch hätte erreichen können, endgültig gelöscht.
Ist das klar, was ich schreibe? Oder diffus? (Hab schon Wein getrunken und geh gleich pennen...

!UD2
Re: undo-Funktion - gelöst
diffus
das muß aber nicht an dir liegen und ist immer noch besser als totale dunkelheit
danke
das muß aber nicht an dir liegen und ist immer noch besser als totale dunkelheit
danke
- Froggerprogger
- Badmin
- Beiträge: 855
- Registriert: 08.09.2004 20:02
Re: undo-Funktion - gelöst
Ein paar Worte noch zur Idee, ohne OOP:
Eine Funktion:
Auf diese Weise würde ein solches System funktionieren, incl. Undo und Redo. Jede rückgängigmachbare Aktion wird also vom z.B. Mausevent nicht direkt aus aufgerufen, sondern nur indirekt über einen Aufruf von Do(...) mit den jeweiligen Parametern. Später kann jederezti Undo und Redo gemacht werden. Wird nach einigen Undo-Schritten eine neue Aktion ausgeführt, dann wird der Rest der geundoten Schritte endgültig gelöscht.
Bei dieser Implementierung wird allerdings folgende Annahme gemacht: Durch Ausführen der UNDO-Funktion lässt sich eine vorher gemachte Aktion exakt wieder rückgängig machen. Das mag zwar in vielen Anwendungen stimmen, aber nicht immer, z.B. bei der Bildverarbeitung gibt es bei jeder Bildrotation Rundungsverluste. Ein häufiges Undo/Redo einer Drehung würde aber jedesmal neu die Drehung berechnen, einmal vorwärts, einmal rückwärts.
Eine alternative Implementierung, die aber speicherintensiver ist, wäre: Vor jeder Aktion wird der komplette Programmstatus gespeichert, z.B. auch das gegenwärtige bearbeitete Bild, alle Parameterwerte, etc.). Dieses DatenBackup wird in die queue gepackt. Bei einem Undo muss nun lediglich das vorherige Backup wiederhergestellt werden. Eine Speicherbegrenzung für die Undo-Liste ist dabei sicherlich sinnvoll. Ein weiterer Vorteil: Ein REDO wird nicht neu berechnet (ebenso wie die UNDO nicht), sondern lediglich wieder aus dem Backup hergestellt. Das ist für größere Aktionen, wie Bildveränderungen sicherlich manchmal performanter, als jedesmal neu zu berechnen.
Welches der beiden Konzepte du wählen solltest, hängt also von deinen Anforderungen ab.
Eine Funktion:
Code: Alles auswählen
Structure Action
actionId.l // hält Typ der Aktion, z.B. 1=Bild drehen, 2=Bild skalieren, ...
d1.d // Parameter aller möglichen Aktionen ggf. auch jeweils als StructureUnion
d2.d
...
EndStructure
NewList history.Action() // hält alle Aktionen
// Im Programm soll nach einem Eingabedialog nun etwa das Bild rotiert werden, dann muss
// man etwa folgende Funktion aufrufen mit DoAction(#True, #ActionId_Rotate, 12)
Procedure DoAction(addToList.l, actionId.l, d1.d, d2.d, ...) // Parameter gemäß Structure, oder gleich ein neues Structure-Objekt selbst als Parameter
If actionId = #ActionId_Rotate
Rotate(#DO, d1) // rotiere das Bild, und zwar als #DO-Aktion
ElseIf actionId = #ActionId_Swap
...
EndIf
If addToList
// nun wird die Aktion der Liste zugefügt. Zuerst lösche alle Listenelemente, die nach dem aktuellen Listenelement liegen. Damit werden bereits "ge-UNDOte" Schritte endgültig gelöscht, und können auch nicht mehr per ReDo ausgeführt werden.
// nun erzeuge neues Element in Liste und setze dessen Parameter gemäß aktueller Aktion
AddElement(queue)
queue\actionId = actionId
queue\d1 = d1
...
EndIf
EndProcedure
Procedure UnDo()
// Führe aktuelles Element (todo: sofern existent) von Liste als UNDO aus
If queue\actionId = #ActionId_Rotate
Rotate(#UNDO, d1) // rotiere das Bild, und zwar als #UNDO-Aktion
ElseIf actionId = #ActionId_Swap
...
EndIf
// todo: Setze das aktuelle Listen-Element auf eine Position vorher
EndProcedure
Procedure ReDo()
// Falls am Ende der List, dann ProcedureReturn
// todo: Setze das aktuelle Listen-Element auf eine Position später
// Führe aktuelles Element von Liste als DO aus
If queue\actionId = #ActionId_Rotate
Rotate(#DO, d1) // rotiere das Bild, und zwar als #DO-Aktion
ElseIf actionId = #ActionId_Swap
...
EndIf
EndProcedure
Procedure Rotate(undo.l, degree.d)
If undo
degree = -degree
EndIf
// rotiere das Bild
EndProcedure
Bei dieser Implementierung wird allerdings folgende Annahme gemacht: Durch Ausführen der UNDO-Funktion lässt sich eine vorher gemachte Aktion exakt wieder rückgängig machen. Das mag zwar in vielen Anwendungen stimmen, aber nicht immer, z.B. bei der Bildverarbeitung gibt es bei jeder Bildrotation Rundungsverluste. Ein häufiges Undo/Redo einer Drehung würde aber jedesmal neu die Drehung berechnen, einmal vorwärts, einmal rückwärts.
Eine alternative Implementierung, die aber speicherintensiver ist, wäre: Vor jeder Aktion wird der komplette Programmstatus gespeichert, z.B. auch das gegenwärtige bearbeitete Bild, alle Parameterwerte, etc.). Dieses DatenBackup wird in die queue gepackt. Bei einem Undo muss nun lediglich das vorherige Backup wiederhergestellt werden. Eine Speicherbegrenzung für die Undo-Liste ist dabei sicherlich sinnvoll. Ein weiterer Vorteil: Ein REDO wird nicht neu berechnet (ebenso wie die UNDO nicht), sondern lediglich wieder aus dem Backup hergestellt. Das ist für größere Aktionen, wie Bildveränderungen sicherlich manchmal performanter, als jedesmal neu zu berechnen.
Welches der beiden Konzepte du wählen solltest, hängt also von deinen Anforderungen ab.
!UD2