Observer Get Previous Value?

Is it possible to get the old value from an observer?

Simple use case: If a user changes the name of a layer, I would like to get the old name of the layer and the new name. If onLayerChanged provided arguments like (layers, new_layer, old_layer) this would be possible.

Can the method not provide this information because the old layer information has already been changed/deleted by the time onLayerChanged is called?

Is the best solution to just save all layer names and then iterate for the one whose name no longer exists in onLayerChanged and assume that is the changed one?

This is one of the most common feature requests for Observers. Sadly observers don’t do this. Perhaps you could save a scene name or custom scene ID as an attribute to get around it?

Given the many limitations of observers:

  1. entity observer crash is you use it to change the model.
  2. no ability to determine what property of the object changed.
  3. No ability to determine the before and after state.

I’m unable to imagine what use they can be??
The only use I’ve seen mentioned anywhere is synching with some external database.
Can anyone describe some other functional examples?

Correct. The Ruby API was added in version 4 of SketchUp, though officially it’s history starts at version 6. SU6 is also when the observer system was added. It’s very thinly bolted on top of the internal notification system in SketchUp - and the way it works is that it pass along only a reference to the object changed - not it’s properties. So the current system have no way of informing what particular property triggered the notification.

Then there is the issue of line-span for notifications about erased entities. It’s often been requested to be able to read data from entities being erased. But the problem is that the internal object might already be deleted at this point. That’s why you only get a Ruby Entity object that yield true to deleted?.

A whole new system would have to be devised for the type of feature you describe. On that topic, we do need to add observers to the C API - and we don’t want to model that on the Ruby API. You described that feature you are looking for, but can you also elaborate on why you are looking for this? (So I can fill in a use-case for a feature request.)

1 Like

don’t assume, store and check changes to arrays…

class WatchLayers < Sketchup::LayersObserver

  def self.all_layers
    Sketchup.active_model.layers.to_a.map {|l| l.name}
  end

  @store = all_layers

  def onLayerRemoved(layers, layer)
    missing = @store - all_layers
    puts
    puts "Removed #{missing[0..-1]}"
    @store = all_layers
  end

   def onLayerAdded(layers, layer)
     puts
     puts "Added #{layer.name}"
     @store = all_layers
   end

   def onLayerChanged(layers, layer)
     missing = @store - all_layers
     puts
     puts "Changed #{missing[0]} to #{layer.name}"
     @store = all_layers
   end
   
  # this never seems to run...
   def onRemovedAllLayers(layers)
    puts
    puts "Removed All #{@store - "Layer0"}"
    @store = all_layers
   end
end # class

# attach the LayersObserver...
def add_watch_layers
  @watch_layers = WatchLayers.new if not @watch_layers
  puts "@watch_layers = #{@watch_layers}"
  Sketchup.active_model.layers.add_observer(@watch_layers)
end # def

add_watch_layers
# to remove
# Sketchup.active_model.layers.remove_observer(@watch_layers)

you possibly also want an model observer…

john

don’t assume, store and check changes to arrays…

Yes, this is actually what I meant by assume. As far as I know the user can only alter one layer at a time and so onLayerChanged will fire in a predictable way and we can find the newly missing name in the stored list of layer names.

I have a database of information stored about each layer, and that database needs to update automatically whenever the user changes the layer names. If I can get the old value for the layer name, it’s a little bit more direct to modify the database rather than store the list of layers separately and find the missing name as john explained.

layers

it does not catch new generic layers e.g. Layer1, Layer2, etc…

that’s why I have onLayerAdded(layers, layer) as well…

john

Ah you’re right. I also noticed that onLayerAdded will fire twice when adding a layer, once when clicking (+) and once when renaming the layer.

Sorry I wasn’t clear. Is it possible that someone might perform an action that would trigger onLayerChanged on multiple layers at the same time? Possibly through a Layer enhancement plugin? Removing layers, even in bulk should only trigger several onLayerRemoved, correct?

I would expect so.

Similarly I’d expect an onLayerChanged event per layer. If you see different behaviour, please post back an example.

I seem to recall that some things like user GUI actions fired observers, but some Ruby calls did not ?
This was in relation to JBB’s Layer Panel extension which has stagnated because of these issues.
Perhaps there is a thread on the issues he ran into around here or at SketchUcation ?

Yea, there are some cases where notifications are fired from the UI layer and not the core itself. Which then means the Ruby API would also have to send their own notification. There have been cases of this which we’ve been trying to fix. But I would not be surprised to still see this.

But there currently is no EASY way to workaround like …

Sketchup::LayersObserver::notify(:onLayerChanged, layers, layer)

… because there is no way to query collection objects and ask if they have an such an observer attached.
Likewise you could find objects that respond to :onLayerChanged method, via ObjectSpace.each_object(), but again no way to ask it if it is actually attached to the layers collection object we are interested in.

(On PC you could make an assumption as there is only 1 model open, but what about on Mac ? Then again I suppose the core only notifies observers for active model objects ?)

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.