Subscribe Bookmark RSS Feed
Craige_Hales

Staff

Joined:

Mar 21, 2013

JMP 12 Expression Column

This post is an example application that can be written in JSL with JMP 12 expression columns.  More JMP 12 posts.

There is a .JMP file attached to this post that looks something like this:

7934_FSM1.PNG

This table holds a Finite State Machine, FSM for short.  If you run the Run Me script, JMP opens a window like this:

7936_FSM2.PNG

which has buttons on the right to navigate between states.  In this application, the room you are in is the state the FSM is in.  There are six rooms, six states, and six rows in the table.

The columns roomname and message are plain old character columns.  Contents is an Expression column containing JSL lists in curly braces.  After you create a column, the column dialog can set the type to expression:

7937_FSM3.PNG

Expression columns mostly don't care what kind of expression is in the cells, and don't care if every cell has the same kind of expression.  The Doorway column is an expression column of matrices, indicated by the square brackets.  The onEnter column is an expression column of JSL that the script will run when a room (state) is entered.

That leaves the picture column, which is also an expression column.  JMP does make an exception for pictures, displaying the picture by default, rather than the text of the expression.  The text of the expression for the first picture looks like this (except the ... is a lot of characters):

New Image(Char To Blob( "58016...j1EF", "base64compressed" ), "png")

The JSL works with the expression columns to make the user interface of this FSM change with state changes.  For this FSM, state changes happen when buttons are pressed.

room = Num( dtx << gettablevariable( "roomRow" ) );

           

the table variable roomRow (upper left corner of first picture) remembers what state the FSM is in.  The script uses that to find the contents of the current room:

getcontents = Function( {},

    {stuff = dtx:contents[room], rc = H List Box(), i},

    For( i = 1, i <= N Items( stuff ), i++,

        rc << append( Eval( Eval Expr( Button Box( stuff, take( Expr( stuff ) ) ) ) ) )

    );

    rc;

);

           

dtx is the FSM data table, so dtx:contents is the column of lists.  the [room] subscript picks the row from the table.  The goal of this routine is to build a horizontal list of button boxes.  Each button is labeled with an item in the room.  The button script calls a take function to take the item from the room and put it in inventory, another table variable.  The Eval/EvalExpr/Expr code is "baking in" the current value of "stuff" so take will get the baked-in value, later, when the button is pressed and the values in stuff and i won't be available anymore.

take = Function( {thing},

    {stuff = Parse( dtx << gettablevariable( "inventory" ) ), pos = Contains( dtx:contents[room], thing)},

  // move from room's content to inventory

    //Open( "$TEMP/take.mp3" );

    tableholder << Bring Window To Front;

    dtx:contents[room] = Remove( dtx:contents[room], pos );

    Insert Into( stuff, thing);

    dtx << settablevariable( "inventory", Char( stuff ) );

    rebuild();

);

           

The take function grabs the table variable inventory string and parses it into a list.  Then it makes a pos variable with the item to take; thing is the name that was baked-in above.  The next few lines manipulate the room's contents and the inventory.  The table can be saved at any point with the current state of the FSM because this JSL always uses the values from the table variable and the expression column.

The last thing take does is call rebuild().

rebuild = Function( {},

    (inv << child) << delete;

    inv << append( getinventory() );

    (content << child) << delete;

    content << append( getcontents() );

    (doors << child) << delete;

    doors << append( getdoors() );

    txt << settext( dtx:message[room] );

    (pic << child) << delete;

    pic << append( dtx:picture[room] );

);

           

the rebuild task is to make the user interface of the FSM consistent with the internal state.  The old lists of inventory, contents, and door buttons are deleted and replaced with fresh lists, and the picture is updated.  All the names (inv, content, doors, txt, pic) are set by the script that creates the window:

New Window( "FSM",

    H List Box(

        V List Box( Text Box( "inventory:\!n  click\!n to drop" ), inv = Border Box( getinventory() ) ),

        V List Box(

            txt = Text Box( dtx:message[room], <<setFontSize( 18 ) ),

            pic = Border Box( dtx:picture[room] ),

            H List Box( Text Box( "click to pick up:" ), content = Border Box( getcontents() ) )

        ),

        V List Box( Text Box( "click to\!ngo through\!ndoor to..." ), doors = Border Box( getdoors() ) ),

        tableholder = Border Box()

    )

);

           

each of the variables identifies a border box.  The border box does not get deleted, but its contents are deleted, and then replaced.

Finally, the far right of the window, not shown here, is being controlled by the script in the onEnter and onExit column.  You can see, way above, the onEnter script for the living room opens a data table of movies you might watch in the living room.  The invisible table then provides a NewDataBox to put in the tableholder border box.  onExit removes it when you leave the room, so the next room's onEnter can provide something different.  Most of the rooms pick an appropriate data table.  The bathroom requires JMP PRO to run the scripts in the table.

The attached table will open in earlier versions of JMP, but the expression columns will be character columns and the script will not run correctly.  JMP12 will be out soon, get ready!

Article Tags