2

I've already browsed many threads on this topic, but nothing seems to fit my specific situation.

I have a swing application which analyzes QR-Codes, extracts the found loginNames and makes DB calls to fetch data for that user. To make sure the capture of the QR-Codes can be canceled and my application is still accessible while capturing, I used a SwingWorker for this purpose. Everything works fine so far, I've included a PropertyChangeListener so the application knows when my SwingWorker successfully read a code. But since I don't want to have the PropertyChangeListener as a nested class within my mainClass (to keep it well structured), I've created a new class for it outside. Now I want to return to my main class from this PropertyChangeListener class to switch to the appropriate panel which displays the fetched data. I have different codes which can be read, so depending on the code I have different panels to switch to (so I can't do a static switch to the same panel over and over again). So how can I delegate the PropertyChangeListener to give the control back to my EDT ? I've tried using wait() and notify() to let my EDT know the SwingWorker finished. But obviously wait() blocks my EDT and the use of SwingWorker is pointless.

I hope I could explain my problem in enough detail, and some of you have a good idea to deal with this. For any code snippets please ask, I'll then add the necessary ones. But since my project is a bit more complex I'll just post what is asked for.

Thanks in advance for any help :)

EDIT: Here is a code excerpt to illustrate what my SwingWorker is doing.

SwingWorker class:

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

As this SwingWorker doesn't have references to any panels, even though the done()-method is done in EDT, I need to somehow notify my mainClass that a code has been read successfully and that it can change the panl now according to the specific code.

Hope this clears things up a bit.

Yeehaw
  • 21
  • 1
  • 3
  • Why the PropertyChangeListener ... SwingWorker can indicate progress. This is explained in the Swing tutorials. Or you can take a look at [this sample code](http://stackoverflow.com/questions/8916721/java-swing-update-label/8917565#8917565) – Robin Apr 19 '12 at 14:50
  • Can you provide a sample of how you are using SwingWorker? From your description it's not clear why you are using PropertyChangedListener instead of overriding `done()`? – Jason Braucht Apr 19 '12 at 14:50
  • Won't calling your code from inside the [SwingUtilities.invokeLater(...)](http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)) , will take you to the EDT, while you need to traverse through your `JPanel`s – nIcE cOw Apr 19 '12 at 14:52
  • I've overridden the done()-method, but to change the active panel displayed by the EDT I need references to all of them, right? Since my SwingWorker class isn't nested either ,with every capture-invocation I'd need to pass these panels as parameters to the worker, or am I thinking wrong? Would it be easier to nest the SwingWorker class and just go on from there? – Yeehaw Apr 19 '12 at 15:02
  • It's OK to use a PropertyChangeListener with the SwingWorker when necessary.\ – Hovercraft Full Of Eels Apr 19 '12 at 15:47

3 Answers3

6

I think that you misunderstood, for what reasons SwingWorker is there, please read SwingWorker tutorial, where implementations quite guarentee that output from methods:

  • done()

  • process()

  • publish()

  • setProgress()

should be done on EDT

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • 2
    [This](http://albertattard.blogspot.com/2008/09/practical-example-of-swing-worker.html) is a fairly complete example of how to use SwingWorker including `process` and `publish`, which the OP probably should be using. – Jim Apr 19 '12 at 19:44
  • @trashgod hate down, I'm sorry I limited my usage of :-), – mKorbel Apr 19 '12 at 19:59
  • @mKorbel: In `setProgress()`, it's the property change listener that executes on the EDT; sorry for the careless edit. +1, BTW. – trashgod Apr 19 '12 at 20:44
3

The simple swing worker answer is to override the done() method. That is executed on the EDT - SwingWorker takes care of that for you.

You can do it yourself, using SwingUtilities.invokeLater.

For the way you pose your question, I suspect you don't have a full grasp of the threading issues and how you switch between threads. So a good review of the tutorial (if you haven't already) may be in order.

Yishai
  • 90,445
  • 31
  • 189
  • 263
3

This is wrong:

protected byte[] doInBackground() throws Exception {

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

As this shows you making critical Swing calls from within the doInBackground method, something that should never be done. Instead consider publishing the Image or the ImageIcon, and setting the JLabel's Icon from the process method override.

As I note in my comment, sometimes its a good idea to use a PropertyChangeListener with your SwingWorker if it decreases code coupling. This is one reason that SwingWorker has its own PropertyChangeSupport and its own state enum.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • @Yeehaw [PropertyChangeListener is for listening of SwingWorkers statuses](http://stackoverflow.com/questions/7053865/cant-get-arrayindexoutofboundsexception-from-future-and-swingworker-if-threa) +1 – mKorbel Apr 19 '12 at 16:36
  • Too much to learn, seems like SwingWorker is right on TOP of my TO DO LIST, +1, for code coupling :-) – nIcE cOw Apr 19 '12 at 16:42
  • @nIcE cOw 1) not plain Runnable#Thread is most confortable and manageable in compare with SwingWorker, 2) Runnable#Thread required sometimes (most cases of) invokeLater, 3) SwingWorker is there, and implements important methods for Swing, 4) betweens Java6_021 upto Java7 isn't buggy, – mKorbel Apr 19 '12 at 20:53
  • @mKorbel : Ahha, that's interesting, more points as to why I must finish adding SwingWorker, to my Knowledge :-) – nIcE cOw Apr 20 '12 at 07:41