My ruby works okay
But when I add toolbar system, It doesn’t work
I can’t figure out where is wrong
require 'sketchup.rb'
class MakeboxTool
model = Sketchup.active_model
entities = model.active_entities
selection = model.selection
faces = selection.grep(Sketchup::Face)
faces.each{|face|
group = entities.add_group(face)
group2 = group.copy
group.explode
entities2 = group2.entities
before = entities2.grep(Sketchup::Face)
before[0].material = [50, 255, 255]
before[0].pushpull( 10.mm )
new_faces = entities2.grep(Sketchup::Face) - before
upper_face = new_faces.find {|f| f.normal == before[0].normal.reverse }
unless upper_face.nil?
start_point = upper_face.bounds.center
offset_vector = upper_face.normal
offset_vector.length = 90.mm
end_point = start_point.offset(offset_vector)
end
edge = group2.entities.add_line(start_point, end_point)
layer = Sketchup.active_model.layers.add("win")
group2.layer = layer
entities2.each{|e| e.layer = layer }
if !model.active_path.nil?
instance = model.active_path.last
definition = instance.definition
group2_copy = group2.parent.parent.parent.entities.add_instance(group2.definition,group2.transformation)
group2.erase!
end
}
end # of makebox class
if( not file_loaded?("makebox.rb") )
UI.menu("Plugins").add_item("Start makebox Tool") { Sketchup.active_model.select_tool MakeboxTool.new }
dir = Sketchup.find_support_file("Plugins")
cmd = UI::Command.new("Start makebox Tool") { Sketchup.active_model.select_tool MakeboxTool.new }
cmd.large_icon = cmd.small_icon = dir+"/makebox.png"
cmd.status_bar_text = cmd.tooltip = "Tool for making blue boxes"
tb = UI::Toolbar.new("makebox")
tb.add_item cmd
tb.show if tb.get_last_state == -1
end
file_loaded("makebox.rb")
There are two major things wrong with your code.
First, your MakeboxTool class has only loose class-scope code, no methods (def’s) at all, not even initialize. So, the code is run as the file loads, but thereafter is dead. Creating an instance via MakeboxTool.new does not cause the class-scope code to run again, so your menu item and toolbar button just create dead objects.
You could convert the class into a one-shot by wrapping the loose code in def initialize…end. initialize is the method run to initialize an object created by new.
But, that takes us into the second major issue: your code does not implement any of the methods of the Tool protocol. It does not really qualify as a thing that should be activated using select_tool. If you want a one-shot, you could create a class method (def self.methodname) and invoke it in the command’s code block instead of selecting a Tool.
Edit: beyond those obvious issues, I did not analyze your code to see if it does what you expected. Also, it should be wrapped within a uniquely-named module to isolate its stuff from any possible conflict with other code.
2 Likes
Agree with Steve.
(1) The only class definitions at the toplevel ObjectSpace should be Ruby core classes (and perhaps the occasional global API class.)
(2) Seek to leave primitive geometry assigned to use “Untagged” (aka “Layer0”.)
Assigned only the group or component instance contexts to use other layers / tags.
(3) Your indentation is abnormal. (Ruby uses 2 space indentation.)
Please do not outdent! It breaks indent alignment lines in code editors.
1 Like
require 'sketchup.rb'
module Makebox
#extend self
def self.makebox
model = Sketchup.active_model
entities = model.active_entities
selection = model.selection
faces = selection.grep(Sketchup::Face)
faces.each{|face|
group = entities.add_group(face)
group2 = group.copy
group.explode
entities2 = group2.entities
before = entities2.grep(Sketchup::Face)
before[0].material = [50, 255, 255]
before[0].pushpull( 10.mm )
new_faces = entities2.grep(Sketchup::Face) - before
upper_face = new_faces.find {|f| f.normal == before[0].normal.reverse }
unless upper_face.nil?
start_point = upper_face.bounds.center
offset_vector = upper_face.normal
offset_vector.length = 90.mm
end_point = start_point.offset(offset_vector)
end
edge = group2.entities.add_line(start_point, end_point)
layer = Sketchup.active_model.layers.add("win")
group2.layer = layer
entities2.each{|e| e.layer = layer }
if !model.active_path.nil?
instance = model.active_path.last
definition = instance.definition
group2_copy = group2.parent.parent.parent.entities.add_instance(group2.definition,group2.transformation)
group2.erase!
end
}
end
end
if( not file_loaded?("makebox.rb") )
UI.menu("Plugins").add_item("Start makebox Tool") { self.makebox }
dir = Sketchup.find_support_file("Plugins")
cmd = UI::Command.new("Start makebox Tool") { self.makebox }
cmd.large_icon = cmd.small_icon = dir+"/makebox.png"
cmd.status_bar_text = cmd.tooltip = "Tool for making blue boxes"
tb = UI::Toolbar.new("makebox")
tb.add_item cmd
tb.show if tb.get_last_state == -1
end
file_loaded("makebox.rb")
I created a def self.methodname
But still same
You close the Makebox module with an end statement before the code that creates the menu and toolbar. As a result, the method makebox is no longer in the namespace when you wire it into the menu and command. You need to use Makebox.makebox, not self.makebox. Then it works.
Edit: let me elaborate for your edification.
In Ruby there is at all times an active “receiver” object. It is referenced by the reserved name “self”. Method calls that are not qualified with a specific object (e.g. do_it() vs foo.do_it()) are automatically dispatched to self, as are ones that are explicitly qualified (e.g. self.makebox). The possibly confusing thing is that when you enter a module or class block, that module or class object is made the receiver. The syntax “def self.makebox” vs “def makebox” distinguishes between a method that belongs to the current module’s own namespace vs a method that will be added to another module or class namespace when this module is mixed in.
When you end the module Makebox, “self” changes back to whatever object was active before the module statement. In this case it is the global “main” object. The method def’d in module Makebox is not in main’s namespace, so the calls in the menu and command fail to find it. Putting the explicit qualifier Makebox.makebox tells Ruby to look for the method in Makebox’s namespace.
2 Likes
I disagree. Agent needs to put ALL his code within his modules.
Also the Makebox
module should be a submodule inside Agent’s toplevel author module.
Each of Agent’s extensions need to be within an extension subfolder, not in the "Plugins"
folder.
Then Agent can use the global __dir__()
method, instead of Sketchup.find_support_file("Plugins")
… and File.join(__dir__,'makebox.png')
instead of dir+"/makebox.png"
2 Likes
You are right. While obviously knowing the fact that the offending code was “loose at top level” I just addressed the “why didn’t it work” question. And a better answer would be to put all the code within the module.
1 Like