Okay, so I've got my menu system up and working from a JFrame. Everything seems to work really well, up until I click the button which starts a canvas. Now what the canvas does is intialize a JFrame which extends Canvas so I can't use a thread. Once the frame is up and running it calls a method which has a while true {} after this I am unable to close the frame. This has never been an issue before when running the canvas application using static void main. How can I fix this issue of the new JFrame not closing?
How can I fix this issue of the new JFrame not closing?
Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead of creating an infinite loop, implement a SwingWorker for long running tasks. See Concurrency in Swing for more details.
1. Make this a rule of thumb when working with GUI application, that Always keep the UI work on the UI thread and Non-UI work on the Non-UI thread.
2. Second doNot mix up SWING AND AWT.
3. The main() method in Java Gui is not long lived, after scheduling the work in the Event Dispatcher Thread (EDT) the main() method quits. Now its solely the responsibility of the EDT to handle the GUI.
4. So never mixup the Non-UI process-intensive work, with the EDT.
Use EDT to handle the GUI.
Eg:
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
myframe.setVisible(true);
});
}
Related
I have a button with an action listener that disposes than reopens a frame. If I physically click the button it works as expected, the frame closes and the same one opens again.
I also have this action linked up with a doClick() in a timer. When the action occurs it opens a new frame without disposing of the other one. The timer works perfectly by itself.
What is the reason for this and how can I fix it? Is it a problem with the timer or calling it through the doClick?
I am relatively new to all this so this may be a simple question. Thanks
Button code:
frmStart s = new frmStart();
s.setVisible(true);
this.dispose();
doClick code (change is boolean):
Timer t = new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
if(change) {
btnReload.doClick();
}
}
}, 400);
With what you have provided, my best guess is to have you switch you Timer import from util.Timer to javax.swing.Timer
Then you would instantiate it like so:
Timer t = new Timer(400, (e)->{
if(change)
btnReload.doClick();
});
Then start it
t.start();
It isn't a good idea, and sometimes won't even work, to interact with swing objects outside the Swing Thread. When using javax.swing.Timer it runs the defined functionality within the Swing Thread
Dispose does not kill the frame. Quote:
Releases all of the native screen resources used by this Window, its
subcomponents, and all of its owned children. That is, the resources
for these Components will be destroyed, any memory they consume will
be returned to the OS, and they will be marked as undisplayable.
The frame itself is alive until the GC kicks in. And the GC does nothing because the timer is still active. So you should dispose the frame and then cancel the timer.
I have the first JFrame and it works fine. When I push a button it is supposed to show a JProgressBar frame , but i get empty JFrame. I open it with
p = new Progress("1/3");
p.setMax(2);
p.setProgress(0, "Getting bytes...");
Anyone know why?
EDIT:
I am going to explain more detail(Because someone misunderstood and corrected my post in the wrong way) - On my main class i start the first JFrame:
new Crypt();
And in the Crypt class i have registered a button ActionListener. OnClick it opens a second JFrame But it is empty:
p = new Progress("1/3");
p.setMax(2);
p.setProgress(0, "Getting bytes...");
The Progress class
Screen shot
in the Crypt class i have registered a button ActionListener. OnClick it opens a second JFrame But it is empty
Code invoked from an Swing listener executes on the Event Dispatch Thread (EDT). The EDT is responsible for painting Swing components. Since your code is executing a long running task on the EDT y9ou are preventing Swing from painting the component until the task is finished.
You need to start a separate Thread for your long running task. Or better yet you should probably be using a SwingWorker. Read the section from the Swing tutorial on Concurrency in Swing which explains this in more detail and provides a working example of a SwingWorker.
(In my applicaton with Swing GUI) I want to display GlassPane during some work performed in a loop or method, which is called after clicking JButton.
For example:
(action performed after clicking a button)
if (item.equals(button)) {
glassPane.setVisible(true);
someTimeConsumingMethod();
glassPane.setVisible(false);
}
Running this code results in not showing the glassPane during execution of someTimeConsumingMethod() - GUI just freezes for a moment, before result is displayed. Removing last line in that loop (glassPane.setVisible(false);) results in showing glassPane after the method is done (when GUI unfreezes).
Is there a simple way to show that glassPane before GUI freezes, or I need to use some advanced knowledge here? (threads?)
UPDATE1:
I've updated my code according to davidXYZ answer (with two changes):
(action performed after clicking a button)
if (item.equals(button)) {
glassPane.setVisible(true);
new Thread(new Runnable(){
public void run(){
someTimeConsumingMethod(); // 1st change: running the someTimeConsumingMethod in new Thread
// instead of setting glassPane to visible
}
}).start();
// 2nd change: moved glassPane.setVisible(false); inside the someTimeConsumingMethod(); (placed at the end of it).
}
The point of 1st change is that setting glassPane visible in new thread right before running someTimeConsumingMethod in my GUI thread was revealing the glassPane after someTimeConsumingMethod finished (double-checked this).
Now it works fine, thank you for all answers. I will definitely check all the links you provided to actually understand threads!
UPDATE2:
Some more info: someTimeConsumingMethod(); in my application is prepering new Swing Components accoriding to the XML data (cards builded from JButtons and JLabels with few JPanels where needed, and adding them in correct places).
UPDATE3:
I am trying to make it work using SwingWorker's invokeLater method. Now it looks like that:
(action performed after clicking a button)
if (item.equals(button)) {
glassPane.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
someTimeConsumingMethod();
glassPane.setVisible(false);
}
});
}
It works not that good as code from UPDATE1 (but still - it works). Problems are:
glassPane loads without .gif animation (file is setted up in custom glassPane class - it works with UPDATE1 code)
there is small delay at the end of "working" process - first cursor changes to normal (from the WAIT_CURSOR), and after very short moment glassPane disappear.
Cursor is changed by the custom glassPane class on activation/deactivation (no delay using new Thread way).
Is it correct way of using SwingWorker's invokeLater method?
EDIT: My mistake, I confused SwingWorker with SwingUtilities.invokeLater(). I guess the image issue is due to GUI freezing when the someTimeCOnsumingMethod starts.
GUI just freezes for a moment, before result is displayed. Removing last line in that loop (glassPane.setVisible(false);) results in showing glassPane after the method is done (when GUI unfreezes).
this is common issue about Event Dispath Thread, when all events in EDT are flushed to the Swing GUI in one moment, then everything in the method if (item.equals(button)) { could be done on one moment,
but your description talking you have got issue with Concurency in Swing, some of code blocking EDT, this is small delay, for example Thread.sleep(int) can caused this issue, don't do that, or redirect code block to the Backgroung taks
Is there a simple way to show that glassPane before GUI freezes, or I need to use some advanced knowledge here? (threads?)
this question is booking example why SwingWorker is there, or easier way is Runnable#Thread
methods implemented in SwingWorker quite guarante that output will be done on EDT
any output from Runnable#Thread to the Swing GUI should be wrapped in invokeLater()
easiest steps from Jbuttons Action could be
show GlassPane
start background task from SwingWorker (be sure that listening by PropertyChangeListener) or invoke Runnable#Thread
in this moment ActionListener executions is done rest of code is redirected to the Backgroung taks
if task ended, then to hide GlassPane
create simple void by wrapping setVisible into invokeLater() for Runnable#Thread
in the case that you use SwingWorker then you can to hide the GlassPane on proper event from PropertyChangeListener or you can to use any (separate) void for hidding the GlassPane
best code for GlassPane by #camickr, or my question about based on this code
You are blocking the EDT (Event Dispatching Thread, the single thread where all UI events are handled) with your time consuming job.
2 solutions:
Wrap the calls to:someTimeConsumingMethod();glassPane.setVisible(false); in SwingUtilities.invokeLater(), this will allow the frame to repaint itself once more. However this will still freeze your GUI.
Move your someTimeConsumingMethod() into a SwingWorker (this is the recommended option). This will prevent your GUI from ever freezing.
Read the javadoc of SwingWorker to understand better what is going on and how to use it.
You may also learn a lot in this tutorial about Swing and multi-threading
JButton startB = new JButton("Start the big operation!");
startB.addActionListener(new ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent A) {
// manually control the 1.2/1.3 bug work-around
glass.setNeedToRedispatch(false);
glass.setVisible(true);
startTimer();
}
});
glasspane here used here is FixedGlassPane glass;
ref: http://www.java2s.com/Code/Java/Swing-JFC/Showhowaglasspanecanbeusedtoblockmouseandkeyevents.htm
Guillaume is right. When you are on the main thread, each line will finish before the next line. You definitely need another thread.
An easy way to solve your problem is to spin off the display of the glasspane in another thread (normal thread or Swing threads - either will work fine).
if (item.equals(button)) {
new Thread(new Runnable(){
public void run(){
glassPane.setVisible(true);
}
}).start();
someTimeConsumingMethod();
glassPane.setVisible(false);
}
That way, a different thread is blocked by setvisible(true) while someTimeConsumingMethod() runs on the main thread. When it's done, glasspane will disappear. The anonymous thread reaches the end of the run method and stops.
My program looks like this:
import java.awt.*;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame jf = new JFrame();
jf.setSize(new Dimension(200, 200));
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
}
I'm just confused why after JVM's quitting from main(), my program does not end instantly? I noticed that if I remove the line "jf.setVisible(true);", it will end.
Is it implemented though techniques like garbage collecting or class destructors? I'm interested that if I want to write something similar, how could I do it.
The reason is that when you call setVisible(true) on the JFrame, behind the scenes a non-daemon thread is started, and the JVM will not exit until all non-daemon threads terminate.
Please have a look here for more on AWT/Swing Threading issues.
It states:
"There is at least one alive non-daemon thread while there is at least one displayable AWT or Swing component within the application (see Component.isDisplayable)."
While this is for Java 1.5, I think that it is still valid information.
Also, I believe that the Event Dispatch Thread or EDT is not a daemon thread, and so it is another thread associated with Swing that drives this.
Edit 1
This suggests that the EDT is in fact a non-Daemon thread:
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class IsEdtDaemon {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
System.out.printf("Is the current thread the EDT thread: %b%n", SwingUtilities.isEventDispatchThread());
System.out.printf("Is our EDT Thread a daemon thread: %b%n", Thread.currentThread().isDaemon());
}
});
}
}
The output from the code is:
Is the current thread the EDT thread: true
Is our EDT Thread a daemon thread: false
When you create the JFrame and make it visible you've created an implicit event listener that is now waiting for an action. If you hadn't set the default close action you would've needed to provide some other way for the application to "know" it can exit.
If you do not call jf.setVisible(true) then your program does construct the JFrame, sets it's dimensions and defines the default close operation, but never draws the JFrame on screen and so it exits. It wouldn't make any sense to create a JFrame if you do not want to ever make it visible.
The behavior one expects from a top-level container like JFrame, would be after setting it to visible to stay up and be used, until someone clicks on the close button which happens because of the jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setting.
Just think about what goes on with any application, i.e. your browser. It starts and stays up until you press the close button or exit the application in another way.
What I am trying to do is have a small splash screen appear while my program is loading something. This is what I have:
SplashScreen.showSplashScreen();
// Do stuff that takes time.
SplashScreen.hideSplashScreen();
All the showSplashScreen() method does is create a new JWindow in the middle of the screen and make it visible.
Now this code is called from the event dispatching thread, so when the showSplashScreen() method is called, I don't get to see the JWindow until the thread has finished, which by then, I don't need the window anymore. What would be the best way to go about showing this splash screen while I was waiting?
Not sure if this is the "best way", but a mechanism I've used before is to do your initialisation on a thread other than the EDT, but show your splash screen using SwingUtilities.invokeAndWait. That way, you'll at least get to see the splash screen even if your initialisation is quick (if that's what you want to happen).
So on your init thread, you go:
SwingUtilities.invokeAndWait( /* Runnable to show splash screen */ );
// Do stuff that takes time.
SwingUtilities.invokeLater( /* Hide splash screen, display main GUI */ );
There is a java.awt.SplashScreen class that was introduced in 1.6, tried using that?