SwingWorker завершает работу, PropertyChangeListener слушает, но как вернуться к EDT?

Я уже просмотрел много тем по этой теме, но ничего не подходит для моей конкретной ситуации.

У меня есть свинг-приложение, которое анализирует QR-коды, извлекает найденные имена входа и выполняет вызовы БД для получения данных для этого пользователя. Чтобы убедиться, что захват QR-кодов можно отменить, а мое приложение по-прежнему доступно во время захвата, я использовал для этой цели SwingWorker. Пока все работает нормально, я включил PropertyChangeListener, чтобы приложение знало, когда мой SwingWorker успешно прочитал код. Но так как я не хочу, чтобы PropertyChangeListener был вложенным классом внутри моего основного класса (чтобы он оставался хорошо структурированным), я создал для него новый класс снаружи. Теперь я хочу вернуться к своему основному классу из этого класса PropertyChangeListener, чтобы переключиться на соответствующую панель, которая отображает извлеченные данные. У меня есть разные коды, которые можно прочитать, поэтому в зависимости от кода у меня есть разные панели для переключения (поэтому я не могу выполнять статическое переключение на одну и ту же панель снова и снова). Итак, как я могу делегировать PropertyChangeListener, чтобы вернуть управление моему EDT? Я пытался использовать wait() и notify(), чтобы мой EDT знал, что SwingWorker завершен. Но очевидно, что wait() блокирует мой EDT, и использование SwingWorker бессмысленно.

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

Заранее благодарю за любую помощь :)

РЕДАКТИРОВАТЬ: Вот фрагмент кода, иллюстрирующий, что делает мой SwingWorker.

Класс SwingWorker:

public class CodeDetector extends SwingWorker<byte[], String> {

String s;                     // read String
byte[] completeCode;          // byte[] which is returned by doInBackground()
BufferedImage resizedImg;
IplImage img;
JLabel labelForStream;
JLabel result;
FrameGrabber grabber = new VideoInputFrameGrabber();     // using JavaCV.


public CodeDetector(JLabel labelForStream, JLabel result) {
    this.labelForStream = labelForStream;
    this.resultLabel = result;
}

@Override
protected byte[] doInBackground() throws Exception {
    try {
        grabber.start();         // 
        while (true) {
            // End if current thread was canceled.
            if (Thread.currentThread().isInterrupted()) {
                return null;
            }

            // Grab each image, save it, scan for code and display it.  
            img = grabber.grab();
            resizedImg = //  resizing image to fit labelForStream.
                            // save resizedImg to file
            // read barcode from saved file
                    if (isBadgeCode(tmp) || isDeviceCode(tmp)) {
                        s = tmp;
                    } else {
                        continue;
                    }
                    break;
                } catch (NotFoundException e) {
                    // Code could not be encoded yet.
                }
                ...

                    // end worker after timeout

        // show image on window
                if (img != null) {
                    labelForStream.setIcon(new ImageIcon(resizedImg));
                }
            }
        }
    } catch (Exception e) { 
        System.err.println("Error: " + e.getMessage() + " - " + e.getStackTrace() + " - " +  e.getClass());
    }
    return s != null ? s.getBytes() : null;
}

@Override
protected void done() {
    try {
        completeCode = get();
        if (completeCode != null) {
            String code = new String(completeCode);
            if (isOtherCode(code)) {
                resultLabel.setText(code);
            } else if (isUsernameCode(code)) {
                // Cut userName from read code (if previously verified) and set label text.
                resultLabel.setText(verify(code, true) ? code.split(":")[0] : null);
            }
        } else {
            resultLabel.setText(null);
        }
        resultLabel.setVisible(true);
        resultLabel.updateUI();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    } catch (CancellationException e) {
        return;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Поскольку этот SwingWorker не имеет ссылок на какие-либо панели, несмотря на то, что метод done() выполняется в EDT, мне нужно как-то уведомить мой основной класс, что код был успешно прочитан и что теперь он может изменить панель в соответствии с конкретный код.

Надеюсь, это немного прояснит ситуацию.


person Yeehaw    schedule 19.04.2012    source источник
comment
Почему PropertyChangeListener ... SwingWorker может показывать прогресс. Это объясняется в руководствах по Swing. Или вы можете взглянуть на этот пример кода   -  person Robin    schedule 19.04.2012
comment
Можете ли вы предоставить пример того, как вы используете SwingWorker? Из вашего описания непонятно, почему вы используете PropertyChangedListener вместо переопределения done()?   -  person Jason Braucht    schedule 19.04.2012
comment
Не будет вызывать ваш код из SwingUtilities.invokeLater(...) , перенесет вас в EDT, в то время как вам нужно пройти через ваши JPanels   -  person nIcE cOw    schedule 19.04.2012
comment
Я переопределил метод done(), но чтобы изменить активную панель, отображаемую EDT, мне нужны ссылки на все из них, верно? Поскольку мой класс SwingWorker также не является вложенным, при каждом вызове захвата мне нужно будет передавать эти панели в качестве параметров рабочему процессу, или я ошибаюсь? Было бы проще вложить класс SwingWorker и просто продолжить работу с ним?   -  person Yeehaw    schedule 19.04.2012
comment
При необходимости можно использовать PropertyChangeListener с SwingWorker.\   -  person Hovercraft Full Of Eels    schedule 19.04.2012


Ответы (3)


Я думаю, что вы неправильно поняли, по каким причинам SwingWorker прочитайте руководство по SwingWorker, где реализация вполне гарантирована которые выводятся из методов:

  • Выполнено()

  • процесс()

  • публиковать()

  • установитьПрогресс()

должно быть выполнено в EDT

person mKorbel    schedule 19.04.2012
comment
Это довольно полный пример того, как использовать SwingWorker, включая process и publish, которые, вероятно, следует использовать OP. - person Jim; 19.04.2012
comment
@trashgod ненавидит, извините, что ограничил использование :-), - person mKorbel; 19.04.2012
comment
@mKorbel: в setProgress() это прослушиватель изменения свойства, который выполняется в EDT; извините за небрежное редактирование. +1, кстати. - person trashgod; 20.04.2012

Это не правильно:

protected byte[] doInBackground() throws Exception {

  // ....
  if (img != null) {
     labelForStream.setIcon(new ImageIcon(resizedImg));
  }
  // ....
}

Поскольку это показывает, что вы делаете критические вызовы Swing из метода doInBackground, чего никогда не следует делать. Вместо этого рассмотрите возможность публикации изображения или ImageIcon и установки значка JLabel из переопределения метода процесса.

Как я заметил в своем комментарии, иногда полезно использовать PropertyChangeListener с вашим SwingWorker, если это уменьшает связанность кода. Это одна из причин, по которой SwingWorker имеет собственный PropertyChangeSupport и собственное перечисление состояний.

person Hovercraft Full Of Eels    schedule 19.04.2012
comment
Слишком многому нужно научиться, похоже, SwingWorker находится в ТОП-е моего списка дел, +1, за связывание кода :-) - person nIcE cOw; 19.04.2012
comment
@nIcE cOw 1) не просто Runnable#Thread является наиболее удобным и управляемым по сравнению с SwingWorker, 2) Runnable#Thread иногда требуется (в большинстве случаев) invokeLater, 3) SwingWorker существует и реализует важные методы для Swing, 4) между ними Java6_021 до Java7 не глючит, - person mKorbel; 20.04.2012
comment
@mKorbel: Ага, это интересно, еще несколько пунктов о том, почему я должен закончить добавлять SwingWorker в свои знания :-) - person nIcE cOw; 20.04.2012

Простой ответ Swing Worker — переопределить метод done(). Это выполняется в EDT — SwingWorker позаботится об этом за вас.

Вы можете сделать это самостоятельно, используя SwingUtilities.invokeLater.

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

person Yishai    schedule 19.04.2012