My Thread.sleep(rand.nextInt(delay)) command in my ButtonListener class crashes my GUI. Any ideas? The program is supposed to add people to an ArrayList, then randomly select them and display them at a random time between 0 and the timeText JTextField, and it works until I add the sleep command. Any help would be appreciated thanks!
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.awt.*;
import javax.swing.Timer;
public class MyProgram extends AppClass{
protected int x,y,width,height;
protected Color color;
private ArrayList<String> people = new ArrayList<String>();
private static JLabel person;
private Timer timer;
private ButtonListener listener;
private Random rand = new Random();
private JLabel addPeople;
private JTextField newPerson;
private JTextField timeText;
private Font font1 = new Font("Arial",1,17);
private Font font2 = new Font("Arial",1,65);
public MyProgram(){
setPreferredSize(new Dimension(1000,800));
people.add("me");
people.add("john");
people.add("greg");
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(600,400));
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
newPerson = new JTextField(2);
newPerson.setFont(font1);
addPeople = new JLabel("Add people:");
addPeople.setFont(font1);
person = new JLabel();
person.setFont(font2);
JButton addButton = new JButton("Add");
addButton.setFont(font1);
JButton startButton = new JButton("Start");
startButton.setFont(font1);
timeText = new JTextField(2);
timeText.setFont(font1);
JLabel time = new JLabel("Maximum time between draws:");
time.setFont(font1);
listener = new ButtonListener();
addButton.addActionListener(listener);
startButton.addActionListener(listener);
panel.add(addPeople);
panel.add(newPerson);
panel.add(addButton);
panel.add(time);
panel.add(timeText);
panel.add(startButton);
panel.add(person);
add(panel);
}
private class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent ae){
JButton button = (JButton) ae.getSource();
if(button.getText().equals("Add")){
people.add(newPerson.getText());
System.out.println(newPerson.getText());
System.out.println("also worked");
}else if(button.getText().equals("Start")){
int delay = Integer.parseInt(timeText.getText());
for(;;){
person.setText(people.get(rand.nextInt(people.size())));
try{
Thread.sleep(rand.nextInt(delay)); // the problem
}catch(Exception error){
System.out.println("Error");
}
}
}
}
}
}
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.awt.*;
import javax.swing.Timer;
public class AppClass extends JPanel{
public static void main(String [] args){
JFrame frame = new JFrame();
frame.getContentPane().add(new Get());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
frame.setTitle("My Program");
}
}
There is only one thread that modifies the UI. This is by design, because it means that, even with the many sources of events that a UI could receive, it is impossible for concurrency bugs to corrupt the UI state.
There is only one thread that modifies the UI. That includes:
Receiving mouse events.
Receiving keyboard events.
Processing requests to repaint.
Processing UI timers.
Among many others.
If you're in code that modifies the UI, then you must be on the UI thread (else, you have a bug). If you're on the UI thread and you call Sleep(), then the UI thread stops doing things.
It'll stop responding to requests to repaint. It'll stop responding to keyboard events, mouse events, etc.
Instead, you have to use a forms timer to perform an animation. When someone clicks your "Start" button, you would set the first value, save off the rest of the values, and then start your timer, and then let the UI thread keep processing.
Each time the timer fires, you advance the state - you update the UI with the next value you want to show. You continue this until you've shown all values, and then stop the timer and release your state that told you where you were in the animation.
Yeah, you're just setting the text on some component, but this still falls under the animation pattern.
Be careful though - if your UI is closed while the animation timer is still running, you'll try to modify a UI that is gone. So now your UI code has to be careful to stop the animation timer if it's still running when the UI is closed.
actually, you are execute your gui into same thread and when you are use Thread.sleep(delay) you are also sleep you gui. so you have to use new thread for other processing.
Thread t = new Thread(new Runnable(){
#Override
public void run() {
}
});
Related
So I am currently having a problem with my Java Swing timer but first lemme describe what I am trying to do.
So I have a Swing GUI that updates a map with JSON data every 'X' number of seconds. The user can input the number of seconds into a text field and then click a button to start updating the map. The map will then update by querying the JSON based on the input.
So I am using a Swing timer to repeat a certain action event based on the input of the the user. Seen below:
clickOkButton.addActionListener(e1 -> {
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String url = "";
url = new_text.getText();
layer[0] = (RenderableLayer) geo.createLayerFromSource(url);
appFrame.getWwd().getModel().getLayers().set(20, layer[0]);
ltree.getModel().refresh(appFrame.getWwd().getModel().getLayers());
}
};
int time = Integer.parseInt(queryTime.getText());
Timer timer = new Timer(time * 1000, actionListener);
timer.setRepeats(true);
//timer.setDelay(1);
timer.start();
d.setVisible(false);
//System.out.println(text);
});
When the program is launched whatever time the user enters first works great. But then if they change the time the timer doesn't change.
int time = Integer.parseInt(queryTime.getText());
Timer timer = new Timer(time * 1000, actionListener);
It has something to do with these lines but I just can't figure it out. I'm pulling the numerical value from the text field and setting it as the delay in the timer. But it only works the first time the program is launched and not when it is changed.
Any help would be much appreciated.
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SwingTimerDemo extends JPanel {
final static int height = 500;
final static int width = 500;
final static String title = "default title";
JFrame frame = new JFrame(title);
JTextField field = new JTextField(10);
JTextArea area = new JTextArea(50,20);
public static void main(String[] args) {
SwingUtilities.invokeLater(
() -> new SwingTimerDemo().start());
}
public SwingTimerDemo() {
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
// add this panel to the frame
frame.add(this);
// add the JTextArea and JTextField to the panel
add(area);
add(field);
setPreferredSize(
new Dimension(500, 500));
frame.pack();
// center the frame on the screen
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void start() {
// append the string to the JTextArea
Timer t = new Timer(0, (ae)->area.append("Processing...\n"));
// set the inter-event delay to 2 seconds
t.setDelay(2000);
// start the timer
t.start();
field.addActionListener(ae->{
String text = field.getText();
field.setText(""); // "erase" the text
// convert to a number
int delay = Integer.parseInt(text);
// reset the timer delay
t.setDelay(delay*300);
});
}
}
Assuming you are familiar with Frames and Panels I will skip to the JTextField and JTextArea.
the field is where the user types in the delay. It is notified using an actionListener. That input is then retrieved, parsed as an int and sets the timer delay.
the area is simply a place where the timer writes the output.
Note that instead of an event when the user types in information, a button could be used instead. The user types in the information and then clicks the button. In that case, there would be no need for JTextField listener. Instead the listener would be for the button to check the text field.
This is a very rudimentary example to demonstrate the interaction between two actionListeners. If the user types in anything but a number an exception will be thrown. You may want to check out the Java Tutorials where they talk about event handling and other things that you would find interesting.
I have a Java program where I plan to take input from GUI, and use that input later for processing in main(). I am using Eclipse.
I am sending an HW object(called HWObj) to the GUI JFrame, and checking for a boolean field in the object to continue processing in main().
InputWindow is custom object which extends JPanel implements ActionListener
It contains a reference to the current JFrame(parentFrame). On clicking a JButton in InputWindow, I have written a custom ActionListener which sets the value of HWObj.check to true and disposes the parentFrame. This should cause execution to resume in main().
Code for HW class is as below :
import java.awt.*;
import javax.swing.*;
public class HW {
//globals
boolean check;
public HW() {
//initialisations
check = false;
}
public static void main(String args[]) {
final HW problem = new HW();
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Create and set up the window.
JFrame frame = new JFrame("Select folders");
frame.setPreferredSize(new Dimension(640, 480));
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
InputWindow Directories = new InputWindow(problem, frame);
Directories.setOpaque(true);
frame.add(Directories);
//Display the window.
frame.pack();
frame.setVisible(true);
}
});
} catch(Exception e) {
System.out.println("Exception:"+e.getLocalizedMessage());
}
while(!problem.finish);
//Do processing on problem
System.out.println("Done");
}
}
The Actionlistener in the gui is as follows:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class InputWindow extends JPanel
implements ActionListener {
private static final long serialVersionUID = 4228345704162790878L;
HW problem;
JFrame parentFrame;
//more globals
public InputWindow(HW problem, JFrame parentFrame) {
super();
this.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
this.parentFrame = parentFrame;
this.problem = problem;
JButton finishButton = new JButton("Finish");
finishButton.setActionCommand("fin");
finishButton.addActionListener(this);
gbc.gridx = 0;
gbc.gridy = 0;
this.add(finishButton, gbc);
//Initialize buttons and text areas and labels
//Code removed for ease of reading
}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if(command.equals("fin")) {
//Do a lot of stuff, then
this.removeAll();
parentFrame.dispose();
problem.check = true;
}
}
}
I have checked, and the control to this function comes normally on button click.
Now, I would expect it to return to main, and exit the while loop, and continue processing.
This does not happen. The debugger in eclipse shows only the main thread running, and when I try to pause it, I see that the thread is stuck in the while loop. But if I try to step through, it exits the while loop as expected, and continues. However, it gets remains stuck in the while loop until I manually try to debug it.
What is the problem? Why is it not resuming the main thread as expected?
How do I resolve this issue?
Your problem is to do with how the Java memory model works. The loop in your main thread will be checking a stale value of check.
When you enter the debugger, the memory is forced to be updated, so that's why it starts working at that point.
If you mark your variable as volatile, that will force the JVM to ensure that all threads are using the up-to-date value:
volatile boolean check;
You can read more about volatile and the Java memory model in the documentation.
It looks like you're using a JFrame where you should be using a modal JDialog. If you use the modal JDialog for an input window, you will know exactly when it is "finished" since code flow will resume from the calling code from right after when the dialog was set visible.
Either that or if you are trying to swapviews, then use a CardLayout to swap your view, and use an observer type pattern to listen for change of state.
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
I am trying to make a tiny program that has 3 buttons, all of them of white color. Pressing the first button (that has the text "Go!") will cause the second button to become orange for 3 seconds and then, after that time, it will become white again AND the third button will become permanently green.
However, in my following code, I have a problem achieving this: When hitting the button "Go!", it causes my program to somewhat freeze for 3 seconds and then the third button becomes green. Can you please help me?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Example extends JFrame
{
public Example(String title)
{
super(title);
GridLayout gl = new GridLayout(3,1);
setLayout(gl);
final JButton b1 = new JButton("Go!");
final JButton b2 = new JButton();
final JButton b3 = new JButton();
b1.setBackground(Color.WHITE);
b2.setBackground(Color.WHITE);
b3.setBackground(Color.WHITE);
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
b2.setBackground(Color.ORANGE);
try
{
Thread.sleep(3000);
} catch (InterruptedException ie) {}
b2.setBackground(Color.WHITE);
b3.setBackground(Color.GREEN);
}
});
add(b1);
add(b2);
add(b3);
setSize(50,200);
setVisible(true);
}
public static void main(String[] args)
{
Example ex = new Example("My Example");
}
}
Swing is single threaded. Calling Thread.sleep in the EDT prevents UI updates. Use a Swing Timer instead.
You're calling Thread.sleep(3000) on the main thread. Hence why your program freezes for three seconds. As #MarounMaroun suggested, you should use a SwingWorker. Here is the documentation.
For class I'm working on my first GUI application. It's just a simple image viewer with four buttons: Previous, Next, Stop, Play. Previous and Next work fine, but honestly I don't even know how to begin working on the slideshow part (Play & Stop). I know there's a timer class that would probably be handy for controlling the speed as the images change...but I'm not sure what kind of logic is typically used to cycle through the images. Can anyone point me in the right direction, my brain is a little fried at this point :0
I've included my code below. I'm new to this, so hopefully people won't be too critical of my technique. If it matters, I'm working in eclipse.
here's my code so far:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.TimerTask;
public class ImageGallery extends JFrame
{
private ImageIcon myImage1 = new ImageIcon ("Chrysanthemum.jpg");
private ImageIcon myImage2 = new ImageIcon ("Desert.jpg");
private ImageIcon myImage3 = new ImageIcon ("Jellyfish.jpg");
private ImageIcon myImage4 = new ImageIcon ("Penguins.jpg");
JPanel ImageGallery = new JPanel();
private ImageIcon[] myImages = new ImageIcon[4];
private int curImageIndex=0;
public ImageGallery ()
{
ImageGallery.add(new JLabel (myImage1));
myImages[0]=myImage1;
myImages[1]=myImage2;
myImages[2]=myImage3;
myImages[3]=myImage4;
add(ImageGallery, BorderLayout.NORTH);
JButton PREVIOUS = new JButton ("Previous");
JButton PLAY = new JButton ("Play");
JButton STOP = new JButton ("Stop");
JButton NEXT = new JButton ("Next");
JPanel Menu = new JPanel();
Menu.setLayout(new GridLayout(1,4));
Menu.add(PREVIOUS);
Menu.add(PLAY);
Menu.add(STOP);
Menu.add(NEXT);
add(Menu, BorderLayout.SOUTH);
//register listener
PreviousButtonListener PreviousButton = new PreviousButtonListener ();
PlayButtonListener PlayButton = new PlayButtonListener ();
StopButtonListener StopButton = new StopButtonListener ();
NextButtonListener NextButton = new NextButtonListener ();
//add listeners to corresponding componenets
PREVIOUS.addActionListener(PreviousButton);
PLAY.addActionListener(PlayButton);
STOP.addActionListener(StopButton);
NEXT.addActionListener(NextButton);
}
public static void main (String [] args)
{
ImageGallery frame = new ImageGallery();
frame.setSize(490,430);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
}
class PreviousButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(curImageIndex>0 && curImageIndex <= 3)
{ ImageGallery.remove(0);
curImageIndex=curImageIndex-1;
ImageIcon TheImage= myImages[curImageIndex];
ImageGallery.add(new JLabel (TheImage));
ImageGallery.validate();
ImageGallery.repaint();
}
else
{
ImageGallery.remove(0);
ImageGallery.add(new JLabel (myImage1));
curImageIndex=0;
ImageGallery.validate();
ImageGallery.repaint();
}
}
}
class PlayButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
// *need help here*//
}
}
class StopButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
// *need help here*//
}
}
class NextButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(curImageIndex>=0 && curImageIndex < 3)
{ ImageGallery.remove(0);
curImageIndex = curImageIndex + 1;
ImageIcon TheImage= myImages[curImageIndex];
ImageGallery.add(new JLabel (TheImage));
ImageGallery.validate();
ImageGallery.repaint();
}
else
{
ImageGallery.remove(0);
ImageGallery.add(new JLabel (myImage4));
curImageIndex=3;
ImageGallery.validate();
ImageGallery.repaint();
}
}
}
}
Why complicating simple things,
I think that this is job for CardLayout and for slideshow is there Swing Timer
put images as Icon to the JLabel
This example shows a start/stop button that controls a javax.swing.Timer. Instead of replacing the label each time, just update the label's Icon, as suggested by #mKorbel and shown here.
You need use a thread for the slideshow. You can use a flag in the run method for continue with the show or stop if this flag change, for example, a boolean var. One example you can see in http://java.sun.com/developer/technicalArticles/Threads/applet/.
These are some guidelines that might get you started:
First you will need a separate thread to control the changing images. I suggest you write a class that implements TimerTask. Override the run() method in this class. In this run method you should put the functionality to change the current image being displayed (similar to what you did in the next and previous function).
In the actionPerformed() method for the play button you will need to create an instance of a Timer class and start your timer using the scheduleAtFixedRate(TimerTask task, long delay, long period) method (other methods in this class may be used as well, scheduleAtFixedRate() seem more appropriate though).
For the stop you will then need to add enough functionality to stop the running timer using the cancel() method in the Timer class