Results 1 to 20 of 20
  1. #1
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default MultiThreaded graphics repaint()

    I have a JFrame, within that i have a JPanel that i would like to add a number of balls to, which will each move independently bouncing off the bounding box.
    I have managed to get it working with 1 ball and a timer class, but now i am trying to get it to work with threads, so i assign 1 thread to each ball.
    At the moment my structure is
    BallFrame extends JFrame
    BallsPanel extends JPanel
    Ball.java extends component
    BallRunnable implements runnable

    I have added a println into the run method in BallRunnable and that prints out so i know my thread is running it is just not painting my component. Could someone tell me where i have gone wrong.

    BallRunnable
    Java Code:
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    public class BallRunnable implements Runnable 
    {
        final static int DIAMETER = 20;
        private Ball newBall;
        private int xCor;
        private int yCor;
        private int speedX;
        private int speedY;
        private int widthBound;
        private int heightBound;
        
        public BallRunnable(Ball aBall, int x, int y, int aSpeedX, int aSpeedY)
        {
            newBall = aBall;
            widthBound = 600;
            heightBound = 400;
            xCor = x;
            yCor = y;
            speedX = aSpeedX;
            speedY = aSpeedY;
            newBall.repaint();
            System.out.println("New Ball");
        }
        
        public void paintComponent(Graphics g)
        {
            paintComponent(g);
            newBall.draw(g);
        }
        
        public void move()
        {
            xCor = xCor + speedX;
            yCor = yCor + speedY;
            
            if(xCor < 0)
            {
                xCor = 0;
                speedX = - speedX;
            } else if(xCor > widthBound)
            {
                xCor = widthBound;
                speedX = - speedX;
            }
            
            if(yCor < 0)
            {
                yCor = 0;
                speedY = - speedY;
            } else if(yCor > heightBound) {
                yCor = heightBound;
                speedY = - speedY;
            }
        }
                
        public void run()
        {   
            while(true)
            {
                System.out.println("Running");
                move();
                newBall.repaint();
            }
            
        }
        
    }
    Ball
    Java Code:
    import java.awt.*;
    import javax.swing.JComponent;
    public class Ball extends JComponent  
    {
        final static int DIAMETER = 20;
        private int xCor;
        private int yCor;
        private int speedX;
        private int speedY;
        private int widthBound;
        private int heightBound;
        
        public Ball(int x, int y, int speedx, int speedy)
        {
            xCor = x;
            yCor = y;
            speedX = speedx;
            speedY = speedy;
        }
        
        public void draw(Graphics g)
        {
            g.setColor(Color.red);
            g.fillOval(xCor,yCor, DIAMETER, DIAMETER);
        }
    and this is where i add a ball to my JPanel (from my JFrame) and also set the thread for the ball
    Java Code:
    public void addBall(int x,int y,int speedX, int speedY)
        {
            Ball newBall = new Ball(x,y,speedX,speedY);
            ballpanel.add(newBall);
            
            Runnable r = new BallRunnable(newBall,x,y,speedX,speedY);
            Thread t = new Thread(r);
            t.start();
        }

  2. #2
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: MultiThreaded graphics repaint()

    Quote Originally Posted by wdh321 View Post
    I have added a println into the run method in BallRunnable and that prints out so i know my thread is running it is just not painting my component. Could someone tell me where i have gone wrong.

    BallRunnable
    Java Code:
        
        public void paintComponent(Graphics g)
        {
            paintComponent(g);
            newBall.draw(g);
        }
    If this got called, it would be dangerous as the recursion would cause you to run out of stack memory resulting in a stackoverflow exception. Even if you didn't have the recursion, you would be drawing in a non-EDT thread which would eventually lead to a threading issue.

    I think you're making a logical mistake having Ball extend a JComponent. Instead have Ball just be a purely logical object and not one that extends a JComponent of any kind. I would have BallPanel hold a collection of Balls, perhaps an ArrayList<Ball>, I would use a single Timer or background thread that with each tick told each Ball to move, and then I would have the BallPanel's paintComponent method iterate through the Ball objects calling their draw(Graphics) (or whatever you want to call this method), telling each Ball to paint itself.

  3. #3
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    Hi, for this particular task, a thread for each ball is a requirement so I have to implement it in that way and not with a timer. Ball didn't use to extend JComponent but when I tried to add it to the JPanel using ballpanel.add(newBall); it gave me an error. Could you advise me on which paint methods I should be calling, and as you mentioned, not calling? I think that is the reason why nothing is displaying in my JPanel and subsequently I cannot tell if my move methods are working either. I had it working nicely using a timer but as obviously the requirement is I use threads, I can't seem to get the same result.
    Thank you

  4. #4
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

  5. #5
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    Write a program with a frame containing the buttons start,add,pause,close
    You must be able to add multiple balls to the frame, the balls should bounce off the edge off the frame as soon as they make contact with it.
    Each ball MUST be controlled by its own thread or runnable.

    I have my JFrame and within that I have my JPanel ballpanel and also a controlPanel which holds my buttons. My balls bounce off a bounding box which is set to the width of my ballpanel. When I click the add button it invokes the addBall method which should create a new Ball and then assign it a thread which will control its continuous movement. This method should also display the ball on the ballpanel JPanel. The thread should also repaint the ball with its new coordinates, calculated from the move method within it. I think I have the concept correct, just not the implementation

    EDIT: looking at it now, I'm thinking that perhaps I should scrap ballrunnable and instead let Ball extend thread, with its own run method?
    Last edited by wdh321; 11-28-2012 at 05:04 AM.

  6. #6
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: MultiThreaded graphics repaint()

    You must give your BallPanel class its own addBall(Ball ball) method so that it can accept Balls and place them in the collection holding the Balls. One way to have the Ball draw itself, give each Ball a setBallPanel(JPanel ballPanel) method. Then in the Ball's run method, change its location and have it call repaint(...) on the BallPanel container. If you see a performance hit from all these Ball objects asking the JPanel to keep repainting itself in its entirety, consider limiting the repaint region to a Rectangle that encompasses the old and current Ball position. Note that I believe that repaint() can be called from any Thread and not necessarily the EDT. Note also that the painting still needs to be done in the BallPanel's paintComponent method and that it still needs to iterate through its collection of Ball objects from within this method.

  7. #7
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    I think the problem still lies with my repaint after the changes i have made.
    I cant seem to access the BallsPanel from my Ball class to repaint it.

    Changed BallPanel to store balls
    Java Code:
    import java.util.LinkedList;
    import java.util.ListIterator;
    import javax.swing.*;
    public class BallsPanel extends JPanel 
    {
        private Ball newBall;
        private LinkedList<Ball> balls = new LinkedList<Ball>();
        public BallsPanel()
        {
            
        }
        
        public void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            Graphics2D g2D = (Graphics2D) g;
            ListIterator<Ball> it = balls.listIterator();
            while(it.hasNext())
            {
               it.next().draw(g2D);
            }
        }
        
        public void addBall()
        {
            newBall = new Ball(0,0,2,3);
            balls.add(newBall);
            newBall.start();
        }
    Changed ball to extend thread
    Java Code:
    public class Ball extends Thread 
    {
        final static int DIAMETER = 20;
        private int xCor;
        private int yCor;
        private int speedX;
        private int speedY;
        private int widthBound = 600;
        private int heightBound = 400;
        private boolean ballMoving;
        private BallsPanel panel;
        private Ellipse2D.Double newBall;
        
        public Ball(int x, int y, int speedx, int speedy)
        {
            xCor = x;
            yCor = y;
            speedX = speedx;
            speedY = speedy;
            newBall = new Ellipse2D.Double(xCor,yCor,20,20);
            ballMoving = true;
        }
        
        public void draw(Graphics2D g)
        {
            g.setColor(Color.red);
            g.fill(newBall);
        }
    
        public void run()
        {
            while(ballMoving)
            {
                System.out.println("running . . .");
                move();
                setBallPanel(panel);
            }
        }
        
        public void setBallPanel(JPanel panel)
        {
            panel.repaint();
        }
    I tried to access BallsPanel directly to do BallsPanel.repaint() but that wouldn't work hence i did what you see above, which i knew probably wouldn't work anyway.

  8. #8
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: MultiThreaded graphics repaint()

    Quote Originally Posted by wdh321 View Post
    I think the problem still lies with my repaint after the changes i have made.
    I cant seem to access the BallsPanel from my Ball class to repaint it.

    Java Code:
        public void setBallPanel(JPanel panel)
        {
            panel.repaint();
        }
    I tried to access BallsPanel directly to do BallsPanel.repaint() but that wouldn't work hence i did what you see above, which i knew probably wouldn't work anyway.
    That's not how you write a setter or mutator method. It must set a class field, just like any other setter method. i.e.,:

    Java Code:
    class Foo {
      Bar bar;
    
      public void setBar(Bar bar) {
        this.bar = bar;
      }
    }

  9. #9
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    Adjusted to the methods, and added BallsPanel ballsPanel.
    Java Code:
        BallsPanel ballsPanel;
    
        public void run()
        {
            while(ballMoving)
            {
                System.out.println("running . . .");
                move();
                setBallPanel();
                ballsPanel.repaint();
            }
        }
        
        public void setBallPanel(BallsPanel ballsPanel)
        {
            this.ballsPanel = ballsPanel;
        }
    I dont know what i should put into setBallPanel() if i add ballsPanel it leads to this error when i call ballsPanel.repaint() in the run method. Also i will put setBallPanel() in the ball constructor not the run method but i kept it in as the question i am now asking is what to feed into that method.
    Exception in thread "Thread-1" java.lang.NullPointerException
    at Ball.run(Ball.java:72)

    Thank You

  10. #10
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: MultiThreaded graphics repaint()

    Quote Originally Posted by wdh321 View Post
    Adjusted to the methods, and added BallsPanel ballsPanel.
    Java Code:
        BallsPanel ballsPanel;
    
        public void run()
        {
            while(ballMoving)
            {
                System.out.println("running . . .");
                move();
                setBallPanel(); // ?????
                ballsPanel.repaint();
            }
        }
    You would call the setBallPanel once when you create your Ball object, not inside a loop like this as this makes no sense.

    Java Code:
        public void setBallPanel(BallsPanel ballsPanel)
        {
            this.ballsPanel = ballsPanel;
        }
    This looks better.


    I dont know what i should put into setBallPanel() if i add ballsPanel it leads to this error when i call ballsPanel.repaint() in the run method. Also i will put setBallPanel() in the ball constructor not the run method but i kept it in as the question i am now asking is what to feed into that method.
    Exception in thread "Thread-1" java.lang.NullPointerException
    at Ball.run(Ball.java:72)

    Thank You
    Then you're not passing in a valid reference to the current BallPanel object. You're passing in a null to the setter method. Of course you don't want to do this.

  11. #11
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    I realised i didn't initialise the panel in Ball so now the class looks like this.

    Java Code:
    public class Ball extends Thread 
    {
        final static int DIAMETER = 20;
        private int xCor;
        private int yCor;
        private int speedX;
        private int speedY;
        private int widthBound = 600;
        private int heightBound = 400;
        private boolean ballMoving;
        BallsPanel ballsPanel = new BallsPanel();
        private Ellipse2D.Double newBall;
        
        public Ball(int x, int y, int speedx, int speedy)
        {
            xCor = x;
            yCor = y;
            speedX = speedx;
            speedY = speedy;
            newBall = new Ellipse2D.Double(xCor,yCor,20,20);
            ballMoving = true;
            setBallPanel(ballsPanel);
        }
        
        public void draw(Graphics2D g)
        {
            g.setColor(Color.red);
            g.fill(newBall);
        }
        
        public void move()
        {
            xCor = xCor + speedX;
            yCor = yCor + speedY;
            
            if(xCor < 0)
            {
                xCor = 0;
                speedX = - speedX;
            } else if(xCor > widthBound)
            {
                xCor = widthBound;
                speedX = - speedX;
            }
            
            if(yCor < 0)
            {
                yCor = 0;
                speedY = - speedY;
            } else if(yCor > heightBound) {
                yCor = heightBound;
                speedY = - speedY;
            }
        }
        
        public void run()
        {
            while(ballMoving)
            {
                System.out.println("running . . .");
                move();
                ballsPanel.repaint();
            }
        }
        
        public void setBallPanel(BallsPanel ballsPanel)
        {
            this.ballsPanel = ballsPanel;
        }
    Now when i press start it adds a ball and starts it running which i can see in the console by my print ln. But still the ball is not visible on my panel
    I added a println into the paint component of BallsPanel. Its is only called once when it is opened but not when the Ball thread is running.
    Last edited by wdh321; 11-28-2012 at 07:27 PM.

  12. #12
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    Ok i changed ballsPanel.repaint() to ballsPanel.paintComponent(g);
    This gave me this error java.lang.NullPointerException at javax.swing.JComponent.paintComponent(JComponent.j ava:781) on the line in paintcomponent which says super.paintcomponent(g).
    I removed this line and my console output is now
    running . . .
    repaint
    running . . .
    repaint

    Which shows it is calling the paintcomponent method within Ballspanel. Still no display in my frame though.

  13. #13
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: MultiThreaded graphics repaint()

    Quote Originally Posted by wdh321 View Post

    Now when i press start it adds a ball and starts it running which i can see in the console by my print ln. But still the ball is not visible on my panel
    I added a println into the paint component of BallsPanel. Its is only called once when it is opened but not when the Ball thread is running.
    Is your BallPanel looping through its Ball collection in its paintComponent(...) method as I suggested?

  14. #14
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: MultiThreaded graphics repaint()

    Quote Originally Posted by wdh321 View Post
    Ok i changed ballsPanel.repaint() to ballsPanel.paintComponent(g);
    This gave me this error java.lang.NullPointerException at javax.swing.JComponent.paintComponent(JComponent.j ava:781) on the line in paintcomponent which says super.paintcomponent(g).
    I removed this line and my console output is now
    running . . .
    repaint
    running . . .
    repaint

    Which shows it is calling the paintcomponent method within Ballspanel. Still no display in my frame though.
    I don't see where we recommended that you do this, and have you seen a tutorial that tells you to do this? In my experience you should almost never, EVER, directly call paint or paintComponent except in very small cases. I suggest you follow this advice.

  15. #15
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    The paintcomponent is looping through my linkedlist of balls here. I have also added where the ball is added to the list.
    I know you didn't suggest doing that but i thought i would try and see what happens. Currently with it back to .repaint() it just prints running in the console.
    Java Code:
        public void paintComponent(Graphics g)
        {
            //super.paintComponent(g);
            Graphics2D g2D = (Graphics2D) g;
            ListIterator<Ball> it = balls.listIterator();
            for(int i = 0; i<balls.size(); i++)
            {
                balls.get(i).draw(g2D);
            }
            System.out.println("Repainted");
        }
        
        public void addBall()
        {
            newBall = new Ball(0,0,2,3);
            balls.add(newBall);
            newBall.start();
        }

  16. #16
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: MultiThreaded graphics repaint()

    You'll want to un-comment that super.paintComponent(g) call.

    Shouldn't addBall() be more like this?:

    Java Code:
    public void addBall()
    {
        newBall = new Ball(0,0,2,3);
        newBall.setBallPanel(this);  // consider calling this here!
        balls.add(newBall);
        newBall.start();
    }

  17. #17
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    Thank you! that was the missing line to getting my ball to print to the panel. I now get the ball displayed at its starting co ordinates, and it appears repaint is still getting called but it is not updating the co ordinates and getting repositioned so i will now look into why that is.

  18. #18
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    Problem is sorted now, i had forgotten to set my ball to its new co ordinates.
    I have adjusted the values to double as the balls were moving very fast. My values are now 0.00001 and 0.00002 for the speed. Is this sensible, and the only way to reduce the speed? Im guessing it is.
    I do have some fairly large performance issues now though. Even adding 1 extra ball affects performance quite a lot! I think i will look on how i can control the threads, and any ways i can speed up performance.
    Thanks Fubarable for all the help.
    Last edited by wdh321; 11-28-2012 at 10:04 PM.

  19. #19
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: MultiThreaded graphics repaint()

    Glad that it helped. I'm not sure what you can do re performance issues, since I'm not a "games" programmer, but one thing to consider is to make sure that your Thread.sleep time is not too short. Another thing is to make sure you use elapsed system time to calculate how far to move the ball rather than the sleep time since the latter can vary.

  20. #20
    wdh321 is offline Senior Member
    Join Date
    Aug 2011
    Posts
    116
    Rep Power
    0

    Default Re: MultiThreaded graphics repaint()

    EDIT: The problem i first mentioned has now been fixed.

    Also my animation buttons are not working at all how i had hoped.
    I have buttons start, add, pause.
    Start > start the animation and after a pause will continue the animation.
    add > add a ball to the frame but will not start it.
    pause > pause animation

    Add uses this code to add the ball to my linkedlist and start the thread running. This should also add the static ball to the frame using the repaint method in run(). At the moment it appears to do nothing
    Java Code:
    public void addBall()
        {
            newBall = new Ball(0,0,0.00001,0.00002);
            newBall.setBallPanel(this);
            balls.add(newBall);
            newBall.start();
        }
    Start and pause use the code below to set the boolean value of each ball in the linked list to either true or false. This controls the while(ball moving) loop within the run method.
    When i press start before any balls are added it adds a new ball and starts its animation, even though it shouldn't be able to do this as there hasn't been a ball created yet? I can then pause the animation. when i press start again it will start all current balls on the frame and also add an extra one. Clearly something is not quite right there. Any ideas on these few problems i have? I can continue on the end of this topic or start a new one depending on which is best.
    Java Code:
    public void controlAnimation(boolean control)
        {
            ListIterator<Ball> it = balls.listIterator(0);
            while(it.hasNext())
            {
                it.next().setBallMoving(control);
            }
        }
    Last edited by wdh321; 11-30-2012 at 12:46 AM.

Similar Threads

  1. multithreaded client socket
    By mdpkishore in forum Advanced Java
    Replies: 4
    Last Post: 08-26-2012, 02:31 PM
  2. How to debug multithreaded applications
    By nxhoaf in forum Eclipse
    Replies: 1
    Last Post: 03-25-2011, 05:41 AM
  3. Multithreaded server crashes
    By skarosg3 in forum Threads and Synchronization
    Replies: 7
    Last Post: 05-26-2010, 10:24 AM
  4. Multithreaded search
    By xjx in forum Threads and Synchronization
    Replies: 2
    Last Post: 01-13-2010, 01:02 AM
  5. A simple multithreaded server
    By Java Tip in forum java.net
    Replies: 0
    Last Post: 04-07-2008, 09:15 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •