4

因此,我正在编写一个 boid 模拟程序作为学校项目。我的程序支持这些不与其他组一起聚集的多个不同组,它们都有不同的设置,我通过在创建新部落时向程序的主 GUI 添加一个 BoxPanel 来完成,并且这些 BoxPanel 有一个设置按钮打开一个带有组设置的新框架。

当我启动程序并添加代码中所有预定义的部落时,这非常有效。现在我制作了 GUI 的一个新部分,让您可以创建这些 boid 的新组并在模拟运行时添加它们,这就是我遇到问题的时候。

由于某些奇怪的原因,它添加了组就好了,在模拟中具有正确的设置,但它不会将 BoxPanels 添加到主 GUI。它使我在模拟一侧的整个设置栏完全消失。我对此进行了测试,如果我在计算线程的开头添加部落,它会做同样的事情,所以这似乎是多线程和摆动的问题。任何想法是什么导致了这个或如何解决这个问题?我对此完全感到困惑。

tl; dr: 当我没有启动线程时,下面用于添加部落的代码可以正常工作,但是如果我在启动线程后尝试使用它,则 optionPanel 显示为空。

这是将 BoxPanels 添加到主 gui 的代码:

      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,但从我收集的有关它的信息来看,我认为它不适合我的程序,因为它是一个连续的模拟,而且我必须每帧都继续制作新的 SwingWorker。

EDIT2:尝试从线程中调用该方法,如下所示:

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

没有任何区别。我开始认为这是我如何使用 TribeBoxPanel 和 TribeSettingFrame 的问题。这些对象都只包含一个返回所需 BoxPanel 或 Frame 的方法。这个实现不好吗?如果是这样,创建动态 BoxPanel 和框架的更好方法是什么?

4

2 回答 2

0

First you should let the UI thread handle all UI manipulation. The simple way should be following Scala-Code:

Swing.onEDT { GUI2D.addTribe(tribe) }

But as you already noted, this won't solve your problem. I had a very similar problem where I only changed the text content of a Swing.Label and it sometimes simply disappeared.

It turned out that it only disappeared, when the text was too long to display it inside the Area which the Label initially reserved for itself. So one way around your Problem could be to give the optionPanel a bigger initial size when you create it.

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

I'm not quite sure whether this has to be set before the component is first drawn (before Frame.open() is called).

于 2015-04-03T18:05:19.763 回答
0

Swing 不是线程安全的。

跟着我重复。

Swing 不是线程安全的。

听到合唱吗?Swing 不是线程安全的 有官方文档

也给出了一个非常简单的解决方法。

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

在 Scala 中,这被支持为:

Swing.invokeLater(/* your stuff */)
于 2015-03-06T12:32:29.023 回答