所以我正在编写一个boid模拟程序作为学校的项目。我的程序支持这些boid的多个不同组,它们不会聚集在其他组中,它们都有不同的设置,我通过在制作新部落时将BoxPanel添加到程序的主GUI中,并且那些BoxPanel具有设置按钮,使用组设置打开新框架。
当我启动程序并添加代码中的所有预定义部落时,这非常有效。现在我创建了GUI的一个新部分,让你创建这些boids的新组,并在模拟运行时添加它们,这就是我遇到问题的时候。
由于一些奇怪的原因,它添加组很好,正确设置到模拟但它不会将BoxPanel添加到主GUI。它使我在模拟一侧的整个设置栏完全消失。我测试了这个,如果我在计算线程的开头添加部落它会做同样的事情,所以这似乎是多线程和摆动的问题。是什么导致了这个或如何解决这个问题?我对这完全感到困惑。
tl; dr: 以下添加部落的代码在我没有启动线程时工作正常,但如果我尝试在之后使用它启动线程时,optionPanel显示为空。
这里是将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,但是从我收集到的信息中我不认为它适合我的程序,因为它是一个连续的模拟,而且我必须每帧都继续制作新的SwingWorkers。
EDIT2:尝试从这样的线程调用方法:
SwingUtilities.invokeLater(new Runnable() {
override def run() {
GUI2D.addTribe(tribe)
}
});
没有任何区别。 我开始认为这是我如何使用TribeBoxPanel和TribeSettingFrame的问题。这些对象都只包含一个返回所需BoxPanel或Frame的方法。这种实施不好吗?如果是这样,创建动态BoxPanel和框架的更好方法是什么?
答案 0 :(得分:0)
Swing不是线程安全的。
在我之后重复。
Swing不是线程安全的。
听到合唱? Swing is not thread safe有official documentation。
还有一个非常简单的解决方法。
SwingUtilities.invokeLater(new Runnable() {
@Override public void run() {
// your stuff
}
});
在Scala中,支持为:
Swing.invokeLater(/* your stuff */)
答案 1 :(得分:0)
首先,您应该让UI线程处理所有UI操作。 简单的方法应该遵循Scala-Code:
Swing.onEDT { GUI2D.addTribe(tribe) }
但正如您已经指出的那样,这不会解决您的问题。我有一个非常类似的问题,我只更改了Swing.Label
的文本内容,有时它只是消失了。
事实证明它只是消失了,当文本太长而无法在Label最初为自己保留的区域内显示它时。因此,解决问题的一种方法是在创建选项时使用更大的初始大小。
Swing.onEDT { top.optionPanel.preferredSize = new Dimension(width,height) }
我不确定在首次绘制组件之前是否必须设置它(在调用Frame.open()之前)。