Welcome to the Java Programming Forums


The professional, friendly Java community. 21,500 members and growing!


The Java Programming Forums are a community of Java programmers from all around the World. Our members have a wide range of skills and they all have one thing in common: A passion to learn and code Java. We invite beginner Java programmers right through to Java professionals to post here and share your knowledge. Become a part of the community, help others, expand your knowledge of Java and enjoy talking with like minded people. Registration is quick and best of all free. We look forward to meeting you.


>> REGISTER NOW TO START POSTING


Members have full access to the forums. Advertisements are removed for registered users.

Results 1 to 6 of 6

Thread: problem with invokeLater and repaint

  1. #1
    Junior Member
    Join Date
    Sep 2011
    Posts
    29
    Thanks
    2
    Thanked 2 Times in 2 Posts

    Default problem with invokeLater and repaint

    I have a weird problem with my gui. I read on the internet that you should use invokeLater to start swing stuff so I thought I'd give it a try. But when in run the gui with it, it just shows a blank jframe and doesn't even respond when I click the X to close it. So I made a little test program:
    package Test;
     
    import java.awt.*;
    import javax.swing.*;
    import java.awt.geom.*;
     
    public class TestWindow
    {
    	private JFrame frame;	
    	private JDialog window;
    	private TestPanel panel;
     
    	public TestWindow()
    	{
    		frame = new JFrame("test");
    		panel = new TestPanel();
    		frame.setContentPane(panel);
    		frame.setPreferredSize(new Dimension(800,600));
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);		
    		frame.pack();
    		frame.setVisible(true);		
    	}
     
    	public void runGUI()
    	{
    		long lasttime = System.currentTimeMillis();		
    		int waittime = 1000;
    		while(true)
    		{
    			long thistime = System.currentTimeMillis();
    			if( (thistime - lasttime) >= waittime )
    			{				
    				System.out.println("checkpoint 1"); //this comes up once a second like it should when i run the program
                                    panel.repaint();
    				lasttime = System.currentTimeMillis();				
    			}
    			else
    			{
    				Thread.yield();
    			}
    		}		
    	}
     
    	public static void main(String[] args)
    	{
    		SwingUtilities.invokeLater( new Runnable()
    		{
    			public void run()
    			{
    				TestWindow test = new TestWindow();
    				test.runGUI(); //<-- this causes the problem
    			}
    		});
    	}
    }
     
    class TestPanel extends JPanel
    {
    	int x;
     
    	public TestPanel()
    	{
    		x = 0;
    	}
     
    	public void paintComponent(Graphics g)
    	{
    		System.out.println("checkpoint 2"); //this never comes up
                    super.paintComponent(g);
    		Graphics2D g2 = (Graphics2D) g;
    		x++;
    		g2.draw( new Rectangle2D.Double(x,x,50,50) );
    	}
    }

    If I never call runGUI() the program works and the square moves when i resize the window. When I do call runGUI() it gives me the blank window. I have no idea why it's doing this or even what to google for.


  2. #2
    Member andbin's Avatar
    Join Date
    Dec 2013
    Location
    Italy
    Posts
    443
    Thanks
    4
    Thanked 122 Times in 114 Posts

    Default Re: problem with invokeLater and repaint

    Quote Originally Posted by Harry Blargle View Post
    If I never call runGUI() the program works and the square moves when i resize the window. When I do call runGUI() it gives me the blank window. I have no idea why it's doing this or even what to google for.
    Several things are really bad. First, never do a loop like that in runGUI. Also and mainly because the yield() is only an "hint" (from javadoc: A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.).

    And second: never update the state of an app/game in a paint/paintComponent because repaints can occur many many times and for several reasons.

    Please, rethink the whole code. And if you want to do something "timed", the simplest and basic ways is the javax.swing.Timer (which dispatches events on the EDT).
    Andrea, www.andbin.net SCJP 5 (91%) SCWCD 5 (94%)

    Useful links for Java beginners My new project Java Examples on Google Code

  3. #3
    Junior Member
    Join Date
    Sep 2011
    Posts
    29
    Thanks
    2
    Thanked 2 Times in 2 Posts

    Default Re: problem with invokeLater and repaint

    package Test;
     
    import java.awt.*;
    import javax.swing.*;
    import java.awt.geom.*;
    import java.awt.event.*;
    import javax.swing.event.*;
     
    public class TestWindow
    {
    	private JFrame frame;	
    	private JDialog window;
    	private TestPanel panel;
     
    	public TestWindow()
    	{
    		frame = new JFrame("test");
    		panel = new TestPanel();
    		frame.setContentPane(panel);
    		frame.setPreferredSize(new Dimension(800,600));
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);		
    		frame.pack();
    		frame.setVisible(true);
    		class PaintAction extends AbstractAction
    		{
    			@Override
    			public void actionPerformed(ActionEvent e)
    			{
    				panel.repaint();
    			}
    		}
    		Timer timer = new Timer(1000, new PaintAction());		
    		timer.start();
    	}
     
    	public static void main(String[] args)
    	{
    		SwingUtilities.invokeLater( new Runnable()
    		{
    			public void run()
    			{
    				TestWindow test = new TestWindow();				
    			}
    		});
    	}
    }
     
    class TestPanel extends JPanel
    {
    	int x;
     
    	public TestPanel()
    	{
    		x = 0;
    	}
     
    	public void paintComponent(Graphics g)
    	{		
    		super.paintComponent(g);
    		Graphics2D g2 = (Graphics2D) g;
    		x++;
    		g2.draw( new Rectangle2D.Double(x,x,50,50) );
    	}
    }

    The timer does make it work, but it's still weird that the while loop doesn't since it works perfectly if I don't use invokeLater.

  4. #4
    Super Moderator
    Join Date
    Jun 2013
    Location
    So. Maryland, USA
    Posts
    4,712
    My Mood
    Mellow
    Thanks
    172
    Thanked 622 Times in 609 Posts

    Default Re: problem with invokeLater and repaint

    It should become less of a mystery if you read up on Swing, the EDT, and how drawing/updating is done. Consider what the while(true) loop is doing as you study.

  5. #5
    Junior Member
    Join Date
    Sep 2011
    Posts
    29
    Thanks
    2
    Thanked 2 Times in 2 Posts

    Default Re: problem with invokeLater and repaint

    With the while loop on the EDT it blocks that whole thread preventing updating and it worked before because they were seperate threads then, right?

  6. #6
    Super Moderator
    Join Date
    Jun 2013
    Location
    So. Maryland, USA
    Posts
    4,712
    My Mood
    Mellow
    Thanks
    172
    Thanked 622 Times in 609 Posts

    Default Re: problem with invokeLater and repaint

    You've actually created an interesting demo that shows some of the important differences that occur when starting a Swing app the right and wrong ways. You've essentially summarized why the right way performed worse than the wrong way, but I've added some code to yours to enhance the demo. I also moved the runGUI() method to the TestPanel class, because that's where it really belongs. The TestWindow is the container, the TestPanel is the content, and the animation is occurring on the TestPanel, so TestPanel should contain the code that creates the animation.

    There are two ways in the main() method to start the app, the right way using invokeLater() and the wrong way which calls the TestWindow() constructor on the main thread. (You can actually do both at once, but it's impossible to tell from which instance the messages are being printed, so there's little value in doing that.) I think you'll be able to verify your statement above by reviewing the messages that are printed while the app is running in the two different cases. Let me know if you have additional questions.
    import java.awt.*;
    import javax.swing.*;
    import java.awt.geom.*;
     
    public class TestWindow
    {
        private JFrame frame; 
        private TestPanel panel;
     
        public TestWindow()
        {
            frame = new JFrame("test");
            panel = new TestPanel();
            frame.setContentPane(panel);
            frame.setPreferredSize(new Dimension(800,600));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
            frame.pack();
            frame.setVisible(true);
     
            System.out.println( "Swing app is started on EDT: " +
                               SwingUtilities.isEventDispatchThread() );
     
            panel.runGUI();
        }
     
        public static void main(String[] args)
        {
            System.out.println( "Main thread is EDT: " +
                          SwingUtilities.isEventDispatchThread() );
     
            // either comment out this line:
            // new TestWindow();
     
            // or comment out this entire invokeLater() call and then run.
            SwingUtilities.invokeLater( new Runnable()
            {
                public void run()
                {
                    new TestWindow();
                }
            });
     
        }
    }
     
    class TestPanel extends JPanel
    {
        int x;
     
        public TestPanel()
        {
            x = 0;
        }
     
        @Override
        public void paintComponent(Graphics g)
        {
            System.out.println("checkpoint 2"); //this never comes up
            System.out.println( "panel's paintComponent() method is on EDT: " +
                   SwingUtilities.isEventDispatchThread() );
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.draw( new Rectangle2D.Double(x,x,50,50) );
        }
     
        // note that i moved this to the TestPanel class, because that's where
        // it really belongs. i would also rename it, 'animatePanel()' or similar
        public void runGUI()
        {
            long lasttime = System.currentTimeMillis();  
            int waittime = 1000;
            while(true)
            {
                long thistime = System.currentTimeMillis();
                if( (thistime - lasttime) >= waittime )
                {    
                    System.out.println("checkpoint 1"); //this comes up once a second like it should when i run the program
                    System.out.println( "while( true ) loop is on EDT: " +
                           SwingUtilities.isEventDispatchThread() );
     
                    x++;
                    repaint();
                    lasttime = System.currentTimeMillis();    
                }
            }  
        }
     
    }

Similar Threads

  1. Using a invokelater to update gui is this correct?
    By jvasher in forum AWT / Java Swing
    Replies: 0
    Last Post: December 4th, 2012, 11:53 AM
  2. EventQueue.invokeLater VS SwingUtilities.invokeLater
    By devett in forum AWT / Java Swing
    Replies: 6
    Last Post: September 29th, 2011, 11:58 AM
  3. Repaint doesn't repaint?
    By PotataChipz in forum AWT / Java Swing
    Replies: 6
    Last Post: January 18th, 2010, 08:56 PM
  4. swingworker OR a swingUtilites.invokeLater()
    By mdstrauss in forum AWT / Java Swing
    Replies: 0
    Last Post: October 11th, 2009, 04:50 AM
  5. Replies: 0
    Last Post: October 10th, 2009, 01:25 PM