I don’t like the ‘execute’ function, when possible it should be avoided.
you could more easily store #(255,0,0) in the tag instead of (color 255 0 0) as string, then recreate the color as color s.tag[1] s.tag[2] s.tag[3]
you can easily store max color in tag as dotnetmxsvalue:
control.tag = dotnetmxsvalue red
print control.tag.value
ps. i usually store structures in control’s tag
That is a good idea.
Execute is usually a last resort of things…super last resort.
To better explain things…this is what I’m after and going for.
The ability to target one master folder for example
C:\User\Scripts
Then within it are subfolders such as Animation,Rendering,Tools
Those subfolders are (as lo put it) the parent buttons and the children are the .ms scripts.
It would be like taking this and converting it so it creates the buttons instead of an rc menu.
Just change the “dir” to a targeted directory of scripts and run it.
(
local fullPath = undefined
dir = @"C:\Users\Crabs User\Desktop estScripts" --//Main Scripts directory
dirArray = GetDirectories (dir+"/*") --//Get subfolders in directory
foldersArray = for d in dirArray collect (trimright (filenameFromPath d) "\\") --//Array which stores just the names of the subfolders
local cm = dotNetObject "System.Windows.Forms.ContextMenuStrip"
local cursor = dotNetClass "System.Windows.Forms.Cursor"
for i in foldersArray do
(
item = cm.Items.Add i
item.name = i
)
fn onPopup s e =
(
for b = 1 to foldersArray.count do
(
i = (s.Items.Find (foldersArray[b] as string) off)[1]
i.DropDownItems.Clear()
sFiles = #()
sFiles = getFiles (dirArray[b] + "*.ms*")
fn fnRunScript s e = --Run the selected script
(
fileIn (s.tag)
)
if sFiles.count > 0 then --//Add the scripts to appropriate list
(
for n in sFiles do
(
scriptName = getFIleNameFile n
item = i.DropDownItems.Add scriptName
item.name = scriptName
item.tag = n -- variable "n" contains the fullpath extention
dotnet.addEventHandler item "Click" fnRunScript
)
)
else
(
item = i.DropDownItems.Add "No Scripts"
item.enabled = off
)
)
)
dotnet.addEventHandler cm "Opening" onPopup
on execute do
(
cm.Show(cursor.Position)
)
)
Now I’m going to look into how to procedural add a button on creation. Which the equivalent of that in maxscript would be rci.addControl #button #myButton “My Button”
Got step one.
Building each button based on a loop. This main buttons being the parents.
Next is building each button and then its children buttons.
For the time being I just stored a string of names in the Array instead of putting a directory. Next version I’ll being referencing a directory though.
parentFolders = #("Animation","Rendering","Modeling") --Main Folders
form=dotNetObject "form"
sysPointer=dotNetObject "system.intPtr" (windows.getMaxHWND())
maxHandle=dotNetObject "maxCustomControls.win32HandleWrapper" sysPointer
form.show maxHandle
form.bounds=dotNetObject "system.drawing.rectangle" 10 90 400 400
form.text="DotNet UI"
for i = 1 to parentFolders.count do (
btn = dotNetObject "system.windows.forms.button"
btn.Text = (parentFolders[i] as string)
btn.Left = 10
btn.Top = 10 + (i*24)
form.Controls.Add(btn)
)
I’m starting to like this dotNet stuff more and more. I feel like I’ll never want to build a ui using rollouts in max.
A word of warning: currently any events you assign to your buttons will be lost with the first maxscript garbage collection. This is because there is no reference to them in maxscript, only in the dotnet form.
You have two possible solutions for this:
- in max2010 and up, use the dotnet.setLifeTimeControl method.
- add all dotnet controls that you need to keep events for in an array, ideally within a struct. See the example I gave earlier in this thread. This is my preferred method because it doesn’t limit your script to 2010 and up.
This issue has been discussed to exhaustion in other threads on this forum, in case you seek more information on it.
Here is the initial code which takes a targeted folder and scans its subfolders for scripts and puts a number value at the beginning of the name to signify which group its it. That is only for the purpose of testing for right now.
This is what I’ve got so far.
Any improvements or comments or suggestions are welcome!
--//Variables
dir = @"C:\Users\IEVFX\Desktop rash\scripts" --//Main Scripts directory
dirArray = GetDirectories (dir+"/*") --//Get subfolders in directory
parentFolders = for d in dirArray collect (trimright (filenameFromPath d) "\\") --//Array which stores just the names of the subfolders
form=dotNetObject "form"
sysPointer=dotNetObject "system.intPtr" (windows.getMaxHWND())
maxHandle=dotNetObject "maxCustomControls.win32HandleWrapper" sysPointer
form.show maxHandle
form.bounds=dotNetObject "system.drawing.rectangle" 10 90 400 400
form.text="DotNet UI"
--//Creates the Parent buttons
for i = 1 to parentFolders.count do (
btn = dotNetObject "system.windows.forms.button"
btn.Text = (parentFolders[i] as string)
btn.Left = 10
btn.Top = 10 + (i*24)
form.Controls.Add(btn)
)
--//Creates children buttons
for b = 1 to parentFolders.count do
(
sFiles = #()
sFiles = getFiles (dirArray[b] + "*.ms*")
if sFiles.count > 0 then --//Add the scripts to appropriate list
(
for n = 1 to sFiles.count do
(
scriptName = getFIleNameFile sFiles[n]
btn = dotNetObject "system.windows.forms.button"
btn.Text = (b as string + "_" + scriptName as string)
btn.Left = 90 * b
btn.Top = 10 + (n*24)
btn.tag = sFiles[n]
print btn.tag
form.Controls.Add(btn)
)
)
)
Hey lo,
So I’ve built the script into the structural format like you mentioned.
This is what I’ve got to far.
Just change the dir variable to a directory with folders containing scripts and it should be good to go.
All dynamically created controls.
So if you guys want to check it out and let me know your thoughts about it that would be great. Thanks
global exampleDotNetTool
--//Variables
dir = @"C:\Users\IEVFX\Desktop rash\scripts" --//Main Scripts directory
dirArray = GetDirectories (dir+"/*") --//Get subfolders in directory
parentFolders = for d in dirArray collect (trimright (filenameFromPath d) "\\") --//Array which stores just the names of the subfolders
struct exampleDotNetToolStr
(
--// components
form,
parentBtns,
childBtns,
--// handlers
fn parentClicked s e =
(
for b in exampleDotNetTool.childBtns do b.visible = b.tag == s.tag
),
fn childClicked s e =
(
format "child of group % pressed
" s.tag
),
--// initialization functions
fn controlFactory type text backColor X Y W H tag =
(
local ctr = dotNetObject type
ctr.text = text
ctr.bounds = dotNetObject "System.Drawing.Rectangle" X Y W H
ctr.backColor = (dotNetClass "System.Drawing.Color").fromArgb backColor.a backColor.r backColor.g backColor.b
ctr.tag = tag
ctr
),
fn initializeForm =
(
form = dotNetObject "maxCustomControls.maxForm"
form.showInTaskbar = false
form.text = "Dotnet Tool Example"
form.bounds = dotNetObject "System.Drawing.Rectangle" 100 100 250 300
form.controls.addRange parentBtns
form.controls.addRange childBtns
local sysPointer = dotNetObject "System.IntPtr" (windows.getMaxHWND())
local maxHandle = dotNetObject "maxCustomControls.win32HandleWrapper" sysPointer
form.show maxHandle
),
fn initializeControls =
(
local parentBtnDefs = #()
local childBtnDefs = #()
local pos = 10
--//Create Groups
for i = 1 to parentFolders.count do (
sFiles = #()
sFiles = getFiles (dirArray[i] + "*.ms*")
groupName = (parentFolders[i] as string)
groupColor = (color (i*50) 130 150)
newGroup = #("Button", groupName,groupColor, pos, 10, 70, 30, i)
append parentBtnDefs newGroup
--// Create children for each group
if sFiles.count > 0 do
(
cPos = 40
for n = 1 to sFiles.count do
(
ChildName = getFIleNameFile sFiles[n]
newChild = #("Button", ChildName,groupColor, pos, cPos, 70, 30, i)
append childBtnDefs newChild
cPos += 30
)
)
pos +=70
)
parentBtns = for b in parentBtnDefs collect (controlFactory b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8])
for b in parentBtns do dotnet.addEventHandler b "Click" parentClicked
childBtns = for b in childBtnDefs collect (controlFactory b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8])
for b in childBtns do dotnet.addEventHandler b "Click" childClicked
initializeForm()
),
init_trigger = initializeControls() --auto-initialize the controls on struct creation
)
::exampleDotNetTool = exampleDotNetToolStr()
A few suggestions:
-
groupColor = (color (i*50) 130 150) will only work for 5 groups or less, otherwise the red component will go over 255 and dotnet will raise an exception.
-
You will probably want to initialize all child buttons to be invisible at startup.
-
You can also move the first 3 global variables (dir, dirArray, parentFolders) into the struct. The nice thing about encompassing a dotnet tool in a struct is that you don’t pollute the global space and avoid collisions with other scripts. For example, leaving the variable ‘dir’ as a global variable is bound to get you in trouble at some point or another.
Thank you for overlooking that and giving the suggestions.
As far as the color goes I’m going to end up doing an array of colors because I’ll know ahead of time how many groups there will be and I’ll want to control it.
ex.
groupColor = #((color 74 134 230),(color 119 187 68),(color 203 73 73))--//Blue,Green,Red
I appreciate all your help with this lo.
I set the visible state right above the initializeForm() after it adds the event handler.
for b in childBtns do b.visible = false
As far as the global variables going into the structure that is a great point to make. It makes complete sense and a good mention.
I’m sure many people have used dir as a variable. I use it quite often.
Next would be connecting the actual script to the button. In doing so I would just store that along with everything else in the tag property correct?
newGroup = #(“Button”, groupName,groupColor[i], pos, 10, 70, 30, i, FilePath)
then when the tag is referenced for the visiblity property I’ll just change it to reference part one of the array in the tag.
b.tag = (groupVisibility,“c:\users\destop\scripts\Trees.ms”)