Java Swing очистить очередь событий

Можно ли это сделать стандартным образом?

Вот сценарий.

  1. Начните делать что-нибудь дорогое в EDT (EDT заблокировано, пока дорогостоящая операция не закончится).

  2. Пока EDT был заблокирован, пользователь продолжал нажимать / перетаскивать кнопки мыши. Все действия мыши куда-то записываются.

  3. Когда EDT бесплатен (делается с помощью дорогостоящих вещей), он начинает обрабатывать события мыши.

На шаге 3 я хочу отбросить накопившиеся события мыши. После освобождения EDT любое новое событие мыши должно обрабатываться обычным образом.

Любые идеи о том, как этого добиться.

PS: Я не могу предотвратить блокировку EDT (я не контролирую поведение некоторых модулей в моей программе).

РЕДАКТИРОВАТЬ: если я могу безопасно вызвать «SunToolkit.flushPendingEvents ()», то я всегда могу поставить стеклянную панель перед тем, как начать дорогостоящую операцию в EDT. После завершения дорогостоящей операции в потоке EDT сбросьте все события - они перейдут на стеклянную панель, которая ничего не сделает. И тогда пусть EDT работает в обычном режиме.

EDIT2: я добавил 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
                    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. Запустите SSCCE

  2. Нажмите на кнопку «Подождите 3 секунды». Имитирует дорогостоящую операцию. Курсор мыши изменится на "занято".

  3. Пока курсор занят, нажмите кнопку-переключатель «Нажми меня». Если через три секунды кнопка переключения меняет свое состояние, значит событие мыши было получено кнопкой переключения и не было захвачено.

Я хочу, чтобы, пока курсор выглядит занятым, сгенерированные события мыши (и другие) отбрасывались.

Спасибо.


person Santosh Tiwari    schedule 16.08.2011    source источник
comment
Нет, большинство компонентов Swing (все различные панели, их содержимое и т. Д.) Создаются разными разработчиками, и они могут измениться в любое время. Я не могу сделать никаких предположений об этих компонентах качания. Я могу только сделать видимым стекло на корневой панели, чтобы они не получали события.   -  person Santosh Tiwari    schedule 17.08.2011
comment
У вас есть два решения. 1) Поместите дорогостоящие операции в рабочий поток. 2) Используйте стекло, чтобы заблокировать другие события. Swing не предназначен для защиты приложения от разработчика.   -  person Romain Hippeau    schedule 17.08.2011
comment
не лучший способ изменить или разделить согласованность из EventQueue, но при самом глубоком поиске в этом форуме EventQueue theQueue = getToolkit().getSystemEventQueue(); не поймите, что если вы отправите какие-либо события в EDT вручную, все ожидающие потоки будут потеряны и останутся навсегда в существующей форме , просто не делай этого,   -  person mKorbel    schedule 17.08.2011
comment
1) не надо 2) нет 3) нет - или, другими словами: вместо того, чтобы вдаваться в подробности, пытаясь сделать что-то невероятно анти-свингистское, вложите всю свою энергию в то, чтобы убедить того, кто отвечает, что это недопустимо. нет-никогда-никогда, что вызовет непредсказуемую боль при обслуживании в любое время в будущем   -  person kleopatra    schedule 17.08.2011


Ответы (3)


Хорошо, наконец-то у меня все заработало. Я отправляю SSCCE для правильно работающего примера. Уловка заключается в том, чтобы скрыть стеклянную панель с помощью метода javax.swing.SwingUtilities.invokeLater (). Оберните необходимый код в Runnable, а затем вызовите его с помощью invokeLater. В таком случае Swing обрабатывает все события мыши (ничего не происходит, поскольку их перехватывает стекло), а затем скрывает стекло. Вот 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;

}

Опять же, EDT, если это вообще возможно, не должен блокироваться. Но если вам нужно, у вас может быть рабочий курсор занятости, как указано выше.

Любые комментарии приветствуются.

person Santosh Tiwari    schedule 23.08.2011

Прочтите эту статью.

В принципе, длительные задачи не должны выполняться на EDT. Java предоставила SwingWorker для подобных задач.

Я бы углубился в подробности, но вы не склонны принимать ответы.

person Reverend Gonzo    schedule 17.08.2011
comment
Я использую SwingWorker (нестандартную реализацию, поскольку мой код должен быть совместим с JAVA 1.5) в моем собственном коде. Это другой код, который я не могу контролировать. Спасибо за Ваш ответ. - person Santosh Tiwari; 17.08.2011
comment
Что ж, я вернулся и принял ответы на свои вопросы (на некоторые из них я ответил сам, поэтому я все равно их принял). Что касается тех, где мне помогли другие сообщения, я вернулся и проголосовал за те сообщения, где я вспомнил. - person Santosh Tiwari; 17.08.2011

Определенно не блокируйте EDT. Вы никогда не должны этого делать!

Вот простой служебный класс для этого (заслуга Сантоша Тивари):

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;
   }

}

Но никогда не используйте это. Что ж, если вы не гордитесь тем, что пишете быструю утилиту, которая затем вышла из-под контроля и стала основным приложением, и у вас нет времени разбирать код, чтобы выяснить, что может работать на воркере, а что - break из-за интеграции с Swing / sql, которая не является поточно-ориентированной.

person Kieveli    schedule 28.06.2018