Linux上使用模态对话框时繁忙光标的Swing渲染问题

发布于 2024-11-30 13:21:42 字数 3142 浏览 1 评论 0原文

关闭模式对话框后在应用程序框架的玻璃窗格上设置忙碌光标时,并不总是显示忙碌光标。有时它有效(第一次它通常总是有效),有时则无效。

更好的是,在打开对话框之前设置忙碌光标时。显示忙碌光标,但是当将鼠标移入对话框然后移出对话框时,忙碌光标不再显示。

请注意,我仅在 Linux 上观察到以下错误。在 Mac OS X 或 Windows 上,行为是确定性且一致的。

另一个提示,在代码示例的第一种情况下,当鼠标未进入对话框并且使用键盘选择 YES_OPTION 时,始终显示忙碌的鼠标光标。同样在这种情况下,玻璃板上的“请稍等...”标签也从未被涂上。

这里有一个 SSCCE 演示了这些错误:

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 actionPerformed(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();
}

}

目前我在 JavaBug 队列中没有发现任何相关问题。在打开新的之前我会进一步搜索。

我也已经阅读了以下文章,但它不是很方便,因为从非模态对话框制作一个好的模态对话框并不简单: http://www.javaspecialists.eu/archive/Issue065.html

任何人都可以提供一些帮助? 预先感谢,皮埃尔

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 actionPerformed(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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

热风软妹 2024-12-07 13:21:42

您在这里遇到一些线程问题。

IsStartingInEDT 是真的吗?

如果是,那么您做错了,因为:

  • 您不应该在 UI 线程中休眠。这将停止屏幕更新。

如果不是,那么您就做错了,因为:

  • 必须从 UI 线程调用 OptionPane.showConfirmDialog()。

你应该这样做:

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();
    }
}

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();
    }
}
花辞树 2024-12-07 13:21:42

第一。通过使用 Tread.sleep(int) 肯定会阻止 EDT,所有问题都与 Swing 中的并发

2.nd 之所以有效,是因为 JOptionPane 的初始化创建了一个新的 EDT,

这里是关于 .... 的简单演示,请仅举个例子,并确保这是违反所有 Swing 规则,但通过在 EDT 期间使用 Tread.sleep(int) 演示了锁定和解锁 EDT

在此处输入图像描述 < img src="https://i.sstatic.net/Xubhb.png" alt="在此处输入图像描述">

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);
            }
        });
    }
}

结论 ->您可以创建新的Thread作为包装到Runnable中的BackGroung任务,如果您想模拟LongRunning任务并使用Thread.sleep(int),也许可以回答您的问题位于此处

确保正确的方法是使用 SwingWorker , Thread.sleep(int) 也是如此

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

enter image description here enter image description here

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文