How toget a Settings Menu to communicate with a Main Menu?
-
I find it annoying sometimes to fill out my own Dialog Boxes. I was thinking of splitting some of the more uncommon options highlighted in orange from this Left Hand Door Dialog and also from a similar Right Hand Door dialog, thus making these 2 Dialogs less confrontational. Moving these options to a common separate Settings Dialog, looks like a better alternative, since most of the times these options are OK by default.
I have added a Settings sub menu with the 5 listed options: successfully. However when I "#" comment out the original 5 listed options in the main script, the new Settings menu is unable to communicate the default values to the main script.
I have been looking unsuccessfully for a script of a similar nature from which I could emulate similar code. Anyone have any ideas on a script with this similar design ?
TIA!
-
If you still are not using modules to wrap your code.. it will be difficult.
If your options are all assigned to module vars, it does not matter how many dialogs you create, to change the options many different ways...
... any methods within the module will be able to read the module vars, when the time comes to use their values.If it's a simple plugin, you can have named vars.. ie:
@@width
,@@height
, etc.But for more complex plugins (that you'll want to save settings for,) it makes it much easier to use a options hash:
@@options = {}
.. and later set an option:
@@options[:width]= 125 @@options[:height]= 50
And sometimes I use a shorter:
@@opts
Since most of your plugins are going to have options, you need to separate your "namespace" into "sub-namespaces" for each plugin, so that the options
@@
var references do not conflict. -
I hear your Module reference loud and clear Nevertheless this by itself will not provide a method for reading the information from the setting menu that ultimately needs to be read and replace the values of the constants in the main menu.... will it?
-
@tomot said:
I hear your Module reference loud and clear Nevertheless this by itself will not provide a method for reading the information from the setting menu that ultimately needs to be read and replace the values of the constants in the main menu.... will it?
Either you are confused, or now I am.
1) Constants are supposed to be static. (This is why Ruby issues a warning to
STDERR
when you change them, because they are supposed to becomefrozen
sometime in the future, perhaps in the Ruby 2.x.x trunk.)2) You were describing splitting ONE input dialog box into TWO dialog boxes, that change variables (not constants,) that your plugin needs to access.
My answer on this is YES. If both dialogs are in the same scope (your plugin submodule,) and they set
@@
module variables, ANY code, methods, etc., within that submodule scope, CAN access them.3) Menus ??? ... menus are the command lists that drop-down from the menubar, or popup when we right-click the mouse. The text strings names of menu commands.. ie:
UI::Command.menutext="Command name"
ormenu.add_item("Command name")
aredup
'ed and stored on the C++ side as real CONSTANTS, and cannot be changed during the session.Try it:
module MenuTest @@testmenu = UI.menu("Plugins").add_submenu("Test") @@textflag = 0 @@menutext = ["Original Menu Text","New String if successful"] @@cmd = UI;;Command.new(@@menutext[0]) { UI.messagebox("Trying to change menu text.") @@textflag =( @@textflag==0 ? 1 ; 0 ) @@cmd.menu_text = @@menutext[@@textflag] } @@testmenu.add_item @@cmd end # module
So are you talking about menus or dialog boxes ???
-
-
@tomot said:
I have added a Settings sub menu with the 5 listed options: successfully. However when I "#" comment out the original 5 listed options in the main script, the new Settings menu is unable to communicate the default values to the main script.
Ok ... this "main script" term.. implies you are attempting plugin "file spanning" without using namespaces (modules.)
You MUST use a namespace.
FYI.. the Ruby keywords
module
andclass
do not mean "define" (That is reserved for methods, ie,def
, and also means "redefine this method.")
The keywordsmodule
andclass
mean "open for editing".SO.. your plugin code, can be in 100 different files if you wish (although that would be extreme.)
But the code in each file must evaluate within the same namespace !!
Ex:
# file; 'tomot/fancydoor/doorwidget_main.rb' # ------------------------------------ module Tomot; end module Tomot;;FancyDoorWidget # declare module vars and constants first # require other parts of this plugin; require('tomot/fancydoor/common_opts.rb') require('tomot/fancydoor/lefthand_doors.rb') require('tomot/fancydoor/righthand_doors.rb') # main plugin script, menu items, etc. end
# file; 'tomot/fancydoor/common_opts.rb' # ------------------------------------ module Tomot;;FancyDoorWidget # method(s) to control common door options end # module
# file; 'tomot/fancydoor/lefthand_doors.rb' # ------------------------------------ module Tomot;;FancyDoorWidget # methods to work with lefthand doors end # module
... etc.
(A poor example... but each author has his/her own way of splitting up there code for ease of maintenance.)
I usually put all plugin options handling (saving, restoring, changing, etc.) in one file with a "_opt.rb" suffix.
.. and usually all menu, command and toolbar setup in another, with a "_mnu.rb" suffix.
The main plugin methods and code, (ie, the "engine",) I put in a file with a "_eng.rb" suffix.
If I have a bunch of misc. methods, I may put them in a separate file, with a "_lib.rb" suffix.I personally find it easier with Notepad++, to switch file tabs, or left-right edit panes, rather than scroll WAY up, and WAY back down within a single file.
It all depends on whether the plugin is simple or complex.
A very simple, single menu item "macro" I'll do all in one file. -
Oh.. another reason to break up your plugin code into multiple files.. is debugging.
If the code that creates the menus, menuitems and toolbars, is all in one file. Once that is loaded, (assuming you pointed the menuitems, and toolbar buttons at methods in your module...,) that particular file does not need to be reloaded during debugging.
If you are debugging, and tweaking options handling, in a separate file... after a tweak and save, you need only reload that specific file to redefine the methods you fixed.
-
@tomot said:
I have added a Settings sub menu with the 5 listed options: successfully. However when I "#" comment out the original 5 listed options in the main script, the new Settings menu is unable to communicate the default values to the main script.
I think we need to see some sample code here. Because all we know is the concept of what you want to do - which should be doable - but clearly there is something in the implementation that's not correct.
-
@thomthom said:
I think we need to see some sample code here. Because all we know is the concept of what you want to do - which should be doable - but clearly there is something in the implementation that's not correct.
I want to avoid, attaching code because I know its asking to much and goes against the bounds of the help you guys are already willing to provide, I will chew through Dan's contribution, and get the module stuff in place first. I also ran across Housebuilder by: Steve Hurlbut which I had forgotten uses a settings menu, ....oy vey!... that code should be fun to look at.
-
@dan rathbun said:
So are you talking about menus or dialog boxes ???
I picture is still worth a 1000 words:
-
OK.. your talking dialogs.
(It does not matter how the dialog box is opened... via a menu choice, a toolbar button click, a press of a hotkey, or a command typed at the console,.. it's all the same. The variables and options, are NOT on the menu.. they are in dialog boxes.)You are pointing at a list of instance variables, not constants. (Constants begin with an uppercase letter if they are a class or Module, otherwise they are all CAPSLIKETHIS.)
Follow what I said.
Paste those instance vars into the Module vars section, and add another @ to the front of them. (Editor search and replace within selection "@" with "@@".) Do the same else where in the code, whenever they are accessed.
If you want to save all those options between settings more easily, then:
You might as well change@dstyle
to@@opts[:dstyle]
,@drail
to@@opts[:drail]
, etc. -
@tomot said:
I hear your Module reference loud and clear Nevertheless this by itself will not ....
Come on.. stop making excuses. Listen to me!
Here's an updated template ... with empty Lefthand, Righthand, and Common Door Setting dialog methods.
Just cut and paste your code into a copy of the template.
- Put any public module functions that you will allow outside access to in the MODULE FUNCTIONS section. (They must be qualified by
def self.methodname
) - Put any private methods that you want only internal plugin access to, within the
private
section of the proxy class block. (They are NOT qualified. Just defined asdef methodname
)
Notice how they can be called like instance methods from anywhere within the module, without qualification? That's because the proxy IS a singleton instance of an anonymous class, linked to
self
(which is the module itself.) Ruby assumes an implied "self
" for all method calls that have no qualification.- Declare all module vars before use, as
nil
, or an initial value, or an empty string, or an empty array or hash, etc., in the MODULE VARIABLE section (Which is always outside the proxy class, but they CAN be accessed from within it.)
module Tomot; end module Tomot;;FancyDoorWidget unless defined?(REGKEY) #{# CONSTANTS # REGKEY = 'Plugin_'<<Module.nesting[0].name.split(';;').join('_') TOPMENU = Sketchup.read_default(REGKEY,'TopMenu','Plugins') SUBMENU = Sketchup.read_default(REGKEY,'SubMenu','') # #}# #{# MODULE VARIABLES # @@menu = nil @@menuitem = [] @@menuvalid = [MF_ENABLED,MF_ENABLED,MF_ENABLED,MF_ENABLED,MF_ENABLED] @@opts = { ;TopMenu => 'Plugins', ;SubMenu => '', } # #}# end # unless defined?(REGKEY) #{# MODULE METHODS # public #{ widget_1() # # Use a fancy Door Widget tool 1. # def self.widget_1() # # code goes here # end#} #{ widget_2() # # Use a fancy Door Widget tool 2. # def self.widget_2() # # code goes here # end#} # #}# #{# PROXY CLASS # class << self private # get_saved_opts() # def get_saved_opts() saved = {} @@opts.each_key {|attr| val = Sketchup.read_default( REGKEY, attr.to_s, 'blank' ) next if val == 'blank' # was never set, use literal @@opts saved[attr]= val } if saved.empty? # first run. opts were never saved. set_saved_opts() else @@opts.update(saved) # in-place call, same as; merge! end end#def get_saved_opts() # 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.gsub(34.chr,92.chr<<34.chr) ) else Sketchup.write_default( REGKEY, attr.to_s, val ) end } end#def set_saved_opts() ### --- DIALOG INPUTBOX METHODS <<---<<<<<<<<<<<<<<<<<<<<< #{ common_door_settings() # # Accesses the Common Door settings. # def common_door_settings() # # Call a inputbox here that sets common door # settings values in the @@opts hash. # # If any values change, call set_saved_opts() # end #} #{ lefthand_door_settings() # # Accesses the Lefthand Door settings. # def lefthand_door_settings() # # Call a inputbox here that sets lefthand door # settings values in the @@opts hash. # # If any values change, call set_saved_opts() # end #} #{ righthand_door_settings() # # Accesses the Common Door settings. # def righthand_door_settings() # # Call a inputbox here that sets righthand door # settings values in the @@opts hash. # # If any values change, call set_saved_opts() # end #} end # proxy class # #}# #{# RUN ONCE # unless file_loaded?( File.basename(__FILE__) ) # Init the plugin settings get_saved_opts() # Add commands to the menu if SUBMENU.empty? add_separator_to_menu(TOPMENU) @@menu = UI.menu(TOPMENU) else @@menu = UI.menu(TOPMENU).add_submenu(SUBMENU) end @@menuitem[0] = @@menu.add_item("Fancy Door Task 1") { self.widget_1 } @@menu.set_validation_proc(@@menuitem[0]) { @@menuvalid[0] } @@menuitem[1] = @@menu.add_item("Fancy Door Task 2") { self.widget_2 } @@menu.set_validation_proc(@@menuitem[1]) { @@menuvalid[1] } @@menu.add_separator() @@menuitem[2] = @@menu.add_item("Set Common Door Options") { @@menuvalid[2]= MF_GRAYED common_door_settings() @@menuvalid[2]= MF_ENABLED } @@menu.set_validation_proc(@@menuitem[2]) { @@menuvalid[2] } @@menuitem[3] = @@menu.add_item("Set Lefthand Door Options") { @@menuvalid[3]= MF_GRAYED lefthand_door_settings() @@menuvalid[3]= MF_ENABLED } @@menu.set_validation_proc(@@menuitem[3]) { @@menuvalid[3] } @@menuitem[4] = @@menu.add_item("Set Righthand Door Options") { @@menuvalid[4]= MF_GRAYED righthand_door_settings() @@menuvalid[4]= MF_ENABLED } @@menu.set_validation_proc(@@menuitem[4]) { @@menuvalid[4] } file_loaded( File.basename(__FILE__) ) end # #}# end # module
- Put any public module functions that you will allow outside access to in the MODULE FUNCTIONS section. (They must be qualified by
-
Whats really needed, from the old programming days of the past is a simple GOTO statement.
I hope my attached picture illustrates a simplified flow of my ruby. Presently if I # comment the @stylewidth variable under Dialog 1 the entity cannot be draw. However with a GOTO statement the program would be directed to the value to the @stylewidth variable under Dialog 2 and then be directed back to draw the entity.p.s. interesting if one Google's Goto Ruby, you get people commenting on either side of this issue.
-
@tomot said:
Whats really needed, from the old programming days of the past is a simple GOTO statement.
ABSOLUTELY TOTALLY UNTRUE !!
@tomot said:
I hope my attached picture illustrates a simplified flow of my ruby.
All it does, is show, that you still suffer from linear thinking.
Your simple example BookShelf plugin also indicates your "linear affliction", where you have all of the executable code within ONE single method.
You need to break away from that kind of thinking, in today's world of GUI programming where anything can happen, at any time, depending upon what the user's actions are.
Take two "Procedural, Event-Driven" pills and call me in the morning...
Ruby has everything you need, now. And there is no reason why if you are properly using scoped variables, that dialog #2 cannot access them.
If it cannot, then you are doing something to hide the variable from dialog #2.
Your pic cannot tell us what it is that you are doing wrong. We need a code example.
-
Let me also say that you really need to break up your code into methods.
A method call IS THE GOTO STATEMENT that you think you need.
So all of dialog #1 code should be within a method of it's own.
So all of dialog #2 code should be within a method of it's own.
The draw routine should be all within a method of it's own.
etc.. etc...
You can also create a getter method to get the value of a variable from anywhere within your module:
module Author;;SomePlugin @@stylewidth = 3.inches class << self def stylewidth() @@stylewidth end end # proxy class end # module
But that is unneeded, as the module var can be accessed modulewide.
Remember I told you that the difference between
@
instance vars and@@
module vars in modules, was subtle.
This is where the subtleness comes into play... Theattr_accessor
methods that create instance vars and getter and setter methods, are for mixin modules, that will be mixed into classes.This is why I was encouraging you to use
@@
module vars instead of@
instance vars in your modules. (But it looks like you copied someone else's use of instance vars in modules.) -
I didn't mean to open the Goto can of worms! it was a deja vu moment from the past, I realize that Goto can easily be satisfied using if. My current Ruby already contains some 20 if statements, which are goto's. I should be able link via another if statement the sharing of the stored registry value of the @stylwidth in Dialog 1 and Dialog 2.....Hopefully
-
Your totally missing the point...
Anyway... I took a walk and thought about it a bit.. and I think I realize what your problem may be.
You need to define (as in declare, for those coming from other programming langauges,) ALL those variables, that you wish to be accessible throughout your module, at the top of the module BEFORE and OUTSIDE of ALL methods.
Did you examine the module template I posted further up the topic ??
Look at the sentence just above the code box:
@dan rathbun said:
- Declare all module vars before use, as nil, or an initial value, or an empty string, or an empty array or hash, etc., in the MODULE VARIABLE section (Which is always outside the proxy class, but they CAN be accessed from within it.)
Now, look near the bottom of the template, in the RUN ONCE section.
What is the FIRST thing I did ??I called a method to get the user's saved options from the registry (which will update certain module vars with the user's values, saved from the previous session.)
From that point on... whenever ANY method gets the values that those module variables point to... the values will be the latest values. ANY method may change them, and ANY method can access their values. (It's just up to you to decide when to save any changes to the values back into the registry.)
-
@tomot said:
..., I realize that Goto can easily be satisfied using if.
No it cannot.
@tomot said:
My current Ruby already contains some 20 if statements, which are goto's.
No they are not branching statements, they are conditional statements.
But you CAN make them branch off to some other part of your code, by making method calls, in response to the boolean evaluation of the conditional expressions.
if condition_is_true then call_method_one() else call_method_two();
@tomot said:
I should be able link via another if statement the sharing of the stored registry value of the @stylewidth in Dialog 1 and Dialog 2..
Again... in a procedural language, a method call (aka procedure call,) takes the place of the old goto from the old sequential linear languages like Fortran and GW-BASIC.
Whenever you identify a particular place in your code, that you need to "goto" (usually repeatedly,)... then THAT is the spot where you need to begin a method.
Advertisement