Dynamic component count to text file

Hi,

I have a question about generate reports. The code sketchup gives makes an output file to csv.

But i saw in the plugin “Profile Builder” an output to a text file with the following result.

It’s much eassier to read and to use. But how can you bring the component data to a text file like the example?

Thank you.

In Ruby, we would create formatted strings using Kernel.format, and append them together using String.<<, then output the textline to the file object.

Some other topic threads that will help:

Hi,

Thank you. I have a little bit of code that can count loaded components from my server.

It’s something like this:

path = (“Z:/Algemeen 2015/Systeemvloer/Tekeningen/Sketchup/Enkelzijdige kolom.skp”)
definitions = Sketchup.active_model.definitions
definition = definitions.load(path)
number = definition.count_used_instances

definitions = Sketchup.active_model.definitions
attribute = definition.get_attribute(@dictionary_name, ‘lenz’).to_f.to_mm

When i run the code the result is 0;0. The components are loaded with my floor builder so every new component gets a #
Is this the reason why it is not working? when i load it from the component tab is works well. When i understand this i can see how to transfer this to a tekst file. In the image you see the column selected what should be count as example (count should be 6x)

Thank you

But your example code does nothing ?
Where is:

puts number

Also why are you reloading the definition ?
Don’t you have a reference to it from earlier ?
Can you access it ‘by name’ ?

definition = definitions["Enkelzijdige kolom"]

If you select an instance of that column in the model and open Entity Info how many of it are reported ?
They are not being “made_unique” ?

Also @dictionary_name needs to be set to "dynamic_attributes".

However do not use an instance variable at the toplevel objectspace (which is Object. They’ll propagate into all other classes.)

For testing at the console just use a local variable like all the others. (Later inside your class or module, you can use @dictionary_name.)

# set dictionary name
dictionary_name = "dynamic_attributes"

# get numerical attribute and convert to Length class object
attribute = definition.get_attribute(dictionary_name, 'lenz').to_l

# convert Length object to String expressed in current model units
height = Sketchup.format_length( attribute )

# create a textline of information
textline = "#{definition.name}  (#{height})  total: #{number}"

# output information to console
puts textline

# get a path:
path = UI::savepanel("Save Report As...","report.txt")
if path
  # save the textline:
  File::write( path, textline )
end

See: Sketchup::format_length()

An example of using Kernel#format to format an output text line:

textline = format( "%-20s   (%8s)   count: %-s", definition.name, height, number )

>>

Enkelzijdige kolom     (  4000mm)   count: 6

Thank you both. Monday im back at the office and going to play With it. The code I did I saw on the sketchup site. So very basic. But this helps a lot to me

Hello,

I give the code a try it gives me the error you can see under the picture. When i change the textline with this: # create a textline of information
textline = “#{“Enkelzijdige kolom”} (#{3000.mm}) total: #{20}” it is working fine. So in the beginning something went wrong.

Thanks.

You are setting the reference definition to be a string.
BUT you want it to be a component-definition !

So do NOT use:

definition = "Enkelzijdige kolom"

Rather use a reference to a component-definition, thus:

definition = Sketchup.active_model.definitions["Enkelzijdige kolom"]

Assuming you are sure that there is a component definition with that exact name !

That was not a complete running snippet of code. It was some sample statements.

It relied upon your previous snippet that assumed (what TIG said) … that the references definition and number have previously been set correctly.

Working :slight_smile: This is great so thank you both.

definition = Sketchup.active_model.definitions[“Enkelzijdige kolom”]
number = definition.count_used_instances

set dictionary name

dictionary_name = “dynamic_attributes”

get numerical attribute and convert to Length class object

attribute = definition.get_attribute(dictionary_name, ‘lenz’).to_f.to_mm/25.4

convert Length object to String expressed in current model units

height = Sketchup.format_length( attribute )

create a textline of information

textline = “#{definition.name} (#{height}) total: #{number}”

output information to console

puts textline

get a path:

path = UI::savepanel(“Save Report As…”,“report.txt”)
if path

save the textline:

File::write( path, textline )
end

Last thing about this and then i can make it how i want it.

I tried to add more than 1 textline but i don’t understand how that works with Kernel format.

The end result should be something like this:

When i set now 2 different Heights it only picks up the one with 3000 mm also with the count.

I’d open the file for writing, then use puts for each line, then puts the total, then close the file.
I’d also change it’s file-type to “.csv”, so that it works with Excel more easily ?
Assuming you have some variables preset…

path = UI.savepanel("Save Report As...", "Report.csv")
begin
  file = File.open(path, "w")
  ### opened with 'w' to Write - overwrites any  existing contents - 'a' to Append, 'r' to Read...
  file.puts("Type,Height.Count\n") ### The header with an extra empty line [\n]
  ### Do your assembly of the data for the various lines here, 
  ### incrementing the 'total_length' and 'total_count' - perhaps in a loop
  
  file.puts("#{this_name},#{this_height},#{this_count}")

  ### Now add the final total footer
  file.puts("____________________________________________")
  file.puts("Total,#{total_length},#{total_count}")
  file.close ### Remember to close it !
rescue
  UI.messagebox("File #{path} could not be opened !\nIt might be open in another Application ?\nIf so, close it and retry...")
end

Your code above is unreadable, please edit that post (with the pencil icon.)

Please wrap you code (in posts) in triple backtick lines like:

```ruby

code goes here

```

… or for plain monospace text, (such as error messages and backtraces) like:

```text

text goes here

```

… or more simply, just:

```

text goes here

```

The format method just formats data into one big long textline.
I often save the format strings as module constants and use the consant in the call using format.

Use use the format call inside a loop that iterates the array of components you have already found. Example code was in the thread:
Selecting Specific Component (or Group) By Name

In that loop you build up a text report by repeatedly appending your textlines to a string object.

path = UI::savepanel("Save Report As...","report.txt")
if path

  # Create a data format string with newline:
  DATALINE = "%-20s   (%8s)   count: %-s\n"

  dictionary_name = "dynamic_attributes"

  # Set Report Captions:
  @report = format( DATALINE, "Type", "Height", "Count" )

  # Iterate the found component definitions:
  #
  # EDIT: SEE NEXT POST <<===================<<<<<

  # Save the Report:
  File::write( path, @report )

end

Oh OK I see that you wish to group the same heights onto one line with the height total at the end.

I’ll need to think a bit on this.

EDIT:

Ok when you change a DC’s attributes, the DC engine makes it a unique component, so it copies the definition, and gives the new one a name with “#number” appened.

ie, “Enkelzijdige kolom#1”, “Enkelzijdige kolom#2”, etc.

So when searching for matching definitions, you need to use a Regular Expression to match all definitions whose name begins with “Enkelzijdige kolom.

dname = "Enkelzijdige kolom"
deflist = Sketchup.active_model.definitions
matches = deflist.find_all {|d|
  !d.group? && !d.image? && d.name =~ /\A(#{dname})/
}

The escaped A (\A) means match at beginning of string.

Then you need to iterate all matches, each one will be the data for each line:

total = 0
matches.each {|definition|
  number = definition.count_used_instances
  total += number
  attribute = definition.get_attribute(dictionary_name, 'lenz').to_l
  height = Sketchup.format_length( attribute )
  @report << format( DATALINE, definition.name, height, number )
}

NOTE: The above snippet was updated in a following post:
http://forums.sketchup.com/t/dynamic-component-count-to-text-file/20979/22

Hi. Thank you for having 2 new codes that are working. When I was running the code I discoverd a problem in my floor builder. It was making every column and beam that were the same unique. So first of all I changed the floor builder code and now it’s working right. Time to work out the report code futher :slight_smile: but in the code of Dan, How can I add empty lines so you can make it more readable as a lay-out? Maybe I Come back with new questions later.

Thanks

See the “\n” at the end of:

# Create a data format string with newline:
  DATALINE = "%-20s   (%8s)   count: %-s\n"

That is an “escape code” for a newline character.

So simply:

@report << "\n"

But these codes must be used within double quoted strings.

See Ruby primer on: Literals: Strings


If you are outputting to STDOUT, (like the Console) you can use

puts()

with no argument.

Hi dan,

When you use the code to count objects I saw a strange thing. When you Load an component and you decide not to use it and delete it. You still get that object in the text file. The total number is zero but the name stays. I think something is going wrong with the code

Thanks