3

The issue here is that after asking a user for the settings of a neural network through a settings JFrame, the new JFrame meant to visualise the network learning only seems to display something after the network is done looping through all the data.

I believe this is because I use a SwingWorker and the loop doesn't wait for it to finish doing the calculations and displaying the result before going onto the next cycle.

Step 1: I ask the user for parameters with a JFrame

public class Settings {

private int width = 1920 / 4;
private int height = 1080 / 4;

private JFrame settings;
private JButton startButton;

public static void main(String[] args) {
    Settings settings = new Settings();
    settings.start();
}

private void start() {
    settings = new JFrame();
    settings.setTitle("Settings");
    settings.setSize(width, height);
    settings.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    startButton = new JButton("start");
    startButton.addActionListener(new FieldListener());
    settings.getContentPane().add(startButton);

    settings.pack();
    settings.setVisible(true);
}

class FieldListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == startButton) {
            prepare();
        }
    }
}

public void prepare() {
    Control control = new Control();
    control.start(amountOfNeurons);
}

Step 2: A control class creates the neural network with the specified parameters, and then feeds it the data

public class Control {

public void start(int amountOfNeurons) {
    Network net = new Network(amountOfNeurons);
    int[][] data = getData();
    net.startLearning(data);
}

Step 3: The network iterates through the data given to learn

public class Network {

int amountOfNeurons;
Visualiser vis;

public Network(int amountOfNeurons) {
    this.amountOfNeurons = amountOfNeurons;
}

public void startLearning(int[][] data) {
    vis = new Visualiser();
    JFrame graph = new JFrame();
    graph.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    graph.setSize(1920, 1080);
    graph.setTitle("Graph");
    graph.getContentPane().add(vis);
    graph.setResizable(false);
    graph.pack();
    graph.setVisible(true);

    for(int i = 0; i < data.length; i++) {
        new TrainTask(data[i]);
    }
}

class TrainTask extends SwingWorker<Void,Void> {
    int[] data;


    public TrainTask(int[] data) {
        this.data = data;
    }

    @Override
    public Void doInBackground() {
        for(int i = 0; i < data.length; i++) {
            calculate(data);
            vis.result = calculate(data);
            vis.repaint();
            System.out.println(i);
        }
        return null;
    }
}

As @c0der and @Frakcool suggested I use a SwingWorker to do the heavy load, i.e. loop through the data and update the visualiser

But the program continues without waiting on the response of the SwingWorker... I would like to try invokeAndWait() so that the program waits, but the network itself is run on the EDT, so it causes the program to crash.

What should I do differently?

P.S. I would like to point out that when I don't create the settings class, and create the network from a main method in the control class, everything seems to work fine...

  • 1
    *"The issue is that the visualiser isn't displaying anything until the loop ends."* Well, see [Concurrency in Swing](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/simple.html). You need to either use a [`Swing Timer`](https://docs.oracle.com/javase/7/docs/api/javax/swing/Timer.html) or a [`Swing Worker`](https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html) for this task. However for better help sooner, post a proper [mcve]. Your loop is blocking the EDT until it finishes, so that's why it's better to use one of the alternatives already mentioned – Frakcool Jun 15 '18 at 20:01
  • @Frakcool I would like to use a InvokeAndWait, the issue is that it seems like all my classes are running on the EDT thread... So I'll try to use a Swing Worker as you suggested, but they seem complicated to work with... I have tried using a Swing Timer in my loop, yet it doesn't seem to work with a null parameter instead of an action listener – Tugdual Kerjan Jun 15 '18 at 20:18
  • 1
    Providing a [mcve] of a sample of what you're trying to do might help, as well as a screenshot maybe... – Frakcool Jun 15 '18 at 21:32
  • 1
    See also [The Use of Multiple JFrames, Good/Bad Practice?](http://stackoverflow.com/q/9554636/418556) – Andrew Thompson Jun 15 '18 at 23:45
  • 1
    *"But the program continues without waiting on the response of the SwingVisualiser it seems..."* What is a SwingVisualiser? And more importantly, when will we see that MCVE as suggested by @Frakcool over 19 hours ago? *"without waiting on the response"* Could be a case for a modal `JDialog`. I say 'could be' because the problem is still not clear to me (mostly for lack of code I can work with). Have you read my answer linked in first comment yet? – Andrew Thompson Jun 16 '18 at 15:12
  • Sorry, SwingVisualiser is a typo - I meant the SwingWorker... as for the MCVE I will upload it in a while (It's exam week in France :P) – Tugdual Kerjan Jun 17 '18 at 07:52

2 Answers2

1

The code posted is not mcve because prepare is never invoked. Network executes a long process which blocks the EDT. Once this process is invoked, statements following it are not executed until the long process ends.
This can be demonstrated easily
by this mcve:

import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Network {

    Network() throws InterruptedException {

        Visualiser visualiser = new Visualiser();
        JFrame graph = new JFrame();
        graph.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        graph.setLocation(new Point(150,150));
        graph.getContentPane().add(visualiser);
        graph.pack();
        graph.setVisible(true);

        for(int i = 0; i < 100; i++) {
            visualiser.setText(String.valueOf(i));
            Thread.sleep(500);
            visualiser.repaint();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        JFrame settings = new JFrame();
        settings.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        settings.add(new JLabel("settings"));
        new Network(); //invoke Network before settings is made visible 
        settings.pack();
        settings.setVisible(true);
    }
}

class Visualiser extends JPanel {

    JLabel lable;
    Visualiser(){
        lable = new JLabel("0");
        add(lable);
    }

    void setText(String s) {
        lable.setText(s);
    }
}

settings becomes visible only after counting ends.
Changing the execution order to

settings.setVisible(true);
new Network();

causes settings to show before counting is executed.
However this is not the right cure for this problem. It is posted to explain the problem.
Long processes should not be invoked on EDT. The right solution, as suggested by Fracool is to use a SwingWorker to take the long process OFF the EDT:

public class Network {

    Visualiser visualiser;
    Network() throws InterruptedException {

        visualiser = new Visualiser();
        JFrame graph = new JFrame();
        graph.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        graph.setLocation(new Point(150,150));
        graph.getContentPane().add(visualiser);
        graph.pack();
        graph.setVisible(true);
        new VisulizerUpdateTask().execute();
    }

    //use swing worker perform long task
    class VisulizerUpdateTask extends SwingWorker<Void,Void> {

        @Override
        public Void doInBackground() {
            for(int i = 0; i < 100; i++) {
                visualiser.setText(String.valueOf(i));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ex) { ex.printStackTrace();   }
                visualiser.repaint();
            }
            return null;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        JFrame settings = new JFrame();
        settings.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        settings.add(new JLabel("settings"));
        new Network();
        settings.pack();
        settings.setVisible(true);
    }
}

Also note the link posted by Andrew Thompson.

c0der
  • 18,467
  • 6
  • 33
  • 65
  • Thanks a lot for the clarification, I now see what is wrong. Question: If I use SwingWorker it seems like the rest of the program continues without waiting for the SwingWorker to finish it's execution. How can I solve this? – Tugdual Kerjan Jun 16 '18 at 11:06
  • Please post a new question and send me a message here. – c0der Jun 17 '18 at 11:04
1

Alright! Thanks to the answer of c0der, and some persistence I managed to get it working.
I was trying to create two JFrames, first one for settings of the neural network and second one for the visual process of it learning.

The issue was that the second JFrame only displayed graphics at the end of the learning phase. This is because it was running heavy work on the EDT thread, and so EDT wouldn't update till it was done looping through the data.
Solved this thanks to c0der and Frakcool's suggestion to use a SwingWorker.

Here is what I started with:

After receiving the settings, we create a network and display it, and move the heavy work to a SwingWorker:

public Network(int amountOfIterations) {
    graph = new JFrame();
    vis = new Visualiser();
    graph.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    graph.getContentPane().add(vis);
    graph.setSize(1920, 1080);
    graph.setTitle("Graph");
    graph.pack();
    graph.setVisible(true);

    for(int i = 0; i < iterations; i++) {
        new train(i).execute();
        System.out.println(i);
    }
}

class train extends SwingWorker<Void,Void> {

    int i;
    public train(int i) {
        this.i = i;
    }

    @Override
    public Void doInBackground() {
        //Do some heavy work
        int x = 0;
        for(int y = 0; y < 10000000; y++) {
            x = x + y;
        }
        vis.iterated = i;
        vis.repaint();
        System.out.println(i + "  " + x);

        return null;
    }
}

The issue I had was that I put the iterations loop out of the SwingWorker. Thus the network constantly created new SwingWorkers. Instead, what I had to do was this:

    public Network(int amountOfIterations) {
    graph = new JFrame();
    vis = new Visualiser();
    graph.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    graph.getContentPane().add(vis);
    graph.setSize(1920, 1080);
    graph.setTitle("Graph");
    graph.pack();
    graph.setVisible(true);
    new train(amountOfIterations).execute();
}

class train extends SwingWorker<Void,Void> {

    int iterations;
    public train(int amountOfIterations) {
        iterations = amountOfIterations;
    }

    @Override
    public Void doInBackground() {
        for(int i = 0; i < iterations; i++) {
            //Do some heavy work
            int x = 0;
            for(int y = 0; y < 10000000; y++) {
                x = x + y;
            }
            vis.iterated = i;
            vis.repaint();
            System.out.println(i + "  " + x);
        }
        return null;
    }
}