Java 在频繁更新的真实模型中理解EDT
我正在用Java编写Sugarscape模拟,需要一个工作的GUI。Sugarscape是一个空间景观,由(糖)瓷砖和移动和消费糖的代理组成。简单来说,我只有一个代理,没有糖-我只想看到代理移动 在过去的两周里,我阅读了《swing中的并发》(concurrency in swing),阅读了肮脏的富客户端和无数的StackOverflow线程,但我必须在这里提出一个问题 我需要将我的模型与GUI分开。这是一个问题,因为99%的教程建议在其他方法中调用重新绘制。我的想法是运行模拟的一个“滴答声”:所有代理移动,然后发送一个事件(我的GUI类扩展了Observer),然后触发重新绘制();请求并更新GUI。然而,问题(误解)在于SwingUtilities.InvokeLater方法。我的代码是:Java 在频繁更新的真实模型中理解EDT,java,swing,concurrency,event-dispatch-thread,Java,Swing,Concurrency,Event Dispatch Thread,我正在用Java编写Sugarscape模拟,需要一个工作的GUI。Sugarscape是一个空间景观,由(糖)瓷砖和移动和消费糖的代理组成。简单来说,我只有一个代理,没有糖-我只想看到代理移动 在过去的两周里,我阅读了《swing中的并发》(concurrency in swing),阅读了肮脏的富客户端和无数的StackOverflow线程,但我必须在这里提出一个问题 我需要将我的模型与GUI分开。这是一个问题,因为99%的教程建议在其他方法中调用重新绘制。我的想法是运行模拟的一个“滴答声”
public void setupGUI()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
System.out.println("GUI is being setup, on EDT now? " + SwingUtilities.isEventDispatchThread());
SugarFrame frame = new SugarFrame(simulation.getWorld());
frame.setVisible(true);
}
});
}
为了了解正在发生的事情,我在每个地方都插入了println。事件的顺序让我困惑:
控制台输出: 1.已创建代理。起始位置:X=19 Y=46//这是在代理构造函数中 2.模拟启动。实验编号:0
现在,我已经读到,通过将主线程置于睡眠状态,您可以给EDT运行重新绘制的时间。然而,这种情况只发生一次。再也不会调用Repaint,我只看到模型的一次迭代 我只是不明白我在正确使用EDT时遗漏了哪些信息。Swingworker和Swingtimer经常被推荐,但对于每一个建议,都有一个概念,即像我这样的模型不需要它们。要么根本不调用paintComponent,要么排队直到结束(然后仍然没有重新绘制,即使我使用了thread.sleep) 我非常感谢你的帮助。为这篇冗长的文章道歉 //编辑:根据要求更多的代码。 整个主要方法是:
public class SimulationController {
static Simulation simulation;
public static final int NUM_EXPERIMENTS = 1;
public SimulationController()
{
Random prng = new Random();
SimulationController.simulation = new Simulation(prng);
}
public void run() {
setupGUI();
for(int i=0; i<NUM_EXPERIMENTS; i++) {
System.out.println("Simulation start. Experiment number: " + i);
simulation.getWorld().addObserver(simulation);
simulation.addObserver(simulation.getWorld());
simulation.run();
}
}
public void setupGUI()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
System.out.println("GUI is being setup, on EDT now? " + SwingUtilities.isEventDispatchThread());
SugarFrame frame = new SugarFrame(simulation.getWorld());
frame.setVisible(true);
}
});
}
public static void main(String[] args) {
SimulationController controller = new SimulationController();
controller.run();
}
这些句子
我需要将我的模型与GUI分开。这是一个问题,因为99%的教程建议在其他方法中调用重新绘制
及
现在,我已经读到,通过将主线程置于睡眠状态,您可以给EDT运行重新绘制的时间 我觉得这听起来不太对,所以我会把事情弄清楚一点,也许如果你重新评估你在这些陈述背后的基本想法,你会发现你遗漏的信息 首先,请始终记住我们所讨论的调度模型。你不能说“现在就帮我做这个!”。它总是“EDT这里还有一个任务你需要做,当你完成你正在做的事情时就去做”。因此,EDT有一个“任务”队列要做,并且一个接一个地执行 这些任务通常由事件创建:按下按钮给EDT一个任务,当GUI组件的状态发生变化时,一些监听器可能会收到通知,并将一些工作排入EDT队列。但是,您也可以直接说“EDT稍后执行这段代码”。这是您使用
invokeLater
所做的,您可以在EDT中安排一项工作,只要它是空闲的。即使您从EDT调用invokeLater
,任务也会被安排,而不是在此时执行
invokeAndWait
yes也会发生同样的情况,代码会按顺序执行,就好像它是在此时执行的一样,但它仍然是一个计划的工作。因此,repaint()
也不例外repaint()
不重新绘制GUI,而是计划重新绘制GUI
然而,repaint()
是一个例外,因为它可以从中调用!这并不奇怪,因为我们知道,唯一做的事情就是安排某项工作,它实际上不会干扰GUI,因此您可以在任何地方调用它
这意味着
SwingUtilities.invokeLater(processEventsRunnable);
其中processEventsRunnable
基本上执行repaint()
是没有意义的,整个勾号系统过于复杂且不必要。当您在GUI或GUI提供的数据上更改某些内容时,只需调用repaint(),这样更改就会反映在屏幕上
此外,如果您想做一些需要在EDT中执行的事情(比如用分数更改标签的文本),您可以将该代码放在主线程的invokeLater
块中。这将正确地排队和执行任务,您不需要执行自己的事件队列系统
将所有这些牢记在心以下是毫无意义的:
我已经读到,通过使主线程休眠,您可以给EDT运行重新绘制的时间
调用repaint()后不久,GUI将自行更新。main做了很多事情并调用了很多重绘,但这并不能阻止GUI的更新。然而,如果你想“睡眠”的主要,所以改变的速度是缓慢的,用户可以欣赏它在屏幕上,你应该使用定时器
因此,只要main不访问GUI值和方法,无论是否定期更改数据,都可以随时调用repaint
编辑:还有我
public void update(Observable o, Object arg)
{
if (arg instanceof TickEnd)
{
TickEvent tickEndevent = new TickEvent();
this.addTickEvent(tickEndevent);
}
}
}
private final BlockingQueue<TickEvent> TICK_EVENTS = new LinkedBlockingQueue<TickEvent>();
/**Runnable object that updates the GUI (I think)**/
private final Runnable processEventsRunnable = new Runnable()
{
public void run()
{
TickEvent event = new TickEvent();
while ((event = TICK_EVENTS.poll()) != null)
{
System.out.println("This is within processEventsRunnable, inside the While loop. Repaint is called now.");
repaint();
}
}
};
/**Add Event to the processing-Events-queue**/
public void addTickEvent(TickEvent event)
{
//System.out.println("This is in the Add TickEvent method, but before the adding. "+TICK_EVENTS.toString());
TICK_EVENTS.add(event);
System.out.println("TickEvent has been added! "+TICK_EVENTS.toString() + "On EDT?" + SwingUtilities.isEventDispatchThread());
if (TICK_EVENTS.size() >= 1)
{
SwingUtilities.invokeLater(processEventsRunnable);
}
}
/** Sugarframe Constructor**/
public SugarFrame(World world)
{
super("Sugarscape"); // creates frame, the constructor uses a string argument for the frame title
grid = new Grid(world); // variable is declared in the class
add(grid);
setDefaultCloseOperation(EXIT_ON_CLOSE); // specifies what happens when user closes the frame. exit_on_close means the program will stop
this.setContentPane(grid);
this.getContentPane().setPreferredSize(new Dimension(500, 500));
this.pack(); // resizes frame to its content sizes (rather than fixed height/width)
System.out.println("The Sugarframe has been created and Grid added. All on EDT? "+ SwingUtilities.isEventDispatchThread());
this.setVisible(true); // makes the Frame appear on screen
}
SwingUtilities.invokeLater(processEventsRunnable);
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingSquareTest
{
int x, y, size, step;
MyPanel panel;
Timer timer;
public static final void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
MovingSquareTest app = new MovingSquareTest();
app.createAndShowGUI();
app.timer.start();
}
});
}
public MovingSquareTest()
{
x = 0;
y = 150;
size = 50;
step = 50;
timer = new Timer(500, new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
x += step;
if (x < 0) x = 0;
if (x + size > panel.getWidth()) x = panel.getWidth() - size;
if (x == 0 || x + size == panel.getWidth()) step *= -1;
panel.repaint();
}
});
}
public void createAndShowGUI()
{
JFrame frame = new JFrame("Dance, my square!");
panel = new MyPanel();
frame.add(panel);
frame.setSize(600, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private class MyPanel extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawRect(x, y, size, size);
}
}
}