I am trying to basically 1) create random DFAs and 2) show them one after another using JFrame. I have worked out how to create DFAs randomly, let GraphViz take care of visualizing them and save them in a .png file, and then display them in a JFrame. The DFAs are created within a loop, in which a function displayAutomaton
is called, which takes care both of creating the .png, and of displaying it.
for(int i = 2; i < 10; i++) {
AutomatonConverter.displayAutomaton(createRandomDFA(i));
while(AutomatonConverter.jFrameExists) System.out.println("this works");
}
The code of displayAutomaton
is the following:
public static void displayAutomaton(Automaton A) {
// Create the automaton-dependent file name
String fileName = getAutomatonName(A);
// Convert the Automaton and save it in a .png
convertAutomaton(A, fileName);
JFrame frame = new JFrame();
// This static variable is used to determine whether the window
// has been closed, so that the next iteration in the loop can start
jFrameExists = true;
ImageIcon icon = new ImageIcon(fileName);
JLabel label = new JLabel(icon);
frame.add(label);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
jFrameExists = false;
frame.dispose();
}
});
frame.pack();
frame.setVisible(true);
}
As you can see, I use a static variable jFrameExists
in my main loop to infer if the window has been closed, because I would like to open the different images of the automatons consecutively. To this end, I've overridden the frame's Window-Listener. Here's the weird part: as you can see above, after displaying the automaton, a while-loop asks if jFrameExists
has been set to false (which happens, again, when the window is closed). And currently, all this works. Each created automaton is shown in a differet JFrame, one after another, and each new window only opens after I close the previous one, which is exactly what I want - but I obviously don't want to print "this works" every time, so I tried to remove this simple instruction. However, then the code does not work anymore - this way, only the first image is shown, and after I close it, nothing happens.
I have tried simply ending the line there, asserting true (while(AutomatonConverter.jFrameExists) assert true;
), and creating empty void methods to put here (while(AutomatonConverter.jFrameExists) doNothing();
), but nothing works correctly - again, this way only the first image is shown, and the loop does not seem to continue to the next automaton. I just can't figure out why. Anybody have any idea?
Edit: Here's a generalized version of my code that is hopefully more understandable (ignore the code in convertAutomaton and createRandomDFA, I just added that for completeness' sake):
public class main {
static boolean jFrameExists = false;
public static void convertAutomaton(Automaton A, String fileName) {
// Creates a png of the automaton A and saves it under the fileName
GraphViz gv = new GraphViz();
gv.addln(gv.start_graph());
gv.addln("rankdir=LR;");
String startName;
String shape;
if(A instanceof DFA) {
// Create the starting node
State q0 = ((DFA) A).getQ0();
startName = "\"" + q0.getName() + "start\"";
shape = A.getF().contains(q0) ? "doublecircle" : "circle";
// Add the node itself...
gv.addln("node [shape = " + shape + "]; \"" + q0.getName() +"\";");
// ... and its corresponding entry arrow
gv.addln("node [shape = point]; " + startName + ";");
gv.addln(startName + " -> \"" + q0.getName() + "\"");
// Create all accepting nodes
for(State s : A.getF()) {
if(!((DFA) A).getQ0().equals(s)) gv.addln("node [shape = doublecircle]; \"" + s.getName() +"\";");
}
} else {
// Create all starting nodes
for(State s : ((NFA) A).getQ0()) {
startName = "\"" + s.getName() + "start\"";
shape = A.getF().contains(s) ? "doublecircle" : "circle";
// Add the node itself...
gv.addln("node [shape = " + shape + "]; \"" + s.getName() +"\";");
// ... and its corresponding entry arrow
gv.addln("node [shape = point]; " + startName + ";");
gv.addln(startName + " -> \"" + s.getName() + "\"");
}
// Create all accepting nodes
for(State s : A.getF()) {
if(!((NFA) A).getQ0().contains(s)) gv.addln("node [shape = doublecircle]; \"" + s.getName() +"\";");
}
}
gv.addln("node [shape = circle];");
// Create all transitions
ArrayList<State> reachable;
String sName, s2Name;
for(State s : A.getStates()) {
for(char c : Automaton.alphabet) {
sName = "\"" + s.getName() + "\"";
reachable = s.delta(c);
for(State s2 : reachable) {
s2Name = "\"" + s2.getName() + "\"";
gv.addln(sName + " -> " + s2Name + " [ label = \"" + c + "\" ];");
}
}
}
gv.add(gv.end_graph());
File out = new File(fileName);
gv.writeGraphToFile(gv.getGraph(gv.getDotSource(), type), out );
}
}
public static void displayAutomaton(Automaton A) {
String fileName = "automaton.png"
convertAutomaton(A, fileName);
JFrame frame = new JFrame();
jFrameExists = true;
ImageIcon icon = new ImageIcon(fileName);
JLabel label = new JLabel(icon);
frame.add(label);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
jFrameExists = false;
frame.dispose();
}
});
frame.pack();
frame.setVisible(true);
}
public static Automaton createRandomDFA(int numberOfStates) {
// Create the states
ArrayList<State> states = new ArrayList<State>();
for(int i = 0; i < length; i++) states.add(new State(Integer.toString(i)));
// Create random transitions
Random r = new Random();
State rState;
for(State s : states) for(char c : Automaton.alphabet) {
rState = states.get(r.nextInt(length));
s.addTransition(c, rState);
}
// Create random accepting states
ArrayList<State> F = new ArrayList<State>();
for(State s : states) {
if(r.nextInt(length) == length - 1) F.add(s);
}
if(F.size() == 0) F.add(states.get(r.nextInt(length)));
// Create random starting state
State q0 = states.get(r.nextInt(length));
return new DFA(states, q0, F);
}
}
public static void main(String[] args) {
for(int i = 2; i < 10; i++) {
displayAutomaton(createRandomDFA(i));
while(jFrameExists) System.out.println("waiting until the window is closed");
}
}
}
That's basically it. I know it's weird to not implement all the visualization stuff in its own class and to simply close the window instead of adding a button handling the functionality I want, but I essentially don't want to visualize anything in the long run, this was just supposed to test if the Automatons are created correctly. I will try different approaches that are more coherent with coding standards in the future, but I would nonetheless really like to know why calling System.out.println()
makes a difference.