Chia sẽ BCDD lên mạng xã hội nhé !

Sau khi đã tập dợt xong với bài hello world thì đây là bài đầu tiên giúp bạn làm quen với việc tạo revit add-ins bằng Python + pyRevit.

Ở bài viết này cùng với BCDD tạo một add-in dùng để kiểm tra tên của các đối tượng đã được chọn trong mô hình Revit.

1. Viết code Python

Đoạn mã dưới đây dùng để chạy cho add-in đã nói ở trên.

import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')

from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document

selectionids = uidoc.Selection.GetElementIds()

for id in selectionids :
	element = doc.GetElement(id)
	TaskDialog.Show("BCDD 2022", element.Symbol.FamilyName)

Import clr để có thể dùng phương thức AddReference() để tham chiếu đến các assembly (mô-đun) như RevitAPIUI…Tiến hành import các classes cần thiết từ các namespace của các mô-đun đã tham chiếu. Ở đây tạm thời BCDD import * tức là import tất cả các classes có trong các namespace tương ứng.

Tiếp đến chúng ta cần định nghĩa UI Document và Document. Lưu ý rằng cách chúng ta định nghĩa ở đây hơi khác một xíu so với cách chúng ta làm ở Python Scripts trong Dynamo. Ở pyrevit hay RevitPythonShell chúng ta phải sử dụng biến global là __revit__ để truy cập vào Revit. 

Nhìn chung là ở pyRevit hay RevitPythonShell chúng ta không cần import quá nhiều thư viện như bên Dynamo, hầu như chỉ cần RevitAPI với Revit APIUI là đủ.

# Cái này là code trong Python Dynamo
doc = DocumentManager.Instance.CurrentDBDocument
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

Đó là mới xong cái khâu import thư viện, giờ để lấy được danh sách các đối tượng được chọn trong Revit thì chúng ta dùng Selection property của class UIDocument

Selection

Từ RevitAPI chúng ta biết được là chúng ta có thể sử dụng phương thức GetElementIds() để tạo một danh sách id của các đối tượng được chọn bên Revit. Tiến hành gán danh sách id này vào biến selectionids.

selectionids = uidoc.Selection.GetElementIds()

Sau khi có được danh sách id của các đối tượng được chọn, chúng ta chạy một vòng lặp for cho danh sách đó. Ứng với mỗi id trong danh sách chúng ta sẽ chuyển đổi id đó thành các element bằng cách dùng phương thức GetElement() với id là giá trị đầu vào của method GetElement().

for id in selectionids :
	element = doc.GetElement(id)

Tới đây thì chúng ta đã thành công trong việc đưa các đối tượng từ Revit sang python rồi. Tiếp theo là gọi tên nó ra thông qua một Revit UI popup bằng cách dùng class TaskDialog của namespace Autodek.Revit.UI

TaskDialog.Show("BCDD 2022", element.Symbol.FamilyName)

Có thể bạn đang tự hỏi? cái dòng element.Symbol.FamilyName lôi ở đâu ra vại? sao mà biết để lôi nó ra để mà lấy name của các đối tượng được chọn?

Revit Lookup Add-in

Để có được dòng ở trên thì thường là bạn phải đọc/tra cứu thật nhiều về RevitAPI hoặc tham khảo code của người khác rồi mò mẫm từ từ. Tuy nhiên BCDD sẽ giới thiệu cho bạn một add-in rất hữu dụng để tra cứu là Revit Lookup được viết bởi Jeremy Tammik – một huyền thoại trong làng RevitAPI.

Từ Revit bạn chọn một hay vài đối tượng gì đó rồi chọn vào add-in Revit Looup bên dưới Add-ins tab. Click chọn Snoop Current Selection.

Bảng bên dưới hiện ra chứa thông tin liên kết với RevitAPI. Bên trái là các đối tượng được chọn bên Revit, còn bên phải là các method() và properties của các đối tượng.

Bây giờ suy nghĩ thuật toán một tí để tra cứu cho hiệu quả nè ! cái chúng ta đang đi tìm là tên của các đối tượng được chọn bên Revit phải không? muốn vậy chúng ta phải biết được type của nó là gì cái đã. Nhìn vào sơ đồ cấp bậc của các đối tượng trong Revit bên dưới, chúng ta thấy rằng chỉ ở cấp type chúng ta mới biết được tên của đối tượng Revit.

Mà Revit Type thì tương đương với Symbol trong RevitAPI (trong RevitAPI người viết ra API họ không gọi nó là type mà gọi nó là symbol).

Vậy chúng ta sẽ click vào dòng symbol in đậm trong Snoop. Lưu ý rằng dòng nào in đậm là khi click vào nó là còn thông tin bên trong.

Kéo xuống chúng ta thấy dòng FamilyName, nhìn sang cột value chúng ta thấy luôn tên của đối tượng này. Vậy đó, đây là cách mà chúng ta truy ra được cách để lấy được tên của element qua dòng code element.Symbol.FamilyName

Có một lưu ý nho nhỏ là TaskDialog.Show() nằm trong vòng lặp for nên bạn chọn ít đối tượng bên Revit thôi, chứ không là khi Run add-in nó pops up một mớ bảng tên đối tượng bạn tắt đuối luôn =.=

for id in selectionids :
	element = doc.GetElement(id)
	TaskDialog.Show("BCDD 2022", element.Symbol.FamilyName)

2. Tạo Add-in từ pyRevit

Tiến hành tạo thư mục pushbutton cho add-in thôi nào. Sau đó save as code python ở trên vào và đổi tên là script.py. Icon cũng phải đổi tên thành icon.png

Tiếp đến vào pyRevit settings reload lại đường dẫn

Tá đa ! Add-in thứ hai xuất hiện rồi =.=

Bắt đầu chạy add-in thôi.

3. Tối ưu python code

Dưới đây là đoạn code sau khi tối ưu, nó ngắn hơn đoạn code lúc đầu một xíu. Viết tới đây tự nhiên admin mệt quá và làm biếng giải thích thêm nên quyết định đi ngủ =.= 

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

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document

selectionids = uidoc.Selection.GetElementIds()

for id in selectionids :
	element = doc.GetElement(id)
	TaskDialog.Show("BCDD 2022", element.Symbol.FamilyName)

4. Phương thức Pick Object(s)

Lại là admin đây, sau khi ngủ dậy admin cảm thấy có lỗi với đọc giả quá nên lại lọ mọ viết tiếp. Tính ra thì admin mất đến hai ngày để viết bài này đấy.

Ở trên chúng ta đã làm quen và tạo được add-in từ Selection để lấy thông tin từ đối tượng được chọn. Giờ thì chúng ta sẽ làm ngược lại bằng cách Run add-in trước rồi pick chọn đối tượng trong Revit sau. Chúng ta có thể chỉ chọn được một hoặc nhiều đối tượng phụ thuộc vào phương thức chúng ta dùng là PickObject hay PickObjects.

Tra RevitAPI thì chúng ta thấy PickObject cần có một argument là ObjectType

Lại tra RevitAPI, chúng ta thấy rằng ObjectType là một Enumeration mà giang hồ hay gọi là enum (kiểu dữ liệu cố định) chỉ chứa một danh sách giá trị cố định.

Chúng ta sẽ sử dụng ObjectType.Element

Tuy nhiên để sử dụng được enum này thì chúng ta phải import ObjectType từ namespace Autodesk.Revit.UI.Selection

from Autodesk.Revit.UI.Selection import ObjectType

Quay lại RevitAPI để tra cứu phương thức PickObject() thì thấy ở dưới dòng Syntax nó trả về dưới dạng Reference chứ không phải element hay elementid như mọi khi.

Nhưng không sao cả, phương thức GetElement() mà chúng ta đã sử dụng cũng xài được với Reference

Vậy là tới đây chúng ta đã có thể lấy được các đối tượng trong revit bằng cách pick rồi, tiến hành gán kết quả trả về Reference cho biến selectionReference. Rồi lấy element nhờ vào method GetElement, tiến hành gán cho biến selectionElement 

selectionReference = uidoc.Selection.PickObject(ObjectType.Element)
selectionElement = doc.GetElement(selectionReference)

Trường hợp đầu tiên BCDD dùng PickObject nên không cần chạy vòng lặp. Code hoàn chỉnh như sau:

from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Selection import ObjectType

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document

selectionReference = uidoc.Selection.PickObject(ObjectType.Element)
selectionElement = doc.GetElement(selectionReference)

TaskDialog.Show("BCDD 2022", selectionElement.Symbol.FamilyName

Test code trên RevitPythonShell thì thấy chạy ok.

Tương tự cho phương thức PickObjects (cho phép pick được nhiều đối tượng) chúng ta chỉ cần chạy vòng lặp for. Cần lưu ý nhỏ là PickObjects trả về một list chứ không phải Reference như PickObject.

from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Selection import ObjectType

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document

selectionids = uidoc.Selection.PickObjects(ObjectType.Element)

for id in selectionids:
	element = doc.GetElement(id)
	TaskDialog.Show("BCDD 2022", element.Symbol.FamilyName)

Test nhanh bằng RevitPythonShell và thấy ok.

5. Phương thức PickElementsByRectangle()

Cảm thấy pick lâu quá nên giờ chuyển sang quét đối tượng bằng phương thức này. Nhưng trước khi làm gì thì làm, chúng ta import thư viện và khai báo biến cái đã.

from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Selection import ISelectionFilter

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document

Vì chúng ta sẽ quét chọn đối tượng nên sẽ dùng phương thức PickElementsByRectangle(). Khi tra RevitAPI thì thấy có đến 4 phương thức với các argument khác nhau. Trong bài viết này BCDD sẽ sử dụng cái thứ 3, tức là PickElementsByRectangle(ISelectionFilter)

Để sử dụng phương thức này chúng ta cần phải có ISelectionFilter (cung cấp khả năng lọc các đối tượng trong một thao tác chọn: quét chọn hay pick chọn). Trong trường hợp này BCDD quét chọn các đối tượng cột kết cấu (structural columns).

class CustomISelectionFilter(ISelectionFilter):
    def __init__(self, nom_categorie):
        self.nom_categorie = nom_categorie
    def AllowElement(self, e):
        if e.Category.Name == self.nom_categorie:
            return True
        else:
            return False
    def AllowReference(self, ref, point):
        return true
        
selection_list = []      
filter1 = CustomISelectionFilter('Structural Columns')
pickedobjects = uidoc.Selection.PickElementsByRectangle(filter1)

Đầu ra là các đối tượng cột nằm trong list pickedobjects. Tiến hành chạy vòng lặp for để duyệt các cột nằm trong list pickedobjects rồi dùng phương thức TaskDialog.Show() để show tên của các đối tượng cột.

for i in pickedobjects:
	element_name = i.Name
	TaskDialog.Show("BCDD 2022", element_name)

Kết quả test nhanh ở RevitPythonShell như sau

Ok tới đây thì đọc giả hoàn toàn tự tạo add-in như các ví dụ trước được rồi.

Nếu thấy bài viết này hữu ích, hãy cân nhắc việc ủng hộ BCDD tại đây

Bài viết do admin 1 Nghia Nguyen và cộng sự BCDD biên soạn.