After searching for an answer lots of threads about similar issues, I still don't find a solution that fits my needs.
Basically, I have a while loop and I'd like to wait for the repaint() method to complete before another iteration of that loop starts.
In more detail, I have some segments that are drawn in the paintComponent method of a MapPanel class that extends JComponent. Then, when the user clicks on a button, an algorithm starts searching for intersections beginning with the "upper" segments (using endpoints as event points). This algorithm is basically the while loop I spoke about, it calls another algorithm in this while loop sending one event point at each iteration (those event points are sent in order, from top to bottom).
What I'd like to do is to show the current "position" of the algorithm by drawing a line at the current event point. As the algorithm finds new intersections, I'd like to show them too. I did draw what was needed in the paintComponent() method.
The problem is that when I call the repaint() method for the MapPanel inside the loop, it waits for the whole loop to end before the UI is updated(I've found a lot about why it does that but not about how to fix this in my particular case).
Okay, I'm done with the explanations, here's the actual algorithm :
private void findIntersection(ArrayList<Segment> segments){
QTree Q=new QTree();
for (Segment s : segments){
Q.initialise(s);
}
TreeT T=new TreeT();
while (Q.getRoot()!=null){
Point p = Q.maxEventPoint(Q.getRoot());
Q.delete(p.getX(), p.getY());
mapPanel.setSweeplinePosition(p.getY());
handleEventPoint(p, T, Q);
mapPanel.repaint()
//should wait here for repaint() to complete
}
System.out.println("all intersections found, found " + mapPanel.getIntersections().size());
}
The problem is that your long-running code is likely being called on the Swing event thread an action which prevents the event thread from doing its actions including drawing the GUI.
The solution is likely similar to what you've found in your search, but we don't know why you cannot use it with your program, and that is to do the while loop in a SwingWorker background thread, to publish the interim results using the SwingWorker's publish/process method pair, and then to do your drawings with the interim data.
For more on this, please read Concurrency in Swing
For example
private void findIntersection(final ArrayList<Segment> segments) {
final SwingWorker<Void, Double> myWorker = new SwingWorker<Void, Double>() {
@Override
protected Void doInBackground() throws Exception {
QTree Q = new QTree();
for (Segment s : segments) {
Q.initialise(s);
}
TreeT T = new TreeT();
while (Q.getRoot() != null) {
Point p = Q.maxEventPoint(Q.getRoot());
Q.delete(p.getX(), p.getY());
publish(p.getY()); // push data to process method
// take care that this method below does not
// make Swing calls
handleEventPoint(p, T, Q);
}
return null;
}
@Override
protected void process(List<Double> chunks) {
for (Double yValue : chunks) {
// this is called on the EDT (Swing event dispatch thread)
mapPanel.setSweeplinePosition(yValue);
mapPanel.repaint();
}
}
};
myWorker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
// get notified when Worker is done
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
// TODO: any clean up code for when worker is done goes here
try {
myWorker.get(); // must call to trap exceptions that occur inside worker
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
myWorker.execute(); // execute worker
}