cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Registration open for Discovery Summit Europe. User Group Members benefit from a 25% discount with the code: DSEUgroup24
Choose Language Hide Translation Bar

2022_Q1 MakeOverChallenge: Action on mouse click in various ways

This is the Makeover Challenge Q1 2022.

 

The goal is to come up with an example, script or guide to fulfill one of the three tasks (all have to do with initiating some action after you click on something in JMP):

 

  1. Challenge A: (images and script to create initial data table for opening images attached: 2022Q1_ChallengeA.zip) 
    • You have: A data table column with images or links to a website or a file (aka expressions) 
    • You want: to click on one cell of this column and open the link or the image outside of JMP
    • The overall goal: clicking in a data table cell should initiate something 
  2. Challenge B: (script to open the data table and to create the report attached: 2022Q1_ChallengeB_starterScript.jsl)
    • You have: Big Class and a Graph Builder report of weight by height (see script)
    • You want: Closing the Graph Builder report should open a distribution report of "age", save this report as an picture and close the distribution report.
    • The overall goal: Closing a window should trigger some action  
  3. Challenge C: (script to open the data table and to create the report attached: 2022Q1_ChallengeC_starterScript.jsl)
    • You have: A Graph Builder report on Growth Measurements sample data. 
    • You want: When clicking on a line or selecting multiple lines (or categories in the legend), you want to generate a subset of the selected rows. 
    • The overall goal: Clicking on a graph or report should trigger some action

 

You can work on as many challenges as you like. You can/should submit your answers directly as a reply to this thread. 

!!! When submitting, please start in the first line with what challenge your reply refers to, e.g. 

CHALLENGE A:

This is my answer...

// this is my code
...

 

In the next meeting (18 May 2022) we will present some of the answers and I will mark the best answers as solution to this thread. 

 

Looking forward to your submissions

 

/****NeverStopLearning****/
10 REPLIES 10
Robbb
Level III

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

CHALLENGE B

Add this to the bottom of the script:

gb << On Close(
	dist = dt << Distribution( Nominal Distribution( Column( :age ) ) );
	image = dist << Get Picture;
	path = "$temp/age_dist.png";
	image << Save Picture( path, "png" );
	dist << Close Window();
	Open( path );
);
Georg
Level VII

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

CHALLENGE C

 

This is my answer, see full script below.

The lower part adds a row state handler to the table, that triggers the action (see example in Scripting Index).

 

Names Default To Here( 1 );

// Open Data Table
dt = Open( "$SAMPLE_DATA/Growth Measurements.jmp", invisible );


// Report snapshot: Growth Measurements - Graph Builder
gb = dt << Graph Builder(
	Size( 522, 452 ),
	Show Control Panel( 0 ),
	Legend Position( "Bottom" ),
	Graph Spacing( 8 ),
	Variables( X( :Age ), Y( :Growth ), Overlay( :Subject ) ),
	Elements( Line( X, Y, Legend( 13 ) ) )
);

// This is my code (Georg)
// (see Scripting Index "Make Row State Handler")
// Define a function, that does the action
f = Function( {a},
	Current Data Table() << Subset( Selected Rows( 1 ), Selected columns only( 0 ) )
);

// Set up a row state handler, that triggers the execution of a function on any change of row states,
// a change on row selection by clicking on a line in Graph Builder is a change of row states in the table
rs = dt << make row state handler( f );
Georg
dk
dk
Level II

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

CHALLENGE C

 

With a mouse box it was not possible to select something, so I changed to a row state handler

 

 

 

Names Default To Here( 1 );

// Open Data Table
dt = Open( "$SAMPLE_DATA/Growth Measurements.jmp", invisible );


// Report snapshot: Growth Measurements - Graph Builder
gb = dt << Graph Builder(
	Size( 522, 452 ),
	Show Control Panel( 0 ),
	Legend Position( "Bottom" ),
	Graph Spacing( 8 ),
	Variables( X( :Age ), Y( :Growth ), Overlay( :Subject ) ),
	Elements( Line( X, Y, Legend( 13 ) ) )
);

active_subset = "";
first_subset = "false";

rsh = dt << make row state handler(
	Function( {a},
		subtbl = dt << Subset( Selected Rows( 1 ) );
		gb << Bring Window To Front;
		if(
			first_subset == "true",
			close(active_subset, no save)
		);
		active_subset = subtbl;
		first_subset = "true";
	)
);

 

Ben_Sagalovsky
Level II

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

CHALLENGE A:

NOTE: I kept this older version since some people had already seen it, but I posted a cleaner solution as a Reply to this one.

 

Not at all a pretty solution, but it does the job... Hoping for a more elegant one to show up!

This to be added after the starterScript:

 

// Defining the file name for an image placeholder for displaying on the OS when clicking:
pictFileName = Get Default Directory() || "verylonganduselessname.png"; //name for the temp image file (to be deleted at the end)
dtx:Picture << Set Property("tmpFileName", pictFileName); //saving the name so it can be retrieved from within the table
dtx << On Close( Delete File(pictFileName) ); //make sure we erase the temp image file at the end.

// Setting a Click event handler for the column of interest: 

dtx:Picture << Set Property(
	"Event Handler",
	Event Handler(
		Click(JSL Quote( Function(
									{thisTable, thisColumn, iRow},  
									pictFileName = thisTable:thisColumn<< Get Property("tmpFileName"); //recover the path for a temp file
									thisTable:thisColumn[iRow] << Save Image( pictFileName ); //save cell contents to temp file
									Web( "file://" || pictFileName ) // open the image file
								)
				)		
		),
		Tip(JSL Quote( Function(
									{thisTable, thisColumn, iRow}, 
									"click to open";
								)
				)		
		)		
	)
);

 

Ben_Sagalovsky
Level II

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

CHALLENGE A

 

Sorry for jumping the gun with my initial solution... Here's a cleaner one.

Names Default to Here ( 1 );
//path = <YOUR FULL PATH TO WHERE THE IMAGES HAVE BEEN SAVED>; 
// eg. 
// path = "C:\EventHandlerImages\"; 
path = "$DOCUMENTS\Scripting\2022Q1\ChallengeA\2022Q1_ChallengeA\"; 
//==========================================================================

//Multiple File Import is new in JMP 14

{dtx} = Multiple File Import(
	<<Set Folder( path ),
	<<Set Name Filter( "*.jpg; *.png;" ),
	<<Set Name Enable( 1 ),
	<<Set Add File Name Column( 1 ),
	<<Set Add File Size Column( 1 ) // for show sizes at end
) << Import Data;

//=================================================================================

// this is my code:

dtx << Set Name ( "Interactive Table"); // just aesthetics...
// File to act as image placeholder for displaying on the OS: $TEMP/todelete.png
 
// Setting a Click event handler for the column of interest: 

dtx:Picture << Set Property(
	"Event Handler",
	Event Handler(
		Click(JSL Quote( Function(
									{thisTable, thisColumn, iRow},  
									thisTable:thisColumn[iRow] << Save Image( "$TEMP/todelete.png" ); //save cell contents to temp file
									Web( "file://" || "$TEMP/todelete.png" ) // open the image file
								)
				)		
		),
		Tip(JSL Quote( Function(
									{thisTable, thisColumn, iRow}, 
									"click to open";
								)
				)		
		)		
	)
);
Ressel
Level VI

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

Challenge C

There was unfortunately little else to do as far as challenges A and B are concerned. The solutions provided seemed impeccable. In contrast, I felt that some improvement was still possible for challenge C. A few words on my answer

  • I have basically stolen the necessary code from here (@txnelson is, like many other scripters, a rock star to me)
  • I have slightly modified the graph by including visible data points, so it is easier to select individual rows
  • After selecting the points in the graph, the data table with the subset is not opened immediately. This is done via the button added below the graph

 

See below the complete script, including the modified graph.

 

// Open Data Table
dt = Open( "$SAMPLE_DATA/Growth Measurements.jmp",invisible );


// Report snapshot: Growth Measurements - Graph Builder
gb = dt << Graph Builder(
	Size( 522, 452 ),
	Show Control Panel( 0 ),
	Legend Position( "Bottom" ),
	Graph Spacing( 8 ),
	Variables( X( :Age ), Y( :Growth ), Overlay( :Subject ) ),
	Elements(
		Points( X, Y, Legend( 17 ), Jitter Limit( 0 ) ), 	// added markers
		Line( X, Y, Legend( 13 ) ) ),
	SendToReport(											// increased markers to decent size
		Dispatch(
		{},
			"Graph Builder",
			FrameBox,
			{Marker Size( 6 ), Marker Drawing Mode( "Normal" )}
		)	
	)
);

// my solution (stolen from here: https://community.jmp.com/t5/Discussions/Creating-a-Subset-from-an-Interactive-Graph/td-p/114459)
subsetButton = Button Box( "Dear user, Please press here to create a Subset of the Selected Points immediately",
	If( N Rows( dt << get selected rows ) > 0,
		dt << subset( selected rows( 1 ), selected columns( 0 ) )
	)
);

Report( gb )["Graph Builder"] << append( subsetButton );

 

 

jthi
Super User

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

Challenge A:

Mostly JMP written code with Add Column Properties. Also added example how to add and open URLs

View more...
Names Default To Here(1);
// Challenge A

path = "2022Q1_ChallengeA"; // path to images

{dtx} = Multiple File Import(
	<<Set Folder( path ),
	<<Set Name Filter( "*.jpg; *.png;" ),
	<<Set Name Enable( 1 ),
	<<Set Add File Name Column( 1 ),
	<<Set Add File Size Column( 1 ) // for show sizes at end
) << Import Data;

// because images are already in the data table, we have no need to open them from the drive, we can just load image to new window
Column(dtx, "Picture") << Add Column Properties(
	Set Property(
		"Event Handler",
		Event Handler(
			Click(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				New Window(Char(thisTable:"File Name"n[iRow]), New Image(thisTable:thisColumn[iRow]));
			))),
			Tip(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				"Enlarge image " || Char(thisTable:"File Name"n[iRow]);
			)))
		)
	)
);

// new column with some URLs
new_col = dtx << New Column("URL", Character, Nominal, Values(
	{"www.jmp.com","www.google.com","community.jmp.com","https://www.jmp.com/support/help/en/16.2/"}
));

// code jmp created for us, just removed the excessive amount of comments
new_col <<	Add Column Properties(
	Set Property(
		"Event Handler",
		Event Handler(
			Click(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				Web(Char(thisTable:thisColumn[iRow]));
			))),
			Tip(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				"Open " || Char(thisTable:thisColumn[iRow]) || " in your browser.";
			))),
			Color(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				RGBColor("link");
			)))
		)
	)
);

Write(); // to avoid annoying print to log of last executed row

Using SAMPLE_IMAGES path variable (this also works as an example on how easy it can be to make changes when you are scripting, just changing one variable and script still works (I removed one URL from list so no empty row will be added to other columns)):

Names Default To Here(1);
// Challenge A

path = Get Path Variable("$SAMPLE_IMAGES");

{dtx} = Multiple File Import(
	<<Set Folder( path ),
	<<Set Name Filter( "*.jpg; *.png;" ),
	<<Set Name Enable( 1 ),
	<<Set Add File Name Column( 1 ),
	<<Set Add File Size Column( 1 ) // for show sizes at end
) << Import Data;

// because images are already in the data table, we have no need to open them from the drive, we can just load image to new window
Column(dtx, "Picture") << Add Column Properties(
	Set Property(
		"Event Handler",
		Event Handler(
			Click(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				New Window(Char(thisTable:"File Name"n[iRow]), New Image(thisTable:thisColumn[iRow]));
			))),
			Tip(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				"Enlarge image " || Char(thisTable:"File Name"n[iRow]);
			)))
		)
	)
);

// new column with some URLs
new_col = dtx << New Column("URL", Character, Nominal, Values(
	{"www.jmp.com", "community.jmp.com","https://www.jmp.com/support/help/en/16.2/"}
));

// code jmp created for us, just removed the excessive amount of comments
new_col <<	Add Column Properties(
	Set Property(
		"Event Handler",
		Event Handler(
			Click(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				Web(Char(thisTable:thisColumn[iRow]));
			))),
			Tip(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				"Open " || Char(thisTable:thisColumn[iRow]) || " in your browser.";
			))),
			Color(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				RGBColor("link");
			)))
		)
	)
);

Write(); // to avoid annoying print to log of last executed row

 

-Jarmo
jthi
Super User

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

Challenge B:

View more...
Names Default To Here(1);

// 2022_Q1 MakeOverChallenge: Action on mouse click in various ways
// Challenge B

dt = Open("$SAMPLE_DATA/Big Class.jmp");

//Use expression to make script a bit more clean IF this were to be expanded
create_dist = Expr(
	dist = dt << Distribution(Nominal Distribution(Column(:age)), Histograms Only);
	wait(1); // add wait so we can see that distribution is created
	dist << Save Picture("$TEMP/Big_Class_age_dist.png", png);
	dist << Close Window;
);

gb = dt << Graph Builder(
	Size(528, 454),
	Show Control Panel(0),
	Variables(X(:height), Y(:weight)),
	Elements(Points(X, Y, Legend(3)), Line Of Fit(X, Y, Legend(5)))
);

gb << On Close(create_dist);

// Open("$TEMP/Big_Class_age_dist.png"); // un-comment to open the image
-Jarmo
jthi
Super User

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

Challenge C:

I definitely suggest doing stuff like this with buttons as it prevents the creation of too many subsets. But here I followed the given challenge and added simple check that we won't at least create same subset twice.

View more...
Names Default To Here(1);

// Open Data Table
dt = Open("$SAMPLE_DATA/Growth Measurements.jmp", invisible);

gb = dt << Graph Builder(
	Size(522, 452),
	Show Control Panel(0),
	Legend Position("Bottom"),
	Graph Spacing(8),
	Variables(X(:Age), Y(:Growth), Overlay(:Subject)),
	Elements(Line(X, Y, Legend(13)))
);

//initialize some variables
prev_rows = [];
dt_list = {};
f = Function({rows_changed}, 
	//Rowstate 1 is selected row
	cur_rows = Loc(dt << Get Row States, 1);

	//if no rows are selected, don't try to create subsets
	If(N Items(cur_rows) == 0,
		return()
	);
	//compare to previous to prevent opening extensive abmount of subsets
	If(N Items(cur_rows) == N Items(prev_rows),
		If(All(cur_rows == prev_rows),
			return();
		);
	);
	
	dt_subset = dt << Subset(Output table("selected_rows"), Rows(cur_rows), invisible);
	Insert Into(dt_list, dt_subset); //keep list to make closing easier
	
	//attempt to bring gb to front, this won't work perfectly without wait
	gb << Bring Window To Front;
	dt_subset << Show Window(1);
	//it is enough if we update prev_rows only when subset was created
	prev_rows = cur_rows;
);

rs = dt << make row state handler(f);

/* run this to close all data tables
For Each({dt_ref}, dt_list,
	Try(Close(dt_ref, no save), Show(exception_msg));
);
Close(dt, no save);
*/

With Row State Handler we cannot access the event of mouse release so we will still create many subsets if user selects multiple lines one by one from graph (there might be a way to do this with Mouse Box(), but I think using button would be much simpler and easier).

-Jarmo
jthi
Super User

Re: 2022_Q1 MakeOverChallenge: Action on mouse click in various ways

I'll write a quick overview what I talked about today in the meeting. You can see my full solutions in my earlier posts in this topic. I will modfify my Challenge A solution to use JMP's SAMPLE_IMAGE folder, so you should be able to directly run it.

Challenge A: Clicking in a data table cell should initiate something

Concept: Using Event Handler column property

 

This is fairly easy to do by letting JMP script this for you. Add Event Handler for web page for example and then modify the script to better fit your needs. Below is my example solution on how to open an image in cell.

 

Column(dtx, "Picture") << Add Column Properties(
	Set Property(
		"Event Handler",
		Event Handler(
			Click(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				New Window(Char(thisTable:"File Name"n[iRow]), New Image(thisTable:thisColumn[iRow]));
			))),
			Tip(JSL Quote(Function({thisTable, thisColumn, iRow}, {},
				"Enlarge image " || Char(thisTable:"File Name"n[iRow]);
			)))
		)
	)
);

 

Event Handlers can be fairly powerful as you could for example query data from database on cell click.

 

Challenge B: Closing a window should trigger some action

Concept: Using << On Close()

Idea in this challenge was to perform an action when Graph Builder is closed. This is fairly simple task to perform by using << On Close(). Below is slightly modified version of the solution provided by @Robbb 

 

gb << On Close(
	dist = dt << Distribution(Nominal Distribution(Column(:age)));
	image = dist << Get Picture;
	image << Save Picture($temp/age_dist.png, "png");
	dist << Close Window();
);

 

One thing to consider with this is, that when you are using references to data table (in this case dt), that if you open new table before closing gb, you will most likely close wrong table.

You can see this issue in this example script:

 

Names Default To Here(1);
dt = Open("$SAMPLE_DATA/Big Class.jmp");
biv = dt << Bivariate(Y(:Weight), X(:Height), Nonpar Density);
biv << On Close(
	close(dt, no save);
);
dt = Open("$SAMPLE_DATA/Semiconductor Capability.jmp");

And this script provides one possible solution by using Eval(EvalExpr()) and Expr() to get explicit reference to the table

 

 

Names Default To Here(1);
dt = Open("$SAMPLE_DATA/Big Class.jmp");
biv = dt << Bivariate(Y(:Weight), X(:Height), Nonpar Density);
dt = Open("$SAMPLE_DATA/Semiconductor Capability.jmp");

Eval(EvalExpr(
	biv << On Close(
		Close(Expr(biv << Get Data Table), no save)
	);
));

You could also possibly use function with On Close, but in this case it will generate more complicated code, as you won't have access to the bivariate platform's reference but rather to the window which has the bivariate, so you have to parse the window name which might or might not work (there might be a way to get reference to the platform inside the window).

 

 

Names Default To Here(1);
dt = Open("$SAMPLE_DATA/Big Class.jmp");
biv = dt << Bivariate(Y(:Weight), X(:Height), Nonpar Density);
biv << On Close(function({this},
	table_name = Trim Whitespace(Word(1, (this << Get Window Title), "-"));
	If(Contains(Get Data Table List() << get name, table_name),
		Close(Datatable(table_name), no save);
	);
));

 

Challenge C: Clicking on a graph or report should trigger some action

Concept: Using << Make Row State Handler()

This had most different solutions and all were a bit different. Big challenge with this type of script is deciding on how you want to manage constantly changing row states. One option is to use a Button to trigger the action (like @Ressel did) instead of Row State Handler, this is in my opinion most robust option in this case.

 

Rest of the solutions did use row state handler and different ways (or not) managing the selected rows being changed all the time. One thing to note with Row State Handler is that it will return modified rows, not just selected rows. So if you are only interested in selected columns, you have to take that into account.

 

In most simple cases you can use something like @Georg gave as an answer.

f = Function({a},
	dt << Subset(Selected Rows(1), Selected columns only(0))
);

rs = dt << make row state handler(f);

 

One option to manage the rows would be to create associative array (JMP allows using Matrix as key in Associative array) in which you collect the rows which have had an subset already created. This should work as long as you don't change row order or add/remove rows.

 

Below is a quick example using associative array to collect rows which have been subsetted. In the example I'm checking for rows which have row state 1 (selected row) and then create subset of those if it hasn't already been created. One benefit with this is, that you can also save the datatable references to the associative array and quicky access them all by with aa_subset_rows << Get Values; but this solution might still be a bit buggy if many lines are being selected by holding and dragging mouse over them.

Names Default To Here(1);

// Open Data Table
dt = Open("$SAMPLE_DATA/Growth Measurements.jmp", invisible);

gb = dt << Graph Builder(
	Size(522, 452),
	Show Control Panel(0),
	Legend Position("Bottom"),
	Graph Spacing(8),
	Variables(X(:Age), Y(:Growth), Overlay(:Subject)),
	Elements(Line(X, Y, Legend(13)))
);

aa_subset_rows = Associative Array();

f = Function({rows_changed}, 
	//Rowstate 1 is selected row
	cur_rows = Loc(dt << Get Row States, 1);

	//if no rows are selected, don't try to create subsets
	If(N Items(cur_rows) == 0,
		return()
	);

	//compare to previous to prevent opening extensive abmount of subsets
	If(Contains(aa_subset_rows , cur_rows),
		return();
	);
	
	dt_subset = dt << Subset(Output table("selected_rows"), Rows(cur_rows));
	aa_subset_rows[cur_rows] = dt_subset;
);

rs = dt << make row state handler(f);

 

-Jarmo