I have created a set of groups, and also defined a common Observer for all of them in the following way:
class GroupObserver < Sketchup::InstanceObserver
def onEraseEntity (group)
name = group.name
end
end
group.add_observer ( GroupObserver.new )
When I remove a group, I would like to retrieve the name of the group removed. Debugging I have observed that when entering the method the element is already deleted, so it is not valid. Is it possible to have access to the entity info inside the onEraseEntity method? If not, is there any other way to solve this issue?
@DanRathbun Thank you for your reply. But, what if I want to check if a page (scene) is removed? I cannot use the #onComponentInstanceRemoved in this case.
My scenario is that I have a pyramid (group of faces) defining a camera pose for a scene, and both page and group have the same name. If I remove the scene I want to remove the pyramid as well, and other way around, if I remove the pyramid the page must be deleted. I want to achieve something like this:
class PageObserver < Sketchup::EntityObserver
def onEraseEntity(page)
group_name = page.name
# Remove group by its name
end
end
class GroupObserver < Sketchup::InstanceObserver
def onEraseEntity(group)
scene_name = group.name
# Remove scene by its name
end
end
page.add_observer( PageObserver.new )
group.add_observer( GroupObserver.new )
Assemble the @@hash as the model loads and adjust it whenever the model’s pages change at all.
You can compare the current array of model.pages.to_a to your array of @@hash.keys, any elements that are missing from the @@hash you add in as new entries because it’s a new page, any that are in the @@hash.keys but not in the current array have been deleted, so that way you can look up that key’s value [name] in @@hash and delete its group etc as you wish - before removing that key from the @@hash because it’s no longer relevant…
It is problematic to be modifying the model inside observer callback methods, so no guarantees here.
Your scenario needs to be sure not to cause a “vicious loop” (hence the @@active class variable and class attr reader method.)
module Author
module SomePlugin
class PagesObserver < Sketchup::PagesObserver
@@active ||= false
def self::active?
@@active ? true : false
end
def onElementRemoved(pages, page)
@@active = true
# bail out if called by the other observer callback:
return if GroupDefinitionObserver::active?
group_name = page.name
# Remove group by its name
grp = nil
pages.model.definitions.each {|cd|
next if cd.instances.empty?
grp = cd.instances.find {|i| i.name == group_name }
break if grp
}
if grp
model.start_operation("Erase Group",true,false,true)
grp.erase! # will fire the other observer
model.commit_operation
end
rescue => e
puts e.inspect
puts e.backtrace if $VERBOSE
ensure # always do when returning from callback
@@active = false
end
end # class
class GroupDefinitionObserver < Sketchup::DefinitionObserver
@@active ||= false
def self::active?
@@active ? true : false
end
def onComponentInstanceRemoved(definition, group_instance)
@@active = true
# bail out if called by the other observer callback:
return if PagesObserver::active?
scene_name = group_instance.name
# Remove scene by its name
return if definition.model.pages.size.zero?
page = definition.model.pages.find {|pg| pg.name == scene_name }
if page
model.start_operation("Erase Scene",true,false,true)
page.erase! # will fire the other observer
model.commit_operation
end
rescue => e
puts e.inspect
puts e.backtrace if $VERBOSE
ensure # always do when returning from callback
@@active = false
end
end # class
# This should be added by an AppObserver:
Sketchup::active_model.pages.add_observer( PagesObserver.new )
# This should be added by a DefinitionsObserver:
group.definition.add_observer( GroupDefinitionObserver.new )
end
end
However, in GroupDefinitionObserver, the element is already deleted when entering in onComponentInstanceRemoved, so it is not possible to have access to its name.
Well sorry about this. I do not like it either. We’ve complained about these frivolous deletion observer callbacks for years without getting the much needed “onBeforeDelete” callbacks.
So like TIG suggests you have to write your own companion collection of data, and when one of these frivolous deletion callbacks gets called, you then iterate your “companion” collection, checking the object reference key for validity.
result = @@hash.find {|key,val| key.deleted? }
if result.nil?
# no match so bailout
else # result is an array of [key, val]
deleted_obj, data = result
# assuming data is a hash:
deleted_name = data[name]
end
You could also use OpenStruct for your data object type.
Yes, I finally used that trick proposed by @TIG to solve it, but it would be more straight-forward to have the option with onEraseEntity and onComponentInstanceRemoved.
It seems that the EntitiesObserver can handle it in onElementRemoved, but the EntityObserver cannot.
Thank you guys for your help.