I have a maybe simple question. Obviously because my program does not do what it is supposed to...
First of all my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
public class Timer
extends JFrame
implements ActionListener
{
protected class TThread extends Thread{
private boolean running = false;
#Override
public void run() {
int timer = 10,
index = 0;
running = true;
while(running){
try {
out.setText(timer + " Secs");
timer--;
if(timer == 0){
if(index % 2 == 0){
timer = ti1;
out.setBackground(Color.red);
}else{
timer = ti2;
out.setBackground(Color.green);
}
index++;
}
sleep(1000L);
} catch (InterruptedException e) {
}
}
}
#Override
public void interrupt() {
running = false;
}
}
private static final long serialVersionUID = 1L;
private JTextField t1 = new JTextField(),
t2 = new JTextField();
private int ti1 = 0, ti2 = 0;
private JLabel l1 = new JLabel("Zeit 1"),
l2 = new JLabel("Zeit 2"),
out = new JLabel("00 Secs", SwingConstants.CENTER);
private JButton go = new JButton("Go"),
stop = new JButton("Stop");
private JPanel cont = new JPanel();
private TThread tr = new TThread();
public Timer() {
super("Timer");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(800, 600);
setLayout(null);
add(cont);
cont.setBounds(0, 0, getWidth(), 200);
cont.setLayout(new GridLayout(3, 2));
cont.add(l1);
cont.add(t1);
cont.add(l2);
cont.add(t2);
cont.add(go);
go.addActionListener(this);
cont.add(stop);
stop.addActionListener(this);
add(out);
out.setBounds(0, 200, getWidth(), getHeight()-200);
out.setFont(new Font("Arial", Font.BOLD, 72));
try {
UIManager.setLookAndFeel(new NimbusLookAndFeel());
SwingUtilities.updateComponentTreeUI(this);
} catch (UnsupportedLookAndFeelException e) {
}
}
public static void main(String[] args) {
Timer t = new Timer();
t.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(go)){
ti1 = Integer.parseInt(t1.getText());
ti2 = Integer.parseInt(t2.getText());
tr.run();
}else if(e.getSource().equals(stop)){
tr.interrupt();
}
}
}
So back to my problem:
If I run the program and hit the 'Go'-Button after entering some numbers the program gets stuck. I think the issue is caused by the while-loop in TThread.
It's quite a time since I last used Threads and now i searched for a long time and nothing worked for me...
Hopefully someone can tell me what the issue is and can give a solution or some hints how to solve the problem.
Greetings
max.
You never run the thread in a background thread by calling start() on it. Instead you call run() which runs it on the current thread and not in a background thread. To solve this, call start() on your Thread object, not run().
so not:
tr.run();
but rather:
tr.start();
Other issues:
You're generally better off having classes implement Runnable rather than extend Thread.
You're trying to make Swing calls from a background thread which shouldn't be done. Almost all Swing method calls should be done on the Swing event thread or EDT. For more on this, please see: Concurrency in Swing.
One of your class names, Timer, is the same as a critical Swing class, the javax.swing.Timer class. I'd rename your class to avoid confusion, especially if you would want to use a Swing Timer.
And in fact on view of your code, I think that you would be much better off using a Swing Timer instead of a background Thread for your application. This is a much easier way to do your timer animation and makes it much easier to ensure that Swing calls are made on the Swing event thread. Please check the Swing Timer tutorial.
You are using null layout. While to a newbie a null layout often appears to be the best way to easily create complex GUI's, as you gain more Swing GUI experience you will find that actually the opposite is true. If you use layout managers, often nesting JPanels, each with its own layout manager, you can more easily create complex, beautiful GUI's, that easily resize, that look good on all platforms, and that are much easier to maintain, debug and enhance.
Related
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.
So, I am trying to figure out how to add pauses in my card game to make the CPU act like they are taking turns. But, there's too much code involved, so I think if we demonstrate the strategy on this code, it could work.
How can I make this code pause for, say 5 seconds after the button has been pushed and THEN print the message.
public class TimerTest extends JFrame implements ActionListener{
private static final long serialVersionUID = 7416567620110237028L;
JTextArea area;
Timer timer;
int count; // Counts the number of sendings done by the timer
boolean running; // Indicates if the timer is started (true) or stopped (false)
public TimerTest() {
super("Test");
setBounds(30,30,500,500);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(null);
area = new JTextArea();
area.setBounds(0, 0, 500, 400);
add(area);
JButton button = new JButton("Click Me!");
button.addActionListener(this);
button.setBounds(200, 400, 100, 40);
add(button);
// Initialization of the timer. 1 second delay and this class as ActionListener
timer = new Timer(5000, this);
timer.setRepeats(true); // Send events until someone stops it
count = 0; // in the beginning, 0 events sended by timer
running = false;
System.out.println(timer.isRepeats());
setVisible(true); // Shows the frame
}
public void actionPerformed(ActionEvent e) {
if (! running) {
timer.start();
running = true;
}
// Writing the current time and increasing the cont times
area.append(Calendar.getInstance().getTime().toString()+"\n");
count++;
if (count == 10) {
timer.stop();
count = 0;
running = false;
}
}
public static void main(String[] args) {
// Executing the frame with its Timer
new TimerTest();
}
}
What I really would like to do is to be able to insert a pause into my method that runs the actions of each CPU. But it sounds like, with the Timer class, you need to perform your post-pause actions in the Timer's action listener. I am mostly confused about why the Timer needs an actionListener. I don't need the timer to repeat itself after it is done.
If it would be better for me to post my own code, let me know. But I am not sure what parts would be useful, since I don't want to have tons of code in this thread.
Conceptually, you want to trigger some event after 5 seconds, Swing Timer is perfect for this AND it's thread safe, making it safe to update the UI from, without the need for more hoop jumping.
The Timer sets up it's own thread to wait in, so it won't block the EDT, this means that you need to wait till the timer is triggered before you can something, it won't block and wait where you call it, this is the reason it has an ActionListener, so you know when it's triggered.
You can use your existing Timer, assuming it's not doing anything else, but for argument sake, I'm creating a new one...
private Timer waitItOut;
private JButton button;
Then in your constructor, you set up the timer, the difference here is I've made it non-repeating, meaning it won't trigger every 5 seconds, but you can re-run it when you want...
waitItOut = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
doImportantWork();
}
});
waitItOut.setRepeats(false);
// I made this an instance field for demonstration purposes
button = new JButton("Click Me!");
button.addActionListener(this);
button.setBounds(200, 400, 100, 40);
add(button);
Then in your actionPerformed method, you simply start the timer...
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
waitItOut.restart();
}
And wait till it calls your "important method which should be run after the specified delay"...
public void doImportantWork() {
button.setEnabled(false);
// Writing the current time and increasing the cont times
area.append(Calendar.getInstance().getTime().toString()+"\n");
count++;
if (count == 10) {
count = 0;
running = false;
}
}
You should launch a new thread which does the following:
Sleep for 5 seconds
Sets your required message
Calls repaint() to signal that the message needs to be redrawn.
On a action performed situation, you can initiate certain functions like:
TimeUnit.SECONDS.sleep(1);
to do this. I see in your code that you are trying to manually count for time. You can let the compiler or cpu do this for you.
Hello guys I am doing a thread to update a ball over JFrame so I repaint the screen... and then paint the ball update its position .. and then draw the screen again ... draw the ball and the same cycle ... here is the code
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
Thread t = new Thread()
{
public void run()
{
while(true)
{
repaint();
b2.update(ob,2);
b2.paint(ob.getGraphics());
b2.setT(b2.getT() + 1);
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
System.out.println("Error in Sleeping");
}
}
}
};
t.start();
}
but the problem is that I don't see the ball... the paint of the screen always overrides the ball and the ball is like down under the Jframe ..
If you want to have animations in Swing, the recommended class to use is the javax.swing.Timer . This class allows you to perform operations on the Event Dispatch Thread at regular intervals.
The Swing Timer tutorial
An animation example posted here on SO (which is linked in the Swing wiki here on SO btw)
Some General Rules
Swing is not thread safe, you should only ever update UI components from within the context of the Event Dispatching Thread.
You do not control the paint process, the repaint manager does. You can request updates to occur by calling repaint, but you should never call update and paint directly when trying to update the display.
The Graphics context used by the paint sub system is a shared resource and is not guaranteed to be the same between paint cycles, you should never maintain a reference to it. You should also not rely on the results from JComponent#getGraphics this method is capable of returning null.
An Example Solution
You have a number of options, depending on what you want to ultimately achieve.
You could use a SwingWorker, but given the fact that all your going to is enter an infinite loop and it would easier to use SwingUtilities#invokeLater then actually use the publish method, this approach would actually be more work.
You could also use a Thread, but you'd end up with the same problems as using a SwingWorker
The simpliset solution, for what you're presented, is actually a javax.swing.Timer
public class Blinky {
public static void main(String[] args) {
new Blinky();
}
public Blinky() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BlinkyPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected class BlinkyPane extends JPanel {
private JLabel blinkyLabel;
private boolean blink = false;
public BlinkyPane() {
setLayout(new GridBagLayout());
blinkyLabel = new JLabel("I'm blinking here");
blinkyLabel.setBackground(Color.RED);
add(blinkyLabel);
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
blink = !blink;
if (blink) {
blinkyLabel.setForeground(Color.YELLOW);
} else {
blinkyLabel.setForeground(Color.BLACK);
}
blinkyLabel.setOpaque(blink);
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
}
}
You can take a look at Swing Timer and Concurrency in Swing for more info
If you access GUI components outside the EDT (Event Dispatch Thread) then you might encounter strange problems, Contrary if you perform long running tasks in the EDT then also you will get problems.
Check this post for more info on GUI Threading in Java
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 am animating a series of images in java. So far I able to animate without any problem. The problem occur only when I add in control(Start, Stop, etc..). When I press Start in my GUI the GUI does not show the animation only the last frame is shown after the animation has ended.
I not sure is it the threading problem or a painting problem, as I have tried a few methods and none of them worked.
Following is my code:
public class SwingAnimation extends JPanel implements ActionListener {
protected JFrame frame;
protected JLabel lblDisplay;
protected JButton BtnStart, BtnStop, BtnPause;
protected JCheckBox chkLoop;
protected Thread th;
public static void main(String[] args) {
SwingAnimation sa = new SwingAnimation();
}
public SwingAnimation() {
frame = new JFrame("Animation");
Panel panel = new Panel();
lblDisplay = new JLabel();
BtnStart = new JButton("Start");
BtnStop = new JButton("Stop");
BtnPause = new JButton("Pause");
chkLoop = new JCheckBox("Loop");
BtnStop.setEnabled(false);
BtnPause.setEnabled(false);
BtnStart.setActionCommand("start");
BtnStop.setActionCommand("stop");
BtnPause.setActionCommand("pause");
panel.add(lblDisplay);
panel.add(BtnStart);
panel.add(BtnPause);
panel.add(BtnStop);
panel.add(chkLoop);
frame.add(panel, BorderLayout.CENTER);
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//set the frame in the center of the screen
Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
int screen_x = (screensize.width - frame.getWidth()) / 2;
int screen_y = (screensize.height - frame.getHeight()) / 2;
frame.setLocation(screen_x, screen_y);
BtnStart.addActionListener(this);
BtnStop.addActionListener(this);
BtnPause.addActionListener(this);
chkLoop.addActionListener(this);
th = new Thread();
//ImageAnimator();
}
public void ImageAnimator() {
try {
for (int i = 0; i <= 299; i++) {
ImageIcon images = new ImageIcon("C:\\Users\\Desktop\\Images\\Snap" + i + ".jpg");
lblDisplay.setIcon(images);
th.sleep(25);
}
} catch (InterruptedException e) {
}
}
public void actionPerformed(ActionEvent e) {
if ("start".equals(e.getActionCommand())) {
BtnStart.setEnabled(false);
BtnStop.setEnabled(true);
BtnPause.setEnabled(true);
lblDisplay.setVisible(true);
ImageAnimator();
} else if ("stop".equals(e.getActionCommand())) {
BtnStart.setText("Start");
BtnStart.setEnabled(true);
BtnStop.setEnabled(false);
BtnPause.setEnabled(false);
lblDisplay.setVisible(false);
th = null;
} else if ("pause".equals(e.getActionCommand())) {
BtnStart.setText("Resume");
BtnStart.setEnabled(true);
BtnStop.setEnabled(true);
BtnPause.setEnabled(false);
}
}
}
Quick Fix:
Change ImageAnimator() like this:
lblDisplay.setIcon(images);
lblDisplay.paintImmediately(getBounds());
th.sleep(25);
The problem you're having is that setting an icon does not automatically cause Swing to repaint the component onscreen. Calling paintImmediately will do that.
General advice:
Animation in swing is normally done using the Swing Timer. You'll notice at the moment that your UI is unresponsive - once you've started the animation, you won't be able to stop it until its over. That's because all Swing events happen on a single thread - the Event Dispatch Thread. Running a loop with Thread.sleep(...) in the middle ties this thread up, leaving it unavailable to process other input (such as pressing the stop button)
This article helped me immeasurably when I was trying to understand how Swing handles concurrency, and The Swing Trail has lots of advice on using Swing effectively, painting custom components etc.
I'd also plug the demo code on Filthy Rich Clients, I'm working through the book at the moment and it's worth a look.
This Java 2D games tutorial covers several basic animation techniques. Some related example that illustrate the techniques are cited here, and this complete example illustrates stop and start buttons.