Обнаружение, когда пользователь заканчивает изменение размера оболочки SWT - PullRequest
3 голосов
/ 16 января 2010

У меня есть оболочка SWT, размер которой можно изменить. Каждый раз, когда он изменяется, я должен делать что-то интенсивное в вычислительном отношении.

Я могу зарегистрировать ControlListener в своей оболочке, но это генерирует события непрерывно на протяжении всей операции изменения размера, и я не знаю, когда закончится операция мыши с изменяемым размером перетаскивания.

Я хотел бы иметь возможность определить, когда пользователь завершил , изменив размер оболочки, а затем начать мою вычислительно-интенсивную операцию. Есть идеи, как это сделать?

Ответы [ 5 ]

6 голосов
/ 16 января 2010

Как насчет использования таймера и начала работы через, скажем, одну секунду после последнего полученного события изменения размера? Черновик:

long lastEvent;

ActionListener taskPerformer = new ActionListener() {
            public void doCalc(ActionEvent evt) {
                if ( (lastEvent + 1000) < System.currentTimeMillis() ) {
                   hardcoreCalculationTask();
                } else {
                  // this can be timed better
                  new Timer(1000, taskPerformer).start();
                }
            }
        };
}

В вашем событии изменения размера:

 lastEvent = System.currentTimeMillis();
 new Timer(1000, taskPerformer).start();
3 голосов
/ 20 февраля 2012

Приведенное ниже решение было разработано на основе стекера и во многом аналогично, за исключением того, что оно использует только SWT API, а также проверяет, нажата ли кнопка мыши перед запуском задачи, интенсивно использующей процессор.

Сначала тип, который выполняет работу:

private class ResizeListener implements ControlListener, Runnable, Listener {

    private long lastEvent = 0;

    private boolean mouse = true;

    public void controlMoved(ControlEvent e) {
    }

    public void controlResized(ControlEvent e) {
        lastEvent = System.currentTimeMillis();
        Display.getDefault().timerExec(500, this);
    }

    public void run() {
        if ((lastEvent + 500) < System.currentTimeMillis() && mouse) {
        ...work
        } else {
            Display.getDefault().timerExec(500, this);
        }
    }
    public void handleEvent(Event event) {
        mouse = event.type == SWT.MouseUp;
    }

}

Тогда нам нужно зарегистрировать это. Также не забудьте отменить регистрацию, когда закончите. Можно также захотеть изменить компонент, используемый для прослушивания мыши, чтобы быть немного более конкретным.

    ResizeListener listener = new ResizeListener();
    widget.addControlListener(listener);
    widget.getDisplay().addFilter(SWT.MouseDown, listener);
    widget.getDisplay().addFilter(SWT.MouseUp, listener);
3 голосов
/ 16 января 2010

Вот альтернативное предложение для той же проблемы: [platform-swt-dev] Прослушиватель изменения размера мыши :

Вы можете попробовать установить флаг и отложить изменение размера с помощью Display.asyncExec (). Когда вы получите изменение размера, если флаг установлен, просто вернитесь. Это должно вызывать изменение размера только тогда, когда пользовательский интерфейс простаивает.

Моя идея заключалась в том, чтобы прослушивать события мыши, но очевидно (я только что попробовал), события мыши не запускаются для действий мыши на границе оболочки. Может быть, так чертовски легко ...

2 голосов
/ 10 января 2012

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

Задачи (Runnables) помещаются в DelayQueue, откуда Scheduler-Thread берет и выполняет их. Последнее запланированное задание также запоминается в переменной, поэтому, если планировщик получает новое задание из очереди, он проверяет, является ли это последнее задание, которое было запланировано. Если это так, он выполняет его, если нет - пропускает.

Я использую String-идентификатор для проверки того, какие задачи считаются принадлежащими одному «газу».

Это код, он также включает в себя обычные возможности планирования, но вы можете проверить здесь важные биты.

package org.uilib.util;

import com.google.common.collect.Maps;

import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class SmartExecutor implements Throttle, Executor {

    //~ Static fields/initializers -------------------------------------------------------------------------------------

    private static final Logger L = LoggerFactory.getLogger(SmartExecutor.class);

    //~ Instance fields ------------------------------------------------------------------------------------------------

    private final ExecutorService executor                      = Executors.newCachedThreadPool();
    private final DelayQueue<DelayedRunnable> taskQueue         = new DelayQueue<DelayedRunnable>();
    private final Map<String, ThrottledRunnable> throttledTasks = Maps.newHashMap();

    //~ Constructors ---------------------------------------------------------------------------------------------------

    /* schedule a Runnable to be executed a fixed period of time after it was scheduled
     * if a new Runnable with the same throttleName is scheduled before this one was called, it will overwrite this */
    public SmartExecutor() {
        this.executor.execute(new Scheduler());
    }

    //~ Methods --------------------------------------------------------------------------------------------------------

    /* execute a Runnable once */
    @Override
    public void execute(final Runnable runnable) {
        this.executor.execute(runnable);
    }

    /* schedule a Runnable to be executed after a fixed period of time */
    public void schedule(final long delay, final TimeUnit timeUnit, final Runnable runnable) {
        this.taskQueue.put(new DelayedRunnable(runnable, delay, timeUnit));
    }

    /* schedule a Runnable to be executed using a fixed delay between the end of a run and the start of the next one */
    public void scheduleAtFixedRate(final long period, final TimeUnit timeUnit, final Runnable runnable) {
        this.taskQueue.put(new RepeatingRunnable(runnable, period, timeUnit));
    }

    /* shut the the executor down */
    public void shutdown() {
        this.executor.shutdownNow();
    }

    @Override
    public void throttle(final String throttleName, final long delay, final TimeUnit timeUnit, final Runnable runnable) {

        final ThrottledRunnable thrRunnable = new ThrottledRunnable(runnable, throttleName, delay, timeUnit);
        this.throttledTasks.put(throttleName, thrRunnable);
        this.taskQueue.put(thrRunnable);
    }

    //~ Inner Classes --------------------------------------------------------------------------------------------------

    private static class DelayedRunnable implements Delayed, Runnable {

        protected final Runnable runnable;
        private final long endOfDelay;

        public DelayedRunnable(final Runnable runnable, final long delay, final TimeUnit delayUnit) {
            this.runnable       = runnable;
            this.endOfDelay     = delayUnit.toMillis(delay) + System.currentTimeMillis();
        }

        @Override
        public int compareTo(final Delayed other) {

            final Long delay1 = this.getDelay(TimeUnit.MILLISECONDS);
            final Long delay2 = other.getDelay(TimeUnit.MILLISECONDS);

            return delay1.compareTo(delay2);
        }

        @Override
        public long getDelay(final TimeUnit unit) {
            return unit.convert(this.endOfDelay - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

        @Override
        public void run() {
            this.runnable.run();
        }
    }

    private static final class RepeatingRunnable extends DelayedRunnable {

        private final long periodInMillis;

        public RepeatingRunnable(final Runnable runnable, final long period, final TimeUnit delayUnit) {
            super(runnable, period, delayUnit);

            this.periodInMillis = delayUnit.convert(period, TimeUnit.MILLISECONDS);
        }

        public RepeatingRunnable reschedule() {
            return new RepeatingRunnable(this.runnable, this.periodInMillis, TimeUnit.MILLISECONDS);
        }
    }

    private final class Scheduler implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {

                    /* wait for the next runnable to become available */
                    final DelayedRunnable task = SmartExecutor.this.taskQueue.take();

                    if (task instanceof RepeatingRunnable) {
                        /* tell executor to run the action and reschedule it afterwards */
                        SmartExecutor.this.executor.execute(
                            new Runnable() {
                                    @Override
                                    public void run() {
                                        task.run();
                                        SmartExecutor.this.taskQueue.put(((RepeatingRunnable) task).reschedule());
                                    }
                                });
                    } else if (task instanceof ThrottledRunnable) {

                        final ThrottledRunnable thrTask = (ThrottledRunnable) task;

                        /* run only if this is the latest task in given throttle, otherwise skip execution */
                        if (SmartExecutor.this.throttledTasks.get(thrTask.getThrottleName()) == thrTask) {
                            SmartExecutor.this.executor.execute(task);
                        }
                    } else {
                        /* tell the executor to just run the action */
                        SmartExecutor.this.executor.execute(task);
                    }
                } catch (final InterruptedException e) {
                    SmartExecutor.L.debug("scheduler interrupted (shutting down)");
                    return;
                }
            }
        }
    }

    private static final class ThrottledRunnable extends DelayedRunnable {

        private final String throttleName;

        public ThrottledRunnable(final Runnable runnable, final String throttleName, final long period,
                                 final TimeUnit delayUnit) {
            super(runnable, period, delayUnit);

            this.throttleName = throttleName;
        }

        public String getThrottleName() {
            return this.throttleName;
        }
    }
}
0 голосов
/ 18 января 2010

Если проблема заключается в блокировке потока пользовательского интерфейса во время изменения размера, вы должны рассмотреть метод asyncExec класса Display

/**
 * Causes the <code>run()</code> method of the runnable to
 * be invoked by the user-interface thread at the next 
 * reasonable opportunity. The caller of this method continues 
 * to run in parallel, and is not notified when the
 * runnable has completed.  Specifying <code>null</code> as the
 * runnable simply wakes the user-interface thread when run.
 * <p>
 * Note that at the time the runnable is invoked, widgets 
 * that have the receiver as their display may have been
 * disposed. Therefore, it is necessary to check for this
 * case inside the runnable before accessing the widget.
 * </p>
 *
 * @param runnable code to run on the user-interface thread or <code>null</code>
 *
 * @exception SWTException <ul>
 *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
 * </ul>
 * 
 * @see #syncExec
 */
public void asyncExec (Runnable runnable) {
    synchronized (Device.class) {
        if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
        synchronizer.asyncExec (runnable);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...