diff --git a/src/kdefrontend/dockwidgets/LiveDataDock.cpp b/src/kdefrontend/dockwidgets/LiveDataDock.cpp index 4ed8e903c..bfb7ecd39 100644 --- a/src/kdefrontend/dockwidgets/LiveDataDock.cpp +++ b/src/kdefrontend/dockwidgets/LiveDataDock.cpp @@ -1,1695 +1,1695 @@ /*************************************************************************** File : LiveDataDock.cpp Project : LabPlot Description : Dock widget for live data properties -------------------------------------------------------------------- Copyright : (C) 2017 by Fabian Kristof (fkristofszabolcs@gmail.com) Copyright : (C) 2018 by Kovacs Ferencz (kferike98@gmail.com) Copyright : (C) 2018 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "LiveDataDock.h" #include #include #include #include #include #include #ifdef HAVE_MQTT #include "kdefrontend/widgets/MQTTWillSettingsWidget.h" #include #include #include #endif LiveDataDock::LiveDataDock(QWidget* parent) : - QWidget(parent), - m_paused(false) - #ifdef HAVE_MQTT - , - m_searching(true), - m_searchTimer(new QTimer()), - m_interpretMessage(true), - m_previousMQTTClient(nullptr) - #endif + QWidget(parent), + m_paused(false) +#ifdef HAVE_MQTT + , + m_searching(true), + m_searchTimer(new QTimer()), + m_interpretMessage(true), + m_previousMQTTClient(nullptr) +#endif { ui.setupUi(this); ui.bUpdateNow->setIcon(QIcon::fromTheme(QLatin1String("view-refresh"))); connect(ui.bPausePlayReading, &QPushButton::clicked, this, &LiveDataDock::pauseContinueReading); connect(ui.bUpdateNow, &QPushButton::clicked, this, &LiveDataDock::updateNow); connect(ui.sbUpdateInterval, static_cast(&QSpinBox::valueChanged), this, &LiveDataDock::updateIntervalChanged); connect(ui.sbKeepNValues, static_cast(&QSpinBox::valueChanged), this, &LiveDataDock::keepNValuesChanged); connect(ui.sbSampleSize, static_cast(&QSpinBox::valueChanged), this, &LiveDataDock::sampleSizeChanged); connect(ui.cbUpdateType, static_cast(&QComboBox::currentIndexChanged), this, &LiveDataDock::updateTypeChanged); connect(ui.cbReadingType, static_cast(&QComboBox::currentIndexChanged), this, &LiveDataDock::readingTypeChanged); #ifdef HAVE_MQTT m_searchTimer->setInterval(10000); connect(this, &LiveDataDock::newTopic, this, &LiveDataDock::setTopicCompleter); connect(m_searchTimer, &QTimer::timeout, this, &LiveDataDock::topicTimeout); connect(ui.bSubscribe, &QPushButton::clicked, this, &LiveDataDock::addSubscription); connect(ui.bUnsubscribe, &QPushButton::clicked, this, &LiveDataDock::removeSubscription); connect(ui.bWillUpdateNow, &QPushButton::clicked, this, &LiveDataDock::willUpdateNow); connect(ui.leTopics, &QLineEdit::textChanged, this, &LiveDataDock::scrollToTopicTreeItem); connect(ui.leSubscriptions, &QLineEdit::textChanged, this, &LiveDataDock::scrollToSubsriptionTreeItem); connect(ui.bWillSettings, &QPushButton::clicked, this, &LiveDataDock::showWillSettings); ui.bSubscribe->setIcon(ui.bSubscribe->style()->standardIcon(QStyle::SP_ArrowRight)); ui.bSubscribe->setToolTip(i18n("Subscribe selected topics")); ui.bUnsubscribe->setIcon(ui.bUnsubscribe->style()->standardIcon(QStyle::SP_ArrowLeft)); ui.bUnsubscribe->setToolTip(i18n("Unsubscribe selected topics")); ui.bWillSettings->setToolTip(i18n("Manage MQTT connection's will settings")); ui.bWillSettings->setIcon(ui.bWillSettings->style()->standardIcon(QStyle::SP_FileDialogDetailedView)); #endif } LiveDataDock::~LiveDataDock() { #ifdef HAVE_MQTT delete m_searchTimer; QMapIterator clients(m_clients); while(clients.hasNext()) { clients.next(); delete clients.value(); } #endif } #ifdef HAVE_MQTT /*! * \brief Sets the MQTTClients of this dock widget * \param clients */ void LiveDataDock::setMQTTClients(const QList &clients) { m_liveDataSources.clear(); m_mqttClients.clear(); m_mqttClients = clients; const MQTTClient* const fmc = clients.at(0); ui.sbUpdateInterval->setValue(fmc->updateInterval()); ui.cbUpdateType->setCurrentIndex(static_cast(fmc->updateType())); ui.cbReadingType->setCurrentIndex(static_cast(fmc->readingType())); if (fmc->updateType() == MQTTClient::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); } if (fmc->isPaused()) { ui.bPausePlayReading->setText(i18n("Continue reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); } else { ui.bPausePlayReading->setText(i18n("Pause reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); } ui.sbKeepNValues->setValue(fmc->keepNValues()); ui.sbKeepNValues->setEnabled(true); if (fmc->readingType() == MQTTClient::ReadingType::TillEnd) { ui.lSampleSize->hide(); ui.sbSampleSize->hide(); } else ui.sbSampleSize->setValue(fmc->sampleSize()); // disable "whole file" option const QStandardItemModel* model = qobject_cast(ui.cbReadingType->model()); QStandardItem* item = model->item(LiveDataSource::ReadingType::WholeFile); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); //show MQTT connected options ui.gbManageSubscriptions->show(); ui.bSubscribe->show(); ui.bUnsubscribe->show(); ui.twTopics->show(); ui.leTopics->show(); ui.lTopicSearch->show(); ui.twSubscriptions->show(); ui.lQoS->show(); ui.cbQoS->show(); ui.lWillSettings->show(); ui.bWillSettings->show(); //if there isn't a client with this hostname we instantiate a new one if(m_clients[fmc->clientHostName()] == nullptr) { m_clients[fmc->clientHostName()] = new QMqttClient(); connect(fmc, &MQTTClient::clientAboutToBeDeleted, this, &LiveDataDock::removeClient); connect(m_clients[fmc->clientHostName()], &QMqttClient::connected, this, &LiveDataDock::onMQTTConnect); connect(m_clients[fmc->clientHostName()], &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceived); m_clients[fmc->clientHostName()]->setHostname(fmc->clientHostName()); m_clients[fmc->clientHostName()]->setPort(fmc->clientPort()); if(fmc->MQTTUseAuthentication()) { m_clients[fmc->clientHostName()]->setUsername(fmc->clientUserName()); m_clients[fmc->clientHostName()]->setPassword(fmc->clientPassword()); } if(fmc->MQTTUseID()) { m_clients[fmc->clientHostName()]->setClientId(fmc->clientID()); } m_clients[fmc->clientHostName()]->connectToHost(); } if(m_previousMQTTClient == nullptr) { connect(fmc, &MQTTClient::MQTTSubscribed, this, &LiveDataDock::fillSubscriptions); //Fill the subscription tree(useful if the MQTTClient was loaded) QVector topics = fmc->topicNames(); for(int i = 0; i < topics.size(); ++i) { addTopicToTree(topics[i]); } fillSubscriptions(); } //if the previous MQTTClient's host name was different from the current one we have to disconnect some slots //and clear the tree widgets else if(m_previousMQTTClient->clientHostName() != fmc->clientHostName()) { disconnect(m_previousMQTTClient, &MQTTClient::MQTTSubscribed, this, &LiveDataDock::fillSubscriptions); disconnect(m_clients[m_previousMQTTClient->clientHostName()], &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceived); connect(m_clients[m_previousMQTTClient->clientHostName()], &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceivedInBackground); disconnect(m_clients[fmc->clientHostName()], &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceivedInBackground); ui.twTopics->clear(); //repopulating the tree widget with the already known topics of the client for(int i = 0; i < m_addedTopics[fmc->clientHostName()].size(); ++i) { addTopicToTree(m_addedTopics[fmc->clientHostName()].at(i)); } //fill subscriptions tree widget ui.twSubscriptions->clear(); fillSubscriptions(); connect(fmc, &MQTTClient::MQTTSubscribed, this, &LiveDataDock::fillSubscriptions); connect(m_clients[fmc->clientHostName()], &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceived); } if(fmc->willUpdateType() == MQTTClient::WillUpdateType::OnClick && fmc->MQTTWillUse()) ui.bWillUpdateNow->show(); m_previousMQTTClient = fmc; } #endif /*! * \brief Sets the live data sources of this dock widget * \param sources */ void LiveDataDock::setLiveDataSources(const QList& sources) { #ifdef HAVE_MQTT m_mqttClients.clear(); #endif m_liveDataSources = sources; const LiveDataSource* const fds = sources.at(0); const LiveDataSource::SourceType sourceType = fds->sourceType(); const LiveDataSource::ReadingType readingType = fds->readingType(); const LiveDataSource::UpdateType updateType = fds->updateType(); ui.sbUpdateInterval->setValue(fds->updateInterval()); ui.cbUpdateType->setCurrentIndex(static_cast(updateType)); ui.cbReadingType->setCurrentIndex(static_cast(readingType)); if (updateType == LiveDataSource::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); } if (fds->isPaused()) { ui.bPausePlayReading->setText(i18n("Continue Reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); } else { ui.bPausePlayReading->setText(i18n("Pause Reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); } ui.sbKeepNValues->setValue(fds->keepNValues()); // disable "whole file" when having no file (i.e. socket or port) auto* model = qobject_cast(ui.cbReadingType->model()); QStandardItem* item = model->item(LiveDataSource::ReadingType::WholeFile); if (sourceType == LiveDataSource::SourceType::FileOrPipe) item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); else item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); if (sourceType == LiveDataSource::SourceType::NetworkTcpSocket || sourceType == LiveDataSource::SourceType::LocalSocket || sourceType == LiveDataSource::SourceType::SerialPort || readingType == LiveDataSource::ReadingType::TillEnd || readingType == LiveDataSource::ReadingType::WholeFile) { ui.lSampleSize->hide(); ui.sbSampleSize->hide(); } else { ui.sbSampleSize->setValue(fds->sampleSize()); } // disable "on new data"-option if not available model = qobject_cast(ui.cbUpdateType->model()); item = model->item(LiveDataSource::UpdateType::NewData); if (sourceType == LiveDataSource::SourceType::NetworkTcpSocket || sourceType == LiveDataSource::SourceType::NetworkUdpSocket || sourceType == LiveDataSource::SourceType::SerialPort) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); } else { item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); } ui.bWillSettings->hide(); ui.lWillSettings->hide(); ui.bWillUpdateNow->hide(); ui.bSubscribe->hide(); ui.bUnsubscribe->hide(); ui.twTopics->hide(); ui.leTopics->hide(); ui.lTopicSearch->hide(); ui.twSubscriptions->hide(); ui.gbManageSubscriptions->hide(); ui.lQoS->hide(); ui.cbQoS->hide(); } /*! * \brief Modifies the sample size of the live data sources or MQTTClient objects * \param sampleSize */ void LiveDataDock::sampleSizeChanged(int sampleSize) { if(!m_liveDataSources.isEmpty()) { for (auto* source : m_liveDataSources) source->setSampleSize(sampleSize); } #ifdef HAVE_MQTT else if (!m_mqttClients.isEmpty()) { for (auto* client : m_mqttClients) client->setSampleSize(sampleSize); } #endif } /*! * \brief Updates the live data sources now */ void LiveDataDock::updateNow() { if(!m_liveDataSources.isEmpty()) { for (auto* source : m_liveDataSources) source->updateNow(); } #ifdef HAVE_MQTT else if (!m_mqttClients.isEmpty()) { for (auto* client : m_mqttClients) client->updateNow(); } #endif } /*! * \brief LiveDataDock::updateTypeChanged * \param idx */ void LiveDataDock::updateTypeChanged(int idx) { if(!m_liveDataSources.isEmpty()) { DEBUG("LiveDataDock::updateTypeChanged()"); const auto type = static_cast(idx); switch (type) { case LiveDataSource::UpdateType::TimeInterval: ui.lUpdateInterval->show(); ui.sbUpdateInterval->show(); ui.lSampleSize->show(); ui.sbSampleSize->show(); for (auto* source : m_liveDataSources) { source->setUpdateType(type); source->setUpdateInterval(ui.sbUpdateInterval->value()); source->setFileWatched(false); } break; case LiveDataSource::UpdateType::NewData: ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); ui.lSampleSize->hide(); ui.sbSampleSize->hide(); for (auto* source : m_liveDataSources) { source->setFileWatched(true); source->setUpdateType(type); } } } #ifdef HAVE_MQTT else if (!m_mqttClients.isEmpty()) { DEBUG("LiveDataDock::updateTypeChanged()"); const MQTTClient::UpdateType type = static_cast(idx); if (type == MQTTClient::UpdateType::TimeInterval) { ui.lUpdateInterval->show(); ui.sbUpdateInterval->show(); for (auto* client : m_mqttClients) { client->setUpdateType(type); client->setUpdateInterval(ui.sbUpdateInterval->value()); } } else if (type == MQTTClient::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); for (auto* client : m_mqttClients) { client->setUpdateType(type); } } } #endif } /*! * \brief Handles the change of the reading type in the dock widget * \param idx */ void LiveDataDock::readingTypeChanged(int idx) { if(!m_liveDataSources.isEmpty()) { const auto type = static_cast(idx); const LiveDataSource* const fds = m_liveDataSources.at(0); const LiveDataSource::SourceType sourceType = fds->sourceType(); const LiveDataSource::UpdateType updateType = fds->updateType(); if (sourceType == LiveDataSource::SourceType::NetworkTcpSocket || sourceType == LiveDataSource::SourceType::LocalSocket || sourceType == LiveDataSource::SourceType::SerialPort || type == LiveDataSource::ReadingType::TillEnd || type == LiveDataSource::ReadingType::WholeFile || updateType == LiveDataSource::UpdateType::NewData) { ui.lSampleSize->hide(); ui.sbSampleSize->hide(); } else { ui.lSampleSize->show(); ui.sbSampleSize->show(); } for (auto* source : m_liveDataSources) source->setReadingType(type); } #ifdef HAVE_MQTT else if (!m_mqttClients.isEmpty()) { MQTTClient::ReadingType type = static_cast(idx); if (type == MQTTClient::ReadingType::TillEnd) { ui.lSampleSize->hide(); ui.sbSampleSize->hide(); } else { ui.lSampleSize->show(); ui.sbSampleSize->show(); } for (auto* client : m_mqttClients) client->setReadingType(type); } #endif } /*! * \brief Modifies the update interval of the live data sources * \param updateInterval */ void LiveDataDock::updateIntervalChanged(int updateInterval) { if(!m_liveDataSources.isEmpty()) { for (auto* source : m_liveDataSources) source->setUpdateInterval(updateInterval); } #ifdef HAVE_MQTT else if (!m_mqttClients.isEmpty()) { for (auto* client : m_mqttClients) client->setUpdateInterval(updateInterval); } #endif } /*! * \brief Modifies the number of samples to keep in each of the live data sources * \param keepNValues */ void LiveDataDock::keepNValuesChanged(const int keepNValues) { if(!m_liveDataSources.isEmpty()) { for (auto* source : m_liveDataSources) source->setKeepNValues(keepNValues); } #ifdef HAVE_MQTT else if (!m_mqttClients.isEmpty()) { for (auto* client : m_mqttClients) client->setKeepNValues(keepNValues); } #endif } /*! * \brief Pauses the reading of the live data source */ void LiveDataDock::pauseReading() { if(!m_liveDataSources.isEmpty()) { for (auto* source: m_liveDataSources) source->pauseReading(); } #ifdef HAVE_MQTT else if (!m_mqttClients.isEmpty()) { for (auto* client : m_mqttClients) client->pauseReading(); } #endif } /*! * \brief Continues the reading of the live data source */ void LiveDataDock::continueReading() { if(!m_liveDataSources.isEmpty()) { for (auto* source : m_liveDataSources) source->continueReading(); } #ifdef HAVE_MQTT else if (!m_mqttClients.isEmpty()) { for (auto* client : m_mqttClients) client->continueReading(); } #endif } /*! * \brief Handles the pausing/continuing of reading of the live data source */ void LiveDataDock::pauseContinueReading() { m_paused = !m_paused; if (m_paused) { pauseReading(); ui.bPausePlayReading->setText(i18n("Continue Reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); } else { continueReading(); ui.bPausePlayReading->setText(i18n("Pause Reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); } } #ifdef HAVE_MQTT /*! *\brief called when use will message checkbox's state is changed in the will settings widget, * Sets the mqttUseWill according to state for every client in m_mqttClients * * \param state the state of the checbox */ void LiveDataDock::useWillMessage(int state) { qDebug()<<"Use will message: " <setMQTTWillUse(true); if (m_mqttClients.first()->willUpdateType() == MQTTClient::WillUpdateType::OnClick) ui.bWillUpdateNow->show(); } else if (state == Qt::Unchecked) { for (auto* source : m_mqttClients) source->setMQTTWillUse(false); ui.bWillUpdateNow->hide(); } } /*! *\brief called when will message's QoS is changed in the will settings widget * sets the will QoS level for every client in m_mqttClients * * \param QoS the QoS level of the will message */ void LiveDataDock::willQoSChanged(int QoS) { for (auto* source : m_mqttClients) source->setWillQoS(QoS); } /*! *\brief called when will message's retain flag is changed in the will settings widget * sets the retain flag for the will message in every client in m_mqttClients * * \param state the state of the will retain chechbox */ void LiveDataDock::willRetainChanged(int state) { if(state == Qt::Checked) { for (auto* source : m_mqttClients) source->setWillRetain(true); } else if (state == Qt::Unchecked) { for (auto* source : m_mqttClients) source->setWillRetain(false); } } /*! *\brief called when will topic combobox's current item is changed in the will settings widget * sets the will topic for every client in m_mqttClients * * \param topic the current text of cbWillTopic */ void LiveDataDock::willTopicChanged(const QString& topic) { for (auto* source : m_mqttClients) { if(source->willTopic() != topic) source->clearLastMessage(); source->setWillTopic(topic); } } /*! *\brief called when the selected will message type is changed in the will settings widget * sets the will message type for every client in m_mqttClients * * \param type the selected will message type */ void LiveDataDock::willMessageTypeChanged(int type) { for (auto* source : m_mqttClients) source->setWillMessageType(static_cast (type)); } /*! *\brief called when the will own message is changed in the will settings widget * sets the will own message for every client in m_mqttClients * * \param message the will message given by the user */ void LiveDataDock::willOwnMessageChanged(const QString& message) { for (auto* source : m_mqttClients) source->setWillOwnMessage(message); } /*! *\brief called when the selected update type for the will message is changed in the will settings widget * sets the will update type for every client in m_mqttClients * * \param type the selected will update type */ void LiveDataDock::willUpdateTypeChanged(int updateType) { for (auto* source : m_mqttClients) source->setWillUpdateType(static_cast(updateType)); if(static_cast(updateType) == MQTTClient::WillUpdateType::TimePeriod) { ui.bWillUpdateNow->hide(); for (auto* source : m_mqttClients) { source->startWillTimer(); } } else if (static_cast(updateType) == MQTTClient::WillUpdateType::OnClick) { ui.bWillUpdateNow->show(); //if update type is on click we stop the will timer for (auto* source : m_mqttClients) source->stopWillTimer(); } } /*! *\brief called when the will update now button is pressed * updates the will message of every client in m_mqttClients */ void LiveDataDock::willUpdateNow() { for (auto* source : m_mqttClients) source->updateWillMessage(); } /*! *\brief called when the update interval for will message is changed in the will settings widget * sets the will update interval for every client in m_mqttClients, then starts the will timer for each one * * \param interval the new will update interval */ void LiveDataDock::willUpdateIntervalChanged(int interval) { for (auto* source : m_mqttClients) { source->setWillTimeInterval(interval); source->startWillTimer(); } } /*! *\brief called when the will statistics are changed in the will settings widget * adds or removes the statistic represented by the index from every client in m_mqttClients */ void LiveDataDock::statisticsChanged(int index) { bool useStatistic = m_mqttClients.first()->willStatistics()[index]; //if it's checked we add it if(!useStatistic) { if(index >= 0) { for (auto* source : m_mqttClients) source->addWillStatistics(static_cast(index) ); } } //otherwise remove it else { if(index >= 0){ for (auto* source : m_mqttClients) source->removeWillStatistics(static_cast(index) ); } } } /*! *\brief called when the client connects to the broker successfully, it subscribes to every topic (# wildcard) * in order to later list every available topic */ void LiveDataDock::onMQTTConnect() { QMqttTopicFilter globalFilter{"#"}; QMqttSubscription * subscription = m_clients[m_mqttClients.first()->clientHostName()]->subscribe(globalFilter, 1); if(!subscription) qDebug()<<"Couldn't make global subscription in LiveDataDock"; } /*! *\brief called when the client receives a message * if the message arrived from a new topic, the topic is put in twTopics */ void LiveDataDock::mqttMessageReceived(const QByteArray& message, const QMqttTopicName& topic) { Q_UNUSED(message) if(!m_addedTopics[m_mqttClients.first()->clientHostName()].contains(topic.name())) { m_addedTopics[m_mqttClients.first()->clientHostName()].push_back(topic.name()); addTopicToTree(topic.name()); } } /*! *\brief called when the subscribe button is pressed * subscribes to the topic represented by the current item of twTopics in every client from m_mqttClients */ void LiveDataDock::addSubscription() { QString name; QTreeWidgetItem *item = ui.twTopics->currentItem(); if(item != nullptr) { QTreeWidgetItem *tempItem = item; //determine the topic name that the current item represents name.prepend(item->text(0)); if(item->childCount() != 0) name.append("/#"); while(tempItem->parent() != nullptr) { tempItem = tempItem->parent(); name.prepend(tempItem->text(0) + '/'); } //check if the subscription already exists QList topLevelList = ui.twSubscriptions->findItems(name, Qt::MatchExactly); if(topLevelList.isEmpty() || topLevelList.first()->parent() != nullptr) { qDebug() << "LiveDataDock: start to add new subscription: " << name; bool foundSuperior = false; for(int i = 0; i < ui.twSubscriptions->topLevelItemCount(); ++i) { //if the new subscriptions contains an already existing one, we remove the inferior one if(checkTopicContains(name, ui.twSubscriptions->topLevelItem(i)->text(0)) && name != ui.twSubscriptions->topLevelItem(i)->text(0)) { ui.twSubscriptions->topLevelItem(i)->takeChildren(); ui.twSubscriptions->takeTopLevelItem(i); i--; continue; } //if there is a subscription containing the new one we set foundSuperior true if(checkTopicContains(ui.twSubscriptions->topLevelItem(i)->text(0), name) && name != ui.twSubscriptions->topLevelItem(i)->text(0)) { foundSuperior = true; qDebug()<<"Can't add "<topLevelItem(i)->text(0); break; } } //if there wasn't a superior subscription we can subscribe to the new topic if(!foundSuperior) { QStringList toplevelName; toplevelName.push_back(name); QTreeWidgetItem* newTopLevelItem = new QTreeWidgetItem(toplevelName); ui.twSubscriptions->addTopLevelItem(newTopLevelItem); if(name.endsWith('#')) { //adding every topic that the subscription contains to twSubscriptions addSubscriptionChildren(item, newTopLevelItem); } //subscribe in every MQTTClient for (auto* source : m_mqttClients) { source->addMQTTSubscription(name, ui.cbQoS->currentIndex()); } if(name.endsWith('#')) { //if an already existing subscription contains a topic that the new subscription also contains //we decompose the already existing subscription //by unsubscribing from its topics, that are present in the new subscription as well QStringList nameList = name.split('/', QString::SkipEmptyParts); QString root = nameList.first(); QVector children; for(int i = 0; i < ui.twSubscriptions->topLevelItemCount(); ++i) { if(ui.twSubscriptions->topLevelItem(i)->text(0).startsWith(root) && name != ui.twSubscriptions->topLevelItem(i)->text(0)) { children.clear(); //get the "leaf" children of the inspected subscription findSubscriptionLeafChildren(children, ui.twSubscriptions->topLevelItem(i)); for(int j = 0; j < children.size(); ++j) { if(checkTopicContains(name, children[j]->text(0))) { //if the new subscription contains a topic, we unsubscribe from it QTreeWidgetItem* unsubscribeItem = children[j]; while(unsubscribeItem->parent() != nullptr) { for(int i = 0; i < unsubscribeItem->parent()->childCount(); ++i) { if(unsubscribeItem->text(0) != unsubscribeItem->parent()->child(i)->text(0)) { //add topic as subscription to every client for (auto* source : m_mqttClients) { source->addBeforeRemoveSubscription(unsubscribeItem->parent()->child(i)->text(0), ui.cbQoS->currentIndex()); } //also add it to twSubscriptions ui.twSubscriptions->addTopLevelItem(unsubscribeItem->parent()->takeChild(i)); i--; } else { //before we remove the topic, we reparent it to the new subscription //so no data is lost for (auto* source : m_mqttClients) { source->reparentTopic(unsubscribeItem->text(0), name); } } } unsubscribeItem = unsubscribeItem->parent(); } qDebug()<<"Remove: "<text(0); //Remove topic/subscription for (auto* source : m_mqttClients) { source->removeMQTTSubscription(unsubscribeItem->text(0)); } ui.twSubscriptions->takeTopLevelItem(ui.twSubscriptions->indexOfTopLevelItem(unsubscribeItem)); } } } } } manageCommonLevelSubscriptions(); updateSubscriptionCompleter(); if(!ui.bWillSettings->isEnabled()) ui.bWillSettings->setEnabled(true); } else { QMessageBox::warning(this, "Warning", "You already subscribed to a topic containing this one"); } } else QMessageBox::warning(this, "Warning", "You already subscribed to this topic"); } else QMessageBox::warning(this, "Warning", "You didn't select any item from the Tree Widget"); } /*! *\brief called when the unsubscribe button is pressed * unsubscribes from the topic represented by the current item of twSubscription in every client from m_mqttClients */ void LiveDataDock::removeSubscription() { QTreeWidgetItem* unsubscribeItem = ui.twSubscriptions->currentItem(); if(unsubscribeItem != nullptr) { qDebug() << "LiveDataDock: unsubscribe from " << unsubscribeItem->text(0); //if it is a top level item, meaning a topic that we really subscribed to(not one that belongs to a subscription) //we can simply unsubscribe from it if(unsubscribeItem->parent() == nullptr) { for (auto* source : m_mqttClients) { source->removeMQTTSubscription(unsubscribeItem->text(0)); } ui.twSubscriptions->takeTopLevelItem(ui.twSubscriptions->indexOfTopLevelItem(unsubscribeItem)); } //otherwise we remove the selected item, but subscribe to every other topic, that was contained by //the selected item's parent subscription(top level item of twSubscriptions) else{ while(unsubscribeItem->parent() != nullptr) { for(int i = 0; i < unsubscribeItem->parent()->childCount(); ++i) { if(unsubscribeItem->text(0) != unsubscribeItem->parent()->child(i)->text(0)) { //add topic as subscription to every client for (auto* source : m_mqttClients) { source->addBeforeRemoveSubscription(unsubscribeItem->parent()->child(i)->text(0), ui.cbQoS->currentIndex()); } ui.twSubscriptions->addTopLevelItem(unsubscribeItem->parent()->takeChild(i)); i--; } } unsubscribeItem = unsubscribeItem->parent(); } //remove topic/subscription from every client for (auto* source : m_mqttClients) { source->removeMQTTSubscription(unsubscribeItem->text(0)); } ui.twSubscriptions->takeTopLevelItem(ui.twSubscriptions->indexOfTopLevelItem(unsubscribeItem)); //check if any common topics were subscribed, if possible merge them manageCommonLevelSubscriptions(); } if(ui.twSubscriptions->topLevelItemCount() <= 0) ui.bWillSettings->setEnabled(false); updateSubscriptionCompleter(); } else QMessageBox::warning(this, "Warning", "You didn't select any item from the Tree Widget"); } /*! *\brief called when a new topic is added to the tree(twTopics) * appends the topic's root to the topicList if it isn't in the list already * then sets the completer for leTopics */ void LiveDataDock::setTopicCompleter(const QString& topicName) { if(!m_searching) { QStringList list = topicName.split('/', QString::SkipEmptyParts); QString topic; if(!list.isEmpty()) { topic = list.at(0); } else topic = topicName; if(!m_topicList[m_mqttClients.first()->clientHostName()].contains(topic)) { m_topicList[m_mqttClients.first()->clientHostName()].append(topic); m_topicCompleter = new QCompleter(m_topicList[m_mqttClients.first()->clientHostName()], this); m_topicCompleter->setCompletionMode(QCompleter::PopupCompletion); m_topicCompleter->setCaseSensitivity(Qt::CaseSensitive); ui.leTopics->setCompleter(m_topicCompleter); } } } /*! *\brief Updates the completer for leSubscriptions */ void LiveDataDock::updateSubscriptionCompleter() { QStringList subscriptionList; QVector subscriptions = m_mqttClients.first()->MQTTSubscriptions(); if(!subscriptions.isEmpty()) { for(int i = 0; i < subscriptions.size(); ++i) { subscriptionList.append(subscriptions[i]); } m_subscriptionCompleter = new QCompleter(subscriptionList, this); m_subscriptionCompleter->setCompletionMode(QCompleter::PopupCompletion); m_subscriptionCompleter->setCaseSensitivity(Qt::CaseSensitive); ui.leSubscriptions->setCompleter(m_subscriptionCompleter); } else { ui.leSubscriptions->setCompleter(nullptr); } } /*! *\brief called when 10 seconds passed since the last time the user searched for a certain root in twTopics * enables updating the completer for le */ void LiveDataDock::topicTimeout() { m_searching = false; m_searchTimer->stop(); } /*! *\brief called when a new the host name of the MQTTClients from m _mqttClients changes * or when the MQTTClients initialize their subscriptions * Fills twSubscriptions with the subscriptions of the MQTTClient */ void LiveDataDock::fillSubscriptions() { const MQTTClient* const fmc = m_mqttClients.at(0); ui.twSubscriptions->clear(); QVector subscriptions = fmc->MQTTSubscriptions(); for (int i = 0; i < subscriptions.count(); ++i) { QStringList name; name.append(subscriptions[i]); bool found = false; for(int j = 0; j < ui.twSubscriptions->topLevelItemCount(); ++j) { if(ui.twSubscriptions->topLevelItem(j)->text(0) == subscriptions[i]) { found = true; break; } } if(!found) { //Add the subscription to the tree widget QTreeWidgetItem* newItem = new QTreeWidgetItem(name); ui.twSubscriptions->addTopLevelItem(newItem); name.clear(); name = subscriptions[i].split('/', QString::SkipEmptyParts); //find the corresponding "root" item in twTopics QTreeWidgetItem* topic = nullptr; for(int j = 0; j < ui.twTopics->topLevelItemCount(); ++j) { if(ui.twTopics->topLevelItem(j)->text(0) == name[0]) { topic = ui.twTopics->topLevelItem(j); break; } } //restore the children of the subscription if(topic != nullptr && topic->childCount() > 0) { restoreSubscriptionChildren(topic, newItem, name, 1); } } } m_searching = false; } /*! *\brief Checks if a topic contains another one * * \param superior the name of a topic * \param inferior the name of a topic * \return true if superior is equal to or contains(if superior contains wildcards) inferior, * false otherwise */ bool LiveDataDock::checkTopicContains(const QString &superior, const QString &inferior) { if (superior == inferior) return true; else { if(superior.contains('/')) { QStringList superiorList = superior.split('/', QString::SkipEmptyParts); QStringList inferiorList = inferior.split('/', QString::SkipEmptyParts); //a longer topic can't contain a shorter one if(superiorList.size() > inferiorList.size()) return false; bool ok = true; for(int i = 0; i < superiorList.size(); ++i) { if(superiorList.at(i) != inferiorList.at(i)) { if((superiorList.at(i) != "+") && !(superiorList.at(i) == '#' && i == superiorList.size() - 1)) { //if the two topics differ, and the superior's current level isn't + or #(which can be only in the last position) //then superior can't contain inferior ok = false; break; } else if(i == superiorList.size() - 1 && (superiorList.at(i) == "+" && inferiorList.at(i) == '#') ) { //if the two topics differ at the last level //and the superior's current level is + while the inferior's is #(which can be only in the last position) //then superior can't contain inferior ok = false; break; } } } return ok; } return false; } } /*! *\brief called when leTopics' text is changed * if the rootName can be found in twTopics, then we scroll it to the top of the tree widget * * \param rootName the current text of leTopics */ void LiveDataDock::scrollToTopicTreeItem(const QString& rootName) { m_searching = true; m_searchTimer->start(); int topItemIdx = -1; for(int i = 0; i< ui.twTopics->topLevelItemCount(); ++i) if(ui.twTopics->topLevelItem(i)->text(0) == rootName) { topItemIdx = i; break; } if(topItemIdx >= 0) { ui.twTopics->scrollToItem(ui.twTopics->topLevelItem(topItemIdx), QAbstractItemView::ScrollHint::PositionAtTop); } } /*! *\brief called when leSubscriptions' text is changed * if the rootName can be found in twSubscriptions, then we scroll it to the top of the tree widget * * \param rootName the current text of leSubscriptions */ void LiveDataDock::scrollToSubsriptionTreeItem(const QString& rootName) { int topItemIdx = -1; for(int i = 0; i < ui.twSubscriptions->topLevelItemCount(); ++i) if(ui.twSubscriptions->topLevelItem(i)->text(0) == rootName) { topItemIdx = i; break; } if(topItemIdx >= 0) { ui.twSubscriptions->scrollToItem(ui.twSubscriptions->topLevelItem(topItemIdx), QAbstractItemView::ScrollHint::PositionAtTop); } } /*! *\brief Returns the "+" wildcard containing topic name, which includes the given topic names * * \param first the name of a topic * \param second the name of a topic * \return The name of the common topic, if it exists, otherwise "" */ QString LiveDataDock::checkCommonLevel(const QString& first, const QString& second) { QStringList firstList = first.split('/', QString::SkipEmptyParts); QStringList secondtList = second.split('/', QString::SkipEmptyParts); QString commonTopic = ""; if(!firstList.isEmpty()) { //the two topics have to be the same size and can't be identic if(firstList.size() == secondtList.size() && (first != second)) { //the index where they differ int differIndex = -1; for(int i = 0; i < firstList.size(); ++i) { if(firstList.at(i) != secondtList.at(i)) { differIndex = i; break; } } //they can differ at only one level bool differ = false; if(differIndex > 0 && differIndex < firstList.size() -1) { for(int j = differIndex +1; j < firstList.size(); ++j) { if(firstList.at(j) != secondtList.at(j)) { differ = true; break; } } } else differ = true; if(!differ) { for(int i = 0; i < firstList.size(); ++i) { if(i != differIndex) { commonTopic.append(firstList.at(i)); } else { //we put "+" wildcard at the level where they differ commonTopic.append('+'); } if(i != firstList.size() - 1) commonTopic.append('/'); } } } } qDebug() << "Common topic for " << first << " and "<childCount() > 0) { for(int i = 0; i < topic->childCount(); ++i) { QTreeWidgetItem* temp = topic->child(i); QString name; //if it has children, then we add it as a # wildcrad containing topic if(topic->child(i)->childCount() > 0) { name.append(temp->text(0) + "/#"); while(temp->parent() != nullptr) { temp = temp->parent(); name.prepend(temp->text(0) + '/'); } } //if not then we simply add the topic itself else { name.append(temp->text(0)); while(temp->parent() != nullptr) { temp = temp->parent(); name.prepend(temp->text(0) + '/'); } } QStringList nameList; nameList.append(name); QTreeWidgetItem* childItem = new QTreeWidgetItem(nameList); subscription->addChild(childItem); //we use the function recursively on the given item addSubscriptionChildren(topic->child(i), childItem); } } } /*! *\brief Restores the children of a top level item in twSubscriptions if it contains wildcards * * \param topic pointer to a top level item in twTopics which represents the root of the subscription topic * \param subscription pointer to a top level item in twSubscriptions, this is the item whose children will be restored * \param list QStringList containing the levels of the subscription topic * \param level the level's number which is being investigated */ void LiveDataDock::restoreSubscriptionChildren(QTreeWidgetItem * topic, QTreeWidgetItem * subscription, const QStringList& list, int level) { if(level < list.size()) { if((level < list.size() - 1) && (list[level] != "+") && (list[level] != '#')) { for(int i = 0; i < topic->childCount(); ++i) { //if the current level isn't + or # wildcard we recursively continue with the next level if(topic->child(i)->text(0) == list[level]) { restoreSubscriptionChildren(topic->child(i), subscription, list, level + 1); break; } } } else if (list[level] == "+") { for(int i = 0; i < topic->childCount(); ++i) { //determine the name of the topic, contained by the subscription QString name; name.append(topic->child(i)->text(0)); for(int j = level + 1; j < list.size(); ++j) { name.append('/' + list[j]); } QTreeWidgetItem* temp = topic->child(i); while(temp->parent() != nullptr) { temp = temp->parent(); name.prepend(temp->text(0) + '/'); } //Add the topic as child of the subscription QStringList nameList; nameList.append(name); QTreeWidgetItem* newItem = new QTreeWidgetItem(nameList); subscription->addChild(newItem); //Continue adding children recursively to the new item restoreSubscriptionChildren(topic->child(i), newItem, list, level + 1); } } else if (list[level] == '#') { //add the children of the # wildcard containing subscription addSubscriptionChildren(topic, subscription); } } } /*! *\brief Returns the index of level where the two topic names differ, if there is a common topic for them * * \param first the name of a topic * \param second the name of a topic * \return The index of the unequal level, if there is a common topic, otherwise -1 */ int LiveDataDock::commonLevelIndex(const QString& first, const QString& second) { QStringList firstList = first.split('/', QString::SkipEmptyParts); QStringList secondtList = second.split('/', QString::SkipEmptyParts); QString commonTopic = ""; int differIndex = -1; if(!firstList.isEmpty()) { //the two topics have to be the same size and can't be identic if(firstList.size() == secondtList.size() && (first != second)) { //the index where they differ for(int i = 0; i < firstList.size(); ++i) { if(firstList.at(i) != secondtList.at(i)) { differIndex = i; break; } } //they can differ at only one level bool differ = false; if(differIndex > 0) { for(int j = differIndex +1; j < firstList.size(); ++j) { if(firstList.at(j) != secondtList.at(j)) { differ = true; break; } } } else differ = true; if(!differ) { for(int i = 0; i < firstList.size(); ++i) { if(i != differIndex) commonTopic.append(firstList.at(i)); else commonTopic.append('+'); if(i != firstList.size() - 1) commonTopic.append('/'); } } } } //if there is a common topic we return the differIndex if(!commonTopic.isEmpty()) return differIndex; else return -1; } /*! *\brief Fills the children vector, with the root item's (twSubscriptions) leaf children (meaning no wildcard containing topics) * * \param children vector of TreeWidgetItem pointers * \param root pointer to a TreeWidgetItem of twSubscriptions */ void LiveDataDock::findSubscriptionLeafChildren(QVector& children, QTreeWidgetItem* root) { if(root->childCount() == 0) { children.push_back(root); } else { for(int i = 0; i < root->childCount(); ++i) { findSubscriptionLeafChildren(children, root->child(i)); } } } /*! *\brief Returns the amount of topics that the "+" wildcard will replace in the level position * * \param levelIdx the level currently being investigated * \param level the level where the new + wildcard will be placed * \param commonList the topic name split into levels * \param currentItem pointer to a TreeWidgetItem which represents the parent of the level * represented by levelIdx * \return returns the childCount, or -1 if some topics already represented by + wildcard have different * amount of children */ int LiveDataDock::checkCommonChildCount(int levelIdx, int level, QStringList& commonList, QTreeWidgetItem* currentItem) { //we recursively check the number of children, until we get to level-1 if(levelIdx < level - 1) { if(commonList[levelIdx] != "+") { for(int j = 0; j < currentItem->childCount(); ++j) { if(currentItem->child(j)->text(0) == commonList[levelIdx]) { //if the level isn't represented by + wildcard we simply return the amount of children of the corresponding item, recursively return checkCommonChildCount(levelIdx + 1, level, commonList, currentItem->child(j)); } } } else { int childCount = -1; bool ok = true; //otherwise we check if every + wildcard represented topic has the same number of children, recursively for(int j = 0; j < currentItem->childCount(); ++j) { int temp = checkCommonChildCount(levelIdx + 1, level, commonList, currentItem->child(j)); if((j > 0) && (temp != childCount)) { ok = false; break; } childCount = temp; } //if yes we return this number, otherwise -1 if(ok) return childCount; else return -1; } } else if (levelIdx == level - 1) { if(commonList[levelIdx] != "+") { for(int j = 0; j < currentItem->childCount(); ++j) { if(currentItem->child(j)->text(0) == commonList[levelIdx]) { //if the level isn't represented by + wildcard we simply return the amount of children of the corresponding item return currentItem->child(j)->childCount(); } } } else { int childCount = -1; bool ok = true; //otherwise we check if every + wildcard represented topic has the same number of children for(int j = 0; j < currentItem->childCount(); ++j) { if((j > 0) && (currentItem->child(j)->childCount() != childCount)) { ok = false; break; } childCount = currentItem->child(j)->childCount(); } //if yes we return this number, otherwise -1 if(ok) return childCount; else return -1; } } else if (level == 1 && levelIdx == 1) return currentItem->childCount(); return -1; } /*! *\brief We search in twSubscriptions for topics that can be represented using + wildcards, then merge them. * We do this until there are no topics to merge */ void LiveDataDock::manageCommonLevelSubscriptions() { bool foundEqual = false; do{ foundEqual = false; QMap> equalTopicsMap; QVector equalTopics; //compare the subscriptions present in the TreeWidget for(int i = 0; i < ui.twSubscriptions->topLevelItemCount() - 1; ++i) { for(int j = i + 1; j < ui.twSubscriptions->topLevelItemCount(); ++j) { QString commonTopic = checkCommonLevel(ui.twSubscriptions->topLevelItem(i)->text(0), ui.twSubscriptions->topLevelItem(j)->text(0)); //if there is a common topic for the 2 compared topics, we add them to the map (using the common topic as key) if(!commonTopic.isEmpty()) { if(!equalTopicsMap[commonTopic].contains(ui.twSubscriptions->topLevelItem(i)->text(0))) { equalTopicsMap[commonTopic].push_back(ui.twSubscriptions->topLevelItem(i)->text(0)); } if(!equalTopicsMap[commonTopic].contains(ui.twSubscriptions->topLevelItem(j)->text(0))) { equalTopicsMap[commonTopic].push_back(ui.twSubscriptions->topLevelItem(j)->text(0)); } } } } if(!equalTopicsMap.isEmpty()) { qDebug()<<"Manage equal topics"; QVector commonTopics; QMapIterator> topics(equalTopicsMap); //check for every map entry, if the found topics can be merged or not while(topics.hasNext()) { topics.next(); int level = commonLevelIndex(topics.value().last(), topics.value().first()); QStringList commonList = topics.value().first().split('/', QString::SkipEmptyParts); QTreeWidgetItem* currentItem = nullptr; //search the corresponding item to the common topics first level(root) for(int i = 0; i < ui.twTopics->topLevelItemCount(); ++i) { if(ui.twTopics->topLevelItem(i)->text(0) == commonList.first()) { currentItem = ui.twTopics->topLevelItem(i); break; } } //calculate the number of topics the new + wildcard could replace int childCount = checkCommonChildCount(1, level, commonList, currentItem); if(childCount > 0) { //if the number of topics found and the calculated number of topics is equal, the topics can be merged if(topics.value().size() == childCount) { qDebug() << "Found common topic to manage: " << topics.key(); foundEqual = true; commonTopics.push_back(topics.key()); } } } if(foundEqual) { //if there are more common topics, the topics of which can be merged, we choose the one which has the lowest level new "+" wildcard int highestLevel = INT_MAX; int topicIdx = -1; for(int i = 0; i < commonTopics.size(); ++i) { int level = commonLevelIndex(equalTopicsMap[commonTopics[i]].first(), commonTopics[i]); if(level < highestLevel) { topicIdx = i; highestLevel = level; } } qDebug() << "Start to manage: " << commonTopics[topicIdx]; equalTopics.append(equalTopicsMap[commonTopics[topicIdx]]); //Add the common topic ("merging") QString commonTopic; commonTopic = checkCommonLevel(equalTopics.first(), equalTopics.last()); QStringList nameList; nameList.append(commonTopic); QTreeWidgetItem* newTopic = new QTreeWidgetItem(nameList); ui.twSubscriptions->addTopLevelItem(newTopic); //remove the "merged" topics for(int i = 0; i < equalTopics.size(); ++i) { for(int j = 0; j < ui.twSubscriptions->topLevelItemCount(); ++j){ if(ui.twSubscriptions->topLevelItem(j)->text(0) == equalTopics[i]) { newTopic->addChild(ui.twSubscriptions->takeTopLevelItem(j)); break; } } } //remove any subscription that the new subscription contains for(int i = 0; i < ui.twSubscriptions->topLevelItemCount(); ++i) { if(checkTopicContains(commonTopic, ui.twSubscriptions->topLevelItem(i)->text(0)) && commonTopic != ui.twSubscriptions->topLevelItem(i)->text(0) ) { ui.twSubscriptions->topLevelItem(i)->takeChildren(); ui.twSubscriptions->takeTopLevelItem(i); i--; } } //make the subscription on commonTopic in every MQTTClient from m_mqttClients for (auto* source : m_mqttClients) { source->addMQTTSubscription(commonTopic, ui.cbQoS->currentIndex()); } } } } while(foundEqual); } /*! *\brief Adds topicName to twTopics * * \param topicName the name of the topic, which will be added to the tree widget */ void LiveDataDock::addTopicToTree(const QString &topicName) { QStringList name; QChar sep = '/'; QString rootName; if(topicName.contains(sep)) { QStringList list = topicName.split(sep, QString::SkipEmptyParts); if(!list.isEmpty()) { rootName = list.at(0); name.append(list.at(0)); QTreeWidgetItem* currentItem; //check whether the first level of the topic can be found in twTopics int topItemIdx = -1; for(int i = 0; i < ui.twTopics->topLevelItemCount(); ++i) { if(ui.twTopics->topLevelItem(i)->text(0) == list.at(0)) { topItemIdx = i; break; } } //if not we simply add every level of the topic to the tree if( topItemIdx < 0) { currentItem = new QTreeWidgetItem(name); ui.twTopics->addTopLevelItem(currentItem); for(int i = 1; i < list.size(); ++i) { name.clear(); name.append(list.at(i)); currentItem->addChild(new QTreeWidgetItem(name)); currentItem = currentItem->child(0); } } //otherwise we search for the first level that isn't part of the tree, //then add every level of the topic to the tree from that certain level else { currentItem = ui.twTopics->topLevelItem(topItemIdx); int listIdx = 1; for(; listIdx < list.size(); ++listIdx) { QTreeWidgetItem* childItem = nullptr; bool found = false; for(int j = 0; j < currentItem->childCount(); ++j) { childItem = currentItem->child(j); if(childItem->text(0) == list.at(listIdx)) { found = true; currentItem = childItem; break; } } if(!found) { //this is the level that isn't present in the tree break; } } //add every level to the tree starting with the first level that isn't part of the tree for(; listIdx < list.size(); ++listIdx) { name.clear(); name.append(list.at(listIdx)); currentItem->addChild(new QTreeWidgetItem(name)); currentItem = currentItem->child(currentItem->childCount() - 1); } } } } else { rootName = topicName; name.append(topicName); ui.twTopics->addTopLevelItem(new QTreeWidgetItem(name)); } //if a subscribed topic contains the new topic, we have to update twSubscriptions for(int i = 0; i < ui.twSubscriptions->topLevelItemCount(); ++i) { QStringList subscriptionName = ui.twSubscriptions->topLevelItem(i)->text(0).split('/', QString::SkipEmptyParts); if (rootName == subscriptionName[0]) { fillSubscriptions(); break; } } //signals that a newTopic was added, in order to fill the completer of leTopics //we have to pass the whole topic name, not just the root name, for testing purposes emit newTopic(topicName); } /*! *\brief called when a client receives a message, if the clients hostname isn't identic with the host name of MQTTClients * if the message arrived from a new topic, the topic is put in m_addedTopics */ void LiveDataDock::mqttMessageReceivedInBackground(const QByteArray& message, const QMqttTopicName& topic) { Q_UNUSED(message) if(!m_addedTopics[m_mqttClients.first()->clientHostName()].contains(topic.name())) { m_addedTopics[m_mqttClients.first()->clientHostName()].push_back(topic.name()); } } /*! *\brief called when an MQTTClient is about to be deleted * removes every data connected to the MQTTClient, and disconnects the corresponding client from m_clients * * \param name the host name of the MQTTClient that will be deleted */ void LiveDataDock::removeClient(const QString& name) { m_clients[name]->disconnectFromHost(); m_addedTopics.remove(name); m_topicList.remove(name); if(m_previousMQTTClient != nullptr && m_previousMQTTClient->clientHostName() == name) { disconnect(m_clients[m_previousMQTTClient->clientHostName()], &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceivedInBackground); m_previousMQTTClient = nullptr; } if(m_mqttClients.first()->clientHostName() == name) { ui.twSubscriptions->clear(); ui.twTopics->clear(); m_mqttClients.clear(); } delete m_clients[name]; m_clients.remove(name); } /*! * \brief Used for testing the MQTT related features * \param topic */ bool LiveDataDock::testSubscribe(const QString& topic){ QStringList topicList = topic.split('/', QString::SkipEmptyParts); QTreeWidgetItem* currentItem = nullptr; for(int i = 0; i topLevelItemCount(); ++i) { if(ui.twTopics->topLevelItem(i)->text(0) == topicList[0]) { currentItem = ui.twTopics->topLevelItem(i); break; } } if (currentItem) { for(int i = 1 ; i < topicList.size(); ++i) { if(topicList[i] == '#') break; for(int j = 0; j < currentItem->childCount(); ++j) { if(currentItem->child(j)->text(0) == topicList[i]) { currentItem = currentItem->child(j); break; } else if (j == currentItem->childCount() - 1) return false; } } } else return false; ui.twTopics->setCurrentItem(currentItem); addSubscription(); return true; } /*! * \brief Used for testing the MQTT related features * \param topic */ bool LiveDataDock::testUnsubscribe(const QString& topic) { QTreeWidgetItem* currentItem = nullptr; for(int i = 0; i < ui.twSubscriptions->topLevelItemCount(); ++i) { if(checkTopicContains(ui.twSubscriptions->topLevelItem(i)->text(0), topic)) { currentItem = ui.twSubscriptions->topLevelItem(i); break; } } if(currentItem) { do { if(topic == currentItem->text(0)) { ui.twSubscriptions->setCurrentItem(currentItem); removeSubscription(); return true; } else { for(int i = 0; i < currentItem->childCount(); ++i) { qDebug()<child(i)->text(0)<<" "<child(i)->text(0), topic)) { currentItem = currentItem->child(i); break; } else if (i == currentItem->childCount() - 1) return false; } } } while(currentItem); } else return false; return false; } void LiveDataDock::showWillSettings() { QMenu menu; const MQTTClient* const fmc = m_mqttClients.at(0); QVector topics = fmc->topicNames(); MQTTWillSettingsWidget willSettings(&menu, fmc->willSettings(), topics); connect(&willSettings, &MQTTWillSettingsWidget::useChanged, this, &LiveDataDock::useWillMessage); connect(&willSettings, &MQTTWillSettingsWidget::messageTypeChanged, this, &LiveDataDock::willMessageTypeChanged); connect(&willSettings, &MQTTWillSettingsWidget::updateTypeChanged, this, &LiveDataDock::willUpdateTypeChanged); connect(&willSettings, &MQTTWillSettingsWidget::retainChanged, this, &LiveDataDock::willRetainChanged); connect(&willSettings, &MQTTWillSettingsWidget::intervalChanged, this, &LiveDataDock::willUpdateIntervalChanged); connect(&willSettings, &MQTTWillSettingsWidget::ownMessageChanged, this, &LiveDataDock::willOwnMessageChanged); connect(&willSettings, &MQTTWillSettingsWidget::topicChanged, this, &LiveDataDock::willTopicChanged); connect(&willSettings, &MQTTWillSettingsWidget::statisticsChanged, this, &LiveDataDock::statisticsChanged); connect(&willSettings, &MQTTWillSettingsWidget::QoSChanged, this, &LiveDataDock::willQoSChanged); connect(&willSettings, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&willSettings); menu.addAction(widgetAction); QPoint pos(ui.bWillSettings->sizeHint().width(),ui.bWillSettings->sizeHint().height()); menu.exec(ui.bWillSettings->mapToGlobal(pos)); } #endif diff --git a/src/kdefrontend/dockwidgets/LiveDataDock.h b/src/kdefrontend/dockwidgets/LiveDataDock.h index b722e0d18..da9212d2b 100644 --- a/src/kdefrontend/dockwidgets/LiveDataDock.h +++ b/src/kdefrontend/dockwidgets/LiveDataDock.h @@ -1,135 +1,135 @@ /*************************************************************************** File : LiveDataDock.h Project : LabPlot Description : Dock widget for live data properties -------------------------------------------------------------------- Copyright : (C) 2017 by Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef LIVEDATADOCK_H #define LIVEDATADOCK_H #ifdef HAVE_MQTT #include #include #include #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/MQTTClient.h" #endif #include #include #include "ui_livedatadock.h" #include "backend/datasources/LiveDataSource.h" class QTimer; class QTreeWidgetItem; class QString; class QCompleter; class LiveDataDock : public QWidget { Q_OBJECT public: explicit LiveDataDock(QWidget *parent = nullptr); void setLiveDataSources(const QList& sources); ~LiveDataDock() override; private: Ui::LiveDataDock ui; QList m_liveDataSources; bool m_paused; void pauseReading(); void continueReading(); private slots: void updateTypeChanged(int); void readingTypeChanged(int); void sampleSizeChanged(int); void updateIntervalChanged(int); void keepNValuesChanged(int); void updateNow(); void pauseContinueReading(); #ifdef HAVE_MQTT public: void setMQTTClients(const QList& clients); bool testSubscribe(const QString&); bool testUnsubscribe(const QString&); private slots: void useWillMessage(int); void willQoSChanged(int); void willRetainChanged(int); void willTopicChanged(const QString &); void willMessageTypeChanged(int); void willOwnMessageChanged(const QString&); void willUpdateTypeChanged(int); void willUpdateNow(); void willUpdateIntervalChanged(int); void statisticsChanged(int); void addSubscription(); void removeSubscription(); void onMQTTConnect(); void mqttMessageReceived(const QByteArray&, const QMqttTopicName&); void mqttMessageReceivedInBackground(const QByteArray&, const QMqttTopicName&); void setTopicCompleter(const QString&); void topicTimeout(); void fillSubscriptions(); void scrollToTopicTreeItem(const QString&); void scrollToSubsriptionTreeItem(const QString&); void removeClient(const QString&); void showWillSettings(); signals: void newTopic(const QString&); -private: +private: void updateSubscriptionCompleter(); void addTopicToTree(const QString&); bool checkTopicContains(const QString& superior, const QString& inferior); QString checkCommonLevel(const QString& first, const QString& second); void findSubscriptionLeafChildren(QVector&, QTreeWidgetItem*); int checkCommonChildCount(int levelIdx, int level, QStringList& namelist, QTreeWidgetItem* currentItem); void manageCommonLevelSubscriptions(); int commonLevelIndex(const QString& first, const QString& second); - void addSubscriptionChildren(QTreeWidgetItem * topic, QTreeWidgetItem * subscription); - void restoreSubscriptionChildren(QTreeWidgetItem * topic, QTreeWidgetItem * subscription, const QStringList&, int level); + void addSubscriptionChildren(QTreeWidgetItem* topic, QTreeWidgetItem* subscription); + void restoreSubscriptionChildren(QTreeWidgetItem* topic, QTreeWidgetItem* subscription, const QStringList&, int level); QList m_mqttClients; QMap m_clients; QCompleter* m_topicCompleter; QCompleter* m_subscriptionCompleter; QMap m_topicList; bool m_searching; QTimer* m_searchTimer; bool m_interpretMessage; const MQTTClient* m_previousMQTTClient; QString m_mqttUnsubscribeName; QMap> m_addedTopics; #endif }; #endif // LIVEDATADOCK_H diff --git a/src/kdefrontend/dockwidgets/WorksheetDock.cpp b/src/kdefrontend/dockwidgets/WorksheetDock.cpp index 4086b8b00..d6da06daa 100644 --- a/src/kdefrontend/dockwidgets/WorksheetDock.cpp +++ b/src/kdefrontend/dockwidgets/WorksheetDock.cpp @@ -1,926 +1,925 @@ /*************************************************************************** File : WorksheetDock.cpp Project : LabPlot Description : widget for worksheet properties -------------------------------------------------------------------- Copyright : (C) 2010-2016 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2013 by Stefan Gerlach (stefan.gerlach@uni-konstanz.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "WorksheetDock.h" #include "kdefrontend/GuiTools.h" #include "kdefrontend/ThemeHandler.h" #include "kdefrontend/TemplateHandler.h" #include #include #include #include #include #include #include #include #include /*! \class WorksheetDock \brief Provides a widget for editing the properties of the worksheets currently selected in the project explorer. \ingroup kdefrontend */ WorksheetDock::WorksheetDock(QWidget *parent): QWidget(parent), m_worksheet(nullptr) { ui.setupUi(this); //Background-tab ui.cbBackgroundColorStyle->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); - QCompleter* completer = new QCompleter(this); + auto* completer = new QCompleter(this); completer->setModel(new QDirModel); ui.leBackgroundFileName->setCompleter(completer); //adjust layouts in the tabs - for (int i=0; icount(); ++i) { - QGridLayout* layout = dynamic_cast(ui.tabWidget->widget(i)->layout()); + for (int i = 0; i < ui.tabWidget->count(); ++i) { + auto* layout = dynamic_cast(ui.tabWidget->widget(i)->layout()); if (!layout) continue; layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); } //SLOTs //General connect(ui.leName, &QLineEdit::textChanged, this, &WorksheetDock::nameChanged); connect(ui.leComment, &QLineEdit::textChanged, this, &WorksheetDock::commentChanged); connect( ui.cbSize, SIGNAL(currentIndexChanged(int)), this, SLOT(sizeChanged(int)) ); connect( ui.sbWidth, SIGNAL(valueChanged(double)), this, SLOT(sizeChanged()) ); connect( ui.sbHeight, SIGNAL(valueChanged(double)), this, SLOT(sizeChanged()) ); connect( ui.cbOrientation, SIGNAL(currentIndexChanged(int)), this, SLOT(orientationChanged(int)) ); //Background connect( ui.cbBackgroundType, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundTypeChanged(int)) ); connect( ui.cbBackgroundColorStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundColorStyleChanged(int)) ); connect( ui.cbBackgroundImageStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundImageStyleChanged(int)) ); connect( ui.cbBackgroundBrushStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundBrushStyleChanged(int)) ); connect( ui.bOpen, SIGNAL(clicked(bool)), this, SLOT(selectFile())); connect( ui.leBackgroundFileName, SIGNAL(returnPressed()), this, SLOT(fileNameChanged()) ); connect( ui.leBackgroundFileName, SIGNAL(textChanged(QString)), this, SLOT(fileNameChanged()) ); connect( ui.kcbBackgroundFirstColor, SIGNAL(changed(QColor)), this, SLOT(backgroundFirstColorChanged(QColor)) ); connect( ui.kcbBackgroundSecondColor, SIGNAL(changed(QColor)), this, SLOT(backgroundSecondColorChanged(QColor)) ); connect( ui.sbBackgroundOpacity, SIGNAL(valueChanged(int)), this, SLOT(backgroundOpacityChanged(int)) ); //Layout connect( ui.chScaleContent, SIGNAL(clicked(bool)), this, SLOT(scaleContentChanged(bool)) ); connect( ui.sbLayoutTopMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutTopMarginChanged(double)) ); connect( ui.sbLayoutBottomMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutBottomMarginChanged(double)) ); connect( ui.sbLayoutLeftMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutLeftMarginChanged(double)) ); connect( ui.sbLayoutRightMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutRightMarginChanged(double)) ); connect( ui.sbLayoutHorizontalSpacing, SIGNAL(valueChanged(double)), this, SLOT(layoutHorizontalSpacingChanged(double)) ); connect( ui.sbLayoutVerticalSpacing, SIGNAL(valueChanged(double)), this, SLOT(layoutVerticalSpacingChanged(double)) ); connect( ui.sbLayoutRowCount, SIGNAL(valueChanged(int)), this, SLOT(layoutRowCountChanged(int)) ); connect( ui.sbLayoutColumnCount, SIGNAL(valueChanged(int)), this, SLOT(layoutColumnCountChanged(int)) ); //theme and template handlers - QFrame* frame = new QFrame(this); - QHBoxLayout* layout = new QHBoxLayout(frame); + auto* frame = new QFrame(this); + auto* layout = new QHBoxLayout(frame); m_themeHandler = new ThemeHandler(this); layout->addWidget(m_themeHandler); connect(m_themeHandler, SIGNAL(loadThemeRequested(QString)), this, SLOT(loadTheme(QString))); connect(m_themeHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); - TemplateHandler* templateHandler = new TemplateHandler(this, TemplateHandler::Worksheet); + auto* templateHandler = new TemplateHandler(this, TemplateHandler::Worksheet); layout->addWidget(templateHandler); connect(templateHandler, SIGNAL(loadConfigRequested(KConfig&)), this, SLOT(loadConfigFromTemplate(KConfig&))); connect(templateHandler, SIGNAL(saveConfigRequested(KConfig&)), this, SLOT(saveConfigAsTemplate(KConfig&))); connect(templateHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); ui.verticalLayout->addWidget(frame); this->retranslateUi(); } void WorksheetDock::setWorksheets(QList list) { m_initializing = true; m_worksheetList = list; m_worksheet = list.first(); //if there are more then one worksheet in the list, disable the name and comment field in the tab "general" if (list.size()==1) { ui.lName->setEnabled(true); ui.leName->setEnabled(true); ui.lComment->setEnabled(true); ui.leComment->setEnabled(true); ui.leName->setText(m_worksheet->name()); ui.leComment->setText(m_worksheet->comment()); } else { ui.lName->setEnabled(false); ui.leName->setEnabled(false); ui.lComment->setEnabled(false); ui.leComment->setEnabled(false); ui.leName->setText(""); ui.leComment->setText(""); } //show the properties of the first worksheet this->load(); this->worksheetLayoutChanged(m_worksheet->layout()); m_themeHandler->setCurrentTheme(m_worksheet->theme()); connect(m_worksheet, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)),this, SLOT(worksheetDescriptionChanged(const AbstractAspect*))); connect(m_worksheet, SIGNAL(pageRectChanged(QRectF)),this, SLOT(worksheetPageRectChanged(QRectF))); connect(m_worksheet,SIGNAL(scaleContentChanged(bool)),this,SLOT(worksheetScaleContentChanged(bool))); connect(m_worksheet,SIGNAL(backgroundTypeChanged(PlotArea::BackgroundType)),this,SLOT(worksheetBackgroundTypeChanged(PlotArea::BackgroundType))); connect(m_worksheet,SIGNAL(backgroundColorStyleChanged(PlotArea::BackgroundColorStyle)),this,SLOT(worksheetBackgroundColorStyleChanged(PlotArea::BackgroundColorStyle))); connect(m_worksheet,SIGNAL(backgroundImageStyleChanged(PlotArea::BackgroundImageStyle)),this,SLOT(worksheetBackgroundImageStyleChanged(PlotArea::BackgroundImageStyle))); connect(m_worksheet,SIGNAL(backgroundBrushStyleChanged(Qt::BrushStyle)),this,SLOT(worksheetBackgroundBrushStyleChanged(Qt::BrushStyle))); connect(m_worksheet,SIGNAL(backgroundFirstColorChanged(QColor)),this,SLOT(worksheetBackgroundFirstColorChanged(QColor))); connect(m_worksheet,SIGNAL(backgroundSecondColorChanged(QColor)),this,SLOT(worksheetBackgroundSecondColorChanged(QColor))); connect(m_worksheet,SIGNAL(backgroundFileNameChanged(QString)),this,SLOT(worksheetBackgroundFileNameChanged(QString))); connect(m_worksheet,SIGNAL(backgroundOpacityChanged(float)),this,SLOT(worksheetBackgroundOpacityChanged(float))); connect(m_worksheet,SIGNAL(layoutChanged(Worksheet::Layout)),this,SLOT(worksheetLayoutChanged(Worksheet::Layout))); connect(m_worksheet,SIGNAL(layoutTopMarginChanged(float)),this,SLOT(worksheetLayoutTopMarginChanged(float))); connect(m_worksheet,SIGNAL(layoutBottomMarginChanged(float)),this,SLOT(worksheetLayoutBottomMarginChanged(float))); connect(m_worksheet,SIGNAL(layoutLeftMarginChanged(float)),this,SLOT(worksheetLayoutLeftMarginChanged(float))); connect(m_worksheet,SIGNAL(layoutRightMarginChanged(float)),this,SLOT(worksheetLayoutRightMarginChanged(float))); connect(m_worksheet,SIGNAL(layoutVerticalSpacingChanged(float)),this,SLOT(worksheetLayoutVerticalSpacingChanged(float))); connect(m_worksheet,SIGNAL(layoutHorizontalSpacingChanged(float)),this,SLOT(worksheetLayoutHorizontalSpacingChanged(float))); connect(m_worksheet,SIGNAL(layoutRowCountChanged(int)),this,SLOT(worksheetLayoutRowCountChanged(int))); connect(m_worksheet,SIGNAL(layoutColumnCountChanged(int)),this,SLOT(worksheetLayoutColumnCountChanged(int))); connect(m_worksheet,SIGNAL(themeChanged(QString)),m_themeHandler,SLOT(setCurrentTheme(QString))); m_initializing = false; } /*! Checks whether the size is one of the QPageSize::PageSizeId and updates Size and Orientation checkbox when width/height changes. */ void WorksheetDock::updatePaperSize() { if (m_worksheet->useViewSize()) { ui.cbSize->setCurrentIndex(0); return; } //In UI we use cm, so we need to convert to mm first before we check with QPageSize float w=(float)ui.sbWidth->value()*10; float h=(float)ui.sbHeight->value()*10; const QSizeF s = QSizeF(w, h); const QSizeF st = s.transposed(); //determine the position of the QPageSize::PageSizeId in the combobox bool found = false; for (int i = 0; i < ui.cbSize->count(); ++i) { const QVariant v = ui.cbSize->itemData(i); if (!v.isValid()) continue; - const QPageSize::PageSizeId id = v.value(); + const auto id = v.value(); const QSizeF ps = QPageSize::size(id, QPageSize::Millimeter); if (s == ps) { //check the portrait-orientation first ui.cbSize->setCurrentIndex(i); ui.cbOrientation->setCurrentIndex(0); //a QPageSize::PaperSize in portrait-orientation was found found = true; break; } else if (st == ps) { //check for the landscape-orientation ui.cbSize->setCurrentIndex(i); ui.cbOrientation->setCurrentIndex(1); //a QPageSize::PaperSize in landscape-orientation was found found = true; break; } } if (!found) ui.cbSize->setCurrentIndex(ui.cbSize->count() - 1); //select "Custom" size } //************************************************************* //****** SLOTs for changes triggered in WorksheetDock ********* //************************************************************* void WorksheetDock::retranslateUi() { m_initializing = true; //Geometry ui.cbOrientation->clear(); ui.cbOrientation->addItem(i18n("Portrait")); ui.cbOrientation->addItem(i18n("Landscape")); const QVector pageSizes = { QPageSize::A0, QPageSize::A1, QPageSize::A2, QPageSize::A3, QPageSize::A4, QPageSize::A5, QPageSize::A6, QPageSize::A7, QPageSize::A8, QPageSize::A9, QPageSize::B0, QPageSize::B1, QPageSize::B2, QPageSize::B3, QPageSize::B4, QPageSize::B5, QPageSize::B6, QPageSize::B7, QPageSize::B8, QPageSize::B9, QPageSize::B10, QPageSize::C5E, QPageSize::DLE, QPageSize::Executive, QPageSize::Folio, QPageSize::Ledger, QPageSize::Legal, QPageSize::Letter, QPageSize::Tabloid, QPageSize::Comm10E, QPageSize::Custom, }; ui.cbSize->clear(); ui.cbSize->addItem(i18n("View Size")); for (QPageSize::PageSizeId id : pageSizes) { ui.cbSize->addItem(QPageSize::name(id), id); } ui.cbSize->insertSeparator(1); //Background ui.cbBackgroundType->clear(); ui.cbBackgroundType->addItem(i18n("Color")); ui.cbBackgroundType->addItem(i18n("Image")); ui.cbBackgroundType->addItem(i18n("Pattern")); ui.cbBackgroundColorStyle->clear(); ui.cbBackgroundColorStyle->addItem(i18n("Single Color")); ui.cbBackgroundColorStyle->addItem(i18n("Horizontal Gradient")); ui.cbBackgroundColorStyle->addItem(i18n("Vertical Gradient")); ui.cbBackgroundColorStyle->addItem(i18n("Diag. Gradient (From Top Left)")); ui.cbBackgroundColorStyle->addItem(i18n("Diag. Gradient (From Bottom Left)")); ui.cbBackgroundColorStyle->addItem(i18n("Radial Gradient")); ui.cbBackgroundImageStyle->clear(); ui.cbBackgroundImageStyle->addItem(i18n("Scaled and Cropped")); ui.cbBackgroundImageStyle->addItem(i18n("Scaled")); ui.cbBackgroundImageStyle->addItem(i18n("Scaled, Keep Proportions")); ui.cbBackgroundImageStyle->addItem(i18n("Centered")); ui.cbBackgroundImageStyle->addItem(i18n("Tiled")); ui.cbBackgroundImageStyle->addItem(i18n("Center Tiled")); GuiTools::updateBrushStyles(ui.cbBackgroundBrushStyle, Qt::SolidPattern); m_initializing = false; } // "General"-tab void WorksheetDock::nameChanged() { if (m_initializing) return; m_worksheet->setName(ui.leName->text()); } void WorksheetDock::commentChanged() { if (m_initializing) return; m_worksheet->setComment(ui.leComment->text()); } void WorksheetDock::scaleContentChanged(bool scaled) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setScaleContent(scaled); } void WorksheetDock::sizeChanged(int i) { - const QPageSize::PageSizeId index = ui.cbSize->itemData(i).value(); + const auto index = ui.cbSize->itemData(i).value(); if (index==QPageSize::Custom) { ui.sbWidth->setEnabled(true); ui.sbHeight->setEnabled(true); ui.lOrientation->hide(); ui.cbOrientation->hide(); } else { ui.sbWidth->setEnabled(false); ui.sbHeight->setEnabled(false); if (i==0) { //no orientation available when using the complete view size (first item in the combox is selected) ui.lOrientation->hide(); ui.cbOrientation->hide(); } else { ui.lOrientation->show(); ui.cbOrientation->show(); } } if (m_initializing) return; if (i==0) { //use the complete view size (first item in the combox is selected) for (auto* worksheet : m_worksheetList) worksheet->setUseViewSize(true); } else if (index==QPageSize::Custom) { if (m_worksheet->useViewSize()) { for (auto* worksheet : m_worksheetList) worksheet->setUseViewSize(false); } } else { //determine the width and the height of the to be used predefined layout QSizeF s = QPageSize::size(index, QPageSize::Millimeter); if (ui.cbOrientation->currentIndex() == 1) { s.transpose(); } m_initializing = true; //s is in mm, in UI we show everything in cm ui.sbWidth->setValue(s.width()/10); ui.sbHeight->setValue(s.height()/10); m_initializing=false; float w = Worksheet::convertToSceneUnits(s.width(), Worksheet::Millimeter); float h = Worksheet::convertToSceneUnits(s.height(), Worksheet::Millimeter); for (auto* worksheet : m_worksheetList) { worksheet->setUseViewSize(false); worksheet->setPageRect(QRect(0,0,w,h)); } } } void WorksheetDock::sizeChanged() { if (m_initializing) return; int w = Worksheet::convertToSceneUnits(ui.sbWidth->value(), Worksheet::Centimeter); int h = Worksheet::convertToSceneUnits(ui.sbHeight->value(), Worksheet::Centimeter); for (auto* worksheet : m_worksheetList) worksheet->setPageRect(QRect(0,0,w,h)); } void WorksheetDock::orientationChanged(int index) { Q_UNUSED(index); if (m_initializing) return; this->sizeChanged(ui.cbSize->currentIndex()); } // "Background"-tab void WorksheetDock::backgroundTypeChanged(int index) { - PlotArea::BackgroundType type = (PlotArea::BackgroundType)index; + auto type = (PlotArea::BackgroundType)index; if (type == PlotArea::Color) { ui.lBackgroundColorStyle->show(); ui.cbBackgroundColorStyle->show(); ui.lBackgroundImageStyle->hide(); ui.cbBackgroundImageStyle->hide(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); ui.lBackgroundFileName->hide(); ui.leBackgroundFileName->hide(); ui.bOpen->hide(); ui.lBackgroundFirstColor->show(); ui.kcbBackgroundFirstColor->show(); - PlotArea::BackgroundColorStyle style = - (PlotArea::BackgroundColorStyle) ui.cbBackgroundColorStyle->currentIndex(); + auto style = (PlotArea::BackgroundColorStyle)ui.cbBackgroundColorStyle->currentIndex(); if (style == PlotArea::SingleColor) { ui.lBackgroundFirstColor->setText(i18n("Color:")); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); } else { ui.lBackgroundFirstColor->setText(i18n("First color:")); ui.lBackgroundSecondColor->show(); ui.kcbBackgroundSecondColor->show(); } } else if (type == PlotArea::Image) { ui.lBackgroundFirstColor->hide(); ui.kcbBackgroundFirstColor->hide(); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); ui.lBackgroundColorStyle->hide(); ui.cbBackgroundColorStyle->hide(); ui.lBackgroundImageStyle->show(); ui.cbBackgroundImageStyle->show(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); ui.lBackgroundFileName->show(); ui.leBackgroundFileName->show(); ui.bOpen->show(); } else if (type == PlotArea::Pattern) { ui.lBackgroundFirstColor->setText(i18n("Color:")); ui.lBackgroundFirstColor->show(); ui.kcbBackgroundFirstColor->show(); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); ui.lBackgroundColorStyle->hide(); ui.cbBackgroundColorStyle->hide(); ui.lBackgroundImageStyle->hide(); ui.cbBackgroundImageStyle->hide(); ui.lBackgroundBrushStyle->show(); ui.cbBackgroundBrushStyle->show(); ui.lBackgroundFileName->hide(); ui.leBackgroundFileName->hide(); ui.bOpen->hide(); } if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundType(type); } void WorksheetDock::backgroundColorStyleChanged(int index) { - PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle)index; + auto style = (PlotArea::BackgroundColorStyle)index; if (style == PlotArea::SingleColor) { ui.lBackgroundFirstColor->setText(i18n("Color:")); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); } else { ui.lBackgroundFirstColor->setText(i18n("First color:")); ui.lBackgroundSecondColor->show(); ui.kcbBackgroundSecondColor->show(); } if (m_initializing) return; int size = m_worksheetList.size(); if (size>1) { m_worksheet->beginMacro(i18n("%1 worksheets: background color style changed", size)); for (auto* w : m_worksheetList) w->setBackgroundColorStyle(style); m_worksheet->endMacro(); } else m_worksheet->setBackgroundColorStyle(style); } void WorksheetDock::backgroundImageStyleChanged(int index) { if (m_initializing) return; - PlotArea::BackgroundImageStyle style = (PlotArea::BackgroundImageStyle)index; + auto style = (PlotArea::BackgroundImageStyle)index; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundImageStyle(style); } void WorksheetDock::backgroundBrushStyleChanged(int index) { if (m_initializing) return; - Qt::BrushStyle style = (Qt::BrushStyle)index; + auto style = (Qt::BrushStyle)index; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundBrushStyle(style); } void WorksheetDock::backgroundFirstColorChanged(const QColor& c) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundFirstColor(c); } void WorksheetDock::backgroundSecondColorChanged(const QColor& c) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundSecondColor(c); } void WorksheetDock::backgroundOpacityChanged(int value) { if (m_initializing) return; float opacity = (float)value/100; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundOpacity(opacity); } //"Layout"-tab void WorksheetDock::layoutTopMarginChanged(double margin) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutTopMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void WorksheetDock::layoutBottomMarginChanged(double margin) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutBottomMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void WorksheetDock::layoutLeftMarginChanged(double margin) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutLeftMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void WorksheetDock::layoutRightMarginChanged(double margin) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutRightMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void WorksheetDock::layoutHorizontalSpacingChanged(double spacing) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutHorizontalSpacing(Worksheet::convertToSceneUnits(spacing, Worksheet::Centimeter)); } void WorksheetDock::layoutVerticalSpacingChanged(double spacing) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutVerticalSpacing(Worksheet::convertToSceneUnits(spacing, Worksheet::Centimeter)); } void WorksheetDock::layoutRowCountChanged(int count) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutRowCount(count); } void WorksheetDock::layoutColumnCountChanged(int count) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutColumnCount(count); } /*! opens a file dialog and lets the user select the image file. */ void WorksheetDock::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "WorksheetDock"); QString dir = conf.readEntry("LastImageDir", ""); QString formats; for (const QByteArray& format : QImageReader::supportedImageFormats()) { QString f = "*." + QString(format.constData()); formats.isEmpty() ? formats+=f : formats+=' '+f; } QString path = QFileDialog::getOpenFileName(this, i18n("Select the image file"), dir, i18n("Images (%1)", formats)); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos!=-1) { QString newDir = path.left(pos); if (newDir!=dir) conf.writeEntry("LastImageDir", newDir); } ui.leBackgroundFileName->setText( path ); for (auto* worksheet : m_worksheetList) worksheet->setBackgroundFileName(path); } void WorksheetDock::fileNameChanged() { if (m_initializing) return; QString fileName = ui.leBackgroundFileName->text(); if (!fileName.isEmpty() && !QFile::exists(fileName)) ui.leBackgroundFileName->setStyleSheet("QLineEdit{background:red;}"); else ui.leBackgroundFileName->setStyleSheet(""); for (auto* worksheet : m_worksheetList) worksheet->setBackgroundFileName(fileName); } //************************************************************* //******** SLOTs for changes triggered in Worksheet *********** //************************************************************* void WorksheetDock::worksheetDescriptionChanged(const AbstractAspect* aspect) { if (m_worksheet != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) ui.leName->setText(aspect->name()); else if (aspect->comment() != ui.leComment->text()) ui.leComment->setText(aspect->comment()); m_initializing = false; } void WorksheetDock::worksheetScaleContentChanged(bool scaled) { m_initializing = true; ui.chScaleContent->setChecked(scaled); m_initializing = false; } void WorksheetDock::worksheetPageRectChanged(const QRectF& rect) { m_initializing = true; ui.sbWidth->setValue(Worksheet::convertFromSceneUnits(rect.width(), Worksheet::Centimeter)); ui.sbHeight->setValue(Worksheet::convertFromSceneUnits(rect.height(), Worksheet::Centimeter)); updatePaperSize(); m_initializing = false; } void WorksheetDock::worksheetBackgroundTypeChanged(PlotArea::BackgroundType type) { m_initializing = true; ui.cbBackgroundType->setCurrentIndex(type); m_initializing = false; } void WorksheetDock::worksheetBackgroundColorStyleChanged(PlotArea::BackgroundColorStyle style) { m_initializing = true; ui.cbBackgroundColorStyle->setCurrentIndex(style); m_initializing = false; } void WorksheetDock::worksheetBackgroundImageStyleChanged(PlotArea::BackgroundImageStyle style) { m_initializing = true; ui.cbBackgroundImageStyle->setCurrentIndex(style); m_initializing = false; } void WorksheetDock::worksheetBackgroundBrushStyleChanged(Qt::BrushStyle style) { m_initializing = true; ui.cbBackgroundBrushStyle->setCurrentIndex(style); m_initializing = false; } void WorksheetDock::worksheetBackgroundFirstColorChanged(const QColor& color) { m_initializing = true; ui.kcbBackgroundFirstColor->setColor(color); m_initializing = false; } void WorksheetDock::worksheetBackgroundSecondColorChanged(const QColor& color) { m_initializing = true; ui.kcbBackgroundSecondColor->setColor(color); m_initializing = false; } void WorksheetDock::worksheetBackgroundFileNameChanged(const QString& name) { m_initializing = true; ui.leBackgroundFileName->setText(name); m_initializing = false; } void WorksheetDock::worksheetBackgroundOpacityChanged(float opacity) { m_initializing = true; ui.sbBackgroundOpacity->setValue( qRound(opacity*100.0) ); m_initializing = false; } void WorksheetDock::worksheetLayoutChanged(Worksheet::Layout layout) { bool b = (layout != Worksheet::NoLayout); ui.sbLayoutTopMargin->setEnabled(b); ui.sbLayoutBottomMargin->setEnabled(b); ui.sbLayoutLeftMargin->setEnabled(b); ui.sbLayoutRightMargin->setEnabled(b); ui.sbLayoutHorizontalSpacing->setEnabled(b); ui.sbLayoutVerticalSpacing->setEnabled(b); ui.sbLayoutRowCount->setEnabled(b); ui.sbLayoutColumnCount->setEnabled(b); if (b) { bool grid = (layout == Worksheet::GridLayout); ui.lGrid->setVisible(grid); ui.lRowCount->setVisible(grid); ui.sbLayoutRowCount->setVisible(grid); ui.lColumnCount->setVisible(grid); ui.sbLayoutColumnCount->setVisible(grid); } else { ui.lGrid->setVisible(true); ui.lRowCount->setVisible(true); ui.sbLayoutRowCount->setVisible(true); ui.lColumnCount->setVisible(true); ui.sbLayoutColumnCount->setVisible(true); } } void WorksheetDock::worksheetLayoutTopMarginChanged(float value) { m_initializing = true; ui.sbLayoutTopMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutBottomMarginChanged(float value) { m_initializing = true; ui.sbLayoutBottomMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutLeftMarginChanged(float value) { m_initializing = true; ui.sbLayoutLeftMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutRightMarginChanged(float value) { m_initializing = true; ui.sbLayoutRightMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutVerticalSpacingChanged(float value) { m_initializing = true; ui.sbLayoutVerticalSpacing->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutHorizontalSpacingChanged(float value) { m_initializing = true; ui.sbLayoutHorizontalSpacing->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutRowCountChanged(int value) { m_initializing = true; ui.sbLayoutRowCount->setValue(value); m_initializing = false; } void WorksheetDock::worksheetLayoutColumnCountChanged(int value) { m_initializing = true; ui.sbLayoutColumnCount->setValue(value); m_initializing = false; } //************************************************************* //******************** SETTINGS ******************************* //************************************************************* void WorksheetDock::load() { // Geometry ui.chScaleContent->setChecked(m_worksheet->scaleContent()); ui.sbWidth->setValue(Worksheet::convertFromSceneUnits( m_worksheet->pageRect().width(), Worksheet::Centimeter) ); ui.sbHeight->setValue(Worksheet::convertFromSceneUnits( m_worksheet->pageRect().height(), Worksheet::Centimeter) ); updatePaperSize(); // Background-tab ui.cbBackgroundType->setCurrentIndex( (int) m_worksheet->backgroundType() ); ui.cbBackgroundColorStyle->setCurrentIndex( (int) m_worksheet->backgroundColorStyle() ); ui.cbBackgroundImageStyle->setCurrentIndex( (int) m_worksheet->backgroundImageStyle() ); ui.cbBackgroundBrushStyle->setCurrentIndex( (int) m_worksheet->backgroundBrushStyle() ); ui.leBackgroundFileName->setText( m_worksheet->backgroundFileName() ); ui.kcbBackgroundFirstColor->setColor( m_worksheet->backgroundFirstColor() ); ui.kcbBackgroundSecondColor->setColor( m_worksheet->backgroundSecondColor() ); ui.sbBackgroundOpacity->setValue( qRound(m_worksheet->backgroundOpacity()*100) ); //highlight the text field for the background image red if an image is used and cannot be found if (!m_worksheet->backgroundFileName().isEmpty() && !QFile::exists(m_worksheet->backgroundFileName())) ui.leBackgroundFileName->setStyleSheet("QLineEdit{background:red;}"); else ui.leBackgroundFileName->setStyleSheet(""); // Layout ui.sbLayoutTopMargin->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutTopMargin(), Worksheet::Centimeter) ); ui.sbLayoutBottomMargin->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutBottomMargin(), Worksheet::Centimeter) ); ui.sbLayoutLeftMargin->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutLeftMargin(), Worksheet::Centimeter) ); ui.sbLayoutRightMargin->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutRightMargin(), Worksheet::Centimeter) ); ui.sbLayoutHorizontalSpacing->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutHorizontalSpacing(), Worksheet::Centimeter) ); ui.sbLayoutVerticalSpacing->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutVerticalSpacing(), Worksheet::Centimeter) ); ui.sbLayoutRowCount->setValue( m_worksheet->layoutRowCount() ); ui.sbLayoutColumnCount->setValue( m_worksheet->layoutColumnCount() ); } void WorksheetDock::loadConfigFromTemplate(KConfig& config) { //extract the name of the template from the file name QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index!=-1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); int size = m_worksheetList.size(); if (size>1) m_worksheet->beginMacro(i18n("%1 worksheets: template \"%2\" loaded", size, name)); else m_worksheet->beginMacro(i18n("%1: template \"%2\" loaded", m_worksheet->name(), name)); this->loadConfig(config); m_worksheet->endMacro(); } void WorksheetDock::loadConfig(KConfig& config) { KConfigGroup group = config.group( "Worksheet" ); // Geometry ui.chScaleContent->setChecked(group.readEntry("ScaleContent", false)); ui.sbWidth->setValue(Worksheet::convertFromSceneUnits(group.readEntry("Width", m_worksheet->pageRect().width()), Worksheet::Centimeter)); ui.sbHeight->setValue(Worksheet::convertFromSceneUnits(group.readEntry("Height", m_worksheet->pageRect().height()), Worksheet::Centimeter)); if (group.readEntry("UseViewSize", false)) ui.cbSize->setCurrentIndex(0); else updatePaperSize(); // Background-tab ui.cbBackgroundType->setCurrentIndex( group.readEntry("BackgroundType", (int) m_worksheet->backgroundType()) ); ui.cbBackgroundColorStyle->setCurrentIndex( group.readEntry("BackgroundColorStyle", (int) m_worksheet->backgroundColorStyle()) ); ui.cbBackgroundImageStyle->setCurrentIndex( group.readEntry("BackgroundImageStyle", (int) m_worksheet->backgroundImageStyle()) ); ui.cbBackgroundBrushStyle->setCurrentIndex( group.readEntry("BackgroundBrushStyle", (int) m_worksheet->backgroundBrushStyle()) ); ui.leBackgroundFileName->setText( group.readEntry("BackgroundFileName", m_worksheet->backgroundFileName()) ); ui.kcbBackgroundFirstColor->setColor( group.readEntry("BackgroundFirstColor", m_worksheet->backgroundFirstColor()) ); ui.kcbBackgroundSecondColor->setColor( group.readEntry("BackgroundSecondColor", m_worksheet->backgroundSecondColor()) ); ui.sbBackgroundOpacity->setValue( qRound(group.readEntry("BackgroundOpacity", m_worksheet->backgroundOpacity())*100) ); // Layout ui.sbLayoutTopMargin->setValue(group.readEntry("LayoutTopMargin", Worksheet::convertFromSceneUnits(m_worksheet->layoutTopMargin(), Worksheet::Centimeter)) ); ui.sbLayoutBottomMargin->setValue(group.readEntry("LayoutBottomMargin", Worksheet::convertFromSceneUnits(m_worksheet->layoutBottomMargin(), Worksheet::Centimeter)) ); ui.sbLayoutLeftMargin->setValue(group.readEntry("LayoutLeftMargin", Worksheet::convertFromSceneUnits(m_worksheet->layoutLeftMargin(), Worksheet::Centimeter)) ); ui.sbLayoutRightMargin->setValue(group.readEntry("LayoutRightMargin", Worksheet::convertFromSceneUnits(m_worksheet->layoutRightMargin(), Worksheet::Centimeter)) ); ui.sbLayoutHorizontalSpacing->setValue(group.readEntry("LayoutHorizontalSpacing", Worksheet::convertFromSceneUnits(m_worksheet->layoutHorizontalSpacing(), Worksheet::Centimeter)) ); ui.sbLayoutVerticalSpacing->setValue(group.readEntry("LayoutVerticalSpacing", Worksheet::convertFromSceneUnits(m_worksheet->layoutVerticalSpacing(), Worksheet::Centimeter)) ); ui.sbLayoutRowCount->setValue(group.readEntry("LayoutRowCount", m_worksheet->layoutRowCount())); ui.sbLayoutColumnCount->setValue(group.readEntry("LayoutColumnCount", m_worksheet->layoutColumnCount())); } void WorksheetDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group( "Worksheet" ); //General group.writeEntry("ScaleContent",ui.chScaleContent->isChecked()); group.writeEntry("UseViewSize",ui.cbSize->currentIndex()==0); group.writeEntry("Width",Worksheet::convertToSceneUnits(ui.sbWidth->value(), Worksheet::Centimeter)); group.writeEntry("Height",Worksheet::convertToSceneUnits(ui.sbHeight->value(), Worksheet::Centimeter)); //Background group.writeEntry("BackgroundType",ui.cbBackgroundType->currentIndex()); group.writeEntry("BackgroundColorStyle", ui.cbBackgroundColorStyle->currentIndex()); group.writeEntry("BackgroundImageStyle", ui.cbBackgroundImageStyle->currentIndex()); group.writeEntry("BackgroundBrushStyle", ui.cbBackgroundBrushStyle->currentIndex()); group.writeEntry("BackgroundFileName", ui.leBackgroundFileName->text()); group.writeEntry("BackgroundFirstColor", ui.kcbBackgroundFirstColor->color()); group.writeEntry("BackgroundSecondColor", ui.kcbBackgroundSecondColor->color()); group.writeEntry("BackgroundOpacity", ui.sbBackgroundOpacity->value()/100.0); //Layout group.writeEntry("LayoutTopMargin",Worksheet::convertToSceneUnits(ui.sbLayoutTopMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutBottomMargin",Worksheet::convertToSceneUnits(ui.sbLayoutBottomMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutLeftMargin",Worksheet::convertToSceneUnits(ui.sbLayoutLeftMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutRightMargin",Worksheet::convertToSceneUnits(ui.sbLayoutRightMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutVerticalSpacing",Worksheet::convertToSceneUnits(ui.sbLayoutVerticalSpacing->value(), Worksheet::Centimeter)); group.writeEntry("LayoutHorizontalSpacing",Worksheet::convertToSceneUnits(ui.sbLayoutHorizontalSpacing->value(), Worksheet::Centimeter)); group.writeEntry("LayoutRowCount", ui.sbLayoutRowCount->value()); group.writeEntry("LayoutColumnCount", ui.sbLayoutColumnCount->value()); config.sync(); } void WorksheetDock::loadTheme(const QString& theme) { for (auto* worksheet : m_worksheetList) worksheet->setTheme(theme); } diff --git a/src/kdefrontend/dockwidgets/XYConvolutionCurveDock.cpp b/src/kdefrontend/dockwidgets/XYConvolutionCurveDock.cpp index c755e90ae..c8d6ed3e2 100644 --- a/src/kdefrontend/dockwidgets/XYConvolutionCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYConvolutionCurveDock.cpp @@ -1,669 +1,669 @@ /*************************************************************************** File : XYConvolutionCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2018 Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for editing properties of convolution curves ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "XYConvolutionCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYConvolutionCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include extern "C" { #include "backend/nsl/nsl_conv.h" } /*! \class XYConvolutionCurveDock \brief Provides a widget for editing the properties of the XYConvolutionCurves (2D-curves defined by a convolution) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYConvolutionCurveDock::XYConvolutionCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), cbY2DataColumn(nullptr), m_convolutionCurve(nullptr) { //hide the line connection type ui.cbLineType->setDisabled(true); //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYConvolutionCurveDock::setupGeneral() { DEBUG("XYConvolutionCurveDock::setupGeneral()"); QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); - QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); + auto* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 3); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 8, 2, 1, 3); cbY2DataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbY2DataColumn, 9, 2, 1, 3); for (int i = 0; i < NSL_CONV_KERNEL_COUNT; i++) uiGeneralTab.cbKernel->addItem(i18n(nsl_conv_kernel_name[i])); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); for (int i = 0; i < NSL_CONV_DIRECTION_COUNT; i++) uiGeneralTab.cbDirection->addItem(i18n(nsl_conv_direction_name[i])); for (int i = 0; i < NSL_CONV_TYPE_COUNT; i++) uiGeneralTab.cbType->addItem(i18n(nsl_conv_type_name[i])); // nsl_conv_method_type not exposed to user for (int i = 0; i < NSL_CONV_NORM_COUNT; i++) uiGeneralTab.cbNorm->addItem(i18n(nsl_conv_norm_name[i])); for (int i = 0; i < NSL_CONV_WRAP_COUNT; i++) uiGeneralTab.cbWrap->addItem(i18n(nsl_conv_wrap_name[i])); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); - QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); + auto* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); DEBUG("XYConvolutionCurveDock::setupGeneral() DONE"); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYConvolutionCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYConvolutionCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.sbSamplingInterval, SIGNAL(valueChanged(double)), this, SLOT(samplingIntervalChanged()) ); connect( uiGeneralTab.cbKernel, SIGNAL(currentIndexChanged(int)), this, SLOT(kernelChanged()) ); connect( uiGeneralTab.sbKernelSize, SIGNAL(valueChanged(int)), this, SLOT(kernelSizeChanged()) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbDirection, SIGNAL(currentIndexChanged(int)), this, SLOT(directionChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.cbNorm, SIGNAL(currentIndexChanged(int)), this, SLOT(normChanged()) ); connect( uiGeneralTab.cbWrap, SIGNAL(currentIndexChanged(int)), this, SLOT(wrapChanged()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); connect( cbY2DataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(y2DataColumnChanged(QModelIndex)) ); } void XYConvolutionCurveDock::initGeneralTab() { DEBUG("XYConvolutionCurveDock::initGeneralTab()"); //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size() == 1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_convolutionCurve = dynamic_cast(m_curve); // hide x-Range per default uiGeneralTab.lXRange->setEnabled(false); uiGeneralTab.cbAutoRange->setEnabled(false); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_convolutionCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_convolutionCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_convolutionCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_convolutionCurve->yDataColumn()); XYCurveDock::setModelIndexFromAspect(cbY2DataColumn, m_convolutionCurve->y2DataColumn()); uiGeneralTab.sbSamplingInterval->setValue(m_convolutionData.samplingInterval); uiGeneralTab.cbKernel->setCurrentIndex(m_convolutionData.kernel); uiGeneralTab.sbKernelSize->setValue((int)m_convolutionData.kernelSize); uiGeneralTab.cbAutoRange->setChecked(m_convolutionData.autoRange); uiGeneralTab.sbMin->setValue(m_convolutionData.xRange.first()); uiGeneralTab.sbMax->setValue(m_convolutionData.xRange.last()); this->autoRangeChanged(); y2DataColumnChanged(cbY2DataColumn->currentModelIndex()); // settings uiGeneralTab.cbDirection->setCurrentIndex(m_convolutionData.direction); uiGeneralTab.cbType->setCurrentIndex(m_convolutionData.type); //m_convolutionData.method not used uiGeneralTab.cbNorm->setCurrentIndex(m_convolutionData.normalize); uiGeneralTab.cbWrap->setCurrentIndex(m_convolutionData.wrap); this->directionChanged(); this->showConvolutionResult(); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_convolutionCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_convolutionCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_convolutionCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_convolutionCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_convolutionCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_convolutionCurve, SIGNAL(y2DataColumnChanged(const AbstractColumn*)), this, SLOT(curveY2DataColumnChanged(const AbstractColumn*))); connect(m_convolutionCurve, SIGNAL(convolutionDataChanged(XYConvolutionCurve::ConvolutionData)), this, SLOT(curveConvolutionDataChanged(XYConvolutionCurve::ConvolutionData))); connect(m_convolutionCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYConvolutionCurveDock::setModel() { DEBUG("XYConvolutionCurveDock::setModel()"); QList list; list << "Folder" << "Datapicker" << "Worksheet" << "CartesianPlot" << "XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list << "Folder" << "Workbook" << "Datapicker" << "DatapickerCurve" << "Spreadsheet" << "FileDataSource" << "Column" << "Worksheet" << "CartesianPlot" << "XYConvolutionCurve"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbY2DataColumn->setTopLevelClasses(list); cbDataSourceCurve->setModel(m_aspectTreeModel); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); cbY2DataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); DEBUG("XYConvolutionCurveDock::setModel() DONE"); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYConvolutionCurveDock::setCurves(QList list) { m_initializing = true; m_curvesList = list; m_curve = list.first(); m_convolutionCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_convolutionData = m_convolutionCurve->convolutionData(); initGeneralTab(); initTabs(); m_initializing = false; //hide the "skip gaps" option after the curves were set ui.lLineSkipGaps->hide(); ui.chkLineSkipGaps->hide(); } //************************************************************* //**** SLOTs for changes triggered in XYConvolutionCurveDock ** //************************************************************* void XYConvolutionCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYConvolutionCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYConvolutionCurveDock::dataSourceTypeChanged(int index) { - XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; + auto type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); uiGeneralTab.lY2Column->show(); cbY2DataColumn->show(); uiGeneralTab.lSamplingInterval->show(); uiGeneralTab.l2SamplingInterval->show(); uiGeneralTab.sbSamplingInterval->show(); uiGeneralTab.lKernel->setText(i18n("or Kernel/Size:")); } else { //xy-curve data source uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); uiGeneralTab.lY2Column->hide(); cbY2DataColumn->hide(); uiGeneralTab.lSamplingInterval->hide(); uiGeneralTab.l2SamplingInterval->hide(); uiGeneralTab.sbSamplingInterval->hide(); uiGeneralTab.lKernel->setEnabled(true); uiGeneralTab.lKernel->setText(i18n("with Kernel/Size:")); uiGeneralTab.cbKernel->setEnabled(true); uiGeneralTab.sbKernelSize->setEnabled(true); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); enableRecalculate(); } void XYConvolutionCurveDock::dataSourceCurveChanged(const QModelIndex& index) { - AbstractAspect* aspect = static_cast(index.internalPointer()); - XYCurve* dataSourceCurve = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* dataSourceCurve = dynamic_cast(aspect); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYConvolutionCurveDock::xDataColumnChanged(const QModelIndex& index) { DEBUG("XYConvolutionCurveDock::xDataColumnChanged()"); if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); if (column != nullptr) { if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } } } void XYConvolutionCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; DEBUG("yDataColumnChanged()"); - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYConvolutionCurveDock::y2DataColumnChanged(const QModelIndex& index) { if (m_initializing) return; DEBUG("y2DataColumnChanged()"); - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setY2DataColumn(column); } void XYConvolutionCurveDock::samplingIntervalChanged() { double samplingInterval = uiGeneralTab.sbSamplingInterval->value(); m_convolutionData.samplingInterval = samplingInterval; enableRecalculate(); } void XYConvolutionCurveDock::kernelChanged() { - nsl_conv_kernel_type kernel = (nsl_conv_kernel_type) uiGeneralTab.cbKernel->currentIndex(); + auto kernel = (nsl_conv_kernel_type) uiGeneralTab.cbKernel->currentIndex(); m_convolutionData.kernel = kernel; //TODO: change selectable sizes uiGeneralTab.sbKernelSize->setEnabled(true); switch (kernel) { case nsl_conv_kernel_avg: // all values allowed case nsl_conv_kernel_smooth_triangle: case nsl_conv_kernel_gaussian: case nsl_conv_kernel_lorentzian: uiGeneralTab.sbKernelSize->setMinimum(2); uiGeneralTab.sbKernelSize->setMaximum(999); uiGeneralTab.sbKernelSize->setSingleStep(1); uiGeneralTab.sbKernelSize->setValue(2); break; case nsl_conv_kernel_smooth_gaussian: uiGeneralTab.sbKernelSize->setMinimum(5); uiGeneralTab.sbKernelSize->setMaximum(9); uiGeneralTab.sbKernelSize->setSingleStep(2); uiGeneralTab.sbKernelSize->setValue(5); break; case nsl_conv_kernel_first_derivative: uiGeneralTab.sbKernelSize->setMinimum(2); uiGeneralTab.sbKernelSize->setValue(2); uiGeneralTab.sbKernelSize->setEnabled(false); break; case nsl_conv_kernel_smooth_first_derivative: uiGeneralTab.sbKernelSize->setMinimum(3); uiGeneralTab.sbKernelSize->setMaximum(999); uiGeneralTab.sbKernelSize->setSingleStep(2); uiGeneralTab.sbKernelSize->setValue(3); break; case nsl_conv_kernel_second_derivative: uiGeneralTab.sbKernelSize->setMinimum(3); uiGeneralTab.sbKernelSize->setValue(3); uiGeneralTab.sbKernelSize->setEnabled(false); break; case nsl_conv_kernel_third_derivative: uiGeneralTab.sbKernelSize->setMinimum(4); uiGeneralTab.sbKernelSize->setValue(4); uiGeneralTab.sbKernelSize->setEnabled(false); break; case nsl_conv_kernel_fourth_derivative: uiGeneralTab.sbKernelSize->setMinimum(5); uiGeneralTab.sbKernelSize->setValue(5); uiGeneralTab.sbKernelSize->setEnabled(false); break; } enableRecalculate(); } void XYConvolutionCurveDock::kernelSizeChanged() { size_t kernelSize = uiGeneralTab.sbKernelSize->value(); m_convolutionData.kernelSize = kernelSize; enableRecalculate(); } void XYConvolutionCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_convolutionData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = nullptr; if (m_convolutionCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_convolutionCurve->xDataColumn(); else { if (m_convolutionCurve->dataSourceCurve()) xDataColumn = m_convolutionCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYConvolutionCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_convolutionData.xRange.first() = xMin; enableRecalculate(); } void XYConvolutionCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_convolutionData.xRange.last() = xMax; enableRecalculate(); } void XYConvolutionCurveDock::directionChanged() { DEBUG("XYConvolutionCurveDock::directionChanged()"); - nsl_conv_direction_type dir = (nsl_conv_direction_type) uiGeneralTab.cbDirection->currentIndex(); + auto dir = (nsl_conv_direction_type) uiGeneralTab.cbDirection->currentIndex(); m_convolutionData.direction = dir; // change name if still default if ( m_curve->name().compare(i18n("Convolution")) == 0 && dir == nsl_conv_direction_backward) { m_curve->setName(i18n("Deconvolution")); uiGeneralTab.leName->setText(m_curve->name()); } if (m_curve->name().compare(i18n("Deconvolution")) == 0 && dir == nsl_conv_direction_forward) { m_curve->setName(i18n("Convolution")); uiGeneralTab.leName->setText(m_curve->name()); } enableRecalculate(); } void XYConvolutionCurveDock::typeChanged() { - nsl_conv_type_type type = (nsl_conv_type_type) uiGeneralTab.cbType->currentIndex(); + auto type = (nsl_conv_type_type)uiGeneralTab.cbType->currentIndex(); m_convolutionData.type = type; enableRecalculate(); } void XYConvolutionCurveDock::normChanged() { - nsl_conv_norm_type norm = (nsl_conv_norm_type) uiGeneralTab.cbNorm->currentIndex(); + auto norm = (nsl_conv_norm_type)uiGeneralTab.cbNorm->currentIndex(); m_convolutionData.normalize = norm; enableRecalculate(); } void XYConvolutionCurveDock::wrapChanged() { - nsl_conv_wrap_type wrap = (nsl_conv_wrap_type) uiGeneralTab.cbWrap->currentIndex(); + auto wrap = (nsl_conv_wrap_type)uiGeneralTab.cbWrap->currentIndex(); m_convolutionData.wrap = wrap; enableRecalculate(); } void XYConvolutionCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setConvolutionData(m_convolutionData); uiGeneralTab.pbRecalculate->setEnabled(false); if (m_convolutionData.direction == nsl_conv_direction_forward) emit info(i18n("Convolution status: %1", m_convolutionCurve->convolutionResult().status)); else emit info(i18n("Deconvolution status: %1", m_convolutionCurve->convolutionResult().status)); QApplication::restoreOverrideCursor(); } void XYConvolutionCurveDock::enableRecalculate() const { DEBUG("XYConvolutionCurveDock::enableRecalculate()"); if (m_initializing) return; bool hasSourceData = false; //no convolution possible without the y-data if (m_convolutionCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectY!=nullptr); } else { hasSourceData = (m_convolutionCurve->dataSourceCurve() != nullptr); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the convolution */ void XYConvolutionCurveDock::showConvolutionResult() { const XYConvolutionCurve::ConvolutionResult& convolutionResult = m_convolutionCurve->convolutionResult(); if (!convolutionResult.available) { uiGeneralTab.teResult->clear(); return; } QString str = i18n("status: %1", convolutionResult.status) + "
"; if (!convolutionResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (convolutionResult.elapsedTime > 1000) str += i18n("calculation time: %1 s", QString::number(convolutionResult.elapsedTime/1000)) + "
"; else str += i18n("calculation time: %1 ms", QString::number(convolutionResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); //enable the "recalculate"-button if the source data was changed since the last convolution uiGeneralTab.pbRecalculate->setEnabled(m_convolutionCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYConvolutionCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYConvolutionCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYConvolutionCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYConvolutionCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { DEBUG("XYConvolutionCurveDock::curveXDataColumnChanged()"); m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); if (column != nullptr) { DEBUG("X Column available"); uiGeneralTab.lXRange->setEnabled(true); uiGeneralTab.cbAutoRange->setEnabled(true); uiGeneralTab.lSamplingInterval->setEnabled(false); uiGeneralTab.l2SamplingInterval->setEnabled(false); uiGeneralTab.sbSamplingInterval->setEnabled(false); } else { DEBUG("X Column not available"); uiGeneralTab.lXRange->setEnabled(false); uiGeneralTab.cbAutoRange->setEnabled(false); uiGeneralTab.lSamplingInterval->setEnabled(true); uiGeneralTab.l2SamplingInterval->setEnabled(true); uiGeneralTab.sbSamplingInterval->setEnabled(true); } m_initializing = false; } void XYConvolutionCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { DEBUG("XYConvolutionCurveDock::curveYDataColumnChanged()"); m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYConvolutionCurveDock::curveY2DataColumnChanged(const AbstractColumn* column) { DEBUG("XYConvolutionCurveDock::curveY2DataColumnChanged()"); m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbY2DataColumn, column); if (column != nullptr) { DEBUG("Y2 Column available"); uiGeneralTab.lKernel->setEnabled(false); uiGeneralTab.cbKernel->setEnabled(false); uiGeneralTab.sbKernelSize->setEnabled(false); } else { DEBUG("Y2 Column not available"); uiGeneralTab.lKernel->setEnabled(true); uiGeneralTab.cbKernel->setEnabled(true); uiGeneralTab.sbKernelSize->setEnabled(true); } m_initializing = false; } void XYConvolutionCurveDock::curveConvolutionDataChanged(const XYConvolutionCurve::ConvolutionData& convolutionData) { m_initializing = true; m_convolutionData = convolutionData; this->directionChanged(); this->showConvolutionResult(); m_initializing = false; } void XYConvolutionCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYCorrelationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYCorrelationCurveDock.cpp index a8c7771b0..b1a4167eb 100644 --- a/src/kdefrontend/dockwidgets/XYCorrelationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYCorrelationCurveDock.cpp @@ -1,546 +1,546 @@ /*************************************************************************** File : XYCorrelationCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2018 Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for editing properties of correlation curves ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "XYCorrelationCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYCorrelationCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include extern "C" { #include "backend/nsl/nsl_corr.h" } /*! \class XYCorrelationCurveDock \brief Provides a widget for editing the properties of the XYCorrelationCurves (2D-curves defined by a correlation) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYCorrelationCurveDock::XYCorrelationCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), cbY2DataColumn(nullptr), m_correlationCurve(nullptr) { //hide the line connection type ui.cbLineType->setDisabled(true); //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYCorrelationCurveDock::setupGeneral() { DEBUG("XYCorrelationCurveDock::setupGeneral()"); QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); - QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); + auto* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 3); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 8, 2, 1, 3); cbY2DataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbY2DataColumn, 9, 2, 1, 3); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); for (int i = 0; i < NSL_CORR_TYPE_COUNT; i++) uiGeneralTab.cbType->addItem(i18n(nsl_corr_type_name[i])); // nsl_corr_method_type not exposed to user for (int i = 0; i < NSL_CORR_NORM_COUNT; i++) uiGeneralTab.cbNorm->addItem(i18n(nsl_corr_norm_name[i])); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); - QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); + auto* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); DEBUG("XYCorrelationCurveDock::setupGeneral() DONE"); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYCorrelationCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYCorrelationCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.sbSamplingInterval, SIGNAL(valueChanged(double)), this, SLOT(samplingIntervalChanged()) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.cbNorm, SIGNAL(currentIndexChanged(int)), this, SLOT(normChanged()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); connect( cbY2DataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(y2DataColumnChanged(QModelIndex)) ); } void XYCorrelationCurveDock::initGeneralTab() { DEBUG("XYCorrelationCurveDock::initGeneralTab()"); //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size() == 1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_correlationCurve = dynamic_cast(m_curve); // hide x-Range per default uiGeneralTab.lXRange->setEnabled(false); uiGeneralTab.cbAutoRange->setEnabled(false); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_correlationCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_correlationCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_correlationCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_correlationCurve->yDataColumn()); XYCurveDock::setModelIndexFromAspect(cbY2DataColumn, m_correlationCurve->y2DataColumn()); uiGeneralTab.sbSamplingInterval->setValue(m_correlationData.samplingInterval); uiGeneralTab.cbAutoRange->setChecked(m_correlationData.autoRange); uiGeneralTab.sbMin->setValue(m_correlationData.xRange.first()); uiGeneralTab.sbMax->setValue(m_correlationData.xRange.last()); this->autoRangeChanged(); y2DataColumnChanged(cbY2DataColumn->currentModelIndex()); // settings uiGeneralTab.cbType->setCurrentIndex(m_correlationData.type); //m_correlationData.method not used uiGeneralTab.cbNorm->setCurrentIndex(m_correlationData.normalize); this->showCorrelationResult(); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_correlationCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_correlationCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_correlationCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_correlationCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_correlationCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_correlationCurve, SIGNAL(y2DataColumnChanged(const AbstractColumn*)), this, SLOT(curveY2DataColumnChanged(const AbstractColumn*))); connect(m_correlationCurve, SIGNAL(correlationDataChanged(XYCorrelationCurve::CorrelationData)), this, SLOT(curveCorrelationDataChanged(XYCorrelationCurve::CorrelationData))); connect(m_correlationCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYCorrelationCurveDock::setModel() { DEBUG("XYCorrelationCurveDock::setModel()"); QList list; list << "Folder" << "Datapicker" << "Worksheet" << "CartesianPlot" << "XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list << "Folder" << "Workbook" << "Datapicker" << "DatapickerCurve" << "Spreadsheet" << "FileDataSource" << "Column" << "Worksheet" << "CartesianPlot" << "XYCorrelationCurve"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbY2DataColumn->setTopLevelClasses(list); cbDataSourceCurve->setModel(m_aspectTreeModel); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); cbY2DataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); DEBUG("XYCorrelationCurveDock::setModel() DONE"); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYCorrelationCurveDock::setCurves(QList list) { m_initializing = true; m_curvesList = list; m_curve = list.first(); m_correlationCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_correlationData = m_correlationCurve->correlationData(); initGeneralTab(); initTabs(); m_initializing = false; //hide the "skip gaps" option after the curves were set ui.lLineSkipGaps->hide(); ui.chkLineSkipGaps->hide(); } //************************************************************* //**** SLOTs for changes triggered in XYCorrelationCurveDock ** //************************************************************* void XYCorrelationCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYCorrelationCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYCorrelationCurveDock::dataSourceTypeChanged(int index) { - XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; + auto type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); uiGeneralTab.lY2Column->show(); cbY2DataColumn->show(); uiGeneralTab.lSamplingInterval->show(); uiGeneralTab.l2SamplingInterval->show(); uiGeneralTab.sbSamplingInterval->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); uiGeneralTab.lY2Column->hide(); cbY2DataColumn->hide(); uiGeneralTab.lSamplingInterval->hide(); uiGeneralTab.l2SamplingInterval->hide(); uiGeneralTab.sbSamplingInterval->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYCorrelationCurveDock::dataSourceCurveChanged(const QModelIndex& index) { - AbstractAspect* aspect = static_cast(index.internalPointer()); - XYCurve* dataSourceCurve = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* dataSourceCurve = dynamic_cast(aspect); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYCorrelationCurveDock::xDataColumnChanged(const QModelIndex& index) { DEBUG("XYCorrelationCurveDock::xDataColumnChanged()"); if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); if (column != nullptr) { if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } } } void XYCorrelationCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; DEBUG("yDataColumnChanged()"); - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYCorrelationCurveDock::y2DataColumnChanged(const QModelIndex& index) { if (m_initializing) return; DEBUG("y2DataColumnChanged()"); - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setY2DataColumn(column); } void XYCorrelationCurveDock::samplingIntervalChanged() { double samplingInterval = uiGeneralTab.sbSamplingInterval->value(); m_correlationData.samplingInterval = samplingInterval; enableRecalculate(); } void XYCorrelationCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_correlationData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = nullptr; if (m_correlationCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_correlationCurve->xDataColumn(); else { if (m_correlationCurve->dataSourceCurve()) xDataColumn = m_correlationCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYCorrelationCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_correlationData.xRange.first() = xMin; enableRecalculate(); } void XYCorrelationCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_correlationData.xRange.last() = xMax; enableRecalculate(); } void XYCorrelationCurveDock::typeChanged() { - nsl_corr_type_type type = (nsl_corr_type_type) uiGeneralTab.cbType->currentIndex(); + auto type = (nsl_corr_type_type) uiGeneralTab.cbType->currentIndex(); m_correlationData.type = type; enableRecalculate(); } void XYCorrelationCurveDock::normChanged() { - nsl_corr_norm_type norm = (nsl_corr_norm_type) uiGeneralTab.cbNorm->currentIndex(); + auto norm = (nsl_corr_norm_type) uiGeneralTab.cbNorm->currentIndex(); m_correlationData.normalize = norm; enableRecalculate(); } void XYCorrelationCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setCorrelationData(m_correlationData); uiGeneralTab.pbRecalculate->setEnabled(false); emit info(i18n("Correlation status: %1", m_correlationCurve->correlationResult().status)); QApplication::restoreOverrideCursor(); } void XYCorrelationCurveDock::enableRecalculate() const { DEBUG("XYCorrelationCurveDock::enableRecalculate()"); if (m_initializing) return; bool hasSourceData = false; //no correlation possible without y-data and y2-data if (m_correlationCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY2 = static_cast(cbY2DataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectY != nullptr && aspectY2 != nullptr); } else { hasSourceData = (m_correlationCurve->dataSourceCurve() != nullptr); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the correlation */ void XYCorrelationCurveDock::showCorrelationResult() { const XYCorrelationCurve::CorrelationResult& correlationResult = m_correlationCurve->correlationResult(); if (!correlationResult.available) { uiGeneralTab.teResult->clear(); return; } QString str = i18n("status: %1", correlationResult.status) + "
"; if (!correlationResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (correlationResult.elapsedTime > 1000) str += i18n("calculation time: %1 s", QString::number(correlationResult.elapsedTime/1000)) + "
"; else str += i18n("calculation time: %1 ms", QString::number(correlationResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); //enable the "recalculate"-button if the source data was changed since the last correlation uiGeneralTab.pbRecalculate->setEnabled(m_correlationCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYCorrelationCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYCorrelationCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYCorrelationCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYCorrelationCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { DEBUG("XYCorrelationCurveDock::curveXDataColumnChanged()"); m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); if (column != nullptr) { DEBUG("X Column available"); uiGeneralTab.lXRange->setEnabled(true); uiGeneralTab.cbAutoRange->setEnabled(true); uiGeneralTab.lSamplingInterval->setEnabled(false); uiGeneralTab.l2SamplingInterval->setEnabled(false); uiGeneralTab.sbSamplingInterval->setEnabled(false); } else { DEBUG("X Column not available"); uiGeneralTab.lXRange->setEnabled(false); uiGeneralTab.cbAutoRange->setEnabled(false); uiGeneralTab.lSamplingInterval->setEnabled(true); uiGeneralTab.l2SamplingInterval->setEnabled(true); uiGeneralTab.sbSamplingInterval->setEnabled(true); } m_initializing = false; } void XYCorrelationCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { DEBUG("XYCorrelationCurveDock::curveYDataColumnChanged()"); m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYCorrelationCurveDock::curveY2DataColumnChanged(const AbstractColumn* column) { DEBUG("XYCorrelationCurveDock::curveY2DataColumnChanged()"); m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbY2DataColumn, column); m_initializing = false; } void XYCorrelationCurveDock::curveCorrelationDataChanged(const XYCorrelationCurve::CorrelationData& correlationData) { m_initializing = true; m_correlationData = correlationData; this->showCorrelationResult(); m_initializing = false; } void XYCorrelationCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp index 1d789b371..79e44cb3b 100644 --- a/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp @@ -1,1349 +1,1349 @@ /*************************************************************************** File : XYFitCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2014-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for editing properties of fit curves ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "XYFitCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/lib/macros.h" #include "backend/gsl/ExpressionParser.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/widgets/ConstantsWidget.h" #include "kdefrontend/widgets/FunctionsWidget.h" #include "kdefrontend/widgets/FitOptionsWidget.h" #include "kdefrontend/widgets/FitParametersWidget.h" #include #include #include #include #include #include extern "C" { #include "backend/nsl/nsl_sf_stats.h" } /*! \class XYFitCurveDock \brief Provides a widget for editing the properties of the XYFitCurves (2D-curves defined by a fit model) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYFitCurveDock::XYFitCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), cbXErrorColumn(nullptr), cbYErrorColumn(nullptr), m_fitCurve(nullptr), m_parametersValid(true) { //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * set up "General" tab */ void XYFitCurveDock::setupGeneral() { DEBUG("XYFitCurveDock::setupGeneral()"); QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); - QGridLayout* gridLayout = qobject_cast(generalTab->layout()); + auto* gridLayout = qobject_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2, 2, 2, 2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 6, 4, 1, 4); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 7, 4, 1, 4); cbXErrorColumn = new TreeViewComboBox(generalTab); cbXErrorColumn->setEnabled(false); uiGeneralTab.hlXError->addWidget(cbXErrorColumn); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 8, 4, 1, 4); cbYErrorColumn = new TreeViewComboBox(generalTab); cbYErrorColumn->setEnabled(false); uiGeneralTab.hlYWeight->addWidget(cbYErrorColumn); // X/Y-Weight for(int i = 0; i < NSL_FIT_WEIGHT_TYPE_COUNT; i++) { uiGeneralTab.cbXWeight->addItem(nsl_fit_weight_type_name[i]); uiGeneralTab.cbYWeight->addItem(nsl_fit_weight_type_name[i]); } uiGeneralTab.cbXWeight->setCurrentIndex(nsl_fit_weight_no); uiGeneralTab.cbYWeight->setCurrentIndex(nsl_fit_weight_no); for(int i = 0; i < NSL_FIT_MODEL_CATEGORY_COUNT; i++) uiGeneralTab.cbCategory->addItem(nsl_fit_model_category_name[i]); uiGeneralTab.teEquation->setMaximumHeight(uiGeneralTab.leName->sizeHint().height() * 2); fitParametersWidget = new FitParametersWidget(generalTab); gridLayout->addWidget(fitParametersWidget, 21, 4, 1, 4); //use white background in the preview label QPalette p; p.setColor(QPalette::Window, Qt::white); uiGeneralTab.lFuncPic->setAutoFillBackground(true); uiGeneralTab.lFuncPic->setPalette(p); uiGeneralTab.tbConstants->setIcon(QIcon::fromTheme("labplot-format-text-symbol")); uiGeneralTab.tbFunctions->setIcon(QIcon::fromTheme("preferences-desktop-font")); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); // TODO: setting checked background color to unchecked color // p = uiGeneralTab.lData->palette(); // QWidget::palette().color(QWidget::backgroundRole()) // not working with 'transparent' // p.setColor(QPalette::Base, Qt::transparent); // uiGeneralTab.lData->setPalette(p); // see https://forum.qt.io/topic/41325/solved-background-of-checked-qpushbutton-with-stylesheet/2 // Styles not usable (here: text color not theme dependent). see https://forum.qt.io/topic/60546/qpushbutton-default-windows-style-sheet/9 // uiGeneralTab.lData->setStyleSheet("QToolButton:checked{background-color: transparent;border: 3px transparent;padding: 3px;}"); // uiGeneralTab.lData->setAutoFillBackground(true); uiGeneralTab.twLog->setEditTriggers(QAbstractItemView::NoEditTriggers); uiGeneralTab.twParameters->setEditTriggers(QAbstractItemView::NoEditTriggers); uiGeneralTab.twGoodness->setEditTriggers(QAbstractItemView::NoEditTriggers); //don't allow word wrapping in the log-table for the multi-line iterations string uiGeneralTab.twLog->setWordWrap(false); // show these options per default showDataOptions(true); showFitOptions(true); showParameters(true); showResults(true); // hide these options per default showWeightsOptions(false); // context menus uiGeneralTab.twParameters->setContextMenuPolicy(Qt::CustomContextMenu); uiGeneralTab.twGoodness->setContextMenuPolicy(Qt::CustomContextMenu); uiGeneralTab.twLog->setContextMenuPolicy(Qt::CustomContextMenu); connect(uiGeneralTab.twParameters, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(resultParametersContextMenuRequest(QPoint)) ); connect(uiGeneralTab.twGoodness, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(resultGoodnessContextMenuRequest(QPoint)) ); connect(uiGeneralTab.twLog, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(resultLogContextMenuRequest(QPoint)) ); uiGeneralTab.twLog->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); uiGeneralTab.twGoodness->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); uiGeneralTab.twGoodness->item(0, 1)->setText(UTF8_QSTRING("χ²")); uiGeneralTab.twGoodness->item(1, 1)->setText(i18n("reduced") + ' ' + UTF8_QSTRING("χ²") + " (" + UTF8_QSTRING("χ²") + "/dof)"); uiGeneralTab.twGoodness->item(3, 1)->setText(UTF8_QSTRING("R²")); uiGeneralTab.twGoodness->item(4, 1)->setText(UTF8_QSTRING("R̄²")); uiGeneralTab.twGoodness->item(5, 0)->setText(UTF8_QSTRING("χ²") + ' ' + i18n("test")); uiGeneralTab.twGoodness->item(5, 1)->setText("P > " + UTF8_QSTRING("χ²")); - QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); + auto* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect(uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFitCurveDock::nameChanged); connect(uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFitCurveDock::commentChanged); connect(uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool))); connect(uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int))); connect(uiGeneralTab.lWeights, &QPushButton::clicked, this, &XYFitCurveDock::showWeightsOptions); connect(uiGeneralTab.cbXWeight, SIGNAL(currentIndexChanged(int)), this, SLOT(xWeightChanged(int))); connect(uiGeneralTab.cbYWeight, SIGNAL(currentIndexChanged(int)), this, SLOT(yWeightChanged(int))); connect(uiGeneralTab.cbCategory, SIGNAL(currentIndexChanged(int)), this, SLOT(categoryChanged(int))); connect(uiGeneralTab.cbModel, SIGNAL(currentIndexChanged(int)), this, SLOT(modelTypeChanged(int))); connect(uiGeneralTab.sbDegree, SIGNAL(valueChanged(int)), this, SLOT(updateModelEquation())); connect(uiGeneralTab.teEquation, SIGNAL(expressionChanged()), this, SLOT(expressionChanged())); connect(uiGeneralTab.tbConstants, SIGNAL(clicked()), this, SLOT(showConstants())); connect(uiGeneralTab.tbFunctions, SIGNAL(clicked()), this, SLOT(showFunctions())); connect(uiGeneralTab.pbOptions, SIGNAL(clicked()), this, SLOT(showOptions())); connect(uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked())); connect(uiGeneralTab.lData, &QPushButton::clicked, this, &XYFitCurveDock::showDataOptions); connect(uiGeneralTab.lFit, &QPushButton::clicked, this, &XYFitCurveDock::showFitOptions); connect(uiGeneralTab.lParameters, &QPushButton::clicked, this, &XYFitCurveDock::showParameters); connect(uiGeneralTab.lResults, &QPushButton::clicked, this, &XYFitCurveDock::showResults); connect(cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex))); connect(cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex))); connect(cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex))); connect(cbXErrorColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xErrorColumnChanged(QModelIndex))); connect(cbYErrorColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yErrorColumnChanged(QModelIndex))); } /* * load curve settings */ void XYFitCurveDock::initGeneralTab() { DEBUG("XYFitCurveDock::initGeneralTab()"); //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size() == 1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); } else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } uiGeneralTab.cbDataSourceType->setCurrentIndex(m_fitCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_fitCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_fitCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_fitCurve->yDataColumn()); XYCurveDock::setModelIndexFromAspect(cbXErrorColumn, m_fitCurve->xErrorColumn()); XYCurveDock::setModelIndexFromAspect(cbYErrorColumn, m_fitCurve->yErrorColumn()); int tmpModelType = m_fitData.modelType; // save type because it's reset when category changes if (m_fitData.modelCategory == nsl_fit_model_custom) uiGeneralTab.cbCategory->setCurrentIndex(uiGeneralTab.cbCategory->count() - 1); else uiGeneralTab.cbCategory->setCurrentIndex(m_fitData.modelCategory); if (m_fitData.modelCategory != nsl_fit_model_custom) uiGeneralTab.cbModel->setCurrentIndex(tmpModelType); m_fitData.modelType = tmpModelType; categoryChanged(m_fitData.modelCategory); // fill model types uiGeneralTab.cbXWeight->setCurrentIndex(m_fitData.xWeightsType); uiGeneralTab.cbYWeight->setCurrentIndex(m_fitData.yWeightsType); uiGeneralTab.sbDegree->setValue(m_fitData.degree); if (m_fitData.paramStartValues.size() > 0) { DEBUG(" B start value 0 = " << m_fitData.paramStartValues.at(0)); } DEBUG(" B model degree = " << m_fitData.degree); this->showFitResult(); uiGeneralTab.chkVisible->setChecked(m_curve->isVisible()); //Slots connect(m_fitCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_fitCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_fitCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_fitCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_fitCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_fitCurve, SIGNAL(xErrorColumnChanged(const AbstractColumn*)), this, SLOT(curveXErrorColumnChanged(const AbstractColumn*))); connect(m_fitCurve, SIGNAL(yErrorColumnChanged(const AbstractColumn*)), this, SLOT(curveYErrorColumnChanged(const AbstractColumn*))); connect(m_fitCurve, SIGNAL(fitDataChanged(XYFitCurve::FitData)), this, SLOT(curveFitDataChanged(XYFitCurve::FitData))); connect(m_fitCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); connect(fitParametersWidget, &FitParametersWidget::parametersChanged, this, &XYFitCurveDock::parametersChanged); connect(fitParametersWidget, &FitParametersWidget::parametersValid, this, &XYFitCurveDock::parametersValid); } void XYFitCurveDock::setModel() { DEBUG("XYFitCurveDock::setModel()"); QList list; list << "Folder" << "Datapicker" << "Worksheet" << "CartesianPlot" << "XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list << "Folder" << "Workbook" << "Spreadsheet" << "FileDataSource" << "Column" << "CantorWorksheet" << "Datapicker"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbXErrorColumn->setTopLevelClasses(list); cbYErrorColumn->setTopLevelClasses(list); cbDataSourceCurve->setModel(m_aspectTreeModel); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); cbXErrorColumn->setModel(m_aspectTreeModel); cbYErrorColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYFitCurveDock::setCurves(QList list) { DEBUG("XYFitCurveDock::setCurves()"); m_initializing = true; m_curvesList = list; m_curve = list.first(); m_fitCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_fitData = m_fitCurve->fitData(); if(m_fitData.paramStartValues.size() > 0) { DEBUG(" A start value 0 = " << m_fitData.paramStartValues.at(0)); } DEBUG(" A model degree = " << m_fitData.degree); fitParametersWidget->setFitData(&m_fitData); initGeneralTab(); initTabs(); m_initializing = false; //init parameter list when not available if (m_fitData.paramStartValues.size() == 0) updateModelEquation(); enableRecalculate(); } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYFitCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYFitCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYFitCurveDock::dataSourceTypeChanged(int index) { - const XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; + const auto type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYFitCurveDock::dataSourceCurveChanged(const QModelIndex& index) { - AbstractAspect* aspect = static_cast(index.internalPointer()); - XYCurve* dataSourceCurve = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* dataSourceCurve = dynamic_cast(aspect); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYFitCurveDock::xDataColumnChanged(const QModelIndex& index) { DEBUG("XYFitCurveDock::xDataColumnChanged()"); if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); // set model dependent start values from new data XYFitCurve::initStartValues(m_fitData, m_curve); } void XYFitCurveDock::yDataColumnChanged(const QModelIndex& index) { DEBUG("XYFitCurveDock::yDataColumnChanged()"); if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); // set model dependent start values from new data XYFitCurve::initStartValues(m_fitData, m_curve); } void XYFitCurveDock::xErrorColumnChanged(const QModelIndex& index) { if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXErrorColumn(column); } void XYFitCurveDock::yErrorColumnChanged(const QModelIndex& index) { if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYErrorColumn(column); } ///////////////////////// fold/unfold options ////////////////////////////////////////////////// void XYFitCurveDock::showDataOptions(bool checked) { if (checked) { uiGeneralTab.lData->setIcon(QIcon::fromTheme("arrow-down")); uiGeneralTab.lDataSourceType->show(); uiGeneralTab.cbDataSourceType->show(); // select options for current source type dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); } else { uiGeneralTab.lData->setIcon(QIcon::fromTheme("arrow-right")); uiGeneralTab.lDataSourceType->hide(); uiGeneralTab.cbDataSourceType->hide(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); } } void XYFitCurveDock::showWeightsOptions(bool checked) { if (checked) { uiGeneralTab.lWeights->setIcon(QIcon::fromTheme("arrow-down")); uiGeneralTab.lXWeight->show(); uiGeneralTab.cbXWeight->show(); uiGeneralTab.lXErrorCol->show(); cbXErrorColumn->show(); uiGeneralTab.lYWeight->show(); uiGeneralTab.cbYWeight->show(); uiGeneralTab.lYErrorCol->show(); cbYErrorColumn->show(); } else { uiGeneralTab.lWeights->setIcon(QIcon::fromTheme("arrow-right")); uiGeneralTab.lXWeight->hide(); uiGeneralTab.cbXWeight->hide(); uiGeneralTab.lXErrorCol->hide(); cbXErrorColumn->hide(); uiGeneralTab.lYWeight->hide(); uiGeneralTab.cbYWeight->hide(); uiGeneralTab.lYErrorCol->hide(); cbYErrorColumn->hide(); } } void XYFitCurveDock::showFitOptions(bool checked) { if (checked) { uiGeneralTab.lFit->setIcon(QIcon::fromTheme("arrow-down")); uiGeneralTab.lCategory->show(); uiGeneralTab.cbCategory->show(); uiGeneralTab.lModel->show(); uiGeneralTab.cbModel->show(); uiGeneralTab.lEquation->show(); m_initializing = true; // do not change start parameter modelTypeChanged(uiGeneralTab.cbModel->currentIndex()); m_initializing = false; } else { uiGeneralTab.lFit->setIcon(QIcon::fromTheme("arrow-right")); uiGeneralTab.lCategory->hide(); uiGeneralTab.cbCategory->hide(); uiGeneralTab.lModel->hide(); uiGeneralTab.cbModel->hide(); uiGeneralTab.lDegree->hide(); uiGeneralTab.sbDegree->hide(); uiGeneralTab.lEquation->hide(); uiGeneralTab.lFuncPic->hide(); uiGeneralTab.teEquation->hide(); uiGeneralTab.tbFunctions->hide(); uiGeneralTab.tbConstants->hide(); } } void XYFitCurveDock::showParameters(bool checked) { if (checked) { uiGeneralTab.lParameters->setIcon(QIcon::fromTheme("arrow-down")); fitParametersWidget->show(); } else { uiGeneralTab.lParameters->setIcon(QIcon::fromTheme("arrow-right")); fitParametersWidget->hide(); } } void XYFitCurveDock::showResults(bool checked) { if (checked) { uiGeneralTab.lResults->setIcon(QIcon::fromTheme("arrow-down")); uiGeneralTab.twResults->show(); } else { uiGeneralTab.lResults->setIcon(QIcon::fromTheme("arrow-right")); uiGeneralTab.twResults->hide(); } } /////////////////////////////////////////////////////////////////////////// void XYFitCurveDock::xWeightChanged(int index) { DEBUG("xWeightChanged() weight = " << nsl_fit_weight_type_name[index]); m_fitData.xWeightsType = (nsl_fit_weight_type)index; // enable/disable weight column switch ((nsl_fit_weight_type)index) { case nsl_fit_weight_no: case nsl_fit_weight_statistical: case nsl_fit_weight_statistical_fit: case nsl_fit_weight_relative: case nsl_fit_weight_relative_fit: cbXErrorColumn->setEnabled(false); uiGeneralTab.lXErrorCol->setEnabled(false); break; case nsl_fit_weight_instrumental: case nsl_fit_weight_direct: case nsl_fit_weight_inverse: cbXErrorColumn->setEnabled(true); uiGeneralTab.lXErrorCol->setEnabled(true); break; } enableRecalculate(); } void XYFitCurveDock::yWeightChanged(int index) { DEBUG("yWeightChanged() weight = " << nsl_fit_weight_type_name[index]); m_fitData.yWeightsType = (nsl_fit_weight_type)index; // enable/disable weight column switch ((nsl_fit_weight_type)index) { case nsl_fit_weight_no: case nsl_fit_weight_statistical: case nsl_fit_weight_statistical_fit: case nsl_fit_weight_relative: case nsl_fit_weight_relative_fit: cbYErrorColumn->setEnabled(false); uiGeneralTab.lYErrorCol->setEnabled(false); break; case nsl_fit_weight_instrumental: case nsl_fit_weight_direct: case nsl_fit_weight_inverse: cbYErrorColumn->setEnabled(true); uiGeneralTab.lYErrorCol->setEnabled(true); break; } enableRecalculate(); } /*! * called when the fit model category (basic functions, peak functions etc.) was changed. * In the combobox for the model type shows the model types for the current category \index and calls \c modelTypeChanged() * to update the model type dependent widgets in the general-tab. */ void XYFitCurveDock::categoryChanged(int index) { if (index == nsl_fit_model_custom) { DEBUG("categoryChanged() category = \"nsl_fit_model_custom\""); } else { DEBUG("categoryChanged() category = \"" << nsl_fit_model_category_name[index] << "\""); } bool hasChanged = true; // nothing has changed when ... if (m_fitData.modelCategory == (nsl_fit_model_category)index || (m_fitData.modelCategory == nsl_fit_model_custom && index == uiGeneralTab.cbCategory->count() - 1) ) hasChanged = false; if (uiGeneralTab.cbCategory->currentIndex() == uiGeneralTab.cbCategory->count() - 1) m_fitData.modelCategory = nsl_fit_model_custom; else m_fitData.modelCategory = (nsl_fit_model_category)index; uiGeneralTab.cbModel->clear(); uiGeneralTab.cbModel->show(); uiGeneralTab.lModel->show(); switch (m_fitData.modelCategory) { case nsl_fit_model_basic: - for(int i = 0; i < NSL_FIT_MODEL_BASIC_COUNT; i++) + for (int i = 0; i < NSL_FIT_MODEL_BASIC_COUNT; i++) uiGeneralTab.cbModel->addItem(nsl_fit_model_basic_name[i]); break; case nsl_fit_model_peak: { - for(int i = 0; i < NSL_FIT_MODEL_PEAK_COUNT; i++) + for (int i = 0; i < NSL_FIT_MODEL_PEAK_COUNT; i++) uiGeneralTab.cbModel->addItem(nsl_fit_model_peak_name[i]); #if defined(_MSC_VER) // disable voigt model const QStandardItemModel* model = qobject_cast(uiGeneralTab.cbModel->model()); QStandardItem* item = model->item(nsl_fit_model_voigt); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); #endif break; } case nsl_fit_model_growth: - for(int i = 0; i < NSL_FIT_MODEL_GROWTH_COUNT; i++) + for (int i = 0; i < NSL_FIT_MODEL_GROWTH_COUNT; i++) uiGeneralTab.cbModel->addItem(nsl_fit_model_growth_name[i]); break; case nsl_fit_model_distribution: { - for(int i = 0; i < NSL_SF_STATS_DISTRIBUTION_COUNT; i++) + for (int i = 0; i < NSL_SF_STATS_DISTRIBUTION_COUNT; i++) uiGeneralTab.cbModel->addItem(nsl_sf_stats_distribution_name[i]); // not-used items are disabled here - const QStandardItemModel* model = qobject_cast(uiGeneralTab.cbModel->model()); + const auto* model = qobject_cast(uiGeneralTab.cbModel->model()); - for(int i = 1; i < NSL_SF_STATS_DISTRIBUTION_COUNT; i++) { + for (int i = 1; i < NSL_SF_STATS_DISTRIBUTION_COUNT; i++) { // unused distributions if (i == nsl_sf_stats_levy_alpha_stable || i == nsl_sf_stats_levy_skew_alpha_stable || i == nsl_sf_stats_bernoulli) { QStandardItem* item = model->item(i); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); } } break; } case nsl_fit_model_custom: uiGeneralTab.cbModel->addItem(i18n("Custom")); uiGeneralTab.cbModel->hide(); uiGeneralTab.lModel->hide(); } if (hasChanged) { //show the fit-model for the currently selected default (first) fit-model uiGeneralTab.cbModel->setCurrentIndex(0); uiGeneralTab.sbDegree->setValue(1); // when model type does not change, call it here updateModelEquation(); } enableRecalculate(); } /*! * called when the fit model type (depends on category) was changed. * Updates the model type dependent widgets in the general-tab and calls \c updateModelEquation() to update the preview pixmap. */ void XYFitCurveDock::modelTypeChanged(int index) { DEBUG("modelTypeChanged() type = " << index << ", initializing = " << m_initializing << ", current type = " << m_fitData.modelType); // leave if there is no selection if(index == -1) return; bool custom = false; if (m_fitData.modelCategory == nsl_fit_model_custom) custom = true; uiGeneralTab.teEquation->setReadOnly(!custom); uiGeneralTab.lModel->setVisible(!custom); uiGeneralTab.cbModel->setVisible(!custom); uiGeneralTab.tbFunctions->setVisible(custom); uiGeneralTab.tbConstants->setVisible(custom); // default settings uiGeneralTab.lDegree->setText(i18n("Degree:")); if (m_fitData.modelType != index) uiGeneralTab.sbDegree->setValue(1); switch (m_fitData.modelCategory) { case nsl_fit_model_basic: switch (index) { case nsl_fit_model_polynomial: case nsl_fit_model_fourier: uiGeneralTab.lDegree->setVisible(true); uiGeneralTab.sbDegree->setVisible(true); uiGeneralTab.sbDegree->setMaximum(10); break; case nsl_fit_model_power: uiGeneralTab.lDegree->setVisible(true); uiGeneralTab.sbDegree->setVisible(true); uiGeneralTab.sbDegree->setMaximum(2); break; case nsl_fit_model_exponential: uiGeneralTab.lDegree->setVisible(true); uiGeneralTab.sbDegree->setVisible(true); uiGeneralTab.sbDegree->setMaximum(10); break; default: uiGeneralTab.lDegree->setVisible(false); uiGeneralTab.sbDegree->setVisible(false); } break; case nsl_fit_model_peak: // all models support multiple peaks uiGeneralTab.lDegree->setText(i18n("Number of peaks:")); uiGeneralTab.lDegree->setVisible(true); uiGeneralTab.sbDegree->setVisible(true); uiGeneralTab.sbDegree->setMaximum(9); break; case nsl_fit_model_growth: case nsl_fit_model_distribution: case nsl_fit_model_custom: uiGeneralTab.lDegree->setVisible(false); uiGeneralTab.sbDegree->setVisible(false); } m_fitData.modelType = index; updateModelEquation(); } /*! * Show the preview pixmap of the fit model expression for the current model category and type. * Called when the model type or the degree of the model were changed. */ void XYFitCurveDock::updateModelEquation() { if (m_fitData.modelCategory == nsl_fit_model_custom) { DEBUG("XYFitCurveDock::updateModelEquation() category = nsl_fit_model_custom, type = " << m_fitData.modelType); } else { DEBUG("XYFitCurveDock::updateModelEquation() category = " << nsl_fit_model_category_name[m_fitData.modelCategory] << ", type = " << m_fitData.modelType); } //this function can also be called when the value for the degree was changed -> update the fit data structure int degree = uiGeneralTab.sbDegree->value(); if (!m_initializing) { m_fitData.degree = degree; XYFitCurve::initFitData(m_fitData); // set model dependent start values from curve data XYFitCurve::initStartValues(m_fitData, m_curve); // udpate parameter widget fitParametersWidget->setFitData(&m_fitData); } // variables/parameter that are known QStringList vars = {"x"}; vars << m_fitData.paramNames; uiGeneralTab.teEquation->setVariables(vars); // set formula picture uiGeneralTab.lEquation->setText(QLatin1String("f(x) =")); QString file; switch (m_fitData.modelCategory) { case nsl_fit_model_basic: { // formula pic depends on degree QString numSuffix = QString::number(degree); if (degree > 4) numSuffix = '4'; if ((nsl_fit_model_type_basic)m_fitData.modelType == nsl_fit_model_power && degree > 2) numSuffix = '2'; file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/fit_models/" + QString(nsl_fit_model_basic_pic_name[m_fitData.modelType]) + numSuffix + ".png"); break; } case nsl_fit_model_peak: { // formula pic depends on number of peaks QString numSuffix = QString::number(degree); if (degree > 4) numSuffix = '4'; file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/fit_models/" + QString(nsl_fit_model_peak_pic_name[m_fitData.modelType]) + numSuffix + ".png"); break; } case nsl_fit_model_growth: file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/fit_models/" + QString(nsl_fit_model_growth_pic_name[m_fitData.modelType]) + ".png"); break; case nsl_fit_model_distribution: file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/gsl_distributions/" + QString(nsl_sf_stats_distribution_pic_name[m_fitData.modelType]) + ".png"); // change label if (m_fitData.modelType == nsl_sf_stats_poisson) uiGeneralTab.lEquation->setText(QLatin1String("f(k)/A =")); else uiGeneralTab.lEquation->setText(QLatin1String("f(x)/A =")); break; case nsl_fit_model_custom: uiGeneralTab.lFuncPic->hide(); uiGeneralTab.teEquation->show(); uiGeneralTab.teEquation->setPlainText(m_fitData.model); } if (m_fitData.modelCategory != nsl_fit_model_custom) { DEBUG("Model pixmap path = " << file.toStdString()); uiGeneralTab.lFuncPic->setPixmap(file); uiGeneralTab.lFuncPic->show(); uiGeneralTab.teEquation->hide(); } enableRecalculate(); } void XYFitCurveDock::showConstants() { QMenu menu; ConstantsWidget constants(&menu); connect(&constants, SIGNAL(constantSelected(QString)), this, SLOT(insertConstant(QString))); connect(&constants, SIGNAL(constantSelected(QString)), &menu, SLOT(close())); connect(&constants, SIGNAL(canceled()), &menu, SLOT(close())); - QWidgetAction* widgetAction = new QWidgetAction(this); + auto* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&constants); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbConstants->width(), -menu.sizeHint().height()); menu.exec(uiGeneralTab.tbConstants->mapToGlobal(pos)); } void XYFitCurveDock::showFunctions() { QMenu menu; FunctionsWidget functions(&menu); connect(&functions, SIGNAL(functionSelected(QString)), this, SLOT(insertFunction(QString))); connect(&functions, SIGNAL(functionSelected(QString)), &menu, SLOT(close())); connect(&functions, SIGNAL(canceled()), &menu, SLOT(close())); - QWidgetAction* widgetAction = new QWidgetAction(this); + auto* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&functions); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbFunctions->width(), -menu.sizeHint().height()); menu.exec(uiGeneralTab.tbFunctions->mapToGlobal(pos)); } /*! * Update parameter by parsing expression * Only called for custom fit model */ void XYFitCurveDock::updateParameterList() { DEBUG("XYFitCurveDock::updateParameterList()"); // use current model function m_fitData.model = uiGeneralTab.teEquation->toPlainText(); ExpressionParser* parser = ExpressionParser::getInstance(); QStringList vars; // variables that are known vars << "x"; //TODO: others? m_fitData.paramNames = m_fitData.paramNamesUtf8 = parser->getParameter(m_fitData.model, vars); // if number of parameter changed int oldNumberOfParameter = m_fitData.paramStartValues.size(); int numberOfParameter = m_fitData.paramNames.size(); DEBUG(" old number of parameter: " << oldNumberOfParameter << " new number of parameter: " << numberOfParameter); if (numberOfParameter != oldNumberOfParameter) { m_fitData.paramStartValues.resize(numberOfParameter); m_fitData.paramFixed.resize(numberOfParameter); m_fitData.paramLowerLimits.resize(numberOfParameter); m_fitData.paramUpperLimits.resize(numberOfParameter); } if (numberOfParameter > oldNumberOfParameter) { for (int i = oldNumberOfParameter; i < numberOfParameter; ++i) { m_fitData.paramStartValues[i] = 1.0; m_fitData.paramFixed[i] = false; m_fitData.paramLowerLimits[i] = -std::numeric_limits::max(); m_fitData.paramUpperLimits[i] = std::numeric_limits::max(); } } parametersChanged(); } /*! * called when parameter names and/or start values for the model were changed * also called from parameter widget */ void XYFitCurveDock::parametersChanged() { DEBUG("XYFitCurveDock::parametersChanged() m_initializing = " << m_initializing); //parameter names were (probably) changed -> set the new names in EquationTextEdit uiGeneralTab.teEquation->setVariables(m_fitData.paramNames); if (m_initializing) return; enableRecalculate(); } void XYFitCurveDock::parametersValid(bool valid) { DEBUG("XYFitCurveDock::parametersValid() valid = " << valid); m_parametersValid = valid; } void XYFitCurveDock::showOptions() { QMenu menu; FitOptionsWidget w(&menu, &m_fitData, m_fitCurve); connect(&w, SIGNAL(finished()), &menu, SLOT(close())); connect(&w, SIGNAL(optionsChanged()), this, SLOT(enableRecalculate())); - QWidgetAction* widgetAction = new QWidgetAction(this); + auto* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&w); menu.addAction(widgetAction); menu.setTearOffEnabled(true); //menu.setWindowFlags(menu.windowFlags() & Qt::MSWindowsFixedSizeDialogHint); QPoint pos(-menu.sizeHint().width() + uiGeneralTab.pbOptions->width(), 0); menu.exec(uiGeneralTab.pbOptions->mapToGlobal(pos)); } void XYFitCurveDock::insertFunction(const QString& str) const { //TODO: not all function have only one argument! uiGeneralTab.teEquation->insertPlainText(str + "(x)"); } void XYFitCurveDock::insertConstant(const QString& str) const { uiGeneralTab.teEquation->insertPlainText(str); } void XYFitCurveDock::recalculateClicked() { DEBUG("XYFitCurveDock::recalculateClicked()"); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_fitData.degree = uiGeneralTab.sbDegree->value(); if (m_fitData.modelCategory == nsl_fit_model_custom) updateParameterList(); for (XYCurve* curve: m_curvesList) dynamic_cast(curve)->setFitData(m_fitData); m_fitCurve->recalculate(); //update fitParametersWidget if (m_fitData.useResults) { for (int i = 0; i < m_fitData.paramNames.size(); i++) m_fitData.paramStartValues[i] = m_fitCurve->fitResult().paramValues[i]; fitParametersWidget->setFitData(&m_fitData); } this->showFitResult(); uiGeneralTab.pbRecalculate->setEnabled(false); emit info(i18n("Fit status: %1", m_fitCurve->fitResult().status)); QApplication::restoreOverrideCursor(); DEBUG("XYFitCurveDock::recalculateClicked() DONE"); } void XYFitCurveDock::expressionChanged() { DEBUG("XYFitCurveDock::expressionChanged()"); if (m_initializing) return; // update parameter list for custom model if (m_fitData.modelCategory == nsl_fit_model_custom) updateParameterList(); enableRecalculate(); } void XYFitCurveDock::enableRecalculate() const { DEBUG("XYFitCurveDock::enableRecalculate()"); if (m_initializing || m_fitCurve == nullptr) return; //no fitting possible without the x- and y-data bool hasSourceData = false; if (m_fitCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX != nullptr && aspectY != nullptr); } else { hasSourceData = (m_fitCurve->dataSourceCurve() != nullptr); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData && m_parametersValid); // PREVIEW as soon as recalculate is enabled (does not need source data) if (m_parametersValid && m_fitData.previewEnabled) { DEBUG(" ENABLE EVALUATE AND PREVIEW"); // use recent fit data m_fitCurve->setFitData(m_fitData); // calculate fit function m_fitCurve->evaluate(true); } else { DEBUG(" EVALUATE PREVIEW DISABLED"); } } void XYFitCurveDock::resultCopySelection() { QTableWidget* tw{nullptr}; int currentTab = uiGeneralTab.twResults->currentIndex(); DEBUG("current tab = " << currentTab); if (currentTab == 0) tw = uiGeneralTab.twParameters; else if (currentTab == 1) tw = uiGeneralTab.twGoodness; else if (currentTab == 2) tw = uiGeneralTab.twLog; else return; const QTableWidgetSelectionRange& range = tw->selectedRanges().constFirst(); QString str; for (int i = 0; i < range.rowCount(); ++i) { if (i > 0) str += '\n'; for (int j = 0; j < range.columnCount(); ++j) { if (j > 0) str += '\t'; str += tw->item(range.topRow() + i, range.leftColumn() + j)->text(); } } str += '\n'; QApplication::clipboard()->setText(str); DEBUG(QApplication::clipboard()->text().toStdString()); } void XYFitCurveDock::resultCopyAll() { const XYFitCurve::FitResult& fitResult = m_fitCurve->fitResult(); int currentTab = uiGeneralTab.twResults->currentIndex(); QString str; if (currentTab == 0) { str = i18n("Parameters:") + '\n'; const int np = fitResult.paramValues.size(); for (int i = 0; i < np; i++) { if (m_fitData.paramFixed.at(i)) str += m_fitData.paramNamesUtf8.at(i) + QString(" = ") + QString::number(fitResult.paramValues.at(i)) + '\n'; else { str += m_fitData.paramNamesUtf8.at(i) + QString(" = ") + QString::number(fitResult.paramValues.at(i)) + UTF8_QSTRING("±") + QString::number(fitResult.errorValues.at(i)) + " (" + QString::number(100.*fitResult.errorValues.at(i)/std::abs(fitResult.paramValues.at(i)), 'g', 3) + " %)\n"; const double margin = fitResult.tdist_marginValues.at(i); QString tdistValueString; if (fitResult.tdist_tValues.at(i) < std::numeric_limits::max()) tdistValueString = QString::number(fitResult.tdist_tValues.at(i), 'g', 3); else tdistValueString = UTF8_QSTRING("∞"); str += " (" + i18n("t statistic:") + ' ' + tdistValueString + ", " + i18n("p value:") + ' ' + QString::number(fitResult.tdist_pValues.at(i), 'g', 3) + ", " + i18n("conf. interval:") + ' '; if (std::abs(fitResult.tdist_tValues.at(i)) < 1.e6) { str += QString::number(fitResult.paramValues.at(i) - margin) + " .. " + QString::number(fitResult.paramValues.at(i) + margin) + ")\n"; } else { str += i18n("too small"); } } } } else if (currentTab == 1) { str = i18n("Goodness of fit:") + '\n'; str += i18n("sum of squared residuals") + " (" + UTF8_QSTRING("χ²") + "): " + QString::number(fitResult.sse) + '\n'; if (fitResult.dof != 0) { str += i18n("reduced") + ' ' + UTF8_QSTRING("χ²") + ": " + QString::number(fitResult.rms) + '\n'; str += i18n("root mean square error") + " (RMSE): " + QString::number(fitResult.rsd) + '\n'; str += i18n("coefficient of determination") + " (" + UTF8_QSTRING("R²") + "): " + QString::number(fitResult.rsquare, 'g', 15) + '\n'; str += i18n("adj. coefficient of determination")+ " (" + UTF8_QSTRING("R̄²") + "): " + QString::number(fitResult.rsquareAdj, 'g', 15) + "\n\n"; str += i18n("P > ") + UTF8_QSTRING("χ²") + ": " + QString::number(fitResult.chisq_p, 'g', 3) + '\n'; str += i18n("F statistic") + ": " + QString::number(fitResult.fdist_F, 'g', 3) + '\n'; str += i18n("P > F") + ": " + QString::number(fitResult.fdist_p, 'g', 3) + '\n'; } str += i18n("mean absolute error:") + ' ' + QString::number(fitResult.mae) + '\n'; str += i18n("Akaike information criterion:") + ' ' + QString::number(fitResult.aic) + '\n'; str += i18n("Bayesian information criterion:") + ' ' + QString::number(fitResult.bic) + '\n'; } else if (currentTab == 2) { str = i18n("status:") + ' ' + fitResult.status + '\n'; str += i18n("iterations:") + ' ' + QString::number(fitResult.iterations) + '\n'; str += i18n("tolerance:") + ' ' + QString::number(m_fitData.eps) + '\n'; if (fitResult.elapsedTime > 1000) str += i18n("calculation time: %1 s", fitResult.elapsedTime/1000) + '\n'; else str += i18n("calculation time: %1 ms", fitResult.elapsedTime) + '\n'; str += i18n("degrees of freedom:") + ' ' + QString::number(fitResult.dof) + '\n'; str += i18n("number of parameters:") + ' ' + QString::number(fitResult.paramValues.size()) + '\n'; str += i18n("fit range:") + ' ' + QString::number(m_fitData.fitRange.first()) + " .. " + QString::number(m_fitData.fitRange.last()) + '\n'; str += i18n("Iterations:") + '\n'; for (const auto &s : m_fitData.paramNamesUtf8) str += s + '\t'; str += UTF8_QSTRING("χ²"); const QStringList iterations = fitResult.solverOutput.split(';'); for (const auto &s : iterations) if (!s.isEmpty()) str += '\n' + s; } QApplication::clipboard()->setText(str); DEBUG(QApplication::clipboard()->text().toStdString()); } void XYFitCurveDock::resultParametersContextMenuRequest(QPoint pos) { - QMenu *contextMenu = new QMenu; + auto* contextMenu = new QMenu; contextMenu->addAction(i18n("Copy Selection"), this, SLOT(resultCopySelection())); contextMenu->addAction(i18n("Copy All"), this, SLOT(resultCopyAll())); contextMenu->exec(uiGeneralTab.twParameters->mapToGlobal(pos)); } void XYFitCurveDock::resultGoodnessContextMenuRequest(QPoint pos) { - QMenu *contextMenu = new QMenu; + auto* contextMenu = new QMenu; contextMenu->addAction(i18n("Copy Selection"), this, SLOT(resultCopySelection())); contextMenu->addAction(i18n("Copy All"), this, SLOT(resultCopyAll())); contextMenu->exec(uiGeneralTab.twGoodness->mapToGlobal(pos)); } void XYFitCurveDock::resultLogContextMenuRequest(QPoint pos) { - QMenu *contextMenu = new QMenu; + auto* contextMenu = new QMenu; contextMenu->addAction(i18n("Copy Selection"), this, SLOT(resultCopySelection())); contextMenu->addAction(i18n("Copy All"), this, SLOT(resultCopyAll())); contextMenu->exec(uiGeneralTab.twLog->mapToGlobal(pos)); } /*! * show the result and details of fit */ void XYFitCurveDock::showFitResult() { DEBUG("XYFitCurveDock::showFitResult()"); //clear the previous result uiGeneralTab.twParameters->setRowCount(0); for (int row = 0; row < uiGeneralTab.twGoodness->rowCount(); ++row) uiGeneralTab.twGoodness->item(row, 2)->setText(""); for (int row = 0; row < uiGeneralTab.twLog->rowCount(); ++row) uiGeneralTab.twLog->item(row, 1)->setText(""); const XYFitCurve::FitResult& fitResult = m_fitCurve->fitResult(); if (!fitResult.available) { DEBUG("fit result not available"); return; } // Log uiGeneralTab.twLog->item(0, 1)->setText(fitResult.status); if (!fitResult.valid) { DEBUG("fit result not valid"); return; } uiGeneralTab.twLog->item(1, 1)->setText(QString::number(fitResult.iterations)); uiGeneralTab.twLog->item(2, 1)->setText(QString::number(m_fitData.eps)); if (fitResult.elapsedTime > 1000) uiGeneralTab.twLog->item(3, 1)->setText(QString::number(fitResult.elapsedTime/1000) + " s"); else uiGeneralTab.twLog->item(3, 1)->setText(QString::number(fitResult.elapsedTime) + " ms"); uiGeneralTab.twLog->item(4, 1)->setText(QString::number(fitResult.dof)); uiGeneralTab.twLog->item(5, 1)->setText(QString::number(fitResult.paramValues.size())); uiGeneralTab.twLog->item(6, 1)->setText(QString::number(m_fitData.fitRange.first()) + " .. " + QString::number(m_fitData.fitRange.last()) ); // show all iterations QString str; for (const auto &s : m_fitData.paramNamesUtf8) str += s + '\t'; str += UTF8_QSTRING("χ²"); const QStringList iterations = fitResult.solverOutput.split(';'); for (const auto &s : iterations) if (!s.isEmpty()) str += '\n' + s; uiGeneralTab.twLog->item(7, 1)->setText(str); uiGeneralTab.twLog->resizeRowsToContents(); // Parameters const int np = m_fitData.paramNames.size(); uiGeneralTab.twParameters->setRowCount(np); QStringList headerLabels; headerLabels << i18n("Name") << i18n("Value") << i18n("Error") << i18n("Error, %") << i18n("t statistic") << QLatin1String("P > |t|") << i18n("Conf. Interval"); uiGeneralTab.twParameters->setHorizontalHeaderLabels(headerLabels); for (int i = 0; i < np; i++) { const double paramValue = fitResult.paramValues.at(i); const double errorValue = fitResult.errorValues.at(i); - QTableWidgetItem* item = new QTableWidgetItem(m_fitData.paramNamesUtf8.at(i)); + auto* item = new QTableWidgetItem(m_fitData.paramNamesUtf8.at(i)); item->setBackground(QApplication::palette().color(QPalette::Window)); uiGeneralTab.twParameters->setItem(i, 0, item); item = new QTableWidgetItem(QString::number(paramValue)); uiGeneralTab.twParameters->setItem(i, 1, item); if (!m_fitData.paramFixed.at(i)) { if (!std::isnan(errorValue)) { item = new QTableWidgetItem(QString::number(errorValue, 'g', 6)); uiGeneralTab.twParameters->setItem(i, 2, item); item = new QTableWidgetItem(QString::number(100.*errorValue/std::abs(paramValue), 'g', 3)); uiGeneralTab.twParameters->setItem(i, 3, item); } else { item = new QTableWidgetItem(UTF8_QSTRING("∞")); uiGeneralTab.twParameters->setItem(i, 2, item); item = new QTableWidgetItem(UTF8_QSTRING("∞")); uiGeneralTab.twParameters->setItem(i, 3, item); } // t values QString tdistValueString; if (fitResult.tdist_tValues.at(i) < std::numeric_limits::max()) tdistValueString = QString::number(fitResult.tdist_tValues.at(i), 'g', 3); else tdistValueString = UTF8_QSTRING("∞"); item = new QTableWidgetItem(tdistValueString); uiGeneralTab.twParameters->setItem(i, 4, item); // p values const double p = fitResult.tdist_pValues.at(i); item = new QTableWidgetItem(QString::number(p, 'g', 3)); // color p values depending on value if (p > 0.05) item->setTextColor(QApplication::palette().color(QPalette::LinkVisited)); else if (p > 0.01) item->setTextColor(Qt::darkGreen); else if (p > 0.001) item->setTextColor(Qt::darkCyan); else if (p > 0.0001) item->setTextColor(QApplication::palette().color(QPalette::Link)); else item->setTextColor(QApplication::palette().color(QPalette::Highlight)); uiGeneralTab.twParameters->setItem(i, 5, item); // Conf. interval if (!std::isnan(errorValue)) { const double margin = fitResult.tdist_marginValues.at(i); if (fitResult.tdist_tValues.at(i) < 1.e6) item = new QTableWidgetItem(QString::number(paramValue - margin) + QLatin1String(" .. ") + QString::number(paramValue + margin)); else item = new QTableWidgetItem(i18n("too small")); uiGeneralTab.twParameters->setItem(i, 6, item); } } } // Goodness of fit uiGeneralTab.twGoodness->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); uiGeneralTab.twGoodness->item(0, 2)->setText(QString::number(fitResult.sse)); if (fitResult.dof != 0) { uiGeneralTab.twGoodness->item(1, 2)->setText(QString::number(fitResult.rms)); uiGeneralTab.twGoodness->item(2, 2)->setText(QString::number(fitResult.rsd)); uiGeneralTab.twGoodness->item(3, 2)->setText(QString::number(fitResult.rsquare, 'g', 15)); uiGeneralTab.twGoodness->item(4, 2)->setText(QString::number(fitResult.rsquareAdj, 'g', 15)); // chi^2 and F test p-values uiGeneralTab.twGoodness->item(5, 2)->setText(QString::number(fitResult.chisq_p, 'g', 3)); uiGeneralTab.twGoodness->item(6, 2)->setText(QString::number(fitResult.fdist_F, 'g', 3)); uiGeneralTab.twGoodness->item(7, 2)->setText(QString::number(fitResult.fdist_p, 'g', 3)); uiGeneralTab.twGoodness->item(9, 2)->setText(QString::number(fitResult.aic, 'g', 3)); uiGeneralTab.twGoodness->item(10, 2)->setText(QString::number(fitResult.bic, 'g', 3)); } uiGeneralTab.twGoodness->item(8, 2)->setText(QString::number(fitResult.mae)); //resize the table headers to fit the new content uiGeneralTab.twLog->resizeColumnsToContents(); uiGeneralTab.twParameters->resizeColumnsToContents(); //twGoodness doesn't have any header -> resize sections uiGeneralTab.twGoodness->resizeColumnToContents(0); uiGeneralTab.twGoodness->resizeColumnToContents(1); uiGeneralTab.twGoodness->resizeColumnToContents(2); //enable the "recalculate"-button if the source data was changed since the last fit uiGeneralTab.pbRecalculate->setEnabled(m_fitCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYFitCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYFitCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYFitCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYFitCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYFitCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYFitCurveDock::curveXErrorColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXErrorColumn, column); m_initializing = false; } void XYFitCurveDock::curveYErrorColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYErrorColumn, column); m_initializing = false; } /*! * called when fit data of fit curve changes */ void XYFitCurveDock::curveFitDataChanged(const XYFitCurve::FitData& fitData) { DEBUG("XYFitCurveDock::curveFitDataChanged()"); m_initializing = true; m_fitData = fitData; if (m_fitData.modelCategory != nsl_fit_model_custom) uiGeneralTab.cbModel->setCurrentIndex(m_fitData.modelType); uiGeneralTab.sbDegree->setValue(m_fitData.degree); m_initializing = false; } void XYFitCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp index 76c125bc8..2c300547e 100644 --- a/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp @@ -1,698 +1,698 @@ /*************************************************************************** File : XYFourierFilterCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for editing properties of Fourier filter curves ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "XYFourierFilterCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include /*! \class XYFourierFilterCurveDock \brief Provides a widget for editing the properties of the XYFourierFilterCurves (2D-curves defined by a Fourier filter) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYFourierFilterCurveDock::XYFourierFilterCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), m_filterCurve(nullptr) { //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYFourierFilterCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); - QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); + auto* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 2); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 2); for(int i=0; i < NSL_FILTER_TYPE_COUNT; i++) uiGeneralTab.cbType->addItem(i18n(nsl_filter_type_name[i])); for(int i=0; i < NSL_FILTER_FORM_COUNT; i++) uiGeneralTab.cbForm->addItem(i18n(nsl_filter_form_name[i])); for(int i=0; i < NSL_FILTER_CUTOFF_UNIT_COUNT; i++) { uiGeneralTab.cbUnit->addItem(i18n(nsl_filter_cutoff_unit_name[i])); uiGeneralTab.cbUnit2->addItem(i18n(nsl_filter_cutoff_unit_name[i])); } uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); - QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); + auto* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFourierFilterCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFourierFilterCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.cbForm, SIGNAL(currentIndexChanged(int)), this, SLOT(formChanged()) ); connect( uiGeneralTab.sbOrder, SIGNAL(valueChanged(int)), this, SLOT(orderChanged()) ); connect( uiGeneralTab.sbCutoff, SIGNAL(valueChanged(double)), this, SLOT(enableRecalculate()) ); connect( uiGeneralTab.sbCutoff2, SIGNAL(valueChanged(double)), this, SLOT(enableRecalculate()) ); connect( uiGeneralTab.cbUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged()) ); connect( uiGeneralTab.cbUnit2, SIGNAL(currentIndexChanged(int)), this, SLOT(unit2Changed()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); } void XYFourierFilterCurveDock::initGeneralTab() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1){ uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_filterCurve = dynamic_cast(m_curve); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_filterCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_filterCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_filterCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_filterCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_filterData.autoRange); uiGeneralTab.sbMin->setValue(m_filterData.xRange.first()); uiGeneralTab.sbMax->setValue(m_filterData.xRange.last()); this->autoRangeChanged(); uiGeneralTab.cbType->setCurrentIndex(m_filterData.type); this->typeChanged(); uiGeneralTab.cbForm->setCurrentIndex(m_filterData.form); this->formChanged(); uiGeneralTab.sbOrder->setValue((int)m_filterData.order); uiGeneralTab.cbUnit->setCurrentIndex(m_filterData.unit); this->unitChanged(); // after unit has set uiGeneralTab.sbCutoff->setValue(m_filterData.cutoff); uiGeneralTab.cbUnit2->setCurrentIndex(m_filterData.unit2); this->unit2Changed(); // after unit has set uiGeneralTab.sbCutoff2->setValue(m_filterData.cutoff2); this->showFilterResult(); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_filterCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_filterCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_filterCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_filterCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_filterCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_filterCurve, SIGNAL(filterDataChanged(XYFourierFilterCurve::FilterData)), this, SLOT(curveFilterDataChanged(XYFourierFilterCurve::FilterData))); connect(m_filterCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYFourierFilterCurveDock::setModel() { QList list; list<<"Folder"<<"Datapicker"<<"Worksheet"<<"CartesianPlot"<<"XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbDataSourceCurve->setModel(m_aspectTreeModel); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYFourierFilterCurveDock::setCurves(QList list) { m_initializing=true; m_curvesList=list; m_curve=list.first(); m_filterCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_filterData = m_filterCurve->filterData(); initGeneralTab(); initTabs(); m_initializing=false; } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYFourierFilterCurveDock::nameChanged(){ if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYFourierFilterCurveDock::commentChanged(){ if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYFourierFilterCurveDock::dataSourceTypeChanged(int index) { - XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; + auto type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYFourierFilterCurveDock::dataSourceCurveChanged(const QModelIndex& index) { - AbstractAspect* aspect = static_cast(index.internalPointer()); - XYCurve* dataSourceCurve = nullptr; + auto* aspect = static_cast(index.internalPointer()); + XYCurve* dataSourceCurve{}; if (aspect) dataSourceCurve = dynamic_cast(aspect); // update range of cutoff spin boxes (like a unit change) unitChanged(); unit2Changed(); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYFourierFilterCurveDock::xDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); // update range of cutoff spin boxes (like a unit change) unitChanged(); unit2Changed(); if (column != nullptr) { if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } } } void XYFourierFilterCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYFourierFilterCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_filterData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = nullptr; if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_filterCurve->xDataColumn(); else { if (m_filterCurve->dataSourceCurve()) xDataColumn = m_filterCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYFourierFilterCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_filterData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYFourierFilterCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_filterData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYFourierFilterCurveDock::typeChanged() { - nsl_filter_type type = (nsl_filter_type)uiGeneralTab.cbType->currentIndex(); + auto type = (nsl_filter_type)uiGeneralTab.cbType->currentIndex(); m_filterData.type = type; switch (type) { case nsl_filter_type_low_pass: case nsl_filter_type_high_pass: uiGeneralTab.lCutoff->setText(i18n("Cutoff:")); uiGeneralTab.lCutoff2->setVisible(false); uiGeneralTab.sbCutoff2->setVisible(false); uiGeneralTab.cbUnit2->setVisible(false); break; case nsl_filter_type_band_pass: case nsl_filter_type_band_reject: uiGeneralTab.lCutoff2->setVisible(true); uiGeneralTab.lCutoff->setText(i18n("Lower cutoff:")); uiGeneralTab.lCutoff2->setText(i18n("Upper cutoff:")); uiGeneralTab.sbCutoff2->setVisible(true); uiGeneralTab.cbUnit2->setVisible(true); break; //TODO /* case nsl_filter_type_threshold: uiGeneralTab.lCutoff->setText(i18n("Value:")); uiGeneralTab.lCutoff2->setVisible(false); uiGeneralTab.sbCutoff2->setVisible(false); uiGeneralTab.cbUnit2->setVisible(false); */ } enableRecalculate(); } void XYFourierFilterCurveDock::formChanged() { - nsl_filter_form form = (nsl_filter_form)uiGeneralTab.cbForm->currentIndex(); + auto form = (nsl_filter_form)uiGeneralTab.cbForm->currentIndex(); m_filterData.form = form; switch (form) { case nsl_filter_form_ideal: uiGeneralTab.sbOrder->setVisible(false); uiGeneralTab.lOrder->setVisible(false); break; case nsl_filter_form_butterworth: case nsl_filter_form_chebyshev_i: case nsl_filter_form_chebyshev_ii: case nsl_filter_form_legendre: case nsl_filter_form_bessel: uiGeneralTab.sbOrder->setVisible(true); uiGeneralTab.lOrder->setVisible(true); break; } enableRecalculate(); } void XYFourierFilterCurveDock::orderChanged() { m_filterData.order = (unsigned int)uiGeneralTab.sbOrder->value(); enableRecalculate(); } void XYFourierFilterCurveDock::unitChanged() { - nsl_filter_cutoff_unit unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit->currentIndex(); + auto unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit->currentIndex(); nsl_filter_cutoff_unit oldUnit = m_filterData.unit; double oldValue = uiGeneralTab.sbCutoff->value(); m_filterData.unit = unit; int n = 100; double f = 1.0; // sample frequency const AbstractColumn* xDataColumn = nullptr; if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_filterCurve->xDataColumn(); else { if (m_filterCurve->dataSourceCurve()) xDataColumn = m_filterCurve->dataSourceCurve()->xColumn(); } if (xDataColumn != nullptr) { n = xDataColumn->rowCount(); double range = xDataColumn->maximum() - xDataColumn->minimum(); f = (n-1)/range/2.; DEBUG(" n =" << n << " sample frequency =" << f); } switch (unit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff->setDecimals(6); uiGeneralTab.sbCutoff->setMaximum(f); uiGeneralTab.sbCutoff->setSingleStep(0.01*f); uiGeneralTab.sbCutoff->setSuffix(" Hz"); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff->setValue(oldValue*f); break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff->setValue(oldValue*f/n); break; } break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff->setDecimals(6); uiGeneralTab.sbCutoff->setMaximum(1.0); uiGeneralTab.sbCutoff->setSingleStep(0.01); uiGeneralTab.sbCutoff->setSuffix(""); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff->setValue(oldValue/f); break; case nsl_filter_cutoff_unit_fraction: break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff->setValue(oldValue/n); break; } break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff->setDecimals(0); uiGeneralTab.sbCutoff->setSingleStep(1); uiGeneralTab.sbCutoff->setMaximum(n); uiGeneralTab.sbCutoff->setSuffix(""); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff->setValue(oldValue*n/f); break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff->setValue(oldValue*n); break; case nsl_filter_cutoff_unit_index: break; } break; } enableRecalculate(); } void XYFourierFilterCurveDock::unit2Changed() { - nsl_filter_cutoff_unit unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit2->currentIndex(); + auto unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit2->currentIndex(); nsl_filter_cutoff_unit oldUnit = m_filterData.unit2; double oldValue = uiGeneralTab.sbCutoff2->value(); m_filterData.unit2 = unit; int n = 100; double f = 1.0; // sample frequency const AbstractColumn* xDataColumn = nullptr; if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_filterCurve->xDataColumn(); else { if (m_filterCurve->dataSourceCurve()) xDataColumn = m_filterCurve->dataSourceCurve()->xColumn(); } if (xDataColumn != nullptr) { n = xDataColumn->rowCount(); double range = xDataColumn->maximum() - xDataColumn->minimum(); f = (n-1)/range/2.; DEBUG(" n =" << n << " sample frequency =" << f); } switch (unit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff2->setDecimals(6); uiGeneralTab.sbCutoff2->setMaximum(f); uiGeneralTab.sbCutoff2->setSingleStep(0.01*f); uiGeneralTab.sbCutoff2->setSuffix(" Hz"); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff2->setValue(oldValue*f); break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff2->setValue(oldValue*f/n); break; } break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff2->setDecimals(6); uiGeneralTab.sbCutoff2->setMaximum(1.0); uiGeneralTab.sbCutoff2->setSingleStep(0.01); uiGeneralTab.sbCutoff2->setSuffix(""); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff2->setValue(oldValue/f); break; case nsl_filter_cutoff_unit_fraction: break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff2->setValue(oldValue/n); break; } break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff2->setDecimals(0); uiGeneralTab.sbCutoff2->setSingleStep(1); uiGeneralTab.sbCutoff2->setMaximum(n); uiGeneralTab.sbCutoff2->setSuffix(""); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff2->setValue(oldValue*n/f); break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff2->setValue(oldValue*n); break; case nsl_filter_cutoff_unit_index: break; } break; } enableRecalculate(); } void XYFourierFilterCurveDock::recalculateClicked() { m_filterData.cutoff = uiGeneralTab.sbCutoff->value(); m_filterData.cutoff2 = uiGeneralTab.sbCutoff2->value(); if ((m_filterData.type == nsl_filter_type_band_pass || m_filterData.type == nsl_filter_type_band_reject) && m_filterData.cutoff2 <= m_filterData.cutoff) { KMessageBox::sorry(this, i18n("The band width is <= 0 since lower cutoff value is not smaller than upper cutoff value. Please fix this."), i18n("band width <= 0") ); return; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setFilterData(m_filterData); uiGeneralTab.pbRecalculate->setEnabled(false); emit info(i18n("Fourier-Filter status: %1", m_filterCurve->filterResult().status)); QApplication::restoreOverrideCursor(); } void XYFourierFilterCurveDock::enableRecalculate() const { if (m_initializing) return; //no filtering possible without the x- and y-data bool hasSourceData = false; if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX!=nullptr && aspectY!=nullptr); } else { hasSourceData = (m_filterCurve->dataSourceCurve() != nullptr); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the filter */ void XYFourierFilterCurveDock::showFilterResult() { const XYFourierFilterCurve::FilterResult& filterResult = m_filterCurve->filterResult(); if (!filterResult.available) { uiGeneralTab.teResult->clear(); return; } QString str = i18n("status: %1", filterResult.status) + "
"; if (!filterResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (filterResult.elapsedTime>1000) str += i18n("calculation time: %1 s", QString::number(filterResult.elapsedTime/1000)) + "
"; else str += i18n("calculation time: %1 ms", QString::number(filterResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); //enable the "recalculate"-button if the source data was changed since the last filter uiGeneralTab.pbRecalculate->setEnabled(m_filterCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYFourierFilterCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYFourierFilterCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYFourierFilterCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYFourierFilterCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYFourierFilterCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYFourierFilterCurveDock::curveFilterDataChanged(const XYFourierFilterCurve::FilterData& filterData) { m_initializing = true; m_filterData = filterData; uiGeneralTab.cbType->setCurrentIndex(m_filterData.type); this->typeChanged(); this->showFilterResult(); m_initializing = false; } void XYFourierFilterCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp index c5e2c74ec..59c8a5961 100644 --- a/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp @@ -1,411 +1,411 @@ /*************************************************************************** File : XYFourierTransformCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for editing properties of Fourier transform curves ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "XYFourierTransformCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYFourierTransformCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include /*! \class XYFourierTransformCurveDock \brief Provides a widget for editing the properties of the XYFourierTransformCurves (2D-curves defined by a Fourier transform) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYFourierTransformCurveDock::XYFourierTransformCurveDock(QWidget *parent): XYCurveDock(parent), cbXDataColumn(nullptr), cbYDataColumn(nullptr), m_transformCurve(nullptr) { //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYFourierTransformCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); - QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); + auto* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 5, 2, 1, 2); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 6, 2, 1, 2); - for (int i=0; i < NSL_SF_WINDOW_TYPE_COUNT; i++) + for (int i = 0; i < NSL_SF_WINDOW_TYPE_COUNT; i++) uiGeneralTab.cbWindowType->addItem(i18n(nsl_sf_window_type_name[i])); - for (int i=0; i < NSL_DFT_RESULT_TYPE_COUNT; i++) + for (int i = 0; i < NSL_DFT_RESULT_TYPE_COUNT; i++) uiGeneralTab.cbType->addItem(i18n(nsl_dft_result_type_name[i])); - for (int i=0; i < NSL_DFT_XSCALE_COUNT; i++) + for (int i = 0; i < NSL_DFT_XSCALE_COUNT; i++) uiGeneralTab.cbXScale->addItem(i18n(nsl_dft_xscale_name[i])); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); - QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); + auto* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFourierTransformCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFourierTransformCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbWindowType, SIGNAL(currentIndexChanged(int)), this, SLOT(windowTypeChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.cbTwoSided, SIGNAL(stateChanged(int)), this, SLOT(twoSidedChanged()) ); connect( uiGeneralTab.cbShifted, SIGNAL(stateChanged(int)), this, SLOT(shiftedChanged()) ); connect( uiGeneralTab.cbXScale, SIGNAL(currentIndexChanged(int)), this, SLOT(xScaleChanged()) ); // connect( uiGeneralTab.pbOptions, SIGNAL(clicked()), this, SLOT(showOptions()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); } void XYFourierTransformCurveDock::initGeneralTab() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_transformCurve = dynamic_cast(m_curve); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_transformCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_transformCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_transformData.autoRange); uiGeneralTab.sbMin->setValue(m_transformData.xRange.first()); uiGeneralTab.sbMax->setValue(m_transformData.xRange.last()); this->autoRangeChanged(); uiGeneralTab.cbWindowType->setCurrentIndex(m_transformData.windowType); this->windowTypeChanged(); uiGeneralTab.cbType->setCurrentIndex(m_transformData.type); this->typeChanged(); uiGeneralTab.cbTwoSided->setChecked(m_transformData.twoSided); this->twoSidedChanged(); // show/hide shifted check box uiGeneralTab.cbShifted->setChecked(m_transformData.shifted); this->shiftedChanged(); uiGeneralTab.cbXScale->setCurrentIndex(m_transformData.xScale); this->xScaleChanged(); this->showTransformResult(); //enable the "recalculate"-button if the source data was changed since the last transform uiGeneralTab.pbRecalculate->setEnabled(m_transformCurve->isSourceDataChangedSinceLastRecalc()); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_transformCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_transformCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_transformCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_transformCurve, SIGNAL(transformDataChanged(XYFourierTransformCurve::TransformData)), this, SLOT(curveTransformDataChanged(XYFourierTransformCurve::TransformData))); connect(m_transformCurve, SIGNAL(sourceDataChangedSinceLastTransform()), this, SLOT(enableRecalculate())); } void XYFourierTransformCurveDock::setModel() { QList list; list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"<<"CantorWorksheet"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYFourierTransformCurveDock::setCurves(QList list) { m_initializing=true; m_curvesList=list; m_curve=list.first(); m_transformCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_transformData = m_transformCurve->transformData(); initGeneralTab(); initTabs(); m_initializing=false; } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYFourierTransformCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYFourierTransformCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYFourierTransformCurveDock::xDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); if (column != nullptr) { if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } } } void XYFourierTransformCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYFourierTransformCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_transformData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); m_transformCurve = dynamic_cast(m_curve); if (m_transformCurve->xDataColumn()) { uiGeneralTab.sbMin->setValue(m_transformCurve->xDataColumn()->minimum()); uiGeneralTab.sbMax->setValue(m_transformCurve->xDataColumn()->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYFourierTransformCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_transformData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYFourierTransformCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_transformData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYFourierTransformCurveDock::windowTypeChanged() { - nsl_sf_window_type windowType = (nsl_sf_window_type)uiGeneralTab.cbWindowType->currentIndex(); + auto windowType = (nsl_sf_window_type)uiGeneralTab.cbWindowType->currentIndex(); m_transformData.windowType = windowType; enableRecalculate(); } void XYFourierTransformCurveDock::typeChanged() { - nsl_dft_result_type type = (nsl_dft_result_type)uiGeneralTab.cbType->currentIndex(); + auto type = (nsl_dft_result_type)uiGeneralTab.cbType->currentIndex(); m_transformData.type = type; enableRecalculate(); } void XYFourierTransformCurveDock::twoSidedChanged() { bool twoSided = uiGeneralTab.cbTwoSided->isChecked(); m_transformData.twoSided = twoSided; if (twoSided) uiGeneralTab.cbShifted->setEnabled(true); else { uiGeneralTab.cbShifted->setEnabled(false); uiGeneralTab.cbShifted->setChecked(false); } enableRecalculate(); } void XYFourierTransformCurveDock::shiftedChanged() { bool shifted = uiGeneralTab.cbShifted->isChecked(); m_transformData.shifted = shifted; enableRecalculate(); } void XYFourierTransformCurveDock::xScaleChanged() { - nsl_dft_xscale xScale = (nsl_dft_xscale)uiGeneralTab.cbXScale->currentIndex(); + auto xScale = (nsl_dft_xscale)uiGeneralTab.cbXScale->currentIndex(); m_transformData.xScale = xScale; enableRecalculate(); } void XYFourierTransformCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setTransformData(m_transformData); uiGeneralTab.pbRecalculate->setEnabled(false); emit info(i18n("Fourier transformation status: %1", m_transformCurve->transformResult().status)); QApplication::restoreOverrideCursor(); } void XYFourierTransformCurveDock::enableRecalculate() const { if (m_initializing) return; //no transforming possible without the x- and y-data AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); bool data = (aspectX!=nullptr && aspectY!=nullptr); uiGeneralTab.pbRecalculate->setEnabled(data); } /*! * show the result and details of the transform */ void XYFourierTransformCurveDock::showTransformResult() { const XYFourierTransformCurve::TransformResult& transformResult = m_transformCurve->transformResult(); if (!transformResult.available) { uiGeneralTab.teResult->clear(); return; } QString str = i18n("status: %1", transformResult.status) + "
"; if (!transformResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (transformResult.elapsedTime>1000) str += i18n("calculation time: %1 s", QString::number(transformResult.elapsedTime/1000)) + "
"; else str += i18n("calculation time: %1 ms", QString::number(transformResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYFourierTransformCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYFourierTransformCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYFourierTransformCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYFourierTransformCurveDock::curveTransformDataChanged(const XYFourierTransformCurve::TransformData& transformData) { m_initializing = true; m_transformData = transformData; uiGeneralTab.cbType->setCurrentIndex(m_transformData.type); this->typeChanged(); this->showTransformResult(); m_initializing = false; } void XYFourierTransformCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp b/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp index 62ca52fb9..ce00879a5 100644 --- a/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp @@ -1,593 +1,593 @@ /*************************************************************************** File : XYSmoothCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 Alexander Semke (alexander.semke@web.de) Description : widget for editing properties of smooth curves ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "XYSmoothCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYSmoothCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include /*! \class XYSmoothCurveDock \brief Provides a widget for editing the properties of the XYSmoothCurves (2D-curves defined by an smooth) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYSmoothCurveDock::XYSmoothCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), m_smoothCurve(nullptr) { //hide the line connection type ui.cbLineType->setDisabled(true); //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYSmoothCurveDock::setupGeneral() { DEBUG("XYSmoothCurveDock::setupGeneral()"); QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); - QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); + auto* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 2); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 2); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 2); for (int i = 0; i < NSL_SMOOTH_TYPE_COUNT; i++) uiGeneralTab.cbType->addItem(i18n(nsl_smooth_type_name[i])); for (int i = 0; i < NSL_SMOOTH_WEIGHT_TYPE_COUNT; i++) uiGeneralTab.cbWeight->addItem(i18n(nsl_smooth_weight_type_name[i])); for (int i = 0; i < NSL_SMOOTH_PAD_MODE_COUNT; i++) uiGeneralTab.cbMode->addItem(i18n(nsl_smooth_pad_mode_name[i])); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); - QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); + auto* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYSmoothCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYSmoothCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.sbPoints, SIGNAL(valueChanged(int)), this, SLOT(pointsChanged()) ); connect( uiGeneralTab.cbWeight, SIGNAL(currentIndexChanged(int)), this, SLOT(weightChanged()) ); connect( uiGeneralTab.sbPercentile, SIGNAL(valueChanged(double)), this, SLOT(percentileChanged()) ); connect( uiGeneralTab.sbOrder, SIGNAL(valueChanged(int)), this, SLOT(orderChanged()) ); connect( uiGeneralTab.cbMode, SIGNAL(currentIndexChanged(int)), this, SLOT(modeChanged()) ); connect( uiGeneralTab.sbLeftValue, SIGNAL(valueChanged(double)), this, SLOT(valueChanged()) ); connect( uiGeneralTab.sbRightValue, SIGNAL(valueChanged(double)), this, SLOT(valueChanged()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); } void XYSmoothCurveDock::initGeneralTab() { DEBUG("XYSmoothCurveDock::initGeneralTab()"); //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_smoothCurve = dynamic_cast(m_curve); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_smoothCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_smoothCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_smoothCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_smoothCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_smoothData.autoRange); uiGeneralTab.sbMin->setValue(m_smoothData.xRange.first()); uiGeneralTab.sbMax->setValue(m_smoothData.xRange.last()); this->autoRangeChanged(); // update list of selectable types xDataColumnChanged(cbXDataColumn->currentModelIndex()); uiGeneralTab.cbType->setCurrentIndex(m_smoothData.type); typeChanged(); // needed, when type does not change uiGeneralTab.sbPoints->setValue((int)m_smoothData.points); uiGeneralTab.cbWeight->setCurrentIndex(m_smoothData.weight); uiGeneralTab.sbPercentile->setValue(m_smoothData.percentile); uiGeneralTab.sbOrder->setValue((int)m_smoothData.order); uiGeneralTab.cbMode->setCurrentIndex(m_smoothData.mode); modeChanged(); // needed, when mode does not change uiGeneralTab.sbLeftValue->setValue(m_smoothData.lvalue); uiGeneralTab.sbRightValue->setValue(m_smoothData.rvalue); valueChanged(); this->showSmoothResult(); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_smoothCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_smoothCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_smoothCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_smoothCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_smoothCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_smoothCurve, SIGNAL(smoothDataChanged(XYSmoothCurve::SmoothData)), this, SLOT(curveSmoothDataChanged(XYSmoothCurve::SmoothData))); connect(m_smoothCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYSmoothCurveDock::setModel() { QList list; list<<"Folder"<<"Datapicker"<<"Worksheet"<<"CartesianPlot"<<"XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"<<"CantorWorksheet"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYSmoothCurveDock::setCurves(QList list) { DEBUG("XYSmoothCurveDock::setCurves()"); m_initializing=true; m_curvesList=list; m_curve=list.first(); m_smoothCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_smoothData = m_smoothCurve->smoothData(); initGeneralTab(); initTabs(); m_initializing=false; //hide the "skip gaps" option after the curves were set ui.lLineSkipGaps->hide(); ui.chkLineSkipGaps->hide(); } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYSmoothCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYSmoothCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYSmoothCurveDock::dataSourceTypeChanged(int index) { - XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; + auto type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYSmoothCurveDock::dataSourceCurveChanged(const QModelIndex& index) { - AbstractAspect* aspect = static_cast(index.internalPointer()); - XYCurve* dataSourceCurve = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* dataSourceCurve = dynamic_cast(aspect); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYSmoothCurveDock::xDataColumnChanged(const QModelIndex& index) { - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); // disable types that need more data points if (column != nullptr) { if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } unsigned int n = 0; for (int row = 0; row < column->rowCount(); row++) if (!std::isnan(column->valueAt(row)) && !column->isMasked(row)) n++; // set maximum of sbPoints to number of columns uiGeneralTab.sbPoints->setMaximum((int)n); } } void XYSmoothCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; - AbstractAspect* aspect = static_cast(index.internalPointer()); - AbstractColumn* column = dynamic_cast(aspect); + auto* aspect = static_cast(index.internalPointer()); + auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYSmoothCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_smoothData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = nullptr; if (m_smoothCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_smoothCurve->xDataColumn(); else { if (m_smoothCurve->dataSourceCurve()) xDataColumn = m_smoothCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYSmoothCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_smoothData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYSmoothCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_smoothData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYSmoothCurveDock::typeChanged() { - nsl_smooth_type type = (nsl_smooth_type)uiGeneralTab.cbType->currentIndex(); + auto type = (nsl_smooth_type)uiGeneralTab.cbType->currentIndex(); m_smoothData.type = type; - const QStandardItemModel* model = qobject_cast(uiGeneralTab.cbMode->model()); + const auto* model = qobject_cast(uiGeneralTab.cbMode->model()); QStandardItem* pad_interp_item = model->item(nsl_smooth_pad_interp); if (type == nsl_smooth_type_moving_average || type == nsl_smooth_type_moving_average_lagged) { uiGeneralTab.lWeight->show(); uiGeneralTab.cbWeight->show(); // disable interp pad model for MA and MAL pad_interp_item->setFlags(pad_interp_item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); } else { uiGeneralTab.lWeight->hide(); uiGeneralTab.cbWeight->hide(); pad_interp_item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); } if (type == nsl_smooth_type_moving_average_lagged) { uiGeneralTab.sbPoints->setSingleStep(1); uiGeneralTab.sbPoints->setMinimum(2); uiGeneralTab.lRightValue->hide(); uiGeneralTab.sbRightValue->hide(); } else { uiGeneralTab.sbPoints->setSingleStep(2); uiGeneralTab.sbPoints->setMinimum(3); if (m_smoothData.mode == nsl_smooth_pad_constant) { uiGeneralTab.lRightValue->show(); uiGeneralTab.sbRightValue->show(); } } if (type == nsl_smooth_type_percentile) { uiGeneralTab.lPercentile->show(); uiGeneralTab.sbPercentile->show(); // disable interp pad model for MA and MAL pad_interp_item->setFlags(pad_interp_item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); } else { uiGeneralTab.lPercentile->hide(); uiGeneralTab.sbPercentile->hide(); } if (type == nsl_smooth_type_savitzky_golay) { uiGeneralTab.lOrder->show(); uiGeneralTab.sbOrder->show(); } else { uiGeneralTab.lOrder->hide(); uiGeneralTab.sbOrder->hide(); } enableRecalculate(); } void XYSmoothCurveDock::pointsChanged() { m_smoothData.points = (unsigned int)uiGeneralTab.sbPoints->value(); // set maximum order uiGeneralTab.sbOrder->setMaximum((int)m_smoothData.points - 1); enableRecalculate(); } void XYSmoothCurveDock::weightChanged() { m_smoothData.weight = (nsl_smooth_weight_type)uiGeneralTab.cbWeight->currentIndex(); enableRecalculate(); } void XYSmoothCurveDock::percentileChanged() { m_smoothData.percentile = uiGeneralTab.sbPercentile->value(); enableRecalculate(); } void XYSmoothCurveDock::orderChanged() { m_smoothData.order = (unsigned int)uiGeneralTab.sbOrder->value(); enableRecalculate(); } void XYSmoothCurveDock::modeChanged() { m_smoothData.mode = (nsl_smooth_pad_mode)(uiGeneralTab.cbMode->currentIndex()); if (m_smoothData.mode == nsl_smooth_pad_constant) { uiGeneralTab.lLeftValue->show(); uiGeneralTab.sbLeftValue->show(); if (m_smoothData.type == nsl_smooth_type_moving_average_lagged) { uiGeneralTab.lRightValue->hide(); uiGeneralTab.sbRightValue->hide(); } else { uiGeneralTab.lRightValue->show(); uiGeneralTab.sbRightValue->show(); } } else { uiGeneralTab.lLeftValue->hide(); uiGeneralTab.sbLeftValue->hide(); uiGeneralTab.lRightValue->hide(); uiGeneralTab.sbRightValue->hide(); } enableRecalculate(); } void XYSmoothCurveDock::valueChanged() { m_smoothData.lvalue = uiGeneralTab.sbLeftValue->value(); m_smoothData.rvalue = uiGeneralTab.sbRightValue->value(); enableRecalculate(); } void XYSmoothCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setSmoothData(m_smoothData); uiGeneralTab.pbRecalculate->setEnabled(false); emit info(i18n("Smoothing status: %1", m_smoothCurve->smoothResult().status)); QApplication::restoreOverrideCursor(); } void XYSmoothCurveDock::enableRecalculate() const { if (m_initializing) return; //no smoothing possible without the x- and y-data bool hasSourceData = false; if (m_smoothCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX!=nullptr && aspectY!=nullptr); } else { hasSourceData = (m_smoothCurve->dataSourceCurve() != nullptr); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the smooth */ void XYSmoothCurveDock::showSmoothResult() { const XYSmoothCurve::SmoothResult& smoothResult = m_smoothCurve->smoothResult(); if (!smoothResult.available) { uiGeneralTab.teResult->clear(); return; } //const XYSmoothCurve::SmoothData& smoothData = m_smoothCurve->smoothData(); QString str = i18n("status: %1", smoothResult.status) + "
"; if (!smoothResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (smoothResult.elapsedTime>1000) str += i18n("calculation time: %1 s", QString::number(smoothResult.elapsedTime/1000)) + "
"; else str += i18n("calculation time: %1 ms", QString::number(smoothResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); //enable the "recalculate"-button if the source data was changed since the last smooth uiGeneralTab.pbRecalculate->setEnabled(m_smoothCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYSmoothCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYSmoothCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYSmoothCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYSmoothCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYSmoothCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYSmoothCurveDock::curveSmoothDataChanged(const XYSmoothCurve::SmoothData& smoothData) { m_initializing = true; m_smoothData = smoothData; uiGeneralTab.cbType->setCurrentIndex(m_smoothData.type); this->showSmoothResult(); m_initializing = false; } void XYSmoothCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/matrix/MatrixFunctionDialog.cpp b/src/kdefrontend/matrix/MatrixFunctionDialog.cpp index 9cfd21bcd..df6ad5861 100644 --- a/src/kdefrontend/matrix/MatrixFunctionDialog.cpp +++ b/src/kdefrontend/matrix/MatrixFunctionDialog.cpp @@ -1,255 +1,255 @@ /*************************************************************************** File : MatrixFunctionDialog.cpp Project : LabPlot Description : Dialog for generating matrix values from a mathematical function ------------------------------------------------------------------------ Copyright : (C) 2015-2016 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "MatrixFunctionDialog.h" #include "backend/lib/macros.h" #include "backend/matrix/Matrix.h" #include "kdefrontend/widgets/ConstantsWidget.h" #include "kdefrontend/widgets/FunctionsWidget.h" extern "C" { #include "backend/gsl/parser.h" } #include #include #include #include #include #include #include #ifndef NDEBUG #include #endif /*! \class MatrixFunctionDialog \brief Dialog for generating matrix values from a mathematical function. \ingroup kdefrontend */ MatrixFunctionDialog::MatrixFunctionDialog(Matrix* m, QWidget* parent) : QDialog(parent), m_matrix(m) { Q_ASSERT(m_matrix); setWindowTitle(i18nc("@title:window", "Function Values")); ui.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui.tbConstants->setIcon( QIcon::fromTheme("labplot-format-text-symbol") ); ui.tbFunctions->setIcon( QIcon::fromTheme("preferences-desktop-font") ); QStringList vars; vars << "x" << "y"; ui.teEquation->setVariables(vars); ui.teEquation->setFocus(); ui.teEquation->setMaximumHeight(QLineEdit().sizeHint().height()*2); QString info = '[' + QString::number(m_matrix->xStart()) + ", " + QString::number(m_matrix->xEnd()) + "], " + i18np("%1 value", "%1 values", m_matrix->columnCount()); ui.lXInfo->setText(info); info = '[' + QString::number(m_matrix->yStart()) + ", " + QString::number(m_matrix->yEnd()) + "], " + i18np("%1 value", "%1 values", m_matrix->rowCount()); ui.lYInfo->setText(info); ui.teEquation->setPlainText(m_matrix->formula()); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.gridLayout_2->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &MatrixFunctionDialog::close); connect(btnBox, &QDialogButtonBox::accepted, this, &MatrixFunctionDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &MatrixFunctionDialog::reject); m_okButton->setText(i18n("&Generate")); m_okButton->setToolTip(i18n("Generate function values")); connect(ui.teEquation, &ExpressionTextEdit::expressionChanged, this, &MatrixFunctionDialog::checkValues); connect(ui.tbConstants, &QToolButton::clicked, this, &MatrixFunctionDialog::showConstants); connect(ui.tbFunctions, &QToolButton::clicked, this, &MatrixFunctionDialog::showFunctions); connect(m_okButton, &QPushButton::clicked, this, &MatrixFunctionDialog::generate); resize(QSize(300,0).expandedTo(minimumSize())); } void MatrixFunctionDialog::checkValues() { if (!ui.teEquation->isValid()) { m_okButton->setEnabled(false); return; } m_okButton->setEnabled(true); } void MatrixFunctionDialog::showConstants() { QMenu menu; ConstantsWidget constants(&menu); connect(&constants, &ConstantsWidget::constantSelected, this, &MatrixFunctionDialog::insertConstant); connect(&constants, &ConstantsWidget::constantSelected, &menu, &QMenu::close); connect(&constants, &ConstantsWidget::canceled, &menu, &QMenu::close); - QWidgetAction* widgetAction = new QWidgetAction(this); + auto* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&constants); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbConstants->width(),-menu.sizeHint().height()); menu.exec(ui.tbConstants->mapToGlobal(pos)); } void MatrixFunctionDialog::showFunctions() { QMenu menu; FunctionsWidget functions(&menu); connect(&functions, &FunctionsWidget::functionSelected, this, &MatrixFunctionDialog::insertFunction); connect(&functions, &FunctionsWidget::functionSelected, &menu, &QMenu::close); connect(&functions, &FunctionsWidget::canceled, &menu, &QMenu::close); - QWidgetAction* widgetAction = new QWidgetAction(this); + auto* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&functions); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbFunctions->width(),-menu.sizeHint().height()); menu.exec(ui.tbFunctions->mapToGlobal(pos)); } void MatrixFunctionDialog::insertFunction(const QString& str) { //TODO: not all functions have only one argument ui.teEquation->insertPlainText(str + "(x)"); } void MatrixFunctionDialog::insertConstant(const QString& str) { ui.teEquation->insertPlainText(str); } /* task class for parallel fill (not used) */ class GenerateValueTask : public QRunnable { public: GenerateValueTask(int startCol, int endCol, QVector>& matrixData, double xStart, double yStart, double xStep, double yStep, char* func): m_startCol(startCol), m_endCol(endCol), m_matrixData(matrixData), m_xStart(xStart), m_yStart(yStart), m_xStep(xStep), m_yStep(yStep), m_func(func) { }; void run() override { const int rows = m_matrixData[m_startCol].size(); double x = m_xStart; double y = m_yStart; DEBUG("FILL col"<name())); //TODO: data types - QVector>* new_data = static_cast>*>(m_matrix->data()); + auto* new_data = static_cast>*>(m_matrix->data()); QByteArray funcba = ui.teEquation->toPlainText().toLocal8Bit(); char* func = funcba.data(); // check if rows or cols == 1 double diff = m_matrix->xEnd() - m_matrix->xStart(); double xStep = 0.0; if (m_matrix->columnCount() > 1) xStep = diff/double(m_matrix->columnCount() - 1); diff = m_matrix->yEnd() - m_matrix->yStart(); double yStep = 0.0; if (m_matrix->rowCount() > 1) yStep = diff/double(m_matrix->rowCount() - 1); #ifndef NDEBUG QElapsedTimer timer; timer.start(); #endif //TODO: too slow because every parser thread needs an own symbol_table // idea: use pool->maxThreadCount() symbol tables and reuse them? /* double yStart = m_matrix->yStart(); const int cols = m_matrix->columnCount(); QThreadPool* pool = QThreadPool::globalInstance(); int range = ceil(double(cols)/pool->maxThreadCount()); DEBUG("Starting" << pool->maxThreadCount() << "threads. cols =" << cols << ": range =" << range); for (int i = 0; i < pool->maxThreadCount(); ++i) { const int start = i*range; int end = (i+1)*range; if (end > cols) end = cols; qDebug() << "start/end: " << start << end; const double xStart = m_matrix->xStart() + xStep*start; GenerateValueTask* task = new GenerateValueTask(start, end, new_data, xStart, yStart, xStep, yStep, func); task->setAutoDelete(false); pool->start(task); } pool->waitForDone(); */ double x = 0, y = 0; parser_var vars[] = {{"x", x}, {"y", y}}; for (int col = 0; col < m_matrix->columnCount(); ++col) { vars[0].value = x; for (int row = 0; row < m_matrix->rowCount(); ++row) { vars[1].value = y; (new_data->operator[](col))[row] = parse_with_vars(func, vars, 2); y += yStep; } y = m_matrix->yStart(); x += xStep; } // Timing DEBUG("elapsed time =" << timer.elapsed() << "ms"); m_matrix->setFormula(ui.teEquation->toPlainText()); m_matrix->setData(new_data); m_matrix->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp b/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp index cb2fd4199..f80809664 100644 --- a/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp @@ -1,367 +1,367 @@ /*************************************************************************** File : FunctionValuesDialog.cpp Project : LabPlot Description : Dialog for generating values from a mathematical function -------------------------------------------------------------------- Copyright : (C) 2014-2015 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "FunctionValuesDialog.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" #include "backend/gsl/ExpressionParser.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/widgets/ConstantsWidget.h" #include "kdefrontend/widgets/FunctionsWidget.h" #include #include #include #include #include #include #include #include /*! \class FunctionValuesDialog \brief Dialog for generating values from a mathematical function. \ingroup kdefrontend */ FunctionValuesDialog::FunctionValuesDialog(Spreadsheet* s, QWidget* parent) : QDialog(parent), m_spreadsheet(s) { Q_ASSERT(s != nullptr); setWindowTitle(i18nc("@title:window", "Function Values")); ui.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui.tbConstants->setIcon( QIcon::fromTheme("labplot-format-text-symbol") ); ui.tbConstants->setIcon( QIcon::fromTheme("format-text-symbol") ); ui.tbFunctions->setIcon( QIcon::fromTheme("preferences-desktop-font") ); ui.teEquation->setMaximumHeight(QLineEdit().sizeHint().height()*2); ui.teEquation->setFocus(); m_topLevelClasses<<"Folder"<<"Workbook"<<"Spreadsheet"<<"FileDataSource"<<"Column"; m_selectableClasses<<"Column"; // needed for buggy compiler #if __cplusplus < 201103L m_aspectTreeModel = std::auto_ptr(new AspectTreeModel(m_spreadsheet->project())); #else m_aspectTreeModel = std::unique_ptr(new AspectTreeModel(m_spreadsheet->project())); #endif m_aspectTreeModel->setSelectableAspects(m_selectableClasses); m_aspectTreeModel->enableNumericColumnsOnly(true); m_aspectTreeModel->enableNonEmptyNumericColumnsOnly(true); ui.bAddVariable->setIcon(QIcon::fromTheme("list-add")); ui.bAddVariable->setToolTip(i18n("Add new variable")); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.verticalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); connect(btnBox, &QDialogButtonBox::accepted, this, &FunctionValuesDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &FunctionValuesDialog::reject); m_okButton->setText(i18n("&Generate")); m_okButton->setToolTip(i18n("Generate function values")); connect( ui.bAddVariable, SIGNAL(pressed()), this, SLOT(addVariable()) ); connect( ui.teEquation, SIGNAL(expressionChanged()), this, SLOT(checkValues()) ); connect( ui.tbConstants, SIGNAL(clicked()), this, SLOT(showConstants()) ); connect( ui.tbFunctions, SIGNAL(clicked()), this, SLOT(showFunctions()) ); connect(m_okButton, &QPushButton::clicked, this, &FunctionValuesDialog::generate); //restore saved settings if available KConfigGroup conf(KSharedConfig::openConfig(), "FunctionValuesDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize(QSize(300, 0).expandedTo(minimumSize())); } FunctionValuesDialog::~FunctionValuesDialog() { KConfigGroup conf(KSharedConfig::openConfig(), "FunctionValuesDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void FunctionValuesDialog::setColumns(QVector columns) { m_columns = columns; ui.teEquation->setPlainText(m_columns.first()->formula()); const QStringList& variableNames = m_columns.first()->formulaVariableNames(); if (!variableNames.size()) { //no formula was used for this column -> add the first variable "x" addVariable(); m_variableNames[0]->setText("x"); } else { //formula and variables are available const QStringList& columnPathes = m_columns.first()->formulaVariableColumnPathes(); //add all available variables and select the corresponding columns const QVector cols = m_spreadsheet->project()->children("Column", AbstractAspect::Recursive); for (int i = 0; i < variableNames.size(); ++i) { addVariable(); m_variableNames[i]->setText(variableNames.at(i)); for (const auto* aspect : cols) { if (aspect->path() == columnPathes.at(i)) { - const AbstractColumn* column = dynamic_cast(aspect); + const auto* column = dynamic_cast(aspect); if (column) m_variableDataColumns[i]->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(column)); else m_variableDataColumns[i]->setCurrentModelIndex(QModelIndex()); break; } } } } } /*! check the user input and enables/disables the Ok-button depending on the correctness of the input */ void FunctionValuesDialog::checkValues() { //check whether the formula syntax is correct if (!ui.teEquation->isValid()) { m_okButton->setEnabled(false); return; } //check whether for the variables where a name was provided also a column was selected. for (int i = 0; i < m_variableDataColumns.size(); ++i) { if (m_variableNames.at(i)->text().simplified().isEmpty()) continue; TreeViewComboBox* cb = m_variableDataColumns.at(i); AbstractAspect* aspect = static_cast(cb->currentModelIndex().internalPointer()); if (!aspect) { m_okButton->setEnabled(false); return; } /* Column* column = dynamic_cast(aspect); DEBUG("row count = " << (static_cast* >(column->data()))->size()); if (!column || column->rowCount() < 1) { m_okButton->setEnabled(false); //Warning: x column is empty return; } */ } m_okButton->setEnabled(true); } void FunctionValuesDialog::showConstants() { QMenu menu; ConstantsWidget constants(&menu); connect(&constants, SIGNAL(constantSelected(QString)), this, SLOT(insertConstant(QString))); connect(&constants, SIGNAL(constantSelected(QString)), &menu, SLOT(close())); connect(&constants, SIGNAL(canceled()), &menu, SLOT(close())); - QWidgetAction* widgetAction = new QWidgetAction(this); + auto* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&constants); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbConstants->width(),-menu.sizeHint().height()); menu.exec(ui.tbConstants->mapToGlobal(pos)); } void FunctionValuesDialog::showFunctions() { QMenu menu; FunctionsWidget functions(&menu); connect(&functions, SIGNAL(functionSelected(QString)), this, SLOT(insertFunction(QString))); connect(&functions, SIGNAL(functionSelected(QString)), &menu, SLOT(close())); connect(&functions, SIGNAL(canceled()), &menu, SLOT(close())); - QWidgetAction* widgetAction = new QWidgetAction(this); + auto* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&functions); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbFunctions->width(),-menu.sizeHint().height()); menu.exec(ui.tbFunctions->mapToGlobal(pos)); } void FunctionValuesDialog::insertFunction(const QString& str) { //TODO: not all functions have only one argument ui.teEquation->insertPlainText(str + "(x)"); } void FunctionValuesDialog::insertConstant(const QString& str) { ui.teEquation->insertPlainText(str); } void FunctionValuesDialog::addVariable() { - QGridLayout* layout = dynamic_cast(ui.frameVariables->layout()); + auto* layout = dynamic_cast(ui.frameVariables->layout()); int row = m_variableNames.size(); //text field for the variable name - QLineEdit* le = new QLineEdit(); + auto* le = new QLineEdit(); le->setMaximumWidth(30); connect(le, SIGNAL(textChanged(QString)), this, SLOT(variableNameChanged())); layout->addWidget(le, row, 0, 1, 1); m_variableNames << le; //label for the "="-sign - QLabel* l = new QLabel("="); + auto* l = new QLabel("="); layout->addWidget(l, row, 1, 1, 1); m_variableLabels << l; //combo box for the data column - TreeViewComboBox* cb = new TreeViewComboBox(); + auto* cb = new TreeViewComboBox(); cb->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); connect( cb, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(checkValues()) ); layout->addWidget(cb, row, 2, 1, 1); m_variableDataColumns << cb; cb->setTopLevelClasses(m_topLevelClasses); cb->setModel(m_aspectTreeModel.get()); cb->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(m_spreadsheet->column(0))); //move the add-button to the next row layout->removeWidget(ui.bAddVariable); layout->addWidget(ui.bAddVariable, row+1,3, 1, 1); //add delete-button for the just added variable if (row != 0) { - QToolButton* b = new QToolButton(); + auto* b = new QToolButton(); b->setIcon(QIcon::fromTheme("list-remove")); b->setToolTip(i18n("Delete variable")); layout->addWidget(b, row, 3, 1, 1); m_variableDeleteButtons<setText(i18n("Variables:")); //TODO: adjust the tab-ordering after new widgets were added } void FunctionValuesDialog::deleteVariable() { QObject* ob = QObject::sender(); int index = m_variableDeleteButtons.indexOf(qobject_cast(ob)) ; delete m_variableNames.takeAt(index+1); delete m_variableLabels.takeAt(index+1); delete m_variableDataColumns.takeAt(index+1); delete m_variableDeleteButtons.takeAt(index); variableNameChanged(); checkValues(); //adjust the layout resize( QSize(width(),0).expandedTo(minimumSize()) ); m_variableNames.size() > 1 ? ui.lVariable->setText(i18n("Variables:")) : ui.lVariable->setText(i18n("Variable:")); //TODO: adjust the tab-ordering after some widgets were deleted } void FunctionValuesDialog::variableNameChanged() { QStringList vars; QString text; for (auto* varName : m_variableNames) { QString name = varName->text().simplified(); if (!name.isEmpty()) { vars << name; if (text.isEmpty()) { text += name; } else { text += ", " + name; } } } if (!text.isEmpty()) text = "f(" + text + ") = "; else text = "f = "; ui.lFunction->setText(text); ui.teEquation->setVariables(vars); } void FunctionValuesDialog::generate() { Q_ASSERT(m_spreadsheet); WAIT_CURSOR; m_spreadsheet->beginMacro(i18np("%1: fill column with function values", "%1: fill columns with function values", m_spreadsheet->name(), m_columns.size())); //determine variable names and the data vectors of the specified columns QStringList variableNames; QStringList columnPathes; QVector*> xVectors; QVector xColumns; int maxRowCount = m_spreadsheet->rowCount(); for (int i = 0; i < m_variableNames.size(); ++i) { variableNames << m_variableNames.at(i)->text().simplified(); AbstractAspect* aspect = static_cast(m_variableDataColumns.at(i)->currentModelIndex().internalPointer()); Q_ASSERT(aspect); - Column* column = dynamic_cast(aspect); + auto* column = dynamic_cast(aspect); Q_ASSERT(column); columnPathes << column->path(); xColumns << column; xVectors << static_cast* >(column->data()); if (column->rowCount() > maxRowCount) maxRowCount = column->rowCount(); } //resize the spreadsheet if one of the data vectors from other spreadsheet(s) has more elements then the current spreadsheet. if (m_spreadsheet->rowCount() < maxRowCount) m_spreadsheet->setRowCount(maxRowCount); //create new vector for storing the calculated values //the vectors with the variable data can be smaller then the result vector. So, not all values in the result vector might get initialized. //->"clean" the result vector first QVector new_data(maxRowCount); - for (auto d : new_data) + for (auto& d : new_data) d = NAN; //evaluate the expression for f(x_1, x_2, ...) and write the calculated values into a new vector. ExpressionParser* parser = ExpressionParser::getInstance(); const QString& expression = ui.teEquation->toPlainText(); parser->evaluateCartesian(expression, variableNames, xVectors, &new_data); //set the new values and store the expression, variable names and the used data columns for (auto* col : m_columns) { col->setFormula(expression, variableNames, columnPathes); col->replaceValues(0, new_data); } m_spreadsheet->endMacro(); RESET_CURSOR; }