Качающаяся панель Scala исчезает при попытке изменить содержимое (только при запуске Thread)

Поэтому я пишу программу симуляции боидов как проект для школы. Моя программа поддерживает несколько разных групп этих boids, которые не объединяются с другими группами, все они имеют разные настройки, которые я делаю, добавляя BoxPanel в основной графический интерфейс программы, когда создается новое племя, и эти BoxPanels имеют настройки Кнопка, которая открывает новый кадр с настройками группы.

Это прекрасно работает, когда я запускаю программу и добавляю все предопределенные трибы, которые есть в коде. Теперь я создал новую часть графического интерфейса, которая позволяет вам создавать новые группы этих boids и добавлять их во время симуляции, и вот тут начинаются проблемы для меня.

По какой-то странной причине он добавляет группу просто отлично, с правильными настройками в симуляции, но не добавляет BoxPanels к основному графическому интерфейсу. Это приводит к тому, что вся панель настроек, которая находится у меня в стороне от симуляции, полностью исчезает. Я проверил это, и если я добавляю трибы в начале моей вычислительной цепочки, это делает то же самое, так что, похоже, это проблема с несколькими нитями и свингом. Есть идеи, что вызывает это или как это исправить? Я полностью озадачен этим.

tl; dr: приведенный ниже код для добавления племен работает нормально, когда я еще не запустил поток, но если я пытаюсь использовать его после запуска потока, опция OptionPanel выглядит пустой.

Вот код, который добавляет BoxPanels к основному графическому интерфейсу:

      def addTribe(tribe: Tribe) = {
        tribeFrames += new TribeSettingFrame(tribe)
        tribeBoxPanels += new TribeBoxPanel(tribe)
        this.refcontents
      }

      private def refcontents = {
        top.optionPanel.contents.clear()
        top.optionPanel.contents += new BoxPanel(Orientation.Vertical) {
          tribeBoxPanels.foreach(contents += _.tribeBoxPanel)
        }
        top.optionPanel.contents += new BoxPanel(Orientation.Horizontal) {
          contents += top.addTribeButton
        }
        top.optionPanel.contents += new BoxPanel(Orientation.Horizontal) {
          contents += top.vectorDebugButton
        }
      }


  new Thread(BoidSimulation).start()

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

РЕДАКТИРОВАТЬ: После некоторого копания это действительно похоже на обновление свинга из потока. Во многих местах предлагается использовать SwingWorker, но из информации, которую я собрал об этом, я не думаю, что она вписалась бы в мою программу, так как это непрерывная симуляция, и мне приходилось бы создавать новые SwingWorkers каждый кадр.

EDIT2: попытался вызвать метод из потока, как это:

SwingUtilities.invokeLater(new Runnable() {
  override def run() {
    GUI2D.addTribe(tribe)
  }
});

Не имеет никакого значения. Я начинаю думать, что это проблема с тем, как я использую TribeBoxPanel и TribeSettingFrame. Это объекты, которые содержат только один метод, который возвращает требуемый BoxPanel или Frame. Это плохая реализация? Если так, что является лучшим способом создания динамических BoxPanels и Frames?

2 ответа

Сначала вы должны позволить потоку пользовательского интерфейса обрабатывать все манипуляции с пользовательским интерфейсом. Простой способ должен быть следующим Scala-Code:

Swing.onEDT { GUI2D.addTribe(tribe) }

Но, как вы уже заметили, это не решит вашу проблему. У меня была очень похожая проблема, когда я менял только текстовое содержимое Swing.Label и это иногда просто исчезало.

Оказалось, что он исчез только тогда, когда текст был слишком длинным, чтобы отобразить его внутри области, которую метка изначально зарезервировала для себя. Таким образом, одним из способов решения вашей проблемы может быть увеличение параметра optionPanel при его создании.

Swing.onEDT { top.optionPanel.preferredSize = new Dimension(width,height) }

Я не совсем уверен, нужно ли это устанавливать до первого рисования компонента (до вызова Frame.open()).

Качели не потокобезопасны.

Повторяй за мной.

Качели не потокобезопасны.

Слышать хор? Качели не потокобезопасны. Есть официальная документация.

Существует также очень простой обходной путь.

SwingUtilities.invokeLater(new Runnable() {
    @Override public void run() {
        // your stuff
    }
});

В Scala это поддерживается как:

Swing.invokeLater(/* your stuff */)
Другие вопросы по тегам