Problem z tabelą w JavaFX

0

Witam
Chcę stworzyć "prosty" program do przechowywania danych na temat zrealizowanych transportów dla naszych klientów. Tabela z kolumnami typu "kierowca", "stawka" itd. Na dole pola tekstowe do wpisywania danych, po prawej stronie na dole guzik "dodaj", po jego naciśnięciu ma się stworzyć nowy obiekt typu Track i dodać do listy. Później chcę dodać coś w stylu jakiejś małej bazy danych, żeby po zamknięciu programu dane nie znikały, ale póki co nie miałem wcale z tym do czynienia więc skupiam się na tym, żeby działały podstawy.

Zrobiłem klasę Track:

package pl.sursped.schedule.model;

public class Track {
    private Integer number;
    private String data;
    private String driver;
    private String registerNumber;
    private String track;
    private String client;
    private Double bid;
    private Double profit;

    public Track(Integer number, String data, String driver, String registerNumber, String track, String client, double bid, double profit) {
        this.number = number;
        this.data = data;
        this.driver = driver;
        this.registerNumber = registerNumber;
        this.track = track;
        this.client = client;
        this.bid = bid;
        this.profit = profit;
    }

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getRegisterNumber() {
        return registerNumber;
    }

    public void setRegisterNumber(String registerNumber) {
        this.registerNumber = registerNumber;
    }

    public String getTrack() {
        return track;
    }

    public void setTrack(String track) {
        this.track = track;
    }

    public String getClient() {
        return client;
    }

    public void setClient(String client) {
        this.client = client;
    }

    public Double getBid() {
        return bid;
    }

    public void setBid(Double bid) {
        this.bid = bid;
    }

    public Double getProfit() {
        return profit;
    }

    public void setProfit(Double profit) {
        this.profit = profit;
    }

    @Override
    public String toString() {
        return "Trasa{" +
                "number=" + number +
                ", data='" + data + '\'' +
                ", driver='" + driver + '\'' +
                ", registerNumber='" + registerNumber + '\'' +
                ", track='" + track + '\'' +
                ", client='" + client + '\'' +
                ", bid=" + bid +
                ", profit=" + profit +
                '}';
    }
}

Oraz klasę ScheduleController, w której chciałem obsługiwać zdarzenia (na razie tylko obsługę zdarzenia kliknięcia na addButton:

package pl.sursped.schedule.controller;

import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import pl.sursped.schedule.model.Track;

public class ScheduleController {

        @FXML
        private MenuItem openFileMenuItem;

        @FXML
        private MenuItem saveMenuItem;

        @FXML
        private MenuItem deleteMenuItem;

        @FXML
        private MenuItem deleteAllMenuItem;

        @FXML
        private MenuItem aboutMenuItem;

        @FXML
        private TableViewController tableViewController;

        @FXML
        private TextField numberTextField;

        @FXML
        private TextField dataTextField;

        @FXML
        private TextField driverTextField;

        @FXML
        private TextField registerNumberTextField;

        @FXML
        private TextField trackTextField;

        @FXML
        private TextField clientTextField;

        @FXML
        private TextField bidTextField;

        @FXML
        private TextField profitTextField;

        @FXML
        private Button addButton;

        public void initialize() {
            clickOnAddButton();
        }

        private ObservableList<Track> tracksList;

        private void clickOnAddButton(){
            addButton.setOnAction(actionEvent -> addTrackToList());
        }

        public void addTrackToList() {
        ObservableList<Track> items = tableViewController.getTracksTableView().getItems();
        Track track = createNewTrack();
        items.add(track);
        }

        public Track createNewTrack(){
        Integer number = Integer.valueOf(numberTextField.getText());
        String data = dataTextField.getText();
        String driver = driverTextField.getText();
        String registerNumber = registerNumberTextField.getText();
        String track = trackTextField.getText();
        String client = clientTextField.getText();
        Double bid = Double.valueOf(bidTextField.getText());
        Double profit = Double.valueOf(profitTextField.getText());
        return new Track(number, data, driver, registerNumber, track, client, bid, profit);
        }

    }

Mam też klasę TableViewController, w której skonfigurowałem tabelę i podpiąłem wartości:


package pl.sursped.schedule.controller;

import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import pl.sursped.schedule.model.Track;

public class TableViewController {

        private static final String NUMBER_COLUMN = "Nr";
        private static final String DATA_COLUMN = "Data";
        private static final String DRIVER_COLUMN = "Kierowca";
        private static final String REGISTER_NUMBER_COLUMN = "Numer rejestracyjny";
        private static final String TRACK_COLUMN = "Trasa";
        private static final String CLIENT_COLUMN = "Klient";
        private static final String BID_COLUMN = "Stawka";
        private static final String PROFIT_COLUMN = "Dochód";

        @FXML
        private TableView<Track> tracksTableView;

        public TableView<Track> getTracksTableView() {
        return tracksTableView;
    }

        public void initialize(){
            configureTableColumns();
        }

        private void configureTableColumns() {
        TableColumn<Track, Integer> numberColumn = new TableColumn<>(NUMBER_COLUMN);
        numberColumn.setCellValueFactory(new PropertyValueFactory<>("number"));

        TableColumn<Track, String> dataColumn = new TableColumn<>(DATA_COLUMN);
        dataColumn.setCellValueFactory(new PropertyValueFactory<>("data"));

        TableColumn<Track, String> driverColumn = new TableColumn<>(DRIVER_COLUMN);
        driverColumn.setCellValueFactory(new PropertyValueFactory<>("driver"));

        TableColumn<Track, String> registerNumberColumn = new TableColumn<>(REGISTER_NUMBER_COLUMN);
        registerNumberColumn.setCellValueFactory(new PropertyValueFactory<>("registerNumber"));

        TableColumn<Track, String> trackColumn = new TableColumn<>(TRACK_COLUMN);
        trackColumn.setCellValueFactory(new PropertyValueFactory<>("track"));

        TableColumn<Track, String> clientColumn = new TableColumn<>(CLIENT_COLUMN);
        clientColumn.setCellValueFactory(new PropertyValueFactory<>("client"));

        TableColumn<Track, String> bidColumn = new TableColumn<>(BID_COLUMN);
        bidColumn.setCellValueFactory(new PropertyValueFactory<>("bid"));

        TableColumn<Track, String> profitColumn = new TableColumn<>(PROFIT_COLUMN);
        profitColumn.setCellValueFactory(new PropertyValueFactory<>("profit"));

        tracksTableView.getColumns().add(numberColumn);
        tracksTableView.getColumns().add(dataColumn);
        tracksTableView.getColumns().add(driverColumn);
        tracksTableView.getColumns().add(registerNumberColumn);
        tracksTableView.getColumns().add(trackColumn);
        tracksTableView.getColumns().add(clientColumn);
        tracksTableView.getColumns().add(bidColumn);
        tracksTableView.getColumns().add(profitColumn);
        }

    }

Tak wygląda moja klasa Main:


package pl.sursped.schedule.main;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }


    @Override
    public void start(Stage stage) throws Exception {
        Pane mainPane = FXMLLoader.load(getClass().getResource("/scheduleView.fxml"));
        Scene scene = new Scene(mainPane);
        stage.setScene(scene);
        stage.setTitle("Tabela kursów");
        stage.show();
    }
}

Po wpisaniu w pola tekstowe danych i naciśnięciu przycisku "dodaj" wyrzuca mi NullPointerException (obojętnie czy w polach tekstowych coś jest wpisane czy nie). Siedzę od wczoraj nad problemem i nie mam pojęcia jak to rozwiązać i w ogóle w czym leży problem, jestem początkujący i staram się dużo rozwiązań szukać w internecie, ale tutaj poległem.

0

W którym miejscu leci wyjątek?
Strzelam, że null jest gdzieś tutaj tableViewController.getTracksTableView().getItems();, a dokładniej to tableViewController.
Sprawdź to https://openjfx.io/javadoc/12/javafx.fxml/javafx/fxml/doc-files/introduction_to_fxml.html -> "Nested Controllers".

0
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
	at tabelakursow/pl.sursped.schedule.controller.ScheduleController.addTrackToList(ScheduleController.java:66)
	at tabelakursow/pl.sursped.schedule.controller.ScheduleController.lambda$clickOnAddButton$0(ScheduleController.java:62)

Wyrzuca nulla w tym miejscu

0

IMHO nie wstrzykniesz TableViewController przez @FXML bo to nie Spring

0

Już usunąłem adnotację @FXML przy TableViewController, nic to jednak nie zmienia
Jadę metoda po metodzie, linijka po linijce i mój mały umysł nie jest w stanie wyłapać w czym dokładnie leży problem...

Z tego co się domyślam to faktycznie problem leży w tej linijce ObservableList<Track> items = tableViewController.getTracksTableView().getItems();

Ale o co dokładnie chodzi?

1

Problem leży w tym że w żadnym miejscu nie przekazujesz z zewnątrz TableViewController i leci Ci null. Na twoim miejscu nie implementowałbym kontrolerów zagnieżdżonych (do czasu aż nie ogarniasz mechanizmów FXa) a tę tabele wrzucił bezpośrednio do ScheduleController i tyle.

0

DZIĘKUJĘ! :D

usunąłem klasę TableViewController, tabelę wrzuciłem do ScheduleController, jeszcze musiałem otworzyć pakiet model dla javafx.base bo o tym zapomniałem i działa :D Będę musiał się poduczyć z tych kontrolerów zagnieżdżonych, bo widocznie czegoś nie kumam i myślę, że robię dobrze a robię źle.
Teraz czas na obsługę wyjątków i zapis do jakiejś małej bazy danych.

0

Witam ponownie! Nie otwieram nowego wątku, bo nie chcę zaśmiecać forum, a dalej tworzę swój mały projekt i napotkałem pewien problem, na którego rozwiązanie nie potrafię trafić.
Stworzyłem bazę danych, która przechowuje moje obiekty Track. Mam już opracowane większość funkcjonalności typu usuwanie tras z tabeli i z bazy danych, dodawanie do bazy danych i do tabeli, usuwanie całej bazy danych i tabeli, wszystko ze sobą odpowiednio połączone, wszystko działa tak jak powinno. Napotkałem jednak problem w ostatniej funkcjonalności - export mojej listy tras do pliku. Najpierw chciałem zapisywać całą tabelę z programu do .pdf, lecz nie mogłem poszukać odpowiedniej biblioteki, która za darmo oferowałaby w miarę prosty zapis tych danych do np. prostej tabeli w pdf. Po dwóch dniach szukania odpuściłem temat, chciałem się zadowolić zapisem do pliku .txt w stylu jedna trasa w jednej linijce, za pomocą fileChoosera, ale walczę z tym i nie mam pojęcia jak to zrobić... Mam wymłodzone coś takiego:

private void saveFile() throws IOException {
                FileChooser fileChooser = new FileChooser();
                fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("plik tekstowy .txt", "*.txt"));
                fileChooser.setTitle("Zapisz listę kursów");
                File file = new File("lista kursów.txt");
                Writer writer = new BufferedWriter(new FileWriter(file, true));
                if (file != null) {
                        ObservableList<Track> items = tracksTableView.getItems();
                        for (Track item : items) {
                                String number = String.valueOf(item.getNumber());
                                String data = item.getData();
                                String driver = item.getDriver();
                                String registerNumber = item.getRegisterNumber();
                                String track = item.getTrack();
                                String client = item.getClient();
                                String bid = String.valueOf(item.getBid());
                                String profit = String.valueOf(item.getProfit());

                                String trackToList = number + ". data: " + data + ", kierowca: " + driver + ", numer rejestracyjny: "
                                        + registerNumber + ", trasa: " + track + ", klient: " + client + ", stawka: "
                                        + bid + " zł, dochód: " + profit + " zł.";

                                writer.append(trackToList);

                        }
                }
                file = fileChooser.showSaveDialog(new Stage());

        }

Przed chwilą zapisywało mi to wszystko w jednej linii w pliku tekstowym w folderze z projektem (to już było osiągnięcie, bo do tej pory nic się nie zapisywało), ale teraz znowu nie zapisuje się nic. Starałem się już jakoś używać PrintWritera, FileWritera itd, ale ciągle bez skutku. Plik "lista kursów" zapisuje się, więc jest tworzony i zapisywany - problem musi być w jego nadpisywaniu. Ogólnie chciałbym doprowadzić do tego, że przy każdym naciśnięciu przycisku "zapisz plik" miałby się tworzyć nowy plik .txt z listą kursów z tabeli, użytkownik mógłby nadać mu nazwę i zapisać gdzie by chciał poprzez FileChoosera i okno showSaveDialog

0

Sytuacja opanowana :) Wystarczyło File file = new File("lista kursów.txt"); zamienić na samo File file; i file = fileChooser.showSaveDialog(new Stage()); przenieść do góry, jako następną linijkę po File file;

0

@PanSmith: Co do szukania 2 dni jak stworzyć tabelę w pdf użyj iText. Nie używałem jej jakoś do super zaawansowanych rzeczy, ale tworzenie tabeli to max 10 linijek kodu.

0

@aolo23: dzięki, próbowałem też tego używać, ale miałem problem z importowaniem klas, nie wiedziałem co jest nie tak... Teraz przy drugim podejściu problem okazał się banalny - całkowicie z głowy wypadło mi dodawanie requires kernel i requires layout, przez co importowało mi inną klasę Document niż powinno.
Aczkolwiek teraz się zastanawiam, czy tworzenie .pdf zamiast .txt będzie lepszym rozwiązaniem, chyba nie w moim przypadku - co miesiąc użytkownik będzie zapisywał całą tabelę i bazę danych do pliku podsumowującego dany miesiąc, potem usuwa całą listę (a więc i całą bazę danych) i tworzy kolejny miesiąc. Jak będzie zapisywało w .pdf to nie będzie już mógł nic zmienić, przy .txt nie ma tego problemu, będzie można sobie zmienić wszystko.

BTW - wie ktoś co mam zrobić, żeby teraz ten mój cały projekt sprowadzić do takiej formy, żeby na komputerze użytkownika można go było uruchomić bez IntelliJ'a? Próbowałem tworzyć .jar z poziomu IntelliJ'a ale po dwukliku na ten .jar nic się nie dzieje, program się nie otwiera (próbowałem na swoim kompie gdzie mam wszystko poinstalowane co jest potrzebne do odpalania javy).

P. S. To mój pierwszy i ostatni projekt desktopowy w javie... :D jeśli chodzi o sprowadzenie tego do użytkowej formy dla zwykłego Kowalskiego to męka pańska. Wiem, że zazwyczaj się javy do takich celów nie używa, ale przerobiłem kurs Javy i JavyFX i chciałem spróbować swoich sił i do tego właśnie potrzebowałem w firmie takiego programu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1