As the title suggests, I'm trying to make my label object (incomingmessage) change dynamically by feeding a string value from a separate class. This separate class is later going to be a UDP client running on another class. I have been able to get another label to change based on the text from a text field, but the incoming message is being fed a string from a scanner, and then settext() is called. My only issue is that incoming message just won't update. The strings are passing into the controller class no problem. Code:
My main class:
package application;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
public class Main extends Application
{
#FXML
Image icon = new Image("UDPChatIcon.png");
static Messages msg = new Messages();
public static void main(String[] args)
{
msg.start();
launch(args);
}
#Override
public void start(Stage arg0) throws Exception
{
Stage stage = new Stage();
Parent root = FXMLLoader.load(getClass().getResource("UDPChatRoom.fxml"));
Scene scene = new Scene(root, 600, 800, Color.BLACK);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
stage.setResizable(false);
stage.getIcons().add(icon);
stage.setTitle("UPD Chatroom");
stage.setScene(scene);
stage.show();
}
}
My controller class:
package application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
public class controller
{
#FXML AnchorPane aPane;
#FXML GridPane gPane;
#FXML Button btn;
#FXML Label message;
#FXML static Label incomingmessage = new Label();
#FXML static String test = "test";
#FXML Messages msg = new Messages();
#FXML TextField inputbox;
#FXML
public void sendpressed(ActionEvent e)
{
updatemessage();
}
#FXML
public static void receive(String msg)
{
System.out.println("Incoming Message #controller: " + msg);
incomingmessage.setText(msg);
System.out.println("changed");
}
#FXML
public void updatemessage()
{
message.setText(inputbox.getText());
}
}
This class is what I am using to send strings to the controller. This is only used to change the variable incomingmessage. Again, these strings will later come from a UDP client, but I am just focusing on the GUI for now.
package application;
import java.util.Scanner;
public class Messages implements Runnable
{
private static String input = "default";
public void start()
{
Thread thread = new Thread(new Messages());
thread.start();
}
public void run()
{
try {
// Displaying the thread that is running
while(true)
{
Scanner in = new Scanner(System.in);
System.out.print("Enter Text: ");
input = in.nextLine();
System.out.println("inputted Message #Messages: " + input);
send(input);
}
}
catch (Exception e) {
// Throwing an exception
System.out.println("Exception is caught: " + e.getMessage());
}
}
public void send(String message)
{
System.out.println("sending #Messages: " + message);
controller.receive(message);
}
public String getInput()
{
return input;
}
}
and my FXML file
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.Cursor?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<AnchorPane fx:id="aPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller">
<cursor>
<Cursor fx:constant="DEFAULT" />
</cursor>
<children>
<Button fx:id="btn" layoutX="507.0" layoutY="761.0" mnemonicParsing="false" onAction="#sendpressed" prefHeight="25.0" prefWidth="79.0" text="Send" AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="501.0" AnchorPane.rightAnchor="20.0" AnchorPane.topAnchor="755.0" />
<TextField id="input" fx:id="inputbox" layoutX="11.0" layoutY="761.0" prefHeight="25.0" prefWidth="478.0" promptText="Enter Message Here..." AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="20.0" AnchorPane.rightAnchor="102.0" AnchorPane.topAnchor="755.0" />
<GridPane fx:id="gPane" prefHeight="742.0" prefWidth="550.0" style="-fx-background-color: BLACK; -fx-background-radius: 20; -fx-border-color: darkgrey; -fx-border-width: 10; -fx-border-radius: 10;" AnchorPane.bottomAnchor="65.0" AnchorPane.leftAnchor="20.0" AnchorPane.rightAnchor="20.0" AnchorPane.topAnchor="20.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="243.0" minHeight="0.0" prefHeight="0.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="741.0" minHeight="10.0" prefHeight="741.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="26.0" minHeight="0.0" prefHeight="0.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label id="incomingmessage" fx:id="incomingmessage" alignment="TOP_LEFT" contentDisplay="CENTER" prefHeight="748.0" prefWidth="280.0" style="-fx-text-fill: Red; -fx-border-color: grey; -fx-border-radius: 5;" wrapText="true" GridPane.rowIndex="1">
<padding>
<Insets left="10.0" />
</padding></Label>
<Label id="message" fx:id="message" alignment="TOP_LEFT" contentDisplay="CENTER" prefHeight="783.0" prefWidth="280.0" style="-fx-text-fill: lightblue; -fx-border-width: 2; -fx-border-color: grey; -fx-border-radius: 5;" wrapText="true" GridPane.columnIndex="1" GridPane.rowIndex="1">
<padding>
<Insets left="10.0" right="10.0" />
</padding></Label>
</children>
</GridPane>
</children>
</AnchorPane>
I thought I would give this a try. From the tests I ran, it appears Scanner blocks when calling any of its methods. I tried BufferedReader and it initially displayed the same behavior. I then used BufferedReader's ready method, and it appears to work.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import javafx.application.Application;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author Sedrick(sedj601)
*/
public class App extends Application{
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
TextArea textArea = new TextArea();
ScheduledService<String> scheduledService = new ScheduledService<String>() {
#Override
protected Task<String> createTask() {
return getScannerLine();
}
};
scheduledService.setOnSucceeded((t) -> {
String output = scheduledService.getValue();
if(output.length() > 0)
{
textArea.appendText(output + System.lineSeparator());
}
});
scheduledService.setPeriod(Duration.seconds(1));
HBox root = new HBox(textArea);
stage.setScene(new Scene(root, 700, 500));
stage.show();
scheduledService.start();
}
final BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
public Task<String> getScannerLine()
{
Task<String> task = new Task() {
#Override
protected Object call() throws Exception {
if(br.ready())
{
return br.readLine();
}
return "";
}
};
return task;
}
}
Related
I'm really struggling to understand JavaFX controllers, my aim is to write to a TextArea to act as a log.
My code is below, but I want to be able to change values ETC from another class that I can call when needed. I have tried to create a controller class that extents Initializable but i cant get it to work. Could some one steer me in the correct direction?
I want to move the #FXML code at the bottom to another class and it update the Scene.
package application;
import javafx.event.ActionEvent;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("Root.fxml"));
Scene scene = new Scene(root,504,325);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
public Thread thread = new Thread(new webimporter());
#FXML
public Label runningLabel;
#FXML
public TextArea txtArea;
#FXML
void runClick(ActionEvent event) throws IOException{
changeLabelValue("Importer running...");
thread.start();
}
#FXML
protected void stopClick(ActionEvent event){
changeLabelValue("Importer stopped...");
thread.interrupt();
}
#FXML
void changeLabelValue(String newText){
runningLabel.setText(newText);
}
void changeTextAreaValue(String newText1){
txtArea.setText(newText1);
}
}
Don't make the Application class a controller. It's a sin. There are other questions and answers which address this, but my search skills cannot find them at this time.
The reason it is a sin is:
You are only supposed to have one Application instance, and, by default, the loader will make a new instance, so you end up with two application objects.
Referencing the member objects is confusing, because the original launched application doesn't have the #FXML injected fields, but the loader created application instance does have #FXML inject fields.
Also, unrelated advice: Don't start trying to write multi-threaded code until you have the application at least working to the extent where it displays your UI.
A multi-threaded logger for JavaFX is in the answer to Most efficient way to log messages to JavaFX TextArea via threads with simple custom logging frameworks, though unfortunately it is not straight-forward in its implementation and comes with little documentation.
textlogger/Root.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="400.0" spacing="10.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="textlogger.ImportController">
<children>
<HBox alignment="BASELINE_LEFT" minHeight="-Infinity" minWidth="-Infinity" spacing="10.0">
<children>
<Button mnemonicParsing="false" onAction="#run" text="Run" />
<Button mnemonicParsing="false" onAction="#stop" text="Stop" />
<Label fx:id="runningLabel" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</HBox>
<TextArea fx:id="textArea" editable="false" prefHeight="200.0" prefWidth="200.0" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
textlogger.ImportController.java
package textlogger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
public class ImportController {
#FXML
private Label runningLabel;
#FXML
private TextArea textArea;
private WebImporter importer;
#FXML
void run(ActionEvent event) throws IOException {
changeLabelValue("Importer running...");
if (importer == null) {
importer = new WebImporter(textArea);
Thread thread = new Thread(
importer
);
thread.setDaemon(true);
thread.start();
}
}
#FXML
void stop(ActionEvent event){
changeLabelValue("Importer stopped...");
if (importer != null) {
importer.cancel();
importer = null;
}
}
private void changeLabelValue(String newText){
runningLabel.setText(newText);
}
}
textlogger.WebImporter.java
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.control.TextArea;
import java.time.LocalTime;
public class WebImporter extends Task<Void> {
private final TextArea textArea;
public WebImporter(TextArea textArea) {
this.textArea = textArea;
}
#Override
protected Void call() throws Exception {
try {
while (!isCancelled()) {
Thread.sleep(500);
Platform.runLater(
() -> textArea.setText(
textArea.getText() + LocalTime.now() + "\n"
)
);
}
} catch (InterruptedException e) {
Thread.interrupted();
}
return null;
}
}
textlogger.TextLoggingSample.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TextLoggingSample extends Application {
#Override
public void start(Stage stage) {
try {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(
getClass().getResourceAsStream(
"Root.fxml"
)
);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
I am just trying to build a simple Gui with SceneBuilder and JavaFx however I can't figure out why I can't populate my TableView, it just stays empty even after inserting simple Testobjects. Here is the main Class and the Goal class.
package application;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ResourceBundle;
import Objects.Goal;
import UtilityClasses.GoalManager;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
public class Main extends Application implements Initializable {
private Stage primaryStage;
private AnchorPane mainLayout;
public static ObservableList<Goal> goalsData = FXCollections.observableArrayList();
#FXML
static TableView<Goal> goalTable = new TableView<Goal>();
#FXML
static TableColumn<Goal, String> goalsColumn = new TableColumn<>();
#FXML
static TableColumn<Goal, String> statusColumn = new TableColumn<>();
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("MainWindow");
try {
showMainView();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
public static void main(String[] args) {
try {
setDefaultSettings();
launch(args);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void showMainView() throws IOException {
SettingControlls mainController = new SettingControlls();
FXMLLoader loader = new FXMLLoader();
loader.setController(mainController);
loader.setLocation(Main.class.getResource("mainFXML.fxml"));
mainLayout = loader.load();
Scene scene = new Scene(mainLayout);
primaryStage.setScene(scene);
primaryStage.show();
}
private static void setDefaultSettings() throws IOException {
File f = new File("Goals.txt");
if(!f.exists()) {
OutputStream os = new FileOutputStream("Goals.txt");
Writer w = new OutputStreamWriter(os);
w.close();
}
}
private static ObservableList<Goal> getObservableList() throws FileNotFoundException, IOException {
ObservableList<Goal> ol = FXCollections.observableArrayList(new Goal("testGoal"));
return ol;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
goalsData.add(new Goal("test"));
System.out.println(goalsData.get(0).getGoal());
goalsColumn.setCellValueFactory(new PropertyValueFactory<Goal, String>("goal"));
statusColumn.setCellValueFactory(new PropertyValueFactory<Goal, String>("status"));
goalTable.setItems(goalsData);
}
}
Here is the Goal class:
package Objects;
import javafx.beans.property.SimpleStringProperty;
public class Goal {
private SimpleStringProperty goal;
private SimpleStringProperty status;
public Goal(String goal) {
this.goal = new SimpleStringProperty(goal);
this.status = new SimpleStringProperty("ongoing");
}
public Goal(String goal, String status) {
this.goal = new SimpleStringProperty(goal);
this.status = new SimpleStringProperty(status);
}
public String getGoal() {
return this.goal.get();
}
public String getStatus() {
return this.status.get();
}
public void setGoal(String newGoal) {
this.goal = new SimpleStringProperty(newGoal);
}
public void setStatus(String newStatus) {
this.status = new SimpleStringProperty(newStatus);
}
}
Because the TableView and the columns get declared in my fxml file it seemed weird to me to generate them with new however if I don't do that I get an Nullpointerexception.
Edit: Added the fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane prefHeight="406.0" prefWidth="721.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children>
<MenuBar layoutX="-7.0" layoutY="14.0" prefHeight="25.0" prefWidth="733.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<menus>
<Menu mnemonicParsing="false" onAction="#GoalsClicked" style="-fx-font-size: 23;" text="Goals" />
<Menu disable="true" mnemonicParsing="false" style="-fx-font-size: 23;" text="Matchups" />
</menus>
</MenuBar>
<Text layoutX="14.0" layoutY="87.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Your current Goals:">
<font>
<Font size="24.0" />
</font>
</Text>
<TableView fx:id="goalTable" layoutY="103.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0">
<columns>
<TableColumn fx:id="goalsColumn" maxWidth="1.7976931348623157E308" minWidth="0.0" prefWidth="625.0" text="Goals" />
<TableColumn fx:id="statusColumn" maxWidth="1.7976931348623157E308" minWidth="0.0" prefWidth="72.0" text="Status" />
</columns>
<contextMenu>
<ContextMenu>
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
<MenuItem mnemonicParsing="false" text="Set status" />
</items>
</ContextMenu>
</contextMenu>
</TableView>
<Button layoutX="619.0" layoutY="315.0" mnemonicParsing="false" onAction="#addGoal" text="+" />
<Button layoutX="655.0" layoutY="315.0" mnemonicParsing="false" onAction="#removeGoal" text="-" />
</children>
</AnchorPane>
You have many problems there :
It is not recommended to use your Main class as a Controller class.
Your Nodes used in .fxml are never static since they belong to the instance not to the class so remove them.
Don't instantiate the Nodes defined in the .fxml file. There is nothing to do with new. The FXMLLoader does the work for you.
So rewrite the following part:
#FXML
static TableView<Goal> goalTable = new TableView<Goal>();
#FXML
static TableColumn<Goal, String> goalsColumn = new TableColumn<>();
#FXML
static TableColumn<Goal, String> statusColumn = new TableColumn<>();
I would also suggest splitting the Main class into a Main and a Controller class.
In Main you should just load the file, and in the Controller do the UI related stuff.
You can split the following way:
Main:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("View.fxml"));
AnchorPane pane = loader.load();
primaryStage.setScene(new Scene(pane, 400, 400));
primaryStage.show();
}
}
Controller:
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
and the .fxml
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="stackoverflow.dummy.Controller">
</AnchorPane>
Then you can complete these classes respecting those two rules I mentioned at 2. and 3.
I'm really struggling to understand JavaFX controllers, my aim is to write to a TextArea to act as a log.
My code is below, but I want to be able to change values ETC from another class that I can call when needed. I have tried to create a controller class that extents Initializable but i cant get it to work. Could some one steer me in the correct direction?
I want to move the #FXML code at the bottom to another class and it update the Scene.
package application;
import javafx.event.ActionEvent;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("Root.fxml"));
Scene scene = new Scene(root,504,325);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
public Thread thread = new Thread(new webimporter());
#FXML
public Label runningLabel;
#FXML
public TextArea txtArea;
#FXML
void runClick(ActionEvent event) throws IOException{
changeLabelValue("Importer running...");
thread.start();
}
#FXML
protected void stopClick(ActionEvent event){
changeLabelValue("Importer stopped...");
thread.interrupt();
}
#FXML
void changeLabelValue(String newText){
runningLabel.setText(newText);
}
void changeTextAreaValue(String newText1){
txtArea.setText(newText1);
}
}
Don't make the Application class a controller. It's a sin. There are other questions and answers which address this, but my search skills cannot find them at this time.
The reason it is a sin is:
You are only supposed to have one Application instance, and, by default, the loader will make a new instance, so you end up with two application objects.
Referencing the member objects is confusing, because the original launched application doesn't have the #FXML injected fields, but the loader created application instance does have #FXML inject fields.
Also, unrelated advice: Don't start trying to write multi-threaded code until you have the application at least working to the extent where it displays your UI.
A multi-threaded logger for JavaFX is in the answer to Most efficient way to log messages to JavaFX TextArea via threads with simple custom logging frameworks, though unfortunately it is not straight-forward in its implementation and comes with little documentation.
textlogger/Root.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="400.0" spacing="10.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="textlogger.ImportController">
<children>
<HBox alignment="BASELINE_LEFT" minHeight="-Infinity" minWidth="-Infinity" spacing="10.0">
<children>
<Button mnemonicParsing="false" onAction="#run" text="Run" />
<Button mnemonicParsing="false" onAction="#stop" text="Stop" />
<Label fx:id="runningLabel" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</HBox>
<TextArea fx:id="textArea" editable="false" prefHeight="200.0" prefWidth="200.0" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
textlogger.ImportController.java
package textlogger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
public class ImportController {
#FXML
private Label runningLabel;
#FXML
private TextArea textArea;
private WebImporter importer;
#FXML
void run(ActionEvent event) throws IOException {
changeLabelValue("Importer running...");
if (importer == null) {
importer = new WebImporter(textArea);
Thread thread = new Thread(
importer
);
thread.setDaemon(true);
thread.start();
}
}
#FXML
void stop(ActionEvent event){
changeLabelValue("Importer stopped...");
if (importer != null) {
importer.cancel();
importer = null;
}
}
private void changeLabelValue(String newText){
runningLabel.setText(newText);
}
}
textlogger.WebImporter.java
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.control.TextArea;
import java.time.LocalTime;
public class WebImporter extends Task<Void> {
private final TextArea textArea;
public WebImporter(TextArea textArea) {
this.textArea = textArea;
}
#Override
protected Void call() throws Exception {
try {
while (!isCancelled()) {
Thread.sleep(500);
Platform.runLater(
() -> textArea.setText(
textArea.getText() + LocalTime.now() + "\n"
)
);
}
} catch (InterruptedException e) {
Thread.interrupted();
}
return null;
}
}
textlogger.TextLoggingSample.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TextLoggingSample extends Application {
#Override
public void start(Stage stage) {
try {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(
getClass().getResourceAsStream(
"Root.fxml"
)
);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
This question already has answers here:
Javafx - Can application class be the controller class
(2 answers)
Closed 7 years ago.
How can I update the text of the label currentPlayerFileLabel from the if statement, so it gets the path from the file (if it is there); otherwise gets the default String?
Code as of now:
package sample;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import sample.controllers.Util;
import sample.model.Config;
import java.io.File;
import java.net.URL;
import java.security.spec.ECField;
import java.util.ResourceBundle;
public class Main extends Application implements Initializable {
private Stage primaryStage = new Stage();
private BorderPane rootLayout;
private AnchorPane anchorPanePlayer;
private BorderPane borderPaneGame;
private Config config = new Config();
private StringProperty isPlayerFileThere = new SimpleStringProperty("No playerfile was fund! Add new one in \"File\"");
#FXML
private Button playersButton;
#FXML
private Button gamesButton;
#FXML
private Button quitButton;
#FXML
private Label currentPlayerFileLabel = new Label();
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Main window");
initLayout();
initConfig();
}
//----------- MY QUESTION IS ABOUT THIS METHODE -------------------------------
public void initConfig() {
File configFile = new File(System.getProperty("user.dir") + "/Config/config.txt");
if (configFile.exists()) { // Returns true as of now, so the "true" statement of the if statement will be called
config = Util.initConfigFile();
isPlayerFileThere.setValue(config.getPlayerFileLocation().toString());
currentPlayerFileLabel.setText(getIsPlayerFileThere());
} else {
currentPlayerFileLabel.setText(getIsPlayerFileThere());
}
}
//----------- MY QUESTION IS ABOUT THIS METHODE -------------------------------
public void initLayout() {
try {
//Load root layout from fxml
FXMLLoader loader = new FXMLLoader(); //Makes a new FXMLLoader
loader.setLocation(Main.class.getResource("view/mainView.fxml")); //sets the location of the main fxml file
rootLayout = loader.load(); //Loads the anchorpane from the loader, (AnchorPane) is redundent.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.getStackTrace();
}
}
public void initPlayerLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/playerEdit.fxml")); // Gets the new layout.
anchorPanePlayer = loader.load(); //Loades the new layout
Scene playerScene = new Scene(anchorPanePlayer); //adds a new scene with the loaded layout
Stage prevStage = (Stage) playersButton.getScene().getWindow(); //Get the stage from where we come from.
prevStage.close(); //Closes the prev stage
primaryStage.setScene(playerScene); //Sets new stage with the new layout
primaryStage.show(); //Shows new stage
} catch (Exception e) {
e.printStackTrace();
}
}
public void initGameLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/editGame.fxml"));
borderPaneGame = loader.load();
Scene gameScene = new Scene(borderPaneGame);
Stage prevStage = (Stage) gamesButton.getScene().getWindow();
prevStage.close();
primaryStage.setScene(gameScene);
primaryStage.show();
} catch (Exception e) {
e.getStackTrace();
}
}
public void quitProgram() {
Stage stageToQuit = (Stage) quitButton.getScene().getWindow();
stageToQuit.close();
}
public Stage getPrimaryStage() {
return primaryStage;
}
public AnchorPane getBorderPanePlayer() {
return anchorPanePlayer;
}
public Config getConfig() {
return config;
}
public void addPlayerFile() {
config.setPlayerFileLocation(Util.addPlayerFile());
}
public String getIsPlayerFileThere() {
return isPlayerFileThere.get();
}
public StringProperty isPlayerFileThereProperty() {
return isPlayerFileThere;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
currentPlayerFileLabel.setText(getIsPlayerFileThere());
}
public static void main(String[] args) {
launch(args);
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Main">
<bottom>
<HBox prefHeight="100.0" prefWidth="200.0" spacing="40.0" BorderPane.alignment="CENTER">
<children>
<Region HBox.hgrow="ALWAYS" />
<Button fx:id="playersButton" mnemonicParsing="false" onAction="#initPlayerLayout" text="Players" />
<Button fx:id="gamesButton" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#initGameLayout" text="Games" />
<Button fx:id="quitButton" layoutX="69.0" layoutY="10.0" mnemonicParsing="false" onAction="#quitProgram" text="Quit" />
<Region layoutX="10.0" layoutY="10.0" HBox.hgrow="ALWAYS" />
</children>
</HBox>
</bottom>
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem fx:id="addPlayerFileMenu" mnemonicParsing="false" onAction="#addPlayerFile" text="Add new player file" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
</top>
<center>
<HBox prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER_RIGHT">
<children>
<VBox alignment="CENTER" HBox.hgrow="ALWAYS">
<children>
<Label text="Welcom to the main menu!" />
<Label fx:id="currentPlayerFileLabel" text="Label" />
</children>
</VBox>
</children>
</HBox>
</center>
</BorderPane>
UPDATE Have now worked out how to do it, with the help from the comments on this question. To others with the same problem, here is the code that I got to work:
Main class:
package sample;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import sample.controllers.MainController;
import sample.controllers.Util;
import sample.model.Config;
import java.io.File;
import java.net.URL;
import java.security.spec.ECField;
import java.util.ResourceBundle;
public class Main extends Application {
MainController mainController = new MainController();
Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("view/mainView.fxml"));
loader.setController(mainController);
Parent root = loader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
The controller for the layout of the main window and the part that is interesting:
...
private Stage primaryStage = new Stage();
private AnchorPane anchorPanePlayer;
private BorderPane borderPaneGame;
private Config config = new Config();
private StringProperty isPlayerFileThere = new SimpleStringProperty("No playerfile was fund! Add new one in \"File\"");
#FXML
private Button playersButton;
#FXML
private Button gamesButton;
#FXML
private Button quitButton;
#FXML
private Label currentPlayerFileLabel;
...
#Override
public void initialize(URL location, ResourceBundle resources) {
File configFile = new File(System.getProperty("user.dir") + "/Config/config.txt");
if (configFile.exists()) { // Returns true as of now, so the "true" statement of the if statement will be called
config = Util.initConfigFile();
isPlayerFileThere.setValue(config.getPlayerFileLocation().toString());
currentPlayerFileLabel.setText(getIsPlayerFileThere());
} else {
currentPlayerFileLabel.setText(getIsPlayerFileThere());
}
}
...
The FXML line that has been updated:
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" >
You are overriding the label, so you do no longer use the instance that is defined in your FXML:
#FXML
private Label currentPlayerFileLabel = new Label();
Remove the assignment to a new label.
I've made an app that works fine but I wan't to add a function and to do so I need to add some TextFields and Label when we change the value of a combobox.
Here is my main code :
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.Parent;
public class maincombo extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent parent = FXMLLoader.load(getClass().getResource("/combobox.fxml"));
Scene scene = new Scene(parent);
stage.setTitle("Application");
stage.setScene(scene);
stage.show();
}
}
My controller :
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
public class comboboxcontroller implements Initializable {
#FXML
private ComboBox<Integer>methode;
ObservableList<Integer>nombre = FXCollections.observableArrayList();
#FXML
private void metho (ActionEvent event){
switch (methode.getValue()) {
case 0 :
break;
case 1 :
break;
case 2 :
break;
case 3 :
break;
case 4 :
break;
case 5 :
break;
default :
break;
}
}
public comboboxcontroller (){
}
public void initialize(URL url,ResourceBundle rb){
nombre.add(new Integer(0));
nombre.add(new Integer(1));
nombre.add(new Integer(2));
nombre.add(new Integer(3));
nombre.add(new Integer(4));
nombre.add(new Integer(5));
methode.setItems(nombre);
}
}
My fxml :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="150.0" prefWidth="150.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="comboboxcontroller">
<children>
<GridPane layoutX="-180.0" layoutY="-205.0" prefHeight="0.0" prefWidth="20.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<ComboBox fx:id="methode" prefWidth="150.0" GridPane.rowIndex="1">
<GridPane.margin>
<Insets left="5.0" right="5.0" />
</GridPane.margin>
</ComboBox>
<Label text="MyComboBox" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
</children>
</GridPane>
</children>
</AnchorPane>
I tried to do something with a switch statement, but it didn't work. What I want is when the user chooses the value 4, then I want it to add 4 labels and Textfields, but if he changes his mind and instead chooses a value of 2 it should show 2 labels and textfields.
Edit : I tried to put all my 5 TextFields and Labels and try to hide them with this line :
MyTextField.setVisible(false);
But everything still visible. So I started to think that maybe it's easier to add them dynamically or maybe with a pop-up window. And that why I here looking for a help.
I did something similar to this a few days ago, check this out:
"Type"-Enum
public enum Type {
boObject("BO-Objekt", new Indicator("Test1"), new Indicator("Test2"), new Indicator("Test3")),
job("Job", new Indicator("Test1"), new Indicator("Test2")),
table("Tabelle", new Indicator("Test1")),
tableColumn("Tabellenspalte", new Indicator("Test1"), new Indicator("Test2"));
private String displayName;
private Indicator[] indics;
private Type(String displayName, Indicator ... indics) {
this.displayName = displayName;
this.indics = indics;
}
#Override public String toString() {
return displayName;
}
public Indicator[] getIndics() {
return indics;
}
public void setIndics(Indicator[] indics) {
this.indics = indics;
}
}
"Indicator"-class:
public class Indicator {
private String label;
public Indicator(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}
onAction-Method of Combobox target:
#FXML private ComboBox<Type> target;
#FXML private VBox targetBox;
#FXML public void onTargetSelected() {
targetBox.getChildren().clear();
for(Indicator indic : target.getValue().getIndics()) {
VBox box = new VBox();
Text t = new Text(indic.getLabel());
box.getChildren().add(t);
TextField txt = new TextField();
txt.setMaxWidth(target.getWidth());
box.getChildren().add(txt);
targetBox.getChildren().add(box);
}
Controller.stage.close();
Controller.stage.show();
}