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
Georg
Level VII

Make Row State Handler not working as expected in a Data Filter Context Box

Dear community,

 

I try to build a dashboard using data filter context/source boxes,

and it works in some details as expected.

But I need additional features, based on the selection I need to filter an additional table, that is located in Tab Page Box Details1.

That I recreate each time by using Make Row State Handler in that context.

When selecting single rows in Tabulate, everything is fine, but:

  • when I deselect all, the Tab Page Box does not change
  • when I select more than one line in Tabulate, wrong selection occurr in Tab Page Box
  • when I select points in GraphBuilder, Table generation is done excessively
  • ...

How to make this szenario more robust?

Is there a different way (w/o row state handler) to get the selected rows in Tabulate and Graph Buildeer?

Is there a better strategy to do things like this?

 

Thanks a lot and BR

 

Names Default To Here( 1 );
Clear Log();

dt = Open( "$SAMPLE_DATA\Big Class.jmp" );
dt << show window( 0 );

tabulate_expr = Expr(
	tb_obj = dt << Tabulate( Show Control Panel( 0 ), Add Table( Row Table( Grouping Columns( :sex, :age ) ) ) )
);
gb_expr = Expr(
	gb_obj = dt << Graph Builder(
		Show Control Panel( 0 ),
		Variables( X( :weight ), Y( :height ) ),
		Elements( Points( X, Y, Legend( 3 ) ), Smoother( X, Y, Legend( 4 ) ) )
	)
);
action_expr = Expr(
	Button Box( "Start Analysis", Print( pb_details << get row states() ) )
);

nw = New Window( "Dashboard",
	Data Filter Context Box(
		H List Box(
			V List Box(
				H List Box(
					Panel Box( "Filter (in Data Filter Source Box)", /* Filter and GB */ dfsb =
						Data Filter Source Box( tabulate_expr )
					),
					Panel Box( "Graph Builder (to be filtered in context", gb_expr )
				),
				Panel Box( "Action", /* Action */ action_expr )
			),
			pb_details = Panel Box( "Details (as well in data filter context box)", /* Tab Box */
				Tab Box(
					Tab Page Box( "Details1 (Data Grid Box, update via Make Row State Handler)", dgb = Data Grid Box() ),
					Tab Page Box( "Details2 (Filtered automatically)", tabulate_expr )
				)
			)
		)
	)
);

detail_dt = dt << subset( invisible, columns( {"name", "age", "sex"} ) );
dgb << set data table( detail_dt );
dgb << close side panels;
nw << on close(
	Close( dt, NoSave );
	Close( detail_dt, NoSave );
	For Each( {dt}, detail_dt_lst, Try( Close( dt, NoSave ) ) );
);

last_a = []; /* remember last selection to avoid double action */
detail_dt_lst = {}; /* save all table references to be able to close later */
details_update = Function( {a},
	If( Is Matrix( a ) & Char( a ) != Char( last_a ),
		Show( a );
		detail_old = dgb << Get Data Table();
		detail_dt = dt << subset( invisible, columns( {"name", "age", "sex"} ) );
		Insert Into( detail_dt_lst, detail_dt );
		detail_dt << select rows( a ) << delete rows();
		dgb << set data table( detail_dt );
		dgb << close side panels;
		Close( detail_old, NoSave );
		last_a = a;
	);
	
);
rsh = pb_details << Make Row State Handler( dt, details_update );



 

 

Georg
2 ACCEPTED SOLUTIONS

Accepted Solutions
jthi
Super User

Re: Make Row State Handler not working as expected in a Data Filter Context Box

Depending on your real use case, here is one option which might work. It relies on updating the subset instead of re-creating it. It will also check selected rows instead of relying the "a" parameter

 

Names Default To Here(1);

dt = Open("$SAMPLE_DATA\Big Class.jmp", Invisible);


tabulate_expr = Expr(
	tb_obj = dt << Tabulate(
		Show Control Panel(0),
		Add Table(Row Table(Grouping Columns(:sex, :age)))
	)
);


gb_expr = Expr(
	gb_obj = dt << Graph Builder(
		Show Control Panel(0),
		Variables(X(:weight), Y(:height)),
		Elements(Points(X, Y, Legend(3)), Smoother(X, Y, Legend(4)))
	)
);


action_expr = Expr(
	Button Box("Start Analysis", Print(pb_details << get row states()))
);


nw = New Window("Dashboard",
	Data Filter Context Box(
		H List Box(
			V List Box(
				H List Box(
					Panel Box("Filter (in Data Filter Source Box)",
						dfsb = Data Filter Source Box(tabulate_expr)
					),
					Panel Box("Graph Builder (to be filtered in context", 
						gb_expr
					)
				),
				Panel Box("Action", 
					action_expr
				)
			),
			pb_details = Panel Box("Details (as well in data filter context box)",
				Tab Box(
					Tab Page Box(
						"Details1 (Data Grid Box, update via Make Row State Handler)",
						dgb = Data Grid Box()
					),
					Tab Page Box("Details2 (Filtered automatically)", 
						tabulate_expr
					)
				)
			)
		)
	)
);

detail_dt = dt << subset(invisible, columns({"name", "age", "sex"}));
dgb << set data table(detail_dt);
dgb << close side panels;
nw << on close(
	Close(dt, NoSave);
	Close(detail_dt, NoSave);
);

last_rows = {}; // use list as you can compare those directly
extra_triggers = 0;

details_update = Function({a},
	// Write("\!N", Round(Tick Seconds(), 2), " \!thappy state handler triggering");
	// Show(dt << Get Row States);
	cur_rows = As List(dt << Get Selected Rows);
	If(N Items(cur_rows) == 0,
		cur_rows = As List((1::N Rows(dt))`); // same as all rows selected
	);
	
	If(cur_rows == last_rows,
		extra_triggers++;
		return();
	);
	
	Write("\!N", extra_triggers, " unnecessary triggers occured.");
	extra_triggers = 0;
	
	last_rows = cur_rows;
	
	If(N Rows(detail_dt) > N Items(cur_rows),
		detail_dt << Delete Rows(N Items(cur_rows) + 1::N Rows(detail_dt));
	, N Rows(detail_dt) < N Items(cur_rows),
		detail_dt << Add Rows(N Items(cur_rows) - N Rows(detail_dt));
	);
	
	detail_dt[0, {"name", "age", "sex"}] = dt[Matrix(last_rows), {"name", "age", "sex"}];
);

rsh = pb_details << Make Row State Handler(dt, details_update);

This isn't the only method of doing this, but this is quite simple option you can try.

 

Wish list item in hopes that state handlers would be usable at some point:Improve JMP's state handlers (hover label, row state handler, filter state handler, scheduler, ...) 

 

-Jarmo

View solution in original post

Re: Make Row State Handler not working as expected in a Data Filter Context Box

One source of confusion with <<Make Row State Handler is how to interpret the a argument.  The vector represents the rows that have changed, but it does not say anything about what state they are in.  You will also receive callbacks for non-filter events - for example: changes to selection, label, or marker row-states made in the table.  This does make it difficult to limit the response to the events of interest.

With that background, I think the main issues with your original script are (1) not responding when the function is called consecutively with the same arguments (this is why clear does not work) and (2) using the row numbers as the rows to be deleted.

The script below (note: JMP 18+ only) does not attempt to track and limit changes, but it should correctly respond with the actual excluded rows removed from the subset.

Names Default To Here( 1 );
Clear Log();
//https://community.jmp.com/t5/Discussions/Make-Row-State-Handler-not-working-as-expected-in-a-Data-Filter/m-p/791520

dt = Open( "$SAMPLE_DATA\Big Class.jmp" );
dt << show window( 0 );

tabulate_expr = Expr(
	tb_obj = dt << Tabulate( Show Control Panel( 0 ), Add Table( Row Table( Grouping Columns( :sex, :age ) ) ) )
);
gb_expr = Expr(
	gb_obj = dt << Graph Builder(
		Show Control Panel( 0 ),
		Variables( X( :weight ), Y( :height ) ),
		Elements( Points( X, Y, Legend( 3 ) ), Smoother( X, Y, Legend( 4 ) ) )
	)
);
action_expr = Expr(
	Button Box( "Start Analysis", Print( pb_details << get row states() ) )
);

nw = New Window( "Dashboard",
	Data Filter Context Box(
		H List Box(
			V List Box(
				H List Box(
					Panel Box( "Filter (in Data Filter Source Box)", /* Filter and GB */ dfsb =
						Data Filter Source Box( tabulate_expr )
					),
					Panel Box( "Graph Builder (to be filtered in context", gb_expr )
				),
				Panel Box( "Action", /* Action */ action_expr )
			),
			pb_details = Panel Box( "Details (as well in data filter context box)", /* Tab Box */
				Tab Box(
					Tab Page Box( "Details1 (Data Grid Box, update via Make Row State Handler)", dgb = Data Grid Box() ),
					Tab Page Box( "Details2 (Filtered automatically)", tabulate_expr )
				)
			)
		)
	)
);

detail_dt = dt << subset( invisible, columns( {"name", "age", "sex"} ) );
dgb << set data table( detail_dt );
dgb << close side panels;
nw << on close(
	Close( dt, NoSave );
	Close( detail_dt, NoSave );
	For Each( {dt}, detail_dt_lst, Try( Close( dt, NoSave ) ) );
);

detail_dt_lst = {}; /* save all table references to be able to close later */
details_update = Function( {a},
	Show( a );
	If( Is Matrix( a ),
		detail_old = dgb << Get Data Table();
		detail_dt = dt << subset( invisible, columns( {"name", "age", "sex"} ) );
		Insert Into( detail_dt_lst, detail_dt );
		filter_state = pb_details << Get Row States(dt);
		detail_dt << select rows( Where(Excluded(As Row State(filter_state))) ) << delete rows();
		dgb << set data table( detail_dt );
		dgb << close side panels;
		Close( detail_old, NoSave );
	);
);
rsh = pb_details << Make Row State Handler( dt, details_update );

 

View solution in original post

5 REPLIES 5
jthi
Super User

Re: Make Row State Handler not working as expected in a Data Filter Context Box

In my opinion there are so many different issues with row state handlers that you cannot make them robust (for example they get removed too easily, trigger too much and many other issues). To avoid some of the issues you are seeing, don't use the parameter you can have in the function for row state handler, a in your case, and instead use << Get Selected Rows.

 

So you want to have dashboard with tabulate and graph builder and these should filter down a data table? Is it necessary to have a data table which is being filtered? Easiest way to avoid all the issues is to utilize a button box to update the table.

-Jarmo
jthi
Super User

Re: Make Row State Handler not working as expected in a Data Filter Context Box

Depending on your real use case, here is one option which might work. It relies on updating the subset instead of re-creating it. It will also check selected rows instead of relying the "a" parameter

 

Names Default To Here(1);

dt = Open("$SAMPLE_DATA\Big Class.jmp", Invisible);


tabulate_expr = Expr(
	tb_obj = dt << Tabulate(
		Show Control Panel(0),
		Add Table(Row Table(Grouping Columns(:sex, :age)))
	)
);


gb_expr = Expr(
	gb_obj = dt << Graph Builder(
		Show Control Panel(0),
		Variables(X(:weight), Y(:height)),
		Elements(Points(X, Y, Legend(3)), Smoother(X, Y, Legend(4)))
	)
);


action_expr = Expr(
	Button Box("Start Analysis", Print(pb_details << get row states()))
);


nw = New Window("Dashboard",
	Data Filter Context Box(
		H List Box(
			V List Box(
				H List Box(
					Panel Box("Filter (in Data Filter Source Box)",
						dfsb = Data Filter Source Box(tabulate_expr)
					),
					Panel Box("Graph Builder (to be filtered in context", 
						gb_expr
					)
				),
				Panel Box("Action", 
					action_expr
				)
			),
			pb_details = Panel Box("Details (as well in data filter context box)",
				Tab Box(
					Tab Page Box(
						"Details1 (Data Grid Box, update via Make Row State Handler)",
						dgb = Data Grid Box()
					),
					Tab Page Box("Details2 (Filtered automatically)", 
						tabulate_expr
					)
				)
			)
		)
	)
);

detail_dt = dt << subset(invisible, columns({"name", "age", "sex"}));
dgb << set data table(detail_dt);
dgb << close side panels;
nw << on close(
	Close(dt, NoSave);
	Close(detail_dt, NoSave);
);

last_rows = {}; // use list as you can compare those directly
extra_triggers = 0;

details_update = Function({a},
	// Write("\!N", Round(Tick Seconds(), 2), " \!thappy state handler triggering");
	// Show(dt << Get Row States);
	cur_rows = As List(dt << Get Selected Rows);
	If(N Items(cur_rows) == 0,
		cur_rows = As List((1::N Rows(dt))`); // same as all rows selected
	);
	
	If(cur_rows == last_rows,
		extra_triggers++;
		return();
	);
	
	Write("\!N", extra_triggers, " unnecessary triggers occured.");
	extra_triggers = 0;
	
	last_rows = cur_rows;
	
	If(N Rows(detail_dt) > N Items(cur_rows),
		detail_dt << Delete Rows(N Items(cur_rows) + 1::N Rows(detail_dt));
	, N Rows(detail_dt) < N Items(cur_rows),
		detail_dt << Add Rows(N Items(cur_rows) - N Rows(detail_dt));
	);
	
	detail_dt[0, {"name", "age", "sex"}] = dt[Matrix(last_rows), {"name", "age", "sex"}];
);

rsh = pb_details << Make Row State Handler(dt, details_update);

This isn't the only method of doing this, but this is quite simple option you can try.

 

Wish list item in hopes that state handlers would be usable at some point:Improve JMP's state handlers (hover label, row state handler, filter state handler, scheduler, ...) 

 

-Jarmo
Georg
Level VII

Re: Make Row State Handler not working as expected in a Data Filter Context Box

Thanks for your suggestion, the buton box would be a solution.

I've two choices:

(1) to load during starting the GUI all details I might need, keeping them as a hidden table and display a part I need in certain context, here I would go for a dynamic solution with rowstate etc.

(2) upon request load the data from database (here I would go for a button solution).

But the case is generally quite interesting for my, because there are a lot of similar cases, where I would prefer (1) or (2), and I want understand better, how JMP works with it.

 

Georg

Re: Make Row State Handler not working as expected in a Data Filter Context Box

One source of confusion with <<Make Row State Handler is how to interpret the a argument.  The vector represents the rows that have changed, but it does not say anything about what state they are in.  You will also receive callbacks for non-filter events - for example: changes to selection, label, or marker row-states made in the table.  This does make it difficult to limit the response to the events of interest.

With that background, I think the main issues with your original script are (1) not responding when the function is called consecutively with the same arguments (this is why clear does not work) and (2) using the row numbers as the rows to be deleted.

The script below (note: JMP 18+ only) does not attempt to track and limit changes, but it should correctly respond with the actual excluded rows removed from the subset.

Names Default To Here( 1 );
Clear Log();
//https://community.jmp.com/t5/Discussions/Make-Row-State-Handler-not-working-as-expected-in-a-Data-Filter/m-p/791520

dt = Open( "$SAMPLE_DATA\Big Class.jmp" );
dt << show window( 0 );

tabulate_expr = Expr(
	tb_obj = dt << Tabulate( Show Control Panel( 0 ), Add Table( Row Table( Grouping Columns( :sex, :age ) ) ) )
);
gb_expr = Expr(
	gb_obj = dt << Graph Builder(
		Show Control Panel( 0 ),
		Variables( X( :weight ), Y( :height ) ),
		Elements( Points( X, Y, Legend( 3 ) ), Smoother( X, Y, Legend( 4 ) ) )
	)
);
action_expr = Expr(
	Button Box( "Start Analysis", Print( pb_details << get row states() ) )
);

nw = New Window( "Dashboard",
	Data Filter Context Box(
		H List Box(
			V List Box(
				H List Box(
					Panel Box( "Filter (in Data Filter Source Box)", /* Filter and GB */ dfsb =
						Data Filter Source Box( tabulate_expr )
					),
					Panel Box( "Graph Builder (to be filtered in context", gb_expr )
				),
				Panel Box( "Action", /* Action */ action_expr )
			),
			pb_details = Panel Box( "Details (as well in data filter context box)", /* Tab Box */
				Tab Box(
					Tab Page Box( "Details1 (Data Grid Box, update via Make Row State Handler)", dgb = Data Grid Box() ),
					Tab Page Box( "Details2 (Filtered automatically)", tabulate_expr )
				)
			)
		)
	)
);

detail_dt = dt << subset( invisible, columns( {"name", "age", "sex"} ) );
dgb << set data table( detail_dt );
dgb << close side panels;
nw << on close(
	Close( dt, NoSave );
	Close( detail_dt, NoSave );
	For Each( {dt}, detail_dt_lst, Try( Close( dt, NoSave ) ) );
);

detail_dt_lst = {}; /* save all table references to be able to close later */
details_update = Function( {a},
	Show( a );
	If( Is Matrix( a ),
		detail_old = dgb << Get Data Table();
		detail_dt = dt << subset( invisible, columns( {"name", "age", "sex"} ) );
		Insert Into( detail_dt_lst, detail_dt );
		filter_state = pb_details << Get Row States(dt);
		detail_dt << select rows( Where(Excluded(As Row State(filter_state))) ) << delete rows();
		dgb << set data table( detail_dt );
		dgb << close side panels;
		Close( detail_old, NoSave );
	);
);
rsh = pb_details << Make Row State Handler( dt, details_update );

 

Georg
Level VII

Re: Make Row State Handler not working as expected in a Data Filter Context Box

Thanks a lot for your input @jthi and @danschikore , this gives me some great ideas to improve my dashboard.

Georg