0

I'm writing a GUI for my neural network(https://github.com/banana01/Neural-Network If you need to see any other classes) to show a map of the network and I have a map organised by layers. I would like to be able to draw connections between the nodes on a transparent JPanel that is on top of the JPanel that has the layers and nodes in it.

I have read the following question but that requires the class be a JFrame, I would like to be able to do it in a JPanel so I can add it to a tab so I can have different tabs for different things such as the map, the input, setting etc. placing a transparent JPanel on top of another JPanel not working

Here is my current class, lacking any sort of overlay layer.

public class NeuralNetworkDisplay extends JPanel  //implements MouseListener
{
    private Network ntk;
    JPanel[] layerPanels;
    JPanel[] layerSubPanels;
    JButton[] nodeButtons;
    JSplitPane splitPane;
    JLayeredPane NNMap;
    JPanel test;
    ArrayList<Layer> layers = new ArrayList<Layer>();
    ArrayList<Node[]> nodes = new ArrayList<Node[]>();
    public NeuralNetworkDisplay(Network ntk)
    {
        setNtk(ntk);
        parseNetworkDesign();
        splitPane = new JSplitPane();
        NNMap = new JLayeredPane();
        test = new JPanel();

        NNMap.setLayout(new GridLayout(3,1,5,5));
        splitPane.setRightComponent(NNMap);
        add(splitPane);

    }
    public void init()
    {
        drawLayers();
        drawNodes();
    }
    public void parseNetworkDesign()
    {
        for (int i = 0;i < ntk.getLayers().size(); i++) 
        {
            layers.add(ntk.getLayers().get(i));
        }
        for (int i = 0; i < layers.size(); i++) 
        {
            nodes.add(layers.get(i).getNodes().toArray(new Node[layers.get(i).getNodes().size()]));
        }
    }
    public Network getNtk() {
        return ntk;
    }
    public void setNtk(Network ntk) {
        this.ntk = ntk;
        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));




    }
    public ArrayList<Layer> getLayers() {
        return layers;
    }
    public void setLayers(ArrayList<Layer> layers) {
        this.layers = layers;
    }
    public ArrayList<Node[]> getNodes() {
        return nodes;

    }
    public int getNodesSize() {
        return nodes.size();
    }
    public int getLayersSize() {
        return layers.size();
    }
    public void setNodes(ArrayList<Node[]> nodes) {
        this.nodes = nodes;
    }




    public void drawLayers()
    {
        layerPanels = new JPanel[getLayersSize()];
        layerSubPanels = new JPanel[getLayersSize()];
        for (int i = 0; i < layerPanels.length; i++) 
        {
            layerPanels[i] = new JPanel();
            layerSubPanels[i] = new JPanel();
            layerPanels[i].setLayout(new FlowLayout());
            layerSubPanels[i].setLayout(new GridLayout(3,5,5,5));
            layerPanels[i].add(new JLabel("Layer::"+i));
            layerPanels[i].add(layerSubPanels[i]);
            NNMap.add(layerPanels[i]);
        }
    }
    public void drawNodes()
    {
        int nod = 0;
        for (int i = 0; i < getNodes().size(); i++) 
        {
            nod += getNodes().get(i).length;
        }
        nodeButtons = new JButton[nod];
        for (int i = 0; i < getLayersSize(); i++) 
        {

            for (int j = 0; j < getNodes().get(i).length; j++) 
            {
                //nodeButtons[j]
                layerSubPanels[i].add(MyFactory.createNODEButton(getNodes().get(i)[j]));
            }

        }

    }



}

This is a JPanel that is added to the main window in a split pane. That is all done in a different class. Here is the Map Panel:

enter image description here

What would I use to create a transparent JPanel on top of the JPanel that contains the map. So I could draw connections between the nodes.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Post an [MCVE](http://stackoverflow.com/help/mcve). Be sure to copy-paste your code to a *new project* and make sure it compiles and runs before posting it here. – user1803551 Feb 23 '16 at 00:30
  • Maybe something like [this](http://stackoverflow.com/questions/16629528/line-not-appearing-on-jdesktoppane/16629787#16629787)? – MadProgrammer Feb 23 '16 at 00:45
  • @MadProgrammer Can layeredPanes be used with JPanels? such as it can be placed in a JPanel? – Nisergious Feb 23 '16 at 01:19
  • @Nisergious Yes, but I'd generally discourage it for something like this. `JLayeredPane` is already setup without a layout manager and has better control over the z-ordering of component – MadProgrammer Feb 23 '16 at 01:21
  • @MadProgrammer Would the topmost pane in the window be the JLayeredPane or could i place this pane in the split panel? – Nisergious Feb 23 '16 at 01:28
  • @Nisergious I'd make it the base component onto which your map is added to – MadProgrammer Feb 23 '16 at 01:37

1 Answers1

2

There's probably a few ways you could do this, but one way might be to create a custom JLayeredPane, which can maintain and paint the relationships between the components, for example...

Drag Connection

import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                GroupPane parent = new GroupPane("Parent", Color.RED);
                GroupPane child1 = new GroupPane("Child 1", Color.BLUE);
                GroupPane child2 = new GroupPane("Child 2", Color.CYAN);

                parent.setBounds(10, 10, 100, 100);
                child1.setBounds(10, 150, 100, 100);
                child2.setBounds(150, 150, 100, 100);

                ConnectionPane connectionPane = new ConnectionPane();
                connectionPane.add(parent, child1);
                connectionPane.add(parent, child2);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(connectionPane);
                frame.setSize(400, 400);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GroupPane extends JPanel {

        public GroupPane(String name, Color background) {
            setLayout(new GridBagLayout());
            add(new JLabel(name));
            setBackground(background);
        }

    }

    public class ConnectionPane extends JLayeredPane {

        private List<Component[]> connections;

        public ConnectionPane() {
            connections = new ArrayList<>();

            MouseAdapter ma = new MouseAdapter() {
                private Component dragComponent;
                private Point clickPoint;
                private Point offset;

                @Override
                public void mousePressed(MouseEvent e) {
                    Component component = getComponentAt(e.getPoint());
                    if (component != ConnectionPane.this && component != null) {
                        dragComponent = component;
                        clickPoint = e.getPoint();
                        int deltaX = clickPoint.x - dragComponent.getX();
                        int deltaY = clickPoint.y - dragComponent.getY();
                        offset = new Point(deltaX, deltaY);
                    }
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    int mouseX = e.getX();
                    int mouseY = e.getY();

                    int xDelta = mouseX - offset.x;
                    int yDelta = mouseY - offset.y;
                    dragComponent.setLocation(xDelta, yDelta);

                    repaint();
                }

            };

            addMouseListener(ma);
            addMouseMotionListener(ma);
        }

        public void add(Component parent, Component child) {
            if (parent.getParent() != this) {
                add(parent);
            }
            if (child.getParent() != this) {
                add(child);
            }
            connections.add(new Component[]{parent, child});
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            for (Component[] connection : connections) {
                Rectangle parent = connection[0].getBounds();
                Rectangle child = connection[1].getBounds();

                g2d.draw(new Line2D.Double(parent.getCenterX(), parent.getCenterY(), child.getCenterX(), child.getCenterY()));
            }
            g2d.dispose();
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I really like this but is it possible to draw a JComponent instead of a line?as the line needs to be mouse over-able and interactable. – Nisergious Feb 23 '16 at 01:27
  • @Nisergious Well, my linked example in the comments above does that or you could use the `mouseMoved` event and calculate if one or more lines have been intersected. You have to remember, a component is ALWAYS rectangular, so even if you tried to use a component, you're still going to have to calculate the if the mouse is positioned over the line or not. You could also run into other issues as components overlap. In either case, you have some calculating to do – MadProgrammer Feb 23 '16 at 01:38