# Meine ProgBar und ProgIndi werden nicht aktualisiert



## Joob (4. Apr 2016)

Ich lese eine Platte ein.
Das übliche Schleifengedöns.

Ich wollte dem Benutzer ProgressBar darstellen, also mußte ich mich mit Threads auseinandersetzen. Hab ich auch gemacht. Leider wurde der Bar erst am Ende der Funktions angezeigt und nicht zur Laufzeit. 

Also habe ich einen ProgressIndicator eingefügt und das ganze mit Property probiert, the same.
Wenn ich System.out verwende sehe ich das Props und Thread zur Laufzeit aktualisiert werden bzw. parallel laufen. 

Irgendwie bekomme ich javafx nicht dazu zur Laufzeit anzuzeigen wie der Status ist.

anliegend das Fensterlayout und der Code ich hoffe das ist nicht zu unübersichtlich:





```
/*
* author JUPP
*/
package wotan;

import SYS_APPSBAS.FIIO;
import SYS_APPSBAS.IBOX;
import SYS_APPSBAS.Prop_Progress;
import SYS_APPSBAS.TRFR;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;


/**
* FXML Controller class
*
* @author Jupp
*/
public class S1_1_PlatteController implements Initializable {

    final Prop_Progress propProg = new Prop_Progress();
   
    @FXML private TextField TF_LaufWerkBuchstabePlatte;
    @FXML private TextField TF_NumPlatte;
    @FXML private Label LB_STATUS;
    @FXML private ProgressBar PB_STATUS;
    @FXML private ProgressIndicator PI_STATUS;


 
    // Variablen deklarieren und initalisieren
    String userHome = System.getProperty("user.home");
    String wotanHome = userHome + "\\WOTAN";
    String dataFileName = "WOTANDATEN.CSV";
    String wotanConfig = wotanHome + "\\config";
    String configFileName = "WOTANCONFIG.CSV";
    String oldString = "";
    int anzRows = 1;
    double aktStatProgIndi = 0.0;

  
    // Initializes the controller class.
    @Override
    public void initialize(URL url, ResourceBundle rb) {
      // TODO
           
            // Propertie setzen
            propProg.setNum(0);
            propProg.prop().addListener(new ChangeListener<Object>() {
                @Override
                public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
                  
                    Integer statProg = new Integer(propProg.getNum());          // Propertie abfragen
                    aktStatProgIndi = (double) statProg / anzRows ;                       // Prozentsatz der Fertigstellung abfrage
                    PI_STATUS.setProgress(aktStatProgIndi);
                                       
                }
            });
        
            // Prüfen Plattennummer
            TF_NumPlatte.focusedProperty().addListener(new ChangeListener<Boolean>() {
               
                @Override
                public void changed(ObservableValue<? extends Boolean> arg0, Boolean oldPropertyValue, Boolean newPropertyValue) {
                    String FieldValue = TF_NumPlatte.getText();

                    if (newPropertyValue) {                                    // Get Focus
                        oldString = FieldValue;
                    } else {                                                // Lost Focus

                        TRFR trfr = new TRFR();
                        TF_NumPlatte.setText(trfr.vali_Len_Of_Str(FieldValue, 2));
                        trfr = null;
                               
                        // Null checker
                        if ((FieldValue == null) || (FieldValue == "") || (FieldValue.isEmpty())) {
                            TF_NumPlatte.requestFocus();
                        }

                    }
                }
            });
     
    }   

   
    public void handle_CB_Choose() {
   
                    final DirectoryChooser dirChooser = new DirectoryChooser();
                    final Stage stage = new Stage();
            
                    File file = dirChooser.showDialog(stage);
                    if (file != null) {
                        TF_LaufWerkBuchstabePlatte.setText(file.toString());
                    }

       
    }
   
   
    public void handle_CB_ArchivPlattenNrLöschen() throws FileNotFoundException, IOException {
   
    TRFR trfr = new TRFR();   

    String strNumPlatte = TF_NumPlatte.getText();   
  
    if ( (!strNumPlatte.isEmpty()) || (trfr.get_Int_F_Str(strNumPlatte) != 0) ) {   
          
           
            FIIO fiio = new FIIO();

            File original = new File(wotanHome + "\\" + dataFileName);
            File kopie = new File(wotanHome + "\\COPY_" + dataFileName);
           
            String strLine;
                      
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(original)));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(kopie)), (int) (long) original.length());       // unproblematisch wird immer auf 60 Mio beschränkt

            // Nur Zeilen einlesen welche nicht mit der Plattennummer übereinstimmen
            String[] tmpArray = new String[3];
           
            while((strLine = br.readLine()) != null){
                   
                    tmpArray = strLine.split(";");
                                       
                    if (!tmpArray[2].equals(strNumPlatte)) {
                        bw.write(strLine);
                        bw.newLine();  
                    }
                   
            }

            bw.close();
            br.close();

            fiio.DelFile(wotanHome, dataFileName);
            fiio.RenameFile(wotanHome, "COPY_" + dataFileName, dataFileName);
           
            fiio = null;
       
        } else {
            IBOX.message("ACHTUNG", "Plattennummer fehlt oder ist 0 !");
       
        }
   
        trfr = null;
    }
   
   
    public void handle_CB_Einlesen() throws FileNotFoundException, IOException {
   
            
        LB_STATUS.setText("Lese Platte");
          
        final Task<Void> pb = new Task<Void>() {
            //@Override
            public Void call() throws Exception {
                
                for (int t = 1; t <= 10; t++) {

                            if (t == 10) {
                                t = 1;
                            }
                                                       
                            updateProgress(t, 10);
                            Thread.sleep(1000);
                           
                        }
                    return null;         
                }
         };
       
        PB_STATUS.progressProperty().unbind();
        PB_STATUS.progressProperty().bind(pb.progressProperty());
       
        new Thread(pb).start();
      
       
       
        String strLW = TF_LaufWerkBuchstabePlatte.getText();
        File fileLW = new File(strLW);
        String strNumPlatte = TF_NumPlatte.getText();
      
       
        if ((strLW != "") && (strNumPlatte != "")) {
            // Instanzen erstellen
            FIIO fiio = new FIIO();
           
            // Variablen deklarieren und initalisieren
            String strLine = "";
            List<File> dataLst = new ArrayList<File>();
            List<String> extensionLst = new ArrayList<String>();
            Integer AnzExtensionEinträge = 0;
            String[] lineArray = new String[3];
            String strDirOrFile = "";
            String strExtension = "";
            String strMedia = "";
            Integer whatInLst = 0;
            File dataFile = new File(wotanHome + "\\" + dataFileName);
            String[] tmpArray = new String[3];
            String strFileName = "";
            Integer lenFile = 0;
            Integer lenExtension = 0;
            long lngDatSize = 0;

           
            // Verzeichnisse anlegen wenn sie noch nicht existieren
            fiio.BuildFolder(wotanHome);
           
            // Prüfen ob die Datendatei zu groß ist
            if (fiio.CheckFileExist(wotanHome, dataFileName) == true) {
                lngDatSize = dataFile.length();

            } else {
                fiio.BuildFile(wotanHome, dataFileName);
               
            }

                    
            // Größe des Datenfiles prüfen
            if (lngDatSize < 70000000) {
           
                // Alte Einträge werden gelöscht
                handle_CB_ArchivPlattenNrLöschen();

                // Zu prüfende Extensions einlesen
                extensionLst = fiio.ReadFileToList(wotanConfig, configFileName, true);

                // Wenn Extensions in der Liste
                if (extensionLst.isEmpty() == false) {


                    // FileBuffer deklarieren
                    FileWriter fw = new FileWriter(dataFile,true);
                    BufferedWriter bw = new BufferedWriter(fw);

                    // Extensionsliste abarbeiten
                    for (int i = 0; i < extensionLst.size(); i++) {


                        // Parameter aus Extensionlist auslesen
                        lineArray = extensionLst.get(i).split(";");
                        strExtension = lineArray[0];
                        strMedia = lineArray[1];
                        strDirOrFile = lineArray[2];
                        lenExtension = strExtension.length();
                       
                       
                       
                        // Prüfen was in Liste stehen soll
                        if (strDirOrFile.equalsIgnoreCase("Datei")) {
                            whatInLst = 1;
                        } else  {
                            whatInLst = 2;
                        }

                        // Einlesen der Daten in die Datendatei
                        dataLst = fiio.getFileDirList(fileLW, strExtension, whatInLst);
                        anzRows = dataLst.size();
                       
                        // Anhängen der Einträge der Platte
                        for (int j = 0; j < dataLst.size(); j++) {

                            strFileName = dataLst.get(j).getName();
                            lenFile = strFileName.length();
                           
                            propProg.setNum(j);
                           
                            // ggf. die Dateiendung entfernen
                            if ( strFileName.substring(lenFile - lenExtension).equals(strExtension)) {
                                strFileName = strFileName.substring(0, lenFile - lenExtension);
                            }

                            bw.write(strMedia + ";" + strFileName + ";" + strNumPlatte + ";" + "\r\n");

                        }

                    }

                    bw.close();
                   
                } else {
                    IBOX.message("ACHTUNG !", "Extensions fehlen, bitte erst festlegen und speichern !");
                }

            } else {
                IBOX.message("ACHTUNG !", "Maximum der Dateigröße erreicht, erst Platten löschen !");
            }
           
            fiio = null;
           
        } else {
            IBOX.message("ACHTUNG !", "Verzeichnis und Plattennummer müssen eingegeben werden, sonst läufs nicht !");
        }
       
        LB_STATUS.setText("Fertig");
        pb.cancel();
    }

}
```


----------



## kneitzel (4. Apr 2016)

Wenn ich das jetzt auf die Schnelle richtig gesehen habe, hast Du die eigentlichen Arbeiten im UI Thread. Somit kann die UI nicht neu gemalt werden und ist blockiert. Die Arbeiten selbst müssen in einen Thread, so dass der UI Thread nicht blockiert wird.


----------



## dzim (5. Apr 2016)

Also ich muss noch dazu anmerken, dass dein Code echt einen schlechten Stil hat. SonarQube oder ähnliche Tools würden die an vielen Stellen Anmerkungen machen. 
Da wir nicht wissen, was "Prog_Progress progProp" (AUGENKREBS!) genau macht, kann ich wie kneitzel auch nur vermuten, dass du auf dem UI-Thread arbeitest und dadurch erst die letzte Änderung des UIs tatsächlich erst angezeigt werden kann.

Schau dir dazu bitte erst einmal folgendes Tutorial an, wenn dir das nicht weiterhilft und du etwas mehr Code zu zeigen hast, schreib einfach noch mal.


----------



## VfL_Freak (5. Apr 2016)

Moin,



dzim hat gesagt.:


> Schau dir dazu bitte erst einmal folgendes Tutorial an


ääh  
Als da wäre ??????? 

Gruß Klaus


----------



## Jardcore (5. Apr 2016)

Hab das mal vor einiger Zeit auch gemacht, so ähnlich hatte ich das damals gestaltet...(gerade alles aus dem Kopf geschrieben, also keine Garantie, dass das genau so noch funktioniert)

```
public class Model {
    private Task updateTask;

    public Model() {
        startUpdateThread();
    }

    private void startUpdateThread() {
        updateTask = new Task<Void>() {
            @Override
            public Void call() {
                int max = 100;
                for(int count = 1; count <= max; count++) {
                     this.updateProgress(count, max);                
                }
            }
        };
        new Thread(updateTask).start();
    }

    public ReadOnlyDoubleProperty progressProperty() {
        return updateTask.progressProperty();
    }
}
```


```
public class View {
    @FXML
    private fxProgressBar;

    public View(Model model) {
        fxProgressBar.progressProperty().bind(model.progressProperty());
    }
}
```


----------



## kneitzel (5. Apr 2016)

@Jardcore: Das Problem dürfte aber nicht ein Thread sein, der die Progressbar anpasst. Das Problem ist aus meiner Sicht, dass er in public void handle_CB_Einlesen() erst so einen Thread startet um dann innerhalb des UI Threads die eigentliche Tätigkeit zu machen. Dies müsste er aus meiner Sicht auch nur in einen extra Thread packen und gut wäre es wohl.


----------



## dzim (5. Apr 2016)

Upps. Link vergessen:
https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm

Aber: Den habe ich schon erst vor kurzem gepostet. Im Forum suchen sollte auch manchmal etwas bringen. Aber Danke @VfL_Freak für den Hinweis!


----------



## VfL_Freak (5. Apr 2016)

Moin,

kein Problem 



dzim hat gesagt.:


> Aber: Den habe ich schon erst vor kurzem gepostet. Im Forum suchen sollte auch manchmal etwas bringen


Wie soll man denn etwas suchen, wenn man nicht weiß, was ??? 

Gruß Klaus


----------



## dzim (5. Apr 2016)

Wenn ich mir den Code vom Task auf GrepCode ansehe
http://grepcode.com/file/repo1.maven.org/maven2/net.java.openjfx.backport/openjfx-78-backport/1.8.0-ea-b96.1/javafx/concurrent/Task.java#Task.updateProgress(double,double)
sieht es so aus, als ob der Task sich schon darum kümmert, ob das Progress-Update auf dem UI-Thread stattfinden soll oder ob es asynchron (vergleichbar zu Platform#runLater) ausgeführt werden soll...


----------



## dzim (5. Apr 2016)

VfL_Freak hat gesagt.:


> Wie soll man denn etwas suchen, wenn man nicht weiß, was ???


Da hast du auch wieder recht. Point taken!


----------



## dzim (5. Apr 2016)

Wenn ich mir den Rest des Codes anschaue, sieht es so aus, als ob nur der kleinste (und vermutlich leichtgewichtigste) Teil tatächlich im Task gemacht wird. Die doppelt verschachtelte Schleife kommt erst noch. Ich denke hier liegt die Crux: Der Teil muss in den Task.
@Joob - überlege vielleicht nochmal, ob dein in Code gegossenes Konzept so wirklich tragbar ist. IMHO ist das generell zu viel Code im Controller. Extrahiere das ganze in eine eigene Task-Klasse, instanziiere sie dort und führe sie aus.


----------



## Joob (5. Apr 2016)

Erst mal schönen Dank für die Hinweise.

Ich denke ich werde wie dzim vorgeschlagen, Code aus dem Controller nehmen, insbesondere die Schleifen.
Ist halt mit den verscheidenen Tests so gewachsen, bin neu in Java und das ist alles zum üben.

Der Porgress Indicator auf Basis der Proterty wird sich da gut umsetzen lassen. Ich denke beim Thread und dem Binding wird es schwieriger, zumal dann wenn der Progressbar nicht wie jetzt einfach vor sich herläuft sonder konkret den Status anzeigen soll, aber dazu kann ich ggf. auch die Proterty verwenden. 

Ich probiers dann erst mal.


----------



## Joob (5. Apr 2016)

Was ich allerdings noch nicht verstehe ist folgendes:
Ich rufe eine Funktion auf z.B. handle_CB_Einlesen() innerhalb dieser starte ich einen Thread und lege eine Property fest. Also 2 Prozesse und ein spez. gespeicherter Wert, nämlich die Property. Ich binde den ProgressBar und erstelle einen ChangeListener für ProgressIndi mit Property. 

Also meine Fragen:

1. Wer zeichnet wann, wodurch die UI neu.
2. Beim Konzept des Bindings muss doch das Binding an Progressbar im ersten Thread handle_CB_Einlesen stehen.
3. Gibt es dazu eventuell eine Regel wer wann was macht. Schaubild,Erläuterung


----------



## dzim (5. Apr 2016)

1) Die UI zeichnet sich sozusagen selbst neu, wenn updates an bestimmten Properties vorgenommen werden (z.B. textProperty an Labels, oder eben dein progressProperty)

2) Das Binding sorgt *nur* für das verketten von Properties, so dass eben nicht immer Listener etc. notwendig sind. Wenn du ein Update (siehe 1.) von Process A ausführst, dann wird es auch von dort gestartet. Wenn A nicht der UI-Thread ist, wird eine Exception geworfen
In etwa so etwas hier:


> java.lang.IllegalStateException: Not on FX application thread;


Um das zu verhindern, gibt es Helper-Methoden in der Klasse "Platform". (Methode isFXThread oder so und runLater - die erste testet, ob man auf dem richtigen Thread is, die andere sorgt dafür, dass es auf dem richtigen Thread ausgeführt wird - kann man für Performance-Optimierung kombinieren, da es nur Sinn macht, #runLater auszuführen, wenn man *nicht* auf dem UI-Thread ist)

3) Die Regel ist: Alle Operationen, die länger dauern können (DB, Netzwerk, Platte, komplizierte Berechnungen...), werden *nicht* auf dem UI-Thread gemacht, sondern mit Threads und Tasks oder Services explizit auf eigenen Threads ausgeführt, können entweder von dort per Platform#runLater updates der UI explizit anstossen, oder - wie im Fall vom progress - implizit über Binding.

------------

Ich denke, dein Problem ist primär nicht, dass du Tasks nicht verwendest, sondern nur, dass du es falsch machst und nicht die eigentliche Last im Task machst.

------------ EDIT ------------

Schau mal in den Thread hier:
http://www.java-forum.org/thema/ui-thread-und-db-thread-trennen-um-z-b-ladebalken-anzuzeigen.172411/

Dort wurde im Prinzip ein gleichartiges Problem besprochen.


----------



## Joob (6. Apr 2016)

Schönen Dank, das mit den langwierigen Prozessen bau ich um, so wie du vorgeschlagen hast.
Allerdings ist mir noch nicht genau klar was UI ist.(UserInterface) aber wo und wann wird dies gestartet.

public void handle_CB_Einlesen() throws FileNotFoundException, IOException {
        LB_STATUS.setText("Lese Platte");

Es kann ja nicht die oben dargestellte Funktions sein, denn wie dzim sagt würde sonst die LB_STATUS aktualisiert.  Also stellt sich mir die Frage wann wird die UI gestartet, wie kann ich in dem Prozess Befelhle platzieren die dann neu gezeichnet werden, wie setText oder Binding oder Property mit Changelistener.


----------



## Jardcore (6. Apr 2016)

Zu deiner Frage was mit UI gemeint ist, ja UserInterface... aber eigentlich ist hier der application thread gemeint: https://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html schaue hier bei Threading


----------



## dzim (6. Apr 2016)

Es gibt da keine absolut einheitliche Nomenklatur bezüglich des Threads.
UI-Thread, Application-Thread und Main-Thread sind durchaus gebräuchlich, aber ich verwende meist den ersten, während du in Oracles Doku meist den zweiten (mit dem Präfix "JavaFX") findest.


----------



## Joob (9. Apr 2016)

Ich habe nun die Schleifen in eine eigene Klasse ausgelagert. Das lief zwar aber der Changlistener bekam die Propertieänderung nicht mit.

Hatte aber auch hinsichtlich der Neuzeichnung keinen Effekt beim ProgBar und dem Textfeld.

Dann habe ich alles in zwei Tasks ausgelagert, also einmal ProgBar und dann die eigentlichen Arbeiten.
Das lief dann, allerdings ließ sich der Thread nicht beenden.

Das läßt mich darauf schließen das wenn ich den Thread ProgBar innerhalb der Anweisungen also handle... starte diese erst von der GUI diesen untergeordnet ansieht und erst am Ende die Anweisungen abarbeitet.

Das wiederspricht aber den Beispielen in den Foren.

Wie bekomme ich das hin, das der Thread welcher ja definitiv nebenläufig funktioniert den ProgBar neu zeichnet. Aufgrund euerer Aussagen zum ApplicationThread habe ich mal was mit Platform getestet hatte aber auch keinen Erfolg. Das hatte ich erst mal mit dem Label getestet.

Ich versteh das nicht, ist es nicht so das das Binding auch automatisch zu einer Aktualisierung des Controls führen soll. 

Was mach ich falsch, kann man im Thread etwas unternehmen um eine Neuzeichnung zu erzwingen ?

Ich hab jetzt mal keinen Code geschickt, weil durch das ganze Umgestelle das bestimmt nicht mehr den Ansprüchen entspricht.


----------



## dzim (11. Apr 2016)

So. Ich hab jetzt mal ein Minimal-Beispiel gebaut, was zeigen soll, wie man es etwa machen kann.
Als erstes habe ich deine UI nachgebaut, aber nur den "start"-Button und die Progress-Controlls aktiv:

```
<?xml version="1.0" encoding="UTF-8"?>
<!--
    Do not edit this file it is generated by e(fx)clipse from ../src/application/ProgressTest.fxgraph
-->

<?import java.lang.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.GridPane?>

<GridPane xmlns:fx="http://javafx.com/fxml" fx:controller="application.ProgressTestController" alignment="CENTER" vgap="5" hgap="5">

    <Label text="HD Drive" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
    <TextField promptText="pick drive" disable="true" GridPane.columnIndex="1" GridPane.rowIndex="0" GridPane.columnSpan="2"/>
    <Button text="Select ..." disable="true" GridPane.columnIndex="3" GridPane.rowIndex="0"/>
    <Label text="Drive No." GridPane.columnIndex="0" GridPane.rowIndex="1"/>
    <TextField promptText="01" disable="true" GridPane.columnIndex="1" GridPane.rowIndex="1" GridPane.columnSpan="3"/>
    <Button text="Delete Archive" disable="true" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2" GridPane.halignment="LEFT"/>
    <Button fx:id="readButton" text="Start Reading" onAction="#handleReadButton" GridPane.columnIndex="2" GridPane.rowIndex="2" GridPane.columnSpan="2" GridPane.halignment="RIGHT"/>
    <ProgressBar fx:id="progressBar" GridPane.columnIndex="0" GridPane.rowIndex="3" GridPane.columnSpan="4">
        <maxWidth><Double fx:constant="MAX_VALUE" /></maxWidth>
        <GridPane.margin>
            <Insets top="10"/>
        </GridPane.margin>
    </ProgressBar>
    <ProgressIndicator fx:id="progressIndicator" GridPane.columnIndex="0" GridPane.rowIndex="4" GridPane.columnSpan="4">
        <GridPane.margin>
            <Insets top="10"/>
        </GridPane.margin>
    </ProgressIndicator>
</GridPane>
```
Dazu gibt es den angebundenen Controller:

```
package application;

import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.value.ChangeListener;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;

public class ProgressTestController {
   
    @FXML
    private Button readButton;
    @FXML
    private ProgressBar progressBar;
    @FXML
    private ProgressIndicator progressIndicator;
   
    private MyFancyService service = null;
   
    @FXML
    public void initialize() {
       
        progressBar.setProgress(0.0);
        progressIndicator.setProgress(0.0);
       
        service = new MyFancyService(100L);
        service.progressProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
            progressBar.setProgress(newValue.doubleValue());
            progressIndicator.setProgress(newValue.doubleValue());
        });
    }
   
    @FXML
    public void handleReadButton(ActionEvent event) {
        if (service.isRunning()) {
            System.out.println("Already running. Nothing to do.");
        } else {
            service.reset();
            progressBar.setProgress(0.0);
            progressIndicator.setProgress(0.0);
            service.start();
        }
    }
   
    private static class MyFancyService extends Service<Long> {
       
        private LongProperty countTo = new SimpleLongProperty(0L);
        private LongProperty current = new SimpleLongProperty(0L);
       
        public MyFancyService(long countTo) {
            this.countTo.set(countTo);
        }
       
        @SuppressWarnings("unused")
        public LongProperty countToProperty() {
            return countTo;
        }
       
        @SuppressWarnings("unused")
        public LongProperty currentProperty() {
            return current;
        }
       
        @Override
        protected Task<Long> createTask() {
            return new Task<Long>() {
                @Override
                protected Long call() throws Exception {
                    current.set(0L);
                    try {
                        for (int i = 0; i <= countTo.get(); i++) {
                            if (isCancelled())
                                break;
                            current.set(i);
                            updateProgress(current.get(), countTo.get());
                            Thread.sleep(100);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return current.get();
                }
            };
        }
    }
}
```
Die beiden Progress-Controls werden wie erwartet  hochgezählt. Im Prinzip könntest du nun den Code in der Schleife durch den Einlese-Code, bzw. deine verschachtelten Schleifen ersetzen und es müsste gehen, solange du zwischendurch ordentlich den Fortschritt dokumentierst:
"updateProgress((double|long) currentValue, (double|long) maxValue);"


----------



## Joob (14. Apr 2016)

Erst mal schönen Dank. Ich bau jetzt mal um und versuche zu verstehen was genau da passiert.

Eine Frage habe ich noch, und ich hoffe die ist zeigt nicht das volle Maß meiner Unkenntnis.
Du hast deinen Service innerhalb der Klasse gebaut, also eine Klasse in einer Klasse. Ich habe gedacht das kann man nicht machen innerhalb einer Klasse gäbe es nur Funktionen.

Das mit dem Service habe ich jetzt in einem Artikel nachgelesen und auch die Zusammenhänge mit dem Application Thread. Ich programmier das jetzt mal, dann wird einem meistens alles klarer.


----------



## dzim (15. Apr 2016)

Das Konzept nennt sich "innere Klassen". Es gibt ein paar wenige Situationen, in denen man so etwas verwenden kann - wir in solchen Show Cases.
Grundsätzlich sollte man deren Verwendung aber auf ein absolutes Minimum reduzieren.

Dokumentation dazu:
https://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html
https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

Ich hoffe, das macht es etwas klarer.


----------



## Joob (17. Apr 2016)

Ich habe es jetzt fast,
Thread funktioniert, 

innerhalb des Threads lasse ich die Funktionen (löschen und lesen) abarbeiten.

Fortschritt läßt sich aufgrund der Art der Funktion nur mit großem Rechenaufwand prozentual darstellen. Ich müßte sonst die Suche zweimal laufen lassen um im Vorfeld festzustellen was gefunden wird um den Wert für 100 % festzulegen. Muss aber auch nicht sein es reicht wenn der User sieht das was passiert.

Aber nun zu dem neuen Problem.
Du hast bestimmt gesehen das ich für die Messageboxen eine Kasse nutze IBOX. Wenn ich die Funktion Platte lesen innerhalb des Threads ausführe werden die Messageboxen nicht mehr angezeigt. Außerhalb des Threads werden Sie angezeigt. Ich könnte das Problem natürlich umgehen indem ich vor dem Start des Threads alle Eingaben prüfe, aber ich verstehe nicht warum das so ist.
Wenn ich debugger, wird die Kasse IBOX mit der Funktion Message aufgefuren und ohne exception passiert dann nichts mehr.

  Auszug aus meiner Klasse IBOX

   /*******************************************************************
    * Messagebox
    *******************************************************************/
   public static void message(String title, String message) {

     Stage window = new Stage();

     //
     window.initModality(Modality.APPLICATION_MODAL);
     window.setTitle(title);
     window.setMinWidth(300);
     window.setHeight(200);     

wenn     Stage window = new Stage(); ausgeführt wird erscheint im Debugger untenstehendes Fenster und es dreht sich im Kreis.




g Option habe ich gesetzt. Obwohl ich nicht weiß was das soll, hatte aber keinen Effect.

Warum kann ich innerhalb des Threads nun kein Messeagefenster mehr anzeigen ?

Ich hab mal den Code angehängt. Wie du siehst sind die Funktionen noch im Controler mach ich später besser, ich wollte die Anzahl der Problem überschaubar halten.


```
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package wotan;

import SYS_APPSBAS.FIIO;
import SYS_APPSBAS.IBOX;
import SYS_APPSBAS.TRFR;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;


/**
* FXML Controller class
*
* @author Jupp
*/
public class S1_1_PlatteController implements Initializable {

     
    @FXML private TextField TF_LaufWerkBuchstabePlatte;
    @FXML private TextField TF_NumPlatte;
    @FXML private ProgressIndicator PI_STATUS;

 
    // Variablen deklarieren und initalisieren
    String userHome = System.getProperty("user.home");
    String wotanHome = userHome + "\\WOTAN";
    String dataFileName = "WOTANDATEN.CSV";
    String wotanConfig = wotanHome + "\\config";
    String configFileName = "WOTANCONFIG.CSV";

    private Service<Void> threadReadLW;
    private Service<Void> threadDelTagsInArc;
   
  
    // Initializes the controller class.
    @Override
    public void initialize(URL url, ResourceBundle rb) {
     
    }   

   
    public void handle_CB_Choose() {
   
        final DirectoryChooser dirChooser = new DirectoryChooser();
        final Stage stage = new Stage();

        File file = dirChooser.showDialog(stage);
        if (file != null) {
            TF_LaufWerkBuchstabePlatte.setText(file.toString());
        }
       
    }
   
   
    public void handle_CB_ArchivPlattenNrLöschen() throws FileNotFoundException, IOException {
       
        threadDelTagsInArc = new Service<Void>() {

            @Override
            protected Task<Void> createTask() {
                return new Task<Void>() {

                    @Override
                    protected Void call() throws Exception {
                        fun_ArchivPlatteLöschen();
                        return null;
                    }
                };
            }  
        };


        threadDelTagsInArc.setOnSucceeded((WorkerStateEvent event) -> {
            PI_STATUS.progressProperty().unbind();
            PI_STATUS.setProgress(1.0);
        });

        PI_STATUS.progressProperty().bind(threadDelTagsInArc.progressProperty());

        threadDelTagsInArc.restart();


    }
   
   
    public void handle_CB_Einlesen(ActionEvent event) throws FileNotFoundException, IOException {
   
        threadReadLW = new Service<Void>() {

            @Override
            protected Task<Void> createTask() {
                return new Task<Void>() {
               
                    @Override
                    protected Void call() throws Exception {
                        fun_Einlesen();
                        return null;
                    }
                };
            }  
       };
        
       
        threadReadLW.setOnSucceeded((WorkerStateEvent event1) -> {
            PI_STATUS.progressProperty().unbind();
            System.out.println("Platte eingelesen");
            PI_STATUS.setProgress(1.0);

        });

        PI_STATUS.progressProperty().bind(threadReadLW.progressProperty());
       
        threadReadLW.restart();

    }


    public void fun_ArchivPlatteLöschen() throws IOException{
       
        TRFR trfr = new TRFR();   

        String strNumPlatte = TF_NumPlatte.getText();   

        if ( (!strNumPlatte.isEmpty()) || (trfr.get_Int_F_Str(strNumPlatte) != 0) ) {   
           

                FIIO fiio = new FIIO();

                File original = new File(wotanHome + "\\" + dataFileName);
                File kopie = new File(wotanHome + "\\COPY_" + dataFileName);

                String strLine;

                BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(original)));
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(kopie)), (int) (long) original.length());       // unproblematisch wird immer auf 60 Mio beschränkt

                // Nur Zeilen einlesen welche nicht mit der Plattennummer übereinstimmen
                String[] tmpArray = new String[3];

                while((strLine = br.readLine()) != null){

                        tmpArray = strLine.split(";");

                        if (!tmpArray[2].equals(strNumPlatte)) {
                            bw.write(strLine);
                            bw.newLine();  
                        }
                }

                bw.close();
                br.close();

                fiio.DelFile(wotanHome, dataFileName);
                fiio.RenameFile(wotanHome, "COPY_" + dataFileName, dataFileName);

                fiio = null;

            } else {
                IBOX.message("ACHTUNG", "Plattennummer fehlt oder ist 0 !");
            }

        trfr = null;

    }
   
   
    public void fun_Einlesen() throws IOException{
       
        
        String strLW = TF_LaufWerkBuchstabePlatte.getText();
        File fileLW = new File(strLW);
        String strNumPlatte = TF_NumPlatte.getText();
      
       
        if ((!"".equals(strLW)) && (!"".equals(strNumPlatte))) {
            // Instanzen erstellen
            FIIO fiio = new FIIO();
           
            // Variablen deklarieren und initalisieren
            String strLine = "";
            List<File> dataLst = new ArrayList<File>();
            List<String> extensionLst = new ArrayList<String>();
            Integer AnzExtensionEinträge = 0;
            String[] lineArray = new String[3];
            String strDirOrFile = "";
            String strExtension = "";
            String strMedia = "";
            Integer whatInLst = 0;
            File dataFile = new File(wotanHome + "\\" + dataFileName);
            String[] tmpArray = new String[3];
            String strFileName = "";
            Integer lenFile = 0;
            Integer lenExtension = 0;
            long lngDatSize = 0;
            int anzRows = 0;


           
            // Verzeichnisse anlegen wenn sie noch nicht existieren
            fiio.BuildFolder(wotanHome);
           
            // Prüfen ob die Datendatei zu groß ist
            if (fiio.CheckFileExist(wotanHome, dataFileName) == true) {
                lngDatSize = dataFile.length();

            } else {
                fiio.BuildFile(wotanHome, dataFileName);
               
            }

                    
            // Größe des Datenfiles prüfen
            if (lngDatSize < 70000000) {
               
                // Alte Einträge werden gelöscht
                fun_ArchivPlatteLöschen();

               
                // Zu prüfende Extensions einlesen
                extensionLst = fiio.ReadFileToList(wotanConfig, configFileName, true);

                // Wenn Extensions in der Liste
                if (extensionLst.isEmpty() == false) {


                    // FileBuffer deklarieren
                    FileWriter fw = new FileWriter(dataFile,true);
                    BufferedWriter bw = new BufferedWriter(fw);

                    // Extensionsliste abarbeiten
                    for (int i = 0; i < extensionLst.size(); i++) {


                        // Parameter aus Extensionlist auslesen
                        lineArray = extensionLst.get(i).split(";");
                        strExtension = lineArray[0];
                        strMedia = lineArray[1];
                        strDirOrFile = lineArray[2];
                        lenExtension = strExtension.length();
                       
                       
                        // Prüfen was in Liste stehen soll
                        if (strDirOrFile.equalsIgnoreCase("Datei")) {
                            whatInLst = 1;
                        } else  {
                            whatInLst = 2;
                        }

                        // Einlesen der Daten in die Datendatei
                        dataLst = fiio.getFileDirList(fileLW, strExtension, whatInLst);
                        anzRows = dataLst.size();
                       
                        // Anhängen der Einträge der Platte
                        for (int j = 0; j < anzRows; j++) {

                            strFileName = dataLst.get(j).getName();
                            lenFile = strFileName.length();

                           
                            // ggf. die Dateiendung entfernen
                            if ( strFileName.substring(lenFile - lenExtension).equals(strExtension)) {
                                strFileName = strFileName.substring(0, lenFile - lenExtension);
                            }

                            bw.write(strMedia + ";" + strFileName + ";" + strNumPlatte + ";" + "\r\n");

                        }

                    }

                    bw.close();
                   
                } else {
                    IBOX.message("ACHTUNG !", "Extensions fehlen, bitte erst festlegen und speichern !");
                }

            } else {
                IBOX.message("ACHTUNG !", "Maximum der Dateigröße erreicht, erst Platten löschen !");
            }
           
            fiio = null;
           
        } else {
            IBOX.message("ACHTUNG !", "Verzeichnis und Plattennummer müssen eingegeben werden, sonst läufs nicht !");
        }
    }
}
```


----------



## Joob (17. Apr 2016)

Hier noch der Code für das FXML:


```
<?xml version="1.0" encoding="UTF-8"?>

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

<AnchorPane id="AnchorPane" fx:id="CB_ArchivPlattenNrLöschen" prefHeight="351.0" prefWidth="563.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="wotan.S1_1_PlatteController">
   <children>
      <GridPane hgap="10.0" prefHeight="145.0" prefWidth="551.0" vgap="10.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="12.0" AnchorPane.topAnchor="30.0">
        <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <Button fx:id="CB_Choose" mnemonicParsing="false" onAction="#handle_CB_Choose" prefHeight="25.0" prefWidth="168.0" text="Wähle" GridPane.columnIndex="2" />
            <Label prefHeight="17.0" prefWidth="174.0" text="Laufwerkbuchstabe der Platte :">
               <GridPane.margin>
                  <Insets left="10.0" />
               </GridPane.margin>
            </Label>
            <TextField fx:id="TF_LaufWerkBuchstabePlatte" editable="false" prefHeight="25.0" prefWidth="106.0" GridPane.columnIndex="1" />
            <Button fx:id="CB_ArchivPlattenNrLöschen" mnemonicParsing="false" onAction="#handle_CB_ArchivPlattenNrLöschen" prefHeight="25.0" prefWidth="167.0" text="Archiv für Platte löschen" GridPane.columnIndex="2" GridPane.rowIndex="1" />
            <TextField fx:id="TF_NumPlatte" prefHeight="25.0" prefWidth="96.0" promptText="01" GridPane.columnIndex="1" GridPane.rowIndex="1" />
            <Button fx:id="CB_Einlesen" mnemonicParsing="false" onAction="#handle_CB_Einlesen" prefHeight="25.0" prefWidth="169.0" text="Einlesen" GridPane.columnIndex="2" GridPane.rowIndex="2" />
            <Label prefHeight="17.0" prefWidth="174.0" text="Nummer der Platte :" GridPane.rowIndex="1">
               <GridPane.margin>
                  <Insets left="10.0" />
               </GridPane.margin>
            </Label>
            <HBox fx:id="B_BUTTONS" prefHeight="39.0" prefWidth="150.0" spacing="30.0">
               <padding>
                  <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
               </padding>
               <GridPane.margin>
                  <Insets />
               </GridPane.margin>
            </HBox>
         </children>
         <padding>
            <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
         </padding>
      </GridPane>
      <VBox fx:id="B_STATUS" layoutX="14.0" layoutY="186.0" prefHeight="155.0" prefWidth="534.0" spacing="20.0" AnchorPane.leftAnchor="14.0">
         <children>
            <ProgressIndicator fx:id="PI_STATUS" prefHeight="143.0" prefWidth="524.0" progress="0.0" />
         </children>
         <padding>
            <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
         </padding>
      </VBox>
   </children>
</AnchorPane>
```


----------



## Joob (18. Apr 2016)

Noch eine Frage :

Ich habe jetzt den Controller überarbeitet, dabei die Prüfungen vor den Thread gebaut. Läuft soweit .
Dann habe ich die Funktionen Del und Einlesen in eine separate Klasse ausgelagert.

Zuvor hatte ich die Funktion privat gemacht, wieso lagert man die Funktionen in eine separte Klasse aus, sie werden doch nur in diesem Controller gebraucht.

Für mich war das ne ganz gute Übung mit einem Constructor, aber wenn ich die Funktionen private deklariere sollte die App doch sicherer sein, oder ?

Ich habe überigens auch den Variablenblock userHome usw. privat final gemacht, ist das OK so.


----------



## Joob (18. Apr 2016)

Ich bin soweit und pooste noch mal den abschließenden Code. 
Wie du siehst habe ich hinsichtlich der Funktionen beide Varianten und bin noch unentschlossen was ich wie verwende.

Ich hab im Kontroller einiges privat gemacht und frage mich warum ich die Funktionen aus dem Controller nehmen soll und eine eigene Klasse verwenden soll, wo sie doch nur im Controler verwendet werden. Außer dem ist die App doch durch die Deklaration Private sicherer, oder ?



```
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package wotan;

import SYS_APPSBAS.FIIO;
import SYS_APPSBAS.IBOX;
import SYS_APPSBAS.TRFR;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;


/**
* FXML Controller class
*
* @author Jupp
*/
public class S1_1_PlatteController implements Initializable {

     
    @FXML private TextField TF_LaufWerkBuchstabePlatte;
    @FXML private TextField TF_NumPlatte;
    @FXML private ProgressIndicator PI_STATUS;

 
    // Variablen deklarieren und initalisieren
    private final String userHome = System.getProperty("user.home");
    private final String wotanHome = userHome + "\\WOTAN";
    private final String dataFileName = "WOTANDATEN.CSV";
    private final String wotanConfig = wotanHome + "\\config";
    private final String configFileName = "WOTANCONFIG.CSV";

    private Service<Void> threadReadLW;
    private Service<Void> threadDelTagsInArc;
   
  
    // Initializes the controller class.
    @Override
    public void initialize(URL url, ResourceBundle rb) {
     
    }   

   
    public void handle_CB_Choose() {
   
        final DirectoryChooser dirChooser = new DirectoryChooser();
        final Stage stage = new Stage();

        File file = dirChooser.showDialog(stage);
        if (file != null) {
            TF_LaufWerkBuchstabePlatte.setText(file.toString());
        }
       
    }
   
   
    public void handle_CB_ArchivPlattenNrLöschen() throws FileNotFoundException, IOException {
    
        TRFR trfr = new TRFR();   

        String strNumPlatte = TF_NumPlatte.getText();   

        if ( (!strNumPlatte.isEmpty()) || (trfr.get_Int_F_Str(strNumPlatte) != 0) ) {   


       
            threadDelTagsInArc = new Service<Void>() {

                @Override
                protected Task<Void> createTask() {
                    return new Task<Void>() {

                        @Override
                        protected Void call() throws Exception {
                            S1_1_PlatteJobs jobDel = new S1_1_PlatteJobs(userHome, wotanHome, dataFileName, wotanConfig, configFileName);
                            jobDel.fun_ArchivPlatteLöschen(strNumPlatte);
                            jobDel = null;
                           
                            //fun_ArchivPlatteLöschen(strNumPlatte);
                            return null;
                        }
                    };
                }  
            };


            threadDelTagsInArc.setOnSucceeded((WorkerStateEvent event) -> {
                PI_STATUS.progressProperty().unbind();
                PI_STATUS.setProgress(1.0);
            });

            PI_STATUS.progressProperty().bind(threadDelTagsInArc.progressProperty());

            threadDelTagsInArc.restart();
           
        } else {
            IBOX.message("ACHTUNG", "Plattennummer fehlt oder ist 0 !");
        }

        trfr = null;

    }
   
   
    public void handle_CB_Einlesen(ActionEvent event) throws FileNotFoundException, IOException {
   
        FIIO fiio = new FIIO();
       
        String strLW = TF_LaufWerkBuchstabePlatte.getText();
        String strNumPlatte = TF_NumPlatte.getText();
        List<String> extensionLst = new ArrayList<String>();
        long lngDatSize = 0;
       
        if ((!"".equals(strLW)) && (!"".equals(strNumPlatte))) {

            // Prüfen ob die Datendatei vorhanden ist, sonst anlegen
            if (fiio.CheckFileExist(wotanHome, dataFileName) == false) {
               
                // Verzeichnisse und Datei anlegen wenn sie noch nicht existieren
                fiio.BuildFolder(wotanHome);
                fiio.BuildFile(wotanHome, dataFileName);
               
            }
           
            File dataFile = new File(wotanHome + "\\" + dataFileName);
            lngDatSize = dataFile.length();

           
            // Größe des Datenfiles prüfen
            if (lngDatSize < 70000000) {
       
                // Zu prüfende Extensions einlesen
                extensionLst = fiio.ReadFileToList(wotanConfig, configFileName, true);

                // Wenn Extensions in der Liste
                if (extensionLst.isEmpty() == false) {
               
                    threadReadLW = new Service<Void>() {

                        @Override
                        protected Task<Void> createTask() {
                            return new Task<Void>() {

                                @Override
                                protected Void call() throws Exception {
                                    fun_Einlesen(strLW, strNumPlatte, dataFile);
                                    return null;
                                }
                            };
                        }  
                    };


                    threadReadLW.setOnSucceeded((WorkerStateEvent event1) -> {
                        PI_STATUS.progressProperty().unbind();
                        System.out.println("Platte eingelesen");
                        PI_STATUS.setProgress(1.0);

                    });

                    PI_STATUS.progressProperty().bind(threadReadLW.progressProperty());

                    threadReadLW.restart();
                   
                   
                } else {
                    IBOX.message("ACHTUNG !", "Extensions fehlen, bitte erst festlegen und speichern !");
                }
                   

            } else {
                IBOX.message("ACHTUNG !", "Maximum der Dateigröße erreicht, erst Platten löschen !");
            }
           
        } else {
            IBOX.message("ACHTUNG !", "Verzeichnis und Plattennummer müssen eingegeben werden, sonst läufs nicht !");
        }
       
        fiio = null;
       
    }


    private void fun_ArchivPlatteLöschen(String strNumPlatte) throws IOException{

        FIIO fiio = new FIIO();

        File original = new File(wotanHome + "\\" + dataFileName);
        File kopie = new File(wotanHome + "\\COPY_" + dataFileName);

        String strLine;

        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(original)));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(kopie)), (int) (long) original.length());       // unproblematisch wird immer auf 60 Mio beschränkt

        // Nur Zeilen einlesen welche nicht mit der Plattennummer übereinstimmen
        String[] tmpArray = new String[3];
       
        while((strLine = br.readLine()) != null){

            tmpArray = strLine.split(";");

            if (tmpArray.length == 3) {                                         // Prüfen ob der Tag korrekt ist, indem er 3 Zeilen aufweist

                if (!tmpArray[2].equals(strNumPlatte)) {
                    bw.write(strLine);
                    bw.newLine();  
                }

            }
        }

        bw.close();
        br.close();

        fiio.DelFile(wotanHome, dataFileName);
        fiio.RenameFile(wotanHome, "COPY_" + dataFileName, dataFileName);

        fiio = null;

   }
   
   
    private void fun_Einlesen(String strLW, String strNumPlatte, File dataFile) throws IOException{
        // Instanzen erstellen
        FIIO fiio = new FIIO();

        // Variablen deklarieren und initalisieren
        String strLine = "";
        List<File> dataLst = new ArrayList<File>();
        List<String> extensionLst = new ArrayList<String>();
       
        File fileLW = new File(strLW);
        Integer AnzExtensionEinträge = 0;
        String[] lineArray = new String[3];
        String strDirOrFile = "";
        String strExtension = "";
        String strMedia = "";
        Integer whatInLst = 0;
        String[] tmpArray = new String[3];
        String strFileName = "";
        Integer lenFile = 0;
        Integer lenExtension = 0;
        int anzRows = 0;
        extensionLst = fiio.ReadFileToList(wotanConfig, configFileName, true);
       
        // Alte Einträge werden gelöscht
        fun_ArchivPlatteLöschen(strNumPlatte);

       // FileBuffer deklarieren
        FileWriter fw = new FileWriter(dataFile,true);
        BufferedWriter bw = new BufferedWriter(fw);

        // Extensionsliste abarbeiten
        for (int i = 0; i < extensionLst.size(); i++) {

            // Parameter aus Extensionlist auslesen
            lineArray = extensionLst.get(i).split(";");
            strExtension = lineArray[0];
            strMedia = lineArray[1];
            strDirOrFile = lineArray[2];
            lenExtension = strExtension.length();

            // Prüfen was in Liste stehen soll
            if (strDirOrFile.equalsIgnoreCase("Datei")) {
                whatInLst = 1;
            } else  {
                whatInLst = 2;
            }
           
            // Einlesen der Daten in die Datendatei
            dataLst = fiio.getFileDirList(fileLW, strExtension, whatInLst);
            anzRows = dataLst.size();

            // Anhängen der Einträge der Platte
            for (int j = 0; j < anzRows; j++) {

                strFileName = dataLst.get(j).getName();
                lenFile = strFileName.length();


                // ggf. die Dateiendung entfernen
                if ( strFileName.substring(lenFile - lenExtension).equals(strExtension)) {
                    strFileName = strFileName.substring(0, lenFile - lenExtension);
                }

                bw.write(strMedia + ";" + strFileName + ";" + strNumPlatte + ";" + "\r\n");

            }

        }

        bw.close();
        fiio = null;
   }
}
```


----------



## dzim (19. Apr 2016)

Also. Meine erste (und grösste) Kritik: Die Variablen- und Methoden- und Klassenbenennung. Zu sagen, diese wäre fernab der Java-Konventionen, wäre noch ein milde Untertreibung. Generell sind Unterstriche in Methoden und Klassennamen ein No-Go. In Variablen nur bei *static final* erforderlich.
Klassen: CamelCase (beginnt mit Grossbuchstaben und wenn mehrere "Worte" enthalten sind, werden diese ebenfalls gross geschrieben).
Methoden: camelCase (wie Klassen, nur beginnen sie mit einem kleinen Buchstaben).
Variablen: 1) *static final*: VARIABLEN_NAME, 2) ansonsten, wie Methoden.

Und generell sprechende Namen, aber auf das notwendige reduziert.

Eher mein privater Geschmack ist, dass man nicht "Denglish" schreibt. Java und die meisten APIs sind nun mal eher in "Englisch" (sofern man da von einer Sprache sprechen kann). Ich präferiere generell englische Begriffe.

Zum Code: Nimm's mir nicht übel, aber das ist zu viel - und zu unübersichtlich - als dass ich mir jedes Detail ansehe.
Was mir auffällt: du verwendest eigentlich nie *updateProgress* - also wirst du wohl nie einen Fortschritt sehen, korrekt?
Dann: Du machst per Methodenaufruf von *handle_CB_ArchivPlattenNrLöschen* (*_Schauder_*) eine neue Service-Instanz (*threadDelTagsInArc* -> besser: deleteTagsInArcService). Damit führst du das Prinzip ad absurdum. Schau noch mal in meinen Code: Ich checke erst, ob er (noch) läuft, und wenn nicht, resette ich ihn und starte ihn neu.

Was du im Controller als private Methoden hast ist generell schon ok, aber...
Du machst dort eigentlich nur nich-GUI-relevante Aufgaben. Das könnte alles in einer separaten Klasse, die Service erweitert, gemacht werden. In deiner GUI würde dann nur noch eine Instanz des Service angelegt und gestartet werden und das war's. Wenn du Daten aus dem Service anzeigen möchtest, dann lege dir dort Properties an, an die du dich in der GUI anhängen kannst.

Was deine Stage-Frage angeht. Ich versteh' sie leider nicht.


----------



## Joob (20. Apr 2016)

Hallo, da hab noch was zu tun.
Ich versuch das mal umzubauen.

Momentan kann ich einen Fortschritt beim Einlesen anzeigen. Ich mach das an dem Schleifendurchlauf der Extensions fest.

Wenn ich fertig bin würd ich dir das gerne mal zeigen. Ich schick dir auch das ganze Projekt dann kannst du schneller ausprobieren, falls dir das recht ist. Allerdings verwende ich zum Drucken Jasper. Aber das spielt ja für das Einlesen keine Rolle. Nur vorab zur Entschuldigung: Das Projekt enthält einige Element die ich mir beim Üben zusammengebaut habe, wahrscheinlich findest du die schrecklich. Ich überarbeite die noch wenn ich das Einlesen fertig habe und freue mich über Kritik.

Könnte aber ein paar Tage dauern, ich kann nur abends spät dran bauen.


----------



## Joob (20. Apr 2016)

Ach so die Sach mit der Stage hat sich erledigt. 
Innerhalb des Threads werden keine Fenster dargestellt, genausowenig wie GUI - Elemente aktualisiert werden, hab ich dann außerhalb gestellt und es läuft OK. Wurde mir erst später klar, interessanterweise führt das aber zu einem Absturz ohne Exception.


----------



## Joob (20. Apr 2016)

Da ist noch was. Das Ding mit den Unterstrichen.
Ich habe die Windows so benannt um in der Projektansicht eine Ordnung zu bekommen. 
Also für das erste Fenster S1 für ein von diesem Fenster aufgerufenes Fenster S1_1 für ein weiteres welches über einen anderen Button aufgerufen wird S1_2, dann ne Bezeichnung dran und ggf. Controller. Sieht im Projekt total übersichtlich aus. Was soll ich als Trennzeichen nehmen wenn nicht den Unterstrich, geht Minus.


----------

