tags:

views:

215

answers:

5

Hi all,

Have you ever heard about a GUI freezing because of repeated calls to the method javax.swing.Document.insertString ??

There is my code:

private int insertNotation(GameNode startNode, StyledDocument doc, int pos) {

    String s = "";
    int startPos = pos;
    boolean isContinuous = false;

    Style boldStyle, regularStyle, commentStyle, currentNodeStyle, nagStyle, grayStyle;
    grayStyle = notationTextPane.getStyle("gray");
    GameNode currentNode = history.getCurrentGameNode();
    if ((currentNode.isLeaf() && startNode == currentNode.getParent()) || startNode == currentNode) {

    try {
        if (startNode.getComment().length() > 0) {
            s = startNode.getComment() + " ";
            commentStyle.addAttribute("gameNode", startNode);
            doc.insertString(pos, s, commentStyle);
            pos += s.length();
        }
        for (int n = 0; n < startNode.getChildCount(); n++) {
            GameNode node = (GameNode) startNode.getChildAt(n);
            boolean isCurrentNode = (node == currentNode);
            if (node.isLeaf()) {
                if (node.isWhiteMove()) {
                    s = node.getFullMoveNumber() + ". ";
                    boldStyle.addAttribute("gameNode", node);
                    doc.insertString(pos, s, boldStyle);
                    pos += s.length();
                    s = node.getMove();
                    Style style = isCurrentNode ? currentNodeStyle : regularStyle;
                    style.addAttribute("gameNode", node);
                    doc.insertString(pos, s, style);
                    pos += s.length();
                    isContinuous = true;
                } else {
                    if (isContinuous) {
                        s = node.getMove();
                        Style style = isCurrentNode ? currentNodeStyle : regularStyle;
                        style.addAttribute("gameNode", node);
                        doc.insertString(pos, s, style);
                        pos += s.length();
                    } else {
                        isContinuous = true;
                        s = node.getFullMoveNumber() + "... ";
                        boldStyle.addAttribute("gameNode", node);
                        doc.insertString(pos, s, boldStyle);
                        pos += s.length();
                        s = node.getMove();
                        Style style = isCurrentNode ? currentNodeStyle : regularStyle;
                        style.addAttribute("gameNode", node);
                        doc.insertString(pos, s, style);
                        pos += s.length();
                    }
                }
               doc.insertString(pos++, " ", regularStyle);
        }
    } catch (BadLocationException e) {
     e.printStackTrace();
    }
    return pos - startPos;
}

I simplified it a lot but as you can see, there are many calls to the insertString() method in my 'doc' StyledDocument variable. This StyledDocument object is added in a JTabbedPane.

I have read here (in Performance Analysis section) that javax.swing.Document.insertString method is extremely slow (here over 1 ms per call).

Repeated calls to it can freeze the GUI???

Thank you for your help!!

+3  A: 

Whenever you do something slow in the main GUI thread, you will freeze the GUI. It redraws based on processing events. Imagine that your event handling code is in a while loop pulling events off a queue -- if, you don't return from your function, the next event can't be processed.

Consider doing long-running or slow processing in a background thread.

See this article: http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html

Lou Franco
A: 

You should look into running your repeated call within a Thread. That should do it for you.

Helen Neely
That will actually mess up your GUI completely. All manipulation of a Swing GUI *must* happen in the Event Dispatch Thread.
Michael Borgwardt
Technically you're supposed to be able to edit documents in a multithreaded fashion. In practice the API is too weak to permit that.
Tom Hawtin - tackline
A: 

Are you actually seeing a freeze (no forward progress), or just very severe slowdown?

We don't have your entire program code, so it's not clear, but is your document already being displayed when you start changing it? Each call to insertString not only modifies the document, but creates a cascade of events, to which the GUI reacts.

If you are essentially building the document and you only want to display the final state, you may want to consider building a new document before adding it to the text component widget, and then setting the ready document. It'll be a little more flicker, but much faster, since until you add your document to the widget with setDocument, you're not really triggering events and updates.

You can then build the document on a different thread.

Uri
+1  A: 

Consider using a background thread to throttle the addition of text to your document. This is best accomplished using a SwingWorker.

First we define queue for throttling. Requests to insert text will simply add to this queue. These requests do not have to be on the Event Dispatch thread.

BlockingQueue<String> toAdd = new LinkedBlockingQueue<String>();
toAdd.add("Some text");
toAdd.add("Some more text");

Next we invoke SwingWorker where background thread continuously polls the queue and publishes results back to the Event Dispatch thread in chunks.

new SwingWorker<Void, String>() {
   // Implementation of 'process' and 'doInBackground' methods to go here.
}.execute();

Now we implement doInBackground to poll until input queue is empty and then publish back to the Event Dispatch thread in one go for more efficient throttling.

  public String doInBackground() {
    while (!Thread.interrupted()) {
      List<String> l = new LinkedList<String>();
      String s = toAdd.poll();

      if (s == null) {
        publish(l.toArray(new String[l.size()]));
        l.clear();
      } else {
        l.add(s);
      }
    }

    // Thread interrupted but publish anything pending before returning.
    if (!l.isEmpty()) {
      publish(l.toArray(new String[l.size()]));
    }

    return null;
  }

Finally we implement process. This is called on Swing thread following a call to publish on the background thread. We join chunks together using a StringBuilder to avoid the need for multiple inserts into document (this the main advantage with this approach).

  public void process(String... chunks) {
    StringBuilder sb = new StringBuilder();
    for (String chunk : chunks) {
      sb.append(chunk);
    }

    // Insert sb.toString() into buffer HERE
  }
Adamski
A: 

Hi all!

The problem is gone as I used a SwingWorker object to alleviate the GUI thread: the calls to javax.swing.Document.insertString was in this SwingWorker.

I also had an other resolved bug: there was too many graphics overloaded treatment process. In this case too I used a SwingWorker.

Here is the code:

 new SwingWorker<Void, Void>()
 {
  @Override
  protected Void doInBackground() throws Exception
   {
    visualBoard.update(); // here your method call, deported from the GUI thread. 
    return null;
   }

 }.execute();

And thank you all for your so fast answers!!