问题描述
我是 Swing 的新手.我正在尝试创建一个 Swing 包装器以允许用户浏览和选择一个文件夹,并且该文件夹路径用作控制台 .exe 程序的命令行参数.在他们选择文件夹并单击启动程序"按钮后,我希望摆动窗口显示一条消息,告诉他们程序正在处理(并显示时钟的动画 gif),运行外部程序,然后显示另一条消息当该程序完成执行时.我遇到的问题是,直到外部程序完成执行后,才会显示正在处理"消息.在下面的代码中,当单击启动程序"按钮时,将执行 onLaunchProgram 方法.我试过 revalidate() 和 repaint(),但没有任何变化.我有一个用于已完成"消息的 waitFor(),但即使我将其取出,正在处理"消息和 gif 也不会在外部程序完成执行后显示.
I am new to Swing. I am trying to create a swing wrapper to allow the user to browse and select a folder, and that folder path is used as a command line parameter to a console .exe program. After they select the folder and click a "Launch Program" button, I want the swing window to display a message telling them that the program is processing (and display an animated gif of a clock), run the external program, then display another message when that program has finished execution. The problem I'm having is that the "Processing" message doesn't get displayed until after the external program finishes execution. In the code below, the onLaunchProgram method gets executed when the "Launch Program" button is clicked. I've tried revalidate() and repaint(), but there was no change. I have a waitFor() for the "Finished" message, but even when I take that out, the "Processing" message and gif don't get displayed until after the external program finishes execution.
...
JTextField txtFolder = new JTextField();
JLabel lblMessage = new JLabel();
JLabel lblPic = new JLabel();
JButton btnLaunchApplication = new JButton("Launch Program");
...
btnLaunchApplication.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
onLaunchProgram(evt);
}
});
...
if (returnVal == JFileChooser.APPROVE_OPTION){
file = fc.getSelectedFile();
txtFolder.setText(file.getAbsolutePath());
}
...
private void onLaunchProgram(ActionEvent evt) {
String strExecutableFilename = "MyExecutableProgam";
String strSourceFolder = txtFolder.getText();
String strCommand = strExecutableFilename + " " + strSourceFolder;
lblMessage.setText("Processing");
ImageIcon icon = new ImageIcon("clock.gif");
lblPic.setIcon(icon);
try {
Process procCommand = Runtime.getRuntime().exec(strCommand);
try {
procCommand.waitFor();
} catch (InterruptedException exception) {
exception.printStackTrace();
} finally {
}
lblMessage.setText("Finished");
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
推荐答案
从您的示例代码中很难确定您是如何执行 onLaunchProgram
方法的,但从您的描述来看,这将是一个安全的最好假设您是在事件调度线程的上下文中执行它.
It's difficult from your sample code to determine how you are executing the onLaunchProgram
method, but from your description, it would be a safe beat to assume you are executing it within the context of the Event Dispatching Thread.
事件调度线程负责(除其他外)调度重绘请求.任何阻止该线程的事情都会阻止它更新 UI.
The Event Dispatching Thread is responsible for (amongst other things) dispatching repaint requests. Any thing that blocks this thread will prevent it from updating the UI.
因为 procCommand.waitFor()
是一个阻塞操作,这将阻止任何重绘请求(或与此相关的任何事件)在它返回之前被处理.
Because procCommand.waitFor()
is a blocking action, this will prevent any repaint request (or any events for that matter) from been processed until it returns.
您应该在单独的线程中执行所有耗时或阻塞的进程.但是,您遇到的问题是,对 UI 的所有更新都必须在 EDT 的上下文中执行(也就是说,您永远不应从 EDT 以外的任何线程更改/更新/修改/创建任何 UI 组件)
You should execute all time consuming or blocking processes in a separate thread. The problem you have though, is all updates to the UI mast be executed within the context of the EDT (that is, you should never change/update/modify/create any UI component from any thread other then the EDT)
在 Swing 中,您有多种选择,就您而言,我建议使用 SwingWorker
.它将允许您在后台线程中执行进程,但有一些易于使用的方法来重新同步 UI 的更新.
In Swing you have a number of options, in your case, I would suggest using a SwingWorker
. It will allow you to execute the process in a background thread, but has some easy to use methods for resyncing updates to the UI.
public class ProcessWorker extends SwingWorker<Integer, String> {
private String program;
private String sourceFolder;
public ProcessWorker(String program, String sourceFolder) {
this.program = program;
this.sourceFolder = sourceFolder;
}
@Override
protected void process(List<String> chunks) {
// Back on the EDT
for (String value : chunks) {
if (value.equalsIgnoreCase("PROCESSING")) {
lblMessage.setText("Processing");
ImageIcon icon = new ImageIcon("clock.gif");
lblPic.setIcon(icon);
} else if (value.equalsIgnoreCase("FINISHED")) {
lblMessage.setText("Finished");
} else {
// Possible some other message...
}
}
}
@Override
protected Integer doInBackground() throws Exception {
int result = -1;
String strExecutableFilename = program;
String strSourceFolder = sourceFolder;
String strCommand = strExecutableFilename + " " + strSourceFolder;
publish("PROCESSING");
// lblMessage.setText("Processing");
// ImageIcon icon = new ImageIcon("clock.gif");
// lblPic.setIcon(icon);
try {
ProcessBuilder pb = new ProcessBuilder(program);
pb.redirectError();
pb.directory(new File(strSourceFolder));
Process procCommand = pb.start();
// Process procCommand = Runtime.getRuntime().exec(strCommand);
try {
result = procCommand.waitFor();
} catch (InterruptedException exception) {
exception.printStackTrace();
} finally {
}
// lblMessage.setText("Finished");
publish("FINISHED");
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
您还应该熟悉 ProcessBuilder
.它有许多用于构建流程的有用方法,并克服了人们在尝试让 Runtime.getRuntime().exec
工作时遇到的一些困难.
You should also familiarise yourself with ProcessBuilder
. It has a number of useful methods for building process and overcomes some of the difficulties people have when trying to get Runtime.getRuntime().exec
to work.
您应该查看 http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html 了解更多详情
You should take a look at http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html for more details
这篇关于直到 Runtime.getRuntime().exec() 完成执行后才会显示 Swing 消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!