3

First of all I want to say I'm aware this aproach is wrong so I'm asking this question because of pure curiousity. Lets say I have a swing application like this:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ThreadSleeping {
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    JButton button = new JButton("Load");
    JLabel label = new JLabel();

    public ThreadSleeping() {
        panel.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                label.setIcon(new ImageIcon(
                        "C:/Users/Public/Pictures/Sample Pictures/Tulips.jpg"));
                System.out.println("Tulips painted");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                label.setIcon(new ImageIcon(
                        "C:/Users/Public/Pictures/Sample Pictures/Koala.jpg"));
                System.out.println("Koala painted");

            }
        });

        frame.add(panel, BorderLayout.NORTH);
        frame.add(label, BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(1024, 768);
        // frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ThreadSleeping();
            }
        });
    }
}

Basically when I click a Load button I expect that Tulips.jpg image displays then GUI freezes for a 2 seconds and after that I expect that Koala.jpg image displays. But what happens is that: I click on button, GUI freezes for a 2 seconds and Koala.jpg displays. No Tulips.jpg before that. But thing that confuses me is when I put those System.out.println("Tulips painted"); and System.out.println("Koala painted");. So when I click on button it prints "Tulips painted" and after 2 seconds "Koala painted". Can someone tell me whats going on here? Regards.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Branislav Lazic
  • 14,388
  • 8
  • 60
  • 85
  • You need to return from `actionPerformed` so that swing can update the GUI. – enobayram Mar 24 '13 at 15:26
  • 1
    if you sleep the edt, the resulting mis-behaviour is basically unpredictable. It doesn't make much sense trying to interpret anything into it. Simply play by the rules and put your time and effort into something useful :-) – kleopatra Mar 24 '13 at 15:34
  • 3
    @kleopatra - I disagree wholeheartedly. It's very educational to understand *why* something is a bad practice, much more valuable than simply listening to others say "Don't!". If you "simply play by the rules" you'll never amount to anything - understand why the rules exist, and use that knowledge to your advantage. – dimo414 Mar 24 '13 at 16:00
  • @dimo414 you are right, should have left out the last sentence - being a big fan of _why_ myself ;-) – kleopatra Mar 25 '13 at 12:17

2 Answers2

3
  1. works in this case, because you programatically freeze ouf Swing GUI, but there is/aren't another update(s), ot another JComponent(s)

  2. doens't works in the case that there are a few another updated to the Swing GUI, Thread.sleep(int) freeze Event Dispatch Thread,

  3. by default all updates to the JComponents XxxModels never will be visible on the JComponents view

  4. example until sleep ended you'll lost all updated to the GUI

Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • important are Native OS, used Look and Feel and methods declared in API as Thread safe, then by defaul WinXP, Java6, MetalLoookAndFeel and methods setText(), append() ... works by default, but in Java7 were changed some of this logics – mKorbel Mar 24 '13 at 15:43
2

The point I intended to make in my comment:

if you sleep the edt, the resulting mis-behaviour is basically unpredictable.

Unpredictable in the sense that you can't know what will happen or not. All we can do, is guess ...

The technical reason is that most ui updates don't happen immediately but are scheduled: we can't really know what's waiting in the line behind us. Remember: it's only one lane, and when in the actionPerformed, it's we that are sitting in it.

For educational reasons, the code below is the original code with a couple of lines to un/comment to demonstrate different scenarios.

  • [0] resetting the icon before/after sleeping: as you already noticed, the first doesn't show even though the property is taken. Technical reason: visual update happens via label.repaint() which is scheduled on the EDT for latter processing (as its api doc states)
  • [1] skimming the api doc, we notice another method: paintImmediately(...) which is documented to do exactly what it's name says and allowed - as we are on the EDT - is allowed to be called. Looks like success, the yellow icon shows up.
  • [2] but wait: being in the center of a Borderline, the label fills that area anyway, independent of whether or not it has an icon. Let's try to put it into a region that requires a re-layout, as f.i. into the south. Back to square [0], yellow icon not showing.
  • [3] looking into the source of setIcon(..) reveals that layout is ... scheduled again. We learned in square [1] that we can force thingies to happen immediately, in case of layout that would be the pair invalidate() / validate(). Bingo, yellow icon even when in south.
  • [4] nasty subclass which schedules the icon property setting (note: while contrived here there is nothing in its contract that hinders subclasses to do it!). As the property isn't even set, yellow isn't showing, back to square [0]

At the end of the day (but before going to sleep the EDT :-), there is simply no way to reliably predict the visual outcome during the sleep. And visuals are only the tip of the ice...

/**
 * Unpredictable behaviour when sleeping the EDT.
 * http://stackoverflow.com/q/15600203/203657
 * 
 * [0] run as-is: property set but yellow not showing
 * [1] uncomment paintImmediately: yellow showing in center
 * [2] add label to south: yellow not showing
 * [3] force immediate in-/validation: yellow showing in south
 * [4] subclass label with invoked property setting: 
 *       property not set, yellow not showing
 * 
 */
public class ThreadSleeping {
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    JButton button = new JButton("Load");
    JLabel label = new JLabel() {
// [4] subclass implemented to schedule the property setting         
//        @Override
//        public void setIcon(final Icon icon) {
//            SwingUtilities.invokeLater(new Runnable() {
//                public void run() {
//                    superSetIcon(icon);
//                    
//                }
//            });
//        }
//        
//        protected void superSetIcon(Icon icon) {
//            super.setIcon(icon);
//        }
//        
    };

    public ThreadSleeping() {
        panel.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                Icon firstIcon = new FixedIcon(Color.YELLOW, 100, 100);
                Icon secondIcon = new FixedIcon(Color.RED, 500, 500);
                label.setIcon(firstIcon);
                // check if the property is already set
                System.out.println(label.getIcon());
                // following lines try to force the output before going to sleep
                // [3] paintImmediately + force immediate re-layout 
                //  label.invalidate();
                //  label.getParent().validate();
                // {1] paintImmediately (fine for center, no effect for south)
                // ((JComponent) label.getParent()).paintImmediately(0, 0, 5000, 5000);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                label.setIcon(secondIcon);

            }
        });

        frame.add(panel, BorderLayout.NORTH);
        // un/comment one of the following, placing the
        // label either in CENTER (= sized to fill)
        frame.add(label, BorderLayout.CENTER);
        // [2] or in SOUTH (= sized to pref)
        // frame.add(label, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(1024, 768);
        frame.setVisible(true);
    }

    /**
     * Simple fixed size Icon filling its area.
     */
    public static class FixedIcon implements Icon {

        private Color background;
        private int width;
        private int height;

        public FixedIcon(Color background, int width, int height) {
            this.background = background;
            this.width = width;
            this.height = height;
        }
        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            g.setColor(background);
            g.fillRect(0, 0, width, height);
        }

        @Override
        public int getIconWidth() {
            return width;
        }

        @Override
        public int getIconHeight() {
            return height;
        }
        @Override
        public String toString() {
            return "[" + background + " w/h " + width + "/" + height + "]";
        }


    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ThreadSleeping();
            }
        });
    }
}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • Thanks for the answer, it helped me a lot. So conclusion: In cases like this we should always stick with swing timer or swing worker to avoid freezing of GUI, right? – Branislav Lazic Mar 25 '13 at 15:03