2

I've been following this thread to try to update a JProgressBar from a separate class in my program: java - Progress Bar - Is it possible to use setProgess() of Progress Bar outside the doInBackground() method?

Unfortunately my code isn't work properly. I probably missed something, but I can't seem to figure out what it was. This is the code I have written so far:

Here is the propertyChangeListener I use to update the progress bar:

PropertyChangeListener listener = new PropertyChangeListener(){
        public void propertyChange(PropertyChangeEvent event){
            if("progress".equals(event.getPropertyName())){
                progressBar.setValue((int)currentPercent);
                progressBar.setString(Integer.toString((int)currentPercent));
            }
        }
    };

SwingWorker class:

class Task extends SwingWorker<Void, String>{

    public Task(){
        md = new ManipulateDirectories(this);
    }
    @Override
    public Void doInBackground(){
        for(int i = 0; i < directories.length; i++){
            md.copyDirectory(directories[i], backupDir);
        }
        return null;
    }

    @Override
    public void process(List<String> chunks){
    }

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

    public void updateProgress(int tick){
        setProgress(tick);
    }
}

ManipulateDirectories class:

public class ManipulateDirectories{
    Task task;
    public ManipulateDirectories(Task task){
        this.task = task;
    }

    //Recursive function to loop through and copy individual files
    public void copyDirectory(File file, File dest){
        if(file.isFile()){
            try {
                FileUtils.copyFileToDirectory(file, dest);
                currentSize = currentSize + getDirSize(file);
                if(currentSize >= ONE_PERCENT){
                    currentPercent = currentPercent + (currentSize / ONE_PERCENT);
                    task.updateProgress((int)currentPercent); //THIS LINE DOES NOT WORK
                    currentSize = currentSize % ONE_PERCENT;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (file.isDirectory()){
            File newDir = new File(String.format("%s\\%s", dest.getAbsolutePath(), file.getName()));
            if(!newDir.exists()){
                newDir.mkdir();
                for(File f : file.listFiles()){
                    copyDirectory(f, newDir);
                }
            }
        }
    }

    public 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;

    }
}

As you can see, the propertyChangeListener should be fired by the updateProgress(int) method within the swingworker class, and that method should be called by the line: task.updateProgress((int)currentPercent) which is in the ManipulateDirectories class. However, that line does not seem to call updateProgress(). I am guessing it might be a problem with the particular instance of "Task"--perhaps I am creating a separate instance that does not have the correct context? (I know the terminology is probably a bit wrong, but hopefully you can understand what I'm trying to say). Can anyone spot my mistake/s? Any help would be very much appreciated, I've been stuck on this for quite a while now.

SSCCE:

import java.awt.BorderLayout;
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 javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;

public class MainClass extends JFrame {
    private JProgressBar progressBar;
    private DoSomething ds;
    private int currentPercent = 0;
    private Task task;
    private static JPanel contentPane;
    private JPanel bottomPane;
    private JButton btnCancel;

    public static void main(String[] args){
        MainClass mc = new MainClass();
        mc.createGUI();
    }
    public void createGUI(){
        //Create frame
        setTitle("Backup Progress");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 350);
        //Create content pane
        contentPane = new JPanel(new BorderLayout());
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        //Create panel for progress bar/cancel button
        bottomPane = new JPanel();
        bottomPane.setLayout(new BoxLayout(bottomPane, BoxLayout.Y_AXIS));

        progressBar = new JProgressBar(0, 100);
        progressBar.setStringPainted(true);
        progressBar.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        progressBar.setValue(0);

        btnCancel = new JButton("Cancel");
        btnCancel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                closeWindow();
            }
        });

        bottomPane.add(progressBar);
        bottomPane.add(btnCancel);
        contentPane.add(bottomPane, BorderLayout.SOUTH);

        PropertyChangeListener listener = new PropertyChangeListener(){
            public void propertyChange(PropertyChangeEvent event){
                if("progress".equals(event.getPropertyName())){
                    progressBar.setValue((int)currentPercent);
                    progressBar.setString(Integer.toString((int)currentPercent));
                }
            }
        };

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

    class Task extends SwingWorker<Void, String>{
        public Task(){
            ds = new DoSomething(this);
        }
        @Override
        public Void doInBackground(){
            for(int i = 0; i < 100; i++){
                ds.incrementPercent();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
        @Override
        public void done(){
            closeWindow();
        }
        public void updateProgress(int tick){
            setProgress(currentPercent + tick);
        }
    }

    public class DoSomething{
        Task task;
        public DoSomething(Task task){
            this.task = task;
        }
        public void incrementPercent(){
            task.updateProgress(1);
        }
    }

    public void closeWindow(){
        WindowEvent close = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
        Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(close);
    }
} 

I realize that's a very long SSCCE, but hopefully it gives you all the information you need to know.

Community
  • 1
  • 1
DerStrom8
  • 1,311
  • 2
  • 23
  • 45
  • 2
    I apologize, I never noticed the swing tag was added. I will remember that from now on. I will put together a quick SSCCE in just a moment. – DerStrom8 Dec 28 '13 at 00:21
  • What does `ONE_PERCENT` equal, what is `currentSize` and `currentPercent` declared as? Your code snippet is really pointless as it does not describe enough of what you are doing to be helpful... – MadProgrammer Dec 28 '13 at 00:37
  • Oops, I forgot to include that. I initially call this class with a variable "totalSize", which is the total size of a group of directories. ONE_PERCENT is totalSize/100, which gives me the size needed for the progress bar to increment by 1%. CurrentSize is the sum of sizes of files already copied for as long as it is less than 1%. CurrentPercent is the total percent which should be displayed on the progress bar. Hope this makes sense. – DerStrom8 Dec 28 '13 at 00:40
  • @AndrewThompson, SSCCE has been added, though it's longer than I had hoped. Hopefully it's good enough though, it's about the best I can do – DerStrom8 Dec 28 '13 at 01:02
  • An SSCCE can easily be 150 LOC before people complain, that is 88 LOC. But note that an SSCCE should be copy/paste/compile/run/see on-screen. When I try to compile that I get 42 compilation errors for lack of imports, and if it **had** compiled, it would not run due to missing `main(String[])`.. Can you change that? Of course ***I*** could change it, but then, why force me and each of 2(?) other people to do that, when you could do it yourself, once. – Andrew Thompson Dec 28 '13 at 01:12
  • Oops, good point. One moment please.... – DerStrom8 Dec 28 '13 at 01:20
  • I apologize for the odd spacing earlier. Apparently the spacing didn't transfer properly. It should be fixed now. ***EDIT*** Looks like you guys beat me to it! It was already fixed by the time I posted my edits – DerStrom8 Dec 28 '13 at 01:37
  • 1
    Based on your SSCCE, currentPercent + tick will always be 1 ;) – MadProgrammer Dec 28 '13 at 03:25

1 Answers1

3

You never add your listener to your SwingWorker.

Change to:

task = new Task();
task.addPropertyChangeListener(listener);

The other error you have is that you never update your currentPercent in updateProgress.

public void updateProgress(int tick){
            currentPercent+=tick;
            setProgress(currentPercent);
}

EDIT

Well and actually your error is the same in your code (not sscce), you never update your currentPercent value.

PropertyChangeListener listener = new PropertyChangeListener(){
            public void propertyChange(PropertyChangeEvent event){
                if("progress".equals(event.getPropertyName())){
                    currentPercent = (int)event.getNewValue();
                    progressBar.setValue(currentPercent);
                    progressBar.setString(Integer.toString(currentPercent));
                }
            }
        };
nachokk
  • 14,363
  • 4
  • 24
  • 53
  • 1
    I'll buy that for a dollar +1 – MadProgrammer Dec 28 '13 at 03:27
  • Well, while that was most certainly part of the problem, it wasn't the whole one. The progress bar still does not update – DerStrom8 Dec 28 '13 at 03:38
  • @derstrom8 i edited answer, hope it helps and thanks for [SSCCE](http://sscce.org). Making sscce makes it easy to find bugs :) – nachokk Dec 28 '13 at 04:10
  • Well that makes the SSCCE run, but my actual code is set up a bit differently. If you look at where updateProgress() is called in my actual code, you'll see that currentPercent is updated prior to the method being called. So I'm still not quite sure why my code isn't working. – DerStrom8 Dec 28 '13 at 04:17
  • @derstrom8 The error is still the same, you don't update your variable, i edit answer, hope it helps you. – nachokk Dec 28 '13 at 04:22
  • Ah, I think I understand. I'm running it now, will take approximately 8 minutes to get to that point. I'll let you know what happens! – DerStrom8 Dec 28 '13 at 04:27
  • Hmm, still no luck. I have a breakpoint set inside the listener but it's never getting there. I also have a breakpoint set inside the updateProgress() method, and it gets there, but it jumps right over the setProgress() method call and the listener does not appear to be fired. – DerStrom8 Dec 28 '13 at 04:50
  • No, as I mentioned it still does not work. It works in the SSCCE but my actual code is set up a bit differently (which is why I included it in the original post). It's not THAT different, I still would have expected it to work, but it did not. – DerStrom8 Dec 28 '13 at 16:41
  • @derstrom8 Then your SSCCE is not a SSCCE itself cause it's not demonstrating your problem that you are trying to solve.Perhaps you have another issue. – nachokk Dec 28 '13 at 16:44
  • Yes, that is what I was realizing when it started working and my code was not. I am stumped though, I don't see how its operation should be any different from the SSCCE – DerStrom8 Dec 28 '13 at 16:48
  • @derstrom8 make "inverse engineer" start with your SSCCE and add things to it until it not work anymore :D then you'll get what is happen – nachokk Dec 28 '13 at 16:50
  • Yep, it's working now! Your solution was definitely the answer, but when I first declared "Task task" inside the main class, I forgot to say "private". I added that in and it works. Thanks very much for all your help! – DerStrom8 Dec 28 '13 at 17:04