In Java, let's say I have a GUI with 2 buttons, Go and Pause.
When I press Go, "Hello" gets printed out over and over again. When I press Pause, "Hello" no longer gets printed to the screen.
Example: User presses Go button. "Hello" gets printed out for 1 minute until the user presses "Pause."
What is the proper way to express this approach in Java? Is it equivalent to my commented pseudocode within the goButton source?
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
// while user has not pressed the pause button
printHello();
}
else if(e.getSource() == pauseButton)
{
pause();
}
}
Thanks
In order to get this to work, in reasonable fashion, you will need a Thread. This is executed in the background until such time as you decide to cancel/pause it.
This is an EXTREMELY basic example. Normally I'd wrap the task and the GUI up in appropriate classes rather then accessing static references, but it gives a basic idea
public class TestHello {
private static HelloTask task;
public static void main(String[] args) {
Thread thread = new Thread((task = new HelloTask()));
thread.setDaemon(true);
thread.start();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
JButton goButton = new JButton("Go");
JButton stopButton = new JButton("Stop");
goButton.setActionCommand("Go");
stopButton.setActionCommand("Stop");
ActionHandler handler = new ActionHandler();
goButton.addActionListener(handler);
stopButton.addActionListener(handler);
frame.add(goButton);
frame.add(stopButton);
frame.setVisible(true);
}
public static class ActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Go")) {
task.start();
} else if (e.getActionCommand().equals("Stop")) {
task.pause();
}
}
}
public static class HelloTask implements Runnable {
private static final Object WAIT_LOCK = new Object();
private boolean dump = false;
public void start() {
synchronized (WAIT_LOCK) {
dump = true;
WAIT_LOCK.notify();
}
}
public void pause() {
synchronized (WAIT_LOCK) {
dump = false;
WAIT_LOCK.notify();
}
}
#Override
public void run() {
while (true) {
while (dump) {
System.out.println("Hello");
}
try {
synchronized (WAIT_LOCK) {
WAIT_LOCK.wait();
}
} catch (Exception e) {
}
}
}
}
}
Some further read:
Java Concurrency
Concurrency in Swing
Caveats
NEVER try and modify the GUI from any thread other then the Event Dispatching Thread.
To have responsive UI you would usually have to run printHello() in separate thread. Then as you do processing in this thread, for example, after every print statement, you check some flag boolean isPaused; and stop execution if it is true. When pause button is clicked you set the value of this flag to true.
You need to implement your loop in a separate thread. Otherwise the GUI will become irresponsive and the user might not be able to click the Pause button at all.
With this threaded approach, you also need a flag which indicates whether or not to print out the message. The printing loop can simply stop executing the thread when the flag is set to no longer print.
what about htis:
boolean flag=true;
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
while(true)
{
printHello();
}
}
else if(e.getSource() == pauseButton)
{
pause();
}
}
You can do this in a few ways the simplest being:
You have a boolean flag, keepPrinting and you set it to true when you push the Go button, false when you push the Pause. Next you have a thread somewhere executing a while loop which will print nothing when keepPrinting is false.
The threading here is really important, without it you're going to have your GUI freeze once the user pushes a button as the program prints hello and happily ignores anything else.
Pseudo Code
//GUI
public ThreadedPrinter greeter;
void ButtonGoPushed(args){
greeter.keepPrinting = true;
}
void ButtonPausePushed(args){
greeter.keepPrinting = false;
}
//ThreadedPrinter
boolean keepPrinting
void run(){
while(true){
if(keepPrinting){
print("Hello");
}
sleep(5); //Make sure that this thread yields if the system doesn't do it automatically
}
The good news about java concurrency versus say C++ is that this will just work, you don't have to worry about the boolean being crazy and inconsistent because in java variable sets are atomic. If you want to do more than just set the variable, make a synchronized method that sets the variable and does anything else you want.
Basically to keep UI responsive such task need to be performed in other thread.
There can be various ways in which you can implement this mechanism in java.
I have used simple mechanism of Runnalbe and volatile flag which ensure that thread exists when you call cancelPrint() method
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
//start the thread here
}
else if(e.getSource() == pauseButton)
{
//call cancel here
}
}
public class HelloPrinter implements Runnable {
volatile boolean cancel = false;
#Override
public void run() {
while (!cancel) {
printHello();
}
}
public void cancelPrint() {
cancel = true;
}
}
I assume you want to do more than just printouts. Take a look at Swing Worker.
It allows you to pretty easily write your GUI-related code that gets executed in the AWT Event Thread and your long-executing code in other thread(s) and pass values back and forth. This will help prevent any GUI lockup issues you might experience.
Related
boolean flag = true;
public void run(){
// some code
loginButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
flag = false;
// Some code
}
});
while(flag){}
}
I am using while loop till the actionListener is invoked which I think is silly perhaps. Please let me know if this can be done in more efficient way.
You dont need a Thread for the Listener itself, the Java UI Thread calls your Listener for you.
However if you want your response to the event non-blocking do the following:
// no need for a second thread here
loginButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// If you dont want to block the UI Thread make a new Thread here
new Thread(YOUR_RUNNABLE).start();
}
}
Related Question
I am trying to make a simple class where the user can change the value of the string test by clicking the button and then return the changed string.
public class TestTest
{
private JFrame mainFrame;
private JPanel panelX;
private String test;
public static void main(String[] args)
{
TestTest run = new TestTest();
run.GUIinit();
run.addButton();
System.out.println(run.returnData()); // This returns null
}
// Method to return string value
public String returnData()
{
return test;
}
// Method to set string value
public void setData(String data)
{
test = data;
}
private void GUIinit()
{
mainFrame = new JFrame("Text");
mainFrame.setSize(200, 200);
mainFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEvent)
{
System.exit(0);
}
});
panelX = new JPanel();
mainFrame.add(panelX);
mainFrame.setVisible(true);
}
// Problematic part
private void addButton()
{
JButton testButton = new JButton("Text");
panelX.add(testButton);
testButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
setData("STRING BLABLA");
}
});
}
}
I know that I am doing something horribly wrong but I cannot grasp what,
my only guess is that the code skips to the println part without waiting for the actionEvent.
My question is: Am I assigning a value to the string with the actionEvent when I click the button and if not, how could I do that?
Could I implement some sort of delay or another class to get the result I want?
To answer your question - yes, you are assigning the value to the variable "test", but the main thread doesn't wait for you to click the button. That's why it prints null. You could make the main thread wait until the value is set, but that is a wrong way to use Swing. There are three kinds of threads in a Swing program:
Initial thread, the thread that executes initial application code. In your case this is the thread that runs the main() method.
Event dispatch thread (EDT), the thread where all event-handling code is executed. All program logic (unless it is time consuming) should be executed on this thread. In your case, whatever you need to do with the "test" variable, you should probably do it on this thread. Since action events are processed on this thread, you should do it in the actionPerformed() method of the listener.
Worker threads. If the work is time consuming, you should create a new thread and do the work on it. Even so, you should start it from EDT (event dispatch thread), not the main (initial) thread.
You can read more here: Lesson: Concurrency in Swing
If, however, for some unlikely reason you really need to access the "test" variable from the main (initial) thread, you can implement a waiting mechanism with Lock and Condition like so:
public class TestTest
{
private JFrame mainFrame;
private JPanel panelX;
private String test;
private static final Lock lock = new ReentrantLock();
private static final Condition valueAssigned = lock.newCondition();
public static void main(String[] args) throws InterruptedException
{
TestTest run = new TestTest();
run.GUIinit();
run.addButton();
// Acquire the lock.
lock.lock();
try {
while (run.returnData() == null) {
// Release the lock and wait for signal.
valueAssigned.await();
}
// Once String value is set, print it.
System.out.println(run.returnData());
} finally {
lock.unlock();
}
}
// Method to return string value
public String returnData()
{
return test;
}
// Method to set string value
public void setData(String data)
{
test = data;
}
private void GUIinit()
{
mainFrame = new JFrame("Text");
mainFrame.setSize(200, 200);
mainFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEvent)
{
System.exit(0);
}
});
panelX = new JPanel();
mainFrame.add(panelX);
mainFrame.setVisible(true);
}
// Problematic part
private void addButton()
{
JButton testButton = new JButton("Text");
panelX.add(testButton);
testButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
setData("STRING BLABLA");
// Acquire the lock.
lock.lock();
try {
// Send a signal to all waiting threads.
valueAssigned.signalAll();
} finally {
lock.unlock();
}
}
});
}
}
Read more about Locks and Conditions here:
Lock Objects
I'm new in java. I'm trying to understand thraeads and timers in java. It was big problem for me to undate ProgressBar from another thread. I read many posts and found answers. So can you say is this the best way to resolw my task. So, what I want is to run second thread and when it's done, stop progress bar and change text on the button. So I creat Frame with button and progress bar in Test class:
After I realize action listener for Button to run second thread:
public class Test extends javax.swing.JFrame {
RunBg thread = new RunBg();
....
// declaration for button, progressbar and main() method
....
private void jButton1MouseClicked(java.awt.event.MouseEvent evt) {
// needed to check is the thread running at first time else creat new object and rund hread again cause thread can be executed once
if(thread == null){
thread = new RunBg();
}
if (jProgressBar1.isIndeterminate()) {
jProgressBar1.setIndeterminate(false);
jButton1.setText("Run!");
thread.suspend();
} else {
jButton1.setText("Working...");
jProgressBar1.setIndeterminate(true);
// let us know is the second thread running or nope so we can know resume it or pause
if (thread.isAlive()) {
thread.resume();
} else {
thread.start();
}
timer.start();
}
}
/*
Then I creat timer to know is my thread still alive else change Progress bar and button
*/
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(!thread.isAlive()){
thread.stop();
jProgressBar1.setIndeterminate(false);
jButton1.setText("Запустить!");
timer.stop();
// thread.interrupt();
thread = null;
}
}
});
...
// end of class
}
And my second class for another thread named RunBG:
public class RunBg extends Thread{
int i = 0;
public void run(){
while(i<20000){
System.out.println(">>>"+i);
i++;
}
System.out.println("<<<Finished>>>");
}
}
Everythig works fine , but I have question. Is there a better way to realize my task or maybe I made some mistakes. Also I thik that my code will help another beginners who looking for help to understand how it's work.
I created JFrame which contains and InternalFrame which draws figures which are moving(each figure is another Thread) I wanted to pauseButtonmake it pause, so I wanted to synchronize them on the passed object.
But when I hit the pause button the whole window is freezing and I cannot hit the play button
Another thing is that only one is running at the time, I want them all running then all pause.
class A extends JFrame{
....
Object o = new Object();
JButtton pauseButton = new JButton("pause");
JButtton playButton = new JButton("play");
B b = new B(o);
pauseButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
synchronized (synchronizator) {
try {
synchronizator.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
playButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
synchronized (synchronizator) {
synchronizator.notifyAll();
}
}
...
}
class B extends JInternalFrame{
Object o;
B(Object o){this.o = o}
./...
many... C thread = new C(o);
....
}
class C extends Thread{
Object o;
booolean running;
public void run(){
while(running){
synchronized(o){
}
}
}
}
Noooo!! ;)
All Swing activity should be done on the AWT Event Dispatch Thread (EDT). Use normal thread-agnostic objects and perhaps javax.swing.Timer (not java.util!) for timing.
You may want to do other things not involving Swing on different threads, but I suggest keeping a really clean separation. That is to say very few objects should be dealing with thread issues.
If you are using the bare low-level Java synchronisation facilities, set a condition prior to notify/notifyAll and put your waits within while loops.
I am trying to call a method repeatedly for as long as a button is pressed. But I get an infinite loop. Could anyone help me?
private void jButton6MousePressed(java.awt.event.MouseEvent evt) {
pressed = true;
while(pressed) {
car.accelerator();
}
}
private void jButton6MouseReleased(java.awt.event.MouseEvent evt) {
pressed = false;
}
Thanks.
You get an infinite loop because you have written an infinite loop, you need
JButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
car.accelerator();
}
});
If you want it to repeat the action while it's held own this is more difficult, here is an example of how you do that. In short you need to use threads.
I am pretty sure that once pressed is set to true, you never exit the while loop, so the fact that the button isn't being pressed never registers, the program is stuck. The only thing I can think of is using a timer to check periodically the state of the JButton. Alternatively, you can use multi-threading. That is, have PRESSED be a field in thread 1 that is set by the JButton (as you've done), and have the loop in a thread 2, checking on the status of PRESSED in thread 1.
Edit: Whoops, I see that bmorris591 has already suggested multithreading.
Swing thread enters an infinite loop.
You should run your loop in another thread:
private class BooleanHolder{
bool pressed;
};
final BooleanHolder pressed = new BooleanHolder();
private void jButton6MousePressed(java.awt.event.MouseEvent evt) {
presed.pressed=true;
Thread t = new Thread( new Runnable(){
public void run(){
while(pressed.pressed)
{
car.accelerator();
}
}
}
t.start();
}
private void jButton6MouseReleased(java.awt.event.MouseEvent evt) {
pressed.pressed=false;
}
Without knowing what car.accelerator(); does, it's impossible to make a accurate suggestion.
If car.accelerator() is interacting with the UI in any way, you need to be careful, you should never update the UI from any Thread other then the EDT.
Instead, you could use a rapid firing javax.swing.Timer
private Timer accelerateTimer;
//** ... **//
accelerateTimer = new Timer(15, new ActionListener() {
public void actionPerformed() {
car.accelerator();
}
});
accelerateTimer.setRepeats(true);
//** ... **//
public void mousePressed(MouseEvent me) {
accelerateTimer.restart();
}
public void mouseReleased(MouseEvent me) {
accelerateTimer.stop()
}