I have a mainClass in Java, that starts a GUI in swing. I ask the user to open a file using a JFileChooser. I want the main to wait until the user has finished picking the file and then continue with the rest of the code in main. How do I do this using threads? Thanks in advance.
Here is the skeleton code:
public class MainClass {
public static void main(String[] args) {
GUI gui= new GUI();
//wait for user input here
//Continue with code
System.out.println("User has picked a file");
}
}
GUI.java
public class GUI{
//User picks file using JFileChooser
JFileChooser chooseFile= new JFileChooser();
//Notify mainclass we're done with fiction to continue with code
}
OK, Two things.
You don't need multiple threads
The thing is, you can accomplish your goal of waiting for a user to select a file simply by using a Modal dialog. This works about like the following:
import javax.swing.*;
public class DialogTest {
public static void main(String[] args) {
JFileChooser chooser = new JFileChooser();
chooser.showOpenDialog(null);
System.out.println("File chooser is now closed. File is: " +
chooser.getSelectedFile().toString());
}
}
The showOpenDialog method will not return until the user has either selected a file, clicked cancel, or else clicked the X. Just be aware that getSelectedFile() will return null if the user cancels.
If you do need threads (you know, for something else)
Swing uses what it calls the Event Dispatch Thread. Swing is not thread safe, as mentioned in the comment. What this means is that any and all method calls to Swing components should be done from the EDT. You can schedule code to be run on the EDT by using SwingUtilities.invokeLater(Runnable). You can schedule something to run in a background thread (using a thread pool) by using a Swing Worker. Most of your code will probably just run on the EDT. Long-running operations can be sent to a background thread using swing workers.
Related
I want to open an Eclipse Wizard or MessageDialog in a new thread, but somehow I always get an exception like this one:
Exception in thread "Thread-7" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:4491)
at org.eclipse.swt.SWT.error(SWT.java:4406)
at org.eclipse.swt.SWT.error(SWT.java:4377)
at org.eclipse.swt.widgets.Widget.error(Widget.java:482)
at org.eclipse.swt.widgets.Shell.<init>(Shell.java:266)
at org.eclipse.swt.widgets.Shell.<init>(Shell.java:362)
at org.eclipse.jface.window.Window.createShell(Window.java:486)
at org.eclipse.jface.window.Window.create(Window.java:429)
at org.eclipse.jface.dialogs.Dialog.create(Dialog.java:1096)
at org.eclipse.jface.window.Window.open(Window.java:792)
at org.eclipse.jface.dialogs.MessageDialog.open(MessageDialog.java:330)
at de.uka.ipd.sdq.beagle.gui.GuiController$DialogPolling.run(GuiController.java:126)
at java.lang.Thread.run(Thread.java:745)
when using code like this:
/**
* Opens up the dialog displaying the actions "pause", "continue", and "abort" to the
* user. These actions are regarding the analysis.
*/
private void engageDialog() {
final String dialogTitle = "Beagle Analysis is Running";
final String dialogMessage = "Beagle Analysis is running.";
final String[] buttonLabels = {"Abort", "Pause"};
this.messageDialog =
new MessageDialog(this.shell, dialogTitle, null, dialogMessage, MessageDialog.INFORMATION, buttonLabels, 0);
new Thread(new DialogPolling()).start();
}
private class DialogPolling implements Runnable {
#Override
public void run() {
final int buttonClick = GuiController.this.messageDialog.open(); // line 126
if (buttonClick == 0) {
System.out.println("User clicked 'Abort'.");
}
if (buttonClick == 1) {
System.out.println("User clicked 'Pause'.");
}
}
}
This is from GuiController and line 126 is marked. Scroll to the right if you can't see the line number.
How can I open a Wizard or a MessageDialog in a new thread?
All wizards, dialogs, ... must be opened in the single SWT UI thread. You can use the Display.syncExec call in another thread to run the dialog open in the UI thread.
Display.getDefault().syncExec(runnable);
Your Runnable can call the dialog open and save the buttonClick value somewhere that you can access when syncExec returns.
GUI systems are usually design as single thread because its almost impossible to write multi thread GUI system. There is to many user interactions and too many events.
Thats why GUI framework usually create his own dedicated thread and all GUI activity is going in this thread. For example Swing has its AWT thread. If long running operation is executing in this thread, it causes freeze of the program (program doesn't react to user input). If you want to avoid this, you must run your logic in different thread. But only your logic, not the GUI actions!
There are some useful classes to solve this issues - like SwingWorked, that is design to run lengthy GUI-interaction tasks in a background thread.
.
I'm using JFace to write a simple file-explorer application. The application's logic can be simplified as:
Display contents of a folder in a TableViewer.
Whenever a folder item gets double-clicked, async-load (to keep UI responsive) its contents and display it.
So in my opnion, there are at least 2 threads get involved: a) the UI thread and b) the background thread that fetches contents of a folder.
What really bothers me here is how does the two threads communicate and do I have to 'invent the wheel'? To be more specific:
How to tell the background thread when an item gets double-clicked? I suppose I need a task queue shared between the two threads or does JFace already provides some async-task mechanism?
How to tell the UI thread that the data have arrived and repaint the table? Which one to choose, asyncexec or syncexec?
What I would usually do is something like this:
// On double-click, start a new thread
new Thread(new Runnable()
{
#Override
public void run()
{
// Get your new data in this thread
final MyFancyDataObject data = SomeOtherClass.goAndGetMyData();
// Update the GUI, this is the safe way to do it from a non-gui-thread
Display.getCurrent().asyncExec(new Runnable()
{
public void run()
{
GuiClass.updateContent(data);
}
});
}
}).start();
I'm executing a Perl script via "Runtime.getRuntime().exec("perl C:/script.pl")"
as part of a Jbutton action listener. I would like to be able to click the button twice and get two instances of the same Perl script running. The script is reading in a text file so it acts a little differently depending on the text file the second time it is started but in general the script does the same thing.
I've tried to combat this by wrapping the runtime command in a new thread and executing a ".run()" on it each time the button is pressed but this only seems to interrupt the first instance and start the new one. There seems to be no way to execute two of the same Perl script in parallel. Any ideas on how I can accomplish this?
ActionListener edit = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
class GetThread implements Runnable {
public void run() {
try {
Runtime.getRuntime().exec("C:/Perl/bin/perl5.16.3.exe C:/Perl/get.pl", null, new File("C:/Perl"));
} catch (IOException e1) {
exceptionLog(e1);
}
}
}
GetThread get = new GetThread();
get.run();
}
}
http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html says
Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime method.
It may be blocking on your call.
I'm making a desktop application which watches a folder using watchservice from java.nio.file . But I need the gui to be loaded before I start watching, because the path to be watched is in a JFieldText on the UI.
public class FileArchiverApp extends SingleFrameApplication {
static FileArchiverView gui;
#Override protected void startup() {
gui = new FileArchiverView(this); //HERE0 I have to wait for this.
show(gui);
...
public static void main(String[] args) throws IOException {
launch(FileArchiverApp.class, args);
....
WatchService watcher = FileSystems.getDefault().newWatchService();
// HERE1 while(gui==null) System.out.println("hi") ;
try {
Path dir = Paths.get(gui.getOriginPath()); // HERE2 I get nullpointer if gui was not ready
WatchKey key = dir.register(watcher, ENTRY_CREATE );
} catch ( Exception x) {
System.err.println(x);
}
while(true){ /*wait for new file event loop*/ }
}
The function getOriginPath() returns the getText() form the text field I mentioned.
In HERE0 is the attribution I mentioned. I get a nullpointer in HERE2 if gui wasn't ready.
I've tried things. If I put that thing in HERE1 it works, but of course I don't want to do that.
How could I make it?
And its taking to long(like two seconds) or the gui to stop being null with this HERE1 I don't know if it is because of the println, but I was expecting it to be almost instantaneous. Is it normal?
Thanks.
Given the limited information posted, I have to make some assumptions. Assumption 1 is that you give the JTextField a default value and use that as the path to the file you wish to watch. Assumption 2 is that you have not coded with an eye towards MVC-like design.
If both are correct, then it sounds like you have the tail wagging the dog -- the view holding the critical data, not the model. Why not fix your problem by going towards MVC and not getting the critical data from the view but rather from the model. Start the model up first thing, including getting the default path from your program Properties, get your listener going, start your view, and then if the view asks the controller to change the watched file, have the controller change the model. And then listeners in the model will notify your any observers of change.
I have created a Java application where the main method (start of the program) initiates a Process object and an object of MainWindow class which creates a JFrame.
public static void main(String[] args) throws Exception {
File file = new File("./access/run.bat");
ProcessBuilder process_builder = new ProcessBuilder("cmd", "/c", file.getName());
process_builder.directory(file.getParentFile());
Process process = process_builder.start();
MainWindow window = new MainWindow(process);
}
I would like to terminate (kill) the process which has been instantiated with a process.destroy() when the window has been closed. Here is some code of the MainWindow class:
public MainWindow(final Process process) throws TransformerException, ParserConfigurationException, Exception{
JFrame mainWindowFrame = new JFrame();
*****some code here*****
mainWindowFrame.addWindowListener(new WindowListener() {
public void windowClosed(WindowEvent arg0) {
process.destroy();
System.exit(0);
}
*****some code here*****
}
}
When the window is closed, unfortunately, the process is not killed...can anyone give me an explanation for this and a possible solution? Thanks!!!
According to the documentation here then windowClosed is called only if the window is disposed. To do that, you can either call dispose on the window or set the default close operation: in your code, after creating the JFrame, add the following:
mainWindowFrame.setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
After looking at your code, I suggest you work differently:
in your listener, you are destroying the process and then exiting. therefore, you can set the deafualt close operation to exit and then implement the process destroying in the
implementation of windowClosing method : modifying the code of MainWindow to the following:
public MainWindow(final Process process) throws TransformerException, ParserConfigurationException, Exception{
JFrame mainWindowFrame = new JFrame();
mainWindowFrame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
*****some code here*****
mainWindowFrame.addWindowListener(new WindowListener() {
public void windowClosing(WindowEvent arg0) {
process.destroy();
}
*****some code here*****
}
}
The Javadoc of the Process class says this :
The subprocess is not killed when there are no more references
to the Process object, but rather the subprocess
continues executing asynchronously.
There is no requirement that a process represented
by a Process object execute asynchronously or concurrently
with respect to the Java process that owns the Process object.
After searching on the Internet, it seems that it's a issue in the Java platform since Java 1.3. I found this blog entry that explains many issues about Process in Java.
The problem is that the process becomes an orphan after killing the application. In your code, you are killing the Process from the GUI since the GUI (the MainWindow class) has its own thread and it is not the Process parent. It's a parent/child problem.
There are two ways to correct that :
Since the main thread is the parent process, so the main thread must call the destroy method. So you must keep a reference to the process object.
The second way is to create the process while launching the MainWindow. In the argument of the MainWindow class, you can pass the arguments of the process. So when the windowClosed method is called, if the MainWindow is closed, the Process will be destroy since the latter is the children of the MainWindow.