Storing last values used in the registry?
-
You are confusing terms again.
They are input dialog boxes, NOT menus.
NO. Each one of your plugins should only use ONE registry key.
- Please begin all your keys with "Plugin_"
Reason.. this sets them apart from all other Sketchup subkeys, but still grouped together. And it follows the convention used by the WebDialog keys, which are always (automatically,) prefixed with "WebDialog_"
- Whitespace (and '::') in keynames should be replaced with "_".
like:
REGKEY = 'Plugin_'<<Module.nesting[0].name.split('::').join('_')
notice I use a constant (all caps,) because it will never change.
It would produce for your example:
'Plugin_TNT_Bookshelf'
BUT ...
Each "set" of values OR special setting has it's own ATTRIBUTE name (under the common plugin KEY.)
"RightHandDoorParams"
"LeftHandDoorParams"
-
Next issue.
I gave you a couple of basic saving and loading option methods, in this thread's code sample:
http://forums.sketchucation.com/viewtopic.php?f=180&t=43673&p=390107#p389920What do you want to do differently ??
We can tweak the process.
For example.. if you still wanted to use unique
@@
names, like@@lhd_width
,@@lhd_height
,@@lhd_style
, etc., instead of@@opts[:lhd_width]
, etc. everywhere in your code ...
... you CAN do that and still use the@@opts
hash of saving and restoring.The trick is to collect ALL module variables (also called class variables,) into a array of var name strings, using the standard method
class_variables()
then iterate that array thus:vars = class_variables() vars.each do |var| eval("@@opts[#{var.gsub('@@',';')}]= #{var}") end # Now we can save the opts hash; set_saved_opts()
The reverse, after calling
get_saved_opts()
, to distribute the saved settings back to your module variables:get_saved_opts() @@opts.each do |attr,val| eval("@@#{attr.to_s} = @@opts[#{attr}]") end
-
@dan rathbun said:
You are confusing terms again.
They are input dialog boxes, NOT menus.yes, menus my bad!... been a long day so far, could not sleep, I started coding at 3:30 am this morning, with good intentions trying your outline, finally had to sideline that effort, it in favor of a simpler older script of mine in need of a module. Regardless this small demonstration script, is a starting point, for going forward. using module & registry works!..........comments welcome!
-
OK looking at it... and making a few quick changes.
ADD: Here's version '1.1.1'
Fixed line 65: forgot to paste in the new attribute name"BookshelfDimensions"
.. sorry.
I fixed it so there is only ONE key for the plugin, and each dialog has a separate attribute beneath that key.
viz:
Also:- Put the plugin inside a plugin submodule:
TNT::BookShelf
. - Plugin now resides with the path "tnt/bookshelf/"
- Added a
VERSION
constant. - Wrapped the undo operation in a
begin
...rescue
...end
block. - Moved the "BookShelf Details" dialog before the undo operation.
- Moved the last line:
file_loaded(*filepath*)
inside thefile_loaded?
conditional block. (Just to be sure it only gets pushed into$loaded_files
once. Althoughfile_loaded()
may test for this, there's no sense calling it if not needed to.) - Made note (in file header,) that on XP the REGKEY path differs from Win6+.
- Put the plugin inside a plugin submodule:
-
Bumped: version '1.1.1'
Fixed line 65: forgot to paste in the new attribute name"BookshelfDimensions"
.. sorry. -
Dan thanks for your patience! I'm going to digest your changes...tomorrow.
After 12 hrs of sitting,... my ass is developing flat spots
In the next stage I will be tackling how the incorporating of therectangle.rb code
which is used in my doortool ruby works with the module & registry stuff I have learned to implement today.
ttyl! -
Looks great Dan! Now unless I'm missing something, (which happens quite often), does the storing of last used dialog values in the registry not make for a more elegant solution then using the @@ approach?
-
Firstly... the difference between
@
vars and@@
vars in modules are subtle. (Save that issue for later.)Elegant? Well your really doing the same thing ... but dialog by dialog, instead of the plugin's whole set of saved values.
Also you're doing it in a sort of brute force manner. You are accessing the stored values in the registry (whether you need to not,) and always saving them (without really testing if they are good values. Relying on the user to input values they wish to save.)
Also you are creating the literal strings for the inputboxes each time the 'main' method is called.
But that's OK for now... you can understand what's happening.Later on you (when you're ready to get fancier,) you might move the defaults, prompts etc out of the method into module vars. But it's not that important for a really simple single command plugin module. As your plugins get more complex and bigger, you will want to centralize common functions like loading and saving defaults into one method ... rather than have them scattered throughout your code. (Easier maintenance!)
Ruby is multi-paradigm... "there is more that one way to skin a cat."
The BIG lessons you have learned, is module wrapping, using variables that have namespace scope, and saving default values.
Also take note of the operation block.
I have noticed problems with the API's
read_default()
on PC when it tries to read REG_SZ strings values. We need to store quotes with the value in the registry. Using theinspect()
method is a good idea, because it will escape embedded ' or " if used for unit symbols.
If I try to read the "SnapLength" attribute in the "Preferences" key, I getnil
, because it's set to1"
instead of'1"'
or"1\""
Also it cannot see any keys with spaces in their names, and just returnsnil
. -
Nice explanation! if I continue for now to go down the registry path
How does or can values accept strings ? such as door open, or door closed?
-
@tomot said:
Nice explanation! if I continue for now to go down the registry path
How does or can values accept strings ? such as door open, or door closed?OK just tested a bit ... the
write_default
andread_default
have no problems if you feed a string (or most simple class types,) to them. The write stores it so it can be read OK.The problem arrises when there is more than one double quote char (
"
) in sequence, AND they are not escaped.
Becausewrite_default
always surrounds a string argument, perhaps by callinginspect()
internally to the method.so this was wrong, on my part:
# set_saved_opts() # def set_saved_opts() @@opts.each_pair {|attr,val| if val.is_a?(String) Sketchup.write_default( REGKEY, attr.to_s, val.inspect ) else Sketchup.write_default( REGKEY, attr.to_s, val ) end } end#def set_saved_opts()
I knew we needed to handle strings specially, but I went the opposite way. (The
inspect
method adds quotes.)We really do not want to ADD any more quotes chars...
... we want to either strip them off of strings, or be sure they are escaped.
Let's go with option 2, and use the
gsub
method, to escape all internal double quotes, like:
gsub(34.chr,92.chr<<34.chr)
# set_saved_opts() # def set_saved_opts() @@opts.each_pair {|attr,val| if val.is_a?(String) Sketchup.write_default( REGKEY, attr.to_s, val.gsub(34.chr,92.chr<<34.chr) ) else Sketchup.write_default( REGKEY, attr.to_s, val ) end } end#def set_saved_opts()
In your case if you had a
results
array that had an embedded string:
[1, 2, "door closed", 2.54]
and you useinspect()
on it, you get this:
"[1, 2, "door closed", 2.54]"
andread_default
cannot eval it, so it returnsnil
Also, you cannot doubleinspect
it, because that will result in the whole registry value delimited by 2 double quotes:
[1, 2, "door closed", 2.54].inspect.inspect
puts this:%(#008000)[""[1, 2, \"door closed\", 2.54]""]
into the registry, andread_default
will poop out again, (returningnil
.)So... you need to use:
results.inspect.gsub(34.chr,92.chr<<34.chr)
You can try it at the console:
Sketchup.write_default("Plugin_TNT_Bookshelf", "Array", [1,2,"door closed",2.54].inspect.gsub(34.chr,92.chr<<34.chr))
and then to test retrieving it:
[ruby:3jpmcjqh]arr = Sketchup.read_default("Plugin_TNT_Bookshelf", "Array")[/ruby:3jpmcjqh]
which if all goes well, should return:
[ruby:3jpmcjqh]'[1,2,"door closed",2.54]'[/ruby:3jpmcjqh] which can be eval'd back into an array.OK?
However...
write_default
andread_default
can ALSO write and read arrays directly if you DON'T inspect them.
This is because the 'write' converts them to a string, before storing them in the registry,... and then the 'read' evals those strings back into an [ruby:3jpmcjqh]Array[/ruby:3jpmcjqh].But.. to confuse the issue... they do not handle hashes well. We have to resort to the [ruby:3jpmcjqh].inspect.gsub(34.chr,92.chr<<34.chr)[/ruby:3jpmcjqh] treatment for any hash arguments to
write_default
, and eval the return strings fromread_default
.
Now.. (if your dizzy trying to absorb this,) perhaps, you'll understand why I like to write one method to get all this proper handling correct, and then reuse that method in multiple plugins. (Say via a mixin module.) Trying to remember all the quirky issues, when it comes time to write and read values, is a pain in the butt.
You don't run into this 'nested string' in hashes and arrays problem, if all your variables are saved into individual attributes in the registry.
(... And they are all single value types, ie, not arrays, hashes, structs, etc.)def save_my_vars() vars = instance_variables + class_variables vars.each{|var| eval("Sketchup.write_default(#{REGKEY}, '#{var}', #{var}") end def load_my_vars() vars = instance_variables + class_variables vars.each{|var| eval("#{var} = Sketchup.read_default(#{REGKEY}, '#{var}'") end
But.. that will save ALL of your variables... even iterators, and ones you do not really need to save.
This is why I collect only the ones I wish to save, into an [ruby:3jpmcjqh]@@opts[/ruby:3jpmcjqh] hash, and then write only those values to the registry.
Example.. at the top of your Bookshelf module, where you define your instance [ruby:3jpmcjqh]@[/ruby:3jpmcjqh] variables... you could just as easily define them AND an [ruby:3jpmcjqh]@@opts[/ruby:3jpmcjqh] hash, all in one go:
# default bookshelf values used for script development unless defined?(@@opts) @@opts = { ;height => @height = 2.feet, ;width => @width = 2.feet, ;depth => @depth = 1.inch, ;ny => @ny = 5, ;nz => @nz = 5, ;dist => @dist = 1.inch } end
Anyway... as long as the simple way works.. use it. When things get more complex... and you need to centralize your default value handling... you've got a topic thread here to mine for ideas.
-
I have been following this topic as I have in the past used a file to store variables between sessions and my question is does the registry storing of variables only work on Win based computers and not on the Apple OS?
Keith
-
The Sketchup.read_defaults and Sketchup.write_defaults use each PC's Sketchup 'Registry' entries OR a MAC's Sketchup 'plist' file entries... so they should work cross-platform.
The main issue with using the Registry is storing data restructured into a format that can be recovered and then converted back into the required 'class' later - e.g. a string/float/integer/length/boolean/array/hash etc will need some 'manipulation' before writing - but if you have been using a 'file' to store such things then you've been writing them as strings already - when you reread the file you presumably convert the strings back with say string.to_f to make the value a number etc... [like an 'ini' file].
The Registry data exists across sessions globally for Sketchup on each PC, not just the current SKP.
So if you want SKP specific data saving then it's best to use model-attributes to save things with the SKP [you can save attributes as most common data types directly - except a hash] - the data is thereby accessible to all uses on the network when you script reads the attributes as it runs, and later updates the attributes as it closes, this can all be done independent of each PC's Registry entries.Horses for courses...
Advertisement