【问题标题】:Updating java GUI as internal state of model changes更新 java GUI 作为模型更改的内部状态
【发布时间】:2012-03-15 05:52:52
【问题描述】:

随着模拟模型的内部状态发生变化,我在更新 gui 时遇到问题。类模拟器运行一定数量的步骤。每一步,计算机的内部状态都会发生变化。 Gui 然后收到通知,并应重新绘制其计算机的图形表示,包括当前状态的文本。不幸的是,对于下面详述的类,gui 仅在模拟运行的最后一步之后更新更改。我正在使用观察者(计算机)和可观察(GUICanvas)模式。我做错了什么,GUI 没有在模拟的中间步骤上重新绘制?

public class NwSim {


   public NwSim() {
   }

   public static void main(String[] args) {
       JFrame frame;
       Simulator simulator = new Simulator();    
       canvas = new GUICanvas();         //create gui  canvas, which paints guy computers
       canvas.setBackground(Color.white);
       contentPane.add(canvas, BorderLayout.CENTER);
       frame.pack();
       frame.setVisible(true);
   simulator.simulate();
   }
}

//represents the controller in the simulation
public class Simulator {
List<Computer> computers;
private int simulationSteps;

public Simulator()
    simulationSteps = 200;
              computers = new ArrayList<Computer>();


    public void simulate() {
        for(int step = 0; step < simulationSteps; step++) {
        for(Computer computer : computers) {
        computer.tick()
    }
    }
    }

    public Computer createComputer() {
    Computer computer = new Computer();
    computers.add(computer)
    }
}



public class Computer extends Observable {

    public void tick {
    ….. // update field state of the computer
        if (state.stateChanged()) {
            setChanged();
            notifyObservers();    //notify observer- gui canvas that the state of computer has changed and it is time to repaint guiComputers 

        }
 }

 public string getState() {
return state;
 }
}

public class GUIComputer  {

 private static final long serialVersionUID = 1L;
 private int width;
 private int height;   
 private Image image;
 private Computer computer; 


 public GUIComputer(int x, int y, Computer computer component, Image image) {
     this.computer = computer;
     setX(x);
     setY(y);
     this.image = image;
     width = image.getWidth(null);
     height = image.getHeight(null);
 }


 @Override
 public void drawGuiComputer(Graphics g){
        g.drawImage(image, getX(), getY(), null);
        Graphics2D g2 = (Graphics2D)g;
        g2.drawString(computer.getState().toString(), getX() + 20, getY()    // repaint the state for each guiComputer taken from Computer 
                + height + 10);
}
}

public class GUICanvas extends JPanel implements  Observer { 

//
private List<GUIComputer> guiComputers;

public GUICanvas(Simulator simulator)  {
     this.guiComputers = new ArrayList<GUIComputer>();
    // create guy computers using method createGuiComputer below , code omitted
}

public createGuiComputer(Transferable transferable, Point dropPoint, Computer computer)  {
 Image image = Toolkit.getDefaultToolkit().getImage("images/" + imageName);
        Computer computer = simulator.createComputer();
                        GUIComputer guiComputer = new   GUIComputer(dropPoint.x, dropPoint.y, computer, image);
                         guiComputers.add(guiComputer);  
                         guiComputer.addObserver(this);

}

    @Override
     public void paintComponent(Graphics g) {

        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;      
            if (!GuiComputers.isEmpty()) {
                    for(GUIComputer guiComputer : guiComputers) {
                // redraw guiComputer
                            guiComputer.drawGuiComputer(g);
                        }
            }
}

    @Override
    public void update(Observable o, Object o1) {
    for(final GUIComputer guiComputer : guiComputers) {
                 if(guiComputer.getComputer().equals(o)) {  
                  //if update requested by Computer object then update gui, redrawing all guiComputers
                  revalidate();
                  repaint();
              }
    }
}
}

【问题讨论】:

  • 您的代码缩进很乱,您的代码示例无法编译。这是第一个问题。

标签: java model-view-controller swing observer-pattern observable


【解决方案1】:

除非您在 tick()simulate() 中的某处有一个您没有显示的 Thread.sleep(),否则模拟应该几乎立即运行。然后,所有对 repaint() 的调用都将合并为一个 repaint。

编辑:

下面是一个简单的例子,其中Observables 在main() 线程上的零星更新显示在观察它们的 GUI 中:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Animation extends JPanel implements Observer {
    Simulation simulation;

    Animation(Simulation simulation) {
        this.simulation = simulation;
        setPreferredSize(new Dimension(200, 200));

        for(Blob blob : simulation.blobs) {
            blob.addObserver(this);
        }
    }

    @Override
    public void update(Observable o, Object arg) {
        Blob blob = (Blob)o;
        repaint(blob.x - 12, blob.y - 12, 24, 24);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        for(Blob blob : simulation.blobs) {
            g.setColor(blob.color);
            g.fillOval(blob.x - 10, blob.y - 10, 20, 20);
        }
    }

    public static void main(String[] args) {
        Simulation simulation = new Simulation();

        JFrame frame = new JFrame();
        frame.getContentPane().add(new Animation(simulation));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);

        simulation.simulate();
    }
}

class Simulation {
    List<Blob> blobs = new ArrayList();

    Simulation() {
        for(int i = 0; i < 20; ++i) {
            blobs.add(new Blob());
        }
    }

    void simulate() {
        while(true) {
            try {
                Thread.sleep(50);
            } catch(InterruptedException e) {
                return;
            }
            for(Blob blob : blobs) {
                blob.tick();
            }
        }
    }
}

class Blob extends Observable {
    int x = (int)(Math.random() * 180 + 10);
    int y = (int)(Math.random() * 180 + 10);
    float hue = (float)Math.random();
    Color color = Color.getHSBColor(hue, 1, 1);

    void tick() {
        if(Math.random() < 0.05) {
            x += 4 * Math.random() - 2 + .5;
            y += 4 * Math.random() - 2 + .5;
            hue += Math.random() * .1 - .05;
            hue -= Math.floor(hue);

            color = Color.getHSBColor(hue, 1, 1);
            setChanged();
            notifyObservers();
        }
    }
}

【讨论】:

  • 谢谢拉塞尔和满载鳗鱼的气垫船。确实问题是由线程引起的
  • 我之前的代码中实际上有 Thread.sleep ......但我的模拟方法在按钮的动作事件中......因此模拟()在 EDT 内部运行,而不是主线程,这解释了为什么 GUI 没有刷新......添加 1 个额外的线程来运行模拟()解决了这个问题,谢谢大家。
【解决方案2】:

您可能会将事件调度线程或 EDT 与在事件线程上运行的长时间运行耗时的代码捆绑在一起:

public void simulate() {
    for(int step = 0; step < simulationSteps; step++) {
      for(Computer computer : computers) {
        computer.tick()
      }
    }
}

尝试使用SwingWorker 或其他后台线程来解决此问题。

【讨论】:

  • 这也是我的第一个想法,但对simulate() 的调用是在main() 中。
  • @RussellZahniser:是的,你是对的。尽管省略了很多代码,但我仍然怀疑 Swing 中存在并发问题。
猜你喜欢
  • 1970-01-01
  • 2019-03-18
  • 2020-07-08
  • 2013-08-01
  • 2023-03-30
  • 2020-07-25
  • 1970-01-01
  • 2011-06-18
  • 1970-01-01
相关资源
最近更新 更多