File indexing completed on 2024-05-12 03:47:26
0001 /* 0002 File : AbstractFilter.cpp 0003 Project : LabPlot 0004 Description : Base class for all analysis operations. 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2007 Knut Franke Tilman Benkert <knut.franke*gmx.de, thzs*gmx.net (use @ for *)> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "AbstractFilter.h" 0011 #include <KLocalizedString> 0012 0013 #include "backend/core/AbstractColumn.h" 0014 #include "backend/lib/macros.h" 0015 0016 /** 0017 * \class AbstractFilter 0018 * \brief Base class for all analysis operations. 0019 * 0020 * AbstractFilter provides an abstraction for analysis operations. It is modelled on an 0021 * electronic filtering circuit: From the outside, a filter appears as a black box with 0022 * a number of input and output ports (obviously, those numbers do not necessarily agree). 0023 * 0024 * \section using Using AbstractFilter 0025 * You can connect one AbstractColumn to each input port using 0026 * input(int port, AbstractColumn* source). Every output(int port) is realized 0027 * again by an AbstractColumn, which you can connect to as many other filters, tables 0028 * or plots as you like. 0029 * Ownership of the data sources always stays with the class which is providing the data, 0030 * that is, neither input() nor output() transfer ownership. 0031 * 0032 * Furthermore, you can use inputCount() and outputCount() to query the number of 0033 * input and output ports, respectively and you can obtain label strings for inputs (via 0034 * inputLabel()) and outputs (via AbstractColumn::label()). This allows generic filter 0035 * handling routines to be written, which is important for using filters provided by plugins. 0036 * 0037 * Its simplicity of use notwithstanding, AbstractFilter provides a powerful and versatile 0038 * basis also for analysis operations that would not commonly be referred to as "filter". 0039 * An example of such a more advanced filter implementation is StatisticsFilter. 0040 * 0041 * \section subclassing Subclassing AbstractFilter 0042 * The main design goal was to make implementing new filters as easy as possible. 0043 * Filters with only one output port can subclass AbstractSimpleFilter, which is even easier 0044 * to use. Filters with more than one output port have to subclass 0045 * AbstractFilter directly, which is slightly more involved, because in 0046 * addition to data transfer between these classes the signals defined by AbstractColumn 0047 * have to be handled on both inputs and outputs. Signals from data sources connected to the input 0048 * ports are automatically connected to a matching set of virtual methods, which can be 0049 * reimplemented by subclasses to handle these events. 0050 * 0051 * While AbstractFilter handles the tedious part of connecting a data source to an input port, 0052 * its subclasses are given a chance to reject such connections (e.g., based on the data type 0053 * of the source) by reimplementing inputAcceptable(). 0054 * 0055 * \sa AbstractSimpleFilter 0056 */ 0057 0058 /** 0059 * \fn AbstractFilter::inputCount() const 0060 * \brief Return the number of input ports supported by the filter or -1 if any number of inputs is acceptable. 0061 */ 0062 0063 /** 0064 * \fn AbstractFilter::outputCount() const 0065 * \brief Return the number of output ports provided by the filter. 0066 * 0067 * %Note that this number need not be static, but can be dynamically determined, for example 0068 * based on the inputs provided to the filter. 0069 */ 0070 0071 /** 0072 * \brief Return the index of the highest input port that is connected. 0073 * 0074 * Note that this is different from both the number of ports that <em>could</em> be connected, 0075 * inputCount(), and the number of ports that actually <em>have been</em> connected, which are 0076 * not necessarily sequential. In conjunction with input(int), this method can be used to 0077 * traverse the connected inputs. 0078 */ 0079 int AbstractFilter::highestConnectedInput() const { 0080 return m_inputs.count() - 1; 0081 } 0082 0083 /** 0084 * \brief Connect the provided data source to the specified input port. 0085 * \param port the port number to which to connect 0086 * \param source the data source to connect to the input port 0087 * \returns true if the connection was accepted, false otherwise. 0088 * 0089 * The port number is checked for validity against inputCount() and both port number and data 0090 * source are passed to inputAcceptable() for review. If both checks succeed, the 0091 * source is recorded in #m_inputs. 0092 * If applicable, the previously connected data source is disconnected before replacing it. 0093 * 0094 * You can also use this method to disconnect an input without replacing it with a new one by 0095 * calling it with source=0. 0096 * 0097 * \sa inputAcceptable(), #m_inputs 0098 */ 0099 bool AbstractFilter::input(int port, const AbstractColumn* source) { 0100 // DEBUG("AbstractFilter::input()"); 0101 0102 if (port < 0 || (inputCount() >= 0 && port >= inputCount())) 0103 return false; 0104 if (source && !inputAcceptable(port, source)) 0105 return false; 0106 0107 if (port >= m_inputs.size()) 0108 m_inputs.resize(port + 1); 0109 const AbstractColumn* old_input = m_inputs.value(port); 0110 if (source == old_input) 0111 return true; 0112 0113 if (old_input) { 0114 disconnect(old_input, nullptr, this, nullptr); 0115 // replace input, notifying the filter implementation of the changes 0116 inputDescriptionAboutToChange(old_input); 0117 inputPlotDesignationAboutToChange(old_input); 0118 inputMaskingAboutToChange(old_input); 0119 inputDataAboutToChange(old_input); 0120 if (source && source->columnMode() != old_input->columnMode()) 0121 inputModeAboutToChange(old_input); 0122 } 0123 if (!source) 0124 inputAboutToBeDisconnected(old_input); 0125 m_inputs[port] = source; 0126 if (source) { // we have a new source 0127 // DEBUG(" new source"); 0128 if (old_input && source->columnMode() != old_input->columnMode()) 0129 inputModeAboutToChange(source); 0130 inputDataChanged(source); 0131 inputMaskingChanged(source); 0132 inputPlotDesignationChanged(source); 0133 inputDescriptionChanged(source); 0134 // connect the source's signals 0135 connect(source, 0136 &AbstractColumn::aspectDescriptionAboutToChange, 0137 this, 0138 static_cast<void (AbstractFilter::*)(const AbstractAspect*)>(&AbstractFilter::inputDescriptionAboutToChange)); 0139 connect(source, 0140 &AbstractColumn::aspectDescriptionChanged, 0141 this, 0142 static_cast<void (AbstractFilter::*)(const AbstractAspect*)>(&AbstractFilter::inputDescriptionChanged)); 0143 connect(source, &AbstractColumn::plotDesignationAboutToChange, this, &AbstractFilter::inputPlotDesignationAboutToChange); 0144 connect(source, &AbstractColumn::plotDesignationChanged, this, &AbstractFilter::inputPlotDesignationChanged); 0145 connect(source, &AbstractColumn::modeAboutToChange, this, &AbstractFilter::inputModeAboutToChange); 0146 connect(source, &AbstractColumn::modeChanged, this, &AbstractFilter::inputModeChanged); 0147 connect(source, &AbstractColumn::dataAboutToChange, this, &AbstractFilter::inputDataAboutToChange); 0148 connect(source, &AbstractColumn::dataChanged, this, &AbstractFilter::inputDataChanged); 0149 connect(source, &AbstractColumn::rowsAboutToBeInserted, this, &AbstractFilter::inputRowsAboutToBeInserted); 0150 connect(source, &AbstractColumn::rowsInserted, this, &AbstractFilter::inputRowsInserted); 0151 connect(source, &AbstractColumn::rowsAboutToBeRemoved, this, &AbstractFilter::inputRowsAboutToBeRemoved); 0152 connect(source, &AbstractColumn::rowsRemoved, this, &AbstractFilter::inputRowsRemoved); 0153 connect(source, &AbstractColumn::maskingAboutToChange, this, &AbstractFilter::inputMaskingAboutToChange); 0154 connect(source, &AbstractColumn::maskingChanged, this, &AbstractFilter::inputMaskingChanged); 0155 connect(source, &AbstractColumn::aboutToBeDestroyed, this, &AbstractFilter::inputAboutToBeDestroyed); 0156 } else { // source == 0, that is, the input port has been disconnected 0157 // DEBUG(" no source"); 0158 // try to shrink m_inputs 0159 int num_connected_inputs = m_inputs.size(); 0160 while (m_inputs.at(num_connected_inputs - 1) == nullptr) { 0161 num_connected_inputs--; 0162 if (!num_connected_inputs) 0163 break; 0164 } 0165 m_inputs.resize(num_connected_inputs); 0166 } 0167 0168 return true; 0169 } 0170 0171 /** 0172 * \brief Connect all outputs of the provided filter to the corresponding inputs of this filter. 0173 * \returns true if all connections were accepted, false otherwise 0174 * 0175 * Overloaded method provided for convenience. 0176 */ 0177 bool AbstractFilter::input(const AbstractFilter* sources) { 0178 if (!sources) 0179 return false; 0180 bool result = true; 0181 for (int i = 0; i < sources->outputCount(); ++i) 0182 if (!input(i, sources->output(i))) 0183 result = false; 0184 return result; 0185 } 0186 0187 /** 0188 * \brief Return the input currently connected to the specified port, or 0. 0189 */ 0190 const AbstractColumn* AbstractFilter::input(int port) const { 0191 return m_inputs.value(port); 0192 } 0193 0194 /** 0195 * \brief Return the label associated to the given input port. 0196 * 0197 * Default labels are In1, In2, ... (or translated equivalents), but implementations can 0198 * reimplement this method to produce more meaningful labels. 0199 * 0200 * Output ports are implicitly labeled through AbstractAspect::name(). 0201 */ 0202 QString AbstractFilter::inputLabel(int port) const { 0203 return i18nc("default labels of filter input ports", "In%1", port + 1); 0204 } 0205 0206 /** 0207 * \fn AbstractColumn *AbstractFilter::output(int port=0) 0208 * \brief Get the data source associated with the specified output port. 0209 * 0210 * The returned pointer may be 0 even for valid port numbers, for example if not all required 0211 * input ports have been connected. 0212 */ 0213 0214 /** 0215 * \fn const AbstractColumn *AbstractFilter::output(int port=0) const 0216 * \brief Overloaded method for const access. 0217 */ 0218 0219 /** 0220 * \brief Return the input port to which the column is connected or -1 if it's not connected 0221 */ 0222 int AbstractFilter::portIndexOf(const AbstractColumn* column) { 0223 for (int i = 0; i < m_inputs.size(); ++i) 0224 if (m_inputs.at(i) == column) 0225 return i; 0226 return -1; 0227 } 0228 0229 /** 0230 * \brief Give implementations a chance to reject connections to their input ports. 0231 * 0232 * If not reimplemented, all connections to ports within [0, inputCount()-1] will be accepted. 0233 */ 0234 bool AbstractFilter::inputAcceptable(int /*port*/, const AbstractColumn* /*source*/) { 0235 return true; 0236 } 0237 0238 /** 0239 * \brief Called whenever an input is disconnected or deleted. 0240 * 0241 * This is only to notify implementations of the event, the default implementation is a 0242 * no-op. 0243 */ 0244 void AbstractFilter::inputAboutToBeDisconnected(const AbstractColumn* /*source*/) { 0245 } 0246 0247 //////////////////////////////////////////////////////////////////////////////////////////////////// 0248 //!\name signal handlers 0249 //@{ 0250 //////////////////////////////////////////////////////////////////////////////////////////////////// 0251 0252 /** 0253 * \brief Name and/or comment of an input will be changed. 0254 * 0255 * \param source is always the this pointer of the column that emitted the signal. 0256 */ 0257 void AbstractFilter::inputDescriptionAboutToChange(const AbstractColumn* /*source*/) { 0258 } 0259 0260 void AbstractFilter::inputDescriptionAboutToChange(const AbstractAspect* aspect) { 0261 const auto* col = qobject_cast<const AbstractColumn*>(aspect); 0262 if (col) 0263 inputDescriptionAboutToChange(col); 0264 } 0265 0266 /** 0267 * \brief Name and/or comment of an input changed. 0268 * 0269 * \param source is always the this pointer of the column that emitted the signal. 0270 */ 0271 void AbstractFilter::inputDescriptionChanged(const AbstractColumn* /*source*/) { 0272 } 0273 0274 void AbstractFilter::inputDescriptionChanged(const AbstractAspect* aspect) { 0275 const auto* col = qobject_cast<const AbstractColumn*>(aspect); 0276 if (col && m_inputs.contains(col)) 0277 inputDescriptionChanged(col); 0278 } 0279 0280 /** 0281 * \brief The plot designation of an input is about to change. 0282 * 0283 * \param source is always the this pointer of the column that emitted the signal. 0284 */ 0285 void AbstractFilter::inputPlotDesignationAboutToChange(const AbstractColumn* /*source*/) { 0286 } 0287 0288 /** 0289 * \brief The plot designation of an input changed. 0290 * 0291 * \param source is always the this pointer of the column that emitted the signal. 0292 */ 0293 void AbstractFilter::inputPlotDesignationChanged(const AbstractColumn* /*source*/) { 0294 } 0295 0296 /** 0297 * \brief The display mode and possibly the data type of an input is about to change. 0298 * 0299 * \param source is always the this pointer of the column that emitted the signal. 0300 */ 0301 void AbstractFilter::inputModeAboutToChange(const AbstractColumn* /*source*/) { 0302 } 0303 0304 /** 0305 * \brief The display mode and possibly the data type has changed. 0306 * 0307 * \param source is always the this pointer of the column that emitted the signal. 0308 */ 0309 void AbstractFilter::inputModeChanged(const AbstractColumn* /*source*/) { 0310 } 0311 0312 /** 0313 * \brief The data of an input is about to change. 0314 * 0315 * \param source is always the this pointer of the column that emitted the signal. 0316 */ 0317 void AbstractFilter::inputDataAboutToChange(const AbstractColumn* /*source*/) { 0318 } 0319 0320 /** 0321 * \brief The data of an input has changed. 0322 * 0323 * \param source is always the this pointer of the column that emitted the signal. 0324 */ 0325 void AbstractFilter::inputDataChanged(const AbstractColumn* /*source*/) { 0326 } 0327 0328 //////////////////////////////////////////////////////////////////////////////////////////////////// 0329 //@} 0330 //////////////////////////////////////////////////////////////////////////////////////////////////// 0331 0332 /** 0333 * \var AbstractFilter::m_inputs 0334 * \brief The data sources connected to my input ports. 0335 */