Change text of a Jtextarea at runtime

Asked

Viewed 482 times

4

Good morning to all,

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

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

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

Follow 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));
    }
    */
}

1 answer

3


Your problem is that you are manipulating files within the EDT (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. Once you make this thread choke while working with files on disk (something that is very slow), AWT and Swing choke. Since everything is in the same thread, AWT and Swing will only regain control of EDT when all their file manipulation is finished.

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

That said, let’s try to fix your code. I’m using the syntax of Java 8. Let me know if you need it obligatorily for Java 7, and then I’ll give you 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 the EDT mess with files directly, and because of that I create another thread for this purpose.

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

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

  • Your class CopyDir is now totally uncoupled from Swing.

  • 1

    Great. It worked out! Thank you. Even Victor Vlw.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.