I am developing a big Swing application with a lot of components that are loaded at the launch of the application. The process of loading the components, setting their attributes and adding them to panels takes a few seconds.
The applications UI freezes during the loading of the application. I would like to show a splash loading screen for the time of loading the whole UI, but the splash also freezes.
If I move the UI construction to another Thread, this would be the EDT Threading violation.
What is the correct way to load such Swing interface without freezing the UI?
I have seen existing answers on this topic but they cover the case where external tasks not related to Swing are blocking the UI, but here, the construction of the complex UI itself is blocking the UI refreshing (splash screen progress bar).
There is two possibilities to solve this problem.
You split your UI initialization in small chunks and call each chunk using SwingUtilities.invokeLater. Each invokeLater triggers repainting, so your GUI can be refreshed. This should be the preferred way.
When you have problems on splitting your appllication into small independent chunks, you can "misaply" the Foxtrot lib to provide synchron breaks for UI repainting
public static void sleepNonBlocking(long millis) {
Worker.post(new Job() {
#Override
public Object run() {
try {
Thread.sleep(millis);
} catch (Exception e) {
// nothing
}
return null;
}
});
}
You need to call this method on some places in your application to force repainitng.
Related
Currently I have a start menu for a game with a button which transform my menu background image from a PNG into a GIF after a button press. Now, I want my code to wait until the GIF animation is over. After that, it should continue normally (by opening a new JFrame with the actual game).
I've read some stuff about a swing timer, although I'm unsure of how to implement this as I am a Java beginner.
private ImageIcon a = new ImageIcon("a.png");
private ImageIcon b = new ImageIcon("b.gif");
class AddInterestListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
bgLabel.setIcon(b); //replace PNG with GIF
//This here is where I want my delay to happen without freezing the GUI
JFrame game = new JFrame(); //Actual game frame starting etc.
}
}
Any suggestions?
You can't use blocking methods like Thread.sleep() or Thread.join() in Swing EDT. This will freeze the UI as Swing EDT handles all the UI refresh events.
You must implement this with events, first event blocks the UI second unblocks it. If you know the GIF animation length you can use SwingUtils.invokeLater() to run an async thread with that will fire the event after a fixed delay.
[Edit] apparently there was already an answer and I don't know if that answer , or my answer for that matter, fits in your software. One thing I can say for sure is that my method allows you to use custom scaling algorithms etc instead of the built-in one (but you probably don't need that either).
Afaik it is not possible to monitor the progress of a GIF displayed using SWING. You'll have to make your own GIF decoder/animator in order for you to 'detect' when the GIF is about to loop or end (yes animated GIFs can end).
For that I used a 3rd party loader from https://github.com/DhyanB/Open-Imaging to obtain the individual frames and timing information. No guarantee that this is still the best library out there as I found this little over a year ago.
From there on you'll need to write your own animator using javax.swing.Timer or similar.
This is my code so far:
// Imported Classes
public class Timer extends Applet
{
public void paint (Graphics page) throws InterruptedException
{
Thread.sleep(1000);
}
}
I just want to know how I can get this to work. I've used the Thread.sleep() method in other code before, but never with Graphics. I don't have much experience with Exceptions either, I usually try my best to avoid or correct them.
You should never call methods such as Thread.sleep on the event dispatch thread (i.e. in paint methods). This will render the whole GUI unresponsive.
You should instead use timers such as SwingTimer to perform animations etc. See the following related questions:
how to use a swing timer to start/stop animation
Java Applet Thread Animation
How to make applet animation?
Drawing images continuously in Java Applet
I am using the latest Eclipse, and GWT Designer, to make a swing application in Java.
The main function in my application window (which is a javax.swing.JFrame) in the auto generated by the tools looks like this:
/* launch the application */
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
AppWindow window = new AppWindow();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
This seems like a lot of noise around what could have been just this:
public static void main(String[] args) {
try {
AppWindow window = new AppWindow();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
I have read that the EventQueue.InvokeLater technique is required in some situations, and another question asks where to use it here.
My question is simpler; Why do this automatically in the code generator here? Why should main return quickly and let the application window get created later by the event queue? Wouldn't blocking be exactly the point? Why is the JFrame auto-generated designer doing this EventQueue stuff? I have tried to see some difference in the start up and showing of forms whether this code is done the simpler way or the harder way, and I can only conclude provisionally that this has some benefits that are not visible in tiny demo apps made by beginners like me, and that perhaps in real-world large complex Jframe based classes, there is some benefit to this delaying/queuing strategy?
Depending on your application and how it's being used, it's possible that there could be something that is drawing on the screen (and thus using the EventQueue) before or during the call to your main method. Calls that modify any UI components should be made on the Event Dispatch Thread, and this includes setting the application visible.
So just to be safe, it's a good practice to start your application on the EDT.
Why do this automatically in the code generator here?
It won't hurt, it's easy to generate, and it's considered good practice.
Why should main return quickly and let the application window get created later by the event queue?
It's possible that the main method is being called from some other application that is using the EDT and may have already drawn something on screen. If you draw your application directly in main, it's possible that your application may be altering some component that is in the process of being handled by something on the EDT, and potentially already drawn on the screen.
So just to be safe in case this situation ever happens, you should leave it up to the EDT to draw your application so it can do it when it won't interfere with anything else.
Wouldn't blocking be exactly the point?
Unless something else is calling main other than the JVM process that your user started by double-clicking the desktop icon, it's not going to make a difference when main returns as long as there is something on the screen.
I can only conclude provisionally that this has some benefits that are not visible in tiny demo apps made by beginners like me
You're right - most of the time it's probably not gonna make a difference, but I presume they included it because it was easy to generate & implement, it can't hurt, and it would exemplify good practice.
1) why is building Swing GUI inside try-catch-finally, I can't see any reason(s) for that, split create non thread safe GUI and non thread safe code to the separates threads,
2) Swing isn't thread safe, then correct is in all cases that pack() + setVisible(true) would be
last GUI rellated code lines
wrapped into invokeLater
forgot for examples from some code ExamplesDepots, this forum, another forums, sure these code works, but with risk that whatever/everything could happen
correct Swing GUI launch
for example
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
AppWindow window = new AppWindow();
window.frame.setVisible(true);
}
});
}
3) is there some Serializable, Custom L&F then (better would be) wrap into invokeAndWait
I have developed a simple MIDI application that allows me to play MIDI notes, in order for the user to be able to interact with the UI whilst the MIDI sounds are playing I have put the logic necessary in an anonymous subclass like so:
public static void Play()
{
new Thread(new Runnable()
{
public void run()
{
if (!_sequencer.isRunning())
{
try
{
_sequencer.setSequence(_sequence);
_sequencer.start();
}
catch (Exception e)
{
Logger.Add(e.getMessage());
}
}
}
}).start();
}
Although the music begins to play the UI still fails to respond when I click on a ComboBox for example, I have something similar working fine in C#.
Is there some sort of caveat to thread in Java that would explain the behaviour i'm seeing?
(The Java API I'm using if it helps - javax.sound.midi)
Thanks for your time.
Edit:
Click around the UI a little more and noticed something interesting; everything seems to work fine with exception to two ComboBoxes in the top left most corner, I've tried deleting them and replacing them but makes no difference. The boxes change to blue when clicked (as it would normally) but the drop down box does not appear below it and the colour does not return to it's default when focus is on another UI component. Could it be a bug introduced by NetBeans perhaps?
Example:
Edit 2:
Well after much trial and error I have finally found the cause of the problem, the threading works great. The problem was that NetBeans has somehow realigned my UI components which causes the Window to fill the screen, manually resizing and testing the ComboBoxes showed that they actually worked fine.
Thanks for everybodys feedback!
What does the combo box do when it's clicked? Does it interact with the _sequencer? Furthermore, was your sequencer created on the UI thread? I would suspect that while you're invoking the start method on a separate thread, the sequencer still runs on the context where it was created (i.e. UI thread).
Try creating the sequencer on the playing thread (i.e. NOT the UI thread) and see if that frees up your UI.
Well after much trial and error I have finally found the cause of the problem, the threading works great. The problem was that NetBeans has somehow realigned my UI components which causes the Window to fill the screen, manually resizing and testing the ComboBoxes showed that they actually worked fine.
I've built a form with Netbeans's visual editor. When I press one of the buttons it should do the following :
set it to disabled
perform a task that takes some time
when the task finishes the button will be enabled again
However, the following happens:
the button remains in a pressed state until the task finishes
when the task finishes, the enabling/disabling of buttons will be very fast (they will happen, but you won't notice them)
This behaviour is not something I want. I tried using repaint on the JButton, on the JFrame and even on the JPanel containing the button, but I can't seem to get it to do what I want. Some hints?
When you do work in a button callback, you are stalling the GUI painting thread until it completes.
What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to update the UI when it completes. Not using invokeLater is not thread safe, and is generally bad mojo.
A basic example is:
button.setEnabled(false);
new Thread(new Runnable() {
public void run() {
// Do heavy lifting here
SwingUtilies.invokeLater(new Runnable() {
public void run() {
button.setEnabled(true);
}
});
}
}).start();
When you do things in a button callback, you are essentially stalling the gui painting thread - not just for the button, but for ANY gui painting. (Try covering the interface with another window and then exposing it again - it won't repaint until the task is finished!)
What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to do the enabling of the button. invokeLater forces the button enable to happen in the gui painting thread.
You may want to set a busy cursor or otherwise lock the interface while the long-running thread is operating.
The Concurrency in Swing tutorial from Sun is well worth a read. Excellent explanation and background reading, including the event dispatching thread, using worker threads, etc
You need to do the task that takes some time in a different thread.
The reason the button is blocking is because the work is being done in the same thread that draws the button. Once the work is done the button can do the rest of what you tell it to.
If you use a different thread the thread will go do the task while the drawing code can continue drawing the form.