1

My JFrame containing an embedded single graph (Graphstream) freezes when I try to update it in a loop that calls Thread,sleep(). I have tried using the same update on a standalone-graph (displayed on it's own) and it works as expected.

I have a single graph embedded in JFrame as follows (AppGraph.java):

public static ViewPanel init(){

    graph.addAttribute("ui.stylesheet", styleSheet);
    graph.setAutoCreate(true);
    graph.setStrict(false);
    graph.addAttribute("ui.quality");
    graph.addAttribute("ui.antialias");

    initGraph();

    initNodes(graph);

    return attachViewPanel();

}

private static ViewPanel attachViewPanel() {
    Viewer viewer = new Viewer(graph, Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD);
    viewer.enableAutoLayout();
    return viewer.addDefaultView(false);
}

private static void initGraph(){
    FileSource fs = new FileSourceDOT();
    String graph_filename = "graph.gv";
    String absolute_path = System.getProperty("user.home") + File.separator + graph_filename;
    fs.addSink(graph);
    try {
        fs.readAll(absolute_path);
    } catch (IOException | NullPointerException e) {
        e.printStackTrace();
    } finally {
        fs.removeSink(graph);
    }
}

Then this is called in the JFrame class as below:

   /*AppWindow.java
    * Set up graph
    */
    GridBagConstraints graphConstraints = new GridBagConstraints();
    graphConstraints.fill = GridBagConstraints.BOTH;
    graphConstraints.gridx = 0;
    graphConstraints.gridy = 1;
    graphConstraints.weightx = 0.5;
    graphConstraints.weighty = 0.5;
    graphConstraints.gridwidth = 4;
    graphConstraints.gridheight = GridBagConstraints.RELATIVE;
    add(AppGraph.init(), graphConstraints);`

On the JFrame are buttons for different search algorithms like BFS. During the execution of these algorithms, edges traversed are colored at fixed time intervals to create a sort of animation effect as shown below:

   //BFSAlgorithm.java 
   private void callBFS(Node startNode, Node goalNode) {
            startNode.setAttribute("parent", "null");
            startNode.setAttribute("level", 0);
            startNode.setAttribute("visited?");
            LinkedList<Node> queueFrontier = new LinkedList<>();
            int level = 1;
            queueFrontier.addLast(startNode);
            while (!queueFrontier.isEmpty()) {
                System.out.println("Level: " + (level - 1));
                LinkedList<Node> next = new LinkedList<>();
                for (Node node : queueFrontier) {
                    if (node == goalNode) {
                        System.out.println(node.getId() + ": Found Found Found!!!");
                        if (node != startNode) {
                            colorEdge(node);
                        }
                        return;
                    }
                    System.out.print(node.getId() + " visited \t");
                    if (node != startNode) {
                        colorEdge(node);
                    }
                    for (Edge edge : node.getEdgeSet()) {
                        Node opposite = edge.getOpposite(node);
                        if (!opposite.hasAttribute("visited?")) {
                            System.out.print(opposite.getId() + " enqueued \t");
                            opposite.setAttribute("level", level);
                            opposite.setAttribute("parent", node);
                            opposite.setAttribute("visited?");
                            next.addLast(opposite);
                        }
                    }
                    System.out.print("\n");
                }
                level++;
                queueFrontier = next;
                sleep();
        }
    }

    private void colorEdge(Node node) {
        Edge visitedEdge = node.getEdgeBetween(node.getAttribute("parent", Node.class));
        visitedEdge.setAttribute("ui.color", 0.5);
        sleep();
    }

    private void sleep() {
        try {
            Thread.sleep(AppWindow.speed);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

This BFSAlgorithm implements DynamicAlgorithm and extends SinkAdapter. I have extended the SinkAdapter to enable it to interact with the View as the algorithm runs. When I call the BFSAlgorithm, while the algorithm runs and the various println statements are delayed by sleep(), the GUI freezes and is unresponsive until after execution before all the visited edges are then colored. I tried implementing ViewerListener in my AppGraph.java as is documented on the graphstream documentation but it only resulted in an infinite loop that crashed the application:

/*...init() method from AppGraph.java*/
ProxyPipe fromViewer = viewer.newThreadProxyOnGraphicGraph();
        fromViewer.addSink(graph);
        fromViewer.pump();

while(loop) {
            fromViewer.pump(); //

}
Behzad
  • 3,502
  • 4
  • 36
  • 63
Korogba
  • 105
  • 9
  • 2
    Have a look at [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/) and [How to use Swing Timers](http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) and [Worker Threads and SwingWorker](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html) – MadProgrammer Mar 05 '16 at 07:51
  • Well I don't have enought time to answer but you should use a [Swing Timer](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) instead – Frakcool Mar 05 '16 at 07:51
  • try re-validating the JPanel that your graph is on – rert588 Mar 05 '16 at 07:54
  • @rert588 Do you mean call repaint() method in the BFSAlgorithm? – Korogba Mar 05 '16 at 09:28
  • @MadProgrammer and Frakcool Thanks! I used a [Swing Timer](http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) after reading up on the SwingWorker and Consurrency. I dont think you can mark comments as answers though – Korogba Mar 05 '16 at 18:20
  • @Korogba no, but you can answer your own question and mark it as accepted answer :) (Just be sure to place your code too) – Frakcool Mar 06 '16 at 05:42

1 Answers1

1

Like @Frakool and @MadProgrammer suggested in the comments, if anyone is having similar issues, using SwingWorker and Swing Timer will provide the desired results. According to the documentation:

In general, we recommend using Swing timers rather than general-purpose timers for GUI-related tasks because Swing timers all share the same, pre-existing timer thread and the GUI-related task automatically executes on the event-dispatch thread. However, you might use a general-purpose timer if you don't plan on touching the GUI from the timer, or need to perform lengthy processing.

Here's how I used it to stop the gui freezing. I created a private inner SwingWorker class that uses a Swing Timer as below:

private class BFSTask extends SwingWorker<LinkedList<Node>, Node>{
    private ArrayList<Node> visitedList;
    private int visitedIndex = 0;
    private boolean traversalDone = false;
    private Timer traversal = new Timer(AppWindow.speed, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent actionEvent) {
            Node lastVisited = visitedList.get(visitedIndex);
            Edge visitedEdge = lastVisited.getEdgeBetween(lastVisited.getAttribute("parent", Node.class));
            visitedEdge.setAttribute("ui.color", 0.5);
            visitedIndex++;
            if(visitedIndex >= visitedList.size()){
                traversal.stop();
                traversalDone = true;
                if(BFSAlgorithm.this.getPathToGoal() != null){
                    startTimer();
                }
            }
        }
    });

     @Override
    protected LinkedList<Node> doInBackground() throws Exception {
        Node found = publishNodeBreadthFirst(getStartNode(), getGoalNode());
        if (found != null) {
            return getPathToGoal(found);
        } else{
            return null;
        }
    }

    @Override
    protected void process(List<Node> list) {
        visitedList = (ArrayList<Node>) list;
        traversal.start();
    }

    @Override
    protected void done() {
        try {
            BFSAlgorithm.this.pathToGoal = get();
            if(traversalDone && BFSAlgorithm.this.getPathToGoal() != null){
                startTimer();
            }
            if(BFSAlgorithm.this.getPathToGoal() == null){
                throw new NullPointerException("Goal Not Found.");
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } catch (NullPointerException e){
            JOptionPane.showMessageDialog(getAppWindow(), "Goal Node Not Found!", "Error", JOptionPane.ERROR_MESSAGE);
            getAppWindow().disableExceptClear();
            getAppWindow().changeStatus("Goal node not found");

        }
    }

    private LinkedList<Node> getPathToGoal(Node found) {
        LinkedList<Node> path = new LinkedList<>();
        Node parent = found.getAttribute("parent");
        path.addLast(found);
        while (parent != getStartNode()){
            path.addLast(parent);
            parent = parent.getAttribute("parent");
        }
        return path;
    }
}
Korogba
  • 105
  • 9
  • I'm having a similar problem as you had. When i call my class that contains the graph, it doesnt render the nodes. So i used a SwingWorker to put the graph on another thread. Ok, it rendered, but now the viewlistener doesnt work, i do not have answer from the user interaction (clicking on nodes). You can see here. http://stackoverflow.com/questions/36899115/graphstream-view-not-loading/36900197 – PlayMa256 Apr 29 '16 at 21:33