Breaking a Loop With a JButton - java

I'm creating a program for a game that clicks in random intervals for X seconds with Y time between each click. Here is the code that does this.
try {
Util.autoCode rand = new Util.autoCode();
Robot robot = new Robot();
int NoC;
NoC = Integer.parseInt(this.numberOfClicksTF.getText().trim());
if (NoC == 0) {
while (NoC == 0) {
// robot.mousePress(InputEvent.BUTTON1_MASK);
System.out.println("Infinite Press");
Thread.sleep(rand.clickDelay());
System.out.println("Infinite Release");
// robot.mouseRelease(InputEvent.BUTTON1_MASK);
Thread.sleep(rand.interval());
break;
}
} else {
for (int i = 0; i < NoC; i++) {
//robot.mousePress(InputEvent.BUTTON1_MASK);
System.out.println("Click Press");
Thread.sleep(rand.clickDelay());
System.out.println("Click Release");
// robot.mouseRelease(InputEvent.BUTTON1_MASK);
Thread.sleep(rand.interval());
}
}
} catch (AWTException ex) {
Logger.getLogger(MainFrame.class
.getName()).log(Level.SEVERE, null, ex);
} catch (NumberFormatException ex) {
Logger.getLogger(MainFrame.class
.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
}
I commented out the mouse press and release just so I could do a check to make sure the timing was right, and that it would perform each click. This code is started by JButton in a JFrame. Whenever I press the start button, it starts the code and nothing but force closing it in Netbeans will stop it. The goal is to have a start button initiate, and a stop button interrupt the code, but not close the JFrame. I have been looking everywhere and haven't been able to find a straight answer.
Any help is welcomed and appreciated!

You need tu use SwingWorker, for example:
in your JFrame you may have a SwingWorker and it "builder" method:
private SwingWorker worker;
private SwingWorker getWorker() {
worker = worker == null ? worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
while (true) {
System.out.println("doInBackground!");
Thread.sleep(1000);
}
}
} : worker;
return worker;
}
now, in the swing main thread (used for visual components updates) you need to call the SwingWorker with your background task:
private void jButtonActionPerformed(java.awt.event.ActionEvent evt) {
if (getWorker().getState().equals(SwingWorker.StateValue.STARTED)) {
worker.cancel(true);
worker = null;
} else {
getWorker().execute();
}
}
You can click on the button as many times as you wish, the app will create and run a SwingWorker or kill and set to null the current SwingWorker.

Literally ran into your exact same problem this week trying to make almost the same thing. First, let me suggest reading up on multithreading in Java. It may seem complicated, but trust me it's necessary to understand why your program is not working. (I'll give a run down of what's happening below)
Essentially, your GUI is running on an Event Dispatcher Thread that needs to be separated from your robot. That is because when you call thread.sleep(), you essentially tell the GUI thread to sleep too, resulting in loss of GUI control. Start your robot and GUI in separate threads and have your JButtons invoke methods to get your robot thread to start and stop. For example:
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new GUI();
}
});
}
To create your GUI(I don't know what your GUI is like, so I just wrote new GUI() to substitute for how you created yours), that way it's in the thread it's supposed to be in. Then in your eventListener, spawn a new thread holding the robot object everytime the start button is pressed:
if(actionEvent.getSource().equals(playButton)) { //listening for the play button
if(robotThread == null) {
autoClicker= new AutoClicker();
Thread = new Thread(autoClicker);
Thread.start();
}
}
And initiate your robot object in the autoClicker() class like you did, and have autoClicker implement runnable.
public class AutoClicker implements Runnable{
private Robot robot;
public void run(){
try {
robot = new Robot();
} catch (AWTException e) {e.printStackTrace();}
//you need to learn about synchronization first
while(true){
synchronized(this){
//do clicks and stuff here
}
}
}
}
This way, your GUI and robots are in separate threads. In order to make JButtons start and stop your robot, you're going to need to learn synchronization to know how to properly multithread.
Another thing is that you should probably use the wait() method instead of sleep(), as sleep() would cause a lot more problems that's too much to go through in a single answer. Again, you need to learn concurrency/synchronization first.
If you want comment below and we can work out a way for me to DM you my code I'm working on right now, which is the exact same thing as you're trying to make. I can explain every part of it to you to help you on your way.

Related

IllegalThreadStateException occuring again and again .. need to restart thread after stopping

need to make a music player as my simple java project. it opens up a file and loads the name in the text field.when play is pressed, the name is encircled in the textfield and when pause is pressed , the encircling suspends . play pause are JTogglebuttons and i have a button names stop. using multithreading, i m able to play and pause the string but after i press Stop once.. if again i open up a new music file and press play, it shows an illegalthreadstateexception.Please help me out here.. Note: i havent yet put in the code for playing music in it.. i will put it in once this problem is solved . thanks a lot for ur
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import sun.audio.*;
public class search extends Thread implements ActionListener
{
JFrame f;
JButton stop,open;
JToggleButton play;
JTextField tf,text;
JTextArea ta;
JLabel lab,lab1;
String str,dest;
JScrollPane scrol;
File fl;
int myfl=0,myfl1=0,myfl2=0;
search()
{
f=new JFrame("Music Player");
f.setLayout(null);
f.setSize(620,300);
play=new JToggleButton("play");
play.setBounds(100,150,270,30);
play.addActionListener(this);
f.add(play);
play.setEnabled(false);
stop=new JButton("stop");
stop.setBounds(400,150,120,30);
stop.addActionListener(this);
f.add(stop);
stop.setEnabled(false);
open=new JButton("open");
open.setBounds(100,200,420,30);
open.addActionListener(this);
f.add(open);
tf=new JTextField();
tf.setBounds(25,50,565,40);
tf.setFont(new Font("DK BabySitter",Font.BOLD,20));
tf.setHorizontalAlignment(JTextField.CENTER);
f.add(tf);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent ae)
{
if(ae.getActionCommand().equals("open"))
{
FileDialog fd=new FileDialog(f,"Open Box",FileDialog.LOAD);
fd.setSize(300,300);
fd.setVisible(true);
String s1="mp3";
str=fd.getFile();
dest=fd.getDirectory()+fd.getFile();
if(str.toLowerCase().endsWith(s1))
{
tf.setText(str);
//pause.setEnabled(true);
play.setEnabled(true);
stop.setEnabled(true);
}
else
{
JOptionPane.showMessageDialog(f, "Select a valid file format");
}
}
if(ae.getActionCommand().equals("stop"))
{
play.setLabel("play");
myfl1=1;
tf.setText(" ");
stop();
}
if(ae.getActionCommand().equals("play"))
{
try
{
play.setLabel("pause");
if(myfl==1 && myfl1==0)
{
resume();
}
if(myfl==0 || myfl1==1)
{
start();
}
}
catch(IllegalThreadStateException e)
{
tf.setText("error a gya re");
Thread newth= new Thread();
newth.start();
}
}
if(ae.getActionCommand().equals("pause"))
{
play.setLabel("play");
myfl=1;
suspend();
}
}
public void run()
{
try
{
String rot=tf.getText();
char rotn[]=new char[rot.length()];
int flag=rot.length();
int move=0;
rotn=rot.toCharArray();
tf.setText(" ");
for(;;)
{
for(;;)
{
sleep(100);
tf.setText( tf.getText() + rotn[move]);
move++;
if(move==(flag-1))
{
move=0;
break;
}
}
tf.setText(" ");
}
}
catch(Exception e)
{
tf.setText("error occured");
}
}
public static void main(String args[])
{
try {
// Set System L&F
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
}
catch (UnsupportedLookAndFeelException e) {
// handle exception
}
catch (ClassNotFoundException e) {
// handle exception
}
catch (InstantiationException e) {
// handle exception
}
catch (IllegalAccessException e) {
// handle exception
}
new search();
}
}
You should not use Thread.stop(), Thread.suspend() or Thread.resume(). These methods are deprecated and explicitly documented to have severe implications (like potential deadlocks etc). See also Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?.
In this specific case the exception occurs because you cannot start() a Thread more than once. You need to code your thread (or better yet: don't extend Thread, but implement Runnable) so that it handles the pause, start, stop etc of your application correctly without using the deprecated and dangerous methods of Thread.
The reason you're getting this error is that you're attempting to manually manipulate the state of the thread you're running in.
The methods you're using here are all deprecated - stop, resume, suspend - these should all be avoided.
A better way to think about how your program is running is that a thread is either alive or it isnt. It may not do anything within the actual run method while alive, but at no point are you pausing/suspending/stopping the actual thread - you're only pausing/stopping the application logic within.
A very quick fix to what you're seeing is to replace all stop/suspend/resume with the following:
private volatile boolean paused = false;
private void pausePlayer(boolean pause) {
this.paused = pause;
}
then everywhere you need to pause the player use:
pausePlayer(true);
and when you want to resume it use:
pausePlayer(false);
In the run method replace the 2nd for loop with:
for (; ; ) {
while(!paused) {
// do your work
Note though that I believe there are larger structural issues with the program as it stands - mixing normal Threads with Swing can be problematic. I would suggest you investigate using a SwingWorker instead, as this is explicitly designed for the sort of thing you're trying to do.

Infinite loop in a JPanel

I want to program a simple Snake.
Therefore I have programmed a custom JPanel, which can hold a Scene.
A Scene just draws something, and you can thread it with the public void run() method, so it implements Runnable.
Now, when I initialise the Scene, I create a Thread of the instance.
if (this.getThread() == null) {
Thread sceneThread = new Thread(this);
this.setThread(sceneThread);
this.getThread().run();
} else {
System.err.println("Scene is already running");
}
And the scene finally begins to be executed in a separate thread:
// Run thread
public void run () {
try {
while (true) {
this.update();
this.getGamePanel().sceneShouldRepaint();
Thread.sleep(this.getFps());
}
}
catch (Exception e) {
System.err.println(e);
}
}
Somehow this is blocking the windows thread.
It does not appear anymore.
Can anyone tell me why?
You are not starting the thread but directly invoke its run method, thus you are blocking the event thread itself in an endless loop - try starting it by calling start() instead.
Plus be sure to read about multithreading in Swing applications as pointed out by Qwerky.

How to make the main method wait for input on GUI without using Listener as a direct trigger?

I am working on a webscraping tool that should perform various operations with the scraped data.
Because of this, I need various different GUIs to work in an orderly manner and because of that, I need the main method to wait before each has completed it's purpose.
After searching for a while, I have found the following StackOverflow questions that provided some clues on how to solve the problem, but that I could not implement because they have some differences to my case:
How to wait for input in a text field
How to make main thread wait a different thread to finish
I know I can trigger code using a Listener to a/the GUI's components (a button, for example), but i'm having a hard time making the main-thread wait for that listener to wake it up, while the code for the GUI's thread (when there is one) is initialized by the main thread...
This is an simplified code to demonstrate how the program is supposed to work:
public class Main {
/*
* Waiter is a simple GUI with just an "Start" button in it. Here in place of my actual GUIs.
*/
private static Waiter auth; //Represents my NTLM-authentication form.
private static Waiter status; //Represents a status-feedback GUI that will be displayed during processing.
private static Waiter operation; //Represents a GUI in with the user choses what to do with the gathered data.
public static void main(String[] args) throws InterruptedException {
auth = new Waiter();
auth.setVisible(true);
System.out.println("NTLM Authentication form. Should wait here until user has filled up the GUI and clicked \"Start\".");
System.out.println("Authenticates WebClient's NTLM using data inputed to the GUI...");
auth.dispose();
Thread srt = new Thread(status = new Waiter());
srt.start();
status.setVisible(true);
//Performs webscraping operations...
System.out.println("Prepares the webscraped data here...Things like downloading files and/or parsing text...");
System.out.println("Keeps the user aware of the progress using the \"status\" GUI.");
status.setVisible(false);
//Clears the status GUI.
operation = new Waiter();
operation.setVisible(true);
System.out.println("Operation selection form. Should wait here until user selects an option.");
System.out.println("Starts performing the operation(s)...");
operation.dispose();
status.setVisible(true);
System.out.println("Performs the operation(s), while giving status-feedback to the user.");
status.setVisible(false);
System.out.println("Displays a file-save dialog to save the results.");
System.out.println("And finally, displays a \"End of operations\" dialog before ending.");
}
}
UPDATE 1:
The main difficulty I'm having is to implement something like this (this is what I want to do):
//Main method...code...
Thread srt = new Thread(status = new Waiter());
//Before "srt.start();"...
status.startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
main.continueExecution();
}
});
//Thread's run() being something like "status.setVisible(true); main.waitGUI();"
srt.start();
//continues here after the Listener is triggered...more code...
Instead of this (what is being the solution to most other people, if I'm understanding it right...) (this is what I don't want to do, if possible):
//GUI before this one...
//code...
Thread srt = new Thread(status = new Waiter());
status.startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
/*
* Code that should come after this GUI.
*/
}
});
//Thread's run() being something like "status.setVisible(true);"
srt.start();
//"ends" here...(Initial code or GUI before this "status")
In other words, I'm having trouble implementing the GUIs and Listeners in a way to trigger main's thread's "sleep" and "wake up" actions, instead of triggering actual processing code.
UPDATE 2:
Following #JB_Nizet 's tip on SwingUtilities.invokeLater(), I took a good look at the SwingUtilities docs, and after I found out about how the SwingUtilities.invokeAndWait() method works, and I think I've found how to do it, using a combination of Semaphore and invokeAndWait().
I need someone with a better understanding of multi-threading and/or GUIs to confirm if it's a safe, valid solution or not. (I'll then edit the question and clean it up, and if confirmed, post this in proper "answer format")
Anyways, here goes the modified code, which seems to be working for me:
public class Main_Test {
//Semaphore:
public static Semaphore semaphore;
//GUIs:
private static Waiter auth; //Represents my NTLM-authentication form.
public static void main(String[] args) {
try {
semaphore = new Semaphore(1);
// semaphore.acquire();
auth = new Waiter() {
#Override
public void run() {
try {
System.out.println(Main_Test.getThread() + this.getName() + " has been created and is now running.");
semaphore.acquire(); //Makes main pause.
this.setVisible(true);
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
auth.jButton1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(getThread() + "NTLM has been hypothetically authenticated.");
semaphore.release(); //Makes main continue after GUI is done.
auth.dispose();
}
});
// semaphore.release();
SwingUtilities.invokeAndWait(auth);
semaphore.acquire(); //<- Where the main effectively gets paused until the permit is released.
/*
* GUI's run() will accquire the semaphore's permit.
* The invokeAndWait() garantees (?) it will happen before main's acquire().
* This causes the main to pause when trying to acquire the permit.
* It stays paused until the actionListener release() that permit.
*/
System.out.println(getThread() + "This message represents the processing, and should come only after the hypothetical NTLM authentication.");
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static String getThread() {
return String.format("%-32s --- ", Thread.currentThread().toString());
}
}
I'm not sure I have completely understood what you want to do, but it seems to me that you have a consumer thread (the main thread, waiting for events from the event dispatch thread), and a producer thread (the event dispatch thread).
The typical way to implement this is to use a blocking queue as a communication mechanism:
Create a blocking queue
Create your GUI and pass it the blocking queue
start a loop which gets data from the queue. Since the queue is blocking, the main thread will be blocked untile there is something in the queue
Have your event listeners, running in the EDT, post data to the blocking queue

Wait for Swing Interface to close before proceeding

I've been searching near and far for a solution to my question but I am having difficulty even defining my search terms.
I have a method that creates a Swing GUI using invokeLater where the user completes some task. Once the task is completed, the window closes and the initial calling thread (e.g. the method) should resume execution. To be more specific, here is a summary of the method:
public class dfTestCase extends JFrame{
public dfTestCase{
... //GUI code here
}
public String run()
{
CountDownLatch c = new CountDownLatch(1);
Runnable r = new Runnable()
{
public void run()
{
setVisible(true); //make GUI visible
}
};
javax.swing.SwingUtilities.invokeLater(r);
//now wait for the GUI to finish
try
{
testFinished.await();
} catch (InterruptedException e)
{
e.printStackTrace();
}
return "method finished";
}
public static void main(String args[]){
dfTestCase test = new dfTestCase();
System.out.println(test.run());
}
}
Within the GUI, I have actionListeners for buttons that will close and countDown the CountDownLatch.
While the CountDownLatch works, it is not suitable for my purposes because I need to run this GUI several times and there is no way to increment the latch. I'm looking for a more elegant solution - it is my best guess that I would need to make use of threads but am unsure how to go about this.
Any help would be much appreciated!
Update
Some clarification: What is happening is that an external class is calling the dfTestCase.run() function and expects a String to be returned. Essentially, the flow is linear with the external class calling dfTestCase.run()-->the GUI being invoked-->the user makes a decision and clicks a button-->control to the initial calling thread is returned and run() is completed.
For now my dirty solution is to just put a while loop with a flag to continuously poll the status of the GUI. I hope someone else can suggest a more elegant solution eventually.
public class dfTestCase extends JFrame{
public dfTestCase{
... //GUI code here
JButton button = new JButton();
button.addActionListener{
public void actionPerformed(ActionEvent e){
flag = true;
}
}
}
public String run()
{
Runnable r = new Runnable()
{
public void run(){
setVisible(true); //make GUI visible
};
javax.swing.SwingUtilities.invokeLater(r);
//now wait for the GUI to finish
while (!flag){
sleep(1000);
}
return "method finished";
}
public static void main(String args[]){
dfTestCase test = new dfTestCase();
System.out.println(test.run());
}
}
Modal dialogs and SwingUtilities#invokeAndWait iso invokeLater should allow you to capture user input and only continue the calling thread when the UI is disposed
For an example of using model dialogs you can check out the ParamDialog class I wrote. In particular, check out ParamDialog.getProperties(Properties);
http://tus.svn.sourceforge.net/viewvc/tus/tjacobs/ui/dialogs/

Delay is affecting entire Block

I have a code right below...take a look.
enter.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (enter.getText().length()>0){
addToChat("You: "+enter.getText());
enter.setText("");
delay(1000);
addToChat("oie");
}
}
});
And here is the delay void.
public static void delay(int delayTime){
try
{
Thread.sleep(delayTime);
} catch (InterruptedException ie)
{
}
}
The problem is whoever I type something into the text box and hit enter, it takes one second for not only the one to show up in the text area, but also the "You: " text block to show up, which is before the delay. Why is this delay affecting things BEFORE it and how can I fix this?
The UI does not get a chance to update before your action listener is finished. If you would like to change something after the delay, you should schedule it on a different thread, rather than wait inside the event handler:
addToChat("You: "+enter.getText());
enter.setText("");
new Thread(
new Runnable() {
public void run() {
delay(1000);
addToChat("oie");
}
}
).start();
You're sleep()ing in the Event Dispatch Thread, which means your UI is frozen and can't repaint itself, or accept input, or anything. You should only perform very quick actions in the EDT to avoid this effect. Check out the Graphical User Interfaces and following tutorial trails for the basics of UI programming.

Categories

Resources