Как синхронизировать модель Swing с быстро меняющейся «реальной» моделью? - PullRequest
16 голосов
/ 16 ноября 2009

Как известно, все, что связано с компонентами Swing, должно выполняться в потоке диспетчеризации событий . Это также относится к моделям позади компонентов, таким как TableModel . Это достаточно просто в элементарных случаях, но все становится довольно сложно, если модель представляет собой «живое представление» чего-то, что должно выполняться в отдельном потоке, потому что оно быстро меняется. Например, живое представление фондового рынка на JTable. Фондовые рынки обычно не происходят в EDT.

Итак, каков предпочтительный шаблон для (де) соединения модели Swing, которая должна быть в EDT, и "реальной", поточно-ориентированной модели, которая должна обновляться из любого места в любое время? Одним из возможных решений было бы на самом деле разделить модель на две отдельные копии: «реальная» модель плюс ее Swing-аналог, который является снимком «реальной» модели. Затем они (двунаправленно) синхронизируются на EDT время от времени. Но это похоже на раздувание. Это действительно единственный жизнеспособный подход, или есть какие-то другие или более стандартные способы? Полезные библиотеки? Что-нибудь?

Ответы [ 3 ]

11 голосов
/ 16 ноября 2009

Я могу рекомендовать следующий подход:

  • Помещать события, которые должны изменить таблицу, в очередь «ожидающих событий», а когда событие помещается в очередь , а очередь пуста , затем вызывать поток диспетчера событий, чтобы опустошить очередь всех события и обновить модель таблицы. Эта оптимизация означает , что вы больше не вызываете поток диспетчеризации событий для каждого полученного события , что решает проблему несоответствия потока диспетчеризации событий с основным потоком событий.
  • Избегайте создания нового Runnable при вызове потока диспетчеризации событий с помощью внутреннего класса без состояния для опустошения очереди ожидающих событий в вашей реализации панели таблицы.
  • Необязательная дополнительная оптимизация: при опустошении очереди ожидающих событий минимизируйте количество событий обновления таблицы, запомнив, запомнив, какие строки таблицы необходимо перекрасить, а затем запустив одно событие (или одно событие в строке) после обработки всех событий. *

Пример кода

public class MyStockPanel extends JPanel {
  private final BlockingQueue<StockEvent> stockEvents;

  // Runnable invoked on event dispatch thread and responsible for applying any
  // pending events to the table model.
  private final Runnable processEventsRunnable = new Runnable() {
    public void run() {
      StockEvent evt;

      while ((evt = stockEvents.poll() != null) {
        // Update table model and fire table event.
        // Could optimise here by firing a single table changed event
        // when the queue is empty if processing a large #events.
      }
    }
  }

  // Called by thread other than event dispatch thread.  Adds event to
  // "pending" queue ready to be processed.
  public void addStockEvent(StockEvent evt) {
    stockEvents.add(evt);

    // Optimisation 1: Only invoke EDT if the queue was previously empty before
    // adding this event.  If the size is 0 at this point then the EDT must have
    // already been active and removed the event from the queue, and if the size
    // is > 0 we know that the EDT must have already been invoked in a previous
    // method call but not yet drained the queue (i.e. so no need to invoke it
    // again).
    if (stockEvents.size() == 1) {
      // Optimisation 2: Do not create a new Runnable each time but use a stateless
      // inner class to drain the queue and update the table model.
      SwingUtilities.invokeLater(processEventsRunnable);
    }
  }
}
2 голосов
/ 16 ноября 2009

Обычный подход заключается в отправке «сигналов» некоторого вида, которые слушает пользовательский интерфейс. В моем коде я часто использую центральный диспетчер, который отправляет сигналы, которые содержат измененный объект, имя поля / свойства плюс старое и новое значение. Для случая oldValue.equals(newValue) или oldValue.compareTo(newValue) == 0 (последний для дат и BigDecimal) сигнал не отправляется.

Затем поток пользовательского интерфейса регистрирует прослушиватель для всех сигналов. Затем он проверяет объект и имя, а затем переводит его в изменение пользовательского интерфейса, которое выполняется с помощью asyncExec().

Вы можете превратить это в слушателя для каждого объекта и сделать так, чтобы каждый элемент интерфейса регистрировался в модели. Но я обнаружил, что это просто распространяет код повсюду. Когда у меня есть огромный набор объектов с обеих сторон, я иногда просто использую несколько сигналов (или событий), чтобы сделать вещи более управляемыми.

2 голосов
/ 16 ноября 2009

Насколько я понимаю, вы не хотите реализовывать интерфейсы модели Swing в своей реальной модели, не так ли? Можете ли вы реализовать модель Swing как «представление» над частью реальной модели? Он преобразует свой доступ для чтения getValueAt () к вызовам реальной модели, а реальная модель будет уведомлять модель Swing об изменениях, предоставляя список изменений или предполагая, что модель Swing позаботится о запросе новых значений все, что он показывает в данный момент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...