cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Check out the JMP® Marketplace featured Capability Explorer add-in
Choose Language Hide Translation Bar
mikedriscoll
Level VI

Variable behavior in JSL functions

 

My scripts generally have two main components: A main function followed by the GUI code, all in the same file. I typically use variables that are contained within the Here namespace, get the variables from user input, and call the main function from an 'OK' button box. I don't pass the main function any variables, and just use the variables that were assigned upon the 'OK' button box script. I am considering changing at least one of my scripts so that there is one file with the GUI, and a second file with the main function. Before I do that I am checking how that works within one JSL file. Below is a stripped down example, bypassing the GUI and just defining variables after the function is defined but before it is called.

 

One thing I've noticed is that it is harder to debug after a script run, because when I hover over the x and y I can see the contents in the status bar at the bottom of the window. But this is not so for the local variables A, B, Z and nw. I wonder if anyone has any tips on this.

 

I'm also not sure how to access the variable contents from the button box script. If you run the code below you can see that A, B, Z show up in the log as expected (from just before the new window is created), and B and Z also show up in the new window text boxes, but the button box script cannot access these variables directly. I was able to use the eval(parse(eval insert())) to get the array B but I haven't been able to get the string Z.  Are there more direct methods for getting this to work?  

names default to here(1);
myFun = Function({A, Z},{Default Local},
	A[2] = 10;
	B = 2*A;
	show (A);
	show(B);
	show(Z);
	
	nw = new window("test", 
		vlistbox(
			textbox(Z),
			textbox(char(B)),
			buttonbox("Show array B (works)", 
				show(parse(Eval(Parse(Eval Insert(
						"\[^"char(B)"^]\"
				)))))
			),			
			buttonbox("Show string Z (does not work)", 
				show(Eval(Parse(Eval Insert(
						"\[^"char(Z)"^]\"
				))))
			),			
		
			buttonbox("show and close (does not work)", show(Z, B); nw << closewindow())
		)
	)
);
x = [2,3];
y="string";
myFun(x,y);

Thanks,
Mike

1 ACCEPTED SOLUTION

Accepted Solutions
vince_faller
Super User (Alumni)

Re: Variable behavior in JSL functions

The problem if you do it with globals or global namespace is reusability (which you may not care about).  

if you run myFun twice with two different buttons (lets say you want to be able to sandbox your window and run two instances of it).  You'll have collision and the second call will override the firsts. 

 

See below. 

names default to here(1);
new namespace("test");
test:myFun = Function({A1, Z1},{Default Local},
	test:A = A1;
	test:Z = Z1;
	test:A[2] = 10;
	test:B = 2*test:A;
	show(test:A);
	show(test:B);
	show(test:Z);
	//::g_b = b;
	//::g_z = z;
	test:nw = new window("test", 
		vlistbox(
			textbox(test:Z),
			textbox(char(test:B)),
			buttonbox("Show array B (works)", 
				show(test:B)
			),
			buttonbox("Show string Z (works)", 
				show(test:Z)
			),			
		
			buttonbox("show and close (works)", 
				show(test:Z, test:B); test:nw << closewindow())
		)
	)
);
x = [2,3];
y ="string";
test:myFun(x,y);
test:myFun([12, 14], "something else");

if you're using a namespace, I recommend using the window namespace. It automatically deletes when the window is gone and you won't have collision between two identical windows.  

 

names default to here(1);
myFun = Function({A1, Z1},{DEFAULT LOCAL},
		A = A1;
		Z = Z1;
		A[2] = 10;
		B = 2*A;
		show(A);
		show(B);
		show(Z);
		//::g_b = b;
		//::g_z = z;
		nw = new window("test", 
			window:B = B; //uses the builtin window namespace
			window:Z = Z; //works because this is a glue statement that returns the last thing
			window:box = vlistbox( //which in this case is the vlistbox
				textbox(window:Z),
				textbox(char(window:B)),
				buttonbox("Show array B (works)", 
					show(window:B)
				),
				buttonbox("Show string Z (works)", 
					show(window:Z)
				),			
			
				buttonbox("show and close (works)", 
					show(window:Z, window:B); window:box << closewindow())
			)
			
		);
);
x = [2,3];
y ="string";
myFun(x,y);
myFun([12, 14], "something else");
Vince Faller - Predictum

View solution in original post

9 REPLIES 9
vince_faller
Super User (Alumni)

Re: Variable behavior in JSL functions

B didn't even work for me.  

This is normally how I do GUI stuff.  It's overly complicated for the example you gave but I think it shows a couple different methods.  

 


names default to here(1);
myFun = Function({A, Z},{Default Local},
	A[2] = 10;
	B = 2*A;
	Eval(
		Substitute(
			Expr(
				nw = new window("test", 
					vlistbox(
						textbox(char(DV_Z)),
						textbox(char(DV_B)),
						buttonbox("Show array B (works)", 
							show([]||DV_B); //otherwise you run into an invalid matrix token
						),			
						buttonbox("Show string Z (does not work)", 
							//show([]||DV_Z); //shouldn't work, you can't make a string a matrix
							show(DV_Z);
							//or
							show(list(DV_Z));
						),			
					
						buttonbox("show and close (does not work)", 
							<<Set Function( 
								Function({self},
									{DEFAULT LOCAL},
									bb3_press(self, DV_Z, DV_B);
									//or you could put the close in here
									//self << close window();
								)
							)
						)
								
					)
				)
			), 
			//what we substitute
			Expr(DV_Z), Z, 
			Expr(DV_B), B
		)
		
	);
);
x = [2,3];
y="string";
myFun(x,y);

bb3_press = Function({self, Z, B}, 
	{DEFAULT LOCAL}, 
	show(Z, B); 
	self << closewindow();
);
Vince Faller - Predictum
pmroz
Super User

Re: Variable behavior in JSL functions

I had to resort to setting global variables to make this work.  It seems that the dialog box code can't see the variables properly.  This isn't a great solution because it defeats the purpose of having local variables inside a function.

names default to here(1);
myFun = Function({A, Z},{Default Local},
	A[2] = 10;
	B = 2*A;
	show(A);
	show(B);
	show(Z);
	::g_b = b;
	::g_z = z;
	::g_nw = new window("test", 
		vlistbox(
			textbox(Z),
			textbox(char(B)),
			buttonbox("Show array B (works)", 
				show(::g_B)
			),
			buttonbox("Show string Z (does not work)", 
				show(::g_Z)
			),			
		
			buttonbox("show and close (does not work)", 
				show(::g_Z, ::g_B); ::g_nw << closewindow())
		)
	)
);
x = [2,3];
y ="string";
myFun(x,y);
mikedriscoll
Level VI

Re: Variable behavior in JSL functions

Thanks, your global idea gave me another idea.  I don't mind so much the fact that the variables are global, but if they do need to be global I'd rather scope them into a contained namespace.

 

I don't think the clear globals() command would bite me here but it got me with my recall functions (my global recall variables got cleared by another script using clear globals()).  I had emailed JMP and they suggested two alternatives:  protect the global variables by locking them or putting them in their own namespace. The namespace was actually more straightforward in terms of robustness.

 

Anyhow, the updated code below seems to work ok. The downside is extra typing "test:" for every time I reference a variable. I realize I may not need it for every variable use in the script but on the other hand, it probably is good practice and because this particular use is a global namespace, I can see the variable contents by hovering my cursor over the variable names.

 

Note the use of A1 and Z1. It didn't let me assign to test:A and test:Z.

Error: argument should be list in access or evaluation of 'Function' , Function/*###*/({test:A, test:Z}, .....

 

I'll see if I can get this working with multiple files.

 

new namespace("test");
test:myFun = Function({A1, Z1},{Default Local},
	test:A = A1;
	test:Z = Z1;
	test:A[2] = 10;
	test:B = 2*test:A;
	show(test:A);
	show(test:B);
	show(test:Z);
	//::g_b = b;
	//::g_z = z;
	test:nw = new window("test", 
		vlistbox(
			textbox(test:Z),
			textbox(char(test:B)),
			buttonbox("Show array B (works)", 
				show(test:B)
			),
			buttonbox("Show string Z (works)", 
				show(test:Z)
			),			
		
			buttonbox("show and close (works)", 
				show(test:Z, test:B); test:nw << closewindow())
		)
	)
);
test:x = [2,3];
test:y ="string";
test:myFun(x,y);
mikedriscoll
Level VI

Re: Variable behavior in JSL functions

Thanks Vince. I hadn't thought the eval(substitute(expr())). For some of my more complex scripts I think it might be too difficult to keep track of what was being substituted and where. My code is not exactly well organized and debug would be tough.
vince_faller
Super User (Alumni)

Re: Variable behavior in JSL functions

The problem if you do it with globals or global namespace is reusability (which you may not care about).  

if you run myFun twice with two different buttons (lets say you want to be able to sandbox your window and run two instances of it).  You'll have collision and the second call will override the firsts. 

 

See below. 

names default to here(1);
new namespace("test");
test:myFun = Function({A1, Z1},{Default Local},
	test:A = A1;
	test:Z = Z1;
	test:A[2] = 10;
	test:B = 2*test:A;
	show(test:A);
	show(test:B);
	show(test:Z);
	//::g_b = b;
	//::g_z = z;
	test:nw = new window("test", 
		vlistbox(
			textbox(test:Z),
			textbox(char(test:B)),
			buttonbox("Show array B (works)", 
				show(test:B)
			),
			buttonbox("Show string Z (works)", 
				show(test:Z)
			),			
		
			buttonbox("show and close (works)", 
				show(test:Z, test:B); test:nw << closewindow())
		)
	)
);
x = [2,3];
y ="string";
test:myFun(x,y);
test:myFun([12, 14], "something else");

if you're using a namespace, I recommend using the window namespace. It automatically deletes when the window is gone and you won't have collision between two identical windows.  

 

names default to here(1);
myFun = Function({A1, Z1},{DEFAULT LOCAL},
		A = A1;
		Z = Z1;
		A[2] = 10;
		B = 2*A;
		show(A);
		show(B);
		show(Z);
		//::g_b = b;
		//::g_z = z;
		nw = new window("test", 
			window:B = B; //uses the builtin window namespace
			window:Z = Z; //works because this is a glue statement that returns the last thing
			window:box = vlistbox( //which in this case is the vlistbox
				textbox(window:Z),
				textbox(char(window:B)),
				buttonbox("Show array B (works)", 
					show(window:B)
				),
				buttonbox("Show string Z (works)", 
					show(window:Z)
				),			
			
				buttonbox("show and close (works)", 
					show(window:Z, window:B); window:box << closewindow())
			)
			
		);
);
x = [2,3];
y ="string";
myFun(x,y);
myFun([12, 14], "something else");
Vince Faller - Predictum
mikedriscoll
Level VI

Re: Variable behavior in JSL functions

Thanks Vince. I didn't know about the window namespace. That might work well, I will try it.

 

Right now I've got my GUI part going in one file, and I'm intending to pass the variables to what i'll call the main function. I would either call the GUI from an addin toolbar, or run a small custom script which would loop through N times, calling the main script for N sets of input parameters. Resusability could be an issue here but overwriting variables may not matter... I'll see how it goes. Thanks for your help.

 

 

 

 

gzmorgan0
Super User (Alumni)

Re: Variable behavior in JSL functions

I was going to suggest using the Window Namespace which is  a good suggestion.  I usually do not use functions for UI windows.  Another method to refer to local objects is to use the Set Function and get a handle on this. The script is below.  I used simpler print statements.  You might want to explore using the Application Builder, it takes a little investment to learn how it works, but it can be handy.

names default to here(1);
myFun = Function({A, Z},{Default Local},
	A[2] = 10;
	B = 2*A;
	show (A);
	show(B);
	show(Z);
	
	nw = new window("test", 
		vlistbox(
			textbox(Z),
			textbox(char(B)),
			buttonbox("Show array B (works)", 
				<<Set Function(Function({this}, {vl},
				  vl = (this << parent);
				  show(tb = vl[textBox(2)] << get text);
				  
			  ) )
			),			
			buttonbox("Show string Z (does not work)", 
				<<Set Function(Function({this}, {vl},
				  vl = (this << parent);
				  show(tb = vl[textBox(1)] << get text);
			  ) )
		    ),			
		
			buttonbox("show and close (does not work)", 
				<<Set Function(Function({this}, {vl, Z, B},
				  vl = (this << parent);
				  Z = vl[textBox(1)] << get text;
				  B = vl[textBox(2)] << get text;
				  
				  show(Z, B); 
				  (vl<<parent) << close window  
			  ) )
			),
		)
	)
);
x = [2,3];
y="string";
myFun(x,y);
gzmorgan0
Super User (Alumni)

Re: Variable behavior in JSL functions

oops!  Forgot to mention, instead of "this" and window namespaces you can also just refer to the current window.  I attached the alternate script,

mikedriscoll
Level VI

Re: Variable behavior in JSL functions

gzmorgan, Thanks for the insight. The current window() solution looked like it might be the simplest solution but it didn't work for me... at least, not the way I tried to code it. I tried to use mostly the eval sub() command for most of the variables, which works, but doesn't work for the nw (my new window variable) or _cw for current window. I probably just missed something, but it's ok.

 

I ended up going back to your other suggestion, which Vince had suggested earlier, the window namespace. I haven't had any problems with it so far.

 

Regarding applicaiton builder, I'm aware of it but haven't used it. I generally just code my own GUI and functions, and create the addin manually. I had a hard time understanding the set function and this (or self) and what was calling what, but I think I see now... I'll take a closer look.

 

Thanks everyone for the inputs. I really appreciate the different approaches / suggestions. My script takes a set of related parameters and plots them a few different ways, but it takes a couple minutes to set it up and manually click the parameters and then export it. With these updates, and looping from a master file I can call the script from a separate calling script instead of the GUI, and have it generate not one set of plots but N plots. It used to take me a couple hours... now it is ten seconds.  of couse it took me a long time to get here but I can reuse this for years.