Повторное выполнение SwingWorker

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

Другое дело, что мне нужно, чтобы SwingWorker выполнялся повторно (перезапуск после его завершения) и каждый раз обновлял графический интерфейс. Поскольку мой код на данный момент, у меня есть цикл while, многократно выполняющий мой SwingWorker, но это приводит к тому, что графический интерфейс перестает отвечать на запросы, поскольку он работает в EDT (предположительно). Каков наилучший способ повторного выполнения SwingWorker? Должен ли я просто создать новый поток для запуска цикла или что-то еще?

Код для моего внутреннего класса ActionListener выглядит следующим образом:

private class TunerListener implements ActionListener {
    private boolean firstUpdate = true;
    private volatile boolean executing = false;

    private final SwingWorker<Void, Void> tunerWorker = new SwingWorker<Void, Void>() {

        @Override
        protected Void doInBackground() {
            model.update();
            return null;
        }

        @Override
        protected void done() {
            if (!this.isCancelled()) {
                prev.setText(model.getPrev());
                currentNote.setText(model.getNote());
                next.setText(model.getNext());
                frequency.setText(model.getFrequency());
                switch (model.getOffset()) {
                    case -2:
                        light_2.setIcon(onRed);
                        light_1.setIcon(off);
                        light0.setIcon(offBig);
                        light1.setIcon(off);
                        light2.setIcon(off);
                        break;
                    case -1:
                        light_2.setIcon(off);
                        light_1.setIcon(onRed);
                        light0.setIcon(offBig);
                        light1.setIcon(off);
                        light2.setIcon(off);
                        break;
                    case 0:
                        light_2.setIcon(off);
                        light_1.setIcon(off);
                        light0.setIcon(onGreen);
                        light1.setIcon(off);
                        light2.setIcon(off);
                        break;
                    case 1:
                        light_2.setIcon(off);
                        light_1.setIcon(off);
                        light0.setIcon(offBig);
                        light1.setIcon(onRed);
                        light2.setIcon(off);
                        break;
                    case 2:
                        light_2.setIcon(off);
                        light_1.setIcon(off);
                        light0.setIcon(offBig);
                        light1.setIcon(off);
                        light2.setIcon(onRed);
                        break;
                }
            }
        }

    };

    @Override
    public void actionPerformed(ActionEvent ae) {

        if (ae.getActionCommand().equals("tune")) {
            if (!executing) {
                tune.setText("Stop Tuning");
                executing = true;

                while (executing) {
                    tunerWorker.execute();
                    firstUpdate = false;
                }
                firstUpdate = true;
            } else {
                tune.setText("Start Tuning");
                executing = false;
                tunerWorker.cancel(true);
                firstUpdate = true;
            }
        }

    }
}

Редактировать: Проблема с текстом кнопки, кажется, исправлена, но я все еще не могу заставить этот SwingWorker работать должным образом. Я попытался полностью удалить цикл while и заставить его повторно выполнить себя из метода done(), но это, похоже, не помогает...


person nihilo90    schedule 05.05.2012    source источник
comment
Вообще лучше задавать отдельный вопрос по каждой проблеме   -  person Tharwen    schedule 05.05.2012
comment
Однако... в этом случае кажется, что проблемы одни и те же.   -  person Tharwen    schedule 05.05.2012
comment
Не могли бы вы уточнить? Я обнаружил, что это действительно не работает так, как я ожидал :(   -  person nihilo90    schedule 05.05.2012
comment
Пишу ответ :)   -  person Tharwen    schedule 05.05.2012


Ответы (2)


ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: многопоточность не является областью моей большой компетенции...

Обе ваши проблемы на самом деле одно и то же, например. Ваш графический интерфейс перестает отвечать на запросы, потому что EDT занят выполнением цикла while. Это означает, что он не может перерисовать кнопку с новым текстовым значением и не может реагировать на ввод пользователя.

Кроме того, каждый экземпляр SwingWorker может быть запущен только один раз, поэтому вызов execute() несколько раз в цикле запустит его только один раз.

Я рекомендую создать объект TunerWorker и поместить в него цикл, а затем создавать новый всякий раз, когда вам нужно запустить цикл. Нравится:

class TunerListener implements ActionListener {

private TunerWorker tw = null;

@Override
public void actionPerformed(ActionEvent ae) {

    if (ae.getActionCommand().equals("tune")) {
        if (tw == null || tw.isDone()) {
            tune.setText("Stop Tuning");
            executing = true;

            tw = new TunerWorker();
            tw.execute();

        } else {
            tune.setText("Start Tuning");
            executing = false;
            tw.cancel(true);
            }
        }
    }
}

final class TunerWorker extends SwingWorker<Void, Void> {

    @Override
    protected Void doInBackground() {
        while (!this.isCancelled()) {
            model.update();
        }        
        return null;
    }    

    @Override
    protected void done() {
        if (!this.isCancelled()) {
            //Removed this code to make the example prettier...
        }
    }
}

О, и я не был уверен, что вы пытаетесь сделать с firstUpdate, поэтому я убрал его из примера. Надеюсь, не составит труда придумать, как вставить его обратно.

РЕДАКТИРОВАТЬ: Упс, этот код на самом деле не работал. Должно быть исправлено сейчас.

person Tharwen    schedule 05.05.2012
comment
+1 за сужение фокуса; графический интерфейс можно безопасно обновить с process(). - person trashgod; 05.05.2012
comment
+1 за Also, each instance of a SwingWorker can only be run once, so calling execute() several times in a loop only runs it once.. Это то, что я искал. - person Tar; 28.01.2017

"SwingWorker предназначен для однократного выполнения ." Вместо этого контролируйте выполнение и жизненный цикл воркера, как показано в этом примере.

person trashgod    schedule 05.05.2012