User:Andrew Nikitin/buttongrid
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: [{{#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.