jLabel not changing text and jProgressbar not updating - java

I have a JFrame. in the JFrame is a JDesktopPane, JComboBox, several JLabels, and a JProgressBar. I am facing two challenges:
I want to update/change the text in one of the JLabels in the JFrame by clicking a JButton that is on a JInternalFrame (the button does some calculations).
Upon clicking a JButton that is on another JInternalFrame (the button performs a small task), I want to use the JProgressBar (progressbar is in JFrame) to show the progress of work done.
I use SwingUtilities.invokelater() to perform the tasks done by the buttons.
am using NetBeans as my IDE.

Hard to know what is happening without code, but probably the Event Dispatch Thread (EDT) is being blocked (or terminated?), e.g. by code being called by invokeLater. The EDT is used to update the GUI and should not be used for (slow) non-GUI related calculations. See tutorial The Event Dispatch Thread and subsequent for more details.
Example (without blocking):
import java.awt.*;
import java.awt.event.ActionEvent;
import java.time.LocalTime;
import javax.swing.*;
public class LabelProgress {
public static void main(String[] args) {
LabelProgress main = new LabelProgress();
SwingUtilities.invokeLater(() -> main.showInternal1());
SwingUtilities.invokeLater(() -> main.showInternal2());
}
private JFrame frame;
private JLabel label;
private JDesktopPane desktop;
private JProgressBar bar;
private int progress = 0;
private LabelProgress() {
label = new JLabel("Label: ");
desktop = new JDesktopPane();
bar = new JProgressBar(0, 100);
bar.setStringPainted(true);
frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.add(label, BorderLayout.BEFORE_FIRST_LINE);
frame.add(desktop, BorderLayout.CENTER);
frame.add(bar, BorderLayout.AFTER_LAST_LINE);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(600, 400);
frame.validate();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void showInternal1() {
JButton change = new JButton("Change");
change.addActionListener(this::doChange);
JInternalFrame internal = new JInternalFrame("Change Label");
internal.setLayout(new FlowLayout());
internal.add(change);
internal.setBounds(20, 20, 200, 150);
internal.setVisible(true);
desktop.add(internal);
}
private void showInternal2() {
JButton task = new JButton("Task");
task.addActionListener(this::doTask);
JInternalFrame internal = new JInternalFrame("Small Task");
internal.setLayout(new FlowLayout());
internal.add(task);
internal.setBounds(150, 100, 200, 150);
internal.setVisible(true);
desktop.add(internal);
}
private void doChange(ActionEvent ev) {
// using a SwingWorker:
// for demonstration I used an anonymous class, maybe a own class is better
SwingWorker<LocalTime , Void> worker = new SwingWorker<LocalTime , Void>() {
#Override
protected LocalTime doInBackground() throws Exception {
// not executed on the EDT - just get the current time
LocalTime someCalculation = LocalTime.now();
return someCalculation;
}
#Override
protected void done() {
// executed on EDT
try {
LocalTime resultOfSomeCalculation = get();
label.setText("Label: " + resultOfSomeCalculation.toString());
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
worker.execute();
}
private void doTask(ActionEvent ev) {
// no need to use SwingWorker
Thread thread = new Thread(this::slowTask);
thread.start();
}
private void slowTask() {
// not really that slow, just for demonstration
progress += 10;
if (progress > 100) progress = 100;
// and now switching to the EDT
SwingUtilities.invokeLater(() -> bar.setValue(progress));
}
}

Related

How to refresh the Label one by one?

public class Test {
public static void main(String[] args) {
Methode met = new Methode();
JFrame f = new JFrame("Label Example");
JLabel l1;
JButton btn;
l1 = new JLabel("Start", SwingConstants.CENTER);
btn = new JButton("Bestätigen");
JPanel panel = new JPanel();
String comboBoxListe[] = { "1", "2", "3", "4", "5" }; // 1=300 2=250 3=200 4=150 5=100
JComboBox bundeslandAuswahl = new JComboBox(comboBoxListe);
panel.add(bundeslandAuswahl);
l1.setBounds(0, 0, 1800, 800);
l1.setFont(new Font("Serif", Font.PLAIN, 100));
btn.setBounds(800, 0, 100, 50);
panel.setBounds(900, 0, 100, 100);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
met.ausgabe(l1);
}
});
f.add(btn);
f.add(l1);
f.add(panel);
f.setLayout(null);
f.setVisible(true);
f.setLocationRelativeTo(null);
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
Timer t = new Timer();
}}
class Methode {
void ausgabe(JLabel l1) {
String temp = "";
String[] arr2 = { "Hallo", "World", "!" };
for (int i = 0; i < arr2.length; i++) {
temp = arr2[i];
l1.setText(temp);
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The Label only shows the last element of the Array, if i use the ActionListener. Without the btn.addActionListener its work. How can i solve it with a button? I want that when I click the method ausgabe, that the code displays step by step the individual array fields in the label.
Thanks
That happens because you have to repaint the component(l1). When you use graphic user interface in a single thread program, your program will run into a while block within the GUI code. So when the part of the code that is being executed is not yours then the program is working in GUI staff like repainting the window or graphic components.
You have to call a method in l1 to repaint it. That's the reason because the last text is the only showed. In that way a GUI code will be executed and your interface will be updated. If you do that your problem will be solved. Use the java documentation to find this method.
Start by looking at Concurrency in Swing for the reasons why this approach won't work and then How to Use Swing Timers for the solution.
The "core" issue is, Swing is single threaded and not thread safe. This means that when you call ausgabe from the ActionListener, you're trying to run a long running/blocking process within the context of the Event Dispatching Thread.
But, until the method exists, the EDT can not process any new paint or other events, so nothing gets updated until it's completed.
Because Swing is also not thread safe, it's not advisable to use a Thread to try and fix the issue. Instead, you should make use of a Swing Timer, which acts like a pseudo loop, but which is called back within the EDT, making it safe to use to update the UI.
For example...
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private Timer timer;
private String text = "Hello World";
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel("");
add(label);
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
timer = null;
}
timer = new Timer(1000, new ActionListener() {
private int counter;
#Override
public void actionPerformed(ActionEvent evt) {
if (counter >= text.length()) {
timer.stop();
timer = null;
}
label.setText(text.substring(0, counter));
counter += 1;
}
});
timer.setInitialDelay(1000);
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
timer = null;
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
You should also take the time to learn how to use the various layout managers, it will save you a lot of time, hassle and head scratching.
See Laying Out Components Within a Container for more details.
One trick I might have used, would have been to use two labels. One with the full text set on it and with it's text color set the same color as the background of the panel, the second used to animate the update. Each positioned so that they will overlap each other.
This way, you provide enough information to the layout manager to make determinations about how much space the component needs.
In the above example, I just cheated and overrode getPreferredSize, again, this could have just calculated the final size of the label, but that's becoming complicated.

How can one put results of 5 threads into Label text?

I have following code for making GridLayout. There are 5 labels.
I need to make 5 threads of CoinFlip and set text of labels as results.
I have no idea where to start, sorry about not being specific.
Thanks in advance!
public class GrLayout {
JFrame frame = new JFrame("Coin Flip Results");
JPanel panel = new JPanel();
JLabel lbl1 = new JLabel("1");
JLabel lbl2 = new JLabel("2");
JLabel lbl3 = new JLabel("3");
JLabel lbl4 = new JLabel("4");
JLabel lbl5 = new JLabel("5");
public GrLayout() {
panel.setLayout(new GridLayout(1,5));
panel.add(lbl1);
panel.add(lbl2);
panel.add(lbl3);
panel.add(lbl4);
panel.add(lbl5);
frame.add(panel);
frame.setSize(500, 100);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
new GrLayout();
}
});
}
}
This is just a simple example how you COULD implement this:
code that needs to be run in a separate thread should be encapsulated in a class that implements interface java.lang.Runnable, inside it's public void run()method. So in your case, that's were the "complex" coinflipping logic comes - complex, computing intense stuff should never happen on Swings so called Event Dispatch Thread (EDT) because this may freeze your GUI and doesn't feel smooth ;-)
next important part: call UI related operations ON the Event Dispatch Thread... this can be achieved by calling SwingUtilities.invokeLater()
To keep things simple and short in this example I created a subclass of javax.swing.JLabel and let it implement the java.lang.Runnable interface. In the CoinFlipApp class I got an ExecutorService instance - think of it as a threadpool. To get things executed in separate threads by this threadpool you have to pass it a Runnable which will then be scheduled for execution. This happens by calling executorService.submit(coinFlip);
import javax.swing.SwingUtilities;
public class CoinFlipApp extends JFrame {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
LayoutManager layoutManager = new BoxLayout(f.getContentPane(), BoxLayout.LINE_AXIS);
f.getContentPane().setLayout(layoutManager);
f.setDefaultCloseOperation(EXIT_ON_CLOSE);
f.setVisible(true);
for (int i=0, j=5; i<j; i++) {
CoinFlip coinFlip = new CoinFlip();
f.getContentPane().add(coinFlip);
executorService.submit(coinFlip);
}
});
}
}
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class CoinFlip extends JLabel implements Runnable {
#Override
public void run() {
final String value = Boolean.toString(randomBoolean()).toUpperCase();
SwingUtilities.invokeLater(() -> {
CoinFlip.this.setText(value);
CoinFlip.this.repaint();
});
}
private boolean randomBoolean() {
try {
// this can be removed later
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Math.random() < 0.5;
}
}

JFrame is inactive during Thread.sleep(int) [duplicate]

This question already has answers here:
Java swing GUI freezes
(5 answers)
Closed 7 years ago.
I have some Java classes that should simulate an old calculating machine. One of them is a JFrame, called GUI, with a JTabbedPane. The calculating process should take some time, so I have included Thread.sleep(int). The problem is that while the calculating function is working, the tabs of the JTabbedPane in my GUI can't be selected.
Here is some code:
import java.util.*;
import java.awt.*;
public class Engine
{
private GUI gui;
private Store store;
private Mill mill;
public static void main(String[] args) {
Engine e = new Engine();
}
public Engine() {
gui = new GUI(this);
mill = new Mill(this);
store = new Store(this);
store.draw(); // affects GUI elements
}
public void read(String operations) {
clearStatus(); // affects GUI elements
try {
String[] op = operations.split("\n");
for (int i = 0; i < op.length; i++) {
setStatus(op[i]); // affects GUI elements
if (op[i].length() == 0) { // empty line
continue;
}
int number = Integer.parseInt(op[i]);
store.addNumber(number);
Thread.sleep(200);
}
store.draw(); // affects GUI elements
} catch(Exception e) {
System.out.println(e);
}
}
public void clearStatus() {
gui.statusLabel.setText("");
}
public void setStatus(String msg) {
gui.statusLabel.setText(msg);
}
}
Here's my GUI (this is a short version):
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GUI extends JFrame
{
private Engine engine;
// Swing
JTabbedPane tabs;
JPanel mill;
JTextArea input, store;
JLabel statusLabel;
JButton sendInput;
public GUI(Engine e)
{
engine = e;
input = new JTextArea();
sendInput = new JButton("Run");
sendInput.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
pressInputButton(evt);
}
});
JPanel inputPanel = new JPanel();
inputPanel.add(input, BorderLayout.CENTER);
inputPanel.add(sendInput, BorderLayout.SOUTH);
mill = new JPanel();
statusLabel = new JLabel();
mill.add(statusLabel);
store = new JTextArea();
JTextPane helpPane = new JTextPane();
helpPane.setText("[...]");
tabs = new JTabbedPane();
tabs.addTab("Input", inputPanel);
tabs.addTab("Mill", mill);
tabs.addTab("Store", new JScrollPane(store));
tabs.addTab("Help", helpPanel);
add(tabs, BorderLayout.CENTER);
setVisible(true);
pack();
}
public void pressInputButton(ActionEvent evt) {
engine.read(input.getText());
}
}
What #sorifiend means is, calling sleep() from an event handler will "lock up" your application's event dispatch thread. The application will not respond to any other user input until the event handler function returns.
If you want the application to be responsive while the calculator is "working", then what you need to do is:
Record that the state of the calculator is "working"
Disable whatever buttons you don't want the user to press while the calculator is "working", --OR-- change the handlers for those buttons to do something different (simulate jamming the machine?) if they are pressed while in the "working" state.
Submit a timer event for some time in the near future (however long the calculation is supposed to take.)
In the handler for the timer event, display the result of the calculation, re-enable the disabled buttons, and change the state from "working" back to "idle" (or whatever).

Make a swing thread that show a "Please Wait" JDialog

The problem is this:
I've a swing application running, at a certain point a dialog requires to insert username and password and to press "ok".
I would like that when the user press "ok" the swing application does in this order:
Open a "Please wait" JDialog
Make some operation(eventually displaying some other JDialog or JOptionPane)
When it finishes with the operation close the "please wait" JDialog
This is the code that I wrote in the okButtonActionPerformed():
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {
//This class simply extends a JDialog and contains an image and a jlabel (Please wait)
final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false);
waitDialog.setVisible(true);
... //Do some operation (eventually show other JDialogs or JOptionPanes)
waitDialog.dispose()
}
This code obviously doesn't works because when I call the waitDialog in the same thread it blocks all till I don't close it.
So I tried to run it in a different thread:
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {
//This class simply extends a JDialog and contains an image and a jlabel (Please wait)
final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
waitDialog.setVisible(true);
}
});
... //Do some operation (eventually show other JDialogs or JOptionPanes)
waitDialog.dispose()
}
But also this doesn't work because the waitDialog is not displayed immediately but only after that the operation completed their work (when they show a joption pane "You are logged in as...")
I also tried to use invokeAndWait instead of invokeLater but in this case it throws an exception:
Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
How can I do?
Consider using a SwingWorker to do your background work, and then closing the dialog either in the SwingWorker's done() method or (my preference) in a PropertyChangeListener that is added to the SwingWorker.
e.g.,
import java.awt.BorderLayout;
import java.awt.Dialog.ModalityType;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class PleaseWaitEg {
public static void main(String[] args) {
JButton showWaitBtn = new JButton(new ShowWaitAction("Show Wait Dialog"));
JPanel panel = new JPanel();
panel.add(showWaitBtn);
JFrame frame = new JFrame("Frame");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class ShowWaitAction extends AbstractAction {
protected static final long SLEEP_TIME = 3 * 1000;
public ShowWaitAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent evt) {
SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>(){
#Override
protected Void doInBackground() throws Exception {
// mimic some long-running process here...
Thread.sleep(SLEEP_TIME);
return null;
}
};
Window win = SwingUtilities.getWindowAncestor((AbstractButton)evt.getSource());
final JDialog dialog = new JDialog(win, "Dialog", ModalityType.APPLICATION_MODAL);
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state")) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
dialog.dispose();
}
}
}
});
mySwingWorker.execute();
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
JPanel panel = new JPanel(new BorderLayout());
panel.add(progressBar, BorderLayout.CENTER);
panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
dialog.add(panel);
dialog.pack();
dialog.setLocationRelativeTo(win);
dialog.setVisible(true);
}
}
Notes:
A key concept is to set everything up, add the PropertyChangeListener, get the SwingWorker running, all before displaying the modal dialog, because once the modal dialog is shown, all code flow from the calling code is frozen (as you've found out).
Why do I prefer the PropertyChangeListener to using the done method (as Elias demonstrates in his decent answer here, which I've up-voted) -- using the listener provides more separation of concerns, looser coupling. This way the SwingWorker has to know nothing of the GUI code that is using it.
public void okButtonActionPerformed(ActionEvent e) {
final JDialog loading = new JDialog(parentComponent);
JPanel p1 = new JPanel(new BorderLayout());
p1.add(new JLabel("Please wait..."), BorderLayout.CENTER);
loading.setUndecorated(true);
loading.getContentPane().add(p1);
loading.pack();
loading.setLocationRelativeTo(parentComponent);
loading.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
loading.setModal(true);
SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
#Override
protected String doInBackground() throws InterruptedException
/** Execute some operation */
}
#Override
protected void done() {
loading.dispose();
}
};
worker.execute();
loading.setVisible(true);
try {
worker.get();
} catch (Exception e1) {
e1.printStackTrace();
}
}
A variation of the above answer
It's an easy and replicable way to do...
//This code goes inside your button action
DialogWait wait = new DialogWait();
SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
//Here you put your long-running process...
wait.close();
return null;
}
};
mySwingWorker.execute();
wait.makeWait("Test", evt);
//end
//Create this class on your project
class DialogWait {
private JDialog dialog;
public void makeWait(String msg, ActionEvent evt) {
Window win = SwingUtilities.getWindowAncestor((AbstractButton) evt.getSource());
dialog = new JDialog(win, msg, Dialog.ModalityType.APPLICATION_MODAL);
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
JPanel panel = new JPanel(new BorderLayout());
panel.add(progressBar, BorderLayout.CENTER);
panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
dialog.add(panel);
dialog.pack();
dialog.setLocationRelativeTo(win);
dialog.setVisible(true);
}
public void close() {
dialog.dispose();
}
}

JProgressBar - Won't work in the class that I am calling it in

I'm making a project and need a progress bar. I've got the class with the Timer and it runs fine when I include a main; but when I try to call it in the mainGUI method, it's all black until it hits 100% then appears.
package microproject.resources;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Timer extends JFrame {
JProgressBar current;
JTextArea out;
JButton find;
Thread runner;
int num = 0;
int length = 0;
public Timer() {
setTitle("Progress");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
length = Integer.parseInt(JOptionPane.showInputDialog(null, "How many seconds:"));
JPanel p = new JPanel(new GridLayout(1,1));
p.setPreferredSize(new Dimension(300,65));
current = new JProgressBar(0, length);
current.setPreferredSize(new Dimension(250,50));
current.setValue(0);
current.setStringPainted(true);
p.add(current);
setVisible(true);
setContentPane(p);
pack();
setVisible(true);
iterate();
}
public void iterate() {
while(num < length +1) {
current.setValue(num);
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
num += 1;
}
}
public static void main(String[] args) {
Timer f = new Timer();
}
}
This is the code for the Timer Class ^
package microproject.resources;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class GUIMain extends JFrame {
public static void main(String []args){
GuiFrame();
}
public static void GuiFrame(){
JFrame frame = new JFrame("Casino Royal3");
frame.setSize(811,577);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(2,1));
frame.setResizable(false);
JPanel PNorth = new JPanel(new FlowLayout(FlowLayout.LEFT,0,0));
JPanel PSouth = new JPanel(new BorderLayout());
//Creating Image for Casino Button
ImageIcon img1 = new ImageIcon("src\\Casino.jpg");
final JButton btn1 = new JButton(img1);
btn1.setPreferredSize(new Dimension(550,274));
btn1.setMargin(new Insets(0,0,0,0));
PNorth.add(btn1, BorderLayout.EAST);
btn1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
btn1.setIcon(new ImageIcon("src\\Casino2.jpg"));
}
});
//Creating Image for Sheridan Label
ImageIcon img2 = new ImageIcon("src\\SHERIDAN_LOGO.jpg");
JButton btn2 = new JButton(img2);
btn2.setMargin(new Insets(0,0,0,0));
PNorth.add(btn2);
btn2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ImageIcon instruc = new ImageIcon("src\\Instructions.jpg");
JLabel instructions = new JLabel(instruc);
JOptionPane.showConfirmDialog(null, instructions, "instructions", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE);
}
});
JPanel timmus = new JPanel(new FlowLayout(FlowLayout.LEFT,0,0));
timmus.setPreferredSize(new Dimension(166, 273));
timmus.setBackground(Color.BLUE);
ImageIcon time = new ImageIcon("src\\Timer.jpg");
JButton timer = new JButton(time);
timer.setMargin(new Insets(0,0,0,0));
timer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Timer f = new Timer();
}
});
timmus.add(timer);
ImageIcon mus = new ImageIcon("src\\music.jpg");
JButton music = new JButton(mus);
music.setMargin(new Insets(0,0,0,0));
timmus.add(music);
JPanel games = new JPanel(new FlowLayout(FlowLayout.LEFT,0,0));
games.setPreferredSize(new Dimension(500,279));
games.setBackground(Color.BLUE);
ImageIcon calculator = new ImageIcon("src\\Calculator.jpg");
JButton calc = new JButton(calculator);
calc.setMargin(new Insets(0,0,0,0));
calc.setPreferredSize(new Dimension(166,273));
games.add(calc);
calc.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Calculator c1 = new Calculator();
}
});
ImageIcon g1 = new ImageIcon("src\\250Hangman.jpg");
JButton game1 = new JButton(g1);
//game1.setBackground(Color.WHITE);
game1.setMargin(new Insets(0,0,0,0));
game1.setPreferredSize(new Dimension(166,273));
games.add(game1);
game1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Hangman h1 = new Hangman();
}
});
ImageIcon g2 = new ImageIcon("src\\Minesweeper.jpg");
JButton game2 = new JButton(g2);
// game2.setBackground(Color.WHITE);
game2.setMargin(new Insets(0,0,0,0));
game2.setPreferredSize(new Dimension(166,273));
games.add(game2);
PSouth.add(timmus, BorderLayout.CENTER);
PSouth.add(games, BorderLayout.EAST);
frame.add(PNorth, BorderLayout.NORTH);
frame.add(PSouth, BorderLayout.SOUTH);
frame.setVisible(true);
frame.pack();
}
}
That's the entire program, the Timer ActionListener is called "timer"
Thanks in advance
Welcome to the wonderful world of blocked Event Dispatching Thread (and violation of the initial thread)
Basically, Swing is a single threaded environment, all updates and modifications to the UI are expected to be executed within the context of the Event Dispatching Thread (AKA EDT).
The EDT is responsible for, amongst other things, processing repaint requests. If, for some reason, you block this thread (for example, using a long running loop or blocking IO), it will prevent the EDT from processing new paint requests, making it appear as if your program has hung...because essentially it has.
The reason you might see a difference between running Timer directly and using it in your GUI is because when the application is started, it will be running within, what is commonly known as, the "main" thread.
When you first create a top level Swing container, the EDT is started (which is a separate thread), meaning that the UI will appear in it's own thread, but the application will continue running in the "main" thread, allowing your iterate method to run independently of the EDT.
However, when you try and run it from within your GUI, it's all running within the context of the EDT, causing it to be blocked.
Start by taking a look at
Concurrency in Swing
Initial Threads
To fix the problem, based on your example code, I would suggest using a SwingWorker. This will allow you to run your "long running task" in a background thread, but provides a number of methods that allow you to resync your updates back to the EDT. This is very important, as you should never attempt to update the UI or change it's state from any thread other then the EDT.
Take a look at Worker Threads and SwingWorker for more details
And if required, some examples...
JProgressBar not updating
JProgressBar too fast
JProgressBar won't update
Progress Bar Java

Categories