Congratulations for your detailed explanations dezmo!
Indeed I can provide better explanations.
For the moment I have not done so to avoid dispersing ourselves on other themes that are not related to my difficulties.
I also wanted to avoid discouraging readers because the problem is relatively long to explain.
Alright, I’ll go into the details.
Origin of the problem:
Some of my dynamic components generate “ComponentDefinition” when the “copy” attribute is used.
This happens because each copy is automatically made unique by the dynamic component script or SketchUp?
The number of copies can evolve with the “OnClick” method which is the little hand that interacts with the dynamic components.
If the number of copies reaches 0, the “ComponentInstance” no longer exist but the “ComponentDefinition” still exist.
Result:
This unnecessarily bloats the file and drastically reduces the response time of my dynamic components!
For this reason I wrote a method “purgeMyComponent” which purges the definitions of my components which do not have an instance in the model.
I now want my method to activate each time a definition without an instance is generated by one of my dynamic components.
Objective:
Found the lightest and most efficient solution possible!
Option 1:Use Sketchup::EntityObserver
EntityObservers only last for the duration of the SketchUp session.
Consequently each time a model is opened, the “onOpenModel” observer must be activated to attach all instances of the components with “onChangeEntity”!
To attach the instances I want to monitor, I need to loop through all instances of the model!
The questions:
If a project has thousands of instances, won’t the observer slow down SketchUp too much?
If thousands of instances are modified at once, how to run the cleanup method only once at the end?
This is the challenge I must take up while deepening my knowledge of Ruby.
Here is a small example of code that I am writing:
# Should run the cleanup method only once after all entities are changed.
class MyEntityObserver < Sketchup::EntityObserver
def initialize
@value = true
end
def onChangeEntity(entity)
entity.remove_observer(self)
return unless @value
p "purgeMyComponent"
@value = false
end
end
# Attach Sketchup::EntityObserver to all instances to watch when the "OnClick"
# tool is activated.
class MyToolsObserver < Sketchup::ToolsObserver
def onActiveToolChanged(tools, tool_name, tool_id)
if tool_name == "RubyTool"
mod = Sketchup.active_model
defs = mod.definitions
defs.each do |d|
d.instances.each do |i|
if i.attribute_dictionary("My_attributes")
ref = i.get_attribute "My_attributes","reference","0"
if ref == "component_to_monitor"
p "Observer Attach to: #{i.definition.name}"
i.add_observer(MyEntityObserver.new)
end
end
end
end
end
end
end
# Attach "Sketchup::ToolsObserver" when opening a project or a new model.
class MyAppObserver < Sketchup::AppObserver
def onNewModel(model)
Sketchup.active_model.tools.add_observer(MyToolsObserver.new)
end
def onOpenModel(model)
Sketchup.active_model.tools.add_observer(MyToolsObserver.new)
end
end
Sketchup.add_observer(MyAppObserver.new)
For the moment I don’t know yet how to execute the cleaning method only once after the change of all the entities.
There is no need to call Sketchup.active_model within these callbacks because SketchUp passes the model object in the model argument, so …
# Attach "Sketchup::ToolsObserver" when opening a project or a new model.
class MyAppObserver < Sketchup::AppObserver
def onNewModel(model)
model.tools.add_observer(MyToolsObserver.new)
end
def onOpenModel(model)
model.tools.add_observer(MyToolsObserver.new)
end
end
I still have to find a better alternative than “ToolsObserver” to start or stop the observer when my dynamic components are used!
The observer must take the minimum possible resources because it must be active all the session to monitor my components.
EntityObserver:
Can be a good alternative provided that I don’t have to attach all my instances each time the model is opened.
This would be much too cumbersome, especially since my components have a lot of entities.
The first thing is to create an attribute library on all the definitions offered by my extension.
For example if all my definitions are present in an open model, I can use this method:
# Attach an attribute dictionary to all definitions of a model
mod = Sketchup.active_model
defs = mod.definitions
defs.each{|d| d.set_attribute "my_dictionary","my_attribute","my_value"}
Then I have to attach Entities Observer to each opening of a model:
# Automatically purge definitions without instances with a defined attribute.
class MyEntitiesObserver < Sketchup::EntitiesObserver
def onElementModified(entities, entity)
return unless entity.is_a?(Sketchup::ComponentDefinition)
return if entity.count_instances > 0 || entity.attribute_dictionaries.nil?
val = entity.get_attribute "my_dictionary","my_attribute"
return unless val == "my_value"
return unless entity.valid?
entities.model.start_operation("Purge Definition", true)
entity.entities.clear!
entities.model.commit_operation
end
end
# Attach "ToolsObserver" when opening a project or a new model
class MyAppObserver < Sketchup::AppObserver
def activate_observer_entities( model )
@change_entities ||= MyEntitiesObserver.new
model.entities.add_observer(@change_entities)
end
def onNewModel(model)
activate_observer_entities( model )
end
def onOpenModel(model)
activate_observer_entities( model )
end
end
Sketchup.add_observer(MyAppObserver.new)
If you have a better alternative, I’m always open to progress.
The method of the previous post is not finally suitable.
If I add a new instance to the model, Sketchup::EntitiesObserver.onElementModified will fire!
The result is that I find myself in a loop where the observer will want to purge the instance that has just been added.
I need to find a way for my watcher to activate only when one of my dynamic components is changed by the user.
This same observer must be deleted after the modification of the component.
When I think I have found a solution, I realize that is far from the case.
It’s not a big deal because it’s with this kind of disappointment that I’m going to end up progressing.