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;
}
}
Related
I want to update jpanel in clients GUIs from the value I get from server continuously. But I can't see anything on the window.I can see the client frame if I set only one time but I need something like
while( get data from server != null){
//update the jpanel
// I tried
frame.panel.setValue(data from server)
frame.repaint()
//and this one too
frame.remove(old panel)
frame.add(new JCustomPanel(with new value))
frame.repaint()
}
I can't share my code because my teacher prohibited it but I can't solve either.Please help me :c
You need to do two things. First of all you need to create another thread, that will work with data independently from the main thread. Secondly, you need to use SwingUtilities.invokeLater method. This is a full example, that shows how it works. It changes label every second (Thread.sleep is used for delay in this example, don't use it in your real code).
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class NewMain {
// JFrame
static JFrame frame;
// JLabel
static JLabel label;
// Main class
public static void main(String[] args) {
frame = new JFrame("panel");
label = new JLabel();
// Creating a panel to add label
JPanel panel = new JPanel();
panel.add(label);
// Adding panel to frame
frame.add(panel);
// Setting the size of frame
frame.setSize(300, 300);
frame.show();
doLoop();
}
private static void doLoop() {
Thread newThread = new Thread(() -> {
while (true) {
SwingUtilities.invokeLater(() -> {
label.setText(new java.util.Date().toString());
});
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(NewMain.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
newThread.start();
}
}
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.
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));
}
}
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).
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