Reputation: 781
I'm using Java Swing to show a problem being solved recursively.
Every time there is a problem that is solved I'd like to repaint my GUI (Solved after if it passes queenSafe
method).
However I'm having an issue here.
I understand that the Event handler and graphics is controlled by the same thread so I can't just tell the thread to sleep to update the GUI when the flag is set. So creating a new thread is best? but not so sure on how to implement it. As of right now I'm trying to use a flag to tell the other thread to repaint. But this seems to give me an infinite loop. Any suggestions?
public void queensProblem(){
this.guiUpdate();
boolean solved = solveQueenBlindSearch(0);
if(!solved){
JOptionPane.showMessageDialog(gui, "Sorry, but this is not solvable");
}
}
boolean solveQueenBlindSearch(int col)
{
if (col >= cb.queens)
return true;
for (int row = 0; row < cb.columns; row++)
{
if (queenSafe(row, col))
{
cb.board[row][col].piece = new Queen();
this.flag = true;
if (solveQueenBlindSearch(col + 1) == true)
return true;
cb.board[row][col].piece = null;
}
}
return false;
}
void guiUpdate() {
new Thread() {
public void run() {
while(true){
if(flag){
mainLayout.removeAll();
mainLayout.add(searches);
JPanel newChessboard = cb.drawChessboard();
mainLayout.add(newChessboard);
mainLayout.repaint();
flag = false;
}
}
}
}.run();
}
Upvotes: 0
Views: 95
Reputation: 1842
As MadProgrammer pointed out you could use a SwingWorker
to facilitate this sort of behavior. However, I've provide a small example (not specific to your program) demonstrating how this delegation to background threads and updating the GUI on the event dispatch thread (EDT) can work.
Note, this is just one method you could adopt.
The example includes two classes, GuiWorker
which is where all the thread handling occurs, and ExampleFrame
which uses GuiWorker
to provide an example.
GuiWorker
This is an abstract class which defines the execution process, running the relevent tasks on the correct thread. It has two abstract methods backgroundProcess()
and postExecute()
which must be implemented.
backgroundProcess()
will not be run on the EDT, but a background thread. This is run before postExecute()
postExecute()
will be run on the EDT and is where the GUI updates after the background task has finished should be carried out.
import javax.swing.SwingUtilities;
public abstract class GuiWorker {
public abstract void backgroundProcess(); // method called on background thread
public abstract void postExecute(); // method called on EDT
public void execute() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Running backgroundProcess() on EDT: " + SwingUtilities.isEventDispatchThread());
// Execute backgroundProcess() on this background thread
backgroundProcess();
// When backgroundProcess() pops, run postExecute() on the EDT
System.out.println("End of background process.");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
System.out.println("Running postExecute() on EDT: " + SwingUtilities.isEventDispatchThread());
postExecute();
}
});
}
}).start(); // start background thread
}
}
ExampleFrame
This is just a small (and unimpressive!) GUI with a label. The main(String[] args)
method is also defined here to reduce the number of classes in this example.
The main(String args)
method (entry point) will construct a new ExampleFrame
instance on the EDT using SwingUtilities.invokeLater(Runnable)
For simplicity everything is carried out in the constructor. Setting up and showing the GUI with a single JLabel
called output
which initially has the text 'Initial', as well as using GuiWorker
to do some background task. In this case, it will perform 10 iterations of a while loop, outputting i
to the console (which increases by 1 with each iteration). Each iteration has a short pause on the background thread of 500ms. Once this is finished, the JLabel
called output
will be updated to say 'Finished'.
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class ExampleFrame extends JFrame {
private JLabel output = new JLabel("Initial");
public static void main(String[] args) {
// Construct and show a new JFrame (ExampleFrame) on the EDT
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new ExampleFrame();
}
});
}
public ExampleFrame() {
System.out.println("Running ExampleFrame() constructor on EDT: " + SwingUtilities.isEventDispatchThread());
// Setup GUI
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(output);
pack();
setVisible(true);
// Implement the abstract methods of GuiWorker and invoke execute() to run
new GuiWorker() {
@Override
public void backgroundProcess() {
// To be run on a background thread
int i = 0;
// iterate 10 times, sleeping for 500 ms
// printing i to the console
while (i < 10) {
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(i);
i++;
}
}
@Override
public void postExecute() {
// when the backgroundProcess has finished
// update the output JLabel on the EDT
output.setText("Finished");
}
}.execute(); // invoke execute to start the worker
}
}
If data during execution of the background task is required for the GUI update you can always introduce class member fields when implementing GuiWorker
as an anonymous class or otherwise which would then be accessible by postExecute()
. Alternatively GuiWorker
can be reworked to allow backgroundProcess()
to return some data which is then passed to postExecute()
as a parameter.
Upvotes: 1