gpt4 book ai didi

java - 了解频繁更新的真实模型中的 EDT

转载 作者:行者123 更新时间:2023-12-01 12:57:07 25 4
gpt4 key购买 nike

我正在用 Java 编写 Sugarscape 模拟,需要一个工作的 GUI。 Sugarscape 是一种空间景观,由(糖的)瓷砖和移动和消耗糖的代理组成。为简单起见,我只有一个代理,没有糖——我只想看到代理移动。

在过去的两周里,我阅读了 painting in java , concurrency in java , 并发,我读过肮脏的富客户端和无数的 StackOverflow 线程,但我必须在这里提出一个问题。

我需要将我的模型与 GUI 分开。这带来了一个问题,因为 99% 的教程建议在其他方法中调用重绘。我的想法是运行模拟的一个“滴答”:所有代理移动,然后发送一个事件(我的 GUI 类扩展了 Observer),然后触发一个 repaint();请求和更新 GUI。然而问题(误解)在于 SwingUtilities.InvokeLater 方法。我的代码是:

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
  • 正在设置 GUI,现在在 EDT 上? true//如您在上面看到的,这是在 SwingUtilities.InvokeLater 部分内。但随后 EDT 暂停,真实模型继续:
  • 记号 0
  • 调用代理操作,触发 TickStart 事件
  • TickStartEvent 已创建
  • 调用代理操作,for-loop 现在开始
  • 0号特工现在搬家:
  • 现在吃糖。
  • 现在搬家。
  • 正在 sleep 。
  • Sugarframe 已创建并添加了 Grid。全部在美国东部时间? true//然后它又回来了。绘制组件随后出现,代理可见的窗口出现。
  • 在 EDT 上调用paintComponent?真实


  • 现在,我已经读到,通过让主线程进入休眠状态,您可以给 EDT 时间来运行重绘。然而,这只会发生一次。 Repaint 再也不会被调用,而且我只看到过模型的一次迭代。

    我根本不明白我缺少哪些信息才能正确使用 EDT。 Swingworker 和 Swingtimer 经常被推荐,但对于每一个建议,都有一个概念,即它们对于像我这样的模型是不需要的。要么根本不调用paintComponent,要么排队到最后(即使我使用thread.sleep,仍然不会重新绘制)。

    我会很感激任何帮助。为长篇道歉。

    //编辑:根据请求更多代码。
    整个main方法:
    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();

    }

    }

    我的 JPanel 类中的油漆覆盖:
        @Override
    public void paintComponent(Graphics g) {
    System.out.println(">>>>>>>>paintComponent called, on EDT? " + SwingUtilities.isEventDispatchThread()+"<<<<<<<<<<");
    super.paintComponent(g);
    //g.clearRect(0, 0, getWidth(), getHeight());
    rectWidth = getWidth() / world.getSizeX();
    rectHeight = getHeight() / world.getSizeY();

    for (int i = 0; i < world.getSizeX(); i++)
    {
    for (int j = 0; j < world.getSizeY(); j++)
    {
    // Upper left corner of this terrain rect
    x = i * rectWidth;
    y = j * rectHeight;
    Tile tile = world.getTile(new Position(i, j));
    if (tile.hasAgent())
    {
    g.setColor(Color.red);
    } else
    {
    g.setColor(Color.black);
    }

    g.fillRect(x, y, rectWidth, rectHeight);
    }

    }
    }

    再次 JPanel 类,更新方法:
        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);
    }
    }

    最后但同样重要的是,JFrame 构造函数:
    /** 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
    }

    最佳答案

    句子,

    I need my model separate from the GUI. This presents a problem since 99% of tutorials suggest to call for repaint within other methods.





    Now, I have read that by putting the main thread to sleep, you give the EDT time to run the repaint.



    对我来说听起来不太正确,所以我会尝试澄清一下,也许如果你重新评估这些陈述背后的基本思想,你会发现你遗漏的信息。

    首先,请始终牢记我们正在讨论的这种调度模型。你不能说“EDT 现在为我做这件事!”。它始终是“EDT 这里是你需要做的另一项任务,当你完成你正在做的任何事情时就去做”。因此,EDT 有一个“任务”队列要做,并一个一个地处理它。

    这些任务通常由事件创建:按下按钮给 EDT 一个任务,当 GUI 组件的状态发生变化时,可能会通知一些监听器并将一些工作排​​入 EDT。但是,您也可以直接说“EDT 稍后执行这段代码”。这就是您对 invokeLater 所做的事情,您可以在 EDT 空闲时安排一项工作。即使您调用 invokeLater从 EDT 开始,任务被安排,目前不执行。
    invokeAndWait 也是如此。是的,代码是按顺序执行的,就好像它正在执行一样,但它仍然是一个预定的工作。所以 repaint()也不异常(exception)。 repaint()不重绘 GUI,而是安排重绘 GUI。

    然而 repaint()在可以从 outside the EDT 调用它的意义上是异常(exception)的。 !这并不奇怪,因为我们知道唯一要做的就是安排某项工作,它实际上并不会干扰 GUI,因此您可以在任何地方调用它。

    这意味着该行
    SwingUtilities.invokeLater(processEventsRunnable);

    在哪里 processEventsRunnable基本上执行 repaint()是没有意义的,整个刻度系统过于复杂和不必要。当您更改 GUI 上的某些内容或 GUI 提供的数据时,您只需调用 repaint(),以便将更改反射(reflect)在屏幕上。

    此外,如果您想做一些需要在 EDT 中执行的操作(例如更改带有分数的标签文本),您可以将该代码放在 invokeLater 中。阻塞在你的主线程中。这将正确地排队和执行任务,您不需要做自己的事件队列系统。

    记住所有这些是没有意义的:

    I have read that by putting the main thread to sleep, you give the EDT time to run the repaint



    在您调用 repaint() 后不久,GUI 将自行更新。 main 做了很多事情并调用了很多 repaints 并不能阻止 GUI 被更新。但是,如果您想“hibernate ”主程序以使更改的速度变慢以便用户可以在屏幕上欣赏它,则应使用计时器。

    因此,只要您的 main 不访问 GUI 值和方法,只要您定期或不定期更改数据,就可以随时调用 repaint。

    编辑:此外,您有一个主线程在做事,这听起来有点奇怪。正如您在并发章节中所读到的,通常您只需在 EDT 中创建 GUI,然后在按下按钮等时应用程序主要是事件驱动的。如果您需要定期进行更改,请使用计时器。您可以使用辅助线程来完成特定的非 GUI 相关的繁重工作,例如读取文件。但是您通常不会将辅助线程作为设计的一部分永久激活。

    下面是一个非常简单的程序,它周期性地移动一个正方形。我只是使用计时器来更改数据并调用 repaint()。请注意,我使用的是 SwingTimer(它在 EDT 中执行),因为我想检查面板宽度。否则我可以在任何线程中运行计时器的代码。

    在您的情况下,您的“ map ”可能独立于 GUI 存储,因此您只需要检查该数据以在需要时正确移动代理的坐标(在键盘按下时,定期...)。

    它看起来像这样:

    example

    完整代码:
    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);
    }
    }
    }

    关于java - 了解频繁更新的真实模型中的 EDT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23786574/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com