So I have this one button in a class that extends JPanel:
startTSP.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
scatterPlotPanel.findShortestPath();
}
});
And my problem is, that the method inside the actionListener (by the name findShortestPath() ), is one that has to constantly call repaint() on another JPanel. The problem is, that once I click the button startTSP, it doesn't repaint or anything. It just hangs until the method completes running (which can take a very long time given the input). The only time it repaints is right at the end. If it matters, I can post the method for findShortestPath() but I don't think it matters whats in it, because my question is, how do I get it to not hang and just carry out the method normally as if the method was called in the main class?
I'm sorry if the question is too vague, I will try my best to explain it if anyone doesn't understand (just mention so in the comments).
Also, basically what this method is doing is, permuting through an arraylist of coordinate points recursively, and finding the shortest path between the list of points. It is a simple brute force TSP solution.
If you have a long method executed in the event dispatch thread, it blocks the event dispatch thread, and thus prevents it from doing its job: dispatching other events, and painting the UI. This effectively freezes the GUI until the long method returns. So you must not have long, blocking methods in the event dispatch thread.
Use a SwingWorker to execute your long-running method in a separate thread. Or if what you want is simply a repetitive event to happen every N milliseconds on the EDT, then use a javax.swing.Timer.
You can try using another thread for painting on the other panel. This way you will have two independent threads doing different jobs.
You can use signals for communicating between these threads.
Related
I'm trying to make "Realtime Feeling JTable". I made GUI with JTable, and then there is a button named START. When I click this button, the program will call below start() method. (It's just an example). Adding row is no problem, It works well. But the problem is, rows come to Jtable just at one time. (maybe program finish start() method, The rows come up with at all together.) There is 1000 more statement in my start() method. It's not realtime feeling. I shoud wait until start method finished. and then 1000 rows added at once.
void start(){
int a = 3+3;
addRow(new String[]{"a is 6"})
int b = 4+4;
addRow(new String[]{"b is 8 "})
}
What I'm gonna do is just "Real Time Feeling", I want to see adding row one by one, not at once. Could you understand my purpose? Imagine "Fiddler" Program. you can understand easily. Can anyone help me? Should I make a Thread?
Thanks for reading, and sorry for ackward expression(english is not my language)
This is because of the way Swing threading works. There is one single thread that handles all rendering, updating, etc. Your processing of the start() method is happening inside that render thread so nothing else on the GUI will respond until you return from the method.
What you need to do is use a SwingWorker or a ThreadPoolExecutor and farm the processing off to another thread. That can then update the GUI on a regular basis as the processing continues.
http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Note that by doing this people can potentially click the button multiple times and call start() again before the first one has finished. You will need to consider and handle that case - for example by disabling the button until the first one completes.
Use a Swing based Timer. In the actionPerformed(..) add a new record. See How to Use Swing Timers for details.
In swing there is a thread called EDT (Event Dispatcher Thread) that is responsible for GUI to behave like a real time, user friednly app. If you interfer EDT with a long running method you will freeze your app GUI. You have to do long running tasks in background (in a thread different than EDT). You can do this by using javax.swing.SwingWorker<T,V> or javax.swing.Timer
I had a program I got some help from here(how do I make my program check the stock market value every hour[java]) and I have been reading about swing worker ever since. I still have not fixed the program as I reread the official doc like 3 times and I am still a bit confused. Here is what I think I understand and please correct me if I am wrong.
You use SwingWorker when you have a long background process, and you put the SwingWorker inside your action performed? Once you create your process if you want it to update the GUI you make it return a value and you get() the value from the SwingWorker done() method. I am confused "where" to initialize SwingWorker because I want to say its the action performed, but isn't that where SwingInvokeLater is involved? if that is the case than what is the difference between the two. I believe SwingInvokeLater and done() both update your GUI by being run on the EDT.
I feel lost just writing that all out, I feel like I am getting closer to understanding but for some reason it just wont click. I don't like the examples the official doc provides, I guess I just don't see the whole picture. The official doc said to initialize your GUI inside a SwingInvokeLater but I don't understand the difference between that and just initializing my GUI in main().
Your questions / my replies:
You use SwingWorker when you have a long background process, and you put the SwingWorker inside your action performed?
It can go inside of an ActionListener, yes. You create it and execute it where it is needed, no more, and no less.
Once you create your process if you want it to update the GUI you make it return a value and you get() the value from the SwingWorker done() method.
That's one way to update the GUI. You can also use the publish/process method pair to update the GUI with interim results. You can also use a PropertyChangeListener attached to a SwingWorker to update the GUI. No matter what, it's usually a good idea to call get() somewhere, even if nothing is returned, as this will allow your Swing GUI to become aware of any exceptions that might have been thrown during the running of your SwingWorker.
I am confused "where" to initialize SwingWorker because I want to say its the action performed, but isn't that where SwingInvokeLater is involved?
The SwingUtilities.invokeLater(...) is used to queue code onto the Swing event thread, the EDT. This is not necessary inside of an ActionListener because its code is already called on the Swing event thread.
if that is the case than what is the difference between the two.
They are completely different. Again, invokeLater(...) is to call code on the event thread, and a SwingWorker is for calling long-running code off of the event thread.
I believe SwingInvokeLater and done() both update your GUI by being run on the EDT.
Yes they both can.
The official doc said to initialize your GUI inside a SwingInvokeLater but I don't understand the difference between that and just initializing my GUI in main().
By using SwingUtilities.invokeLater(...) you guarantee that the code passed into it is run on the EDT, the Event Dispatch Thread. If you don't do this, you don't have this guarantee. While many Swing programs will run most of the time without doing this, they may (and do) sometimes fail if this care isn't taken.
Edit
So I guess I am heading in the right direction. If I have a process that checks a value every hour on a website, since its a short process(takes a second) will it be better to use invokeLater()?
You could use some type of timer for this, possibly a ScheduledExecutorService which would be run in the background of Swing, perhaps with a SwingWorker. Then the process would be called background to the Swing thread, and you can update the GUI via publish/process.
Does the entire block of code go inside invokeLater or just the updating the GUI part. I feel like the entire code should go inside invokeLater but someone told me just to update the GUI such as (text.setText()) inside invokeLater().
As mentioned, your GUI needs to start up inside of a Runnable that is passed into a call to invokeLater(...). As for while your program is running, if the background code is run using a SwingWorker then usually there is no need a call to invokeLater(...). That is one of the reasons for using a SwingWorker rather than a plain vanilla Thread.
Edit 2
You state:
one last question I just came across while testing..inside an action performed I made the buttonclick change textfield to say hi, then I put in a try catch for Thread.sleep(1000) then change textfield to say ho. How come the result only outputs ho? it doesnt show hi, I tested with numbers and can see the program locking up. I know using a thread will fix this but just wondering why it wont display the output if I put a sleep.
When you call Thread.sleep(...) you put the calling thread, here the Swing Event Dispatch Thread or EDT, to sleep. Since it is responsible for all Swing painting and user interaction, the whole application goes to sleep, and the GUI is unable to perform any updates until the sleep has completed. This is precisely why you must use a background thread to perform this sort of thing.
I have a thread that does display updates on my JFrame by using SwingUtilities.invokeLater. The thread's speed is adjustable, and when it set to full speed (no sleeping between updates), my program gets slowed down badly. I guess the problem is my thread is generating too much SwingUtilities.invokeLater events that JFrame's thread can not consume. So is there anything I can do on my thread to remove previously added but not consumed events? Or should I use some other ways to update JFrame without using SwingUtilities.invokeLater?
Thanks in advance.
This might be a perfect job for SwingWorker. You can publish incremental updates, and SwingWorker will batch them to solve the performance problem:
Because the process method is invoked asynchronously on the Event Dispatch Thread multiple invocations to the publish method might occur before the process method is executed. For performance purposes all these invocations are coalesced into one invocation with concatenated arguments.
The code you want to run on the EDT, you add by implementing process(). The list of updates is passed to you in the argument.
It sounds like you want to avoid saturating the event dispatch thread. The class javax.swing.Timer, discussed in How to Use Swing Timers, includes setCoalesce(), which "coalesces multiple pending ActionEvent firings." It may an alternative way to pace your updates.
As mentioned here, SwingWorker is limited to 33 Hz.
Can you use a simple repaint()? The advantage of that is that multiple calls are merged into one.
(Elaboration added)
Let's say you are constantly updating your GPS location and displaying it in two text fields. Your thread to do the updating:
run() {
while (keepGoing) {
Point myLocation = computeMyLocation();
locationModel.setLocation(myLocation);
locationComponent.repaint();
}
}
then, in MyLocationComponent
#Override
public void paintComponent(Graphics g) {
Point myLocation = locationModel.getLocation();
// you'd really want a NumberFormat
latitudeTextArea.setText(String.valueOf(myLocation.y));
longitudeTextArea.setText(String.valueOf(myLocation.x));
super.paintComponent(g);
}
The advantage is that this splits the model from the view (if you think of the thread as the controller, this is MVC), and all the threading should work - no need for any invokeLater(). One disadvantage is that your thread needs to know all of the JComponents that need to be updated. In a "real" scenario you'd probably fire events to listeners that trigger the repaints, either from the "controller" (your thread) or from the model.
NOTE: As pointed out by #trashgod, in LocationModel, the getLocation() and setLocation() methods should be synchronized so that updates appear immediately.
I have a MouseListener Thread where this method is called each time there is a click:
public void mousePressed(MouseEvent event){
//my Logic here
}
Now what happens is that there are so many clicks in little time, while my logic here, takes more time to process. What i was expecting is that further clicks will be discarded and this method will continue on the latest upcoming clicks.
But what is happening that while the logic is processing, the incoming clics are queued and even when clicking is stopped, qued clicks keep calling this mousePressed method, as a result i have multiple delayed executions.
Unfortunatelly i do not have control over the Listener (why it ques and keep sending delayed clicks), so in such a scenario, can you tell me what is the efficient way of handling such that i do not make a que due to delay in my processing.
I think the most obvious way would be using another thread, but i am wondering this might trigger so many threads in little time, or is there any way i can lock just one thread while the rest of clicks just go through empty loop?
Updates: See Phillip's comments as to why this doesn't work for Swing!
It's unfortunate that you don't have access to the eventListener. The ideal solution would be to unregister the callback while it is being executed.
Here's a solution that emulates unregistering the callback function while it is being executed, assuming you want queued clicks to go away:
private AtomicBoolean engaged = new AtomicBoolean(); // thread-safe boolean
public void mousePressed(MouseEvent event){
if (!engaged.get()) {
engaged.set(true);
// your logic here
engaged.set(false);
}
}
The AtomicBoolean acts as a test-and-set lock preventing multiple threads concurrently running the pressed event callback. Queued presses will be dissipated during the lock.
One solution would be to access the component on which the user clicked and disable it during the processing of the MouseListener. This way it doesn't accept new clicks. This would have the additional benefit of giving the user visual feedback that he is not supposed to click on the component at this time.
You can access the component through the MouseEvent:
public void mousePressed(MouseEvent event) {
event.getComponent().setEnabled(false);
try {
// ....
} finally {
event.getComponent().setEnabled(true);
}
In general though you shouldn't do too much computation in the Swing event listener thread, because Swing also needs it for processing other events and painting. You should use separate Threads for doing actual work and just start them in the MouseListener. You can also use an ExecutorService to simplify this.
It would still be a good idea to disable the gui component during the whole computation in order to give the user feedback.
Edit:
This solution of course depends on Swing. Any similar solution depends on the details of your GUI library, so if you use your own GUI library, you are one your own.
You can of course use a general solution involving separate threads for doing the work. I really recommend an ExecutorService here, were you don't need to care about the details and just submit tasks to execute. You can then have a simple volatile boolean variable which indicates whether the action is currently being executed and new clicks should be ignored.
I have a JFrame with a CardLayout set as its layout manager. This has two JPanel subclasses in it. One is a panel, WordsLoadingPanel, which displays the text "Loading words..." and has a JProgressBar. The other has to actually load the words. This takes a while (about 10-14 seconds for 100 words; it's a pretty selective algorithm), so I want to assure the user that the program is still working. I made the loading algorithm in the panel fire a property change with firePropertyChange(String, int, int), and the WordsLoadingPanel is catching the change just fine - I know this because I added a listener for this event to perform a println, and it works. However, when I change the println to actually changing the JProgressBar's value, it doesn't do anything. I know I'm changing the value right, because if I set the value before the algorithm starts, it works, and it works on the last iteration of the loop. I'm guessing this is because my algorithm is eating the computing power and won't let JProgressBar update.
So, my question is: How do I make my algorithm wait for Swing (would this be the AWT Dispatching Thread?) to finish updating the progress bar before continuing? I've tried:
Thread.yield in each iteration of the loop
Thread.sleep(1000L) in each iteration of the loop, in a try/catch
putting everything in the loop in a SwingUtilities.invokeLater(Runnable)
putting only the CPU-intensive algorithm in a SwingUtilities.invokeLater(Runnable)
EDIT: To further support my hypothesis of the CPU-eating algorithm (sounds like a children's story…), when I set the JProgressBar to indeterminate, it only starts moving after the algorithm finishes.
Does anyone have any suggestions?
Thanks!
To do expensive operations in background, consider using the SwingWorker class. The documentation has examples on how to use it to do tasks that interact with the user interface in a separate thread, including progress display in JProgressBars.
If you have trouble understanding how the class works, consider:
SwingWorker is a generic class that takes two parameters: T, and V
The doInBackground method returns T and is executed in a separate thread.
Since Swing may only be manipulated in the Event Dispatch Thread, you may not manipulate Swing in doInBackground.
The process method takes a List<V> as a parameter and is called asynchronously on the Event Dispatch Thread.
The publish method takes V... arguments and sends them for processing in the process method.
In conclusion:
T is the type of the result of the computation, if any.
V is the type of the data needed to manipulate the user interface.
Your algorithm should run entirely in doInBackground.
The user interface should be manipulated in the process method.
Your algorithm should use publish to send data to the process method.
OK, I've solved it. For anyone who may have a similar problem, my solution was to change the method which begun the algorithm from executing it synchonously to asynchronously (with new Thread(Runnable).start). So, my code is now
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Thread(new Runnable () {
public void run () {
window.keyboardTrainingPanel.initialize();
}
}).start();
}
});
I hope this can help someone! However, if there is a better way to do this, feel free to notify me.