简体   繁体   English

从另一个方法中更新 JFrame

[英]Update JFrame from Within Another Method

I have a.csv file that is full pixel data from 60000 hand-drawn digits (the mnist dataset of hand-written numbers).我有一个 .csv 文件,它是来自 60000 个手绘数字(手写数字的 mnist 数据集)的完整像素数据。 A function called train() is called when a button is pushed.按下按钮时调用名为 train() 的 function。 From within the train() function, I would like each of the 60000 digits to be displayed on the screen.在 train() function 中,我希望将 60000 个数字中的每一个都显示在屏幕上。 The code works perfectly fine, I just can't get the JFrame to update from within the train() method.代码工作得很好,我只是无法从 train() 方法中更新 JFrame。

I tested the code and it 100% works as intended.我测试了代码,它 100% 按预期工作。 I just can't get the JFrame to update while in the train() function.我只是无法在 train() function 中更新 JFrame。

    public void trainMenu() {
        JButton trainBtn = new JButton("Train");
        JLabel otp = new JLabel();
        JPanel bottomBar = new JPanel();
        trainImage = new ImageIcon();
        JLabel imageLabel = new JLabel(this.trainImage);

        bottomBar.setLayout(new GridLayout(1,2,5,5));
        bottomBar.add(trainBtn);
        bottomBar.add(otp);
        this.frame.getContentPane().add(BorderLayout.CENTER,imageLabel);
        this.frame.getContentPane().add(BorderLayout.SOUTH, bottomBar);
        SwingUtilities.updateComponentTreeUI(this.frame);

        ActionListener trainListener = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (e.getSource() == trainBtn) {
                    train();
                }
            }
        };

        trainBtn.addActionListener(trainListener);
    }

    void train() {
        BufferedImage bImage;
        //all 60000 numbers are extracted from a .csv file
        //i omitted everything from this function because it's not important for this question
                ...

                //this line is run 60000 times. it displays the converted pixel data (very fast) the code definitely works, it simply won't update.
                this.trainImage.setImage(bImage);
                SwingUtilities.updateComponentTreeUI(frame);
            }
        }


    }

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

When I press the "train" button, the GUI freezes and is unresponsive until the train() method is done.当我按下“train”按钮时,GUI 冻结并且没有响应,直到 train() 方法完成。

Rather then updating trainImage , set a new ImageIcon to the JLabel与其更新trainImage ,不如为JLabel设置一个新的ImageIcon

imageLabel.setIcon(new ImageIcon(bImage));

You may also need to call revalidate and/or repaint on the label to trigger a new layout/paint pass.您可能还需要在 label 上调用revalidate和/或repaint来触发新的布局/绘制通道。

Personally, I would return the BufferedImage from the train method, as the train method really shouldn't be updating the UI, that's not it's job.就个人而言,我会从train方法返回BufferedImage ,因为train方法真的不应该更新 UI,这不是它的工作。

When I press the "train" button, the GUI freezes and is unresponsive until the train() method is done.当我按下“train”按钮时,GUI 冻结并且没有响应,直到 train() 方法完成。

Yep, that's because Swing, like most UI toolkits, is single threaded.是的,这是因为 Swing 与大多数 UI 工具包一样,是单线程的。 This means if you perform any blocking or long running operations within the context of the Event Dispatching Thread, it will prevent it from updating the UI or responding to any user input.这意味着如果您在事件调度线程的上下文中执行任何阻塞或长时间运行的操作,它将阻止它更新 UI 或响应任何用户输入。

See Concurrency in Swing for more details.有关详细信息,请参阅Swing中的并发。

"A" possible solution would be to use a SwingWorker . “一个”可能的解决方案是使用SwingWorker This allows you to execute the blocking/long running operation on a different thread, but provides a number of ways to sync the updates back to the UI safely (Swing is also not thread safe;))这允许您在不同的线程上执行阻塞/长时间运行的操作,但提供了多种方式将更新安全地同步回 UI(Swing 也不是线程安全的;))

See Worker Threads and SwingWorker for more details有关更多详细信息,请参阅工作线程和 SwingWorker

Your GUI is hanging because you are doing a lot of image manipulation on the Swing event thread.您的 GUI 挂起,因为您正在 Swing 事件线程上进行大量图像处理。 Is it possible for you to use something like a SwingWorker?您可以使用 SwingWorker 之类的东西吗? This way you can build the image on a separate thread and only update the GUI when necessary.这样,您可以在单独的线程上构建映像,并且仅在必要时更新 GUI。

private void train() {
    SwingWorker<BufferedImage, Object> worker = new SwingWorker<BufferedImage, Void>() {
        @Override
        protected BufferedImage doInBackground() throws Exception {
            // load the CSV file
            BufferedImage bImage = new BufferedImage();
            // ... fill up the bImage
            return bImage;
        }

        @Override
        protected void done() {
            try {
                BufferedImage img = get();
                // ... set the trainImage
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    };

    worker.execute();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM