Highlight Layer/ Select Object Layer

In V8 one can now select an object an then use the “Highlight Object Layer” Button or “Select Object Layer” in the layer list. This reveals the objects layer. This is very useful in files with many layer.

Is there an option/ script that constantly shows the object layer a soon as one selects an object WITHOUT clicking on the tool erverytime? This would be very useful (could be set to on/off)…

Thanks.

Hi @matthias,

The status bar layer pane shows you the layer on which a selected object resides.

– Dale

Dear Dale

Thanks. Is this the stauts bar layer pane:

Still, if the layers are not in alphabetical order on has to search for it in the sometimes long list it would be very useful to have a “live” indicator.

Would something like this be possible with a script or macro? At the moment I always select the object and then run an alias/shortcut for each object separately…

Hi @matthias,

Look here ^^…

– Dale

Thanks.

Still, is there a way for us to get live-layer-indication (as it was in an earlier Rhino 8 Beta)? Thanks.

Hi @matthias,

The status bar layer pane is “live”. When you select an object, it changes to indicate the layer on which the selected object sits.

We did play with some kind of indicator in the Layers panel during the Rhino 8 WIP. But it never felt right, so we shelved the work, to be reconsidered for Rhino 9.

– Dale

Hi Dale

Thanks. Still, do you see any scripting option for us to get the indicator directly in the layer list?

Sorry for the hassle…

No hassle at all.

Rhino 8 has a new HighlightObjectLayers command you might find useful.

Here is a script that does similar.

HighlightObjectLayers.py (1.8 KB)

– Dale

Dale

Thanks. I think I was unclear. My intention was the following:

As soon as there is an objected selected, its associated layer gets highlighted without another command/script. This was offered in some earlier Rhino 8 WIP version.

This functionality could be optional (to switch on/off).

This is the third thread in quite a short interval I’ve seen asking for this

Maybe in another decade?

Hi @matthias,

I understand what you are asking. There is no option in the Layers panel, at this time, to behave in this way. I’ve offered some alternatives (see above).

Thanks,

– Dale

I know this isn’t the built-in functionality you are after but in the mean time you can use an event handler subscription in @dale’s script so that it works in “real-time”

In the example below I am running an alias called “EnableLayerHighlight” that runs the python script, in that script it’s basically Dales code but listening for changes to Selected Objects so anytime you select a new object it fires the script function, highlight said layer in real-time.

Currently when you want to use, select an object and run the alias or script via script editor and then you don’t need to run it again it should continue to work until Rhino is closed/reopened.

Presumably this could easily be extended as a command with a “toggle” functionality to turn this on and off but I’m a newbie with event handling so I’m not going to take that on right now.

Quick video example:

Modified code:

"""
HighlightObjectLayers.py -- February 2024
If this code works, it was written by Dale Fugier.
If not, I don't know who wrote it.
Works with Rhino 7.

Modified on 03.11.2024
If it works, it was modified by Michael Vollrath.
If not, it was ChatGPT.
Works with Rhino 7 & 8.
"""

import Rhino
import System
import scriptcontext as sc

def _GetSelectedObjectLayers():
    # Enumerate normal, active, reference, and selected objects
    settings = Rhino.DocObjects.ObjectEnumeratorSettings()
    settings.ActiveObjects = True
    settings.ReferenceObjects = True
    settings.NormalObjects = True
    settings.HiddenObjects = False
    settings.LockedObjects = False
    settings.DeletedObjects = False
    settings.SelectedObjectsFilter = True
    rh_objects = sc.doc.Objects.FindByFilter(settings)
    # Return the layer indices
    if rh_objects:
        layers_indices = set()
        for rh_obj in rh_objects:
            layers_indices.add(rh_obj.Attributes.LayerIndex)
        return layers_indices
    return None

def HighlightObjectLayers():
    # Get layer indices of pre-selected objects
    layers_indices = _GetSelectedObjectLayers()
    if not layers_indices:
        print("No objects selected.")
        return
    
    # Process each layer index
    for layer_index in layers_indices:
        layer = sc.doc.Layers[layer_index]
        if not layer: continue
        
        # If a child layer, expand parents if needed
        parent_id = layer.ParentLayerId 
        while parent_id != System.Guid.Empty:
            parent = sc.doc.Layers.FindId(parent_id)
            if parent:
                if not parent.IsExpanded:
                    parent.IsExpanded = True
                parent_id = parent.ParentLayerId
            else:
                parent_id = System.Guid.Empty
    
    # Select layers in the UI
    sc.doc.Layers.Select(layers_indices, True)

def OnSelectObjects(sender, e):
    HighlightObjectLayers()

# Main
if __name__ =="__main__":
    # Register event handler for selection change
    Rhino.RhinoDoc.SelectObjects += OnSelectObjects
    # To manually run the script for the initially selected objects
    HighlightObjectLayers()

Alias example:

EnableLayerHighlight
NoEcho !_-RunPythonScript “path where your script is saved\20240311_Highlight_Object_Layer_Real_Time_01a.py”

Also it should unhighlight when objects are deselected/unselected but I didn’t implement that in this go around. Maybe I’ll come back to it if I find the time.

Perhaps this helps for now @matthias ?

Don’t mean to hammer all the threads on this topic, but this is a good user experience feature and Photoshop does it well. They automatically highlight the selected object associated layer when the “auto-select” toggle is activated.

But since Rhino works a little differently, I think the object layer should be highlighted by default and the option to disable it exist in Rhino options or advanced settings.

This is great! But how can we turn it off?

Hi @declan ,

You need to unsubscribe to the Selection event aka “Select Objects” and replace this:

# Main
if __name__ =="__main__":
    # Register event handler for selection change
    Rhino.RhinoDoc.SelectObjects += OnSelectObjects
    # To manually run the script for the initially selected objects
    HighlightObjectLayers()

with something along these lines (where “enabled” is a boolean variable you set somewhere):

# something like...

if __name__ == "__main__":
	# Subscribe to SelectObjects event and call Highlight function
	if enabled:
		Rhino.RhinoDoc.SelectObjects += OnSelectObjects
		# To manually run the script for the initially selected objects
		HighlightObjectLayers()
		
	else:
		# Unsubscribe from SelectObjects and call nothing else
		Rhino.RhinoDoc.SelectObjects -= OnSelectObjects
		return

I can’t test or properly code that right now but along those lines should work.

Very cool. This is a handy technique - thank you for sharing!

Following up on the earlier discussion about wanting live layer highlighting in the Layers panel (not just the status bar) without manually running HighlightObjectLayers every time.

Since McNeel confirmed this isn’t natively available yet (shelved after Rhino 8 WIP, possibly reconsidered for Rhino 9), I put together a Python workaround based on Dale’s HighlightObjectLayers.py script, wrapped in an Idle event listener so it runs continuously and updates automatically whenever the selection changes — no need to press anything after it’s turned on.

I bound it to F8 as a toggle (press once to turn on, press again to turn off). Sharing it here in case it’s useful to others with the same workflow need (I work on interior scenes with 100+ layers, and constantly needing to know which layer a selected object is on).

“”"
AutoHighlightObjectLayers.py

Automatically highlights (selects, in the Layers panel) the layer(s)
of whatever object is currently selected in the viewport, without
needing to press a hotkey or run a command manually.

Based on the logic from HighlightObjectLayers.py by Dale Fugier (McNeel),
wrapped in an Idle-event listener so it runs continuously in the background.

Toggle on/off by running this script again (e.g. bound to a hotkey via
"! _-RunPythonScript ").

Works with Rhino 7/8 PythonScript (IronPython).
“”"

import Rhino
import System
import scriptcontext as sc

_last_selection_ids = set()

def _get_selected_object_layers():
settings = Rhino.DocObjects.ObjectEnumeratorSettings()
settings.ActiveObjects = True
settings.ReferenceObjects = True
settings.NormalObjects = True
settings.HiddenObjects = False
settings.LockedObjects = False
settings.DeletedObjects = False
settings.SelectedObjectsFilter = True
rh_objects = sc.doc.Objects.FindByFilter(settings)
if not rh_objects:
return None
layers_indices = set()
for rh_obj in rh_objects:
layers_indices.add(rh_obj.Attributes.LayerIndex)
return layers_indices

def _highlight_layers_for_selection():
layers_indices = _get_selected_object_layers()
if not layers_indices:
return

for layer_index in layers_indices:
    layer = sc.doc.Layers[layer_index]
    if not layer:
        continue

    parent_id = layer.ParentLayerId
    while parent_id != System.Guid.Empty:
        parent = sc.doc.Layers.FindId(parent_id)
        if parent:
            if not parent.IsExpanded:
                parent.IsExpanded = True
            parent_id = parent.ParentLayerId
        else:
            parent_id = System.Guid.Empty

sc.doc.Layers.Select(list(layers_indices), True)

def _on_idle(sender, e):
global _last_selection_ids
try:
selected = sc.doc.Objects.GetSelectedObjects(False, False)
current_ids = set(obj.Id for obj in selected) if selected else set()
if current_ids != _last_selection_ids:
_last_selection_ids = current_ids
if current_ids:
_highlight_layers_for_selection()
except Exception:
pass

def StartAutoHighlightLayers():
old_handler = sc.sticky.get(“auto_highlight_layers_handler”)
if old_handler is not None:
Rhino.RhinoApp.Idle -= old_handler

Rhino.RhinoApp.Idle += _on_idle
sc.sticky["auto_highlight_layers_handler"] = _on_idle
sc.sticky["auto_highlight_layers_active"] = True
print("AutoHighlightObjectLayers: ON")

def StopAutoHighlightLayers():
old_handler = sc.sticky.get(“auto_highlight_layers_handler”)
if old_handler is not None:
Rhino.RhinoApp.Idle -= old_handler
sc.sticky[“auto_highlight_layers_handler”] = None

sc.sticky["auto_highlight_layers_active"] = False
print("AutoHighlightObjectLayers: OFF")

def ToggleAutoHighlightLayers():
is_active = sc.sticky.get(“auto_highlight_layers_active”, False)
if is_active:
StopAutoHighlightLayers()
else:
StartAutoHighlightLayers()

if name == “main”:
ToggleAutoHighlightLayers()

How to set it up:

  1. Save the script anywhere on disk, e.g. C:\Scripts\AutoHighlightObjectLayers.py.
  2. Options → Keyboard, assign a key (I used F8) with this macro:
    ! _-RunPythonScript "C:\Scripts\AutoHighlightObjectLayers.py"
  3. Press the key once to turn tracking on, press again to turn it off.

One technical gotcha worth mentioning for anyone adapting this: RunPythonScript re-executes the whole file from scratch every time, so a naive Idle -= _on_idle on the “off” press won’t actually unsubscribe (the function object is a new instance each run, so identity comparison fails silently). The fix is to stash the actual subscribed handler reference in sc.sticky and unsubscribe using that stored reference — that’s what the sticky["auto_highlight_layers_handler"] bit above is doing.

Not a replacement for a proper native indicator in the Layers panel, but it closes the gap until (if) that gets revisited for Rhino 9.

Here is the same script but properly formatted:

import Rhino
import System
import scriptcontext as sc

_last_selection_ids = set()


def _get_selected_object_layers():
    settings = Rhino.DocObjects.ObjectEnumeratorSettings()
    settings.ActiveObjects = True
    settings.ReferenceObjects = True
    settings.NormalObjects = True
    settings.HiddenObjects = False
    settings.LockedObjects = False
    settings.DeletedObjects = False
    settings.SelectedObjectsFilter = True

    rh_objects = sc.doc.Objects.FindByFilter(settings)
    if not rh_objects:
        return None

    layers_indices = set()
    for rh_obj in rh_objects:
        layers_indices.add(rh_obj.Attributes.LayerIndex)
    return layers_indices


def _highlight_layers_for_selection():
    layers_indices = _get_selected_object_layers()
    if not layers_indices:
        return

    for layer_index in layers_indices:
        layer = sc.doc.Layers[layer_index]
        if not layer:
            continue

        parent_id = layer.ParentLayerId
        while parent_id != System.Guid.Empty:
            parent = sc.doc.Layers.FindId(parent_id)
            if parent:
                if not parent.IsExpanded:
                    parent.IsExpanded = True
                parent_id = parent.ParentLayerId
            else:
                parent_id = System.Guid.Empty

    sc.doc.Layers.Select(list(layers_indices), True)


def _on_idle(sender, e):
    global _last_selection_ids
    try:
        selected = sc.doc.Objects.GetSelectedObjects(False, False)
        current_ids = set(obj.Id for obj in selected) if selected else set()
        if current_ids != _last_selection_ids:
            _last_selection_ids = current_ids
            if current_ids:
                _highlight_layers_for_selection()
    except Exception:
        pass


def StartAutoHighlightLayers():
    old_handler = sc.sticky.get("auto_highlight_layers_handler")
    if old_handler is not None:
        Rhino.RhinoApp.Idle -= old_handler

    Rhino.RhinoApp.Idle += _on_idle
    sc.sticky["auto_highlight_layers_handler"] = _on_idle
    sc.sticky["auto_highlight_layers_active"] = True
    print("AutoHighlightObjectLayers: ON")


def StopAutoHighlightLayers():
    old_handler = sc.sticky.get("auto_highlight_layers_handler")
    if old_handler is not None:
        Rhino.RhinoApp.Idle -= old_handler
    sc.sticky["auto_highlight_layers_handler"] = None
    sc.sticky["auto_highlight_layers_active"] = False
    print("AutoHighlightObjectLayers: OFF")


def ToggleAutoHighlightLayers():
    is_active = sc.sticky.get("auto_highlight_layers_active", False)
    if is_active:
        StopAutoHighlightLayers()
    else:
        StartAutoHighlightLayers()


if __name__ == "__main__":
    ToggleAutoHighlightLayers()
return

for layer_index in layers_indices:
    layer = sc.doc.Layers[layer_index]
    if not layer:
        continue

    parent_id = layer.ParentLayerId
    while parent_id != System.Guid.Empty:
        parent = sc.doc.Layers.FindId(parent_id)
        if parent:
            if not parent.IsExpanded:
                parent.IsExpanded = True
            parent_id = parent.ParentLayerId
        else:
            parent_id = System.Guid.Empty

sc.doc.Layers.Select(list(layers_indices), True)

def _on_idle(sender, e):
global _last_selection_ids
try:
selected = sc.doc.Objects.GetSelectedObjects(False, False)
current_ids = set(obj.Id for obj in selected) if selected else set()
if current_ids != _last_selection_ids:
_last_selection_ids = current_ids
if current_ids:
_highlight_layers_for_selection()
except Exception:
pass

def StartAutoHighlightLayers():
old_handler = sc.sticky.get("auto_highlight_layers_handler")
if old_handler is not None:
Rhino.RhinoApp.Idle -= old_handler

Rhino.RhinoApp.Idle += _on_idle
sc.sticky["auto_highlight_layers_handler"] = _on_idle
sc.sticky["auto_highlight_layers_active"] = True
print("AutoHighlightObjectLayers: ON")

def StopAutoHighlightLayers():
old_handler = sc.sticky.get("auto_highlight_layers_handler")
if old_handler is not None:
Rhino.RhinoApp.Idle -= old_handler
sc.sticky["auto_highlight_layers_handler"] = None
sc.sticky["auto_highlight_layers_active"] = False
print("AutoHighlightObjectLayers: OFF")

def ToggleAutoHighlightLayers():
is_active = sc.sticky.get("auto_highlight_layers_active", False)
if is_active:
StopAutoHighlightLayers()
else:
StartAutoHighlightLayers()

if name == "main":
ToggleAutoHighlightLayers()