Change text in a JTextArea at runtime

4

Good morning everyone,

I have a backup routine that should write the result to a JTextArea , at runtime.

The copy works, the problem is that the window containing JTextArea gets stuck and does not append the text every time a file or directory is copied. It only writes after the copy ends completely. I need you to write the status file to file.

Can anyone help me? It would be of great value, already have a time that I am breaking the head and do not find solution to similar problem on the net. Thank you.

Here are the codes:

private void executarActionPerformed(java.awt.event.ActionEvent evt) {                                         
    Path origem = Paths.get("\\apolo\sobe");
    Path destino = Paths.get("\\hermes\DCPD\BKP-SOBE\teste5");

    try {
        jtaRetorno.append("Executando Cópia");
        Files.walkFileTree(origem, new CopyDir(origem, destino, jtaRetorno));

    } catch (IOException ex) {
        Logger.getLogger(FrmExecutaCopia.class.getName()).log(Level.SEVERE, null, ex);
    }
}

The class CopyDir has the following code:

public class CopyDir extends SimpleFileVisitor<Path> {
    private final Path origem;
    private final Path destino;
    private final JTextArea retorno;

    // Construtor com origem e destino
    public CopyDir(Path origem, Path destino, JTextArea retorno) {
        this.origem = origem;
        this.destino = destino;
        this.retorno = retorno;
    }

    // Usado para criar o diretorio
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
        throws IOException {
        copiaPath(dir);
        retorno.append("Diretorio "+dir.toString()+" criado.\n");
        System.out.println("Diretorio "+dir.toString()+" criado.\n");
        return FileVisitResult.CONTINUE;
    }
    // Copia cada arquivo existente na arvore
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
        throws IOException {
        copiaPath(file);
        retorno.append("Arquivo "+file.toString()+" copiado.\n");
        System.out.println("Arquivo "+file.toString()+" copiado.\n");
        return FileVisitResult.CONTINUE;
    }

    // Metodo que efetivamente copia os arquivos
    private void copiaPath(Path entrada) throws IOException {
        // encontra o caminho equivalente na arvore de copia
        Path novoDiretorio = destino.resolve(origem.relativize(entrada));
        Files.copy(entrada, novoDiretorio);
    }
    /*
    public static void main(String[] args) throws IOException{
        Path origem = Paths.get("\\apolo\sobe");
        Path destino = Paths.get("\\hermes\DCPD\BKP-SOBE\teste");

        Files.walkFileTree(origem, new CopyDir(origem, destino, retorno));
    }
    */
}
    
asked by anonymous 10.11.2015 / 13:22

1 answer

3

Your problem is that you are manipulating files within the Event-Dispatching Thread ). This is the thread that AWT and Swing use to render the screen and process keyboard and mouse events, among other things of the genre. Since you make this thread choke when you are working with files on disk (something that is soooo slow), AWT and Swing choke. Since everything is in the same thread, AWT and Swing will only have control of the EDT when all their file manipulation is finished.

You should never do this kind of thing on EDT, but on a separate thread. See more details in this other answer of mine .

That said, let's try to sort out your code. I'm using the Java 8 syntax. Let me know if you need this for Java 7, which I'll give an adapted:

private void executarActionPerformed(java.awt.event.ActionEvent evt) {                                         
    Path origem = Paths.get("\\apolo\sobe");
    Path destino = Paths.get("\\hermes\DCPD\BKP-SOBE\teste5");

    jtaRetorno.append("Executando Cópia");
    Thread t = new Thread(() -> copiaArquivos(origem, destino));
    t.start();
}

private void copiaArquivos(Path origem, Path destino) {
    try {
        Files.walkFileTree(origem, new CopyDir(origem, destino, this::imprimeStatus));
    } catch (IOException ex) {
        Logger.getLogger(FrmExecutaCopia.class.getName()).log(Level.SEVERE, null, ex);
        imprimeStatus(ex.toString());
    }
}

private void imprimeStatus(String texto) {
    System.out.println(texto);
    EventQueue.invokeLater(() -> jtaRetorno.append(texto + "\n"));
}
public class CopyDir extends SimpleFileVisitor<Path> {
    private final Path origem;
    private final Path destino;
    private final Consumer<String> retorno;

    // Construtor com origem e destino.
    public CopyDir(Path origem, Path destino, Consumer<String> retorno) {
        this.origem = origem;
        this.destino = destino;
        this.retorno = retorno;
    }

    // Usado para criar o diretório.
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
            throws IOException
    {
        copiaPath(dir);
        retorno.accept("Diretório " + dir.toString() + " criado.");
        return FileVisitResult.CONTINUE;
    }

    // Copia cada arquivo existente na árvore.
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
            throws IOException
    {
        copiaPath(file);
        retorno.accept("Arquivo " + file.toString() + " copiado.");
        return FileVisitResult.CONTINUE;
    }

    // Método que efetivamente copia os arquivos.
    private void copiaPath(Path entrada) throws IOException {
        // Encontra o caminho equivalente na árvore de cópia.
        Path novoDiretorio = destino.resolve(origem.relativize(entrada));
        Files.copy(entrada, novoDiretorio);
    }

    /*
    public static void main(String[] args) throws IOException {
        Path origem = Paths.get("\\apolo\sobe");
        Path destino = Paths.get("\\hermes\DCPD\BKP-SOBE\teste");

        Files.walkFileTree(origem, new CopyDir(origem, destino, System.out::println));
    }
    */
}

The idea is:

  • Never let EDT tinker with files directly, and because of that I create another thread for this purpose.

  • This other thread should never access Swing directly, including JTextArea , since Swing is not thread-safe . Instead, I use the EventQueue.invokeLater for this other thread to post a task to be executed by Swing, passing only String between one thread and another.

  • I use Consumer<String> of Java 8 as an object where the thread will post String s. On the caller side, it redirects this to the imprimeStatus method that is responsible for calling EventQueue.invokeLater .

  • Your class CopyDir is now fully uncoupled from Swing.

10.11.2015 / 14:16