Мой графический интерфейс заморожен - PullRequest
0 голосов
/ 07 августа 2009

У меня есть кое-что, чего я не могу понять: мой графический интерфейс Swing содержит кнопки «play» и «pause». У меня также есть статическая переменная, которая определяет состояния «ВКЛ» и «ВЫКЛ». (Основная программа генерирует графический интерфейс). Нажав на «play», я изменяю состояние моей статической переменной на «ON» и запускаю трудоемкий процесс в потоке, который также изменяет GUI. Пока статическая переменная находится в цикле «ВКЛ» в одном и том же процессе. Нажатие на «паузу» изменит статическую переменную на OFF. Но при нажатии «play» графический интерфейс останавливается и, следовательно,

  1. GUI не обновляется
  2. Процесс не может быть приостановлен с помощью моей кнопки "пауза".

Я слышал о EDT и SwingWorker, но у вас есть простой способ сделать это, я так понимаю.

Спасибо за вашу помощь и простите мой плохой английский ...

Ответы [ 5 ]

6 голосов
/ 07 августа 2009

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

Тем не менее, это добавляет еще одно осложнение: близость. Вызов методов для компонентов пользовательского интерфейса обычно требует, чтобы вы делали это из потока пользовательского интерфейса. Поэтому вам необходимо использовать специальные функции, чтобы вернуться к потоку пользовательского интерфейса из рабочего потока. SwingWorker также дает вам эту возможность.

Я предлагаю вам прочитать эту документацию .

3 голосов
/ 07 августа 2009

Вам нужно прочитать Параллельность в Swing , чтобы понять, как работают EDT и SwingWorkers.

Все обновления GUI выполняются в EDT, поэтому при щелчке по компоненту GUI любой метод, который вызывает этот вызов, будет выполняться в EDT. Если это трудоемкий процесс, то это блокирует EDT от выполнения любых дальнейших обновлений GUI. Следовательно, ваш графический интерфейс зависает, и вы не можете нажать кнопку паузы.

Вам необходимо использовать SwingWorker для выполнения трудоемкого процесса в другом потоке. Ссылка, которую я привел выше, подробно описывает, как это сделать.

0 голосов
/ 07 августа 2009

Поток отправки событий (EDT) - единственный поток, в котором безопасно читать или обновлять графический интерфейс.

Кнопка паузы должна устанавливать переменную включения / выключения в потоке отправки событий.

Длительная операция и цикл должны не находиться в EDT. (Цикл также не должен работать непрерывно, ничего не делая, кроме проверки переменной, или он может легко съесть весь ваш процессор. Если ему больше нечего делать, он должен проверить, а затем в течение некоторого времени вызывать Thread.sleep() (скажем, 100 мс ).)

Если вы можете доказать, что для переменной вкл / выкл задано значение OFF, но, тем не менее, она всегда читается как ON, возможно, значение переменной не копируется из EDT в рабочий поток. Сделайте volatile, или synchronize доступ к нему, или используйте AtomicReference, или прочитайте его в EDT, используя SwingUtilities.invokeAndWait().

SwingWorker вероятно, - это самый простой путь, здесь. Реализуйте трудоемкую операцию и проверку включения / выключения в методе doInBackground(), а обновление графического интерфейса - в методе done().

public enum State {
    RUNNING, STOPPED
}

public class ThreadSafeStateModel {
    private State state = State.STOPPED;

    public synchronized void stop() {
        state = State.STOPPED;
    }

    public synchronized void start() {
        state = State.RUNNING;
    }

    public boolean isRunning() {
        return state == State.RUNNING;
    }
}

public class ExpensiveProcessWorker extends SwingWorker<Void, Void> {

    private final ThreadSafeStateModel model;

    public ExpensiveProcessWorker(ThreadSafeStateModel model) {
        this.model = model;
    }

    @Override // Runs in background
    protected Void doInBackground() throws Exception { 
        while (model.isRunning()) {
            // do one iteration of something expensive
        }
        return null;
    }

    @Override // Runs in event dispatch thread
    protected void done() { 
        // Update the GUI
    }
}

public class StopButton extends JButton {
    public StopButton(final ThreadSafeStateModel model) {
        super(new AbstractAction("Stop") {
            @Override
            public void actionPerformed(ActionEvent e) {
                model.stop();
            }
        });
    }
}

public class StartButton extends JButton {
    public StartButton(final ThreadSafeStateModel model) {
        super(new AbstractAction("Start") {
            @Override
            public void actionPerformed(ActionEvent e) {
                model.start();
                new ExpensiveProcessWorker(model).execute();
            }
        });
    }
}

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

0 голосов
/ 07 августа 2009

Это довольно простая причина: хотя Java работает над вашим трудоемким процессом, она не может обновлять графический интерфейс. Решение: запустить трудоемкий процесс в отдельном потоке. Существует множество способов программирования, и это, вероятно, будет зависеть от того, как написана ваша программа.

0 голосов
/ 07 августа 2009

Вы не должны запускать долго выполняющиеся процессы в обработчике событий Swing, потому что он заморозит ваш графический интерфейс, вы это знаете сейчас. :) Запустите его в новой теме. Вам нужно использовать SwingWorker, только если вы планируете манипулировать GUI из рабочего потока (поскольку Swing не является поточно-ориентированным).

...