Hi! I am new to SketchUp and ruby all together. I got the following code to work with which displays a menu item called “Dimensions”, which basically shows the measurements of a plane on the ruby console. I also need to show the measurements, in a floating window
I need the output to change when the plane is modified (something like using a change listener). My problem is the onChangeEntity doesn’t seem to fire when I make a change to the entity.
The script dimentCh.rb
# This is an example of an observer that watches an entity for deletion
# and shows a messagebox.
class MyEntityObserver < Sketchup::EntityObserver
def onChangeEntity(entity)
#UI.messagebox("onChangeEntity: " + entity.to_s)
model = Sketchup.active_model
mname = model.title
boundingBox = model.selection[0].bounds
dims = [ boundingBox.height,
boundingBox.width,
boundingBox.depth ]
dims.sort!
puts "****START****"
puts "Thickness: "
puts dims[0].to_s
puts "\nWidth: "
puts dims[1].to_s
puts "\nLength: "
puts dims[2].to_s
puts "******END****"
end
end
# Attach the observer. (Assumes there is an entity in the model.)
Sketchup.active_model.entities[0].add_observer(MyEntityObserver.new)
Once again I have no experience in ruby or SketchUp, Please help, Thanks!
You were attaching the observer to the first entity in the model, which is not necessarily the entity you wanted (most likely it was an edge). Unfortunately some observers don’t trigger reliably and EntityObserver seems not to trigger changes on edges.
For testing/development, use rather model.selection[0]. Also, don’t refer to the selection from inside the observer, because you cannot predict what the user has currently selected.
Little side note: How do you know that the model on which your observer acts is the currently focussed model? Sketchup.active_model just means “give me whatever model is currently focussed”. It just happens that the Windows version of SketchUp only opens one model at a time, but as a programmer one follows instead the API’s “contract”. Try always to avoid assumptions if you can not guarantee it’s 100% of the time the case.
class MyEntityObserver < Sketchup::EntityObserver
def onChangeEntity(entity)
# If you need a reference to the model on which the observer was triggered:
# model = entity.model
boundingBox = entity.bounds
dims = [ boundingBox.height,
boundingBox.width,
boundingBox.depth ]
dims.sort!
puts "****START****"
puts "Thickness: "
puts dims[0].to_s
puts "\nWidth: "
puts dims[1].to_s
puts "\nLength: "
puts dims[2].to_s
puts "******END****"
# You want to know when an error happens, so you can inspect what's wrong:
rescue Exception => e
puts e
puts e.backtrace
end
end
# Attach the first currently selected entity.
Sketchup.active_model.selection[0].add_observer(MyEntityObserver.new)
A more reliable alternative is the EntitiesObserver (the entities collection that contains the entity). It triggers for every changed entity in this entity collection, but you can for example tell it for which entity to listen specifically:
module MyNameSpace
class MyEntitiesObserver < Sketchup::EntitiesObserver
def initialize(entity)
@entity = entity
end
def onElementModified(entities, entity)
if entity == @entity
bounds = entity.bounds
dimensions = [:height, :width, :depth].map!{ |d| bounds.send(d) }.sort
puts "entity #{entity} has changed its dimensions #{dimensions}"
end
end
end
def self.start(entity_to_observe)
entities = entity_to_observe.parent.entities
# Pass to the observer information that it needs.
entities.add_observer(MyEntitiesObserver.new(entity_to_observe))
end
self.start(Sketchup.active_model.selection[0])
end
You can use this pattern also for passing results from the observer back to a class of your plugin.
class MyEntitiesObserver < Sketchup::EntitiesObserver
def initialize(tool)
@tool = tool
end
def onElementModified(entities, entity)
if entity.is_a?(Sketchup::Edge)
@tool.handle_further(entity)
end
end
end
class MyTool
def initialize(model)
@model = model
# Pass to the observer the instance of this tool,
# so that the observer can call methods of this tool
observer = MyEntitiesObserver.new(self)
@model.entities.add_observer(observer)
end
def handle_further(entity)
# Do something.
end
end
The link that @Aerilius gave as “some observers don’t trigger reliably” hasn’t been updated since SketchUp 8, so don’t be surprised if you find inconsistencies in later releases. Also, as discussed in this topic the logic of what fires when can sometimes be different than what you might expect.
It will help if you go to: http://ruby-doc.org/core-2.0.0/
And read the pages listed under Files section …
that are at the “doc/” root level, and “doc/syntax” level.
(Skip the “rdoc” and “rake” documents, you don’t need to know that for embedded Ruby.)
I’ve been using similar patterns lately. I do minimal code in the observer events directly - mainly delegate into whatever needs to listen. That way the observer doesn’t need to know the logic of the listener.
I’ve also started to define the initialize and forwarder functions in a mix-in module so I can reuse the pattern in multiple observers. It’s on my list of articles to write about.