I'm running a simulation which times the movement of an objects reaching a destination, and then displays the results on a JTable after the simulation ends. However, when the window containing JTable is closed, I want to be able to have the main class run again.
I was thinking of using frame.setDefaultCloseOperation(); somehow, but I'm unsure of how to do this.
Here is what my code currently looks like:
public class Main {
public static void main(String[] args) throws InterruptedException {
Scanner scan = new Scanner(System.in);
ArrayList<Integer> inputsA = new ArrayList<>();
ArrayList<Integer> timesA = new ArrayList<>();
System.out.println("Please enter the time for A: ");
int inputA = scan.nextInt();
inputsA.add(inputA);
Simulation sim = new Simulation(inputA);
sim.runSim();
timesA.add(sim.getA());
int length = timesA.size();
Object[][] tableData = new Object[length][2];
for (int i = 0; i < length; i++) {
tableData[i] = new Object[]{inputsA.get(i), timesA.get(i)};
}
JFrame frame = new JFrame();
String[] tableColumn = {"Input A", "Time A"};
TableModel model = new DefaultTableModel(tableData, tableColumn) {
public Class getColumnClass(int column) {
Class returnValue;
if ((column >= 0) && (column < getColumnCount())) {
returnValue = getValueAt(0, column).getClass();
} else {
returnValue = Object.class;
}
return returnValue;
}
};
RowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model);
JTable table = new JTable(tableData, tableColumn);
table.setBounds(100, 55, 300, 200);
table.setRowSorter(sorter);
JScrollPane jScrollPane = new JScrollPane(table);
frame.add(jScrollPane);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
One solution is to
frameSetDefaultCloseOperation(JFame.DISPOSE_ON_CLOSE);to prevent closing JFrame from closing the JVM. But this is the default setting for JFrames, and so this isn't needed.This solution is simple, straightforward, easy, and wrong, because it does not address the glaring issues with your code, and those include:
Instead, I recommend that you
For example,
An improvement would be to run the simulation from within a background thread, such as a SwingWorker so that any time delay caused by the simulation does not freeze the GUI.
The code explained:
First of all, any Swing GUI code should be called on a specific thread, the Swing event thread, also known as the EDT for "Event Dispatch Thread". Doing this helps ensure that the Swing code has lower risk of throwing unpredictable and hard to debug thread-induced errors.
One way to do this is to put the code that creates and runs the GUI within a Runnable that is called inside of
SwingUtilities.invokeLater(runnable-goes-here):Next, I create a class, SimPanel that either extends or can return a JPanel, one that can easily be placed into the main window (JFrame) or in another JPanel, or anywhere I desire.
This array,
COLUMN_NAMES, will be passed into my DefaultTableModel's constructor to create the table's column headers:I get my input from a JSpinner and not a JTextField because with a JSpinner, the user cannot enter non-numeric input. It's not possible:
The JButton starts the simulation and the JTable displays the simulation results which is all pushed into its table model:
In the SimPanel constructor, I create a JPanel for the user to input their data. It holds both the JSpinner and JButton:
The butotn's ActionListener gets the user's input from the spinner, creates a new Simulation object with that input, runs the simulation, and passes the result to the table model:
Again, ideally, the simulation would be run within a SwingWorker, but that is not being done here for simplicity's sake.
I here make the SimPanel's layout a BorderLayout. I then add the input panel to the top of the sim JPanel, and add the JTable, held in a JScrollPane, in the center position. Note that I never set the size of a JTable, since that will break the table's ability to expand with new data that might extend beyond the viewport size of the scrollpane that holds it: