G4P: TextArea Scrolling NullPointerException

edited July 2014 in Library Questions

Hello! I have made a program, that has a thread once in 2 seconds writing 5(or more) lines to G4P textarea. To be able to look through those lines I added scrollbars, but when I start to scroll my program gives error:

java.lang.NullPointerException at g4p_controls.StyledString.getLines(Unknown Source) at g4p_controls.GTextArea.appendText(Unknown Source) at CanDecoder.UARTread(CanDecoder.java:54) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at processing.core.PApplet.method(PApplet.java:4167) at processing.core.PApplet$4.run(PApplet.java:4197)

If I move the scrollbar up among writings it crashes with the same error when it comes time to write lines and textarea tries to move cursor to the last line. Any ideas how to fix this? Maybe there is a possibility to stop autoscroll? There is a method to move cursor to the end of text in G4P API(moveCaretEndOfText (TextLayoutHitInfo currPos)), but it requires something I don't understand.

I also had an error writing 5 lines, but it was solved when I added 100ms delay between writings

Thanks in advance Best regards, Paul

Answers

  • If your program code is short can you post it here or create a simple sketch to demonstrate the error?

  • main:

    // Need G4P library
    import g4p_controls.*;
    StringList text = new StringList();
    void write() {
      while (true) {
        for (int i = 0; i<text.size (); i++) {
          textarea1.appendText(text.get(i));
          delay(100);
        }
        delay(2000);
      }
    }
    
    public void setup() {
      text.append("Hello world");
      text.append("Hello again");
      text.append("Hello once again");
      text.append("Hello for last time");
      text.append("Hello this time last for sure");
      text.append("Goodbye, cruel world!");
      size(800, 500, JAVA2D);
      createGUI();
      customGUI();
      // Place your setup code here
    }
    
    public void draw() {
      background(230);
    }
    
    // Use this method to add additional statements
    // to customise the GUI controls
    public void customGUI() {
    }
    

    gui:

    public void button1_click1(GButton source, GEvent event) { //_CODE_:button1:351408:
      println("button1 - GButton event occured " + System.currentTimeMillis()%10000000 );
      thread("write");
    } //_CODE_:button1:351408:
    
    public void textarea1_change1(GTextArea source, GEvent event) { //_CODE_:textarea1:504313:
      println("textarea1 - GTextArea event occured " + System.currentTimeMillis()%10000000 );
    } //_CODE_:textarea1:504313:
    
    
    
    // Create all the GUI controls. 
    // autogenerated do not edit
    public void createGUI(){
      G4P.messagesEnabled(false);
      G4P.setGlobalColorScheme(GCScheme.BLUE_SCHEME);
      G4P.setCursor(ARROW);
      if(frame != null)
        frame.setTitle("Sketch Window");
      button1 = new GButton(this, 35, 34, 80, 30);
      button1.setText("Face text");
      button1.addEventHandler(this, "button1_click1");
      textarea1 = new GTextArea(this, 158, 40, 532, 384, G4P.SCROLLBARS_VERTICAL_ONLY |  G4P.SCROLLBARS_AUTOHIDE);
      textarea1.setOpaque(true);
      textarea1.addEventHandler(this, "textarea1_change1");
    }
    
    // Variable declarations 
    // autogenerated do not edit
    GButton button1; 
    GTextArea textarea1; 
    

    Explanation: Run, press the button, wait for about 7 cycles(14 seconds), move scrollbar to upper position, enjoy:)

  • If you change the write() and buttonClick() methods to

    void write() {
      for (int i = 0; i<text.size (); i++) 
        textarea1.appendText(text.get(i));
    }
    

    and

    public void button1_click1(GButton source, GEvent event) { //_CODE_:button1:351408:
      println("button1 - GButton event occured " + System.currentTimeMillis()%10000000 );
      write();
    } //_CODE_:button1:351408:
    

    Then 6 lines (with no delay) are appended every time the button is clicked :)

    In your original program the statement thread("write"); causes the write method to be called asynchronously in another thread. It meant that appendText was being called before the last appendText code had finished. That is why adding a 100ms delay worked because it made sure the previous call to appendThread had time to finish.

    You should not modify any G4P controls in an asynchronous thread.

  • I write UART messages(from Arduino) to textarea, so clicking all the time wouldn't be a good idea. Of course I knew about asynchronous using of appendText, that’s why I solved this problem quite fast. But taking this loop out of thread to main cycle would slow down GUI. I have thread that appends text to textarea and button that can be pressed any time to change message look before it goes to textarea. So neither moving loop to button event, nor to draw() would be solution for my problem. The only thing left is to stop appending text to read previous messages. Is there a possibility to make scroll panel invisible/visible? I could make button to stop/resume appending text and in this moment scroll panel should appear/hide

  • The solution is not to use appendText from a secondary thread rather add the new data to the end of a LinkedList.

    In the main sketch register the pre() method to read data from the front of the list and append it to the text area.

    You need to use a boolean flag to make sure the list is not being read or written to at the same time.

  • edited July 2014 Answer ✓

    The following sketch demonstrates what I mean in my last comment. Please note no NPE is thrown :-bd

    // Need G4P library
    import g4p_controls.*;
    import java.util.*;
    
    String[] dummy = {
      "Mary, Mary, quite contrary,                           ", 
      "How does your garden grow?                         ", 
      "With silver bells, and cockle shells,               ", 
      "And pretty maids all in a row.                      ", 
      "----------------------------------    "
    };
    
    LinkedList<String> buffer = new LinkedList<String>();
    
    boolean available = true;
    boolean inputAvailable = true;
    boolean clearTextArea = false;
    int ln = 0, bufferSize;
    
    void write() {
      while (inputAvailable) {
        if (available) {
          available = false;
          buffer.addLast(dummy[ln] + millis());
          ln = ++ln % dummy.length; // increment and wrap index
          available = true;
          // simulate data arriving at ~ 40 items per second 
          // i.e. 25 ms interval between data items.
          delay(25);
          // this will give the main thread a chance to process
          // data on the buffer.
        }
      }
    }
    
    void pre() {
      if (clearTextArea) {
        textarea1.setText("");
        clearTextArea = false;
      }
      if (available && !buffer.isEmpty()) {
        available = false;
        textarea1.appendText(buffer.removeFirst());
        bufferSize = buffer.size();
        available = true;
      }
    }
    
    public void setup() {
      size(800, 500, JAVA2D);
      createGUI();
      customGUI();
      // Place your setup code here
      registerMethod("pre", this);
      thread("write");
    }
    
    
    public void draw() {
      background(200, 200, 240);
      fill(0);
      text("Buffer size " + bufferSize, 8, 20);
    }
    
    // Use this method to add additional statements
    // to customise the GUI controls
    public void customGUI() {
    }
    
    public void button1_click1(GButton source, GEvent event) { //_CODE_:button1:351408:
      clearTextArea = true;
    } //_CODE_:button1:351408:
    
    // Create all the GUI controls.
    // autogenerated do not edit
    public void createGUI() {
      G4P.messagesEnabled(false);
      G4P.setGlobalColorScheme(GCScheme.BLUE_SCHEME);
      G4P.setCursor(ARROW);
      if (frame != null)
        frame.setTitle("Sketch Window");
      button1 = new GButton(this, 35, 60, 80, 30);
      button1.setText("Clear Text");
      button1.addEventHandler(this, "button1_click1");
      textarea1 = new GTextArea(this, 158, 60, 532, 384, G4P.SCROLLBARS_VERTICAL_ONLY |  G4P.SCROLLBARS_AUTOHIDE);
      textarea1.setOpaque(true);
    }
    
    // Variable declarations
    // autogenerated do not edit
    GButton button1;
    GTextArea textarea1; 
    
  • I solved the problem changing delays in writing to timers and putting writing to main cycle. So now program prepares text to write in thread, but writes it to textarea when timer passes 2 seconds, so program doesn't stop for 2 seconds by delay, but works and waits for timer event

Sign In or Register to comment.