Random rows/columns in a csv file and print text with typewriter effect

You can get rid of frameRate and then use the timer only to advance the next letter

So around here

2 Likes

Thanks @Chrisir

Thanks Confused yet again, not sure how to equate timer seconds to full.lenght() as this they are not comparable?

I tried

else {
   timer = second(1); //timer equals a second
   timer++; //timer incremenets by a second?
   if (timer <= full.length()){ // if timer amount is less than full length
   counter++; //increase the counter by one to give typeWriter effect
 }

But obviously doesn’t work .

Also, I would like to keep full on screen for 2 seconds and then clear screen for 2 seconds before starting a new iteration. You’ve already said not to use delay (), should I use the same timer?

1 Like

Please distinguish between the two sketches you have now. Best save with separate names eg rowString1 and rowString2 and make a comment at the beginning of the sketches.

First one is working with frameRate

Second one is not working and is with timer

Don’t mix those up.

Now

Now for the timer. The timer is always the same - measuring how much time has passed since the timer was set - you just insert what has to happen when the time (1200) has passed. It’s like a stop watch.

The timer uses millis() which is the time since the sketch started.

At the end of setup() you say timer = millis();

And then in draw() :

if (millis()-timer > 1200) { // if 1200 millis have passed 
    counter++;      // go to next letter 
    timer=millis(); // reset timer 
   }

and kill frameRate here or say frameRate(60);

Thus you have full control over how fast the letter appear with the timer.

Chrisir

2 Likes

You called it correctly but the name of the function was not the best / appropriate (in terms of its purpose/ what’s it doing).

2 Likes

Thanks again @Chrisir.

For clarity, this is the sketch I am now working off //commented line by line with a description at top as you suggested.

/* This sketch randomly selects rows from a CSV file
these rows are then printed out to screen using 
a 'typewriter' style effect.
*/

Table table; // table object 
TableRow row;  //Table row object 
int timer; //creates a a timer variable
int counter = 0; //creates a counter variable and sets it to zero 
String amount="", name=""; //two strings repseneting columns in csv
String full; //a string to combine amount + name 
PFont font; //font 


void setup() {
  //size(500, 500); //size 
  fullScreen(); //sets output to full screen 
  table = loadTable("ip.csv", "header");
  smooth(); 
  font = createFont("Arial", 48); //creates font
  textFont(font, 120); //text font 
  textAlign(CENTER, CENTER); //allings text to Center
  selectRow(); //calls selectRow fucntion 
   timer=millis();
}

void draw() {
  
  background(0);//background colour
  fill(255);//text colour
  text(full.substring(0, counter), 0, 40, width, height); //the text to print to screen 
  
  if(counter >= full.length()){ // if my counter is more or equal to full String  
   counter=0; //set counter back to zero 
   selectRow(); //call selectRow()
  }  
if (millis()-timer > 100) { // if 1200 millis have passed 
    counter++;      // go to next letter 
    timer=millis(); // reset timer 
   }
  }

void selectRow() {  
    
    row = table.getRow((int)random(0, 3677)); //print a random row  
    amount = row.getString("amount"); //a string with amount
    name = row.getString("name"); // prints name for row
    full = amount + " " +  name;  //combines name and row into new String called full 
    println (full); //just prints to serial monitor so I can test al is working 
   
  }

What I think is the final piece (for now anyway) is this idea of keeping text on screen for 2 seconds followed by a blank screen for 2 seconds .

For this my pseudocode is like this:

if ( the rows have printed out fully){
wait two seconds;
Then clear screen:
wait anther two seconds;
the go through loop again;
}

Would using a boolean be the way to go?

1 Like

You could add a timer =millis (); here to make sure the first letter is displayed long enough

For your final piece: please don’t use delay but use one or 2 timer

Since we have a lot of different states here (wait, show, blank, wait…) I suggest to work with a variable state of type int showing you the state you are in.

Start with switch (state) {

Google state here in the forum and see below

Chrisir

1 Like

Hello,

I did not look through all the posts.
I was inspired by the “typewriter effect” and sharing my first rough version of code:

String s = " INCOMING TELETYPE MESSAGE TO FOLLOW... BE PREPARED!";

int x = 0;
float space;

void setup()
  {
  size(1000, 100, P2D);
  textSize(24);
  }

void draw()
  {
  background(0);
  for(int i = 0; i <= x; i++)
    {
    text(s.charAt(i), i*15, 50);  
    }
  space = 0;  
  
  if (frameCount%5 == 0)
    x++;
  if (x >= s.length())
    x = 0;
  }

My final version uses:
https://processing.org/reference/textWidth_.html for proper spacing
https://processing.org/reference/random_.html for staggered typing effect.
And other refinements.

:slight_smile:

1 Like

Continue my state idea. Like water can have different states like ice, water and vapor so can have your sketch: e.g. typewrite the word, choose new row, show a blank screen etc. - as you described before. The int variable state can be 0,1,2… each number signifies a different state of the sketch. The variable state holds the information in which state the sketch currently is.

Having states in the sketch makes it easier for you to have precise control over the sketch and over the code. It’s a powerful concept for handling different situations or screens. It is a powerful tool for different use cases.
E.g. in a game you have a splash screen, a game screen, a game over screen, a high score screen each with a different number of the variable state.

switch() is like if...else if....else if.....

In each state you do what is necessary in this state (e.g. show the typewriter).
Make sure the transition from one state to the next works. Either when typewriter has shown all letters of the current variable full or when a timer is over you move on to the next state (state=2; or state++;). Also, sometimes you want to initialize the next state e.g. by setting the timer to millis().

All this is in draw(). Nothing outside the switch-clause is allowed in draw(). The state concept is a powerful concept to structure and steer a sketch/ program. You always have an exact situation the sketch is in. Instead of different boolean variables that indicate situations (partially mutually exclusive…) you have only ONE control variable state that can have different states. Nice.

Example below.

switch (state) {

case 0:
// show growing text/ typewriter 
background (0);
counter++;
text(...
if(counter>=full.length()){ 
  counter = 0; // reset
  timer = millis();
  state=1;  // move on 
}
break;

case 1:
//wait I 
background (0);
if(millis()-timer>2000) {
selectRow();
state=2;    // move on
}
break;

case 2:
// not necessary but never mind 
state=0;   // move on / reset 
break;

default:
// error
println(”error“);
break;

}// switch

Chrisir

1 Like

Hey @Chrisir thanks for the input once again, really helpful.

Before I give this a try can I just ask you if I can call other functions within void draw using states?

As in can I still call selectRow() within draw () or I should I incorporate into draw()?

1 Like

Definitely call functions from draw() but call them inside the switch () clause

In fact in much bigger sketches people just call a function in each case/break section

2 Likes

I was able to get the sketch to pause using your code above @Chrisir but having a little problem keeping text on screen for two seconds .

void draw() {
  
switch (state){ //this is the start of switch 
case 0: //sets switch to 0
      
background (0); //background 

text(full.substring(0, counter), 0, 40, width, height); //text to print 

if (millis()-timer > 100) { //this is the typewriter effect 
    counter++;      // go to next letter 
    timer=millis(); // reset timer  
    if (millis()-timer > 2000) { //if 5 seconds has passed 
      state=1; //go to next state 
    }
}
    
case 1:
if(counter>=full.length()){ //this if statement resets counter to 0 when text is finsihed 
  counter = 0; // reset
  timer = millis();
  state=2;  // move on 
 
}

break;

case 2: //this case makes the screen blank for 2 seconds 
 background (0);
 if (millis()-timer > 2000) {
    selectRow();
    state=3;
 }

break;
 
case 3:
state=0;
break;
  }
}

As you can see I made another if Statement in the typewriter ‘section’

My pseudo being
If ( text is on screen) {
Leave it there for two seconds}

I tried to teat ‘leave text on screen’ as its own state:

void draw() {
  
switch (state){ //this is the start of switch 
case 0: //sets switch to 0
      
background (0); //background 

text(full.substring(0, counter), 0, 40, width, height); //text to print 

if (millis()-timer > 100) { //this is the typewriter effect 
    counter++;      // go to next letter 
    timer=millis(); // reset timer  
   
}
    
case 1:

 if (millis()-timer > 2000) { //if 5 seconds has passed 
      state=2; //go to next state 
    }
    break;
 

case 2:
if(counter>=full.length()){ //this if statement resets counter to 0 when text is finsihed 
  counter = 0; // reset
  timer = millis();
  state=3;  // move on 
 
}

break;

case 3: //this case makes the screen blank for 2 seconds 
 background (0);
 if (millis()-timer > 2000) {
    selectRow();
    state=4;
 }

break;
 
case 4:
state=0;
break;
  }
}

But this returned a string out of range error.

1 Like

Remember to close each case section with break !!!

2 Likes

mmmm, option with 4 cases

void draw() {
  
switch (state){ //this is the start of switch 
case 0: //sets switch to 0
      
background (0); //background 

text(full.substring(0, counter), 0, 40, width, height); //text to print 

if (millis()-timer > 100) { //this is the typewriter effect 
    counter++;      // go to next letter 
    timer=millis(); // reset timer  
   
}
break; //break for case 0
    
case 1:

 if (millis()-timer > 2000) { //if 5 seconds has passed 
      state=2; //go to next state 
    }
    break; //break for case 1
 

case 2:
if(counter>=full.length()){ //this if statement resets counter to 0 when text is finsihed 
  counter = 0; // reset
  timer = millis();
  state=3;  // move on  
}

break; //break for case 2 

case 3: //this case makes the screen blank for 2 seconds 
 background (0);
 if (millis()-timer > 2000) {
    selectRow();
    state=4;
 }

break; //break for case 3
 
case 4:
state=0;
break; //final break
  }
}

still returns out of range error, as does 3 cases

void draw() {
  
switch (state){ //this is the start of switch 
case 0: //sets switch to 0
      
background (0); //background 

text(full.substring(0, counter), 0, 40, width, height); //text to print 

if (millis()-timer > 100) { //this is the typewriter effect 
    counter++;      // go to next letter 
    timer=millis(); // reset timer  
    if (millis()-timer > 5000) { //if 5 seconds has passed 
      state=1; //go to next state 
    } 
}
 break; // break for case 0 
    
case 1:
if(counter>=full.length()){ //this if statement resets counter to 0 when text is finsihed 
  counter = 0; // reset
  timer = millis();
  state=2;  // move on 
 
}

break; //break for case 1 

case 2: //this case makes the screen blank for 2 seconds 
 background (0);
 if (millis()-timer > 2000) {
    selectRow();
    state=3;
 }

break; //break for case 2 
 
case 3:
state=0;
break; //break for final break 
  }
}
1 Like

Not really! It’s more that this section will be executed in the case that state is 0.

2 Likes

You need this if clause in the section where state is 0 !!

Here you increase counter, here you need to check it.

1 Like

Do you mean I increase counter and check it in state 0?

1 Like

Yes of course

Otherwise it will exceed full.length()

You can put all that in a function typewriter (that you call from case 0)

Also, remember to use ctrl-t regularly in processing to get auto indents / format

Chrisir

1 Like

Thanks @Chrisir

I haven’t created a function typewriter yet as I’m still trying to get it to work. But to be honest, I’m going around in circles (or loops).

I moved

if(counter>=full.length()){ 

up to case 0 per your suggestion, however this was just looping the same data each time.

So I moved tableRow() up to case 0 …which will now change data each iteration but now it no longer pauses between iterations and I still can’t get the text to stay on screen, so back to where I started yesterday morning.

void draw() {
  
switch (state){ //this is the start of switch 

case 0: //this will be exectued if state = 0
      
background (0); //background 

text(full.substring(0, counter), 0, 40, width, height); //text to print 

if (millis()-timer > 100) { //this is the typewriter effect 
   counter++;      // go to next letter 
   timer=millis(); // reset timer  

  }
    
if(counter>=full.length()){ //this if statement resets counter to 0 when text is finsihed 
     counter++;
     selectRow();
     counter = 0; // reset
    
     
     if (millis()-timer > 5000) { //if 5 seconds has passed 
      state=1; //go to next state
      } 
      
}
  
break; // break for case 0 
    
case 1: //this will be exectued if state = 1

  timer = millis();
  state=2;  // move on 
 
//}

break; //break for case 1 

case 2: ////this will be exectued if state = 2
 
  if (millis()-timer > 2000) {
    //selectRow();
    state=3;
}

break; //break for case 2 
 
case 3:
state=0;
break; //break for final break 
  
  }

}
1 Like

Think I got it :slight_smile:

This seems to work. @Chrisir if you wouldn’t mind taking a look a the code, I’ve tested it for the last 15 minutes and seems to be stable.

/* This sketch randomly selects rows from a CSV file
 these rows are then printed out to screen using 
 a 'typewriter' style effect.
 I want to thetext to print out and then stay on screen for 5 seconds
 I then want the screen to stay blank for 5 seconds before starting again. 
 */
int state; 
Table table; // table object 
TableRow row;  //Table row object 
int timer; //creates a a timer variable
int counter = 0; //creates a counter variable and sets it to zero 
String amount="", name=""; //two strings repseneting columns in csv
String full; //a string to combine amount + name 
PFont font; //font 

void setup() {
  size(1000, 1000); //size 
  // fullScreen(); //sets output to full screen 
  table = loadTable("ip.csv", "header");
  smooth(); 
  font = createFont("Arial", 48); //creates font
  textFont(font, 120); //text font 
  textAlign(CENTER, CENTER); //allings text to Center
  selectRow(); //calls selectRow fucntion 
  timer=millis();
  typeWriter();
}

void draw() {

  switch (state) { //this is the start of switch 

  case 0: //this will be exectued if state = 0

    background (0); //background 

    text(full.substring(0, counter), 0, 40, width, height); //text to print 

    if (millis()-timer > 100) { //this is the typewriter effect 
      counter++;      // go to next letter 
      timer=millis(); // reset timer
    }

    typeWriter();


    break; // break for case 0 

  case 1: //this will be exectued if state = 1

    if (millis()-timer > 5000) { //if 5 seconds has passed 
      timer=millis();
      state=2;  // move on
    }


    break; //break for case 1 

  case 2: ////this will be exectued if state = 2
    background(0);

    if (millis()-timer > 5000) {
      selectRow();
      state=3;
    }

    break; //break for case 2 
  case 3:
    state=0;
    break; //break for final break
  }
}


void typeWriter() {
  if (counter>=full.length()) { //this if statement resets counter to 0 when text is finsihed 
    counter++;
    counter = 0; // reset
    state=1;
  }
}

void selectRow() {  

  row = table.getRow((int)random(0, 3677)); //print a random row  
  amount = row.getString("amount"); //a string with amount
  name = row.getString("name"); // prints name for row
  full = amount + " " +  name;  //combines name and row into new String called full 
  println (full); //just prints to serial monitor so I can test al is working
}
2 Likes

Well done!

Congratulations!

A few remarks.

Remark

if (counter>=full.length()) {  
    counter++;  **// not necessary here !!!!**
    counter = 0; // reset

just use if (counter > full.length()) {

Remark

Not necessary to call typeWriter(); from setup()

The function typeWriter()

The function typeWriter() could hold more code:


void typeWriter() {
  text(full.substring(0, counter), 
    0, 40, width, height); //text to print 

  if (millis()-timer > 100) { //this is the typewriter effect 
    counter++;      // go to next letter 
    timer=millis(); // reset timer
  }

  if (counter>=full.length()) { //this if statement resets counter to 0 when text is finsihed 
    counter = 0; // reset
    state=1;
  }
}

Remark

You can move some variables into functions, when the variables are only used there.

Remark

This is unnecessary, in case 2 you can go to 0 directly

case 3:
    state=0;
    break; //break for final break

Here is my version:



/* This sketch randomly selects rows from a CSV file
 these rows are then printed out to screen using 
 a 'typewriter' style effect.
 I want the text to print out and then stay on screen for 5 seconds.
 I then want the screen to stay blank for 5 seconds before starting again. 
 */

int state=0; // state of the program 
Table table; // table object 
int timer; //creates a a timer variable
int counter = 0; //creates a counter variable and sets it to zero 
String full; //a string to combine amount + name 

void setup() {
  size(1000, 1000); //size
  // fullScreen(); //sets output to full screen 

  PFont font; //font 

  table = loadTable("ip.csv", "header");
  smooth(); 
  font = createFont("Arial", 48); //creates font
  textFont(font, 120); //text font 
  textAlign(CENTER, CENTER); //aligns text to Center
  selectRow(); //calls selectRow fucntion 
  timer=millis();
  // typeWriter();
}

void draw() {

  switch (state) { //this is the start of switch 

  case 0:
    // this will be exectued if state = 0
    // the typewriter 
    background (0); //background 
    typeWriter();
    break; // break for case 0 

  case 1:
    // this will be exectued if state = 1
    // the text stays on screen for 5 seconds.
    if (millis()-timer > 5000) { //if 5 seconds has passed 
      timer=millis();
      state=2;  // move on
    }
    break; //break for case 1 

  case 2:
    // this will be exectued if state = 2
    // the screen stays blank for 5 seconds before starting again. 
    background(0); //background 
    if (millis()-timer > 5000) {
      selectRow();
      state=0;
    }
    break; //break for case 2
    //
  }//switch
}//func 

// ---------------------------------------------------------------------

void typeWriter() {
  text(full.substring(0, counter), 
    0, 40, width, height); //text to print 

  if (millis()-timer > 100) { //this is the typewriter effect 
    counter++;      // go to next letter 
    timer=millis(); // reset timer
  }

  if (counter > full.length()) { //this if statement resets counter to 0 when text is finished 
    counter = 0; // reset
    state=1;
  }
}

void selectRow() {  

  TableRow row;  //Table row object 
  String amount="", name=""; //two strings representing columns in csv

  row = table.getRow((int)random(table.getRowCount())); //print a random row  
  amount = row.getString("amount"); //a string with amount
  name = row.getString("name"); // prints name for row
  full = amount + " " +  name;  //combines name and row into new String called full 
  // println (full); //just prints to serial monitor so I can test all is working
}
//

Congratulations again!!!

Chrisir

4 Likes