Adding text to a label from another class. Java - java

In my program, there are two Form and Checked classes.
In the Form class, there is a Label and a Button. At the click in the Button I create an instance of the class Checked and start its thread.
Now, what I'm having trouble with is that I need to pass the text from the Checked class and change the Label value, but I have not succeeded.
Here is my code:
public class MainForm extends Application {
protected static int intVerifiedNews = 0;
Button btnPlay = new Button("Button");
Label lbVerifiedNews = new Label("News: ");
#Override
public void start(Stage primaryStage) throws IOException {
final BorderPane border = new BorderPane();
final HBox hbox = addHBox();
Scene scene = new Scene(border, 850, 500, Color.BLACK);
btnPlay.setPrefSize(100, 24);
btnPlay.setMinSize(24, 24);
btnPlay.setOnAction((event) -> {
Checked ch = new Checked();
ch.start();
}
);
border.setTop(hbox);
hbox.getChildren().addAll(btnPlay, lbVerifiedNews);
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox addHBox() {
HBox hbox = new HBox();
hbox.setPadding(new Insets(5, 0, 5, 5));
return hbox;
}
public static void main(String[] args) throws IOException {
launch(args);
}
}
Checked class:
public class Checked extends Thread {
public void run() {
for (int i = 0; i <= 5; i++) {
MainForm.intVerifiedNews ++;
//Here you need to pass the intVerifiedNews value to the Label
System.out.println(MainForm.intVerifiedNews);
}
}
}

Pass lbVerifiedNews into the constructor of the Checked class and store this reference in a field.
Checked ch = new Checked(lbVerifiedNews);
public class Checked extends Thread {
Label checkedLabelReference;
public Checked(Label label){
this.checkedLabelReference = label;
}
public void run() {
for (int i = 0; i <= 5; i++) {
MainForm.intVerifiedNews ++;
//Here you need to pass the intVerifiedNews value to the Label
Platform.runLater(new Runnable() {
#Override public void run() {
checkedLabelReference.setText(MainForm.intVerifiedNews);//update text
}});
System.out.println(MainForm.intVerifiedNews);
}
}
}

Generically you'll want to pass reference to the MainForm or form object that you want to update into your Checked class so that you have access to its update methods directly.
public class Checked implements Runnable {
public Checked(MainForm form1) {
// store form (or the object representing the text box directly) to update later
}
public void run() {
}
}

Related

how to switching scenes in javafx (NO FXML)

I ask for your understanding, I am a beginner;)
I'm trying to build a simple application using JavaFX. The problem is that when I open the window the first time it goes well, but if I want to change the scene it throws an error...
Exception in thread "JavaFX Application Thread"
java.lang.IllegalArgumentException:
AnchorPane#1809546[styleClass=root]is already set as root of another
scene#
Main class
public class Main extends Application{
//private Stage primaryStage;
#Override
public void start(Stage primaryStage) {
Login login = new Login();
Scene scene = login.okno();
primaryStage.setTitle("Komunikator sieciowy JAVA");
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
//public Stage getPrimaryStage() {
// return this.primaryStage;
//}
public static void main(String[] args) {
launch(args);
}
}
Login
public class Login {
private GridPane grid;
private Scene scene;
private Text title;
private Label nick;
private Button wejdzBtn;
private TextField userName;
//private Alert oknoDlg;
public Login() {
grid = new GridPane();
grid.setAlignment (Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25,25,25,25));
scene = new Scene (grid, 300, 150);
utworzBtn();
utworzLogin();
utworzTekst();
utworzNick();
//oknoDialogowe();
}
//private void oknoDialogowe() {
//Alert oknoDlg = new Alert(Alert.AlertType.CONFIRMATION);
//oknoDlg.setTitle("Informacja");
//oknoDlg.setContentText("test");
// oknoDlg.setHeaderText(null);
//oknoDlg.showAndWait();
//}
private void utworzBtn() {
wejdzBtn = new Button("Zaloguj si\u0119");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment (Pos.BOTTOM_RIGHT);
hbBtn.getChildren().add(wejdzBtn);
grid.add(hbBtn, 1, 2);
//wejdzBtn.setDisable(true);
wejdzBtn.setOnAction(e -> {
Messages mess = new Messages();
grid.getScene().setRoot(mess.messa());;
});
}
private void utworzLogin() {
nick = new Label("Nick:");
grid.add(nick, 0, 1);
}
private void utworzNick() {
userName = new TextField();
grid.add(userName,1,1);
// informacja w polu tekstowym
userName.setPromptText("Max 15 znak\u00f3w");
userName.setFocusTraversable(false);
//maksymalna ilość znaków
final int maxLength = 15;
userName.setOnKeyTyped(t -> {
if (userName.getText().length() > maxLength)
{
int pos = userName.getCaretPosition();
userName.setText(userName.getText(0, maxLength));
userName.positionCaret(pos);
}
});
}
private void utworzTekst() {
title = new Text ("Dzień dobry!");
title.setFont(Font.font("Calibri", FontWeight.NORMAL, 20));
grid.add(title, 0, 0, 2, 1);
}
public Scene okno() {
return scene;
}
}
and and a little another class that I'm trying to change with button from login.java
public class Messages {
private AnchorPane anchor;
private Scene scena;
//private Label nick;
private Button sendBtn;
private TextField poleDoWpisywania;
private TextArea poleDoWyswietlania, pobierzNick;
public Messages() {
anchor = new AnchorPane();
scena = new Scene(anchor, 700, 600);
pobierzNick();
poleDoWpisywania();
poleDoWyswietlania();
utworzPrzycisk();
}
private void utworzPrzycisk() {
sendBtn = new Button("Wy\u015Blij");
sendBtn.setDisable(true);
}
private void pobierzNick(){
pobierzNick = new TextArea();
pobierzNick.setEditable(false);
pobierzNick.setWrapText(true);
}
private void poleDoWpisywania() {
poleDoWpisywania = new TextField();
}
private void poleDoWyswietlania() {
poleDoWyswietlania = new TextArea();
poleDoWyswietlania.setEditable(false);
poleDoWyswietlania.setWrapText(true);
}
public Pane messa() {
return anchor;
}
}
could I ask you to show the right way to fix the bug?
JavaFX defines a scene graph which is a tree data structure that has a single root node. For your application (i.e. the code you posted), the root node is the primaryStage (this is the parameter in method start() in class Main). The primaryStage can have several Scenes. Each Scene must have its own root node.
The error message you are getting means that a Scene's root cannot also be the root of another Scene. In other words anchor is the root for scena in class Messages which means it can't be set as the root for scene in class Login.
Apart from that, if you want to change Scene's you need to call method setScene() of class Stage. Here is your Login class and Messages class with changes that solve the run-time error you are getting and perform the scene change when the user clicks on wejdzBtn button.
Login.java
(I only changed the lambda expression in method utworzBtn().)
public class Login {
private GridPane grid;
private Scene scene;
private Text title;
private Label nick;
private Button wejdzBtn;
private TextField userName;
public Login() {
grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25,25,25,25));
scene = new Scene(grid, 300, 150);
utworzBtn();
utworzLogin();
utworzTekst();
utworzNick();
}
private void utworzBtn() {
wejdzBtn = new Button("Zaloguj si\u0119");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment (Pos.BOTTOM_RIGHT);
hbBtn.getChildren().add(wejdzBtn);
grid.add(hbBtn, 1, 2);
wejdzBtn.setOnAction(e -> {
Messages mess = new Messages();
Window w = scene.getWindow();
if (w instanceof Stage) {
Stage s = (Stage) w;
s.setScene(mess.getScena());
}
});
}
private void utworzLogin() {
nick = new Label("Nick:");
grid.add(nick, 0, 1);
}
private void utworzNick() {
userName = new TextField();
grid.add(userName,1,1);
userName.setPromptText("Max 15 znak\u00f3w");
userName.setFocusTraversable(false);
final int maxLength = 15;
userName.setOnKeyTyped(t -> {
if (userName.getText().length() > maxLength)
{
int pos = userName.getCaretPosition();
userName.setText(userName.getText(0, maxLength));
userName.positionCaret(pos);
}
});
}
private void utworzTekst() {
title = new Text ("Dzień dobry!");
title.setFont(Font.font("Calibri", FontWeight.NORMAL, 20));
grid.add(title, 0, 0, 2, 1);
}
public Scene okno() {
return scene;
}
}
Messages.java
(I added method getScena().)
public class Messages {
private AnchorPane anchor;
private Scene scena;
private Button sendBtn;
private TextField poleDoWpisywania;
private TextArea poleDoWyswietlania, pobierzNick;
public Messages() {
anchor = new AnchorPane();
scena = new Scene(anchor, 700, 600);
pobierzNick();
poleDoWpisywania();
poleDoWyswietlania();
utworzPrzycisk();
}
private void utworzPrzycisk() {
sendBtn = new Button("Wy\u015Blij");
sendBtn.setDisable(true);
}
private void pobierzNick() {
pobierzNick = new TextArea();
pobierzNick.setEditable(false);
pobierzNick.setWrapText(true);
}
private void poleDoWpisywania() {
poleDoWpisywania = new TextField();
}
private void poleDoWyswietlania() {
poleDoWyswietlania = new TextArea();
poleDoWyswietlania.setEditable(false);
poleDoWyswietlania.setWrapText(true);
}
public Scene getScena() {
return scena;
}
public Pane messa() {
return anchor;
}
}
Thanks a lot Abra, I've been thinking about it for the last 6 hours and haven't noticed this problem. I have also removed
public Pane messa ();
return anchor;
I do not need it ;)

Access to Javafx elements from another class without FXML

I am using Javafx8 and lets say i have created many ui items (like buttons and so on...). I thought its a good idea to put all EventHandler for these buttons into a separates class. My question is: How can i get access from an EventHandler to any button, e.g. to diable it or to manipulate it on any other way.
Here is a minimum example with two buttons and a separate class for EventHandlers
Let's say this is my Start class:
public class App extends Application
{
#Override
public void start(Stage primaryStage) throws Exception {
Button b1 = new Button();
b1.setOnAction(ListenerClass.createB1Event());
Button b2 = new Button();
b2.setOnAction(ListenerClass.createB2Event());
VBox vbox = new VBox(b1, b2);
Scene scene = new Scene(vbox, 200, 200);
primaryStage.setTitle("App");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main( String[] args )
{
Application.launch(args);
}
}
And my (separate) listener class:
public class ListenerClass {
public static EventHandler<ActionEvent> createB1Event() {
return new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
//Access here to b1 and b2...
//Deactivate B1
//Activate B2
}
};
}
public static EventHandler<ActionEvent> createB2Event() {
return new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
//Access here to b1 and b2...
//Activate B1
//Dectivate B2
}
};
}
}
Thanks
So based on your principal. You want the button that is located in a vbox and got pressed to be disabled and all other buttons from that vbox to be enabled.
And your problem is how to find button that got pressed.
You need to use ActionEvent.getSource() method.
Heres how I'd code it....
This is Start class:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
VBox vbox = new VBox();
Button b1 = addNewButton("Button1",vbox);
Button b2 = addNewButton("Button2",vbox);
Button b3 = addNewButton("Button3",vbox);
Scene scene = new Scene(vbox, 200, 200);
primaryStage.setTitle("App");
primaryStage.setScene(scene);
primaryStage.show();
}
public static Button addNewButton(String label, VBox ownerVBox){
Button button = new Button(label);
ownerVBox.getChildren().add(button);
button.setOnAction(ListenerClass.createBEvent());
return button;
}
public static void main(String[] args) {
launch(args);
}
}
Listener class:
public class ListenerClass {
public static EventHandler<ActionEvent> createBEvent() {
return new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
Button b = (Button) t.getSource();
VBox vbox =(VBox) b.getParent();
vbox.getChildren().forEach(button-> {
button.setDisable(false);
});
b.setDisable(true);
}
};
}
}

How to change the style of a normal pane (Not layout pane)

well i am new to JavaFx and i haven't been using java for a really long time, so i am having many problems. And the biggest is how to change the bg of the damn pane.
Below is the Controller class
//Styling prePane
public class Controller {
//Declaring elements
public Pane prePane;
public Button generate;
public TextArea info;
#FXML
ProgressBar progressBar;
public void onGenerate() throws IOException {
//Styling prePane
prePane=new Pane();
prePane.getStyleClass().add("prePane");
//Creating and embedding progressBar
generate.setDisable(true);
progressBar.setProgress(0);
//Creating task object
Task copyWorker = createWorker();
progressBar.progressProperty().unbind();
progressBar.progressProperty().bind(copyWorker.progressProperty());
copyWorker.messageProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
System.out.println(newValue);
}
});
//Starting task thread
new Thread(copyWorker).start();
//QR Code generation
String details;
info.getParagraphs();
details=String.valueOf(info.getText());
ByteArrayOutputStream out= net.glxn.qrgen.QRCode.from(details).to(ImageType.GIF).stream();
File file=new File("D:\\JavaFXQRGenerator-master\\QrGenerator\\QrCode\\details.jpg");
FileOutputStream fos=new FileOutputStream(file);
fos.write(out.toByteArray());
fos.flush();
}
//Defining the task
public Task createWorker() {
return new Task() {
#Override
protected Object call() throws Exception {
for (int i = 0; i < 10; i++) {
updateProgress(i + 1, 10);
}
return true;
}
};
}
}
Main Class
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("QR Generator");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Below the actual Style sheet
.prePane{
-fx-background-image: url("D:\JavaFXQRGenerator-master\QrGenerator\resources\genPane.jpg");
}
Any kind of help is appreciated.
Assuming the resources folder is part of your build path, genPane.jpg will be in the root of the classpath. So the correct path, according to the CSS documentation is just
.prePane{
-fx-background-image: url("/genPane.jpg");
}

JavaFX: Disable all components while a process is running and show progress indicator

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;
}

JavaFX custom component get action from button in custom component

I want to get the event of two buttons in my custom component.
the component is a imageview with two buttons to move between images, but I need to get the position of the image that is currently displayed, Im storing the key of the image, but I need to know when a button have been pressed outside the custom component, so I can change a Label outside the custom component.
public class TransitionSlider extends AnchorPane {
#FXML
private AnchorPane transitionSliderPane;
#FXML
private ImageView transitionSliderImageView;
#FXML
private Button prevButton;
#FXML
private Button nextButton;
private Map<Integer,Image> imageMap;
private Image currentImage;
private DropShadow imageViewDropShadow;
private int currentKey = 1;
private Image[] images;
public TransitionSlider() {
FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setController(this);
loader.setLocation(this.getClass().getResource("TransitionSlider.fxml"));
loader.setClassLoader(this.getClass().getClassLoader());
try {
loader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
prevButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= 1){
currentKey = currentKey + 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
nextButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= imageMap.size()){
currentKey = currentKey - 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
}
// more code here...
}
I want a way to capture the event and get variables inside the component and change a label outside the custom component...
for example:
public class Gallery extends Application {
#FXML
TransitionSlider ts;
Label label;
#Override
public void start(Stage stage) throws Exception {
label = new Label();
TransitionSlider ts = new TransitionSlider();
ts.captureButtonEvent(){ // need a way to capture this
label.setText(ts.getCurrentKey());
}
// more code here....
}
If I understood your question correctly, you want a binding.. Follow these steps:
1) Put bindable field and its getter/setter into TransitionSlider:
private IntegerProperty currentKey = new SimpleIntegerProperty(1);
public int getCurrentKey() {
return currentKey.get();
}
public void setCurrentKey(int val) {
return currentKey.set(val);
}
public IntegerProperty currentKeyProperty() {
return currentKey;
}
2) Bind this property to label's text in Gallery:
label = new Label();
TransitionSlider ts = new TransitionSlider();
label.textProperty.bind(ts.currentKeyProperty().asString());
Alternatively, if you want to do stuff more than just changing label's text, you can add a change listener to currentKeyProperty:
ts.currentKeyProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
label.setText(newValue);
// do other stuff according to "oldValue" and "newValue".
}
});

Categories