Question

This is a homework assignment where I'm to read in a file and convert all the characters to upper or lower case depending on what the user selects. I've got all of that working, but I'm not sure if my progress bar is updating with each character write or if it's jumping to 100% when the file write is done. My logic was after each character is written I increment a counter and following that increment I so progressBar.setValue(100 * (symbolsWritten / totalSymbols)); Here's part of my code that checks to see if a character is lower case and converts it to upper case if needed. I'm just learning so please don't bash my code too bad :) Thank you for your comments.

public void writeFileLC(char[] theCharArray)
{
    int totalSymbols = theCharArray.length;
    int symbolsWritten = 0;
    int symbolsConverted = 0;

    /*this loop checks to see if the character is upper case and converts
     it if needed. Then it writes the characters to the output file via
     the output object output*/
    for (int i = 0; i < theCharArray.length; i++)
    {
        if (Character.isUpperCase(theCharArray[i]))
        {
            try
            {
                output.format("%c", Character.toLowerCase(theCharArray[i]));
                symbolsConverted++;
                symbolsWritten++;
                Symbols_converted_textfield.setText(String.valueOf(symbolsConverted));
                ProgressBar.setValue(100 * (symbolsWritten / totalSymbols));
            }//end try block
            catch (FormatterClosedException formatterClosedException)
            {
                JOptionPane.showMessageDialog(this, "Error writing to file",
                        "Error writing to file", JOptionPane.ERROR_MESSAGE);

            }//end catch
        }//end if

        else
        {
            try
            {
                output.format("%c", theCharArray[i]);
                symbolsWritten++;
                Symbols_converted_textfield.setText(String.valueOf(symbolsConverted));
                ProgressBar.setValue(100 * (symbolsWritten / totalSymbols));
            }//end try block
            catch (FormatterClosedException formatterClosedException)
            {
                JOptionPane.showMessageDialog(this, "Error writing to file",
                        "Error writing to file", JOptionPane.ERROR_MESSAGE);

            }//end catch
        }//end else         
    }//end for
}//end method writeFileLC
Was it helpful?

Solution

There's two things going on here. The first is integer division because symbolsWritten and totalSymbols are both int. So you will never get a fractional value from int / int. It will just jump from 0 to 1. You can make a cast if you want:

bar.setValue((int)Math.round(
    100.0 * ((double)symbolsWritten / (double)totalSymbols)
));

(Either that or define them as double to begin with. It's OK to use ++ on a double if the values are in the range of an int.)

The other is the way you are updating the value because it ought to be done on the Event Dispatch Thread. And it depends on how this method gets called. If this is called in an event, you need to be starting a background thread or the updates will not be shown until the process is complete.

@Override
public void actionPerformed(ActionEvent ae) { // some action event
    final String someString = "SoMe StRiNg"; // some String

    SwingWorker<Void, Void> task = new SwingWorker<Void, Void>() {
        @Override
        public Void doInBackground() {
            writeFileLC(someString.toCharArray());
            return (Void)null;
        }
        @Override
        public void done() {
            try {
                get();
            } catch(Exception e) {
                e.printStackTrace(System.err);
            }
        }
    };

    task.execute();
}

(The funny business in done is weird but important if you want to catch exceptions that get thrown in doInBackground. SwingWorker will eat them but will throw an ExecutionException with the eaten exception as its cause if you call get.)

If you are already running this in a SwingWorker or other Thread, you should be calling these updates on the EDT. Either by using SwingWorker's publish or by using invokeLater.

So when you want to update the progress bar inside writeFileLC:

final int progress = (int)Math.round(
    100.0 * ((double)symbolsWritten / (double)totalSymbols)
);

EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        bar.setValue(progress);
    }
});

publish is a bit less responsive typically than invokeLater because it combines the results. Personally, I usually use invokeLater to update a progress bar but I guess publish is the way you are "supposed" to do it.

Also see Tasks that Have Interim Results for the tutorial on publish.

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