javafx bind button disable property to folder exists - javafx

I have a stage with a button "Continue" which is want to be disabled until a folder "folderFramework" is created.
Here is my snippet which unfortunately does not work.
File folderFramework = new File("path to the folder");
ObjectProperty<File> file = new SimpleObjectProperty<>();
file.set(folderFramework); // tested with setValue too...
BooleanBinding fileExists = Bindings.createBooleanBinding(() ->
file.get() != null && file.get().exists(),
file);
btnContinue.disableProperty().bind(fileExists.not());
What am I missing? Thank you!

WatchService in the Task did the trick for me. Here is the final solution:
private void handleBtnContinue() {
// task definition for watching changes in framework folder
Task<Void> watchingTask = new Task<>() {
#Override
protected Void call() {
WatchService watchService;
try {
watchService = FileSystems.getDefault().newWatchService();
Path path = Paths.get(ConstantUtils.getPathFolderFramework());
path.register(
watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey key;
boolean quitTask = false;
while ((key = watchService.take()) != null && !quitTask) {
for (WatchEvent<?> event : key.pollEvents()) {
// when src folder is cloned -> enable the button continue
if (event.kind().toString().equals("ENTRY_MODIFY") && event.context().toString().equals("src")) {
// enable btn continue
Platform.runLater(() -> btnContinue.setDisable(false));
// end the task
quitTask = true;
}
}
key.reset();
}
} catch (IOException | InterruptedException e) {
LOG.error("Error occurred while trying to watch changes in folder: " + ConstantUtils.getPathFolderAppData());
}
return null;
}
};
// run the task in a new thread
Thread th = new Thread(watchingTask);
th.setDaemon(true);
th.start();
}

Related

How to enable Platform.runlater within method

I am creating a Javafx chat app which also allows for file transfer. My issue is I open a FileOutputStream for the received file within the below method. I can see my listener.statusTransferring() updating the UI only if I enable Platform.runLater. I think I now need to enable the same on the fos.write(b, 0, tmpTransferred) within the while loop but don't know how to do this. I have tried unsuccessfully wrapping the whole method within Platform runlater. Note: If I don't use platform runlater I don't get any errors however the UI does not update until the file transfer is complete eg listener.statusCompleted() is called;. The error I get now as a result of the fos being in Platform runlater. is below.. Line 185 is fos.write(b, 0, tmpTransferred); The other listener calls appear to work fine. Just not listener.statusTransferring(); or listener.transferUpdate(); which utilise the fos. Any help will be greatly appreciated. Also for your own sanity I am a self taught google programmer. Yep the worst kind I am sure. Thanks in advance.
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at net.thebowdens.net.FileReceiver.transfer(FileReceiver.java:185)
at net.thebowdens.net.DefaultMessageResponder.fileSend(DefaultMessageResponder.java:543)
public boolean transfer() {
listener.statusConnecting();
received = false;
cancel = false;
try {
if (sSock != null) {
sock = sSock.accept();
listener.statusTransferring();
Platform.runLater(() ->{
try {
fos = new FileOutputStream(file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
is = sock.getInputStream();
final byte[] b = new byte[1024];
transferred = 0;
percent = 0;
int tmpTransferred = 0;
int tmpPercent = 0;
int transCounter = 0;
bCounter.prepare();
while (!cancel && (tmpTransferred = is.read(b)) != -1) {
fos.write(b, 0, tmpTransferred);
transferred += tmpTransferred;
percent = (int) ((transferred * 100) / size);
bCounter.addBytes(tmpTransferred);
transCounter++;
if (percent > tmpPercent || transCounter >= 250) {
transCounter = 0;
tmpPercent = percent;
listener.transferUpdate();
}
}
if (!cancel && transferred == size) {
received = true;
listener.statusCompleted();
}
else {
listener.statusFailed();
}
}
}
catch (final IOException e) {
LOG.log(Level.SEVERE, e.toString());
listener.statusFailed();
}
finally {
stopReceiver();
cleanupConnections();
}
return received;
}
Keep in mind that you should use Platform.runLater only for updating the UI, everything else should be outside it otherwhise the UI will become unresponsive.
I suggest you to to refactor your code according to this.
Well after much discussion over the correct language and other issues I solved my problem of the UI updating. I had two issues. My choice selector and Filechooser methods were not on the Javafx application thread (hope this is the right terminology) so I had to do the following:
private ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
#Override
public void run() {
Platform.runLater(() -> {
try {
receiveRequest(tmpUser, fileRes, user, fileName, size, fileHash);
} catch (IOException | ServerException | CommandException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
});
I then had to do the same within the Filechooser and file accept method for the transfer class UI to update
executorService.execute(new Runnable() {
#Override public void run() {
if (fileRes.transfer()) {
ui.showSystemMessage("Successfully received " + fileName +
" from " + user + ", and saved as " + fileRes.getFile().getName());
}
else {
ui.showSystemMessage("Failed to receive " + fileName + " from " + user);
fileRes.cancel();
}
}
});
}

How can I use JXBrowser to upload whole Directory, not just multiple files

Chrome supported the non-standard 'webkitdirectory' property for html 'input' element since 2011, and it works great. codepen sample
I would like to run jxbrower. Its latest version as of Mon, 21 May, 2018 was based on Chromium 64.0.3282.24 (Dec 12, 2017), yet it does not support this property.
What am I missing ? Any suggestions would be greatly appreciated.
<html>
<div>
<label for="uploaddir" style="width: 600px; background: #ccc;border: 1px solid black;">Choose directory to upload</label>
<input type="file" id="uploaddir" webkitdirectory onchange="updateImageDisplay()" style="opacity:0;">
</div>
<div>
<ul id="listing"></ul>
</div>
</html>
<script>
var input = document.getElementById('uploaddir');
var output = document.getElementById('listing');
function updateImageDisplay() {
while (output.firstChild) {
output.removeChild(output.firstChild);
}
var curFiles = input.files;
for (let i = 0; i < curFiles.length; i++) {
const item = document.createElement('li');
item.innerHTML = `${curFiles[i].webkitRelativePath} (${curFiles[i].size} bytes)`;
output.appendChild(item);
}
}
</script>
JxBrowser can upload the whole directory. What can be wrong in your case is the file chooser dialog that is not configured to chose directories.
I make a simple example to demonstrate how you could configure file chooser dialog in swing:
browser.setDialogHandler(new DefaultDialogHandler(view) {
#Override
public CloseStatus onFileChooser(FileChooserParams params) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
if (fileChooser.showOpenDialog(view) == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
params.setSelectedFiles(selectedFile.getAbsolutePath());
return CloseStatus.OK;
}
return CloseStatus.CANCEL;
}
});
#Dmitry. This is the javafx implementation analogous to this swing example. Note the following two crucial observations.
Need 'Runnable runnable' or it throws this error: 'SEVERE: The DialogHandler.onFileChooser() method has thrown exception:
java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = Browser Events Thread'.
Need 'FutureTask' to BLOCK the Event(?) thread until the user clicks OK in the directory browser. Otherwise, onFileChooser will return CloseStatus.CANCEL immediately. By the time params.setSelectedFiles is called , it's already too late, and any uploaded files will not be received by the angular controller.
private String path = "C:\\Users\\user\\Desktop\\temp\\foo";
private void setDirectoryListener(Stage primaryStage) {
browser.setDialogHandler(new DefaultDialogHandler(view) {
#Override
public CloseStatus onFileChooser(FileChooserParams params) {
final AtomicReference<CloseStatus> status = new AtomicReference<>(
CloseStatus.CANCEL);
Runnable runnable = () -> {
if (params.getMode() == FileChooserMode.OpenFolder) {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Open Resource Folder");
directoryChooser.setInitialDirectory(new File(path));
File selectedDirectory = directoryChooser.showDialog(primaryStage);
if (selectedDirectory != null) { // in case of CANCEL
List<File> allFiles = new ArrayList<>();
getOnlyFiles(selectedDirectory, allFiles);
params.setSelectedFiles(allFiles.toArray(new File[0]));
status.set(CloseStatus.OK);
}
}
};
FutureTask<Void> task = new FutureTask<>(runnable, null);
Platform.runLater(task);
try {
task.get();
}
catch (InterruptedException interrupt) {
throw new Error("Unexpected interruption");
}
catch (ExecutionException exc) {
throw new RuntimeException(exc);
}
return status.get();
}
});
}
private static void getOnlyFiles(File file, List<File> files {
if (file.isFile()) {
System.out.println(file.getAbsolutePath());
files.add(file);
}
File[] children = file.listFiles();
if (children == null)
return;
for (File child : children) {
getOnlyFiles(child, files);
}
}
INPUT:
`<input id="dirinput1" type="file" webkitdirectory ngf-select="uploadDir($files)/>`
OUTPUT:
C:\Users\user\Desktop\temp\foo\bar\csv.png
C:\Users\user\Desktop\temp\foo\bar\import_OQ_Manual.txt
C:\Users\user\Desktop\temp\foo\mz.PNG
C:\Users\user\Desktop\temp\foo\test.txt

Task: how to be notified when the task is "finished"

... that is after all its properties - including its value - are updated?
The use-case is a Task that
"collects" items into an ObservableList which is the result of the call method
the list should be set as value when the task is "finished", no matter if normally or cancelled
A snippet of the Task implementation (complete example at end):
#Override
protected ObservableList<Rectangle> call() throws Exception {
ObservableList<Rectangle> results = FXCollections.observableArrayList();
for (int i=0; i<=count; i++) {
// do fill list
//...
try {
Thread.sleep(200);
} catch (InterruptedException interrupted) {
if (isCancelled()) {
// do update value on cancelled
updateValue(results);
break;
}
}
}
return results;
}
It's intended usage:
bind the itemsProperty of a tableView to the valueProperty
unbind on "finished"
My approach was to listen to its state property and unbind on state changes to SUCCEEDED or CANCELLED. The former works just fine, the latter doesn't because at the time of receiving the cancelled, the value is not yet updated and consequently the items not set.
// working ... but when to unbind?
table.itemsProperty().bind(task.valueProperty());
task.stateProperty().addListener((src, ov, nv) -> {
if (Worker.State.SUCCEEDED == nv ) {
// this is fine because implementation in TaskCallable first
// updates the value (with the result it got from T call())
// then updates state
LOG.info("succeeded" + task.getValue());
table.itemsProperty().unbind();
} else if (Worker.State.CANCELLED == nv) {
LOG.info("receiving cancelled " + task.getValue());
// can't unbind here, value not yet updated
// table.itemsProperty().unbind();
}
});
So in case of cancelled, this leaves me with either a property that's still bound or an empty table. Feels like I'm doing something wrong. Or core Task impl is not as useful as expected? It would mean that we simply can't bind to the value property (nor any of the others like progress) due to being unable to safely cleanup (using table items here is just an example, because it's easy to see, same for all types of properties).
Question is, how to do it correctly/overcome the limitation?
The complete example:
public class TaskValueBinding extends Application {
private Parent createListPane() {
Task<ObservableList<Rectangle>> task = createListTask();
Thread thread = new Thread(task);
thread.setDaemon(true);
TableView<Rectangle> table = new TableView<>();
TableColumn<Rectangle, Double> xCol = new TableColumn<>("X");
xCol.setCellValueFactory(new PropertyValueFactory<>("x"));
TableColumn<Rectangle, Double> yCol = new TableColumn<>("Y");
yCol.setCellValueFactory(new PropertyValueFactory<>("y"));
table.getColumns().addAll(xCol, yCol);
// working ... but when to unbind?
table.itemsProperty().bind(task.valueProperty());
task.stateProperty().addListener((src, ov, nv) -> {
if (Worker.State.SUCCEEDED == nv ) {
// this is fine because implementation in TaskCallable first
// updates the value (with the result it got from T call())
// then updates state
LOG.info("succeeded" + task.getValue());
table.itemsProperty().unbind();
} else if (Worker.State.CANCELLED == nv) {
LOG.info("receiving cancelled " + task.getValue());
// can't unbind here, value not yet updated
// table.itemsProperty().unbind();
}
});
Label messageLabel = new Label("Message: ");
Label message = new Label();
message.textProperty().bind(task.messageProperty());
Label progressAsText = new Label();
Label progressLabel = new Label("Progress: ");
progressAsText.textProperty().bind(task.progressProperty().asString());
ProgressBar progress = new ProgressBar();
progress.progressProperty().bind(task.progressProperty());
Button start = new Button("Start");
start.setOnAction(e -> {
start.setDisable(true);
thread.start();
});
Button cancel = new Button("Cancel");
cancel.setOnAction(e -> task.cancel());
cancel.disableProperty().bind(task.runningProperty().not());
int row = 0;
GridPane grid = new GridPane();
grid.add(table, 0, row++, 20, 1);
grid.add(messageLabel, 0, row);
grid.add(message, 1, row++);
grid.add(progressLabel, 0, row);
grid.add(progressAsText, 1, row++);
grid.add(progress, 0, row++, 2, 1);
grid.add(start, 0, row);
grid.add(cancel, 1, row++);
return grid;
}
private Task<ObservableList<Rectangle>> createListTask() {
Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
#Override
protected ObservableList<Rectangle> call() throws Exception {
updateMessage("Creating Rectangles ...");
ObservableList<Rectangle> results = FXCollections.observableArrayList();
String message = "finished";
int count = 10;
for (int i=0; i<=count; i++) {
if (isCancelled()) {
updateValue(results);
// when do we get here?
message = "cancelled";
break;
}
Rectangle r = new Rectangle(10, 10);
r.setX(10 * i);
results.add(r);
updateProgress(i, count);
// Now block the thread for a short time, but be sure
// to check the interrupted exception for cancellation!
try {
Thread.sleep(200);
} catch (InterruptedException interrupted) {
if (isCancelled()) {
updateValue(results);
message = "interrupted";
break;
}
}
}
updateMessage(message);
return results;
}
};
return task;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createListPane()));
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TaskValueBinding.class.getName());
}
Cancelling the task immediately triggers an update of the state property. If canceled from the application thread Platfrom.runLater is not used for this purpose but the call of the cancel method updates the state immediately. This results in the state being changed before any updateValue call updates the value property using Platform.runLater.
Task is not designed to allow partial results so you need to implement custom logic to accommodate for this. Depending on your needs you could subclass Task to trigger a custom event when the task completes in any way.
public abstract class PartialResultTask<T> extends Task<T> {
// handler triggered after last change of value
private Runnable onDone;
public Runnable getOnDone() {
return onDone;
}
public void setOnDone(Runnable onDone) {
this.onDone = onDone;
}
protected abstract T calculateResult() throws Exception;
private void onDone() {
if (onDone != null) {
Platform.runLater(onDone);
}
}
#Override
protected final T call() throws Exception {
try {
T result = calculateResult();
updateValue(result); // update value to the final value
onDone();
return result;
} catch (Exception ex) {
onDone();
throw ex;
}
}
}
private PartialResultTask<ObservableList<Rectangle>> createListTask() {
PartialResultTask<ObservableList<Rectangle>> task = new PartialResultTask<ObservableList<Rectangle>>() {
#Override
protected ObservableList<Rectangle> calculateResult() throws Exception {updateMessage("Creating Rectangles ...");
ObservableList<Rectangle> results = FXCollections.observableArrayList();
int count = 10;
for (int i = 0; !isCancelled() && i <= count; i++) {
Rectangle r = new Rectangle(10, 10);
r.setX(10 * i);
results.add(r);
updateProgress(i, count);
// Now block the thread for a short time, but be sure
// to check the interrupted exception for cancellation!
try {
Thread.sleep(200);
} catch (InterruptedException interrupted) {
}
}
updateMessage(isCancelled() ? "canceled" : "finished");
return results;
}
};
return task;
}
task.setOnDone(() -> {
table.itemsProperty().unbind();
});
task.stateProperty().addListener((src, ov, nv) -> {
if (Worker.State.SUCCEEDED == nv) {
// this is fine because implementation in TaskCallable first
// updates the value (with the result it got from T call())
// then updates state
LOG.info("succeeded" + task.getValue());
} else if (Worker.State.CANCELLED == nv) {
LOG.info("receiving cancelled " + task.getValue());
}
});

Issues appending text to a TextArea (JavaFX 8)

I am receiving strings from my server that I want to append into a Textarea on the client side (Think chat window). Problem is, when I receive the string, the client freezes.
insertUserNameButton.setOnAction((event) -> {
userName=userNameField.getText();
try {
connect();
} catch (IOException e) {
e.printStackTrace();
}
});
public Client() {
userInput.setOnAction((event) -> {
out.println(userInput.getText());
userInput.setText("");
});
}
private void connect() throws IOException {
String serverAddress = hostName;
Socket socket = new Socket(serverAddress, portNumber);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
while (true) {
String line = in.readLine();
if (line.startsWith("SUBMITNAME")) {
out.println(userName);
} else if (line.startsWith("MESSAGE")) {
Platform.runLater(()->serverOutput.appendText(line.substring(8) + "\n"));
} else if (line.startsWith("QUESTION")) {
Platform.runLater(()->serverOutput.appendText(line.substring(8) + "\n"));
} else if (line.startsWith("CORRECTANSWER")) {
Platform.runLater(()->serverOutput.appendText(line.substring(14) + "\n"));
}
}
}
public static void main(String[] args) {
launch(args);
}
I have done some research and it seems that using Platform.runLater on each append should fix the problem. It doesn't for me.
Anyone has an idea of what it can be caused by? Thank you!
You are calling connect() on the FX Application Thread. Since it blocks indefinitely via the
while(true) {
String line = in.readLine();
// ...
}
construct, you block the FX Application Thread and prevent it from doing any of its usual work (rendering the UI, responding to user events, etc).
You need to run this on a background thread. It's best to use a Executor to manage the threads:
private final Executor exec = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
and then do
insertUserNameButton.setOnAction((event) -> {
userName=userNameField.getText();
exec.execute(() -> {
try {
connect();
} catch (IOException e) {
e.printStackTrace();
}
});
});

No 'output' extra specified Exception

I use the Google Photo app to pick gallery photo and then when I crop the picture and save, it catches an exception, Here is my code :
goto_picture.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
IMAGE_UNSPECIFIED);
startActivityForResult(intent, PHOTO_ZOOM);
dialog.cancel();
}
});
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == PHOTO_GRAPH) {
startPhotoZoom(Uri.fromFile(file));
String imagePath = SystemUtils.getSDPath() + "/temp.jpg";
File picture = new File(imagePath);
if (picture.exists()) {
pictureBitmap = BitmapFactory.decodeFile(imagePath);
ImageUtils.SaveCacheBitmap(pictureBitmap);
rvEditAvatar.setImageBitmap(pictureBitmap);
}
}
if (requestCode == PHOTO_ZOOM) {
startPhotoZoom(data.getData());
}
if (requestCode == PHOTO_RESULT) {
Bundle extras = data.getExtras();
if (extras != null) {
pictureBitmap = extras.getParcelable("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
pictureBitmap.compress(Bitmap.CompressFormat.JPEG, 100,
stream);
ImageUtils.SaveCacheBitmap(pictureBitmap);
rvEditAvatar.setImageBitmap(pictureBitmap);
}
}
}
}
public void startPhotoZoom(Uri uri) {
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 180);
intent.putExtra("outputY", 180);
intent.putExtra("return-data", true);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
startActivityForResult(intent, PHOTO_RESULT);
}
Logcat:
Process: com.google.android.apps.photos, PID: 7031
java.lang.RuntimeException: Unable to resume activity
{com.google.android.apps.photos/com.google.android.apps.photos.photoeditor.intents.EditActivity}:
java.lang.UnsupportedOperationException: No 'output' extra specified
and can not save to specified inputUri:
content://com.google.android.apps.photos.contentprovider/0/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F72072/ACTUAL
As the exception said, you have to specify output extra like the following code.
intent.putExtra(MediaStore.EXTRA_OUTPUT, someOutPutPath);
And return data is not secure in case of big image cropped which may cause crash. I think that's why it forces you to use an output extra but not the data directly. So you may set the return-data to false as well:
intent.putExtra("return-data", false);
I meet this problem today, and solved by double check the data pass-back.I test following code on both Android L and Android 4. On Android L the fileUri is not empty while pre Android L we got fileUri null(in this case, I got the bitmap by simply getData).
private Bitmap decodeBitmapFromCrop(Intent data) {
Bundle extras = data.getExtras();
Bitmap photo = null;
if (extras != null) {
photo = extras.getParcelable("data");
} else {
Uri fileUri = data.getData();
if (fileUri != null) {
try {
photo = MediaStore.Images.Media.getBitmap(getContentResolver(), fileUri);
} catch (IOException e) {
XXLog.d(TAG, "Media.getBitmap", e);
}
}
}
return photo;
}

Resources