Перерисовка в многопоточной среде

я работаю над апплетом с десятью различными источниками данных (например, статистика/журнал ошибок/...). Каждый источник данных обновляется одним сетевым соединением и сообщает об обновлениях через механизм наблюдателя. Апплет имеет различные представления, которые отображают части данных. Каждое представление интересуется только некоторыми частями данных и регистрируется как Observer в необходимых Observables.

Представления (расширенные JPanels) в основном состоят из стандартных компонентов Swing (например, JLabels, JButton,...). Некоторые атрибуты компонентов в представлениях зависят от информации из базовой модели данных.

Пример:

StatisticPanel::paintComponent(Graphics g) {
  clearStatisticButton.setEnabled(stat.hasEntries());
  minValueLabel.setText(stat.getMinValue());
  super.paintComponent(g);
}

Эта логика реализована в методе paintComponent() панели StatisticPanel, а методы update() просто вызывают repaint(), потому что я не хотел манипулировать компонентами вне EDT.

Является ли это предполагаемым способом обновления компонентов Swing в многопоточной среде? Лучше ли использовать Runnable с SwingUtitlies.invokeLater()? Есть ли лучшие подходы к этой проблеме?


person tfk    schedule 24.05.2011    source источник


Ответы (2)


Я второй рекомендации camickr, но относительно этого фрагмента кода:

StatisticPanel::paintComponent(Graphics g) {
  clearStatisticButton.setEnabled(stat.hasEntries());
  minValueLabel.setText(stat.getMinValue());
  super.paintComponent(g);
}

У вас есть методы без рисования в вашем методе paintComponent (первые два метода), и это не должно быть так: 1) вы хотите, чтобы этот метод был как можно более простым и быстрым и, следовательно, имел только код, связанный с рисованием, и 2) у вас нет абсолютного контроля над тем, когда этот метод вызывается или даже если он вызывается, и поэтому код, не связанный с рисованием, и логика программы здесь неуместны. По этим причинам я настоятельно рекомендую вам убрать их оттуда, но вместо этого их следует вызывать отдельно от paintComponent, но, как и в большинстве кодов Swing, в EDT.

РЕДАКТИРОВАНИЕ 1
Я не профессионал, но как насчет того, чтобы вы дали своей StaticPanel метод, подобный этому:

   public void doMyUpdate() {
      if (SwingUtilities.isEventDispatchThread()) {
         clearStatisticButton.setEnabled(stat.hasEntries());
         minValueLabel.setText(stat.getMinValue());
      } else {
         SwingUtilities.invokeLater(new Runnable() {
            public void run() {
               clearStatisticButton.setEnabled(stat.hasEntries());
               minValueLabel.setText(stat.getMinValue());
            }
         });
      }
      repaint(); // if the paintComponent method has more than just a super call.
   }

EDIT 2
Кроме того, ознакомьтесь с этой темой: проверить, нужна ли нить-обновления

person Hovercraft Full Of Eels    schedule 24.05.2011
comment
+1 за более глубокое изучение кода. Изменение свойства компонента приведет к повторной перерисовке этого компонента. В некоторых случаях это может даже вызвать бесконечный цикл. - person camickr; 24.05.2011
comment
Существуют ли лучшие практики для решения такой проблемы? Потому что, если я вас правильно понимаю, подход Swingutilties.invokeLater лучше в том смысле, что он не выполняет логику программы в коде paintComponent(), но также может вызвать бесконечный цикл. - person tfk; 24.05.2011
comment
@tfk: см. РЕДАКТИРОВАТЬ 1 и РЕДАКТИРОВАТЬ 2 - person Hovercraft Full Of Eels; 24.05.2011

repaint() используется для вызова Swing RepaintManger, который, в свою очередь, будет планировать перерисовку компонента, поэтому да, можно просто вызвать перерисовку напрямую. RepaintManager позаботится о том, чтобы вся перерисовка выполнялась в EDT.

person camickr    schedule 24.05.2011