Reputation: 18792
To explain my question here is an MCVE where clicking clicking a JButton
on JDialog
A opens JDialog
B:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
public class DiagA extends JDialog {
private DiagB diag;
public DiagA() {
super();
setTitle("main diag");
setSize(200, 150);
setLocation(400,400);
JButton btn = new JButton("Show DiagB");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
showDiag();
}
});
add(btn, BorderLayout.NORTH);
//make main frame visible
setVisible(true);
}
void showDiag() {
if(diag == null) {
diag = new DiagB();
//this prints out as expected
System.out.println("set visible done");
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {}
//only after the delay diag shows in full
}
}
public static void main(String[] args) {
new DiagA();
}
}
class DiagB extends JDialog {
public DiagB() {
super();
setTitle("2nd diag");
setSize(150, 100);
setLocation(600,420);
setLayout(new FlowLayout(FlowLayout.CENTER));
getContentPane().setBackground(Color.YELLOW);
setVisible(true);
}
}
As you can see in the code I added a 3 sec delay after creating DiagB
.
Clicking the button DiagB
shows like this:
Only after the 3 sec delay ends, DiagB
shows in full:
My questions are:
a. Why doesn't DiagB
show completely after it is constructed ? (It shows in full only when showDiag()
returns).
b. The reason for my question is that DiagB
needs to be updated, by long processes in DiagA
.
What is the right way to update ? Does it require using a SwingWorker
for every updating process ?
Upvotes: 3
Views: 193
Reputation: 18792
Based on Marko Topolnik answer and Andrew Thompson comment, I warped the long process with a SwingWorker
.
This works fine:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingWorker;
public class DiagA extends JDialog {
private FrameB frame;
private JButton btn;
public DiagA() {
super();
setTitle("main frame");
setSize(200, 150);
setLocation(400,400);
btn = new JButton("Show Frame B");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
show2ndFrame();
}
});
add(btn, BorderLayout.NORTH);
setVisible(true);
}
void show2ndFrame() {
if(frame == null) {
frame = new FrameB();
btn.setText("Exit");
}else {
System.exit(0);
}
doWork();
}
private void doWork() {
SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
try {
for(int i = 1 ; i<=100 ; i++) {
//represents a long process
Thread.sleep(100);
frame.update(i);
}
} catch (InterruptedException ex) {}
return null;
}
};
sw.execute();
}
public static void main(String[] args) {
new DiagA();
}
}
class FrameB extends JFrame {
JLabel label;
public FrameB() {
super();
setTitle("2nd frame");
setSize(150, 100);
setLocation(600,420);
setLayout(new FlowLayout(FlowLayout.CENTER));
getContentPane().setBackground(Color.YELLOW);
label = new JLabel("0");
add(label);
setVisible(true);
}
void update(int progress) {
label.setText(String.valueOf(progress));
}
}
Upvotes: 0
Reputation: 200138
a. showDiag
runs on the GUI thread. The GUI will be completely dead while you make the GUI thread sleep.
b. Yes, use a SwingWorker
for long-running tasks and use SwingUtilities.invokeLater()
to submit GUI-updating tasks back to the GUI thread. Alternatively, implement SwingWorker#done()
which is a convenience method that runs on the GUI thread after the SwingWorker
task completes.
Upvotes: 4