Undo Dynamo Action

Hello Dynamo Friends :slight_smile:

I would like to know which actions that run in a dynamo script can be undone in revit after the run.

An example: I have a dynamo script that duplicates a view. After the run i can not undo that in revit.

Can i affect that behavior by wrapping something in the script in a transaction?

Pretty shure thats a revit thing and not a dynamo thing, would be thankfull for any information regarding the undo behavior :slight_smile:
Kind regards

2022-04-05_14h03_43

Hello,

If your script is realy doing something you can just go back!

KR

Andreas

Be wary though, there are 1-2 things you can’t just undo (I think deleting some items is one of these.)

Always save your Revit file BEFORE you run your script, that way if you can’t Ctrl+Z you still have your original file.

1 Like

Strange, i can undo a view duplication i made in revit, but not if i do it with dynamo. Maybe it has to do with oder things in my script, i will post more details on this later.

Hello,
You can use PostableCommand.Undo (‘one undo’)

import clr
import sys
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import Revit APIUI namespace
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication

commandId = RevitCommandId.LookupPostableCommandId(PostableCommand.Undo)
uiapp.PostCommand(commandId)
4 Likes

Should be doable unless there is an additional issue at play (ie: opening the document in the background, syncing or saving). Please post the graph so we can help.

You can roll back a transaction… I don’t know but how your script looks like:

Hi @c.poupin I want to apply two times undo though postable command. Is it possible ? if it is, then how should I proceed ? I have forced another undo python node till first one executes but the second one is not working.

Can you please tell me how to undo two times ?

#Postable Command by nathan.chapman
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')
import Autodesk
from Autodesk.Revit.UI import RevitCommandId
from Autodesk.Revit.UI import UIApplication
from Autodesk.Revit.UI import ExternalCommandData
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication

RunIt = IN[0]

if RunIt ==  True:
	commandId = RevitCommandId.LookupPostableCommandId(PostableCommand.Undo)
	uiapp.PostCommand(commandId)
	#uiapp.PostCommand(commandId)
	errorReport = "Success"
else:
	errorReport = "Failed to do the action"

#Assign your output to the OUT variable
OUT = errorReport

Hi @shashank.baganeACM

the only solution I know of would be to work with Revit Events, but there are a few (if not many) precautions to take

I’ll post an example, just for the experience

I encountered another undo problem just a few days ago.
It´s a code that sets sheet parameters with param.Set().
While I can undo that in revit perfectly fine, I can not undo it when setting the parameters in dynamo or pyrevit.
It´s a really really rare case that a transaction is not allowing me to undo it later in the revit UI.
Edit: Not sure if I actually tried that with dynamo…maybe just a pyrevit problem…

def update_parameter(sheet, param, param_name, current_value, new_value):
    if WorksharingUtils.GetCheckoutStatus(doc, sheet.Id) != CheckoutStatus.OwnedByOtherUser:
        t = Transaction(doc, "Set Sheet Parameter")
        t.Start()
        try:
            # Check if new_value is a float and a whole number
            if isinstance(new_value, float) and new_value.is_integer():
                new_value = str(int(new_value))  # Convert to integer string if it's a whole number
            else:
                new_value = str(new_value)  # Otherwise, just convert to string

            param.Set(new_value)
            t.Commit()
            print("Success: {0} - {1}, the parameter value {2} was changed from {3} to {4}".format(sheet.SheetNumber, sheet.Name, param_name, current_value, new_value))
        except Exception as e:
            print("ERROR: {0} - {1} for {2} from {3} - Exception: {4}".format(sheet.SheetNumber, sheet.Name, param_name, current_value, str(e)))
            t.RollBack()
    else:
        print("ERROR: {0} - {1} is owned by another user, the parameters could not be set.".format(sheet.SheetNumber, sheet.Name))```

A workaround for several undos

undoCommand

use with caution


import clr
import sys
import re
import System
from System import EventHandler, Uri
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB.Events import DocumentChangedEventArgs

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)


clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')
import System.Drawing
import System.Windows.Forms

from System.Drawing import *
from System.Windows.Forms import *

import  time

class RollBackForm(Form):
	def __init__(self, nbr_undo):
		self._commandId = RevitCommandId.LookupPostableCommandId(PostableCommand.Undo)
		self._app = app
		self._uiapp = uiapp
		self._counter = 0
		self._nbr_RollBack = nbr_undo
		self.InitializeComponent()
	
	def InitializeComponent(self):
		self._label1 = System.Windows.Forms.Label()
		self.SuspendLayout()
		self.FormClosing += self.Form1_FormClosing
		self._app.DocumentChanged += self.ModifEvent
		# 
		# label1
		# 
		self._label1.Font = System.Drawing.Font("Microsoft Sans Serif", 12, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, 0)
		self._label1.Location = System.Drawing.Point(60, 42)
		self._label1.Name = "label1"
		self._label1.Size = System.Drawing.Size(230, 63)
		self._label1.TabIndex = 0
		self._label1.Text = "Wait RollBack in Progress..."
		# 
		# Form35
		# 
		self.ClientSize = System.Drawing.Size(346, 163)
		self.Controls.Add(self._label1)
		self.Name = "RollBack"
		self.Text = "RollBack"
		self.Shown += self.Form35Shown
		self.ResumeLayout(False)

	def RollBack(self):
		self._counter += 1
		if self._counter > self._nbr_RollBack:
			self.Close()
		else:
			self._uiapp.PostCommand(self._commandId)

	def Form35Shown(self, sender, e):
		self.RollBack()
	
	def ModifEvent(self, sender, e):
		# prevent if unregister  event failed
		if self.Controls.Count > 0 and all(not x.IsDisposed for x in self.Controls):
			time.sleep(1)
			self.RollBack()

	def Form1_FormClosing(self, sender, e):
		print("Closing...")
		self._app.DocumentChanged -= self.ModifEvent
		

nbr_undo = IN[1]
RollBackForm(nbr_undo).Show()
4 Likes