As you may have seen from the TaskDialog…Hello, Revit! example, you can create a Dialog Box with only a few lines of code. However, there is more you can do with it than just displaying a simple message. You can use it to actually control code flow too.
In this example I will show a bit more functionality of the TaskDialog Class by creating an Element.Delete node where we use the TaskDialog to warn the user they are going to Delete some Elements from the Revit Document. We will be dressing this Python Script so it is fit for packages (Dressing is my way of saying the python node is going to be a Custom Node).
The end result will look like this…
and will prompt the User before deleting with a Dialog like this…
You can see why this may be useful. For instance, what if the last user left the Run boolean toggle on True and the graph execution mode on Automatic? They would find Dynamo may have actually Deleted some Elements without them knowing! Scary stuff!
So, lets get into the code…
We will be using a couple References in this node as we will be dealing with the UI and we will be making a modification to the Revit Document so we will need the TransactionManager. So, you will need the following References:-
- RevitServices – Gives access to the DocumentManager and TransactionManager
- RevitAPIUI – Gives access to the UI Namespace and Classes
In a new Python Node, clean it up and add the References. Your code should look as follows…
import clr #access the Revit Transaction and Document Managers... clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager #get the current Document and application... doc = DocumentManager.Instance.CurrentDBDocument #access the UI Namespace and classes... clr.AddReference("RevitAPIUI") from Autodesk.Revit.UI import *
I have also used a helper function which I have borrowed from some code (maybe from Spring Nodes) to ensure that the Elements passing in are a list. Many errors have come from expecting Lists and getting just an Element. This way I can ensure that I am always iterating over a List…
#convert to List object... def tolist(obj1): if hasattr(obj1,"__iter__"): return obj1 else: return [obj1]
Now we will add the IN-Variables and OUT-Variables to use in our code…
#IN-Variables... runIt = IN[0] elems = tolist(UnwrapElement(IN[1])) #OUT-Variables... outList = []
The outList is a container I frequently use to store all my OUT data. This could be a single string or a List of Lists of Lists…
For the fun part. We need to check that runIt is set to True…
#Check if runIt is True... if runIt:
We will then add the TaskDialog (with some advanced features). We want to get a result back from this to let us know if the User really meant to Delete these Elements…
res = TaskDialog.Show("Warning", " You are about to delete " + str(len(elems)) + " Elements." + "\n Are you sure you want to do this?",TaskDialogCommonButtons.Yes|TaskDialogCommonButtons.No, TaskDialogResult.No)
Let me quickly break this down as it is a little confusing…
We are using the TaskDialog.Show method from the TaskDialog Class. This has some Overloads (meaning, more than one way of taking arguments). Although we are looking at the C# version we can use this to see what we need to input and what it returns. I want to add the buttons I need and in this Overload I can do just that. It will return the result of which button was pressed…
So, a breakdown of the code above is as follows (don’t copy this bit in your code though)…
res = TaskDialog.Show("Warning",
The variable “res” will store the Result for which button was pressed and the String “Warning” is the Title…
"You are about to delete " + str(len(elems)) + " Elements." + "\n Are you sure you want to do this?",
This is the mainInstruction or the message. I am using the length of the list of Elements to let the user know how many they are about to Delete, useful info!
TaskDialogCommonButtons.Yes|TaskDialogCommonButtons.No,
I am declaring which Buttons I want to show in the Dialog (in this case, just Yes and No)…
TaskDialogResult.No)
This part is simply saying which Button is the default. Since I don’t want any accidents then I have made the Default Button = No…
Ok, moving onto the actual main purpose of the Node, we need to Delete some Elements using the Document.Delete method. Now we have the Users attention, if they click Yes, the TaskDialogResult will also be Yes, and now we can do the following…
#is it a yes?... if res == TaskDialogResult.Yes: #start a new Transaction... TransactionManager.Instance.EnsureInTransaction(doc) #loop through Elements.... for e in elems: #Try Delete the Elements... try: doc.Delete(e.Id) outList.append("Element Deleted") #If failed then why?... except Exception, e: outList.append("Failed \n" + e.message) #end the Transaction... TransactionManager.Instance.TransactionTaskDone() #return the results to User... OUT = outList
If the Result was a No, then we can return a nice message saying that they cancelled…
#if it was a No... else: OUT = "Phew!!! You have cancelled and saved the day!"
and finally, we can end on giving another informative message to the User if they have not set Run to True…
#If runIt is False... else: OUT = "Please set Run to True"
So, the whole code will look like this…
import clr #access the Revit Transaction and Document Managers... clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager #get the current Document and application... doc = DocumentManager.Instance.CurrentDBDocument #access the UI Namespace and classes... clr.AddReference("RevitAPIUI") from Autodesk.Revit.UI import * #convert to List object... def tolist(obj1): if hasattr(obj1,"__iter__"): return obj1 else: return [obj1] #IN-Variables... runIt = IN[0] elems = tolist(UnwrapElement(IN[1])) #OUT-Variables... outList = [] if runIt: res = TaskDialog.Show("Warning", " You are about to delete " + str(len(elems)) + " Elements." + "\n Are you sure you want to do this?",TaskDialogCommonButtons.Yes|TaskDialogCommonButtons.No, TaskDialogResult.No) if res == TaskDialogResult.Yes: TransactionManager.Instance.EnsureInTransaction(doc) for e in elems: try: doc.Delete(e.Id) outList.append("Element Deleted") except Exception, e: outList.append("Failed \n" + e.message) TransactionManager.Instance.TransactionTaskDone() OUT = outList else: OUT = "Phew!!! You have cancelled and saved the day!" else: OUT = "Please set Run to True"
Now, let’s get it dressed up and looking pretty. I created a Custom Node from this by right clicking and selecting “New Node From Selection” (or ctrl D). And then, while inside the new Custom Node, I added a few things…
I am setting the Default Value to False for the Run input and I am also flattening the List of Elements too. This way I don’t have to handle Lists of Lists of Lists…
Anyway, that about covers some of the more advanced features of the TaskDialog. Go create some Dialogs and see how you get on.
Leave a Reply