File indexing completed on 2024-05-12 15:28:06

0001 /***************************************************************************
0002     File             : XYEquationCurveDock.cpp
0003     Project          : LabPlot
0004     --------------------------------------------------------------------
0005     Copyright        : (C) 2014-2021 Alexander Semke (alexander.semke@web.de)
0006     Description      : widget for editing properties of equation curves
0007 
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "XYEquationCurveDock.h"
0030 #include "backend/core/AspectTreeModel.h"
0031 #include "backend/core/Project.h"
0032 #include "backend/worksheet/plots/cartesian/XYEquationCurve.h"
0033 #include "backend/gsl/ExpressionParser.h"
0034 #include "kdefrontend/widgets/ConstantsWidget.h"
0035 #include "kdefrontend/widgets/FunctionsWidget.h"
0036 
0037 #include <QCompleter>
0038 #include <QKeyEvent>
0039 #include <QMenu>
0040 #include <QWidgetAction>
0041 
0042 #include <KLocalizedString>
0043 
0044 /*!
0045   \class XYEquationCurveDock
0046   \brief  Provides a widget for editing the properties of the XYEquationCurves
0047         (2D-curves defined by a mathematical equation) currently selected in
0048         the project explorer.
0049 
0050   If more then one curves are set, the properties of the first column are shown.
0051   The changes of the properties are applied to all curves.
0052   The exclusions are the name, the comment and the datasets (columns) of
0053   the curves  - these properties can only be changed if there is only one single curve.
0054 
0055   \ingroup kdefrontend
0056 */
0057 
0058 XYEquationCurveDock::XYEquationCurveDock(QWidget *parent): XYCurveDock(parent) {
0059     //remove the tab "Error bars"
0060     ui.tabWidget->removeTab(5);
0061 }
0062 
0063 /*!
0064  *  // Tab "General"
0065  */
0066 void XYEquationCurveDock::setupGeneral() {
0067     QWidget* generalTab = new QWidget(ui.tabGeneral);
0068     uiGeneralTab.setupUi(generalTab);
0069     m_leName = uiGeneralTab.leName;
0070     m_leComment = uiGeneralTab.leComment;
0071 
0072     auto* gridLayout = dynamic_cast<QGridLayout*>(generalTab->layout());
0073     if (gridLayout) {
0074         gridLayout->setContentsMargins(2, 2, 2, 2);
0075         gridLayout->setHorizontalSpacing(2);
0076         gridLayout->setVerticalSpacing(2);
0077     }
0078 
0079     auto* layout = new QHBoxLayout(ui.tabGeneral);
0080     layout->setMargin(0);
0081     layout->addWidget(generalTab);
0082 
0083     uiGeneralTab.tbConstants1->setIcon( QIcon::fromTheme("labplot-format-text-symbol") );
0084     uiGeneralTab.tbFunctions1->setIcon( QIcon::fromTheme("preferences-desktop-font") );
0085 
0086     uiGeneralTab.tbConstants2->setIcon( QIcon::fromTheme("labplot-format-text-symbol") );
0087     uiGeneralTab.tbFunctions2->setIcon( QIcon::fromTheme("preferences-desktop-font") );
0088 
0089     uiGeneralTab.cbType->addItem(i18n("Cartesian"));
0090     uiGeneralTab.cbType->addItem(i18n("Polar"));
0091     uiGeneralTab.cbType->addItem(i18n("Parametric"));
0092 //  uiGeneralTab.cbType->addItem(i18n("Implicit"));
0093 
0094     uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build"));
0095 
0096     uiGeneralTab.teEquation2->setExpressionType(XYEquationCurve::EquationType::Parametric);
0097 
0098 //  uiGeneralTab.teEquation1->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()*2);
0099 //  uiGeneralTab.teEquation2->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()*2);
0100     uiGeneralTab.teMin->setMaximumHeight(uiGeneralTab.leName->sizeHint().height());
0101     uiGeneralTab.teMax->setMaximumHeight(uiGeneralTab.leName->sizeHint().height());
0102 
0103     //Slots
0104     connect(uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYEquationCurveDock::nameChanged);
0105     connect(uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYEquationCurveDock::commentChanged);
0106     connect(uiGeneralTab.chkVisible, &QCheckBox::clicked, this, &XYEquationCurveDock::visibilityChanged);
0107 
0108     connect(uiGeneralTab.cbType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYEquationCurveDock::typeChanged);
0109     connect(uiGeneralTab.teEquation1, &ExpressionTextEdit::expressionChanged, this, &XYEquationCurveDock::enableRecalculate);
0110     connect(uiGeneralTab.teEquation2, &ExpressionTextEdit::expressionChanged, this, &XYEquationCurveDock::enableRecalculate);
0111     connect(uiGeneralTab.tbConstants1, &QToolButton::clicked, this, &XYEquationCurveDock::showConstants);
0112     connect(uiGeneralTab.tbFunctions1, &QToolButton::clicked, this, &XYEquationCurveDock::showFunctions);
0113     connect(uiGeneralTab.tbConstants2, &QToolButton::clicked, this, &XYEquationCurveDock::showConstants);
0114     connect(uiGeneralTab.tbFunctions2, &QToolButton::clicked, this, &XYEquationCurveDock::showFunctions);
0115     connect(uiGeneralTab.teMin, &ExpressionTextEdit::expressionChanged, this, &XYEquationCurveDock::enableRecalculate);
0116     connect(uiGeneralTab.teMax, &ExpressionTextEdit::expressionChanged, this, &XYEquationCurveDock::enableRecalculate);
0117     connect(uiGeneralTab.sbCount, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYEquationCurveDock::enableRecalculate);
0118     connect(uiGeneralTab.pbRecalculate, &QPushButton::clicked, this, &XYEquationCurveDock::recalculateClicked);
0119 }
0120 
0121 /*!
0122   sets the curves. The properties of the curves in the list \c list can be edited in this widget.
0123 */
0124 void XYEquationCurveDock::setCurves(QList<XYCurve*> list) {
0125     m_initializing = true;
0126     m_curvesList = list;
0127     m_curve = list.first();
0128     m_aspect = list.first();
0129     m_equationCurve = dynamic_cast<XYEquationCurve*>(m_curve);
0130     Q_ASSERT(m_equationCurve);
0131     m_aspectTreeModel =  new AspectTreeModel(m_curve->project());
0132     XYCurveDock::setModel();
0133     initGeneralTab();
0134     initTabs();
0135     uiGeneralTab.pbRecalculate->setEnabled(false);
0136     m_initializing = false;
0137 }
0138 
0139 void XYEquationCurveDock::initGeneralTab() {
0140     //if there are more then one curve in the list, disable the tab "general"
0141     if (m_curvesList.size() == 1) {
0142         uiGeneralTab.lName->setEnabled(true);
0143         uiGeneralTab.leName->setEnabled(true);
0144         uiGeneralTab.lComment->setEnabled(true);
0145         uiGeneralTab.leComment->setEnabled(true);
0146 
0147         uiGeneralTab.leName->setText(m_curve->name());
0148         uiGeneralTab.leComment->setText(m_curve->comment());
0149     } else {
0150         uiGeneralTab.lName->setEnabled(false);
0151         uiGeneralTab.leName->setEnabled(false);
0152         uiGeneralTab.lComment->setEnabled(false);
0153         uiGeneralTab.leComment->setEnabled(false);
0154 
0155         uiGeneralTab.leName->setText(QString());
0156         uiGeneralTab.leComment->setText(QString());
0157     }
0158 
0159     //show the properties of the first curve
0160     const auto* equationCurve = dynamic_cast<const XYEquationCurve*>(m_curve);
0161     Q_ASSERT(equationCurve);
0162     const XYEquationCurve::EquationData& data = equationCurve->equationData();
0163     uiGeneralTab.cbType->setCurrentIndex(static_cast<int>(data.type));
0164     this->typeChanged(static_cast<int>(data.type));
0165     uiGeneralTab.teEquation1->setText(data.expression1);
0166     uiGeneralTab.teEquation2->setText(data.expression2);
0167     uiGeneralTab.teMin->setText(data.min);
0168     uiGeneralTab.teMax->setText(data.max);
0169     uiGeneralTab.sbCount->setValue(data.count);
0170 
0171     uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() );
0172 
0173     //Slots
0174     connect(m_equationCurve, &XYEquationCurve::aspectDescriptionChanged,
0175             this, &XYEquationCurveDock::curveDescriptionChanged);
0176     connect(m_equationCurve, &XYEquationCurve::equationDataChanged,
0177             this, &XYEquationCurveDock::curveEquationDataChanged);
0178 }
0179 
0180 //*************************************************************
0181 //**** SLOTs for changes triggered in XYEquationCurveDock *****
0182 //*************************************************************
0183 void XYEquationCurveDock::typeChanged(int index) {
0184     const auto type{XYEquationCurve::EquationType(index)};
0185     if (type == XYEquationCurve::EquationType::Cartesian) {
0186         uiGeneralTab.lEquation1->setText("y=f(x)");
0187         uiGeneralTab.lEquation2->hide();
0188         uiGeneralTab.teEquation2->hide();
0189         uiGeneralTab.tbFunctions2->hide();
0190         uiGeneralTab.tbConstants2->hide();
0191         uiGeneralTab.lMin->show();
0192         uiGeneralTab.lMax->show();
0193         uiGeneralTab.teMin->show();
0194         uiGeneralTab.teMax->show();
0195         uiGeneralTab.lMin->setText(i18n("x, min"));
0196         uiGeneralTab.lMax->setText(i18n("x, max"));
0197     } else if (type == XYEquationCurve::EquationType::Polar) {
0198         uiGeneralTab.lEquation1->setText(QString::fromUtf8("r(φ)"));
0199         uiGeneralTab.lEquation2->hide();
0200         uiGeneralTab.teEquation2->hide();
0201         uiGeneralTab.tbFunctions2->hide();
0202         uiGeneralTab.tbConstants2->hide();
0203         uiGeneralTab.lMin->show();
0204         uiGeneralTab.lMax->show();
0205         uiGeneralTab.teMin->show();
0206         uiGeneralTab.teMax->show();
0207         uiGeneralTab.lMin->setText(i18n("φ, min"));
0208         uiGeneralTab.lMax->setText(i18n("φ, max"));
0209     } else if (type == XYEquationCurve::EquationType::Parametric) {
0210         uiGeneralTab.lEquation1->setText("x=f(t)");
0211         uiGeneralTab.lEquation2->setText("y=f(t)");
0212         uiGeneralTab.lEquation2->show();
0213         uiGeneralTab.teEquation2->show();
0214         uiGeneralTab.tbFunctions2->show();
0215         uiGeneralTab.tbConstants2->show();
0216         uiGeneralTab.lMin->show();
0217         uiGeneralTab.lMax->show();
0218         uiGeneralTab.teMin->show();
0219         uiGeneralTab.teMax->show();
0220         uiGeneralTab.lMin->setText(i18n("t, min"));
0221         uiGeneralTab.lMax->setText(i18n("t, max"));
0222     } else if (type == XYEquationCurve::EquationType::Implicit) {
0223         uiGeneralTab.lEquation1->setText("f(x,y)");
0224         uiGeneralTab.lEquation2->hide();
0225         uiGeneralTab.teEquation2->hide();
0226         uiGeneralTab.tbFunctions2->hide();
0227         uiGeneralTab.tbConstants2->hide();
0228         uiGeneralTab.lMin->hide();
0229         uiGeneralTab.lMax->hide();
0230         uiGeneralTab.teMin->hide();
0231         uiGeneralTab.teMax->hide();
0232     }
0233 
0234     uiGeneralTab.teEquation1->setExpressionType(type);
0235     this->enableRecalculate();
0236 }
0237 
0238 void XYEquationCurveDock::recalculateClicked() {
0239     XYEquationCurve::EquationData data;
0240     data.type = (XYEquationCurve::EquationType)uiGeneralTab.cbType->currentIndex();
0241     data.expression1 = uiGeneralTab.teEquation1->document()->toPlainText();
0242     data.expression2 = uiGeneralTab.teEquation2->document()->toPlainText();
0243     data.min = uiGeneralTab.teMin->document()->toPlainText();
0244     data.max = uiGeneralTab.teMax->document()->toPlainText();
0245     data.count = uiGeneralTab.sbCount->value();
0246 
0247     for (auto* curve : m_curvesList)
0248         static_cast<XYEquationCurve*>(curve)->setEquationData(data);
0249 
0250     uiGeneralTab.pbRecalculate->setEnabled(false);
0251 }
0252 
0253 void XYEquationCurveDock::showConstants() {
0254     QMenu menu;
0255     ConstantsWidget constants(&menu);
0256 
0257     if (QObject::sender() == uiGeneralTab.tbConstants1)
0258         connect(&constants, &ConstantsWidget::constantSelected, this, &XYEquationCurveDock::insertConstant1);
0259     else
0260         connect(&constants, &ConstantsWidget::constantSelected, this, &XYEquationCurveDock::insertConstant2);
0261 
0262     connect(&constants, &ConstantsWidget::constantSelected, &menu, &QMenu::close);
0263     connect(&constants, &ConstantsWidget::canceled, &menu, &QMenu::close);
0264 
0265     auto* widgetAction = new QWidgetAction(this);
0266     widgetAction->setDefaultWidget(&constants);
0267     menu.addAction(widgetAction);
0268 
0269     if (QObject::sender() == uiGeneralTab.tbConstants1) {
0270         QPoint pos(-menu.sizeHint().width()+uiGeneralTab.tbConstants1->width(),-menu.sizeHint().height());
0271         menu.exec(uiGeneralTab.tbConstants1->mapToGlobal(pos));
0272     } else {
0273         QPoint pos(-menu.sizeHint().width()+uiGeneralTab.tbConstants2->width(),-menu.sizeHint().height());
0274         menu.exec(uiGeneralTab.tbConstants2->mapToGlobal(pos));
0275     }
0276 }
0277 
0278 void XYEquationCurveDock::showFunctions() {
0279     QMenu menu;
0280     FunctionsWidget functions(&menu);
0281     if (QObject::sender() == uiGeneralTab.tbFunctions1)
0282         connect(&functions, &FunctionsWidget::functionSelected, this, &XYEquationCurveDock::insertFunction1);
0283     else
0284         connect(&functions, &FunctionsWidget::functionSelected, this, &XYEquationCurveDock::insertFunction2);
0285 
0286     connect(&functions, &FunctionsWidget::functionSelected, &menu, &QMenu::close);
0287     connect(&functions, &FunctionsWidget::canceled, &menu, &QMenu::close);
0288 
0289     auto* widgetAction = new QWidgetAction(this);
0290     widgetAction->setDefaultWidget(&functions);
0291     menu.addAction(widgetAction);
0292 
0293     if (QObject::sender() == uiGeneralTab.tbFunctions1) {
0294         QPoint pos(-menu.sizeHint().width()+uiGeneralTab.tbFunctions1->width(),-menu.sizeHint().height());
0295         menu.exec(uiGeneralTab.tbFunctions1->mapToGlobal(pos));
0296     } else {
0297         QPoint pos(-menu.sizeHint().width()+uiGeneralTab.tbFunctions2->width(),-menu.sizeHint().height());
0298         menu.exec(uiGeneralTab.tbFunctions2->mapToGlobal(pos));
0299     }
0300 }
0301 
0302 void XYEquationCurveDock::insertFunction1(const QString& functionName) {
0303     const auto type{XYEquationCurve::EquationType(uiGeneralTab.cbType->currentIndex())};
0304 
0305     uiGeneralTab.teEquation1->insertPlainText(functionName + ExpressionParser::functionArgumentString(functionName, type));
0306 }
0307 
0308 void XYEquationCurveDock::insertConstant1(const QString& constantsName) {
0309     uiGeneralTab.teEquation1->insertPlainText(constantsName);
0310 }
0311 
0312 void XYEquationCurveDock::insertFunction2(const QString& functionName) {
0313     uiGeneralTab.teEquation1->insertPlainText(functionName + ExpressionParser::functionArgumentString(functionName, XYEquationCurve::EquationType::Parametric));
0314 }
0315 
0316 void XYEquationCurveDock::insertConstant2(const QString& constantsName) {
0317     uiGeneralTab.teEquation2->insertPlainText(constantsName);
0318 }
0319 
0320 void XYEquationCurveDock::enableRecalculate() const {
0321     if (m_initializing)
0322         return;
0323 
0324     //check whether the formular expressions are correct
0325     bool valid = false;
0326     const auto type = XYEquationCurve::EquationType(uiGeneralTab.cbType->currentIndex());
0327     if (type != XYEquationCurve::EquationType::Parametric)
0328         valid = uiGeneralTab.teEquation1->isValid();
0329     else
0330         valid = (uiGeneralTab.teEquation1->isValid() && uiGeneralTab.teEquation2->isValid());
0331 
0332     valid = (valid && uiGeneralTab.teMin->isValid() && uiGeneralTab.teMax->isValid());
0333     uiGeneralTab.pbRecalculate->setEnabled(valid);
0334 }
0335 
0336 //*************************************************************
0337 //*********** SLOTs for changes triggered in XYCurve **********
0338 //*************************************************************
0339 //General-Tab
0340 void XYEquationCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) {
0341     if (m_curve != aspect)
0342         return;
0343 
0344     const Lock lock(m_initializing);
0345     if (aspect->name() != uiGeneralTab.leName->text())
0346         uiGeneralTab.leName->setText(aspect->name());
0347     else if (aspect->comment() != uiGeneralTab.leComment->text())
0348         uiGeneralTab.leComment->setText(aspect->comment());
0349 }
0350 
0351 void XYEquationCurveDock::curveEquationDataChanged(const XYEquationCurve::EquationData& data) {
0352     const Lock lock(m_initializing);
0353     uiGeneralTab.cbType->setCurrentIndex(static_cast<int>(data.type));
0354     uiGeneralTab.teEquation1->setText(data.expression1);
0355     uiGeneralTab.teEquation2->setText(data.expression2);
0356     uiGeneralTab.teMin->setText(data.min);
0357     uiGeneralTab.teMax->setText(data.max);
0358     uiGeneralTab.sbCount->setValue(data.count);
0359 }