0

I'm going to start off by saying that I can almost guarantee that this question is going to be a copy but I couldn't find any other answers that suited my needs / worked on my program. I need to draw on a JPanel, but I can't seem to be able to get the line I need to draw to show up.

JFrame drawF = new JFrame("Simulator");
        JPanel simPanel = new JPanel();
        drawF.setVisible(true);
        drawF.setSize(1000,650);
        drawF.add(simPanel);
        drawF.pack();
        simPanel.setLayout(null);
        simPanel.setSize(1000,650);
        simPanel.setVisible(true);
        simPanel.setBackground(Color.BLACK);

        //add drawing surface


        //start drawing
            simPanel.add(new JComponent()
            {
            public void paint(Graphics g)
            {
                g.setColor(Color.RED);
                int xLast,yLast;
                for(int i=0;i < 5000000; i++)  // five million
                {

                    double dX = (Math.E*Math.exp(-numd1*i)*Math.sin(i*numf1+nump1))+(Math.E*Math.exp(-numd2*i)*Math.sin(i*numf2*nump2));
                    double dY = (Math.E*Math.exp(-numd3*i)*Math.sin(i*numf3+nump3))+(Math.E*Math.exp(-numd4*i)*Math.sin(i*numf4*nump4));
                    int drawX = (int)dX;
                    int drawY = (int)dY;
                    if (i==0)
                    {
                        xLast = 0;
                        yLast = 0;
                        g.drawLine(xLast,yLast,drawX,drawY);

                        simPanel.revalidate();
                        simPanel.repaint();
                    }
                    else
                    {
                        xLast = drawX;
                        yLast = drawY;
                        g.drawLine(xLast,yLast,drawX,drawY);

                        simPanel.revalidate();
                        simPanel.repaint();
                    }
                }
            }
        });





        repaint();

is there something wrong with adding a JComponent to my JPanel?

Mark Coats
  • 11
  • 7
  • What equation are you trying to draw? Why are you using `Math.E` in your calculations and multiplying to the same number (Euler's constant, e) raised to some power? – Hovercraft Full Of Eels May 25 '16 at 23:49
  • Sorry, I've been busy. I am simulating a harmonograph. – Mark Coats May 26 '16 at 01:39
  • Oh, and by the way this (obviously) is just one part of a much larger program. – Mark Coats May 26 '16 at 01:41
  • If it's a larger GUI, then you will not want to be displaying this in its own JFrame. If it needs to go into a sub-window, then display it in a dialog window such as in a JDialog. – Hovercraft Full Of Eels May 26 '16 at 01:44
  • Just for curiosity what could happen by having multiple JFrames? Decreased frame rate? – Mark Coats May 26 '16 at 02:17
  • It's more of a behavioral problem as you'd be creating a GUI with multiple application windows, which is not the expected behavior of most programs, including and especially professional programs, that you run. You risk creating a window that when closed will close down the whole application for instance, you create multiple icons unnecessarily on the task bar. An application should have only one main application window and then can be supported by dialog windows if need be. Please see [The Use of Multiple JFrames, Good/Bad Practice?](http://stackoverflow.com/questions/9554636). – Hovercraft Full Of Eels May 26 '16 at 02:21

1 Answers1

2

Your problem is likely one of layout manager. JPanel uses FlowLayout by default, so your JComponent will size to its natural preferred size which is [0, 0], and so nothing will show in this situation. Try making your JPanel's layout BorderLayout and add the JComponent BorderLayout.CENTER.

Other problems:

  • Override the JComponent's paintComponent method, not the paint method, otherwise you risk unexpected side effects by not taking into account the painting of borders and children.
  • Call the super's painting method in your override, super.paintComponent(g); if you're overriding paintComponent as I recommend. This will help clean any dirty pixels.
  • Get rid of all those unnecessary calls to repaint and revalidate and setVisible.
  • Call setVisible on the JFrame after adding all components to the GUI.
  • Myself, I prefer to paint within a JPanel's paintComponent method, not a JComponent, since it won't paint the background automatically.
  • And I prefer to do my painting in its own stand alone class, not buried within an anonymous inner class. It's too important a function to bury in such a way.
  • The more long running or CPU-intense code you call within a painting method, the less responsive your GUI will seem. Off load the for loops and the math calculations in your class's constructor and out of the painting method. Store the results in an array or list, or if you desire paint to a BufferedImage and then display that image within the paintComponent method.

For example:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.*;

@SuppressWarnings("serial")
public class DrawingStuff extends JPanel {
    private static final int PREF_W = 1000;
    private static final int PREF_H = 650;
    private static final Color BG = Color.BLACK;
    private static final Color GRAPHICS_COLOR = Color.RED;
    private static final int LOOP_COUNT = 5000000; // 5 million

    private BufferedImage image = null;
    private MathParams[] params;

    public DrawingStuff(MathParams[] params) {
        this.params = params;
        setBackground(BG);
        image = drawImage();
    }

    private BufferedImage drawImage() {
        BufferedImage img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();

        g2.setColor(GRAPHICS_COLOR);
        int xLast, yLast;
        for (int i = 0; i < LOOP_COUNT; i++) {
            double dX = params[0].doCalc0(i) + params[1].doCalc1(i);
            double dY = params[2].doCalc0(i) + params[3].doCalc1(i);
            int drawX = (int) dX;
            int drawY = (int) dY;
            if (i == 0) {
                xLast = 0;
                yLast = 0;
                g2.drawLine(xLast, yLast, drawX, drawY);
            } else {
                xLast = drawX;
                yLast = drawY;
                g2.drawLine(xLast, yLast, drawX, drawY);
            }
        }

        g2.dispose();
        return img;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (image != null) {
            g.drawImage(image, 0, 0, this);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PREF_W, PREF_H);
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("Drawing Stuff");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // TODO: fix this -- what numbers to use??? These don't work
        MathParams[] paramArray = { 
                new MathParams(1, 2, 3), 
                new MathParams(1, 2, 3), 
                new MathParams(3, 4, 5),
                new MathParams(3, 4, 5)
                };

        frame.getContentPane().add(new DrawingStuff(paramArray));
        frame.setResizable(false);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

public class MathParams {
    private int numd;
    private int numf;
    private int nump;

    public MathParams(int numd, int numf, int nump) {
        this.numd = numd;
        this.numf = numf;
        this.nump = nump;
    }

    public double doCalc0(int i) {
        return (Math.E * Math.exp(-numd * i) * Math.sin(i * numf + nump));
    }

    public double doCalc1(int i) {
        return (Math.E * Math.exp(-numd * i) * Math.sin(i * numf * nump));
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thank you, I will try this and inform you if it works – Mark Coats May 26 '16 at 01:40
  • @MarkCoats: the code is just FYI and is not meant to "work" since I have no idea what you're trying to graph or what parameters are correct. The concepts enumerated **are** correct, **will** work, and are the actual answer to your question. – Hovercraft Full Of Eels May 26 '16 at 01:45
  • the use of the numbers (1,2,3,4) in the num variables are do differentiate the values for pendulum 1 x and y values and pendulum 2 x and y values ( if that was what you asked about the numbers in you paramArray) – Mark Coats May 26 '16 at 01:50
  • I've had a bed time with this specific program. I've tried using the paintComponent method and a few other things, but I get some errors... I'll try to revise my code and I'll get back to you. – Mark Coats May 26 '16 at 01:57
  • @MarkCoats: Then it looks like it's time for you to do some debugging. – Hovercraft Full Of Eels May 26 '16 at 02:05
  • Okay, so I've fixed it, the frame pops up, but I'm calling the BufferedImage from a different class and I don't understand what your paramArray method is. What is the purpose of the `frame.getContentPane().add(new%CLASS_NAME%(paramArray));` – Mark Coats May 26 '16 at 03:26
  • @MarkCoats: Your code had an ugly bunch of 12 int variables, and so I refactored them into the MathParams class. This class holds 3 int vars plus the two methods that you do on image creation. Look at that class code I've posted on the bottom. It should all be self explanatory to you since it's based on your code! – Hovercraft Full Of Eels May 26 '16 at 03:31
  • Oh, I see, you're using the long way of `JFrame.add()` when you said `JFrame.getContentPane().add();` – Mark Coats May 26 '16 at 03:40
  • @MarkCoats: that has saved me much grief when trying to paint background colors. – Hovercraft Full Of Eels May 26 '16 at 03:41