[Closed] macroscript UI state saved with maxfile
The more macros I write, the more often I’d like to be able to have the state of the UI of the macro (values,state of checkboxes etc) saved with the maxfile. I have a way to do this at the moment, by storing them in the custom file properties. But the way I’m doing it seems to be very long-winded and laborious. Not to mention convoluted
Below is a simple random wirecolor script with the technique that I use in it. Can anyone help me with a quicker, less verbose way to store UI state in a max file?
macroscript RandomWireColourHSV
category: "CgRay"
buttontext:"RwcHSV"
Icon:#("CgRay-RandomWireColourHSV",1)
(
clearlistener()
try (destroydialog RandomWirecolorHSV) catch()
rollout RandomWirecolorHSV "Random Wirecolor HSV"
(
progressbar col1 "" value:100 color:red height:8 width:38 align:#right offset:[26,0] across:2
progressbar col2 "" value:100 color:red height:8 width:38 align:#right offset:[0,0]
label huelabel "Hue Range" align:#left across:3
spinner spn_huelow range:[0,255,0] type:#integer width:40 align:#right
spinner spn_huehigh range:[0,255,255] type:#integer width:40 align:#right
label satlabel "Sat Range" align:#left across:3
spinner spn_satlow range:[0,255,0] type:#integer width:40 align:#right
spinner spn_sathigh range:[0,255,255] type:#integer width:40 align:#right
label vallabel "Val Range" align:#left across:3
spinner spn_vallow range:[0,255,0] type:#integer width:40 align:#right
spinner spn_valhigh range:[0,255,255] type:#integer width:40 align:#right
label randomise "Randomise:" align:#left across:3
button btn_selected "Selected" align:#right offset:[19,0] across:2
button btn_all "All" align:#right
fn checkLow &lowVal &highVal =
(
if lowVal > highVal do
(
highval = lowval
)
)
fn checkHigh &lowVal &highVal =
(
if lowVal > highVal do
(
lowval = highval
)
)
fn hueChange =
(
tempcol2 = RandomWirecolorHSV.col1.color
tempcol2.h = RandomWirecolorHSV.spn_huelow.value
RandomWirecolorHSV.col1.color = tempcol2
tempcol3 = RandomWirecolorHSV.col2.color
tempcol3.h = RandomWirecolorHSV.spn_huehigh.value
RandomWirecolorHSV.col2.color = tempcol3
)
fn FilePropInit =
(
HueLowIndex = fileProperties.findProperty #custom "HueLow"
HueHighIndex = fileProperties.findProperty #custom "HueHigh"
SatLowIndex = fileProperties.findProperty #custom "SatLow"
SatHighIndex = fileProperties.findProperty #custom "SatHigh"
ValLowIndex = fileProperties.findProperty #custom "ValLow"
ValHighIndex = fileProperties.findProperty #custom "ValHigh"
if HueLowIndex != 0 then
( RandomWirecolorHSV.spn_huelow.value = fileProperties.getPropertyValue #custom HueLowIndex
hueChange()
)
if HueHighIndex != 0 then
( RandomWirecolorHSV.spn_huehigh.value = fileProperties.getPropertyValue #custom HueHighIndex
hueChange()
)
if SatLowIndex != 0 then
( RandomWirecolorHSV.spn_satlow.value = fileProperties.getPropertyValue #custom SatLowIndex
)
if SatHighIndex != 0 then
( RandomWirecolorHSV.spn_sathigh.value = fileProperties.getPropertyValue #custom SatHighIndex
)
if ValLowIndex != 0 then
( RandomWirecolorHSV.spn_vallow.value = fileProperties.getPropertyValue #custom ValLowIndex
)
if ValHighIndex != 0 then
( RandomWirecolorHSV.spn_valhigh.value = fileProperties.getPropertyValue #custom ValHighIndex
)
)
fn changeHueProp =
(
fileProperties.addProperty #custom "HueLow" (RandomWirecolorHSV.spn_huelow.value)
fileProperties.addProperty #custom "HueHigh" (RandomWirecolorHSV.spn_huehigh.value)
)
fn changeSatProp =
(
fileProperties.addProperty #custom "SatLow" (RandomWirecolorHSV.spn_satlow.value)
fileProperties.addProperty #custom "SatHigh" (RandomWirecolorHSV.spn_sathigh.value)
)
fn changeValProp =
(
fileProperties.addProperty #custom "ValLow" (RandomWirecolorHSV.spn_vallow.value)
fileProperties.addProperty #custom "ValHigh" (RandomWirecolorHSV.spn_valhigh.value)
)
on RandomWirecolorHSV open do
( FilePropInit ()
)
on spn_huelow changed huelowval do
( checkLow &spn_huelow.value &spn_huehigh.value
changeHueProp ()
hueChange ()
)
on spn_huehigh changed huehighval do
( checkHigh &spn_huelow.value &spn_huehigh.value
changeHueProp ()
hueChange ()
)
on spn_satlow changed satlowval do
( checkLow &spn_satlow.value &spn_sathigh.value
changeSatProp ()
)
on spn_sathigh changed sathighval do
( checkHigh &spn_satlow.value &spn_sathigh.value
changeSatProp ()
)
on spn_vallow changed vallowval do
( checkLow &spn_vallow.value &spn_valhigh.value
changeValProp ()
)
on spn_valhigh changed valhighval do
( checkHigh &spn_vallow.value &spn_valhigh.value
changeValProp ()
)
on btn_selected pressed do
(
if selection.count !=0 then
(
for obj in selection do
(
hue = (random spn_huelow.value spn_huehigh.value)
sat = (random spn_satlow.value spn_sathigh.value)
val = (random spn_vallow.value spn_valhigh.value)
tempColor = color 0 0 0
tempColor.v = val
tempColor.s = sat
tempColor.h = hue
obj.wirecolor = tempColor
)
)
else
(
messagebox "Select something first"
)
)
on btn_all pressed do
(
if objects.count !=0 then
(
for obj in objects do
(
hue = (random spn_huelow.value spn_huehigh.value)
sat = (random spn_satlow.value spn_sathigh.value)
val = (random spn_vallow.value spn_valhigh.value)
tempColor = color 0 0 0
tempColor.v = val
tempColor.s = sat
tempColor.h = hue
obj.wirecolor = tempColor
)
)
else
(
messagebox "No objects in scene"
)
)
)
createdialog RandomWirecolorHSV 185 110
)
why not store them in a custom attribute block (set on the sceneroot object (has issues in older max versions), or a track view node, or anywhere that’s got a maxwrapper really)?
if nothing else, you could abstract the file properties function and call it more directly from the UI event handlers and such; but I really don’t think file properties is an appropriate spot for such storage anyway
it’s possible to add your states into a persistant array. I don’t know if it’s the best way to do it, but it works.
I think one of the problems with a persistent global was that if you started to load a file, but then didn’t actually load it (or merge it), then your global variable would still be overwritten.
It does have the major advantage of being able to store almost any maxscript data in any combination you want… whereas with CA’s, you’re stuck with the parameter types allowed and there’s no mixing them.
I haven’t gotten a chance to play with it much but Rob has a ui manager script on tech-artists as part of their Community scripts initiative.
From the script header:
This code is in the public domain. Published and maintained by www.tech-artists.org
*************************/
/*
The basic idea of iniMgr is scripters can place a single line in a their rollout events to save and load ini settings.
"on theRollout open do (iniMgr.load( ) )" would load the rollout settings last saved.
"on theRollout close do (iniMgr.save() ) would save the rollout settings to an ini file.
This sort of integration is ideal for scripters, who will not need to worry about dynamically saving/loading settings
specified for each rollout.
The idea is NOT to save ALL rollout data. Just the CONTROL PROPERTIES that the user can change via the UI.
We will NOT store properties NOT ACCESSABLE through the UI (such as height, width, etc.)
Support can be provided at a later date to write ALL UI data but this will have to be done on a per-control
basis by passing them to initMgr.save and initMgr.load
*/
the main site: http://tech-artists.org/
the uiMgr repository: http://tech-artists.org/hg/tao_official/mxs_uimgr/
But now that i read the actual question, i see that you’re looking PER MAXFILE, and this is PER SCRIPT
shouldn’t be an issue to make it per-maxfile if it loops over e.g. .controls, get the control type, then gets/sets its value accordingly. only ‘problem’ is that you often don’t really want all controls to be enumerated in that manner (could exclude them by naming them separately to begin with) – but yes, 3rd will have to decide how/where he wants to store things, first
Thanks everyone. I think I’ll go with the persistent globals option as they seem to do the trick for me. I know I could just use the premade script from tech-artists, but then I wouldn’t learn how to do it myself. I just had a play around with the advice from ZeBoxx2 and never having used the controls array before, I am pleasantly surprised. I came up with the code below. Is this sort of what you were talking about ZeBoxx2?
for i = 1 to (RandomWirecolorHSVDialog.controls.count) do
(
currentControl = RandomWirecolorHSVDialog.controls[i]
if (classof currentControl == SpinnerControl) then
(
pgName = ("perGlobUI_" + currentControl.name)
if (execute(pgName)==undefined) then
(
execute ("persistent global " + pgName + " = " + (currentControl.value as string))
format "Making Persistent Global %
"pgName
)
)
)
)
Cheers,
Cg.
just a thought, couldn’t you implement a preset file (like an ini, or xml) that is named the same as the file itself and stored in a dedicated folder somewhere. You could then, when opening the script, check if a preset exists the same as the maxfilename, and load it. Might be a problem it you are using hundreds of maxfiles but the preset file would be tiny. You could also save one when exiting the script if there wasnt one present. Perhaps an option without having to use persistent globals ick :argh:
totally agree !!!
quick test ( using [i].ini[/i] ) :
YourFolder_Inside_MaxRoot = "Custom_User_Settings"
--eg C:\Arquivos de programas\Autodesk\3ds Max 9\Custom_User_Settings
YourScript_Name = "YourScript_Settings" + (".ini")
global YourIniFile = getDir #maxroot + "\\" + YourFolder_Inside_MaxRoot + "\\" + YourScript_Name
testVal = [0,10,40]
testType = "Category"
testControl = "Controller"
setIniSetting YourIniFile testTypey testControl (testVal as string)
execute (getIniSetting YourIniFile testType testControl)
i strongly recomend you to check outthis Bobo´s DVD, because describes it in step-by-step using .ini files.
ps
would be cool if someone can post an example using XML file !
persistent globals are far less ‘ick’ than having to track another file (2010’s asset tracking enhancements might make that slightly more feasible – but I’ve not looked into that).
The only thing I’ve found ick about persistent globals is that potential overwrite issue* and that plugins can’t access data stored within the persistent globals (which is not an issue if you don’t intend to have anything but your script(s) access it anyway).
I think there were other issues (bobo explained them in a thread on here once… ‘ll search for it in a bit), but…
- a persistent global will store e.g. #(true,1.0,“hello world”). A custom attribute block will not (not without setting up 3 parameters of types #boolean, #float and #string, and assigning to those)
- a persistent global will store, say, a struct. No can do in custom attributes, meaning that any struct data would have to be copied piece-wise (again to different parameters where appropriate)
- for that matter… a persistent global will store any MaxScript value including references to any MaxWrappers. A CA will store most of the maxwrappers, but an ini file, xml file, etc. most certainly wouldn’t.
Unless there’s another method to store MaxScript values in a max scene file so that they can be reloaded with ease, persistent globals are sometimes maybe not the best, but certainly the most convenient solution.
- Edit: imagine that… the whole ‘overwrite on merge’ thing was warned so vigorously about, that I thought it still existed. Looks like that was fixed in 3ds Max 8:
http://forums.cgsociety.org/showpost.php?p=5023737&postcount=10
Still can’t find the post with all the pros/cons of persistent globals… maybe it wasn’t Bobo’s :\