开发者

Swing rendering problem of busy cursors when using modal dialogs on Linux

When setting a busy cursor on the glass pane of the application frame after closing a modal dialog, the busy cursor is not always displayed. Sometimes it works (the first time it is mostly always working), sometimes not.

Even better, when setting the busy cursor before opening the dialog. The busy cursor get displayed but when moving the mouse inside and then outside the dialog the busy cursor is not displayed anymore.

Note that I observe the following bug on Linux only. On Mac OS X or Windows the behavior is deterministic and consistent.

Another hint, in the first case of the code sample, when the mouse is NOT entering the dialog and the YES_OPTION is selected using the keyboard, the busy mouse cursor is always shown. Also in this case, the "Please wait..." label on the glass pane get never painted.

Here a SSCCE demonstrating these bugs:

import java.awt.event.*;
import javax.swing.*;

public class TestFrame extends JFrame {

private JPanel panel;
private JPanel glassPane;

public TestFrame() {
    final JButton button1 = new JButton(new AbstractAction("Start activity indicator after closing the dialog") {
        @Override
        public void actionPerfo开发者_如何学JAVArmed(ActionEvent e) {
            doAction1();
        }
    });

    final JButton button2 = new JButton(new AbstractAction("Start activity indicator before opening the dialog") {
        @Override
        public void actionPerformed(ActionEvent e) {
            doAction2();
        }
    });

    panel = new JPanel();
    panel.add(button1);
    panel.add(button2);
    getContentPane().add(panel, BorderLayout.NORTH);

    glassPane = (JPanel) getGlassPane();
    glassPane.setLayout(new BorderLayout());
    glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    glassPane.add(new JLabel("Please Wait..."), BorderLayout.CENTER);

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(800, 600);
    setVisible(true);
}

public void doAction1() {
    System.out.println("IsStartingInEDT?: "+  SwingUtilities.isEventDispatchThread());
    final int response = JOptionPane.showConfirmDialog(this, "Click on the YES_OPTION, busy indicator must start (if it does, try again).");
    if (JOptionPane.YES_OPTION == response) {
        startActivity();
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(200);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        stopActivity();
    }
}

public void doAction2() {
    startActivity();
    System.out.println("IsStartingInEDT?: "+  SwingUtilities.isEventDispatchThread());
    final int response = JOptionPane.showConfirmDialog(this, "Move the mouse inside the dialog (me) and then outside, the busy indicator is not shown anymore");
    if (JOptionPane.YES_OPTION == response) {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(200);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    stopActivity();
}

public void startActivity() {
    System.out.println("TestFrame.startActivity()");
    glassPane.setVisible(true);
}

public void stopActivity() {
    System.out.println("TestFrame.stopActivity()");
    glassPane.setVisible(false);
}

/**
 * @param args
 */
public static void main(String[] args) {
    new TestFrame();
}

}

At the moment I did not find any related issues in the JavaBug parade. I will search further before opening a new one.

I also already read the following article but it is not very convenient as making a good modal dialog from a non-modal one is not straightforward: http://www.javaspecialists.eu/archive/Issue065.html

Can anyone provide some help? Thanks in advance, Pierre


You have some threading issue here.

Is IsStartingInEDT true?

If yes, you are doing it wrong because:

  • You should not sleep in UI thread. This would stop the screen update.

If no, you are doing it wrong because:

  • OptionPane.showConfirmDialog() must be called from the UI thread.

you should do something like this:

public void doAction1() {
    if (!SwingUtilities.isEventDispatchThread())  {
         System.err.println("error, must be edt");
         return;
    }

    final int response = JOptionPane.showConfirmDialog(this, "Click on the YES_OPTION, busy indicator must start (if it does, try again).");

    if (JOptionPane.YES_OPTION == response) {
        startActivity();   // change glass panel in edt    

        // new thread for long standing task
        new Thread( new Runnable() { public void run() {    
           for (int i = 0; i < 5; i++) {
               try {
                   Thread.sleep(200);
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }

           SwingUtilities.invokeAndWait(new Runnable(){ public void run() {
              // changing glass panel need edt
              stopActivity();
           });
        }).start();
    }
}


1st. by using Tread.sleep(int) pretty sure to block EDT, with all issues desribed about Concurrency in Swing

2.nd works because initializations for JOptionPane create a new EDT

here is simple demonstrations about ...., please that only example and be sure that is against all Swing rules, but demostrated lock and unlock EDT by usage Tread.sleep(int) during EDT

Swing rendering problem of busy cursors when using modal dialogs on Linux

Swing rendering problem of busy cursors when using modal dialogs on Linux

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class ShakeComponents1 {

    private JFrame frame = new JFrame();
    private final String items[] = {"One", "Two", "Three"};
    private Timer timer;
    private JPanel panel = new JPanel();
    private JPanel buttonPanel = new JPanel();
    private JButton button = new JButton("  Exit  ");
    private boolean repeats = true;
    private boolean runs = false;
    private Color clr[] = {Color.red, Color.blue, Color.magenta};
    private Insets initMargin;

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ShakeComponents1().makeUI();
            }
        });
    }

    public void makeUI() {
        buttonPanel = new JPanel();
        buttonPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
        buttonPanel.setLayout(new BorderLayout());
        button.setPreferredSize(new Dimension(100, 45));
        button.setForeground(Color.darkGray);
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent event) {
                Runnable doRun = new Runnable() {

                    @Override
                    public void run() {
                        System.exit(0);
                    }
                };
                SwingUtilities.invokeLater(doRun);
            }
        });
        button.addMouseListener(new java.awt.event.MouseListener() {

            @Override
            public void mouseClicked(MouseEvent e) {
            }

            @Override
            public void mousePressed(MouseEvent e) {
            }

            @Override
            public void mouseReleased(MouseEvent e) {
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                if (runs) {
                    SwingUtilities.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            runs = false;
                            timer.stop();
                            changePnlBorder(new EmptyBorder(5, 5, 5, 5));
                            changeBtnForegroung(Color.darkGray);
                        }
                    });
                }
            }

            @Override
            public void mouseExited(MouseEvent e) {
                if (!runs) {
                    timer.start();
                    runs = true;
                }
            }
        });
        buttonPanel.add(button);
        final Insets margin = button.getMargin();
        panel.add(buttonPanel);
        for (int i = 0; i < 2; i++) {
            JComboBox combo = new JComboBox(items);
            combo.setMinimumSize(new Dimension(50, 25));
            combo.setMaximumSize(new Dimension(150, 25));
            combo.setPreferredSize(new Dimension(100, 25));
            combo.addActionListener(new ShakeAction());
            panel.add(combo);
        }
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel);
        frame.pack();
        frame.setLocation(50, 50);
        frame.setVisible(true);
        timer = new Timer(500, new ShakeAction());
        timer.setRepeats(repeats);
        initMargin = button.getMargin();
    }

    private class ShakeAction extends AbstractAction {

        private static final long serialVersionUID = 1L;
        private int noColor = 0;
        private Border border;
        private int count = 0;

        @Override
        public void actionPerformed(ActionEvent e) {
            timer.start();
            if (count > 5) {
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        try {
                            Thread.sleep(500);
                            changeBtnForegroung(Color.darkGray);
                            Thread.sleep(500);
                            count = 0;
                            Thread.sleep(750);
                        } catch (Exception e) {
                            System.out.println(e);
                        }
                    }
                }).start();
            } else {
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        try {
                            runs = true;
                            if (noColor < 2) {
                                noColor++;
                                changeBtnForegroung(clr[noColor]);
                            } else {
                                noColor = 0;
                                changeBtnForegroung(clr[noColor]);
                            }
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left + 10, initMargin.bottom, initMargin.right - 10));
                            border = new EmptyBorder(0, 5, 10, 5);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left - 10, initMargin.bottom, initMargin.right + 10));
                            border = new EmptyBorder(0, 0, 10, 10);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left + 10, initMargin.bottom, initMargin.right - 10));
                            border = new EmptyBorder(5, 10, 5, 0);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left - 10, initMargin.bottom, initMargin.right + 10));
                            border = new EmptyBorder(10, 10, 0, 0);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left, initMargin.bottom, initMargin.right));
                            border = new EmptyBorder(5, 5, 5, 5);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            count++;
                        } catch (Exception e) {
                            System.out.println(e);
                        }
                    }
                }).start();
            }
        }
    }

    private void changePnlBorder(final Border b) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                buttonPanel.setBorder(b);
                buttonPanel.revalidate();
                buttonPanel.repaint();
            }
        });
    }

    private void changeBtnForegroung(final Color c) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                button.setForeground(c);
            }
        });
    }

    private void changeBtnMargin(final Insets margin) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                button.setMargin(margin);
            }
        });
    }
}

conclusion -> you can create new Thread as BackGroung Task(s) wrapped into Runnable, if you wnat to simulate LongRunning Task and with Thread.sleep(int), maybe answer to your question is here

sure correct way would be by using SwingWorker for that, with Thread.sleep(int) too

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜