LuciadLightspeed application objects can change during their lifetime. Not only does the object itself change, in most cases the change also affects other application objects. This article describes how application objects can change, how LuciadLightspeed handles these changes automatically, and in which case the program needs to take care of these changes. Finally, the LuciadLightspeed multi-threading fundamentals to handle those changes are discussed.
For information about listening for changes in a data model, see Listening for changes in a model. |
Modifying application objects
A LuciadLightspeed application object can change in many ways. Model data, for example, can change directly when the model receives its data from a real-time data stream through a radar feed. In order to visualize the updated model data in the view, the associated layer objects, painter objects, and view objects need to be updated as well. Model data can also change indirectly, for example when a user interacts with the view and moves objects or changes their shape. To visualize the changed objects and save the changes to the model, the model data and its associated layer, painter, and view objects need to change accordingly.
Changing application objects is typically a three-step process:
-
Change: An action object calls a method on an application object and changes that object.
-
Notification: The application objects that are interested in the changed object are notified of the change. There are two notification mechanisms as described in Notifying objects of changes.
-
Action: Each notified object optionally performs an action as a result of the change. This action itself can change another application object, making this the first step of a new change.
Notifying objects of changes
There are two ways to notify application objects of changes in another application object. The applicable notification mechanism depends on the ability of the changed object to register listeners to it. A listener is an application object that registers to another application object to get notified of a change in the object it has been registered to. The two notification mechanisms are:
-
Notifying objects of changes with listeners. In this case, the changed object automatically fires events to notify its listeners of the change. This mechanism is based on the so called Observer pattern and is applied to most interfaces and classes of LuciadLightspeed . Notifying objects of changes with listeners describes the usage of listeners in detail.
-
Notifying objects of changes programmatically. In this case, the object that executed the change (the action object) needs to notify other application objects of the change. The use cases for this scenario are limited and are listed in Notifying objects of changes programmatically.
Notifying objects of changes with listeners
The generally applied scenario for notifying application objects of changes in another application object, is based on the Observer (or Listener) pattern. According to this pattern, one or more application objects (listeners or observers) can register to a single application object (subject). When the subject changes, it automatically fires an event to its listeners with a notification of the change. The listeners itself have the ability to receive events and react to it. This mechanism is also referred to as the publish-subscribe interaction. Almost any of the most commonly used LuciadLightspeed interfaces and classes provide methods to add and remove listeners. The limited use cases in which an application object does not have the ability to register listeners are listed in Notifying objects of changes programmatically.
LuciadLightspeed provides a set of listener interfaces for the handling of changes for specific types of application objects.
For example, to listen to changes in an ILcdSelection
object, the interface ILcdSelectionListener
is used as described in Listening to changes in a selection. Similar interfaces are available for ILcdModel
and ILcdLayered
objects, and other. In many use cases LuciadLightspeed uses a Property Change Listener that listens to changes of specific
properties of application objects. The following sections provide more information on selection listeners, model listeners,
and Property Change Listeners.
Listening to changes in a selection
This section describes how to listen to changes in a selection. An ILcdSelection
is a container for objects that are selected from a model that is wrapped in a layer. ILcdSelection
provides methods to add and remove ILcdSelectionListener
objects. Whenever the selection state of one of the objects in an ILcdSelection
changes, the selection automatically notifies its registered listeners of the change by calling the method selectionChanged
. The parameter of selectionChanged
is TLcdSelectionChangedEvent
, which contains both the selection that fired the event as the changes in the selection. It also provides the following methods
to retrieve the objects in the selection that have changed:
-
selectedElements
: returns all objects for which the selection state has changed from deselected to selected -
deselectedElements
: returns all objects for which the selection state has changed from selected to deselected -
elements
: returns all objects for which the selection state has changed
Figure 1, “Listening to changes in a selection” shows the usage of ILcdSelection
and ILcdSelectionListener
in a diagram.
Additionally you can retrieve more information about the change in selection of a given object by using the method retrieveChange
. Refer to the API reference for a detailed description of ILcdSelection
, ILcdSelectionListener
, and TLcdSelectionChangedEvent
.
Program: Listening to changes in selection of the ILcdLayer
objects of a view shows how to create a selection listener that listens to changes in a layer. The layer selection listener is added to the
view and listens to selection changes in each of the layers that are added to the view.
ILcdLayer
objects of a view`)
view.addLayerSelectionListener( (ILcdSelectionListener<Object>) aSelectionEvent -> { ILcdLayer layer = (ILcdLayer) aSelectionEvent.getSelection(); if (layer.getSelectionCount() == 1) { TLcdDomainObjectContext objectContext = new TLcdDomainObjectContext( layer.selectedObjects().nextElement(), layer.getModel(), layer, view); manager.setBalloonDescriptor(new TLcdModelElementBalloonDescriptor(objectContext)); } else { manager.setBalloonDescriptor(null); } });
Listening to property changes
A specific use case of the Observer pattern is the use of a java.beans.PropertyChangeListener
which listens to changes in a Java bean property. This kind of property is a named attribute of a Java bean that you can
read using a get
method and that you can change using a similarly named set
method. The name of the property is derived from the get/set method pair. It is the name that would be used for the matching
private field, according to the Java naming conventions: drop the get
or set
, and start the name with lower case. For example, for the methods getScale
and setScale
, the returned property name is scale
(in lower case). In the case of the methods setGXYController
and getGXYController
, the returned property name is GXYController
. As there are multiple consecutive capitals in the returned name, all capitals are kept.
Whenever a property of an application object changes, the changed object notifies its Property Change Listeners using the
propertyChanged
method with a PropertyChangeEvent
as parameter. Program: Using a PropertyChangeListener
shows how to create and register a Property Change Listener that listens to changes in the return value of ILcdGXYView.getXYWorldReference
. It thus listens to the property that is named XYWorldReference
.
PropertyChangeListener
(from samples/gxy/common/toolbar/ProjectionComboBox
)
// listen to projection changes aGXYView.addPropertyChangeListener(event -> { if ("XYWorldReference".equals(event.getPropertyName())) { viewProjectionHasChanged(aGXYView, actions); } });
Notifying objects of changes programmatically
Because of efficiency reasons it is in some cases not feasible to register listeners and fire events for every possible change that can occur in an application object. In these cases, the application object that executes the change (the action object), needs to notify other application objects of the change. In the following common use cases a method is required to notify other application objects that a change has occurred:
-
Changing a domain object. When changing a domain object from a model, the action object needs to notify the model of the change using the method
elementChanged
. Changing domain objects describes this common use case in more detail. -
Changing indirect layer properties. This applies to changing properties that affect a layer indirectly. In this case, the action object needs to use the method
TLcdLayer.invalidate
to notify the layer directly of the change or notify the view of the change in the layer. Changing indirect layer properties describes this use case in more detail.
There a few other, advanced use cases which are mentioned in the advanced parts of this guide. In all other cases, listeners are used as described in Notifying objects of changes with listeners. The sections below provide more information on changing domain objects and changing indirect layer properties.
Changing domain objects
When a domain object changes, the model that contains the object is not aware of this change until it gets notified. To notify
a model of changes in one of its domain objects, use the method elementChanged
. The model can then, in its turn, notify registered ILcdModelListener
instances of the change by firing events.
Whenever objects are added to or removed from a model, or when a model is notified of a change in one of its objects, the
firing event mode ILcdFireEventMode
has to be given to the model as well. There are three modes for firing events:
-
ILcdFireEventMode.FIRE_NOW
: to send out the event immediately. -
ILcdFireEventMode.FIRE_LATER
: to collect the event and send out an event later. After all changes have been made, the methodfireCollectedModelChanges
should be called on anILcdModel
so that the model can fire one event for the collected changes. This mode is useful in case of bulk changes to a model. -
ILcdFireEventMode.NO_EVENT
: to not send an event at all. This mode is mostly used when adding objects to a model that has just been created.
See the Working with models tutorial for examples on how the model gets notified that an object has changed and how the model is instructed to wait with firing an event to its listeners.
Changing indirect layer properties
This section applies to GXY Views only. |
When changing one of the layer properties on an ILcdGXYLayer
, for example using setVisible
or
setLabeled
, the view automatically gets notified of the change. But when you change properties that indirectly affect a layer, for example
the properties of an associated painter, you need to use the invalidate
method to notify the layer of the change. You can also notify the view of the fact that one of its layers has changed using
the invalidateGXYLayer
method.
Program: The color map of a layer has changed and the view is notified of the change so it can repaint the layer shows how a new operator chain is set to an image painter to reflect a change in color style. The painter is responsible for painting the model used by the layer. The last line of the code snippet shows how the view gets notified that one of its layers needs a repaint because the underlying painter has changed.
samples/gxy/colormap/LayerColorCustomizerPanel
)
painter.setOperatorChain(new ColorMapOperatorChain(aColorMap)); // Notify the view that the layer needs a repaint aView.invalidateGXYLayer(aImagePainterLayerSFCT, true, "LayerColorCustomizerPanel#installColorMapOnLayerSFCT", "Changed color model");