从另一个方法中更新 JFrame



我有一个.csv文件,它是来自60000个手绘数字(手写数字的mnist数据集(的完整像素数据。按下按钮时会调用一个名为 train(( 的函数。在 train(( 函数中,我希望 60000 位数字中的每一个都显示在屏幕上。代码工作得很好,我只是无法从 train(( 方法中更新 JFrame。

我测试了代码,它 100% 按预期工作。我只是无法在 train(( 函数中更新 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();
}
}

当我按下"train"按钮时,GUI冻结并且无响应,直到train((方法完成。

而不是更新trainImage,为JLabel设置一个新的ImageIcon

imageLabel.setIcon(new ImageIcon(bImage));

您可能还需要调用标签上的revalidate和/或repaint以触发新的布局/绘画通道。

就个人而言,我会从train方法返回BufferedImage,因为train方法真的不应该更新 UI,这不是它的工作。

当我按下"train"按钮时,GUI冻结并且无响应,直到train((方法完成。

是的,这是因为 Swing 和大多数 UI 工具包一样,是单线程的。 这意味着,如果在事件调度线程的上下文中执行任何阻塞或长时间运行的操作,它将阻止它更新 UI 或响应任何用户输入。

有关更多详细信息,请参阅 Swing 中的并发。

"A"可能的解决方案是使用SwingWorker。 这允许您在不同的线程上执行阻塞/长时间运行操作,但提供了多种将更新安全地同步回 UI 的方法(Swing 也不是线程安全的;)(

有关更多详细信息,请参阅 Worker Threads 和 SwingWorker

您的 GUI 挂起是因为您在 Swing 事件线程上进行了大量图像处理。您是否可以使用像 Swingworker 这样的东西?这样,您可以在单独的线程上构建映像,并且仅在必要时更新 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();
}

最新更新