I have a JDialog being displayed on screen and I want to simulate its movement (Drag from one location to another) based on a condition. Is there any way this can be done ?
See this piece of code below. I have just tested it and it works fine. It is just a proof of concept.
private void startDialog() {
final JDialog d = new JDialog(this, "Test", true);
d.getContentPane().add(new JLabel("Something"));
d.setBounds(100, 100, 400, 300);
Thread t = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 50; i++) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Point p = d.getLocation();
d.setLocation(p.x + 10, p.y + 10);
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// ignore
}
}
}
});
t.start();
d.setVisible(true);
}
You can improve the code yourself:
use a Timer instead of a regular Thread
tweak the sleep times and the location jumps and so on
Just call this method from any Swing application and it will work.
Related
I'm trying to code a primitive spammer. Is it okay to use thread.sleep() when coding a bot?
I'm a novice programmer. If there is any place in my code to fix it, I would appreciate it if you let me know. I may have used JComponents improperly. If it catches your eye, you can specify. Thank you.
Note: "It looks like your post is mostly code; please add some more details." I'm writing this note because I can't find any more details to add. Sorry
public class Spammer extends JFrame implements Runnable{
private boolean running = false;
private JButton jButton1;
private JLabel jLabel1, jLabel2;
private JScrollPane jScrollPane1;
private JSpinner jSpinner1;
private JTextArea jTextArea1;
public Spammer() {
setLayout(null);
jLabel1 = new JLabel("Text: ");
jTextArea1 = new JTextArea(10,28);
jLabel2 = new JLabel("Interval: ");
jSpinner1 = new JSpinner();
jScrollPane1 = new JScrollPane();
jButton1 = new JButton("Spam");
jButton1.setSize(350, 60);
jButton1.setLocation(100, 220);
jLabel1.setSize(50, 150);
jLabel1.setLocation(15, 10);
jLabel1.setFont(new Font("Verdana" , Font.BOLD , 14));
jTextArea1.setSize(350, 150);
jTextArea1.setLocation(100, 10);
jLabel2.setSize(80, 25);
jLabel2.setLocation(15, 180);
jLabel2.setFont(new Font("Verdana" , Font.BOLD , 12));
jSpinner1.setSize(350, 25);
jSpinner1.setLocation(100, 180);
getContentPane().add(jLabel1);
getContentPane().add(jTextArea1);
getContentPane().add(jLabel2);
getContentPane().add(jSpinner1);
getContentPane().add(jScrollPane1);
getContentPane().add(jButton1);
setTitle("Spammer by Me");
setLocationRelativeTo(null);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(500, 340));
pack();
jButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1ActionPerformed();
}
} );
}
private void jButton1ActionPerformed() {
if(!running) {
jTextArea1.setEnabled(false);
jSpinner1.setEnabled(false);
jButton1.setText("Spamming in 3 seconds...");
jButton1.setEnabled(false);
running = true;
new Thread(this).start();
}else {
jTextArea1.setEnabled(true);
jSpinner1.setEnabled(true);
jButton1.setText("Spam");
running = false;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Spammer().setVisible(true);
}
});
}
public void run() {
Robot robot = null;
try {
robot = new Robot();
} catch (AWTException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
int[] keys = new int[jTextArea1.getText().length()];
if((int) jSpinner1.getValue() < 0) {
jSpinner1.setValue((int) 0);
}
int interval = (int) jSpinner1.getValue();
for(int i = 0 ; i < keys.length; i++) {
keys[i] = KeyEvent.getExtendedKeyCodeForChar(jTextArea1.getText().charAt(i));
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
jButton1.setEnabled(true);
jButton1.setText("Stop");
while(running) {
for(int i = 0 ; i < keys.length; i++) {
robot.keyPress(keys[i]);
robot.keyRelease(keys[i]);
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
The key principle at work here is primarily the 'EDT' - the Event Dispatch Thread. This is a thread that does GUI stuff - if for example you drag the titlebar of a completely different app's window across the screen, and this moves that app's window over yours, the EDT is hard at work redrawing everything. The EDT is the thread that sees you press your mouse down on a button, and will render the button in the 'pressed in' view.
The EDT is the only thread from which you can do GUI stuff, whether it is to retrieve info, such as getText(), or whether it is to change things, such as updating the text of a label or whatnot.
The EDT is also the thread you're in when your code runs that you registered as a handler for events, such as the code that responds to a button click or whatnot.
You therefore must not sleep on the EDT (Because then your app looks non-responsive; the thread that responds to button clicks or repaints what needs repainting is not actively running), but you can only fetch GUI data / set GUI stuff from the EDT.
The rules:
Do not interact with any GUI elements unless you are in the EDT
Never sleep in the EDT
Your code is broken, not because you sleep (that's fine - that run() method is not in the EDT), but because you do GUI stuff from this non-EDT thread.
You need to do a careful dance here: You want to sleep (not allowed on the EDT), but interact with GUI elements, such as the interval box, to know how long to sleep, which can only be done on the EDT.
To do this, you can 'send' code to run in the EDT via SwingWorkers, or simply via:
SwingUtilities.invokeAndWait(() -> {
// code that will run in the EDT goes here
});
You can't set any variables from within this code, but you can use AtomicReference and friends to create objects you can change. So, instead of:
int[] keys = new int[jTextArea1.getText().length()];
if (jSpinner1.getValue() < 0) {
jSpinner1.setValue(0);
}
int interval = (int) jSpinner1.getValue();
which is doing GUI stuff, do:
AtomicInteger interval = new AtomicInteger();
SwingUtilities.invokeAndWait(() -> {
int[] keys = new int[jTextArea1.getText().length()];
if (jSpinner1.getValue() < 0) {
jSpinner1.setValue(0);
}
interval.set((int) jSpinner1.getValue());
};
I got a java progressbar which loads perfectly, but I can't see the process, only the result. (when the bar finished loading)
I want to see every percentage of the progress. When I run the code, the frame appears but the progressbar doesn't, only when it's on 100%. Where's the problem?
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
JFrame f = new JFrame("JProgressBar Sample");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container content = f.getContentPane();
progressBar = new JProgressBar();
progressBar.setStringPainted(true);
Border border = BorderFactory.createTitledBorder("Reading...");
progressBar.setBorder(border);
content.add(progressBar, BorderLayout.CENTER);
f.setSize(300, 100);
f.setVisible(true);
progressBar.setValue(0);
inc(); //fill the bar
//It fills, but I can't se the whole loading...
}
//Here's the filling path
public static void inc(){
int i=0;
try{
while (i<=100){
progressBar.setValue(i+10);
Thread.sleep(1000);
i+=20;
}
}catch(Exception ex){
//nothing
}
}
Your GUI is not updated while you are running a long process in the GUI's thread, like filling a progress bar and sleeping some seconds.
Just emerge a thread, which will handle the long operation.
Within this thread set the progress bar to the desired value within a SwingUtilities.invokeLater(new Runnable() {...}.
Runnable r = new Runnable() {
public void run() {
int i=0;
try{
while (i<=100){
final int tmpI = i+10;
SwingUtilities.invokeLater(new Runnable(){
public void run() {
progressBar.setValue(tmpI);
}
});
Thread.sleep(1000);
i += 20;
}
} catch(Exception ex){
//nothing
}
}
};
Thread t = new Thread(r);
t.start();
I have a constructor of a JFrame where I have a Thread(t1) which is running thanks to a
while(true)
I would like to know how to implement my JFrame so it can kill the thread when I close it, because t1 need to be running when the JFrame is active
EDIT:
Here is the code:
public class Vue_Session extends JFrame {
private JPanel contentPane;
private int idsess;
private User u;
public Vue_Session(User us, int id) {
this.u = us;
this.idsess = id;
toServ t=new toServ(idsess);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds((int) screenSize.getWidth() / 2 - 800 + (800 / 2), 90, 800,
600);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new BorderLayout());
Vue_Idee vueIdee = new Vue_Idee(this.idsess, this.u);
contentPane.add(vueIdee, BorderLayout.SOUTH);
Vue_IdeeSession vueSess = new Vue_IdeeSession(this.idsess);
contentPane.add(vueSess, BorderLayout.CENTER);
Thread t1 = new Thread( new Runnable(){
public void run(){
while(true){
try{
Thread.sleep(500);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t.getIdee();
vueSess.act();
revalidate();
}
}
});
t1.start();
}
You can handle it with a boolean variable like
boolean end = false;
while (!end){...}
Also I suggest you use ExecutorService or ForkJoinPool so you can simply control your tasks, threads, etc
EDIT:
boolean end = false;
new Thread(() -> {
while (!end) {
//...
}
}).start();
and this is where you should end your tasks:
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosed(java.awt.event.WindowEvent evt) {
end = true;
System.exit(0);
// or this.dispose();
}
});
good luck :)
It's a controversial topic but in general I would replace the while (true) construct with
while(!Thread.currentThread().isInterrupted()){
try{
Thread.sleep(500);
}catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
t.getIdee();
vueSess.act();
revalidate();
}
More information on this topic can be found here:
http://www.javaspecialists.eu/archive/Issue056.html
Okay, here was the answer:
I needed to add en WindowListenne:
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent b) {
t1.stop();
dispose();
}
});
And also:
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
First, you need to make the thread kill-able. To do this, instead of looping forever, loop as long as certain looping flag is true.
After that, you need to create a listener that gets called whenever the user closes the frame. You can use WindowAdapter to do this. When the listener is called, set the looping flag to false. Once the thread dies, you can safely terminate the program.
For example:
public class Vue_Session extends JFrame {
Thread thread = null;
boolean threadAlvie = true;
boolean threadDie = false;
public Vue_Session(User us, int id) {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEvent) {
threadAlive = false;
// Wait until the thread dies
while (!threadDie) {
// Sleep for 100 milliseconds.
Thread.sleep(100);
}
System.exit(0);
}
});
thread = new Thread(new Runnable() {
public void run(){
while (threadAlive){
// do something
}
threadDie = true;
}
});
thread.start();
}
}
I'm trying to figure out why the text field isn't updating. I'm aware that using SwingWorker will probably fix this problem, but I can't understand why it doesn't work in the first place.
public class waitExample {
private JFrame frame;
private JTextField txtLeadingText;
private String one = "update string 1";
private String two = "update string 2";
private String three = "update string 3";
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
waitExample window = new waitExample();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public waitExample() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
txtLeadingText = new JTextField();
txtLeadingText.setHorizontalAlignment(SwingConstants.CENTER);
txtLeadingText.setText("leading text");
frame.getContentPane().add(txtLeadingText, BorderLayout.SOUTH);
txtLeadingText.setColumns(10);
JButton btnClickMeTo = new JButton("CLICK ME TO UPDATE TEXT");
btnClickMeTo.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
try {
updateOne();
Thread.sleep(1000);
updateTwo();
Thread.sleep(1000);
updateThree();
Thread.sleep(1000);
updateLast();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
frame.getContentPane().add(btnClickMeTo, BorderLayout.CENTER);
}
private void updateOne() {
txtLeadingText.setText(one);
}
private void updateTwo() {
txtLeadingText.setText(two);
}
private void updateThree() {
txtLeadingText.setText(three);
}
private void updateLast() {
txtLeadingText.setText("default text");
}
}
From what I understand, the default Thread will prevent any GUI updates. That shouldn't matter because I am setting the textField BEFORE the Thread.sleep.
Why doesn't the text field update? Shouldn't the text be set, then the Thread wait?
EDIT: As per the answers, the above code has been updated.
You are invoking Thread.sleep(1000); on EDT. This means that when your method will end - only then the repaint() will fire (at some point in time later).
Until then your GUI is freezed.
Consider that this is going on one thread (so processing is straightforward):
txtLeadingText.setText(one);
Thread.sleep(1000);
txtLeadingText.setText(two);
Thread.sleep(1000);
txtLeadingText.setText(three);
Thread.sleep(1000);
...
<returning from updateText()>
<processing other events on button click>
...
// some time later
<Swing finds out that GUI needs repaint: calls rapaint()>
This is what you should do (I didn't compile or test it):
public class MyRunnable implements Runnable {
private List<String> strsToSet;
public MyRunnable(List<String> strsToSet) {
this.strsToSet = strsToSet;
}
#Override
public void run() {
try {
if(strsToSet.size() > 0) {
final String str = strsToSet.get(0);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
txtLeadingText.setText(str);
}
});
Thread.sleep(1000);
List<String> newList = new LinkedList<String>(strsToSet);
newList.remove(0);
new Thread(new MyRunnable(newList)).start();
}
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
new Thread(new MyRunnable(Arrays.asList(one, two, three))).start();
It is hard to do in Swing but in contrast in dynamically languages (like Groovy) it would go as simple as that (you'll get a better grasp of what is going on):
edt {
textField.setText(one)
doOutside {
Thread.sleep(1000);
edt {
textField.setText(two)
doOutside {
Thread.sleep(1000);
edt {
textField.setText(three)
}
}
}
}
}
The GUI event loop updates the screen, but it can't update the screen until you return.
I suggest you avoid doing any blocking operations in the GUI event thread.
First code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class cos {
public static int a;
private static JLabel labeler;
// public static Runnable r1;
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
a = 0;
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
cos window = new cos();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public cos() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
public void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 205, 194);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel lblTime = new JLabel("Time:");
frame.getContentPane().add(lblTime, BorderLayout.WEST);
final JLabel labeler = new JLabel("");
frame.getContentPane().add(labeler, BorderLayout.CENTER);
JButton btnNewButton = new JButton("New button");
btnNewButton.addActionListener(new ActionListener() {
Runnable r1 = new Runnable() {
public void run() {
while (a <= 10) {
a = a + 1;
labeler.setText(Integer.toString(a));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
public void actionPerformed(ActionEvent arg0) {
Thread threder = new Thread(r1);
threder.start();
// liczniczek bla = new liczniczek();
}
});
frame.getContentPane().add(btnNewButton, BorderLayout.SOUTH);
}
public void licznik() {
while (a < 60) {
a = a + 1;
labeler.setText(Integer.toString(a));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
And now my question. I wanna use code like this:
Runnable r1 = new Runnable(){
public void run(){
licznik();
}
};
But that doesen't work. What i must do to separate this code ? Sorry for my bad english
Sierran.
never use Thread#sleep(int) during EDT, sure if is there only this thread then works correctly (with blockng EDT),
Runnable r1 = new Runnable(){
public void run(){
licznik();
}
};
is wrong than same as you call plain licznik();, you have to wrap that this way
Runnable r1 = new Runnable(){
public void run(){
labeler.setText(Integer.toString(a));
}
};
but again without Thread#sleep(int), you have three choises
1) change Thread to the javax.swing.Timer
2) change Thread to the Runnable#Thread, there you can delaying with Thread#sleep(int), but output to the GUI must be
Runnable r1 = new Runnable(){
public void run(){
labeler.setText(Integer.toString(a));
}
};
3) use SwingWorker, where output is in the EDT and you can use Thread#sleep(int) too
example Thread#sleep(int) during EDT
put all together
EDIT
don't use reserved words as class, method, variable, whatever Name in the Programing languages (meaning cos)
your code works by implements all three options that I post here,
What do you mean "it doesn't work"? It works for me. How are you trying to use this code, and what errors or problems are you having when you run it? Myself, I'd use a SwingWorker though and I'd set the JLabel's text via the SwingWorker's publish/process method pair. To learn more on how to use this, please see this tutorial: Concurrency in Swing
Edit
Actually, an easier way to accomplish what you want is to not use threads or Runnables directly at all but to use a Swing Timer as they're built for just this case. For more on this, please check out the Swing Timer Tutorial
I gather that you want the function licznik() to run in a separate thread. You create a Runnable, but you have to do something more to make its run() method execute. There are a couple of ways to do this:
Runnable r1 = new Runnable(){
public void run(){
licznik();
}
};
new Thread(r1).start();
or you can just subclass Thread directly:
Thread r1 = new Thread(){
public void run(){
licznik();
}
};
r1.start();
Runnable interface has no method licznik(). You can create class that implements Runnable with licznik() method.
Or if you do not need to reuse this method and use it just once, then the fastest way is to move its implementation inside new Runnable() block
Runnable r1 = new Runnable(){
public void run(){
this.licznik();
}
public void licznik(){
while (a < 60){
a = a + 1 ;
labeler.setText(Integer.toString(a));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
Look on GitHub under at https://github.com/greggwon/Ham. Look at the source code in https://github.com/greggwon/Ham/blob/master/SwingUtil/src/org/wonderly/swing/ComponentUpdateThread.java to see how I've packaged this whole detail into a single class which uses anonymous inner classes to do the work. It would be possible to change this to lambdas now, but I have not used Java in several years and thus haven't made that change.
new ComponentUpdateThread( new Action[] { add, del, edit } ) {
public void setup() {
super.setup();
list.setEnabled(false);
list.clearSelection();
}
public Object construct() {
try {
Vector v = remote.getData();
Collections.sort( v );
return v;
} catch( Exception ex ) {
reportException(ex);
}
return null;
}
public void finished() {
try {
Vector v = (Vector)getValue();
if( v != null ) list.setListData(v);
} finally {
super.finished();
list.setEnabled(true);
edit.setEnabled(false);
del.setEnaled(false);
}
}
}.start();
With this style of work, you can use final values from surrounding blocks or other class visible data to control various aspects of what happens before, during and after background thread execution.
I've change this code around over the years in various ways and there are other variations of this that exist.
The arguments to the ComponentUpdateThread constructor are controls/actions to be "disabled" while the background thread is running. Other enable/disable activities can be more literally embedded into the activities in setup() and finished() (which are run in the AWT event thread) before "construct" is run in the background thread.