Programmeinstellungen für Anwendung??

Bitte aktiviere JavaScript!
Nun möchte ich für mein JavaFX Programm Einstellungen speichern. Nicht nur nur Position, Größe und der letzten geöffneten Dateien. Ich möchte auch Verzeichnispfade (File) speichern, sowie boolsche Werte für aktivierte und deaktivierte Funktionen also nicht nur Strings und Integer Werte.
Angefangen über Registry, ini, XML und Preferences habe ich Informationen gefunden.
Leider ist mir nicht ganz klar wie Einstellungen überhaupt funktionieren?
Mir kommt es vor als wenn die Informationen in einer Externen datei gespeichert werden und bei Bedarf von Werten aus dieser Datei geladen werden?
Oder macht man eine eigene Klasse mit zu speichernden informationen, erstellt ein Objekt und macht es durch weiterreichen verfügbar?
Auf keinen Fall kommen ini und Regsitry in Frage, da ich gerne Platformübergreifend bleiben möchte.
Über eine Erleuchtung würde ich mich freuen.
Danke
Mi
 
Danke, habe ich gemacht.
Mir erschließt sich nicht ob die Wertepaare in einem ungeordneten Speicher sowas wie globale Variablen sind? Ich habe keine Idee, wie ich mit properties vorgehen soll?
Die Werte sollen nicht nur mit der GUI Synchonisiert werden, die Einstellungen zum Programm auflistet und auch dort geändert werden können. Diese Werte sollte im gesamten Programm zur Verfügung stehen.
Properties in JavaFX kenne ich als eingepackte Datentypen in Objekte, die mit der GUI synchronisiert werden.
 
ob die Wertepaare in einem ungeordneten Speicher sowas wie globale Variablen sind?
je nachdem...
Ich habe keine Idee, wie ich mit properties vorgehen soll?
vor dem start laden?

entweder die ließt die datei immer wenn du etwas benötigst oder vor dem start alles in ein object speichern und dieses durch die application reichen...beim beenden kannst du es ja dann speichern
 
Danke für die Info.
Das durchreichen hört sich ziemlich aufwendig an, da man auch berücksichtigen muss wenn sich das Objekt in einer Methode verändert wieder zurück zu geben.
Beim ändern in die Datei zuschreiben und bei bedarf von einem Ort die Werte zu laden, das hört sich ziemlich unkompliziert an. Jedoch habe ich keine Erfahrungen, ob sich die Leistung des Programms durch ständige Zugriffe auf das Dateisystem beeinträchtigt wird?
 
Hi,
ich weiß es ist schon etwas länger her, muss aber immer noch am dem Problem mit den Programmeinstellungen arbeiten.
Lange habe ich gebraucht mit JavaFX eine GUI auf FXML Basis zu erstellen und auch funktionales mit dem MVC Modell erlernen.
War für mich nicht einfach, habe lange im Netz und in Büchern verbracht.
Die Aktuelle Situation ist, das ich im Modell StringProperties verwendet habe.
Das Speichern mit Serialize speichern möchte.
Die Datei wird zwar geschrieben, gibt aber eine Fehlermeldung.

Mein log
Code:
INFO  start - Das Programm wird initialisiert init()-Methode
INFO  start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  kontroller.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  kontroller.Einstellungen - Einstellungen werden initialisiert
INFO  kontroller.Einstellungen - sucheQuellverzeichnis wurde aufgerufen
INFO  kontroller.Einstellungen - Selektiertes Quellverzeichnis StringProperty [value: B:\01 Quelle]
INFO  kontroller.Einstellungen - Methode einstellungenSpeichern wurde aufgerufen
INFO  modelle.Einstellungen - Es trat ein Fehler beim schreiben der Programmeinstellungen auf
INFO  kontroller.Einstellungen - einstellungenLaden wurde aufgerufen
INFO  kontroller.Einstellungen - Alle Werte im Fenster Einstellungen werden gelöscht
INFO  kontroller.Einstellungen - Alle Werte für Einstellungen werden von Festplatte geladen
INFO  modelle.Einstellungen - lese EinstellungenHD wurde aufgerufen
INFO  kontroller.Einstellungen - Neu geladene Werte für Einstellungen werden im Fenster aktualisiert
INFO  kontroller.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  dms.DMS - Programm-Ende wird ausgeführt stop()-Methode
Methode Speichern auf HD
Java:
public void schreibeEinstellungenHD(Einstellungen einstellungen) {
        OutputStream dateiAusgabeStream = null;
        try {
            dateiAusgabeStream = new FileOutputStream(dateiEinstellungen);
            ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream);
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (IOException e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf");
        } finally {
            try {
                dateiAusgabeStream.close();
            } catch (IOException e) {
            }
        }
    } // schreibeEinstellungenHD
Methode Laden von HD
Java:
public Einstellungen leseEinstellungenHD() {
        LOG.info("lese EinstellungenHD wurde aufgerufen");
        Einstellungen programmEinstellungen = null;
        // prg.schreibePfadQuelle("Das ist ein neuer Quellpfad");
        try {
            try (FileInputStream dateiEingabeStream = new FileInputStream(dateiEinstellungen)) {
                ObjectInputStream objektEingabeStream = new ObjectInputStream(dateiEingabeStream);
                programmEinstellungen = (Einstellungen) objektEingabeStream.readObject();
                LOG.info("Die Programmeinstellungen wurde in das Objekt geladen");
                LOG.info("Quellverzeichnis wurde geladen : " + programmEinstellungen.holePfadQuelle());
                LOG.info("Benutzername wurde geladen     : " + programmEinstellungen.holeDbBenutzername());
            }
        } catch (IOException | ClassNotFoundException e) {
        }

        return programmEinstellungen;
    } // leseEinstellungenHD
NachSuchenQuelle.JPG
Button Speichern
Java:
@FXML
    private void einstellungenSpeichern(ActionEvent event) {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        // Alle gemachten Einstellungen werden dauerhaft gespeichert
        einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichern
NachLadenButton.JPG
Button Laden
Java:
@FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
        
        LOG.info("Alle Werte im Fenster Einstellungen werden gelöscht");
        lblBildverzeichnis.setText("");
        lblGeschuetztesVerzeichnis.setText("");
        lblLoeschverzeichnis.setText("");
        lblOCRVerzeichnis.setText("");
        lblOhneDokumentartVerzeichnis.setText("");
        lblQuellverzeichnis.setText("");
        lblZielverzeichnis.setText("");
        
        LOG.info("Alle Werte für Einstellungen werden von Festplatte geladen");
        this.einstellungen.leseEinstellungenHD();
        
        LOG.info("Neu geladene Werte für Einstellungen werden im Fenster aktualisiert");
        lblQuellverzeichnis.setText(einstellungen.holePfadQuelle().toString());
        tfDBBenutzername.setText(einstellungen.holeDbBenutzername().toString());
    } // einstellungenLaden
1. Fehlermeldung "Es trat ein Fehler beim schreiben der Programmeinstellungen auf"
Obwohl die Datei erstellt wird, wird die catch IOException e ausgeführt?

2. Im Fenster Einstellungen "StringProperty"
Im Fenster wird StringProperty inklusive Value und den Vert in Eckigen Klammern ausgegeben.
Kannman im Fenster auch nur den Wert anzeigen lassen? "B:\01 Quelle"

3. Button "Laden"
Damit ich auch wirklich merke das neue Einstellungen im Fenster label dargestellt werden habe ich alle
Werte in den Labels zurückgesetzt lblQuellverzeichnis.setText("");
Dann lade ich die Einstellungen aus der Datei und es wird dann die StringProperty dargestellt

Schein zu funktionieren, bin mir aber nicht so ganz sicher, ob das wirklich so ist.

Ich würde mich freuen wenn mal jemand darüber schauen würde um den Punkt 1 und 2 zu klären.

Danke
Mi
 
Schau Dir doch bitte die Methoden von Deiner log Instanz an!

Diese haben normalerweise auf Methoden, denen Du am Ende einfach die Exception (bzw. ein Throwable, wovon Exception erbt) übergeben kannst.

Bitte merke Dir eine Sache: Entwickler, die in einem catch die Exception komplett ignorieren, landen auf dem Scheiterhaufen. :)

Also im Ernst: So Exceptions solltest Du wirklich nie ignorieren. Es mag evtl. ganz seltene Ausnahmen geben, bei denen das ok sein kann, aber selbst da ist Tracing meist noch sinnvoll....

Und sobald du das, was ich als erster Angesprochen habe, gemacht hast, bekommst Du auch deutlich mehr Informationen als nur ein "Es trat ein Fehler beim schreiben der Programmeinstellungen auf"!

Und noch ein kleiner Hinweis:
Und dieses close im finally Block: Schau Dir doch einmal das "try with resources" an. Dann entfällt dies komplett und Java kümmert sich darum.
 
Zu 2. hatte ich noch nichts geschrieben: Hier habe ich Probleme, Dich genau zu verstehen. Du setzt ja den Text über setText(...) und Du rufst bei holePfadQuelle() einfach .toString() auf. (Und bei dem anderen Wert natürlich auch!)

Ich vermute einmal, dass das Resultat mit den eckigen Klammern von der toString Implementation der Klasse, von der die Instanz, die da zurück gegeben wird, kommt. Und wenn das ein key / value Paar ist, dann kannst Du bestimmt auch den Wert abfragen. Also nicht einfach .toString() darauf aufrufen.
 
Mit Log Instanz meinst du das Logger Objekt?
Meinst Du das man IOException an den Logger übergibt anstatt es in einer Catch Anweisung zu definieren?

Eine catch Exception zu ignorieren ist nicht meine Absicht und habe dies unter 1 beschrieben. Da ich in Büchern die Vorgehensweise erlernt habe die in etwa so aussieht frage ich mich wo das problem liegen könnte. Sehr viele Zeilen sind fsa ja nicht bei denen man etwas falsch machen könnte?
Und das die Datei dann plötzlich da ist sagt mir dass das writeObject etwas geschrieben hat.

Der Funktionskopf und finally habe ich geändert in
Java:
public void schreibeEinstellungenHD(Einstellungen einstellungen) throws IOException {
...

} finally {
                dateiAusgabeStream.close();
        }
    } // schreibeEinstellungenHD
Zu 2. StringProperties
Damit hatte ich zuvor noch nie gearbeitet und dachte, das es wie Strings behandelt wird die sich halt Serialisieren lassen.
Werde mir die StringProperty noch mal genauer ansehen.
Eigentlich ist der Inhalt nur ein String "Ein Verzeichnispfad" und wird durch holePfadQuelle() aus dem objekt geholt und mit toString als String für das Label deklariert.

Anscheinend habe ich das Serializieren und Laden nicht richtig verstanden, da ich beim Einfügen eines zweiten Stringwertes gesehen habe das diese nicht richtig den labels zugeordnet werden. Die Inhalte sind vertauscht. Ich könnte mir vorstellen das ich das schreiben und laden bezüglich des Datenobjektes nicht wirklich korrekt gemacht habe.
 
Zuletzt bearbeitet:
Ja, ich meinte Deine LOG Instanz. Ich weiß nicht, welches Logging Framework Du verwendest, aber Du könntest einmal schauen, ob der Code so funktionieren würde (im Forum-Editor geschrieben, daher evtl. Tippfehler vorhanden!)
Java:
        // 1. Try with ressource
        try (dateiAusgabeStream = new FileOutputStream(dateiEinstellungen)) {
            ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream);
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (IOException e) {
            // 2. Exception wird mit übergeben. Das ist bei den meisten Logging Frameworks so gebaut ...
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
        // 3. finally Block entfällt da try with ressource das close übernimmt.
Man kann dann auch die Deklaration mit in das try ziehen und da der ObjectOutputStream auch AutoCloseable implementiert, würde ich dann am Ende zu sowas tendieren:
Java:
        // 0. Die Deklaration von dateiAusgabeStream vor dem try entfällt!
        // 1. Try with ressource
        try (FileOutputStream dateiAusgabeStream = new FileOutputStream(dateiEinstellungen);
             ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream)) {
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (IOException e) {
            // 2. Exception wird mit übergeben. Das ist bei den meisten Logging Frameworks so gebaut ...
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
        // 3. finally Block entfällt da try with ressource das close übernimmt.
Die Kommentare mit dem 0. / 1. / 2. und 3. sind nur für Dein Verständnis. Im Code würde ich die nicht setzen.
 
Properties sind dazu da, an eine andere Property gebunden zu werden um automatisch aktualisiert zu werden.
So kannst du die TextProperty deines Textfeldes an die StringProperty deines Objektes binden.
Java:
lblQuellverzeichnis.textProperty() .bind(einstellungen.holePfadQuelle());
 
@Robat
Danke nochmal für die Aufklärung. Ist schon länger her, aber ja genau deshalb sollte ich mich damals mit Properties beschäftigen, da ich ja mit einer GUI Arbeiten wollte. Bezüglich GUI Programmierung war echt nicht viel Literatur zu finden nur die übliche für Anfänger, bei denen scheinbar alle abschreiben. Jedenfalls kommt es mir so vor, da die Inhalte alle gleich sind.
Hat man bedarf darüber hinaus, sieht es schlecht aus.
Habe den SetText durch das oben vorgeschlagene Binding ersetzt und bekomme das gewünschte Ergebnis.
Danke freue mich dafür und werde das Kapitel nochmal etwas vertiefen.
GuiBinding.JPG

@kneizel
Danke für den Vorschlag, ich werde das mal ausprobieren. Ich nutze Log4j2.

Mein Ziel war es ein Objekt mit dem Namen "einstellungen" zu erstellen. In dieses Objekt wollte ich alles was an Programmeinstellungen benötigt wird hinein speichern. Dieses Objekt sollte zu beginn des Programmes init() geladen werden. Mit dem aufrufen des Fensters "Einstellungen sollten alle Inhalte dieses Objektes verändert, aktualisiert oder gelöscht werden können. Der Laden Button habe ich aktuell nur zur Kontrolle eingebaut um die Speicherung zu testen. Beim Drücken von Speichern sollen alle Inhalte des Objektes auf die Festplatte gespeichert und das Fenster Einstellungen geschlossen werden.
Klingt nicht gerade kompliziert, aber für mich nicht gerade einfach.
 
Zuletzt bearbeitet:
Deinen Vorschlag für die Log Instanz habe ich implementiert und siehe da, es kommen jede Menge informationen
Code:
INFO  start - Das Programm wird initialisiert init()-Methode
INFO  start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  kontroller.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  kontroller.Einstellungen - Einstellungen werden initialisiert
INFO  kontroller.Einstellungen - sucheQuellverzeichnis wurde aufgerufen
INFO  kontroller.Einstellungen - Selektiertes Quellverzeichnis StringProperty [value: B:\01 Quelle]
INFO  kontroller.Einstellungen - Methode einstellungenSpeichern wurde aufgerufen
INFO  modelle.Einstellungen - Es trat ein Fehler beim schreiben der Programmeinstellungen auf
java.io.NotSerializableException: javafx.beans.property.SimpleStringProperty
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185) ~[?:?]
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553) ~[?:?]
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510) ~[?:?]
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433) ~[?:?]
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179) ~[?:?]
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349) ~[?:?]
    at modelle.Einstellungen.schreibeEinstellungenHD(Einstellungen.java:161) ~[DMS.jar:1.0]
    at kontroller.Einstellungen.einstellungenSpeichern(Einstellungen.java:139) ~[DMS.jar:1.0]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76) ~[javafx.base:?]
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275) ~[javafx.base:?]
    at com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670) ~[javafx.fxml:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Node.fireEvent(Node.java:8865) ~[javafx.graphics:?]
    at javafx.scene.control.Button.fire(Button.java:200) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274) ~[javafx.controls:?]
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3876) ~[javafx.graphics:?]
    at javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604) ~[javafx.graphics:?]
    at javafx.scene.Scene.processMouseEvent(Scene.java:1874) ~[javafx.graphics:?]
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295) ~[javafx.graphics:?]
    at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.handleMouseEvent(View.java:556) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.notifyMouse(View.java:942) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175) ~[javafx.graphics:?]
    at java.lang.Thread.run(Thread.java:844) [?:?]
INFO  kontroller.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  start - Programm-Ende wird ausgeführt stop()-Methode
Da ich mir nicht vorstellen kann das es an der Schreibe Methode liegt, denke ich das es an meinem Datenodell liegt?

Hier sind zwei Methoden für die Knöpfe Quellverzeichnis und Zielverzeichnis.
Java:
@FXML
    private void sucheQuellverzeichnis(ActionEvent event) {
        LOG.info("sucheQuellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Quellverzeichnis");
        einstellungen.schreibePfadQuelle(verzeichnis);
        lblQuellverzeichnis.textProperty().bind(einstellungen.holePfadQuelle());
        LOG.info("Selektiertes Quellverzeichnis " + einstellungen.holePfadQuelle());
    } // sucheQuellverzeichnis

    @FXML
    private void sucheZielverzeichnis(ActionEvent event) {
        LOG.info("sucheZiellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Zielverzeichnis");
        einstellungen.schreibePfadZiel(verzeichnis);
//        lblZielverzeichnis.textProperty().bind(einstellungen.holePfadZiel());
        lblZielverzeichnis.setText(verzeichnis);
        LOG.info("Selektiertes Zielverzeichnis " + einstellungen.holePfadZiel());
    } // sucheZielverzeichnis
Wenn ich statt des Bindings setText verwende wird es richtig in das Fenster geschrieben, arbeite ich mit dem Binding, ist es falsch.
So wie im Bild einstellungen. Versteh ich nicht, wenn ich zwei verschiedene Labels mit Binding anpreche, wie kann denn dann der gleiche inhalt bei beiden Labels gleich sein obwohl das Quellverzeichnis schon korrekt dargestellt wurde?
einstellungen.JPG
Daher gehe ich davon aus, das mein Modell fehlerhaft sein könnte?
Die Klasse für das Modell ist im package modelle
Java:
public class Einstellungen implements Serializable {

    // Erstellt ein LoggerObjekt und holt dessen Instanz
    private static final Logger LOG = (Logger) LogManager.getLogger(Einstellungen.class);

    // Deklarationen Standardeinstellungen
    String dateiNameEinstellungen = "Programmeinstellungen.ser";
    String pfadEinstellungen = "B:\\00 Einstellungen\\";
    File dateiEinstellungen = new File(pfadEinstellungen + dateiNameEinstellungen);

    private final StringProperty pfadQuelle = new SimpleStringProperty();
    private final StringProperty pfadZiel = new SimpleStringProperty();
    private final StringProperty pfadOCR = new SimpleStringProperty();
    private final StringProperty pfadOhneDokumentArt = new SimpleStringProperty();
    private final StringProperty pfadLoeschen = new SimpleStringProperty();
    private final StringProperty pfadGeschuetzt = new SimpleStringProperty();
    private final StringProperty pfadBild = new SimpleStringProperty();

    private final StringProperty dbServeradresse = new SimpleStringProperty();
    private final StringProperty dbDatenbankname = new SimpleStringProperty();
    private final StringProperty dbBenutzername = new SimpleStringProperty();
    private final StringProperty dbPasswort = new SimpleStringProperty();
    private final StringProperty dnbToken = new SimpleStringProperty();

    // Konstruktor ohne Parameter
    public Einstellungen() {

    } // Konstruktor ohne Parameter

    // Methoden
    public StringProperty holePfadQuelle() {
        return pfadQuelle;
    }

    public void schreibePfadQuelle(String quellVerzeichnis) {
        pfadQuelle.set(quellVerzeichnis);
    }

    public StringProperty pfadQuelleProperty() {
        return pfadQuelle;

    }

    public StringProperty holePfadZiel() {
        return pfadZiel;
    }

    public void schreibePfadZiel(String zielVerzeichnis) {
        pfadQuelle.set(zielVerzeichnis);
    }

    public StringProperty holePfadOCR() {
        return pfadOCR;
    }

    public void schreibePfadOCR(String ocrVerzeichnis) {
        pfadQuelle.set(ocrVerzeichnis);
    }

    public StringProperty holePfadOhneDokumentArt() {
        return pfadOhneDokumentArt;
    }

    public void schreibePfadOhneDokumentArt(String ohneDokumentartVerzeichnis) {
        pfadQuelle.set(ohneDokumentartVerzeichnis);
    }

    public StringProperty holePfadLoeschen() {
        return pfadLoeschen;
    }

    public void schreibePfadLoeschen(String loeschenVerzeichnis) {
        pfadQuelle.set(loeschenVerzeichnis);
    }

     public void schreibePfadGeschuetzt(String geschuetztVerzeichnis) {
        pfadQuelle.set(geschuetztVerzeichnis);
    }

    public StringProperty holePfadBild() {
        return pfadBild;
    }

    public void schreibePfadBild(String bildVerzeichnis) {
        pfadQuelle.set(bildVerzeichnis);
    }

    public StringProperty holeDbServeradresse() {
        return dbServeradresse;
    }

    public void schreibeDbServeradresse(String serveradresse) {
        pfadQuelle.set(serveradresse);
    }

    public StringProperty holeDbDatenbankname() {
        return dbDatenbankname;
    }

    public void schreibeDbDatenbankname(String datenbankname) {
        pfadQuelle.set(datenbankname);
    }

    public StringProperty holeDbBenutzername() {
        return dbBenutzername;
    }

    public void schreibeDbBenutzername(String benutzername) {
        pfadQuelle.set(benutzername);
    }

    public StringProperty holeDbPasswort() {
        return dbPasswort;
    }

    public void schreibeDbPasswort(String passwort) {
        pfadQuelle.set(passwort);
    }

    public StringProperty holeDnbToken() {
        return dnbToken;
    }

    public void schreibeDnbToken(String token) {
        pfadQuelle.set(token);
    }

    public void schreibeEinstellungenHD(Einstellungen einstellungen) throws IOException {
        try (FileOutputStream dateiAusgabeStream = new FileOutputStream(dateiEinstellungen);
            ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream)) {
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (Exception e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }

    } // schreibeEinstellungenHD

    public Einstellungen leseEinstellungenHD() {
        LOG.info("lese EinstellungenHD wurde aufgerufen");
        Einstellungen programmEinstellungen = null;
        try {
            try (FileInputStream dateiEingabeStream = new FileInputStream(dateiEinstellungen)) {
                ObjectInputStream objektEingabeStream = new ObjectInputStream(dateiEingabeStream);
                programmEinstellungen = (Einstellungen) objektEingabeStream.readObject();
                LOG.info("Die Programmeinstellungen wurde in das Objekt geladen");
                LOG.info("Quellverzeichnis wurde geladen : " + programmEinstellungen.holePfadQuelle());
                LOG.info("Benutzername wurde geladen     : " + programmEinstellungen.holeDbBenutzername());
            }
        } catch (IOException | ClassNotFoundException e) {
        }
        return programmEinstellungen;
    } // leseEinstellungenHD
} // Einstellungen
Der Kontroller ist im package kontroller
Java:
public class Einstellungen implements Initializable {

    // Erstellt ein LoggerObjekt und holt dessen Instanz
    private static final Logger LOG = (Logger) LogManager.getLogger(Einstellungen.class);

    modelle.Einstellungen einstellungen = new modelle.Einstellungen();

    @FXML
    private TextField tfDBServeradresse;
    @FXML
    private TextField tfDBDatenbankname;
    @FXML
    private TextField tfDBBenutzername;
    @FXML
    private TextField tfDBPasswort;
    @FXML
    private TextField tfTokenDNB;
    @FXML
    private Label lblQuellverzeichnis;
    @FXML
    private Label lblZielverzeichnis;
    @FXML
    private Label lblOCRVerzeichnis;
    @FXML
    private Label lblOhneDokumentartVerzeichnis;
    @FXML
    private Label lblLoeschverzeichnis;
    @FXML
    private Label lblGeschuetztesVerzeichnis;
    @FXML
    private Label lblBildverzeichnis;

    /**
     * Initialisierung der Kontroller Klasse.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        LOG.info("Einstellungen werden initialisiert");     
    } // initialize

    @FXML
    private void sucheQuellverzeichnis(ActionEvent event) {
        LOG.info("sucheQuellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Quellverzeichnis");
        einstellungen.schreibePfadQuelle(verzeichnis);
        lblQuellverzeichnis.textProperty().bind(einstellungen.holePfadQuelle());
        LOG.info("Selektiertes Quellverzeichnis " + einstellungen.holePfadQuelle());
    } // sucheQuellverzeichnis

    @FXML
    private void sucheZielverzeichnis(ActionEvent event) {
        LOG.info("sucheZiellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Zielverzeichnis");
        einstellungen.schreibePfadZiel(verzeichnis);
//        lblZielverzeichnis.textProperty().bind(einstellungen.holePfadZiel());
        lblZielverzeichnis.setText(verzeichnis);
        LOG.info("Selektiertes Zielverzeichnis " + einstellungen.holePfadZiel());
    } // sucheZielverzeichnis

    @FXML
    private void sucheOCRVerzeichnis(ActionEvent event) {
        LOG.info("sucheOCRVerzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das OCR Verzeichnis");
        einstellungen.schreibePfadOCR(verzeichnis);
        lblOCRVerzeichnis.textProperty().bind(einstellungen.holePfadOCR());
        LOG.info("Selektiertes Zielverzeichnis " + einstellungen.holePfadOCR());
    } // sucheOCRVerzeichnis

    @FXML
    private void sucheBildverzeichnis(ActionEvent event) {
        LOG.info("sucheBildverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Bildverzeichnis");
        einstellungen.schreibePfadBild(verzeichnis);
        lblBildverzeichnis.textProperty().bind(einstellungen.holePfadBild());
        LOG.info("Selektiertes Bildverzeichnis " + einstellungen.holePfadBild());
    } // sucheBildverzeichnis

    @FXML
    private void sucheOhneDokumentartVerzeichnis(ActionEvent event) {
        LOG.info("sucheOhneDokumentartVerzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Verzeichnis ohne Dokumentart");
        einstellungen.schreibePfadOhneDokumentArt(verzeichnis);
        lblOhneDokumentartVerzeichnis.textProperty().bind(einstellungen.holePfadOhneDokumentArt());
        LOG.info("Selektiertes Verzeichnis ohne Dokumentart " + einstellungen.holePfadOhneDokumentArt());
    } // sucheOhneDokumentartVerzeichnis

    @FXML
    private void sucheLoeschverzeichnis(ActionEvent event) {
        LOG.info("sucheLoeschverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Löschverzeichnis");
        einstellungen.schreibePfadLoeschen(verzeichnis);
        lblLoeschverzeichnis.textProperty().bind(einstellungen.holePfadLoeschen());
        LOG.info("Selektiertes Löschverzeichnis " + einstellungen.holePfadLoeschen());
    } // sucheLoeschverzeichnis

    @FXML
    private void sucheGeschuetztesVerzeichnis(ActionEvent event) {
        LOG.info("sucheGeschuetztesVerzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Geschützte Verzeichnis");
        einstellungen.schreibePfadGeschuetzt(verzeichnis);
        lblGeschuetztesVerzeichnis.textProperty().bind(einstellungen.holePfadGeschuetzt());
        LOG.info("Selektiertes Geschützte Verzeichnis " + einstellungen.holePfadGeschuetzt());
    } // sucheGeschuetztesVerzeichnis

    @FXML
    private void einstellungenSpeichern(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        // Alle gemachten Einstellungen werden dauerhaft gespeichert
        einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichern
    

    @FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
        
        LOG.info("Alle Werte im Fenster Einstellungen werden gelöscht");
        lblQuellverzeichnis.setText("");
        lblZielverzeichnis.setText("");
        lblOCRVerzeichnis.setText("");
        lblBildverzeichnis.setText("");
        lblOhneDokumentartVerzeichnis.setText("");
        lblLoeschverzeichnis.setText("");
        lblGeschuetztesVerzeichnis.setText("");
        
        LOG.info("Alle Werte für Einstellungen werden von Festplatte geladen");
        this.einstellungen.leseEinstellungenHD();
        
        LOG.info("Neu geladene Werte für Einstellungen werden im Fenster aktualisiert");
          lblQuellverzeichnis.textProperty().bind(einstellungen.holePfadQuelle());
          lblZielverzeichnis.textProperty().bind(einstellungen.holePfadZiel());
          lblOCRVerzeichnis.textProperty().bind(einstellungen.holePfadOCR());
          lblBildverzeichnis.textProperty().bind(einstellungen.holePfadBild());
          lblOhneDokumentartVerzeichnis.textProperty().bind(einstellungen.holePfadOhneDokumentArt());
          lblLoeschverzeichnis.textProperty().bind(einstellungen.holePfadLoeschen());
          lblGeschuetztesVerzeichnis.textProperty().bind(einstellungen.holePfadGeschuetzt());
                  
    } // einstellungenLaden

} // Einstellungen
 
Also das Problem ist in Deinem Log diese Stelle:
java.io.NotSerializableException: javafx.beans.property.SimpleStringProperty

Du kannst mit ObjectOutputStream nur Objekte schreiben, die Serializable sind. Und das ist SimpleStringProperty nicht.

Aber Du implementierst Serializable ja bei Einstellungen implementierst, wäre eine Möglichkeit, dass Du die Serialisierung selbst vornimmst:
a) alle Properties markierst du als transient
b) Du liest und schreibst die Werte selbst in den entsprechenden Funktionen:
Java:
private void writeObject(ObjectOutputStream s) throws IOException {
    s.defaultWriteObject();
    s.writeLong(idProperty().longValue());
    s.writeUTF(aStringProperty().getValueSafe()); // getValueSafe damit kein null rein kommen kann.
    // ...
}

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    idProperty.set(s.readLong());
    aStringProperty.set(s.readUTF());
    // Gleiche Reihenfolge wie bei writeObject()
    // ...
}
 
Ich würde das ganze aufspalten, sodass deine Einstellungen keine Properties haben. Die Properties wandern in ein ViewModel EinstellungenViewModel, das aus einem Model konstruiert werden kann. Im Controller verbindest du nur noch den View mit dem ViewModel
Java:
tfDBServeradresse.textProperty().bind(einstellungenVm.tfDBServeradresseProperty());
Die Einstellungen sind somit einfach zu serialisieren und die Zuständigkeiten besser geklärt. Ich hoffe das war verständlich genug skizziert.
 
Hi, wie meinst du das mit dem aufteilen?
Ich habe ein Model, einen Kontroller und eine View! Ich habe ewig gebraucht das zu begreifen und jetzt versuche ich das in der Praxis. Im Kontroller "EinstellungenK" wird wir ein Objekt erzeugt welches durch den Parameterlosen Konstruktor ein Einstellungen Objekt erzeugt, mit den Methoden und Properties aus der Klasse "EinstellungenM". Somit habe doch doch schon mal eine Verbindung zum Modell durch den Kontroller hergestellt. War doch richtig?
Und im Kontroller und dem Binding wird das Modell dann zum Fenster gebunden?!?!
Wenn ich jetzt etwas in die Properties stecke, sollte es dann doch im Fenster sichtbar werden? War doch so?
Eine wirklich gute Literatur hierzu habe ich nie wirklich gefunden die das an einem Beispiel in JavaFX zeigt.

Es scheint so das ich etwas mit den Methoden "schreibePfadQuelle", "schreibePfadZiel" zwar hineinschreibe aber es nicht wirklich drin ist oder so. Zumindest habe ich das Gefühl, dass das objekt nicht so reagiert wie ich es erwartet hätte.
 
Dein Model hat das Problem, dass es JavaFX-Properties benutzt, und damit kaum von der View unabhängig ist. Besser ist’s, das Model möglichst einfach zu halten.

Um das Model trotzdem an die View binden zu können, führt man ein ViewModel ein, dieses enthält die Properties (an die sich die View binden kann) und kennt das Model, holt sich also den aktuellen Zustand daher und schreib Änderungen dort hin.
 
Ein Minimalbeispiel, um es darzustellen.
Die Einstellungen (Modell) als POJO ohne Abhängigkeiten zu JavaFX. Reines Java Standardlib.
Java:
// POJO
public class Einstellungen {

    private String tfDBServeradresse;

    public Einstellungen setTfDBServeradresse(final String tfDBServeradresse) {
        this.tfDBServeradresse = tfDBServeradresse;
        return this;
    }

    public String getTfDBServeradresse() {
        return tfDBServeradresse;
    }

    public static Einstellungen defaultSettings() {
        return new Einstellungen()
                .setTfDBServeradresse("localhost");
    }
}
Das ViewModel als Mapper zwischen Modell und View. Hier kommt jede Logik rein, die zur Darstellung benötigt wird. z.B. die Logik, ob ein Feld disabled sein soll oder nicht (wird wiederum als Property nach außen sichtbar, um die View daran zu binden). Das ViewModel darf keine Referenzen auf die View haben.
Java:
public class EinstellungenViewModel {
    private final StringProperty tfDBServeradresse = new SimpleStringProperty(this, "tfDBServeradresse");

    public EinstellungenViewModel(Einstellungen einstellungen) {
        tfDBServeradresse.set(einstellungen.getTfDBServeradresse());
    }

    public String getTfDBServeradresse() {
        return tfDBServeradresse.get();
    }

    public StringProperty tfDBServeradresseProperty() {
        return tfDBServeradresse;
    }

    public void setTfDBServeradresse(final String tfDBServeradresse) {
        this.tfDBServeradresse.set(tfDBServeradresse);
    }

}
Der View-Controller macht das Binding der Observables der View und des ViewModel. Streng genommen kommt hier kein bisschen Logik rein, sondern nur das Herstellen der Bindings.
Java:
public class EinstellungenView implements Initializable {

    @FXML
    private TextField tfDBServeradresse;

    private final EinstellungenViewModel viewModel;

    public EinstellungenView() {
        // Load from file in real world example
        Einstellungen einstellungen = Einstellungen.defaultSettings();
        // Construct view model
        this.viewModel = new EinstellungenViewModel(einstellungen);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        tfDBServeradresse.textProperty().bind(viewModel.tfDBServeradresseProperty());
    }
}
Ein Startercode, um die GUI zu erzeugen.
Java:
public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("EinstellungenView.fxml"));
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.sizeToScene();
        primaryStage.show();
    }
}
Das sehr simple FXML
XML:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="EinstellungenView"
            prefHeight="400.0" prefWidth="600.0">

    <TextField fx:id="tfDBServeradresse"/>

</AnchorPane>
Ich hoffe dir wird ersichtlich, wie deine Klasse Einstellungen sauberer wird und die Serialisieren wieder einfach.
 
Vielen Dank für die Ausführliche Darstellung.
Wenn ich das richtig verstanden habe, sollte ich mein Modell genau so gestalten, wie ich es bei meinen Konsolenprogrammen ohne GUI in normalen Java Code gemacht habe. Dann eine weitere Modell Klasse (ViewModell) die im Prinzip die gleichen Inhalte der zu speichernden Werte hat wie das Modell, aber als Properties weil diese an das View gebunden werden können. Im Kontroller sind dann nur Methoden enthalten die zum View gehören wie Buttons, Labels usw.
Methoden zur Verarbeitung von Werten kommen weiterhin in das Modell!
Das Serialisieren der Werte wird dann nur mit dem Modell durchgeführt, also dort implements Serializable?
Habe dann mal unter ViewModel im Netz nachgesehen und da tauchte dann das Konzept des MVVM auf.
Ich werde mein Programm nach den o.a. Informationen abändern und hoffe, das es klappt :)

Eine Frage hätte ich noch zur Organisation der Pakete.
Sollte man diese nach Fenster gruppieren, was zum Fenster Einstellungen gehört?
Also Paket Einstellungen und darunter dann das Modell, ViewModell, Kontroller, FXML, stil.css usw.

Oder wie im Bild nach Gruppen wie Fenster, Kontroller, Modelle, Stiele.css, FXMLs?
packages.JPG
Vielen Dank für die tolle Hilfe, ich melde mich zurück wenn es es funktioniert, oder wenn es auch nicht funktioniert :)
Mi
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben