
Introduzione alla Selezione UI#
Il Revit API offre un robusto set di strumenti per permettere agli utenti di selezionare elementi interattivamente invece di raccoglierli automaticamente. Questa è la differenza chiave:
- FilteredElementCollector (articolo precedente) = Trova elementi automaticamente con il codice
- UI Selection (questo articolo) = L'utente clicca sugli elementi che vuole processare
Quando usare la selezione interattiva:
- L'utente deve scegliere visualmente cosa modificare
- La selezione non può essere automatizzata con filtri
- Vuoi conferma visiva prima di modificare elementi
- Serve flessibilità — utenti diversi selezionano cose diverse
Quando usare i collector:
- I criteri di selezione sono chiari e automatizzabili ("tutti i muri del piano terra")
- Serve velocità — processare centinaia di elementi automaticamente
- Il workflow deve essere ripetibile senza intervento manuale
Questo è il terzo articolo di una serie in tre parti sullo sviluppo con Revit API:
- Primi Passi con l'Automazione BIM
- Raccolta Elementi con FilteredElementCollector
- Metodi di Selezione UI (questo articolo)
Librerie e Setup Richiesti#
La selezione UI richiede librerie aggiuntive rispetto alla raccolta automatica:
1import clr2clr.AddReference('RevitAPIUI') # <-- Nota: aggiungiamo UI3import Autodesk4from Autodesk.Revit.UI import *5from Autodesk.Revit.UI.Selection import *6
7clr.AddReference('RevitServices')8import RevitServices9from RevitServices.Persistence import DocumentManager10
11# Ottieni il documento corrente e il documento UI12doc = DocumentManager.Instance.CurrentDBDocument13uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocumentDifferenza chiave:
doc= Il modello Revit (dati)uidoc= L'interfaccia utente (cosa vede l'utente)
Tutti i metodi di selezione usano uidoc, non doc.
Nota Importante per Dynamo#
ATTENZIONE: La maggior parte degli esempi in questo articolo funzionano solo in add-in C#, non in Dynamo.
Dynamo non supporta direttamente metodi come PickObject() perché:
- Dynamo è progettato per programmazione visuale, non interazione via codice
- I nodi Dynamo gestiscono input/output in modo diverso dagli add-in
Alternative in Dynamo:
- Usa nodi come "Select Model Element" o "Select Model Elements"
- Per selezioni avanzate, crea un add-in C# separato
- Per prototipazione, usa PyRevit (framework Python standalone per Revit)
Questo articolo è utile se:
- Stai imparando Revit API per creare add-in C# in futuro
- Vuoi capire come funzionano internamente i nodi di selezione Dynamo
- Usi PyRevit o framework simili che supportano
uidoc.Selection
1. Selezione Base degli Elementi#
1.1 Selezionare un Singolo Elemento#
Il metodo PickObject() è la base della selezione elementi nel Revit API. Richiede all'utente di selezionare un singolo elemento nell'interfaccia Revit.
1try:2 selected_reference = uidoc.Selection.PickObject(ObjectType.Element, "Seleziona un elemento")3 element = doc.GetElement(selected_reference.ElementId)4 print(f"Elemento selezionato: {element.Name}")5 print(f"Categoria: {element.Category.Name}")6 print(f"ID: {element.Id}")7 8 # Ottieni alcuni parametri base9 for param in element.Parameters:10 print(f"{param.Definition.Name}: {param.AsValueString()}")11except Exception as e:12 print(f"Selezione annullata o fallita: {str(e)}")Questo esempio non solo seleziona un elemento, ma recupera e stampa anche varie proprietà e parametri dell'elemento selezionato.
Esercizio: Modifica il codice per permettere la selezione di una categoria specifica (es: solo muri). Suggerimento: Dovrai usare un ISelectionFilter personalizzato.
1.2 Selezionare Elementi Multipli#
Quando devi selezionare più elementi, il metodo PickObjects() è utile:
1try:2 selected_references = uidoc.Selection.PickObjects(ObjectType.Element, "Seleziona più elementi")3 elements = [doc.GetElement(ref.ElementId) for ref in selected_references]4 5 category_count = {}6 for elem in elements:7 category = elem.Category.Name8 category_count[category] = category_count.get(category, 0) + 19 10 print(f"Selezionati {len(elements)} elementi")11 for category, count in category_count.items():12 print(f"{category}: {count}")13except Exception as e:14 print(f"Selezione annullata o fallita: {str(e)}")Questo esempio seleziona più elementi e fornisce un riepilogo degli elementi selezionati per categoria.
Esercizio: Estendi il codice per calcolare e stampare il volume totale di tutti gli elementi selezionati che hanno un parametro volume.
2. Tecniche Avanzate di Selezione#
2.1 Selezione per Rettangolo#
Il metodo PickElementsByRectangle() permette un processo di selezione più visuale, simile al trascinamento di una casella di selezione nell'interfaccia Revit:
1try:2 selected_elements = uidoc.Selection.PickElementsByRectangle("Seleziona elementi per rettangolo")3 4 levels = {}5 for elem_id in selected_elements:6 elem = doc.GetElement(elem_id)7 if hasattr(elem, 'Level'):8 level_name = elem.Level.Name if elem.Level else "Nessun Livello"9 levels[level_name] = levels.get(level_name, 0) + 110 11 print(f"Selezionati {len(selected_elements)} elementi")12 for level, count in levels.items():13 print(f"Livello {level}: {count} elementi")14except Exception as e:15 print(f"Selezione annullata o fallita: {str(e)}")Questo esempio seleziona elementi all'interno di un rettangolo e li organizza per livello, fornendo un conteggio degli elementi per ogni livello.
2.2 Pick Box#
Il metodo PickBox() crea una casella di selezione basata su due punti, utile per definire una regione 3D:
1from Autodesk.Revit.DB import BoundingBoxIntersectsFilter, Outline2
3try:4 box = uidoc.Selection.PickBox(PickBoxStyle.Enclosing, "Seleziona area")5 outline = Outline(box.Min, box.Max)6 bb_filter = BoundingBoxIntersectsFilter(outline)7 8 collector = FilteredElementCollector(doc).WherePasses(bb_filter)9 elements = list(collector)10 11 print(f"Trovati {len(elements)} elementi dentro la box selezionata")12 category_count = {}13 for elem in elements:14 category = elem.Category.Name if elem.Category else "Nessuna Categoria"15 category_count[category] = category_count.get(category, 0) + 116 17 for category, count in category_count.items():18 print(f"{category}: {count}")19except Exception as e:20 print(f"Selezione annullata o fallita: {str(e)}")Questo esempio usa la box selezionata per creare un filtro bounding box, poi trova tutti gli elementi che intersecano con questa box.
3. Filtri di Selezione Personalizzati#
I filtri di selezione personalizzati permettono un controllo fine su quali elementi possono essere selezionati:
1class FiltroPersonalizzato(ISelectionFilter):2 def __init__(self, nome_categoria, nome_parametro, valore_parametro):3 self.nome_categoria = nome_categoria4 self.nome_parametro = nome_parametro5 self.valore_parametro = valore_parametro6 7 def AllowElement(self, elem):8 if elem.Category and elem.Category.Name == self.nome_categoria:9 parameter = elem.LookupParameter(self.nome_parametro)10 if parameter and parameter.AsString() == self.valore_parametro:11 return True12 return False13 14 def AllowReference(self, reference, point):15 return False16
17try:18 filtro_personalizzato = FiltroPersonalizzato("Muri", "Commenti", "Importante")19 selected_elements = uidoc.Selection.PickObjects(20 ObjectType.Element, 21 filtro_personalizzato, 22 "Seleziona muri importanti"23 )24 25 print(f"Selezionati {len(selected_elements)} muri importanti")26 for ref in selected_elements:27 elem = doc.GetElement(ref.ElementId)28 print(f"ID Muro: {elem.Id}, Nome: {elem.Name}")29except Exception as e:30 print(f"Selezione annullata o fallita: {str(e)}")Questo esempio crea un filtro personalizzato che permette solo la selezione di muri con un valore di commento specifico.
3.1 Filtro per Categoria#
Un filtro più semplice che permette solo la selezione di categorie specifiche:
1class FiltroCategorie(ISelectionFilter):2 def __init__(self, nomi_categorie):3 self.nomi_categorie = nomi_categorie if isinstance(nomi_categorie, list) else [nomi_categorie]4 5 def AllowElement(self, elem):6 if elem.Category:7 return elem.Category.Name in self.nomi_categorie8 return False9 10 def AllowReference(self, reference, point):11 return False12
13# Uso: Seleziona solo porte e finestre14filtro_porte_finestre = FiltroCategorie(["Porte", "Finestre"])15selected = uidoc.Selection.PickObjects(16 ObjectType.Element, 17 filtro_porte_finestre, 18 "Seleziona porte o finestre"19)4. Lavorare con le Viste Selezionate#
Recuperare e lavorare con le viste selezionate nel browser di progetto:
1def ottieni_viste_selezionate():2 selected_ids = uidoc.Selection.GetElementIds()3 selected_views = [4 doc.GetElement(id) for id in selected_ids 5 if doc.GetElement(id).GetType().Name.startswith("View")6 ]7 return selected_views8
9selected_views = ottieni_viste_selezionate()10print(f"Selezionate {len(selected_views)} viste")11
12for view in selected_views:13 print(f"Nome Vista: {view.Name}, Tipo: {view.ViewType}")14 15 # Conta elementi visibili in ogni vista16 collector = FilteredElementCollector(doc, view.Id).WhereElementIsNotElementType()17 element_count = collector.GetElementCount()18 print(f" Elementi visibili: {element_count}")19 20 # Controlla proprietà specifiche della vista21 if hasattr(view, 'Scale'):22 print(f" Scala: 1:{view.Scale}")5. Selezione Punti#
La selezione punti è cruciale per molte operazioni, come creare nuovi elementi o misurare distanze:
1from Autodesk.Revit.DB import XYZ2
3def seleziona_punti(prompt, quantita):4 punti = []5 for i in range(quantita):6 try:7 punto = uidoc.Selection.PickPoint(8 ObjectSnapTypes.Endpoints, 9 f"{prompt} (Punto {i+1}/{quantita})"10 )11 punti.append(punto)12 except Exception as e:13 print(f"Selezione punto annullata o fallita: {str(e)}")14 return None15 return punti16
17def distanza_tra_punti(p1, p2):18 return p1.DistanceTo(p2)19
20punti = seleziona_punti("Seleziona due punti per misurare la distanza", 2)21if punti and len(punti) == 2:22 distanza = distanza_tra_punti(punti[0], punti[1])23 print(f"Distanza tra i punti: {distanza:.2f} piedi")24 25 # Crea un elemento linea tra i due punti26 try:27 linea = Line.CreateBound(punti[0], punti[1])28 doc.Create.NewDetailCurve(doc.ActiveView, linea)29 print("Creata una linea di dettaglio tra i punti")30 except Exception as e:31 print(f"Creazione linea di dettaglio fallita: {str(e)}")Questo esempio dimostra come selezionare più punti, calcolare la distanza tra loro e creare una linea di dettaglio.
6. Interazione Utente con TaskDialog#
Creare dialoghi interattivi migliora l'esperienza utente:
1def crea_task_dialog(titolo, istruzione_principale, contenuto, comandi, testo_verifica=None):2 dialog = TaskDialog(titolo)3 dialog.MainInstruction = istruzione_principale4 dialog.MainContent = contenuto5 6 for comando in comandi:7 dialog.AddCommandLink(comando[0], comando[1])8 9 if testo_verifica:10 dialog.VerificationText = testo_verifica11 12 return dialog13
14# Esempio di uso15comandi = [16 (TaskDialogCommandLinkId.CommandLink1, "Opzione 1: Seleziona muri"),17 (TaskDialogCommandLinkId.CommandLink2, "Opzione 2: Seleziona porte"),18 (TaskDialogCommandLinkId.CommandLink3, "Opzione 3: Annulla operazione")19]20
21dialog = crea_task_dialog(22 "Selezione Elementi",23 "Scegli cosa selezionare",24 "La tua scelta determinerà quali elementi possono essere selezionati.",25 comandi,26 "Ricorda la mia scelta"27)28
29risultato = dialog.Show()30
31if risultato == TaskDialogResult.CommandLink1:32 print("L'utente ha scelto di selezionare muri")33 # Procedi con selezione muri34elif risultato == TaskDialogResult.CommandLink2:35 print("L'utente ha scelto di selezionare porte")36 # Procedi con selezione porte37elif risultato == TaskDialogResult.CommandLink3:38 print("L'utente ha annullato l'operazione")39
40if dialog.WasVerificationChecked():41 print("L'utente vuole ricordare questa scelta")7. Selezione Facce e Spigoli#
Per selezione geometrica più precisa:
1# Seleziona una faccia2try:3 face_ref = uidoc.Selection.PickObject(ObjectType.Face, "Seleziona una faccia")4 element = doc.GetElement(face_ref.ElementId)5 face = element.GetGeometryObjectFromReference(face_ref)6 7 print(f"Faccia selezionata da: {element.Name}")8 print(f"Area faccia: {face.Area:.2f} piedi²")9except Exception as e:10 print(f"Selezione faccia fallita: {str(e)}")11
12# Seleziona uno spigolo13try:14 edge_ref = uidoc.Selection.PickObject(ObjectType.Edge, "Seleziona uno spigolo")15 element = doc.GetElement(edge_ref.ElementId)16 edge = element.GetGeometryObjectFromReference(edge_ref)17 18 print(f"Spigolo selezionato da: {element.Name}")19 print(f"Lunghezza spigolo: {edge.ApproximateLength:.2f} piedi")20except Exception as e:21 print(f"Selezione spigolo fallita: {str(e)}")8. Impostare Pre-Selezione#
Puoi anche impostare programmaticamente la selezione corrente:
1from Autodesk.Revit.DB import ElementId2from System.Collections.Generic import List3
4# Raccogli tutti i muri5muri = FilteredElementCollector(doc).OfClass(Wall).ToElements()6
7# Crea una lista di ElementIds8ids_muri = List[ElementId]([muro.Id for muro in muri])9
10# Imposta la selezione11uidoc.Selection.SetElementIds(ids_muri)12
13print(f"Pre-selezionati {len(ids_muri)} muri")9. Esempio Completo: Editor Interattivo Elementi#
Ecco un esempio completo che combina più tecniche di selezione:
1class EditorElementi:2 def __init__(self, doc, uidoc):3 self.doc = doc4 self.uidoc = uidoc5 6 def esegui(self):7 # Step 1: Mostra dialogo per scegliere azione8 dialog = TaskDialog("Editor Elementi")9 dialog.MainInstruction = "Cosa vorresti fare?"10 dialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink1, "Modifica elemento singolo")11 dialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink2, "Modifica elementi multipli")12 dialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink3, "Modifica elementi nell'area")13 14 risultato = dialog.Show()15 16 if risultato == TaskDialogResult.CommandLink1:17 self.modifica_singolo()18 elif risultato == TaskDialogResult.CommandLink2:19 self.modifica_multipli()20 elif risultato == TaskDialogResult.CommandLink3:21 self.modifica_in_area()22 23 def modifica_singolo(self):24 try:25 ref = self.uidoc.Selection.PickObject(ObjectType.Element, "Seleziona elemento da modificare")26 elemento = self.doc.GetElement(ref.ElementId)27 self.mostra_info_elemento(elemento)28 except:29 print("Selezione annullata")30 31 def modifica_multipli(self):32 try:33 refs = self.uidoc.Selection.PickObjects(ObjectType.Element, "Seleziona elementi da modificare")34 elementi = [self.doc.GetElement(ref.ElementId) for ref in refs]35 for elem in elementi:36 self.mostra_info_elemento(elem)37 except:38 print("Selezione annullata")39 40 def modifica_in_area(self):41 try:42 elementi = self.uidoc.Selection.PickElementsByRectangle("Disegna rettangolo per selezionare")43 for elem_id in elementi:44 elem = self.doc.GetElement(elem_id)45 self.mostra_info_elemento(elem)46 except:47 print("Selezione annullata")48 49 def mostra_info_elemento(self, elemento):50 print(f"\n--- {elemento.Name} ---")51 print(f"ID: {elemento.Id}")52 print(f"Categoria: {elemento.Category.Name if elemento.Category else 'N/A'}")53
54# Esegui l'editor55editor = EditorElementi(doc, uidoc)56editor.esegui()Conclusione#
Questa guida copre un'ampia gamma di metodi di selezione del Revit API, dalla selezione base degli elementi alle selezioni filtrate avanzate e interazioni utente. La chiave per padroneggiare la selezione nel Revit API è pratica e sperimentazione.
Ricorda:
- Usa filtri personalizzati per limitare cosa gli utenti possono selezionare
- Avvolgi sempre le selezioni in blocchi try-except per gestione elegante delle annullazioni
- Combina metodi di selezione con FilteredElementCollector per workflow potenti
- Fornisci prompt chiari per guidare gli utenti
Prossimi Passi#
Questo conclude la nostra serie in tre parti sullo sviluppo con Revit API:
- Primi Passi con l'Automazione BIM — Fondamenti e concetti chiave
- Raccolta Elementi con FilteredElementCollector — Trovare elementi programmaticamente
- Metodi di Selezione UI — Selezione interattiva utente (questo articolo)
Questions or Feedback?
I'd love to hear your thoughts on this article. Reach out directly and let's start a conversation.
Follow me on LinkedIn for more BIM tips and updates
