Results 1 to 3 of 3
  1. #1
    Kephix is offline Member
    Join Date
    Apr 2009
    Posts
    2
    Rep Power
    0

    Default [SOLVED] Repainting in a thread

    Well, I'm trying to make a test program to see if I can do this, and so far I've had poor luck. What I've done so far is have a panel that I paint some things to, which I update by clicking. Then, what I'm trying to do is when I click a different button, something else shows up and animates by itself, by using a separate thread.

    The problem I'm having is that when I hit the other button to run the thread, the new objects are being drawn and updated properly, but the old objects are being removed until the thread finishes. Also, I've found that clicking updates the variables for the background objects so that when the thread ends, they are still drawn based on how many times I clicked.

    I've also found that the problem probably lies in the fact that when repaint is called by the thread to repaint the panel, the paint method in the panel refuses to be called until after the thread finishes. I'm not sure on this, but it's just my guess based on what I've tried.

    Here's the code that draws the stuff the thread should be calling and calls the panel's repaint.

    Java Code:
    private void drawThings()
    {
    	Graphics2D g2d = (Graphics2D)panel.getGraphics();
    	g2d.setColor(panel.getBackground());
    	g2d.fillRect(0, 0, panel.getWidth(), panel.getHeight());
    	panel.repaint();
    	//draw stuff
    }
    Before it causes any confusion, the panel class I have extends JPanel, and has a Thread object in it. The Thread object's run is called when I click the correct mouse button. The panel itself is passed into the Thread object's constructor, so it can call repaint on the panel. Also, the thread does call Thread.sleep(50) in the animation loop, in case someone was thinking that I need to give the panel time to update.

    Any indication to what I'm doing wrong would help, thanks!

  2. #2
    hardwired's Avatar
    hardwired is offline Senior Member
    Join Date
    Jul 2007
    Posts
    1,576
    Rep Power
    9

    Default

    Multi–threaded drawing can get messy. In my experience it is preferrable to have a single thread calling repaint on the graphic component, like a drum beat. Update state variables in the graphic component, which will be rendered during repaints, from the other background threads.
    There are often a lot of ways to put these kind of apps together. Here's a demo that attempts to show the basic idea.
    Java Code:
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.geom.*;
    import javax.swing.*;
    
    public class MultiThreads extends JPanel {
        StateScheduler scheduler = new StateScheduler(this);
        Point2D.Double leftOrigin  = new Point2D.Double(0,0);
        Point2D.Double rightOrigin = new Point2D.Double();
        Point2D.Double leftToRight = new Point2D.Double();
        Point2D.Double rightToLeft = new Point2D.Double();
        double radius;
    
        public boolean setLeft(Point2D.Double p) {
            leftToRight = p;
            return !getBounds().contains(p);
        }
    
        public boolean setRight(Point2D.Double p) {
            rightToLeft = p;
            return !getBounds().contains(p);
        }
    
        public boolean setOut(double radius) {
            this.radius = radius;
            return radius > Math.min(getWidth()/2, getHeight()/2);
        }
    
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                RenderingHints.VALUE_ANTIALIAS_ON);
            double w = getWidth();
            rightOrigin.setLocation(w,0);
            g2.setPaint(Color.red);
            g2.draw(new Line2D.Double(leftOrigin, leftToRight));
            g2.setPaint(Color.blue);
            g2.draw(new Line2D.Double(rightOrigin, rightToLeft));
            g2.setPaint(Color.green.darker());
            g2.draw(new Ellipse2D.Double(w/2-radius, getHeight()/2.0-radius,
                                         2*radius, 2*radius));
        }
    
        public Dimension getPreferredSize() {
            return new Dimension(400,400);
        }
    
        private JPanel getUIPanel() {
            String[] ids = { "left", "right", "out" };
            ActionListener al = new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    String id = e.getActionCommand();
                    if(id.equals("left")) {
                        scheduler.startLeft();
                    }
                    if(id.equals("right")) {
                        scheduler.startRight();
                    }
                    if(id.equals("out")) {
                        scheduler.startOut();
                    }
                }
            };
            JPanel panel = new JPanel();
            for(int i = 0; i < ids.length; i++) {
                JButton button = new JButton(ids[i]);
                button.setActionCommand(ids[i]);
                button.addActionListener(al);
                panel.add(button);
            }
            return panel;
        }
    
        public static void main(String[] args) {
            MultiThreads test = new MultiThreads();
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(test);
            f.add(test.getUIPanel(), "Last");
            f.pack();
            f.setLocation(200,200);
            f.setVisible(true);
        }
    }
    
    class StateScheduler implements Runnable {
        MultiThreads view;
        Thread thread;
        long delay = 100;
        boolean isRunning    = false;
        boolean leftRunning  = false;
        boolean rightRunning = false;
        boolean outRunning   = false;
        double deltaL = 3.0;
        double deltaR = 1.5;
        double deltaO = 2.0;
    
        public StateScheduler(MultiThreads mt) {
            view = mt;
        }
    
        public void run() {
            while(isRunning) {
                try {
                    Thread.sleep(delay);
                } catch(InterruptedException e) {
                    stop();
                }
                view.repaint();
            }
        }
    
        private void start() {
            if(!isRunning) {
                isRunning = true;
                thread = new Thread(this);
                thread.setPriority(Thread.NORM_PRIORITY);
                thread.start();
                //System.out.println("repaint thread has started");
            }
        }
    
        private void stop() {
            isRunning = false;
            if(thread != null) {
                thread.interrupt();
            }
            thread = null;
            //System.out.println("repaint thread has stopped");
        }
    
        private void testStop() {
            if(!leftRunning && !rightRunning && !outRunning) {
                stop();
            }
        }
    
        public void startLeft() {
            if(!leftRunning) {
                if(!isRunning) start();
                leftRunning = true;
                new Thread(leftRunner).start();
            }
        }
    
        public void startRight() {
            if(!rightRunning) {
                if(!isRunning) start();
                rightRunning = true;
                new Thread(rightRunner).start();
            }
        }
    
        public void startOut() {
            if(!outRunning) {
                if(!isRunning) start();
                outRunning = true;
                new Thread(outRunner).start();
            }
        }
    
        private Runnable leftRunner = new Runnable() {
            public void run() {
                Point2D.Double p = new Point2D.Double();
                boolean done = false;
                double distance = deltaL;
                do {
                    double dy = view.getHeight() - 0;
                    double dx = view.getWidth() - 0;
                    double theta = Math.atan2(dy, dx);
                    double x = distance*Math.cos(theta);
                    double y = distance*Math.sin(theta);
                    p.setLocation(x, y);
                    done = view.setLeft(p);
                    distance += deltaL;
                    try {
                        Thread.sleep(delay);
                    } catch(InterruptedException e) {
                        break;
                    }
                } while(!done);
                p.setLocation(0,0);
                view.setRight(p);
                leftRunning = false;
                testStop();
            }
        };
    
        private Runnable rightRunner = new Runnable() {
            public void run() {
                Point2D.Double p = new Point2D.Double();
                boolean done = false;
                double distance = deltaR;
                do {
                    double dy = view.getHeight() - 0;
                    double dx = 0 - view.getWidth();
                    double theta = Math.atan2(dy, dx);
                    double x = view.getWidth() + distance*Math.cos(theta);
                    double y = distance*Math.sin(theta);
                    p.setLocation(x, y);
                    done = view.setRight(p);
                    distance += deltaR;
                    try {
                        Thread.sleep(delay);
                    } catch(InterruptedException e) {
                        break;
                    }
                } while(!done);
                p.setLocation(0,0);
                view.setRight(p);
                rightRunning = false;
                testStop();
            }
        };
    
        private Runnable outRunner = new Runnable() {
            public void run() {
                Point2D.Double p = new Point2D.Double();
                boolean done = false;
                double distance = deltaO;
                do {
                    done = view.setOut(distance);
                    distance += deltaO;
                    try {
                        Thread.sleep(delay);
                    } catch(InterruptedException e) {
                        break;
                    }
                } while(!done);
                view.setOut(0);
                outRunning = false;
                testStop();
            }
        };
    }
    Last edited by hardwired; 04-11-2009 at 06:29 AM. Reason: Remove thread url comment from file.

  3. #3
    Kephix is offline Member
    Join Date
    Apr 2009
    Posts
    2
    Rep Power
    0

    Default

    If I understand you right, you're suggesting that I put the
    Java Code:
    //draw stuff
    in the panel's paint method, and toggle drawing it from the second thread by use of a boolean or some other variable checked in the repaint method. That's probably the best way to do it, now that I think about it, so thanks for the help.

Similar Threads

  1. Difference between Thread.yield() and Thread.sleep() methods
    By Nageswara Rao Mothukuri in forum New To Java
    Replies: 12
    Last Post: 07-30-2010, 05:37 PM
  2. repainting lines separately
    By rstepler in forum Java 2D
    Replies: 8
    Last Post: 07-07-2008, 02:46 AM
  3. data from the main/GUI thread to another runnin thread...
    By cornercuttin in forum Threads and Synchronization
    Replies: 2
    Last Post: 04-23-2008, 10:30 PM
  4. Replies: 0
    Last Post: 01-28-2008, 07:02 AM

Posting Permissions

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