I have the following event handler:
public class AssignPAMHandler {
private static final Logger logger = Logger.getLogger(AssignPAMHandler.class);
#Execute
public void execute() {
System.err.println(Thread.currentThread());
showDialog();
}
private void showDialog() {
Display.getDefault().asyncExec(() -> showDialogSWT());
}
private void showDialogSWT() {
System.err.println(Thread.currentThread());
new JFXPanel(); // initializes JavaFX environment
Platform.runLater(() -> showDialogFX());
}
private void showDialogFX() {
System.err.println(Thread.currentThread());
TextInputDialog dialog = new TextInputDialog("walter");
dialog.setTitle("Text Input Dialog");
dialog.setHeaderText("Look, a Text Input Dialog");
dialog.setContentText("Please enter your name:");
dialog.showAndWait().ifPresent(name -> System.out.println("Your name: " + name));
}
}
When I execute this handler via a menu item, I get the following output and exception (the dialog shows up, nevertheless):
Thread[main,6,main]
Thread[main,6,main]
Thread[JavaFX Application Thread,6,main]
Exception in thread "JavaFX Application Thread" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:4533)
at org.eclipse.swt.SWT.error(SWT.java:4448)
at org.eclipse.swt.SWT.error(SWT.java:4419)
at org.eclipse.swt.widgets.Widget.error(Widget.java:483)
at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:421)
at org.eclipse.swt.widgets.Widget.getData(Widget.java:537)
at org.eclipse.e4.ui.internal.workbench.swt.ShellActivationListener.handleEvent(ShellActivationListener.java:61)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
at org.eclipse.swt.widgets.Display.filterEvent(Display.java:1605)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1339)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1366)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1345)
at org.eclipse.swt.widgets.Shell.filterProc(Shell.java:807)
at org.eclipse.swt.widgets.Display.filterProc(Display.java:1621)
at com.sun.glass.ui.gtk.GtkApplication.enterNestedEventLoopImpl(Native Method)
at com.sun.glass.ui.gtk.GtkApplication._enterNestedEventLoop(GtkApplication.java:211)
at com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:511)
at com.sun.glass.ui.EventLoop.enter(EventLoop.java:107)
at com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:583)
at javafx.stage.Stage.showAndWait(Stage.java:474)
at javafx.scene.control.HeavyweightDialog.showAndWait(HeavyweightDialog.java:162)
at javafx.scene.control.Dialog.showAndWait(Dialog.java:341)
at net.openchrom.chromatogram.xxd.identifier.supplier.chromident.ui.handler.AssignPAMHandler.showDialogFX(AssignPAMHandler.java:54)
The executing thread seems to be the same all the time and the FX dialog shows up. Nevertheless, the exception is thrown.
Replace JFXPanel with FXCanvas to make it run.
Related
I am working on a JavaFx application, there i have a script that extract a zip followed by some other operation like updating files etc.
I want to have a textArea that displays whats going on in background, like "Zip extracting...", "Updating xyz file" etc.
Till now i have tried following way:
MyTask<String> task;
task = new MyTask<String>() {
#Override
protected String call() throws Exception {
File path = new File(exportTo.getAbsolutePath());
updateMessage("Extracting modular app to target directory...");
patcher.unZip(appPath.getAbsolutePath(), path.getAbsolutePath());
if (path.exists()) {
AppInfo info = getAppInfo();
patcher.patchAndroid(info, resourceZip, new File(path.getAbsolutePath() + "/" + appPath.getName().substring(0, appPath.getName().lastIndexOf("."))), this);
showOkAlert("Build completed!");
} else {
showOkAlert("Modular app folder not found");
}
return "";
}
#Override
protected void updateProgress(double workDone, double max) {
patcher.reportLogs(message);
}
private String message;
#Override
public void updateMessage(final String message) {
Platform.runLater(() -> patcher.reportLogs(message));
this.message = message;
//updateProgress(0, 0);
}
};
task.run();
MyTask class
abstract class MyTask<T> extends Task<T> {
abstract public void updateMessage(String message);
}
I have tried using updateProgress method, Platform.runLater() but nothing is working.
All the message i printed in textArea are printed after all operation is done.
Please help.
As javadoc for Task states you need to manually create a Thread to execute your Task:
Thread th = new Thread(task);
th.start();
Currently your task is being run on Application UI thread and blocks UI updates.
I read much about the JavaFX GUI Model, Plattform->RunLater and Threads, but I still do not figure out how to get this right. I had a JavaFX GUI which on a button click executed a process and updated a Progress Bar and Label. This was running well with Threading and Platform, but I had to Change this to an Observer Model.
I invoke a Progress Tracker in a Singleton Model, which gets updated by the class executing the process and is Observable. I implemented an Observer as well which should update the two UI Elements.
GUI Controller with Button Event
private void createKeyPressed(ActionEvent event) {
// Make Progressbar visible
pbKeyProgress.visibleProperty().set(true);
if (!Check.keyFileExistant() || cbKeyOverwrite.selectedProperty().get()) {
ProgressTracker.getTracker().addObserver(new ProgressObserver(pbKeyProgress, lblKeyProgress));
Creator.createKey(cbKeyLength.getValue());
} else {
}
}
Progress Observer
public class ProgressObserver implements Observer {
private final ProgressBar progressBar;
private final Label statusLabel;
public ProgressObserver(ProgressBar progressBar, Label statusLabel) {
this.progressBar = progressBar;
this.statusLabel = statusLabel;
}
#Override
public void update(Observable o, Object o1) {
Platform.runLater(() -> {
System.out.println("Tracker set to "+ProgressTracker.getProgress() + " " + ProgressTracker.getStatus());
progressBar.setProgress(ProgressTracker.getProgress());
statusLabel.setText(ProgressTracker.getStatus());
});
}
}
Progress Tracker
public synchronized void setTracker(int currentStep, String currentStatus) {
checkInstance();
instance.step = currentStep;
instance.status = currentStatus;
instance.notifyObservers();
System.out.println(instance.countObservers());
}
Creator
public static void createKey(String length) {
Task<Void> task;
task = new Task<Void>() {
#Override
public Void call() throws Exception {
initTracker(0,"Start");
doStuff();
ProgressTracker.getTracker().setTracker(1,"First");
doStuff();
ProgressTracker.getTracker().setTracker(2,"Second");
// and so on
return null;
}
};
new Thread(task)
.start();
}
The Print within the ProgressTracker gets executed. However, if I add a print within the update of the Observer nothing will be printed. If I check within the Progresstracker, the Observer Count is 1.
Why does the Observer not get notified or execute anything, even if the Notify is called? Did I get the Threading and Execution Modell wrong?
The Progress Bar and the Label will also stay on their initial values.
Don't reinvent the wheel. The JavaFX Properties Pattern is a ready-made implementation of the Observable pattern: there is no need to implement it yourself. Additionally, Task already defines methods for updating various properties, which can be called from any thread but will schedule the actual updates on the FX Application Thread. See updateProgress() and updateMessage(), for example.
So you can do, for example:
public static Task<Void> createKey(String length) {
Task<Void> task;
task = new Task<Void>() {
final int totalSteps = ... ;
#Override
public Void call() throws Exception {
updateProgress(0, totalSteps);
updateMessage("Start");
doStuff();
updateProgress(1, totalSteps);
updateMessage("First");
doStuff();
updateProgress(2, totalSteps);
updateMessage("Second");
// and so on
return null;
}
};
new Thread(task)
.start();
return task ;
}
and
private void createKeyPressed(ActionEvent event) {
// Make Progressbar visible
pbKeyProgress.visibleProperty().set(true);
if (!Check.keyFileExistant() || cbKeyOverwrite.selectedProperty().get()) {
Task<Void> task = Creator.createKey(cbKeyLength.getValue());
pbKeyProgress.progressProperty().bind(task.progressProperty());
lblKeyProgress.textProperty().bind(task.messageProperty());
} else {
}
}
I'm making a program which reads text files. What I would like to do is show an arbitrary node (Alert or other Node) which is created in separate thread before or during the file reading. I tried using Task and Platform.runLater() like this:
if (filetoopen != null)
{
Platform.runLater(new Runnable() {
#Override
void run() {
Alert alert=new Alert(Alert.AlertType.INFORMATION)
alert.setHeaderText('TEST')
}
})
//method to read the file
Tools.convertFromFile(filetoopen,newredactor)
lastDirectory = filetoopen.getParentFile()
}
I'd like to show an Alert or progress bar of reading the file, but the Control initializes after the reading is finished. So, is it possible to show a Node with a progress bar while the file is being read? Or the Runnable I create will always be executed in the end?
Edit: an attempt with Task:
class Alerter extends Task{
Alerter(File f,Editor e)
{
file=f
editor=e
}
File file
Editor editor
#Override
protected Object call() throws Exception {
Dialog dialog=new Dialog()
DialogPane dp=dialog.getDialogPane()
dp.setHeaderText('TEST')
dp.getButtonTypes().add(new ButtonType('Cancel',ButtonBar.ButtonData.CANCEL_CLOSE))
dialog.setOnCloseRequest(new javafx.event.EventHandler<DialogEvent>() {
#Override
void handle(DialogEvent event) {
dialog.close()
}
})
dialog.show()
Tools.convertFromFile(file,editor)
return null
}
}
The dialog still initializes after Tools.convertFromFile.
There are two threading rules in JavaFX (and in almost every other UI toolkit):
Changes to the scene graph (i.e. creating new scenes or windows, or changing the state of nodes already displayed) must be done on the FX Application Thread.
Long-running processes should be performed on a background thread (i.e. not the FX Application Thread), otherwise the UI will become unresponsive.
Your first code block violates the second rule (probably, you haven't shown much context) and your second code block violates the first rule.
So basically you need to:
Show the dialog from the FX Application Thread
Start a new thread which processes the file in the background
From the new thread, schedule any changes to the new UI on the FX Application Thread
When processing the file finishes, update the UI on the FX Application Thread
You can use Platform.runLater(...) to schedule code to run on the FX Application Thread, but the Task class provides more convenient API for these updates.
So:
// set up and show dialog:
ProgressBar progressBar = new ProgressBar();
DialogPane dialogPane = new DialogPane();
dialogPane.getButtonTypes().setAll(ButtonType.OK);
dialogPane.setHeaderText("Processing file");
dialogPane.setContent(progressBar);
dialogPane.lookupButton(ButtonType.OK).setDisable(true);
Dialog dialog = new Dialog();
dialog.setDialogPane(dialogPane);
dialog.show();
// create task:
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
Tools.convertFromFile(file, editor);
// can call updateProgress(...) here to update the progress periodically
return null ;
}
};
// update progress bar with progress from task:
progressBar.progressProperty().bind(task.progressProperty());
// when task completes, update dialog:
task.setOnSucceeded(event -> {
dialogPane.lookupButton(ButtonType.OK).setDisable(false);
progressBar.progressProperty().unbind();
progressBar.setProgress(1);
dialogPane.setHeaderText("Processing complete");
});
// handles errors:
task.setOnFailed(event -> {
dialogPane.lookupButton(ButtonType.OK).setDisable(false);
progressBar.progressProperty().unbind();
progressBar.setProgress(0);
dialogPane.setHeaderText("An error occurred");
});
// run task in background thread:
Thread thread = new Thread(task);
thread.start();
Note here that your Tools.convertFromFile(...) method is called from a background thread, so it must not update the UI (or at least any calls in that method that do update the UI must be wrapped in Platform.runLater(...)).
Here is a complete SSCCE (which just sleeps as a demo of a long-running process):
import java.util.Random;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.DialogPane;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TaskWithProgressDemo extends Application {
#Override
public void start(Stage primaryStage) {
Button button = new Button("Start process");
button.setOnAction(e -> {
button.setDisable(true);
// set up and show dialog:
ProgressBar progressBar = new ProgressBar();
DialogPane dialogPane = new DialogPane();
dialogPane.getButtonTypes().setAll(ButtonType.OK);
dialogPane.setHeaderText("Processing file in progress");
dialogPane.setContent(progressBar);
dialogPane.lookupButton(ButtonType.OK).setDisable(true);
Dialog<Void> dialog = new Dialog<Void>();
dialog.setDialogPane(dialogPane);
dialog.show();
// create task:
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
Random rng = new Random();
for (int i = 0 ; i <= 100 ; i++) {
Thread.sleep(rng.nextInt(40));
updateProgress(i, 100);
}
if (rng.nextBoolean()) {
System.out.println("Simulated error");
throw new Exception("An unknown error occurred");
}
return null ;
}
};
// update progress bar with progress from task:
progressBar.progressProperty().bind(task.progressProperty());
// when task completes, update dialog:
task.setOnSucceeded(event -> {
dialogPane.lookupButton(ButtonType.OK).setDisable(false);
button.setDisable(false);
progressBar.progressProperty().unbind();
progressBar.setProgress(1);
dialogPane.setHeaderText("Processing complete");
});
// handles errors:
task.setOnFailed(event -> {
dialogPane.lookupButton(ButtonType.OK).setDisable(false);
button.setDisable(false);
progressBar.progressProperty().unbind();
progressBar.setProgress(0);
dialogPane.setHeaderText("An error occurred");
});
// run task in background thread:
Thread thread = new Thread(task);
thread.start();
});
StackPane root = new StackPane(button);
root.setPadding(new Insets(20));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
So I finally figured it out. I had to move both my file loading code and progress update to a Task, so it wouldn't block FX thread. The indicator shows progress of loading a file.
Edit: to achieve progress display in a separate non-blocking window, must use a new Stage instead of anything else.
I have an AsyncTask that I call from the main thread, and I wish to pop a dialog out when it completes. Other than putting the dialog code in OnPostExecute(), is there a way where I can put it in the main activity code instead?
Thanks.
You may use an interface for that. It is simple and forward:
1- create a new interface:
public interface IShowPopup {
public void showPopup(String title, string message);
}
2 - implement that interface in your activity:
... MyActivity extends Activity implements IShowPopup {
...
public void showPopup(String title, String message) {
// create a DialogAlert here.
AlertDialog.Builder builder = new AlertDialog.Builder(MyActivity.this);
builder.setMessage(message);
builder.setTitle(R.string.app_license_title);
AlertDialog dialog = builder.create();
// show dialog.
dialog.show();
}
...
}
3 - in your task you keep an instance of the activity:
...MyTask extends AsyncTask<...> {
private IShowPopup iShowPopup ;
// get the interface from constructor.
public MyTask(IShowPopup isp) {
this.iShowPopup = isp;
}
4 - use the interface in your onPostExecute:
#Override
public void onPostExecute(??) {
// get some title and message.
iShowPopup.showPopup(title, message);
}
That should be it!
I want to perform some long running operation (e.g. listening to some event raised by OS) on the background thread. Most of the times, operation will run continuously without any problem. But in certain rare conditions, OS level API sends some error code and I need to raise exception from background thread which has to be propagated to the main thread to show it to the user of my WinFrom application.
I had decided to use BackgroundWorker for this. But .NET 4.0 provides Task class of the Task Parallel Library which is a better option as per various blogs on the TPL.
In my application, I have to kick off the background task before actual form is shown. Since actual code is quite complex, I have written some sample code simulating real time problem:
public static Task task;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ThreadTest tt = new ThreadTest();
task = new Task(() => tt.PerformTask("hi"));
task.Start();
try
{
task.Wait();
}
catch (AggregateException aggregateException)
{
// Handle exception here.
}
Application.Run(new Form1());
}
In this code, I never see the main form simply because background task keeps running without exception and task.Wait() call makes the current thread waiting until background task finishes!
Can I use TPL's Task for such scenarios where main thread should not wait until background task is finished but at the same time, it should get exception details whenever exception is raised from the background task?
In above code, one of the solutions could be to move the task creation code at some later stage. But my question is more academic in this case.
Yes you can. Please see the code below.
The program code is:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
Task longRunningTask = new Task((state) =>
{
LongRunningWork.DoWork( cancellationTokenSource.Token);
},cancellationTokenSource.Token,TaskCreationOptions.LongRunning);
var newForm = new Form1(cancellationTokenSource);
new Thread((state) =>
{
longRunningTask.Start();
try
{
longRunningTask.Wait();
}
catch (AggregateException exception)
{
Action<Exception> showError = (ex) => MessageBox.Show(state as Form, ex.Message);
var mainForm = state as Form;
if (mainForm != null)
{
mainForm.BeginInvoke(showError, exception.InnerException);
}
}
}).Start(newForm);
Application.Run(newForm);
And the code for the long running task is:
public class LongRunningWork
{
public static void DoWork( CancellationToken cancellationToken)
{
int iterationCount = 0;
//While the
while (!cancellationToken.IsCancellationRequested &&iterationCount <5)
{
//Mimic that we do some long jobs here
Thread.Sleep(1000);
iterationCount++;
//The jobs may throw the exception on the specific condition
if (iterationCount ==5)
{
throw new InvalidOperationException("Invalid action");
}
}
//cancel the task
cancellationToken.ThrowIfCancellationRequested();
}
}
Finally, the code for the Form1 which includes a exit button, whose function is to terminate the program on clicking.
public partial class Form1 : Form
{
private CancellationTokenSource _cancellationTokenSource;
public Form1()
{
InitializeComponent();
}
public Form1(CancellationTokenSource cancellationTokenSource):this()
{
_cancellationTokenSource = cancellationTokenSource;
}
private void exitBtn_Click(object sender, EventArgs e)
{
//Cancel out the task
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
}
//Exit the program
Application.Exit();
}
}
Start your long running operation from the form itself rather than before the form is created. Remember that Application.Run() starts a message loop on the current thread, but that means you can use that message loop to poll your task from the Timer class.
class Form1 : Form
{
private Timer PollingTimer;
private Task BackgroundTask;
public Form1()
{
InitializeComponent();
// Begin the background task.
ThreadTest tt = new ThreadTest();
this.BackgroundTask = new Task(() => tt.PerformTask("hi"));
this.BackgroundTask.Start();
// Monitor the task's status by polling it regularly.
this.PollingTimer = new Timer();
this.PollingTimer.Interval = 1000; // In milliseconds.
this.PollingTimer.Tick += timerCallback;
this.PollingTimer.Start();
}
private timerCallback(object sender, EventArgs e)
{
if (this.BackgroundTask.IsFaulted)
{
// Exception information is in BackgroundTask.Exception.
}
}
}
If you dislike polling (which I do), you'll need to catch the exception from your task and marshall it back to your UI thread. The best way to do that is simply not catch the exception in the task itself and provide a continuation method which will only execute on error.
class Form1 : Form
{
private Task BackgroundTask;
public Form1()
{
InitializeComponent();
// Capture the UI thread context.
// (Note, it may be safer to run this in the Form.Load event than the constructor.
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
// Begin the background task.
ThreadTest tt = new ThreadTest();
this.BackgroundTask = new Task(() => tt.PerformTask("hi"))
// Schedule a continuation to be executed after the task is completed.
.ContinueWith((t,arg) =>
{
// Exception information is in t.Exception
},null, null,
// Only execute the continuation if the task throws an exception.
TaskContinuationOptions.OnlyOnFaulted,
// Execute the continuation on the UI thread we captured above.
uiContext);
this.BackgroundTask.Start();
}
}
MSDN references for Task.ContinueWith() and TaskScheduler.FromCurrentSynchronizationContext().
And, if you have the luxury of .NET 4.5 with async and await:
class Form1 : Form
{
private Task BackgroundTask;
public Form1()
{
InitializeComponent();
}
private async void Form1_Load(object sender, EventArgs e)
{
ThreadTest tt = new ThreadTest();
try
{
// Move your Task creation and start logic into a method.
await tt.RunAsync();
}
catch (Exception ex)
{
// Really smart compiler writers make sure you're on the right thread
// and everything Just Works(tm).
}
}
}