Search code examples
javamultithreadingswt

Java8: SWT panel updates occur AFTER long task is finished althogth it's a concurrent thread


I have a SWT panel where I set a root folder and some parameters, and I want to do some calculations on each of the files found (DocumentTree). After each file processing, it should update the panel status bar (a label text), but it updates occur all at once after all processing is finished. I have searched similar problems with threads and SWT, but could not find the error in my code. Thanks in advance

  • Pane:
public class MainPane {
    private Display display;
    private Shell shell;
    // ... some controls ...
    Label barStatus;    // Should show the file/folder being processed, but it updates AFTER processing is finished!!!

    public MainPane() {
        display = Display.getDefault();
        shell = new Shell(display);
        shell.setLayout(null);
        // ...Formating and controls placing... 
    }
    
    public void open() {
        shell.open();
        shell.layout();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
    }
        
    public void setStatusbarText(String estado) {
        try {
            barStatus.setText(estado);
            barStatus.update();
            barStatus.getParent().layout();     // According to some answers in forums, it is neccessary to get labels redrawn
        } catch (Exception e) {
            System.out.println("ERROR: " + e);
        }
    }
}
  • Controller:
public class MainPaneController  implements SelectionListener {
    private DocumentTree tree;
    private MainPane mainPane;

    // ... Some code ...

    @Override
    public void widgetSelected(SelectionEvent e) {
        if (e.getSource() == mainPane.btnCalculateMetadata()) {
            new Thread(new Runnable() {              // Launch the action as parallel thread so the panel can be updated
                public void run() {
                    metadataGenerationEvent();              
                }
            }).run();
        }
        // .. More if clauses for other control events ...
    }
        
    public void metadataGenerationEvent() {
        tree.metadataGeneration();        
    }
            
    public void updateStatus(String tarea, String mensaje) {
        String status = tarea + ": " + mensaje;
        mainPane.getDisplay().asyncExec(new Runnable() {        // Request label text update as a SWT display's thread not to raise an exception
            public void run() {
                mainPane.setStatusbarText(status);
            }
        });     
    }
}
  • Data & processing:
public class DocumentTree {
   // ... Data structure and some other methods ...

   public void metadataGeneration() {
        for (Documento document : this.documents) {
            mainController.updateStatus("Calculating metadata of file", document.absolutePath());  // This should update the status BEFORE EACH file is processed, but status is updated alltogether AFTER ALL processing is finished
            document.calculateMetadata();   // It takes some seconds for each one
        }
    }
}

Solution

  • You need to call the start method of Thread not run. Only start creates a new thread, so:

    new Thread(new Runnable() {              // Launch the action as parallel thread so the panel can be updated
       public void run() {
           metadataGenerationEvent();              
       }
    }).start();
    

    which can be shortened to:

    new Thread(this::metadataGenerationEvent).start()
    

    Or even look at CompletableFuture rather than using Thread directly.