/ / Update Panel von Controller-Methode - Java, Swing, Jpanel

Update Panel von Controller-Methode - Java, Swing, Jpanel

Ich möchte ein Panel auf meinem Hauptrahmen (in der Klassen-GUI) von meinem Controller aus aktualisieren. Grundsätzlich sollte das Panel bei jedem Simulationsschritt aktualisiert werden.

    for (int step = 0; step < simSteps; step++){
this.gui.updateGUI(this.stats, step);
}

In meiner GUI-Klasse sieht die Methode updateGUI (...) folgendermaßen aus:

 public void updateGUI(Stats stats, int step){
this.stats = stats;
getContentPane().remove(pollutionPanel);
pollutionPanel = new BarPanel(settings, stats, possibleColors);
getContentPane().add(pollutionPanel);
validate();
repaint();
}

und meine BarPanel-Klasse überschreibt diepaintComponent-Methode, bei der ich einfach ein grafisches Objekt verwende, um ein paar Dinge zu malen. Beim Starten der Simulation wird das Panel erst nach dem letzten Simulationsschritt aktualisiert, obwohl ich bereits weiß, dass die updateGUI () -Methode in jedem Simulationsschritt aufgerufen wird (ich habe einige Debugging-Ausgaben gemacht).

Hat jemand eine Idee, warum das passiert?

Antworten:

2 für die Antwort № 1

Als Grundkonzept möchten Sie also eine Animation generieren, die über die Benutzeroberfläche über einen bestimmten Zeitraum aktualisiert wird. Keine ungewöhnliche Anfrage mit vielen Beispielen.

Dinge, die zu beachten sind:

  • Swing ist Single Threaded
  • Tun Sie nichts, was den Event Dispatching Thread blockieren könnte. Dadurch wird verhindert, dass die Benutzeroberfläche aktualisiert wird, bis die Steuerung an sie zurückgegeben wird
  • Aktualisieren Sie die Benutzeroberfläche nicht von außerhalb des EDT-Kontexts

Okay, das klingt ein wenig widersprüchlich, aber Swing bietet ein paar Werkzeuge, um Ihnen das Leben zu erleichtern

Du könntest...

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

public static void main(String[] args) {
new Test();
}

public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public interface StepContainer {

public void addStep(int index);
}

public class TestPane extends JPanel implements StepContainer {

private int step = 0;

public TestPane() {
setLayout(new GridBagLayout());
PopulateWorker worker = new PopulateWorker(10, this);
worker.execute();
}

@Override
public void addStep(int step) {
JButton btn = new JButton(Integer.toString(step));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;

add(btn, gbc);
revalidate();
repaint();
}

}

public class PopulateWorker extends SwingWorker<Void, Integer> {

private int steps;
private StepContainer parent;

public PopulateWorker(int steps, StepContainer parent) {
this.steps = steps;
this.parent = parent;
}

@Override
protected Void doInBackground() throws Exception {
Thread.sleep(1000);
for (int index = 0; index < steps; index++) {
publish(index);
Thread.sleep(1000);
}
return null;
}

@Override
protected void process(List<Integer> chunks) {
for (int step : chunks) {
parent.addStep(step);
}
}

}

}

Verwenden Sie eine Schaukel Timer

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

public static void main(String[] args) {
new Test();
}

public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public interface StepContainer {

public void addStep(int index);
}

public class TestPane extends JPanel implements StepContainer {

private int step = 0;

public TestPane() {
setLayout(new GridBagLayout());
Timer timer = new Timer(1000, new PopulateAction(10, this));
timer.start();
}

@Override
public void addStep(int step) {
JButton btn = new JButton(Integer.toString(step));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;

add(btn, gbc);
revalidate();
repaint();
}

}

public class PopulateAction implements ActionListener {

private int steps;
private StepContainer parent;
private int step;

public PopulateAction(int steps, StepContainer parent) {
this.steps = steps;
this.parent = parent;
}

@Override
public void actionPerformed(ActionEvent e) {
parent.addStep(step);
if (step == steps - 1) {
((Timer) e.getSource()).stop();
step = 0;
}
step++;
}
}
}

Welche Sie verwenden, hängt von der Komplexität Ihres Problems ab. SwingWorker benutzt es selbst Thread anrufen doInBackgroundDies ermöglicht Ihnen die Bearbeitung zeitaufwändiger Aufgaben, bietet jedoch die Möglichkeit, die Benutzeroberfläche sicher zu aktualisieren.

Timer Löst die Aktualisierungen im Kontext des EDT aus, sodass es gespeichert wird, um die Benutzeroberfläche zu aktualisieren, aber keine zeitaufwändigen Aufgaben auszuführen


0 für die Antwort № 2

Wenn Sie die GUI sehr schnell aktualisieren, werden die Zwischenänderungen nicht angezeigt. Fügen Sie daher zwischen den einzelnen Schritten in der Schleife eine Pause hinzu:

new Thread() {
public void run() {
for (int step = 0; step < simSteps; step++){
this.gui.updateGUI(this.stats, step);
try {
Thread.currentThread().sleep(1000); // wait 1s before each step
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();


public void updateGUI(final Stats stats, final int step){
// the declaration of stats in this class should be marked as volatile like:
// private volatile Stats;
this.stats = stats;
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
getContentPane().remove(pollutionPanel);
pollutionPanel = new BarPanel(settings, stats, possibleColors);
getContentPane().add(pollutionPanel);
validate();
repaint();
}
});
}

Denken Sie auch daran, dass diese Schleife oben nicht ausgeführt werden sollte EventThreadDaher sollten Sie diesen Code nicht direkt in einem ausführen ActionListener (zB nach JButton gedrückt wurde), sondern in der ActionListener.actionPerformed() erstellen new Thread, der den obigen Code in der enthält run() Methode, dann starten Sie einen solchen Thread mit yourThread.start(). Sie können es auch auf andere Weise in einem separaten Thread ausführen, aber das Thema ist zu weit gefasst, um in den Umfang Ihrer Frage zu passen.