问题描述
我是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.
你应该看看了解更多详情
You should take a look at http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html for more details
这篇关于直到Runtime.getRuntime()。exec()完成执行后才会显示Swing消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!