Solved

GlassPane component not displaying immediately

Posted on 2001-06-05
20
275 Views
Last Modified: 2010-03-31
I have a Swing/JDK 1.3 application that (in a nutshell) retrieves a record from a database and displays the data in an editable screen.

When the user clicks the "Retrieve" button, I process the following instructions:

1) Create and display a GlassPane in the main JFrame that has the Cursor set to an Hourglass, and captures all mouse click events so the user can't click anything when it is displayed.  
2) Retrieve the data from the database
3) Build the editable screen and display it
4) Hide the GlassPane

The problem I am experiencing is that even though I am telling the GlassPane to display itself in step one, it isn't actually displayed until after step 2 (or so).

I am able to work around this by putting the code for steps 2 and 3 in their own thread.  Then the EventThread is able to display the GlassPane.  I would rather NOT do it this way.  

Does anyone have a clean way of doing this?

Thanks,

AGE_Nicolls
0
Comment
Question by:AGE_Nicolls
  • 8
  • 5
  • 3
  • +2
20 Comments
 
LVL 4

Expert Comment

by:kylar
ID: 6157815
If you could post your code then that would help. I'm going to go out on a limb and guess that the event dispatch thread is postponing the repainting and revalidation of the GlassPane for some reason. Putting the database code in another thread is actually a very good idea.  If you could post the code where you set the GlassPane visible, then the call to the dbase etc.. that would help a lot.

Cheers,
Kylar
0
 
LVL 92

Expert Comment

by:objects
ID: 6158109
All Swing stuff happens on the event dispatch thread, so it sounds like your db code is being executed (on its thread) before the glass pane enabling get's handled on the swing thread.

Use SwingUtilities.invokeAndWait() method to enable your glass pane, and then perform your db code.
This'll ensure that the glass is enabled before you execute your db code.

eg.

SwingUtilities.invokeAndWait(new Runnable()
  { public void run() { enableGlassPane(); }});
retrieveDataFromDatabase();
displayData();
disableGlassPane();

0
 
LVL 1

Author Comment

by:AGE_Nicolls
ID: 6159925
Here is my code.  Since the original code is too complex to post, I have created a simplified version the recreates the problem.

I know the problem is that Swing is postponing its events - that is why it works when I put my processing code in its own thread.  But I am looking for someone who can show me a way to force Swing to complete the tasks I give it before my subsequent lines of code are executed.

Oh yeah, I am unable to use the invokeAndWait method since I am running in the event thread.

Thanks,
AGE_Nicolls

==========================================================
==========================================================
==========================================================
==========================================================

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Test {
   
    public static void main(String[] args){
       
        // create JFrame and JButton
        JFrame frm = new JFrame();
        JButton btn = new JButton("Click Me!");
       
        // set glass pane visible (activate it)
        frm.getGlassPane().setVisible(true);
       
        // create MyBtnListener (inner class) that
        // listens to JButton
        btn.addActionListener(new MyBtnListener(frm.getGlassPane()));
       
       
        // set layout and add button to content pane
        frm.getContentPane().setLayout(new BorderLayout());
        frm.getContentPane().add(btn, BorderLayout.CENTER);
       
       
        // size and display JFrame
        frm.setBounds(100, 100, 400, 250);        
        frm.setVisible(true);
       
    }
   
   
    public static class MyBtnListener implements ActionListener {
       
        private Component glassPane;
       
        private MouseListener ml = new MouseAdapter(){
                public void mouseClicked(MouseEvent me){
                    System.out.println("++++++++++++++++++++++++ Glass Pane Clicked! ++++++++++++++++++++++++");
                }
            };
       
        public MyBtnListener(Component glassPane){
            this.glassPane = glassPane;
        }
       
       
        public void actionPerformed(ActionEvent e){
           
            System.out.println("SETTING TO WAIT");          
           
            // add mouse listener to catch an click events
            // while hourglass cursor is displayed
            glassPane.addMouseListener(ml);
           
            // set cursor to hourglass on glass pane
            glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
           
           
            // simulated processing
            for(int x=0; x<10000; x++){
                System.out.print(".");
            }
           
           
           
            System.out.println("SETTING TO DEFAULT");
           
            // remove mouse listener so mouse events get through
            // to components beneath the glass pane
            glassPane.removeMouseListener(ml);
           
            // change cursor back to normal cursor
            glassPane.setCursor(Cursor.getDefaultCursor());
           
        }
    }
}
==========================================================
==========================================================
==========================================================
==========================================================
0
 
LVL 4

Expert Comment

by:kylar
ID: 6160600
did you try forcing a repaint() call before the processing starts?

myFrame.getGlassPane().repaint();

Cheers,
Kylar
0
 
LVL 1

Author Comment

by:AGE_Nicolls
ID: 6160678
yes.  the repaint is pushed off just as the addMouseListener method is.

try running the code I posted.  the goal is to make it so when the processing section of the code is executing, the user cannot click the button.  the best way to do that is to add a mouse listener to the glass pane.  if the glass pane has a registered mouse listener, then the clicks are not passed down to the components beneath it.

if you run this code, you can click the button while the processing code is running.  when it completes its process, it starts processing the second click event and starts all over.  if the mouse listener was being registered immediately, this would not be happening.

one way to reporoduce this is to add the mouse listener at the beginning of code, then you won't ever be able to click the button.

AGE_Nicolls

0
 
LVL 7

Expert Comment

by:Igor Bazarny
ID: 6161132
Hi,

As far as I understand, you don't need glasspane manipulation at all if you don't want to spawn a new thread. Anyway GUI elements will not receive events. If your database process is long, that will affect painting. So, you would better start new thread and keep glasspane manipulations as is.

> if the glass pane has a registered mouse listener, then the clicks are not passed
> down to the components beneath it.
I thought, that's not necessary. Java components shouldn't pass clicks that way. Making glasspane visible should work--it will intercept mouse events. It's not visible (transparent), but otherwise fully functional component. Well, your sample shows that I was not right.  

> If you run this code, you can click the button while the processing code is running.  when it
> completes its process, it starts processing the second click event and starts all over.  if the
> mouse listener was being registered immediately, this would not be happening.
I have another explanation for this effect. While your process is running, event handling loop is blocked and system accumulates mouse clicks in event queue. When you process finishes and deactivates glasspane,
delayed events delivered and processed.

Regards,
Igor Bazarny
Brainbench MVP for Java 1
www.brainbench.com
0
 
LVL 1

Author Comment

by:AGE_Nicolls
ID: 6161185
bazarny - You are right about the OS queuing events.  But, that is not the problem.

The problem is that my mouselistener is not being added until after the processing is finished.  How can I force swing to do it before any other processing occurrs?

Any ideas?

AGE_Nicolls
0
 
LVL 7

Expert Comment

by:Igor Bazarny
ID: 6161393
AGE_Nicols,

Your listener doesn't have any chance to handle events. It's added and removed during handling of one event. Whole event handling mechanism looks like:

while(true){
    Event event = getEventFromQueue();
    dispatch(event);
// Through dispatch, event reaches your ActionListener.
// Note that until dispatch returns, no further event processing
// can occur.  
}

Regards,
Igor Bazarny
0
 
LVL 1

Author Comment

by:AGE_Nicolls
ID: 6161452
doh! I don't know how I missed that one.

at any rate, now I need to figure out a way to remove the mouse listener only after all events that were queued while the processing was going on have fired.

once I can do that, the problem will be solved (i hope).

thanks,
AGE_Nicolls
0
 
LVL 7

Accepted Solution

by:
Igor Bazarny earned 200 total points
ID: 6161568

Just put your porcessing in separate thread. It will look better, believe me. You block not only mouse events, but repaint either. With your example, try to switch to another application and overlap frame by another window. Then switch back to your application and see what it looks like. GUI Event handling should be fast,
long processing should be done in separate threads. BTW, during processing your button is pressed. It doesn't look good and has same reason--events are not handled.

Regards,
Igor Bazarny
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 1

Author Comment

by:AGE_Nicolls
ID: 6161608
I already have my processing in a seperate thread.  

In fact, I have created the following class that works very well...

It extends Thread and has an abstract method that you implement and put your processing code in.  All of the logic to display the wait cursor, etc. is handled automatically.

Here it is if you want to check it out.  

--

Now, I'm still wondering if there is a way to do what I originally asked.  I'm not saying it is the best way to do it, I just want to know how...


--

Thanks,
AGE_Nicolls


public abstract class WaitDisplayThread extends Thread {
   
     /**
      * Create the WaitDisplayThread. The wait cursor is automatically displayed
      * before the processEvent method is called. The cursor is automatically
      * hidden after the wait cursor is complete.
      */
     public WaitDisplayThread() {
         this.start();
     }

     public void run() {          MyApplication.displayWaitCursor(true);
         
          try{              
              processEvent();
          }
          catch(Exception e){
                 e.printStackTrace();
          }
          finally{                      
              MyApplication.displayWaitCursor(false);
          }
     }

     /**
      * This method should perform processing that takes a long time.
      */
     public abstract void processEvent();
}
0
 
LVL 7

Expert Comment

by:Igor Bazarny
ID: 6161687

While your thread is good I would just consider a bit more flexible approach:

public class WaitingRunnable implements Runnable{
     private Runnable block;
     public WaitingRunnable(Runnable longProcess){
           block = longProcess;
     }
     public void run(){
           MyApplication.displayWaitCursor(true);
           try{              
                 processEvent();
           }
           catch(Exception e){
                 e.printStackTrace();
           }
           finally{                      
                 MyApplication.displayWaitCursor(false);
           }    
      }
}

Then you don't need to extend Thread. Just wrap your code into Runnable and Wrap runnable into WaitingRunnable.

As for your initial question, you might consider to modify event queue behaviour. See java.awt.Toolkit.getSystemEventQueue(), java.awt.EventQueue.push() and other EventQueue methods.  I don't think it's worth the efforts, because efforts could be huge--you could create unexpected problems in all GUI by small mistake in EventQueue. And problems will not be easily trackable to event queue, because event handling is a really complex multilevel process.

Regards,
Igor Bazarny
Brainbench MVP for Java 1
www.brainbench.com
0
 
LVL 1

Author Comment

by:AGE_Nicolls
ID: 6161733
In the code example you posted...
======================================================
public class WaitingRunnable implements Runnable{
    private Runnable block;
    public WaitingRunnable(Runnable longProcess){
          block = longProcess;
    }
    public void run(){
          MyApplication.displayWaitCursor(true);
          try{              
                processEvent();
          }
          catch(Exception e){
                e.printStackTrace();
          }
          finally{                      
                MyApplication.displayWaitCursor(false);
          }    
     }
}
======================================================


Did you mean to call run() the Runnable block, like this...

======================================================
public class WaitingRunnable implements Runnable{
    private Runnable block;
    public WaitingRunnable(Runnable longProcess){
          block = longProcess;
    }
    public void run(){
          MyApplication.displayWaitCursor(true);
          try{              
                block.run();
          }
          catch(Exception e){
                e.printStackTrace();
          }
          finally{                      
                MyApplication.displayWaitCursor(false);
          }    
     }
}
======================================================

AGE_Nicolls
0
 
LVL 7

Expert Comment

by:Igor Bazarny
ID: 6161840
Oh, yes. I'm sorry. That evil copy-paste programming :-).
0
 
LVL 92

Expert Comment

by:objects
ID: 6161868

Adding your glass pane as a mouse listener doesn't stop anything else recieving mouse events.
You need to consume the mouse event in your mouse listener:

public void mousePressed(MouseEvent e)
{
  e.consumeEvent();
}

Probably need to do similiar for other mouse event types.

> Oh yeah, I am unable to use the invokeAndWait method
> since I am running in the event thread.

Does that mean you are also doing your processing in the event queue?


0
 
LVL 1

Author Comment

by:AGE_Nicolls
ID: 6164555
>> Adding your glass pane as a mouse listener doesn't stop anything else recieving mouse events. You need to consume the mouse event in your mouse listener

I haven't been consuming them and the events are not getting through.  Are you sure about that?



>>Probably need to do similiar for other mouse event types.

Yeah, all mouse and keyboard events.  I figure once I can get the mouse events to work the way I want them to, the other stuff will work the same.  I like to start out simple, and add complexity as I go along.


>> Does that mean you are also doing your processing in the event queue?

I'm not sure what you mean about processing in the event queue, but since I am running my code in the event thread, all event processing is on hold until my processing completes.
0
 

Expert Comment

by:amp072397
ID: 6175058
AGE, please clean up the open questions you have.

amp
Community Support Moderator
Experts Exchange
0
 
LVL 92

Expert Comment

by:objects
ID: 6175150
> I haven't been consuming them and the events are not
> getting through.  Are you sure about that?

It appears that you don't need to. Well I've learnt something today, excellent :)

> I'm not sure what you mean about processing in the
> event queue, but since I am running my code in the
> event thread, all event processing is on hold until my
> processing completes.

And your gui is frozen.


I ran your test code (Test.class) but it wasn't obvious what it was demonstrating?? It worked as I would have expected it to.


0
 
LVL 1

Author Comment

by:AGE_Nicolls
ID: 6177531
bazarny,

Although you never directly answered my question, you gave me enough reason to not care any more, and just put my code in its own thread.  I appreciate your input and expertise.

Thanks,
AGE_Nicolls
0
 

Expert Comment

by:amp072397
ID: 6179661
(Thank you VERY MUCH, AGE!!)
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

An old method to applying the Singleton pattern in your Java code is to check if a static instance, defined in the same class that needs to be instantiated once and only once, is null and then create a new instance; otherwise, the pre-existing insta…
Java had always been an easily readable and understandable language.  Some relatively recent changes in the language seem to be changing this pretty fast, and anyone that had not seen any Java code for the last 5 years will possibly have issues unde…
Viewers learn about the “while” loop and how to utilize it correctly in Java. Additionally, viewers begin exploring how to include conditional statements within a while loop and avoid an endless loop. Define While Loop: Basic Example: Explanatio…
This tutorial covers a practical example of lazy loading technique and early loading technique in a Singleton Design Pattern.

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now