5

In my Java GUI app I have a JButton and when clicked it calls a function to connect to a database, then calls a function to clear a table in the DB, then calls a function that reads text from one file and loads variables, which calls a function that reads text from another file, compares the data from both and then calls a function to either update or insert data in the DB, all of that works fine.

However my question is related to the JButton, when its clicked I want to run a Indeterminate progress bar just so the user knows work is being done and then right before it leaves the the action listener setIndeterminate to false and set the value of the progress bar to 100(complete), but in my case when you click the button it stays in the clicked state and the progress bar freezes.

What should I implement to prevent this? threading possibly? but Im quite new to threading in java. here is my action listener:

    private class buttonListener implements ActionListener
        {
            public void actionPerformed(ActionEvent e)
            {
                if( e.getSource() == genButton )
                {
                    progressBar.setIndeterminate(true);
                    progressBar.setString(null);
                    try 
                    {
                        dbConnect(); //connects to DB
                        clearSchedules(); // deletes data in tables
                        readFile(); // reads first file and calls the other functions
                        dbClose();// closes the DB
                        progressBar.setIndeterminate(false);
                        progressBar.setValue(100);
                    } 
                    catch (Exception e1){
                        System.err.println("Error: " + e1.getMessage());
                    }
                }
            }
        }

On a side note, I would like to have the action bar actually move as the the program progresses but I wasnt sure how to monitor its progress.

Thanks, Beef.

UPDATE here is my example of SwingWorker and how I used it:

Declared globally

 private functionWorker task;


    private abstract class functionWorker extends SwingWorker { 

        public void execute() {
            try {
                dbConnect();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            clearSchedules();
            try {
                readFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            dbClose();
        }
    }

Inside my actionPerformed method

if( e.getSource() == genButton )
            {

                progressBar.setIndeterminate(true);
                progressBar.setString(null);
                try 
                {
                    task.execute();

                    progressBar.setIndeterminate(false);
                    progressBar.setValue(100);
                } 
                catch (Exception e1){
                    System.err.println("Error: " + e1.getMessage());
                }
            }
Beef
  • 1,413
  • 6
  • 21
  • 36
  • please learn java naming conventions and stick to them – kleopatra Sep 24 '11 at 08:40
  • typically, override doInBackground not execute ... which is final anyway. Check your jdk and classpath, if on 6+ remove any swingworker.jar (the old stand-alone version) – kleopatra Sep 24 '11 at 08:48
  • @kleopatra what exactly is wrong with my naming convention? I read up on it and I dont see where I went wrong, at least in these code snippets – Beef Sep 26 '11 at 02:47
  • class names should start upper case – kleopatra Sep 26 '11 at 06:28
  • @kleopatra ah thanks, I will fix that, and I am using the jdk 7 – Beef Sep 26 '11 at 12:37
  • in jdk6+ your functionWorker doesn't compile (execute is final), so I suspect there's an old swingworker.jar somewhere in the classpath - find and remove – kleopatra Sep 26 '11 at 13:17
  • @kleopatra I installed the JDK 7 and edited the `CLASSPATH` and `PATH` in native variables in the systems advanced settings, but I dont think `execute` and `doInBackGround` are overridden methods in my swing work, cause when I tell it to add unimplemented/override methods it only adds one called construct – Beef Sep 26 '11 at 15:08
  • one last time: if that code snippet above (functionWorker) compiles that functionWorker, then the SwingWorker it extends is _not_ the javax.swing.SwingWorker in core (jdk6/7). It's up to you to track down where it comes from ... if you don't you work against an instable system – kleopatra Sep 26 '11 at 15:14

4 Answers4

7

The problem is probably related to connecting to doing expensive operations in the UI thread (connecting to a database, reading from a file, calling other functions). Under no circumstances should you call code that uses excessive CPU time from the UI thread, as the entire interface can't proceed while it is executing your code, and it results in a 'dead' looking application, with components remaining in their state at the time before an expensive operation until completion. You should execute another thread, do the expensive work in that, and then use a SwingUtilities.invokeLater(Runnable doRun) with a passed runnable where you'd update the progress.

There may be synchronisation issues relating to the states of components, but you can fix these later.

Chris Dennett
  • 22,412
  • 8
  • 58
  • 84
  • by expensive operations do you mean the operations that have to go outside of the java program itself, such as to the text file and db? – Beef Sep 23 '11 at 15:02
  • Yep. Expensive in terms of CPU time. – Chris Dennett Sep 23 '11 at 15:02
  • so could I create the new thread when the action is performed and call the new functions in the thread, or should I do the threading within the actual function itself? – Beef Sep 23 '11 at 15:05
  • You should create the new thread within the listener method. It's not really an expensive operation. There are ways to improve this (using executors created externally, for instance), but it's not important. – Chris Dennett Sep 23 '11 at 15:09
  • 1
    Alright, I havnt done much threading in java, and didnt have a good understanding of I did, but I'll give it another go, thanks for your help – Beef Sep 23 '11 at 15:11
6

Could I create the new thread when the action is performed and call the new functions in the thread, or should I do the threading within the actual function itself?

You can start a SwingWorker from your button's handler, as shown here. A related example implementing Runnable is seen here.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks, I'll see what I can pull from those examples and try to implement them accordingly – Beef Sep 23 '11 at 15:28
  • Looking closer at your side note, [`TwoRoot`](http://stackoverflow.com/questions/4637215/can-a-progress-bar-be-used-in-a-class-outside-main/4637725#4637725) is an example showing incremental progress in a `SwingWorker`.. – trashgod Sep 23 '11 at 15:33
  • thanks again, I'll check that out once done with the threading part, still trying to breakdown the examples – Beef Sep 23 '11 at 15:38
  • so would I create a `SwingWorker` just like `private class functionWorker extends SwingWorker {}` but call my 4 methods inside the `SwingWorker` and then create task of the `SwingWorker` and execute that task in my `actionlistener`, seems like theres alot going on in that first example, having trouble getting my head wrapped around it – Beef Sep 23 '11 at 16:17
  • Indeed, [`TableAddTest`](http://stackoverflow.com/questions/7519244/jar-bundler-using-osxadapter-causing-application-to-lag-or-terminate/7519403#7519403) _looks_ easier, but `SwingWorker` offer more leverage. [`RandomDataDemo`]() shows a DB connection. It declares `SwingWorker, Double>`, but you'll have to choose your desired granularity. Maybe `SwingWorker, Record>`, where `Record` represents one file's content and metadata? – trashgod Sep 23 '11 at 16:30
  • Im not sure I understand how I decide what parameters to have for `SwingWorker` or the use of them – Beef Sep 23 '11 at 16:35
  • I always have to look at the [API](http://download.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html). `V` is the basic unit of work, while `T` is usually a collection of `V`. – trashgod Sep 23 '11 at 16:43
5

One method to handle progressbars are to extend SwingWorker in a class. SwingWorker takes care of running background tasks for you and so you do not have to implement your own threading that can end up in unknown issues. To begin with, your class that takes care of progress bar UI should implement PropertyChangeListener And implement public void propertyChange(PropertyChangeEvent evt) { - to update the progressbar status based on a global variable.

The background task class should look like the following(this could be an inner class) :

class ProgressTask extends SwingWorker<Void, Void> {
        @Override
        public Void doInBackground() {
             //handle your tasks here
             //update global variable to indicate task status.
        }
        @Override
        public void done() {
             //re-enabled your button
        }
}

on your button's event listener :

 public void actionPerformed(ActionEvent evt) {
    //disable your button
    //Create new instance of "ProgressTask"
    //make the task listen to progress changes by task.addPropertyChangeListener(this);
    //calll task.execute();
 }

I have tried to water down code example, you would have to read some tutorial to understand how all these pieces fit together. However, the main point is do not code your Threads, instead use SwingWorker

ring bearer
  • 20,383
  • 7
  • 59
  • 72
  • Im not sure I follow, my program doesnt like the `@Override` methods or the implements of `PropertyChangeListener` or the `Void, Void` arguments – Beef Sep 23 '11 at 17:18
  • Ooops..what JDK are you using ? all these are J2 SE 5 + features :) – ring bearer Sep 23 '11 at 17:40
  • I was using the latest version but Ive recently been using a new user account so Im not entirely sure – Beef Sep 23 '11 at 17:50
0
                    progressBar.setIndeterminate(true);
                    progressBar.setValue(0);                        
dbConnect(); //connects to DB
                    progressBar.setIndeterminate(true);
                    progressBar.setValue(10);
                    clearSchedules(); // deletes data in tables
                    progressBar.setIndeterminate(true);
                    progressBar.setValue(50);
                    readFile(); // reads first file and calls the other functions
                    progressBar.setIndeterminate(true);
                    progressBar.setValue(75);
                    dbClose();// closes the DB
                    progressBar.setIndeterminate(false);
                    progressBar.setValue(100);

You will need to tell the progress bar how much progress has been made because it does not know the percentage completed. Better yet, write a method that updates and repaints the progress bar rather than repeating the method calls here.

updateProgressBar(int progress, boolean isDeterminate, String msg){};

You will also need to make sure that your specific button is firing the action performed.

class IvjEventHandler implements java.awt.event.ActionListener {
        public void actionPerformed(java.awt.event.ActionEvent e) {
            if (e.getSource() == JMyPanel.this.getJButtonUpdate()) 
                connEtoC1(e);
    };
};

The connEtoC1(e); should execute a controller class or SwingWorker rather than firing from the GUI

iowatiger08
  • 1,892
  • 24
  • 30