【问题标题】:Scala swing panel disappears when trying to change contents (only when running a Thread)尝试更改内容时,Scala 摆动面板消失(仅在运行线程时)
【发布时间】:2015-05-05 07:13:41
【问题描述】:

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

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

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

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

以下是将 BoxPanel 添加到主 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 和框架的更好方法是什么?

【问题讨论】:

  • 嗨 Rusina,欢迎来到 Stack Overflow。上面的块文本很难阅读,如果您想要一个快速简洁的答案,请考虑总结您的问题:)
  • 稍微格式化并添加了一个 tl;dr.我希望这会更好。

标签: multithreading scala scala-swing boids


【解决方案1】:

Swing 不是线程安全的。

跟着我重复。

Swing 不是线程安全的。

听到合唱了吗? Swing is not thread safeofficial documentation

还有一个非常简单的解决方法。

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

在 Scala 中,这被支持为:

Swing.invokeLater(/* your stuff */)

【讨论】:

  • 感谢您的回答!但这似乎不起作用,我尝试将修改 GUI 的代码放在那里,但它仍然冻结。我开始认为这是我实现 TribeSettingFrame 和 TribeBoxPanel 的方式,这些只是具有返回定义的对象想要的 BoxPanel 或 Frame,是不是在线程中没有正确处理这些?我现在正在测试这个。
【解决方案2】:

首先,您应该让 UI 线程处理所有 UI 操作。 简单的方法应该遵循Scala-Code:

Swing.onEDT { GUI2D.addTribe(tribe) }

但正如您已经指出的,这不会解决您的问题。我有一个非常相似的问题,我只更改了Swing.Label 的文本内容,有时它就消失了。

事实证明,它只是在文本太长而无法在 Label 最初为自己保留的区域内显示时才消失。因此,解决您的问题的一种方法可能是在创建 optionPanel 时为其提供更大的初始大小。

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

我不太确定这是否必须在组件第一次绘制之前设置(在调用 Frame.open() 之前)。

【讨论】:

    猜你喜欢
    • 2011-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-17
    • 1970-01-01
    • 2015-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多