java swing clear the event queue

a 夏天 提交于 2019-12-01 18:00:27

问题


Is it possible to do this in a standard manner?

Here is the scenario.

  1. Start doing something expensive in EDT (EDT is blocked till the expensive operation is over).

  2. While EDT was blocked, the user kept on clicking/dragging the mouse buttons. All the mouse actions are recorded somewhere.

  3. When EDT is free (done with the expensive stuff), it starts to process the mouse events.

What I want in step 3 is to discard the mouse events that have piled up. After the EDT is free, any new mouse event should be handled in the usual manner.

Any ideas on how to achieve this.

PS: It is not possible for me to prevent the EDT from getting blocked (I do not control the behavior of some of the modules in my program).

EDIT: If I can safely call "SunToolkit.flushPendingEvents()" then I can always put a glasspane before starting the expensive operation in EDT. After the expensive operation is over then on the EDT thread, flush all the events - they will go to a glass pane that wont do anything. And then let EDT work as normal.

EDIT2: I have added a SSCCE to demonstrate the issue.


public class BusyCursorTest2 extends javax.swing.JFrame {

    public BusyCursorTest2() {

        javax.swing.JButton wait = new javax.swing.JButton("Wait 3 seconds");
        getContentPane().setLayout(new java.awt.GridLayout(2, 1, 0, 0));
        getContentPane().add(wait);
        getContentPane().add(new javax.swing.JToggleButton("Click me"));
        setTitle("Busy Cursor");
        setSize(300, 200);
        setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
        setVisible(true);

        wait.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent event) {

                final java.util.Timer timer = switchToBusyCursor(BusyCursorTest2.this);

                try {
                    //do something expensive in EDT
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        //do nothing
                    }
                } finally {
                    switchToNormalCursor(BusyCursorTest2.this, timer);
                }
            }

        });
    }

    public static java.util.Timer switchToBusyCursor(final javax.swing.JFrame frame) {
        startEventTrap(frame);
        java.util.TimerTask timerTask = new java.util.TimerTask() {

            public void run() {
                startWaitCursor(frame);
            }

        };
        final java.util.Timer timer = new java.util.Timer();
        timer.schedule(timerTask, DELAY_MS);
        return timer;
    }

    public static void switchToNormalCursor(final javax.swing.JFrame frame, final java.util.Timer timer) {
        timer.cancel();
        stopWaitCursor(frame);
        stopEventTrap(frame);
    }

    private static void startWaitCursor(javax.swing.JFrame frame) {
        frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
        frame.getGlassPane().addMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(true);
    }

    private static void stopWaitCursor(javax.swing.JFrame frame) {
        frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
        frame.getGlassPane().removeMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(false);
    }

    private static void startEventTrap(javax.swing.JFrame frame) {
        frame.getGlassPane().addMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(true);
    }

    private static void stopEventTrap(javax.swing.JFrame frame) {
        frame.getGlassPane().removeMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(false);
    }

    private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {
    };

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                new BusyCursorTest2();
            }

        });

    }

    private static final int DELAY_MS = 250;

}
  1. Run the SSCCE

  2. Click on the button "Wait 3 seconds". It simulates an expensive operation. The mouse cursor will change to busy.

  3. While the cursor is busy, click on the toggle button "Click me". If after three seconds, the toggle button changes its state, then the mouse event was received by the toggle button and was not trapped.

I want that while the cursor looks busy, the generated mouse (and other) events be discarded.

Thanks.


回答1:


OK, I finally got everything to work. I am posting the SSCCE for a correctly working example. The trick is to hide the glasspane using "javax.swing.SwingUtilities.invokeLater()" method. Wrap the necessary code in a Runnable and then invoke it using invokeLater. In such a case, Swing processes all the mouse events (nothing happens since a glasspane intercepts them), and then hides the glasspane. Here is the SSCCE.

public class BusyCursorTest2 extends javax.swing.JFrame {

    public BusyCursorTest2() {

        javax.swing.JButton wait = new javax.swing.JButton("Wait 3 seconds");
        getContentPane().setLayout(new java.awt.GridLayout(2, 1, 0, 0));
        getContentPane().add(wait);
        getContentPane().add(new javax.swing.JToggleButton("Click me"));
        setTitle("Busy Cursor");
        setSize(300, 200);
        setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
        setVisible(true);

        wait.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent event) {

                final java.util.Timer timer = switchToBusyCursor(BusyCursorTest2.this);

                try {
                    //do something expensive in EDT or otherwise
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        //do nothing
                    }
                } finally {
                    switchToNormalCursorEventThread(BusyCursorTest2.this, timer);
                }

            }

        });
    }

    public static java.util.Timer switchToBusyCursor(final javax.swing.JFrame frame) {
        startEventTrap(frame);
        java.util.TimerTask timerTask = new java.util.TimerTask() {

            public void run() {
                startWaitCursor(frame);
            }

        };
        final java.util.Timer timer = new java.util.Timer();
        timer.schedule(timerTask, DELAY_MS);
        return timer;
    }

    public static void switchToNormalCursorEventThread(final javax.swing.JFrame frame, final java.util.Timer timer) {

        Runnable r = new Runnable() {

            public void run() {
                switchToNormalCursor(frame, timer);
            }

        };

        javax.swing.SwingUtilities.invokeLater(r);

    }

    public static void switchToNormalCursor(final javax.swing.JFrame frame, final java.util.Timer timer) {
        timer.cancel();
        stopWaitCursor(frame);
        stopEventTrap(frame);
    }

    private static void startWaitCursor(javax.swing.JFrame frame) {
        frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
        frame.getGlassPane().addMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(true);
    }

    private static void stopWaitCursor(javax.swing.JFrame frame) {
        frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
        frame.getGlassPane().removeMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(false);
    }

    private static void startEventTrap(javax.swing.JFrame frame) {
        frame.getGlassPane().addMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(true);
    }

    private static void stopEventTrap(javax.swing.JFrame frame) {
        java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue();
        frame.getGlassPane().removeMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(false);
    }

    private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {
    };

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                new BusyCursorTest2();
            }

        });

    }

    private static final int DELAY_MS = 250;

}

Again, EDT if at all possible must not be blocked. But if you have to, you can have a working busy cursor as above.

Any comments are welcome.




回答2:


Read this article.

Basically, long running tasks should not be done on the EDT. Java has provided SwingWorker for tasks such as that.

I'd go into more detail, but you don't tend to accept answers.




回答3:


Definitely don't block the EDT. You should never do that!

Here's an easy utility class to do this (credit to Santosh Tiwari) :

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

/**
 * When blocking the EDT (Event Queue) in swing, the cursor won't update, and windows won't render.
 * This should show the hourglass even when you're blocking the EDT.
 *
 * Source:
 * https://stackoverflow.com/questions/7085239/java-swing-clear-the-event-queue
 * 
 * @author Kieveli, Santosh Tiwari
 *
 */
public class BlockingWaitCursor {

   private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {};

   /**
    * The Dialog or main window is required to show the cursor and animate it. The actionListener is called
    * as soon as initial setup is completed and the animation timer is running.
    * @param currentComponent A panel, dialog, frame, or any other swing component that is the current focus
    * @param action Your action to perform on the EDT. This is started extremely quickly and without delay.
    */
   public static void showWaitAndRun(Component currentComponent, ActionListener action ) {

      Timer timer = setupWaitCursor(currentComponent);

      try {
         // now allow our caller to execute their slow and delayed code on the EDT
         ActionEvent event = new ActionEvent(BlockingWaitCursor.class, ActionEvent.ACTION_PERFORMED, "run");
         action.actionPerformed(event);
      }
      finally {
         resetWaitCursor(currentComponent, timer);
      }
   }

   private static Timer setupWaitCursor(Component currentComponent) {
      final Component glassPane = findGlassPane(currentComponent);
      if ( glassPane == null ) {
         return null;
      }

      // block mouse-actions with a glass pane that covers everything
      glassPane.addMouseListener(mouseAdapter);
      glassPane.setVisible(true);

      // animate the wait cursor off of the EDT using a generic timer.
      Timer timer = new Timer();
      timer.schedule( new TimerTask() {
         @Override
         public void run() {
            glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            glassPane.addMouseListener(mouseAdapter);
            glassPane.setVisible(true);
         }

      }, 250l);

      return timer;
   }

   private static void resetWaitCursor(Component currentComponent, final Timer timer) {
      final Component glassPane = findGlassPane(currentComponent);
      if ( glassPane == null ) {
         return;
      }
      // Invoke later so that the event queue contains user actions to cancel while the loading occurred
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            if ( timer != null )
               timer.cancel();
            Toolkit.getDefaultToolkit().getSystemEventQueue();
            glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            glassPane.removeMouseListener(mouseAdapter);
            glassPane.setVisible(false);  
         }
      });
   }

   private static Component findGlassPane(Component currentComponent) {
      // try to locate the glass pane by looking for a frame or dialog as an ancestor
      JFrame frame = findFrame(currentComponent);
      JDialog dialog = findDialog(currentComponent);
      Component glassPane = null;
      if ( frame != null )
         glassPane = frame.getGlassPane();
      if ( dialog != null )
         glassPane = dialog.getGlassPane();
      return glassPane;
   }

   private static JFrame findFrame(Component currentComponent) {
      // find the frame if it exists - it may be the currentComponent
      if ( currentComponent instanceof JFrame )
         return (JFrame) currentComponent;

      Window window = SwingUtilities.getWindowAncestor(currentComponent);
      if ( window == null )
         return null;
      if ( ! (window instanceof JFrame) )
         return null;
      return (JFrame)window;
   }

   private static JDialog findDialog(Component currentComponent) {
      // find the dialog if it exists - it may be the currentComponent
      if ( currentComponent instanceof JDialog )
         return (JDialog) currentComponent;

      Window window = SwingUtilities.getWindowAncestor(currentComponent);
      if ( window == null )
         return null;
      if ( ! (window instanceof JDialog) )
         return null;
      return (JDialog)window;
   }

}

But never use it. Well, unless you're not proud and writing a quick utility that then got out of hand and became a main application, and you don't have time to pull your code apart to figure out what can run on a worker, and what would break because of integrations with swing / sql that are not thread-safe.



来源:https://stackoverflow.com/questions/7085239/java-swing-clear-the-event-queue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!