我在jFrame中有一个jLabel,它必须从shell脚本的输出更新其内容。 shell脚本的执行是从另一个jFrame中的线程完成的,我在其中将输出存储在StringBuilder中(公共和静态):
p = Runtime.getRuntime().exec("testpad -i -c"+can+" -n"+pad+" "+pathFile);
final InputStream inStream = p.getInputStream();
Thread uiThread = new Thread("UIHandler") {
@Override
public void run() {
InputStreamReader reader = new InputStreamReader(inStream);
Scanner scan = new Scanner(reader);
prec=null;
while (scan.hasNextLine()) {
prec=scan.nextLine();
System.out.println(prec);
sbuff.append(prec);
sbuff.append('\n');
}
}
};
uiThread.start();
当显示jPannel时,我更新了jLabel(jPannel中的内容):
private void jPanel1ComponentShown(java.awt.event.ComponentEvent evt) {
// TODO add your handling code here:
jLabel2.setText(inizio.sbuff.toString());
}
我认为这是一个竞争条件的麻烦,但是我把一个Thread.sleep()放了很多秒,但是jLabel的内容没有更新。 我这样做的例子如下:
当我按下jButton时,shell脚本以红色打印矩形中的输出,然后它会打开一个新的jFrame,它应该更新jLabel,但它不会更改。我哪里错了?感谢。
答案 0 :(得分:1)
由于你没有提供任何支持代码执行方式的证据,因此不可能100%,但我假设当你启动Thread
时,你也会打开第二帧。
这可能是一个坏主意。相反,您可以使用SwingWorker
来允许您在后台执行脚本,从而释放UI线程。
当SwingWorker
完成后,您将创建并显示第二个窗口,将脚本的结果传递给它......
public class ExecuteWorker extends SwingWorker<String, String> {
private String can, pad, pathfile;
public ExecuteWorker(String can, String pad, String pathfile) {
this.can = can;
this.pad = pad;
this.pathfile = pathfile;
}
@Override
protected String doInBackground() throws Exception {
ProcessBuilder pb = new ProcessBuilder("testpad", "-i", "-c" + can, "-n" + pad, pathfile);
pb.redirectErrorStream(true);
StringJoiner sj = new StringJoiner("\n");
Process p = pb.start();
try (InputStreamReader reader = new InputStreamReader(p.getInputStream());
Scanner scan = new Scanner(reader)) {
while (scan.hasNextLine()) {
String text = scan.nextLine();
System.out.println(text);
sj.add(text);
}
}
return sj.toString();
}
@Override
protected void done() {
try {
String text = get();
// Create and show second UI here...
} catch (Exception e) {
}
}
}
有关详细信息,请查看Concurrency in Swing和Worker Threads and SwingWorker。我还要查看The Use of Multiple JFrames, Good/Bad Practice?并考虑使用CardLayout
,有关详细信息,请参阅How to Use CardLayout
另一种方法可能是使用Producer-Consumer模式,其中SwingWorker
是生产者,我们使用interface
作为消费者的合同。通过这种方式,UI可以implement
消费者合同,并在新内容可用时得到通知
public interface Consumer {
public void consume(String text);
}
public class ExecuteWorker extends SwingWorker<String, String> {
private String can, pad, pathfile;
private Consumer consumer;
public ExecuteWorker(Consumer consumer, String can, String pad, String pathfile) {
this.can = can;
this.pad = pad;
this.pathfile = pathfile;
}
@Override
protected void process(List<String> chunks) {
for (String text : chunks) {
consumer.consume(text);
}
}
@Override
protected String doInBackground() throws Exception {
ProcessBuilder pb = new ProcessBuilder("testpad", "-i", "-c" + can, "-n" + pad, pathfile);
pb.redirectErrorStream(true);
StringJoiner sj = new StringJoiner("\n");
Process p = pb.start();
try (InputStreamReader reader = new InputStreamReader(p.getInputStream());
Scanner scan = new Scanner(reader)) {
while (scan.hasNextLine()) {
String text = scan.nextLine();
System.out.println(text);
publish(text);
sj.add(text);
}
}
return sj.toString();
}
}
这意味着您可以启动SwingWorker
并同时设置新的UI,这也意味着您无需等待进程退出,然后再从中获取输出。
在上面的示例中,我们使用worker的publish
/ process
功能来同步UI线程的更新,从而可以安全地更新UI组件。
哦,除非你用HTML包装输出文本,JLabel
可能不是你最好的选择,也许可以考虑使用JTextArea
而不是
答案 1 :(得分:1)
查看问题描述,我的猜测是您在获取结果之前尝试更新标签。
尝试在后台处理完成后显式设置文本,而不是更新组件中显示的回调文本。
您的方法存在其他问题,例如不使用swing worker进行后台任务,尝试在JLabel中显示多行文本(JTextArea将更适合此)。