Related
I have a minimal app to show Java Swing GUI update problem, seems it's not painting the components properly, the errors look like this :
Here is my program :
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import java.util.concurrent.*;
public class Java_Test extends JPanel
{
static JFrame frame=new JFrame("Java Test");
static int W=800,H=260,Executor_Count=12;
JPanel Progress_Panel=new JPanel(),Center_Panel;
JButton Do_Test_Button=new JButton("Do Test");
Get_Time Timer=new Get_Time();
ThreadPoolExecutor executor;
Progress_Bar Progress_bar;
Timer Display_Timer=new Timer(1);
public Java_Test()
{
setLayout(new BorderLayout());
JPanel Top_Panel=new JPanel();
Top_Panel.setPreferredSize(new Dimension(W-6,60));
add("North",Top_Panel);
Do_Test_Button.setFont(new Font("Times New Roman",0,16));
Do_Test_Button.setBackground(new Color(118,198,250));
Do_Test_Button.setForeground(new Color(0,28,218));
Do_Test_Button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Do_Test(); } });
Top_Panel.add(Do_Test_Button);
JPanel Center_Panel=new JPanel();
Center_Panel.setPreferredSize(new Dimension(W-2,170));
add("Center",Center_Panel);
Progress_Panel.setPreferredSize(new Dimension(W-2,160));
Center_Panel.add(Progress_Panel);
JLabel Progress_Label=new JLabel("Progress");
Progress_Label.setFont(new Font("Times New Roman",0,20));
Progress_Label.setBackground(new Color(253,253,253));
Progress_Label.setForeground(new Color(8,68,128));
Progress_Label.setPreferredSize(new Dimension(W-20,53));
Progress_Label.setHorizontalAlignment(SwingConstants.CENTER);
Progress_Panel.add(Progress_Label);
Progress_bar=new Progress_Bar(620,26);
Progress_Panel.add(Progress_bar);
JPanel Time_Used_Panel=new JPanel(new FlowLayout(FlowLayout.CENTER,6,26));
Time_Used_Panel.setPreferredSize(new Dimension(W-20,60));
Progress_Panel.add(Time_Used_Panel);
JLabel Time_Used_Label=new JLabel("Time : ");
Time_Used_Label.setFont(new Font("Times New Roman",0,14));
Time_Used_Label.setForeground(new Color(0,0,238));
Time_Used_Label.setHorizontalAlignment(SwingConstants.CENTER);
Time_Used_Panel.add(Time_Used_Label);
Display_Timer.setFont(new Font("Times New Roman",0,14));
Display_Timer.setForeground(new Color(0,0,238));
Display_Timer.setPreferredSize(new Dimension(50,17));
Time_Used_Panel.add(Display_Timer);
Clock clock=new Clock(0);
clock.setFont(new Font("Monospaced",Font.PLAIN,16));
clock.setBackground(new Color(0,110,220));
clock.setForeground(new Color(250,250,250));
clock.setOpaque(true);
clock.setPreferredSize(new Dimension(288,30));
clock.start();
JPanel Bottom_Panel=new JPanel();
Bottom_Panel.setPreferredSize(new Dimension(W-2,50));
Bottom_Panel.add(clock);
add("South",Bottom_Panel);
setPreferredSize(new Dimension(W,H));
}
void Do_Test()
{
String Info="",Result;
Out("Do_Test");
try
{
Display_Timer.start();
Timer.Start();
Output_Time("[ 1 ]");
Progress_bar.Set_Progress(1);
int Task_Count=222;
executor=new ThreadPoolExecutor(Executor_Count,Executor_Count*2,1,TimeUnit.SECONDS,new LinkedBlockingQueue());
ArrayList<Future<String>> futures=new ArrayList<>(Task_Count);
Test_Runner A_Runner;
try
{
for (int i=0;i<Task_Count;i++)
{
A_Runner=new Test_Runner();
futures.add(executor.submit(A_Runner));
}
executor.shutdown();
while (!executor.isTerminated()) { executor.awaitTermination(100,TimeUnit.MILLISECONDS); }
for (Future<String> future : futures)
{
Result=future.get();
if (Result!=null) Info+=Result;
}
}
catch (Exception e) { e.printStackTrace(); }
}
catch (Exception e) { e.printStackTrace(); }
Output_Time("[ 2 ]");
Progress_bar.Set_Progress(100);
Out("Done");
Display_Timer.stop();
}
String Output_Time(String Id)
{
Timer.End();
String Time_Duration=Id+" : Time = "+Timer.Get_Duration_Hour_Minute_Second();
Out(Time_Duration);
return Time_Duration;
}
private static void Out(String message) { System.out.println(message); }
static void Create_And_Show_GUI()
{
final Java_Test demo=new Java_Test();
frame.add(demo);
frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Create_And_Show_GUI(); } }); }
}
class Progress_Bar extends JPanel implements Runnable
{
int W,H,Last_Progress=-99,Progress,counter=0,Unit_Size=20;
static JProgressBar b=new JProgressBar();
boolean Started_B=false,Do_Step_B=false;
Thread Progress_Bar_Runner_Thread;
public Progress_Bar(int W,int H)
{
setPreferredSize(new Dimension(W,H));
b.setPreferredSize(new Dimension(W-20,H-8));
b.setStringPainted(true);
add(b);
start();
}
public void Set_Progress(int Progress)
{
if (Progress==1 || (this.Progress<Progress && Progress<=100))
{
this.Progress=Progress;
b.setValue(Progress);
b.paintImmediately(0,0,b.getWidth(),b.getHeight());
Out(" Progress = "+Progress+" %");
}
if (Progress==1) Started_B=true;
else if (Progress>=100)
{
Started_B=false;
Do_Step_B=false;
}
}
public void run()
{
try
{
while (Progress<=100)
{
if ((Progress==0 || Progress==50 || Progress==100 || Do_Step_B) && Last_Progress!=Progress)
{
b.setValue(Progress);
// revalidate();
b.paintImmediately(0,0,b.getWidth(),b.getHeight());
Last_Progress=Progress;
}
Thread.sleep(200); // Delay the thread
Do_Step_B=(Started_B && (counter++ % Unit_Size ==0));
if (Progress<100 && Do_Step_B) Progress++;
}
}
catch (Exception e) { e.printStackTrace(); }
}
public void start()
{
if (Progress_Bar_Runner_Thread==null)
{
Progress_Bar_Runner_Thread=new Thread(this);
Progress_Bar_Runner_Thread.setPriority(Thread.NORM_PRIORITY);
Progress_Bar_Runner_Thread.start();
}
}
public void stop() { if (Progress_Bar_Runner_Thread!=null) Progress_Bar_Runner_Thread=null; }
private static void Out(String message) { System.out.println(message); }
}
class Test_Runner implements Callable<String>
{
int Start_Index=0,End_Index=6999;
StringBuilder StrBdr=new StringBuilder();
public Test_Runner() { }
public String call() throws InterruptedException
{
try { for (int i=Start_Index;i<End_Index;i++) StrBdr.append("Test_Runner + Test_Runner + Test_Runner + Test_Runner + Test_Runner + Test_Runner"); }
catch (Exception e) {}
return StrBdr.toString();
}
}
class Timer extends JLabel implements Runnable
{
public static final long serialVersionUID=26362862L;
private Thread Timer_Thread;
String Time_Text="";
int updateInterval=1000,Format=0;
Get_Time Timer=new Get_Time();
public Timer()
{
setFont(new Font("Monospaced",Font.PLAIN,16));
setVerticalAlignment(SwingConstants.CENTER);
setHorizontalAlignment(SwingConstants.CENTER);
}
public Timer(int Format) { this.Format=Format; }
public void start()
{
Timer.Start();
if (Timer_Thread==null)
{
Timer_Thread=new Thread(this);
Timer_Thread.setPriority(Thread.NORM_PRIORITY);
Timer_Thread.start();
}
}
public void run()
{
Thread myThread=Thread.currentThread();
while (Timer_Thread==myThread)
{
switch (Format)
{
case 1 : Time_Text=Timer.Get_Duration_Hour_Minute_Second();break;
}
setText(Time_Text);
paintImmediately(0,0,getWidth(),getHeight());
revalidate();
try { Thread.sleep(updateInterval); }
catch (InterruptedException e) { }
}
}
public void stop() { if (Timer_Thread != null) Timer_Thread=null; }
}
class Clock extends JLabel implements Runnable
{
public static final long serialVersionUID=26362862L;
private Thread clockThread;
String Time_Text="";
int updateInterval=1000,Format=0;
Get_Time Timer=new Get_Time();
public Clock() { start(); }
public Clock(int Format)
{
this.Format=Format;
start();
}
public void start()
{
setVerticalAlignment(SwingConstants.CENTER);
setHorizontalAlignment(SwingConstants.CENTER);
if (clockThread==null)
{
clockThread=new Thread(this);
// clockThread.setPriority(Thread.NORM_PRIORITY);
clockThread.setPriority(Thread.MIN_PRIORITY);
clockThread.start();
}
}
public void run()
{
Thread myThread=Thread.currentThread();
while (clockThread==myThread)
{
switch (Format)
{
case 0 : Time_Text=" "+new java.util.Date().toString().substring(0,19)+" ";break;
}
setText(Time_Text);
paintImmediately(0,0,getWidth(),getHeight());
revalidate();
try { Thread.sleep(updateInterval); }
catch (InterruptedException e) { }
}
}
public void stop() { if (clockThread != null) clockThread=null; }
}
class Get_Time
{
private long start,end;
public int Hours,Minutes,Seconds,Total_Seconds;
String ST_Hours,ST_Minutes,ST_Seconds;
public Get_Time() { Reset(); }
public void Start() { start=System.currentTimeMillis(); }
public void End()
{
int half_second;
end=System.currentTimeMillis();
Total_Seconds=(int)(end-start)/1000;
half_second=(int)(end-start)%1000;
if (half_second>499) Total_Seconds++;
Hours=Total_Seconds/3600;
Minutes=(Total_Seconds%3600)/60;
Seconds=(Total_Seconds%3600)%60;
ST_Hours=new String((Hours>9)?""+Hours:"0"+Hours);
ST_Minutes=new String((Minutes>9)?""+Minutes:"0"+Minutes);
ST_Seconds=new String((Seconds>9)?""+Seconds:"0"+Seconds);
}
public String Get_Duration_Hour_Minute_Second()
{
End();
return ST_Hours+":"+ST_Minutes+":"+ST_Seconds;
}
public void Reset() { start=0;end=0; }
}
My computer has a 12 core 3.33 GH Intel Corei7 x 980 CPU, with 24 GB of RAM, running on Windows 7, Java Version = 1.8.0_212
The question is how to fix it ?
Enumeration of Swing threading and painting problems:
All of the following code is called from the Swing event dispatch thread (EDT), and so far so good....
void Do_Test() {
String Info = "", Result;
Out("Do_Test");
try {
Display_Timer.start();
Timer.Start();
Output_Time("[ 1 ]");
Progress_bar.Set_Progress(1); // OK to call this on the event thread
int Task_Count = 222;
executor = new ThreadPoolExecutor(Executor_Count, Executor_Count * 2, 1,
TimeUnit.SECONDS, new LinkedBlockingQueue());
ArrayList<Future<String>> futures = new ArrayList<>(Task_Count);
Test_Runner A_Runner;
try {
for (int i = 0; i < Task_Count; i++) {
A_Runner = new Test_Runner();
futures.add(executor.submit(A_Runner));
}
executor.shutdown();
OK, here we start seeing problems as you're calling a potentially long while loop on the EDT, potentially blocking it:
while (!executor.isTerminated()) {
executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
Same here, as the call to Future#get() is a blocking call:
for (Future<String> future : futures) {
Result = future.get();
if (Result != null)
Info += Result;
}
OK, now we start seeing some truly unusual Swing code where you make Swing calls, including setting a JProgressBar's value off the EDT and then try to force the EDT to do some painting via paintImmediately(....). Making calls off the EDT can result in intermittent and unpredictable errors, and can cause the painting to mess up, and the GUI to sometimes freeze:
class Progress_Bar extends JPanel implements Runnable {
// .....
public void Set_Progress(int Progress) {
if (Progress == 1 || (this.Progress < Progress && Progress <= 100)) {
this.Progress = Progress; // ****** being called off the EDT
b.paintImmediately(0, 0, b.getWidth(), b.getHeight()); // ***** Don't do this
Out(" Progress = " + Progress + " %");
}
// .....
}
public void run() {
try {
while (Progress <= 100) {
if ((Progress == 0 || Progress == 50 || Progress == 100 || Do_Step_B)
&& Last_Progress != Progress) {
// ***** called off the EDT -- don't do this
b.setValue(Progress);
// revalidate();
b.paintImmediately(0, 0, b.getWidth(), b.getHeight()); // **** and don't do this
Last_Progress = Progress;
}
Thread.sleep(200); // Delay the thread
Do_Step_B = (Started_B && (counter++ % Unit_Size == 0));
if (Progress < 100 && Do_Step_B)
Progress++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// .......
}
Similar strange goings-ons in this class, the Timer class, where you set a JLabel's text off of the EDT:
class Timer extends JLabel implements Runnable {
// ....
public void run() {
Thread myThread = Thread.currentThread();
while (Timer_Thread == myThread) {
switch (Format) {
case 1:
Time_Text = Timer.Get_Duration_Hour_Minute_Second();
break;
}
// again this is dangerous code **********
setText(Time_Text);
paintImmediately(0, 0, getWidth(), getHeight());
// ....
Same for the Clock class...
Solutions:
To repeatedly call code in a Swing GUI, use a javax.swing.Timer or "Swing Timer". For an example of use of this, please see my implementation of your MCVE, but using a Swing Timer for your threading code above.
The other code, the one that calls long-running tasks, should be done within a SwingWorker. This worker thread usually communicates with the Swing GUI in one of two ways (or both) -- either using a publish/process method pair, or (as in the example below), using a PropertyChangeListener attached to the Worker. Workers have several "bound" properties, fields that notify listeners of change, including the progress property, that can hold a value from 0 to 100, and the SwingWorker.StateValue, or "state" property:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
#SuppressWarnings("serial")
public class JavaTest2 extends JPanel {
private static final int W = 800;
private static final int H = 260;
private Action doTestAction = new DoTestAction("Do Test");
private JProgressBar progressBar = new JProgressBar(0, 100);
private MyClockPanel clockPanel = new MyClockPanel();
private MyTimerPanel timerPanel = new MyTimerPanel();
public JavaTest2() {
JPanel topPanel = new JPanel();
topPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 35, 5));
topPanel.add(new JButton(doTestAction));
progressBar.setStringPainted(true);
JPanel progressPanel = new JPanel(new GridBagLayout());
progressPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0;
progressPanel.add(progressBar, gbc);
JLabel progressLabel = new JLabel("Progress", SwingConstants.CENTER);
progressLabel.setFont(new Font("Times New Roman", 0, 20));
progressLabel.setForeground(new Color(8, 68, 128));
JPanel centralPanel = new JPanel(new BorderLayout(5, 5));
centralPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
centralPanel.add(progressLabel, BorderLayout.PAGE_START);
centralPanel.add(progressPanel);
JPanel clockWrapper = new JPanel();
clockWrapper.add(clockPanel);
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.PAGE_AXIS));
bottomPanel.add(timerPanel, BorderLayout.PAGE_START);
bottomPanel.add(clockWrapper, BorderLayout.PAGE_END);
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
add(topPanel, BorderLayout.PAGE_START);
add(bottomPanel, BorderLayout.PAGE_END);
add(centralPanel);
}
#Override
public Dimension getPreferredSize() {
Dimension superSize = super.getPreferredSize();
if (isPreferredSizeSet()) {
return superSize;
} else {
int w = Math.max(superSize.width, W);
int h = Math.max(superSize.height, H);
return new Dimension(w, h);
}
}
private class DoTestAction extends AbstractAction {
private MyWorker myWorker = null;
public DoTestAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
if (myWorker != null && myWorker.getState() == SwingWorker.StateValue.STARTED) {
return; // still running
}
timerPanel.start();
progressBar.setValue(0);
myWorker = new MyWorker();
myWorker.addPropertyChangeListener(new WorkerListener());
myWorker.execute();
setEnabled(false);
}
}
class WorkerListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
// if the worker is changing its progress bound property:
if (evt.getPropertyName().equals("progress")) {
int progress = (int) evt.getNewValue();
// just for safety's sake, limit progress to 100 and no more
progress = Math.min(progress, 100);
progressBar.setValue(progress);
} else if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
// else if worker is done
try {
// get the result to at least trap errors
String result = ((MyWorker) evt.getSource()).get();
// can display result in the GUI
timerPanel.stop();
} catch (Exception e) {
// worker's exception is available to the GUI if desired here
e.printStackTrace();
}
progressBar.setValue(100);
doTestAction.setEnabled(true);
}
}
}
private static class MyWorker extends SwingWorker<String, Void> {
private static final int EXECUTOR_COUNT = 12;
private static final int TASK_COUNT = 222;
#Override
protected String doInBackground() throws Exception {
ExecutorService executor = new ThreadPoolExecutor(EXECUTOR_COUNT, EXECUTOR_COUNT * 2, 1,
TimeUnit.SECONDS, new LinkedBlockingQueue<>());
List<Future<String>> futures = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < TASK_COUNT; i++) {
Callable<String> aRunner = new ARunner();
futures.add(executor.submit(aRunner));
}
executor.shutdown();
int index = 0;
for (Future<String> future : futures) {
String result = future.get();
sb.append(result);
sb.append(" ");
index++;
int progress = (100 * index) / TASK_COUNT;
progress = Math.min(progress, 100);
setProgress(progress);
}
return sb.toString();
}
}
private static class ARunner implements Callable<String> {
private static final long SLEEP_TIME = 800;
#Override
public String call() throws Exception {
TimeUnit.MILLISECONDS.sleep(SLEEP_TIME);
return "Foo";
}
}
private static void createAndShowGui() {
JavaTest2 mainPanel = new JavaTest2();
JFrame frame = new JFrame("Java Test 2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class MyClockPanel extends JPanel {
private static final Color FG = new Color(250, 250, 250);
private static final Color BG = new Color(0, 110, 220);
private static final int TIMER_DELAY = 200;
private static final Font FONT = new Font("Monospaced", Font.PLAIN, 16);
private JLabel clockLabel = new JLabel("", SwingConstants.CENTER);
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E MMM dd yyyy kk:mm:ss");
public MyClockPanel() {
setBackground(BG);
clockLabel.setForeground(FG);
clockLabel.setFont(FONT);
displayDateTime();
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(5, 50, 5, 50));
add(clockLabel);
new javax.swing.Timer(TIMER_DELAY, e -> {
displayDateTime();
}).start();
}
private void displayDateTime() {
LocalDateTime dateTime = LocalDateTime.now();
String text = dateTime.format(formatter);
clockLabel.setText(text);
}
}
#SuppressWarnings("serial")
class MyTimerPanel extends JPanel {
private static final Color FG = new Color(0, 0, 238);
private static final Font FONT = new Font("Times New Roman", 0, 14);
private static final int TIMER_DELAY = 40;
private static final String FORMAT_TXT = "Elapsed Time: %02d:%02d:%02d";
private JLabel timerLabel = new JLabel("", SwingConstants.CENTER);
private LocalDateTime startTime = null;
private Timer timer = null;
public MyTimerPanel() {
timerLabel.setForeground(FG);
timerLabel.setFont(FONT);
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(5, 50, 5, 50));
timerLabel.setText(String.format(FORMAT_TXT, 0, 0, 0));
add(timerLabel);
}
public void start() {
stop();
startTime = LocalDateTime.now();
timer = new Timer(TIMER_DELAY, e -> incrementTime());
timer.start();
}
public void stop() {
if (timer != null && timer.isRunning()) {
timer.stop();
}
}
private void incrementTime() {
LocalDateTime currentTime = LocalDateTime.now();
long hours = ChronoUnit.HOURS.between(startTime, currentTime);
long minutes = ChronoUnit.MINUTES.between(startTime, currentTime) % 60;
long seconds = ChronoUnit.SECONDS.between(startTime, currentTime) % 60;
String text = String.format(FORMAT_TXT, hours, minutes, seconds);
timerLabel.setText(text);
}
}
My project uses Java Swing as a GUI. I am making a Towers of Hanoi game. I've just about got the GUI all working, but my solve command wont work properly.
Without threading calls, it immediately solves the towers as expected. I added a couple Thread.waits expected it to solve it step by step so the user can see how it does but instead, it waits some time, then solves the entire puzzle at once. I'm thinking it might not be repainting, but I'm not sure why. Does anyone know what is going on?
Heres the code for the solve:
public class Solver {
public Solver() {
// nothing
}
public void solve(
int numberBlocks,
int startPin,
int auxiliaryPin,
int endPin) {
if (numberBlocks == 1) {
movePin(startPin, endPin);
try {
Thread.sleep(200);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
solve(numberBlocks - 1, startPin, endPin, auxiliaryPin);
movePin(startPin, endPin);
try {
Thread.sleep(200);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
solve(numberBlocks - 1, auxiliaryPin, startPin, endPin);
}
}
private void movePin(int startPin, int endPin) {
TowersOfHanoiGame.moveTopBlock(startPin, endPin);
}
Here is the code from the GUI that does the work:
I know its terribly written, this is my first time writing with Java Swing, Im learning it as I go. If anyone has any pointers on how to better structure this, I'd love to hear about that also.
I'm pasting the whole class, but the important methods are initListeners, and moveTopBlock, and the methods they call.
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class TowersOfHanoiGame {
private static JFrame mainWindow;
private static JPanel mainContentPanel;
private static JPanel content;
private static ArrayList<Block> pegOneBlocks = new ArrayList<Block>();
private static ArrayList<Block> pegTwoBlocks = new ArrayList<Block>();
private static ArrayList<Block> pegThreeBlocks = new ArrayList<Block>();
private Color[] randomColors = new Color[8];
private Dimension menuSize = new Dimension(100, 100);
private static final int DISCSTEXTSIZE = 20;
private static final int MOVESTEXTSIZE = 30;
private ActionListener downButtonListener;
private ActionListener upButtonListener;
private ActionListener solveButtonListener;
private JLabel discs;
private JLabel moves;
private int discsNumber = 3;
private int movesNumber = 0;
private Solver solver = new Solver();
public TowersOfHanoiGame() {
// do nothing
initRandomColors();
initBlocks(3);
}
/**
* Initialize and display the game
*/
public void display() {
initListeners();
initWindow();
mainWindow.setVisible(true);
}
private void initListeners() {
downButtonListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
if (discsNumber > 3) {
discsNumber--;
updateLabels();
// clearContentFrame();
clearBlockArrays();
initBlocks(discsNumber);
reDrawContentFrame();
}
}
};
upButtonListener = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (discsNumber < 8) {
discsNumber++;
updateLabels();
// clearContentFrame();
clearBlockArrays();
initBlocks(discsNumber);
reDrawContentFrame();
}
}
};
solveButtonListener = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
solver.solve(discsNumber, 0, 1, 2);
}
};
}
private void updateLabels() {
discs.setText("DISCS: " + discsNumber);
moves.setText("MOVES: " + movesNumber);
}
/**
* Init the main window
*/
private void initWindow() {
mainWindow = new JFrame("Towers Of Hanoi");
initContentPanel();
mainWindow.setContentPane(mainContentPanel);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setSize(1000, 1000);
mainWindow.setResizable(false);
mainWindow.getContentPane().setBackground(Color.WHITE);
}
/**
* Init the main content panel
*/
private void initContentPanel() {
mainContentPanel = new JPanel(new BorderLayout(50, 50));
JPanel menu = initMenuFrame();
content = initContentFrame();
mainContentPanel.add(menu, BorderLayout.PAGE_START);
mainContentPanel.add(content, BorderLayout.CENTER);
}
private static JPanel initContentFrame() {
JPanel ret = new JPanel(new BorderLayout());
JPanel pegs = new JPanel(new BorderLayout());
pegs.setBackground(Color.WHITE);
ret.setBackground(Color.WHITE);
Peg peg1 = new Peg(25, 500, 1.2);
Peg peg2 = new Peg(50, 500, 1.2);
Peg peg3 = new Peg(0, 500, 1.2);
peg1.addBlocks(pegOneBlocks);
peg2.addBlocks(pegTwoBlocks);
peg3.addBlocks(pegThreeBlocks);
pegs.add(peg1, BorderLayout.LINE_START);
pegs.add(peg2, BorderLayout.CENTER);
pegs.add(peg3, BorderLayout.LINE_END);
ret.add(pegs, BorderLayout.CENTER);
return ret;
}
private Color randomColor() {
int R = (int)(Math.random() * 256);
int G = (int)(Math.random() * 256);
int B = (int)(Math.random() * 256);
Color color = new Color(R, G, B); // random color, but can be bright or
// dull
// to get rainbow, pastel colors
Random random = new Random();
final float hue = random.nextFloat();
final float saturation = 0.9f;// 1.0 for brilliant, 0.0 for dull
final float luminance = 1.0f; // 1.0 for brighter, 0.0 for black
color = Color.getHSBColor(hue, saturation, luminance);
return color;
}
private void initRandomColors() {
for (int i = 0; i < 8; i++) {
randomColors[i] = randomColor();
}
}
private void initBlocks(int numBlocks) {
int startWidth = Block.LONGESTWIDTH;
for (int i = 0; i < numBlocks; i++) {
Block b = new Block((startWidth - (i * 15)), randomColors[i]);
pegOneBlocks.add(b);
}
}
private static void clearContentFrame() {
mainContentPanel.remove(content);
mainContentPanel.repaint();
}
private void clearBlockArrays() {
pegOneBlocks.clear();
pegTwoBlocks.clear();
pegThreeBlocks.clear();
}
public static void reDrawContentFrame() {
content = initContentFrame();
mainContentPanel.add(content, BorderLayout.CENTER);
mainContentPanel.repaint();
}
public static void moveTopBlock(int startPin, int destinationPin) {
Block b = null;
if (startPin == 0) {
b = pegOneBlocks.get(pegOneBlocks.size() - 1);
pegOneBlocks.remove(pegOneBlocks.size() - 1);
}
else if (startPin == 1) {
b = pegTwoBlocks.get(pegTwoBlocks.size() - 1);
pegTwoBlocks.remove(pegTwoBlocks.size() - 1);
}
else if (startPin == 2) {
b = pegThreeBlocks.get(pegThreeBlocks.size() - 1);
pegThreeBlocks.remove(pegThreeBlocks.size() - 1);
}
if (destinationPin == 0) {
pegOneBlocks.add(b);
}
else if (destinationPin == 1) {
pegTwoBlocks.add(b);
}
else if (destinationPin == 2) {
pegThreeBlocks.add(b);
}
reDrawContentFrame();
content.validate();
mainContentPanel.validate();
mainWindow.validate();
}
/**
* Build the menu panel
*
* #return menu panel
*/
private JPanel initMenuFrame() {
JPanel ret = new JPanel(new BorderLayout());
ret.setPreferredSize(menuSize);
// left
JPanel left = new JPanel(new FlowLayout());
left.setPreferredSize(menuSize);
JLabel label = new JLabel("DISCS: 3");
discs = label;
label.setFont(new Font("Serif", Font.BOLD, DISCSTEXTSIZE));
Button down = new Button("Decrease");
down.addActionListener(downButtonListener);
Button up = new Button("Increase");
up.addActionListener(upButtonListener);
left.add(label);
left.add(up);
left.add(down);
// mid
moves = new JLabel("MOVES: 0");
moves.setHorizontalAlignment(JLabel.CENTER);
moves.setFont(new Font("Serif", Font.BOLD, MOVESTEXTSIZE));
// right
JPanel right = new JPanel(new FlowLayout());
Button solve = new Button("Solve");
solve.addActionListener(solveButtonListener);
Button reset = new Button("Reset");
right.add(solve);
right.add(reset);
// sync
JPanel menu = new JPanel(new BorderLayout());
menu.add(left, BorderLayout.LINE_START);
menu.add(moves, BorderLayout.CENTER);
menu.add(right, BorderLayout.LINE_END);
ret.add(menu, BorderLayout.CENTER);
return ret;
}
}
solveButtonListener = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
solver.solve(discsNumber, 0, 1, 2);
}
};
The problem is that code invoked for any listener is executed on the Event Dispatch Thread (EDT). The EDT is responsible for responding to event and repaint the GUI. The Thread.sleep() method causes the EDT to sleep and as a result the GUI can't repaint itself until all the code has finished executing.
What you need to do is start a separate Thread when you invoke the solver.solve(...) method.
Read the section from the Swing tutorial on Concurrency for more information.
Note, the above suggestion to use a separate Thread is still not a proper solution. Swing was designed to be single Thread, which means that all updates to the state of your GUI and the repainting of the GUI should be done on the EDT. So this would mean you should also be using SwingUtilities.invokeLater() to add code to the EDT for processing. I have never tried doing this when using recursion, so I'm not sure the best way to do this.
I am printing simple value to append JTextArea using simple for loop, and when I run it, it's properly Run if I print value in console output...
But if I append JTextArea and print value in the text area, they are appended all after whole program run.
public class SwingThread {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SwingThread window = new SwingThread();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public SwingThread() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane();
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
JTextArea textArea = new JTextArea();
scrollPane.setViewportView(textArea);
JButton btnNewButton = new JButton("New button");
scrollPane.setColumnHeaderView(btnNewButton);
btnNewButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
try
{
for(int i = 0 ; i <= 5 ; i++)
{
textArea.append("Value "+i+"\n");
System.out.println("Value is" + i);
Thread.sleep(1000);
}
}
catch(Exception e)
{
System.out.println("Error : "+e);
}
}
});
}
}
I want to append one by one, but it was appended after the whole program runs.
Your problem is with your use of Thread.sleep, since when you call this on the Swing event thread (or EDT for Event Dispatch Thread) as you are doing, it will put the entire Swing event thread to sleep. When this happens the actions of this thread cannot be performed, including painting the GUI (updating it) and interacting with the user, and this will completely freeze your GUI -- not good. The solution in this current situation is to use a Swing Timer as a pseudo-loop. The Timer creates a loop within a background thread and guarantees that all code within its actionPerformed method will be called on the Swing event thread, a necessity here since we don't want to append to the JTextArea off of this thread.
Also as others have noted, if all you want to do is to perform a repeated action with delay in Swing, then yes, use this Swing Timer. If on the other hand you wish to run a long-running bit of code in Swing, then again this code will block the EDT and will freeze your program. For this situation use a background thread such as one supplied by a SwingWorker. Please check out Lesson: Concurrency in Swing for more on this.
e.g.,
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// delay between timer ticks: 1000
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener() {
private int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
// timer's stopping condition
if (counter >= MAX_VALUE) { // MAX_VALUE is a constant int = 5
((Timer) e.getSource()).stop();
} else {
textArea.append("Value " + counter + "\n");
}
counter++; // increment timer's counter variable
}
}).start();
}
});
The whole thing:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;
import javax.swing.*;
public class SwingThread2 {
protected static final int MAX_VALUE = 5; // our constant
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SwingThread2 window = new SwingThread2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SwingThread2() {
initialize();
}
private void initialize() {
frame = new JFrame();
// frame.setBounds(100, 100, 450, 300); // avoid this
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane();
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
JTextArea textArea = new JTextArea(15, 40);
scrollPane.setViewportView(textArea);
JButton btnNewButton = new JButton("New button");
scrollPane.setColumnHeaderView(btnNewButton);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// delay between timer ticks: 1000
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener() {
private int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
// timer's stopping condition
if (counter >= MAX_VALUE) { // MAX_VALUE is a constant int = 5
((Timer) e.getSource()).stop();
} else {
textArea.append("Value " + counter + "\n");
}
counter++; // increment timer's counter variable
}
}).start();
}
});
// better to avoid setting sizes but instead to
// let the components size themselves vis pack
frame.pack();
frame.setLocationRelativeTo(null);
}
}
Just for further information, here is an example of the same program above that uses a SwingWorker to perform a long running action, and then update a JProgressBar with this action. The worker is quite simple, and simply uses a while loop to advance a counter variable by a bounded random amount. It then transmits uses this value to update its own progress property (a value that can only be from 0 to 100, and so in other situations, the value will need to be normalized to comply with this). I attach a PropertyChangeListener to the worker, and this is notified on the Swing event thread whenever the worker's progress value changes and also whenever the SwingWorker changes state, such as when it is done operating. In the latter situation, the worker's StateValue becomes StateValue.DONE. The listener then updates the GUI accordingly. Please ask if any questions.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
public class SwingThread2 {
protected static final int MAX_VALUE = 5; // our constant
private JFrame frame;
private JProgressBar progressBar = new JProgressBar();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SwingThread2 window = new SwingThread2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SwingThread2() {
initialize();
}
private void initialize() {
frame = new JFrame();
// frame.setBounds(100, 100, 450, 300); // avoid this
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane();
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
JTextArea textArea = new JTextArea(15, 40);
scrollPane.setViewportView(textArea);
JButton btnNewButton = new JButton("New button");
scrollPane.setColumnHeaderView(btnNewButton);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// delay between timer ticks: 1000
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener() {
private int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
// timer's stopping condition
if (counter >= MAX_VALUE) { // MAX_VALUE is a constant
// int = 5
((Timer) e.getSource()).stop();
} else {
textArea.append("Value " + counter + "\n");
}
counter++; // increment timer's counter variable
}
}).start();
}
});
progressBar.setStringPainted(true);
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS));
bottomPanel.add(new JButton(new MyAction("Press Me")));
bottomPanel.add(progressBar);
frame.getContentPane().add(bottomPanel, BorderLayout.PAGE_END);
// better to avoid setting sizes but instead to
// let the components size themselves vis pack
frame.pack();
frame.setLocationRelativeTo(null);
}
private class MyAction extends AbstractAction {
public MyAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
public void actionPerformed(ActionEvent e) {
progressBar.setValue(0);
setEnabled(false);
MyWorker myWorker = new MyWorker();
myWorker.addPropertyChangeListener(new WorkerListener(this));
myWorker.execute();
}
}
private class WorkerListener implements PropertyChangeListener {
private Action action;
public WorkerListener(Action myAction) {
this.action = myAction;
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
int progress = (int) evt.getNewValue();
progressBar.setValue(progress);
} else if ("state".equals(evt.getPropertyName())) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
action.setEnabled(true);
#SuppressWarnings("rawtypes")
SwingWorker worker = (SwingWorker) evt.getSource();
try {
// always want to call get to trap and act on
// any exceptions that the worker might cause
// do this even though get returns nothing
worker.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
}
}
private class MyWorker extends SwingWorker<Void, Void> {
private static final int MULTIPLIER = 80;
private int counter = 0;
private Random random = new Random();
#Override
protected Void doInBackground() throws Exception {
while (counter < 100) {
int increment = random.nextInt(10);
Thread.sleep(increment * MULTIPLIER);
counter += increment;
counter = Math.min(counter, 100);
setProgress(counter);
}
return null;
}
}
}
I'm making a game client/server and I'm having a new thread update some info on the client GUI (which is using Swing). I'm trying to use SwingUtilities but it's not working. Also, I heard SwingUtilities creates a new thread everytime you use it so I'm looking for a new way as well (I have 10 or so JTextFields to be updated). Is there a way to do it without using SwingUtilities? Here is what I have right now.
SwingUtilities.invokeLater(
new Runnable() {
public void run()
{
Client.status.setText("status = "+status);
}
});
Interestingly, just recently I encountered a similar issue, so In order to get over this I used SwingUtilities.invokeAndWait(Runnable runnable), that's what made the SSCCE I was creating to work as expected, though if I change all calls to invokeAndWait() with invokeLater(), one can clearly see the difference between the two things.
A quote from Java Doc says :
Causes doRun.run() to be executed synchronously on the AWT event dispatching thread.
This call blocks until all pending AWT events have been processed and
(then) doRun.run() returns. This method should be used when an application thread
needs to update the GUI.
This is a small program I had made as an SSCCE to represent a Bubble Sort Animation :
import javax.swing.*;
public class BubbleSortFrame extends JFrame
{
private BubbleSortView contentPane;
private void displayGUI()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = new BubbleSortView();
setContentPane(contentPane);
pack();
setLocationByPlatform(true);
setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new BubbleSortFrame().displayGUI();
}
});
}
}
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.InvocationTargetException;
import javax.swing.*;
public class BubbleSortView extends JPanel
{
private JLabel sizeLabel;
private JTextField sizeField;
private JTextField[] vField;
private JLabel[] vLabel;
private JButton startButton, createButton;
private int size;
private JPanel createPanel, animationPanel;
private BubbleSort bubbleSort;
public BubbleSortView()
{
size = 5;
displayAndCreateGUI();
}
private void displayAndCreateGUI()
{
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
setOpaque(true);
setBackground(Color.WHITE);
JPanel basePanel = new JPanel();
basePanel.setLayout(new GridLayout(2, 1, 5, 5));
basePanel.setOpaque(true);
basePanel.setBackground(Color.WHITE);
JPanel topPanel = new JPanel();
topPanel.setOpaque(true);
topPanel.setBackground(Color.WHITE);
topPanel.setBorder(
BorderFactory.createTitledBorder("Input : "));
topPanel.setLayout(new GridLayout(2, 1, 5, 5));
/*
* This will act as the area
* for taking the input for
* number of elements in an Array.
*/
JPanel sizePanel = new JPanel();
sizePanel.setOpaque(true);
sizePanel.setBackground(Color.WHITE);
sizeLabel = new JLabel("Enter Number of Elements : ");
sizeField = new JTextField(10);
createButton = new JButton("CREATE");
/*
* This will act as the area
* where we will specify the values
* for each index in an Array.
*/
createPanel = new JPanel();
createPanel.setOpaque(true);
createPanel.setBackground(Color.WHITE);
createPanel.setBorder(
BorderFactory.createTitledBorder("Please Enter values for an Array : "));
createPanel.setVisible(false);
animationPanel = new JPanel();
animationPanel.setOpaque(true);
animationPanel.setBackground(Color.WHITE);
animationPanel.setBorder(
BorderFactory.createTitledBorder("Animation : "));
animationPanel.setVisible(false);
createButton.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent ae)
{
if (sizeField.getDocument().getLength() > 0)
{
size = Integer.parseInt(sizeField.getText());
vField = new JTextField[size];
createPanel.setVisible(true);
for (int i = 0; i < size; i++)
{
vField[i] = new JTextField(5);
/*
* Adding the Listener to the
* last JTextField on the Right
* Side.
*/
if (i == (size - 1))
{
vField[i].addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent ae)
{
animationPanel.setLayout(
new GridLayout(1, size, 2, 2));
animationPanel.setVisible(true);
vLabel = new JLabel[size];
for (int i = 0; i < size; i++)
{
vLabel[i] = new JLabel(
vField[i].getText(), JLabel.CENTER);
vLabel[i].setOpaque(true);
vLabel[i].setBackground(Color.YELLOW);
vLabel[i].setForeground(Color.RED);
animationPanel.add(vLabel[i]);
}
animationPanel.revalidate();
animationPanel.repaint();
bubbleSort = new BubbleSort(vLabel, size);
Thread t = new Thread(bubbleSort);
t.start();
}
});
}
createPanel.add(vField[i]);
}
createPanel.revalidate();
createPanel.repaint();
createButton.setEnabled(false);
}
else
size = 5;
}
});
sizePanel.add(sizeLabel);
sizePanel.add(sizeField);
sizePanel.add(createButton);
/*
* Initializing JTextField Array
* so that it can be first presented
* to the USER to take input for
* 5 values.
*/
//for (int i = 0; i < size; i++)
// vField[i] = new JTextField(5);
topPanel.add(sizePanel);
topPanel.add(createPanel);
basePanel.add(topPanel);
basePanel.add(animationPanel);
add(basePanel);
}
private class BubbleSort implements Runnable
{
private int[] arr;
private JLabel[] vLabel;
private int size;
private int pass;
public BubbleSort(JLabel[] label, int size)
{
vLabel = label;
this.size = size;
pass = 1;
for (int i = 0; i < size; i++)
System.out.print("" + vLabel[i].getText() + "\t");
System.out.println("");
}
#Override
public void run()
{
try
{
bubbleSorting();
}
catch (InvocationTargetException ite)
{
ite.printStackTrace();
}
catch(InterruptedException ie)
{
ie.printStackTrace();
}
}
private void bubbleSorting()
throws InterruptedException, InvocationTargetException
{
while (pass < size)
{
for (int i = 0; i < (size - pass); i++)
{
final int j = i;
SwingUtilities.invokeAndWait(new Runnable()
{
#Override
public void run()
{
vLabel[j].setBackground(Color.RED);
vLabel[j].setForeground(Color.WHITE);
vLabel[j + 1].setBackground(Color.RED);
vLabel[j + 1].setForeground(Color.WHITE);
}
});
try
{
Thread.sleep(1500);
}
catch(InterruptedException ie)
{
ie.printStackTrace();
}
int left = Integer.parseInt(vLabel[i].getText());
int right = Integer.parseInt(vLabel[i + 1].getText());
if (left > right)
{
String temp = vLabel[i].getText();
vLabel[i].setText(vLabel[i + 1].getText());
vLabel[i + 1].setText(temp);
}
SwingUtilities.invokeAndWait(new Runnable()
{
#Override
public void run()
{
vLabel[j].setBackground(Color.YELLOW);
vLabel[j].setForeground(Color.RED);
vLabel[j + 1].setBackground(Color.YELLOW);
vLabel[j + 1].setForeground(Color.RED);
}
});
}
System.out.println("Pass : " + pass + "\tSize : " + size);
SwingUtilities.invokeAndWait(new Runnable()
{
#Override
public void run()
{
vLabel[size - pass].setBackground(Color.GREEN);
vLabel[size - pass].setForeground(Color.BLUE);
}
});
pass++;
}
SwingUtilities.invokeAndWait(new Runnable()
{
#Override
public void run()
{
vLabel[0].setBackground(Color.GREEN);
vLabel[0].setForeground(Color.BLUE);
}
});
}
}
}
I don't know where you heard about "SwingUtilities creates a new thread" from, but I think you've either misunderstood or being informed incorrectly. SwingUtilities.invokeLater places the Runnable onto the end of the Event Dispatcher's queue. The queue then processes this event within the it's own thread context (in time), calling Run, there is no "new" thread created for this process.
As to your question.
You may need to call validate() (and possibly repaint()) on the fields parent container to encourage it update ;)
And no, there isn't any other way to sync the UI across threads, that's the reason for SwingUtilities
Hi everybody I am a bit of stack here. When I run the program and press the button submit it is supposed to change 4 pictures in every 2 seconds.However it is not redisplaying the images. If anyone can give me a hand it would be great. I am using eclipse and the program is compiling and running. Here is the code.
/** Here is the GUI of the program
* class name SlideShowGui.java
* #author Kiril Anastasov
* #date 07/03/2012
*/
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SlideShowGui extends JPanel implements ActionListener, Runnable
{
JLabel name, comments, images;
JTextField namejtf, commentsjtf, captionjtf;
JButton submit;
ImageIcon pictures1, pictures2, pictures3, pictures4;
//ImageIcon []pictures2 = {galileo1.jpg};
SlideShowGui()
{
name = new JLabel("Name:");
this.add(name);
namejtf = new JTextField(15);
this.add(namejtf);
comments = new JLabel("Comments:");
this.add(comments);
commentsjtf = new JTextField(15);
this.add(commentsjtf);
submit = new JButton("Submit");
this.add(submit);
submit.addActionListener(this);
pictures1 = new ImageIcon("galileo1.jpg");
images = new JLabel(pictures1);
this.add(images);
pictures2 = new ImageIcon("galileo2.jpg");
this.add(images);
pictures3 = new ImageIcon("galileo3.jpg");
this.add(images);
pictures4 = new ImageIcon("galileo4.jpg");
this.add(images);
captionjtf = new JTextField(24);
this.add(captionjtf);
//pictures = new ImageIcon("galileo1.jpg");
// images.setIcon(pictures);
}
public void actionPerformed(ActionEvent ae)
{
Thread t = new Thread(this);
t.start();
if(ae.getSource() == submit)
{
int i = 0;
boolean go = true;
while(go)
{
i++;
System.out.println(i);
try
{
Thread.sleep(2000);
if(i == 1)
{
pictures1 = new ImageIcon("galileo1.jpg");
images.setIcon(pictures1);
System.out.println("picture 1 should be displayed here");
}
if(i == 2)
{
pictures2 = new ImageIcon("galileo2.jpg");
images.setIcon(pictures2);
System.out.println("picture 2 should be displayed here");
}
if(i == 3)
{
pictures3 = new ImageIcon("galileo3.jpg");
images.setIcon(pictures3);
System.out.println("picture 3 should be displayed here");
}
if(i == 4)
{
pictures4 = new ImageIcon("galileo4.jpg");
images.setIcon(pictures4);
System.out.println("picture 4 should be displayed here");
}
if(i == 4)
{
i = 0;
}
}
catch (InterruptedException ie)
{
System.out.println("thread exception");
}
}
}
}
public void run()
{
}
}
/**The driver class of the program. Here is the JFrame
* class name TestSlideShow.java
* #author Kiril Anastasov
* #date 07/03/2012
*/
import java.awt.*;
import javax.swing.*;
public class TestSlideShow
{
public static void main(String[] args)
{
JFrame application = new JFrame();
SlideShowGui panel = new SlideShowGui();
application.add(panel);
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.setSize(300,600);
application.setLocation(400,100);
application.setVisible(true);
}
}
Always use javax.swing.Timer never use Thread.sleep(...) in Swing atleast. Here try this code, but do replace the path to your images :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SlideShow extends JPanel
{
private int i = 0;
private Timer timer;
private JLabel images = new JLabel();
private Icon bg = UIManager.getIcon("OptionPane.warningIcon");
private Icon red = UIManager.getIcon("OptionPane.errorIcon");
private Icon blue = UIManager.getIcon("OptionPane.informationIcon");
private ImageIcon pictures1, pictures2, pictures3, pictures4;
private ActionListener action = new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
boolean go = true;
i++;
System.out.println(i);
if(i == 1)
{
images.setIcon(bg);
System.out.println("picture 1 should be displayed here");
}
if(i == 2)
{
images.setIcon(red);
System.out.println("picture 2 should be displayed here");
}
if(i == 3)
{
images.setIcon(blue);
System.out.println("picture 3 should be displayed here");
}
if(i == 4)
{
images.setIcon(bg);
System.out.println("picture 4 should be displayed here");
}
if(i == 5)
{
go = false;
timer.stop();
System.exit(0);
}
revalidate();
repaint();
}
};
public SlideShow()
{
JFrame frame = new JFrame("SLIDE SHOW");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.getContentPane().add(this);
add(images);
frame.setSize(300, 300);
frame.setVisible(true);
timer = new Timer(2000, action);
timer.start();
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new SlideShow();
}
});
}
}
The whole if (i == ... part can be simplified. Declare a static class member in SlideShowGUI:
private final static ImageIcon[] icons = {new ImageIcon("galileo1.jpg"),
new ImageIcon("galileo2.jpg"),
new ImageIcon("galileo3.jpg"),
new ImageIcon("galileo4.jpg")};
and use it in the replacement of the if statements:
images.setIcon(icons[i-1]);
System.printf("Picture %s should be displayed%n", i-1);
if (i == 4) {
i = 0;
}
You can simplify your code as Andreas_D mentioned.
Your current design will block the main thread as you call Thread.sleep() to it, this will freeze your application.
If you would like to update the Image, you should implement the update code inside run() method.
So if you detect the user press on submit JButton, create and start the new Thread for updating UI.