2

我构建了一个Swing应用程序,其中一部分功能应该是以可视方式和可听方式处理和输出一些文本(使用Mary TTS)。我需要一些关于GUI和文本处理类进行通信的最佳方式的建议。从可运行的更新GUI

GUI类是JPanel的一个子类。其中我有一个实现Runnable的类,名为LineProcesser,它准备将文本发送给音频播放器。我正在使用线程执行程序来保持美国东部时间(这可能不是最好的方式,但它似乎达到了我以后的结果)。

我的意图是让LineProcessor运行所有文本并更新每行结尾的JTextArea。此外,它需要暂停并等待用户在某些时候输入。用户输入完成后,GUI类应该告诉它恢复处理。

下面的代码说明了什么我目前有:

public class MyPanel extends JPanel { 
    ExecutorService lineExecutor = Executors.newSingleThreadExecutor(); 
    Runnable lineProcessor = new LineProcessor(); 

    public class LineProcessor implements Runnable { 

     private int currentLineNo = 0; 

      public LineProcessor() { 
      // ... 
      } 

      @Override 
      public void run() { 
       // call getText(); 
       // call playAudio(); 
       currentLineNo++; 
      } 
     } 
    } 

    private JButton statusLbl = new JLabel();  
    private JButton mainControlBtn = new JButton(); 

    private void mainControlBtnActionPerformed(ActionEvent evt) { 

     if (mainControlBtn.getText().equals("Start")) { 
          lineExecutor.submit(lineProcessor); 
          mainControlBtn.setText("Running"); 
     } 
    } 
} 

如何LineProcessor通知他们需要改变和它如何从GUI中暂停和重新GUI组件?我很困惑我是否需要一个Swing Worker,属性/事件监听器或其他东西?我读过的例子是有道理的,但我不明白我可以如何将它们应用于我在这里的代码。

回答

6

您需要做的只是将任何Swing调用包装在Runnable中,并通过SwingUtilities.invokeLater(myRunnable);将其排队在EDT上。而已。不需要SwingWorker。

例如,

public class LineProcessor implements Runnable { 
    private int currentLineNo = 0; 
    Runnable LineProcessor = new LineProcessor(); // won't this cause infinite recursion? 

    public LineProcessor() { 
    // ... 
    } 

    @Override 
    public void run() { 
    // call getText(); 
    // call playAudio(); 
    currentLineNo++; 

    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      // *** Swing code can go here *** 
     } 
    }); 
    } 
} 
+0

+1好的建议。 'invokeLater'对于进度报告来说是完美的,但是他需要'invokeAndWait'调用'暂停并等待用户输入'部分 – Robin 2012-04-28 16:42:06

+0

aaaaach谢谢你的回答,因为这里的所有答案(最近两个月)建议使用(我的观点)非常复杂的SwingWorker解决方法与简单的Runnable#线程相比,使用普通的invokeLater,+1 – mKorbel 2012-04-28 16:42:31

+0

我的答案不如robin或mprabhat,虽然它们都更加全面并且涉及OP的其他方面的问题。我想在几分钟内删除这个答案。 – 2012-04-28 16:45:08

3

你将不得不同时使用SwingWorker类和事件的方法。

  1. 将长时间运行的代码放在Swing Worker中。
  2. 创建新的属性更改事件,监听器,管理器
  3. 在您的SwingWorker中,发生更改事件时,调用PropertyChangeManager以通知所有liseners。
  4. 想要通过事件通知的所有GUI组件都应该使用PropertyChangeManager注册自己。
  5. 你PropertyChangeManager将调用的PropertyChangeListener的customProperyChange方法将通过properyChangeEvent
+0

比我的更清洁更多的面向对象方法。 1+ – 2012-04-28 16:42:00

+0

@HovercraftFullOfEels但可能会变得复杂...... UI组件可能不会在非EDT上被通知(或者至少不处理事件),而我可以假设其他方有兴趣在线程上使用这些事件他们是否真的发生过。我更喜欢你的这种情况下的简单方法 – Robin 2012-04-28 16:46:35

+0

PropertyChangeListener + SwingWorker +1 – mKorbel 2012-04-28 17:00:52

3

你所寻找的是一个SwingWorker。该类允许在工作线程上执行工作,在EDT上进行定期更新,最后更新EDT。

有几个例子可以在SO和Swing教程中找到。就在几个环节

报告进度可以用publish方法完成,这些结果将传递给process方法,您可以在其中更新UI。最后,调用done方法,允许您执行一些最终的UI更新。

对于暂停/重启功能...您可以在doInBackground方法中使用invokeAndWait方法,并使用阻塞方法调用(例如,显示JOptionPane要求用户输入)。但是,如果你开始在doInBackground中使用invokeAndWait,那么使用SwingWorker可能会矫枉过正,你可以简单地选择使用@Hovercraft Full Of Eels建议

+0

也更好。 1 + – 2012-04-28 16:45:20

+0

非常好的答案+1 – mKorbel 2012-04-28 17:01:11

+0

谢谢,我现在在我的代码中的其他地方使用SwingWorker。关于我的问题,我实际上使用了'CountDownLatch'而不是'invokeAndWait',因为线程正在向主GUI返回控制权,并且我不想在EDT上进行阻塞呼叫。 – sonicdeathmonkey 2012-05-04 09:48:25