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.
Related
I have a program I'm working on that is supposed to facilitate the making of texture maps by allowing you to edit individual textures in the map. I have several places where I use JDialog's for things like input of the tile-size of a map, the initial size of a new map, and one that simply has a button on it that the user presses when he or she is done editing the selected textures with an external program (such as photoshop or paint.) However, any time one of these JDialog's is up, if the program loses focus it becomes completely unresponsive. Here's my code for the JDialog that pops up when you're editing selected textures externally:
JDialog editing = new JDialog(mainFrame, "Externally Editing");//mainFrame is the name of the JFrame containing everything.
JPanel pnl = new JPanel();
editing.setLayout(new BorderLayout());
pnl.setPreferredSize(new Dimension(150, 60));
editing.add(pnl);
editing.pack();
JButton button = new JButton("Done Editing");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
editng = false;//Obviously, a boolean that I have outside of this method.
}
});
pnl.add(button);
Thread.sleep(100);
editing.setVisible(true);
while(editng){System.out.print("");}//Doing nothing while the user is externally editing.. Unfortunately, if I don't have "System.out.print("");" it doesn't work.. Oh, Java..
new Thread(new Runnable(){public void run(){editing.dispose();}}).start();//Gotta dispose of it in a separate thread since AWT isn't Thread-safe... Ugh..
I would assume that it freezes in the AWT Thread that it creates/has for the JDialog, while my thread is just waiting for the button to be pressed.. Which can't happen because the JDialog is frozen.
Make your dialog modal. Your application will stop processing until the dialog is closed
Why this bogus roundabout? Simply dispose the dialog in the listener.
(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.
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);
});
}
I am writing an app that requires the user to enter some data into a Swing GUI which the app will then use. After the user enters the data, there is no longer any need for the GUI as the app will then write some data to files.
The General idea is this:
launchGui();
closeGui();
continueWithComputation();
I understand that Swing uses a few threads in the background which I understand is why the program doesn't block until the GUI is closed.
Is it possible in any way to wait for the GUI to close (single JFrame closed with dispose()) before continuing with continueWithComputation()?
Wait for Swing GUI to close before continuing
Use a modal dialog. See the following for further details:
How to Make Dialogs
How to Use Modality in Dialogs
Is it possible in any way to wait for the GUI to close (single JFrame closed with dispose()) before continuing with continueWithComputation()?
user actions add WindowListener
from code to call JFrame#setVisible(false), then you can running continueWithComputation(), you have to close this JVM by System.exit(0), otherwise stays in PC's RAM untill restarted or power-off
I just had a similar problem, but without a closeGui() method and came up with this relatively short code snippet, using a WindowListener and some Java synchronization:
AtomicBoolean closed = new AtomicBoolean(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosed(WindowEvent e) {
synchronized(closed) {
closed.set(true);
closed.notify();
}
super.windowClosed(e);
}
} );
frame.setVisible(true);
synchronized(closed) {
while (!closed.get()) {
closed.wait();
}
}
// executes after the Frame has been disposed
My suggestion is to open the JFrame GUI as Modal and then everything will wait until it disappears from the screen. As JFrame cannot be set as modal you have to open the GUI frame as JDialog. I am using:
public class frameMain extends JDialog{
be sure that into the constructor you use:
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
Wherever you need to start the GUI frame you should use:
frameMain frm= new frameMain ();
frm.setModal(true);
frm.setVisible(true);
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.