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 */