I searched around for this and couldn't get a clear answer. I've written a game that needs to pause until the user clicks a button with their decision, and then continue to execute. Is there a standard way to do this?
I've seen similar questions that refer to using 'wait()' and 'notify()', but I wasn't sure I needed to add more threads, especially since I'm not executing complex or time-consuming code.
I should clarify it's a computer version of a board game, so nothing more than a frame with some components. Here's some of what I'm trying to do, thanks guys:
public class TreasureHunterFrame extends javax.swing.JFrame
{
public TreasureHunterFrame()
{
initComponents();
startNewGame();
}
private void startNewGame()
{
...
// User asked to click button while this method is running
synchronized (this) // wait until Stay of Leave button is clicked
{
try
{
while (!userHasMadeDecision)
this.wait();
}
catch (InterruptedException ie)
{
}
}
....
}
private void userStayButtonActionPerformed(java.awt.event.ActionEvent evt)
{
userHasMadeDecision = true;
userLeaving = false;
synchronized (this)
{
notifyAll();
}
}
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new TreasureHunterFrame().setVisible(true);
}
});
}
}
Related
I have a question about using events to run loops since doing so seems to lock down the thread. For example I have an nativeMousePressed and nativeMouseReleased event and I am trying to execute some code continuously while the mouse is pressed and then stop when its released. I tried to do this by creating a static boolean variable in another manager class and then setting it to true when the mouse is being pressed and false when the mouse is released. Then I decided to make a while loop that gets called from inside that nativeMousePressed event that uses the boolean value I talked about earlier. The issue is that no events can be called while that while loop is running which means the boolean value when never become false creating an infinite loop. How can I run the while loop while keeping the events running as well?
I assume this has to do with the thread being locked down but I have not worked with stuff like this much and would like some help figuring out how to run both these things in parallel.
public class NativeMouseEvent implements NativeMouseListener {
Program program = new Program();
#Override
public void nativeMouseClicked(org.jnativehook.mouse.NativeMouseEvent e) {
}
#Override
public void nativeMousePressed(org.jnativehook.mouse.NativeMouseEvent e) {
if(e.getButton() == 1 && Controller.threeClicked) {
Controller.fixAim = true;
program.start();
}
}
#Override
public void nativeMouseReleased(org.jnativehook.mouse.NativeMouseEvent e) {
program.interrupt();
Controller.fixAim = false;
}
}
Here is what my second thread is running...
public class Program extends Thread {
public void run() {
while(Controller.fixAim) {
System.out.println("test");
}
}
Here my second attempt which also gives me an error saying that this.program is null.
public class NativeMouseEvent implements NativeMouseListener {
Program program;
#Override
public void nativeMouseClicked(org.jnativehook.mouse.NativeMouseEvent e) {
}
#Override
public void nativeMousePressed(org.jnativehook.mouse.NativeMouseEvent e) {
if(e.getButton() == 1 && Controller.threeClicked) {
Controller.fixAim = true;
if(program != null) {
program = new Program();
program.start();
}
}
}
#Override
public void nativeMouseReleased(org.jnativehook.mouse.NativeMouseEvent e) {
program.interrupt();
program = null;
Controller.fixAim = false;
}
}
Start a tread on mouse down and stop the tread on mouse up. In the thread do circle drawing.
Something like below java code. Note: it is just an example. You need to make changes to make it work in your android environment.
public class Test {
Thread drawTask;
public void mouseDown() {
drawTask = new Thread(()-> {
int i = 0;
try {
for(;;) {
System.out.print("\rDrawing circle " + i++);
Thread.sleep(500);
}
} catch(InterruptedException e) {
System.out.println("finished drawing circle.");
}
});
drawTask.start();
}
public void mouseUp() {
if(drawTask != null) {
drawTask.interrupt();
drawTask = null; //<--- make sure you do this
}
}
public static void main(String[] args) {
Test test = new Test();
Scanner in = new Scanner(System.in);
System.out.println("type anything and press Enter to simulate mouse down/up");
in.next();
test.mouseDown();
in.next();
test.mouseUp();
in.next();
in.close();
}
}
I'm creating some kind of menu (in swing), here's my code
public class JavaApplication5
{
public static void main(String[] args)
{
NewJFrame frame = new NewJFrame();
frame.setVisible(true);
if(frame.getRunning() == true)
{
System.out.println("why it doesn't work");
}
}
}
And here is my menu class:
public class NewJFrame extends javax.swing.JFrame
{
public boolean isRunning = false;
public void setRunning(boolean isRunning)
{
this.isRunning = isRunning;
}
public boolean getRunning()
{
return isRunning;
}
public NewJFrame()
{
initComponents(); //this refers to auto-generated code by swing library
}
//some swing stuff...
private void PlayActionPerformed(java.awt.event.ActionEvent evt) {
this.setRunning(true);
System.out.println(getRunning());
}
}
And the output is whenever i click on "Play" button:
"true
true
true"
And here my question rises: Why in the output's console, the following line is not displayed "why it doesn't work" if I've changed that value from false to true (by pressing button)? How to call that constanly changing variable in the main function? Thanks in advance.
Your application does not block. It sets the JFrame to visible (i.e. displays the window) and then immediately checks whether frame.getRunning() == true. There's absolutely zero chance that you can hit the button before the condition is evaluated.
NewJFrame frame = new NewJFrame();
frame.setVisible(true);
// you're assuming the application waits here, but it does not
if(frame.getRunning() == true)
{
System.out.println("why it doesn't work");
}
The way to solve this is either use a synchronization aid (maybe a CountDownLatch) which will block your main thread or rethink the sequence of events that happen in your program. Perhaps you should register a listener / callback.
I have some doubts about the use of the methods wait() and notify(). I have the next code which has some button events, the first time the user pushes the button it has to stop printing, and the second time it restarts printing again. I understand that is better to use Runnable instead of Thread, but I have to use Thread because of the requirements. The code works fine the first time the button is pushed but the second time it doesn´t, I want to use the wait() and the notify, but i don´t know how to do it with this particular code.
class Thr extends Thread{
private int count = 0;
private long pause;
private boolean canPrint = true;
private JTextArea textArea;
Thr(long miliseconds,JTextArea text){
pause = miliseconds;
textArea = text;
}
public void pushedButton(){
if(canPrint)
this.canPrint = false;
else
this.canPrint = true;
}
public void run()
{
while(this.canPrint)
{
try
{
this.printCounter();
Thread.sleep(pause);
this.count++;
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
public void printCounter(){
String time;
time = Integer.toString(count);
textArea.setText(time);
}
}
class Interface extends JFrame implements ActionListener{
private JTextArea textArea,textArea2;
private JButton button;
private Thr thread,threadEvent;
Interface()
{
textArea = new JTextArea(10,7);
textArea2 = new JTextArea(10,7);
thread = new Thr(2000,textArea);
threadEvent = new Thr(1000,textArea2);
button = new JButton("Pausar/Reanudar");
this.getContentPane().add(button,BorderLayout.SOUTH);
this.getContentPane().add(textArea,BorderLayout.WEST);
this.getContentPane().add(textArea2,BorderLayout.EAST);
thread.start();
threadEvent.start();
button.addActionListener(this);
}
public void actionPerformed(ActionEvent event)
{
threadEvent.pushedButton();
}
}
public class MensajesHilos {
public static void main(String[] args){
Interface i = new Interface();
i.setTitle("Control Threads");
i.setBounds(200, 200, 300, 240);
i.setVisible(true);
}
}
The way you have coded, if you want to achieve the desired result,
I feel the modification need to be done in run method,
public void run()
{
while(true)
{
if(this.canPrint){
try
{
this.printCounter();
Thread.sleep(pause);
this.count++;
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
in this way, your Thread will never go dead and toggle printing based on canPrint boolean value.
Also, make sure to declare canPrint variable volatile, so that changes to it will be directly written to main memory and reflected immediately.
"button event doesn´t work properly"
This is false, if you put a print statement in the actionPerformed method, you will see that it is called every time you press the button.
By the way note that you can simplify this
if(canPrint)
this.canPrint = false;
else
this.canPrint = true;
To
this.canPrint = !this.canPrint;
Note that it is a good practice to always put #Override anotation on top of overriden method.
#Override
public void actionPerformed(ActionEvent event)
{
threadEvent.pushedButton();
}
Now why don't you get the expected result ?
You ommit to call thread.pushedButton, so the canPrint will only be reseted in the threadEvent object, and will never be in thread.
Note that once the boolean are set to false, you will exit the loop and the process won't start back after even if you re-set the boolean value to true. This example will works using while(true) however, you should change the true to any sentinel value to handle the exit of the program as this will loop forever.
#Override
public void run()
{
while(true)
{
if(this.canPrint)
{
this.printCounter();
this.count++;
}
try
{
Thread.sleep(pause);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Also, make sure that pause is never 0 else you will eat all of the computer process.
Note that, as other stated, you should declare variables that are accessed in thread as volatile (canPrint) in your case.
I have a Jframe which is my application's window (appFrame in the following code) that contains a lot of logic and takes like 1 second or so to load. In the meantime I want to show my user a very nice loading frame (initFrame). However, when I run this code, the initFrame does appear but the text in a JLabel on it doesn't appear immediately - it actually doesn't appear at all in the brief moment till the app frame is loaded.
If i comment out all the appFrame, and only launch the initFrame, the text is loaded instantly, no waiting time at all. Why is this so? Might this be a concurrency issue?
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() { //as per best practice for concurrency in swing - see http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
#Override
public void run() {
final JFrame initFrame = new InitFrame();
initFrame.setVisible(true);
final AppFrame appFrame = new AppFrame();
appFrame.setVisible(true);
initFrame.setVisible(false);
initFrame.dispose();
}
});
}
I would separate the frames' creation into two threads. The first, initializing InitFrame. Running this thread and calling isShowing() on the InitFrame object. When it returns true, run the second thread to initialize and show AppFrame.
This will force a happens before relationship between the visibility of the two frames.
class Main {
JFrame initFrame = null;
AppFrame appFrame = null;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initFrame = new InitFrame();
initFrame.setVisible(true);
}
});
while(!initFrame.isShowing()) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
appFrame = new AppFrame();
appFrame.setVisible(true);
initFrame.setVisible(false);
initFrame.dispose();
}
});
}
}
Here's an example of what might be going wrong in your AppFrame.
You can run the test with threading:
java SplashTest true
or without
java SplashTest
When threading is enabled, you see the SplashFrame and AppFrame updating every 250ms, more or less.
When threading is not enabled, you get to see the SplashFrame with no components showing, the app 'hangs' for 4 seconds, then you see the AppFrame.
The example is somewhat contrived, but might give you some ideas.
Note that the SplashFrame has no 'direct' connection to the AppFrame. All communication is through the AppFrameWorkListener interface.
I've also put the 'work' in the AppFrame. But really if there is a lot of processing to be done it should be extracted out of the UI code, run in a separate Thread, and the AppFrame would be notified of progress by the task, in the same way as the SplashFrame currently is.
import javax.swing.*;
class SplashTest {
static boolean useThread = false;
public static void main(String[] args) {
// Pass true at the command line to turn on threading.
// No args, or any value other than true will turn off threading.
if (args.length > 0) {
useThread = new Boolean(args[0]);
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SplashFrame splashFrame = new SplashFrame();
splashFrame.setVisible(true);
new AppFrame(splashFrame).setVisible(true);
}});
}
private static class BaseFrame extends JFrame {
public BaseFrame() {
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(200, 200);
setTitle(getClass().getSimpleName());
}
}
private static class SplashFrame extends BaseFrame implements AppFrameWorkListener {
JLabel status;
public SplashFrame() {
setLocation(0, 0);
status = new JLabel("Splash Frame");
getContentPane().add(status);
}
public void appFrameWorkStart() {
status.setText("Work started");
}
public void appFrameWorkProgress(long timeElapsed) {
status.setText("Work has taken " + timeElapsed + "ms so far");
}
public void appFrameWorkDone() {
// http://stackoverflow.com/questions/1234912/how-to-programmatically-close-a-jframe
setVisible(false);
dispose();
}
}
private static class AppFrame extends BaseFrame {
JLabel status;
AppFrameWorkListener listener;
public AppFrame(AppFrameWorkListener listener) {
setLocation(200, 200);
status = new JLabel("App Frame");
getContentPane().add(status);
this.listener = listener;
// None of this 'heavy lifting' should be in a constructor.
if (useThread) {
new Thread(new Runnable() {
#Override
public void run() {
doLotsOfWork(4);
}
}).start();
} else {
doLotsOfWork(4);
onWorkDone();
}
}
private void doLotsOfWork(int workLengthSeconds) {
// We're starting. Ensure onWorkStart is called on the EDT,
// as this method may be called from a different Thread.
invokeOnWorkStartOnEDT();
long start = System.currentTimeMillis();
// Hammer the CPU for "workLengthSeconds" number of seconds.
// And do some contrived progress reporting.
long workLengthMs = workLengthSeconds * 1000;
while (System.currentTimeMillis() - start < workLengthMs) {
long innerStart = System.currentTimeMillis();
// Consume 250ms CPU before issuing progress update.
while (System.currentTimeMillis() - innerStart < 250);
invokeOnWorkProgressOnEDT(System.currentTimeMillis() - start);
}
// We're done now. Ensure onWorkDone is called on the EDT,
// as this method may be called from a different Thread.
invokeOnWorkDoneOnEDT();
}
private void invokeOnWorkStartOnEDT() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
onWorkStart();
}
});
}
private void invokeOnWorkProgressOnEDT(final long timeElapsed) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
onWorkProgress(timeElapsed);
}
});
}
private void invokeOnWorkDoneOnEDT() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
onWorkDone();
}
});
}
private void onWorkStart() {
status.setText("Work Started");
if (null != listener) {
// Tell someone who's interested in the work status.
listener.appFrameWorkStart();
}
}
private void onWorkProgress(long timeElapsed) {
status.setText("Work has taken " + timeElapsed + "ms so far");
if (null != listener) {
// Tell someone who's interested in the work status.
listener.appFrameWorkProgress(timeElapsed);
}
}
private void onWorkDone() {
status.setText("Work Done");
if (null != listener) {
// Tell someone who's interested in the work status.
listener.appFrameWorkDone();
}
}
}
interface AppFrameWorkListener {
public void appFrameWorkDone();
public void appFrameWorkStart();
public void appFrameWorkProgress(long timeElapsed);
}
}
You Should use Java Thread and you can show an interactive Splash Screen (Custom made) to your user in the mean while while your code is generating whatever you want here is a tutorial just take a look
You should use Threads for good and efficient concurrency thats it
In my GUI I have a PDF file creation operation. The operation can take up to 10-15 seconds to complete. When I start the operation, I attach a listener to it. The listener changes the cursor and disables the GUI, until the operation completes.
I would also like to add a progressbar, so the users will have a idea when it is going to complete.
Created a method startProgressBar() and called it from the start of the operation method.
See Below:
private void startSavePdfOperation() {
startProgressBar();
saveOp = new AplotSaveOperation(appReg.getString("aplot.message.SAVETOPDF"), "PDF", session);
saveOp.addOperationListener(new MyOperationListener(this) {
startProgressBar Method - See Below:
public void startProgressBar() {
Shell shell = new Shell(getShell());
shell.setSize(260, 120);
final ProgressBar bar = new ProgressBar(shell, SWT.SMOOTH);
bar.setBounds (20, 20, 200, 20);
shell.open();
final int maximum = bar.getMaximum();
new Thread(new Runnable() {
public void run() {
for (final int[] i = new int[1]; i[0] <= maximum; i[0]++) {
try {Thread.sleep (100);} catch (Throwable th) {}
if (Display.getDefault().isDisposed()) return;
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (bar.isDisposed ()) return;
bar.setSelection(i[0]);
}
});
}
}
}).start();
The code above created the ProgressBar. The issue is that the operation would end well before the progressbar indicator was close to ending.
Question: Is this because in the method I am creating a new thread and the indicator is updating according to the new thread and not the operation thread?
Question: Is it possible to create a new thread that watches the GUI thread and updates the progressbar accordingly?
Read a article suggesting using ProgressMonitorDialog with IRunnableWithProgress.
Method startProgressBar using ProgressMonitorDialog - see below:
public void startProgressBar() {
ProgressMonitorDialog dialog = new ProgressMonitorDialog(getShell());
try {
dialog.run(true, true, new IRunnableWithProgress(){
public void run(IProgressMonitor monitor) {
monitor.beginTask("Some nice progress message here ...", 100);
** getThread(); **
monitor.done();
}
});
}
catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void getThread() {
new Thread etc.. etc...
}
It seems that it will have the same issues with threading and updating as the code above.
Question: So now I am thinking can I just add or update the ProgressBar to my existing Listener
OperationListener Code - see below:
public abstract class MyOperationListener implements InterfaceAIFOperationListener {
AplotCreatePDFDialog w = null;
public MyOperationListener(AplotCreatePDFDialog win) {
w = win;
}
public void startOperation(String startMessage) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_WAIT));
w.recursiveSetEnabled(getShell(), getShell().getEnabled());
w.getShell().setEnabled(!getShell().getEnabled());
}
});
}
public void endOperation() {
try {
endOperationImpl();
}
finally {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_ARROW));
w.recursiveSetEnabled(getShell(), true);
w.getShell().setEnabled(!getShell().getEnabled());
w.close();
}
});
}
}
abstract protected void endOperationImpl();
} // end class MyOperationListener
Thanks for any help you can give me with this.
EDIT
Baz, your answer below is exactly what the question asked, so thank you for answering.
But I am starting to think that what I am trying to do is not possible.
When my operation starts, I wanted the progress bar indicator to start and when my operation ended I wanted the indicator be at the end and the monitor would close.
I thought there might bee a way to use my listener to add the progressbar. Something like the following.
public void startOperation(String startMessage) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
->monitor.beginTask("Creating PDF File(s)", IProgressMonitor.UNKNOWN);<-
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_WAIT));
w.recursiveSetEnabled(getShell(), getShell().getEnabled());
w.getShell().setEnabled(!getShell().getEnabled());
}
});
}
public void endOperation() {
try {
->monitor.worked(1);<-
endOperationImpl();
}
finally {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_ARROW));
w.recursiveSetEnabled(getShell(), true);
w.getShell().setEnabled(!getShell().getEnabled());
->monitor.done();<-
w.close();
}
});
}
}
abstract protected void endOperationImpl();
} // end class MyOperationListener
But I am starting to see that the ProgressBar has to have some sort of measurement to display the indicator correctly.
I would be happy if the indicator just went back and forth and the monitor would close at the end of the operation.
Why not use ProgressMonitorDialog?
Here is a related answer from me showing a simple example.
This is what it looks like:
If you are not sure about the workload, use this code:
monitor.beginTask("Copying files", IProgressMonitor.UNKNOWN);
It will show the idle bar while running.