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();
}
});
}
}
Related
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'm currently working on a little game with JavaFX, and i had some problems to catch keyEvents.
Now i can catch them but the program throw a java.lang.StackOverflowError and it didn't do what I expected when a key is pressed.
Here is the main class:
public class WarbladeFX extends Application {
Game root;
public void start(Stage primaryStage) {
root = new Game();
Scene scene = new Scene(root, 800, 600);
scene.setFill(new Color(0,0,0,1));
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
Event.fireEvent(root, event);
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
Event.fireEvent(root, event);
}
});
primaryStage.setTitle("WarbladeFX");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
And the Game class:
public class Game extends Group {
Entity ship;
long delta;
HashSet<String> input = new HashSet<>();
public Game() {
File f = new File("src/ressources/ship.gif");
ship = new Entity(new Image("file:///" + f.getAbsolutePath().replace("\\", "/")) ,300, 300);
getChildren().add(ship);
setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
//System.out.println(".handle()");
String code = event.getCode().toString();
if(!input.contains(code))
input.add(code);
}
});
setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
String code = event.getCode().toString();
input.remove(code);
}
});
new AnimationTimer(){
#Override
public void handle(long now) {
if(input.contains("LEFT"))
ship.setVelX(-1);
if(input.contains("RIGHT"))
ship.setVelX(1);
ship.move(now);
getChildren().clear();
getChildren().add(ship);
}
}.start();
}
}
Some help would be very great.
Events fired on nodes in the scene graph will "bubble up" to the parent, and then the parent of the parent, and eventually all the way up to the scene. So the event handlers that you defined on the scene "refire" the event to the root, which will then bubble up to the scene and get handled again, being refired again to the root, and so on...
If you want to catch the events anywhere in the scene, and then handle them in the Game class, define some methods in Game to process the events and invoke those methods. Don't "refire" the events.
For example:
public class Game extends Group {
Entity ship;
long delta;
HashSet<String> input = new HashSet<>();
public Game() {
File f = new File("src/ressources/ship.gif");
ship = new Entity(new Image("file:///" + f.getAbsolutePath().replace("\\", "/")) ,300, 300);
getChildren().add(ship);
new AnimationTimer(){
#Override
public void handle(long now) {
if(input.contains("LEFT"))
ship.setVelX(-1);
if(input.contains("RIGHT"))
ship.setVelX(1);
ship.move(now);
getChildren().clear();
getChildren().add(ship);
}
}.start();
}
public void keyDown(String key) {
if(!input.contains(key))
input.add(key);
}
public void keyUp(String key) {
input.remove(code);
}
}
and then you can do
public class WarbladeFX extends Application {
Game root;
public void start(Stage primaryStage) {
root = new Game();
Scene scene = new Scene(root, 800, 600);
scene.setFill(new Color(0,0,0,1));
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
game.keyDown(event.getCode().toString());
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
game.keyUp(event.getCode().toString());
}
});
primaryStage.setTitle("WarbladeFX");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
you are generating an infinite loop when you fire the event again in the event-handle method. Try to handle the event in this method, i.e. react to the users input.
here is my code:
#Override
void start(Stage stage) throws Exception {
def root = new VBox() {
{
children.add(new TextArea() {
{
setId("ta1")
}
})
children.add(new TextArea() {
{
setId("ta2")
}
})
}
}
root.setOnFocus(new OnFocus() {
void onFocus(Node focusedTarget) {
// handle focusedTarget
}
})
def scene = new Scene(root, 800, 600)
stage.setScene(scene)
stage.show()
}
I hope implement following code to handle focusing child events
root.setOnFocus(new OnFocus() {
void onFocus(Node focusedTarget) {
// handle focusedTarget
}
})
if i set #ta1 and #ta2's focusedProperty, if child are large, it hard to do it, so I hope directly listen the parent, how to do it?
The standard event dispatching can be used to fire a custom event on the Scene. A listener atached to the focusOwner property of the Scene can be used to trigger the event.
Example (java)
public class FocusEvent extends Event {
public static final EventType FOCUS_EVENT_TYPE = new EventType(EventType.ROOT);
public FocusEvent(Object source, EventTarget target) {
super(source, target, FOCUS_EVENT_TYPE);
}
}
#Override
public void start(Stage primaryStage) {
TextArea ta1 = new TextArea();
ta1.setId("ta1");
TextArea ta2 = new TextArea();
ta2.setId("ta2");
VBox root = new VBox(ta1, ta2);
root.addEventHandler(FocusEvent.FOCUS_EVENT_TYPE, evt -> {
System.out.println("focused "+ evt.getTarget());
});
ta1.addEventHandler(FocusEvent.FOCUS_EVENT_TYPE, evt -> {
System.out.println("You focused the first TextArea");
evt.consume();
});
Scene scene = new Scene(root);
scene.focusOwnerProperty().addListener((o, old, newValue) -> {
if (newValue != null) {
FocusEvent event = new FocusEvent(scene, newValue);
Event.fireEvent(newValue, event);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
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!
I have JavaFX project in which I have to listen to keyboard-input as our barcode scanner is configured that way.
Are there any libraries in JavaFX where I can keep a listener active and perform suitable action upon reception of a String by barcode-scanner.
I searched on net, but didn't find any good solution unfortunately.
Here is my code :
public class Main extends Application {
private Scene scene;
MyBrowser myBrowser;
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Our Application");
java.net.CookieManager manager = new java.net.CookieManager();
java.net.CookieHandler.setDefault(manager);
myBrowser = new MyBrowser();
scene = new Scene(myBrowser, 1080, 1920);
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.show();
// # being the escape character
scene.setOnKeyTyped(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
String text = event.getCharacter();
if (text.equals("#")) {
String tempText = completeText;
completeText = "";
processText(tempText);
}else {
completeText = completeText+text;
}
}
});
}
private void processText(String text){
System.out.println("I will process "+text);
}
public static void main(String[] args) {
launch(args);
}
public class MyBrowser extends Region {
final String hellohtml = "index.html";
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
public MyBrowser() {
webEngine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", this);
}
});
URL urlHello = getClass().getResource(hellohtml);
webEngine.load(urlHello.toExternalForm());
webView.setPrefSize(1080, 1920);
webView.setContextMenuEnabled(false);
getChildren().add(webView);
}
Kindly let me know. Thank you.
So I think I understand what your problem is, you have a barcode scanner that sends in barcodes as keyevents to your application, And then you need to respond to the whole bar code when you're done receiving the code?
If that's the case you can use a KeyListener to intercept the key press events. Then you just need to implement the logic to put the individual key events together.
class MyListener implements KeyListener{
#Override
public void keyPressed(KeyEvent e) {
// Logic goes here
}
#Override
public void keyReleased(KeyEvent e) {
// Logic goes here
}
#Override
public void keyTyped(KeyEvent e) {
// Logic goes here
}
}