Grimeire
Grimeire

Reputation: 349

Delete selected row from TableView, and the effective use of SceneBuilder in JavaFX

I'm trying to make an application that creates a table from text in a file and then the user can add or delete words. The text file is used else where in my app to populate dropdown boxes so the user can only choose the text in the dropdown boxes.

My problem is I can't delete from the table only add to it and anything I can find on the net are tables that are made from classes. My table is just a very basic one that has 1 column of strings. For some reason when I print the selected items of the table I always get nothing? I can't figure out why.

I only started javaFx a few weeks ago I really just learning from videos and books but they are very simple compared to the application I'm expected to make.

Here is my code:

public class MaintenWindow {

private static Stage window;
private static TableView<String> table = new TableView<String>();
private static TextField input;
private static ObservableList<String> stringList;
private static BorderPane bP;
private static Scene scene;

private static File types = new File("type.txt");
private static File location = new File("location.txt");
private static File status = new File("status.txt");

public static void display() throws FileNotFoundException  {
    window = new Stage();
    BorderPane bP = new BorderPane();

    window.setTitle("Mainten Window");

    MenuBar menuBar = new MenuBar();

    Menu fileMenu = new Menu("_Add/Remove From DropDowns");
    MenuItem editLocation = new MenuItem("_Edit Locations");
    MenuItem editAtypes = new MenuItem("_Edit Animal Types");
    MenuItem editStatus = new MenuItem("_Edit Status's");
    MenuItem exit = new MenuItem("Exit");
    fileMenu.getItems().addAll(editLocation, editAtypes, editStatus, new SeparatorMenuItem(),exit);

    menuBar.getMenus().addAll(fileMenu);
    editLocation.setOnAction(e -> {
        bP.setTop(menuBar);
        bP.setCenter(vBox("Locations", location));
        window.setScene(scene);
        window.show();              
    });

    editAtypes.setOnAction(e -> {
        bP.setTop(menuBar);
        bP.setCenter(vBox("Animal Types", types));
        window.setScene(scene);
        window.show();              
    });

    editStatus.setOnAction(e -> {
        bP.setTop(menuBar);
        bP.setCenter(vBox("Status", status));
        window.setScene(scene);
        window.show();              
    });


    bP.setTop(menuBar);
    scene = new Scene(bP, 800,800);

    Label label = new Label("welcome to the maintenance window");


    bP.setCenter(label);

    window.setScene(scene);
    window.show();

}

/*public static MenuBar menuBar()
{
    //file tab menu
    Menu fileMenu = new Menu("_Add/Remove From DropDowns");
    MenuItem editLocation = new MenuItem("_Edit Locations");
    MenuItem editAtypes = new MenuItem("_Edit Animal Types");
    MenuItem editStatus = new MenuItem("_Edit Status's");
    MenuItem exit = new MenuItem("Exit");
    fileMenu.getItems().addAll(editLocation, editAtypes, editStatus, new SeparatorMenuItem(),exit);

    editLocation.setOnAction(e -> {
        bP.setTop(menuBar());
        bP.setCenter(vBox("Locations", location));
        window.setScene(scene);
        window.show();              
    });

    editAtypes.setOnAction(e -> {
        bP.setTop(menuBar());
        bP.setCenter(vBox("Animal Types", types));
        window.setScene(scene);
        window.show();              
    });

    editStatus.setOnAction(e -> {
        bP.setTop(menuBar());
        bP.setCenter(vBox("Status", status));
        window.setScene(scene);
        window.show();              
    });

    MenuBar menuBar = new MenuBar();
    menuBar.getMenus().addAll(fileMenu);

    return menuBar;
}*/

public static ObservableList<String> getString(File file) throws FileNotFoundException{
    stringList = FXCollections.observableArrayList();

    Scanner kb = new Scanner(file);
    String item;
    while (kb.hasNext()) {
        item = kb.nextLine();
        stringList.add(item);
    }

    kb.close();
    return stringList;
}

@SuppressWarnings("unchecked")
public static TableView<String> table(String string,File file)
{
    TableColumn<String, String> tColumn = new TableColumn<String, String>(string);
    tColumn.setCellValueFactory(cellData -> 
    new ReadOnlyStringWrapper(cellData.getValue()));
    TableView<String> table = new TableView<String>();
    try {
        table.setItems(getString(file));
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    table.getColumns().addAll(tColumn);

    table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
    return table;
}

public static void addButtonClicked(){

    stringList.add(input.getText());
    input.clear();
}

//Delete button clicked
public static void deleteButtonClicked(){
    ObservableList<String> allStrings, stringSelected;
    allStrings = table.getItems();
    //printElements(allStrings);
    System.out.println(table.getSelectionModel().getSelectedItems());

    stringSelected = table.getSelectionModel().getSelectedItems();
    System.out.println(stringSelected);
    stringSelected.forEach(allStrings::remove);
}

public static HBox textFields() {
    input = new TextField();
    input.setPromptText("Input");
    input.setMinWidth(75);

    HBox hBox = new HBox();
    hBox.setPadding(new Insets(10,10,10,10));
    hBox.setSpacing(10);
    hBox.getChildren().addAll(input);

    return hBox;
}

public static HBox addRemove(File file){
    //Button
    Button addButton = new Button("Add");
    addButton.setOnAction(e -> addButtonClicked());
    Button deleteButton = new Button("Delete");
    deleteButton.setOnAction(e -> deleteButtonClicked());
    Button writeButton = new Button("Write to file");
    try {
        try {
            writeButton.setOnAction(e -> write(file));
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    } catch (Exception e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    Button closeButton = new Button("Close Window");
    closeButton.setOnAction(e -> window.close());

    HBox hBox2 = new HBox();
    hBox2.setPadding(new Insets(10,10,10,10));
    hBox2.setSpacing(10);
    hBox2.getChildren().addAll(addButton, deleteButton, writeButton, closeButton);

    return hBox2;
}

private static void printElements(ObservableList<String> list) {
    System.out.println("Size: " + list.size());
    for (Object o : list) {
        System.out.println(o.toString());
    }
    System.out.println("");
}

public static void write(File file)
{
    PrintWriter outFile = null;
    try {
        outFile = new PrintWriter(file);
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    for(int i = 0; i < stringList.size(); i++)
    {
        outFile.println(stringList.get(i));
    }
    outFile.close();
}

public static VBox vBox(String string, File file){
    Label label = new Label(string);
    label.setFont(new Font("Arial", 20));

    VBox vBox = new VBox();
    vBox.setSpacing(5);
    vBox.setPadding(new Insets(10, 0, 0, 10));
    vBox.getChildren().addAll(label, table(string,file), textFields(), addRemove(file));

    return vBox;
}

}

I would be greatful for any help. if u have any tips or advice for me please post If you have links to more advanced javafx programs than I'm finding please post them.

Upvotes: 2

Views: 10223

Answers (1)

jewelsea
jewelsea

Reputation: 159486

What's going wrong

You have a logic error in your code where you create two instances of your table. You declare in your class:

private static TableView<String> table = new TableView<String>();

Then later on in your table() function you declare a new table local to that function

TableView<String> table = new TableView<String>();
table.setItems(getString());
table.getColumns().addAll(tColumn);

return table;

Then you eventually add the table returned by your table() function to the scene:

vbox.getChildren().addAll(label, table(), textFields(), addRemove());

But elsewhere in your application, such as deleteButtonClicked(), you invoke:

table.getSelectionModel().getSelectedItems();

But that is going to be working off the instance declared in your class, not the instance declared in your method, so it will always return an empty list.

How to fix it

To fix this, only create a single new TableView, not two.

Here is some simplified sample code:

import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.text.Font;
import javafx.stage.Stage;

public class MaintenanceWindow extends Application {
    private TableView<String> table;
    private TextField input;
    private Stage myStage;

    private static final String[] ITEMS = { "apples", "oranges", "peaches", "pears" };

    @Override
    public void start(Stage stage) throws Exception {
        myStage = stage;
        final Label label = new Label("list");
        label.setFont(new Font("Arial", 20));

        VBox vbox = new VBox(
                5,
                label, table(), textFields(), addRemove()
        );
        vbox.setPadding(new Insets(10, 0, 0, 10));

        BorderPane bP = new BorderPane();
        bP.setTop(menuBar());
        bP.setCenter(vbox);

        stage.setScene(new Scene(bP, 700, 800));
        stage.show();
    }

    private MenuBar menuBar() {
        Menu fileMenu = new Menu("_Add/Remove From DropDowns");
        MenuItem locationAdd = new MenuItem("Location DropDowns Add");
        locationAdd.setOnAction(e -> addButtonClicked());
        MenuItem locationRemove = new MenuItem("Location DropDowns Remove");
        locationRemove.setOnAction(e -> deleteButtonClicked());
        MenuItem exit = new MenuItem("Exit");
        exit.setOnAction(e -> closeApp());
        fileMenu.getItems().addAll(
                locationAdd,
                new SeparatorMenuItem(),
                locationRemove,
                new SeparatorMenuItem(),
                exit
        );

        MenuBar menuBar = new MenuBar();
        menuBar.getMenus().addAll(fileMenu);
        return menuBar;
    }

    private TableView<String> table() {
        TableColumn<String, String> tColumn = new TableColumn<>("String");
        tColumn.setMinWidth(250);
        tColumn.setCellValueFactory(
                cellData -> new ReadOnlyStringWrapper(cellData.getValue())
        );
        table = new TableView<>();
        table.getItems().addAll(ITEMS);
        table.getColumns().add(tColumn);

        return table;
    }

    private void addButtonClicked() {
        if (!(input.getText() == null || "".equals(input.getText().trim())) {
            table.getItems().add(input.getText());
            input.clear();
        }
    }

    private void deleteButtonClicked() {
        table.getItems().removeAll(
                table.getSelectionModel().getSelectedItems()
        );
    }

    private HBox textFields() {
        input = new TextField();
        input.setPromptText("Input");
        input.setMinWidth(75);

        HBox hBox = new HBox();
        hBox.setPadding(new Insets(10, 10, 10, 10));
        hBox.setSpacing(10);
        hBox.getChildren().addAll(input);

        return hBox;
    }

    private HBox addRemove() {
        Button addButton = new Button("Add");
        addButton.setOnAction(e -> addButtonClicked());
        Button deleteButton = new Button("Delete");
        deleteButton.setOnAction(e -> deleteButtonClicked());
        Button closeButton = new Button("Close Window");
        closeButton.setOnAction(e -> closeApp());

        HBox hBox2 = new HBox(
                10,
                addButton, deleteButton, closeButton
        );
        hBox2.setPadding(new Insets(10, 10, 10, 10));
        hBox2.setSpacing(10);

        return hBox2;
    }

    private void closeApp() {
        myStage.close();
    }
}

image


Unrelated advice for future questions:

Those not looking for advice can skip reading the rest of this answer as it doesn't really apply directly to the question.

  • Create an mcve.
  • Follow the steps for debugging small programs. If you have a large program and can't work out what is going on, just create a small program.
  • As your program gets larger, either design it with multiple classes from the start or refactor it to use multiple classes.
  • Use FXML and SceneBuilder for complex layouts such as this rather than hand coding them.
  • Go through the makery JavaFX tutorial and study it very closely (it seems pretty similar to what you are trying to achieve here).
  • Just ask a single question per question on StackOverflow and isolate the code that addresses each single question to a specific mcve that only addresses that. e.g. for your original additional question on file exceptions (which it looks like somebody has now edited out of your question), just post that as a seperate question.
  • When you have a program that works (but is not too large), post its code to codereview for feedback on general programming techniques, conventions and design approaches (for example they will certainly give you feedback not use static methods everywhere). For an example of the kinds of things you should post to code review and the kind of feedback you can expect, see the coderview for An alarm application in Java(FX).
  • Stackoverflow isn't really a tips and advice site, it just pure Q&A.

Would u recommend scenebuilder for novice javafx programmers?

Yes. I used to not not recommend it for beginners, but have come around to thinking it is a good idea to use that tool for initial experimentation and later development, even for novice JavaFX programmers. Using the SceneBuilder tool rather than directly programming against the Java API, it is probably easier and more efficient for a new developer to get insight into how layouts work and what properties are exposed on nodes, regions and various controls.

However, learning SceneBuilder and FXML alone is no substitute for actually learning how to program against the Java API, for which you need to write code. So definitely do both things, play around with stuff in SceneBuilder and write code against the Java API. At first do those things separate, by exploring JavaFX controls and layouts only in scene builder and reviewing the FXML it generates and by writing small standalone programs that are coded just against the Java API, with no use of FXML. Then, only once you are relatively comfortable with both of those things, tackle a project like that in the Makery tutorial linked earlier, that combines Scene Builder for definition of FXML based UIs and usage of those FXML files in an intermediate sized application that also includes code against the Java API.

Also, neither playing with SceneBuilder nor playing around with the API, is a substitute for working through the JavaFX tutorials and understanding layout fundamentals such as Groups, layout panes and Regions, which must be done to get a reasonable grasp on the technology.

No JavaFX program can rely on FXML alone, if you use FXML, you need a combination of FXML and Java code to get a working program. Any significant JavaFX program that uses a lot of controls and layouts is best off being coded with FXML layout, CSS for the style and Java (or other language) code for the logic. So, eventually you need to learn all three technologies (FXML, CSS and JavaFX API) and how to make them function together in order to proceed beyond trivial programs.

One issue with SceneBuilder which I see new JavaFX developers encounter, is that is is relatively simple to create a complex UI in SceneBuilder, but it is relatively hard for a novice to take that complex UI and add correct logic to it to make the application actually functional as it should be. So, don't make the mistake of defining the complete UI for your application in SceneBuilder before you gain a decent understanding of both how to use the JavaFX Java API and how to add logic to some very basic FXML files using the JavaFX Java API.

Also once you start coding up multiple FXML files, a problems which everybody runs into is:

i choose not to use it as i thought it looked very complicated since u would need an xml and controller

Yeah, it is (relatively speaking), more complicated to understand and you have to have more skills to initially create an FXML/CSS based application than something written against a pure Java API. However, once you have built one, my guess is that you will find an intermediate sized FXML/CSS based application easier to understand and maintain than an equivalent application written purely against the Java API.

Upvotes: 1

Related Questions