agentleFS
VisionXt-techVisionXt-tech/bim-skills/skills/bim-tools/pyrevit/SKILL.md

pyrevit

Skill1 starsChanged 2 months agoMITView on GitHub

Sviluppo rapido ed enterprise di estensioni BIM, comandi Ribbon e script Python (CPython 3 / PythonNet) per Autodesk Revit tramite pyRevit. Usare per pushbutton, smartbutton, menu pulldown, interfacce dinamiche pyrevit.forms/XAML, hook di evento (pre-sync, pre-save), report interattivi linkify, integrazione librerie Python (pandas, openpyxl, requests) e deployment enterprise con pyRevit 6.x.

The file

Files
SKILL.md

BIM pyRevit Extension Development & CPython 3 Automation

Guida specialistica per lo sviluppo rapido ed enterprise di estensioni, pulsanti Ribbon e script di automazione per Autodesk Revit tramite il framework open-source pyRevit, utilizzando l'engine moderno CPython 3 (PythonNet 3). Copre l'architettura dei bundle, la gestione delle transazioni, la creazione di interfacce utente native con pyrevit.forms e XAML, gli hook di validazione pre-salvataggio e la generazione di report interattivi collegati direttamente agli elementi del modello. I contenuti sono allineati alla linea pyRevit 6.x (loader riscritto in C#).


Scope

Questa skill fornisce pattern architetturali e codice production-ready per:

  • Architettura dei Bundle pyRevit: strutturazione gerarchica di .extension, .tab, .panel, .pushbutton, .smartbutton, .pulldown, .splitbutton e .urlbutton.
  • Engine CPython 3: supporto alla sintassi moderna Python 3.10+, type hints, pathlib, f-string e importazione di moduli esterni da virtualenv (pandas, openpyxl, requests).
  • Gestione Transazionale e Modifiche al DB: utilizzo del context manager revit.Transaction e revit.TransactionGroup con assimilazione delle modifiche.
  • Interfacce Utente Dinamiche (pyrevit.forms): finestre di dialogo standard (SelectFromList, CommandSwitchWindow, alert, pick_file, ProgressBar) e GUI custom XAML (forms.WPFWindow).
  • Reporting Avanzato e Navigazione Modello (pyrevit.script): finestre di output formattate in Markdown/HTML, tabelle interattive e collegamenti ipertestuali dinamici agli elementi del modello (output.linkify(element_id)).
  • Automazione degli Eventi (Event Hooks): intercettazione di eventi Revit (doc-opened, doc-saving, doc-synced) per controlli di qualità bloccanti pre-sincronizzazione.
  • Configurazione e Metadati: gestione di bundle.yaml (contesti di attivazione, icone, tooltip, autorizzazioni) e extension.json.
  • Deployment Enterprise (pyRevit 6.x): installazione per tutti gli utenti, comportamento prevedibile degli installer e allineamento al runtime .NET della versione di Revit ospite.

NON fa

  • Non genera codice legacy per IronPython 2.7 (linguaggio e runtime deprecati, privi di supporto alle moderne librerie Python).
  • Non esegue modifiche dirette al database Revit al di fuori di un blocco with revit.Transaction.
  • Non compila assembly DLL binarie (esegue script interpretati al volo da pyRevit).

Versioni pyRevit e Runtime di Revit

ElementoStato alla revisione (set. 2026)
Linea correntepyRevit 6.x (ultima release verificata: 6.5.4, ago. 2026)
Cambiamento principale della 6.0.0 (gen. 2026)Loader riscritto in C#: il codice Python viene analizzato e tradotto in codice eseguibile da Revit
Supporto RevitRevit 2026 supportato dalla 5.1.0 (apr. 2025); la 6.0.0 anticipa la posizione di installazione sicura degli add-in di Revit 2027
LocalizzazioneUI tradotta in 7 lingue (inglese, francese, tedesco, spagnolo, cinese, russo, portoghese)
pyrevit.formsBackend separati per IronPython e CPython

[!IMPORTANT] Il runtime .NET non si deduce dall'anno di Revit. Revit 2025.5 e 2026.5 girano su .NET 10 mentre gli update precedenti degli stessi anni sono su .NET 8. Uno script o un deployment che sceglie assembly e dipendenze in base al solo anno di Revit può agganciare il runtime sbagliato: leggere il numero di update/build. Verificare sempre sulle release note di pyRevit quale build copre la versione di Revit installata prima di distribuire un'estensione in azienda.


Struttura Gerarchica dell'Estensione pyRevit

NomeEstensione.extension/
├── extension.json                    # Metadati globali dell'estensione
├── lib/                              # Moduli e librerie Python condivise
│   ├── __init__.py
│   ├── db_queries.py                 # Query e filtri riutilizzabili
│   └── excel_exporter.py
├── hooks/                            # Script eseguiti su eventi globali di Revit
│   ├── doc-opened.py                 # Eseguito all'apertura del modello
│   └── doc-saving.py                 # Eseguito prima del salvataggio (controllo QA/QC)
└── NomeEstensione.tab/                # Scheda Ribbon personalizzata
    └── QualitaDati.panel/            # Pannello Ribbon
        ├── ValidaParametri.pushbutton/
        │   ├── bundle.yaml           # Configurazione comando e contesto
        │   ├── icon.png              # Icona 32x32 o 16x16 PNG
        │   └── script.py             # Codice Python del comando
        ├── StrumentiAvanzati.pulldown/
        │   ├── SottoComandoA.pushbutton/
        │   │   └── script.py
        │   └── SottoComandoB.pushbutton/
        │       └── script.py
        └── ToggleControllo.smartbutton/
            ├── icon.png
            └── script.py             # Bottone bistabile con stato on/off

Pattern di Programmazione Fondamentali

1. Script PushButton Standard con Transazione e Filtro Nativo

# -*- coding: utf-8 -*-
"""
Nome: Popola Parametri WBS
Descrizione: Assegna il codice WBS a tutti i muri del livello selezionato.
Autore: BIM Skills Italia
"""

from pyrevit import revit, DB, forms, script

# Inizializzazione contesto
doc = revit.doc
output = script.get_output()

# 1. Selezione del Livello tramite interfaccia grafica
levels = DB.FilteredElementCollector(doc)\
    .OfClass(DB.Level)\
    .WhereElementIsNotElementType()\
    .ToElements()

level_dict = {lvl.Name: lvl for lvl in levels}
selected_level_name = forms.SelectFromList.show(
    sorted(level_dict.keys()),
    title="Seleziona Livello di Progetto",
    multiselect=False
)

if not selected_level_name:
    script.exit()

target_level = level_dict[selected_level_name]

# 2. Input del Codice WBS
wbs_code = forms.ask_for_string(
    default="WBS.STR.01",
    prompt=f"Inserisci il codice WBS per il livello {selected_level_name}:",
    title="Input WBS"
)

if not wbs_code:
    script.exit()

# 3. Raccolta Muri del Livello con Filtro Nativo Rapido
level_filter = DB.ElementLevelFilter(target_level.Id)
walls = DB.FilteredElementCollector(doc)\
    .OfCategory(DB.BuiltInCategory.OST_Walls)\
    .WhereElementIsNotElementType()\
    .WherePasses(level_filter)\
    .ToElements()

if not walls:
    forms.alert(f"Nessun muro trovato al livello {selected_level_name}.", title="Attenzione")
    script.exit()

# 4. Modifica Transazionale con Barra di Avanzamento
updated_count = 0
with revit.Transaction(f"Assegna WBS al livello {selected_level_name}"):
    with forms.ProgressBar(title="Aggiornamento elementi... ({value} di {max_value})", cancellable=True) as pb:
        for i, wall in enumerate(walls):
            if pb.cancelled:
                break

            param = wall.LookupParameter("Codice_WBS")
            if param and not param.IsReadOnly:
                param.Set(wbs_code)
                updated_count += 1

            pb.update_progress(i + 1, len(walls))

# 5. Output Report Formattato con Link Interattivo agli Elementi
output.print_md(f"# Risultato Operazione: {selected_level_name}")
output.print_md(f"**Elementi aggiornati con successo:** {updated_count} / {len(walls)}")

report_rows = []
for wall in walls[:10]:  # Mostra i primi 10
    report_rows.append([
        output.linkify(wall.Id),  # Cliccando sul link l'elemento viene selezionato in Revit
        wall.Name,
        wbs_code
    ])

output.print_table(
    table_data=report_rows,
    columns=["Element ID", "Nome Famiglia/Tipo", "Codice WBS Assegnato"],
    title="Anteprima Elementi Modificati"
)

2. Creazione di Finestre di Dialogo Custom in XAML/WPF (forms.WPFWindow)

Quando i form preconfezionati non bastano, pyRevit consente di collegare un file XAML nativo a una classe Python:

File XAML (UIWindow.xaml):

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Configuratore Parametri CAM" Height="220" Width="360"
        WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
    <Grid Margin="15">
        <StackPanel>
            <TextBlock Text="Percentuale Contenuto Riciclato Minimo (%):" FontWeight="Bold" Margin="0,0,0,5"/>
            <TextBox x:Name="txtRecycled" Text="15.0" Margin="0,0,0,15"/>
            
            <CheckBox x:Name="chkDisassembly" Content="Imponi disassemblabilità >= 70%" IsChecked="True" Margin="0,0,0,20"/>
            
            <Button x:Name="btnApply" Content="Applica Modifiche" Height="30" Click="on_apply_clicked"/>
        </StackPanel>
    </Grid>
</Window>

File Python (script.py):

from pyrevit import forms, revit, DB

class CamConfigWindow(forms.WPFWindow):
    def __init__(self, xaml_file_name):
        forms.WPFWindow.__init__(self, xaml_file_name)
        self.recycled_value = None

    def on_apply_clicked(self, sender, args):
        try:
            self.recycled_value = float(self.txtRecycled.Text)
            self.disassembly_required = self.chkDisassembly.IsChecked
            self.Close()
        except ValueError:
            forms.alert("Inserire un valore numerico valido.", title="Errore")

win = CamConfigWindow("UIWindow.xaml")
win.ShowDialog()

if win.recycled_value is not None:
    forms.alert(f"Valore impostato: {win.recycled_value}%", title="Successo")

3. Event Hook Pre-Salvataggio: Controllo QA/QC Bloccante (doc-saving.py)

Gli hook consentono di imporre la conformità aziendale o di commessa. Se posizionato in hooks/doc-saving.py, questo script intercetta ogni comando di salvataggio:

from pyrevit import EXEC_PARAMS, DB, forms

doc = EXEC_PARAMS.event_args.Document

# Controlla se esistono muri senza parametro "Codice_WBS"
unassigned_walls = []
walls = DB.FilteredElementCollector(doc)\
    .OfCategory(DB.BuiltInCategory.OST_Walls)\
    .WhereElementIsNotElementType()\
    .ToElements()

for w in walls:
    p = w.LookupParameter("Codice_WBS")
    if not p or not p.HasValue or not p.AsString().strip():
        unassigned_walls.append(w.Id)

if unassigned_walls:
    # Mostra allerta all'utente
    res = forms.alert(
        f"Attenzione! Rilevati {len(unassigned_walls)} muri privi di parametro 'Codice_WBS'.\n"
        "Procedere comunque con il salvataggio?",
        title="Controllo Qualità Informativa",
        yes=True, no=True
    )
    if not res:
        # Cancella l'operazione di salvataggio in Revit
        EXEC_PARAMS.event_args.Cancel()

4. bundle.yaml: Configurazione Avanzata del Comando

title: "Verifica\nCAM Edilizia"
tooltip: >
  Esegue la scansione dei materiali del modello attivo e verifica
  la rispondenza alle percentuali di riciclato previste dal D.M. 24/11/2025.
author: "BIM Skills Italia"
min_revit_version: "2022"
max_revit_version: "2026"
context: "zerodoc"             # Disponibile anche senza documenti aperti
help_url: "https://github.com/VisionXt-tech/bim-skills"
highlight: "new"               # Mostra pallino di evidenziazione UI

Anti-pattern nello Sviluppo pyRevit

Errore ComuneConseguenzaSoluzione Corretta
import clr con interfacce WinForms/WPF manualiDisallineamento grafico, crash di threading e finestre fantasmaUsare sempre pyrevit.forms o forms.WPFWindow.
Usare print() standard per l'outputOutput non formattato, testo perso o finestra anonimaUsare script.get_output() con Markdown, tabelle e linkify.
Scrivere parametri di lunghezza in metri direttamenteDimensioni errate nel database Revit (1 metro salvato come 3.28 piedi)Convertire sempre tramite DB.UnitUtils.ConvertToInternalUnits(val, DB.UnitTypeId.Meters).
Omessa gestione di pb.cancelled in ProgressBarL'utente preme "Annulla" ma lo script continua a girare all'infinitoInserire if pb.cancelled: break all'interno di ogni iterazione del loop.
Modificare il modello fuori da with revit.TransactionEccezione immediata InvalidOperationException sollevata da RevitAvvolgere sempre le modifiche nel context manager della transazione.

Output Strutturato

Quando invocata, la skill fornisce:

  1. Script Python Completi (script.py) con gestione eccezioni, barra di progresso e transazioni.
  2. File di Configurazione bundle.yaml con icone, contesti e tooltip formattati.
  3. Template XAML per Finestre WPF Custom.
  4. Script di Hook per Eventi Globali (hooks/) per il controllo automatico dei gate informativi.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.