I am new in this forum and also to programming. Furthermore, my English is not the best, but I hope you can understand what I mean and help me out.
I want to program a GUI and using JavaFX and the Gauges from the medusa- library. What I need to do is changing the maxValue and the minValue of the Gauge while the program is running. I can change the values, but the scale of the Gauge does not rearrange the ticks properly. For example, when I create a Gauge from 0 to 10 and then set the maxValue to 100, the scale shows all numbers as a major tick and the scale becomes unreadable. Because I could not find how to fix this, I have tried to delete the original Gauge and create simply a new one.
Here is what I have tried(I deleted the rest of the class, because it has over 800 lines):
package application;
import eu.hansolo.medusa.Gauge;
import eu.hansolo.medusa.Gauge.SkinType;
import eu.hansolo.medusa.GaugeBuilder;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
public class Controller {
#FXML
StackPane stackPane;
private Gauge gauge;
private Button button;
#FXML
private void initialize() {
gauge = GaugeBuilder.create().skinType(SkinType.QUARTER).barBackgroundColor(Color.LIGHTGREY)
.needleColor(Color.RED).decimals(0).valueVisible(true).valueColor(Color.BLACK).title("Stromstärke")
.unit("[mA]").subTitle("Phase 1").minValue(0).maxValue(10).build();
stackPane.getChildren().add(gauge);
}
public void setMaxValueGauge(StackPane pStackPane, Gauge pGauge, int intMinValue, int pMaxValue) {
pStackPane.getChildren().remove(pGauge);
Gauge newGauge = GaugeBuilder.create().skinType(pGauge.getSkinType()).barBackgroundColor(pGauge.getBarColor())
.needleColor(pGauge.getNeedleColor()).decimals(0).valueVisible(true).valueColor(Color.BLACK)
.title(pGauge.getTitle()).unit(pGauge.getUnit()).subTitle(pGauge.getSubTitle()).minValue(intMinValue)
.maxValue(pMaxValue).build();
pGauge = null;
pGauge = newGauge;
pStackPane.getChildren().add(pGauge);
}
#FXML
public void testButton() {
setMaxValueGauge(stackPane, gauge, 0, 30);
}
}
The method testButton() is only for testing. When I call testButton() the first time, it works well, but when I use it twice or more, it seems that the old Gauge is not replaced. Instead the new one stacks on top of the old one.
Can you please help me. I need either to fix the ticks of the scale, when I set a new maxValue, or to properly replace the old Gauge in the Stackpane.
You appear to be doing too much to set the max-value.
Here is an MCVE that changes the maxValue.
import eu.hansolo.medusa.Gauge;
import eu.hansolo.medusa.Gauge.SkinType;
import eu.hansolo.medusa.GaugeBuilder;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author blj0011
*/
public class MedusaGaugeTest extends Application
{
#Override
public void start(Stage primaryStage)
{
Gauge gauge = GaugeBuilder.create().skinType(SkinType.QUARTER).barBackgroundColor(Color.LIGHTGREY)
.needleColor(Color.RED).decimals(0).valueVisible(true).valueColor(Color.BLACK).title("Stromstärke")
.unit("[mA]").subTitle("Phase 1").minValue(0).maxValue(10).build();;
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), (ActionEvent event) -> {
if (gauge.getValue() <= gauge.getMaxValue()) {
gauge.setValue(gauge.getValue() + 1);
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
Button btn = new Button();
btn.setText("Start");
btn.setOnAction((ActionEvent event) -> {
timeline.play();
});
Button btn2 = new Button();
btn2.setText("Increase MaxValue");
btn2.setOnAction((ActionEvent event) -> {
gauge.setMaxValue(15);
});
VBox root = new VBox(gauge, new VBox(btn, btn2));
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Related
I'm currently working on a capstone project for a Java class and a problem I'm coming across frequently is displaying a variable's value in a JavaFX scene. I need a kickstart to get me moving, my google searches aren't bearing any fruit.
Thanks all :)
You can use a Label. Attach it to your scene and call Label.setText(String text) with the string representation of your variable value. Here's a complete example, using a Label:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class main3 extends Application {
static Integer variable = 250; // The value will be displayed in the window
#Override public void start(Stage primaryStage) {
Label variableLabel = new Label();
variableLabel.setFont(new Font(30));
variableLabel.setText("" + variable);
variableLabel.setLayoutX(175);
variableLabel.setLayoutY(125);
Group group = new Group(variableLabel);
Scene scene = new Scene(group, 400, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args){
launch();
}
}
Result
Is it possible to use a Node as a mouse cursor? I'm thinking in a ProgressIndicator. For example a determinate one, letting the user know how much percentage of the current task is done.
Probably the most reliable way to do this is to set the cursor to Cursor.NONE, and have a label with the progress indicator as its graphic, which tracks the mouse coordinates.
I tried using an ImageCursor which updated, but nothing appeared: I am guessing the images couldn't be computed quickly enough.
Here's an SSCCE of the first technique:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ProgressIndicatorAsCursor extends Application {
#Override
public void start(Stage primaryStage) {
Button button = new Button("Start");
Service<Void> service = new Service<Void>() {
#Override
public Task<Void> createTask() {
return new Task<Void>() {
#Override
public Void call() throws Exception {
for (int i = 1 ; i <= 1000; i++) {
Thread.sleep(10);
updateProgress(i, 1000);
}
return null ;
}
};
}
};
button.disableProperty().bind(service.runningProperty());
button.setOnAction(e -> service.restart());
ProgressIndicator pi = new ProgressIndicator();
pi.progressProperty().bind(service.progressProperty());
Pane pane = new Pane();
// fill pane with rectangle as task progresses:
Rectangle rectangle = new Rectangle();
rectangle.setFill(Color.CORNFLOWERBLUE);
rectangle.setX(0);
rectangle.widthProperty().bind(pane.widthProperty());
rectangle.heightProperty().bind(pane.heightProperty().multiply(service.progressProperty()));
rectangle.yProperty().bind(pane.heightProperty().subtract(rectangle.heightProperty()));
pane.getChildren().add(rectangle);
Label label = new Label();
label.graphicProperty().bind(
Bindings.when(service.runningProperty())
.then(pi)
.otherwise((ProgressIndicator)null));
pane.setOnMouseEntered(e ->
pane.getChildren().add(label));
pane.setOnMouseExited(e ->
pane.getChildren().remove(label));
pane.setOnMouseMoved(e -> label.relocate(e.getX(), e.getY()));
pane.cursorProperty().bind(
Bindings.when(service.runningProperty())
.then(Cursor.NONE)
.otherwise(Cursor.DEFAULT));
BorderPane.setAlignment(button, Pos.CENTER);
BorderPane.setMargin(button, new Insets(10));
BorderPane root = new BorderPane(pane, new Rectangle(0,0,0,20), null, button, null);
primaryStage.setScene(new Scene(root, 400, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Sees it's impossible, but you could obtain the cursor property and keep updating it with an image as your desire.
Is there any way to translate somethings like pop up windows(that are not Node) in java FX ?
for example fade transition , translate transition or any timeline transition .... .
Thank's
Create a property and use a Timeline to "animate" the property. Register a listener with the property and update the window when its value changes.
For example:
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TranslateWindowExample extends Application {
#Override
public void start(Stage primaryStage) {
Button moveButton = new Button("Move");
moveButton.setOnAction(event -> {
double currentX = primaryStage.getX() ;
DoubleProperty x = new SimpleDoubleProperty(currentX);
x.addListener((obs, oldX, newX) -> primaryStage.setX(newX.doubleValue()));
KeyFrame keyFrame = new KeyFrame(Duration.seconds(1), new KeyValue(x, currentX + 100));
Timeline animation = new Timeline(keyFrame);
animation.play();
});
StackPane root = new StackPane(moveButton);
Scene scene = new Scene(root, 250, 150);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Can you give an example of what do you call popup window ?
For instance, you can create a new stage on the top of your main stage.
You can also use some Tooltip class to add some text over the top of your window.
If you want more classical windows, you should have a look to Alert class.
Anthony
If you create a binding between a JavaFX TextField and a property, then this binding is invalidated on every keystroke, which causes a change to the text.
If you have a chain of bindings the default behavior could cause problems, because in the middle of the editing values may be not valid.
Ok, I know I could create an uni-directional binding from the property to the textfield and register a change listener to get informed when the cursor leaves the field and update the property manually if necessary.
Is there an easy, elegant way to change this behavior so that the binding is only invalidated when the editing is complete, e.g. when the cursor leaves the field?
Thanks
I think you've pretty much described the only way to do it. Here's about the cleanest way I can see to implement it (using Java 8, though it's easy enough to convert the lambdas back to be JavaFX 2.2 compatible if you need):
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CommitBoundTextField extends Application {
#Override
public void start(Stage primaryStage) {
TextField tf1 = new TextField();
createCommitBinding(tf1).addListener((obs, oldText, newText) ->
System.out.printf("Text 1 changed from \"%s\" to \"%s\"%n", oldText, newText));
TextField tf2 = new TextField();
createCommitBinding(tf2).addListener((obs, oldText, newText) ->
System.out.printf("Text 2 changed from \"%s\" to \"%s\"%n", oldText, newText));
VBox root = new VBox(5, tf1, tf2);
Scene scene = new Scene(root, 250, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
private StringBinding createCommitBinding(TextField textField) {
StringBinding binding = Bindings.createStringBinding(() -> textField.getText());
textField.addEventHandler(ActionEvent.ACTION, evt -> binding.invalidate());
textField.focusedProperty().addListener((obs, wasFocused, isFocused)-> {
if (! isFocused) binding.invalidate();
});
return binding ;
}
public static void main(String[] args) {
launch(args);
}
}
I realize that I am a little late with a response, but thought this might be useful to someone.
When using TextFields, I often attach a TextFormatter to help validate entries. You can attach a listener to the formatters' valueProperty. That property is updated when the text is committed, rather than on every keystroke.
Here's an example of what I am talking about using a TextField specialized for integer inputs. When you make edits in the text field, the changes will be reflected in the Label when you tap Enter, lose focus by clicking the button, switch to a different window, and so on.
import javafx.application.Application;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;
class IntTextField extends TextField {
private final IntegerProperty value;
TextFormatter<Integer> formatter;
public double getValue() {
return value.getValue();
}
public void setValue(int newValue) {
value.setValue(newValue);
}
public IntegerProperty valueProperty() {
return value;
}
public StringBinding getStringBinding () {
return value.asString();
}
IntTextField(int initValue) {
value = new SimpleIntegerProperty(initValue);
setText(initValue + "");
formatter = new TextFormatter(new IntegerStringConverter(), initValue);
formatter.valueProperty().addListener((ObservableValue<? extends Integer> obs,
Integer oldValue, Integer newValue) -> value.setValue(newValue));
setTextFormatter(formatter);
}
IntTextField() {
this(0);
}
}
public class TFBindingDemo extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("TFBindingDemo");
IntTextField intTextField = new IntTextField(12345);
intTextField.setMaxWidth(150);
Label label = new Label("Type in the TextField");
label.textProperty().bind(intTextField.getStringBinding());
Button removeFocusButton = new Button("Click Here to Remove Focus");
VBox root = new VBox(20, intTextField, label, removeFocusButton);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
Scene scene = new Scene(root, 325, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I don't know if anyone could understand my problem from the title, but here's more specific description. I have class, in which I created a FlowPane, where I added objects of another class(images packed inside VBoxes). Each VBox have ContextMenu, where is MenuItem "Remove File". My problem is, how to remove this object while beeing inside the VBox class. Here is a little part of my code:
//removed, entire code is below after edit
The code where I'm accessing my CustomPane (my class of FlowPane, with specified attributes) works, because I can remove object if I'm doing it by their indexes, but when I remove one of them, other's indexes changes, so I'm looking for another solution. I need to specifically remove the object of the class in the code.
Okay so here is so called sscce, which of I had no idea, since now:
package sscce;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Sscce extends Application {
#Override
public void start(Stage primaryStage) {
CustomPane root = new CustomPane();
root.setPadding(new Insets(20));
root.setHgap(10);
root.setVgap(10);
for (int i = 0; i < 10; i++) {
RectangleBox recB = new RectangleBox();
root.getChildren().add(recB);
}
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class RectangleBox extends VBox {
static int index = 0;
public RectangleBox() {
Rectangle rec = new Rectangle(150, 150);
rec.setFill(Color.GREEN);
StackPane sp = new StackPane();
sp.getChildren().add(rec);
Label label = new Label(Integer.toString(index));
index++;
sp.getChildren().add(label);
getChildren().add(sp);
final ContextMenu cm = new ContextMenu();
MenuItem removeRec = new MenuItem("Remove Rectangle");
removeRec.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
((CustomPane) getParent()).getPane().getChildren().remove(0); //here is the problem, I want this line to remove the rectangle I clicked on(now it's removing first element in the pane).
}
});
cm.getItems().add(removeRec);
createContextMenuEvent(cm, rec);
}
private void createContextMenuEvent(final ContextMenu cm, final Rectangle rec) {
addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if (t.getButton() == MouseButton.SECONDARY) {
cm.show(rec, t.getScreenX(), t.getScreenY());
}
}
});
}
}
class CustomPane extends FlowPane {
public CustomPane() {
//setAlignment(Pos.CENTER);
setHgap(25);
setVgap(25);
setPadding(new Insets(20));
}
public CustomPane getPane() {
return this;
}
}
It should work after copy/paste this entire code to java project. So I removed everything that is not neccessary, and I have replaced images to rectangles, now this program looks kind of stupid;p
I added comment to a line I have problem with. I hope now it's a lot clearer than before.
try this:
((CustomPane) RectangleBox.this.getParent()).getChildren().remove(RectangleBox.this);
hope it helps.