User:Andrew Nikitin/buttongrid

From J Wiki
Jump to navigation Jump to search

ButtonGrid -- command buttons array

Background

Sometimes during J data analysis session, certain commands need to be executed over and over. Notable examples would be pd 'clip', pd 'keypos rti;show', etc. This application allows to assign a j phrase to a command button and execute this phrase (in base locale) with one button click. [{{#file: "buttongrid.ijs"}} Download script: buttongrid.ijs ]

cocurrent 'jzbg'
«utilities»
«data»
«access»
«form»
«command_handlers»
«event_handlers»
HELP=:0 : 0
«help»
)
«locale_cover»

Utilities

[{{#file: "utilities"}} Download script: utilities ]

controlgrid=:(,~"0/&([: +/\ }:@(0&,)) ,"1 ,~"0/)&,
moveby=:+/@,:"1
ltrb=:0 1 _2 _1{(<./ , >./) ,/ (2&{. , [: +/ _2 ]\ ])"1
NB. turn any array of rectangles into a list of rectangles (2d array)
rravel=:(, $~ (*/@}: , {:)@$)

NB. bounding rectangle of the list of rectangles
runion=:[: (<./ ([ , -~) >./) [: ,/ ([: +/\ _2 ]\ ])"1

rexpand=:4 : '((-x)+2{.y),(2*x)+2}.y'
NB. top left point
rnw=:rtl=:2 & {.
NB. bottom right point
rse=:rbr=:[: +/ _2 ]\ ]
NB. top right point
rne=:rtr=:0 2&(+/@:{) , 1&{
NB. bottom left point
rsw=:rbl=:0&{ , 1 3&(+/@:{)
NB. place rectangle identical to y next to y
rxnext=:(+ 2&{ 0} 0">)

require 'regex'
template=:(4 : 0)"1
  m=. '%(\d+)%?' rxmatches x
  i=.(#y) <. (1&{"2 m) ".@>@rxfrom x
  <(i{(":each y),<,'?') (0&{"2 m) rxmerge x
)
once=: [: ".^:((('=:'&-:)@>@{: *. (0>4!:0)@{.)@(2 {. ;:)) ;._2 ,&LF

These are copied from elsewhere. Most of these verbs manipulate rectangles for programmatic GUI generation. once executes assignment unless the name is already defined.

Actions and Labels

[{{#file: "access"}} Download script: access ]

once 'ACTION=:0 0 2$ a:'
get_action=:0&{"1
get_label=:1&{"1

Program stores actions and labels in boxed 3d array ACTION. It has shape m×n×2. Each "1 item contains j expression (action) and arbitrary string (label) in that order

Form

[{{#file: "data"}} Download script: data ]

NB. --- Default and initial values of configuration parameters

[{{#file: "data"}} Download script: data ]

once 'COCREATOR=:<''base'''

The form is executed in its own locale, jzbg. Right now there can only be one command button array per session. I considered making it a class (like plot or adjust), but I never seem to want more than one command button array at a time anyway. [{{#file: "form"}} Download script: form ]

bg_makeform=:3 : 0
    y=.$ID
    FID=:'bg'
    wd 'pc ',FID,';pn "ButtonGrid (',(>COCREATOR),')"'
    «button_array»
    «worktext»
    «shift_switches»
    «service_switches»
    wd 'pas ',(":2$MARGIN),';pshow'
    BForm=:wd 'qhwndp'
    wd 'pmovex ',":(2{.LASTPOS),2}.".wd 'qformx'
)

This form should look something like this: Buttongrid-form.png [{{#file: "button_array"}} Download script: button_array ]

    s=.BUTTON+MARGIN
    xywh=.<"1 (2$MARGIN) moveby BUTTON,~"1 s*"1 |."1 (#: i.) y
    wd&> 'xywh %0%;cc %1% button;cn "%2%"' template xywh ,"_2 ID ,"_2 get_label ACTION

The main functional component of a form is an array of command buttons. When user clicks on a button, event handler executes j phrase associated with this button. As a feedback the executed phrase is placed into editbox (named worktext in the code). [{{#file: "worktext"}} Download script: worktext ]

    tb=.(0,MARGIN) moveby (rbl , 2&{ , EH"_) runion rravel >xywh
    wd 'xywh ',(":tb),';cc worktext edit es_autohscroll;cn ""'

[{{#file: "shift_switches"}} Download script: shift_switches ]

    cb0=.(_1 1*MARGIN) moveby (rbl tb), 0,RBH
    cb1r=. 25 (2}) (MARGIN,0) moveby rxnext cb0
    cb2r=. 25 (2}) (MARGIN,0) moveby rxnext cb1r
    cb3r=. 20 (2}) (MARGIN,0) moveby rxnext cb2r
    t=.'xywh %0%;cc %1% radiobutton group;cn "%2%"'
    wd > t template cb1r;'cblabel';'label'
    wd > t template cb2r;'cbview';'view'
    wd > t template cb3r;'cbsetexec';'set'

Radio buttons below the text box work kind of like calculator shift keys. User selects one of the radio buttons and then clicks one of the buttons on the command button array. This will modify appropriate setting for the button or display pertinent information. After this radio button is turned off and command buttons return to their normal "execute j phrase" mode.

  • label -- text from worktext becomes label on a command button
  • view -- j phrase associated with the button is copied into worktext
  • set -- worktext becomes new j phrase associated with a button

[{{#file: "service_switches"}} Download script: service_switches ]

    r=.+/0 2{tb
    t=.1{cb3r
    cbt=.(r-CBW),t,CBW,RBH
    cbq=.(r-CBW+MARGIN+CBW),t,CBW,RBH
    t=.'xywh %0%;cc %1% checkbox;cn "%2%"'
    wd >t template cbq;'cbq';'&Q'
    wd >t template cbt;'cbt';'&T'

Two checkboxes modify the default behavior of the form. "Quiet" (Q) does not display the expression and its result in .ijx window. "On top" (T) makes form "always on top". They are placed in the same row as shift switches and aligned to the right edge of workbox. If workbox is not wide enough, the 2 areas may overlap. [{{#file: "event_handlers"}} Download script: event_handlers ]

bg_cbt_button=:3 : 0
  wd 'ptop ',cbt
)

Configuration

All defining globals must be set by the time bg_makeform verb is run. They are: [{{#file: "data"}} Download script: data ]

once 'BUTTON=:30 30'

[{{#file: "help"}} Download script: help ]

button <w> <h> -- change size of a command button

Each command button size. [{{#file: "data"}} Download script: data ]

once 'MARGIN=:3'

[{{#file: "help"}} Download script: help ]

margin <x> -- change space between buttons

Intervals between buttons and extra space around the entire array. [{{#file: "data"}} Download script: data ]

once 'EH=:14'

[{{#file: "help"}} Download script: help ]

eh <height> -- height of textbox

Heigh of a textbox where user enters J command or button label. This is a single line textbox. Changing its height makes sense when font is smaller or larger than anticipated. [{{#file: "data"}} Download script: data ]

once 'RBH=:12'

[{{#file: "help"}} Download script: help ]

rbh <height> -- height of checkboxes and radio buttons

[{{#file: "data"}} Download script: data ]

once 'CBW=:15'

[{{#file: "help"}} Download script: help ]

cbw <width> -- T and Q checkbox width

Service checkbox width. Should be able to accomodate single character plus checkbox itself.

Commands

[{{#file: "command_handlers"}} Download script: command_handlers ]

bg_cmd=:3 : 0
  'cmd rest'=.y({.~ ; (}.~ >:))y i. ' '
  if. 3=nc :: _1: <'bgc_' (, :: [)cmd do.
    ('bgc_',cmd)~ rest
    return.
  elseif. 0=nc :: _1: <toupper cmd do.
    (toupper cmd)=:0".rest
    y=.''
  end.

  NB. *** numeric or empty argument -- build as needed and show
  if. ''-:y do. y=.2{.$ACTION end.
  if. 0=*/y do. y=.3 4 end.
  ID=:('b' <@, ":)">i. y

  bg_close ''
  ACTION=:y {. ACTION
  bg_makeform ''
  bgc_show ''
)

bg_cmd is an interface to entire command button array functionality. It has cover verb bg in z locale for convenience. It does several things.

If input (y) is numeric 2 element vector, it is interpreted as a number of rows and columns in the command button array. bg_cmd resizes internal storage structures for j phrases and button labels, generates necessary GUI elements and assigns id's to them.

If input is empty vector, bg_cmd activates the form with current parameters. If form was accidentally or intentionally closed, empty argument will restore it.

If input is a string, it is considered a command. It is split into first word and rest of the string.

If first word after capitalization becomes one of the internal noun names, te command means "change the name and regenerate form". For example:

bg 'button 40 20'

sets button size noun BUTTON to 40 20 and regenerates form with buttons of this new size.

Otherwise, program looks for a verb named bgc_first-word. If found, program runs this verb and passes the rest of the string as a y parameter.

bgc_help

[{{#file: "command_handlers"}} Download script: command_handlers ]

bgc_help=:3 : 0
 if. 'r'e. y do.
   NB. list allcaps nouns
   l=.(#~ (-: toupper)&>) nl 0
   NB. list bgc_* named verbs
   l=.l,(#~ ('bgc_' -: 4&{.)&>) nl 3
   list l
 else.
   HELP
 end.
)

[{{#file: "help"}} Download script: help ]

help [r] -- list of commands (r -- raw list)

bgc_help is a handler for help command.

There are 2 help modes. Automatically generated help ('r' for raw) shows all bgc_* verbs that may be called by bg_cmd and capitalized nouns that can be changed by bg_cmd. Preformatted help (default) is more human readable, but may be behind.

bgc_show

[{{#file: "command_handlers"}} Download script: command_handlers ]

bgc_show=:3 : 0
  if. '0' e. y do.
    bg_close ''
  else.
    try.
      wd 'psel ',FID,';pactive;pshow'
      bg_savepos ''
    catch.
      bg_makeform ''
    end.
    wd 'pmovex ',":(2{.LASTPOS),2}.".wd 'qformx'
  end.
)

[{{#file: "help"}} Download script: help ]

show [0] -- show form if it was closed before (hide when 0)

bgc_save

[{{#file: "command_handlers"}} Download script: command_handlers ]

bgc_save=:3 : 0
s=.(CRLF,~ [ , '=:' , [: 5!:5 <)
if. ''-:PREDEFINEDLOCATION do.
  self=.(>(4!:3 ''){~(4!:4 <'bg_save_jzbg_'))
else.
  self=.PREDEFINEDLOCATION
end.
r=. 'require ''',self,'''',CRLF
r=.r,'cocurrent ''',(>coname''),'''',CRLF
r=. r,;s&.> 'VERSION';'ACTION';'BUTTON';'MARGIN';'LASTPOS'
r=.r,'bg ''''',CRLF
(1!:2&(<y))^:(-.''-:y) r
)

[{{#file: "help"}} Download script: help ]

save <filename> -- save form configuration to a file including button
        asignments, dimensions and form position. use j `load` command
        to load the saved form.


bg 'save'

generates j script that loads command button array and initializes it with currently set-up parameters, including all sizes, button labels and j phrases attached to them.

bg 'save filename.ijs'

saves this sript to a file so that

load 'filename.ijs'

creates same form again. [{{#file: "data"}} Download script: data ]

PREDEFINEDLOCATION=:'bg'

Script generated by bgc_save needs to load this script first. The location of this script may be specified in PREDEFINEDLOCATION configuration noun. (I made a PUBLIC_j_ alias bg for this script on my system.) If PREDEFINEDLOCATION is left empty, it will attempt to make a guess using 4!:3 and 4!:4 (list of scripts and index in a list of scripts) foreigns.

This variable is intended to be modified in the source code to whatever is appopriate for the specific installation.

bgc_compact

[{{#file: "command_handlers"}} Download script: command_handlers ]

bgc_compact=:3 : 0
wd 'psel ',FID
if. (''-:y) +. 0~: 0".y do.
  wd 'pas ',":MARGIN,-+/EH,MARGIN,RBH
else.
  wd 'pas ',":2#MARGIN
end.
)

[{{#file: "help"}} Download script: help ]

compact [0] -- hide (or show) non-button area of the form

Hide worktext, shift and service switches, leave only buttons visible.

bg 'compact 0'

turns this off and restores service area.

bgc_exch

[{{#file: "command_handlers"}} Download script: command_handlers ]

bgc_exch=:3 : 0
  y=.4({.!._) _".y
  assert. (4=#y) *. *./,_2 (0&<:*.(2{.$ACTION)>])\ y
  i=._2 <\ y
  ACTION=:(i{ACTION) (|.i)}ACTION
  bg_close ''
  bg_makeform ''
  bgc_show''
)

Exchange contents and labels of 2 buttons with given row and column coordinates. [{{#file: "help"}} Download script: help ]

exch <row1> <col1> <row2> <col2> -- exchange 2 buttons and specified
        positions

bgc_size

[{{#file: "command_handlers"}} Download script: command_handlers ]

bgc_size=: 3 : 0
bg_cmd 2({.!.1) 1".y
)

[{{#file: "help"}} Download script: help ]

size <rows> <cols> -- change number of buttons, equivalent of `bg rows,cols`

Event handlers

[{{#file: "event_handlers"}} Download script: event_handlers ]

bg_default=:3 : 0
  exec=.''
  if. -.'button'-:'systype' wdget wdq do. return. end.
  ind=.<"1 ($ #: I.@,) (<'syschild' wdget wdq) = ID
  if. 1~:#ind do. return. end.
  ind=.{.ind
  NB. --- "shifted" keys (radio buttons) processings
  if. ".cblabel do.
    ACTION=: (<worktext) (ind,&.>1)} ACTION
    wd 'setcaption ',(>ind{ID),' *',worktext
  elseif. ".cbview do.
    wd 'set worktext *',(>ind{get_action ACTION)
  elseif. ".cbsetexec do.
    ACTION=: (<worktext) (ind,&.>0)} ACTION
    if. (ind{get_label ACTION)-:a: do.
      ACTION=: (<worktext) (ind,&.>1)} ACTION
      wd 'setcaption ',(>ind{ID),' *',worktext
    end.
  elseif. 1 do.
    exec=.>ind{get_action ACTION
    wd 'set worktext *',exec
  end.
  wd 'set cblabel 0'
  wd 'set cbview 0'
  wd 'set cbsetexec 0'
  if. -.''-:exec do.
    r=.do__COCREATOR exec
    if. -. ".cbq do. NB. -.0 0-:$r
      smoutput '   ',exec
      smoutput r
    end.
  end.
)

Since the number of buttons is not known beforehand, all processing is performed in default form handler. Events from radio buttons could have been processed separately, but i found it easier to keep everything in one place. [{{#file: "data"}} Download script: data ]

once 'LASTPOS=:16 16 0 0'

[{{#file: "event_handlers"}} Download script: event_handlers ]

bg_savepos=: 3 : 0
  wd 'psel bg'
  LASTPOS=:".wd 'qformx'
)

Form stores its last know position in LASTPOS noun and tries to keep it up to date at every opportunity. [{{#file: "event_handlers"}} Download script: event_handlers ]

bg_close=:3 : 0
  try.
    bg_savepos ''
  catch.
  end.
  wd 'pclose'
)

Close event needs to be processed to capture last known position to be able to restore it at next start.

Epilogue

[{{#file: "locale_cover"}} Download script: locale_cover ]

bg_z_=:bg_cmd_jzbg_

Cover verb for main command processor placed in z locale to simplify typing. [{{#file: "data"}} Download script: data ]

NB. --- end of configuration section
VERSION=:'$Revision: 1.12 $'

See also: adjust.lit

Tips and tricks

A button can be assigned to a command of saving current button configuration to a predefined file:

bg 'save C:\Data\p\ttqtopos\2012-02-08-11p99583\buttons.ijs'

To make a button that makes current window permanent on top, assign:

wd 'ptop 1'

A button to restore normal status:

wd 'ptop 0'

To hide radiobuttons and worktext use:

wd 'psel bg;pas ',":do_jzbg_ 'MARGIN,-+/EH,MARGIN,RBH'

To restore use

wd 'psel bg;pas ',":2#MARGIN_jzbg_

Some pretty unicode symbols to use in button labels (copy and paste)

← ↓ ↑ →
◄ ▼ ▲ ►
∑ ∏ σ √ ∫ ∂ ∆ ∞ ∩ ∪

Ideas for further improvements

I try to keep both interface and operation as simple as possible. Most of these ideas defeat this goal and therefore unlikely to be implemented. Nevertheless, I list them here as a food for thought.

multiline button labels

There seems to be no way to do it in wd

Color buttons

Buttons may have color. Static coloring enhances button grouping by function. Dynamic coloring may indicate "state" of some variable that this button affects.

Moving rectangular blocks of buttons around

Expanding button array to fit more buttons often requires existing buttons to be slightly rearranged. 'exch' command allows to do it on small scale. Direct manipulation of ACTION global does it on larger scale, but is inconvenient due to lack of convenient multidimensional indexing in J.

Tabs

The whole point is to have all commands available one click away. Having them on different tabs makes it two clicks away.