PDA

View Full Version : macroscript UI state saved with maxfile


3rd Dimentia
04-14-2009, 05:41 AM
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
)

ZeBoxx2
04-14-2009, 10:53 AM
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 :)

holycause
04-14-2009, 02:06 PM
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. ;)

ZeBoxx2
04-14-2009, 03:53 PM
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.

Vsai
04-14-2009, 08:24 PM
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

ZeBoxx2
04-14-2009, 08:37 PM
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 :)

3rd Dimentia
04-15-2009, 03:35 AM
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 %\n"pgName
)
)
)
)



Cheers,

Cg.

ZeBoxx2
04-15-2009, 10:28 AM
yep, something like that :)

LoneRobot
04-15-2009, 10:40 AM
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:

ZeBoxx2
04-15-2009, 11:24 AM
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 :\

Polimeno
04-16-2009, 04:32 AM
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.

totally agree !!!

quick test ( using .ini ) :

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 out this Boboīs DVD (http://store.autodesk.com/store/adsk/en_US/pd/productID.81369900), because describes it in step-by-step using .ini files.

ps
would be cool if someone can post an example using XML file ! http://www.lotus911.com/blogs/smelser.nsf/emoticons/MSMR-7777JU/$File/EmoticonHappy.gif

eek
04-16-2009, 06:32 AM
I'd store it in a CA, as ZeBoxx suggests - pretty easy to access then and it stays with the scene. As to persistency structs fall into that problem too - wish there would be some sort of 'weak' struct - other than instancing and hoping someone else has got the same one.

RobGalanakis
04-16-2009, 02:03 PM
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.

But now that i read the actual question, i see that you're looking PER MAXFILE, and this is PER SCRIPT

Yeah, this is really meant for rollouts. It relies on the open/close event handler. I'm not sure it has use for macros, unfortunately.

It also works per-rollout and per-file. So any settings you save will be specific to that rollout AND that maxfile (I cull off numbers from the end so iterative versions are considered the same). That said, I love uiMgr, I use it at work in all my scripts, and I cannot possibly imagine coding without it anymore. Having TWO lines of code give you all the rollout memory you need is exactly why I push libraries and reuse in the first place. The work that would be involved in adding memory to every rollout individually (as I used to before uiMgr, which meant very few scripts got it) is just not worth it (globals and persistent globals storing properties, having to declare and reference AND use them in the rollout code. With uiMgr, just two lines of code.).

But I do need to update the TAO version, I forgot to merge in my changes from work! It is uiMgr, not iniMgr. See, I urge people to use this stuff, then when someone does I forget to update it, doh! I'll do it this weekend.

3rd Dimentia
04-22-2009, 03:06 AM
Polimeno, can you tell me more about that DVD by Bobo. There's very minimal info about it on the Autodesk site. It doesn't even say whether it's C++ or Maxscript or both. It just says:


User interface creation
Optimal workflows for designing artistic tools

Polimeno
04-22-2009, 06:01 AM
Polimeno, can you tell me more about that DVD by Bobo. There's very minimal info about it on the Autodesk site.


i think it was a Master Class he did during siggraph 2008....has about approx 80 min,
seems fast but itīs not dificult to understand.





Creating Tool ( 100% MXS, simple way to script a CountfacePolygons tool with some option, etc)
Interface for this
Adding options and refining the code
.ini file, template code, workflow, etc
Wrap up and finish with good tips n tricks

http://www.lotus911.com/blogs/smelser.nsf/emoticons/MSMR-7777JU/$File/EmoticonHappy.gif

LoneRobot
04-22-2009, 09:54 AM
It's also available on download if you are on subscription :thumbsup:

3rd Dimentia
04-23-2009, 07:04 AM
Great!!! Thanks. Downloading it now.. Along with all the other things there that might be useful. I had no idea we had access to all this stuff.

3rd Dimentia
04-24-2009, 06:22 AM
After watching some of the DVD from Bobo, I'm in 2 camps at the moment. For the floater position, I'm keen on using the ini file option so the floater pops up in the same position no matter what max file I have open. But for the UI elements, I'm going to use the persistent globals option so the UI settings get saved in each max file.

I have another related question though... Is there a way to iterate thru the UI controls and have them reset to their default?

I know the following to be able to iterate thru but don't know how to acess the default settings of the UI elements. if it's possible at all.

for currentControl in pcGraphToolsDialog.controls do

Cheers,

Cg.

JHN
04-24-2009, 11:10 AM
What you can do is after a clean first run, get all values in an array from the rollout.controls property, and hardcode those values in the script. I have done a similar thing with hardcoding all position of buttons and such and use those values as base values when I scale my dialog and stuff floats around. A same thing can be done for default values. There's no way to access the default settings in the script when you have modified settings. So grab it once and hardcode it in the script I say...

-Johan

CGTalk Moderation
04-24-2009, 11:10 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.