Question

I am having some trouble with my JScrollPane/JTextArea when using a SwingWorker. Here is what I currently have:

JTextArea txtDirs;
Task task;

//EDT
public void createGUI(){
    txtDirs = new JTextArea();
    txtDirs.setBounds(10, 56, 414, 125);
    txtDirs.setEditable(false);
    contentPane.add(new JScrollPane(txtDirs));

    task = new Task();
    task.execute();
}

class Task extends SwingWorker<Void, Void>{
    @Override
    public void doInBackground(){
        for(file : files){
            txtDirs.append(file.getAbsolutePath);
        }
    }

    @Override
    public void done(){
        closeWindow();
    }
}

This isn't the exact code, but it should give you the gist of it. Anyway, the problem is that the JScrollPane doesn't appear at all, though the code in the doInBackground() method does run. I expect it has something to do with it being updated (txtDirs.append(file.getAbsolutePath())) in the background task. However, I'm not quite sure how to make it work in this case. The frame appears as expected, but it is entirely blank. Should there be a "process" method in the Task class? And is that where the "txtDirs.append(file.getAbsolutePath())" should be placed? I'm afraid I'm rather new to the use of SwingWorkers, so any help would be appreciated.

EDIT:

After a little confusion I decided I'd simply post my full code. I know there are things wrong with it, and I probably misunderstood some of what was mentioned in the comments, so I'm hoping posting this will allow someone to point out any obvious mistakes I've made.

Full code:

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JButton;
import javax.swing.JProgressBar;
import javax.swing.JLabel;
import javax.swing.SwingWorker;

import org.apache.commons.io.FileUtils;

@SuppressWarnings("serial")
public class BasicCopy extends JFrame {

    private JPanel contentPane;
    private JTextArea txtCopiedDirs;
    private JButton btnCancel;
    private JProgressBar progressBar;
    private JLabel lblCopying;
    private String mainDrive;
    private String backupDrive;
    private String username;
    private String backupDir;
    long totalSize = 0L;        //total size of directories/files
    long currentSize = 0L;      //current size of files counting up to ONE_PERCENT
    long currentPercent = 0L;   //current progress in %
    long ONE_PERCENT;       //totalSize / 100
    Task task;

    public BasicCopy() {
    }

    public BasicCopy(String inDrive, String outDrive, String username, long space){
        mainDrive = inDrive;
        backupDrive = outDrive;
        this.username = username;
        totalSize = space*1048576;
        ONE_PERCENT = totalSize/100;
        createGUI();
    }

    public void createGUI(){
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Backup Progress");
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        txtCopiedDirs = new JTextArea(10, 50);
        txtCopiedDirs.setEditable(false);
        contentPane.add(new JScrollPane(txtCopiedDirs));

        btnCancel = new JButton("Cancel");
        btnCancel.setBounds(169, 227, 89, 23);
        btnCancel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                closeWindow();
            }
        });
        contentPane.add(btnCancel);

        progressBar = new JProgressBar(0, 100);
        progressBar.setBounds(10, 192, 414, 24);
        progressBar.setValue(0);
        contentPane.add(progressBar);

        lblCopying = new JLabel("Now backing up your files....");
        lblCopying.setBounds(10, 11, 157, 34);
        contentPane.add(lblCopying);

        setVisible(true);
        task = new Task();
        task.execute();
    }


    /**
     * Swing Worker class
     */
    class Task extends SwingWorker<Void, String>{
        @Override
        public Void doInBackground(){
            setProgress(0);

            //Create Backup Directory
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy_HMMSS");
            String timestamp = sdf.format(date);
            backupDir = backupDrive + ":\\" + "Backup_" + timestamp;
            File backupDirectory = new File(backupDir);
            backupDirectory.mkdir();

            //Copy Files
            //Main directories
            String pics = mainDrive + ":\\Users\\" + username + "\\Pictures\\";
            String docs = mainDrive + ":\\Users\\" + username + "\\Documents\\";
            String vids = mainDrive + ":\\Users\\" + username + "\\Videos\\";
            String musc = mainDrive + ":\\Users\\" + username + "\\Music\\";
            //Backup directories
            String bkPics = backupDir + "\\Pictures\\";
            String bkDocs = backupDir + "\\Documents\\";
            String bkVids = backupDir + "\\Documents\\";
            String bkMusc = backupDir + "\\Pictures\\";

            String[] directories = {pics, docs, vids, musc};
            String[] bkDirectories = {bkPics, bkDocs, bkVids, bkMusc};

            //Loop through directories and copy files
            for (int i = 0; i < directories.length; i++){
                File dir = new File(directories[i]);
                File dest = new File(bkDirectories[i]);
                for(File file: dir.listFiles()){
                    try{
                        if(file.isFile()){
                            FileUtils.copyFileToDirectory(file, dest);
                            publish(file.getAbsolutePath() + "\n");
                        } else if (file.isDirectory()){
                            FileUtils.copyDirectoryToDirectory(file, dest);
                            publish(file.getAbsolutePath() + "\n");
                        }
                        if(getDirSize(file) >= ONE_PERCENT){
                            currentPercent = getDirSize(file)/ONE_PERCENT;
                            progressBar.setValue((int)currentPercent);
                            currentSize = 0;
                        } else {
                            currentSize = currentSize + getDirSize(file);
                            if(currentSize >= ONE_PERCENT){
                                currentPercent = currentSize/ONE_PERCENT;
                                currentPercent++;
                                progressBar.setValue((int)currentPercent);
                                currentSize = 0;
                            }
                        }
                    } catch (IOException e){
                        e.printStackTrace();
                    }
                }
            }
            return null;
        }

        @Override
        public void process(List<String> chunks){
            for(String path : chunks){
                txtCopiedDirs.append(path);
            }
        }

        @Override
        public void done(){
            closeWindow();
        }
    }

    public static Long getDirSize(File file) {
        long size = 0L;

        if (file.isFile() && file != null){       
            size += file.isDirectory() ? getDirSize(file) : file.length();
        } else if (file.isDirectory()){
            for (File f : file.listFiles()) {
            size += f.isDirectory() ? getDirSize(f) : file.length();
        }
        }
        return size;
    }

    /* Close current window */
    public void closeWindow() {
        WindowEvent close = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
        Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(close);
        System.exit(0);
    }
}

EDIT #2:

I have made some changes to the createGUI() method to avoid contentPane from having a null layout:

public void createGUI(){
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setTitle("Backup Progress");
    setBounds(100, 100, 450, 300);
    contentPane = new JPanel(new BorderLayout());
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    setContentPane(contentPane);
    contentPane.setLayout(null);

    txtCopiedDirs = new JTextArea(10, 50);
    txtCopiedDirs.setEditable(false);
    scrollPane = new JScrollPane(txtCopiedDirs);
    contentPane.add(scrollPane, BorderLayout.CENTER);

    btnCancel = new JButton("Cancel");
    btnCancel.setBounds(169, 227, 89, 23);
    btnCancel.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            closeWindow();
        }
    });
    contentPane.add(btnCancel);

    progressBar = new JProgressBar(0, 100);
    progressBar.setBounds(10, 192, 414, 24);
    progressBar.setValue(0);
    contentPane.add(progressBar);

    lblCopying = new JLabel("Now backing up your files....");
    lblCopying.setBounds(10, 11, 157, 34);
    contentPane.add(lblCopying);

    setVisible(true);
    task = new Task();
    task.execute();
}
Was it helpful?

Solution

  • Don't use setBounds, instead use JTextArea(rows, cols)
  • Don't modify the any UI components from outside of the EDT, instead use publish and override process. process is called within the content of the EDT

Take a look at Concurrency in Swing for more details

Possible causes of your problem include...

  • Adding other components to the frame while it's using a BorderLayout, without specifying a position constraint (other than BorderLayout.CENTER)
  • Calling visible before the components are added it

Updated

The main problem is, the contentPane is using a null layout and the scroll panes default bounds are 0x0 position by 0x0 width and height.

Best choice is to not use WinodwBuilder and learn how to use layout managers

Update with example

This is an example using two layout managers, BorderLayout as the base layout and GridBagLayout to provide some additional support for the extra components.

Basic rule of thumb. If it's a UI component, ESPECIALLY if it's on the screen (or you don't know it's on the screen or not), you must only ever update it from the context of the EDT.

This means calling progressBar.setValue from within the doInBackground is a violation of the single thread rule of Swing. SwingWorker provides progress updates via it's PropertyChange support

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;

public class BasicCopy extends JFrame {

    private JPanel contentPane;
    private JTextArea txtCopiedDirs;
    private JButton btnCancel;
    private JProgressBar progressBar;
    private JLabel lblCopying;
    private String mainDrive;
    private String backupDrive;
    private String username;
    private String backupDir;
    long totalSize = 0L;        //total size of directories/files
    long currentSize = 0L;      //current size of files counting up to ONE_PERCENT
    long currentPercent = 0L;   //current progress in %
    long ONE_PERCENT;       //totalSize / 100
    Task task;

    public BasicCopy() {
    }

    public BasicCopy(String inDrive, String outDrive, String username, long space) {
        mainDrive = inDrive;
        backupDrive = outDrive;
        this.username = username;
        totalSize = space * 1048576;
        ONE_PERCENT = totalSize / 100;
        createGUI();
    }

    public void createGUI() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Backup Progress");
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(new BorderLayout());

        txtCopiedDirs = new JTextArea(10, 50);
        txtCopiedDirs.setEditable(false);
        contentPane.add(new JScrollPane(txtCopiedDirs));

        JPanel controls = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = new Insets(4, 4, 4, 4);

        lblCopying = new JLabel("Now backing up your files....");
        contentPane.add(lblCopying, gbc);

        gbc.gridy++;

        progressBar = new JProgressBar(0, 100);
        progressBar.setValue(0);
        contentPane.add(progressBar, gbc);

        gbc.gridy++;

        btnCancel = new JButton("Cancel");
        btnCancel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                closeWindow();
            }
        });
        controls.add(btnCancel, gbc);
        contentPane.add(controls, BorderLayout.SOUTH);

        setLocationRelativeTo(null);
        pack();
        setVisible(true);
        task = new Task();
        task.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
                    progressBar.setValue((int) evt.getNewValue());
                }
            }
        });
        task.execute();
    }

    /**
     * Swing Worker class
     */
    class Task extends SwingWorker<Void, String> {

        @Override
        public Void doInBackground() throws Exception {
            setProgress(0);

            //Create Backup Directory
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy_HMMSS");
            String timestamp = sdf.format(date);
            backupDir = backupDrive + ":\\" + "Backup_" + timestamp;
            File backupDirectory = new File(backupDir);
            backupDirectory.mkdir();

            //Copy Files
            //Main directories
            String pics = mainDrive + ":\\Users\\" + username + "\\Pictures\\";
            String docs = mainDrive + ":\\Users\\" + username + "\\Documents\\";
            String vids = mainDrive + ":\\Users\\" + username + "\\Videos\\";
            String musc = mainDrive + ":\\Users\\" + username + "\\Music\\";
            //Backup directories
            String bkPics = backupDir + "\\Pictures\\";
            String bkDocs = backupDir + "\\Documents\\";
            String bkVids = backupDir + "\\Documents\\";
            String bkMusc = backupDir + "\\Pictures\\";

            String[] directories = {pics, docs, vids, musc};
            String[] bkDirectories = {bkPics, bkDocs, bkVids, bkMusc};

            //Loop through directories and copy files
            for (int i = 0; i < directories.length; i++) {
                File dir = new File(directories[i]);
                File dest = new File(bkDirectories[i]);
                for (File file : dir.listFiles()) {
                    try{
                    if (file.isFile()) {
                        FileUtils.copyFileToDirectory(file, dest);
                        publish(file.getAbsolutePath() + "\n");
                    } else if (file.isDirectory()) {
                        FileUtils.copyDirectoryToDirectory(file, dest);
                        Thread.sleep(1000);
                        publish(file.getAbsolutePath() + "\n");
                    }
                    if (getDirSize(file) >= ONE_PERCENT) {
                        currentPercent = getDirSize(file) / ONE_PERCENT;
                        setProgress((int) currentPercent);
                        currentSize = 0;
                    } else {
                        currentSize = currentSize + getDirSize(file);
                        if (currentSize >= ONE_PERCENT) {
                            currentPercent = currentSize / ONE_PERCENT;
                            currentPercent++;
                            setProgress((int) currentPercent);
                            currentSize = 0;
                        }
                    }
                    } catch (IOException e){
                        e.printStackTrace();
                    }
                }
            }
            return null;
        }

        @Override
        public void process(List<String> chunks) {
            for (String path : chunks) {
                txtCopiedDirs.append(path);
            }
        }

        @Override
        public void done() {
            closeWindow();
        }
    }

    public static Long getDirSize(File file) {
        long size = 0L;

        if (file.isFile() && file != null) {
            size += file.isDirectory() ? getDirSize(file) : file.length();
        } else if (file.isDirectory()) {
            for (File f : file.listFiles()) {
                size += f.isDirectory() ? getDirSize(f) : file.length();
            }
        }
        return size;
    }

    /* Close current window */
    public void closeWindow() {
        WindowEvent close = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
        Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(close);
        System.exit(0);
    }
}

OTHER TIPS

Try this, i don't know if append it's thread-safe, try calling in publish where you know that is called in EDT.

class Task extends SwingWorker<Void, String>{
    @Override
    public void doInBackground(){
        for(file : files){
            publish(file.getAbsolutePath); //or you can store them in an array and call it later
        }
    }

    @Override
    public void process(List<String> chunks){
           for(String filePath : chunks){
             txtDirs.append(filePath);
           }
    }

    @Override
    public void done(){
        closeWindow();
    }
}

If this doesn't help consider posting a SSCCE

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top