Warning, /education/labplot/src/doc/aspect-framework.dox is written in an unsupported language. File is not indexed.

0001 /**\page aspect The Aspect Framework
0002 
0003 The notion of what we call Aspects pervades and unifies the internal representation of SciDAVis / LabPLot
0004 projects. Basically, every line in the project explorer corresponds to an Aspect, although Aspects
0005 which the user normally does not need to know about are hidden by default. Also, note that Aspects
0006 are internal in the sense that they do not interact directly with the user. The latter task is done
0007 by views. Most Aspects have a primary view with which they have a 1:1 relationship; specifically, if
0008 its primary view is closed, an Aspect gets removed. However, Aspects also can have multiple views on
0009 them or no views at all. Aspects organize themselves into trees, usually with a Project at the top.
0010 They can also be used standalone, although in that case some features (especially undo) do not work.
0011 
0012 The Aspect framework provides the following features:
0013   - Utilities and guidelines which make implementation of undo-aware changes relatively painless.
0014   - Organization of object trees with multi-level undo for all changes and powerful
0015     features like object hiding and querying trees based on the object's class and hidden status.
0016   - XML (de-)serialization of (partial) trees
0017   - Common attributes like name or comment, also with full undo support.
0018   - Easy access to global settings via global() and setGlobal().
0019 
0020 Every Aspect is internally divided into three layers: private %data, undo commands and public API.
0021 Additionally, two more layers are built on top of Aspects.
0022 
0023 \section layer0 Layer 0 (private data)
0024 
0025 Every Aspect class has an internal class called Private, which manages its persistent %data. The fact
0026 that it is internal implies that it cannot inherit from QObject (or any of its descendants);
0027 usually, Private will not inherit from any class. %Note that since most classes in the inheritance
0028 chain of an Aspect _class_ have their own private _classes_, Aspect _objects_ in general have
0029 multiple private _objects_ associated to them. Every private object knows its owning Aspect and can
0030 use its inherited public API (layer 2) to talk to the other private objects of the same Aspect
0031 object. The technique of introducing a separate class which hides the content of an object is known
0032 variously as "Pimpl idiom", "Cheshire Cat", "D-Pointer" etc.; one of the main advantages is that it
0033 makes it easier to change the implementation of a class without breaking binary compatibility (i.e.,
0034 plugins don't need to be recompiled). Also, in the context of this framework, the indirection
0035 involved in accessing layer 0 from layer 2 (the API class) serves as a reminder that changes to the
0036 %data must be routed through layer 1 (i.e. instantiating a command).
0037 
0038 Since encapsulation of an Aspect is already provided by layer 2, private classes can be pretty
0039 promiscuous with their member variables; responsibility for maintaining a coherent state is shared
0040 with layers 1 and 2 (this is necessary in cases where a single action needs to change %data
0041 contained in multiple private objects). In addition to the member variables, private classes usually
0042 provide functionality common to multiple undo commands (layer 1).
0043 These methods must only operate on the internal %data, since write calls into layer 2 usually cause
0044 undo commands to be pushed onto the undo stack (which must not happen within another undo command).
0045 Purely internal Aspects which are not part of the hierarchy of the Aspect in question (e.g.,
0046 temporary filters or columns) count as internal %data and can of course be modified.
0047 
0048 \section layer1 Layer 1 (undo commands)
0049 
0050 Every undo command instance is associated with exactly one private object and accesses only %data
0051 and methods of this object. For common cases, generic undo commands (like PropertyChangeCommand or
0052 SignallingUndoCommand) can be used; more involved cases require undo commands written specifically
0053 for one Aspect class (say, MyAspect). The latter follow the naming convention MyAspect*Cmd and are
0054 gathered into source files myaspectcommands.cpp and myaspectcommands.h in order to reflect their
0055 association. Read access to other private objects of the same Aspect is done via the Aspect's
0056 inherited public API. If write access to multiple private objects is necessary, separate undo
0057 commands are created and instantiated within a command macro (compare e.g.
0058 AbstractColumn::removeRows()).
0059 
0060 Undo commands are also responsible for triggering signals that are part of the public API of an
0061 Aspect (i.e. change notifications which allow views and other Aspects to update themselves if
0062 necessary). Usually this can be accomplished by using
0063 AbstractAspect::exec(QUndoCommand*,const char*,const char*,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument)
0064 or SignallingUndoCommand.
0065 
0066 Undo commands specific to one Aspect class will usually rely on implementation details of its
0067 private class; they even may require a call protocol, that is, make assumptions about the context
0068 of their employment. Since undo commands are internal to an Aspect and often only used in one place,
0069 this is not a big problem. Still, it makes sense to document such assumptions to ease future changes
0070 to the code.
0071 
0072 
0073 \section layer2 Layer 2 (public API)
0074 
0075 The public API of an Aspect consists exclusively of methods which a) are undo safe, b) have no side
0076 effects (orthogonality) and c) don't require call protocols (they are atomic). Basically, the API
0077 will provide direct read access to the %data in Private (layer 0) and translate write access into
0078 instantiations of appropriate undo commands (layer 1). Actions that require changes to multiple
0079 Private objects are a bit more subtle, since they require multiple undo commands to be
0080 instantiated and packaged into a macro; possibly with some simple logic to connect them.
0081 As opposed to layers 0 and 1, layer 2 may also use the public API of other Aspects in the hierarchy
0082 without restrictions.
0083 
0084 In the simplest case, an Aspect's private object MyAspect::Private has a public member variable
0085 (say, QString m_tag) which doesn't have to be coordinated with anything else. In that case,
0086 no custom undo command needs to be written and MyAspect could use a setter method like this:
0087 \code
0088 01  void MyAspect::setTag(const QString &value) {
0089 02    if (value == m_my_private->m_tag) return; // don't create superfluous commands
0090 03    exec(new PropertyChangeCommand<QString>(tr("%1: change tag").arg(name()),
0091 04        &m_my_private->m_tag, value));
0092 05  }
0093 \endcode
0094 Line 2 is a short check which is supposed to prevent creation of command instances that do not
0095 actually change anything. Lines 3 and 4 instantiate a PropertyChangeCommand (which changes a
0096 single variable) and hand it to AbstractAspect::exec(). The PropertyChangeCommand receives a
0097 short user-visible description, a pointer to the variable to be changed and the new value of the
0098 variable. It takes care of creating a backup of the current value and switching between the old
0099 and the new value on undo/redo. AbstractAspect::exec() makes sure that the command is executed at
0100 least once and, if the MyAspect instance is part of a Project, saves the command for later
0101 undo/redo.
0102 
0103 Part of the public API are also signals sent to views and other Aspects in order to notify them of
0104 changes in the %data. While this is the responsibility of layer 2, convenience methods are provided
0105 so that also in this case, no custom undo command needs to be written. If MyAspect wishes to send
0106 the signals "void tagAboutToChange(const MyAspect*)" and "void tagChanged(const MyAspect*)" before
0107 and after its tag property changes, this can be accomplished by adding a single line to the above
0108 example:
0109 \code
0110 01  void MyAspect::setTag(const QString &value) {
0111 02    if (value == m_my_private->m_tag) return; // don't create superfluous commands
0112 03    exec(new PropertyChangeCommand<QString>(tr("%1: change tag").arg(name()),
0113 04        &m_my_private->m_tag, value),
0114 05      "tagAboutToChange", "tagChanged", Q_ARG(const MyAspect*,this));
0115 06  }
0116 \endcode
0117 This calls an overloaded version of exec() which creates a command macro and two instances of
0118 SignallingUndoCommand behind the scenes.
0119 
0120 \section layer3 Layer 3 (Qt models)
0121 
0122 Between an Aspect and one of its views, classes derived from QAbstractItemModel may be used to
0123 translate between the Aspect's public API (layer 2) and Qt's Model/View framework. Every view
0124 (layer 4) can own one or more such translation objects.
0125 Examples include AspectTreeModel and SpreadsheetModel.
0126 
0127 This layer is only needed in order to use existing views of Qt's Model/View framework;
0128 and as such it is optional.
0129 
0130 \section layer4 Layer 4 (views and scripts)
0131 
0132 Views use layers 2 and 3 of an Aspect in order to build the GUI and process user
0133 input. An Aspect and its primary view know about each other and call each other's API. View-related
0134 actions in the project explorer (like "maximize") always act on the primary view. The primary view
0135 class of an Aspect class MyAspect is called MyAspectView. Most users will not notice a difference or
0136 have to make a distinction between an Aspect and its primary view, whereas secondary views are
0137 represented as distinct entities (typically modeless dialogs) to the user. Showing and hiding of the
0138 primary view is done via the public API of the Aspect so as to be available via the Aspect's context
0139 menu.
0140 
0141 Views can access layer 2 APIs of other Aspects, although this is expected to be rare. A view
0142 receives signals only from its associated Aspect. Should a view need to react on changes to multiple
0143 Aspects, then one Aspect has to gather signals from the others and relay them to the view.
0144 
0145 Because views and scripts make changes to the underlying %data only via the public API, one does not
0146 have to care about undo or synchronisation between different views or scripts and views on this
0147 layer. Views just have to make sure that they update themselves upon the appropriate change signals
0148 from their Aspect. Also, only the API of layer 2 has to be wrapped for access from within scripting
0149 languages.
0150 
0151 The details of handling primary views is dependent on the kind of Aspect. For example, AbstractPart
0152 expects its implementations to provide a view() method returning a QWidget. To the outside world,
0153 it provides the method mdiSubWindow(), which wraps the result of view() into an MDI window and
0154 makes sure that this window gets closed when the Aspect is removed and vice versa.
0155 
0156 \section bases Base Classes for Aspects
0157 
0158 \subsection AbstractAspect AbstractAspect
0159 
0160 The base class of all Aspects is AbstractAspect. It implements undo-aware adding and removing of
0161 child aspects, basic properties like name and creation time and provides methods for handling undo
0162 commands. Upon changes to the Aspect tree or to the properties, signals are emitted. It also allows
0163 implementations to specify a context menu and an icon for use by the project explorer.
0164 
0165 \subsection AbstractPart AbstractPart
0166 
0167 AbstractPart is the base class of all Parts, which are somewhat analogous to KDE's KParts. As a
0168 first approximation, a Part is everything that gets displayed in its own MDI subwindow, although in
0169 principle Parts could be displayed differently, e.g. in their own main windows.
0170 
0171 \subsection AbstractColumn AbstractColumn
0172 
0173 AbstractColumn provides a generic interface for data columns. Its purpose is to abstract away the
0174 differences between stored and computed data, allowing filters and graphs to transparently
0175 handle all kinds of data sources.
0176 
0177 \subsection AbstractFilter AbstractFilter
0178 
0179 AbstractFilter is the base class for all analysis operations. It is modelled on integrated circuits,
0180 which is to say, a filter is basically a black box with input and output ports. Input ports accept
0181 their data in the form of AbstractColumn instances; output ports provide their data in the same
0182 form. Typically filters generate their data lazily; that is, only when asked for data on an output
0183 port they will start reading from their inputs. This is not true for all filters, however;
0184 StatisticsFilter being a noteworthy exception. Notification of changes to the input data is
0185 propagated in the form of signals.
0186 
0187 */