Hi Guys
I have a program thats supposed to generate 9 random objects made from lines and then allow me to save the panel showing the state of all 9 objects.
Currently all 9 objects are being generated and when i click save the panel is being saved to a png file. However it seems that the png file and the panel are in two different states. Ive attached pictures at the end.

code for my current GUI where the save button is located:
`import javax.swing.*;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class GUI extends JFrame{

Random rGen;
BiomorphObject[] morphs = new BiomorphObject[9];
double[] genes;
//Create containers to hold the components
JPanel biomorphPanel  = new JPanel(new BorderLayout());
JPanel buttonsPanel   = new JPanel(new BorderLayout());
JPanel HOF = new JPanel(new BorderLayout());

public GUI(){

JFrame mainFrame = new JFrame("Biomorph");

BiomorphObject object1 = new BiomorphObject();
BiomorphObject object2 = new BiomorphObject();
BiomorphObject object3 = new BiomorphObject();
BiomorphObject object4 = new BiomorphObject();
BiomorphObject object5 = new BiomorphObject();
BiomorphObject object6 = new BiomorphObject();
BiomorphObject object7 = new BiomorphObject();
BiomorphObject object8 = new BiomorphObject();
BiomorphObject object9 = new BiomorphObject();

JButton hof   = new JButton("Hall of Fame");

//Create the buttons
JButton randomButton = new JButton("Random Biomorph");
JButton saveButton   = new JButton("Save");
JButton loadButton   = new JButton("Load");
JButton printButton  = new JButton("Print?");
JButton exitButton   = new JButton("Exit");

//Set the properties of the components
randomButton.setToolTipText("Create New Random Biomorph"); 
randomButton.setPreferredSize(new Dimension(150, 40));
randomButton.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    biomorphPanel.repaint();
  }
});

saveButton.setToolTipText("Save the current Biomorph"); 
saveButton.setPreferredSize(new Dimension(150, 40));
saveButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        BufferedImage bi = new BufferedImage(biomorphPanel.getSize().width, biomorphPanel.getSize().height, BufferedImage.TYPE_INT_ARGB); 
        Graphics g = bi.createGraphics();
        biomorphPanel.paint(g);  
        g.dispose();
        try{ImageIO.write(bi,"png",new File("test.png"));}catch (Exception e1) {}
    }
});


loadButton.setToolTipText("Load previously made Biomorph"); 
loadButton.setPreferredSize(new Dimension(150, 40));

printButton.setToolTipText("Print Biomorph"); 
printButton.setPreferredSize(new Dimension(150, 40));

exitButton.setToolTipText("Close the Application"); 
exitButton.setPreferredSize(new Dimension(150, 40));
exitButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.exit(0);
      }
    });

//add the buttons to the panel.     
buttonsPanel.add(randomButton, BorderLayout.WEST);
buttonsPanel.add(saveButton, BorderLayout.CENTER);
buttonsPanel.add(loadButton, BorderLayout.CENTER);
buttonsPanel.add(printButton, BorderLayout.CENTER);
buttonsPanel.add(exitButton, BorderLayout.EAST);
buttonsPanel.setLayout(new FlowLayout());

biomorphPanel.add(object1);
biomorphPanel.add(object2);
biomorphPanel.add(object3);
biomorphPanel.add(object4);
biomorphPanel.add(object5);
biomorphPanel.add(object6);
biomorphPanel.add(object7);
biomorphPanel.add(object8);
biomorphPanel.add(object9);
biomorphPanel.setLayout(new GridLayout(3,3));

HOF.add(hof,BorderLayout.CENTER);

mainFrame.getContentPane().add(HOF, BorderLayout.EAST);
mainFrame.getContentPane().add(biomorphPanel, BorderLayout.CENTER);
mainFrame.getContentPane().add(buttonsPanel, BorderLayout.SOUTH);


mainFrame.setVisible(true);
mainFrame.setSize(800,400);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setResizable(false);
}

}

This is what the panel looks like:

and this is what gets saved:

If you need more information then please ask

any help will be appreciated.

Dani AI

Generated

Short diagnosis: the saved PNG differs because the drawing routine rebuilds random geometry inside paintComponent every time it runs. The save action paints the component into an offscreen image, which triggers another paintComponent call and therefore a different random state. correctly called this out — painting must be deterministic.

Practical fix (high level): separate the model (the line/segment data) from the view. Each BiomorphObject should store its generated coordinates in fields or a small list. Add a randomize() (or generate()) method that fills that model once (called from the Random button listener) and then calls repaint(). In paintComponent only draw the already-stored model; never create new random numbers or mutate state there. When the save action renders the component to a BufferedImage, the image will then match what is shown on screen.

A minimal structural example (illustrative only — adapt to the existing BiomorphObject class):

class BiomorphObject extends JComponent {
  private final List<Line2D.Double> segments = new ArrayList<>();
  private final Random rand = new Random();

  void randomize() {
    segments.clear();
    // populate 'segments' once using 'rand'
    repaint();
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    for (Line2D.Double s : segments) g2.draw(s);
  }
}

Extra practical notes: run GUI initialization on the EDT (SwingUtilities.invokeLater); call setSize/pack before setVisible; avoid suppressing exceptions when writing files (use e.printStackTrace() or proper logging as suggested); ensure getWidth()/getHeight() are valid (>0) before creating a BufferedImage; choose TYPE_INT_ARGB if PNG transparency is needed or fill the background for opaque output. Keeping paintComponent side-effect-free is the key to consistent on-screen and saved images.

Recommended Answers

All 7 Replies

Line 62 is a terrible mistake.

You have a problem related to a file being written
In the code that writes the file you go out of your way to supress any exceptions and hide/discard any exception messages. You are saying "if anything goes wrong just pretend it didn't and keep going - don't bother me with it".

Nobody here is going to waste another second on ths until you put an e.printStackTrace(); in your catch block and run it a few more times.
(Maybe that's not where the problem is, but it's dumb not to eliminate it first.)

I added the e.printStackTrace(); code in but it doesnt seem to do anything.
Still having the same issue with the different versions being saved

What does the paint (or paintComponent) method for the panel look like? The random button just seems to repaint without any explicit randomising, so is the randomising embedded in the paint? (In which case, that's your problem.)

This is the code for paintComponent

public void paintComponent(Graphics g) {

    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.setColor(Color.BLUE);
    Dimension size = getSize();

    int w = size.width;
    int h = size.height;

    Random ran = new Random();
    int x = Math.abs(ran.nextInt(100)) % w + 120;
    int y = Math.abs(ran.nextInt(100)) % h;
    int xMirror = x - 120;
    g2d.drawLine(120, 85, x, y);
    g2d.drawLine(120, 85, 120 - xMirror, y);
    int x1 = x;
    int y1 = y;
    int x2 = Math.abs(ran.nextInt(50)) % w + 120;
    int y2 = Math.abs(ran.nextInt(50)) % h;

    for (int i = 0; i <= 10; i++) {
        int xMirror1 = x1 - 120;
        int xMirror2 = x2 - 120;
        g2d.drawLine(x1, y1, x2, y2);
        g2d.drawLine(120 - xMirror1, y1, 120 - xMirror2, y2);
        x1 = x2;
        y1 = y2;
        x2 = Math.abs(ran.nextInt(100)) % w + 120;
        y2 = Math.abs(ran.nextInt(100)) % h;
    }

That's it, just as I suspected.
Every time you execute paintComponent you get new random shapes.
To get the graphics for the file you call paint, which calls paintComponent...

It's always a mistake to change anything in paintComponent because it can be called at any time (eg when the window is re-sized). You should create a separate method to create new random shapes, and just paint the current shapes in paintComponent

Im a little confused as to how to do that.
In the class where paintComponent is called should i create another method that does the random shapes?

You need to do something like:
1. define some variables/arrays/whatever that can hold the definition of the latest random shapes.
2. write a method that populates that with new random data
3. call that method from your randomButton ActionListener (then call repaint();)
4. in paintComponent use that data to draw all the shapes

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.