Data panel

IDs of Selection

Copy the Revit element IDs of the selected items to the clipboard.

On the ribbonStacked button, Data panel
NeedsA selection
Shift+ClickOpens Element ID Settings

What it does#

The reverse trip. Resolves an ID for each selected item, reading it off the nearest ancestor that is a real element, because a click in the view usually lands on a geometry leaf below the element. The list goes to the clipboard, ready for Select by IDs, for Revit's own Select by ID, or for a report.

How to use it#

  1. Select the items, in the view or from a set.
  2. Click IDs of Selection. The toast names a single ID or counts several, and says how many selected items carry no ID at all.
  3. Paste.

Limitations#

  • Items with no element ID, such as a whole file node or non-Revit geometry, are counted in the toast and skipped.

Other uses#

  • Tell a modeller exactly which elements to fix, in the one identifier both tools share.

Source#

The tool's own files, word for word as shipped in Data.panel/Element_IDs.stack/IDs_of_Selection.pushbutton. Every pyNavis tool is an ordinary bundle: copy these into your own extension and change what you like.

script.py Runs on a click. View on GitHub
"""Copies the Revit element IDs of the current selection to the clipboard.

The Revit command of the same name, for a federated model. Each selected item
resolves its id the two ways an id survives federation: the "Element ID"
property tab an NWC exported from Revit carries, and the "[123456]" suffix a
design coordination model carries in its name instead.

The list lands on the clipboard ready to paste back into Select by IDs, into
Revit's Select by ID, or into a report. Settings are shared with Select by
IDs; Shift+Click opens them.

Everything above the guard at the bottom is pure, so this file doubles as an
importable module for tests (src/PyNavis.Tests/IdsOfSelectionScriptTests.cs).
"""

import elementids

from pynavis import props, script, settings, toast

# How far up the tree to look for an id. A click in the view selects whichever
# node the current selection resolution lands on, often a geometry leaf below
# the Revit element, so the walk has to go up; the cap keeps it from reaching
# a model root whose own name happens to carry a bracketed number.
_MAX_ANCESTORS = 8


def summary(ids, without, copied=True):
    """(level, message, detail) for the toast. One id is named rather than
    counted, and items that carry no id at all are always reported, so the
    tool never finishes looking like it did nothing. copied is False when the
    clipboard refused the list (another application holding it open), and
    the toast then says so instead of claiming a copy."""
    if not ids:
        return ('warning', 'No element IDs found',
                'None of the %d selected item(s) carry an element ID.' % without)

    if not copied:
        return ('error', 'Could not copy the element IDs',
                'Another application is using the clipboard. Try again.')

    if len(ids) == 1:
        message = 'Element ID %s copied' % ids[0]
    else:
        message = '%d element IDs copied' % len(ids)

    if without:
        return ('warning', message,
                '%d selected item(s) carry no element ID.' % without)
    return ('success', message, None)


# --- API half (Navisworks only) ---------------------------------------------


def id_of(item, values):
    """The element id for one selected ModelItem, or None.

    The nearest ancestor-or-self that is a real element wins - the one the
    exporter marked with its Element category (elementids.ELEMENT_CATEGORY).
    That is not always the nearest node with an "Element ID" tab: the nested
    parts under a family instance carry the tab too, holding the TYPE's id,
    and a click in the view usually lands on one of those. Reading the first
    tab found copied the type id out, and Select by IDs then resolved it to
    the first instance of that family - the round trip through the clipboard
    landed on a different fixture. Field-checked (12345678 was the
    type, 12345679 the instance).

    A model whose exporter never wrote the category falls back to the old
    rule: the first ancestor-or-self carrying an id at all. Descendants are
    deliberately not searched either way: a group node would then report one
    arbitrary child's id as if it were its own.
    """
    node = item
    fallback = None
    for _depth in range(_MAX_ANCESTORS + 1):
        if node is None:
            break
        found = _id_on(node, values)
        if found and elementids.is_element(props.categories(node)):
            return found
        if found and fallback is None:
            fallback = found
        node = node.Parent
    return fallback


def _id_on(item, values):
    value = props.get(item, values['id_category'], values['id_property'])
    if value is not None:
        text = str(value).strip()
        if text:
            return text
    return elementids.id_from_name(getattr(item, 'DisplayName', None))


def run():
    from pynavis import selection

    items = selection.get_items()
    if not items:
        toast.info('Nothing selected', 'Select some elements first.')
        return

    values = settings.load(elementids.SETTINGS_KEY, elementids.DEFAULTS)

    ids = []
    seen = {}
    without = 0
    for item in items:
        found = id_of(item, values)
        if not found:
            without += 1
            continue
        if found not in seen:
            seen[found] = True
            ids.append(found)

    copied = script.clipboard_copy(elementids.format_id_list(ids)) if ids else True
    toast.show(*summary(ids, without, copied))


if '__commandpath__' in globals():
    run()
config.py Runs on Shift+Click. View on GitHub
"""Shift+Click settings for both element id buttons.

The window itself is shared (extensions/pyNavis.extension/lib), because the
two buttons read one settings store.
"""

import elementidconfig

elementidconfig.edit()
bundle.yaml The bundle contract: the keys pyNavis reads for this tool. View on GitHub
title: IDs of Selection
tooltip: Copy the Revit element IDs of the current selection to the clipboard, ready to paste into Select by IDs or back into Revit. Shift+Click for settings.
context: selection