Java / Swing: проблема быстрого / медленного связывания пользовательского интерфейса - PullRequest
4 голосов
/ 30 марта 2010

Мне нужен способ привязки индикаторов пользовательского интерфейса к быстро меняющимся значениям.

У меня есть класс NumberCruncher, который выполняет тяжелую обработку в критическом потоке без пользовательского интерфейса, тысячи итераций цикла в секунду, и некоторые из них приводят к изменениям в наборе параметров, которые мне нужны , (думайте о них как о хранилище ключей)

Я хочу отображать их с меньшей скоростью в потоке пользовательского интерфейса; 10-20 Гц было бы хорошо. Как добавить уведомление в стиле MVC, чтобы моему NumberCruncher коду не нужно было знать о коде пользовательского интерфейса / привязке?

Ответы [ 4 ]

5 голосов
/ 31 марта 2010

Идиоматический способ сделать это состоит в том, чтобы использовать класс SwingWorker и использовать вызовы к publish(V...) для периодического уведомления потока диспетчеризации событий, в результате чего он обновляет интерфейс.

В приведенном ниже примере, взятом из Javadoc, сжатие чисел происходит в рабочем потоке в методе doInBackground (), который вызывает публикацию при каждой итерации. Этот вызов приводит к асинхронному вызову метода process (V ...) в потоке диспетчеризации событий , что позволяет ему обновлять пользовательский интерфейс. Обратите внимание, что это гарантирует, что пользовательский интерфейс всегда обновляется из потока диспетчера событий . Также обратите внимание, что вы можете выбрать публикацию каждые N итераций, чтобы уменьшить частоту обновления пользовательского интерфейса.

Пример из Javadoc

 class PrimeNumbersTask extends 
         SwingWorker<List<Integer>, Integer> {
     PrimeNumbersTask(JTextArea textArea, int numbersToFind) { 
         //initialize 
     }

     @Override
     public List<Integer> doInBackground() {
         while (! enough && ! isCancelled()) {
                 number = nextPrimeNumber();
                 publish(number);
                 setProgress(100 * numbers.size() / numbersToFind);
             }
         }
         return numbers;
     }

     @Override
     protected void process(List<Integer> chunks) {
         for (int number : chunks) {
             textArea.append(number + "\n");
         }
     }
 }
3 голосов
/ 31 марта 2010

SwingWorker, предложенный @Adamski, является предпочтительным; но экземпляр javax.swing.Timer является удобной альтернативой для этого, поскольку «обработчики событий действия для таймеров выполняют [в] потоке диспетчеризации событий».

2 голосов
/ 30 марта 2010

Похоже, вы, возможно, захотите использовать подход «слушателя». Разрешите вашему обработчику чисел регистрировать прослушиватели, а затем каждые 100-200 циклов (настраиваемых) (или при некоторых условиях изменения) уведомлять слушателей об обновлении, о котором они должны знать.

Слушатель может быть другим классом, в котором есть поток wait (), и когда он получает уведомление, он просто обновляет свою внутреннюю переменную, а затем уведомляет ожидающий поток. В этом случае у класса fast loop есть быстрый способ обновить внешнее значение и не беспокоиться о доступе к его быстро меняющемуся внутреннему состоянию.

Другой поток, ожидающий (), также может иметь функцию wait () в потоке таймера, для которого установлено значение 10-20 Гц (настраивается) для ожидания в таймере перед ожиданием () следующего обновления из вашего синхронизированного класса. .

2 голосов
/ 30 марта 2010

Имейте единственный объект, который ваш NumberCrucher изменяет / продолжает изменять в зависимости от многочисленных операций, которые вы делаете. Пусть это работает в отдельном потоке. Иметь пользовательский интерфейс, который использует тот же объект, который изменяет NumberCruncher. Этот поток будет считывать значения только в указанный период времени, поэтому он не должен быть проблемой взаимоблокировок потока.

NumberCruncher

public class NumberCruncher implements Runnable{
 CommonObject commonObj;
 public NumberCruncher(CommonObject commonObj){
  this.commonObj = commonObj;
 }
 public void run() {
  for(;;){
   commonObj.freqChangeVal = Math.random();
  }
 }
}

CommonObject:

public class CommonObject {
 public double freqChangeVal;
}

UI:

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class UI extends JFrame implements Runnable{

    private CommonObject commonObj = new CommonObject();

    JLabel label ;

    public static void main(String args[]){
        UI ui = new UI();
        ui.begin();
        Thread t2 = new Thread(ui);
        t2.start();
    }

    private void begin(){
        JPanel panel = new JPanel();
        label = new JLabel("Test");
        panel.add(label);

        Thread thread = new Thread(new NumberCruncher(commonObj));
        thread.start();

        this.add(panel);
        this.setSize(200,200);
        this.setVisible(true);
    }

    public void run() {
        for(;;){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            label.setText(commonObj.freqChangeVal+"");
            this.repaint();
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...