JButton stays in pressed state
Asked Answered
S

4

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());
                }
            }
Seamus answered 23/9, 2011 at 14:54 Comment(8)
please learn java naming conventions and stick to themNovelist
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)Novelist
@Novelist 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 snippetsSeamus
class names should start upper caseNovelist
@Novelist ah thanks, I will fix that, and I am using the jdk 7Seamus
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 removeNovelist
@Novelist 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 constructSeamus
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 systemNovelist
C
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.

Chantry answered 23/9, 2011 at 14:58 Comment(5)
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?Seamus
Yep. Expensive in terms of CPU time.Chantry
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?Seamus
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.Chantry
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 helpSeamus
W
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.

Wanda answered 23/9, 2011 at 15:12 Comment(7)
Thanks, I'll see what I can pull from those examples and try to implement them accordinglySeamus
Looking closer at your side note, TwoRoot is an example showing incremental progress in a SwingWorker..Wanda
thanks again, I'll check that out once done with the threading part, still trying to breakdown the examplesSeamus
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 itSeamus
Indeed, TableAddTest looks easier, but SwingWorker offer more leverage. [RandomDataDemo]() shows a DB connection. It declares SwingWorker<List<Double>, Double>, but you'll have to choose your desired granularity. Maybe SwingWorker<List<Record>, Record>, where Record represents one file's content and metadata?Wanda
Im not sure I understand how I decide what parameters to have for SwingWorker or the use of themSeamus
I always have to look at the API. V is the basic unit of work, while T is usually a collection of V.Wanda
P
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

Peptone answered 23/9, 2011 at 15:11 Comment(3)
Im not sure I follow, my program doesnt like the @Override methods or the implements of PropertyChangeListener or the Void, Void argumentsSeamus
Ooops..what JDK are you using ? all these are J2 SE 5 + features :)Peptone
I was using the latest version but Ive recently been using a new user account so Im not entirely sureSeamus
O
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

Obligee answered 23/9, 2011 at 15:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.