Let's continue where we stopped at the previous lesson and learn how to transfer View Templates between projects
Let's continue where we stopped at the previous lesson and learn how to transfer View Templates between projects
Summary
Intro
We will continue this lesson with the code from the previous one. The lesson was getting too long, so I split it into 2 parts.
In this second part, you will learn how to transfer View Templates between projects, and most importantly, how to override its parameters.
Why Transfer ViewTemplates
I know that many people work on projects with multiple buildings, and we often split them into separate models. This workflow has a ton of benefits, but also means that we have to be smarter managing our standards, families and other things across projects.
Including - ViewTemplates.
So I wanted to show you how to transfer them and also show you the nuances about overriding its parameters.
Get Projects
Firstly, we need to get 2 projects.
You've seen that in the video, I've used 2 simple projects, and I made sure that I removed all the View Templates in one of them, so it's easy to see when we bring new ones in.
You might want to create some form to ask user Project_from and Project_to, but I kept it simple for the lesson and got all my projects with app.Documents.
💡Keep in mind that you will get DocumentSet, and you can't get items by using indexes. Therefore, make sure you convert it into python list()
app = __revit__.Application
all_docs = list(app.Documents)
doc_from = all_docs[0]
doc_to = all_docs[1]
print('Copy ViewTemplates:')
print('-'*50)
print('Copying from: {}'.format(doc_from.Title))
print('Copying To: {}'.format(doc_to.Title))
⚠️ From now on, pay attention to which doc you use.
It's important when you want to get the right elements, create transaction and other steps that might involve doc variable.
It's very common to use the default doc variable instead of specific doc_to/doc_from and it can cause some errors. But I've showed you a few cases where it might occur.
Select ViewTemplates
Once you have your projects, we can get all ViewTemplates and ask user to select which ones to copy. For that we will use Dictionary and sort data like {VT.Name : VT}
Once we have a dictionary of ViewTemplates, we can use pyrevit.forms.SelectFromList to ask user to select from the list. Also make sure you set multiselect=True.
Then after user will make the selection, you can create list of selected ViewTemplates and list of their Ids.
from pyrevit.forms import SelectFromList
all_views = FilteredElementCollector(doc_from).OfCategory(BuiltInCategory.OST_Views).ToElements()
all_vt = [v for v in all_views if v.IsTemplate]
dict_vt = {vt.Name:vt for vt in all_vt}
sel_vt_names = SelectFromList.show(dict_vt.keys(), button_name='Select ViewTemplate', multiselect=True)
sel_vt = [dict_vt[vt_name] for vt_name in sel_vt_names]
sel_vt_ids = [vt.Id for vt in sel_vt]
Overall, it's simple to use because we will create default options for the Transform and CopyPasterOptions, so we just need
doc_from
doc_to
list_of_el_ids
Here is the code snippet of how it works:
t = Transaction(doc_to, 'Copy ViewTemplates')
t.Start()
List_copy_el_ids = List[ElementId](sel_vt_ids)
transform = Transform.Identity
options = CopyPasteOptions()
ElementTransformUtils.CopyElements(doc_from,
List_copy_el_ids,
doc_to,
transform,
options)
t.Commit()
print('✔️ ViewTemplate Transfer is Completed!')
💡 Make sure you use correct doc in the Transaction.
You might get an error if you use regular doc, which will be the Active Document. This is a common mistake we do because of a habit. And it might even work if you have the right project open. So pay attention here.
Example 1 - Complete Snippet:
Here is the snippet put together
from Autodesk.Revit.DB import *
import clr
clr.AddReference('System')
from System.Collections.Generic import List
app = __revit__.Application
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
app = __revit__.Application
all_docs = list(app.Documents)
doc_from = all_docs[0]
doc_to = all_docs[1]
print('Copy ViewTemplates:')
print('-'*50)
print('Copying from: {}'.format(doc_from.Title))
print('Copying To: {}'.format(doc_to.Title))
from pyrevit.forms import SelectFromList
all_views = FilteredElementCollector(doc_from).OfCategory(BuiltInCategory.OST_Views).ToElements()
all_vt = [v for v in all_views if v.IsTemplate]
dict_vt = {vt.Name:vt for vt in all_vt}
sel_vt_names = SelectFromList.show(dict_vt.keys(), button_name='Select ViewTemplate', multiselect=True)
sel_vt = [dict_vt[vt_name] for vt_name in sel_vt_names]
sel_vt_ids = [vt.Id for vt in sel_vt]
t = Transaction(doc_to, 'Copy ViewTemplates')
t.Start()
List_copy_el_ids = List[ElementId](sel_vt_ids)
transform = Transform.Identity
options = CopyPasteOptions()
ElementTransformUtils.CopyElements(doc_from,
List_copy_el_ids,
doc_to,
transform,
options)
t.Commit()
print('✔️ ViewTemplate Transfer is Completed!')
Transfer and Override ViewTemplate
You might notice that if you try to copy the ViewTemplate that already exists, it doesn't change it. But instead, it will create a copy of that ViewTemplate.
It might be a good idea sometimes, but mostly, we want to transfer and update ViewTemplates with the same name. So let's look into how we're going to do that.
Brainstorming
We will have to do the following steps:
Get A/B Documents
Ask user to select ViewTemplates from doc_A
1. Find matching ViewTemplate in doc_B and sort views that use it
1.2 Delete matching ViewTemplates
2. Copy ViewTemplates (they will be new, because we deleted matching one)
3. Reassign ViewTemplates to Views which used these templates.
By doing these steps, we've first cleared any duplicates and brough viewTemplates as new. And then we made sure that all the views have exactly the same ViewTemplates, as before we deleted them.
So Firstly, let's prepare our main part in the code. Also, I will use my ef_Transaction, which me made in the context-manager lesson, so we can save some space here.
from Snippets._context_managers import ef_Transaction
with ef_Transaction(doc_to, 'Delete ViewTemplates', debug = True):
dict_del_vt = delete_existing_view_templates(doc_to, sel_vt_names)
with ef_Transaction(doc_to, 'Copy ViewTemplates', debug = True):
List_copy_el_ids = List[ElementId](sel_vt_ids)
transform = Transform.Identity
options = CopyPasteOptions()
ElementTransformUtils.CopyElements(doc_from, List_copy_el_ids, doc_to, transform, options)
with ef_Transaction(doc_to, 'Delete ViewTemplates', debug = True):
reassign_del_view_templates(doc_to, dict_del_vt)
print('✔️ ViewTemplate Transfer is Completed!')
Now we need to write functions for:
Delete matching ViewTemplates
Reassign ViewTemplates
Delete matching ViewTemplates
Before we are going to delete existing ViewTemplates, we need to collect all the views that use them.
So we will create a dictionary, and then iterate through all the views in doc_to.
Then we will check if the view has a ViewTemplate, and if its name is in the list of ViewTempalte Names that we want to Transfer. If we have a match we will write it in a dictionary.
Once we created a sorted dictionary of views sorted by matching ViewTemplates, we can iterate and start deleting these ViewTemplates.
Lastly, don't forget to return
your dictionary of sorted views, we will need it to reassign ViewTemplates.
Here is the complete function to do all that:
def delete_existing_view_templates(given_doc, list_vt_names):
"""This function will delete ViewTemplates in the given doc based on list of provided ViewTemplate Names.
The Dictionary will be returned of removed ViewTemplates and all the views where it was used.
e.g. {ViewTemplate.Name : list(View1,View2)}"""
from collections import defaultdict
dict_del_vt = defaultdict(list)
all_views_vt = FilteredElementCollector(given_doc).OfCategory(BuiltInCategory.OST_Views).ToElements()
all_views = [v for v in all_views_vt if not v.IsTemplate]
all_vt = [v for v in all_views_vt if v.IsTemplate]
for view in all_views:
vt_id = view.ViewTemplateId
if vt_id != ElementId(-1):
vt = doc.GetElement(vt_id)
if vt.Name in list_vt_names:
dict_del_vt[vt.Name].append(view)
for vt in all_vt:
if vt.Name in list_vt_names:
doc_to.Delete(vt.Id)
return dict_del_vt
Once this step is complete we will Copy Elements same as we did before.
Reassign ViewTemplates
We've deleted matching ViewTemplates, transfered the desired ones. So, it's time to assign ViewTemplates back to the same views.
It's very simple, because we already have a dictionary that has all the views and the name of the ViewTemplate.
So we need to get all ViewTemplates in the project, and then iterate through to get the ones that we need by checking if their name is in the dictionary.
Then we will iterate through views, and assign ViewTemplate with the same name as before.
def reassign_del_view_templates(given_doc, dict_vt):
all_vt = [v for v in FilteredElementCollector(given_doc).OfClass(View).ToElements() if v.IsTemplate]
for vt_name, list_views in dict_vt.items():
new_vt = [v for v in all_vt if v.Name == vt_name][0]
for view in list_views:
view.ViewTemplateId = new_vt.Id
Final Code Snippet
Here is the complete snippet to Transfer ViewTemplates and override their parameters.
from Autodesk.Revit.DB import *
import clr
clr.AddReference('System')
from System.Collections.Generic import List
app = __revit__.Application
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
def delete_existing_view_templates(given_doc, list_vt_names):
"""This function will delete ViewTemplates in the given doc based on list of provided ViewTemplate Names.
The Dictionary will be returned of removed ViewTemplates and all the views where it was used.
e.g. {ViewTemplate.Name : list(View1,View2)}"""
from collections import defaultdict
dict_del_vt = defaultdict(list)
all_views_vt = FilteredElementCollector(given_doc).OfCategory(BuiltInCategory.OST_Views).ToElements()
all_views = [v for v in all_views_vt if not v.IsTemplate]
all_vt = [v for v in all_views_vt if v.IsTemplate]
for view in all_views:
vt_id = view.ViewTemplateId
if vt_id != ElementId(-1):
vt = doc.GetElement(vt_id)
if vt.Name in list_vt_names:
dict_del_vt[vt.Name].append(view)
for vt in all_vt:
if vt.Name in list_vt_names:
doc_to.Delete(vt.Id)
return dict_del_vt
def reassign_del_view_templates(given_doc, dict_vt):
all_vt = [v for v in FilteredElementCollector(given_doc).OfClass(View).ToElements() if v.IsTemplate]
for vt_name, list_views in dict_vt.items():
new_vt = [v for v in all_vt if v.Name == vt_name][0]
for view in list_views:
view.ViewTemplateId = new_vt.Id
app = __revit__.Application
all_docs = list(app.Documents)
doc_from = all_docs[0]
doc_to = all_docs[1]
print('Copy ViewTemplates:')
print('-'*50)
print('Copying from: {}'.format(doc_from.Title))
print('Copying To: {}'.format(doc_to.Title))
from pyrevit.forms import SelectFromList
all_views = FilteredElementCollector(doc_from).OfCategory(BuiltInCategory.OST_Views).ToElements()
all_vt = [v for v in all_views if v.IsTemplate]
dict_vt = {vt.Name:vt for vt in all_vt}
sel_vt_names = SelectFromList.show(dict_vt.keys(), button_name='Select ViewTemplate', multiselect=True)
sel_vt = [dict_vt[vt_name] for vt_name in sel_vt_names]
sel_vt_ids = [vt.Id for vt in sel_vt]
from Snippets._context_managers import ef_Transaction
with ef_Transaction(doc_to, 'Delete ViewTemplates', debug = True):
dict_del_vt = delete_existing_view_templates(doc_to, sel_vt_names)
with ef_Transaction(doc_to, 'Copy ViewTemplates', debug = True):
List_copy_el_ids = List[ElementId](sel_vt_ids)
transform = Transform.Identity
options = CopyPasteOptions()
ElementTransformUtils.CopyElements(doc_from, List_copy_el_ids, doc_to, transform, options)
with ef_Transaction(doc_to, 'Delete ViewTemplates', debug = True):
reassign_del_view_templates(doc_to, dict_del_vt)
print('✔️ ViewTemplate Transfer is Completed!')