The api only observe a single element is added to the Sketchup::Entities collection.
But How to observe multiple element is added to the Sketchup::Entities collection?
I write a code to explain the issue.
model = Sketchup.active_model
# create two lines
line1 = model.entities.add_line [0,0,0], [10,5,0]
line2 = model.entities.add_line [10,5,0], [10,10,0]
# puts the persistent id of lines
puts line1.persistent_id, line2.persistent_id # => 9036, 9039
# earse one line
model.start_operation('erase line1', true)
model.entities.erase_entities(line1)
model.commit_operation
# earse another line, append operation to the previous operation
model.start_operation('erase line2', true, false, true)
model.entities.erase_entities(line2)
model.commit_operation
# bind a observer and puts the the persistent id of new entity.
class IssueObserver < Sketchup::EntitiesObserver
def onElementAdded(entities, entity)
# FIXME: only show the undo earse line1 operation
p entity.persistent_id # => 9036
end
end
model.entities.add_observer IssueObserver.new
# undo the earse operation
Sketchup.undo
Do I see it right that the code sample adds the observer after the entities have been created?
Then it won’t be notified about these entities (maybe an onElementRemoved or onEraseEntities would be notified).
Use a combo of ModelObserver and EntitiesObserver. Listen to when onStartTransaction and corresponding commit/abort triggers. Any entity you trigger in between you can then act upon in bulk.
I can’t watch the observer to find the attribute of the deleted entity .
...
def onComponentInstanceRemoved(definition, instance)
# TODO: do something, just print the the id of test_dict here
puts instance.get_attribute('test_dict', 'id')
end
...
entities = Sketchup.active_model.entities
test_group = entities.add_group
test_group.set_attribute 'test_dict', 'id', 123
entities.erase_entities(test_group)
This is a sadly thing for me.
So I have to write a lot of hack codes to improve the api.
In the process, I encountered more problems about the ruby api.
Then I have to write move codes to fix the problems.
# open Entity class and cache attributes.
class Sketchup::Entity
# initialize attributes from attribute_dictionaries
def init_attribute
@dicts = {}
unless self.attribute_dictionaries.nil?
self.attribute_dictionaries.each do |attribute_dictionary|
dict = {}
attribute_dictionary.each { |key, value| dict[key] = value }
@dicts[attribute_dictionary.name] = dict
end
end
end
alias_method :origin_get_attribute, :get_attribute
alias_method :origin_set_attribute, :set_attribute
def get_attribute(dict_name, key, default_value = nil)
if self.deleted?
if !@dicts.nil?
dict = @dicts[dict_name]
if !dict.nil? && dict.has_key?(key)
dict[key]
else
default_value
end
end
else
origin_get_attribute(dict_name, key, default_value = nil)
end
end
def set_attribute(dict_name, key, value)
@dicts ||= {}
@dicts[dict_name] ||= {}
@dicts[dict_name][key] = value
origin_set_attribute(dict_name, key, value)
end
end
class Sketchup::ComponentDefinition
# initialize attributes of instances
def init_attribute
return if self.deleted?
self.instances.each(&:init_attribute)
end
end
class Sketchup::DefinitionList
# initialize attributes of definitions
def init_attribute
return if self.deleted?
self.each(&:init_attribute)
end
end
# initialize attributes when new component instance define
class HackAttrDefinitionObserver < Sketchup::DefinitionObserver
def onComponentInstanceAdded(definition, instance)
instance.init_attribute
end
def onComponentInstanceRemoved(definition, instance)
# TODO: do something, just print the the id of test_dict here
puts instance.get_attribute('test_dict', 'id')
end
end
class HackAttrDefinitionsObserver < Sketchup::DefinitionsObserver
def initialize(model)
@observer = HackAttrDefinitionObserver.new
model.definitions.init_attribute
model.definitions.each { |definition| definition.add_observer @observer }
end
def onComponentAdded(definitions, definition)
return if definition.deleted?
definition.init_attribute
definition.add_observer @observer
end
end
# undo and redo may cause attributes error
class HackAttrModelObserver < Sketchup::ModelObserver
def init_attribute(model)
model.definitions.init_attribute
end
alias_method :onTransactionUndo, :init_attribute
alias_method :onTransactionRedo, :init_attribute
end
class HackAttrAppObserver < Sketchup::AppObserver
def initialize
bind_observers(Sketchup.active_model)
end
def bind_observers(model)
model.add_observer HackAttrModelObserver.new
model.definitions.add_observer HackAttrDefinitionsObserver.new(model)
end
alias_method :onNewModel, :bind_observers
alias_method :onOpenModel, :bind_observers
alias_method :onActivateModel, :bind_observers
end
unless Object.const_defined? 'HACK_ATTR_APP_OBSERVER'
HACK_ATTR_APP_OBSERVER = HackAttrAppObserver.new
Sketchup.add_observer HACK_ATTR_APP_OBSERVER
end
## TEST CODE
entities = Sketchup.active_model.entities
test_group = entities.add_group
test_group.set_attribute 'test_dict', 'id', 123
entities.erase_entities(test_group)
So I hope the SU to impove the api,the next version(SU 2019)
can get the info of delete entity(not the enttiyId)
element change by the undo and redo operation can observe
BTW, Revit can read the delete info directly.
Hope to be more and more friendly to developers soon
I hope this code you show is only for your internal purpose.
It is not allowable to release public code that modifies either Ruby Core classes or SketchUp API classes. This is a shared environment we all use.