Notifications
Clear all

[Closed] MaxText to multiple strings

Hello

I’m relatively new to MaxScripting and I was wondering if someone could help me.
Is there a way to convert MaxText to multiple strings in editor? In my case, I would like to connect every row from MaxText to “keyframe name” and name should be printed in editbox below slider (just to check if is correct). In script I created a complicated way of this solution by adding single string for each name. I connected variable slider to sliderTime and all I want script to do is to select by hit MaxText node from maxscene (or select by name), print it in edittext box under the button, collect it and to convert every row to a specific string (that will be printed in editbox below slider) with removed first three symbols (“03” and “space” in, for example, “03 three-pose” to “three-pose”) –I want to remove those first three because that name will be in automated render save file image later in script.

I would be very grateful if someone could help me handle this.
Thanks in advance, the script is below.


try destroydialog ::TT catch()

Rollout TT "TimeText" width:120

(
	spinner spTT "TimeSlider" type:#integer range:[0,100,currentTime] align:#left width:100 height:20
	edittext nmTT readOnly:true align:#left offset:[0,-5] width:100 height:18

	button bnSelText "Select MaxText" align:#left height:30 width:97
	listbox list_text "" align:#left  height:5 width:97 across:2 readOnly:true enabled:true	
	
	on spTT changed ttime do
	(
		pose00 = "00 zero-pose"
		pose01 = "01 one-pose"
		pose02 = "02 two-pose"
		pose03 = "03 three-pose"
		pose04 = "04 four-pose"
		pose05 = "05 five-pose"
		pose06 = "06 six-pose"
		pose07 = "07 seven-pose"
		pose08 = "08 eight-pose"
		pose09 = "09 nine-pose"
		pose10 = "10 ten-pose"
	
	local p1 = 
		(
			if ttime == 0f then pose00 else
				(if ttime == 1f then pose01 else
					(if ttime == 2f then pose02 else
						(if ttime == 3f then pose03 else
							(if ttime == 4f then pose04 else
								(if ttime == 5f then pose05 else
									(if ttime == 6f then pose06 else
										(if ttime == 7f then pose07 else
											(if ttime == 8f then pose08 else
												(if ttime == 9f then pose09 else
													(if ttime == 10f then pose10 else
														(if ttime > 10f then "10+undefined-pose"
														)
													)
												)
											)
										)
									)
								)
							)
						)
					)
				)
		)

		nmTT.text = p1
		sliderTime = ttime --<<< when slider "TimeSlider" is changed value it changes "sliderTime" inside Max
	)
	
	on TT open do 
	(
		nmTT.text = "undefined" --<<< instead of undifined this should be current name for that frame
	)
	
		
	fn FilterShapes obj = isKindOf obj shape
on bnSelText pressed do
	(
		sel = selectByName title:"Pick Text" filter:FilterShapes showHidden:true
		if sel != undefined then
		(
			theObjects = sel
			list_text.items = for j in theObjects collect j.name --<<< instead of collecting and printing name of that MaxText, script should collect content text inside this MaxText
		)
	)		
		
		
/* 	00 zero-pose
 	01 one-pose
	02 two-pose
 	03 three-pose
	04 four-pose
 	05 five-pose
	06 six-pose
 	07 seven-pose
	08 eight-pose
 	09 nine-pose
	10 ten-pose
*/ --this should be inside MaxText in the scene
	
	
	
	
	
	
	)
createDialog TT
9 Replies

Oh my… You’re doing some things in a very contrived way. : )

Like declaring a separate variable for every string. What you should really look into is arrays; they’re for storing a list of stuff and getting it using indexes. Would solve the problem of having a new variable for every frame.

Also, it’s probably better you declare your array of poses in the rollout, and not in the “slider changed” block. What happens currently is you redeclare the variables again every time the slider is moved. And those variables are local to the “slider changed” block, so the “load” block doesn’t see them.

Unless I misunderstood your intentions, this part…

list_text.items = for j in theObjects collect j.name 
-- instead of collecting and printing name of that MaxText, script should collect content text inside this MaxText

…would simply be:

list_text.items = for j in theObjects collect j.text

As for the actual problem you are having… both your code and your explanation is a bit hard to follow.
There’s probably a better way of doing this. What is this for? What is the higher-level problem you are trying to solve? If you could simply and clearly describe what you need done, and how you want it to work, we can probably come up with a fitting simpler solution for it.

Thank you for response, Julius. Here I will post whole script and describe problem as close as I can.

I’m doing still poses (with biped) for a character with switchable face expressions and render them as separate: Head and body – on the same pose i can switch and render facial expressions defined by a diffuse map in separate material (number of facial expressions = standard materials in scene). This is for a game and those rendered images (body and few different expressions) are merged to a complete character. Later on, this will be animated with switchable faces with dialog text above and so on…
Scene is made from biped (and mesh skinned to it) MaxText with predefined poses for character formatted as (“pose number”, “(space)”, “pose name”) for example “04 welcoming” – pose number is also timeslider number in Max just to found specified poses more easily (it can be to 200 poses). Materials are node-based – standard, selfillumination:100, diffuse map (face expression). It can be more than 30 different expressions to switch on a face mesh so I want that to be automated with spinner somehow too. Temporarily I have setup for expressions using state sets (each set is different material) and it can be easily changed in between.

I’ve written a script with big help from Jorge Rodríguez here on this forum in thread: “MaxScript MATTE Global Override” Everything works perfectly, just as I wanted. Only thing that I want to do to finish the script is to add a function to those spinners on top of the script – pose and expressions. I want to collect text by hit or by list to a listbox, print them like you suggested, Julius, only difference is that has to separate it by rows with “
” or with “filterString” or with some other way to print poses one under another (just for 10 rows or less). And that kind of text to be connected to a “Pose” spinner as a string, I suppose, with first three symbols removed for example in “04 welcoming” to “welcoming” – and that string should be printed in edittext next to Pose spinner. When pose is named, text in this edittext is named in rendered .png under that name for example “welcoming.png” (which is already settled). The same thing is with expressions spinner – It should be a switchable materials in array with named materials in edittext – the same thing with rendering – only this time, in name of .png of expressions, it is named “pose name”, “-” “expression(material) name” for example “welcoming-happy.png” (this is also already settled) Also, the diffuse map name is the same name as it is material for example: happy.jpg as a diffuse map to a happy.material.

All that I want is to connect pose spinner to a variable strings by changing time/poses and expression spinner to expressions/materials. I hope that this makes more sense than previous post sry for bad English. The screenshots from scene are attached and the code for whole script is below. Thank you in advance and I hope that there must be some kind of solution for that situation.

try destroydialog ::CRM catch()

Rollout CRM "CRM" width:225
(
	group "Character Def"
		(
		spinner sp_Pose "Pose" type:#integer range:[0,200,currentTime] align:#left across:2 width:100 height:20
		spinner sp_Expression "Expression" type:#integer range:[0,20,1] align:#right width:100 height:20
		
		edittext tx_Pose readOnly:true align:#left across:2 offset:[0,-5] width:100 height:18
		edittext tx_Expression readOnly:true align:#right offset:[0,-5] width:100 height:18
		)
	group "Select and Location"
		(
		button bt_selectFolder "..." tooltip:"Pick path for render" align:#left width:30 offset: [0,0] across:2 height:18
		edittext tx_folder "" readonly:true align:#right height:18 width:165
		
		button bt_SelectGeometry "Select Geometry" align:#right height:30 width:97 across:2
		button bt_SelectCamera "Select Camera"   align:#left  height:30 width:97
		listbox list_geometry "" align:#left  height:5 width:97 across:2 readOnly:true enabled:true
		listbox list_cameras "" align:#right height:5 width:97
		button bnSelText "Select MaxText" align:#left height:30 width:97
		listbox list_text "" align:#left  height:5 width:97 readOnly:true enabled:true	
			
		label rend_option "Render:" align:#left across:2
		radiobuttons rad_options "" labels:#("Pose","Expression") align:#left offsets:#([-50,0],[-40,0])
		)
	group "Update - Render"
		(
		button bt_update "Update" align:#left across:2 width: 97 height:30
		button bt_render "Render" align:#right width:97 height:30
		)
	
	local matteMaterial
	local theObjects = #()
	local theCameras = #()
	local outputFolder = undefined
	
	local p1 = "pose" + sliderTime as string --<<<this should be a variable string for each pose or something that will cycle poses in MaxText that is selected and convert them to strings
	local e1 = "expression1" --<<<this should me material cycle that will print material name
		
	on CRM open do
	(
		matteMaterial = Matte_Shadow_Reflection__mi catch_shadows:false ao_on:false catch_reflections:false
		
		tx_Pose.text = p1
		tx_Expression.text = "exp0"
	)
	
	fn FilterGeometry obj = isKindOf obj geometryClass and not isKindOf obj Targetobject --<<<it should list only geometry objects but it also lists bones in selectByName window for some reason
	fn FilterCameras  obj = isKindOf obj camera
	fn FilterShapes obj = isKindOf obj shape	
	
	on sp_Pose changed ttime do
	(
		p1 = if ttime >0 then "poza" + sliderTime as string
		tx_Pose.text = p1 as string
		sliderTime = ttime
	)
	
	on sp_Expression changed ttime do
	(
		tx_Expression.text = e1
	)
	
	on bt_SelectGeometry pressed do
	(
		sel = selectByName title:"Pick" filter:FilterGeometry showHidden:true
		if sel != undefined then
		(
			theObjects = sel
			list_geometry.items = for j in theObjects collect j.name
		)else(
			list_geometry.items = #()
			theObjects = #()
		)
	)

	on bt_SelectCamera pressed do
	(
		sel = selectByName title:"Pick" filter:FilterCameras showHidden:true
		if sel != undefined then
		(
			theCameras = sel
			list_cameras.items = for j in theCameras collect j.name
		)else(
			list_cameras.items = #()
			theCameras = #()
		)
	)

	
	
	on bnSelText pressed do
	(
		sel = selectByName title:"Pick Text" filter:FilterShapes showHidden:true
		if sel != undefined then
		(
			theObjects = sel
			list_text.items = for j in theObjects collect j.text --<<< instead of printing text in one row it should be separated in each line below
		)
	)		
	
	
	
	on bt_selectFolder pressed do
	(
		outputFolder = getSavePath()
		if outputFolder != undefined then tx_folder.text = outputFolder else tx_folder.text = "undefined"
	)
	
	on bt_render pressed do 
	(
		if outputFolder == undefined do return messagebox "No Output Folder Selected"
		if theObjects.count == 0 do return messagebox "No Geometry Selected"
		if theCameras.count == 0 do return messagebox "No Camera Selected"
		
		setwaitcursor()
		
	
		cam = theCameras[list_cameras.selection]
		
		case rad_options.state of
		(
			1:
			(
				matteObjects = theObjects
				filename = outputFolder + "\\" + p1 + ".png"
			)
			2:
			(
				matteObjects = for j in geometry where finditem theObjects j == 0 collect j
				filename = outputFolder + "\\" + p1 + "-" + e1 + ".png"
			)
		)
		

		oldMaterials = for j in matteObjects collect #(j, j.material)
		
		matteObjects.material = matteMaterial
		
		render camera:cam vfb:false outputwidth:128 outputheight:256 outputfile:filename
		
		for j in oldMaterials do j[1].material = j[2]
		
		messagebox ("Image Rendered to:
" + filename)
		
		setarrowcursor()
	)
)

createdialog CRM

script_problem_02

wonder why don’t you just parse some text file for those pose names?

on bnSelText pressed do
	(
		sel = selectByName title:"Pick Text" filter:FilterShapes showHidden:true
		if sel != undefined then
		(
			theObjects = sel
			
			local arr = #()
			for s in theObjects do (
				
				for w in filterstring s.text "
" do append arr (substring w 4 (w.count - 2))
				
			)
			list_text.items = arr
			
		)
	)

Thank you for reply, Panteleev. And thank you for the code solution, it parsed text and removed first three symbols just like I wanted. Now I need to convert that parsed text in list_text.items into multiple strings or as a variable string if it is possible so they can be showed in tx_Expression edittext when changing spinner for poses. I’m still searching a way to do this and watching many tutorials on MaxScript but can’t find any similar problem or a solution to that. I attached 2 more images to simplify as close as I can.

004

maxscript arrays 1,2

on sp_Pose changed ttime do
(
	p1 = if ttime > 0 then "poza" + sliderTime as string
	tx_Pose.text = p1 as string
	sliderTime = ttime

	if ttime > 0 then (
		tx_Expression.text = if list_text.items[ ttime ] != undefined then list_text.items[ttime] else ""
	) else (
		tx_Expression.text = ""		
	)
)

on sp_Expression changed ttime do
(		
	if ttime > 0 then (
		tx_Expression.text = if list_text.items[ ttime ] != undefined then list_text.items[ttime] else ""
	) else (
		tx_Expression.text = ""		
	)
)

It worked like a charm, thank you, Panteleev …and for the links, I’ll study those arrays more through and test them in various ways as soon as possible. Like I said, I’m still a newbie in scripting but little by little I’ll figure it out.
I have one more problem to solve, looks very simple but still can’t figure it out. When rendering to a image it appears to be a “space” between pose name and extension “.png” I’ve tried trimRight, replace, and token commands on it but none of that is working… here is the screenshot:

space_problem

And here is the full script below. Now it is 90% finished, all I need to do is to connect material spinner to cycle through materials somehow and it’s done. I’ll figure it out somehow, there’s weekend ahead

try destroydialog ::CRM catch()

Rollout CRM "CRM" width:225
(
	group "Character Def"
		(
		spinner sp_Pose "Pose" type:#integer range:[0,200,currentTime] align:#left across:2 width:100 height:20
		spinner sp_Material "Material" type:#integer range:[0,20,1] align:#right width:100 height:20
		
		edittext tx_Pose readOnly:true align:#left across:2 offset:[0,-5] width:100 height:18
		edittext tx_Material readOnly:true align:#right offset:[0,-5] width:100 height:18
		)
	group "Select and Location"
		(
		button bt_selectFolder "..." tooltip:"Pick path for render" align:#left width:30 offset: [0,0] across:2 height:18
		edittext tx_folder "" readonly:true align:#right height:18 width:165
		
		button bt_SelectGeometry "Select Geometry" align:#right height:30 width:97 across:2
		button bt_SelectCamera "Select Camera"   align:#left  height:30 width:97
		listbox list_geometry "" align:#left  height:5 width:97 across:2 readOnly:true enabled:true
		listbox list_cameras "" align:#right height:5 width:97
		button bt_SelectText "Select MaxText" align:#left height:30 width:97
		listbox list_text "" align:#left  height:5 width:97 readOnly:true enabled:true	
			
		label rend_option "Render:" align:#left across:2
		radiobuttons rad_options "" labels:#("Pose","FaceExpression") align:#left offsets:#([-50,0],[-40,0])
		)
	group "Update - Render"
		(
		button bt_update "Update" align:#left across:2 width: 97 height:30
		button bt_render "Render" align:#right width:97 height:30
		)
	
	local matteMaterial
	local theObjects = #()
	local theCameras = #()
	local outputFolder = undefined
			
	on CRM open do
	(
		matteMaterial = Matte_Shadow_Reflection__mi catch_shadows:false ao_on:false catch_reflections:false
		
		tx_Pose.text = "undefined"
		tx_Material.text = "undefined"
	)
	
	fn FilterGeometry obj = isKindOf obj geometryClass and not isKindOf obj Targetobject --<<<it should list only geometry objects but it also lists bones in selectByName window for some reason
	fn FilterCameras  obj = isKindOf obj camera
	fn FilterShapes obj = isKindOf obj shape	
	
	on sp_Pose changed ttime do
(
	sliderTime = ttime
	
		if ttime > 0 then (
			tx_Pose.text = if list_text.items[ ttime ] != undefined then list_text.items[ttime] else ""
		) else (
			tx_Pose.text = ""		
		)

)

	
	on bt_SelectGeometry pressed do
	(
		sel = selectByName title:"Pick" filter:FilterGeometry showHidden:true
		if sel != undefined then
		(
			theObjects = sel
			list_geometry.items = for j in theObjects collect j.name
		)else(
			list_geometry.items = #()
			theObjects = #()
		)
	)

	on bt_SelectCamera pressed do
	(
		sel = selectByName title:"Pick" filter:FilterCameras showHidden:true
		if sel != undefined then
		(
			theCameras = sel
			list_cameras.items = for j in theCameras collect j.name
		)else(
			list_cameras.items = #()
			theCameras = #()
		)
	)

	
	
	on bt_SelectText pressed do
	(
		sel = selectByName title:"Pick Text" filter:FilterShapes showHidden:true
		if sel != undefined then
		(
			theObjects = sel
			
			local arr = #()
			for s in theObjects do (
				
				for w in filterstring s.text "
" do append arr (substring w 4 (w.count - 2))
				
			)
			list_text.items = arr
			
		)
	)		
	
	
	
	on bt_selectFolder pressed do
	(
		outputFolder = getSavePath()
		if outputFolder != undefined then tx_folder.text = outputFolder else tx_folder.text = "undefined"
	)
	
	on bt_render pressed do 
	(
		if outputFolder == undefined do return messagebox "No Output Folder Selected"
		if theObjects.count == 0 do return messagebox "No Geometry Selected"
		if theCameras.count == 0 do return messagebox "No Camera Selected"
		
		setwaitcursor()
		
	
		cam = theCameras[list_cameras.selection]
		rendName = tx_Pose.text
		
		case rad_options.state of
		(
			1:
			(
				matteObjects = theObjects
				filename = outputFolder + "\\" + rendName + ".png"
			)
			2:
			(
				matteObjects = for j in geometry where finditem theObjects j == 0 collect j
				filename = outputFolder + "\\" + rendName + "-" + "material_name" + ".png"
			)
		)
		

		oldMaterials = for j in matteObjects collect #(j, j.material)
		
		matteObjects.material = matteMaterial
		
		render camera:cam vfb:false outputwidth:128 outputheight:256 outputfile:filename
		
		for j in oldMaterials do j[1].material = j[2]
		
		--messagebox ("Image Rendered to:
" + filename)
		
		setarrowcursor()
	)
)

createdialog CRM

Thanks everyone! I have figured out almost everything thanks to cgsociety and scriptspot forums and now I have 99% of script settled. I have one more issue to solve and still can’t figure it out… I want my sp_Material spinner to instantly change and assign those materials (which are collected from scene and in array and listed) to selected mesh or, if it is possible, let Max to found mesh in which has a name in it (“Head” in “MESH_Head_01” – that would be fantastic) and make that as a selection (just to avoid manually selecting in scene) I tried to access the material index via spinner.value but i got “–No ““get”” function for undefined” error. Here is the code and screenshot is in the attachment.

try destroydialog ::CRM catch()
 
Rollout CRM "CRM" width:225
(
	group "Character Def"
		(
		spinner sp_Pose "Pose" type:#integer range:[0,200,currentTime] align:#left across:2 width:100 height:20
		spinner sp_Material "Material" type:#integer range:[0,20,1] align:#right width:100 height:20
 
		edittext tx_Pose readOnly:true align:#left across:2 offset:[0,-5] width:100 height:18
		edittext tx_Material readOnly:true align:#right offset:[0,-5] width:100 height:18
		)
	group "Select and Location"
		(
		button bt_selectFolder "..." tooltip:"Pick path for render" align:#left width:30 offset: [0,0] across:2 height:18
		edittext tx_folder "" readonly:true align:#right height:18 width:165
 
		button bt_SelectGeometry "Select Geometry" align:#right height:30 width:97 across:2
		button bt_SelectCamera "Select Camera"   align:#left  height:30 width:97
		listbox list_geometry "" align:#left  height:5 width:97 across:2 readOnly:true enabled:true
		listbox list_cameras "" align:#right height:5 width:97
		button bt_SelectText "Select MaxText" align:#left height:30 width:97 across:2
		button bt_SelectMaterials "Collect Materials" align:#right height:30 width:97
		listbox list_text "" align:#left  height:5 width:97 readOnly:true across:2 enabled:true 	
		listbox list_materials "" align:#right height:5 width:97 readonly:true enabled:true
 
		label rend_option "Render:" align:#left across:2
		radiobuttons rad_options "" labels:#("Pose","FaceExpression") align:#left offsets:#([-50,0],[-40,0])
		)
	group "Update - Render"
		(
		--button bt_update "Update" align:#left across:2 width: 97 height:30
		button bt_render "Render" border:false align:#center width:206 height:256
		)
 
	local matteMaterial
	local theObjects = #()
	local theCameras = #()
	local outputFolder = undefined
 
	local tpose = "t-pose"
 
	on CRM open do
	(
		matteMaterial = Matte_Shadow_Reflection__mi catch_shadows:false ao_on:false catch_reflections:false
 
		tx_Pose.text = tpose
		tx_Material.text = "undefined"
	)
 
	fn FilterGeometry obj = isKindOf obj geometryClass and not isKindOf obj Targetobject --<<<it should list only geometry objects but it also lists bones in selectByName window for some reason
	fn FilterCameras  obj = isKindOf obj camera
	fn FilterShapes obj = isKindOf obj shape	
 
	on sp_Pose changed ttime do
	(
 
	sliderTime = ttime
 
	if ttime > 0 then 
	(
		tx_Pose.text = if list_text.items[ ttime+1 ] != undefined then list_text.items[ttime+1] else ""
	) 
	else if ttime == 0 then 
	(
		tx_Pose.text = "t-pose"		
	)
	)
 
 
	on sp_Material changed FEx do
	(
		if sp_Material.value > 0 then (
		tx_Material.text = if list_materials.items[sp_Material.value] != undefined then list_materials.items[sp_Material.value] else ""
	) else (
		tx_Material.text = ""		
	)
 
	FEx = theMaterials[sp_Material.value]
	face = $
	face.material = FEx showInViewport:true
 
	)
 
 
	on bt_SelectGeometry pressed do
	(
		sel = selectByName title:"Pick" filter:FilterGeometry showHidden:true
		if sel != undefined then
		(
			theObjects = sel
			list_geometry.items = for j in theObjects collect j.name
		)else(
			list_geometry.items = #()
			theObjects = #()
		)
	)
 
	on bt_SelectCamera pressed do
	(
		sel = selectByName title:"Pick" filter:FilterCameras showHidden:true
		if sel != undefined then
		(
			theCameras = sel
			list_cameras.items = for c in theCameras collect c.name
		)else(
			list_cameras.items = #()
			theCameras = #()
		)
	)
 
 
 
	on bt_SelectText pressed do
	(
		sel = selectByName title:"Pick Text" filter:FilterShapes showHidden:true
		if sel != undefined then
		(
			theObjects = sel
 
			local arr = #()
			for s in theObjects do (
 
				for w in filterstring s.text "
" do append arr (substring w 4 (w.count - 2))
 
			)
			list_text.items = arr
 
		)
	)		
 
 
 
	on bt_selectFolder pressed do
	(
		outputFolder = getSavePath()
		if outputFolder != undefined then tx_folder.text = outputFolder else tx_folder.text = "undefined"
	)
 
 
 
	local theMaterials = #()
	on bt_SelectMaterials pressed do
	(
		viewNode = sme.GetView (sme.activeView)
		smeViewMats = #()
		for n = 1 to trackViewNodes[#sme][(sme.activeView)].numSubs do 
		(
			m = trackViewNodes[#sme][(sme.activeView)][n].reference
			if superclassof m == material do append smeViewMats m
		)
 
		local selMats = smeViewMats
		if selMats != undefined then
		(
			theMaterials = selMats
			list_materials.items = for m in theMaterials collect m.name
 
		)
		else
		(
			list_materials.items = #()
			theMaterials = #()
		)
 
	)
 
 
 
 
	on bt_render pressed do 
	(
		if outputFolder == undefined do return messagebox "No Output Folder Selected"
		if theObjects.count == 0 do return messagebox "No Geometry Selected"
		if theCameras.count == 0 do return messagebox "No Camera Selected"
 
		setwaitcursor()
 
 
	cam = theCameras[list_cameras.selection]
 
	rendName = tx_Pose.text as string
	local poseName = rendName = substring rendName 1 (rendName.count - 1) 
 
 
		case rad_options.state of
		(
			1:
			(					
				matteObjects = theObjects
				filename = outputFolder + "\\" + poseName + ".png"
			)
			2:
			(
				matteObjects = for j in geometry where finditem theObjects j == 0 collect j
				filename = outputFolder + "\\" + poseName + "-" + "material_name" + ".png"
			)
		)
 
 
		oldMaterials = for j in matteObjects collect #(j, j.material)
 
		matteObjects.material = matteMaterial
 
		fnRender = render camera:cam vfb:false outputwidth:128 outputheight:256 outputfile:filename
		bt_Render.images = #(fnRender, undefined, 1,1,1,1,1 )
		fnRender
 
		for j in oldMaterials do j[1].material = j[2]
 
		--messagebox ("Image Rendered to:
" + filename)
 
		setarrowcursor()
	)
)
 
createdialog CRM

material

check this and this . quite simple things

Thank you for reply! I’ve finished script and it’s finally working.
Cheers