TBC Macros and Extensions

 View Only
Expand all | Collapse all

Using Select by Layer macro by API

  • 1.  Using Select by Layer macro by API

    Posted 09-13-2025 03:36

    Hi,
    I am trying to use a native TBC Macro (Select by Layer, in this case) in my custom macro, but I can't access it. And it seems there is no documentation.
    Can someone help with the proper way to access TBC tools in macros?

    Sincerely:
    Eero Isola



    ------------------------------
    Eero Isola
    ------------------------------


  • 2.  RE: Using Select by Layer macro by API

    Posted 09-14-2025 01:26

    From my understanding you can't start a TBC function from within a macro.

    If you use the "MemberSelection" as suggested in my previous post

    In the UI XAML

        <Wpf:MemberSelection x:Name="objs" Height="30" Margin="0"/>

    You can activate the options menu there, as shown in the TBC sample macros.

    in the macro in OnLoad

            optionMenu = SelectionContextMenuHandler()
            # remove options that don't apply here
            optionMenu.ExcludedCommands = "SelectObservations | SelectPoints | SelectDuplicatePoints"
            self.objs.ButtonContextMenu = optionMenu # set what is visible when clicking the option button

    Screenshot from an old 4.xx TBC version, just as example, don't have a dongle currently

    Includes more mouse clicking. You weren't clear on how automated you want the process.



    ------------------------------
    Ronny Schneider
    ------------------------------



  • 3.  RE: Using Select by Layer macro by API

    Posted 09-14-2025 01:43

    Thank you for your reply Ronny.
    I read your previous reply, but I was unable to reply, since it was QA thread.

    I had limited success with that method. But It may have been implementation problem.

    My goal is to make "one-click" solution. I have selection and after macro, I have all objects from the same layer. No extra clicks, menus, or text.
    I am very surprised that one cannot use macros from macros.



    ------------------------------
    Eero Isola
    ------------------------------



  • 4.  RE: Using Select by Layer macro by API

    Posted 09-22-2025 03:54

    Hello,
    Below is my current code, but it cannot fetch layer data from the object.

    Output/Error
    Selection.py
    # -*- coding: utf-8 -*-
    """
    Step-by-Step Layer Selection Wizard
    Interactive wizard that guides users through layer selection process step by step.
    Each step is executed when the user clicks "Next" button.
    """
    
    import clr
    clr.AddReference('IronPython.Wpf')
    import wpf
    from System.IO import StreamReader
    from System.Windows.Controls import StackPanel
    from System import String
    from System.Collections.Generic import List
    
    # Core references - try multiple approaches
    for asm in ['Trimble.Sdk','Trimble.Vce.Core','Trimble.Vce.Geometry']:
        try: clr.AddReference(asm)
        except: pass
    
    # UI and Command references
    for asm in ['Trimble.Vce.UI.UIManager', 'Trimble.Vce.UI.BaseCommands', 'Trimble.Vce.UI.Controls']:
        try: clr.AddReference(asm)
        except: pass
    
    # Add Trimble.Vce.UI.Wpf assembly reference
    try:
        clr.AddReference(r"C:\Program Files\Trimble\Trimble Business Center\Trimble.Vce.UI.Wpf.dll")
    except:
        try:
            clr.AddReference("Trimble.Vce.UI.Wpf")
        except:
            pass
    
    # Multiple approaches to access GlobalSelection
    GlobalSelection = None
    SelectionContextMenuHandler = None
    
    # Try primary import
    try:
        from Trimble.Vce.Core import GlobalSelection
        print("✅ GlobalSelection imported from Trimble.Vce.Core")
    except Exception as e:
        print(f"❌ Primary GlobalSelection import failed: {e}")
    
    # Try alternative imports if primary failed
    if GlobalSelection is None:
        try:
            import Trimble.Vce.Core as VceCore
            if hasattr(VceCore, 'GlobalSelection'):
                GlobalSelection = VceCore.GlobalSelection
                print("✅ GlobalSelection found via Trimble.Vce.Core module")
        except Exception as e:
            print(f"❌ Alternative GlobalSelection import failed: {e}")
    
    # Try UI context import
    try:
        from Trimble.Vce.UI.Wpf import SelectionContextMenuHandler
    except:
        SelectionContextMenuHandler = None
    
    try:
        from Trimble.Vce.Interfaces.Client import CommandGranularity
    except:
        CommandGranularity = None
    
    try:
        from Trimble.Vce.Interfaces.SnapIn import IMemberManagement
    except:
        IMemberManagement = None
    
    
    # ---------- Setup ----------
    def Setup(cmdData, macroFileFolder):
        cmdData.Key = "LayerSelectWizard"
        cmdData.CommandName = "LayerSelectionWizard"
        cmdData.Caption = "_Layer Selection Wizard"
        cmdData.UIForm = "Selection"
        try:
            cmdData.Version = 2.1
            cmdData.MacroAuthor = "Assistant"
            cmdData.MacroInfo = "Step-by-step wizard that guides users through layer selection process with multiple selection access methods."
        except: pass
        try:
            cmdData.DefaultTabKey = "Macros"
            cmdData.DefaultTabGroupKey = "Selection"
            cmdData.ShortCaption = "LayerWiz"
            cmdData.ToolTipTitle = "Layer Selection Wizard"
            cmdData.ToolTipTextFormatted = "Interactive wizard that guides you through selecting all objects on the same layer(s) step by step."
        except: pass
        if CommandGranularity:
            for name in ['Selection','ObjectSelection','Components']:
                if hasattr(CommandGranularity, name):
                    try:
                        cmdData.CommandGranularity = getattr(CommandGranularity, name)
                        break
                    except: pass
        try:
            cmdData.RequireSelection = False  # Allow starting without selection
        except: pass
    
    
    # ---------- Step-by-Step UI Panel ----------
    class Selection(StackPanel):
        def __init__(self, currentProject, macroFileFolder):
            try:
                # Load the XAML file
                with StreamReader(macroFileFolder + r"\Selection - Copy.xaml") as s:
                    wpf.LoadComponent(self, s)
            except Exception as e:
                print(f"Error loading XAML: {e}")
                self._create_fallback_ui()
            
            self.currentProject = currentProject
            self.cmdManager = None
            
            # Selection access - try to establish during initialization
            self.selection_accessor = None
            self._establish_selection_access()
            
            # Wizard state
            self.current_step = 1
            self.total_steps = 5
            self.initial_selection = []
            self.layers_found = set()
            self.objects_on_layers = []
            self.wizard_completed = False
            
            # Step definitions
            self.steps = {
                1: {
                    'title': 'Initialize Components',
                    'description': 'Checking TBC components and connections...',
                    'action': self._step1_initialize
                },
                2: {
                    'title': 'Analyze Selection',
                    'description': 'Analyzing currently selected objects...',
                    'action': self._step2_analyze_selection
                },
                3: {
                    'title': 'Extract Layers',
                    'description': 'Extracting layer information from selected objects...',
                    'action': self._step3_extract_layers
                },
                4: {
                    'title': 'Find Layer Objects',
                    'description': 'Finding all objects on the identified layers...',
                    'action': self._step4_find_objects
                },
                5: {
                    'title': 'Select Objects',
                    'description': 'Selecting all objects on the same layers...',
                    'action': self._step5_select_objects
                }
            }
    
        def _establish_selection_access(self):
            """Try multiple methods to access TBC selection"""
            # Method 1: Direct GlobalSelection
            if GlobalSelection is not None:
                self.selection_accessor = GlobalSelection
                print("✅ Using direct GlobalSelection access")
                return
            
            # Method 2: Try to access via current project
            if self.currentProject:
                try:
                    # Some TBC versions expose selection through project
                    if hasattr(self.currentProject, 'Selection'):
                        self.selection_accessor = self.currentProject.Selection
                        print("✅ Using project-based selection access")
                        return
                    
                    # Try accessing via project's selection items
                    if hasattr(self.currentProject, 'SelectedItems'):
                        self.selection_accessor = self.currentProject.SelectedItems
                        print("✅ Using project SelectedItems access")
                        return
                except Exception as e:
                    print(f"Project-based selection access failed: {e}")
            
            # Method 3: Try to access via reflection/late binding
            try:
                import Trimble.Vce.Core as Core
                if hasattr(Core, 'Application'):
                    app = Core.Application
                    if hasattr(app, 'Selection'):
                        self.selection_accessor = app.Selection
                        print("✅ Using application-based selection access")
                        return
            except Exception as e:
                print(f"Application-based selection access failed: {e}")
            
            print("❌ Warning: No selection access method established")
    
        def _get_selected_objects_multiple_methods(self):
            """Get selected objects using multiple fallback methods"""
            selected_objects = []
            
            # Method 1: Use established selection accessor
            if self.selection_accessor:
                try:
                    # If it's GlobalSelection-like
                    if hasattr(self.selection_accessor, 'Items') and self.currentProject:
                        items_accessor = self.selection_accessor.Items(self.currentProject)
                        if items_accessor:
                            for obj in items_accessor:
                                selected_objects.append(obj)
                            if selected_objects:
                                return selected_objects
                    
                    # If it's a direct collection
                    elif hasattr(self.selection_accessor, '__iter__'):
                        for obj in self.selection_accessor:
                            selected_objects.append(obj)
                        if selected_objects:
                            return selected_objects
                    
                    # If it has a Count and indexer
                    elif hasattr(self.selection_accessor, 'Count'):
                        count = self.selection_accessor.Count
                        for i in range(count):
                            try:
                                obj = self.selection_accessor[i]
                                selected_objects.append(obj)
                            except:
                                continue
                        if selected_objects:
                            return selected_objects
                            
                except Exception as e:
                    print(f"Selection accessor method failed: {e}")
            
            # Method 2: Try direct GlobalSelection even if initially failed
            if GlobalSelection is not None:
                try:
                    items_accessor = GlobalSelection.Items(self.currentProject)
                    if items_accessor:
                        for obj in items_accessor:
                            selected_objects.append(obj)
                        if selected_objects:
                            return selected_objects
                except Exception as e:
                    print(f"Direct GlobalSelection method failed: {e}")
            
            # Method 3: Try command manager to get selection
            if self.cmdManager:
                try:
                    cmd_selection = getattr(self.cmdManager, 'Selection', None)
                    if cmd_selection:
                        if hasattr(cmd_selection, '__iter__'):
                            for obj in cmd_selection:
                                selected_objects.append(obj)
                            if selected_objects:
                                return selected_objects
                except Exception as e:
                    print(f"Command manager selection method failed: {e}")
            
            # Method 4: Try project-based approaches
            if self.currentProject:
                try:
                    # Try various project selection properties
                    for prop_name in ['SelectedObjects', 'Selection', 'CurrentSelection', 'SelectedItems']:
                        if hasattr(self.currentProject, prop_name):
                            selection_prop = getattr(self.currentProject, prop_name)
                            if selection_prop and hasattr(selection_prop, '__iter__'):
                                for obj in selection_prop:
                                    selected_objects.append(obj)
                                if selected_objects:
                                    return selected_objects
                except Exception as e:
                    print(f"Project selection method failed: {e}")
            
            return selected_objects
    
        def _select_objects_multiple_methods(self, objects):
            """Select objects using multiple fallback methods"""
            success_count = 0
            
            # Method 1: Use established selection accessor
            if self.selection_accessor:
                try:
                    # Clear selection first
                    if hasattr(self.selection_accessor, 'Clear'):
                        self.selection_accessor.Clear()
                    
                    # Add objects
                    if hasattr(self.selection_accessor, 'Add'):
                        for obj in objects:
                            try:
                                self.selection_accessor.Add(obj)
                                success_count += 1
                            except:
                                continue
                        if success_count > 0:
                            return success_count
                    
                    # Try bulk selection
                    if hasattr(self.selection_accessor, 'Set') and self.currentProject:
                        serial_list = List[int]()
                        for obj in objects:
                            try:
                                serial = getattr(obj, 'SerialNumber', None)
                                if serial is not None:
                                    serial_list.Add(serial)
                            except:
                                continue
                        
                        if serial_list.Count > 0:
                            try:
                                self.selection_accessor.Items(self.currentProject).Set(serial_list)
                                return serial_list.Count
                            except Exception as e:
                                print(f"Bulk selection failed: {e}")
                                
                except Exception as e:
                    print(f"Selection accessor method failed: {e}")
            
            # Method 2: Try direct GlobalSelection
            if GlobalSelection is not None:
                try:
                    GlobalSelection.Clear()
                    for obj in objects:
                        try:
                            GlobalSelection.Add(obj)
                            success_count += 1
                        except:
                            continue
                    if success_count > 0:
                        return success_count
                except Exception as e:
                    print(f"Direct GlobalSelection failed: {e}")
            
            # Method 3: Try command manager selection
            if self.cmdManager:
                try:
                    cmd_selection = getattr(self.cmdManager, 'Selection', None)
                    if cmd_selection and hasattr(cmd_selection, 'Set'):
                        serial_list = []
                        for obj in objects:
                            try:
                                serial = getattr(obj, 'SerialNumber', None)
                                if serial is not None:
                                    serial_list.append(serial)
                            except:
                                continue
                        
                        if serial_list:
                            cmd_selection.Set(serial_list)
                            return len(serial_list)
                except Exception as e:
                    print(f"Command manager selection failed: {e}")
            
            return success_count
    
        def _create_fallback_ui(self):
            """Create a basic UI if XAML loading fails"""
            from System.Windows.Controls import TextBlock, Button
            from System.Windows import Thickness, FontWeights
            
            # Add a header
            header = TextBlock()
            header.Text = "Layer Selection Wizard (Fallback UI)"
            header.FontWeight = FontWeights.Bold
            header.Margin = Thickness(5)
            self.Children.Add(header)
            
            # Add status display
            self.StatusText = TextBlock()
            self.StatusText.Margin = Thickness(5)
            self.Children.Add(self.StatusText)
            
            # Add next button
            self.NextButton = Button()
            self.NextButton.Content = "Next"
            self.NextButton.Margin = Thickness(5)
            self.NextButton.Click += self.NextClicked
            self.Children.Add(self.NextButton)
    
        def OnLoad(self, cmd, buttons, event):
            """Called by TBC after the panel is created"""
            # Store command manager reference
            try:
                self.cmdManager = cmd
            except:
                self.cmdManager = None
            
            # Re-establish selection access with command manager context
            self._establish_selection_access()
            
            # Initialize the wizard
            self._initialize_wizard()
    
        def _initialize_wizard(self):
            """Initialize the wizard interface"""
            self._update_step_display()
            self._update_status("Welcome to the Layer Selection Wizard! Click 'Next' to begin.")
    
        def _update_step_display(self):
            """Update the step indicator and progress bar"""
            try:
                if hasattr(self, 'StepIndicator'):
                    self.StepIndicator.Text = f"Step {self.current_step} of {self.total_steps}"
                
                if hasattr(self, 'ProgressBar'):
                    self.ProgressBar.Value = self.current_step
                
                if hasattr(self, 'StepDescription') and self.current_step in self.steps:
                    step_info = self.steps[self.current_step]
                    self.StepDescription.Text = f"{step_info['title']}: {step_info['description']}"
                
                # Update button states
                if hasattr(self, 'BackButton'):
                    self.BackButton.IsEnabled = self.current_step > 1
                
                if hasattr(self, 'NextButton'):
                    if self.wizard_completed:
                        self.NextButton.Content = "Finish"
                    elif self.current_step >= self.total_steps:
                        self.NextButton.Content = "Complete"
                    else:
                        self.NextButton.Content = "Next"
                
                # Show debug button after step 1
                if hasattr(self, 'DebugButton'):
                    from System.Windows import Visibility
                    self.DebugButton.Visibility = Visibility.Visible if self.current_step > 1 else Visibility.Collapsed
                    
            except Exception as e:
                print(f"Error updating step display: {e}")
    
        def NextClicked(self, sender, e):
            """Handle Next button click"""
            if self.wizard_completed:
                # Close the wizard
                self.CloseClicked(sender, e)
                return
            
            if self.current_step <= self.total_steps:
                # Execute current step
                if self.current_step in self.steps:
                    step_info = self.steps[self.current_step]
                    self._update_status(f"Executing: {step_info['title']}...")
                    
                    try:
                        step_info['action']()
                    except Exception as e:
                        self._update_status(f"❌ Error in {step_info['title']}: {e}")
                        return
                
                # Move to next step
                if self.current_step < self.total_steps:
                    self.current_step += 1
                    self._update_step_display()
                else:
                    self.wizard_completed = True
                    self._update_step_display()
                    self._update_status("✅ Wizard completed! All objects on the same layers have been selected.")
                    self._show_results()
    
        def BackClicked(self, sender, e):
            """Handle Back button click"""
            if self.current_step > 1:
                self.current_step -= 1
                self.wizard_completed = False
                self._update_step_display()
                self._update_status(f"Returned to step {self.current_step}")
    
        def _step1_initialize(self):
            """Step 1: Initialize and check components"""
            self._update_status("Checking TBC components...")
            
            # Check selection access
            if self.selection_accessor is None and GlobalSelection is None:
                self._update_status("❌ No selection access method available")
                raise Exception("Cannot access TBC selection system")
            else:
                self._update_status("✅ Selection access established")
            
            # Check currentProject
            if self.currentProject is None:
                self._update_status("❌ Current project not available")
                raise Exception("Current project not available")
            else:
                proj_type = self.currentProject.GetType().Name
                self._update_status(f"✅ Current project available: {proj_type}")
            
            # Check command manager
            if self.cmdManager:
                self._update_status("✅ Command manager available")
            else:
                self._update_status("⚠ Command manager not available (some features may be limited)")
            
            self._update_status("✅ Step 1 complete: Components initialized")
    
        def _step2_analyze_selection(self):
            """Step 2: Analyze current selection"""
            self._update_status("Analyzing current selection...")
            
            # Get current selection using multiple methods
            self.initial_selection = self._get_selected_objects_multiple_methods()
            
            if not self.initial_selection:
                self._update_status("❌ No objects currently selected")
                raise Exception("Please select objects in TBC before proceeding")
            
            self._update_status(f"✅ Found {len(self.initial_selection)} selected objects:")
            
            # List selected objects
            for i, obj in enumerate(self.initial_selection[:5], 1):  # Show first 5
                obj_info = self._get_object_info(obj)
                self._update_status(f"  {i}. {obj_info}")
            
            if len(self.initial_selection) > 5:
                self._update_status(f"  ... and {len(self.initial_selection) - 5} more objects")
            
            self._update_status("✅ Step 2 complete: Selection analyzed")
    
        def _step3_extract_layers(self):
            """Step 3: Extract layer information"""
            self._update_status("Extracting layer information...")
            
            # Extract layers from selected objects
            self.layers_found = self._get_layers_from_objects(self.initial_selection)
            
            if not self.layers_found:
                self._update_status("❌ Could not determine layers from selected objects")
                raise Exception("No layer information found in selected objects")
            
            self._update_status(f"✅ Found {len(self.layers_found)} unique layers:")
            for layer in self.layers_found:
                self._update_status(f"  - {layer}")
            
            # DEBUG: Print detailed layer extraction information
            self._update_status("")
            self._update_status("=== LAYER EXTRACTION DEBUG ===")
            self._update_status(f"Total objects analyzed: {len(self.initial_selection)}")
            self._update_status(f"Unique layers found: {len(self.layers_found)}")
            
            # Debug each object's layer information
            for i, obj in enumerate(self.initial_selection, 1):
                try:
                    obj_type = obj.GetType().Name
                    obj_name = getattr(obj, 'Name', '<No Name>')
                    self._update_status(f"Object {i}: {obj_type} - {obj_name}")
                    
                    # Try each layer property
                    layer_found = False
                    layer_props = ['Layer', 'LayerName', 'LayerId', 'LayerKey']
                    
                    for prop in layer_props:
                        if hasattr(obj, prop):
                            try:
                                layer_value = getattr(obj, prop)
                                if layer_value:
                                    # Determine layer value type and content
                                    if hasattr(layer_value, 'Name'):
                                        layer_display = f"{layer_value.Name} (via {prop}.Name)"
                                    elif hasattr(layer_value, 'Key'):
                                        layer_display = f"{layer_value.Key} (via {prop}.Key)"
                                    else:
                                        layer_display = f"{layer_value} (via {prop})"
                                    
                                    self._update_status(f"  ✅ Layer: {layer_display}")
                                    layer_found = True
                                    break
                            except Exception as e:
                                self._update_status(f"  ❌ Error accessing {prop}: {e}")
                    
                    if not layer_found:
                        self._update_status("  ⚠ No layer information found")
                        
                        # Try reflection-based discovery
                        try:
                            obj_type_obj = obj.GetType()
                            if hasattr(obj_type_obj, 'GetProperties'):
                                layer_related_props = []
                                for prop in obj_type_obj.GetProperties():
                                    prop_name = prop.Name.lower()
                                    if 'layer' in prop_name:
                                        layer_related_props.append(prop.Name)
                                
                                if layer_related_props:
                                    self._update_status(f"  📋 Available layer-related properties: {', '.join(layer_related_props)}")
                                else:
                                    self._update_status("  📋 No layer-related properties found via reflection")
                        except Exception as e:
                            self._update_status(f"  ❌ Reflection analysis failed: {e}")
                    
                except Exception as e:
                    self._update_status(f"Object {i}: Error analyzing - {e}")
            
            # Summary of found layers with details
            self._update_status("")
            self._update_status("Layer Summary:")
            if self.layers_found:
                for layer in sorted(self.layers_found):
                    # Count how many objects are on this layer
                    objects_on_layer = 0
                    for obj in self.initial_selection:
                        obj_layers = self._get_layers_from_objects([obj])
                        if layer in obj_layers:
                            objects_on_layer += 1
                    
                    self._update_status(f"  📁 '{layer}' - {objects_on_layer} object(s)")
            else:
                self._update_status("  ❌ No layers identified")
            
            self._update_status("=== END LAYER EXTRACTION DEBUG ===")
            self._update_status("")
            
            self._update_status("✅ Step 3 complete: Layers extracted")
    
        def _step4_find_objects(self):
            """Step 4: Find all objects on the identified layers"""
            self._update_status("Searching for objects on identified layers...")
            
            # Try TBC native command first
            if self._try_native_select_by_layer():
                self.objects_on_layers = self._get_selected_objects_multiple_methods()
                self._update_status(f"✅ Used TBC native command, found {len(self.objects_on_layers)} objects")
            else:
                # Use custom implementation
                self._update_status("Using custom layer search...")
                self.objects_on_layers = self._find_objects_on_layers(self.layers_found)
            
            if not self.objects_on_layers:
                self._update_status("❌ No objects found on target layers")
                raise Exception("No objects found on the identified layers")
            
            self._update_status(f"✅ Found {len(self.objects_on_layers)} objects on target layers")
            self._update_status("✅ Step 4 complete: Objects found")
    
        def _step5_select_objects(self):
            """Step 5: Select all found objects"""
            self._update_status("Selecting all objects on target layers...")
            
            try:
                # Use multiple methods to select objects
                selected_count = self._select_objects_multiple_methods(self.objects_on_layers)
                
                if selected_count > 0:
                    self._update_status(f"✅ Successfully selected {selected_count} objects")
                    self._update_status("✅ Step 5 complete: Objects selected")
                else:
                    self._update_status("❌ No objects were successfully selected")
                    raise Exception("Failed to select any objects")
                
            except Exception as e:
                self._update_status(f"❌ Error selecting objects: {e}")
                raise
    
        def _try_native_select_by_layer(self):
            """Try to use TBC's native Select by Layer command"""
            if not hasattr(self, 'cmdManager') or not self.cmdManager:
                return False
            
            try:
                mgr = getattr(self.cmdManager, 'CommandManager', None)
                if mgr:
                    command_variants = ["SelectByLayer", "Select by Layer", "SelectSimilarByLayer"]
                    
                    for cmd_name in command_variants:
                        try:
                            if hasattr(mgr, 'ExecuteCommand'):
                                result = mgr.ExecuteCommand(cmd_name)
                                if result:
                                    self._update_status(f"✅ Used TBC command: {cmd_name}")
                                    return True
                        except:
                            continue
            except:
                pass
            
            return False
    
        def _show_results(self):
            """Show the results of the wizard"""
            try:
                # Show results section
                if hasattr(self, 'ResultsHeader'):
                    from System.Windows import Visibility
                    self.ResultsHeader.Visibility = Visibility.Visible
                    self.ObjectsList.Visibility = Visibility.Visible
                    
                    # Populate results list
                    self.ObjectsList.Items.Clear()
                    for obj in self.objects_on_layers[:50]:  # Show first 50 results
                        obj_info = self._get_object_info(obj)
                        self.ObjectsList.Items.Add(obj_info)
                    
                    if len(self.objects_on_layers) > 50:
                        self.ObjectsList.Items.Add(f"... and {len(self.objects_on_layers) - 50} more objects")
            except Exception as e:
                print(f"Error showing results: {e}")
    
        # Include all the helper methods from the original implementation
        def _get_selected_objects(self):
            """Get currently selected objects - legacy method for compatibility"""
            return self._get_selected_objects_multiple_methods()
    
        def _get_layers_from_objects(self, objects):
            """Extract layer names/IDs from the given objects"""
            layers = set()
            
            for obj in objects:
                try:
                    layer_props = ['Layer', 'LayerName', 'LayerId', 'LayerKey']
                    
                    for prop in layer_props:
                        if hasattr(obj, prop):
                            layer_value = getattr(obj, prop)
                            if layer_value:
                                if hasattr(layer_value, 'Name'):
                                    layers.add(layer_value.Name)
                                elif hasattr(layer_value, 'Key'):
                                    layers.add(str(layer_value.Key))
                                else:
                                    layers.add(str(layer_value))
                                break
                            
                except Exception as e:
                    continue
            
            return layers
    
        def _find_objects_on_layers(self, target_layers):
            """Find all objects in the project that are on the specified layers"""
            objects_on_layers = []
            
            try:
                all_objects = self._get_all_project_objects()
                
                for obj in all_objects:
                    try:
                        obj_layers = self._get_layers_from_objects([obj])
                        if obj_layers.intersection(target_layers):
                            objects_on_layers.append(obj)
                    except:
                        continue
                        
            except Exception as e:
                print(f"Error finding objects on layers: {e}")
            
            return objects_on_layers
    
        def _get_all_project_objects(self):
            """Get all objects in the current project"""
            all_objects = []
            
            try:
                if hasattr(self.currentProject, 'Members'):
                    for member in self.currentProject.Members:
                        all_objects.append(member)
                elif hasattr(self.currentProject, 'Objects'):
                    for obj in self.currentProject.Objects:
                        all_objects.append(obj)
                elif hasattr(self.currentProject, 'Items'):
                    for item in self.currentProject.Items:
                        all_objects.append(item)
            except Exception as e:
                print(f"Error getting project objects: {e}")
            
            return all_objects
    
        def _get_object_info(self, obj):
            """Get descriptive information about an object"""
            if obj is None:
                return "<None>"
            
            parts = []
            
            try:
                type_name = obj.GetType().Name
                parts.append(type_name)
            except:
                parts.append("<Unknown Type>")
            
            try:
                name = getattr(obj, 'Name', None)
                if name:
                    parts.append(f"Name='{name}'")
            except:
                pass
            
            try:
                layers = self._get_layers_from_objects([obj])
                if layers:
                    parts.append(f"Layer={list(layers)[0]}")
            except:
                pass
            
            return " | ".join(parts) if parts else "<No Info>"
    
        def _update_status(self, message):
            """Update status display"""
            print(message)  # Always print to console
            
            try:
                if hasattr(self, 'StatusText'):
                    # Append to existing status (keep log)
                    current_text = getattr(self.StatusText, 'Text', '')
                    if current_text:
                        self.StatusText.Text = current_text + "\n" + message
                    else:
                        self.StatusText.Text = message
            except:
                pass
    
        def _debug_components(self):
            """Debug helper method to inspect selection access and currentProject"""
            self._update_status("=== COMPONENT DEBUGGING ===")
            
            # Debug selection access methods
            self._update_status("Selection Access Analysis:")
            
            if GlobalSelection is not None:
                try:
                    self._update_status(f"GlobalSelection Type: {GlobalSelection.GetType()}")
                    
                    import System.Reflection as Reflection
                    members = GlobalSelection.GetType().GetMembers(Reflection.BindingFlags.Public | Reflection.BindingFlags.Static | Reflection.BindingFlags.Instance)
                    member_names = [m.Name for m in members if not m.Name.startswith('_')]
                    self._update_status(f"GlobalSelection Members: {', '.join(sorted(set(member_names)))}")
                    
                except Exception as e:
                    self._update_status(f"Error analyzing GlobalSelection: {e}")
            else:
                self._update_status("GlobalSelection is None")
            
            if self.selection_accessor is not None:
                try:
                    self._update_status(f"Selection Accessor Type: {self.selection_accessor.GetType()}")
                except Exception as e:
                    self._update_status(f"Error analyzing selection accessor: {e}")
            else:
                self._update_status("No selection accessor established")
            
            # Debug currentProject in detail  
            self._update_status("currentProject Analysis:")
            if self.currentProject is None:
                self._update_status("currentProject is None")
            else:
                try:
                    self._update_status(f"currentProject Type: {self.currentProject.GetType()}")
                    
                    import System.Reflection as Reflection
                    members = self.currentProject.GetType().GetMembers(Reflection.BindingFlags.Public | Reflection.BindingFlags.Instance)
                    member_names = [m.Name for m in members if not m.Name.startswith('_')]
                    self._update_status(f"currentProject Members: {', '.join(sorted(set(member_names)))}")
                    
                except Exception as e:
                    self._update_status(f"Error analyzing currentProject: {e}")
            
            # Debug current wizard state and found layers
            self._update_status("")
            self._update_status("Wizard State Analysis:")
            self._update_status(f"Current Step: {self.current_step} of {self.total_steps}")
            self._update_status(f"Wizard Completed: {self.wizard_completed}")
            self._update_status(f"Initial Selection Count: {len(self.initial_selection)}")
            
            # Debug layers if we've reached step 3 or beyond
            if self.current_step >= 3 and self.layers_found:
                self._update_status("")
                self._update_status("Found Layers Debug:")
                self._update_status(f"Total Unique Layers: {len(self.layers_found)}")
                for i, layer in enumerate(sorted(self.layers_found), 1):
                    self._update_status(f"  {i}. '{layer}' (Type: {type(layer).__name__})")
            elif self.current_step >= 3:
                self._update_status("No layers found yet (may indicate extraction issue)")
            
            # Debug objects on layers if we've reached step 4 or beyond
            if self.current_step >= 4 and self.objects_on_layers:
                self._update_status("")
                self._update_status("Objects on Layers Debug:")
                self._update_status(f"Total Objects Found: {len(self.objects_on_layers)}")
                
                # Show first few objects as examples
                for i, obj in enumerate(self.objects_on_layers[:3], 1):
                    obj_info = self._get_object_info(obj)
                    self._update_status(f"  {i}. {obj_info}")
                
                if len(self.objects_on_layers) > 3:
                    self._update_status(f"  ... and {len(self.objects_on_layers) - 3} more objects")
            elif self.current_step >= 4:
                self._update_status("No objects on layers found yet")
            
            self._update_status("=== END COMPONENT DEBUGGING ===")
    
        def DebugClicked(self, sender, e):
            """Debug button handler"""
            self._debug_components()
    
        def CloseClicked(self, sender, e):
            """Close button handler"""
            closer = getattr(self, 'CloseUICommand', None)
            if closer:
                try: 
                    closer()
                except: 
                    pass

    Selection.xaml
    <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <StackPanel Margin="10">
            <!-- Header with step indicator -->
            <StackPanel Orientation="Horizontal" Margin="0,0,0,10">
                <TextBlock Text="Layer Selection Wizard" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
                <TextBlock x:Name="StepIndicator" Text="Step 1 of 5" FontSize="12" Foreground="Gray" 
                           Margin="10,0,0,0" VerticalAlignment="Center"/>
            </StackPanel>
            
            <!-- Progress bar -->
            <ProgressBar x:Name="ProgressBar" Height="4" Margin="0,0,0,10" Minimum="0" Maximum="5" Value="1"/>
            
            <!-- Step description -->
            <TextBlock x:Name="StepDescription" Text="Initializing wizard..." FontWeight="SemiBold" 
                       Margin="0,0,0,10" TextWrapping="Wrap"/>
            
            <!-- Status display -->
            <TextBlock x:Name="StatusText" Text="Ready to start..." Margin="0,0,0,10" TextWrapping="Wrap" 
                       Background="LightGray" Padding="5"/>
            
            <!-- Results display -->
            <TextBlock x:Name="ResultsHeader" Text="Results:" FontWeight="SemiBold" Margin="0,5,0,5" 
                       Visibility="Collapsed"/>
            <ListBox x:Name="ObjectsList" Height="150" Margin="0,0,0,10" Visibility="Collapsed"/>
            
            <!-- Action buttons -->
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                <Button x:Name="BackButton" Content="Back" Width="60" Margin="0,0,5,0" Click="BackClicked" 
                        IsEnabled="False" ToolTip="Go to previous step"/>
                <Button x:Name="NextButton" Content="Next" Width="60" Margin="0,0,5,0" Click="NextClicked" 
                        ToolTip="Proceed to next step"/>
                <Button x:Name="DebugButton" Content="Debug" Width="60" Margin="0,0,5,0" Click="DebugClicked" 
                        ToolTip="Show debugging information" Visibility="Collapsed"/>
                <Button Content="Close" Width="60" Click="CloseClicked"/>
            </StackPanel>
        </StackPanel>
    </UserControl>


    I feel like I am poking in the dark trying to find the proper attribute name by sheer luck.

    Any help is appreciated.,


    ------------------------------
    Eero Isola
    ------------------------------



  • 5.  RE: Using Select by Layer macro by API

    Posted 09-22-2025 14:29

    I'm still on vacation until about mid October, but I quickly got me a trial license.

    The attached code is basically the one I posted in the other thread, and despite a typo it works.

    Have you checked out my training videos https://www.dropbox.com/scl/fo/7j7skk1rou5ixjjqm216x/h?rlkey=jk61t50kurct88uydriocsyt1&st=24lbqwjc&dl=0

    I'm not a software developer and they might be a bit lengthy but should give you an idea about using the object browser and finding attribute names as well.

    They have been recorded long before V5.90, so they show the old macro folder location. Just ignore that and use the new one "C:\ProgramData\Trimble\MacroCommands3".

    Have a look at video "00 Setup VS and start from Template (no sound - from GIF recorder)" in order to see how to populate the object browser.

    And video "Macro 3" as an example on how to find available attributes and methods.

    My public GitHub repo in case you haven't seen the post about it. https://github.com/RonnySchneider/SCR_Macros_Public



    ------------------------------
    Ronny Schneider
    ------------------------------

    Attachment(s)



  • 6.  RE: Using Select by Layer macro by API

    Posted 09-25-2025 05:50

    Thank you for your help.
    With that file, I could build a suitable solution.

    I only need to figure out how to make a UI-less macro now.



    ------------------------------
    Eero Isola
    ------------------------------