Search code examples
rubysketchup

SketchUp Ruby API - ArgumentError when delete all layers


I made a script to delete all layers imported from a .dwg file:

# import the dwg file
show_summary = false
if Sketchup.active_model.import file_path, show_summary

    # delete all layer
    protected_layers = ['Layer0', '! 3D', '! AutoCad']
    10.times do
        Sketchup.active_model.definitions.purge_unused
        Sketchup.active_model.layers.purge_unused

        Sketchup.active_model.layers.each { |layer|
            layer_name = layer.name
            if not layer.deleted? and not protected_layers.include? layer_name  
                puts "\tdeleted"
                Sketchup.active_model.layers.remove layer
            end
        }
    end
end

When I import a big dwg file, all layers are not instantly imported (more or less 100 layers)... So I added a 10.times loop to all layers are completly removed. My script work perfectly until there are 10 layers left. I got an error like this:

Error: #<ArgumentError: layer doesn't belong to this model>

So how to check my layer is not already deleted? I tryed theses solution but no one seem worked

  • layer.deleted? not return true
  • layers.at layer.name not return nil
  • begin;rescue;end around the loop crash Sketchup

Solution

  • You are iterating the collection you are removing elements from. That is never a safe thing to do.

    Cache the layers to an array an iterate that.

    Here is an example:

    model = Sketchup.active_model
    # Map layer names to layers - as object comparison is much faster than string
    # comparisons.
    protected_layers = ['Layer0', '! 3D', '! AutoCad'].map { |layer_name|
      model.layers[layer_name]
    }
    # Start an operation as this keeps the undo stack clean and makes the script
    # fun faster.
    model.start_operation('Remove Layers', true)
    model.layers.to_a.each { |layer|
      next if layer.deleted? || protected_layers.include?(layer)
      model.layers.remove(layer)
    }
    model.commit_operation