Reload JPanel every X seconds (with threads?) - java

I have a settings tab in my program. The data you can set there, is not only changeable from this panel. That's why I want to reload this data like every 5 seconds. I think this has to be done with an extra Thread, but my knowledge about Threads is minimal. I already have a reload method for this purpose.
What should I use to do this (and how...)?

Use a ScheduledExecutorService:
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(yourRunnable, 5, 5, SECONDS);
Then reload your JPanel in yourRunnable (just follow the example from the JavaDocs).

but my knowledge about Threads is minimal...
You absolutely need to learn about threads in general, and the Java Concurrency Tutorial can help.
Then you should learn about concurrency in Swing in particular.
Draw your GUI's graphic representation of the data in your JPanel's paintComponent(...) method, or perhaps better, in a BufferedImage that is then displayed inside of paintComponent(...).
Reload the data in a background thread such as a SwingWorker. This Worker can have a java.util.Timer or a ScheduledExecutorService as per syb0rg's answer (1+ to syb0rg's answer) that requests and obtains new data every 5 seconds.
Then call repaint() from the Swing event thread after your data is changed. If using a SwingWorker, the process/publish method pair could help with this. You could publish your data to the Swing event thread with this.

You could use Timer to update periodically your data.

Related

What does SwingUtilities.invokeLater do? [duplicate]

This question already has answers here:
SwingUtilities.invokeLater() why is it needed?
(7 answers)
Closed 7 years ago.
What does SwingUtilities.invokeLater do? Is it just delaying the execution of a block of codes inside its run method? What is the difference between calling an action within the invokeLater function or simply calling it at the end of the thread we want to be executed? Can anyone help me with what really does the invokeLater function do?
As other answers have said, it executes your Runnable on the AWT event-dispatching thread. But why would you want to do that? Because the Swing data structures aren't thread-safe, so to provide programmers with an easily-achievable way of preventing concurrent access to them, the Swing designers laid down the rule that all code that accesses them must run on the same thread. That happens automatically for event-handling and display maintenance code, but if you've initiated a long-running action - on a new thread, of course - how can you signal its progress or completion? You have to modify a Swing control, and you have to do it from the event-dispatching thread. Hence invokeLater.
It will run the piece of code on the AWT thread. Which lets you modify the GUI from other threads.
From Docs:
Causes doRun.run() to be executed
asynchronously on the AWT event
dispatching thread. This will happen
after all pending AWT events have been
processed. This method should be used
when an application thread needs to
update the GUI.
As already noted, InvokeLater allows you to safely call methods in swing classes when you are not running on the EventQueue to begin with. However, you can simplify your code and your life by accessing other fields and classes only from the EventQueue. They can work with swing and each other without all the hassles of multi-threading. If you have started another thread, use InvokeLater to get back to the EventQueue as quickly as possible and minimize the number of fields that must be synchronized or otherwise guarded.
If you need to make the most of multiple cores you will have to reduce your use of the EventQueue, and you will have to pay a big price in complexity.
that's would be Comment, but looks like as longer as..., just basic stuff
1/ create own EDT for correct update to the GUI, f.e. if you are executed some code by using plain vanilla Thread, java.util.Timer, Executor .. more here
2/ helps with set Focus to the JComponents iof there are some Listeners because if is there f.e. DocumentListener then you hard to set Focus to the desired JComponents
3/ delay code executions block and move that to the ends of EDT
Note that you eventually get a call to your doRun.run( ) method for every time you call invokeLater(doRun). So if you call it ten times before the event thread gets an opportunity to perform its processing, you are likely to then get ten successive calls to doRun.run( ).

Using a timer to start a SwingWorker?

The task I need to perform involves requesting some data from an external server, performing some (fairly lengthy) processing on the data, and then updating the GUI with the results of the processing. Because the server might be unresponsive, the task is well suited to a SwingWorker: the doInBackground() method gets the results, and then the done method updates the GUI.
I need this to happen once every few seconds. I know I can just use a while loop and Thread.sleep, and create a new SwingWorker after each sleep. But everything I've read frowns upon using loops and sleep. I'd like to use a timer but:
Using a swing timer seems counterproductive; since they run on the EDT, I would essentially have no reason to use SwingWorker's doInBackground method. If the server were not responsive, the GUI would be unresponsive.
Using a java.util.Timer seems a bit wasteful: it seems to create a background thread for the TimerTask(), and since I am just creating a SwingWorker to do the actual work, I'm essentially creating a background thread that creates another background thread.
Can anybody tell me what's the best solution? I'd like to stick with SwingWorker since it seems ideally suited to the task, but I would like to avoid using a while loop if I can help it.
Thanks
You could use a ScheduledExecutorService:
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
// get a scheduled executor service with one thread
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// schedule the worker at an intervall of 5 seconds
scheduler.scheduleAtFixedRate(myWorker, 0, 5, TimeUnit.SECONDS);
I don't see why you couldn't use a Swing Timer to start a Swing Worker. What have you tried?
I think you're on the right track with SwingWorker. Now you need to look at its publish and process methods. As your processing progresses, you publish() an object from the background thread then the process() method is called on the Swing(EDT) thread so you can update the gui.
This way there aren't a bunch of timers and other threads to coordinate.
The javadocs have a straightforward example with prime numbers:
http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html
How large is the set of data you are retrieving? If it is fairly small I would completely detach the task of fetching/processing and displaying.
Use some sort of in memory cache to hold the most recently processed set of data.
Use a javax.swing.Timer to update the GUI with the cached data.
Use a java.util.Timer to fetch the data from the database, process it and update the cache.
Be careful of synchronisation issues between the two times on your cache. You don't want your swing timer grabbing data at the same time as the other timer is updating it.

Scheduled tasks (Timers) in Swing

A GUI using Swing, must update part of its UI from a every 5 seconds. (output.setPage(url))
The code is running in a timer, but on every update, the GUI hangs.
How do I avoid making the UI hang?
Here is my code:
<insert code here>
Assuming you are using a setPage() method of JEditorPane, the event dispatch thread is blocked while the page is fetched. Using SwingWorker is a reasonable alternative.
Addendum: SwingWorker is convenient for showing progress; but as an alternative, you might look into the asynchronous feature of setPage() using setAsynchronousLoadPriority().

Thread.sleep and repainting

I have a panel that displays text. I want the panel to change its text and then have the application pause before anything else happens. I'm using Thread.sleep(1000). For some reason, though, the application doesn't finish painting the panel before Thread.sleep gets called (the text doesn't get changed). I also tried this:
board.invalidate();
board.setLeftMessage("Not");
board.setRightMessage("Here");
board.revalidate();
Date current = new Date();
long timeNow = current.getTime();
Date newDate = new Date(timeNow + 1000);
while (current.before(newDate))
current = new Date();
but no luck there either. Anyone have a suggestion?
Thanks so much.
You are blocking the AWT Event Dispatch Thread (EDT). The EDT handle repainting and input events, so your code need not be multithreaded (which would be effectively impossible). Use javax.swing.Timer to send an event later on the EDT. (Do not confuse javax.swing.Timer with java.util.Timer!)
Take a look at javax.swing.Timer - I think this is what you'll need to accomplish this.
EDIT #1: The Sun\Oracle documentation actually suggests using Swing Timers from here.
In general, we recommend using Swing
timers rather than general-purpose
timers for GUI-related tasks because
Swing timers all share the same,
pre-existing timer thread and the
GUI-related task automatically
executes on the event-dispatch thread.
However, you might use a
general-purpose timer if you don't
plan on touching the GUI from the
timer, or need to perform lengthy
processing.
EDIT #2: It looks like there are some good basic tutorials here.
EDIT #3: Removed suggestion of using TimerTask - in this case it would be a bad idea.
Your "long running task" needs to run in a separate Thread. Then when you want to update the GUI you need the code to run on the Event Dispatch Thread (EDT). Then you can ask the separate Thread to sleep and it won't affect the painting of the GUI.
You should be able to use a SwingWorker for this. Read the section from the Swing tutorial on Concurrency for more information.
If you don't like using the SwingWorker, then you need to create your own Thread and wrap the code that updates the GUI in a SwingUtilities.invokeLater().
You shouldn't be updating GUI components from your main thread. Use SwingUtilities.invokeLater to schedule your update on the event thread:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
board.invalidate();
board.setLeftMessage("Not");
board.setRightMessage("Here");
board.revalidate();
}
};
Thread.sleep is a long running task. When you a running such a task in the EDT it blocks all repaint requests from being executed. All repaint requests which are pending and which were sent during the sleep phase are queued for future processing.
As a result when the EDT comes out of the sleep phase it coalesce all such repaint request (if coalescing is enabled which is the default property) into a single repaint which gets executed. If coalescing is not enabled then all queued request are executed serially without any time gap in between. As a result it seems that the UI did not update.
To correct the situation use a timer which triggers periodically after specific intervals of time.

Swing: Passing a value back to the UI from a scheduled thread

I have a system tray UI in Java that requires a schedule database poll. What is the best method for spawning a new thread and notifying the UI?
I'm new to Swing and it's threading model.
SwingWorker is the exact thing designed to do this.
It allows you to run a task that won't block the GUI and then return a value and update the GUI when it is done.
Java has a great tutorial on how to use SwingWorker.
Basically do the database pull in the doInBackground() method. And, in the done() method, update your GUI.
As jinguy mentioned SwingWorker should be the first place you look at.
Wikipedia, of all places, has some interesting examples that may be good to look at before you tackle the JavaDocs.
As jjnguy mentioned, SwingWorker helps abstract away the complexity here, but basically you do the work in a new thread, and when the method comes back, you need to update the GUI in the swing thread. If you aren't using SwingWorker, the underlying method is SwingUtilities (or EventQueue) .invokeLater(Runnable).
Do not update anything Swing related (including models) outside of the swing queue, unpredictable things will happen. And don't attempt to hold a reference to the queue and use that, as queues are suspended and replaced (if for example you open a model dialog box).

Categories

Resources