I'm converting a console application to one that uses Swing. At the moment I want my program to do a similar thing to this .nextInt(); how can I achieve this by using .getText(); or something similar?
In short;
How can I hold the execution of the program till the user has entered something in the text field and pressed enter.
Update: So you want to wait for the user to to input something from the GUI. This is possible but needs to be synchronized since the GUI runs in another thread.
So the steps are:
Create an "holder" object that deligates the result from GUI to "logic" thread
The "logic" thread waits for the input (using holder.wait())
When the user have entered text it synchronizes the "holder" object and gives the result + notifies the "logic" thread (with holder.notify())
The "logic" thread is released from its lock and continues.
Full example:
public static void main(String... args) throws Exception {
final List<Integer> holder = new LinkedList<Integer>();
final JFrame frame = new JFrame("Test");
final JTextField field = new JTextField("Enter some int + press enter");
frame.add(field);
field.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
synchronized (holder) {
holder.add(Integer.parseInt(field.getText()));
holder.notify();
}
frame.dispose();
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
// "logic" thread
synchronized (holder) {
// wait for input from field
while (holder.isEmpty())
holder.wait();
int nextInt = holder.remove(0);
System.out.println(nextInt);
//....
}
}
Console application and GUI application are quite different in the behaviour. Console application takes input from command line arguments or wait for user entered input from keyboard while GUI application is driven by event mechanism in order to perform a task.
For example, you add a TextField object to your Frame and add a keyListener to your text field object. The listener is invoked when the key event has ben notified. There are lots of example out there, e.g. official java example http://download.oracle.com/javase/tutorial/uiswing/events/keylistener.html
Related
Edit: a user marked that my question is a possible duplicate of this question: "What is the volatile keyword useful for", whose title is "What is the volatile keyword useful for?". I read the question but I don't see how it relates to my question.
Here is a program written in two .java files.
My question involves the if..else.. in the main method.
Note in the code below, the single line within the else {..} is commented out. I'll call this "version 1" of the program, and I'll call the program with that line commented back in "version 2".
// -------------
// The code below is in IfElseBugProgram.java
public class IfElseBugProgram {
public static void main(String[] args) {
MyJFrame terminal = new MyJFrame();
while (true) {
String keyReleased = terminal.getKeyReleased();
if (! keyReleased.equals("") )
{
System.out.print("#" + keyReleased);
}
else
{
// System.out.print("!" + keyReleased);
}
}
}
}
// -----
//The code below is in file MyJFrame.java
import javax.swing.JFrame;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
public class MyJFrame extends JFrame implements KeyListener
{
private List<KeyEvent> keyEventQueue;
public MyJFrame()
{
keyEventQueue = new ArrayList<KeyEvent>();
this.addKeyListener(this);
pack();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
public void keyPressed(KeyEvent e)
{
}
public void keyTyped(KeyEvent e)
{
}
public void keyReleased(KeyEvent keyEvent)
{
keyEventQueue.add(keyEvent);
System.out.println("some key was released!" + keyEventQueue.size());
}
public String getKeyReleased()
{
int i = keyEventQueue.size();
if (i == 0)
{
return ("");
}
else
{
return ("" + i);
}
}
}
I expect that the code in the if {...} to be run, once I press a key on my keyboard. That is, I expect the
System.out.print("#" + keyReleased);
code to be run as soon as I press a key.
With version 1, I never seem to get System.out.print("#" + keyReleased); to be run; there are never "#1" or "#2" or "#3"s etc printed in the console.
With version 2 (that is, with the code in the else {..} block commented back in),
what USUALLY happens is that the print statement that prints out "!" gets run repeatedly, until I press a key. At that point, things like "#1" or "#2" etc get repeatedly printed.
what SOMETIMES happens is that I get no "!" nor "#1" or "#2" printed out! (With the same source code!)
Question: Why does the System.out.print("#" + keyReleased); line in the if {..} block not run in version 1, but (usually) does in version 2?
Additional Observations:
The print statement System.out.println("some key was released!" + keyEventQueue.size()); in MyJFrame#keyReleased() always prints out, no matter how I change the code.
I thought that perhaps something might be wrong with my console in Netbeans. But I tried running other code in the if {..} block, such as the line of code java.awt.Toolkit.getDefaultToolkit().beep();, which makes a sound. I also tried making some graphics display in the JFrame itself. The result is the same: with the line of code in the else {...} commented out, the code in the if {..} block doesn't seem to run; but if I comment the line of code in the else {..} back in, the code in the if {...} block (including making a sound or displaying something in the JFrame) does in fact run.
I'm pretty sure that the condition of the if, if (! keyReleased.equals("") ) is correct. I tried adding the one line of code System.out.println("?" + (! keyReleased.equals("")) ); just above the if (! keyReleased.equals("") ), and consistently on my console I would get "?false" printed repeatedly, until I pressed a key, at which point I got "?true" printed repeatedly. But also: weirdly, putting in this one line of code (System.out.println("?" + (! keyReleased.equals("")) );) above the if in version 1 causes the lines of code in the if {..} block to now run?!
(optional) Background:
This section explains the code I am ultimately trying to write. If you wish to suggest an approach that might get me to this goal, that is different than the approach I'm using above, please feel free to suggest it.
I am trying to build a simple class called MyJFrame which can help motivate a friend learn how to program. The idea is to allow him to learn about variables in a visually motivated way, by programming extremely simple games, just like I learned when learning BASIC as a child. I want him to be able to write a program, completely contained in one main() method; I want him to have a simple way to draw a string on the screen, and a simple way to get user input. An example program might look like this:
int playerLocationX = 3;
int playerLocationY = 4;
MyJFrame terminal = new MyJFrame();
while(true)
{
// erase player that was drawn during the previous iteration of this loop
terminal.write(" ", playerLocationX, playerLocationY);
// get a key the user last pressed (if the user pressed a key) and update
// player location
String keyReleased = terminal.getKeyReleased();
if (keyReleased.equals("LEFT"))
{
playerLocationX = playerLocationX - 1;
}
else if (keyReleased.equals("RIGHT"))
{
playerLocationY = playerLocationY + 1;
}
// draw player again, using most recent player location
terminal.write("#", playerLocationX, playerLocationY);
}
I don't want to have him need to write his own keyReleased() method (ie, implementing a KeyListener interface), because this requires knowledge of writing your own methods; it also requires knowledge of objects, because a keyReleased() method has no way of modifying the local variables playerLocationX and playerLocationY stored in main().
The Java VM is allowed to optimize consecutive, unsynchronized loads by assuming that a variable will not be modified by concurrent threads.
If you want a spin loop over a field that is going to be changed by another thread, you can have the VM make sure every read sees the latest changes by marking it as volatile:
private volatile List<KeyEvent> keyEventQueue;
As the JLS puts it:
A write to a volatile field (ยง8.3.1.4) happens-before every subsequent read of that field.
I don't know whether your V2 is guaranteed to work according to the JLS, but the System.out PrintStream will synchronize on every write, restricting the optimizations that the VM is allowed to do.
The main reason you are experiencing such unpredictable behavior is because you actually created a multi-threaded program where you are accessing Swing components from a thread other than the event dispatching thread.
Specifically the line:
MyJFrame terminal = new MyJFrame();
starts the Swing event dispatching thread but the line (for example):
String keyReleased = terminal.getKeyReleased();
accesses the terminal (a Swing component) from the main thread.
From the Swing package documentation:
In general Swing is not thread safe. All Swing components and related classes, unless otherwise documented, must be accessed on the event dispatching thread.
Before moving ahead on trying to get this code to work, I would suggest going through the tutorial Lesson: Concurrency in Swing.
Inside a method, I start a thread that waits for user input (swing pushbutton).
Only after that input, the thread can be closed and the method returns a value.
My problem is that the code waiting for the input is not run inside that thread, but elsewhere:
String returnString = "";
Thread waitThread = new Thread(
() -> {
while (xy == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw THREAD_INTERRUPTED.toException();
}
}
}, "waitThread"
);
waitThread.start();
try {
waitThread.join();
} catch (InterruptedException e) {
throw THREAD_INTERRUPTED.toException();
}
// -> wait for user input -> xy != null -> waitThread ends -> main thread joins -> continue code:
returnString = xy;
return ReturnString;
Why is this necessary? Because the method has to return the value (xy) that is set elsewhere by clicking a pushbutton.
The code above just ends up in an infinity loop, not allowing any interaction with the swing components.
Not being a pro in swing, I suppose the main thread is meant to catch interaction events. Since it is stuck in the waitThread.join(); , thats not possible. Correct?
Is there a way to restructure this?
Why reinvent the wheel? Plenty of ways to do this out-of-the-box:
public static void main(String[] args) {
String message = JOptionPane.showInputDialog("What are you gonna tell me?");
System.out.println(message);
}
I think you are going down the wrong route.
Clicking a button leads to an event, and then there should be an ActionListener reacting to that.
And that listener could update some "statish" thingy, and your other thread is reading that information.
To answer jannis' question: The method opens a popup window that holds lets say two buttons. Each button sets a specific return value for the popup, which is then returned by the same method. So the method needs to open and close the popup. I know this is stupid, but it has to be this way. The setup would work, if I could keep interaction with the frontend enabled while waiting somehow.
Judging from this comment you seem to be trying to rediscover what is called a "modal dialog" and it's not stupid, at all. Please see the official documentation about dialogs in Swing: How to Make Dialogs
.
So I got this new game I am coding.
Long story short, there is a textarea in my GUI which acts as an event displayer. When you fight a monster, this textarea is used to display lines of text such as "You deal 3 damages to skeleton" or "Skeleton casts fireball on you and hit you for 5 damages".
The code works in a way so that the monster attacks after you. So as soon as you hit the "Attack" button, both the line saying what you did and the line saying what the monster did appears at the same time in the textarea. Like if the monster could hit you at the speed of light right after you hit it.
What I want to do is to delay the display of the monster's line. So that when I hit the "Attack" button, the textarea displays the line of what I did, then wait a second and then displays the monster's line.
I tried using the Thread.sleep() method, but all it does is pausing the UI for 1 second and then both lines appear in the textarea.
private void attackBareFists() {
if (initPJ > enemyINIT) { // Player has more initiative ->
// Player attacks first
turnPlayerBareFists(); // This method will display the player's line
if (!endTurn()) { // endTurn() checks that the player or the monster
// are still alive before continuing the fight
delay();
turnMonster(); // This method will display the monster's line
endTurn();
}
} ... // The code continues, but it's the same as above except monster attacks first
}
/**
* Wait for some time
*/
private void delay(){
}
What should I put in delay()? This is where I've tried Thread.sleep(1000). And like I said, doing so caused the code of turnPlayerBareFists() and turnMonster() to be executed after delay(). Any help would be appreciated.
I think a better/more consistent way to achieve this is by using timers. You could use any java implementation, though javafx itself provides several mechanism for timed events.
One way is the TimeLine
Timeline timeline = new Timeline(new KeyFrame(
Duration.millis(1000),
ae -> doSkellyTurn()),
new KeyFrame(
Duration.millis(1000 + 1000), // as mentioned by fabien, the time offset is relative to the 'start()' method, not to its previous keyframe!
ae -> endSkellyTurn()));
timeline.play();
The above way is also the basics for javafx animations (as you can read in the documentation)
This blog shows some more examples of how you can accomplish timed tasks. (Ps. It uses reactFX as well!)
Great question! I suggest using something like this:
public static void delay(long delayMs, Runnable toRun){
Thread t = new Thread(() ->{
try { Thread.sleep(delayMs); }catch(InterruptedException ignored){}
Platform.runLater(toRun);
});
t.setDaemon(true);
t.start();
}
This way, you can specify exactly how long the delay should between the call to delay() and when your code should be executed. Essentially you pass a Runnable containing whatever code you want to run as the second argument of the method.
For example, this would mean that your the monster's turn would be represented as such:
if(!endTurn())
delay(1000, ()->{ turnMonster(); endTurn(); });
If, for some reason, you don't want to use lambdas (the "()->" things), you can use this:
public static void delay(long delayMs, Runnable toRun){
Thread t = new Thread(new Runnable(){
public void run(){
try { Thread.sleep(delayMs); }catch(InterruptedException ignored){}
Platform.runLater(toRun);
}
});
t.setDaemon(true);
t.start();
}
And your monster code would look like this:
if(!endTurn())
delay(1000, new Runnable(){ public void run(){ turnMonster(); endTurn(); } });
If you're wondering, Platform refers to javafx.application.Platform and the runLater() method will always post the given Runnable to the JavaFX thread, so it's safe to pass it code that manipulates the UI.
I am trying to use progress bar in my swing code. It works well but i am enable to reset to zero after the execution is finished. Here is my code logic to get user inputs and call respective methods.
final JProgressBar progressBar = new JProgressBar();
btnRun = new JButton("Run");
btnRun.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btnRun.setEnabled(false);
if (textStartDate.getText().length() == 0 ||textEndDate.getText().length() == 0 ||textField.getText().length() == 0
|| textField_1.getText().length() == 0) {
JOptionPane.showMessageDialog(frame,"Please Enter all fields");
}
// else if(minDate.compareTo(maxDate)>0 ){
// JOptionPane.showMessageDialog(frame,"Starting Date should be lesser then end Date");
// }
else{
ArrayList<String> ss = list.listFiles(fSource,
textStartDate.getText(), textEndDate.getText());
for (String string : ss) {
i++;
progressBar.setMaximum(i);
System.out.println(i);
progressBar.setValue(i);
System.out.println(textField.getText().replace('\\', '/'));
list.writeToFolder(ftarget, string);
}
btnRun.setEnabled(true);
}
}
});
Set values to 0 like next:
progressBar.setValue(0);
progressBar.setMaximum(0);
progressBar.setString("");
The main problem you're having is your running a loop within the context of the Event Dispatching Thread, which will prevent it from process, amongst other things, paint requests.
This means the the progress bar won't actually update to until you exit the actionPerformed method.
There are a number of possible solutions, but the easiest would be to use a SwingWorker, which will allow you to run the loop in a background thread, but has the ability to provide both progress updates as well as re-sync updates back to the EDT.
Take a look at Concurrency in Swing for more details
For example...
java swingworker thread to update main Gui
Progress Bar Java
JProgressBar isn't progressing
I would, also, focus on maintaining the maximum value as a static value, for example...
progressBar.setValue(0);
progressBar.setMaximum(100);
//... Within the SwingWorker...
ArrayList<String> ss = list.listFiles(fSource,
textStartDate.getText(), textEndDate.getText());
for (String string : ss) {
i++;
int progress = (int)(((float)i / (float)ss.size()) * 100f);
setProgress(progress);
//...
}
For example. It will make the progress bar actually progress, otherwise it will always appear to be 100% (because i is both the value and the maximum).
This will automatically re-seed the progress bar the next time you create a new instance of the SwingWorker and execute it...
I have an application that is redirecting my System.out text to a Jtextarea. This works fine but when I call one of the methods in my application is creates multiple threads and uses a latch counter to wait for them to finish. That method then calls latch.await() so that it does not finish running its code until the other threads are finished. The problem is that once the latch.await() code is called my JtextArea stops posting text until all the threads have finished. Any ideas around this? Eclipse console is able to keep posting while the latch.await() is running so it has to be possible.
Example:
From the GUI:
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
System.out.println("You pressed Start");
MyGoogleSearch startParsing = new MyGoogleSearch();
try {
startParsing.startParser(othoSelection); ...
MyGoogleSearch:
Enumeration e = hm.elements();
//Read in src/Ontology/Ontology.txt
//Put each line into the searchQuery ArrayQueue
while ((strLine = br.readLine()) != null)
{
searchQuery.put(strLine);
}
System.out.println("Finsihed loading");
//Create 32 threads (More threads allows you to pull data from Bing faster. Any more than 32 and Bing errors out (you start to pull data
//too fast)
for(int i = 0; i < 32; i++)
{
System.out.println("Starting thread: " + i);
new NewThread();
}
//Wait for all of the threads to finish
latch.await();
e = hm.keys();
//Write the URL's from the hashmap to a file
while (e.hasMoreElements())
{
out.write(e.nextElement() + "\n");
}
//close input/output stream
in.close();
out.close();
System.out.println("Done");
and the Thread does some stuff then
MyGoogleSearch.latch.countDown();
This works fine but when I call one of the methods in my application is creates multiple threads and uses a latch counter to wait for them to finish.
You could get around that by calling the method in a separate thread. However, I suspect the method is waiting for all the threads to complete because it wants to aggregate some results and then return the aggregated result (or something similar). If that's the case, then there are several ways to deal with it, but probably the one that will make the most sense for a graphical application is to have the thread invoke a callback with any results which were obtained from the method.
If you post some sample code, then we can provide you with more specific answers and examples of how to do it.
Update:
I'm having a hard time reading your code, but I presume that 'startParser' is the call that's blocking. Additionally, it doesn't appear that the UI needs to wait for the results so I would recommend you do the simplest thing possible:
MyGoogleSearch startParsing = new MyGoogleSearch();
Thread t = new Thread(new Runnable(){
public void run(){
startParsing.startParser(othoSelection);
}
}
// don't wait for this thread to finish
t.start();