Results 1 to 11 of 11
  1. #1
    snowman418 is offline Member
    Join Date
    Feb 2011
    Posts
    5
    Rep Power
    0

    Default Updating a GUI While Loading

    The program starts with Main.java. My program displays a Swing splash screen (Splash.java), then runs a Database class that loads up all the information from a file.

    The Database class loads in stages, reading from each table until all the information is in memory. What I want is while each stage is completed, the Database updates the Splash screen with that information. This is what I have so far:

    Java Code:
    //Main.java
    
    final Splash splash = new Splash();
    splash.setLocationRelativeTo(null);
    splash.setVisible(true);
    
    new Thread(new Runnable() {
    
        public void run() {
            // Create MainFrame here
            SwingUtilities.invokeLater(new Runnable() {
    
                public void run() {
                    //new Database(splash);
                    try{
                        new Database(splash);
                    }catch(Exception e){
                        System.out.println("Critical software fault: "+e);
                        e.printStackTrace();
                    }
                }
            });
    
        }
    }).start();

    and this is how Database starts...

    Java Code:
    // Database.java
    
    Splash screen;
    
    Database(Splash splash) {
        screen = splash;
    
        initializeDatabase(); // this is the method that reads the database from file
    
        screen.setVisible(false); // discard the splash screen
    }
    
    private void initializeDatabase() {
        loadDriver();
        try {
            Properties props = new Properties();
    
            String dbName = "database"; // the name of the database
    
            g.conn = DriverManager.getConnection(protocol + dbName
                    + ";create=true", props); // connect to the database
    
    ------->*** this is where I want to tell Splash to update a JLabel that the database has been loaded ***
    
            // continue loading tables
    
            // show the main window, and allow the splash to be closed.
    }

    No matter where I try to put the invokeLater code, it just won't update the GUI while loading. Any help is greatly appreciated. Thanks.
    Last edited by snowman418; 02-04-2011 at 08:41 PM.

  2. #2
    Moon_E is offline Member
    Join Date
    Jul 2010
    Posts
    8
    Rep Power
    0

    Default

    You are invoking the initializeDatabase method from within the Event Dispatch Thread which means your calls to update the splash screen are queued untill it is done loading.

  3. #3
    snowman418 is offline Member
    Join Date
    Feb 2011
    Posts
    5
    Rep Power
    0

    Default

    Quote Originally Posted by Moon_E View Post
    You are invoking the initializeDatabase method from within the Event Dispatch Thread which means your calls to update the splash screen are queued untill it is done loading.
    Thanks for the reply, so what's the fix?

  4. #4
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default

    A SwingWorker object could work well for you. Or you could use the SplashScreen display that Java provides for you.

  5. #5
    snowman418 is offline Member
    Join Date
    Feb 2011
    Posts
    5
    Rep Power
    0

    Default

    The replies are appreciated but I'm still unclear on the actual implementation of your suggestions, which is why I'm asking for help (I know the concept, just not the execution). If you could provide adjustment in my code, or where I should shift the worker around and to which methods, that would solve my issue.

  6. #6
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default

    Quote Originally Posted by snowman418 View Post
    The replies are appreciated but I'm still unclear on the actual implementation of your suggestions, which is why I'm asking for help (I know the concept, just not the execution). If you could provide adjustment in my code, or where I should shift the worker around and to which methods, that would solve my issue.
    Unfortunately you're not asking a direct question that can be answered in a straightforward manner, and my goal is not to write your code or shift your code, but have you learn how to do it. Again, I suggest you study the SwingWorker tutorial and then give it a try. Then if it doesn't work, let's see your attempt. Also, perhaps you should work this out use of a SwingWorker with a simpler example first before trying to implement it into your program. The key concept though is that processes that take time should not be run on the main Swing thread, the EDT, but rather should be called in a background thread such as can be done with a SwingWorker. The tutorial is pretty thorough and should give you enough information to get you started, but again, if you get stuck, please show us your latest code and your questions.
    Last edited by Fubarable; 02-05-2011 at 08:49 PM.

  7. #7
    snowman418 is offline Member
    Join Date
    Feb 2011
    Posts
    5
    Rep Power
    0

    Default

    I did that. I've tried every combination of putting SwingWorker into place, either with the Splash or the database initialization. Neither works. I'm not even asking you to write the code but rather where to put the SwingWorker. I'm starting to think that no one actually knows how this works, but just rather how it should work. Practical implementation is vague at best, and the tutorial doesn't directly address where Swing elements, such as JLabels, are updated. It just says that they are. I thank you for your attempt, Fubarable, but I don't think that you know my solution, just rather where I can find the solution. I already know that. I don't drop down to forums to seek assistance when the answer is googleable or can be found in existing information. Obviously, this solution is beyond my understanding, and hence, the interactive solution approach.

    If I could teach myself this, I wouldn't be asking for help.

  8. #8
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default

    Quote Originally Posted by snowman418 View Post
    I did that. I've tried every combination of putting SwingWorker into place, either with the Splash or the database initialization. Neither works.
    "Neither works" doesn't clue us in to what you're doing wrong. Using SwingWorkers for this has worked for me many times, so you have a bug in your code but given the information above we can't tell you where.


    I'm not even asking you to write the code but rather where to put the SwingWorker.
    Since this will be done on start-up of the GUI, I'd probably create the SwingWorker on code startup, possibly in the main method. It will need to be executed on the Swing thread, the EDT, and likely will need an instance of the GUI, the code that holds the JLabel, passed into it.


    I'm starting to think that no one actually knows how this works, but just rather how it should work. Practical implementation is vague at best, and the tutorial doesn't directly address where Swing elements, such as JLabels, are updated. It just says that they are.
    This is always the case. The specifics on how to use this in your program depend entirely on the overall structure of your program, something only you know right now. So while many if not most of us will know how to use a SwingWorker for something like this, no one here excepting you will know how to apply it specifically in your program.


    I thank you for your attempt, Fubarable, but I don't think that you know my solution, just rather where I can find the solution. I already know that.
    The quality of the information we can give you will depend completely on our knowledge of your program and your problem, something we have no control over. You can improve the quality of our answers by creating an SSCCE and giving us as much relevant detail as possible about your problem and your code. The link below on "how to ask smart questions" will help you to figure out just what information we need, but most important is the link (also in my signature links below) on how to create an SSCCE.


    I don't drop down to forums to seek assistance when the answer is googleable or can be found in existing information. Obviously, this solution is beyond my understanding, and hence, the interactive solution approach.
    Then you may be beyond our help (though I doubt this) since the interactive approach is the main approach of this forum.


    If I could teach myself this, I wouldn't be asking for help.
    We know this. I'm trying to help, really I am. So please help me/us help you by creating and posting an SSCCE.

    So again, no one here knows how to fully implement this for your program of course, since we aren't privy to the full structure of your program. But in general, you'll want to create a SwingWorker object and in its doInBackground method start your database. Most folks here could whip up a solution if we had all your code and some time to go through it (the latter is the crux), but we all have lives and responsibilities so this won't work (see the SSCCE suggestion below for a way around this). The SwingWorker's execute method will be called on the EDT. There are several ways to update your JLabel from within the SwingWorker, and probably the best is to give your GUI a public method, say setLabelText(String text) that changes the label's text with the String passed into this method. Then in your SwingWorker use public/process to call this method -- the tutorial will explain this better to you than I can.

    But again, if you're still stuck, create a small simple app that simplifies greatly your problem (no database code for instance), that is an SSCCE, we'll be able to more easily help you including helping you with adjusting your code.

    Luck.
    Last edited by Fubarable; 02-05-2011 at 10:35 PM.

  9. #9
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default

    Another thing, if you put in the effort to create a decent SSCCE, most will be better able to help you and will more likely put in the effort to show you a solution, even a code solution, because your SSCCE will make it much easier for us to do this.

    Again, luck.

  10. #10
    snowman418 is offline Member
    Join Date
    Feb 2011
    Posts
    5
    Rep Power
    0

    Default

    Well I figured it out, and it's not as simple as just saying "use a swingworker."

    In case anyone else has this issue, I'm going to explain it in general terms (which would've answered my question in the first place....) that are strangely simple to point out in the most basic case. Yes, this may not be efficient, and yes, I may not be using it correctly, but the end result is what I want.

    On to the code...


    Main.java (this is the starting program)
    Java Code:
    //Main.java
    public static void main(String args[]){
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new Database(new Splash());
            }
        });
    }

    Database is my "initialization" class, and reads data from file. "Splash" is the GUI that shows during loading. It's a simple JFrame with no frills except a JLabel. I need to pass Database() the Splash because it has to know where to update (good example for people who have another class doing work and want to share the information with another JFrame).

    Here's the Splash

    Java Code:
    // Splash.java
    public Splash() {
        initComponents(); // readies the Swing components
        setVisible(true); // so we can see the frame
        setLocationRelativeTo(null); // centered on the screen
    }
    
    // this is what updates the JLabel ("loadingText" is my JLabel)
    public void updateStatus(String str){
        loadingText.setText(str);
    }

    And here's the Database...
    Java Code:
    public Splash screen; // this is the "Splash" class we passed in Main
    private Worker w; // the SwingWorker
    
    Database(Splash splash) {
        screen = splash; // assign the global so we can update it later
        (w = new Worker()).execute(); // this starts all the magic
    }
    
    private class Worker extends SwingWorker<Object, String> {
    
        @Override
        protected Void doInBackground() {
            // do something very time consuming, like reading a database
            // ... 
            publish("Connected to database..."); // this is what calls the "process()" method in Worker(), note it isn't a LIST (more on that later)
                
            new MainFrame(g).setVisible(true); // this opens my "Main" frame
            screen.setVisible(false); // close the splash
    
            return null; // you need this because SwingWorker demands it
        }
    
        // this is the method called by "process()"
        @Override
        protected void process(List<String> str) { // it is CRITICAL that this is a LIST
            // str.get(str.size()-1) grabs the last element in the list
            screen.updateStatus(str.get(str.size() - 1)); // "Splash.java" has the method "updateStatus(String str)" that updates the JLabel
        }
    }

    The trick was telling process() it had a list of data to update. If you set up the constructor with just a single value, it doesn't work. Instead you make it a list, then grab the last element from the list (the current status). Note that when you call "publish" it does NOT have to be a list. This is what was preventing my updates and is the critical element I was missing. Every time your status changes, just call "publish(String)" again.

    I hope this helps someone in the same boat with wanting to have a SwingWorker update a JFrame in another class.

    // EDIT:

    On a side note, if you want to keep things simple and just hack n' slash this solution into your code, you can leave your method as is. Just put it in the "doProcess()" method.

    For instance,
    Java Code:
    private class Worker extends SwingWorker<Object, String> {
    
        @Override
        protected Void doInBackground() {
            yourMethodGoesHere();
        }

    Simply add a linked list to yourMethodGoesHere(), add to the list every time you want to update the status, then call "worker.process(yourLinkedList)"
    You don't have to call "publish()".

    For example:
    Java Code:
    private void yourMethodGoesHere(){
        List<String> myList = new LinkedList<String>();
        
        // do some stuff
        
        myList.add("this is an updated status to send to the GUI");
        worker.process(myList);
        
        // do more stuff
    }

    I had a serious compiler bug by hacking my code too much and this turned out to be a much more elegant solution.
    Last edited by snowman418; 02-07-2011 at 05:34 PM.

  11. #11
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

Similar Threads

  1. Updating JFrame
    By shadowfax57 in forum AWT / Swing
    Replies: 1
    Last Post: 11-21-2010, 03:01 PM
  2. AWT label not updating
    By Iron Lion in forum New To Java
    Replies: 6
    Last Post: 11-20-2010, 04:19 PM
  3. Component not updating??!!
    By XmisterIS in forum New To Java
    Replies: 5
    Last Post: 09-03-2010, 11:25 AM
  4. Updating Arrays
    By drymsza1234 in forum New To Java
    Replies: 3
    Last Post: 12-06-2009, 02:34 PM
  5. Updating my GUI
    By Catkill in forum AWT / Swing
    Replies: 6
    Last Post: 09-01-2009, 05:09 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •