3

Why won't my image repaint for a simple animation? I call repaint() from two different methods and one causes repaint but the other doesn't. The method that does force the repaint is generated from an event listener. The one that doesn't is a timed animation thread. I know the animation thread is running correctly, and as long as I continuously slide the slider, it displays perfectly. Help please!
PS yes, I've seen many similar problems here, and I've tried validate, revalidate, and using paint vs paintComponent. Four classes that comprise the code follow:

import javax.swing.*;

public class gutz{

    public static int windowWidth = 640;
    public static int windowHeight = 480;

    public static void main(String[] args){

        hBod hb1 = new hBod(50, 30, 21, 111, 7, -11);   //mass, radius, xpos, ypos, xvel, yvel
        Thread t1 = new Thread(hb1);

        windowmakr w = new windowmakr();
        w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        w.setSize(windowWidth, windowHeight);
        w.setVisible(true);

        t1.start();
    }
}

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

public class windowmakr extends JFrame {

    private JSlider slider;
    private drawr panel;

    public windowmakr(){
        super("Orbit Explorer");
        panel = new drawr();
        panel.setBackground(Color.BLACK);

        slider = new JSlider(SwingConstants.HORIZONTAL, 0, 200, 10);
        slider.setMajorTickSpacing(10);
        slider.setPaintTicks(true);

        slider.addChangeListener(
                new ChangeListener(){
                    public void stateChanged(ChangeEvent e){
                        panel.setSpeed(slider.getValue());
                    }
                }
        );

        add(slider, BorderLayout.SOUTH);
        add(panel, BorderLayout.CENTER);

    }

}

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

public class drawr extends JPanel{

    private int diameter = 10;
    private static int rad;
    private static int xpos;
    private static int ypos;

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.setColor(Color.ORANGE);
        g.fillOval((gutz.windowWidth-diameter)/2, ((gutz.windowHeight-diameter)/2)-40, diameter, diameter);
        g.setColor(Color.CYAN);
        g.fillOval(xpos, ypos, rad, rad);

    }

/*  public void paint(Graphics g){
        super.paint(g);
        g.setColor(Color.ORANGE);
        g.fillOval((gutz.windowWidth-diameter)/2, ((gutz.windowHeight-diameter)/2)-40, diameter, diameter);
        g.setColor(Color.CYAN);
        g.fillOval(xpos, ypos, rad, rad);   
    }
*/  
    public void setSpeed(int newD){
        diameter = (newD >= 0 ? newD : 10);
        repaint();
    }

    public void renderImage(int r, int xp, int yp){
        rad=r;
        xpos=xp;
        ypos=yp;
        repaint();
    }

    public Dimension getPreferredSize(){
        return new Dimension(200,200);
    }

    public Dimension getMinimumSize(){
        return getPreferredSize();
    }
}

public class hBod implements Runnable{

    private int mass;
    private int rad;
    private int xpos;
    private int ypos;
    private double xvel;
    private double yvel;

    public drawr render;

    public hBod(int m, int r, int xp, int yp, double xv, double yv){
        mass=m;
        rad=r;
        xpos=xp;
        ypos=yp;
        xvel=xv;
        yvel=yv;

        render = new drawr();
    }

    public void run(){
        try{
            while(true){
                xpos+=xvel;
                ypos+=yvel;
                yvel+=1;
                System.out.printf("rad - %d, xpos - %d, ypos - %d\n", rad, xpos, ypos);
                render.renderImage(rad, xpos, ypos);

                Thread.sleep(1000);
            }
        }catch(Exception e){}
    }
}
user2671186
  • 107
  • 2
  • 2
  • 5
  • 2
    Have you tried calling the `repaint` call (from the animation thread) using `SwingUtilities.invokeLater` as Swing/AWT is _not_ thread safe? – Neet Oct 10 '13 at 15:40
  • 1
    For better help sooner, provide a [SSCCE](http://sscce.org) – Jonathan Drapeau Oct 10 '13 at 15:41
  • 1
    Even if I can't find a duplicate, this kinda of question has been answered quite a few times : [Here](http://stackoverflow.com/questions/15412260/clever-asynchronous-repaint-in-java) and [here](http://stackoverflow.com/questions/19061351/repainting-a-jpanel-with-data-from-another-object-thread) with a fast [search](http://stackoverflow.com/search?page=1&tab=relevance&q=[java]%20repaint%20from%20another%20thread) – Jonathan Drapeau Oct 10 '13 at 15:47
  • How is the posted code a SSCCE? Does the code compile? – camickr Oct 10 '13 at 16:03
  • I did try the .invokeLater method without success (although I may have used it incorrectly.) As stated, I have looked at many other similar questions and tried a lot of options but haven't found anything that works yet. Sorry, I'm just a caveman (and a nube caveman at that). – user2671186 Oct 10 '13 at 16:07
  • Does `render` and `panel` object both referring to the same instance. Had you tried sending `panel` object to the `Thread` class and then calling `panel.renderImage(...)`. It appears to me, as if `render` and `panel` are references holding two distinct object locations, and not just one, that's why you are getting what you say you do !!! – nIcE cOw Oct 10 '13 at 16:10
  • The original code compiles and runs (4 classes). What I've posted is just pieces of that code. I'll prepare an SSCCE, but it will take me a little while. – user2671186 Oct 10 '13 at 16:10
  • 1
    For what it's worth, the JComponent repaint method is supposed to be thread-safe. – RalphChapin Oct 10 '13 at 16:11
  • @RalphChapin What does being thread-safe mean? My timer thread calls a method in the JPanel sub-class. – user2671186 Oct 10 '13 at 23:23
  • @user2671186 Swing is generally _not_ thread-safe. This means if you call a swing method from a thread other than the EventQueue it might do strange things. (It usually works, but every once in a while it gets weird. Sometimes very, very weird.) You normally want to call swing methods using `invokeLater` (or other EventQueue method) to move the call onto the EventQueue. _However,_ JComponent's `repeat` method is specially designed so you can call it from any thread with no danger. – RalphChapin Oct 11 '13 at 13:27

2 Answers2

6

You can use the getContentPane().repaint() method in your JFrame class

Amitesh
  • 992
  • 7
  • 11
3

Like I said before, the problem lies in this line render = new drawr(); inside the hBod constructor. Create a single instance like private drawr panel = new drawr() inside the gutz class and pass it to other two classes using their constructor, like hBod hb1 = new hBod(50, 30, 21, 111, 7, -11, panel) and windowmakr w = new windowmakr(panel) and simply use this reference to point to panel.renderImage(...) inside hBod class and panel.setSpeed(...) from windowmakr class.

Here is the modified code, Please learn Java Coding Conventions :

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

public class Gutz {

    public static int windowWidth = 640;
    public static int windowHeight = 480;

    public static void main(String[] args){
        Drawr panel = new Drawr();
        panel.setBackground(Color.BLACK);
        HBod hb1 = new HBod(50, 30, 21, 111, 7, -11, panel);   //mass, radius, xpos, ypos, xvel, yvel
        Thread t1 = new Thread(hb1);

        WindowMakr w = new WindowMakr(panel);
        w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        w.setSize(windowWidth, windowHeight);
        w.setVisible(true);

        t1.start();
    }
}

class WindowMakr extends JFrame {

    private JSlider slider;
    private Drawr panel;    

    public WindowMakr(Drawr p){
        super("Orbit Explorer");
        final Drawr panel = p;        

        slider = new JSlider(SwingConstants.HORIZONTAL, 0, 200, 10);
        slider.setMajorTickSpacing(10);
        slider.setPaintTicks(true);

        slider.addChangeListener(
                new ChangeListener(){
                    public void stateChanged(ChangeEvent e){
                        panel.setSpeed(slider.getValue());
                    }
                }
        );

        add(slider, BorderLayout.SOUTH);
        add(panel, BorderLayout.CENTER);

    }

}

class Drawr extends JPanel{

    private int diameter = 10;
    private static int rad;
    private static int xpos;
    private static int ypos;

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.setColor(Color.ORANGE);
        g.fillOval((Gutz.windowWidth-diameter)/2, ((Gutz.windowHeight-diameter)/2)-40, diameter, diameter);
        g.setColor(Color.CYAN);
        g.fillOval(xpos, ypos, rad, rad);

    }

    public void setSpeed(int newD){
        diameter = (newD >= 0 ? newD : 10);
        repaint();
    }

    public void renderImage(int r, int xp, int yp){
        rad=r;
        xpos=xp;
        ypos=yp;
        repaint();
    }

    public Dimension getPreferredSize(){
        return new Dimension(200,200);
    }

    public Dimension getMinimumSize(){
        return getPreferredSize();
    }
}

class HBod implements Runnable{

    private int mass;
    private int rad;
    private int xpos;
    private int ypos;
    private double xvel;
    private double yvel;

    public Drawr render;

    public HBod(int m, int r, int xp, int yp, double xv, double yv, Drawr panel){
        mass=m;
        rad=r;
        xpos=xp;
        ypos=yp;
        xvel=xv;
        yvel=yv;

        render = panel;
    }

    public void run(){
        try{
            while(true){
                xpos+=xvel;
                ypos+=yvel;
                yvel+=1;
                System.out.printf("rad - %d, xpos - %d, ypos - %d\n", rad, xpos, ypos);
                render.renderImage(rad, xpos, ypos);

                Thread.sleep(1000);
            }
        }catch(Exception e){}
    }
}
nIcE cOw
  • 24,468
  • 7
  • 50
  • 143
  • Too cool!!! Many thanks. (and I am working jAVA cODING cONVENTIONS. But it's slow going. Like I said: I am just a Caveman! Thanks again to all. – user2671186 Oct 12 '13 at 00:39
  • 2
    But, I wanted to ask you to change the logic thingy a bit, and instead of using a `Thread` to repeat a task, try to use a [javax.swing.Timer](http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) for this purpose, as shown [here](http://stackoverflow.com/a/13795187/1057230). Please feel free to ask anything, that is still unclear. For the rest You're MOST WELCOME and KEEP SMILING :-) – nIcE cOw Oct 12 '13 at 02:43
  • I'm going away for a couple of days, but will try when I return. (Can't help smiling; this is really fun!) – user2671186 Oct 12 '13 at 17:19
  • fyi - I tried to compare the before and after but all the changes you made to JAVa CodinG ConVenTionS interfered. I ended up having to remove all your extra work. :) – john k Jul 11 '18 at 04:06