Java秋千等待消息不会出现

发布于 2025-02-04 16:16:37 字数 5195 浏览 2 评论 0原文

我有一个JAVA3D场景图更新任务,我的笔记本电脑需要3-4秒即可完成。在此期间,我希望一个窗口出现,要求用户等到作业完成。我有一个类似的情况,需要更长的时间,而我对摇摆工的实施则效果很好。但是,当我将代码应用于此更新操作时,对话框根本不会显示。

这是我想显示的waitdialog(我也尝试从jdialog继承 - 没有更改)

public class WaitDialog extends JFrame {
    
    private static final long serialVersionUID = 1L;            
    
    protected int rows = 2;
    protected int cols= 1;
    protected int rowSpace= 12;
    protected int colSpace= 12;
    
    public WaitDialog(Component parent, String message) {
        JPanel panel = new JPanel(new GridLayout(rows, cols, rowSpace, colSpace));      
        panel.setBorder( new EmptyBorder(12, 12, 12, 12) );                                                     
        panel.add(new JLabel(message), BorderLayout.CENTER);                                                    
        JProgressBar progressBar = new JProgressBar();                                                          
        progressBar.setIndeterminate(true);                                                                     
        panel.add(progressBar);                                                                                 
        setUndecorated(true);                                                                                   
        setAlwaysOnTop(true);
        getContentPane().add(panel);                                                                            
        pack();                                                                                                 
        setLocationRelativeTo(parent);                                                                          
        setVisible(true);                                                                                       
    }
    
    public void close() {
        setVisible(false);                                                                                      
        dispose();                                                                                              
    }
}

使我头痛看起来像这样的方法...

public Result doIt(String fileName) throws Exception {
        
        WaitDialog waitDialog = new WaitDialog(null, "Update...");      
        
        InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
        
        Result result = ansicht.calculateResult(inputStream);  // Java3D calculation
        
        waitDialog.close();

        return result;
    }

在此版本中,未显示waitdialog,但calculteresult效果很好。 因此,如上所述,我从另一个地方复制了工作逻辑,看起来像这样……

public Result doIt(String fileName) throws Exception {
        
        WaitDialog waitDialog = new WaitDialog(null, "Update...");          
        
        SwingWorker<Result,Void> worker = new SwingWorker<Result,Void>() {                          
            @Override
            protected Result doInBackground() throws Exception {
                InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
        
                Result result = ansicht.calculateResult(inputStream);  // Java3D calculation
                return result;                                                                  
            }

            @Override
            protected void done() {
                waitDialog.close();                                                             
            }
        };
        worker.execute();                                                                       
        
        Result result = worker.get();
        return result;
    }

没有效果。没有显示的消息,计算菌都可以正常工作。 因此,我在网络上查看了我可能拥有的选择,并尝试了这一点...

public Result doIt(String fileName) throws Exception {
        
        WaitDialog waitDialog = new WaitDialog(null, "Update...");  
        
        InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));

        SwingUtilities.invokeAndWait(() -> {
            try {
                result = ansicht.calculateResult(inputStream);  // Java3D calculation
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        
        waitDialog.close();

        return result;
    }

结果现在是类变量。此版本显示了“等待对话框”,但是计算器呼叫没有回来,对话框不会消失。

我还尝试了一个带有老式线程的版本,我在网络中找到了它……

public Result doIt(String fileName) throws Exception {

        WaitDialog waitDialog = new WaitDialog(null, "Update...");  

        Thread thread = new Thread(() -> {                                  
                SwingUtilities.invokeAndWait(() -> {
                    try {
                        InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
                        result = ansicht.calculateResult(inputStream);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        thread.start(); 

        while(thread.isAlive()) {};
        waitDialog.close();

        return result;
    }

也没有做我想做的事情。

您能告诉我如何做吗?先感谢您。

I have a Java3D scenegraph update task which takes 3-4 seconds on my laptop to complete. During this time I want a window showing up, asking the user to wait until the job completes. I have a simelar situation which takes longer and my implementation with a SwingWorker works fine. But when I apply the code to this update operation the dialog doesn't show up at all.

Here is the WaitDialog which I want to show (I also tried to inherit from JDialog - no change)

public class WaitDialog extends JFrame {
    
    private static final long serialVersionUID = 1L;            
    
    protected int rows = 2;
    protected int cols= 1;
    protected int rowSpace= 12;
    protected int colSpace= 12;
    
    public WaitDialog(Component parent, String message) {
        JPanel panel = new JPanel(new GridLayout(rows, cols, rowSpace, colSpace));      
        panel.setBorder( new EmptyBorder(12, 12, 12, 12) );                                                     
        panel.add(new JLabel(message), BorderLayout.CENTER);                                                    
        JProgressBar progressBar = new JProgressBar();                                                          
        progressBar.setIndeterminate(true);                                                                     
        panel.add(progressBar);                                                                                 
        setUndecorated(true);                                                                                   
        setAlwaysOnTop(true);
        getContentPane().add(panel);                                                                            
        pack();                                                                                                 
        setLocationRelativeTo(parent);                                                                          
        setVisible(true);                                                                                       
    }
    
    public void close() {
        setVisible(false);                                                                                      
        dispose();                                                                                              
    }
}

The method that gives me headache looks like this...

public Result doIt(String fileName) throws Exception {
        
        WaitDialog waitDialog = new WaitDialog(null, "Update...");      
        
        InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
        
        Result result = ansicht.calculateResult(inputStream);  // Java3D calculation
        
        waitDialog.close();

        return result;
    }

In this version, the waitDialog is not shown but calculteResult works fine.
Therefore, as indicate above, I copied the working logic from another place, which looks like this...

public Result doIt(String fileName) throws Exception {
        
        WaitDialog waitDialog = new WaitDialog(null, "Update...");          
        
        SwingWorker<Result,Void> worker = new SwingWorker<Result,Void>() {                          
            @Override
            protected Result doInBackground() throws Exception {
                InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
        
                Result result = ansicht.calculateResult(inputStream);  // Java3D calculation
                return result;                                                                  
            }

            @Override
            protected void done() {
                waitDialog.close();                                                             
            }
        };
        worker.execute();                                                                       
        
        Result result = worker.get();
        return result;
    }

which shows no effect. No message shown and calculateResult works still fine.
So I looked in the web what alternatives I might have and tried this...

public Result doIt(String fileName) throws Exception {
        
        WaitDialog waitDialog = new WaitDialog(null, "Update...");  
        
        InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));

        SwingUtilities.invokeAndWait(() -> {
            try {
                result = ansicht.calculateResult(inputStream);  // Java3D calculation
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        
        waitDialog.close();

        return result;
    }

result being now a Class variable. This version shows the wait dialog, but the calculateResult call is not coming back and the dialog doesn't disappear.

I also tried a version with an old fashioned Thread, which I found in the web...

public Result doIt(String fileName) throws Exception {

        WaitDialog waitDialog = new WaitDialog(null, "Update...");  

        Thread thread = new Thread(() -> {                                  
                SwingUtilities.invokeAndWait(() -> {
                    try {
                        InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
                        result = ansicht.calculateResult(inputStream);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        thread.start(); 

        while(thread.isAlive()) {};
        waitDialog.close();

        return result;
    }

also not doing what I want.

Can you please advise me how to do this? Thank you in advance.

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

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

发布评论

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

评论(2

゛清羽墨安 2025-02-11 16:16:37

Swing的工作方式是您拥有不断运行和处理GUI事件的事件调度线程(EDT)。当您执行长期运行任务时,您将其运行在其他线程上,然后告诉EDT您已经完成。

在EDT上被称为“ DOIT”吗?我要假设是。

您的所有版本都会阻止“ DOIT”,直到结果完成后。您需要让DOIT完成。我建议回电。

public void processResult( Result result){
  //this should be a continuation of the code from where you call doIt
}

然后,在第二版中,您可以更新它。

public void doIt(String fileName) throws Exception {
    
    WaitDialog waitDialog = new WaitDialog(null, "Update...");          
    
    SwingWorker<Result,Void> worker = new SwingWorker<Result,Void>() {                          
        @Override
        protected Result doInBackground() throws Exception {
            InputStream inputStream = new BufferedInputStream(new   FileInputStream(fileName));
    
            Result result = ansicht.calculateResult(inputStream);  // Java3D calculation
            return result;                                                                  
        }

        @Override
        protected void done() {
            waitDialog.close();
            processResult( get() );                                                             
        }
    };
    worker.execute();                                                                       
    
    //this is blocking and preventing your gui from updating.
    //Result result = worker.get();
    //return result;
}

这是您的代码最糟糕的版本。

SwingUtilities.invokeAndWait(() -> {
        try {
            result = ansicht.calculateResult(inputStream);  // Java3D calculation
        } catch (Exception e) {
            e.printStackTrace();
        }
    });

您实际上是将长期运行的任务发布到EDT并阻止当前线程,我怀疑这也是EDT。

The way swing works is you have the Event Dispatch Thread (EDT) that is constantly running and processing GUI events. When you have a long running task, you run it on a different thread, then tell the EDT that your are finished.

Is "doIt" being called on the EDT? eg did you start it from an event like a button? I am going to assume it is.

All of your versions are blocking "doIt" until after the result is finished. You need to let doIt finish. I would suggest a call back.

public void processResult( Result result){
  //this should be a continuation of the code from where you call doIt
}

Then in second version you can update it.

public void doIt(String fileName) throws Exception {
    
    WaitDialog waitDialog = new WaitDialog(null, "Update...");          
    
    SwingWorker<Result,Void> worker = new SwingWorker<Result,Void>() {                          
        @Override
        protected Result doInBackground() throws Exception {
            InputStream inputStream = new BufferedInputStream(new   FileInputStream(fileName));
    
            Result result = ansicht.calculateResult(inputStream);  // Java3D calculation
            return result;                                                                  
        }

        @Override
        protected void done() {
            waitDialog.close();
            processResult( get() );                                                             
        }
    };
    worker.execute();                                                                       
    
    //this is blocking and preventing your gui from updating.
    //Result result = worker.get();
    //return result;
}

This is the worst version of your code.

SwingUtilities.invokeAndWait(() -> {
        try {
            result = ansicht.calculateResult(inputStream);  // Java3D calculation
        } catch (Exception e) {
            e.printStackTrace();
        }
    });

That you are literally posting your long running task to the EDT and blocking the current thread, which I suspect is also the EDT.

生活了然无味 2025-02-11 16:16:37

除了(希望支持)马特的回答。

秋千不是线程,也不是单线线程,这意味着您应该尝试从事件派遣线程或执行长期运行或从上下文中更新UI的状态(或UI所依赖的任何内容)事件派遣线程的内容。

参见 Swing 同意的同意。

这里的重要一点是 swingworker#get 方法。 Javadocs州:

如有必要等待计算完成,然后检索其结果。

注意:呼叫上事件派遣线程将封锁所有事件,包括重新粉刷,从处理直到完成此次摇摆工作。

当您希望摇摆工作者阻止事件调度线程时,我们建议您使用模态对话框。

这对解释您的问题将有很长的路要走。

以下是另一种可能的方法。它利用专用swingworker ...,

public class MagicWorker extends SwingWorker<String, Void> {
    @Override
    protected String doInBackground() throws Exception {
        // This is where the  magic happens
        Thread.sleep(5000);
        return "Rabbit";
    }
}

并通过使用property> propertyChangelistener ...

worker.addPropertyChangeListener(new PropertyChangeListener() {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (worker.getState() == SwingWorker.StateValue.DONE) {
            dialog.dispose();
            try {
                String magic = worker.get();
                observer.magicDidHappen(magic);
            } catch (InterruptedException | ExecutionException ex) {
                observer.magicDidFailToHappen(ex);
            }
        }
    }
});

并使用观察者(又称听众)来通知呼叫者操作完成或失败,以便感兴趣的方可以处理操作的结果。

重要的是要注意,这只是您可以实现这一目标的一种方式,而Matt的解决方案也是有效的解决方案!

可运行的示例...

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new MainPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MainPane extends JPanel {

        public interface MagicObserver {
            public void magicDidHappen(String result);
            public void magicDidFailToHappen(Exception exp);
        }

        public MainPane() {
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());

            JButton makeMagic = new JButton("Make the magic happen");
            add(makeMagic);

            makeMagic.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    makeMagic.setEnabled(false);
                    makeMagic(new MagicObserver() {
                        @Override
                        public void magicDidHappen(String result) {
                            makeMagic.setEnabled(true);
                            JOptionPane.showMessageDialog(MainPane.this, result, "Magic", JOptionPane.INFORMATION_MESSAGE);
                        }

                        @Override
                        public void magicDidFailToHappen(Exception exp) {
                            makeMagic.setEnabled(true);
                            exp.printStackTrace();
                            JOptionPane.showMessageDialog(MainPane.this, "Failed to make magic", "Error", JOptionPane.ERROR_MESSAGE);
                        }
                    });
                }
            });
        }

        protected void makeMagic(MagicObserver observer) {
            MagicWorker worker = new MagicWorker();
            JDialog dialog = new JDialog(SwingUtilities.windowForComponent(this));
            dialog.add(new WaitPane());
            dialog.setModal(true);
            dialog.setUndecorated(true);
            dialog.pack();
            dialog.setLocationRelativeTo(this);

            worker.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if (worker.getState() == SwingWorker.StateValue.DONE) {
                        dialog.dispose();
                        try {
                            String magic = worker.get();
                            observer.magicDidHappen(magic);
                        } catch (InterruptedException | ExecutionException ex) {
                            observer.magicDidFailToHappen(ex);
                        }
                    }
                }
            });
            worker.execute();
            dialog.setVisible(true);
        }

    }

    public class WaitPane extends JPanel {
        public WaitPane() {
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());

            JProgressBar progressBar = new JProgressBar();
            progressBar.setIndeterminate(true);

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = new Insets(8, 8, 8, 8);
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            add(new JLabel("Making the magic happen"), gbc);
            add(progressBar, gbc);
        }
    }

    public class MagicWorker extends SwingWorker<String, Void> {
        @Override
        protected String doInBackground() throws Exception {
            // This is where the  magic happens
            Thread.sleep(5000);
            return "Rabbit";
        }
    }
}

In addition to (and hopefully supporting) matt's answer.

Swing is not thread and is single threaded, this means that you should try and update the state of the UI (or anything the UI relies on) from outside the context of the Event Dispatching Thread or execute long running or blocking operations from within the context of the Event Dispatching Thread.

See Concurrency in Swing for more details.

The important point here is the SwingWorker#get method. The JavaDocs state:

Waits if necessary for the computation to complete, and then retrieves its result.

Note: calling get on the Event Dispatch Thread blocks all events, including repaints, from being processed until this SwingWorker is complete.

When you want the SwingWorker to block on the Event Dispatch Thread we recommend that you use a modal dialog.

This would go a long way to explaining your issue.

The following is another possible approach. It makes use of dedicated SwingWorker...

public class MagicWorker extends SwingWorker<String, Void> {
    @Override
    protected String doInBackground() throws Exception {
        // This is where the  magic happens
        Thread.sleep(5000);
        return "Rabbit";
    }
}

and is monitored through the use of PropertyChangeListener...

worker.addPropertyChangeListener(new PropertyChangeListener() {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (worker.getState() == SwingWorker.StateValue.DONE) {
            dialog.dispose();
            try {
                String magic = worker.get();
                observer.magicDidHappen(magic);
            } catch (InterruptedException | ExecutionException ex) {
                observer.magicDidFailToHappen(ex);
            }
        }
    }
});

and makes use of an observer (AKA listener) to notify the caller that the operation completed or failed, so that the interested party can deal with the result of the operation.

It's important to note that this is just one way you might achieve this and matt's solution is also a valid solution!

Runnable example...

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new MainPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MainPane extends JPanel {

        public interface MagicObserver {
            public void magicDidHappen(String result);
            public void magicDidFailToHappen(Exception exp);
        }

        public MainPane() {
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());

            JButton makeMagic = new JButton("Make the magic happen");
            add(makeMagic);

            makeMagic.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    makeMagic.setEnabled(false);
                    makeMagic(new MagicObserver() {
                        @Override
                        public void magicDidHappen(String result) {
                            makeMagic.setEnabled(true);
                            JOptionPane.showMessageDialog(MainPane.this, result, "Magic", JOptionPane.INFORMATION_MESSAGE);
                        }

                        @Override
                        public void magicDidFailToHappen(Exception exp) {
                            makeMagic.setEnabled(true);
                            exp.printStackTrace();
                            JOptionPane.showMessageDialog(MainPane.this, "Failed to make magic", "Error", JOptionPane.ERROR_MESSAGE);
                        }
                    });
                }
            });
        }

        protected void makeMagic(MagicObserver observer) {
            MagicWorker worker = new MagicWorker();
            JDialog dialog = new JDialog(SwingUtilities.windowForComponent(this));
            dialog.add(new WaitPane());
            dialog.setModal(true);
            dialog.setUndecorated(true);
            dialog.pack();
            dialog.setLocationRelativeTo(this);

            worker.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if (worker.getState() == SwingWorker.StateValue.DONE) {
                        dialog.dispose();
                        try {
                            String magic = worker.get();
                            observer.magicDidHappen(magic);
                        } catch (InterruptedException | ExecutionException ex) {
                            observer.magicDidFailToHappen(ex);
                        }
                    }
                }
            });
            worker.execute();
            dialog.setVisible(true);
        }

    }

    public class WaitPane extends JPanel {
        public WaitPane() {
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());

            JProgressBar progressBar = new JProgressBar();
            progressBar.setIndeterminate(true);

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = new Insets(8, 8, 8, 8);
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            add(new JLabel("Making the magic happen"), gbc);
            add(progressBar, gbc);
        }
    }

    public class MagicWorker extends SwingWorker<String, Void> {
        @Override
        protected String doInBackground() throws Exception {
            // This is where the  magic happens
            Thread.sleep(5000);
            return "Rabbit";
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文