1

I have a panel called DataPanel which extends JPanel, and a worker called DataPanelWorker which extends SwingWorker.

Currently when I create the DataPanel, I start the DataPanelWorker which does some calculations and fires property changes after each calculation.

The DataPanel listens for these property changes and displays a message each time. e.g. "Calculation 1 complete" "Calculation 2 complete"

This works fine!

What I now want to do now is create a second instance of DataPanel (let's call this DataPanel2) but I want to use the original DataPanelWorker to save computation. I register DataPanel2 as another propertyChangeListener on DataPanelWorker.

My problem is I might register DataPanel2 after calculation 1 has been completed and the first propertyChangeEvent has been fired. How can I know how far through the worker is so that I can get DataPanel2 to be displaying the same messages as DataPanel1?

What I would like ideally is to keep a queue of propertyChangeEvents and when registering a new component, fire them all on just that component. Is there a standard way of doing this? Or am I looking at it from the wrong view?

Thanks

Duane Allman
  • 531
  • 2
  • 6
  • 20
  • 1
    Why not have the worker hold a List of state changes, and simply query the state of this list? – Hovercraft Full Of Eels Oct 06 '17 at 16:29
  • I'm worried about events being fired in the nanosecond between finishing looking at this list, and registering the new listener. Presumably you'd register the listener after looking at the events, if we did it before, we might get events twice. – Duane Allman Oct 06 '17 at 16:44
  • Is there a way to pause the doInBackground() thread in order to build the second panel from this list, making sure we don't miss any property changes? – Duane Allman Oct 06 '17 at 16:47
  • @HovercraftFullOfEels: I'm wary of synchronizing access to the list; I'd update a single `DataModel` as outlined below. – trashgod Oct 06 '17 at 19:00

1 Answers1

2

Conceptually, your SwingWorker should publish() interim instances of Calculation so that process() can update your DataModel and notify any listeners.

class DataPanelWorker extends SwingWorker<List<Calculation>, Calculation> {…}
class DataModel {
    private List<Calculation> list = new ArrayList<>();
    …
}

Your implementation of process() can then update your DataModel on the event dispatch thread. In this way, your DataModel always has the complete state of the calculation. Any listening DataPanel can ask the DataModel for the current List<Calculation> to display. Your DataModel can notify listeners of the arrival of new data using any of the approaches shown here. This example updates a JLabel; this example also updates a chart; this example updates the TableModel of a listening JTable.

DataPanelWorker…does some calculations and fires property changes after each calculation.

Avoid firing property change events in your implementation of doInBackground(), as access to any shared data will need to be synchronized independently of the worker. Instead, update your DataModel in your implementation of process(), which synchronizes access for you. The DataModel can then fire events for registered listeners, knowing that updates will happen on the event dispatch thread.

Is there a way to pause the doInBackground() thread?

It's permitted, but you should't have to do so in this context. If necessary, multiple workers can cooperate as shown here.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • 1
    I like this idea. This way the list is only manipulated, read or written to, on one thread. – Hovercraft Full Of Eels Oct 06 '17 at 21:07
  • Yes this makes much more sense. I hadn't thought to have a DataModel. Much better - thanks! – Duane Allman Oct 07 '17 at 15:08
  • 1
    @DuaneAllman: It's common in Swing [architecture](http://www.oracle.com/technetwork/java/architecture-142923.html). You'll want to work through some examples, but you may be able to reuse your property change events in the `DataModel`. – trashgod Oct 07 '17 at 17:46