0

EDIT: I got it to work by moving the animation to a separate thread. Thanks everyone!

I'm teaching myself Java in order to code a small applet to demonstrate how the ocean's water column sorts itself by density. I have a small animation that is self-contained in its own class called animationBox, and I'm trying to call it from my GUI. The GUI was created using the netbeans GUI creator where you just click and drop. This question is a very similar problem to Override paintComponent in Netbeans GUI I think, but I tried swapping to flowlayout and the problem was not solved.

The GUI calls the animationBox class currently, which creates its own JFrame in which to run the animation. I know the code for the animation is running because I tried putting some System.out.println() commands in and everything is running smoothly except the bit which actually draws the animation. I'm guessing based off of what I've been reading on this site that my overriden paintComponent is not being called.

My main confusion is that it works in animationBox's main method, but when I call the same method from the GUI that the main method in animationBox called it doesn't work. Any ideas on how to fix this, or where I can go for more information? Any help would be greatly appreciated!

Here is my code, I've tried to mark what I think are the relevant parts with //RELEVANT

animationBox Code (relevant stuff more towards the end):

package water_density_gui_2;

import javax.swing.*;
import java.awt.*;


public class animateBox extends JPanel {

//initial starting conditions for the moving box
int x = 200;
int y = 100;

public static void main(String[] args) {
    animateBox gui = new animateBox();
    gui.play(1.025); // random input density as placeholder
}

public void play(double boxDensity) {

    //initialize frame to hold animation
    //RELEVANT
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    int height = 212;
    int width = 400;
    frame.setSize(width, height);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(panel); //this seems to do nothing, but I hoped it'd trigger the painting
    DrawPanel draw = new DrawPanel(); //this is the class with overriden paintComponent
    frame.getContentPane().add(draw);
    frame.setVisible(true);
    panel.repaint();//these work when I run animateBox, but the GUI stays white
    draw.repaint();


    //calculate density curve for pycnocline [not relevant to problem]

    pycnoclineCalculations pycno = new pycnoclineCalculations();
    double[][] pycnoclineAndDepth = pycno.summerPycnocline();
    double[] pycnocline = new double[212];
    double[] depth = new double[212];

    for (int i = 0; i < 212; i++) {
        pycnocline[i] = pycnoclineAndDepth[0][i];
        depth[i] = pycnoclineAndDepth[1][i];
    }//end density curve calculations

    //calculate differences between box density and density curve
    //also not relevant to problem

    double[] deltaDensity = new double[212];
    for (int i = 0; i < 212; i++) {
        deltaDensity[i] = Math.abs(pycnocline[i] - boxDensity);
    }
    //get index of minimum difference
    int depthIndex = getMinValueIndex(deltaDensity);
    int targetDepth = (int) Math.round((height * depth[depthIndex] / 2000));
    int deltaDepth = Math.abs(y - targetDepth);
    //catch if box density is not shown on curve
    if (boxDensity < 1.02346) {
        targetDepth = 0;
    } else if (boxDensity > 1.02852) {
        targetDepth = height;
    }//end box density calculations

    //kinda RELEVANT towards end of loop
    //most of the FOR loop is doing the calculations for where the box should go
    //the loop interacts with the drawing by changing x,y & calling draw.repaint()

    //initialize variables for FOR loop
    int t = 0; //time variable for damping term A*e^-kt
    boolean up = false; //direction box is moving
    boolean reached = false; //reached destination
    depthObj temp = new depthObj(y, false, false); //holds current depth, up, reached
    boolean cosine = false; //part of path that requires damping
    int currentTarget = targetDepth; //where the box is going to
    boolean done = false; //if the animation loop should end

    for (int i = 0; !done; i++) {
        if (!reached) { //if not at destination
            temp = incrementDepth(currentTarget, y); 
            y = temp.y;
            reached = temp.reached;
            if (!reached) {
                up = temp.up;
            }
        } else { //if reached, calculate new destination based off of damping term
            double A = deltaDepth / 8;
            double k = 0.5;
            int newTarget = 0;

            //new destination calculations
            if (Math.round(A * Math.pow(2.71828, -k * t)) != 0) {
                if (up & !cosine) {
                    newTarget = targetDepth - (int) Math.round(A * Math.pow(2.71828, -k * t));
                    if (newTarget < 0) {
                        newTarget = 0;
                    }
                    cosine = true;
                } else if (!up & !cosine) {
                    newTarget = targetDepth + (int) Math.round(A * Math.pow(2.71828, -k * t));
                    cosine = true;
                } else if (up & cosine) {
                    newTarget = targetDepth + (int) Math.round(A * Math.pow(2.71828, -k * t));
                } else if (!up & cosine) {
                    newTarget = targetDepth - (int) Math.round(A * Math.pow(2.71828, -k * t));
                    if (newTarget < 0) {
                        newTarget = 0;
                    }
                }
                t++; //increment time for damping
                temp = incrementDepth(currentTarget, y);
                currentTarget = newTarget;
                y = temp.y;
                reached = false;

            } else {

                //  System.out.println("Damping constant is zero");
                x++; //move box off of screen sideways

                if (x > 410) { //if box is off screen, get rid of frame
                    frame.setVisible(false); //RELEVANT....?
                    frame.dispose();
                    done = true; //end animation
                }
            }
        }

        //DEFINITELY RELEVANT PART
        draw.repaint();  //tells the panel to redraw itself so we can see the box in new location
        //should trigger with every iteration of the FOR loop. Works in this code, not in GUI. The frame is created, but stays white the hold time

        try {
            Thread.sleep(25); //slows down animation slightly
        } catch (Exception e) {
        }
    }
}

//increments the depth position of the box [probably not relevant]

public static depthObj incrementDepth(int target, int y) {
    boolean reached = false;
    boolean up;
    if (y < target) {
        y++;
        up = false;
    } else if (y > target) {
        y--;
        up = true;
    } else {
        reached = true;
        up = true; //default, caught above
    }
    depthObj temp = new depthObj(y, up, reached);
    return temp;
}

//calculates sigma-t density value from the water's temperature and salinity signatures
//probably not relevant to drawing problem

public static double calculateDensity(double salinity, double temp) {
    /**       rhos = density in kg/m^3 as a function of temperature and salinity
     *S = salinity in g/kg
     *rhos = rho + AS + BS^(3/2) + CS^2
     *A = 8.24493E-1 - 4.0899E-3*T + 7.6438E-5*T^2 -8.2467E-7*T^3 + 5.3675E-9*T^4
     *B = -5.724E-3 + 1.0227E-4*T - 1.6546E-6*T^2
     *C = 4.8314E-4
     * rho = density in kg/m^3 as a function of temperature
     *T = temperature in C
     *rho = 1000(1 - (T+288.9414)/(508929.2*(T+68.12963))*(T-3.9863)^2)
     */
    double density = 0;
    double rho = 0;
    double eqn1 = 0;
    double eqn2 = 0;
    double eqn3 = 0;
    double rhos = 0;

    rho = 1000 * (1 - ((temp + 288.9419) / (508929.2 * (temp + 68.12963))) * (Math.pow((temp - 3.9863), 2)));
    eqn1 = 0.824493 - temp * 0.0040899 + Math.pow(temp, 2) * Math.pow(10, -5) * 7.6438 - (Math.pow(10, -7) * 8.2467 * Math.pow(temp, 3)) + 5.3675 * Math.pow(10, -9) * Math.pow(temp, 4);
    eqn2 = -5.724 * Math.pow(10, -3) + 1.0227 * Math.pow(10, -4) * temp - 1.6546 * Math.pow(10, -6) * Math.pow(temp, 2);
    eqn3 = 4.8314 * Math.pow(10, -4);
    rhos = rho + eqn1 * salinity + eqn2 * Math.pow(salinity, (3 / 2)) + eqn3 * Math.pow(salinity, 2);
    density = rhos - 1000;
    density = (double) 1 + (Math.floor(density * 100) / 100) / 1000;
    return density;
}

//gets the index of the minimum value of an array
//also probably not relevant

public static int getMinValueIndex(double[] numbers) {
    double minValue = numbers[0];
    int minValueIndex = 0;
    for (int i = 1; i < numbers.length; i++) {
        if (numbers[i] < minValue) {
            minValue = numbers[i];
            minValueIndex = i;
        }
    }
    return minValueIndex;
}

//DEFINITELY RELEVANT PART
//draws the animation. Overriding paintComponent works when I run this program, but not in the GUI. In the GUI the frame gets created, but stays white the whole time

   class DrawPanel extends JPanel {

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);//call method on...JPanel? Not exactly sure what this does
        Graphics2D g2d = (Graphics2D) g; 
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

        //get dimensions of window
        int w = getWidth();
        int h = getHeight();
        int h1 = (int) Math.round(h * 200 / 2000); //first color change point
        int h2 = (int) Math.round(h * 600 / 2000); //second color change point

        //add background gradients (NOTE: color corresponds to pycnocline)
        Color color1 = Color.RED;
        Color color2 = Color.BLUE;
        GradientPaint gp = new GradientPaint(0, 0, color1, 0, h1, color1);
        GradientPaint gp2 = new GradientPaint(0, h1, color1, 0, h2, color2);
        GradientPaint gp3 = new GradientPaint(0, h2, color2, 0, h, color2);
        g2d.setPaint(gp);
        g2d.fillRect(0, 0, w, h1);
        g2d.setPaint(gp2);
        g2d.fillRect(0, h1, w, h2);
        g2d.setPaint(gp3);
        g2d.fillRect(0, h2, w, h);

        //add box
        g.setColor(Color.BLACK);
        g.fillRect(x - 2, y - 2, 19, 19);
        g.setColor(Color.GREEN);
        g.fillRect(x, y, 15, 15);
    }
}

//object to hold information about depth, direction, if at destination
// probably not relevant to problem
static class depthObj {

    int y = 0;
    boolean up = false;
    boolean reached = false;

    public depthObj(int ycur, boolean direction, boolean there) {
        this.y = ycur;
        this.up = direction;
        this.reached = there;
    }
}

}


GUI Code (relevant part at very end):

 //not really relevant....? 

    package water_density_gui_2;
    import java.awt.*;
    import javax.swing.*;

    public class thermohalineCirculationGUI2 extends javax.swing.JFrame {

/** Creates new form WaterDensity */
public thermohalineCirculationGUI2() {
    initComponents();
}

/** This method is called from within the constructor to
 * initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is
 * always regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {

    seasonsButtons = new javax.swing.ButtonGroup();
    tempPermLabel = new javax.swing.JLabel();
    tempTextField = new javax.swing.JFormattedTextField();
    salPermLabel = new javax.swing.JLabel();
    salTextField = new javax.swing.JFormattedTextField();
    densityPermLabel = new javax.swing.JLabel();
    densityLabel = new javax.swing.JLabel();
    runButton = new javax.swing.JButton();
    seasonLabel = new javax.swing.JLabel();
    summerRadioButton = new javax.swing.JRadioButton();
    winterRadioButton = new javax.swing.JRadioButton();
    runSimulationButton = new javax.swing.JButton();
    jMenuBar1 = new javax.swing.JMenuBar();
    fileMenu = new javax.swing.JMenu();
    exitMenuComand = new javax.swing.JMenuItem();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setTitle("Water_Density_GUI");
    setBounds(new java.awt.Rectangle(0, 0, 0, 0));
    setMinimumSize(new java.awt.Dimension(400, 33));
    setResizable(false);
    getContentPane().setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

    tempPermLabel.setText("Temperature (C):");
    getContentPane().add(tempPermLabel);

    double defaultTemp = 20;
    tempTextField.setValue(new Double(defaultTemp));
    tempTextField.setColumns(3);
    getContentPane().add(tempTextField);

    salPermLabel.setText("Salinity:");
    getContentPane().add(salPermLabel);

    double defaultSal = 35;
    salTextField.setValue(new Double(defaultSal));
    salTextField.setColumns(3);
    getContentPane().add(salTextField);

    densityPermLabel.setText("Sigma-t:");
    getContentPane().add(densityPermLabel);

    densityLabel.setText("_______");
    getContentPane().add(densityLabel);

    runButton.setText("Calculate Density");
    runButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            runButtonActionPerformed(evt);
        }
    });
    getContentPane().add(runButton);

    seasonLabel.setText("Season:");
    getContentPane().add(seasonLabel);

    seasonsButtons.add(summerRadioButton);
    summerRadioButton.setText("Summer");
    getContentPane().add(summerRadioButton);

    seasonsButtons.add(winterRadioButton);
    winterRadioButton.setText("Winter");
    getContentPane().add(winterRadioButton);

    runSimulationButton.setText("Run Simulation");
    runSimulationButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            runSimulationButtonActionPerformed(evt);
        }
    });
    getContentPane().add(runSimulationButton);

    fileMenu.setText("File");

    exitMenuComand.setText("Exit");
    exitMenuComand.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            exitMenuComandActionPerformed(evt);
        }
    });
    fileMenu.add(exitMenuComand);

    jMenuBar1.add(fileMenu);

    setJMenuBar(jMenuBar1);

    pack();
}// </editor-fold>

private void runButtonActionPerformed(java.awt.event.ActionEvent evt) {                                          
    // update sigma-t value on GUI when run button is pressed
    if (!(tempTextField.getText().equals("") | salTextField.getText().equals(""))) {
        double temp = Double.parseDouble(tempTextField.getText());
        double sal = Double.parseDouble(salTextField.getText());
        double density = calculateDensity(sal, temp);
        densityLabel.setText(Double.toString(density));


    } else {
        densityLabel.setText("N/a");
    }



}                                         

private void exitMenuComandActionPerformed(java.awt.event.ActionEvent evt) {                                               
    // exit GUI
    System.exit(0);
}                                              

    //RELEVANT

   //This is the method run when the button is pushed to start the animation

   private void runSimulationButtonActionPerformed(java.awt.event.ActionEvent evt) {
    //try to retrieve density value. If that fails, do nothing
    try {

        double density = Double.parseDouble(densityLabel.getText());
        startAnimation(density); //this method is below

    } catch (Exception e) {
    }

}

//no longer relevant until next break
public static double calculateDensity(double salinity, double temp) {
    /**       rhos = density in kg/m^3 as a function of temperature and salinity
     *S = salinity in g/kg
     *rhos = rho + AS + BS^(3/2) + CS^2
     *A = 8.24493E-1 - 4.0899E-3*T + 7.6438E-5*T^2 -8.2467E-7*T^3 + 5.3675E-9*T^4
     *B = -5.724E-3 + 1.0227E-4*T - 1.6546E-6*T^2
     *C = 4.8314E-4
     * rho = density in kg/m^3 as a function of temperature
     *T = temperature in C
     *rho = 1000(1 - (T+288.9414)/(508929.2*(T+68.12963))*(T-3.9863)^2)
     */
    double density = 0;
    double rho = 0;
    double eqn1 = 0;
    double eqn2 = 0;
    double eqn3 = 0;
    double rhos = 0;

    rho = 1000 * (1 - ((temp + 288.9419) / (508929.2 * (temp + 68.12963))) * (Math.pow((temp - 3.9863), 2)));
    eqn1 = 0.824493 - temp * 0.0040899 + Math.pow(temp, 2) * Math.pow(10, -5) * 7.6438 - (Math.pow(10, -7) * 8.2467 * Math.pow(temp, 3)) + 5.3675 * Math.pow(10, -9) * Math.pow(temp, 4);
    eqn2 = -5.724 * Math.pow(10, -3) + 1.0227 * Math.pow(10, -4) * temp - 1.6546 * Math.pow(10, -6) * Math.pow(temp, 2);
    eqn3 = 4.8314 * Math.pow(10, -4);
    rhos = rho + eqn1 * salinity + eqn2 * Math.pow(salinity, (3 / 2)) + eqn3 * Math.pow(salinity, 2);
    density = rhos - 1000;
    density = (double) 1 + (Math.floor(density * 100) / 100) / 1000;
    return density;

}

/**
 * @param args the command line arguments
 */
public static void main(String args[]) {
    java.awt.EventQueue.invokeLater(new Runnable() {

        public void run() {
            new thermohalineCirculationGUI2().setVisible(true);
        }
    });
}
// Variables declaration - do not modify
private javax.swing.JLabel densityLabel;
private javax.swing.JLabel densityPermLabel;
private javax.swing.JMenuItem exitMenuComand;
public javax.swing.JMenu fileMenu;
public javax.swing.JMenuBar jMenuBar1;
public javax.swing.JButton runButton;
private javax.swing.JButton runSimulationButton;
private javax.swing.JLabel salPermLabel;
private javax.swing.JFormattedTextField salTextField;
private javax.swing.JLabel seasonLabel;
private javax.swing.ButtonGroup seasonsButtons;
private javax.swing.JRadioButton summerRadioButton;
public javax.swing.JLabel tempPermLabel;
private javax.swing.JFormattedTextField tempTextField;
private javax.swing.JRadioButton winterRadioButton;
// End of variables declaration


   //RELEVANT

   //This is where I try to run the animation
    public void startAnimation(double density) {

    animateBox gui = new animateBox();
    int height = 212; //dimensions of frame I want
    int width = 400;

    gui.play(density); //this calls the play() method from animateBox
}  

}

Other Code (Used to calculate pycnocline, not used in drawing)

      package water_density_gui_2;


     public class pycnoclineCalculations {

    public static void main(String[] args){
        double[][] pycnocline = summerPycnocline();

     }
     public static double[][] summerPycnocline() {
        double[] pycnocline = new double[212];
        double[] depth = {0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 2000, 3000, 4000};
        double[] thermocline = {27, 27, 27, 24, 20, 15, 10, 8, 7, 6, 5, 5, 2, 2};
        double[] halocline = {35, 35, 35, 34.9, 34.8, 34.7, 34.4, 34.4, 34.4, 34.45, 34.5, 34.5, 34.5, 34.5};
        double[] pycnoclineDiscreet = new double[12];

        for (int i = 0; i < 12; i++) {
            pycnoclineDiscreet[i] = calculateDensity(halocline[i], thermocline[i]);

        }
        for (int i = 0; i < 11; i=i+1) {
            double[] interpdVals = interp2dens(pycnoclineDiscreet[i], pycnoclineDiscreet[i + 1], depth[i], depth[i + 1], 10);

            for (int j = 0; j < 10; j++) {
                pycnocline[i*10 + j] = interpdVals[j];
            }
        }

        double[] temp = interp2dens(pycnoclineDiscreet[10],pycnoclineDiscreet[11],depth[10],depth[11],102);

        for(int i=0;i<102;i++){
            pycnocline[i+110]=temp[i];
        }

double[] depthsFull=new double[212];
        for(int k=0;k<212;k++){
          depthsFull[k] = k*10;
        }

        double[][] pycnoclineAndDepth = {pycnocline, depthsFull};
        return pycnoclineAndDepth;
    }


     public static double[] interp2dens(double val1, double val2, double depth1, double depth2, int numPointsWanted) {

        int size = numPointsWanted;
        double[] interpdVal = new double[size];
        //y = mx+b
        //slope = y2-y1/(x2-x1)
        //intercept b = y-mx
        double slope = (val2 - val1) / (depth2 - depth1);
        double intercept = val2 - slope * depth2;
        double distanceStep = Math.abs(depth2 - depth1) / numPointsWanted;
        interpdVal[0]=val1;
        interpdVal[size-1]=val2;
        for(int i =1;i<size-1;i++){
            interpdVal[i]=slope*(depth1+distanceStep*i)+intercept;
        }       
        return interpdVal;
    }


    public static double calculateDensity(double salinity, double temp) {
        /**       rhos = density in kg/m^3 as a function of temperature and salinity
         *S = salinity in g/kg
         *rhos = rho + AS + BS^(3/2) + CS^2
         *A = 8.24493E-1 - 4.0899E-3*T + 7.6438E-5*T^2 -8.2467E-7*T^3 + 5.3675E-9*T^4
         *B = -5.724E-3 + 1.0227E-4*T - 1.6546E-6*T^2
         *C = 4.8314E-4
         * rho = density in kg/m^3 as a function of temperature
         *T = temperature in C
         *rho = 1000(1 - (T+288.9414)/(508929.2*(T+68.12963))*(T-3.9863)^2)
         */
        double density = 0;
        double rho = 0;
        double eqn1 = 0;
        double eqn2 = 0;
        double eqn3 = 0;
        double rhos = 0;

        rho = 1000 * (1 - ((temp + 288.9419) / (508929.2 * (temp + 68.12963))) * (Math.pow((temp - 3.9863), 2)));
        eqn1 = 0.824493 - temp * 0.0040899 + Math.pow(temp, 2) * Math.pow(10, -5) * 7.6438 - (Math.pow(10, -7) * 8.2467 * Math.pow(temp, 3)) + 5.3675 * Math.pow(10, -9) * Math.pow(temp, 4);
        eqn2 = -5.724 * Math.pow(10, -3) + 1.0227 * Math.pow(10, -4) * temp - 1.6546 * Math.pow(10, -6) * Math.pow(temp, 2);
        eqn3 = 4.8314 * Math.pow(10, -4);
        rhos = rho + eqn1 * salinity + eqn2 * Math.pow(salinity, (3 / 2)) + eqn3 * Math.pow(salinity, 2);
        density = rhos - 1000;
        density = (double) 1 + (Math.floor(density * 100) / 100) / 1000;
        return density;

    }
}
Community
  • 1
  • 1
KataObbe
  • 1
  • 3
  • relevant to Concurency in Swing and Event Dispath Thread, code snipped doesn't help me somehow, – mKorbel Dec 23 '13 at 07:35
  • Was breaking up the code unhelpful? I can make it all one block again. I have other classes that are referenced in order to calculate the background data, but they don't have anything to do with the drawing. Should I include them anyway? – KataObbe Dec 23 '13 at 07:40
  • The problem is the amount of code you posted, an SSCCE would be much more appropriate. – jzd Dec 23 '13 at 13:10

0 Answers0