I've got a class that creates and runs a thread, which creates a GUI.
I want the initial class to remain suspended until the GUI is closed (OK button for example)
I tried thread.join(); but since the GUI is created on the event dispatch thread this does not seem to work, and the class continues as the GUI pops up.
private void CreateAndRunThread(){
GUIMaker GM= new GUIMaker(data);
GM.run();
try {
TFM.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MessageDialog.showDialog("GM Done");
}
thread's GUI creation:
#Override
public void run() {
//Schedule a job for the event dispatch thread:
//creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Turn off metal's use of bold fonts
UIManager.put("swing.boldMetal", Boolean.FALSE);
CreateAndShowGUI(frame); //adds frame, packs and sets visible
}
});
}
Use CountDownLatch:
CountDownLatch latch = new CountDownLatch(1);
Call the following in the inital class to block:
latch.await();
Call the following when the GUI is closed:
latch.countDown();
Also it seems that you are not starting thread properly. You need to call GM.start() method instead of GM.run().
Related
Edit: Updated question including a MRE and an answer can be found here.
I'm pretty lost after stumbling across this issue and finding no way to resolve it myself.
Description:
I have an application which has a Java Swing GUI. All the communication to the GUI is done via custom Events, mostly created by the main program, which are then processed by a Controller to control the GUI.
One event I have will trigger the Controller to open a custom Modal JDialog which acts just as a "please wait while the stuff in the background is being processed". So after the background task is finished, an Event will trigger the Dialog to dispose, to make the main GUI frame accessible again.
The issue:
When the Dialog is disposed, the main Frame will magically be set to the background. Not minimized, not completely in the background of all open windows, but actually in the background of the last open window. I'm completely lost on how and why this happens.
The important parts of the Controller class look vaguely like this:
class Controller {
private ModalWaitDialog dialog;
private JFrame frame;
private void createMainFrame() {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
// create frame
frame = new JFrame();
// make the frame visible
frame.setVisible(true);
}
});
}
private void showWaitPopup(String msg) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
modalWaitDialog = new ModalWaitDialog(frame, message);
modalWaitDialog.showDialog();
}
});
}
// after this was executed, my frame will be set in the background
private void endWaitPopup() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (modalWaitDialog != null) {
modalWaitDialog.dispose();
modalWaitDialog = null;
}
}
});
}
}
I know this is not a MCVE, but maybe somebody knows how and why this happens.
Edit:
I added a WindowFocusListener to the Frame and added some print statements to the endWaitPopup, and called invokeAndWait to further see what is happening. The following is the result:
private void endWaitPopup() {
System.out.println("In EndWaitPopup");
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
System.out.println("EndWaitPopup... run started");
// implement as if it could be called if no wait popup is available
if (modalWaitDialog != null) {
System.out.println("Disposing the modal dialog");
modalWaitDialog.dispose();
modalWaitDialog = null;
}
System.out.println("End of EndWaitPopup");
}
});
} catch (InvocationTargetException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Output:
In EndWaitPopup
+++Focus of Frame lost+++
EndWaitPopup... run started
Disposing the modal dialog
End of EndWaitPopup
my program's UI freezes for some time after pressing a JButton. I discovered that a cause of this is a Semaphore clogging the Swing thread. This is the method containing the acquire() call on the Semaphore:
private void fetch(int numThreads) {
//some code here
sem = new Semaphore(numThreads);
for (int i = 0; i < model.getRowCount(); i++){
try {
sem.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
//some code here
}
And here is the only method that makes a call to fetch()
concFetchButt.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
switchButtonStates(false);
}
});
fetch(Integer.parseInt(threadsNumField.getText()));
}
As I understand, this code ends up running fetch() on the Swing thread, though, supposedly it has nothing to do with Swing.
I guess, my question is this: How do I run a method called from 'ActionPerformed()' of Swing on the main thread of the program instead of the Swing thread?
No need to specifically run that on the "main" thread. Simply run it on any other thread but the Swing UI thread.
The most simple solution to get there:
add an ExecutorService to your class
put that code fetch(Integer.parseInt(threadsNumField.getText())); into a Runnable object
Submit that Runnable to the ExecutorService
Along the lines of:
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
public void run() {
fetch(Integer.parseInt(threadsNumField.getText()));
}
});
I'm trying to make an JavaFX application that tracks the movement of my mouse for this im using this code in the controller class:
new Thread(new Runnable() {
#Override public void run() {
while (Main.running) {
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
label.setText(MouseInfo.getPointerInfo().getLocation().toString());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}).start();
But it couses my application to lag big time.
How should i fix this lag problem?
Thanks i fixed it:
new Thread(new Runnable() {
#Override public void run() {
while (Main.running) {
Platform.runLater(new Runnable() {
#Override
public void run() {
label.setText(MouseInfo.getPointerInfo().getLocation().toString());
}
});
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
What you doing is letting Javafx Application thread Thread.sleep(1000); <-wait
Any long term action you shoud put OUT of JFX-AT. And only update your ui components on it.
new Thread(()->{
while(Main.running){
Platform.runLater(()->{
//updateui component
//this is updating on FXAT
});
Thread.sleep(time)//This way you dont let JFXAT wait
}
}).start();
//Not sure if formatted and curly braces correctly.Bud you hopefully understand.Make sure you know which thread you let wait.Otherwise you wont be able to recieve events from paused jfxat.
You should put your Thread.sleep() call in your while loop and not in your Runnable, otherwise the loop keeps posting a lot of runLater tasks and those tasks stops the event thread for 1000ms after updating your mouse position
You call Thread.sleep(long) inside a Runnable that will be executed on the UI thread. If the thread is sleeping, it can't do anything else but sleep there. If you want your label to update every 1000 milliseconds, you can use the java.util.Timer class to make that happen.
I am interfacing with a JNI that allows me to use a fingerprint scanner. The code I have written takes the scanned ByteBuffer parsed back to it by the JNI and turns it into a BufferedImage for saving.
What I can't figure out is how to wait for the scan thread to finish before the the jlabel icon on my GUI tries to update. What would be the easiest way to do this?
What else do I need to add?
Edit:
//Scanner class
Thread thread = new Thread() {
public void run() {
// [...] get ByteBuffer and Create Image code
try {
File out = new File("C:\\Users\\Desktop\\print.png");
ImageIO.write(padded, "png", out);
// [???] set flag here
} catch (IOException e) {
e.printStackTrace();
}
}
};
thread.start();
return true;
//Gui class
private void btnScanPrintActionPerformed(java.awt.event.ActionEvent evt) {
Scanner scanPrint = new Scanner();
boolean x = scanPrint.initDevice();
//Wait for the scanning thread to finish the Update the jLabel here to show
//the fingerprint
}
Not sure if you are using Swing or Android for UI but you would want to notify the main event dispatch thread (in swing it is called just that). You would run the scanning thread, then when complete send a 'message' to the EDT with the action that you want to be done to the button.
Thread thread = new Thread(new Runnable(){
public void run(){
//scan
SwingUtiltilies.invokeLater(new Runnable(){
//here you can update the the jlabel icon
public void run(){
jlabel.setText("Completed");
}
});
}
});
In UI development there is no waiting for an action to complete because you always want the EDT to be responsive.
at the end of a the scan thread, use SwingUtilities.invokeLater() to update the gui with the results of the scan.
When the program starts, a new JFrame is created. Once the user clicks the start button a thread is created and started. Part of this threads execution is to validate the data on the form and then execute with that data. Once the data has been validated the thread calls dispose() on the original frame and then creates a new JFrame that acts as a control panel.
There is also an automatic mode of the program that doesn't display any GUI at all, this mode reads data from a configuration file and then starts the execution thread and runs everything but without the control panel.
I want the program to end once the thread completes, but in GUI mode, only if the user has closed the control panel as well.
Is it possible to make the thread wait for the frame to close. I assuming that the frame is run from it's own Thread? or is that not the case.
Thanks.
The answer you chose is a little awkward. Using Thread.sleep(1000) will check for window state every second. It is not a performance issue, but just bad coding style. And you may have a one second response time.
This code is a little bit better.
private static Object lock = new Object();
private static JFrame frame = new JFrame();
/**
* #param args
*/
public static void main(String[] args) {
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setVisible(true);
Thread t = new Thread() {
public void run() {
synchronized(lock) {
while (frame.isVisible())
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Working now");
}
}
};
t.start();
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent arg0) {
synchronized (lock) {
frame.setVisible(false);
lock.notify();
}
}
});
t.join();
}
You can make reference from your thread to the JFrame. Then set the default close operation of JFrame to HIDE_ON_CLOSE. If the JFrame is closed, you can stop the thread.
Example code:
import java.awt.Dimension;
import javax.swing.JFrame;
public class FrameExample extends JFrame {
public FrameExample() {
setSize(new Dimension(100, 100));
setDefaultCloseOperation(HIDE_ON_CLOSE);
setVisible(true);
}
private static class T implements Runnable {
private FrameExample e;
public T(FrameExample e) {
this.e = e;
}
#Override
public void run() {
while (true) {
if (e.isVisible()) {
// do the validation
System.out.println("validation");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
break;
}
}
}
}
}
public static void main(String[] args) {
FrameExample frameExample = new FrameExample();
new Thread(new T(frameExample)).start();
}
}
All Swing components, including JFrame, are managed by a single thread, called the Event Dispatch Thread, or EDT. (It's possible to call methods on Swing objects from other threads, but this is usually unsafe, except in a few cases not relevant here.)
You'll probably accomplish what you want here by putting the data validation and execution code in its own object which is otherwise completely unaware of the outside world. Then, call it from one of two other objects: one that manages a GUI, and another that runs in "automatic mode".