从shell脚本的输出更新jLabel内容

我在jFrame中有一个jLabel,它必须从shell脚本的输出更新其内容。 shell脚本的执行是从另一个jFrame中的线程完成的,我在其中将输出存储在StringBuilder(public和static)中:

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,但它不会改变。 我哪里错了? 谢谢。

因为你没有提供任何支持代码执行方式的证据,所以不可能100%,但我假设当你启动Thread ,你也打开了第二帧。

这可能是一个坏主意。 相反,您可以使用SwingWorker ,它允许您在后台执行脚本,释放UI线程。

SwingWorker完成时,您将创建并显示第二个窗口,将脚本的结果传递给它…

 public class ExecuteWorker extends SwingWorker { 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) { } } } 

有关更多详细信息,请查看Swing和工作线程中的并发 和SwingWorker 。 我还要看看多个JFrame的使用,好/坏做法? 并考虑使用CardLayout ,请参阅如何使用CardLayout获取更多详细信息

另一种方法可能是使用Producer-Consumer模式,其中SwingWorker是生产者,我们使用一个为消费者提供合同的interface 。 通过这种方式,您可以implement消费者合同,并在新内容可用时得到通知

 public interface Consumer { public void consume(String text); } public class ExecuteWorker extends SwingWorker { 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 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 / processfunction来同步UI线程的更新,从而可以安全地更新UI组件。

哦,除非你用HTML包装输出文本, JLabel可能不是你最好的选择,也许可以考虑使用JTextArea代替

看一下问题描述,我的猜测是你在获得结果之前尝试更新标签。

而不是更新组件显示的回调中的文本,尝试在后台处理完成后显式设置文本。

您的方法还有其他问题,例如不使用swing worker进行后台任务,尝试在JLabel中显示多行文本(JTextArea将更适合此)。