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

时间:2015-06-12 07:43:50

标签: java multithreading swing shell

我在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的内容没有更新。 我这样做的例子如下:

enter image description here

当我按下jButton时,shell脚本以红色打印矩形中的输出,然后它会打开一个新的jFrame,它应该更新jLabel,但它不会更改。我哪里错了?感谢。

2 个答案:

答案 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 SwingWorker 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将更适合此)。