cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Try the Materials Informatics Toolkit, which is designed to easily handle SMILES data. This and other helpful add-ins are available in the JMP® Marketplace
Choose Language Hide Translation Bar
ThomasB
Level II

Dealing with "On Change" within a Class in JSL

Hello community!

 

I got a bit stuck while using Classes, so I'm hoping someone could help me out.

 

A bit of context: I am working on a script which is divided into two: a main script and a functions script, where the latter is then included into the main script. I am trying to work with Classes and Namespaces, to make sure variables are only found when they are supposed to be found. My specific problem comes when trying to use the "On Change" function with a Col List Box.

 

I made a minimal working example that illustrates my problem. Consider the main code:

Names Default To Here(1);
Clear log(); Clear Globals(); Clear Symbols();

// Include the class script
If( !Namespace Exists( "mainNamespace" ), New Namespace( "mainNamespace" ) );
mainNamespace:dir = Get Default Directory();
Include(mainNamespace:dir || "classScript.jsl");

// Create an object of the class and try to use the function
testObject = New Object( exampleClass());
testObject:method1();

and then the class script:

Names Default To Here(1);
If( !Namespace Exists( "classNamespace" ), New Namespace( "classNamespace" ) );

Define Class("exampleClass",

	_init_ = Method( {},
		this:BC = Open( "$SAMPLE_DATA/Big Class.jmp", Show(1) );
		this:window = Empty(); 
	);
	
	method1 = Method({},
		this:window = New Window( "Test Window", clb = Col List Box( all, On Change( Show( clb << Get Items ) ) ) );
	);
	
);

The idea is to have a window open where you can select some columns from a data table, and then get a list with the columns selected. This works fine when I don't use Classes, i.e., if I just write the classes part of the script as "normal" code. The code above manages to open up the Big Class data table, and the window with the Col List Box, but whenever I click on a column, I get an error: 

 

Send Expects Scriptable Object in access or evaluation of 'Send', clb << /*###*/Get Items /*###*/

 

Is there something I'm doing wrong, or not understanding? Or is it something to do with how "On Change" is programmed? Any help is appreciated!

 

 

4 REPLIES 4
ThomasB
Level II

Re: Dealing with "On Change" within a Class in JSL

Actually, the error message I'm getting is:

Name Unresolved: clb in access or evaluation of 'clb', clb/*###*/

sorry about that!

 

Craige_Hales
Super User

Re: Dealing with "On Change" within a Class in JSL

I think the class members have a default local mechanism, whether you want it or not. In

method1 = Method({},
		this:window = New Window( "Test Window", clb = Col List Box( all, On Change( Show( clb << Get Items ) ) ) );
	);

clb is assigned when method1 is running, then forgotten as method1 returns. Much later, when the OnChange method runs, it can't be found. If you want a quick, imperfect fix, make clb more global.

 

A better, but uglier fix requires embedding clb's value in place of clb.

Define Class( "exampleClass",
	// create the instance variables
	m_clb = .;
	m_window = .;
	m_BC = .;
	_init_ = Method( {},
		m_BC = Open( "$SAMPLE_DATA/Big Class.jmp" );
		m_window = Empty();
	);
	method1 = Method( {},
		m_clb = Col List Box( all );
		// add the on change script after creating the box. The script needs
		// to reference the box. Substitute the actual box that m_clb refers
		// to for the xxx placeholder name, then evaluate the resulting script
		Eval( Substitute(
			Expr( m_clb << onChange( Show( xxx << getselected ) ) ),
			Expr( xxx ), 
			m_clb
		));
		m_window = New Window( "Test Window", m_clb );
	);
);

// Create an object of the class and try to use the function
testObject = New Object( exampleClass() );
testObject:method1();

 

this probably doesn't do what you expect. There is no equivalent to a C++ this-> and you'll have a hard time inventing it without creating a memory leak. In the example above, it would be even better to embed this rather than clb, but you need to invent a way to get this.

I wrote two blogs about classes, How to use Define Class  and Functional programming using JSL objects  . I've tried to use classes several times and never been satisfied. One thing that a class does really well is passing by reference, and that may be the best reason to use them. Wrapping a large array in a class instance makes it possible to pass the array to user written functions without making a copy of the array.

 

And if only the <<OnChange call back received a this parameter, pointing to the box, this would be so much easier.

Craige
jthi
Super User

Re: Dealing with "On Change" within a Class in JSL

<< Set Function might be a good option to use instead of << On Change, as it has "this" as function argument. Depending what you are trying to do in the end, I would say there are at least three different options

1. Do what Craige suggest and evaluate the reference to the Col List Box

2. Use << Set Function and then you can reference this (which will refer to the col list box itself). These do have a bit different behaviors but it might affect your application

Define Class( "exampleClass",
	m_clb = Empty();
	m_window = .;
	m_BC = .;
	_init_ = Method( {},
		m_BC = Open( "$SAMPLE_DATA/Big Class.jmp" );
		m_window = Empty();
	);
	method1 = Method( {},
		m_window = New Window("Test Window", 
			m_clb = Col List Box(
				Datatable(m_BC),
				all,
				<< Set Function(function({this},
					Show(this << get selected)
				))
			)
		);
	);
);

// Create an object of the class and try to use the function
testObject = New Object( exampleClass() );
testObject:method1();

3. Use window scope ( Scripting Guide > Programming Methods > Advanced Scoping and Namespaces > Scoped Names )

Define Class( "exampleClass",
	m_window = .;
	m_BC = .;
	_init_ = Method( {},
		m_BC = Open( "$SAMPLE_DATA/Big Class.jmp" );
		m_window = Empty();
	);
	method1 = Method( {},
		m_window = New Window("Test Window", 
			window:m_clb = Col List Box(
				Datatable(m_BC),
				all,
				<< onChange(Show(window:m_clb << Get Selected))
			)
		);
	);
);

// Create an object of the class and try to use the function
testObject = New Object( exampleClass() );
testObject:method1();
-Jarmo
bculver
Level II

Re: Dealing with "On Change" within a Class in JSL

This is an older thread, but I wanted to post one more solution that I think is very handy when dealing with windows inside classes. Similar to Jarmo's solution, you can use the << Set Function message, but use a Method definition. It has approximately the same functionality as a Function with the added bonus of evaluating within the scope of the class, meaning you can reference object variables.

 

Define Class( "exampleClass",
	m_clb = Empty();
	m_window = .;
	m_BC = .;
	_init_ = Method( {},
		m_BC = Open( "$SAMPLE_DATA/Big Class.jmp" );
		m_window = Empty();
	);
	method1 = Method( {},
		m_window = New Window("Test Window", 
			m_clb = Col List Box(
				Datatable(m_BC),
				all,
				<< Set Function(Method({this},
					Show(this << get selected);
					Show(m_clb << get selected);
					Show(m_window);
					Show(m_BC);
				))
			)
		);
	);
);

// Create an object of the class and try to use the function
testObject = New Object( exampleClass() );
testObject:method1();

The 'this' variable works the same as Jarmo explained, but you can also reference any other variable from the class. Here 'this' is equivalent to 'm_clb'.

 

I find using Methods this way is very convenient for creating modular windows with interactive components.