Returning a variable after actionPerformed - java

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

Related

How can i make System.in read from a JTextField

I am trying to make System.in InputStream read from a JTextField.
What i need it to do, is to allow .read() to proceed once a user presses enter in a JTextField with a String in it.
The issue is that I don't know when .read() will be invoked, and i want it to block without freezing the main thread, until enter is pressed by the user in the JTextField where it will notify the thread waiting.
So far i tried the following:
public class InputStreamHandlerThread extends Thread {
private JTextField txt;
public InputStreamHandlerThread(JTextField txt) {
this.txt = txt;
start();
}
#Override
public void run() {
System.setIn(new FakeInputStream());
}
class FakeInputStream extends InputStream {
private int indx = 0;
#Override
public int read() throws IOException {
if (indx == txt.getText().length()) {
indx = 0;
try {
synchronized (this) {
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int byt = txt.getText().getBytes()[indx];
indx++;
return byt;
}
}
}
which is initialized and started by the GUI thread.
So once the GUI loads, it creates an instance of this class and keeps a pointer so that when the enter key is pressed on the keyboard within the JTextField, it is notified to read from it.
in = new JTextField();
in.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent a) {
if (a.getKeyChar() == '\n') {
inputStreamHandler.notify();
}
}
});
So currently there are three threads:
1. The main thread on what the GUI runs on
2. The InputStream handler thread (See above ^)
3. The thread that reads from System.in
The problem is that once i invoke inputStreamHandler.notify(); it throws a java.lang.IllegalMonitorStateException which according to the docs, is thrown if the thread is not the holder of the lock.
How do i resolve this?
Thank you :-)
String text;
...
in.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
text = in.getText();
}
});
Make sure you make text and in both fields.

Java Threading Issue with run()

I have 2 classes defined below:
public class TextsManager extends Thread {
LinkedList<String> lstOfPendingStr = new LinkedList<String>();
boolean stopLoop = false;
JTextArea txtArea;
public void run()
{
while (!stopLoop)
{
while (!lstOfPendingStr.isEmpty())
{
String tmp = lstOfPendingStr.getFirst();
this.txtArea.append(tmp);
lstOfPendingStr.removeFirst();
}
try {
Thread.sleep(0); // note: I had to force this code
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void AddNewStr(String newStr)
{
this.lstOfPendingStr.add(newStr);
}
}
And
public class ClientApp {
private JFrame frame;
private JTextField textField;
private JTextArea textArea;
static private TextsManager txtManager;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientApp window = new ClientApp();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientApp() {
initialize();
/*
* Client app
*/
txtManager = new TextsManager(textArea);
txtManager.start();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
textArea = new JTextArea();
textField = new JTextField();
textField.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER)
{
txtManager.AddNewStr(textField.getText() + "\n");
textField.setText("");
}
}
});
}
}
The program will read User Input from textField, pass it into TextsManager.lstOfPendingStr. Then, on each loop inside TextsManager.run(), it will check for existed members in lstOfPendingStr and output them via txtArea.
The problem is that if I removed the code Thread.sleep(0) inside run(), the run() then apparently stopped working. Despite lstOfPendingStr had been successfully updated with new elements, codes inside the loop while(!lstOfPendingStr.isEmpty()) would not ever to be called.
I put hard codes such as System.out.println or Thread.sleep(0) (as in the provided code) inside the while(!stopLoop), then it worked fine.
Although, I managed to solve the problem by forcing the thread to sleep for a few miliseconds, I want to know the reason behind this issue.
I appreciate your wisdom.
Regard :)
You have a couple of problems.
You are calling methods on lstOfPendingStr from two threads, but initialized it with LinkedList, which is not thread-safe. You should use a thread safe class, LinkedBlockingQueue seems the best options as far as I understood from your code.
Inside the thread you are calling JTextArea#append(). As all AWT/Swing methods, you can not call them from arbitrary threads, but only from the AWT thread. Wrap the call inside an invokeLater block.
The fact that sleep appears to make your code work is just a sign of the concurrency problems.

Waiting for thread while updating Swing

I have problem with handling threads in my application. It creates JFrame and starts a new Thread. Last one will execute external application and update GUI. Then
I have problem to make Main class to wait for second thread to finish, but also to update GUI simultaneously.
Here's my example (shortened):
class Main {
public int status;
public Main() {
// Creating GUI etc.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JDialog id = new JDialog();
id.button.addMouseListener(new MouseListener()); // Calls generate() method
}
});
}
public void generate() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Make changes to GUI
}
});
GeneratorThread genTest = new GeneratorThread(this, 1, 1, 1);
genTest.start();
//while (status == 0);
System.out.println("Next step.");
}
}
And Thread class:
public class GeneratorThread extends Thread {
protected Main main;
protected int setSize, minValue, maxValue;
public GeneratorThread(Main main, int setSize, int minValue, int maxValue) {
this.main = main;
this.setSize = setSize;
this.minValue = minValue;
this.maxValue = maxValue;
}
public void run() {
// Execute program etc.
// Change GUI from main in the same time
// About 3 seconds
main.status = 1;
}
}
I'm in progress and I wanted to check how it works so far. While worked nice, but it locks Swing somehow and any changes are visible only when GeneratorThread finishes. I would like to update GUI in the real time.
I've tried join(), effects are the same. I also tried wait() (on Main), but then I got IllegalStateMonitorException.
Any hints?
Swing is a single threaded environment. That is, there is a single thread responsible for managing all the interactions and updates to the Swing UI - the Event Dispatching Thread.
Among the golden rules of Swing are...
DON'T block the EDT (Thread.sleep, Thread#join, Object#wait, block IO and/or time consuming tasks (among others) should never be called from within the EDT), doing so will stop the EDT from dispatching events and paint updates (amongst other things)
ONLY create/update Swing UI elements from within the EDT.
This raises a question...how do you "wait" for a thread?
The best way is use an Observer pattern. Basically, you provide the Thread with some kind of reference that it will call to provide notification of events, such as errors and completion...
This will require you to think very carefully about the design of your applications, as you can not rely on a simple A to B execution of your code.
For example...
public class TestThreadCallBack {
public static void main(String[] args) {
new TestThreadCallBack();
}
public TestThreadCallBack() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface ThreadCallBack {
public void threadCompleted(Runnable source);
public void threadFailed(Runnable source);
}
public class TestPane extends JPanel implements ThreadCallBack {
private JLabel message;
private JLabel dots;
private int count;
private Timer timer;
public TestPane() {
setLayout(new GridBagLayout());
message = new JLabel("Running background task, please wait");
dots = new JLabel(" ");
add(message);
add(dots);
timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
count++;
if (count > 3) {
count = 0;
}
StringBuilder sb = new StringBuilder(3);
for (int index = 0; index < count; index++) {
sb.append(".");
}
for (int index = count; index < 3; index++) {
sb.append(" ");
}
dots.setText(sb.toString());
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
Thread thread = new Thread(new BackgroundTask(this));
thread.start();
}
#Override
public void threadCompleted(Runnable source) {
timer.stop();
message.setText("Task completed successfully");
}
#Override
public void threadFailed(Runnable source) {
timer.stop();
message.setText("Task failed");
}
}
public class BackgroundTask implements Runnable {
private ThreadCallBack callBack;
public BackgroundTask(ThreadCallBack callBack) {
this.callBack = callBack;
}
#Override
public void run() {
System.out.println("Background task underway...");
try {
Thread.sleep(2000);
} catch (InterruptedException interruptedException) {
}
int result = (int) Math.round((Math.random() * 1));
if (result == 0) {
callBack.threadCompleted(this);
} else {
callBack.threadFailed(this);
}
}
}
}
Updating the UI from within a Thread other then the EDT is, well, messy. An easier solution would actually be to use a SwingWorker. This has publish/process methods that make easy to update the UI and progress methods that can be used to provide feedback about the progress of the current task.
You can use it's done method to notify interested parties when the worker has completed.
Update your GUI from within the thread using SwingUtilitied.invokeLater or, alternatively, synchronise the main variable!
http://www.vogella.com/articles/JavaConcurrency/article.html#concurrencyjava
Maybe it already suffices to make "status" volatile?

Java Swing cancelling an Infinite loop

I've hit the infinite loop problem in Swing. Done some research and come across SwingWorker threads but not really sure how to implement them. I've knocked together a simple program that shows the problem. One button starts the infinite loop and I want the other button to stop it but of course due to the Swing single thread problem the other button has frozen. Code below and help appreciated:-
public class Model
{
private int counter;
private boolean go = true;
public void go()
{
counter = 0;
while(go)
{
counter++;
System.out.println(counter);
}
}
public int getCounter()
{
return counter;
}
public void setGo(boolean value)
{
this.go = value;
}
}
public class View extends JFrame
{
private JPanel topPanel, bottomPanel;
private JTextArea messageArea;
private JButton startButton, cancelButton;
private JLabel messageLabel;
private JScrollPane scrollPane;
public View()
{
setSize(250, 220);
setTitle("View");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
topPanel = new JPanel();
bottomPanel = new JPanel();
messageArea = new JTextArea(8, 20);
messageArea.setEditable(false);
scrollPane = new JScrollPane(messageArea);
messageLabel = new JLabel("Message Area");
topPanel.setLayout(new BorderLayout());
topPanel.add(messageLabel, "North");
topPanel.add(scrollPane, "South");
startButton = new JButton("START");
cancelButton = new JButton("CANCEL");
bottomPanel.setLayout(new GridLayout(1, 2));
bottomPanel.add(startButton);
bottomPanel.add(cancelButton);
Container cp = getContentPane();
cp.add(topPanel, BorderLayout.NORTH);
cp.add(bottomPanel, BorderLayout.SOUTH);
}
public JButton getStartButton()
{
return startButton;
}
public JButton getCancelButton()
{
return cancelButton;
}
public void setMessageArea(String message)
{
messageArea.append(message + "\n");
}
}
public class Controller implements ActionListener
{
private Model theModel;
private View theView;
public Controller(Model model, View view)
{
this.theModel = model;
this.theView = view;
view.getStartButton().addActionListener(this);
view.getCancelButton().addActionListener(this);
}
public void actionPerformed(ActionEvent ae)
{
Object buttonClicked = ae.getSource();
if(buttonClicked.equals(theView.getStartButton()))
{
theModel.go();
}
else if(buttonClicked.equals(theView.getCancelButton()))
{
theModel.setGo(false);
}
}
}
public class Main
{
public static void main(String[] args)
{
Model model = new Model();
View view = new View();
Controller controller = new Controller(model, view);
view.setVisible(true);
}
}
You can do it easily without implementing any timer, you just need to add two lines to your actionPerformed method:
public void actionPerformed(ActionEvent ae)
{
Object buttonClicked = ae.getSource();
if(buttonClicked.equals(theView.getStartButton()))
{
theModel.setGo(true); //make it continue if it's just stopped
Thread t = new Thread(new Runnable() { public void run() {theModel.go();}}); //This separate thread will start the new go...
t.start(); //...when you start the thread! go!
}
else if(buttonClicked.equals(theView.getCancelButton()))
{
theModel.setGo(false);
}
}
As your Model.go() is running in a separate thread, the Event Dispatch Thread is free to do its stuff, like drawing the button released again, instead of hanging with the button down.
There's a catch! however, because the thread running Model.go() will run wildly!, it's virtually called as many times per second as your system can.
If you plan to implement some animation or the like, then you will need to:
use a Timer,
or
add some sleep time to the new thread.
Example if you go with threads:
public void go()
{
counter = 0;
while(go)
{
counter++;
System.out.println(counter);
try {
Thread.sleep(1500); //Sleep for 1.5 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
As you can see I added Thread.sleep(1500) being 1500 the time in milliseconds (1.5 seconds). Thread.sleep can be interrupted for some reasons, so you must catch the InterruptedException.
It's not necessary to go deeper on handling correctly the InterruptedException in this particular case, but if you feel curious about it you can read this nice article.
You are blocking the Event Dispatch Thread (EDT). The thread is responsible to process painting and other UI related requests. Once EDT is blocked the UI will become frozen since it cannot process any events. For more details see The Event Dispatch Thread tutorial.
Consider using timers (How to Use Swing Timers), SwingWorker or an auxiliary background thread. Background thread can communicate with EDT using SwingUtilities.invokeLater(). This mechanism is already implemented in SwingWorker, so it may be easier to go with it. It depends on functionality that is required.
Use a javax.swing.Timer to do the go() work once, (with some optional delay), using start() and stop() in the event handling.
I decided to use a SwingWorker thread and below is the updated Controller class. It does what I need it to do but my question is, is it the correct way and is it clean code? Also, I've tried getting the output of the model.go() method into the view's textarea as per the commented out lines but not been succesful, anyone know how?
public class Controller implements ActionListener
{
private Model theModel;
private View theView;
private SwingWorker<Void, Void> worker;
public Controller(Model model, View view)
{
this.theModel = model;
this.theView = view;
view.getStartButton().addActionListener(this);
view.getCancelButton().addActionListener(this);
}
public void actionPerformed(ActionEvent ae)
{
Object buttonClicked = ae.getSource();
if(buttonClicked.equals(theView.getStartButton()))
{
theModel.setGo(true);
worker = new SwingWorker<Void, Void>()
{
#Override
protected Void doInBackground()
{
// theView.setMessageArea(theModel.getCounterToString());
return theModel.go();
}
#Override
protected void done()
{
// theView.setMessageArea(theModel.getCounterToString());
}
};
worker.execute();
}
else if(buttonClicked.equals(theView.getCancelButton()))
{
theModel.setGo(false);
}
}
}
public class Model
{
public Void go()
{
counter = 0;
while(go)
{
counter++;
System.out.println(counter);
}
return null;
}

How to Interrupt in Java

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.

Categories

Resources