File indexing completed on 2025-07-13 03:32:48
0001 /* 0002 File : XYEquationCurveDock.cpp 0003 Project : LabPlot 0004 Description : widget for editing properties of equation curves 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2014-2024 Alexander Semke <alexander.semke@web.de> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "XYEquationCurveDock.h" 0012 #include "backend/gsl/ExpressionParser.h" 0013 #include "backend/worksheet/plots/cartesian/XYEquationCurve.h" 0014 #include "kdefrontend/widgets/ConstantsWidget.h" 0015 #include "kdefrontend/widgets/FunctionsWidget.h" 0016 0017 #include <QCompleter> 0018 #include <QKeyEvent> 0019 #include <QMenu> 0020 #include <QWidgetAction> 0021 0022 #include <KLocalizedString> 0023 0024 /*! 0025 \class XYEquationCurveDock 0026 \brief Provides a widget for editing the properties of the XYEquationCurves 0027 (2D-curves defined by a mathematical equation) currently selected in 0028 the project explorer. 0029 0030 If more than one curves are set, the properties of the first column are shown. 0031 The changes of the properties are applied to all curves. 0032 The exclusions are the name, the comment and the datasets (columns) of 0033 the curves - these properties can only be changed if there is only one single curve. 0034 0035 \ingroup kdefrontend 0036 */ 0037 0038 XYEquationCurveDock::XYEquationCurveDock(QWidget* parent) 0039 : XYCurveDock(parent) { 0040 // remove the tab "Error bars" 0041 ui.tabWidget->removeTab(5); 0042 } 0043 0044 /*! 0045 * // Tab "General" 0046 */ 0047 void XYEquationCurveDock::setupGeneral() { 0048 auto* generalTab = new QWidget(ui.tabGeneral); 0049 uiGeneralTab.setupUi(generalTab); 0050 setPlotRangeCombobox(uiGeneralTab.cbPlotRanges); 0051 setBaseWidgets(uiGeneralTab.leName, uiGeneralTab.teComment); 0052 setVisibilityWidgets(uiGeneralTab.chkVisible, uiGeneralTab.chkLegendVisible); 0053 0054 auto* gridLayout = dynamic_cast<QGridLayout*>(generalTab->layout()); 0055 if (gridLayout) { 0056 gridLayout->setContentsMargins(2, 2, 2, 2); 0057 gridLayout->setHorizontalSpacing(2); 0058 gridLayout->setVerticalSpacing(2); 0059 } 0060 0061 auto* layout = new QHBoxLayout(ui.tabGeneral); 0062 layout->setContentsMargins(0, 0, 0, 0); 0063 layout->addWidget(generalTab); 0064 0065 uiGeneralTab.tbConstants1->setIcon(QIcon::fromTheme(QStringLiteral("labplot-format-text-symbol"))); 0066 uiGeneralTab.tbFunctions1->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-font"))); 0067 0068 uiGeneralTab.tbConstants2->setIcon(QIcon::fromTheme(QStringLiteral("labplot-format-text-symbol"))); 0069 uiGeneralTab.tbFunctions2->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-font"))); 0070 0071 uiGeneralTab.cbType->addItem(i18n("Cartesian")); 0072 uiGeneralTab.cbType->addItem(i18n("Polar")); 0073 uiGeneralTab.cbType->addItem(i18n("Parametric")); 0074 // uiGeneralTab.cbType->addItem(i18n("Implicit")); 0075 0076 uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme(QStringLiteral("run-build"))); 0077 0078 uiGeneralTab.teEquation2->setExpressionType(XYEquationCurve::EquationType::Parametric); 0079 0080 // uiGeneralTab.teEquation1->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()*2); 0081 // uiGeneralTab.teEquation2->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()*2); 0082 uiGeneralTab.teMin->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()); 0083 uiGeneralTab.teMax->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()); 0084 0085 // Slots 0086 connect(uiGeneralTab.cbType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYEquationCurveDock::typeChanged); 0087 connect(uiGeneralTab.teEquation1, &ExpressionTextEdit::expressionChanged, this, &XYEquationCurveDock::enableRecalculate); 0088 connect(uiGeneralTab.teEquation2, &ExpressionTextEdit::expressionChanged, this, &XYEquationCurveDock::enableRecalculate); 0089 connect(uiGeneralTab.tbConstants1, &QToolButton::clicked, this, &XYEquationCurveDock::showConstants); 0090 connect(uiGeneralTab.tbFunctions1, &QToolButton::clicked, this, &XYEquationCurveDock::showFunctions); 0091 connect(uiGeneralTab.tbConstants2, &QToolButton::clicked, this, &XYEquationCurveDock::showConstants); 0092 connect(uiGeneralTab.tbFunctions2, &QToolButton::clicked, this, &XYEquationCurveDock::showFunctions); 0093 connect(uiGeneralTab.teMin, &ExpressionTextEdit::expressionChanged, this, &XYEquationCurveDock::enableRecalculate); 0094 connect(uiGeneralTab.teMax, &ExpressionTextEdit::expressionChanged, this, &XYEquationCurveDock::enableRecalculate); 0095 connect(uiGeneralTab.sbCount, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYEquationCurveDock::enableRecalculate); 0096 connect(uiGeneralTab.pbRecalculate, &QPushButton::clicked, this, &XYEquationCurveDock::recalculateClicked); 0097 } 0098 0099 void XYEquationCurveDock::initGeneralTab() { 0100 // show the properties of the first curve 0101 const auto* equationCurve = static_cast<const XYEquationCurve*>(m_curve); 0102 Q_ASSERT(equationCurve); 0103 const XYEquationCurve::EquationData& data = equationCurve->equationData(); 0104 uiGeneralTab.cbType->setCurrentIndex(static_cast<int>(data.type)); 0105 this->typeChanged(static_cast<int>(data.type)); 0106 uiGeneralTab.teEquation1->setText(data.expression1); 0107 uiGeneralTab.teEquation2->setText(data.expression2); 0108 uiGeneralTab.teMin->setText(data.min); 0109 uiGeneralTab.teMax->setText(data.max); 0110 uiGeneralTab.sbCount->setValue(data.count); 0111 0112 uiGeneralTab.chkLegendVisible->setChecked(m_curve->legendVisible()); 0113 uiGeneralTab.chkVisible->setChecked(m_curve->isVisible()); 0114 0115 // Slots 0116 connect(m_equationCurve, &XYEquationCurve::equationDataChanged, this, &XYEquationCurveDock::curveEquationDataChanged); 0117 } 0118 0119 /*! 0120 sets the curves. The properties of the curves in the list \c list can be edited in this widget. 0121 */ 0122 void XYEquationCurveDock::setCurves(QList<XYCurve*> list) { 0123 CONDITIONAL_LOCK_RETURN; 0124 m_curvesList = list; 0125 m_curve = list.first(); 0126 setAspects(list); 0127 m_equationCurve = static_cast<XYEquationCurve*>(m_curve); 0128 Q_ASSERT(m_equationCurve); 0129 XYCurveDock::setModel(); 0130 initGeneralTab(); 0131 initTabs(); 0132 setSymbols(list); 0133 0134 updatePlotRangeList(); 0135 0136 uiGeneralTab.pbRecalculate->setEnabled(false); 0137 } 0138 0139 //************************************************************* 0140 //**** SLOTs for changes triggered in XYEquationCurveDock ***** 0141 //************************************************************* 0142 void XYEquationCurveDock::typeChanged(int index) { 0143 const auto type{XYEquationCurve::EquationType(index)}; 0144 if (type == XYEquationCurve::EquationType::Cartesian) { 0145 uiGeneralTab.lEquation1->setText(QStringLiteral("y=f(x)")); 0146 uiGeneralTab.lEquation2->hide(); 0147 uiGeneralTab.teEquation2->hide(); 0148 uiGeneralTab.tbFunctions2->hide(); 0149 uiGeneralTab.tbConstants2->hide(); 0150 uiGeneralTab.lMin->show(); 0151 uiGeneralTab.lMax->show(); 0152 uiGeneralTab.teMin->show(); 0153 uiGeneralTab.teMax->show(); 0154 uiGeneralTab.lMin->setText(i18n("x, min")); 0155 uiGeneralTab.lMax->setText(i18n("x, max")); 0156 } else if (type == XYEquationCurve::EquationType::Polar) { 0157 uiGeneralTab.lEquation1->setText(QString::fromUtf8("r(φ)")); 0158 uiGeneralTab.lEquation2->hide(); 0159 uiGeneralTab.teEquation2->hide(); 0160 uiGeneralTab.tbFunctions2->hide(); 0161 uiGeneralTab.tbConstants2->hide(); 0162 uiGeneralTab.lMin->show(); 0163 uiGeneralTab.lMax->show(); 0164 uiGeneralTab.teMin->show(); 0165 uiGeneralTab.teMax->show(); 0166 uiGeneralTab.lMin->setText(i18n("φ, min")); 0167 uiGeneralTab.lMax->setText(i18n("φ, max")); 0168 } else if (type == XYEquationCurve::EquationType::Parametric) { 0169 uiGeneralTab.lEquation1->setText(QStringLiteral("x=f(t)")); 0170 uiGeneralTab.lEquation2->setText(QStringLiteral("y=f(t)")); 0171 uiGeneralTab.lEquation2->show(); 0172 uiGeneralTab.teEquation2->show(); 0173 uiGeneralTab.tbFunctions2->show(); 0174 uiGeneralTab.tbConstants2->show(); 0175 uiGeneralTab.lMin->show(); 0176 uiGeneralTab.lMax->show(); 0177 uiGeneralTab.teMin->show(); 0178 uiGeneralTab.teMax->show(); 0179 uiGeneralTab.lMin->setText(i18n("t, min")); 0180 uiGeneralTab.lMax->setText(i18n("t, max")); 0181 } else if (type == XYEquationCurve::EquationType::Implicit) { 0182 uiGeneralTab.lEquation1->setText(QStringLiteral("f(x,y)")); 0183 uiGeneralTab.lEquation2->hide(); 0184 uiGeneralTab.teEquation2->hide(); 0185 uiGeneralTab.tbFunctions2->hide(); 0186 uiGeneralTab.tbConstants2->hide(); 0187 uiGeneralTab.lMin->hide(); 0188 uiGeneralTab.lMax->hide(); 0189 uiGeneralTab.teMin->hide(); 0190 uiGeneralTab.teMax->hide(); 0191 } 0192 0193 uiGeneralTab.teEquation1->setExpressionType(type); 0194 this->enableRecalculate(); 0195 } 0196 0197 void XYEquationCurveDock::recalculateClicked() { 0198 XYEquationCurve::EquationData data; 0199 data.type = (XYEquationCurve::EquationType)uiGeneralTab.cbType->currentIndex(); 0200 data.expression1 = uiGeneralTab.teEquation1->document()->toPlainText(); 0201 data.expression2 = uiGeneralTab.teEquation2->document()->toPlainText(); 0202 data.min = uiGeneralTab.teMin->document()->toPlainText(); 0203 data.max = uiGeneralTab.teMax->document()->toPlainText(); 0204 data.count = uiGeneralTab.sbCount->value(); 0205 0206 for (auto* curve : m_curvesList) 0207 static_cast<XYEquationCurve*>(curve)->setEquationData(data); 0208 0209 uiGeneralTab.pbRecalculate->setEnabled(false); 0210 updatePlotRangeList(); // axes range may change when range on auto scale 0211 } 0212 0213 void XYEquationCurveDock::showConstants() { 0214 QMenu menu; 0215 ConstantsWidget constants(&menu); 0216 0217 if (QObject::sender() == uiGeneralTab.tbConstants1) 0218 connect(&constants, &ConstantsWidget::constantSelected, this, &XYEquationCurveDock::insertConstant1); 0219 else 0220 connect(&constants, &ConstantsWidget::constantSelected, this, &XYEquationCurveDock::insertConstant2); 0221 0222 connect(&constants, &ConstantsWidget::constantSelected, &menu, &QMenu::close); 0223 connect(&constants, &ConstantsWidget::canceled, &menu, &QMenu::close); 0224 0225 auto* widgetAction = new QWidgetAction(this); 0226 widgetAction->setDefaultWidget(&constants); 0227 menu.addAction(widgetAction); 0228 0229 if (QObject::sender() == uiGeneralTab.tbConstants1) { 0230 QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbConstants1->width(), -menu.sizeHint().height()); 0231 menu.exec(uiGeneralTab.tbConstants1->mapToGlobal(pos)); 0232 } else { 0233 QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbConstants2->width(), -menu.sizeHint().height()); 0234 menu.exec(uiGeneralTab.tbConstants2->mapToGlobal(pos)); 0235 } 0236 } 0237 0238 void XYEquationCurveDock::showFunctions() { 0239 QMenu menu; 0240 FunctionsWidget functions(&menu); 0241 if (QObject::sender() == uiGeneralTab.tbFunctions1) 0242 connect(&functions, &FunctionsWidget::functionSelected, this, &XYEquationCurveDock::insertFunction1); 0243 else 0244 connect(&functions, &FunctionsWidget::functionSelected, this, &XYEquationCurveDock::insertFunction2); 0245 0246 connect(&functions, &FunctionsWidget::functionSelected, &menu, &QMenu::close); 0247 connect(&functions, &FunctionsWidget::canceled, &menu, &QMenu::close); 0248 0249 auto* widgetAction = new QWidgetAction(this); 0250 widgetAction->setDefaultWidget(&functions); 0251 menu.addAction(widgetAction); 0252 0253 if (QObject::sender() == uiGeneralTab.tbFunctions1) { 0254 QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbFunctions1->width(), -menu.sizeHint().height()); 0255 menu.exec(uiGeneralTab.tbFunctions1->mapToGlobal(pos)); 0256 } else { 0257 QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbFunctions2->width(), -menu.sizeHint().height()); 0258 menu.exec(uiGeneralTab.tbFunctions2->mapToGlobal(pos)); 0259 } 0260 } 0261 0262 void XYEquationCurveDock::insertFunction1(const QString& functionName) { 0263 const auto type{XYEquationCurve::EquationType(uiGeneralTab.cbType->currentIndex())}; 0264 0265 uiGeneralTab.teEquation1->insertPlainText(functionName + ExpressionParser::functionArgumentString(functionName, type)); 0266 } 0267 0268 void XYEquationCurveDock::insertConstant1(const QString& constantsName) { 0269 uiGeneralTab.teEquation1->insertPlainText(constantsName); 0270 } 0271 0272 void XYEquationCurveDock::insertFunction2(const QString& functionName) { 0273 uiGeneralTab.teEquation1->insertPlainText(functionName + ExpressionParser::functionArgumentString(functionName, XYEquationCurve::EquationType::Parametric)); 0274 } 0275 0276 void XYEquationCurveDock::insertConstant2(const QString& constantsName) { 0277 uiGeneralTab.teEquation2->insertPlainText(constantsName); 0278 } 0279 0280 void XYEquationCurveDock::enableRecalculate() { 0281 CONDITIONAL_RETURN_NO_LOCK; 0282 0283 // check whether the formula expressions are correct 0284 bool valid = false; 0285 const auto type = XYEquationCurve::EquationType(uiGeneralTab.cbType->currentIndex()); 0286 if (type != XYEquationCurve::EquationType::Parametric) 0287 valid = uiGeneralTab.teEquation1->isValid(); 0288 else 0289 valid = (uiGeneralTab.teEquation1->isValid() && uiGeneralTab.teEquation2->isValid()); 0290 0291 valid = (valid && uiGeneralTab.teMin->isValid() && uiGeneralTab.teMax->isValid()); 0292 uiGeneralTab.pbRecalculate->setEnabled(valid); 0293 0294 updatePlotRangeList(); 0295 } 0296 0297 //************************************************************* 0298 //*********** SLOTs for changes triggered in XYCurve ********** 0299 //************************************************************* 0300 // General-Tab 0301 void XYEquationCurveDock::curveEquationDataChanged(const XYEquationCurve::EquationData& data) { 0302 CONDITIONAL_LOCK_RETURN; 0303 uiGeneralTab.cbType->setCurrentIndex(static_cast<int>(data.type)); 0304 uiGeneralTab.teEquation1->setText(data.expression1); 0305 uiGeneralTab.teEquation2->setText(data.expression2); 0306 uiGeneralTab.teMin->setText(data.min); 0307 uiGeneralTab.teMax->setText(data.max); 0308 uiGeneralTab.sbCount->setValue(data.count); 0309 }