I have a Dialog class with a wait method in it to display my custom Progress Dialog:
public static void wait(String title){
isOpen = true;
ProgressIndicator progress = new ProgressIndicator(-1);
Label label = new Label(title);
label.getStyleClass().add("login-label");
HBox container = new HBox();
container.setStyle("-fx-background-color: white;");
container.setAlignment(Pos.CENTER);
container.getChildren().addAll(progress,label);
if(Main.HEIGHT < 700){
container.setSpacing(10);
container.setPadding(new Insets(10,15,10,15));
}else if(Main.HEIGHT < 1200){
container.setSpacing(15);
container.setPadding(new Insets(15,20,15,20));
}else{
container.setSpacing(20);
container.setPadding(new Insets(20,30,20,30));
}
show("", container);
}
I have this piece of code in one of my class to dislay my Progess Dialog:
Platform.runLater(new Runnable(){
#Override
public void run() {
Dialog.wait("Processing, please wait...");
}
});
But unfortunately there is a delay in its showing, I also tried to wrap it inside a Thread but it didn't work as well, I tried to run it in Desktop and it works perfectly but why not in my Android Device?
Here's the complete code:
download = new Button("Download");
download.getStyleClass().add("terminal-button");
download.setPrefWidth(Main.HEIGHT > 700 ? 180 : 140);
download.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
Platform.runLater(new Runnable(){
#Override
public void run() {
Dialog.wait("Processing, please wait...");
}
});
Platform.runLater(new Runnable(){
#Override
public void run() {
//GET THE SELECTED AREAS FOR DOWNLOAD
List<String> selectedSectors = new ArrayList();
String sectorid = null;
for(Sector r : listView.getItems()){
if(r.isSelected()){
selectedSectors.add(r.getObjid());
sectorid = r.getObjid();
}
}
if(selectedSectors.size() > 1){
Dialog.hide();
Dialog.showAlert("Multiple downloads are not supported!");
return;
}
MobileDownloadService mobileSvc = new MobileDownloadService();
//INIT DOWNLOAD
Map params = new HashMap();
params.put("assigneeid", SystemPlatformFactory.getPlatform().getSystem().getUserID());
params.put("sectorid", sectorid);
batchid = mobileSvc.initForDownload(params);
int recordcount = -1;
while (true) {
int stat = mobileSvc.getBatchStatus(batchid);
if ( stat < 0 ) {
try {
Thread.sleep(2000);
}catch(Throwable t){;}
} else {
recordcount = stat;
break;
}
}
if ( recordcount <= 0 ) {
Dialog.hide();
Dialog.showError("No data to download");
return;
}
downloadsize = recordcount;
accountList = new ArrayList();
int start=0, limit=50;
while ( start < recordcount ) {
params = new HashMap();
params.put("batchid", batchid);
params.put("_start", start);
params.put("_limit", limit);
List<Map> list = mobileSvc.download(params);
//if ( list != null ) accountList.addAll( list );
System.out.println("fetch results is " + list.size());
//new Thread( new ProcessDownloadResultTask(start,list)).start();
start += limit;
}
Dialog.hide();
//SAVE AREA, STUBOUTS
clearSector();
for(Sector r : listView.getItems()){
if(r.isSelected()){
saveSector(r);
}
}
label.setVisible(true);
progressbar.setVisible(true);
progressbar.progressProperty().bind(task.progressProperty());
new Thread(task).start();
download.setText("Cancel");
download.setDisable(false);
download.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
continueDownload = false;
label.setVisible(false);
progressbar.setVisible(false);
download.setText("Back");
download.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
Main.ROOT.setCenter(new Home().getLayout());
}
});
root.setOnKeyReleased(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.ESCAPE){
if(Dialog.isOpen){ Dialog.hide(); return; }
Main.ROOT.setCenter(new Home().getLayout());
}
}
});
Map params = new HashMap();
params.put("batchid", batchid);
params.put("downloadedlist", downloadedList);
MobileDownloadService svc = new MobileDownloadService();
svc.cancelDownload(params);
}
});
download.setDisable(false);
}
});
}
});
The said scenario occur when you click the button, the output should be: Dialog will popup IMMEDIATELY as soon as you click the button, but sad to say, the Dialog will display after the entire process of the button was completed! I tried to wrap it in Thread but no luck!
Please help me! Any idea?
This is a short sample showing how can you use a Gluon's Dialog to handle the progress notification of a background task.
It uses a dummy task, but you can see how to handle showing and hiding the dialog, as well as using a ProgressBar to notify the progress, and even cancelling the task.
Using the Gluon Plugin for your IDE, create a Single View mobile project, and modify the view with this one:
public class BasicView extends View {
public BasicView(String name) {
super(name);
Dialog dialog = new Dialog("Download Progress");
final ProgressBar progressBar = new ProgressBar();
progressBar.setPrefWidth(200);
final Label label = new Label("Process has ended");
VBox vbox = new VBox(10, new Label("Download in progress..."), progressBar, label);
vbox.setAlignment(Pos.CENTER);
dialog.setContent(vbox);
final Button cancel = new Button("Cancel");
dialog.getButtons().add(cancel);
dialog.setOnShown(e -> {
cancel.setDisable(false);
label.setVisible(false);
final Task<Void> task = createDownloadTask();
progressBar.progressProperty().bind(task.progressProperty());
cancel.setOnAction(a -> task.cancel(true));
task.setOnCancelled(c -> {
PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(t -> dialog.hide());
cancel.setDisable(true);
label.setVisible(true);
pause.play();
});
task.setOnSucceeded(s -> {
PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(t -> dialog.hide());
cancel.setDisable(true);
label.setVisible(true);
pause.play();
});
final Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
});
Button button = new Button("Download");
button.setGraphic(new Icon(MaterialDesignIcon.CLOUD_DOWNLOAD));
button.setOnAction(e -> dialog.showAndWait());
setCenter(new StackPane(button));
}
#Override
protected void updateAppBar(AppBar appBar) {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu")));
appBar.setTitleText("Downloads View");
}
private Task<Void> createDownloadTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
for(int i = 0; i <= 10; i++) {
if (isCancelled()) {
break;
}
try {
Thread.sleep(1000);
updateProgress(i, 10);
} catch (InterruptedException ie) {
if (isCancelled()) {
break;
}
}
}
return null;
}
};
}
}
Try replacing the dummy task with yours and see how it goes.
I solved the problem by separating its execution in one of the mouse events, instead of putting all together in the setOnAction, I placed the code Dialog.wait("Processing, please wait..."); in the setOnMousePressed, like this:
download.setOnMousePressed(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
if(!Dialog.isOpen) Dialog.wait("Processing, please wait...");
}
});
download.setOnMouseReleased(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
doDownload();
}
});
This code works!
Related
I want to close my JavaFX application if the user is inactive for a period of time. I have this code in Swing and I want to do the same in JavaFX. This class redirect the user to the login panel if no event happens during the time indicated.
import javax.swing.Timer;
public class AutoClose {
private Timer timer;
public AutoClose(JFrame centralpanel) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
Object source = event.getSource();
if (source instanceof Component) {
Component comp = (Component) source;
Window win = null;
if (comp instanceof Window) {
win = (Window) comp;
} else {
win = SwingUtilities.windowForComponent(comp);
}
if (win == centralpanel) {
timer.restart();
}
}
}
}, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK);
timer = new Timer(3600000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
centralpanel.dispose();
//Redirect to the login panel.
Login login = new Login();
login.setVisible(true);
timer.stop();
JOptionPane.showMessageDialog(null,"Connection closed due to inactivity");
}
});
timer.start();
}
});
}
}
Create a PauseTransition to trigger a delayed exit and add a event filter for InputEvents to all your scenes that restart the transition:
#Override
public void start(Stage primaryStage) throws IOException {
Button button = new Button("abcde");
StackPane root = new StackPane(button);
Scene scene = new Scene(root, 400, 400);
// create transition for logout
Duration delay = Duration.seconds(10);
PauseTransition transition = new PauseTransition(delay);
transition.setOnFinished(evt -> logout());
// restart transition on user interaction
scene.addEventFilter(InputEvent.ANY, evt -> transition.playFromStart());
primaryStage.setScene(scene);
primaryStage.show();
transition.play();
}
private void logout() {
// TODO: replace with logout code
Platform.exit();
}
Note: It's important to use an event filter since events can be consumed before they reach an event handler.
I have done this code and now it works correctly as I wanted.
public class AutoClose {
private Timeline timer;
public AutoClose(VBox mainPanel) {
timer = new Timeline(new KeyFrame(Duration.seconds(3600), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent evt) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Inactivity");
alert.setHeaderText("Connection closed due to inactivity");
alert.show();
try {
Stage mainWindowStage = Login.getPrimaryStage();
Parent root = FXMLLoader.load(getClass().getResource("/view/Login.fxml"));
Scene scene = new Scene(root);
mainWindowStage.setScene(scene);
mainWindowStage.show();
timer.stop();
} catch (IOException ex) {
}
}
}));
timer.setCycleCount(Timeline.INDEFINITE);
timer.play();
mainPanel.addEventFilter(MouseEvent.ANY, new EventHandler<Event>() {
#Override
public void handle(Event event) {
timer.playFromStart();
}
});
}
}
I have a method that read values from the the database and returns a Map<Integer,String>. This method takes some time to return the map.
Till the time values are getting read I want a progress indicator(only loading ring like indicator will be enough,no need for progress bar) to be displayed on screen and all other components should be disabled till the time progress bar is shown.
public void scanDevice() {
ObservableList<TextField> list = FXCollections.observableArrayList(vehicleId, vehicleName, deviceType,
offboardBroker1, offboardBroker2, postfixQueue, pKIServer);
editedValuesMap.clear();
// devicePlugged = true;
if (cbChooseProject.getSelectionModel().getSelectedItem() != null) {
try {
devicePlugged = dsAdapter.getAdapter();
if (devicePlugged) {
if (bScanDevice.isFocused()) {
readMap = new HashMap<Integer, String>();
//Process Start
readMap = dsAdapter.initScan();
//Process End
if (!readMap.isEmpty() && readMap != null) {
isWritten = true;
isDeviceSideEnabled();
editDeviceContents.setDisable(false);
vehicleId.setText(readMap.get(0));
vehicleName.setText(readMap.get(1));
deviceType.setText(readMap.get(2));
offboardBroker1.setText(readMap.get(3));
offboardBroker2.setText(readMap.get(4));
postfixQueue.setText(readMap.get(5));
pKIServer.setText(readMap.get(6));
lContentsSerialNo.setText(readMap.get(7));
}
}
}
You could disabled all nodes with a method like the following but if you are also wanting to wait while something is happening an overlay using StackPanes may be the preferred choice.
public void setNodesDiabled(boolean disable, Node... nodes) {
for(Node node : nodes) {
node.setDisable(disable);
}
}
With an arbitrary node count, you can disable and re-enable as many nodes that are relevant to the process. It also helps to clean up as you won't have several node.setDisable(true); node2.setDisable(true); and so on.
Here in this example you won't need setNodesDisabled() because the StackPane overlay prevents clicking anything other than what's inside it. The background color is gray with 70% alpha so that you can tell it's an overlay.
public class ProgressExample extends Application {
public StackPane layout, main, progress;
public StackPane createProgressPane() {
ProgressIndicator indicator = new ProgressIndicator();
indicator.setMaxHeight(50);
indicator.setMaxWidth(50);
StackPane pane = new StackPane();
pane.setAlignment(Pos.CENTER);
pane.setStyle("-fx-background-color: rgba(160,160,160,0.7)");
pane.getChildren().add(indicator);
Task<Void> task = new Task<Void>(){
protected Void call() throws Exception {
// Your process here.
// Any changes to UI components must be inside Platform.runLater() or else it will hang.
Thread.sleep(2000);
Platform.runLater(() -> {
layout.getChildren().remove(pane);
});
return null;
}
};
new Thread(task).start();
return pane;
}
public StackPane createMainPane() {
Label label = new Label("Hello World!");
label.setFont(Font.font("Tahoma", FontWeight.SEMI_BOLD, 16));
Button start = new Button("Start Process");
start.setOnAction(action -> {
progress = createProgressPane();
layout.getChildren().add(progress);
});
VBox vbox = new VBox(10);
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(label, start);
vbox.setPadding(new Insets(10,10,10,10));
StackPane pane = new StackPane();
pane.getChildren().add(vbox);
return pane;
}
public void start(Stage stage) throws Exception {
main = createMainPane();
layout = new StackPane();
layout.getChildren().add(main);
Scene scene = new Scene(layout, 900, 550);
stage.setScene(scene);
stage.setTitle("Progress Example");
stage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I believe the problem is that you are trying to change the values of TextFields inside the Task which is not the FX application thread which is why you are getting Not on FX application thread. To fix this you need to put your lines that modify nodes inside a Platform.runLater() like the following to your if statement.
if (readMap != null && !readMap.isEmpty()) { // Swap the order, can't check empty if it's null.
isWritten = true;
isDeviceSideEnabled();
Platform.runLater(() -> {
editDeviceContents.setDisable(false);
vehicleId.setText(readMap.get(0));
vehicleName.setText(readMap.get(1));
deviceType.setText(readMap.get(2));
offboardBroker1.setText(readMap.get(3));
offboardBroker2.setText(readMap.get(4));
postfixQueue.setText(readMap.get(5));
pKIServer.setText(readMap.get(6));
lContentsSerialNo.setText(readMap.get(7));
});
}
Here is an SSCCE:
It uses a Service that can be started more than once. It is not completebut something to start with.
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
Service<Void> serv = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
int maxWork = 10;
for (int i = 0; i < maxWork; i++) {
Thread.sleep(1000);
updateProgress(i + 1, maxWork);
}
return null;
}
#Override
protected void succeeded() {
super.succeeded();
updateProgress(1, 1);
}
#Override
protected void cancelled() {
super.cancelled();
updateProgress(1, 1);
}
#Override
protected void failed() {
super.failed();
updateProgress(1, 1);
}
};
}
};
ProgressIndicator pi = new ProgressIndicator();
pi.progressProperty().bind(serv.progressProperty());
Button bStart = new Button("Start");
bStart.setOnAction(e -> {
serv.reset();
serv.start();
});
root.setCenter(bStart);
root.setBottom(pi);
primaryStage.setScene(scene);
primaryStage.show();
pi.getScene().getRoot().disableProperty().bind(serv.runningProperty());
}
public static void main(String[] args) {
launch(args);
}
}
In CSS I added:
.progress-indicator:disabled {
-fx-opacity: 1;
}
I have SplashScreen where I load my assets and show an image
Everything works well but fadeIn Action doesn't work although fadeOut works
Here is my code :
public void show() {
stage = new Stage();
Texture splashTexture = new Texture(Gdx.files.internal("splash.png"));
splash = new Image(splashTexture);
splash.setPosition(Constants.WIDTH/2 - splash.getWidth()/2, Constants.HEIGHT/2 - splash.getHeight()/2);
stage.addActor(splash);
splash.getColor().a = 0;
SequenceAction sequenceAction = new SequenceAction(Actions.fadeIn(2.0f), Actions.delay(2.0f),
Actions.fadeOut(2.0f), Actions.run(new Runnable() {
#Override public void run() { gameMain.setScreen(new MenuScreen(gameMain, null, true)); } }));
splash.addAction(Actions.parallel(Actions.run(new Runnable() {
#Override public void run() { Assets.load();
} }), sequenceAction)); }
Try using Actions.fadeOut(0f) just before fadeIn action
SequenceAction sequenceAction = new SequenceAction(Actions.fadeOut(0f),Actions.fadeIn(2.0f), ...);
or
instead splash.getColor().a = 0; use splash.setColor(1,1,1,0);
I am trying to add a slider on my page like progress bar. But my code is not working well.
My task is when I am going to copy something from one location to another I want to display a progress bar on my page.
So in javaFx I wrote following task but it is not working well. That code runs but I want show the work in percentage like 30%, 50% and "finish". But my code fails to gives me like requirement so please help me.
My code is:
1.Declaration of progress bar and progress indicator
#FXML
final ProgressBar progressBar = new ProgressBar();
#FXML
final ProgressIndicator progressIndicator = new ProgressIndicator();
2.Assign values when I click on copy button.
#FXML
private void handleOnClickButtonAction(MouseEvent event) {
if (fromLabel.getText().isEmpty()
|| toLabel.getText().isEmpty()
|| fromLabel.getText().equalsIgnoreCase("No Directory Selected")
|| toLabel.getText().equalsIgnoreCase("No Directory Selected")) {
// Nothing
} else {
progressBar.setProgress(0.1f);
progressIndicator.setProgress(progressBar.getProgress());
this.directoryCount.setText("Please Wait !!!");
}
}
This code shows me only 10% completion an then directly shows "done", but I want whole process in percentage like 10,20,30,.. etc and then "done".
My copy code:
double i = 1;
while (rst.next()) {
File srcDirFile = new File(fromLabel.getText() + "/" + rst.getString("nugget_media_files"));
File dstDirFile = new File(toLabel.getText() + "/" + rst.getString("nugget_media_files"));
File dstDir = new File(toLabel.getText() + "/" + rst.getString("nugget_directory"));
if (srcDirFile.lastModified() > dstDirFile.lastModified()
|| srcDirFile.length() != dstDirFile.length()) {
copyDirectory(srcDirFile, dstDirFile, dstDir);
}
this.currentNuggetCount = i / this.nuggetFolderSize;
System.out.println("Nugget Count : " + this.currentNuggetCount);
Platform.runLater(new Runnable() {
#Override
public void run() {
progressBar.setProgress(1.0f);
progressIndicator.setProgress(progressBar.getProgress());
}
});
++i;
}
This is the copyDirectory method:
private static void copyDirectory(File srcDir, File dstDir,File destNugget) {
System.out.println(srcDir+" >> "+dstDir);
if(!destNugget.exists()) {
destNugget.mkdirs();
}
if (srcDir.isDirectory()) {
if (!dstDir.exists()) {
dstDir.mkdirs();
}
String[] children = srcDir.list();
for (int i=0; i<children.length; i++) {
copyDirectory(new File(srcDir, children[i]),
new File(dstDir, children[i]),
destNugget);
}
} else {
InputStream in = null;
try {
in = new FileInputStream(srcDir);
OutputStream out = new FileOutputStream(dstDir);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
} catch (IOException ex) {
System.out.println("Exceptio "+ex);
} finally {
try {
in.close();
} catch (IOException ex) {
System.out.println("Exceptio "+ex);
}
}
}
}
Try this code. It will give you Progress bar with progress indicator which depends on the slider control.
public class Main extends Application {
#Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Progress Controls");
final Slider slider = new Slider();
slider.setMin(0);
slider.setMax(50);
final ProgressBar pb = new ProgressBar(0);
final ProgressIndicator pi = new ProgressIndicator(0);
slider.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
pb.setProgress(new_val.doubleValue()/50);
pi.setProgress(new_val.doubleValue()/50);
}
});
final HBox hb = new HBox();
hb.setSpacing(5);
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(slider, pb, pi);
scene.setRoot(hb);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You must enter your code inside a Task and set within UpdateProgress method. Before you run the Task you have to set progressBar.progressProperty (). Bind (task.progressProperty ());
This is an example:
TaskTest
I want to use a context menu item on the lines of a listView. In the event handler of the listView's MOUSE_CLICKED event, the getSelectionModel().getSelectedItem() returns the selected item, thats ok. But, when I handle the contextMenuItem's onAction event, it returns null. However, graphically the item is selected.
Is there a way to "keep" the selection after the first event handling?
Here is the relevant part of the code:
ListView<Text> nameList = new ListView<>();
final ContextMenu cCm = new ContextMenu();
MenuItem cItem = new MenuItem("someText");
cCm.getItems().add(cItem);
...
nameList.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
if (e.getButton() == MouseButton.SECONDARY) {
//its OK here:
System.out.println(nameList.getSelectionModel().getSelectedItem().getText());
cCm.show(nameList, e.getScreenX(), e.getScreenY());
}
}
});
cItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
final Stage dialog = new Stage();
dialog.initModality(Modality.WINDOW_MODAL);
//nullPointerException on the following:
Text t = new Text(nameList.getSelectionModel().getSelectedItem().getText());
//showing dialog, etc.
I pretty much created an exact replica of what you did, and my implementation worked:
private void initRandomCardListView() {
populateRandomList();
final ContextMenu randomListContextMenu = new ContextMenu();
MenuItem replaceCardMenuItem = new MenuItem("Replace");
replaceCardMenuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
replaceRandomCard();
}
});
randomListContextMenu.getItems().add(replaceCardMenuItem);
randomCardList.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton().equals(MouseButton.SECONDARY)) {
randomListContextMenu.show(randomCardList, event.getScreenX(), event.getScreenY());
}
}
});
}
private void replaceRandomCard() {
System.out.println("jobs done");
System.out.println("card selected: " + randomCardList.selectionModelProperty().get().getSelectedItem().toString());
System.out.println("card index: " + randomCardList.getSelectionModel().getSelectedIndex());
System.out.println("card index: " + randomCardList.getSelectionModel().getSelectedItem().toString());
}
I don't have any null pointer exceptions. Overall your implementation looks good. There is most likely something that is wrong with the items in your listview.