I’m inspired by something I saw in other applications and the following discussion:
What I’d like to create is an extension that shows and hides layer folders in a number of ways. I’m not a coder, but thought this would be a nice exercise to learn more about coding and Ruby.
So far I came up with a basic script, based on an example of DanRathbun in the discussion above, which can hide or show all top level folders… Basbva_FolderVisibility:003.rb (892 Bytes)
What I would like to achieve next are a number of things, but first of all to add to this script the possibility to hide all Folders except those that hold the active tag (as selected in the Tag Window).
Any clues or thoughts as to how to set about and achieve this will be greatly appreciated.
I’m strongly aware of the need of keeping raw elements untaged and I learned the hard way, coming from other applications. There are more ways than one though of grouping and attributing.
Another is to select a tag and start grouping, which I think can be usefull, but you certainly do need to be aware of what you’re doing. Is it really recommended by Sketchup not to do that at all?
@3DxJFD You’ve made my day already. As Dezmo suggests other child folders of the folder above the active child folder aren’t hidden. In the other thread I mentioned, DanRathbun seems to suggest that to achieve something like that, one would need to ‘walk through the tree structure’, which can be done with a recursive method. So that would be the next challenge, I guess.
Apart from that, I think you’re suggestion already can be pretty usefull.
I just want to mention that Layer0 (Untagged) cannot appear in a folder. This is a fact.
You can group other Tags (Layers in Ruby) and Tag Folder(s) to folder as you whish.
You can retrieve the parents folders of the layer or layer folder e.g. with this method, by calling it a and pass the the layer of layer folder as parameter:
(You will get an Array of Sketchup::LayerFolder)
def parents(l_f)
parents = []
while l_f.folder
parents<<l_f.folder
l_f = l_f.folder
end
parents
end
E.g. If you have structure like this:
Call like this to get the parent layer folders of “Tag” parents(Sketchup.active_model.layers["Tag"])
Then you need to check if this array contains the folder that you want to change visibility to hidden…
__
An other note:
This does not return all the folders in the model, only those that are direct children of the layer manager.
To retrieve all folders in a model into an Array you can use e.g. this method:
def all_folders(folders = [], root_folder = Sketchup.active_model.layers)
root_folder.each_folder do |folder|
folders<<folder
all_folders(folders, folder)
end
folders
end
Call it like: all_folders_in_a_model = all_folders()
Then the toggle_off should be something like this (did not tested )
def toggle_off
model = Sketchup.active_model
active_folder = model.active_layer.folder
# Edited: I guess you need to include the active_folder in a comparation...
active_folders = parents( active_folder )<<active_folder
all_folders_in_a_model = all_folders()
all_folders_in_a_model.each do |folder|
folder.visible = false unless active_folders.include?(folder)
end
end
For better understanding I tried to add your first suggestions to @3DxJFD’s Ruby.
And played around with it too. Sorry, if I’m ignorant. I can read and write, but I’m not a coder. The problem I guess is that there doesn’t seem to be a definition for folders that covers all of those that -need- to be hidden. Besides I don’t completely understand it
What I did notice is that the method active_layer in 3DxJFD’s script isn’t necessary. Only ‘layer’ will do. Which might help for a better understanding of what an active folder is
def toggle_off
model = Sketchup.active_model
layers = model.layers
folders = layers.folders
active_folder = model.layer.folder
#active_folder = model.active_layer.folder
#active_folders = parents( folder )<<folder
folders.each do |a|
a.visible = false unless a == active_folder
end
end
module Basbva
module FolderVisibility
extend self
VERSION ||= "0.0.5"
MENU ||= "View"
# Method to recursively collect all folders
def all_folders(folders = [], root_folder = Sketchup.active_model.layers)
root_folder.each_folder do |folder|
folders << folder
all_folders(folders, folder)
end
folders
end
# Method to find the hierarchy path to the active folder
def find_hierarchy_path(active_folder)
path = []
current_folder = active_folder
while current_folder
path.unshift(current_folder)
current_folder = current_folder.folder if current_folder.respond_to?(:folder)
end
path
end
def toggle_off
model = Sketchup.active_model
active_folder = model.active_layer.folder
hierarchy_path = find_hierarchy_path(active_folder)
# Gather all folders
all_folders = all_folders()
# Hide all folders except those in the hierarchy path to the active folder
all_folders.each do |folder|
folder.visible = hierarchy_path.include?(folder)
end
end
def toggle_on
model = Sketchup.active_model
layers = model.layers
layers.folders.each { |folder| folder.visible = true }
end
unless defined?(@ui_loaded)
add_separator_to_menu(MENU)
menu = UI.menu(MENU).add_submenu("Folder Visibility")
menu.add_item("Hide Folders") { toggle_off() }
menu.add_item("Show Folders") { toggle_on() }
@ui_loaded = true
end
end
end
The method you accidentally discovered returns the default layer “Layer0”.
As I mentioned Layer0 (Untagged) cannot appear in a folder. Calling #folder method on it will return nil.
The active layer is that layer is that where the pencil is located. (marked with red rectangle)
Therefore the active folder is (are) that folders(s) that containing the active layer or congaing tag folder that containing the active layer…etc. (marked with orange rectangles)
Notes:
The “active folder” is an arbitrary given name in this topic, there is no such a things in Ruby API (Neither ins Sketchup)
The layer folders can be in several levels.
I intentionally left the names of the layer folders the same. You should be aware that users are given the freedom that the layer folder names can be the same. You can have hundreds of layer folders called “Tag Folders” nested together in different ways…
Despite the common name, this hundreds of folders are different objects in Ruby.
Ahhh,
Because Sketchup basically doesn’t allow you to hide the active tag, in any way!
You do not need to check, it is done automatically.
So your code…
…can be simoplified:
def toggle_off
model = Sketchup.active_model
layers = model.layers
folders = layers.folders
folders.each do |a|
a.visible = false
end
This still does not hide all the folders in the model - except folder(s) containing the active tag -, only those that are direct children of the layer manager.
This got me distracted from my work today. Very interesting. Although this hides all but the active folder, I don’t understand what this:
# Method to find the hierarchy path to the active folder
def find_hierarchy_path(active_folder)
path = []
current_folder = active_folder
while current_folder
path.unshift(current_folder)
current_folder = current_folder.folder if current_folder.respond_to?(:folder)
end
path
end
adds to the functionality of the code. Although it might be of value when you don’t want to hide all but the ‘active’ folders.
Also naming your def ‘Folder’ interferes with the Folder method… Sketchup freezes when you combine both methods in one def. That seems dangerous to me, so I renamed it.
Taking into account Dezmo’s last reply, suggesting to keep things simple, I’ve been editing your last script. Here all active folders are hidden and top level folders shown, just as an example. Now sure whether this is proper coding practice though…
module Basbva
module FolderVisibility
extend self
VERSION ||= "0.0.6"
MENU ||= "View"
# Method to recursively collect all folders
def inactive(inactive = [], root_folder = Sketchup.active_model.layers)
root_folder.each_folder do |a|
inactive << a
inactive(inactive, a)
end
inactive
end
def toggle_off
model = Sketchup.active_model
active = model.layers.folders
# note that model.layers.folders here only returns the 'active' folder
# Gather child folders
inactive = inactive()
# Hide child folders
inactive.each do |b|
b.visible = false unless b == active
end
model = Sketchup.active_model
top_level_folders = model.layers.folders
# note that model.layers.folders here also returns top level folders
top_level_folders.each do |c|
c.visible = true
end
end
def toggle_on
inactive = inactive()
inactive.each do |a|
a.visible = true
end
end
unless defined?(@ui_loaded)
add_separator_to_menu(MENU)
menu = UI.menu(MENU).add_submenu("Folder Visibility")
menu.add_item("Hide Folders") { toggle_off() }
menu.add_item("Show Folders") { toggle_on() }
@ui_loaded = true
end
end
end
#folders = model.layers.folders
#returns folders that are direct children of the layer manager and of the active layer
I have no idea, why you are naming this method like this. The method recursively collect all folders, and it will return all layer folder in a model independently if it is contains the #active_layer or not.
(The active layer means that all Sketchup::Drawingelement you drawn/placed via UI or by code, will be assigned to that layer.
The default active layer in SketchUp is Layer0, and it is not recommended to change. Unless you know what the active layer is and its purpose. )
No. It will returns top level folders.
(The Model #layers method retrieves a collection of all Layers objects in the model.
The Layers #folders method returns the folders of the layer manager)
Therefore
will never be true, because you are comparing a LayerFolder object to an Array containing Sketchup::LayerFolders (or nil)
However the condition is not need at all, as I mentioned earlier, but since it is always false it’s like it’s not even there, that’s why it works.
You unnecessarily assigned it two times within a method.
Not sure why you want to keep the top level folders always visible, but it is up to you.
This version was a quick and dirty response with a number of mistakes in it. Because it took me some time figuring this out.
First of all I read that @3DxJFD s script implies there is a root-folder, which is equal to Sketchup.active_model.layers
Your suggestion I forgot to fully implement but if I do this is what you get:
EDIT:
# Hide inactive folders
inactive.each do |b|
b.visible = false
end
Then you’ll notice the first def in the module doesn’t represent all folders. Both the so called root folder, as the so called active folders are missing in this equation.
You unnecessarily assigned it two times within a method.
This version is toggling subfolder visibility back on. I disambiguated the method name (all_folders). I’m a flowchart pseudocoder, not a programmer, so my stuff is just suggestive.
module Basbva
module FolderVisibility
extend self
VERSION ||= "0.0.7"
MENU ||= "View"
# Method to recursively collect all folders
def collect_all_folders(folders = [], root_folder = Sketchup.active_model.layers)
root_folder.each_folder do |folder|
folders << folder
all_folders(folders, folder)
end
folders
end
# Method to find the hierarchy path to the active folder
def find_hierarchy_path(active_folder)
path = []
current_folder = active_folder
while current_folder
path.unshift(current_folder)
current_folder = current_folder.folder if current_folder.respond_to?(:folder)
end
path
end
def toggle_off
model = Sketchup.active_model
active_folder = model.active_layer.folder
hierarchy_path = find_hierarchy_path(active_folder)
# Gather all folders
all_folders = all_folders()
# Hide all folders except those in the hierarchy path to the active folder
all_folders.each do |folder|
folder.visible = hierarchy_path.include?(folder)
end
end
def toggle_on
model = Sketchup.active_model
# Gather all folders
all_folders = all_folders()
# Set all folders to visible
all_folders.each { |folder| folder.visible = true }
end
unless defined?(@ui_loaded)
add_separator_to_menu(MENU)
menu = UI.menu(MENU).add_submenu("Folder Visibility")
menu.add_item("Hide Folders") { toggle_off() }
menu.add_item("Show Folders") { toggle_on() }
@ui_loaded = true
end
end
end
I think the question is always: what is the algorithm?
The ‘correct’ pseudocode is always the answer.
The rest is details. ;^)!
Pseudocode
Function collect_all_folders(folders, root_folder)
For each folder in root_folder
Add folder to folders
Recursively call collect_all_folders with current folder
Return folders
Function find_hierarchy_path(active_folder)
Initialize empty path list
Set current_folder to active_folder
While current_folder exists
Add current_folder to the start of the path list
Set current_folder to its parent folder if it exists
Return path
Function toggle_off
Get the current model
Get the active folder from the model active layer
Find hierarchy path of the active folder
Gather all folders
For each folder
Set folder visibility based on its presence in the hierarchy path
Function toggle_on
Get the current model
Gather all folders
Set all folders to visible
SU UI does not allow you to hide the active(current) Tag even by trying to hide by its layer folder:
If you try to do it by code, SU will react in a similar way. (Unfortunately, not that way what I like… more on that, later) Therefore the code can be simplified:
module SUForum
module FolderVisibility
extend self
VERSION ||= "0.0.8"
MENU ||= "View"
def all_folders(folders = [], root_folder = Sketchup.active_model.layers)
root_folder.each_folder do |folder|
folders << folder
all_folders(folders, folder)
end
folders
end
def toggle(state)
all_folders.each { |f| f.visible = state }
end
unless defined?(@ui_loaded)
add_separator_to_menu(MENU)
menu = UI.menu(MENU).add_submenu("Folder Visibility")
menu.add_item("Hide Folders") { toggle(false) }
menu.add_item("Show Folders") { toggle(true) }
@ui_loaded = true
end
end
end
_
_
What I don’t like about running Ruby code on this regards:
If you try to hide the parent folder of the active layer, e.g:: Sketchup.active_model.layers["Tag"].folder.visible = false
It will return false, but silentlyfail to set visibility to false.
.
Even worse: If you try to hide the active layer, e.g.: Sketchup.active_model.layers["Tag"].visible = false
that will happen, however Layer0 will be silently set as the active(current) layer !!
.
My test done in SU2021 on Windows, need to check on later versions, and it is similar I will report this to SketchUp API issue tracker