Vì Revit chỉ cho phép mỗi lần load được một loại Family cho nên nếu muốn load nhiều families cùng một lúc thì sẽ phải dùng Dynamo. Bài này BCDD viết về chủ đề này.
1. Dynamo Scripts
Load tất cả files từ một đường dẫn trong máy tính vào Dynamo
Chỉ lọc ra các families trong đường dẫn bằng node String.EndsWith. Ở đây BCDD lọc ra các family bằng cách tận dụng đuôi .rfa của các family trong Revit
Sở dĩ chúng ta có thể lọc được bằng cách này vì kiểu dữ liệu đầu ra là string
Tới đây là đã có được các families rồi, tiếp theo chúng ta sẽ dùng Python Script Node trong Dynamo để xử lý tiếp.
2. Python Code
2.1 Import thư viện
Như mọi khi, trước hết là phải import thư viện
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
Tiếp đến là đưa các families đã lấy được lúc nãy vào Python node
filepaths = IN[0]
2.2 Tra method LoadFamily trong Revit API
Để có thể load families vào thì BCDD sẽ dùng method LoadFamily. Tiến hành tra Revit API
Chà xem ra có khá nhiều cách để load families vào Revit. Ở bài viết này chúng ta sẽ dùng cách cuối cùng.
Nhìn vào cú pháp C# của method LoadFamily này chúng ta thấy nó có ba tham số lần lượt là: filename, familyLoadOptions và family. Tuy nhiên, trong ba tham số này thì giờ chúng ta chỉ quan tâm đến mỗi familyLoadOptions thôi vì chúng ta đã có filename rồi (chính là family lọc được từ Dynamo ở trên). Còn đối với tham số family, chúng ta để ý đến chữ out
đứng trước nó, cho nên tham số này có nghĩa là Family trả về.
Nói tóm lại thì method LoadFamily mà chúng ta dùng cần filename và familyLoadOptions và sẽ trả về giá trị bool (true/false), tức là có load được family hay không? nếu có thì tiếp tục trả về các families đã load được.
Mà kiểu dữ liệu trả về của familyLoadOptions là IFamilyLoadOptions, đây là một interface class có nhiệm vụ cung cấp lệnh gọi lại cho các tùy chọn khi load families.
Tức là nếu vì một lý do nào đó, chúng ta không load được các families hoặc các families chúng ta load đã có trong Revit (không có sự thay đổi) thì sẽ không có điều gì xảy ra cả. Ngược lại, nếu chúng ta load được các families hoặc các families cần load có sự thay đổi so với families có sẵn trong Revit thì interface class ở trên mới được gọi.
Lưu ý khi chúng ta load family trong Revit sẽ có hai trường hợp: một là các families bình thường và hai là các nested/shared families. Ở đây Revit API cũng có hai methods là OnFamilyFound và OnSharedFamilyFound tương ứng với hai trường hợp trên.
Tất nhiên là chúng ta cần phải cover cả hai trường hợp nên bước tiếp theo ta sẽ phải định nghĩa một class có cả hai functions giống y chang hai methods trên trong Revit API.
2.3 Định nghĩa một class trong Python
class FamilyLoaderOptionsHandler(IFamilyLoadOptions):
def OnFamilyFound(self, familyInUse, overwriteParameterValues):
overwriteParameterValues = False
return True
def OnSharedFamilyFound(self, sharedFamily, familyInUse, source,
overwriteParameterValues):
source = FamilySource.Family
overwriteParameterValues = True
return True
Để hiểu được các dòng codes trên thì phải biết về cách định nghĩa class trong Python. Nhưng đại loại thì các dòng codes trên có nghĩa là (đối với OnFamilyFound) nếu khi load các families mà thấy có family nào giống với family có trong Revit thì tiến hành override (ghi đè) family đó luôn.
Còn trong trường hợp OnSharedFamilyFound thì cũng tương tự như trên. Nhưng trường hợp này có thêm vụ FamilySource nửa, tức là nó muốn biết nguồn gốc family chúng ta đang đề cấp đến là từ nguồn family hay là nguồn project (file Revit chứa families). Trong trường hợp này nguồn của chúng ta đơn giản là các families trong một đường dẫn nào đó thôi nên chúng ta dùng source = FamilySource.Family
Định nghĩa xong class rồi thì gán cho nó một biến để sau đó cho vào tham số familyLoadOptions của method LoadFamily
Lưu ý:
- Vì method LoadFamily sẽ trả về bool và Family được load như đã nói lúc nãy nên phải gán cho nó hai biến là res (respond) và family như code bên dưới.
- Việc load family vào Revit làm thay đổi database của Revit nên phải được thực hiện trong một transactions.
TransactionManager.Instance.EnsureInTransaction(doc)
fload_handler = FamilyLoaderOptionsHandler()
outputs = []
for filepath in filepaths:
res, family = doc.LoadFamily(filepath, fload_handler)
outputs.append(family)
OUT = outputs
TransactionManager.Instance.TransactionTaskDone()
Tới đây thì chạy được rồi. Kết quả cho ra ba families và một giá trị null. Sở dĩ có null là vì BCDD đã cố tình load trước một family trong đường dẫn vào Revit mà không hề thay nổi nội dung bên trong family đó.
Tuy nhiên, nếu để kết quả đầu ra là null sẽ dễ gây hiểu lầm nên sẽ thêm vào một vài dòng code. Đoại loại như nếu load được family thì tiến hành lấy Name còn không thì trả về một string là đã load family này rồi.
TransactionManager.Instance.EnsureInTransaction(doc)
fload_handler = FamilyLoaderOptionsHandler()
outputs = []
for filepath in filepaths:
res, family = doc.LoadFamily(filepath, fload_handler)
if family:
family = family.Name
else:
family = "Da load family nay roi"
outputs.append(family)
OUT = outputs
TransactionManager.Instance.TransactionTaskDone()
Kết quả như sau:
Family mà BCDD đã load vào rồi có tên là Web opening. Bây giờ giả sử BCDD mở family này lên thay đổi nội dung bên trong một ít (xong lưu) rồi Run lại thì kết quả như sau:
Kết quả trả về báo rằng các family còn lại đã load vào rồi. Còn riêng family Web opening mặc dù đã load vào rồi nhưng do đã thay đổi nội dung nên phải load lại và override (ghi đè) nội dung như đã thiếp lập cho class FamilyLoaderOptionsHandler.
Nếu thấy bài viết này hữu ích, hãy cân nhắc việc follow BCDD tại đây
Bài viết do admin 1 Nghia Nguyen và cộng sự BCDD biên soạn.
Tham khảo (reference source): DynamoBIM – Python Tools for Revit – Ep003 – Load Lots of Families