Does JavaFX have a binding type where i supply an observable map with a key and the computed value will always update as the key/value pair changes? Removing that key the key from the map will result to a null value, adding it back will track that value back.
I've been looking around the JavaDoc and found MapProperty, MapBinding and ObservableMapValue, but nothing seems to serve this purpose.
I've already designed my own variant, but would like to use a rather failsafe and tested version.
You can just do
someObjectProperty.bind(Bindings.createObjectBinding(
() -> myObservableMap.get(key), myObservableMap);
or, as #VGR points out in the comments
someObjectProperty.bind(Bindings.valueAt(someObservableMap, key));
Here is a SSCCE using the second approach:
import java.util.Arrays;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class BindToObservableMap extends Application {
private static final String[] keys = {"key1", "key2", "key3"};
#Override
public void start(Stage primaryStage) {
ObservableMap<String, String> map = FXCollections.observableHashMap();
for (String k : keys) map.put(k, k.replaceAll("key", "value"));
GridPane grid = new GridPane();
grid.setHgap(5);
grid.setVgap(5);
grid.setPadding(new Insets(10));
for (int i = 0 ; i < keys.length; i++) {
grid.add(new Label(keys[i]), 0, i);
Label boundLabel = new Label();
boundLabel.textProperty().bind(Bindings.valueAt(map, keys[i]));
grid.add(boundLabel, 1, i);
}
ComboBox<String> keyCombo = new ComboBox<>();
keyCombo.getItems().setAll(keys);
TextField valueField = new TextField();
Button update = new Button("Update");
EventHandler<ActionEvent> handler = e -> {
map.put(keyCombo.getValue(), valueField.getText());
valueField.clear();
keyCombo.requestFocus();
};
valueField.setOnAction(handler);
update.setOnAction(handler);
grid.addRow(keys.length, keyCombo, valueField);
grid.add(update, 0, keys.length + 1);
Scene scene = new Scene(grid);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
Does JavaFX have a binding type where i supply an observable map with a key and the computed value will always update as the key/value pair changes? Removing that key the key from the map will result to a null value, adding it back will track that value back.
I've been looking around the JavaDoc and found MapProperty, MapBinding and ObservableMapValue, but nothing seems to serve this purpose.
I've already designed my own variant, but would like to use a rather failsafe and tested version.
You can just do
someObjectProperty.bind(Bindings.createObjectBinding(
() -> myObservableMap.get(key), myObservableMap);
or, as #VGR points out in the comments
someObjectProperty.bind(Bindings.valueAt(someObservableMap, key));
Here is a SSCCE using the second approach:
import java.util.Arrays;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class BindToObservableMap extends Application {
private static final String[] keys = {"key1", "key2", "key3"};
#Override
public void start(Stage primaryStage) {
ObservableMap<String, String> map = FXCollections.observableHashMap();
for (String k : keys) map.put(k, k.replaceAll("key", "value"));
GridPane grid = new GridPane();
grid.setHgap(5);
grid.setVgap(5);
grid.setPadding(new Insets(10));
for (int i = 0 ; i < keys.length; i++) {
grid.add(new Label(keys[i]), 0, i);
Label boundLabel = new Label();
boundLabel.textProperty().bind(Bindings.valueAt(map, keys[i]));
grid.add(boundLabel, 1, i);
}
ComboBox<String> keyCombo = new ComboBox<>();
keyCombo.getItems().setAll(keys);
TextField valueField = new TextField();
Button update = new Button("Update");
EventHandler<ActionEvent> handler = e -> {
map.put(keyCombo.getValue(), valueField.getText());
valueField.clear();
keyCombo.requestFocus();
};
valueField.setOnAction(handler);
update.setOnAction(handler);
grid.addRow(keys.length, keyCombo, valueField);
grid.add(update, 0, keys.length + 1);
Scene scene = new Scene(grid);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I would like to display only certain information in table view such as only "male" people in a database. I'm only at using javafx. Thanks for your help in advance.
This is my current table
I would like to filter the table so that only rows with "order status : PAID" are displayed in the table.
If you can use java 8 you can use the built in FilteredList and predicates. Here's something I wrote to test regex filtering. I modified it a bit to be more like your example and use javafx 2.2 if required. Just change some of the commented lines to use java 8.
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TableTest extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<LineItem> items = FXCollections.observableArrayList();
items.addAll(new LineItem("hello",123.45),
new LineItem("paid in full",0.01),
new LineItem("paid",0.01),
new LineItem("due",0.01),
new LineItem("paid",0.01));
//for java8
//FilteredList<LineItem> filteredItems = new FilteredList(items, e->true);
//not java8
ObservableList<LineItem> filteredItems = FXCollections.observableArrayList(items);
TableView tableView = new TableView(filteredItems);
TableColumn<LineItem,String> descCol = new TableColumn<>("desc");
descCol.setCellValueFactory(new PropertyValueFactory<>("desc"));
TableColumn<LineItem, Double> amountCol = new TableColumn<>("amount");
amountCol.setCellValueFactory(new PropertyValueFactory<>("amount"));
tableView.getColumns().addAll(descCol,amountCol);
TextField filterText = new TextField();
filterText.setPromptText("type filter and press enter");
filterText.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
//normal java8
//filteredItems.setPredicate(li -> li.desc.getValue().contains(filterText.getText()));
//regex java 8
//filteredItems.setPredicate(li -> li.desc.getValue().matches("(?i)"+filterText.getText()));
//not javafx 8
filteredItems.clear();
for (LineItem li: items)
if (li.desc.getValue().contains(filterText.getText()))
filteredItems.add(li);
}
});
VBox root = new VBox();
root.getChildren().addAll(tableView, filterText);
Scene scene = new Scene(root, 300, 300);
primaryStage.setTitle("Filter table test");
primaryStage.setScene(scene);
primaryStage.show();
}
public class LineItem {
private final StringProperty desc = new SimpleStringProperty();
private final DoubleProperty amount = new SimpleDoubleProperty();
public StringProperty descProperty() {return desc;}
public DoubleProperty amountProperty() {return amount;}
public LineItem(String dsc, double amt) {
desc.set(dsc); amount.set(amt);
}
}
}
todo, there's supposedly a way to bind the predicateProperty, but I can't figure that out.
Edit: If you want a binding, instead of the handler for ActionEvent, do something like
filteredItems.predicateProperty().bind(
Bindings.createObjectBinding(() ->
li -> li.desc.getValue().contains(filterText.getText()),
filterText.textProperty())
);
If you are using Javafx 2.0+, you have to write your custom table view filter or you can use javafx_filterable_columns. For custom table filters, you can follow this link, which provides a very nice approach to writing filters
http://code.makery.ch/blog/javafx-8-tableview-sorting-filtering/
In Javafx8, may be the facility is inbuilt. But, I am not sure, since I have never used it personally !
I am making an alarm clock kind of program and I need a way to make the clock face a specific font. I have tried multiple times in multiple ways. If that is not possible can you please provide another solution? Thank you in advance!
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.print.DocFlavor.URL;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Menu extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
//Frame stuff (works)
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
//Frame Size
Scene scene = new Scene(new DigitalClock(),1080, 720);
//Icon
primaryStage.getIcons().add(new Image(getClass().getResourceAsStream(("/lib/logo.png"))));
primaryStage.setTitle("Clock: 140 Edition");
//Necessities
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String args[]) {
launch(args);
}
}
class Util {
public static String pad(int fieldWidth, char padChar, String s) {
StringBuilder sb = new StringBuilder();
for (int i = s.length(); i < fieldWidth; i++) {
sb.append(padChar);
}
sb.append(s);
return sb.toString();
}
}
class DigitalClock extends Label {
public DigitalClock() {
bindToTime();
}
// the digital clock updates once a second.
private void bindToTime() {
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(0),
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
Calendar time = Calendar.getInstance();
String hourString = Util.pad(2, ' ', time.get(Calendar.HOUR) == 0 ? "12" : time.get(Calendar.HOUR) + "");
String minuteString = Util.pad(2, '0', time.get(Calendar.MINUTE) + "");
String secondString = Util.pad(2, '0', time.get(Calendar.SECOND) + "");
String ampmString = time.get(Calendar.AM_PM) == Calendar.AM ? "AM" : "PM";
setText(hourString + ":" + minuteString + ":" + secondString + " " + ampmString);
}
}
),
new KeyFrame(Duration.seconds(1))
);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
I also know some of the imports are not used, I would prefer to keep them. Thanks again!
🍒The Font has to do with the Label you are using in the example.
Must
read:http://www.guigarage.com/2014/10/integrate-custom-fonts-javafx-application-using-css/
<-----------------Ways to do----------------->
1)Using external css:
/*The font path*/
#font-face{
src: url("../fonts/Younger than me Bold.ttf");
}
/* An element which has this id*/
#LabelID{
-fx-font-family:"Younger than me";
-fx-font-size:18.0;
}
//or for all labels
.label{
-fx-font-family:"Younger than me";
-fx-font-size:18.0;
}
2)Using setStyle(...):
`label.setStyle("-fx-font-family:monospace; -fx-font-size:16px; -fx-text-fill:black; -fx-border-color:red;");`
3)Using setFont(...):
b.setFont(new Font("Arial", 24));
4)Tricky and not recommended ( not included in JavaFX docs) : here
Relative posts:
How to set custom fonts in JavaFX Scene Builder using CSS
https://blog.idrsolutions.com/2014/04/use-external-css-files-javafx/
Have a look at https://gist.github.com/jewelsea/2658491
Am trying to load a new scene from another class into the main javafx page and this is the main javafx page
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Cbt extends Application {
Stage stageCbt;
Scene staffMainPage;
BorderPane border;
FlowPane addFlowPane;
Hyperlink SQuestions, setParameters;
public void start(Stage theMainStage){
stageCbt = theMainStage;
border = new BorderPane();
SQuestions.setOnAction(ae -> createQuestionsLinkClicked(ae));
}
private FlowPane addFlowPaneLO() {
addFlowPane = new FlowPane();
addFlowPane.getStyleClass().addAll("pane", "vbox");
Hyperlink options[] = new Hyperlink[]{
setParameters = new Hyperlink("set Questions Parameters"),
SQuestions = new Hyperlink("Select past jamb questions")
};
for (int i = 0; i < 2; i++) {
addFlowPane.setMargin(options[i], new Insets(0, 0, 0, 8));
addFlowPane.getChildren().add(options[i]);
}
addFlowPane.getStyleClass().addAll("pane", "flow");
addFlowPane.setPrefWrapLength(170);
return addFlowPane;
}
public void createQuestionsLinkClicked(ActionEvent ae) {
if (ae.getSource() == SQuestions) {
//Questions ques = new Questions();
stageCbt.setScene(staffMainPage);
borderpane.setCenter(new Questions().getAnchor());
}
}
public static void main(String[] args) {
launch(args);
}
}
and this is the the subclass(Questions.java)
import javafx.beans.property.StringProperty;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListView;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
public class Questions {
TextArea putQues;
Button end, edit, review, next;
BorderPane bpane;
GridPane gdQues;
AnchorPane anchor;
ToggleGroup oneSelected;
Text msg, option;
public static String lvo[] = new String[]{
"Option A",
"Option B",
"Option C",
"Option D",
"Option E"
};
public static String rbo[] = new String[]{
"A",
"B",
"C",
"D",
"E"
};
RadioButton rbs[] = new RadioButton[rbo.length];
ListView lvs [] = new ListView[lvo.length];
public Questions(){
gdQues = new GridPane();
gdQues.setHgap(10);
gdQues.setVgap(10);;
gdQues.setAlignment(Pos.TOP_LEFT);
gdQues.getStyleClass().add("grid");
msg = new Text("Questions should be put below");
msg.setFont(Font.font("Arial", FontWeight.BOLD, 20));
gdQues.add(msg, 3, 3);
putQues = new TextArea();
putQues.setPromptText("Type question here");
gdQues.add(putQues, 4, 0);
option = new Text("Options");
option.setFont(Font.font("Monotype Corsiva", FontWeight.BOLD, 10));
gdQues.add(option, 6, 0);
//options = new ListView();
//ListView optionAll = new ListView();
for(int i = 0; i < rbo.length; i++){
RadioButton rb = rbs[i] = new RadioButton(rbo[i]);
ListView lv = lvs[i];
rbs[i].setToggleGroup(oneSelected);
}
//gdQues.getChildren().addAll(rbs);
//gdQues.getChildren().addAll(lvs);
anchor = new AnchorPane();
anchor.getStyleClass().add("pane");
end = new Button("End");
edit = new Button("Edit");
review = new Button("Review");
next = new Button("Next");
HBox hb = new HBox();
hb.getStyleClass().add("hb");
hb.getChildren().addAll(end, edit, review, next);
anchor.getChildren().addAll(gdQues, hb);
}
public Parent getGdQues(){
return gdQues;
}
public Parent getAnchor(/*GridPane gdQues*/){
return anchor;
}
public Parent getPutQues(){
return putQues;
}
public Text getMsg(){
return msg;
}
/*public Parent getRbs(){
return rbo;
}
public Parent getLvs(){
return lvs;
}*/
}
all other controls display except the radiobuttons(of which I guess is because the function wasn't move into the constructor).
My question now is how get the radiobuttons to display?
I do not know the format your are using while in instantiating your radio button, see:
RadioButton rb = rbs[i] = new RadioButton(rbo[i]);
See also this question: initialize multiple variables in a same line in java
Maybe are the instantiation in the other oder as you think done. Or you simply forget to delete the fisrt part. Whatever, it seams, you do not use the rb variable, so prefer:
rbs[i] = new RadioButton(rbo[i]);
Or separate it into 2 lines.
At http://docs.oracle.com/javafx/2/charts/pie-chart.htm Oracle suggests using
caption.setTranslateX(e.getSceneX());
caption.setTranslateY(e.getSceneY());
to place a Label where the mouse was clicked.. But this does not work at all. See this print screen for proof:
In the code for the example you cite, the PieChart and caption Label are both placed directly in a Group which is the root of the scene. The position of the Label before applying transformations is therefore (0,0) (the top left of the Scene), and so translating it by (e.getSceneX(), e.getSceneY()) moves it to the position of the mouse.
If your layout is different, then the same computation will not necessarily work. For a more general solution, put the chart and caption in a Group, and then call sceneToLocal(...) on the Group to translate the scene coordinates to the correct coordinates in the Group:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class PieChartSample extends Application {
#Override
public void start(Stage stage) {
BorderPane root = new BorderPane();
ObservableList<PieChart.Data> pieChartData =
FXCollections.observableArrayList(
new PieChart.Data("Grapefruit", 13),
new PieChart.Data("Oranges", 25),
new PieChart.Data("Plums", 10),
new PieChart.Data("Pears", 22),
new PieChart.Data("Apples", 30));
final PieChart chart = new PieChart(pieChartData);
chart.setTitle("Imported Fruits");
final Label caption = new Label("");
caption.setTextFill(Color.DARKORANGE);
caption.setStyle("-fx-font: 24 arial;");
Group chartWithCaption = new Group(chart, caption);
for (final PieChart.Data data : chart.getData()) {
data.getNode().addEventHandler(MouseEvent.MOUSE_PRESSED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
Point2D locationInScene = new Point2D(e.getSceneX(), e.getSceneY());
Point2D locationInParent = chartWithCaption.sceneToLocal(locationInScene);
caption.relocate(locationInParent.getX(), locationInParent.getY());
caption.setText(String.valueOf(data.getPieValue()) + "%");
}
});
}
root.setCenter(chartWithCaption);
// Just some stuff to change the overall layout:
HBox controls = new HBox(5);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(new Label("Some other stuff here"), new TextField(), new Button("OK"));
root.setTop(controls);
root.setPadding(new Insets(0, 0, 10, 40));
root.setLeft(new Circle(25, Color.SALMON));
Scene scene = new Scene(root);
stage.setTitle("Imported Fruits");
stage.setWidth(600);
stage.setHeight(500);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}