adding progress bar to each table cell for file progress - Java
Asked Answered
I

3

13

The application encrypts each file that is put into the table when you click encrypt and I would like to show the progress of the files as they are being encrypted. The "Status" column will then change from "Not Processed" to "Processed."

Similar to the way you watch multiple files attach in an email. I've been looking into the cell renderer and the ProgressBarTablecell but am unsure how to go about implementing them. Any help appreciated. I'm posting the table.

        import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Point;
    import java.awt.datatransfer.DataFlavor;
    import java.awt.datatransfer.Transferable;
    import java.awt.datatransfer.UnsupportedFlavorException;
    import java.awt.dnd.DnDConstants;
    import java.awt.dnd.DropTarget;
    import java.awt.dnd.DropTargetDragEvent;
    import java.awt.dnd.DropTargetDropEvent;
    import java.io.File;
    import java.io.IOException;
    import java.util.List;

    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.table.DefaultTableModel;

    public class DropTable {

        public static void main(String[] args) {
            new DropTable();
        }

        public DropTable() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager
                                .getSystemLookAndFeelClassName());//get look and feel of whatever OS we're using
                    } catch (ClassNotFoundException | InstantiationException
                            | IllegalAccessException
                            | UnsupportedLookAndFeelException ex) {
                    }

                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new DropPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }

            });
        }

        public class DropPane extends JPanel {

            /**
             * 
             */
            private static final long serialVersionUID = 1L;
            private JTable table;
            private JScrollPane scroll;
            private DefaultTableModel tm = new DefaultTableModel(new String[] {
                    "File", "File Type", "Size", "Status" }, 0);

            public DropPane() {
                table = new JTable();
                table.setShowGrid(true);
                table.setShowHorizontalLines(true);
                table.setShowVerticalLines(true);
                table.setGridColor(Color.GRAY);


                table.setModel(tm);
                table.setFillsViewportHeight(true);
                table.setPreferredSize(new Dimension(500, 300));

                scroll = new JScrollPane(table);

                table.setDropTarget(new DropTarget() {
                    @Override
                    public synchronized void dragOver(DropTargetDragEvent dtde) {
                        Point point = dtde.getLocation();
                        int row = table.rowAtPoint(point);
                        if (row < 0) {
                            table.clearSelection();
                        } else {
                            table.setRowSelectionInterval(row, row);
                        }
                        dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
                    }

                    @Override
                    public synchronized void drop(DropTargetDropEvent dtde) {
                        if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
                        {//make sure the flavors are files
                            dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);//dndconstants tells what to do with the drag files
                            //change to ACTION_COPY so it removes the file from the directory
                            Transferable t = dtde.getTransferable();
                            List fileList = null;
                            try {
                                fileList = (List) t.getTransferData(DataFlavor.javaFileListFlavor);//get file
                                if (fileList.size() > 0) {
                                    table.clearSelection();
                                    Point point = dtde.getLocation();//point is (x,y) 
                                    int row = table.rowAtPoint(point);
                                    DefaultTableModel model = (DefaultTableModel) table.getModel();
                                    for (Object value : fileList) {
                                        if (value instanceof File) {
                                            File f = (File) value;

                                            if (row < 0) {//insert rows into the right columns
                                            model.addRow(new Object[]{f.getAbsolutePath(), "", f.length(), "", ""});//path under "File"
                                            } else {
                                                model.insertRow(row, new Object[]{f.getAbsolutePath(), "", f.length(), "", ""});//get size of file
                                                row++;

                                            }
                                        }
                                    }
                                }
                            } catch (UnsupportedFlavorException e) {
                                e.printStackTrace();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } else {
                            dtde.rejectDrop();
                        }
                    }

                });

                add(scroll, BorderLayout.CENTER);
            }
        }
    }
Idleman answered 6/12, 2012 at 22:12 Comment(0)
S
30

Here is a basic example, this basically uses a SwingWorker to scan the root directory of your drive and lists all the files. Once that's completed, it will attempt to read each file, in it's own SwingWorker updating the table as it goes.

Disclaimer: This is an example. I use a Thread.sleep to slow the reads down slightly, ignore the buffers and a few other things I would do it differently in production code, but I wanted to highlight the progress updates.

enter image description here

How it works

First, you need a cell renderer capable of displaying progress updates. I've chosen a simple custom JProgressBar, but you might like something a little more sophisticated.

You need some way to update the table model. I've chose to provide a simple updateStatus method, passing the file I'm updating, this allows me to use internal look ups to find the row in question. I then use the setValueAt method to update the row object. This is not really required, but I wanted to demonstrate the use of the setValueAt method, you could have updated the row object from the updateStatus method directly.

And finally, notify the table of changes to the model so it will repaint itself.

public class UpdateTable {

    public static void main(String[] args) {
        new UpdateTable();
    }

    public UpdateTable() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                UpdatableTableModel model = new UpdatableTableModel();

                JTable table = new JTable();
                table.setModel(model);

                table.getColumn("Status").setCellRenderer(new ProgressCellRender());

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(table));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                FileFinderWorker worker = new FileFinderWorker(model);
                worker.execute();

            }
        });
    }

    public class ProgressCellRender extends JProgressBar implements TableCellRenderer {

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            int progress = 0;
            if (value instanceof Float) {
                progress = Math.round(((Float) value) * 100f);
            } else if (value instanceof Integer) {
                progress = (int) value;
            }
            setValue(progress);
            return this;
        }
    }

    public class RowData {

        private File file;
        private String type;
        private long length;
        private float status;

        public RowData(File file, String type) {
            this.file = file;
            this.type = type;
            this.length = file.length();
            this.status = 0f;
        }

        public File getFile() {
            return file;
        }

        public long getLength() {
            return length;
        }

        public float getStatus() {
            return status;
        }

        public String getType() {
            return type;
        }

        public void setStatus(float status) {
            this.status = status;
        }
    }

    public class UpdatableTableModel extends AbstractTableModel {

        private List<RowData> rows;
        private Map<File, RowData> mapLookup;

        public UpdatableTableModel() {
            rows = new ArrayList<>(25);
            mapLookup = new HashMap<>(25);
        }

        @Override
        public int getRowCount() {
            return rows.size();
        }

        @Override
        public int getColumnCount() {
            return 4;
        }

        @Override
        public String getColumnName(int column) {
            String name = "??";
            switch (column) {
                case 0:
                    name = "File";
                    break;
                case 1:
                    name = "File Type";
                    break;
                case 2:
                    name = "Size";
                    break;
                case 3:
                    name = "Status";
                    break;
            }
            return name;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            RowData rowData = rows.get(rowIndex);
            Object value = null;
            switch (columnIndex) {
                case 0:
                    value = rowData.getFile();
                    break;
                case 1:
                    value = rowData.getType();
                    break;
                case 2:
                    value = rowData.getLength();
                    break;
                case 3:
                    value = rowData.getStatus();
                    break;
            }
            return value;
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            RowData rowData = rows.get(rowIndex);
            switch (columnIndex) {
                case 3:
                    if (aValue instanceof Float) {
                        rowData.setStatus((float) aValue);
                    }
                    break;
            }
        }

        public void addFile(File file) {
            RowData rowData = new RowData(file, "A File");
            mapLookup.put(file, rowData);
            rows.add(rowData);
            fireTableRowsInserted(rows.size() - 1, rows.size() - 1);
        }

        protected void updateStatus(File file, int progress) {
            RowData rowData = mapLookup.get(file);
            if (rowData != null) {
                int row = rows.indexOf(rowData);
                float p = (float) progress / 100f;
                setValueAt(p, row, 3);
                fireTableCellUpdated(row, 3);
            }
        }
    }

    public class FileFinderWorker extends SwingWorker<List<File>, File> {

        private UpdatableTableModel model;

        public FileFinderWorker(UpdatableTableModel model) {
            this.model = model;
        }

        @Override
        protected void process(List<File> chunks) {
            for (File file : chunks) {
                model.addFile(file);
            }
        }

        @Override
        protected List<File> doInBackground() throws Exception {
            File files[] = new File(System.getProperty("user.dir")).listFiles();
            List<File> lstFiles = new ArrayList<>(Arrays.asList(files));
            for (File file : lstFiles) {
                // You could actually publish the entire array, but I'm doing this
                // deliberatly ;)
                publish(file);
            }
            return lstFiles;
        }

        @Override
        protected void done() {
            try {
                List<File> files = get();
                for (File file : files) {
                    new FileReaderWorker(model, file).execute();
                }
            } catch (Exception exp) {
                exp.printStackTrace();
            }
        }
    }

    public class FileReaderWorker extends SwingWorker<File, File> {

        private File currentFile;
        private UpdatableTableModel model;

        public FileReaderWorker(UpdatableTableModel model, File file) {
            this.currentFile = file;
            this.model = model;

            addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if (evt.getPropertyName().equals("progress")) {
                        FileReaderWorker.this.model.updateStatus(currentFile, (int) evt.getNewValue());
                    }
                }
            });

        }

        @Override
        protected File doInBackground() throws Exception {
            if (currentFile.isFile()) {
                setProgress(0);
                long fileLength = currentFile.length();
                BufferedReader reader = null;
                char[] cbuf = new char[1024];
                try {
                    reader = new BufferedReader(new FileReader(currentFile));
                    int bytesRead = -1;
                    int totalBytesRead = 0;
                    while ((bytesRead = reader.read(cbuf)) != -1) {
                        totalBytesRead += bytesRead;
                        int progress = (int) Math.round(((double) totalBytesRead / (double) fileLength) * 100d);
                        setProgress(progress);
                        Thread.sleep(25);
                    }
                    setProgress(100);
                } catch (Exception e) {
                    e.printStackTrace();
                    setProgress(100);
                } finally {
                    try {
                        reader.close();
                    } catch (Exception e) {
                    }
                }
            } else {
                setProgress(100);
            }
            return currentFile;
        }
    }
}

Important concepts.

NEVER, EVER block the Event Dispatching Thread with any kind of long running operation. Instead, move these time consuming operations off into a background thread. Here, I've used SwingWorker

Have a read through Concurrency in Swing for more info

Santos answered 7/12, 2012 at 0:35 Comment(17)
+1 Also consider System.getProperty("user.dir") over "C:/".Nothing
@Santos Big thanks for the example. Looking forward to running it but when I tried to run it I received a "cannot cast from object to int" error here: FileReaderWorker.this.model.updateStatus(currentFile, (int) evt.getNewValue()); and here rowData.setStatus((float) aValue);Idleman
@Nothing There had to be something :PSantos
@healix What version of Java are you using?Santos
@healix If you cast there Object equivalent (ie (float) to (Float)) it should workSantos
@Santos funny you ask because I just went to my PC which has 1.7 on it. My mac has 1.6 so I'm guessing thats what's giving me the issuesIdleman
@healix: See also Highlights of Technology Changes in Java SE 7.Nothing
@healix You'll also have trouble with the UIManager exception handler and "diamond" brackets on the collections...I'm using Java 7Santos
@Nothing I know there's quite a bit. I was using the new IO in another project and I just keep forgetting my mac is not 1.7. Need to stay consistent with coding on the PC.Idleman
@Santos The UIManager I'm aware of. Had trouble with that a few days ago. The diamond brackets are new to me. I need mac to start supporting Java 7. Going between two computers is doing no goodIdleman
@healix "diamond" brackets are generic short hand, for example List<File> files = new ArrayList<>(25) is simply short hand for List<File> files = new ArrayList<File>(25). Mac can support Java 7, but only Snow Lion (I think), don't hold your breath for anything below that ;)Santos
The diamond bracket error in java 7 is new to me.I have worked with collections and generics a little. I have snow lion and java 7 but still no go. I changed everything in eclipse to java 7 but the JDK is no where to be found on my system. I'm just going to stick with PC for nowIdleman
@healix Apple is no longer producing Java updates, you can download JDK 7 for Mac from here. It use to be on the Sun Web Site, but appears to have moved :PSantos
let us continue this discussion in chatSantos
@Santos is it possible to add that bars using some interval and can be differ in color?Predella
@Krishna Yes...but you'll need some way to get the color information from the modelSantos
@Santos i am getting colors info but just dont know how to implement it just its like chart that every bar will show some value and there will be interval and every interval will have color accordingly..Predella
N
7

You'll need a TableCellRenderer that contains a JProgressBar, as shown here. You can update each file's progress from a SwingWorker, seen here.

image

Nothing answered 7/12, 2012 at 0:0 Comment(0)
C
0

You need to have some kind of a process with an integer value between 0 and 100. For example :

class Process {

    public Process(String name, int progress, String description) {
        super();
        this.name = name;
        this.progress = progress;
        this.description = description;
    }

    String name;
    int progress;
    String description; }

This gets modelled in the tables tableModel.

A renderer is needed to return a progress bar on one of the tables columns :

class ProgressRenderer extends DefaultTableCellRenderer {
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row2, int column) {

        int row = table.convertRowIndexToModel(row2);

        ProcessTableModel mtm = (ProcessTableModel) table.getModel();
        Process p = (Process) mtm.getProcessAt(row);

        JProgressBar bar = new JProgressBar();
        bar.setValue(p.progress);
        return bar;
    }
}

Now you just need a thread to be doing something in the background and updating the process objects. The complete example, of which I have copied snippets of code, can be found here.

enter image description here

Concepcion answered 22/9, 2014 at 10:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.