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();
Related
I have a section of my JAVA program where you click a button and the actionListener should go through the following process;
Change the text on the button from "Start" to "Standby"
Add a label to a panel stating that a process has started
Execute a method (that sorts data and returns it via addelement to a defaultListModel JList, and finally
Change the text on the button from "Start" to "Complete"
as per below
uploadNotamButton.addActionListener((ActionEvent e) -> {
if(e.getSource()==uploadNotamButton)
uploadNotamButton.setText("STANDBY");
progressLabel.setText("Process Has Begun, standby...");
progressLabel.setVisible(true);
uploadNotams();
uploadNotamButton.setText("COMPLETE");
});
However, when I press the button, the button text does not change, the label does not show, but the method executes. Only when the method is complete, does the button text change to "Complete" (never showed "STANDBY") and the label stating "the process has begun, standby" displays (when the process is complete).
Is this a feature of defaultlistmodel that takes priority over everything or my coding inexperience?
Also, the data that gets analysed in the method, is displayed in the JList in one go, and not each element at a time. If the data was shown in the list as it was analysed, it would at least show that something was happening. Is this not possible with the defaultListModel?
Many Thanks in advance
PG
Is this a feature of defaultlistmodel that takes priority over everything or my coding inexperience?
This has nothing to do with DefaultListModel and all to do with Swing being single-threaded. Your long running process is being run on the Swing event thread, blocking this thread from doing its necessary actions, including drawing text and images on your GUI and interacting with users.
The solution is to use a background thread such as can be obtained through a SwingWorker, running your long-running code in this background thread, adding a PropertyChangeListener to the worker to be notified when it's done, and then respond to this notification.
For example (code not tested)
uploadNotamButton.addActionListener((ActionEvent e) -> {
// if(e.getSource()==uploadNotamButton)
uploadNotamButton.setText("STANDBY");
progressLabel.setText("Process Has Begun, standby...");
progressLabel.setVisible(true);
// create worker to do background work
SwingWorker<Void, Void> worker = new SwingWorker<>() {
#Override
protected Void doInBackground() throws Exception {
// this is all done within a background thread
uploadNotams(); // don't make any Swing calls from within this method
return null;
}
};
// get notified when the worker is done, and respond to it
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue == SwingWorker.StateValue.DONE) {
uploadNotamButton.setText("COMPLETE");
// the code below needs to be surrounded by a try/catch block
// and you'll need to handle any exceptions that might be caught
((SwingWorker) evt.getSource()).get();
}
}
});
worker.execute(); // run the worker
});
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.
.
EDIT:
I noticed that my question was linked to another. While our goals are similar, the other question's set up is different: they are creating all the GUI aspects of the program within the main class of their program, they are also setting the trigger event of the button press within the start method. Therefore the solution of using the "setOnAction(event->" coupled with Task works. It is a single class program, I was able to make the solution work if I created a new, single class program, this application does not work for me for my situation.
In my set up I am not running this event out of the main class, but out of the Controller class that is linked to my FXML and I have the event that triggers the method already defined. I did not post my entire Controller class as that seemed unnecessary. If there is a way to make the linked question's solution work for my different set up, or a link for guidance that would be stellar. I have looked into the "task" set up, taking from the linked question, but so far have not been able to get it to work successfully as pictured below:
#FXML
private void goForIt(ActionEvent event)
{
kickTheBaby();
}
private void kickTheBaby()
{
java.util.Date now = calendar.getTime();
java.sql.Timestamp currentTimestamp = new java.sql.Timestamp(now.getTime());
statusFld.setOnAction(event -> {statusFld.setText("Running");
Task task = new Task<Void>() {
#Override
public Void call()
{
(new Thread(new EmailCommunication("", currentTimestamp, "START"))).start();
(new Thread(new DataGathering2())).start();
return null;
}
};
task.setOnSucceeded(taskFinishEvent -> statusFld.setText(statusFld.getText()
+ "All done time to sleep..."));
new Thread(task).start();
});
}
I have a program in Java8 using FXML that downloads and parses data. I wish to make the program update the GUI TextField (called "statusFld" here) to say "Running" when I click the start button. Below is the method in the controller that should be responsible for this series of events.
#FXML
private void goForIt(ActionEvent event)
{
statusFld.setText("Running!");
java.util.Date now = calendar.getTime();
java.sql.Timestamp currentTimestamp = new java.sql.Timestamp(now.getTime());
(new Thread(new EmailCommunication("", currentTimestamp, "START"))).start();
(new Thread(new DataGathering2())).start();
}
However, when I attempt to run the program the GUI does not visually update and goes straight into the other two threads. So I attempted to utilize the "Platform.runLater()" methodology in one of the other threads by passing the status field to it as so:
Platform.runLater(() ->
{
statusFld.setText("Running!");
});
But after 20 minutes it had not given a visual update to the GUI. My guess is that this is probably due to the sheer amount of data processing that I am having it do, so who knows what "later" will actually be in this case.
My question is how can I be sure that the GUI visually updates before moving on to the other, very processing intense, threads?
Thank you!
I'm using a ProgressMonitorDialog with an IRunnableWithProgress to read a file in the background.
If an error occurs during this file processing (the data isn't what I'm expecting), I would like to ask the user if s/he wants to continue with the next line.
The problem is now that in order to ask the user if they want to continue, I'd have to show a dialog. Showing a dialog from a non-UI thread involves using Display.asyncExec() or Display.syncExec(). In order to return the result (user decision) to the background thread I'd have to use a callback.
Now, the problem is, that when I get the result in a callback in the background thread, how can I continue reading the file? Or, in other words, how can I pause the execution of the background thread until the feedback returns and then continue it?
I'm open to suggestions and willing to restructure my environment to accommodate this behaviour.
Dislay.syncExec blocks the thread you call it from so you can do something like:
final int[] result = new int[] {0};
display.syncExec(new Runnable() {
public void run() {
Shell shell = display.getActiveShell();
MessageDialog dialog = .... your message dialog
result[0] = dialog.open();
}
});
... dialog return code in result[0]
(heavily adapted from code in org.eclipse.equinox.internal.p2.ui.ValidationDialogServiceUI)
In JME I try to use threading but when I run the program the function never starts.
I have a server socket who is listening to input from Netbeans.
Listener
while (isRunning) {
//Reads and prints the input
String receivedString = (String) in.readObject();
System.out.println(receivedString);
String[] parts = receivedString.split(";");
if(parts[0].equals("craneCon"))
{
final int containerId = Integer.parseInt(parts[1]);
m.enqueue(new Callable<Spatial>(){
public Spatial call() throws Exception{
m.removeContainersFromMaritime(containerId);
return null;
}
});
}
So in the main there is the function removeContainersFromMaritime
public void removeContainersFromMaritime(final int idContainer)
{
Node container = Maritime.listOfContainers.get(idContainer);
martime.detachChild(Maritime.listOfContainers.get(idContainer));
seagoingcrane.attachChild(Maritime.listOfContainers.get(idContainer));
container.setLocalTranslation(0,5,0);
System.out.println(Maritime.listOfContainers.get(0).getWorldTranslation().z);
}
The connection is alright but the method is never executed. How can I fix this?
jMonkeyEngine uses a swing-style threading model where there is a single render thread that does all the work. Any changes to the scene graph have to be done from that render thread.
To get into the render thread you can implement AppStates, Controls or you can enqueue Callables which are then executed on the render thread in a similar way to Swing's invokeLater.
The code snippet you posted looks about right, so assuming m is your running jME3 SimpleApplication then m.enqueue() will cause the enqueued callable to be executed next time around the render loop (i.e. at the start of the next frame).
If you are not seeing it executed then either:
Your application is not running
You created more than one application and enqueued it to the wrong one
The code is actually running and you just think it isn't.
Stepping through the code in the debugger and/or adding debug statements (for example breakpoint inside removeContainersFromMaritime to see if it is actually called should allow you to narrow this down.
I might be missing something but what is "m" in m.enqueue(...)?
I'm guessing it is an executor service of some sort and it's probably where the problem lies.
You could try instead:
new Thread() {public void run()
{
m.removeContainersFromMaritime(containerId);
}}.start();
It will at least show you if the problem is coming from "m" as an executor.