1

Is it possible to repaint a JPanel from within a loop in another object? I have a JFrame that consists of a JPanel (DrawPanel) and a SA object. I would like to update/repaint the JPanel during the while loop in this SA object. I started a new thread, but still panel.repaint() does not execute.

public class Mainform extends JFrame {
    private DrawPanel DrawPanel;
    public static void main(String[] args) {
        DrawPanel panel = new DrawPanel();
        SA sa = new SA(panel);
        Thread t = new Thread(sa);
        t.start();
        //...
    }
}
public class DrawPanel extends JPanel implements MouseMotionListener, MouseListener {
    public DrawPanel() {
        super();
        setBackground(Color.WHITE);
        addMouseWheelListener(this);
        addMouseListener(this);
        addMouseMotionListener(this);
    }
    //...
}
public class SA implements Runnable {
    private DrawPanel panel;
    public SA(DrawPanel p) {
        this.panel = p;
        init();
    }
    public void run() {
        while (true) {
            //...
            panel.repaint();
        }
    }
}

EDIT: run is public

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • run can't be private - but basically, yes...this assumes that the panel you are trying to repaint has being added to a container, which is attached to a native peer and is visible... – MadProgrammer Jan 08 '14 at 10:28
  • Try to invalidate the panel instead of calling repaint(). – JTMon Jan 08 '14 at 10:31
  • @jtmon invalidating the component will mark for layout, which will, once it's laid out, will be repainted... – MadProgrammer Jan 08 '14 at 10:34

1 Answers1

2

The basic answer is "yes".

This assumes that the component you are trying to repaint is

  1. Added to a container
  2. That container is attached to some kind of native peer (ie a window)
  3. That window is visible.

The RepaintManager is generally smart enough to know not to waste time painting something that isn't displayable.

The following example is rather basic, but will increment a counter within the paintComponent of a JPanel each time it is called. The Runnable, which is attached to a Thread, will update every second...

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RepaintTest {

    public static void main(String[] args) {
        new RepaintTest();
    }

    public RepaintTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                TestPane tp = new TestPane();
                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(tp);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Thread thread = new Thread(new Repainter(tp));
                thread.setDaemon(true);
                thread.start();
            }
        });
    }

    public class Repainter implements Runnable {

        private JPanel panel;

        public Repainter(JPanel panel) {
            this.panel = panel;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                }
                panel.repaint();
            }
        }

    }

    public class TestPane extends JPanel {

        private int repaints = 0;

        public TestPane() {
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics g2d = (Graphics2D) g.create();
            repaints++;
            FontMetrics fm = g2d.getFontMetrics();
            String text = Integer.toString(repaints);
            int x = (getWidth() - fm.stringWidth(text)) / 2;
            int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
            g2d.drawString(text, x, y);
            g2d.dispose();
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I think it's a bad praxis, to call repaint outside of a swing thread. I would do the same using the swing-timer or at least using SwingUtilities.invokeLater – Sergiy Medvynskyy Jan 08 '14 at 10:49
  • @MadProgrammer [please did you read](http://stackoverflow.com/questions/20905985/is-the-swing-repaint-method-still-safe-to-use-outside-the-edt-in-java-7), I'm still think that repaint() isn't thread safe in (because I see that) JDK7 (is used in your code example), sure it could be endless discusion about – mKorbel Jan 08 '14 at 10:50
  • @mKorbel I'm not sure about Java 7, but assuming that the RepaintManager is scheduling repaints onto the Event Queue, how can it not be...? – MadProgrammer Jan 08 '14 at 11:06
  • @SergiyMedvynskyy repaint makes a request to the RepaintManager, which posts a repaint event onto the event queue, which is the processed by the a EDT, so that the actual paint is completed within the context of the EDT – MadProgrammer Jan 08 '14 at 11:09
  • I added `System.out.println(EventQueue.isDispatchThread());` to the `paintComponent` method and it always prints `true`, test using Java 7 on Mac OSX – MadProgrammer Jan 08 '14 at 11:12
  • Further, digging into the `repaint` code, it basically calls `PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE, new Rectangle(x, y, width, height));` and then `Toolkit.getEventQueue().postEvent(e);`, which is further wrapped around a `ReentrantLock` within the queue...There is also an issue around no one actually knowing one way or another...? – MadProgrammer Jan 08 '14 at 11:15
  • 2
    @SergiyMedvynskyy 9 out of 10 times I'd prefer to use a Swing `Timer`, but the question relates to a `Thread` so I followed that line of logic. Besides, even with that 1 case, I'd be trying to see how I could make it use a Swing `Timer` as well :P – MadProgrammer Jan 08 '14 at 11:21
  • @MadProgrammer PaintEvent --> ReentrantLock, I'm not sure about, this question was initialized by my comments in another (linked) threads, no idea whats happends but from JRE1.7_025 here were bunch of question why repaint() from Thread/Runnable#Thread isn't natural or smooth in compare with previous JREs, then I'm asumming that those (part of) events are lost somewhere in (recrusive preventer) RepaintManager, I'm have big trouble with RepaintManager – mKorbel Jan 09 '14 at 07:55