How to pass variable to a tool object without using globals?

I think I understand all of that (but I probably don’t). The problem is distinguishing the first time in a session the tool is used (when I have to either pass the 90 default to it, or use the initialize method to set it) and later uses when I want the last user override (if any) to be used as default. Here’s my code with the default set in the initialize method. please edit to demonstrate how It should work:

module Testtool

class PutTool

 def initialize
	 @@defangle = 90
	 puts "PutTool: initialize angle = " + @@defangle.to_s
end

def enableVCB?
  puts "PutTool: enableVCB? callback"
  return true
end

 def activate
  puts "PutTool: activate callback"
	 puts "PutTool: activate angle = " + @@defangle.to_s
	 #here'e where I will insert call rotate method to do initial rotation
	 #here's users chance to reset default:
	 Sketchup.vcb_label = "Enter new angle to revise rotation."
	 Sketchup.status_text = "Type new angle and press [ENTER]."
end

def deactivate(view)
  puts "PutTool: deactivate callback"
  Sketchup.status_text = ""
  Sketchup.vcb_value = ""
  Sketchup.vcb_label = "Measurements"

end

def onKeyDown(key, repeat, flags, view)
  return false
end

def onKeyUp(key, repeat, flags, view)
  return false
end

def onUserText(text, view)
	@@defangle = text
	puts "PutTool: onUserText = "<< @@defangle.inspect
  Sketchup.vcb_value = ""
end

end # class

end # module

First, in your post above, please insert a line exactly like

```ruby

before ALL of your code, and a line:

```

AFTER all of your code.

(I am no longer going to respond to improperly posted code blocks.)

1 Like

Can you please make use of code markup?
```
code
```

Obviously the code writes onto @@defangle every time the intialize method is run. The initialize method is run every time a PutTool instance is created. If don’t reuse tool instances but create a new instance everytime the user selects your tool, it will overwrite whatever the user set for @@defangle.

  • Option 1: do not set @@defangle to 90 in initialize, but outside in the module/class (yes, for class variables you can do this), as Dan’s example suggests.
  • Option 2: reuse the instance (still doesn’t solve the problem that every new instance creation would reset @@defangle to 90). If you have ever done point = Geom::Point3d.new() you shouldn’t find it hard to transfer this to keeping a reference to a tool instance:
    @@tool_instance ||= PutTool.new() # only once … model.select_tool(@@tool_instance)

Class variables are always alive, from the moment they are defined.

You DON"T. You set it when the class that it belongs to is defined, (when SketchUp loads and evaluates the file the defines it.)

This means is is set to it’s default value upon definition, even before first use (irregardless of whether it is even ever used at all.)

It was actually not ME that suggested you do this. This has led you away from the easiest and most straight-forward approach.

It was Steve who first told you to use a class variable, and Andreas gave you a simple example of defining in within your class (which in your case, is a Tool class.)

All method return values, are returned to what called them. For observers (and a Tool is really a UI observer,) their callback methods are called by the SketchUp engine, which may or may not use the return value.

Your code cannot use the return value from these special callback methods. This is why we are trying to get you to assign what the user enters into the VCB (in onUserText,) to a class variable.

The text string must be converted to a numeric. But all the conversion methods never raise an exception if the string is non-numeric, instead just returning 0, or 0.0, etc. So a regular expression must be used, to test if the string begins (\A) with a digit character (\d), preceeded by any spaces (\s*) and possibly a sign character.

@@defangle = text.to_f unless text !~ /\A\s*(\-|\+)?\d/i
So the old (of @@defangle,) value is left as is, if the string cannot be interpreted as a numeric string.

[quote=“barry_milliken_droid, post:16, topic:14191”]
As I understand the tool object (at least before you introduced the subject of suspend and resume), all tool instances end by interruption when user clicks on a different tool.[/quote]
Tools end by deactivation. They CAN be interrupted, ie, suspended (temporarily) and resumed, (currently) ONLY by SketchUp, when the user uses a special interrupter tool. [see below]

  • I have been one to ask for the ability to write interrupter tools, but so far, the power has not been written into the API.

No. Normally, only the SketchUp engine calls these callbacks, when special interrupter zoom tools are temporarily used via the middle mouse button. [Pan, Zoom, Orbit, etc.]

module Testtool

class PutTool

 def initialize
	 @@defangle = 90
	 puts "PutTool: initialize angle = " + @@defangle.to_s
end

def enableVCB?
  puts "PutTool: enableVCB? callback"
  return true
end

 def activate
  puts "PutTool: activate callback"
	 puts "PutTool: activate angle = " + @@defangle.to_s
	 #here'e where I will insert call rotate method to do initial rotation
	 #here's users chance to reset default:
	 Sketchup.vcb_label = "Enter new angle to revise rotation."
	 Sketchup.status_text = "Type new angle and press [ENTER]."
end

def deactivate(view)
  puts "PutTool: deactivate callback"
  Sketchup.status_text = ""
  Sketchup.vcb_value = ""
  Sketchup.vcb_label = "Measurements"
end

def onKeyDown(key, repeat, flags, view)
  return false
end

def onKeyUp(key, repeat, flags, view)
  return false
end

def onUserText(text, view)
	@@defangle = text
	puts "PutTool: onUserText = "<< @@defangle.inspect
  Sketchup.vcb_value = ""
end
end # class

end # module

Sorry guys, I never heard about proper posting of code blocks, I assumed the “block quote” tool did that and didn’t see any other tool. Now I understand I have to type ``` manually. So I posted the tool above. Since all the many suggestions are laden with ambiguity, (and sometimes conflict with each other), I would appreciate a concrete example by edit of my posted code. Thanks

Here is the modified tool:

module Testtool

  class PutTool
  
    @@defangle = 90.0

    def initialize(*args)
      puts "PutTool: initialize angle = " + @@defangle.to_s
    end

    def do_initial_rotation
      # code here
    end

    def do_revised_rotation
      # code here
    end

    def enableVCB?
      puts "PutTool: enableVCB? callback"
      return true
    end

    def activate
      puts "PutTool: activate callback"
      puts "PutTool: activate angle = " + @@defangle.to_s
      #
      do_initial_rotation()
      #
      Sketchup.vcb_label = "Enter new angle to revise rotation."
      Sketchup.status_text = "Type new angle and press [ENTER]."
      # After this callback's return, is user's chance to reset default:
    end

    def resume(view)
      puts "PutTool: resume callback"
      puts "PutTool: resume angle = " + @@defangle.to_s
      Sketchup.vcb_label = "Enter new angle to revise rotation."
      Sketchup.status_text = "Type new angle and press [ENTER]."
      # After this callback's return, is user's chance to reset default:
    end

    def deactivate(view)
      puts "PutTool: deactivate callback"
      Sketchup.status_text = ""
      Sketchup.vcb_value = ""
      Sketchup.vcb_label = "Measurements"
    end

    def suspend(view)
      puts "PutTool: suspend callback"
      Sketchup.status_text = ""
      Sketchup.vcb_value = ""
      Sketchup.vcb_label = "Measurements"
    end

    def onKeyDown(key, repeat, flags, view)
      return false
    end

    def onKeyUp(key, repeat, flags, view)
      return false
    end

    def onUserText(text, view)
      puts "PutTool: onUserText = "<< text.inspect
      unless text !~ /\A\s*(\-|\+)?\d/i
        newangle = text.to_f
        if newangle != @@defangle
          @@defangle = newangle
          do_revised_rotation()
        end
      end
      Sketchup.vcb_value = ""
      puts "PutTool: onUserText(): @@defangle = "<< @@defangle.inspect
    end

  end # class

end # module

Yea, that tool is about useless. We’ve complained but it is a Discourse thing, not specific to the SketchUp forum. (It has a funky way of working in that you click it first then copy your preformatted text into it. But it does not work well for code that has multiple paragraphs.)

Besides the ```ruby
does syntax hilighting, and makes it much nicer to read.

You can also use ```text for blocks of Ruby error text, or other programming languages.

Looks good. I would never have figured that I needed resume/suspend (or onKeyup or onKeydown) from the API.
Also, as I suspected, there had to be a way to do this without mixins or setter/getters. No matter how much anyone learns about ruby techniques, and ruby classes, the Sketchup tool class is like no other.

Now I’ll have to add one thing: Pass the command object to the tool so I can update the tooltip when I set a new @@defangle. This I know how to do.

In the beginning all I wanted to do us use the VCB instead of Inputbox and assumed it would be just as simple to do. NOT! Has anyone suggested a method in the API to use the VCB directly without all this incredible complexity?

Thanks a bunch.

Yea you can do that, …

… or if the command is referenced by a constant just refer to it by qualification:
Droid::Rotator::CMD.tooltip="some new informative text"

Or if it has a method that allows access to it’s instance:
Droid::Rotator::command.tooltip="some new informative text"

No, because it’s moot. The VCB is “owned” by the active tool, and there is ALWAYS an active tool. So to use the VCB, you must do so via an instance of the Tool class interface.

In order to “take over the VCB”, our tool must be an interrupter kind of tool, that interrupts the active tool temporarily suspending it, and then resume it after we are done with the VCB. I’ve talked about it, but I suppose the “Team” feels it is fraught with potential problems.