Blog / Python for Architects: Practical Scripting in Rhino and Revit (With Real Examples)

Python for Architects: Practical Scripting in Rhino and Revit (With Real Examples)

A practical guide to Python scripting for architects - real code examples for Rhino and Revit, what to learn first, and when scripting saves time.

A
Archgyan Editor
· 9 min read

Go deeper with Archgyan Academy

Structured BIM and Revit learning paths for architects and students.

Explore Academy →

Python is the most useful programming language an architect can learn. Not because you need to become a programmer, but because 20 lines of Python can replace 2 hours of repetitive clicking in Rhino or Revit.

This guide covers practical scripting for both platforms - with real code you can copy, modify, and use on your projects today. No computer science background required.


Why Python (And Not Some Other Language)

Python is the default scripting language for both Rhino and Revit because:

FactorPythonOther Options
Rhino supportBuilt-in (RhinoPython, GhPython)C#, VB.NET (more verbose)
Revit supportpyRevit, Dynamo Python nodesC# add-ins (steeper learning curve)
ReadabilityReads almost like EnglishC# requires more syntax knowledge
Learning curveDays to write useful scriptsWeeks for equivalent C#
Community resourcesLargest architecture scripting communityFewer tutorials and examples
TransferableWorks in Blender, QGIS, data analysisPlatform-specific

You don’t need to “learn Python” in the abstract. You need to learn enough Python to do specific things in Rhino or Revit. That’s a much smaller task.


Python in Rhino: Where to Write Code

Rhino offers three places to run Python:

1. Script Editor (Rhino 8+)

The new Script Editor in Rhino 8 replaces the old EditPythonScript window. Access it via Tools > Script Editor or type ScriptEditor in the command line.

  • Full code editor with autocomplete
  • Supports Python 3 (via CPython) and IronPython 2
  • Can create commands that appear in Rhino’s toolbar

2. GhPython Component (Grasshopper)

Drag a GhPython Script component onto the Grasshopper canvas. This lets you mix visual programming with custom Python code - useful when a standard Grasshopper node doesn’t do what you need.

3. Command Line (Quick Scripts)

Type RunPythonScript to run a saved .py file. Good for scripts you’ve already written and want to execute quickly.


Rhino Python: 5 Practical Examples

Example 1: Select All Short Walls and Highlight Them

Find all surfaces below a certain height - useful for identifying parapet walls or low partitions:

import rhinoscriptsyntax as rs

threshold = 1200  # mm

all_surfaces = rs.ObjectsByType(8)  # 8 = surface objects
short_ones = []

for srf in all_surfaces:
    bbox = rs.BoundingBox(srf)
    if bbox:
        height = bbox[4][2] - bbox[0][2]  # Z difference
        if height < threshold:
            short_ones.append(srf)

rs.SelectObjects(short_ones)
print(f"Found {len(short_ones)} surfaces shorter than {threshold}mm")

Example 2: Distribute Objects Along a Curve

Place copies of a block at equal intervals along any curve - for fencing, lighting, or trees:

import rhinoscriptsyntax as rs

curve = rs.GetObject("Select curve", 4)
block = rs.GetObject("Select object to distribute", 0)
count = rs.GetInteger("Number of copies", 10, 2, 500)

if curve and block and count:
    params = [i / (count - 1.0) for i in range(count)]
    for t in params:
        pt = rs.EvaluateCurve(curve, rs.CurveParameter(curve, t))
        rs.CopyObject(block, [0, 0, 0], pt)

Example 3: Export All Layer Names and Object Counts

Generate a quick audit of your model’s organisation:

import rhinoscriptsyntax as rs

layers = rs.LayerNames()
print("Layer Name | Object Count | Colour")
print("-" * 50)

for layer in sorted(layers):
    objects = rs.ObjectsByLayer(layer)
    count = len(objects) if objects else 0
    colour = rs.LayerColor(layer)
    if count > 0:
        print(f"{layer} | {count} | {colour}")

Example 4: Create a Grid of Points

Generate a point grid for panel layouts, structural grids, or landscape planting:

import rhinoscriptsyntax as rs

cols = 10
rows = 8
spacing = 1500  # mm

for x in range(cols):
    for y in range(rows):
        rs.AddPoint(x * spacing, y * spacing, 0)

print(f"Created {cols * rows} points at {spacing}mm spacing")

Example 5: Rename All Blocks with a Prefix

Batch-rename blocks for file organisation:

import rhinoscriptsyntax as rs

prefix = "SITE_"
blocks = rs.BlockNames()

for name in blocks:
    if not name.startswith(prefix):
        rs.RenameBlock(name, prefix + name)
        print(f"Renamed: {name} -> {prefix}{name}")

Python in Revit: The Options

Revit Python scripting works differently from Rhino. You can’t just open a script editor inside Revit - you need a bridge:

ToolWhat It IsBest For
pyRevitFree add-in with a Python script runner and pre-built toolsCustom toolbar buttons, automation scripts
Dynamo Python nodesPython Script node inside DynamoMixing visual programming with custom code
RevitPythonShellInteractive Python console in RevitQuick experiments, one-off queries
Revit API (C# add-in)Full API access via compiled add-inProduction tools (not Python, but worth knowing about)

Recommended starting point: Install pyRevit. It’s free, actively maintained, and comes with dozens of useful scripts you can use immediately while learning to write your own.


Revit Python: 5 Practical Examples

These examples use pyRevit’s environment (IronPython with Revit API access):

Example 1: List All Rooms with Areas

from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory

doc = __revit__.ActiveUIDocument.Document

rooms = FilteredElementCollector(doc) \
    .OfCategory(BuiltInCategory.OST_Rooms) \
    .WhereElementIsNotElementType() \
    .ToElements()

print("Room Number | Room Name | Area (sqm)")
print("-" * 50)

for room in rooms:
    number = room.Number
    name = room.get_Parameter(
        BuiltInParameter.ROOM_NAME).AsString()
    area = room.get_Parameter(
        BuiltInParameter.ROOM_AREA).AsDouble() * 0.0929  # sqft to sqm
    print(f"{number} | {name} | {area:.1f}")

Example 2: Rename Views by Adding a Prefix

from Autodesk.Revit.DB import (
    FilteredElementCollector, Transaction, ViewType
)

doc = __revit__.ActiveUIDocument.Document

views = FilteredElementCollector(doc) \
    .OfClass(View) \
    .ToElements()

floor_plans = [v for v in views
               if v.ViewType == ViewType.FloorPlan
               and not v.IsTemplate]

t = Transaction(doc, "Rename Floor Plans")
t.Start()

for view in floor_plans:
    old_name = view.Name
    if not old_name.startswith("FP_"):
        view.Name = "FP_" + old_name

t.Commit()
print(f"Renamed {len(floor_plans)} floor plans")

Example 3: Find Doors Missing Mark Values

from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory

doc = __revit__.ActiveUIDocument.Document

doors = FilteredElementCollector(doc) \
    .OfCategory(BuiltInCategory.OST_Doors) \
    .WhereElementIsNotElementType() \
    .ToElements()

missing = []
for door in doors:
    mark = door.get_Parameter(
        BuiltInParameter.ALL_MODEL_MARK).AsString()
    if not mark or mark.strip() == "":
        missing.append(door.Id)

print(f"Found {len(missing)} doors without Mark values")
print("Element IDs:", [str(m) for m in missing])

Example 4: Export Wall Schedule Data

from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
import csv

doc = __revit__.ActiveUIDocument.Document

walls = FilteredElementCollector(doc) \
    .OfCategory(BuiltInCategory.OST_Walls) \
    .WhereElementIsNotElementType() \
    .ToElements()

data = []
for wall in walls:
    wall_type = wall.WallType.get_Parameter(
        BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString()
    length = wall.get_Parameter(
        BuiltInParameter.CURVE_ELEM_LENGTH).AsDouble() * 0.3048  # ft to m
    data.append([wall_type, f"{length:.2f}"])

# Write to CSV
with open("C:/temp/wall_data.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["Wall Type", "Length (m)"])
    writer.writerows(data)

print(f"Exported {len(data)} walls to CSV")

Example 5: Count Elements by Category

Quick model audit showing what’s in your project:

from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory

doc = __revit__.ActiveUIDocument.Document

categories = [
    ("Walls", BuiltInCategory.OST_Walls),
    ("Doors", BuiltInCategory.OST_Doors),
    ("Windows", BuiltInCategory.OST_Windows),
    ("Rooms", BuiltInCategory.OST_Rooms),
    ("Floors", BuiltInCategory.OST_Floors),
]

print("Category | Count")
print("-" * 30)

for name, cat in categories:
    count = FilteredElementCollector(doc) \
        .OfCategory(cat) \
        .WhereElementIsNotElementType() \
        .GetElementCount()
    print(f"{name} | {count}")

What to Learn First

Don’t try to learn “all of Python.” Learn just enough to be productive:

Week 1-2: Python Basics

ConceptWhy You Need ItExample
VariablesStore valuesheight = 3000
ListsCollections of itemsrooms = ["Office", "Kitchen"]
For loopsDo something to every itemfor room in rooms:
If/elseMake decisionsif area > 100:
FunctionsReusable code blocksdef get_area(width, length):
PrintSee resultsprint(f"Area: {area}")

Week 3-4: Platform-Specific

For Rhino: Learn rhinoscriptsyntax - it’s a simplified wrapper around RhinoCommon. Start by automating something you do frequently (selecting objects by type, exporting data, creating geometry).

For Revit: Install pyRevit and read existing scripts in its library. Learn the FilteredElementCollector pattern - it’s the foundation of nearly every Revit script.

Month 2+: Build a Library

Start saving your scripts in a folder. After a month, you’ll have 5-10 scripts that save you real time. After six months, your personal script library becomes one of your most valuable professional tools.


When Scripting Saves Time (And When It Doesn’t)

ScenarioScript?Why
Rename 200 viewsYes5 minutes vs 2 hours
Extract room data to ExcelYesRepeatable, error-free
Create one section viewNoFaster manually
Generate a panel grid from parametersYesImpossible to do accurately by hand at scale
Place one doorNoJust click
Audit model for missing dataYesCatches what manual checking misses
Quick design sketchNoScripting adds overhead
Process 50 DWG filesYesBatch operations are scripting’s strength

The rule: if you’ll do the task more than 10 times, or if it involves processing data at scale, write a script. If it’s a one-off creative task, use the GUI.


Free Learning Resources

ResourcePlatformWhat You Learn
Rhino Developer DocsRhinoRhinoPython and RhinoCommon reference
pyRevit DocumentationRevitScript examples and API patterns
Automate the Boring Stuff (book)GeneralPython fundamentals (free online)
The Dynamo PrimerRevitPython nodes in Dynamo
McNeel ForumRhinoCommunity Q&A with code examples
DesignalyzeRhinoTutorials specific to architecture

Want to develop your computational design skills? The Archgyan Academy offers courses in Revit, BIM workflows, and design automation for architects and students.

Level up your skills

Ready to learn hands-on?

  • Project-based Revit & BIM courses for architects
  • Go from beginner to confident professional
  • Video lessons you can follow at your own pace
Explore Archgyan Academy
← Back to Blog