Creating a grid of toggle buttons

Hello,

I wish to create a GUI to control the ON/OFF state of an 8 by 8 array of LEDs. What I’d like the GUI to look like would be an 8 by 8 array of toggle buttons, one for each LED, to toggle each LED ON/OFF accordingly. The LED array would only update when the user presses a separate “Update Array” button, and not every time they toggle one of the 8x8 buttons.

This is how the hardware works, to give some background to what information I need to get from these buttons:

This array is controlled by a MAX7221 driver chip and an Arduino. To define which LEDs are ON in a particular row in the grid, you first write one byte to specify the Register associated with that row, then a second byte to define which LED “columns” are ON in that row. For example:

  • (0x01, 0xFF) would switch on all the LEDs (0xFF = 1111,1111) in row 0 (Register 0x01)
  • (0x08, 0x81) would switch on the first and last LEDs (0x81 = 1000,0001) in row 7 (Register 0x08).

So, when the user presses “Update array” I’d like the GUI to check, row by row, which toggle buttons are on in that row and generate a matching byte. For example, if the toggle buttons in the first row were:

  • ON ON ON OFF OFF OFF OFF ON

I’d like to generate the byte “E1” (1110,0001), which would be sent to the Arduino along with the corresponding register address (e.g. 0x01, 0xE1). I’d then do that for the other 7 rows.

If anyone has any advice on how to a) create those buttons and b) read a byte from each row that would be great. The rest of the problem I can (mostly) figure out myself.

Thanks in advance!

here is a array of class of rectangles
( with too many features you not need to use )

where a mouse click switch ONE LED ON / OFF ( mouse left/right button )
and a key [c] clears all.

you might need to make a add function like
key [s] send:
read array of class variable selected to your byte array for send to arduino…

// https://discourse.processing.org/t/scalable-grid-with-rectangles/7256
// https://discourse.processing.org/t/better-grid-performance/7314
// https://discourse.processing.org/t/gryd-system-graphic-design/10457
// https://discourse.processing.org/t/change-rectangles-seperately-in-for-loop/10908/2
// https://discourse.processing.org/t/creating-a-grid-of-toggle-buttons/12162
// use backup memory array for resize/reinit grid but remember selected rects

int x = 30, y = x, w = 50, h = w, off= 5, grid = 8, many = grid*grid;
float rang = 0, strokew=1.0;
boolean auto = false;//false;//true;
boolean shownum    = false;    // key [n]
boolean showFPS    = true;
boolean togfill    = false;

// color setup:
color bg  = color(200, 200, 0);
color stk = color(0, 0, 200);
color fillsel = color(0, 200, 0);
color fillnsel= color(200, 0, 200);

Myrect[]   myrects   = new Myrect[many];
Mybackup[] myselects = new Mybackup[many];  // temporary memory

void setup() {
  size(800, 520);
  noSmooth();
  set_grid(true);     // init
  String help = "";
  help += "use: mouse LEFT to select, mouse RIGHT to deselect\n";
  help += "key UP DOWN RIGHT LEFT for position\n";
  help += "key [+]  [-]  for grid size\n";
  help += "key [n] toggle shownumber\n";
  help += "key [f] toggle fill\n";
  help += "key [c] clear selected\n";
  help += "____MouseWheel Plus Plus ____\n";
  help += "use key [x] [y] [w] [h] [o] for position and size\n";
  help += "use key [r] rotate / key [s] stroke width\n";
  help += "and turn mouse wheel";
  println(help);
}

void draw() {
  background(bg);
  fill(0);
  if (showFPS ) text(nf(frameRate, 1, 1), 10, 10);
  for (int i = 0; i < many; i++) myrects[i].drawit();               // draw each rect ( and check on mouse over + click )
}

class Mybackup {
  boolean selected=false;
}

class Myrect {
  int x, y, id;//, w, h, off, rang; from global
  boolean selected=false;
  Myrect (int x, int y, int id) {
    this.x = x; 
    this.y = y;
    this.id = id;
  }
  
  void drawit() {
    if ( strokew > 0.2 )  { strokeWeight(strokew); stroke(stk); }
    else                  noStroke();
    if ( selected )  fill(fillsel);
    else             fill(fillnsel);
    if ( togfill )   noFill();
    push();
    translate(x,y);
    rotate(rang);
    rect(-(w-off)/2,-(h-off)/2, (w-off), (h-off));
    fill(0);   // black text
    if (shownum) text(id, -w/8, +h/8);
    pop();
    sel();
  }
  
  boolean over() {
    return( x-(w-off)/2 < mouseX && mouseX < x+(w-off)/2 && y-(h-off)/2 < mouseY && mouseY < y+(h-off)/2 );
  }
  
  void sel() {
    if ( over() ) {
      if (  selected && mousePressed && mouseButton == RIGHT) selected=false;
      if ( !selected && mousePressed && mouseButton == LEFT)  selected=true;
    }
  }
}

void set_wh() {
  // use x,y,grid as master and auto adjust rectangles (w,h) to window:
  println(" width= "+width+", height= "+height+", grid= "+grid);
  w = ( width  - 2 * x ) / grid-off;   
  println(" w= "+w+", x= "+x+", ( grid * w + 2 * x )= "+(grid*w+2*x));
  h = ( height - 2 * y ) / grid-off;
  println(" h= "+h+", y= "+y+", ( grid * h + 2 * y )= "+(grid*h+2*y));
}

void set_grid(boolean init) {
  if ( auto ) set_wh();
  if ( init )  for (int i = 0; i < many; i++)  myselects[i]=new Mybackup();                     // init backup memory
  else         for (int i = 0; i < many; i++)  myselects[i].selected = myrects[i].selected;     // backup
  for (int i = 0; i < many; i++)  myrects[i]=new Myrect(x+(i%grid)*w, y+(floor(i/grid))*h, i);  // resize
  if ( !init ) for (int i = 0; i < many; i++)  myrects[i].selected = myselects[i].selected;     // restore
}

void clear_selected() {
  for (int i = 0; i < many; i++)  myrects[i].selected = false; 
  println("clear selected after key [c]"); 
}

void keyPressed() {
  if      ( keyCode == UP )    y--;
  else if ( keyCode == DOWN )  y++;
  else if ( keyCode == LEFT )  x--;
  else if ( keyCode == RIGHT ) x++;
  else if ( key     == '+' )   { w++; h++; }
  else if ( key     == '-' )   { w--; h--; }
  //h=w;              // quadrat only
  auto = false;     // confirm
  set_grid(false);  // resize
  if ( key == 'n' ) shownum = !shownum;
  if ( key == 'f' ) togfill = !togfill;
  if ( key == 'c' ) clear_selected();
}

void mouseWheel(MouseEvent event) {
  float e = event.getCount(); //println(e);
  if ( keyPressed && key == 'x' ) { 
    x += e ;
    println(" key x: x "+x);
  }
  if ( keyPressed && key == 'y' ) { 
    y += e;
    println(" key y: y "+y);
  }
  if ( keyPressed && key == 'w' ) { 
    w += e;
    w=constrain(w,0,width);
    println(" key w: w "+w);
  }
  if ( keyPressed && key == 'h' ) { 
    h += e;
    h=constrain(h,0,height);
    println(" key h: h "+h);
  }
  if ( keyPressed && key == 'o' ) { 
    off += e;
    off=constrain(off,0,w);
    println(" key o: off "+off);
  }
  if ( keyPressed && key == 'r' ) { 
    rang += e*0.03;
    println(" key r: rang "+int(degrees(rang)));
  }
  if ( keyPressed && key == 's' ) { 
    strokew += e*0.1;
    strokew = constrain(strokew,0,w/2);
    println(" key s: strokew "+nf(strokew,1,1));
  }
}

with p5.js i have this old sketch
https://editor.p5js.org/kll/sketches/QUnp7YbGv

I’m not sure, bit it might be possible to manage generation of this with the p5.gui library. Haven’t tried with toggle buttons, they might not be supported “in bulk” in the way that you want, although it could potentially be modified.

1 Like

Seems like this p5.gui is mostly a wrapper over the dat.gui library: :smirk:

2 Likes

Hi,

Thanks for your suggestion (and to the others for theirs too). I’ll if I can make progress with that.