Redraw all dynamic components?

Is anyone aware of a way to redraw a group of components or the entire model in one shot? I am finding that individually selecting and right clicking to redraw is tedious and it’s easy to miss a component in large models…

1 Like
 Sketchup.active_model.selection.grep(Sketchup::ComponentInstance).each do |s|
        $dc_observers.get_latest_class.redraw_with_undo(s)
 end

Open the Ruby Console (Window) and paste

4 Likes

This worked great. Added it with toolbar editor as an icon. Select the entire model and execute. Works like “regenall” in Autocad. Thanks!!

1 Like

Hello Mike. Would it be possible tor run this script every time I click on DC using onClick tool? Thank you.

1 Like

I think (but am not sure) that the DCInteractTool will do a redraw for the single instance clicked, when it is appropriate to do so.
(If it does not, when it should, please open a bug report in the Technical Issues > SketchUp category.)

As to making it work upon ALL DC instances in the model, the answer is NO.

  • This tool is part of Trimble’s proprietary (and closed source) Dynamic Components extension, and changing it is a “no-no”.

  • Regardless, it also is not a good idea to modify a tool that acts upon a single object, to affect multiple objects.


There is a single instance redraw command in the right-click context menu (if you right-click on a instance that is a DC.)


Code to add a right-click command to redraw all instances of the right-clicked instance’s definition :

UI.add_context_menu_handler do |context_menu|
  ss = Sketchup.active_model.selection
  if defined?($dc_observers) && ss.single_object? &&
  (ss[0].attribute_dictionary('dynamic_attributes') ||
  ss[0].definition.attribute_dictionary('dynamic_attributes'))
    context_menu.add_item("Redraw all these DCs") {
      dco = $dc_observers.get_latest_class
      ss[0].definition.instances.each {|i| dco.redraw_with_undo(i) }
    }
  end
end

(And no we cannot put the command on the DC edit submenu.
The API does not give access to previously defined menu objects.)

1 Like

Thank you very much for info. I will try to get my head around it. Thanks again

Yep that was easy (because you’ve wrote all the code :slight_smile: ) Just pasted into .rb file and now menu option is there.
So there is no script that listens to interact tool being clicked on DC? I am just in the beginig of getting involved with ruby.

Was a mistake in the first code post. Fixed it (above).
Ie, I had to check both the instance and the definition for a DC attribute dictionary.

There is a ToolsObserver but it did not work all that well for native tools in the past.

However, this is a RubyTool and in a very recent release the Ruby tool names are now able to read by the observer. (In the past we’d only get "Rubytool" or something similarly worthless.)

I do not think the engine calls ToolsObserver#onToolStateChanged for Ruby tools.

Also, with the 2016 release, observers were overhauled cache up their callback queues and then do them all at once at the end of the tool’s action.

All in all, it’d be easier to write your own “Redraw” click tool to do this.

1 Like

Thank. But the code only works with one component. If you select 2 or more components, there is no right-click redraw menu.111 2222

Yes, I wrote it this way.

@KScher & @mozarov

Here is a revised edition that will work on multiple selection …

# Redraw all selected (and siblings)
UI.add_context_menu_handler do |context_menu|
  ss = Sketchup.active_model.selection
  if defined?($dc_observers) && !ss.empty?
    insts = ss.grep(Sketchup::ComponentInstance)
    if !insts.empty?
      dynatts = 'dynamic_attributes'
      insts.select! {|i| i.definition.attribute_dictionary(dynatts) }
      if !insts.empty?
        cdefs = insts.map(&:definition).uniq
        dco = $dc_observers.get_latest_class
        context_menu.add_item("Redraw ALL sibling DCs") {
          cdefs.each {|cdef|
            puts "\nRedrawing Instances of: \"#{cdef.name}\"..."
            cdef.instances.each {|i|
              puts "Redrawing ... #{i.inspect}"
              dco.redraw_with_undo(i)
            }
          }
        }
        context_menu.add_item("Redraw ONLY selected DCs") {
          puts
          insts.each {|i|
            puts "Redrawing Instance of: \"#{i.definition.name}\"... #{i.inspect}"
            dco.redraw_with_undo(i) 
          }
        }
      end # if DC instances were selected
    end # if component instances were selected
  end # if DC loaded and something is selected 
end # context menu handler block
5 Likes

Thanks Dan! This helped me a lot!

1 Like

Thanks 4 providing this :slight_smile:

Hey @DanRathbun, is there a way to get this redraw to see DC’s inside a selected group or groups? For example I have the framing of a deck in a couple of different groups and I want to trigger a redraw on all of them. I do this to update some aspects of the DCs I use that seem to get stuck, like the material/colour in a concatenated description, or length of a piece lumber. A redraw fixes this, however going into each group to select and redraw is time consuming and I run the risk of missing something. What I want to do is select the whole model or part that I have been working on and redraw all of the DCs in the selected group(s) at once. I would also like to reset the nominal len XYZ in the same manner. Learning Ruby is on my list but time just hasn’t been available to get to it, any help you can offer is greatly appreciated. Thanks

Try this … (untested) …

ADD (2021-04-10): This file has been posted to this topic:
Force Redraw - #4 by DanRathbun

# Redraw all selected (and siblings)
UI.add_context_menu_handler do |context_menu|
  ss = Sketchup.active_model.selection
  if defined?($dc_observers) && !ss.empty?
    insts = ss.grep(Sketchup::ComponentInstance)
    grps  = ss.grep(Sketchup::Group)
    if !grps.empty?
      grps.each {|grp|
        insts.push(*grp.entities.grep(Sketchup::ComponentInstance)) 
      }
    end
    if !insts.empty?
      dynatts = 'dynamic_attributes'
      insts.select! {|i| i.definition.attribute_dictionary(dynatts) }
      if !insts.empty?
        cdefs = insts.map(&:definition).uniq
        dco = $dc_observers.get_latest_class
        context_menu.add_item("Redraw ALL sibling DCs") {
          cdefs.each {|cdef|
            puts "\nRedrawing Instances of: \"#{cdef.name}\"..."
            cdef.instances.each {|i|
              puts "Redrawing ... #{i.inspect}"
              dco.redraw_with_undo(i)
            }
          }
        }
        context_menu.add_item("Redraw ONLY selected DCs") {
          puts
          insts.each {|i|
            puts "Redrawing Instance of: \"#{i.definition.name}\"... #{i.inspect}"
            dco.redraw_with_undo(i) 
          }
        }
      end # if DC instances were selected
    end # if component instances were selected
  end # if DC loaded and something is selected 
end # context menu handler block

It is the same as above, but after …

    insts = ss.grep(Sketchup::ComponentInstance)

… I added these few lines …

    grps  = ss.grep(Sketchup::Group)
    if !grps.empty?
      grps.each {|grp|
        insts.push(*grp.entities.grep(Sketchup::ComponentInstance)) 
      }
    end

It should separately “grep” groups from the selection set and then iterate them adding any nested component instances to the “redraw” collection.


This would be “a bit” off-topic for this thread. I do have a code snippet that is so old (it was from NOV 2020) … I cannot remember if it works or not. I’ll add the bit to “grep” groups and PM it to you so you can try it.

ADD: FYI, Ryan also had started the topic on resetting DC nominal length attributes …

2 Likes