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