Forum Stats

  • 3,825,882 Users
  • 2,260,571 Discussions
  • 7,896,720 Comments

Discussions

How to display "Loading data..." to the user

843805
843805 Member Posts: 49,999
edited Dec 6, 2006 3:56AM in Swing
Hi I have a complex ui screen.

In that there is a panel, which displays a graph to the user and it updates to a different graph, depepnding upon the user clicks.
I need to get a lot of data from the backend and then change the graph to the user.

When i am loading hte data, i need to change the panel display to indicate that the data is being acquired and processed.

I dont want to use the cardlayout,
i want to know what other appoaches i can use to show the information to the user.

One of the things i have beein thinking about is dim the current panel and show a message box inside the panel i am updating.

Does anyone know how i can change the alpha(and make it dimmer) in case of a JPanel and throw a small popupwindow inside th epanel to display the message?

any clue or hint is greately appreciated,

thanks,
vani
«1

Comments

  • 843805
    843805 Member Posts: 49,999
    You could have a non-opaque JPanel above your panel in an Overlay Layout and draw on it with an AlphComposite for a "dimming" affect. Also check out the JProgressBar and ProgressMonitor classes. The Swing tutorial has a page in which these are discussed: How to Use Progress Bars.
  • 843805
    843805 Member Posts: 49,999
    Another option is using a glass pane above the app and do the same thing that crwood mentioned (alpha composite painting, JProgressBars, etc).
  • 843805
    843805 Member Posts: 49,999
    Personally, I like crwood's suggestion. Extend JPanel, override the paintComponent method, and paint a partially transparent rectangle over the entire thing.

    However, beware of this problem: Most Component subclasses have asynchronous repaint methods (such as JPanel and JLabel). That means that the work of actually painting each pixel runs in another thread, which usually doesn't finish until other operations are complete. In other words, your screen dimming and loading data message may not occur until after the data has already been loaded.

    To solve this problem, call the Components's paintImmediately method right after you display the it. That forces the paint operation to be executed in the same thread, so the desired action will occur BEFORE the data starts loading (which is the behavior you are trying to create). Note, that JProgressBar is immune from this problem.

    I fought this issue for hours, so I'm just giving you the heads up.

    Hope it helps,
    Bill

    PS: A lot more info is available here: http://java.sun.com/products/jfc/tsc/articles/painting/ . Pay attention to the stuff about synchronization and threads.
  • camickr
    camickr Member Posts: 24,931
    edited Dec 2, 2006 2:26PM
    Personally, I like crwood's suggestion. Extend JPanel, override the paintComponent method....
    That is what a GlassPane is for. You create the panel as described. Then you use:

    frame.setGlassPane(...);
    To solve this problem, call the Components's paintImmediately method...
    The real problem is that you are trying to execute a long running task in the GUI Event Thread. The long running task should execute in its own Thread so the GUI is free to repaint itself.
    How to display "Loading data..." to the user
    If you really want to display some text, then you should probably be using an indeterminate progress bar since is displays an actual window with borders. If you just try to write text on the JPanel or GlassPane, you never know what you will be painting over unless you customize the painting to fill in the background before you write the text.
  • 843805
    843805 Member Posts: 49,999
    That is what a GlassPane is for. You create the panel
    as described. Then you use:
    frame.setGlassPane(...);
    The glass pane will work fine too. I'd choose the overlay approach simply because it allows me to create a JPanel subclass DimmableJPanel that doesn't rely on a JFrame to help. For the benefit of the OP, heres a link that explains the glass pane: http://java.sun.com/docs/books/tutorial/uiswing/components/rootpane.html
    The real problem is that you are trying to execute a
    long running task in the GUI Event Thread. The long
    running task should execute in its own Thread so the
    GUI is free to repaint itself.
    Yep. Thats why repaint starts a new thread. But in this case, the text has to be painted before starting a new task, hence the call to paintImmediately. JProgressBar does the same thing, because progress bars have to be updated in real time.
    If you really want to display some text, then you
    should probably be using an indeterminate progress
    bar since is displays an actual window with borders.
    If you just try to write text on the JPanel or
    GlassPane, you never know what you will be painting
    over unless you customize the painting to fill in the
    background before you write the text.
    Right. But if you don't want to use an indeterminate progress bar, you can recreate that same behavior with a JLabel and paintImmediately. (Yes, you do have to accept responsibility for the background if you don't repaint the entire JPanel.)

    - Bill
  • camickr
    camickr Member Posts: 24,931
    I'd choose the overlay approach simply because it allows me to
    create a JPanel subclass DimmableJPanel that doesn't rely on a JFrame to help
    Don't understand this statement. How do you show a DimmableJPanel overtop of an existing panel?
    But in this case, the text has to be painted before starting a new task, hence the call to paintImmediately.
    There is no need to use paintImmediately. You update the GUI component. Then you start a separate Thread for the long running task. The GUI is free to repaint the label or whatever. Yes, technically you may have to wait milliseconds (over using paintImmediately), but the user will see no difference. paintImmediately is not recommended to be used. The RepaintManager manages paint requests and consolidates multiple paint requests into one for efficiency reasons. Using paintImmediately overrides the default behaviour and is not needed in this situation.
    Right. But if you don't want to use an indeterminate progress bar, you
    can recreate that same behavior with a JLabel and paintImmediately.
    The point is not to recreate code, but to use the code that is available.

    I was also suggesting that a progress bar is a dialog that only covers a certain amount of space. A disable panel would cover the entire frame. So it depends on the look that the user is going for.

    Here is an example of a disable GlassPane:
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.border.*;
    
    public class DisabledGlassPane extends JComponent
    	implements KeyListener
    {
    	private final static Color DEFAULT_BACKGROUND = new Color(128, 128, 128, 128);
    	private final static Border MESSAGE_BORDER = new EmptyBorder(10, 10, 10, 10);
    
    	private JLabel message = new JLabel();
    
    	public DisabledGlassPane()
    	{
    		setOpaque( false );
    		setBackground( DEFAULT_BACKGROUND );
    		setLayout( new GridBagLayout() );
    		add(message, new GridBagConstraints());
    
    		message.setOpaque(true);
    		message.setBorder(MESSAGE_BORDER);
    
    		//  Disable Mouse, Key and Focus events for the glass pane
    
    		addMouseListener( new MouseAdapter() {} );
    		addMouseMotionListener( new MouseMotionAdapter() {} );
    
    		addKeyListener( this );
    
            setFocusTraversalKeys(
                KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.EMPTY_SET );
            setFocusTraversalKeys(
                KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, Collections.EMPTY_SET );
    	}
    
    	protected void paintComponent(Graphics g)
    	{
    		g.setColor( getBackground() );
    		g.fillRect(0, 0, getSize().width, getSize().height);
    	}
    
    	public void setBackground(Color background)
    	{
    		super.setBackground( background );
    
    		Color messageBackground =
    			new Color(background.getRed(), background.getGreen(), background.getBlue());
    		message.setBackground( messageBackground );
    	}
    
    	public void keyPressed(KeyEvent e)
    	{
    		e.consume();
    	}
    
    	public void keyTyped(KeyEvent e)
    	{
    		e.consume();
    	}
    	public void keyReleased(KeyEvent e)
    	{
    		e.consume();
    	}
    
    	public void activate(String text)
    	{
    		if  (text != null && text.length() > 0)
    		{
    			message.setVisible( true );
    			message.setText( text );
    			message.setForeground( getForeground() );
    		}
    		else
    			message.setVisible( false );
    
    		setVisible( true );
    		setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    		requestFocusInWindow();
    	}
    
    	public void deactivate()
    	{
    		setCursor(null);
    		setVisible( false );
    	}
    
    	public static void main(String[] args)
    	{
    		final DisabledGlassPane glassPane = new DisabledGlassPane();
    		glassPane.setBackground( new Color(255, 128, 128, 128) );
    		glassPane.setForeground( Color.WHITE );
    
    		final JButton button = new JButton( "Click Me" );
    		button.setMnemonic('c');
    		button.addActionListener( new ActionListener()
    		{
    			public void actionPerformed(ActionEvent e)
    			{
    				glassPane.activate("Please Wait...");
    
    				Thread thread = new Thread()
    				{
    					public void run()
    					{
    						try { this.sleep(5000); }
    						catch (InterruptedException ie) {}
    
    						glassPane.deactivate();
    					}
    				};
    				thread.start();
    			}
    		});
    
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    		frame.setGlassPane( glassPane );
    		frame.getContentPane().add(new JLabel("NORTH"), BorderLayout.NORTH );
    		frame.getContentPane().add( button );
    		frame.getContentPane().add(new JTextField(), BorderLayout.SOUTH);
    		frame.setSize(300, 300);
    		frame.setLocationRelativeTo( null );
    		frame.setVisible(true);
    	}
    }
  • 843805
    843805 Member Posts: 49,999
    hi codebook, using a glasspane is not a viable option for me, because i want to display the Loading data on only one of the panels in the frame.

    vani
  • 843805
    843805 Member Posts: 49,999
    Hi All,
    i went through all of your reply and unfortunately only a part of the screen is being updated and only that panel need to show the "Loading Data..." message not the whole screen. So i can not use the glass pane nad block the whole screen.

    If some one knows how to update only a part of the screen, that would be very helpful.

    vani
  • 843805
    843805 Member Posts: 49,999
    Is there a way to set the Z order of the Panels in the Overlay layout?
    How can i choose the Panel whoich should be showing on top?

    thanks
  • 843805
    843805 Member Posts: 49,999
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
     
    public class DimOverlay {
        DimmerPanel dimmerPanel;
     
        private JPanel getContent() {
            dimmerPanel = new DimmerPanel();
            JPanel content = new JPanel(new BorderLayout());
            JLabel label = new JLabel("graphical data", JLabel.CENTER);
            content.add(label);
            JPanel panel = new JPanel();
            OverlayLayout overlay = new OverlayLayout(panel);
            panel.setLayout(overlay);
            panel.add(dimmerPanel);
            panel.add(content);
            return panel;
        }
     
        private void reload() {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    int count = 0;
                    boolean workAway = true;
                    while(workAway && count <= 10) {
                        try {
                            Thread.sleep(1000);
                        } catch(InterruptedException e) {
                            workAway = false;
                        }
                        System.out.println("count = " + count++);
                    }
                    dimmerPanel.clearView();
                }
            });
            thread.setPriority(Thread.NORM_PRIORITY);
            thread.start();
            dimmerPanel.dimView();
        }
     
        private JPanel getControl() {
            JButton reload = new JButton("reload");
            reload.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    reload();
                }
            });
            JPanel panel = new JPanel();
            panel.add(reload);
            return panel;
        }
     
        public static void main(String[] args) {
            DimOverlay test = new DimOverlay();
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.getContentPane().add(test.getContent());
            f.getContentPane().add(test.getControl(), "Last");
            f.setSize(400,400);
            f.setLocation(200,200);
            f.setVisible(true);
        }
    }
     
    class DimmerPanel extends JPanel {
        float alpha = 0.0f;
     
        public DimmerPanel() {
            setOpaque(false);
        }
     
        public void dimView() {
            alpha = 0.25f;
            repaint();
        }
     
        public void clearView() {
            alpha = 0.0f;
            repaint();
        }
     
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D)g;
            AlphaComposite ac =
                AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
            g2.setComposite(ac);
            g2.setPaint(Color.gray);
            g2.fillRect(0, 0, getWidth(), getHeight());
        }
    }
This discussion has been closed.