To Hide JavaFx fxml or JavaFx swing application to System Tray - javafx-2

I want to develop a client app for website .
I want the app to reside in system tray when minimised.
I dont know how to accomplish this task .
Is their any example for this type of operation.

The key here is to set the implicit exit to false Platform.setImplicitExit(false);
Also is important to show and hide the stage in a new thread.
Platform.runLater(new Runnable() {
#Override
public void run() {
stage.show();
}
});
Platform.runLater(new Runnable() {
#Override
public void run() {
stage.hide();
}
});
Next, the whole code:
import java.awt.AWTException;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URL;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javax.imageio.ImageIO;
/**
*
* #author alvaro
*/
public class TrayTest extends Application {
private boolean firstTime;
private TrayIcon trayIcon;
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
createTrayIcon(stage);
firstTime = true;
Platform.setImplicitExit(false);
Scene scene = new Scene(new Group(), 800, 600);
stage.setScene(scene);
stage.show();
}
public void createTrayIcon(final Stage stage) {
if (SystemTray.isSupported()) {
// get the SystemTray instance
SystemTray tray = SystemTray.getSystemTray();
// load an image
java.awt.Image image = null;
try {
URL url = new URL("http://www.digitalphotoartistry.com/rose1.jpg");
image = ImageIO.read(url);
} catch (IOException ex) {
System.out.println(ex);
}
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
hide(stage);
}
});
// create a action listener to listen for default action executed on the tray icon
final ActionListener closeListener = new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
System.exit(0);
}
};
ActionListener showListener = new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
Platform.runLater(new Runnable() {
#Override
public void run() {
stage.show();
}
});
}
};
// create a popup menu
PopupMenu popup = new PopupMenu();
MenuItem showItem = new MenuItem("Show");
showItem.addActionListener(showListener);
popup.add(showItem);
MenuItem closeItem = new MenuItem("Close");
closeItem.addActionListener(closeListener);
popup.add(closeItem);
/// ... add other items
// construct a TrayIcon
trayIcon = new TrayIcon(image, "Title", popup);
// set the TrayIcon properties
trayIcon.addActionListener(showListener);
// ...
// add the tray image
try {
tray.add(trayIcon);
} catch (AWTException e) {
System.err.println(e);
}
// ...
}
}
public void showProgramIsMinimizedMsg() {
if (firstTime) {
trayIcon.displayMessage("Some message.",
"Some other message.",
TrayIcon.MessageType.INFO);
firstTime = false;
}
}
private void hide(final Stage stage) {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (SystemTray.isSupported()) {
stage.hide();
showProgramIsMinimizedMsg();
} else {
System.exit(0);
}
}
});
}
}

As far as I know it will be possible in JFX 8. Right now the best solution is to embed your application into AWT and hide the AWT window itself.

Related

Implementing a pause and resume feature for JavaFX Tasks

I'm building a UI for a Simulator running in background. Since this Simulator may not hold for a long time, it of course has to be in a separate thread from the JavaFx Thread. I want to start, pause, resume and stop/terminate the Simulator when the corresponding button is clicked.
The service class that advances the simulator looks like this:
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class SimulatorService extends Service<Void> {
private Simulator simulator;
private long cycleLengthMS = 1000;
private final AtomicBoolean simulatorStopped = new AtomicBoolean(false);
public SimulatorService(Simulator simulator){
this.simulator = simulator;
}
#Override
protected Task<Void> createTask() {
return new Task<>() {
#Override
protected Void call() {
System.out.println("Requested start of Simulator");
int state;
do {
// advance
state = simulator.nextStep();
try {
TimeUnit.MILLISECONDS.sleep(cycleLengthMS);
if(simulatorStopped.get()){
super.cancel();
}
} catch (InterruptedException e) {
return null;
}
}
while (state == 0 && !simulatorStopped.get());
return null;
}
};
}
#Override
public void start(){
if(getState().equals(State.READY)){
simulatorStopped.set(false);
super.start();
}
else if(getState().equals(State.CANCELLED)){
simulatorStopped.set(false);
super.restart();
}
}
#Override
public boolean cancel(){
if(simulatorStopped.get()){
simulatorStopped.set(true);
return false;
} else{
simulatorStopped.set(true);
return true; //if value changed
}
}
}
The Simulator starts the Service if a button on the GUI is pressed:
import javafx.application.Platform;
import java.util.concurrent.atomic.AtomicInteger;
public class Simulator {
Model model;
private final SimulatorService simulatorService;
public Simulator(Model model){
this.model = model;
simulatorService = new SimulatorService(this);
}
public int nextStep(){
final AtomicInteger res = new AtomicInteger(0);
Platform.runLater(new Thread(()-> {
res.set(model.nextStep());
}));
return res.get();
}
public boolean stopSimulationService() throws IllegalStateException{
return simulatorService.cancel();
}
public void startSimulationService() throws IllegalStateException{
simulatorService.start();
}
}
Parts of the window are redrawn if a observed value in the model changes:
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class Model {
private final IntegerProperty observedValue = new SimpleIntegerProperty(0);
public int nextStep() {
observedValue.set(observedValue.get() + 1);
return observedValue.get() > 500000 ? 1 : 0;
}
public int getObservedValue() {
return observedValue.get();
}
public IntegerProperty observedValueProperty() {
return observedValue;
}
public void setObservedValue(int observedValue) {
this.observedValue.set(observedValue);
}
}
The redraw happens in another class:
import javafx.beans.value.ChangeListener;
public class ViewController {
private View view;
private Simulator simulator;
private Model model;
public ViewController(Simulator simulator) {
this.simulator = simulator;
this.view = new View();
setModel(simulator.model);
view.nextStep.setOnMouseClicked(click -> {
simulator.nextStep();
});
view.startSim.setOnMouseClicked(click -> {
simulator.startSimulationService();
});
view.stopSim.setOnMouseClicked(click ->{
simulator.stopSimulationService();
});
}
public View getView() {
return view;
}
private final ChangeListener<Number> observedValueListener = (observableValue, oldInt, newInt) -> {
view.updateView(newInt.intValue());
};
public void setModel(Model m) {
this.model = m;
m.observedValueProperty().addListener(observedValueListener);
}
}
The corresponding view:
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;
public class View extends BorderPane {
Button nextStep = new Button("next step");
Button startSim = new Button("start");
Button stopSim = new Button("stop");
GridPane buttons = new GridPane();
Text num = new Text();
public View(){
buttons.add(nextStep,0,0);
buttons.add(startSim,0,1);
buttons.add(stopSim,0,2);
buttons.setAlignment(Pos.BOTTOM_LEFT);
setCenter(buttons);
setTop(num);
}
public void updateView(int num){
this.num.setText("" + num);
}
}
Main:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
ViewController c = new ViewController(new Simulator(new Model()));
Scene scene = new Scene(c.getView(),200,200);
stage.setScene(scene);
stage.show();
}
}

Threads in JavaFX: Not on FX application thread

I want study how to work with Threads in JavaFX. For example, 2 processes, which should change text on the lables every 100 ms, and updating information on the screen also every 100 ms.
But in this case it doesnt works. IDEA writes:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
I have read many examples with the same problem, but any of their solutions doesnt worked.
What I should to do?
Thanks.
sample.fxml
...
<Button fx:id="startBut" layoutX="100.0" layoutY="50.0" mnemonicParsing="false" onAction="#testingOfThread" prefHeight="25.0" prefWidth="65.0" text="Export" />
<Label fx:id="firstStatus" layoutX="100.0" layoutY="100" text="Status" />
<Label fx:id="secondStatus" layoutX="100.0" layoutY="150" text="Status" />
...
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Sample");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
//Take control to Controller
public void initializeController(){
FXMLLoader loader = new FXMLLoader();
Controller controller = loader.getController();
controller.setMain(this);
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
package sample;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.*;
public class Controller {
#FXML
private Label firstStatus;
#FXML
private Label secondStatus;
#FXML
public Button startBut;
//Link to MainApp
private Main Main;
//Constructor
public Controller(){
}
//Link for himself
public void setMain(Main main){
this.Main = main;
}
#FXML
private void testingOfThread(){
Task<Void> task = new Task<Void>() {
#Override public Void call() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.interrupted();
break;
}
System.out.println(i + 1);
firstStatus.setText(i+"");
}
return null;
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
Task<Void> task2 = new Task<Void>() {
#Override public Void call() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.interrupted();
break;
}
System.out.println(i + 1);
secondStatus.setText(i+"");
}
return null;
}
};
Thread th2 = new Thread(task2);
th2.start();
}
}
Find the code which update the GUI from a thread other than the application thread,then put them in runLater().
Platform.runLater(new Runnable() {
#Override
public void run() {
//update application thread
}
});

Running an infinite loop in a JavaFX(Embedded in javax.swing.JFrame) App

I am trying to run an Infinite loop in my JavaFX app.
An infinite while loop is present in my code in the Kulta.java file.
This loop actually freezes my app.
While the same thing works when I port the app to normal javax.swing.
Now since java.lang.Thread doesn't work for javafx, I came accross javafx.concurrent.Task,
which is not working as intended. As one of the main features of multithreading, i.e. running an infinite loop in a GUI app, is not served properly, please help me with the solution.
This is my code:
Urania.java
import java.awt.Toolkit;
import java.awt.Dimension;
import javax.swing.SwingUtilities;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
public class Urania {
public static final Dimension DIMENSION = Toolkit.getDefaultToolkit().getScreenSize();
public static void main(String[] args) {
SwingUtilities.invokeLater(
new Runnable() {
#Override
public void run() {
Kulta kulta = new Kulta();
kulta.setTitle("Abha K Pauri");
kulta.setSize(DIMENSION.width/2, DIMENSION.height/2);
kulta.setLocationRelativeTo(null);
kulta.setDefaultCloseOperation(EXIT_ON_CLOSE);
kulta.setVisible(true);
}
}
);
}
}
And here is my JFrame in which I have embedded my JavaFX app.
Kulta.java
import javax.swing.JFrame
import javafx.embed.swing.JFXPanel;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.control.Button;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.concurrent.Task;
public class Kulta extends JFrame {
private JFXPanel fxpanel;
private Scene scene;
private BorderPane borderpane;
private Button button;
public static final String INVOKE = "INVOKE";
public static final String INTERRUPT = "INTERRUPT";
public static final String[] COLORS = new String[]{"yellow", "pink", "green", "blue", "orange"};
public Kulta() {
fxpanel = new JFXPanel();
add(fxpanel);
Platform.runLater(
new Runnable() {
#Override
public void run() {
Kulta.this.setScene();
Kulta.this.setButton();
Kulta.this.setListener();
}
}
);
}
private void setScene() {
borderpane = new BorderPane();
scene = new Scene(borderpane);
fxpanel.setScene(scene);
}
private void setButton() {
button = new Button(INVOKE);
borderpane.setTop(button);
}
private void setListener() {
Event event = new Event();
button.setOnAction(event);
}
private class Event implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
boolean flag = true;
Task<Void> onInvoke = new Task<Void>() {
#Override
public Void call() {
int count = 0;
flag = true;
button.setText(INTERRUPT);
/* This loop freezes the app. */
while(flag) {
borderpane.setStyle("-fx-color: "+COLORS[count]+";");
count++;
if(count == COLORS.length)
count = 0;
}
return null;
}
};
Task<Void> onInterrupt = new Task<Void>() {
#Override
public Void call() {
button.setText(INVOKE);
if(flag)
flag = false; // This will stop the onInvoke thread
return null;
}
};
Task<Void> change = new Task<Void>() {
#Override
public Void call() {
if(button.getText().equals(INVOKE))
onInvoke().run();
else if(button.getText().equals(INTERRUPT))
onInterrupt().run();
}
};
change.run();
}
}
}
How should I write the loop in order to not let the app freeze.
Any code, solution, link or any help in any form will help a lot.
Thanks in advance.

Change an Observable Collection (bound to JavaFX node) in thread

What is the correct way to manipulate an Observable collection in a thread, where the collection is already bound to a JavaFX UI-node?
In my sample application, the connection between the collection and the nodes are broken before the thread can do any manipulation; and then they are re-connected after the thread is done. The methods are disconnectObservable() and connectObservable() respectively. Without these two methods, java.lang.IllegalStateException: Not on FX application thread is reported.
Ideally I would like ChangeObservableTask to make its changes to mWords, and then I would call some method to tell mObservable to refresh itself and notify its listeners. Is there such a thing?
Thanks.
package theapp;
import java.util.LinkedList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ThreadObList extends Application {
private final List<String> mWords;
private final ObservableList<String> mObservable;
private ListView mListView;
private Label mCount;
public ThreadObList() {
mWords = new LinkedList<>();
mObservable = FXCollections.observableList(mWords);
mWords.add("park");
}
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Start thread");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
ChangeObservableTask task = new ChangeObservableTask();
Thread thd = new Thread(task);
disconnectObservable();
thd.start();
try {
task.get();
System.out.println("ChangeObservableTask exited normally.");
}
catch(Exception ex) {
System.out.println(ex.getMessage());
}
connectObservable();
}
});
mCount = new Label();
mListView = new ListView();
VBox root = new VBox(5, btn, mCount, mListView);
VBox.setVgrow(mListView, Priority.ALWAYS);
connectObservable();
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private void connectObservable() {
mListView.setItems(mObservable);
mCount.textProperty().bind(Bindings.size(mObservable).asString());
}
private void disconnectObservable() {
mListView.setItems(null);
mCount.textProperty().unbind();
}
private class ChangeObservableTask extends Task<Void> {
#Override
protected Void call() throws Exception {
mObservable.add("dart");
mObservable.add("truck");
mObservable.add("ocean");
return null;
}
}
}
Once the list is used as the contents of the ListView, you can only manipulate it from the FX Application Thread. See the Task javadocs for a bunch of usage examples.
You can create a copy of your ObservableList and pass it to your task, manipulate the copy and return the results. Then update the ObservableList with the results in the onSucceeded handler.
Also note that you shouldn't make any blocking calls, such as task.get() on the FX Application Thread, as you can make the UI unresponsive by doing so.
So you should do something along the lines of:
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
ChangeObservableTask task = new ChangeObservableTask(new ArrayList<>(mObservable));
Thread thd = new Thread(task);
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
mObservable.setAll(task.getValue());
}
});
thd.start();
}
});
and
private class ChangeObservableTask extends Task<List<String>> {
private final List<String> data ;
ChangeObservableTask(List<String> data) {
this.data = data ;
}
#Override
protected List<String> call() throws Exception {
data.add("dart");
data.add("truck");
data.add("ocean");
return data;
}
}

JavaFX2: MODAL capability to a context menu

Is there a way to add MODAL capability to a context menu?
My code is below:
package snippet;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class ContextMenuSample extends Application
{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage)
{
stage.setTitle("ContextMenuSample");
Scene scene = new Scene(new Group(), 450, 250);
Label toLabel = new Label("To: ");
TextField notification = new TextField();
final ContextMenu contextMenu = new ContextMenu();
contextMenu.setAutoHide(false);
contextMenu.setOnShowing(new EventHandler<WindowEvent>()
{
public void handle(WindowEvent e)
{
System.out.println("showing the context menu");
}
});
contextMenu.setOnShown(new EventHandler<WindowEvent>()
{
public void handle(WindowEvent e)
{
System.out.println("context menu has been shown");
}
});
MenuItem closeItem = new MenuItem("Close");
closeItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
contextMenu.hide();
}
});
MenuItem colorItem = new MenuItem("Choose", new ColorPicker());
colorItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
System.out.println("Preferences");
}
});
GridPane contextGridPane = new GridPane();
Pane pane = new Pane();
pane.getChildren().add(contextGridPane);
contextMenu.getItems().addAll(colorItem, deleteItem// , subsystem1,
// radioItem
);
toLabel.setContextMenu(contextMenu);
GridPane grid = new GridPane();
grid.setVgap(4);
grid.setHgap(10);
grid.setPadding(new Insets(5, 5, 5, 5));
grid.add(toLabel, 0, 0);
grid.add(notification, 1, 0);
grid.add(new ColorPicker(), 2, 0);
Group root = (Group) scene.getRoot();
root.getChildren().add(grid);
stage.setScene(scene);
stage.show();
}
}
When the user clicks on the label "To", a context menu appears. I wish to have modal capability for this context menu such that the user is not able to do anything else on the application unless some operation is performed on the context menu. Also, when the context menu is active, the user should not be able to click anywhere else on the application.
Regards,
The easiest solution would be to call another Stage and set its modality with initModality before you show the stage. You probably want to use Modality.APPLICATION_MODEL as far as I understood you.
Here is a small example derived from yours (btw your code was not even runnable, it had errors)
public class ContextMenuSample extends Application
{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(final Stage stageOne)
{
final Stage stageTwo = new Stage();
stageTwo.initModality(Modality.APPLICATION_MODAL);
final Pane layoutOne = new HBox(10);
Pane layoutTwo = new HBox(10);
Label labelOne = new Label("click");
Label labelTwo = new Label("other click");
labelOne.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
stageTwo.show();
}
});
labelTwo.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
stageTwo.close();
}
});
Scene sceneOne = new Scene(layoutOne);
Scene sceneTwo = new Scene(layoutTwo);
layoutOne.getChildren().add(labelOne);
layoutTwo.getChildren().add(labelTwo);
stageOne.setScene(sceneOne);
stageTwo.setScene(sceneTwo);
stageOne.show();
}
}

Resources