I have tried a lot, but can't seem to get it to work.
I was told to use EDT with the following example.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Modify the GUI here
}
});
I have read on this topic a lot and still don't understand. I get what a thread is, but the .invokeLater still makes no sense to me. Honestly if you can explain in detail this it would be a big help!
Goal of Program: To get the randomly generated key that is constantly created every second to update itself afterward in the GUI.
So there is an EDT (Event Dispatch Thread). All actions that appear on your screen are executed by the EDT. There is only one EDT per Swing application.
You are in some arbitrary thread and you want to update the GUI through that thread? Well like I said there is only one EDT for each swing application, so you have to tell that EDT to display the label (or whatever context you want).
The idea here, is you push this Runnable onto a queue that the EDT pulls from. Eventually, your runnable will be processed by the EDT when all other actions before it are completed.
I recommend you get the book Filthy Rich Clients. There's a chapter where they explain Swing's threading model to great detail.
Basically in Swing, any code that modifies the GUI should be executed in the Event Dispatcher Thread. The SwingUtilities class that you are using there provides you with an easy way to post events to the event queue that is then dispatched by the EDT. That's what the invokeLater method does, it takes a new Runnable() as argument which is ultimately executed on the EDT.
From the book:
The invokeLater() implementation takes
care of creating and queuing a special
event that contains the Runnable. This
event is processed on the EDT in the
order it was received, just like any
other event. When its time comes, it
is dispatched by running the
Runnable’s run() method.
This is a pretty common element of all GUI programming. You have one thread that handles drawing the GUI, getting input, and running callbacks. If another thread tries to change the GUI related objects, it will conflict with the GUI thread. Say, for example, it was half way through drawing something and you change the color from a different thread.
All invokeLater does is queue up something for the GUI thread to run. By "later" it's really runs almost instantly but the current thread doesn't have to wait for it. The GUI thread may be doing a draw or waiting for a callback to return which would delay executing the code you gave it.
Needs to be a member so we can change it and still use it from an inner class
protected long secret=0;
... this needs to be in your code somewhere it'll get run...
JFrame f = new JFrame("foo");
new Thread(){
public void run() {
for(;;){
try {
sleep(1000);
} catch Interrupted (Exception ix){
return;
}
// TODO update your secret key here
// please don't use random()
SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.setTitle("secret "+x);
}
});
}
}
}).start();
....
Only ever update Swing from the EDT so that it paints properly.
When you are in the EDT ( running code in an event handler) you can call paintImmediately() if you really must.
If you're all looking to do is update the UI on a known schedule, try something like the following. This assumes that a JFrame is the component you wish to update every 1 second.
private static final int WAIT_LENGTH = 1000; // 1 second
private JFrame frame = new JFrame();
// Thread will update the UI (via updateUI() call) about every 1 second
class UIUpdater extends Thread {
#Override
void run() {
while (true) {
try {
// Update variables here
}
catch (Exception e) {
System.out.println("Error: " + e);
}
finally {
frame.repaint();
Thread.sleep(WAIT_LENGTH);
}
}
}
}
To start this thread:
UIUpdater t = new UIUpdater();
t.start();
Related
The purpose of the following code is to handle menu option choice. Because processing in method update() takes quite a bit time, I want to display info before this takes place.
My info is simply an anchor pane with label in it, which normally is set to be not visible. Unfortunately, the code below sets anchor pane visible only for a very short time after update() terminates. How can I do it properly?
public void updateRates(ActionEvent event)
{
updateInfo.setVisible(true);
update();
updateInfo.setVisible(false);
}
Most user interface libraries are single threaded. That thread is created by and controlled by the UI system. All listeners are called on that thread. When the UI thread is held up, such as when your update() call is running, the UI does not redraw at all, and keyboard events and mouse events will “pile up” in the event queue, appearing to be ignored, until the thread is allowed to continue running (at which point all of them will be processed immediately, in order).
For this reason, lengthy operations must never execute on the UI thread. One option is to run a Task in a different thread:
Task<Boolean> updater = new Task<Boolean>() {
#Override
public Boolean call() {
updateValue(true);
update();
return false;
}
};
updateInfo.visibleProperty().bind(updater.valueProperty());
new Thread(updater).start();
You could also create a Thread from a plain Runnable:
updateInfo.setVisible(true);
Runnable updater = new Runnable() {
#Override
public void run() {
update();
Platform.runLater(() -> updateInfo.setVisible(false));
}
};
new Thread(updater).start();
Platform.runLater is required in this case, because user interface objects, including all JavaFX Nodes, may only be accessed and modified in the UI thread. If your update() method is manipulating any Nodes, it will need to use Platform.runLater to do so.
The Task class has built-in support for showing work progress in the UI, but since you haven’t included the code for your update() method, I can’t tell you whether that support would be useful in your situation.
I am using a scheduledexecutorservice to perform an animation for drawing a graph(one vertex and one edge at a time). I have a problem in updating the actual UI step by step and instead I can't see the actual animation but only the final graph.
private Runnable newRunnable() {
return new Runnable() {
#Override
public void run() {
// this method just adds the graph to a JPanel
displayDiagram();
}
};
}
private void animate() {
executorService.schedule(newRunnable(), 1500, TimeUnit.MILLISECONDS);
executorService.shutdown();
try {
executorService.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
executorService = new ScheduledThreadPoolExecutor(1);
}
What I want to achieve is to be able to draw one vertex and one edge, and update the UI every time and then wait for 1500ms and do that again until the whole graph is displayed.
Method animate would be called multiple times as the graph is dynamically being created.
Your code does not obey Swing threading rules in that it makes state changes to a Swing application off of the Swing event dispatch thread. I would suggest that you use a SwingWorker for background tasks as well as its publish/process method pair to pass information back to the Swing GUI in a thread-safe manner. If you need to use other methods to create your background thread, then be sure to queue any Swing calls made within your background thread on to the Swing event queue by using something like
SwingUtilities.invokeLater(() -> {
// swing call here
});
awaitTermination is a blocking method. Instead you should either be using a SwingWorker (and taking advantage of it's publish/process functionality) or a Swing Timer.
Take a look at Concurrency in Swing, Worker Threads and SwingWorker and How to use Swing Timers for more details
Maybe something like this
I'm creating a board game using a GUI and JFrames/JPanels where you can play against the computer. I have a method called showPieces() which updates board GUI by changing the image icons on an array of buttons (which are laid out in a grid format). Once the icons have been updated the revalidate() and repaint() methods to update the GUI.
The showPieces() method has a parameter that needs to be passed to it every time it is called.
The main issue I'm having is I want the human to make a move, update the GUI, wait 1 second, the computer makes it's move and then loop until someone wins.
My basic code is the following:
do{
human.makeMove();
gui.showPieces(data);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
computer.makeMove()
gui.showPieces(data);
}while(playing);
This cause the issue where when the human player makes their move, the GUI will freeze for one second and then after the delay, both moves are made at the same time.
I hope it makes sense, but I'm a novice with Java and may have to look more into threading as I don't understand it well enough.
Thread.sleep() is done on the Event Dispatch Thread which will lock the GUI.
So If you need to wait for a specific amount of time, don't sleep in the event dispatch thread. Instead, use a timer.
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
As with most all similar Swing questions, you're putting your entire Swing GUI to sleep by calling Thread.sleep(...) on the GUI's event thread (the EDT or Event Dispatch Thread), and when during this period the GUI will not be able to update its images or interact with the user whatsoever. The solution here is not to use Thread.sleep(...) but rather to use a Swing Timer to cause your 1 second delay.
Swing Timer Tutorial.
I had a problem earlier where I was trying to add objects to my canvas but the SwingUtilities.invokeLater hadn't really done its job yet. I really need to be able to add things reliably to this JPanel but I keep running into this same error. Is there a better alternative to the below code?
private void startNewGame() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gameFrame = new InvadersGameFrame();
}
});
}
See SwingUtilities.invokeAndWait(Runnable) which:
Causes doRun.run() to be executed synchronously on the AWT event dispatching thread. This call blocks until all pending AWT events have been processed and (then) doRun.run() returns. This method should be used when an application thread needs to update the GUI.
I modified the sample code for Activator posted on the Oracle/Sun AWT tutorial page here
The modification is as follows
f.add(new MyCanvas(f.getGraphicsConfiguration()),
BorderLayout.CENTER);
The paint method in MyCanvas is overridden as follows
MyCustomRunnable mcr = new MyCustomRunnable();
Thread th = new Thread(mcr);
th.start();
while(Thread.currentThread().isAlive()){
mcr.getData();
//do UI stuff
Thread.yield();
}
Similarly MyCustomRunnable has a corresponding loop in run()
public void run(){
while(Thread.currentThread().isAlive){
//do Stuff
Thread.yield();
}
}
The Runnable, and Canvas paint (both) run a loop. With this bit of code running, System Menu close on the UI window are not invoked. Why?
The short answer is that paint() is called on the event thread, which is also the thread that handles all UI events, and you are taking over that thread and putting it into an infinite loop.
When you are doing this in the paint method...
while(Thread.currentThread().isAlive()){
...
}
... the "current thread" you are working on is the same thread you entered that method on, which is the "event dispatch thread". I would guess that what you really wanted was to run a background thread that periodically repaints the view. You could do this in the constructor of your AWT component:
new Thread() {
public void run() {
while(runBackgroundThread) {
mcr.getData();
repaint();
}
}
}.start();
Note that I also created a boolean variable runBackgroundThread which would be a volatile field in the component class. Setting it false would stop the loading thread. In contrast, Thread.currentThread().isAlive() will always be true - the currently running thread must by definition be alive.
0Almost certainly because your thread is keeping control and not letting the window events thread to run.
There is no point starting a background thread if all you are going to do is to wait for it to finish. Instead you can make the background thread do what you want to happen when it finishes.