Wat is pyRevit — en wat is deze toolbar?
pyRevit is een gratis uitbreiding op Revit waarmee scripts als gewone ribbon-knoppen beschikbaar komen. Elke knop in de toolbar is een klein Python-script dat via de Revit API met het geopende model praat: elementen verzamelen, parameters lezen of schrijven, views en sheets aanmaken.
De BDJ Toolbar bundelt de scripts die het repeterende tekenkamerwerk overnemen — taggen, opschonen, views en sheets opzetten. Deze handleiding legt uit hoe de toolbar in elkaar zit en hoe je er zelf, veilig, een knop aan toevoegt.
pyRevit draait scripts standaard in IronPython 2.7. Dat is een ouder Python-dialect: moderne f"..."-strings werken er níet, en het delen van twee gehele getallen rondt af naar beneden. In hoofdstuk 07 staan de valkuilen op een rij.
Installatie & de toolbar koppelen
2.1 pyRevit installeren
- Download de nieuwste installer van github.com/pyrevitlabs/pyRevit/releases en installeer met de standaardinstellingen.
- Start Revit opnieuw. Er verschijnt een tabblad pyRevit in de ribbon.
2.2 De toolbar-extensie koppelen
- Zorg dat je de map van de toolbar-extensie hebt (de map die eindigt op
.extension), lokaal of op de netwerkschijf. - Ga naar pyRevit → Settings → Custom Extension Directories, voeg de map toe die de
.extension-map bevat en klik Save Settings + Reload. - Het toolbar-tabblad verschijnt in de ribbon. Zie je het niet: controleer of je de bovenliggende map hebt gekozen, niet de
.extension-map zelf.
Na élke wijziging aan een script of mapnaam: pyRevit → Reload. Revit hoeft niet opnieuw op te starten — reload duurt een paar seconden en je wijziging is actief.
Mappenstructuur: mappen wórden knoppen
pyRevit bouwt de ribbon rechtstreeks uit de mappenstructuur op schijf. De naam van de map bepaalt de tekst in Revit; de extensie van de mapnaam bepaalt het type. Zo ziet de opbouw eruit:
3.1 De bundeltypes die wij gebruiken
| Mapextensie | Wat het oplevert |
|---|---|
.pushbutton | Een gewone knop die script.py uitvoert. Het werkpaard van de toolbar. |
.pulldown | Een uitklapmenu; de .pushbutton-mappen erbínnen worden de menu-items (zo zijn de Tags- en Purge-menu's gebouwd). |
.urlbutton | Een knop die een URL of document opent — bijvoorbeeld deze handleiding. Alleen een bundle.yaml met een hyperlink:-regel nodig. |
.smartbutton | Een knop die zijn eigen icoon/status kan bijwerken op basis van het model (zoals de sections-toggle met drie standen). |
.stack | Stapelt 2–3 knoppen verticaal binnen een paneel om ruimte te besparen. |
3.2 Naamgeving
- Panelen krijgen een nummerprefix (
01_,02_…) — pyRevit sorteert alfabetisch, dus zo bepaal je de volgorde in de ribbon. - Knopmappen krijgen de Nederlandse knoptekst als naam; een
\nin__title__breekt de tekst op de knop af over twee regels. - Bestandsnamen en code: Engels. Teksten die de gebruiker ziet: Nederlands.
Hernoem of verplaats je een map, doe dan altijd een Reload en controleer of de knop nog werkt — een typfout in .pushbutton of een ontbrekende script.py laat de knop stilletjes uit de ribbon verdwijnen.
Anatomie van een script — het vaste sjabloon
Elk script in de toolbar volgt hetzelfde stramien, van boven naar beneden: header → imports → variabelen → config → main → rapportage. Wie één script kan lezen, kan ze daardoor allemaal lezen. Dit is het volledige, generieke sjabloon:
# -*- coding: utf-8 -*-
__title__ = "Knopnaam"
__author__ = "Bram de Jong"
__version__ = "1.0"
__doc__ = """Version: 1.0
Date: 14 juli 2026
Author: Bram de Jong (BDJ)
__________________________________________________________
Description:
[NL] Korte omschrijving van wat het script doet.
__________________________________________________________
How-to:
-> Stap 1
-> Stap 2
__________________________________________________________
"""
# IMPORTS
# ==================================================
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.DB import *
from pyrevit import revit, forms, script
# VARIABLES
# ==================================================
doc = revit.doc
uidoc = revit.uidoc
output = script.get_output()
# CONFIG (benoemde constanten - nooit losse getallen in de logica)
# ==================================================
SOME_LIMIT_MM = 1200
# MAIN
# ==================================================
elements = FilteredElementCollector(doc)\
.OfCategory(BuiltInCategory.OST_DuctCurves)\
.WhereElementIsNotElementType().ToElements()
if not elements:
forms.alert("Niets gevonden.", exitscript=True)
changed, errors = 0, []
# context manager -> automatische rollback bij een fout
with revit.Transaction("Omschrijving van de actie"):
for el in elements:
try:
# ... hier de bewerking per element ...
changed += 1
except Exception as e:
errors.append((el.Id, str(e)))
# REPORT
# ==================================================
output.print_md("## Resultaat")
output.print_md("- Aangepast: **{}**".format(changed))
output.print_md("- Fouten: {}".format(len(errors)))
for eid, msg in errors:
output.print_md("- {} -> {}".format(output.linkify(eid), msg))4.1 Waarom elk blok er staat
| Blok | Functie |
|---|---|
| Header | __title__ wordt de knoptekst, __doc__ de tooltip. De eerste regel (utf-8) is verplicht zodra er een é of ë in je teksten staat. |
| Imports | Vast blok: Revit API via clr, en revit, forms, script uit pyRevit. Geen Dynamo-referenties (RevitServices) — die horen hier niet. |
| Variables | doc, uidoc en output altijd bovenaan, zodat elk script dezelfde ingangen heeft. |
| Config | Alle grenswaarden en namen als benoemde constante. Aanpassen kan dan zonder in de logica te graven. |
| Main | Verzamelen → controleren → wijzigen. Wijzigen gebeurt altijd binnen with revit.Transaction("..."):. |
| Report | Resultaten naar het output-paneel met output.print_md; pop-ups alleen voor echte fouten of bevestigingen. |
De vijf gouden regels
Het doel achter alle conventies is één ding: een script mag Revit nooit laten vastlopen bij een collega. Deze vijf regels borgen dat:
- Wijzigen alléén via de context manager —
with revit.Transaction("Nederlandse omschrijving"):. Gaat er iets mis, dan rolt Revit automatisch terug in plaats van in een open transactie te blijven hangen. - Per element
try/except Exception as e:in elke loop met een risicovolle actie. Eén kapot element mag nooit de hele batch afbreken; verzamel fouten en rapporteer ze. - Cancel-check na elke keuzelijst. Klikt iemand op Annuleren, dan geeft
forms.SelectFromList.show(...)Noneterug — controleer daarop en stop netjes metscript.exit(). - Rapportage via
output.print_md, niet via een pop-up per actie. Eén overzicht aan het einde, metoutput.linkify(element_id)zodat je naar elementen kunt doorklikken. - Benoemde constanten in het CONFIG-blok — geen magische getallen of familienamen verstopt in de logica.
Draait een script twee keer achter elkaar zonder dubbele resultaten, meldt het netjes wat het heeft overgeslagen, en overleeft het een klik op Annuleren — dan voldoet het aan de huisstijl.
Oefeningen
Drie opdrachten, oplopend in moeilijkheid. Werk in een eigen oefen-extensie (hoofdstuk 03) en test op een kopie van een project — nooit op het echte model.
Maak in je oefen-extensie de map HalloTekenkamer.pushbutton met een script.py die het volledige header-blok uit hoofdstuk 04 heeft en één melding toont:
from pyrevit import forms
forms.alert("Hallo tekenkamer! Dit is mijn eerste knop.")Doe een Reload en controleer: staat de knop in de ribbon, en toont de tooltip jouw __doc__?
Maak een tweede knop die de naam van het geopende model toont:
from pyrevit import revit, forms
doc = revit.doc
forms.alert("Je werkt nu in: {}".format(doc.Title))Bonus: vervang doc.Title eens door doc.PathName en bekijk het verschil.
Bouw een knop die telt hoeveel ducts er in de actieve view zichtbaar zijn en het resultaat in het output-paneel zet:
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
from pyrevit import revit, script
doc = revit.doc
output = script.get_output()
ducts = FilteredElementCollector(doc, doc.ActiveView.Id)\
.OfCategory(BuiltInCategory.OST_DuctCurves)\
.WhereElementIsNotElementType().ToElements()
output.print_md("## Telling actieve view")
output.print_md("- Ducts gevonden: **{}**".format(len(ducts)))Bonus: tel óók de duct fittings (OST_DuctFitting) en zet beide tellingen in het rapport. Dit is precies het lees-en-rapporteer-patroon waarop de meeste toolbar-knoppen zijn gebouwd.
Problemen oplossen
7.1 De knop verschijnt niet
- Mapnaam eindigt exact op
.pushbutton(of ander geldig type) en bevat eenscript.py. - De bovenliggende structuur klopt:
.extension → .tab → .panel → .pushbutton. - Je hebt pyRevit → Reload gedaan na de wijziging.
7.2 Rode foutmelding (traceback)
Lees een traceback altijd van onder naar boven: de onderste regel zegt wat er misging, de regels erboven waar. De drie IronPython 2.7-klassiekers:
| Symptoom | Oorzaak & oplossing |
|---|---|
SyntaxError op een regel met f"..." | F-strings bestaan niet in IronPython 2.7 — herschrijf naar "...{}".format(x). |
| Een deling geeft geruisloos een te laag geheel getal | Twee gehele getallen delen rondt af naar beneden: 1850 / 1000 → 1. Maak er één een kommagetal van: / 1000.0. |
| Rare tekens of een fout bij é/ë in teksten | # -*- coding: utf-8 -*- moet de allereerste regel van het script zijn. |
7.3 "Er gebeurt niets"
Controleer het output-paneel (verschijnt achter het Revit-venster) — de meeste knoppen rapporteren daar in plaats van met een pop-up. Vindt een script niets om te doen, dan meldt het dat daar ook.
Test elke nieuwe of aangepaste knop eerst op een kopie van een echt project — niet alleen op een leeg testmodel, en nooit rechtstreeks op productiewerk.