Is there a default undo operation?

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
lainegates
Posts: 216
Joined: Tue Oct 02, 2012 7:29 am

Is there a default undo operation?

Post by lainegates »

I tried to implement the undo function for my project, I tried to use 'Ctrl+Z', which is in line with the user habits.
But when I create objects and press 'Ctrl+Z'(I code nothing for the 'Ctrl+Z'), some objects hided.
So, is there a default undo operation? How to work with it, or could I just close it?
User avatar
jriegel
Founder
Posts: 3369
Joined: Sun Feb 15, 2009 5:29 pm
Location: Ulm, Germany
Contact:

Re: Is there a default undo operation?

Post by jriegel »

The Gui objects (ViewProvider) are not included in the Undo (transaction) mechanisms .

The Transaction system has a interface in the Document python class, you don't need to intercept the key strokes...
Stop whining - start coding!
lainegates
Posts: 216
Joined: Tue Oct 02, 2012 7:29 am

Re: Is there a default undo operation?

Post by lainegates »

Currently, I am planning to use the follow code to implement the undo function:

Code: Select all

from pivy import coin
class OperationObverser:
    '''
    observe the operation, catch keyboard event to supply redo/undo application
    '''
    def __init__(self):
        self.call = None
        self.on = False
    
    def Activated(self, name="None"):
        self.on = True
        self.view = FreeCADGui.ActiveDocument.ActiveView
        self.call = self.view.addEventCallback("SoEvent", self.action)
        
    def IsActive(self):
        return self.on

    def action(self, arg):
        "scene event handler"
        if arg["Type"] == "SoKeyboardEvent":
#            FreeCAD.Console.PrintMessage('keyboardEvent\n')
            if arg["CtrlDown"] and arg["Key"] == 'z':
                print 'ctrl z'
                #  do the undo process

    def Deactivated(self):
        if self.call:
            self.view.removeEventCallback("SoEvent", self.call)
        self.call=None
        self.on = False
        self.view = None
    
import FreeCADGui 
FreeCADGui.DDAOperationObverser = OperationObverser()
Then I could use

Code: Select all

FreeCADGui.DDAOperationObverser.Activated()                 # start  my undo observation
FreeCADGui.DDAOperationObverser.Deactivated()             # stop my undo observation
I used Document python class, and the App part of my objects do nothing for most of my shapes .

To be honest I just design a lightweight architecture, which only applies to DDA and may not be consistent with FreeCAD in architecture. In the architecture, there is a database in the background to store all DDA data, data is stored by category. And all shapes of one kind are display with only one document object. The objects are kept in my workbench once created until the workbench done. Once data in database changed, corresponding ViewProvider(by category) is trigger to update graph.
So the key of undo process in my project is the changes of database.
(The design of my architecture of my project is in iteration, it 6th version now. Because I didn't read FreeCAD code in-depth, this is the best architecure I could designed currently. I will try to improve my architecture)

I don't want to intercept the key strokes as well, but I have to do my custom undo process.
Currently, if I press 'Ctrl+Z', some objects disappeared in my surprise, which is out of my plan.
So could I close the default Undo mechanisms, or how could I implement my undo process under the default Undo mechanisms(just the start to put my undo code is ok)?
User avatar
jriegel
Founder
Posts: 3369
Joined: Sun Feb 15, 2009 5:29 pm
Location: Ulm, Germany
Contact:

Re: Is there a default undo operation?

Post by jriegel »

Ok, I see...

If you want to use the undo/redo system you have to give the document informations about you modeling steps. So if you ,for exampel, create a object, open a new transaction:
App.AcctiveDocument.openTransaction("name of the transaction").

if you change the object open a new transaction.
now when the user do Undo, the transactions get rolled back one after another. If its role back the first transaction the object gets removed (not deleted cause of a possible redo). If you have only one transaction running from the start, a Undo will undo all from the start.....

Most of the FreeCAD commands open a transaction, so you can look into that code....
Stop whining - start coding!
lainegates
Posts: 216
Joined: Tue Oct 02, 2012 7:29 am

Re: Is there a default undo operation?

Post by lainegates »

Ahh, thank you, jriegel.
I read the 'todo' of 'Draft' workbench again, I understand the todo.commitList now. I will try to use it.
And it is incredible that FreeCAD could do Undo steps like this. It is called much simpler than my undo process. :D
But could you explain a bit for me. I want to understand the mechanism to code better.
Let's look at the simplest step of Draft, like adding a line.

Code: Select all

            # the following code comes from Line.finish() of 'DraftTools.py'
            self.commit(translate("draft","Create DWire"),
                        ['import Draft',
                         'points='+pts,
                         'Draft.makeWire(points,closed='+str(closed)+',face='+fil+',support='+sup+')'])
If I created 3 lines with the class 'Line', the code above were used for 3 times. So there are 3 transactions, we named them A B and C . If I press 'Ctrl+Z', how did FreeCAD do?
Does FreeCAD do transaction A and B again, and don't do transaction C?
If not , how does FreeCAD roll back?
User avatar
jriegel
Founder
Posts: 3369
Joined: Sun Feb 15, 2009 5:29 pm
Location: Ulm, Germany
Contact:

Re: Is there a default undo operation?

Post by jriegel »

Its much simpler, you don't need to bother with what you do. After start a transaction the document records all changes to the objects in the document. Also creation and deletion of objects. The size of transactions is on you. Normaly one user command or the result of a dialog. When undoing a transaction or do a roll-back in a transaction, the document just simply undo all changes to the document and you have the state before start of a transaction.

If the document is in transaction mode, there is allways one transaction open. A new transaction end the last one. If you close a transaction a automatic one will be opened if you change the document . So the only thing you have to do is give the document the points in time where to start a new transaction, and of course name, which see the user in the undo box.

Yorik put in Draft ontop of this mechanism a additional logic, to make it easier to use in Draft. Thats all. If you want to keep it simple, just use App.ActiveDocument.openTransaction(name)......
Stop whining - start coding!
lainegates
Posts: 216
Joined: Tue Oct 02, 2012 7:29 am

Re: Is there a default undo operation?

Post by lainegates »

Thank you, jriegel.
I will try. :D
lainegates
Posts: 216
Joined: Tue Oct 02, 2012 7:29 am

Re: Is there a default undo operation?

Post by lainegates »

The transaction attracts me a lot, and I tried to use it today.
I write a simple example, but it doesn't work.

Code: Select all

import FreeCAD , FreeCADGui , Part
from FreeCAD import Vector

obj = FreeCAD.ActiveDocument.addObject("Part::Feature", 'ABC')
v1 = Vector(1,1,0)
v2 = Vector(1,0,0)
v3 = Vector(0,0,0)
v4 = Vector(0,1,0)
l1 = Part.makeLine(v1,v2)
l2 = Part.makeLine(v1,v3)
l3 = Part.makeLine(v1,v4)

def saveTransaction(commands):
    assert isinstance(commands , list)
    FreeCAD.ActiveDocument.openTransaction('abc')
    for command in commands:
        FreeCADGui.doCommand(command)
    FreeCAD.ActiveDocument.commitTransaction()

FreeCAD.ActiveDocument.getObject('ABC').Shape = l1
saveTransaction(['''FreeCAD.ActiveDocument.getObject('ABC').ViewObject.LineColor = (1.0,0.0,0.0)'''])
FreeCAD.ActiveDocument.getObject('ABC').Shape = l2
saveTransaction(['''FreeCAD.ActiveDocument.getObject('ABC').ViewObject.LineColor = (0.0,1.0,0.0)'''])
FreeCAD.ActiveDocument.getObject('ABC').Shape = l3
saveTransaction(['''FreeCAD.ActiveDocument.getObject('ABC').ViewObject.LineColor = (0.0,0.0,1.0)'''])
After the script, I press 'Ctrl+Z', nothing happened.
Do I use it in a wrong way?

There are something confusing me at the same time:
1. Does a transaction starts with 'FreeCAD.ActiveDocument.openTransaction([name])' and ends with FreeCAD.ActiveDocument.commitTransaction() ?
2. Will commands between 2 transactions be recorded?
3.Do commands in a transaction must have run before store in a transaction?
3. What kinds of command could be recorded?
wmayer
Founder
Posts: 20243
Joined: Thu Feb 19, 2009 10:32 am
Contact:

Re: Is there a default undo operation?

Post by wmayer »

You are mixing up two concepts: the transaction framework and macro recording
1. Does a transaction starts with 'FreeCAD.ActiveDocument.openTransaction([name])' and ends with FreeCAD.ActiveDocument.commitTransaction() ?
Yes. And for convenience you can also do:

Code: Select all

FreeCAD.ActiveDocument.openTransaction("Command 1")
... # change some properties
FreeCAD.ActiveDocument.openTransaction("Command 2")
...
The second call of openTransaction automatically closes the first transaction.
2. Will commands between 2 transactions be recorded?
The recording of a command has nothing to do with the transaction framework. Since (funnily) for Python it's impossible to record the code (because we need to save a string) Yorik wrote the saveTransaction() function where he can pass a string to the interpreter and thus make it possible to record the command.
3. What kinds of command could be recorded?
If macro recording is activated and the command comes through the doCommand() function it gets recorded.

And again for the transaction framework it's currently impossible to store changes on the GUI side. So, that's why your script doesn't do for CTRL+Z. Here is a modified one:

Code: Select all

import FreeCAD , FreeCADGui , Part
from FreeCAD import Vector

obj = FreeCAD.ActiveDocument.addObject("Part::Feature", 'ABC')
v1 = Vector(1,1,0)
v2 = Vector(1,0,0)
v3 = Vector(0,0,0)
v4 = Vector(0,1,0)
l1 = Part.makeLine(v1,v2)
l2 = Part.makeLine(v1,v3)
l3 = Part.makeLine(v1,v4)

def saveTransaction(commands):
    assert isinstance(commands , list)
    FreeCAD.ActiveDocument.openTransaction('abc')
    for command in commands:
        FreeCADGui.doCommand(command)
    FreeCAD.ActiveDocument.commitTransaction()

saveTransaction(['''FreeCAD.ActiveDocument.getObject('ABC').Shape = l1'''])
saveTransaction(['''FreeCAD.ActiveDocument.getObject('ABC').Shape = l2'''])
saveTransaction(['''FreeCAD.ActiveDocument.getObject('ABC').Shape = l3'''])
lainegates
Posts: 216
Joined: Tue Oct 02, 2012 7:29 am

Re: Is there a default undo operation?

Post by lainegates »

And again for the transaction framework it's currently impossible to store changes on the GUI side. So, that's why your script doesn't do for CTRL+Z. Here is a modified one:
To speed up my program, as I said above, I code the gui class and app class for my objects in my project. The app class I wrote does nothing, and gui class read data from database in background to draw shapes.
Does this mean that I could not use transaction to implement undo process?
Post Reply