diff --git a/src/kdefrontend/datasources/DatabaseManagerDialog.cpp b/src/kdefrontend/datasources/DatabaseManagerDialog.cpp index a41a2ea10..fe5cd77ca 100644 --- a/src/kdefrontend/datasources/DatabaseManagerDialog.cpp +++ b/src/kdefrontend/datasources/DatabaseManagerDialog.cpp @@ -1,92 +1,92 @@ /*************************************************************************** File : DatabaseManagerDialog.cc Project : LabPlot Description : dialog for managing database connections -------------------------------------------------------------------- - Copyright : (C) 2016-2017 Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2016-2019 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 "DatabaseManagerDialog.h" #include "DatabaseManagerWidget.h" #include #include #include #include -#include +#include /*! \class DatabaseManagerDialog \brief dialog for managing database connections \ingroup kdefrontend */ DatabaseManagerDialog::DatabaseManagerDialog(QWidget* parent, const QString& conn) : QDialog(parent), mainWidget(new DatabaseManagerWidget(this, conn)) { setWindowIcon(QIcon::fromTheme("network-server-database")); setWindowTitle(i18nc("@title:window", "SQL Database Connections")); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); auto* layout = new QVBoxLayout(this); layout->addWidget(mainWidget); layout->addWidget(buttonBox); connect(mainWidget, &DatabaseManagerWidget::changed, this, &DatabaseManagerDialog::changed); connect(buttonBox->button(QDialogButtonBox::Ok),&QPushButton::clicked, this, &DatabaseManagerDialog::save); connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &DatabaseManagerDialog::close); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - QTimer::singleShot(0, this, &DatabaseManagerDialog::loadSettings); -} - -void DatabaseManagerDialog::loadSettings() { - //restore saved settings - QApplication::processEvents(QEventLoop::AllEvents, 0); + //restore saved settings if available + create(); // ensure there's a window created KConfigGroup conf(KSharedConfig::openConfig(), "DatabaseManagerDialog"); - KWindowConfig::restoreWindowSize(windowHandle(), conf); + if (conf.exists()) { + KWindowConfig::restoreWindowSize(windowHandle(), conf); + resize(windowHandle()->size()); // workaround for QTBUG-40584 + } else + resize(QSize(0, 0).expandedTo(minimumSize())); } QString DatabaseManagerDialog::connection() const { return mainWidget->connection(); } DatabaseManagerDialog::~DatabaseManagerDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "DatabaseManagerDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void DatabaseManagerDialog::changed() { setWindowTitle(i18nc("@title:window", "SQL Database Connections [Changed]")); m_changed = true; } void DatabaseManagerDialog::save() { //ok-button was clicked, save the connections if they were changed if (m_changed) mainWidget->saveConnections(); } diff --git a/src/kdefrontend/datasources/DatabaseManagerDialog.h b/src/kdefrontend/datasources/DatabaseManagerDialog.h index 8a2bc897a..402d8738f 100644 --- a/src/kdefrontend/datasources/DatabaseManagerDialog.h +++ b/src/kdefrontend/datasources/DatabaseManagerDialog.h @@ -1,55 +1,54 @@ /*************************************************************************** File : DatabaseManagerDialog.h Project : LabPlot Description : dialog for managing database connections -------------------------------------------------------------------- Copyright : (C) 2016-2017 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 * * * ***************************************************************************/ #ifndef DATABASEMANAGERDIALOG_H #define DATABASEMANAGERDIALOG_H #include class DatabaseManagerWidget; class DatabaseManagerDialog : public QDialog { Q_OBJECT public: explicit DatabaseManagerDialog(QWidget*, const QString&); ~DatabaseManagerDialog() override; QString connection() const; private: DatabaseManagerWidget* mainWidget; bool m_changed{false}; private slots: void changed(); void save(); - void loadSettings(); }; #endif diff --git a/src/kdefrontend/datasources/FileInfoDialog.cpp b/src/kdefrontend/datasources/FileInfoDialog.cpp index 5efa67754..ffb74b0ac 100644 --- a/src/kdefrontend/datasources/FileInfoDialog.cpp +++ b/src/kdefrontend/datasources/FileInfoDialog.cpp @@ -1,214 +1,216 @@ /*************************************************************************** File : FileInfoDialog.cpp Project : LabPlot Description : import file data dialog -------------------------------------------------------------------- - Copyright : (C) 2009-2018 Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2009-2019 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2015-2018 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 "FileInfoDialog.h" #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/filters/filters.h" #include #include #include #include #include #include +#include #include #include #include /*! \class ImportWidget \brief Provides a dialog containing the information about the files to be imported. \ingroup kdefrontend */ FileInfoDialog::FileInfoDialog(QWidget* parent) : QDialog(parent) { m_textEditWidget.setReadOnly(true); m_textEditWidget.setLineWrapMode(QTextEdit::NoWrap); auto* layout = new QVBoxLayout(this); layout->addWidget(&m_textEditWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); connect(buttonBox, &QDialogButtonBox::rejected, this, &FileInfoDialog::reject); connect(buttonBox, &QDialogButtonBox::accepted, this, &FileInfoDialog::accept); layout->addWidget(buttonBox); setWindowIcon(QIcon::fromTheme("help-about")); setWindowTitle(i18nc("@title:window", "File Information")); setAttribute(Qt::WA_DeleteOnClose); setLayout(layout); - QTimer::singleShot(0, this, &FileInfoDialog::loadSettings); -} - -void FileInfoDialog::loadSettings() { - //restore saved settings + //restore saved settings if available + create(); // ensure there's a window created KConfigGroup conf(KSharedConfig::openConfig(), "FileInfoDialog"); - KWindowConfig::restoreWindowSize(windowHandle(), conf); + if (conf.exists()) { + KWindowConfig::restoreWindowSize(windowHandle(), conf); + resize(windowHandle()->size()); // workaround for QTBUG-40584 + } else + resize(QSize(300, 200).expandedTo(minimumSize())); } FileInfoDialog::~FileInfoDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "FileInfoDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void FileInfoDialog::setFiles(QStringList& files) { QString infoString; for (const auto& fileName : files) { if (fileName.isEmpty()) continue; if (!infoString.isEmpty()) infoString += "


"; infoString += fileInfoString(fileName); } m_textEditWidget.document()->setHtml(infoString); } /*! returns a string containing the general information about the file \c name and some content specific information (number of columns and lines for ASCII, color-depth for images etc.). */ QString FileInfoDialog::fileInfoString(const QString& name) const { QString infoString; QFileInfo fileInfo; QString fileTypeString; QIODevice *file = new QFile(name); QString fileName; #ifdef Q_OS_WIN if (name.at(1) != QLatin1Char(':')) fileName = QDir::homePath() + name; else fileName = name; #else if (name.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + name; else fileName = name; #endif if (!file) file = new QFile(fileName); if (file->open(QIODevice::ReadOnly)) { QStringList infoStrings; infoStrings << "" + fileName + "
"; // File type given by "file" #ifdef Q_OS_LINUX auto* proc = new QProcess(); QStringList args; args << "-b" << fileName; proc->start( "file", args); if (proc->waitForReadyRead(1000) == false) infoStrings << i18n("Reading from file %1 failed.", fileName); else { fileTypeString = proc->readLine(); if (fileTypeString.contains(i18n("cannot open"))) fileTypeString = ""; else { fileTypeString.remove(fileTypeString.length() - 1, 1); // remove '\n' } } infoStrings << i18n("File type: %1", fileTypeString); #endif // General: fileInfo.setFile(fileName); infoStrings << "" << i18n("General:") << ""; infoStrings << i18n("Readable: %1", fileInfo.isReadable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Writable: %1", fileInfo.isWritable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Executable: %1", fileInfo.isExecutable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Created: %1", fileInfo.created().toString()); infoStrings << i18n("Last modified: %1", fileInfo.lastModified().toString()); infoStrings << i18n("Last read: %1", fileInfo.lastRead().toString()); infoStrings << i18n("Owner: %1", fileInfo.owner()); infoStrings << i18n("Group: %1", fileInfo.group()); infoStrings << i18n("Size: %1", i18np("%1 cByte", "%1 cBytes", fileInfo.size())); // Summary: infoStrings << "" << i18n("Summary:") << ""; //depending on the file type, generate summary and content information about the file //TODO: content information (in BNF) for more types switch (AbstractFileFilter::fileType(fileName)) { case AbstractFileFilter::Ascii: infoStrings << AsciiFilter::fileInfoString(fileName); break; case AbstractFileFilter::Binary: infoStrings << BinaryFilter::fileInfoString(fileName); break; case AbstractFileFilter::Image: infoStrings << ImageFilter::fileInfoString(fileName); break; case AbstractFileFilter::HDF5: infoStrings << HDF5Filter::fileInfoString(fileName); infoStrings << "" << i18n("Content:") << ""; infoStrings << HDF5Filter::fileDDLString(fileName); break; case AbstractFileFilter::NETCDF: infoStrings << NetCDFFilter::fileInfoString(fileName); infoStrings << "" << i18n("Content:") << ""; infoStrings << NetCDFFilter::fileCDLString(fileName); break; case AbstractFileFilter::FITS: infoStrings << FITSFilter::fileInfoString(fileName); break; case AbstractFileFilter::JSON: infoStrings << JsonFilter::fileInfoString(fileName); break; case AbstractFileFilter::ROOT: infoStrings << ROOTFilter::fileInfoString(fileName); break; case AbstractFileFilter::NgspiceRawAscii: case AbstractFileFilter::NgspiceRawBinary: infoStrings << NgspiceRawAsciiFilter::fileInfoString(fileName); break; } infoString += infoStrings.join("
"); } else infoString += i18n("Could not open file %1 for reading.", fileName); return infoString; } diff --git a/src/kdefrontend/datasources/FileInfoDialog.h b/src/kdefrontend/datasources/FileInfoDialog.h index 772a5d358..b3f945f2b 100644 --- a/src/kdefrontend/datasources/FileInfoDialog.h +++ b/src/kdefrontend/datasources/FileInfoDialog.h @@ -1,52 +1,49 @@ /*************************************************************************** File : FileInfoDialog.h Project : LabPlot Description : file info dialog -------------------------------------------------------------------- - Copyright : (C) 2009-2018 Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2009-2019 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2015-2016 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 * * * ***************************************************************************/ #ifndef FILEINFODIALOG_H #define FILEINFODIALOG_H #include #include class FileInfoDialog : public QDialog { Q_OBJECT public: explicit FileInfoDialog(QWidget*); ~FileInfoDialog() override; void setFiles(QStringList&); private: QTextEdit m_textEditWidget; QString fileInfoString(const QString&) const; - -private slots: - void loadSettings(); }; #endif //IMPORTFILEDIALOG_H diff --git a/src/kdefrontend/datasources/ImportFileDialog.cpp b/src/kdefrontend/datasources/ImportFileDialog.cpp index bfb6ab0c2..025115475 100644 --- a/src/kdefrontend/datasources/ImportFileDialog.cpp +++ b/src/kdefrontend/datasources/ImportFileDialog.cpp @@ -1,491 +1,491 @@ /*************************************************************************** File : ImportDialog.cc Project : LabPlot Description : import file data dialog -------------------------------------------------------------------- Copyright : (C) 2008-2019 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2008-2015 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 "ImportFileDialog.h" #include "ImportFileWidget.h" #include "backend/core/AspectTreeModel.h" #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/filters/AbstractFileFilter.h" #include "backend/datasources/filters/filters.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/core/Workbook.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/MainWin.h" #ifdef HAVE_MQTT #include "backend/datasources/MQTTClient.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class ImportFileDialog \brief Dialog for importing data from a file. Embeds \c ImportFileWidget and provides the standard buttons. \ingroup kdefrontend */ ImportFileDialog::ImportFileDialog(MainWin* parent, bool liveDataSource, const QString& fileName) : ImportDialog(parent), m_importFileWidget(new ImportFileWidget(this, liveDataSource, fileName)) { vLayout->addWidget(m_importFileWidget); //dialog buttons QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Reset |QDialogButtonBox::Cancel); okButton = buttonBox->button(QDialogButtonBox::Ok); m_optionsButton = buttonBox->button(QDialogButtonBox::Reset); //we highjack the default "Reset" button and use if for showing/hiding the options okButton->setEnabled(false); //ok is only available if a valid container was selected vLayout->addWidget(buttonBox); //hide the data-source related widgets if (!liveDataSource) setModel(); //Signals/Slots connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); if (!liveDataSource) setWindowTitle(i18nc("@title:window", "Import Data to Spreadsheet or Matrix")); else setWindowTitle(i18nc("@title:window", "Add New Live Data Source")); setWindowIcon(QIcon::fromTheme("document-import-database")); //restore saved settings if available create(); // ensure there's a window created QApplication::processEvents(QEventLoop::AllEvents, 0); m_importFileWidget->loadSettings(); KConfigGroup conf(KSharedConfig::openConfig(), "ImportFileDialog"); if (conf.exists()) { m_showOptions = conf.readEntry("ShowOptions", false); m_showOptions ? m_optionsButton->setText(i18n("Hide Options")) : m_optionsButton->setText(i18n("Show Options")); m_importFileWidget->showOptions(m_showOptions); //do the signal-slot connections after all settings where loaded in import file widget and check the OK button after this connect(m_importFileWidget, &ImportFileWidget::checkedFitsTableToMatrix, this, &ImportFileDialog::checkOnFitsTableToMatrix); connect(m_importFileWidget, static_cast(&ImportFileWidget::fileNameChanged), this, &ImportFileDialog::checkOkButton); connect(m_importFileWidget, static_cast(&ImportFileWidget::sourceTypeChanged), this, &ImportFileDialog::checkOkButton); connect(m_importFileWidget, &ImportFileWidget::hostChanged, this, &ImportFileDialog::checkOkButton); connect(m_importFileWidget, &ImportFileWidget::portChanged, this, &ImportFileDialog::checkOkButton); //TODO: do we really need to check the ok button when the preview was refreshed? //If not, remove this together with the previewRefreshed signal in ImportFileWidget //connect(m_importFileWidget, &ImportFileWidget::previewRefreshed, this, &ImportFileDialog::checkOkButton); #ifdef HAVE_MQTT connect(m_importFileWidget, &ImportFileWidget::subscriptionsChanged, this, &ImportFileDialog::checkOkButton); connect(m_importFileWidget, &ImportFileWidget::checkFileType, this, &ImportFileDialog::checkOkButton); #endif connect(m_optionsButton, &QPushButton::clicked, this, &ImportFileDialog::toggleOptions); KWindowConfig::restoreWindowSize(windowHandle(), conf); resize(windowHandle()->size()); // workaround for QTBUG-40584 } else - resize(QSize(300, 0).expandedTo(minimumSize())); + resize(QSize(0, 0).expandedTo(minimumSize())); checkOkButton(); } ImportFileDialog::~ImportFileDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ImportFileDialog"); conf.writeEntry("ShowOptions", m_showOptions); if (cbPosition) conf.writeEntry("Position", cbPosition->currentIndex()); KWindowConfig::saveWindowSize(windowHandle(), conf); } int ImportFileDialog::sourceType() const { return static_cast(m_importFileWidget->currentSourceType()); } /*! triggers data import to the live data source \c source */ void ImportFileDialog::importToLiveDataSource(LiveDataSource* source, QStatusBar* statusBar) const { DEBUG("ImportFileDialog::importToLiveDataSource()"); m_importFileWidget->saveSettings(source); //show a progress bar in the status bar auto* progressBar = new QProgressBar(); progressBar->setRange(0, 100); connect(source->filter(), &AbstractFileFilter::completed, progressBar, &QProgressBar::setValue); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); WAIT_CURSOR; QTime timer; timer.start(); DEBUG(" Initial read()"); source->read(); statusBar->showMessage( i18n("Live data source created in %1 seconds.", (float)timer.elapsed()/1000) ); RESET_CURSOR; statusBar->removeWidget(progressBar); source->ready(); } #ifdef HAVE_MQTT /*! triggers data import to the MQTTClient \c client */ void ImportFileDialog::importToMQTT(MQTTClient* client) const{ m_importFileWidget->saveMQTTSettings(client); client->read(); client->ready(); } #endif /*! triggers data import to the currently selected data container */ void ImportFileDialog::importTo(QStatusBar* statusBar) const { DEBUG("ImportFileDialog::importTo()"); QDEBUG(" cbAddTo->currentModelIndex() =" << cbAddTo->currentModelIndex()); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { DEBUG("ERROR in importTo(): No aspect available"); DEBUG(" cbAddTo->currentModelIndex().isValid() = " << cbAddTo->currentModelIndex().isValid()); DEBUG(" cbAddTo->currentModelIndex() row/column = " << cbAddTo->currentModelIndex().row() << ' ' << cbAddTo->currentModelIndex().column()); return; } if (m_importFileWidget->isFileEmpty()) { KMessageBox::information(nullptr, i18n("No data to import."), i18n("No Data")); return; } QString fileName = m_importFileWidget->fileName(); auto filter = m_importFileWidget->currentFileFilter(); auto mode = AbstractFileFilter::ImportMode(cbPosition->currentIndex()); //show a progress bar in the status bar auto* progressBar = new QProgressBar(); progressBar->setRange(0, 100); connect(filter, &AbstractFileFilter::completed, progressBar, &QProgressBar::setValue); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); WAIT_CURSOR; QApplication::processEvents(QEventLoop::AllEvents, 100); QTime timer; timer.start(); if (aspect->inherits("Matrix")) { DEBUG(" to Matrix"); auto* matrix = qobject_cast(aspect); filter->readDataFromFile(fileName, matrix, mode); } else if (aspect->inherits("Spreadsheet")) { DEBUG(" to Spreadsheet"); auto* spreadsheet = qobject_cast(aspect); DEBUG(" Calling readDataFromFile() with spreadsheet " << spreadsheet); filter->readDataFromFile(fileName, spreadsheet, mode); } else if (aspect->inherits("Workbook")) { DEBUG(" to Workbook"); auto* workbook = qobject_cast(aspect); QVector sheets = workbook->children(); AbstractFileFilter::FileType fileType = m_importFileWidget->currentFileType(); // multiple data sets/variables for HDF5, NetCDF and ROOT if (fileType == AbstractFileFilter::HDF5 || fileType == AbstractFileFilter::NETCDF || fileType == AbstractFileFilter::ROOT) { QStringList names; if (fileType == AbstractFileFilter::HDF5) names = m_importFileWidget->selectedHDF5Names(); else if (fileType == AbstractFileFilter::NETCDF) names = m_importFileWidget->selectedNetCDFNames(); else names = m_importFileWidget->selectedROOTNames(); int nrNames = names.size(), offset = sheets.size(); //TODO: think about importing multiple sets into one sheet int start = 0; // add nrNames sheets (0 to nrNames) if (mode == AbstractFileFilter::Replace) // add only missing sheets (from offset to nrNames) start = offset; // add additional sheets for (int i = start; i < nrNames; ++i) { Spreadsheet *spreadsheet = new Spreadsheet(i18n("Spreadsheet")); if (mode == AbstractFileFilter::Prepend) workbook->insertChildBefore(spreadsheet, sheets[0]); else workbook->addChild(spreadsheet); } // start at offset for append, else at 0 if (mode != AbstractFileFilter::Append) offset = 0; // import all sets to a different sheet sheets = workbook->children(); for (int i = 0; i < nrNames; ++i) { if (fileType == AbstractFileFilter::HDF5) static_cast(filter)->setCurrentDataSetName(names[i]); else if (fileType == AbstractFileFilter::NETCDF) static_cast(filter)->setCurrentVarName(names[i]); else static_cast(filter)->setCurrentObject(names[i]); int index = i + offset; if (sheets[index]->inherits("Matrix")) filter->readDataFromFile(fileName, qobject_cast(sheets[index])); else if (sheets[index]->inherits("Spreadsheet")) filter->readDataFromFile(fileName, qobject_cast(sheets[index])); } } else { // single import file types // use active spreadsheet/matrix if present, else new spreadsheet Spreadsheet* spreadsheet = workbook->currentSpreadsheet(); Matrix* matrix = workbook->currentMatrix(); if (spreadsheet) filter->readDataFromFile(fileName, spreadsheet, mode); else if (matrix) filter->readDataFromFile(fileName, matrix, mode); else { spreadsheet = new Spreadsheet(i18n("Spreadsheet")); workbook->addChild(spreadsheet); filter->readDataFromFile(fileName, spreadsheet, mode); } } } statusBar->showMessage(i18n("File %1 imported in %2 seconds.", fileName, (float)timer.elapsed()/1000)); RESET_CURSOR; statusBar->removeWidget(progressBar); } void ImportFileDialog::toggleOptions() { m_importFileWidget->showOptions(!m_showOptions); m_showOptions = !m_showOptions; m_showOptions ? m_optionsButton->setText(i18n("Hide Options")) : m_optionsButton->setText(i18n("Show Options")); //resize the dialog layout()->activate(); resize( QSize(this->width(), 0).expandedTo(minimumSize()) ); } void ImportFileDialog::checkOnFitsTableToMatrix(const bool enable) { if (cbAddTo) { QDEBUG("cbAddTo->currentModelIndex() = " << cbAddTo->currentModelIndex()); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { DEBUG("ERROR: no aspect available."); return; } if (aspect->inherits("Matrix")) { okButton->setEnabled(enable); if (enable) okButton->setToolTip(i18n("Close the dialog and import the data.")); else okButton->setToolTip(i18n("Cannot import into a matrix since the data contains non-numerical data.")); } } } void ImportFileDialog::checkOkButton() { DEBUG("ImportFileDialog::checkOkButton()"); if (cbAddTo) { //only check for the target container when no file data source is being added QDEBUG(" cbAddTo->currentModelIndex() = " << cbAddTo->currentModelIndex()); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { okButton->setEnabled(false); okButton->setToolTip(i18n("Select a data container where the data has to be imported into.")); lPosition->setEnabled(false); cbPosition->setEnabled(false); return; } else { lPosition->setEnabled(true); cbPosition->setEnabled(true); //when doing ASCII import to a matrix, hide the options for using the file header (first line) //to name the columns since the column names are fixed in a matrix const auto* matrix = dynamic_cast(aspect); m_importFileWidget->showAsciiHeaderOptions(matrix == nullptr); } } QString fileName = m_importFileWidget->fileName(); #ifndef HAVE_WINDOWS if (!fileName.isEmpty() && fileName.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif DEBUG("Data Source Type: " << ENUM_TO_STRING(LiveDataSource, SourceType, m_importFileWidget->currentSourceType())); switch (m_importFileWidget->currentSourceType()) { case LiveDataSource::SourceType::FileOrPipe: { DEBUG("fileName = " << fileName.toUtf8().constData()); const bool enable = QFile::exists(fileName); okButton->setEnabled(enable); if (enable) okButton->setToolTip(i18n("Close the dialog and import the data.")); else okButton->setToolTip(i18n("Provide an existing file.")); break; } case LiveDataSource::SourceType::LocalSocket: { const bool enable = QFile::exists(fileName); if (enable) { QLocalSocket lsocket{this}; DEBUG("CONNECT"); lsocket.connectToServer(fileName, QLocalSocket::ReadOnly); if (lsocket.waitForConnected()) { // this is required for server that send data as soon as connected lsocket.waitForReadyRead(); DEBUG("DISCONNECT"); lsocket.disconnectFromServer(); // read-only socket is disconnected immediately (no waitForDisconnected()) okButton->setEnabled(true); okButton->setToolTip(i18n("Close the dialog and import the data.")); } else { DEBUG("failed connect to local socket - " << lsocket.errorString().toStdString()); okButton->setEnabled(false); okButton->setToolTip(i18n("Could not connect to the provided local socket.")); } } else { okButton->setEnabled(false); okButton->setToolTip(i18n("Selected local socket does not exist.")); } break; } case LiveDataSource::SourceType::NetworkTcpSocket: { const bool enable = !m_importFileWidget->host().isEmpty() && !m_importFileWidget->port().isEmpty(); if (enable) { QTcpSocket socket(this); socket.connectToHost(m_importFileWidget->host(), m_importFileWidget->port().toUShort(), QTcpSocket::ReadOnly); if (socket.waitForConnected()) { okButton->setEnabled(true); okButton->setToolTip(i18n("Close the dialog and import the data.")); socket.disconnectFromHost(); } else { DEBUG("failed to connect to TCP socket - " << socket.errorString().toStdString()); okButton->setEnabled(false); okButton->setToolTip(i18n("Could not connect to the provided TCP socket.")); } } else { okButton->setEnabled(false); okButton->setToolTip(i18n("Either the host name or the port number is missing.")); } break; } case LiveDataSource::SourceType::NetworkUdpSocket: { const bool enable = !m_importFileWidget->host().isEmpty() && !m_importFileWidget->port().isEmpty(); if (enable) { QUdpSocket socket(this); socket.bind(QHostAddress(m_importFileWidget->host()), m_importFileWidget->port().toUShort()); socket.connectToHost(m_importFileWidget->host(), 0, QUdpSocket::ReadOnly); if (socket.waitForConnected()) { okButton->setEnabled(true); okButton->setToolTip(i18n("Close the dialog and import the data.")); socket.disconnectFromHost(); // read-only socket is disconnected immediately (no waitForDisconnected()) } else { DEBUG("failed to connect to UDP socket - " << socket.errorString().toStdString()); okButton->setEnabled(false); okButton->setToolTip(i18n("Could not connect to the provided UDP socket.")); } } else { okButton->setEnabled(false); okButton->setToolTip(i18n("Either the host name or the port number is missing.")); } break; } case LiveDataSource::SourceType::SerialPort: { const QString sPort = m_importFileWidget->serialPort(); const int baudRate = m_importFileWidget->baudRate(); if (!sPort.isEmpty()) { QSerialPort serialPort{this}; DEBUG(" Port: " << sPort.toStdString() << ", Settings: " << baudRate << ',' << serialPort.dataBits() << ',' << serialPort.parity() << ',' << serialPort.stopBits()); serialPort.setPortName(sPort); serialPort.setBaudRate(baudRate); const bool serialPortOpened = serialPort.open(QIODevice::ReadOnly); okButton->setEnabled(serialPortOpened); if (serialPortOpened) { okButton->setToolTip(i18n("Close the dialog and import the data.")); serialPort.close(); } else { DEBUG("Could not connect to the provided serial port"); okButton->setToolTip(i18n("Could not connect to the provided serial port.")); } } else { okButton->setEnabled(false); okButton->setToolTip(i18n("Serial port number is missing.")); } break; } case LiveDataSource::SourceType::MQTT: { #ifdef HAVE_MQTT const bool enable = m_importFileWidget->isMqttValid(); if (enable) { okButton->setEnabled(true); okButton->setToolTip(i18n("Close the dialog and import the data.")); } else { okButton->setEnabled(false); okButton->setToolTip(i18n("Either there is no connection, or no subscriptions were made, or the file filter is not ASCII.")); } #endif break; } } } QString ImportFileDialog::selectedObject() const { return m_importFileWidget->selectedObject(); } diff --git a/src/kdefrontend/datasources/ImportProjectDialog.cpp b/src/kdefrontend/datasources/ImportProjectDialog.cpp index 546ec9c7c..e1f7ca73c 100644 --- a/src/kdefrontend/datasources/ImportProjectDialog.cpp +++ b/src/kdefrontend/datasources/ImportProjectDialog.cpp @@ -1,422 +1,424 @@ /*************************************************************************** File : ImportProjectDialog.cpp Project : LabPlot Description : import project dialog -------------------------------------------------------------------- - Copyright : (C) 2017 Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2017-2019 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 "ImportProjectDialog.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/datasources/projects/LabPlotProjectParser.h" #ifdef HAVE_LIBORIGIN #include "backend/datasources/projects/OriginProjectParser.h" #endif #include "kdefrontend/MainWin.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include #include #include #include +#include #include #include #include #include /*! \class ImportProjectDialog \brief Dialog for importing project files. \ingroup kdefrontend */ ImportProjectDialog::ImportProjectDialog(MainWin* parent, ProjectType type) : QDialog(parent), m_mainWin(parent), m_projectType(type), m_aspectTreeModel(new AspectTreeModel(parent->project())) { auto* vLayout = new QVBoxLayout(this); //main widget QWidget* mainWidget = new QWidget(this); ui.setupUi(mainWidget); ui.chbUnusedObjects->hide(); vLayout->addWidget(mainWidget); ui.tvPreview->setAnimated(true); ui.tvPreview->setAlternatingRowColors(true); ui.tvPreview->setSelectionBehavior(QAbstractItemView::SelectRows); ui.tvPreview->setSelectionMode(QAbstractItemView::ExtendedSelection); ui.tvPreview->setUniformRowHeights(true); ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); m_cbAddTo = new TreeViewComboBox(ui.gbImportTo); m_cbAddTo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); ui.gbImportTo->layout()->addWidget(m_cbAddTo); QList list; list << "Folder"; m_cbAddTo->setTopLevelClasses(list); m_aspectTreeModel->setSelectableAspects(list); m_cbAddTo->setModel(m_aspectTreeModel); m_bNewFolder = new QPushButton(ui.gbImportTo); m_bNewFolder->setIcon(QIcon::fromTheme("list-add")); m_bNewFolder->setToolTip(i18n("Add new folder")); ui.gbImportTo->layout()->addWidget(m_bNewFolder); //dialog buttons m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); vLayout->addWidget(m_buttonBox); //ok-button is only enabled if some project objects were selected (s.a. ImportProjectDialog::selectionChanged()) m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); //Signals/Slots connect(ui.leFileName, SIGNAL(textChanged(QString)), SLOT(fileNameChanged(QString))); connect(ui.bOpen, SIGNAL(clicked()), this, SLOT (selectFile())); connect(m_bNewFolder, SIGNAL(clicked()), this, SLOT(newFolder())); connect(ui.chbUnusedObjects, &QCheckBox::stateChanged, this, &ImportProjectDialog::refreshPreview); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QString title; switch (m_projectType) { case (ProjectLabPlot): m_projectParser = new LabPlotProjectParser(); title = i18nc("@title:window", "Import LabPlot Project"); break; case (ProjectOrigin): #ifdef HAVE_LIBORIGIN m_projectParser = new OriginProjectParser(); title = i18nc("@title:window", "Import Origin Project"); #endif break; } //dialog title and icon setWindowTitle(title); setWindowIcon(QIcon::fromTheme("document-import")); - QTimer::singleShot(0, this, &ImportProjectDialog::loadSettings); -} - -void ImportProjectDialog::loadSettings() { - //restore saved settings + //restore saved settings if available + create(); // ensure there's a window created KConfigGroup conf(KSharedConfig::openConfig(), "ImportProjectDialog"); - KWindowConfig::restoreWindowSize(windowHandle(), conf); + if (conf.exists()) { + KWindowConfig::restoreWindowSize(windowHandle(), conf); + resize(windowHandle()->size()); // workaround for QTBUG-40584 + } else + resize(QSize(300, 0).expandedTo(minimumSize())); QString lastImportedFile; switch (m_projectType) { case (ProjectLabPlot): lastImportedFile = QLatin1String("LastImportedLabPlotProject"); break; case (ProjectOrigin): lastImportedFile = QLatin1String("LastImportedOriginProject"); break; } QApplication::processEvents(QEventLoop::AllEvents, 100); ui.leFileName->setText(conf.readEntry(lastImportedFile, "")); } ImportProjectDialog::~ImportProjectDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ImportProjectDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); QString lastImportedFile; switch (m_projectType) { case (ProjectLabPlot): lastImportedFile = QLatin1String("LastImportedLabPlotProject"); break; case (ProjectOrigin): lastImportedFile = QLatin1String("LastImportedOriginProject"); break; } conf.writeEntry(lastImportedFile, ui.leFileName->text()); } void ImportProjectDialog::setCurrentFolder(const Folder* folder) { m_cbAddTo->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(folder)); } void ImportProjectDialog::importTo(QStatusBar* statusBar) const { DEBUG("ImportProjectDialog::importTo()"); //determine the selected objects, convert the model indexes to string pathes const QModelIndexList& indexes = ui.tvPreview->selectionModel()->selectedIndexes(); QStringList selectedPathes; for (int i = 0; i < indexes.size()/4; ++i) { QModelIndex index = indexes.at(i*4); const auto* aspect = static_cast(index.internalPointer()); //path of the current aspect and the pathes of all aspects it depends on selectedPathes << aspect->path(); QDEBUG(" aspect path: " << aspect->path()); for (const auto* depAspect : aspect->dependsOn()) selectedPathes << depAspect->path(); } selectedPathes.removeDuplicates(); Folder* targetFolder = static_cast(m_cbAddTo->currentModelIndex().internalPointer()); //check whether the selected pathes already exist in the target folder and warn the user const QString& targetFolderPath = targetFolder->path(); const Project* targetProject = targetFolder->project(); QStringList targetAllPathes; for (const auto* aspect : targetProject->children(AbstractAspect::Recursive)) { if (!dynamic_cast(aspect)) targetAllPathes << aspect->path(); } QStringList existingPathes; for (const auto& path : selectedPathes) { const QString& newPath = targetFolderPath + path.right(path.length() - path.indexOf('/')); if (targetAllPathes.indexOf(newPath) != -1) existingPathes << path; } QDEBUG("project objects to be imported: " << selectedPathes); QDEBUG("all already available project objects: " << targetAllPathes); QDEBUG("already available project objects to be overwritten: " << existingPathes); if (!existingPathes.isEmpty()) { QString msg = i18np("The object listed below already exists in target folder and will be overwritten:", "The objects listed below already exist in target folder and will be overwritten:", existingPathes.size()); msg += '\n'; for (const auto& path : existingPathes) msg += '\n' + path.right(path.length() - path.indexOf('/') - 1); //strip away the name of the root folder "Project" msg += "\n\n" + i18n("Do you want to proceed?"); const int rc = KMessageBox::warningYesNo(nullptr, msg, i18n("Override existing objects?")); if (rc == KMessageBox::No) return; } //show a progress bar in the status bar auto* progressBar = new QProgressBar(); progressBar->setMinimum(0); progressBar->setMaximum(100); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QApplication::processEvents(QEventLoop::AllEvents, 100); //import the selected project objects into the specified folder QTime timer; timer.start(); connect(m_projectParser, SIGNAL(completed(int)), progressBar, SLOT(setValue(int))); #ifdef HAVE_LIBORIGIN if (m_projectType == ProjectOrigin && ui.chbUnusedObjects->isVisible() && ui.chbUnusedObjects->isChecked()) reinterpret_cast(m_projectParser)->setImportUnusedObjects(true); #endif m_projectParser->importTo(targetFolder, selectedPathes); statusBar->showMessage( i18n("Project data imported in %1 seconds.", (float)timer.elapsed()/1000) ); QApplication::restoreOverrideCursor(); statusBar->removeWidget(progressBar); } /*! * show the content of the project in the tree view */ void ImportProjectDialog::refreshPreview() { QString project = ui.leFileName->text(); m_projectParser->setProjectFileName(project); #ifdef HAVE_LIBORIGIN if (m_projectType == ProjectOrigin) { auto* originParser = reinterpret_cast(m_projectParser); if (originParser->hasUnusedObjects()) ui.chbUnusedObjects->show(); else ui.chbUnusedObjects->hide(); originParser->setImportUnusedObjects(ui.chbUnusedObjects->isVisible() && ui.chbUnusedObjects->isChecked()); } #endif ui.tvPreview->setModel(m_projectParser->model()); connect(ui.tvPreview->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection)) ); //show top-level containers only if (ui.tvPreview->model()) { QModelIndex root = ui.tvPreview->model()->index(0,0); showTopLevelOnly(root); } //extand the tree to show all available top-level objects and adjust the header sizes ui.tvPreview->expandAll(); ui.tvPreview->header()->resizeSections(QHeaderView::ResizeToContents); } /*! Hides the non-toplevel items of the model used in the tree view. */ void ImportProjectDialog::showTopLevelOnly(const QModelIndex& index) { int rows = index.model()->rowCount(index); for (int i = 0; i < rows; ++i) { QModelIndex child = index.child(i, 0); showTopLevelOnly(child); const auto* aspect = static_cast(child.internalPointer()); ui.tvPreview->setRowHidden(i, index, !isTopLevel(aspect)); } } /*! checks whether \c aspect is one of the allowed top level types */ bool ImportProjectDialog::isTopLevel(const AbstractAspect* aspect) const { foreach (const char* classString, m_projectParser->topLevelClasses()) { if (aspect->inherits(classString)) return true; } return false; } //############################################################################## //################################# SLOTS #################################### //############################################################################## void ImportProjectDialog::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { Q_UNUSED(deselected); //determine the dependent objects and select/deselect them too const QModelIndexList& indexes = selected.indexes(); if (indexes.isEmpty()) return; //for the just selected aspect, determine all the objects it depends on and select them, too //TODO: we need a better "selection", maybe with tri-state check boxes in the tree view const auto* aspect = static_cast(indexes.at(0).internalPointer()); const QVector aspects = aspect->dependsOn(); const auto* model = reinterpret_cast(ui.tvPreview->model()); for (const auto* aspect : aspects) { QModelIndex index = model->modelIndexOfAspect(aspect, 0); ui.tvPreview->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); } //Ok-button is only enabled if some project objects were selected bool enable = (selected.indexes().size() != 0); m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable); if (enable) m_buttonBox->button(QDialogButtonBox::Ok)->setToolTip(i18n("Close the dialog and import the selected objects.")); else m_buttonBox->button(QDialogButtonBox::Ok)->setToolTip(i18n("Select object(s) to be imported.")); } /*! opens a file dialog and lets the user select the project file. */ void ImportProjectDialog::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "ImportProjectDialog"); QString title; QString lastDir; QString supportedFormats; QString lastDirConfEntryName; switch (m_projectType) { case (ProjectLabPlot): title = i18nc("@title:window", "Open LabPlot Project"); lastDirConfEntryName = QLatin1String("LastImportLabPlotProjectDir"); supportedFormats = i18n("LabPlot Projects (%1)", Project::supportedExtensions()); break; case (ProjectOrigin): #ifdef HAVE_LIBORIGIN title = i18nc("@title:window", "Open Origin Project"); lastDirConfEntryName = QLatin1String("LastImportOriginProjecttDir"); supportedFormats = i18n("Origin Projects (%1)", OriginProjectParser::supportedExtensions()); #endif break; } lastDir = conf.readEntry(lastDirConfEntryName, ""); QString path = QFileDialog::getOpenFileName(this, title, lastDir, supportedFormats); 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 != lastDir) conf.writeEntry(lastDirConfEntryName, newDir); } ui.leFileName->setText(path); refreshPreview(); } void ImportProjectDialog::fileNameChanged(const QString& name) { QString fileName = name; #ifndef HAVE_WINDOWS // make relative path if ( !fileName.isEmpty() && fileName.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif bool fileExists = QFile::exists(fileName); if (fileExists) ui.leFileName->setStyleSheet(""); else ui.leFileName->setStyleSheet("QLineEdit{background:red;}"); if (!fileExists) { //file doesn't exist -> delete the content preview that is still potentially //available from the previously selected file ui.tvPreview->setModel(nullptr); m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); return; } refreshPreview(); } void ImportProjectDialog::newFolder() { QString path = ui.leFileName->text(); QString name = path.right( path.length()-path.lastIndexOf(QDir::separator())-1 ); bool ok; QInputDialog* dlg = new QInputDialog(this); name = dlg->getText(this, i18n("Add new folder"), i18n("Folder name:"), QLineEdit::Normal, name, &ok); if (ok) { auto* folder = new Folder(name); m_mainWin->addAspectToProject(folder); m_cbAddTo->setCurrentModelIndex(m_mainWin->model()->modelIndexOfAspect(folder)); } delete dlg; } diff --git a/src/kdefrontend/datasources/ImportProjectDialog.h b/src/kdefrontend/datasources/ImportProjectDialog.h index 6faa9712f..a8b7dcf6c 100644 --- a/src/kdefrontend/datasources/ImportProjectDialog.h +++ b/src/kdefrontend/datasources/ImportProjectDialog.h @@ -1,78 +1,77 @@ /*************************************************************************** File : ImportProjectDialog.h Project : LabPlot Description : import project dialog -------------------------------------------------------------------- - Copyright : (C) 2017 Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2017-2019 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 * * * ***************************************************************************/ #ifndef IMPORTPROJECTDIALOG_H #define IMPORTPROJECTDIALOG_H #include #include "ui_importprojectwidget.h" class AbstractAspect; class AspectTreeModel; class Folder; class ProjectParser; class TreeViewComboBox; class MainWin; class QDialogButtonBox; class QStatusBar; class ImportProjectDialog : public QDialog { Q_OBJECT public: enum ProjectType {ProjectLabPlot, ProjectOrigin}; explicit ImportProjectDialog(MainWin*, ProjectType); ~ImportProjectDialog() override; void setCurrentFolder(const Folder*); void importTo(QStatusBar*) const; private: Ui::ImportProjectWidget ui; MainWin* m_mainWin; ProjectParser* m_projectParser{nullptr}; ProjectType m_projectType; AspectTreeModel* m_aspectTreeModel; TreeViewComboBox* m_cbAddTo; QPushButton* m_bNewFolder; QDialogButtonBox* m_buttonBox; void showTopLevelOnly(const QModelIndex&); bool isTopLevel(const AbstractAspect*) const; private slots: - void loadSettings(); void fileNameChanged(const QString&); void refreshPreview(); void selectionChanged(const QItemSelection&, const QItemSelection&); void selectFile(); void newFolder(); }; #endif //IMPORTPROJECTDIALOG_H diff --git a/src/kdefrontend/datasources/ImportSQLDatabaseDialog.cpp b/src/kdefrontend/datasources/ImportSQLDatabaseDialog.cpp index 557ae2a4d..de9dfeeca 100644 --- a/src/kdefrontend/datasources/ImportSQLDatabaseDialog.cpp +++ b/src/kdefrontend/datasources/ImportSQLDatabaseDialog.cpp @@ -1,177 +1,177 @@ /*************************************************************************** File : ImportSQLDatabaseDialog.cpp Project : LabPlot Description : import SQL dataase dialog -------------------------------------------------------------------- Copyright : (C) 2016 by Ankit Wagadre (wagadre.ankit@gmail.com) Copyright : (C) 2016-2017 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 "ImportSQLDatabaseDialog.h" #include "ImportSQLDatabaseWidget.h" #include "backend/core/AspectTreeModel.h" #include "backend/lib/macros.h" #include "kdefrontend/MainWin.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/core/Workbook.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include #include #include #include #include /*! \class ImportSQLDatabaseDialog \brief Dialog for importing data from a SQL database. Embeds \c ImportSQLDatabaseWidget and provides the standard buttons. \ingroup kdefrontend */ ImportSQLDatabaseDialog::ImportSQLDatabaseDialog(MainWin* parent) : ImportDialog(parent), importSQLDatabaseWidget(new ImportSQLDatabaseWidget(this)) { vLayout->addWidget(importSQLDatabaseWidget); setWindowTitle(i18nc("@title:window", "Import Data to Spreadsheet or Matrix")); setWindowIcon(QIcon::fromTheme("document-import-database")); setModel(); //dialog buttons QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(false); //ok is only available if a valid container was selected vLayout->addWidget(buttonBox); //Signals/Slots connect(importSQLDatabaseWidget, &ImportSQLDatabaseWidget::stateChanged, this, &ImportSQLDatabaseDialog::checkOkButton); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); //restore saved settings if available create(); // ensure there's a window created KConfigGroup conf(KSharedConfig::openConfig(), "ImportSQLDatabaseDialog"); if (conf.exists()) { KWindowConfig::restoreWindowSize(windowHandle(), conf); resize(windowHandle()->size()); // workaround for QTBUG-40584 } else - resize(QSize(300, 0).expandedTo(minimumSize())); + resize(QSize(0, 0).expandedTo(minimumSize())); } ImportSQLDatabaseDialog::~ImportSQLDatabaseDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ImportSQLDatabaseDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void ImportSQLDatabaseDialog::importTo(QStatusBar* statusBar) const { DEBUG("ImportSQLDatabaseDialog::import()"); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { DEBUG("ERROR: No aspect available!"); return; } const auto mode = AbstractFileFilter::ImportMode(cbPosition->currentIndex()); //show a progress bar in the status bar auto* progressBar = new QProgressBar(); progressBar->setMinimum(0); progressBar->setMaximum(100); connect(importSQLDatabaseWidget, &ImportSQLDatabaseWidget::completed, progressBar, &QProgressBar::setValue); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); WAIT_CURSOR; QApplication::processEvents(QEventLoop::AllEvents, 100); QTime timer; timer.start(); if (aspect->inherits("Matrix")) { auto* matrix = qobject_cast(aspect); importSQLDatabaseWidget->read(matrix, mode); } else if (aspect->inherits("Spreadsheet")) { auto* spreadsheet = qobject_cast(aspect); importSQLDatabaseWidget->read(spreadsheet, mode); } else if (aspect->inherits("Workbook")) { // use active spreadsheet or matrix (only if numeric data is going to be imported) if present, // create a new spreadsheet in the selected workbook otherwise auto* workbook = qobject_cast(aspect); Spreadsheet* spreadsheet = workbook->currentSpreadsheet(); Matrix* matrix = workbook->currentMatrix(); if (spreadsheet) importSQLDatabaseWidget->read(spreadsheet, mode); else if (matrix && importSQLDatabaseWidget->isNumericData()) importSQLDatabaseWidget->read(matrix, mode); else { spreadsheet = new Spreadsheet(i18n("Spreadsheet")); workbook->addChild(spreadsheet); importSQLDatabaseWidget->read(spreadsheet, mode); } } statusBar->showMessage( i18n("Data imported in %1 seconds.", (float)timer.elapsed()/1000) ); RESET_CURSOR; statusBar->removeWidget(progressBar); } QString ImportSQLDatabaseDialog::selectedObject() const { return importSQLDatabaseWidget->selectedTable(); } void ImportSQLDatabaseDialog::checkOkButton() { DEBUG("ImportSQLDatabaseDialog::checkOkButton()"); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { okButton->setEnabled(false); okButton->setToolTip(i18n("Select a data container where the data has to be imported into.")); cbPosition->setEnabled(false); return; } //check whether a valid connection and an object to import were selected if (!importSQLDatabaseWidget->isValid()) { okButton->setEnabled(false); okButton->setToolTip(i18n("Select a valid database object (table or query result set) that has to be imported.")); cbPosition->setEnabled(false); return; } //for matrix containers allow to import only numerical data if (dynamic_cast(aspect) && !importSQLDatabaseWidget->isNumericData()) { okButton->setEnabled(false); okButton->setToolTip(i18n("Cannot import into a matrix since the data contains non-numerical data.")); cbPosition->setEnabled(false); return; } okButton->setEnabled(true); okButton->setToolTip(i18n("Close the dialog and import the data.")); cbPosition->setEnabled(true); } diff --git a/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp b/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp index 1e8922aff..cdcb778a7 100644 --- a/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp @@ -1,387 +1,387 @@ /*************************************************************************** File : FunctionValuesDialog.cpp Project : LabPlot Description : Dialog for generating values from a mathematical function -------------------------------------------------------------------- Copyright : (C) 2014-2018 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 #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 create(); // ensure there's a window created KConfigGroup conf(KSharedConfig::openConfig(), "FunctionValuesDialog"); if (conf.exists()) { KWindowConfig::restoreWindowSize(windowHandle(), conf); resize(windowHandle()->size()); // workaround for QTBUG-40584 } else - resize(QSize(300, 0).expandedTo(minimumSize())); + resize(QSize(0, 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 auto* column = dynamic_cast(aspect); if (column) m_variableDataColumns[i]->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(column)); else m_variableDataColumns[i]->setCurrentModelIndex(QModelIndex()); break; } } } } checkValues(); } /*! 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())); 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())); 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() { auto* layout = dynamic_cast(ui.frameVariables->layout()); int row = m_variableNames.size(); //text field for the variable name 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 auto* l = new QLabel("="); layout->addWidget(l, row, 1, 1, 1); m_variableLabels << l; //combo box for the data column 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) { 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*> xNewVectors; 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); auto* column = dynamic_cast(aspect); Q_ASSERT(column); columnPathes << column->path(); if (column->columnMode() == AbstractColumn::Integer) { //convert integers to doubles first auto* xVector = new QVector(column->rowCount()); for (int i = 0; irowCount(); ++i) xVector->operator[](i) = column->valueAt(i); xNewVectors << xVector; xVectors << xVector; } else 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) 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) { if (col->columnMode() != AbstractColumn::Numeric) col->setColumnMode(AbstractColumn::Numeric); col->setFormula(expression, variableNames, columnPathes); col->replaceValues(0, new_data); } m_spreadsheet->endMacro(); //delete help vectors created for the conversion from int to double for (auto* vector : xNewVectors) delete vector; RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/PlotDataDialog.cpp b/src/kdefrontend/spreadsheet/PlotDataDialog.cpp index 6d6cb3be2..7e4248eab 100644 --- a/src/kdefrontend/spreadsheet/PlotDataDialog.cpp +++ b/src/kdefrontend/spreadsheet/PlotDataDialog.cpp @@ -1,690 +1,687 @@ /*************************************************************************** File : PlotDataDialog.cpp Project : LabPlot Description : Dialog for generating plots for the spreadsheet data -------------------------------------------------------------------- - Copyright : (C) 2017 by Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2017-2019 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 "PlotDataDialog.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/core/column/Column.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/plots/cartesian/XYAnalysisCurve.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/plots/cartesian/XYDataReductionCurve.h" #include "backend/worksheet/plots/cartesian/XYDifferentiationCurve.h" #include "backend/worksheet/plots/cartesian/XYIntegrationCurve.h" #include "backend/worksheet/plots/cartesian/XYInterpolationCurve.h" #include "backend/worksheet/plots/cartesian/XYSmoothCurve.h" #include "backend/worksheet/plots/cartesian/XYFitCurve.h" #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h" #ifdef HAVE_MQTT #include "backend/datasources/MQTTTopic.h" #endif #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/TextLabel.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include -#include +#include #include #include #include #include "ui_plotdatawidget.h" /*! \class PlotDataDialog \brief Dialog for generating plots for the spreadsheet data. \ingroup kdefrontend */ PlotDataDialog::PlotDataDialog(Spreadsheet* s, PlotType type, QWidget* parent) : QDialog(parent), ui(new Ui::PlotDataWidget()), m_spreadsheet(s), m_plotsModel(new AspectTreeModel(m_spreadsheet->project())), m_worksheetsModel(new AspectTreeModel(m_spreadsheet->project())), m_plotType(type) { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle(i18nc("@title:window", "Plot Spreadsheet Data")); setWindowIcon(QIcon::fromTheme("office-chart-line")); QWidget* mainWidget = new QWidget(this); ui->setupUi(mainWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_okButton = buttonBox->button(QDialogButtonBox::Ok); m_okButton->setDefault(true); m_okButton->setToolTip(i18n("Plot the selected data")); m_okButton->setText(i18n("&Plot")); auto* layout = new QVBoxLayout(this); layout->addWidget(mainWidget); layout->addWidget(buttonBox); setLayout(layout); //create combox boxes for the existing plots and worksheets auto* gridLayout = dynamic_cast(ui->gbPlotPlacement->layout()); cbExistingPlots = new TreeViewComboBox(ui->gbPlotPlacement); cbExistingPlots->setMinimumWidth(250);//TODO: use proper sizeHint in TreeViewComboBox gridLayout->addWidget(cbExistingPlots, 0, 1, 1, 1); cbExistingWorksheets = new TreeViewComboBox(ui->gbPlotPlacement); cbExistingWorksheets->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); gridLayout->addWidget(cbExistingWorksheets, 1, 1, 1, 1); QList list; list<<"Folder"<<"Worksheet"<<"CartesianPlot"; cbExistingPlots->setTopLevelClasses(list); list.clear(); list<<"CartesianPlot"; m_plotsModel->setSelectableAspects(list); cbExistingPlots->setModel(m_plotsModel); //select the first available plot, if available auto plots = m_spreadsheet->project()->children(AbstractAspect::Recursive); if (!plots.isEmpty()) { const auto* plot = plots.first(); cbExistingPlots->setCurrentModelIndex(m_plotsModel->modelIndexOfAspect(plot)); } list.clear(); list<<"Folder"<<"Worksheet"; cbExistingWorksheets->setTopLevelClasses(list); list.clear(); list<<"Worksheet"; m_worksheetsModel->setSelectableAspects(list); cbExistingWorksheets->setModel(m_worksheetsModel); //select the first available worksheet, if available auto worksheets = m_spreadsheet->project()->children(AbstractAspect::Recursive); if (!worksheets.isEmpty()) { const auto* worksheet = worksheets.first(); cbExistingWorksheets->setCurrentModelIndex(m_worksheetsModel->modelIndexOfAspect(worksheet)); } //hide the check box for creation of original data, only shown if analysis curves are to be created ui->chkCreateDataCurve->setVisible(false); //SIGNALs/SLOTs connect(buttonBox, &QDialogButtonBox::accepted, this, [=]() { hide(); plot(); }); connect(buttonBox, &QDialogButtonBox::rejected, this, &PlotDataDialog::reject); connect(buttonBox, &QDialogButtonBox::accepted, this, &PlotDataDialog::accept); connect(ui->rbCurvePlacement1, &QRadioButton::toggled, this, &PlotDataDialog::curvePlacementChanged); connect(ui->rbCurvePlacement2, &QRadioButton::toggled, this, &PlotDataDialog::curvePlacementChanged); connect(ui->rbPlotPlacement1, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged); connect(ui->rbPlotPlacement2, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged); connect(ui->rbPlotPlacement3, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged); connect(cbExistingPlots, &TreeViewComboBox::currentModelIndexChanged, this, &PlotDataDialog::checkOkButton); connect(cbExistingWorksheets, &TreeViewComboBox::currentModelIndexChanged, this, &PlotDataDialog::checkOkButton); - QTimer::singleShot(0, this, &PlotDataDialog::loadSettings); -} - -void PlotDataDialog::loadSettings() { //restore saved settings if available - QApplication::processEvents(QEventLoop::AllEvents, 0); - const KConfigGroup conf(KSharedConfig::openConfig(), "PlotDataDialog"); + create(); // ensure there's a window created + KConfigGroup conf(KSharedConfig::openConfig(), "PlotDataDialog"); if (conf.exists()) { - KWindowConfig::restoreWindowSize(windowHandle(), conf); - int index = conf.readEntry("CurvePlacement", 0); if (index == 2) ui->rbCurvePlacement2->setChecked(true); index = conf.readEntry("PlotPlacement", 0); if (index == 2) ui->rbPlotPlacement2->setChecked(true); if (index == 3) ui->rbPlotPlacement3->setChecked(true); + + KWindowConfig::restoreWindowSize(windowHandle(), conf); + resize(windowHandle()->size()); // workaround for QTBUG-40584 } else - resize( QSize(0,0).expandedTo(minimumSize()) ); + resize(QSize(0, 0).expandedTo(minimumSize())); processColumns(); plotPlacementChanged(); } PlotDataDialog::~PlotDataDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "PlotDataDialog"); int index = 0; if (ui->rbCurvePlacement1->isChecked()) index = 1; if (ui->rbCurvePlacement2->isChecked()) index = 2; conf.writeEntry("CurvePlacement", index); if (ui->rbPlotPlacement1->isChecked()) index = 1; if (ui->rbPlotPlacement2->isChecked()) index = 2; if (ui->rbPlotPlacement3->isChecked()) index = 3; conf.writeEntry("PlotPlacement", index); KWindowConfig::saveWindowSize(windowHandle(), conf); delete m_plotsModel; delete m_worksheetsModel; } void PlotDataDialog::setAnalysisAction(AnalysisAction action) { m_analysisAction = action; m_analysisMode = true; ui->chkCreateDataCurve->setVisible(true); } void PlotDataDialog::processColumns() { //columns to plot auto* view = reinterpret_cast(m_spreadsheet->view()); QVector selectedColumns = view->selectedColumns(true); //use all spreadsheet columns if no columns are selected if (selectedColumns.isEmpty()) selectedColumns = m_spreadsheet->children(); //skip error and non-plottable columns for (Column* col : selectedColumns) { if ((col->plotDesignation() == AbstractColumn::X || col->plotDesignation() == AbstractColumn::Y || col->plotDesignation() == AbstractColumn::NoDesignation) && col->isPlottable()) m_columns << col; } //disable everything if the spreadsheet doesn't have any columns to plot if (m_columns.isEmpty()) { ui->gbCurvePlacement->setEnabled(false); ui->gbPlotPlacement->setEnabled(false); return; } //determine the column names //and the name of the first column having "X" as the plot designation (relevant for xy-curves only) QStringList columnNames; QString xColumnName; for (const Column* column : m_columns) { columnNames << column->name(); if (m_plotType == PlotXYCurve && xColumnName.isEmpty() && column->plotDesignation() == AbstractColumn::X) xColumnName = column->name(); } if (m_plotType == PlotXYCurve && xColumnName.isEmpty()) { //no X-column was selected -> look for the first non-selected X-column left to the first selected column const int index = m_spreadsheet->indexOfChild(selectedColumns.first()) - 1; if (index >= 0) { for (int i = index; i >= 0; --i) { Column* column = m_spreadsheet->column(i); if (column->plotDesignation() == AbstractColumn::X) { xColumnName = column->name(); m_columns.prepend(column); columnNames.prepend(xColumnName); break; } } } } switch (m_plotType) { case PlotXYCurve: processColumnsForXYCurve(columnNames, xColumnName); break; case PlotHistogram: processColumnsForHistogram(columnNames); break; } } void PlotDataDialog::processColumnsForXYCurve(const QStringList& columnNames, const QString& xColumnName) { m_columnComboBoxes << ui->cbXColumn; m_columnComboBoxes << ui->cbYColumn; //ui-widget only has one combobox for the y-data -> add additional comboboxes dynamically if required if (m_columns.size()>2) { auto* gridLayout = dynamic_cast(ui->scrollAreaYColumns->widget()->layout()); for (int i = 2; i < m_columns.size(); ++i) { QLabel* label = new QLabel(i18n("Y-data")); auto* comboBox = new QComboBox(); gridLayout->addWidget(label, i+1, 0, 1, 1); gridLayout->addWidget(comboBox, i+1, 2, 1, 1); m_columnComboBoxes << comboBox; } } else { //two columns provided, only one curve is possible -> hide the curve placement options ui->rbCurvePlacement1->setChecked(true); ui->gbCurvePlacement->hide(); ui->gbPlotPlacement->setTitle(i18n("Add Curve to")); } //show all selected/available column names in the data comboboxes for (QComboBox* const comboBox : m_columnComboBoxes) comboBox->addItems(columnNames); if (!xColumnName.isEmpty()) { //show in the X-data combobox the first column having X as the plot designation ui->cbXColumn->setCurrentIndex(ui->cbXColumn->findText(xColumnName)); //for the remaining columns, show the names in the comboboxes for the Y-data //TODO: handle columns with error-designations int yColumnIndex = 1; //the index of the first Y-data comboBox in m_columnComboBoxes for (const QString& name : columnNames) { if (name != xColumnName) { QComboBox* comboBox = m_columnComboBoxes[yColumnIndex]; comboBox->setCurrentIndex(comboBox->findText(name)); yColumnIndex++; } } } else { //no column with "x plot designation" is selected, simply show all columns in the order they were selected. //first selected column will serve as the x-column. int yColumnIndex = 0; for (const QString& name : columnNames) { QComboBox* comboBox = m_columnComboBoxes[yColumnIndex]; comboBox->setCurrentIndex(comboBox->findText(name)); yColumnIndex++; } } } void PlotDataDialog::processColumnsForHistogram(const QStringList& columnNames) { ui->gbData->setTitle(i18n("Histogram Data")); ui->line->hide(); ui->chkCreateDataCurve->hide(); //use the already available cbXColumn combo box ui->lXColumn->setText(i18n("Data")); m_columnComboBoxes << ui->cbXColumn; ui->cbXColumn->addItems(columnNames); ui->cbXColumn->setCurrentIndex(0); if (m_columns.size() == 1) { //one column provided, only one histogram is possible //-> hide the curve placement options and the scroll areas for further columns ui->rbCurvePlacement1->setChecked(true); ui->gbCurvePlacement->hide(); ui->gbPlotPlacement->setTitle(i18n("Add Histogram to")); ui->scrollAreaYColumns->hide(); } else { ui->gbCurvePlacement->setTitle(i18n("Histogram Placement")); ui->rbCurvePlacement1->setText(i18n("All histograms in one plot")); ui->rbCurvePlacement2->setText(i18n("One plot per histogram")); ui->gbPlotPlacement->setTitle(i18n("Add Histograms to")); //use the already available cbYColumn combo box ui->lYColumn->setText(i18n("Data")); m_columnComboBoxes << ui->cbYColumn; ui->cbYColumn->addItems(columnNames); ui->cbYColumn->setCurrentIndex(1); //add a ComboBox for every further column to be plotted auto* gridLayout = dynamic_cast(ui->scrollAreaYColumns->widget()->layout()); for (int i = 2; i < m_columns.size(); ++i) { auto* label = new QLabel(i18n("Data")); auto* comboBox = new QComboBox(); gridLayout->addWidget(label, i+1, 0, 1, 1); gridLayout->addWidget(comboBox, i+1, 2, 1, 1); comboBox->addItems(columnNames); comboBox->setCurrentIndex(i); m_columnComboBoxes << comboBox; } } } void PlotDataDialog::plot() { DEBUG("PlotDataDialog::plot()"); WAIT_CURSOR; if (ui->rbPlotPlacement1->isChecked()) { //add curves to an existing plot auto* aspect = static_cast(cbExistingPlots->currentModelIndex().internalPointer()); auto* plot = dynamic_cast(aspect); plot->beginMacro( i18n("Plot data from %1", m_spreadsheet->name()) ); addCurvesToPlot(plot); plot->endMacro(); } else if (ui->rbPlotPlacement2->isChecked()) { //add curves to a new plot in an existing worksheet auto* aspect = static_cast(cbExistingWorksheets->currentModelIndex().internalPointer()); auto* worksheet = dynamic_cast(aspect); worksheet->beginMacro( i18n("Plot data from %1", m_spreadsheet->name()) ); if (ui->rbCurvePlacement1->isChecked()) { //all curves in one plot CartesianPlot* plot = new CartesianPlot( i18n("Plot data from %1", m_spreadsheet->name()) ); plot->initDefault(CartesianPlot::FourAxes); //set the axis titles before we add the plot to the worksheet //set the x-axis names const QString& xColumnName = ui->cbXColumn->currentText(); for (auto* axis : plot->children()) { if (axis->orientation() == Axis::AxisHorizontal) { axis->title()->setText(xColumnName); break; } } //if we only have one single y-column to plot, we can set the title of the y-axes if (m_columnComboBoxes.size() == 2) { const QString& yColumnName = m_columnComboBoxes[1]->currentText(); for (auto* axis : plot->children()) { if (axis->orientation() == Axis::AxisVertical) { axis->title()->setText(yColumnName); break; } } } worksheet->addChild(plot); addCurvesToPlot(plot); } else { //one plot per curve addCurvesToPlots(worksheet); } worksheet->endMacro(); } else { //add curves to a new plot(s) in a new worksheet AbstractAspect* parent = m_spreadsheet->parentAspect(); #ifdef HAVE_MQTT MQTTTopic* topic = qobject_cast(m_spreadsheet); if (topic != nullptr) parent = qobject_cast(m_spreadsheet->project()); #endif parent->beginMacro( i18n("Plot data from %1", m_spreadsheet->name()) ); Worksheet* worksheet = new Worksheet(i18n("Plot data from %1", m_spreadsheet->name())); parent->addChild(worksheet); if (ui->rbCurvePlacement1->isChecked()) { //all curves in one plot CartesianPlot* plot = new CartesianPlot( i18n("Plot data from %1", m_spreadsheet->name()) ); plot->initDefault(CartesianPlot::FourAxes); //set the axis titles before we add the plot to the worksheet //set the x-axis names const QString& xColumnName = ui->cbXColumn->currentText(); for (auto* axis : plot->children()) { if (axis->orientation() == Axis::AxisHorizontal) { axis->title()->setText(xColumnName); break; } } //if we only have one single y-column to plot, we can set the title of the y-axes if (m_columnComboBoxes.size() == 2) { const QString& yColumnName = m_columnComboBoxes[1]->currentText(); for (auto* axis : plot->children()) { if (axis->orientation() == Axis::AxisVertical) { axis->title()->setText(yColumnName); break; } } } worksheet->addChild(plot); addCurvesToPlot(plot); } else { //one plot per curve addCurvesToPlots(worksheet); } parent->endMacro(); } RESET_CURSOR; } Column* PlotDataDialog::columnFromName(const QString& name) const { for (auto* column : m_columns) { if (column->name() == name) return column; } return nullptr; } /*! * * for the selected columns in this dialog, creates a curve in the already existing plot \c plot. */ void PlotDataDialog::addCurvesToPlot(CartesianPlot* plot) const { QApplication::processEvents(QEventLoop::AllEvents, 100); switch (m_plotType) { case PlotXYCurve: { Column* xColumn = columnFromName(ui->cbXColumn->currentText()); for (auto* comboBox : m_columnComboBoxes) { const QString& name = comboBox->currentText(); Column* yColumn = columnFromName(name); if (yColumn == xColumn) continue; addCurve(name, xColumn, yColumn, plot); } break; } case PlotHistogram: { for (auto* comboBox : m_columnComboBoxes) { const QString& name = comboBox->currentText(); Column* column = columnFromName(name); addHistogram(name, column, plot); } break; } } plot->scaleAuto(); } /*! * for the selected columns in this dialog, creates a plot and a curve in the already existing worksheet \c worksheet. */ void PlotDataDialog::addCurvesToPlots(Worksheet* worksheet) const { QApplication::processEvents(QEventLoop::AllEvents, 100); worksheet->setSuppressLayoutUpdate(true); switch (m_plotType) { case PlotXYCurve: { const QString& xColumnName = ui->cbXColumn->currentText(); Column* xColumn = columnFromName(xColumnName); for (auto* comboBox : m_columnComboBoxes) { const QString& name = comboBox->currentText(); Column* yColumn = columnFromName(name); CartesianPlot* plot = new CartesianPlot(i18n("Plot %1", name)); plot->initDefault(CartesianPlot::FourAxes); //set the axis names in the new plot bool xSet = false; bool ySet = false; for (auto* axis : plot->children()) { if (axis->orientation() == Axis::AxisHorizontal && !xSet) { axis->title()->setText(xColumnName); xSet = true; } else if (axis->orientation() == Axis::AxisVertical && !ySet) { axis->title()->setText(name); ySet = true; } } worksheet->addChild(plot); addCurve(name, xColumn, yColumn, plot); plot->scaleAuto(); } break; } case PlotHistogram: { for (auto* comboBox : m_columnComboBoxes) { const QString& name = comboBox->currentText(); Column* column = columnFromName(name); CartesianPlot* plot = new CartesianPlot(i18n("Plot %1", name)); plot->initDefault(CartesianPlot::FourAxes); //set the axis names in the new plot bool xSet = false; for (auto* axis : plot->children()) { if (axis->orientation() == Axis::AxisHorizontal && !xSet) { axis->title()->setText(name); xSet = true; } } worksheet->addChild(plot); addHistogram(name, column, plot); plot->scaleAuto(); } } } worksheet->setSuppressLayoutUpdate(false); worksheet->updateLayout(); } /*! * helper function that does the actual creation of the curve and adding it as child to the \c plot. */ void PlotDataDialog::addCurve(const QString& name, Column* xColumn, Column* yColumn, CartesianPlot* plot) const { DEBUG("PlotDataDialog::addCurve()"); if (!m_analysisMode) { auto* curve = new XYCurve(name); curve->suppressRetransform(true); curve->setXColumn(xColumn); curve->setYColumn(yColumn); curve->suppressRetransform(false); plot->addChild(curve); } else { bool createDataCurve = ui->chkCreateDataCurve->isChecked(); XYCurve* curve = nullptr; if (createDataCurve) { curve = new XYCurve(name); curve->suppressRetransform(true); curve->setXColumn(xColumn); curve->setYColumn(yColumn); curve->suppressRetransform(false); plot->addChild(curve); } XYAnalysisCurve* analysisCurve = nullptr; switch (m_analysisAction) { case DataReduction: analysisCurve = new XYDataReductionCurve(i18n("Reduction of '%1'", name)); break; case Differentiation: analysisCurve = new XYDifferentiationCurve(i18n("Derivative of '%1'", name)); break; case Integration: analysisCurve = new XYIntegrationCurve(i18n("Integral of '%1'", name)); break; case Interpolation: analysisCurve = new XYInterpolationCurve(i18n("Interpolation of '%1'", name)); break; case Smoothing: analysisCurve = new XYSmoothCurve(i18n("Smoothing of '%1'", name)); break; case FitLinear: case FitPower: case FitExp1: case FitExp2: case FitInvExp: case FitGauss: case FitCauchyLorentz: case FitTan: case FitTanh: case FitErrFunc: case FitCustom: analysisCurve = new XYFitCurve(i18n("Fit to '%1'", name)); static_cast(analysisCurve)->initFitData(m_analysisAction); static_cast(analysisCurve)->initStartValues(curve); break; case FourierFilter: analysisCurve = new XYFourierFilterCurve(i18n("Fourier Filter of '%1'", name)); break; } if (analysisCurve != nullptr) { analysisCurve->suppressRetransform(true); analysisCurve->setXDataColumn(xColumn); analysisCurve->setYDataColumn(yColumn); analysisCurve->recalculate(); analysisCurve->suppressRetransform(false); plot->addChild(analysisCurve); } } } void PlotDataDialog::addHistogram(const QString& name, Column* column, CartesianPlot* plot) const { auto* hist = new Histogram(name); plot->addChild(hist); // hist->suppressRetransform(true); hist->setDataColumn(column); // hist->suppressRetransform(false); } //################################################################ //########################## Slots ############################### //################################################################ void PlotDataDialog::curvePlacementChanged() { if (ui->rbCurvePlacement1->isChecked()) { ui->rbPlotPlacement1->setEnabled(true); ui->rbPlotPlacement2->setText(i18n("new plot in an existing worksheet")); ui->rbPlotPlacement3->setText(i18n("new plot in a new worksheet")); } else { ui->rbPlotPlacement1->setEnabled(false); if (ui->rbPlotPlacement1->isChecked()) ui->rbPlotPlacement2->setChecked(true); ui->rbPlotPlacement2->setText(i18n("new plots in an existing worksheet")); ui->rbPlotPlacement3->setText(i18n("new plots in a new worksheet")); } } void PlotDataDialog::plotPlacementChanged() { if (ui->rbPlotPlacement1->isChecked()) { cbExistingPlots->setEnabled(true); cbExistingWorksheets->setEnabled(false); } else if (ui->rbPlotPlacement2->isChecked()) { cbExistingPlots->setEnabled(false); cbExistingWorksheets->setEnabled(true); } else { cbExistingPlots->setEnabled(false); cbExistingWorksheets->setEnabled(false); } checkOkButton(); } void PlotDataDialog::checkOkButton() { bool enable = false; QString msg; if ( (m_plotType == PlotXYCurve && (ui->cbXColumn->currentIndex() == -1 || ui->cbYColumn->currentIndex() == -1)) || (m_plotType == PlotHistogram && ui->cbXColumn->currentIndex() == -1) ) msg = i18n("No data selected to plot."); else if (ui->rbPlotPlacement1->isChecked()) { AbstractAspect* aspect = static_cast(cbExistingPlots->currentModelIndex().internalPointer()); enable = (aspect != nullptr); if (!enable) msg = i18n("An already existing plot has to be selected."); } else if (ui->rbPlotPlacement2->isChecked()) { AbstractAspect* aspect = static_cast(cbExistingWorksheets->currentModelIndex().internalPointer()); enable = (aspect != nullptr); if (!enable) msg = i18n("An already existing worksheet has to be selected."); } else enable = true; m_okButton->setEnabled(enable); if (enable) m_okButton->setToolTip(i18n("Close the dialog and plot the data.")); else m_okButton->setToolTip(msg); } diff --git a/src/kdefrontend/spreadsheet/PlotDataDialog.h b/src/kdefrontend/spreadsheet/PlotDataDialog.h index 4b44db06c..584dcd244 100644 --- a/src/kdefrontend/spreadsheet/PlotDataDialog.h +++ b/src/kdefrontend/spreadsheet/PlotDataDialog.h @@ -1,93 +1,92 @@ /*************************************************************************** File : PlotDataDialog.h Project : LabPlot Description : Dialog for generating plots for the spreadsheet data -------------------------------------------------------------------- Copyright : (C) 2017 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 * * * ***************************************************************************/ #ifndef PLOTDATADIALOG_H #define PLOTDATADIALOG_H namespace Ui { class PlotDataWidget; } #include class QComboBox; class AspectTreeModel; class CartesianPlot; class Column; class Spreadsheet; class TreeViewComboBox; class Worksheet; class PlotDataDialog : public QDialog { Q_OBJECT public: enum PlotType {PlotXYCurve, PlotHistogram}; enum AnalysisAction {DataReduction, Differentiation, Integration, Interpolation, Smoothing, FitLinear, FitPower, FitExp1, FitExp2, FitInvExp, FitGauss, FitCauchyLorentz, FitTan, FitTanh, FitErrFunc, FitCustom, FourierFilter}; explicit PlotDataDialog(Spreadsheet*, PlotType = PlotXYCurve, QWidget* parent = nullptr); ~PlotDataDialog() override; void setAnalysisAction(AnalysisAction); private: Ui::PlotDataWidget* ui; QPushButton* m_okButton; Spreadsheet* m_spreadsheet; TreeViewComboBox* cbExistingPlots; TreeViewComboBox* cbExistingWorksheets; QVector m_columns; QVector m_columnComboBoxes; AspectTreeModel* m_plotsModel; AspectTreeModel* m_worksheetsModel; PlotType m_plotType; AnalysisAction m_analysisAction{Differentiation}; bool m_analysisMode{false}; void processColumns(); void processColumnsForXYCurve(const QStringList& columnNames, const QString& xColumnName); void processColumnsForHistogram(const QStringList&); void addCurvesToPlot(CartesianPlot*) const; void addCurvesToPlots(Worksheet*) const; void addCurve(const QString& name, Column* xColumn, Column* yColumn, CartesianPlot*) const; void addHistogram(const QString& name, Column* column, CartesianPlot*) const; Column* columnFromName(const QString&) const; protected slots: virtual void checkOkButton(); private slots: void plot(); void curvePlacementChanged(); void plotPlacementChanged(); - void loadSettings(); }; #endif diff --git a/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp b/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp index dc90426bf..710f7f7ac 100644 --- a/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp @@ -1,917 +1,920 @@ /*************************************************************************** File : RandomValuesDialog.cpp Project : LabPlot Description : Dialog for generating non-uniformly distributed random numbers -------------------------------------------------------------------- - Copyright : (C) 2014 by Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2014-2019 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016-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 "RandomValuesDialog.h" #include "backend/core/column/Column.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include +#include #include #include -#include +#include #include #include #include #include #include extern "C" { #include "backend/nsl/nsl_sf_stats.h" #include #include } /*! \class RandomValuesDialog \brief Dialog for generating non-uniform random numbers. \ingroup kdefrontend */ RandomValuesDialog::RandomValuesDialog(Spreadsheet* s, QWidget* parent) : QDialog(parent), m_spreadsheet(s) { setWindowTitle(i18nc("@title:window", "Random Values")); QWidget* mainWidget = new QWidget(this); ui.setupUi(mainWidget); auto* layout = new QVBoxLayout(this); auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_okButton = buttonBox->button(QDialogButtonBox::Ok); m_okButton->setDefault(true); m_okButton->setToolTip(i18n("Generate random values according to the selected distribution")); m_okButton->setText(i18n("&Generate")); connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &RandomValuesDialog::close); connect(buttonBox, &QDialogButtonBox::accepted, this, &RandomValuesDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &RandomValuesDialog::reject); layout->addWidget(mainWidget); layout->addWidget(buttonBox); setLayout(layout); setAttribute(Qt::WA_DeleteOnClose); for (int i = 0; i < NSL_SF_STATS_DISTRIBUTION_RNG_COUNT; i++) ui.cbDistribution->addItem(i18n(nsl_sf_stats_distribution_name[i]), i); //use white background in the preview label QPalette p; p.setColor(QPalette::Window, Qt::white); ui.lFuncPic->setAutoFillBackground(true); ui.lFuncPic->setPalette(p); ui.leParameter1->setClearButtonEnabled(true); ui.leParameter2->setClearButtonEnabled(true); ui.leParameter3->setClearButtonEnabled(true); ui.leParameter1->setValidator( new QDoubleValidator(ui.leParameter1) ); ui.leParameter2->setValidator( new QDoubleValidator(ui.leParameter2) ); ui.leParameter3->setValidator( new QDoubleValidator(ui.leParameter3) ); connect(ui.cbDistribution, static_cast(&QComboBox::currentIndexChanged), this, &RandomValuesDialog::distributionChanged); connect(ui.leParameter1, &QLineEdit::textChanged, this, &RandomValuesDialog::checkValues); connect(ui.leParameter2, &QLineEdit::textChanged, this, &RandomValuesDialog::checkValues); connect(ui.leParameter3, &QLineEdit::textChanged, this, &RandomValuesDialog::checkValues); connect(buttonBox, &QDialogButtonBox::accepted, this, &RandomValuesDialog::generate); //restore saved settings if available + create(); // ensure there's a window created const KConfigGroup conf(KSharedConfig::openConfig(), "RandomValuesDialog"); if (conf.exists()) { ui.cbDistribution->setCurrentIndex(conf.readEntry("Distribution", 0)); this->distributionChanged(ui.cbDistribution->currentIndex()); //if index=0 no signal is emitted above, call this slot directly here ui.leParameter1->setText(conf.readEntry("Parameter1")); ui.leParameter2->setText(conf.readEntry("Parameter2")); ui.leParameter3->setText(conf.readEntry("Parameter3")); KWindowConfig::restoreWindowSize(windowHandle(), conf); + resize(windowHandle()->size()); // workaround for QTBUG-40584 } else { //Gaussian distribution as default this->distributionChanged(0); resize( QSize(400,0).expandedTo(minimumSize()) ); } } RandomValuesDialog::~RandomValuesDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "RandomValuesDialog"); conf.writeEntry("Distribution", ui.cbDistribution->currentIndex()); conf.writeEntry("Parameter1", ui.leParameter1->text()); conf.writeEntry("Parameter2", ui.leParameter2->text()); conf.writeEntry("Parameter3", ui.leParameter3->text()); KWindowConfig::saveWindowSize(windowHandle(), conf); } void RandomValuesDialog::setColumns(QVector columns) { m_columns = columns; } void RandomValuesDialog::distributionChanged(int index) { nsl_sf_stats_distribution dist = (nsl_sf_stats_distribution)ui.cbDistribution->itemData(index).toInt(); // default settings (used by most distributions) ui.lParameter1->show(); ui.leParameter1->show(); ui.lParameter2->show(); ui.leParameter2->show(); ui.lParameter3->hide(); ui.leParameter3->hide(); ui.lFunc->setText("p(x) ="); switch (dist) { case nsl_sf_stats_gaussian: ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_gaussian_tail: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.lParameter3->setText("a ="); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("0.0"); break; case nsl_sf_stats_exponential: ui.lParameter1->setText(UTF8_QSTRING("λ =")); ui.leParameter1->setText("1.0"); ui.lParameter2->setText(UTF8_QSTRING("μ =")); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_laplace: ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_exponential_power: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.lParameter3->setText("b ="); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("1.0"); break; case nsl_sf_stats_cauchy_lorentz: ui.lParameter1->setText(UTF8_QSTRING("γ =")); ui.lParameter2->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_rayleigh: ui.lParameter2->hide(); ui.leParameter2->hide(); ui.lParameter1->setText(UTF8_QSTRING("σ =")); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_rayleigh_tail: ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_landau: ui.lParameter1->hide(); ui.leParameter1->hide(); ui.lParameter2->hide(); ui.leParameter2->hide(); break; case nsl_sf_stats_levy_alpha_stable: ui.lParameter1->setText("c ="); ui.lParameter2->setText(UTF8_QSTRING("α =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_levy_skew_alpha_stable: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(UTF8_QSTRING("c =")); ui.lParameter2->setText(UTF8_QSTRING("α =")); ui.lParameter3->setText(UTF8_QSTRING("β =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("1.0"); break; case nsl_sf_stats_flat: ui.lParameter1->setText("a ="); ui.lParameter2->setText("b ="); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_gamma: ui.lParameter1->setText(UTF8_QSTRING("θ =")); ui.lParameter2->setText("k ="); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_weibull: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText("k ="); ui.lParameter2->setText(UTF8_QSTRING("λ =")); ui.lParameter3->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("1.0"); break; case nsl_sf_stats_beta: ui.lParameter1->setText("a ="); ui.lParameter2->setText("b ="); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_gumbel1: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(UTF8_QSTRING("σ =")); ui.lParameter2->setText(UTF8_QSTRING("β =")); ui.lParameter3->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("0.0"); break; case nsl_sf_stats_gumbel2: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText("a ="); ui.lParameter2->setText("b ="); ui.lParameter3->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("0.0"); break; case nsl_sf_stats_pareto: ui.lParameter1->setText("a ="); ui.lParameter2->setText("b ="); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_lognormal: ui.lParameter1->setText(UTF8_QSTRING("σ =")); ui.lParameter2->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_chi_squared: ui.lParameter2->hide(); ui.leParameter2->hide(); ui.lParameter1->setText("n ="); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_fdist: ui.lParameter1->setText(UTF8_QSTRING("ν₁ =")); ui.lParameter2->setText(UTF8_QSTRING("ν₂ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_tdist: ui.lParameter2->hide(); ui.leParameter2->hide(); ui.lParameter1->setText(UTF8_QSTRING("ν =")); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_logistic: ui.lParameter1->setText(UTF8_QSTRING("σ =")); ui.lParameter2->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_poisson: ui.lParameter2->hide(); ui.leParameter2->hide(); ui.lFunc->setText("p(k) ="); ui.lParameter1->setText(UTF8_QSTRING("λ =")); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_bernoulli: case nsl_sf_stats_geometric: case nsl_sf_stats_logarithmic: ui.lParameter2->hide(); ui.leParameter2->hide(); if (dist == nsl_sf_stats_bernoulli) ui.lFunc->setText(""); else ui.lFunc->setText("p(k) ="); ui.lParameter1->setText("p ="); ui.leParameter1->setText("0.5"); break; case nsl_sf_stats_binomial: case nsl_sf_stats_negative_binomial: case nsl_sf_stats_pascal: ui.lFunc->setText("p(k) ="); ui.lParameter1->setText("p ="); ui.lParameter2->setText("n ="); ui.leParameter1->setText("0.5"); ui.leParameter2->setText("100"); break; case nsl_sf_stats_hypergeometric: ui.lParameter3->show(); ui.leParameter3->show(); ui.lFunc->setText("p(k) ="); ui.lParameter1->setText("n1 ="); ui.lParameter2->setText("n2 ="); ui.lParameter3->setText("t ="); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("2.0"); ui.leParameter3->setText("3.0"); break; case nsl_sf_stats_maxwell_boltzmann: // additional non-GSL distros case nsl_sf_stats_sech: case nsl_sf_stats_levy: case nsl_sf_stats_frechet: break; } QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/gsl_distributions/" + QString(nsl_sf_stats_distribution_pic_name[dist]) + ".png"); DEBUG("Distribution pixmap path = " << file.toStdString()); ui.lFuncPic->setPixmap(QPixmap(file)); } void RandomValuesDialog::checkValues() { if (ui.leParameter1->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } if (ui.leParameter2->isVisible() && ui.leParameter2->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } if (ui.leParameter3->isVisible() && ui.leParameter3->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } m_okButton->setEnabled(true); return; } void RandomValuesDialog::generate() { Q_ASSERT(m_spreadsheet); //create a generator chosen by the environment variable GSL_RNG_TYPE gsl_rng_env_setup(); const gsl_rng_type* T = gsl_rng_default; gsl_rng* r = gsl_rng_alloc(T); WAIT_CURSOR; for (auto* col : m_columns) col->setSuppressDataChangedSignal(true); m_spreadsheet->beginMacro(i18np("%1: fill column with non-uniform random numbers", "%1: fill columns with non-uniform random numbers", m_spreadsheet->name(), m_columns.size())); const int index = ui.cbDistribution->currentIndex(); const nsl_sf_stats_distribution dist = (nsl_sf_stats_distribution)ui.cbDistribution->itemData(index).toInt(); DEBUG("random number distribution: " << nsl_sf_stats_distribution_name[dist]); const int rows = m_spreadsheet->rowCount(); QVector data(rows); QVector data_int(rows); switch (dist) { case nsl_sf_stats_gaussian: { double mu = ui.leParameter1->text().toDouble(); double sigma = ui.leParameter2->text().toDouble(); DEBUG(" mu = " << mu << ", sigma = " << sigma); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_gaussian(r, sigma) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_gaussian(r, sigma) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_gaussian_tail: { double mu = ui.leParameter1->text().toDouble(); double sigma = ui.leParameter2->text().toDouble(); double a = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_gaussian_tail(r, a, sigma) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_gaussian_tail(r, a, sigma) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_exponential: { double l = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { //GSL uses the inverse for exp. distrib. for (int i = 0; i < rows; ++i) data[i] = gsl_ran_exponential(r, 1./l) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_exponential(r, 1./l) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_laplace: { double mu = ui.leParameter1->text().toDouble(); double s = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_laplace(r, s) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_laplace(r, s) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_exponential_power: { double mu = ui.leParameter1->text().toDouble(); double a = ui.leParameter2->text().toDouble(); double b = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_exppow(r, a, b) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_exppow(r, a, b) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_cauchy_lorentz: { double gamma = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_cauchy(r, gamma) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_cauchy(r, gamma) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_rayleigh: { double s = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_rayleigh(r, s); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_rayleigh(r, s)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_rayleigh_tail: { double mu = ui.leParameter1->text().toDouble(); double sigma = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_rayleigh_tail(r, mu, sigma); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_rayleigh_tail(r, mu, sigma)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_landau: for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_landau(r); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_landau(r)); col->replaceInteger(0, data_int); } } break; case nsl_sf_stats_levy_alpha_stable: { double c = ui.leParameter1->text().toDouble(); double alpha = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_levy(r, c, alpha); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_levy(r, c, alpha)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_levy_skew_alpha_stable: { double c = ui.leParameter1->text().toDouble(); double alpha = ui.leParameter2->text().toDouble(); double beta = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_levy_skew(r, c, alpha, beta); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_levy_skew(r, c, alpha, beta)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_gamma: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_gamma(r, a, b); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_gamma(r, a, b)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_flat: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_flat(r, a, b); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_flat(r, a, b)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_lognormal: { double s = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_lognormal(r, mu, s); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_lognormal(r, mu, s)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_chi_squared: { double n = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_chisq(r, n); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_chisq(r, n)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_fdist: { double nu1 = ui.leParameter1->text().toDouble(); double nu2 = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_fdist(r, nu1, nu2); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_fdist(r, nu1, nu2)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_tdist: { double nu = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_tdist(r, nu); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_tdist(r, nu)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_beta: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_beta(r, a, b); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_beta(r, a, b)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_logistic: { double s = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_logistic(r, s) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_logistic(r, s) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_pareto: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_pareto(r, a, b); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_pareto(r, a, b)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_weibull: { double k = ui.leParameter1->text().toDouble(); double l = ui.leParameter2->text().toDouble(); double mu = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_weibull(r, l, k) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_weibull(r, l, k) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_gumbel1: { double s = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); double mu = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_gumbel1(r, 1./s, b) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_gumbel1(r, 1./s, b) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_gumbel2: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); double mu = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_gumbel2(r, a, b) + mu; col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_gumbel2(r, a, b) + mu); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_poisson: { double l = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_poisson(r, l); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_poisson(r, l)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_bernoulli: { double p = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_bernoulli(r, p); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_bernoulli(r, p)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_binomial: { double p = ui.leParameter1->text().toDouble(); double n = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_binomial(r, p, n); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_binomial(r, p, n)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_negative_binomial: { double p = ui.leParameter1->text().toDouble(); double n = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_negative_binomial(r, p, n); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_negative_binomial(r, p, n)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_pascal: { double p = ui.leParameter1->text().toDouble(); double n = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_pascal(r, p, n); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_pascal(r, p, n)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_geometric: { double p = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_geometric(r, p); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_geometric(r, p)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_hypergeometric: { double n1 = ui.leParameter1->text().toDouble(); double n2 = ui.leParameter2->text().toDouble(); double t = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_hypergeometric(r, n1, n2, t); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_hypergeometric(r, n1, n2, t)); col->replaceInteger(0, data_int); } } break; } case nsl_sf_stats_logarithmic: { double p = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { if (col->columnMode() == AbstractColumn::Numeric) { for (int i = 0; i < rows; ++i) data[i] = gsl_ran_logarithmic(r, p); col->replaceValues(0, data); } else if (col->columnMode() == AbstractColumn::Integer) { for (int i = 0; i < rows; ++i) data_int[i] = (int)round(gsl_ran_logarithmic(r, p)); col->replaceInteger(0, data_int); } } break; } // additional non-GSL distributions not needed case nsl_sf_stats_maxwell_boltzmann: case nsl_sf_stats_sech: case nsl_sf_stats_levy: case nsl_sf_stats_frechet: break; } for (auto* col : m_columns) { col->setSuppressDataChangedSignal(false); col->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; gsl_rng_free(r); } diff --git a/src/kdefrontend/worksheet/ExportWorksheetDialog.cpp b/src/kdefrontend/worksheet/ExportWorksheetDialog.cpp index 8e32aec50..8b4c5e11b 100644 --- a/src/kdefrontend/worksheet/ExportWorksheetDialog.cpp +++ b/src/kdefrontend/worksheet/ExportWorksheetDialog.cpp @@ -1,268 +1,268 @@ /*************************************************************************** File : ExportWorksheetDialog.cpp Project : LabPlot Description : export worksheet dialog -------------------------------------------------------------------- - Copyright : (C) 2011-2016 by Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2011-2019 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 "ExportWorksheetDialog.h" #include "ui_exportworksheetwidget.h" #include #include #include #include -#include +#include #include #include #include #include #include /*! \class ExportWorksheetDialog \brief Dialog for exporting a worksheet to a file. \ingroup kdefrontend */ ExportWorksheetDialog::ExportWorksheetDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ExportWorksheetWidget()) { ui->setupUi(this); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_showOptionsButton = new QPushButton; connect(btnBox, &QDialogButtonBox::clicked, this, &ExportWorksheetDialog::slotButtonClicked); btnBox->addButton(m_showOptionsButton, QDialogButtonBox::ActionRole); ui->verticalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_cancelButton = btnBox->button(QDialogButtonBox::Cancel); auto* completer = new QCompleter(this); completer->setModel(new QDirModel); ui->leFileName->setCompleter(completer); ui->bOpen->setIcon(QIcon::fromTheme(QLatin1String("document-open"))); ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("application-pdf")), QLatin1String("Portable Data Format (PDF)")); ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-svg+xml")), QLatin1String("Scalable Vector Graphics (SVG)")); ui->cbFormat->insertSeparator(3); ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-x-generic")), QLatin1String("Portable Network Graphics (PNG)")); ui->cbExportArea->addItem(i18n("Object's bounding box")); ui->cbExportArea->addItem(i18n("Current selection")); ui->cbExportArea->addItem(i18n("Complete worksheet")); ui->cbResolution->addItem(i18nc("%1 is the value of DPI of the current screen", "%1 (desktop)", QString::number(QApplication::desktop()->physicalDpiX()))); ui->cbResolution->addItem(QLatin1String("100")); ui->cbResolution->addItem(QLatin1String("150")); ui->cbResolution->addItem(QLatin1String("200")); ui->cbResolution->addItem(QLatin1String("300")); ui->cbResolution->addItem(QLatin1String("600")); ui->cbResolution->setValidator(new QIntValidator(ui->cbResolution)); connect(ui->cbFormat, static_cast(&KComboBox::currentIndexChanged), this, &ExportWorksheetDialog::formatChanged ); connect(ui->bOpen, &QPushButton::clicked, this, &ExportWorksheetDialog::selectFile); connect(ui->leFileName, &QLineEdit::textChanged, this, &ExportWorksheetDialog::fileNameChanged); connect(m_showOptionsButton, &QPushButton::clicked, this, &ExportWorksheetDialog::toggleOptions); ui->leFileName->setFocus(); setWindowTitle(i18nc("@title:window", "Export Worksheet")); setWindowIcon(QIcon::fromTheme(QLatin1String("document-export-database"))); - QTimer::singleShot(0, this, &ExportWorksheetDialog::loadSettings); -} - -void ExportWorksheetDialog::loadSettings() { - //restore saved setting + //restore saved settings if available KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); - - KWindowConfig::restoreWindowSize(windowHandle(), conf); - ui->cbFormat->setCurrentIndex(conf.readEntry("Format", 0)); ui->cbExportArea->setCurrentIndex(conf.readEntry("Area", 0)); ui->chkExportBackground->setChecked(conf.readEntry("Background", true)); ui->cbResolution->setCurrentIndex(conf.readEntry("Resolution", 0)); m_showOptions = conf.readEntry("ShowOptions", true); m_showOptions ? m_showOptionsButton->setText(i18n("Hide Options")) : m_showOptionsButton->setText(i18n("Show Options")); ui->gbOptions->setVisible(m_showOptions); + + create(); // ensure there's a window created + if (conf.exists()) { + KWindowConfig::restoreWindowSize(windowHandle(), conf); + resize(windowHandle()->size()); // workaround for QTBUG-40584 + } else + resize(QSize(0, 0).expandedTo(minimumSize())); } ExportWorksheetDialog::~ExportWorksheetDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); conf.writeEntry("Format", ui->cbFormat->currentIndex()); conf.writeEntry("Area", ui->cbExportArea->currentIndex()); conf.writeEntry("Background", ui->chkExportBackground->isChecked()); conf.writeEntry("Resolution", ui->cbResolution->currentIndex()); conf.writeEntry("ShowOptions", m_showOptions); KWindowConfig::saveWindowSize(windowHandle(), conf); } void ExportWorksheetDialog::setFileName(const QString& name) { KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); QString dir = conf.readEntry("LastDir", ""); if (dir.isEmpty()) dir = QDir::homePath(); ui->leFileName->setText(dir + QDir::separator() + name); this->formatChanged(ui->cbFormat->currentIndex()); } QString ExportWorksheetDialog::path() const { return ui->leFileName->text(); } WorksheetView::ExportFormat ExportWorksheetDialog::exportFormat() const { int index = ui->cbFormat->currentIndex(); //we have a separator in the format combobox at the 3th position -> skip it if (index > 2) index--; return WorksheetView::ExportFormat(index); } WorksheetView::ExportArea ExportWorksheetDialog::exportArea() const { return WorksheetView::ExportArea(ui->cbExportArea->currentIndex()); } bool ExportWorksheetDialog::exportBackground() const { return ui->chkExportBackground->isChecked(); } int ExportWorksheetDialog::exportResolution() const { if (ui->cbResolution->currentIndex() == 0) return QApplication::desktop()->physicalDpiX(); else return ui->cbResolution->currentText().toInt(); } void ExportWorksheetDialog::slotButtonClicked(QAbstractButton* button) { if (button == m_okButton) okClicked(); else if (button == m_cancelButton) { reject(); } } //SLOTS void ExportWorksheetDialog::okClicked() { if ( QFile::exists(ui->leFileName->text()) ) { int r = KMessageBox::questionYesNo(this, i18n("The file already exists. Do you really want to overwrite it?"), i18n("Export")); if (r == KMessageBox::No) return; } KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); conf.writeEntry("Format", ui->cbFormat->currentIndex()); conf.writeEntry("Area", ui->cbExportArea->currentIndex()); conf.writeEntry("Resolution", ui->cbResolution->currentIndex()); QString path = ui->leFileName->text(); if (!path.isEmpty()) { QString dir = conf.readEntry("LastDir", ""); ui->leFileName->setText(path); int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { QString newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastDir", newDir); } } accept(); } /*! Shows/hides the GroupBox with export options in this dialog. */ void ExportWorksheetDialog::toggleOptions() { m_showOptions = !m_showOptions; ui->gbOptions->setVisible(m_showOptions); m_showOptions ? m_showOptionsButton->setText(i18n("Hide Options")) : m_showOptionsButton->setText(i18n("Show Options")); //resize the dialog resize(layout()->minimumSize()); layout()->activate(); resize( QSize(this->width(),0).expandedTo(minimumSize()) ); } /*! opens a file dialog and lets the user select the file. */ void ExportWorksheetDialog::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); const QString dir = conf.readEntry("LastDir", ""); QString format; if (ui->cbFormat->currentIndex() == 0) format = i18n("Portable Data Format (PDF) (*.pdf *.PDF)"); else if (ui->cbFormat->currentIndex() == 1) format = i18n("Scalable Vector Graphics (SVG) (*.svg *.SVG)"); else format = i18n("Portable Network Graphics (PNG) (*.png *.PNG)"); const QString path = QFileDialog::getSaveFileName(this, i18n("Export to file"), dir, format); if (!path.isEmpty()) { ui->leFileName->setText(path); int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastDir", newDir); } } } /*! called when the output format was changed. Adjusts the extension for the specified file. */ void ExportWorksheetDialog::formatChanged(int index) { //we have a separator in the format combobox at the 3rd position -> skip it if (index > 2) index --; QStringList extensions; extensions << QLatin1String(".pdf") << QLatin1String(".svg") << QLatin1String(".png"); QString path = ui->leFileName->text(); int i = path.indexOf(QLatin1Char('.')); if (i == -1) path = path + extensions.at(index); else path = path.left(i) + extensions.at(index); ui->leFileName->setText(path); // show resolution option for png format ui->lResolution->setVisible(index == 3); ui->cbResolution->setVisible(index == 3); } void ExportWorksheetDialog::fileNameChanged(const QString& name) { m_okButton->setEnabled(!name.simplified().isEmpty()); } diff --git a/src/kdefrontend/worksheet/ExportWorksheetDialog.h b/src/kdefrontend/worksheet/ExportWorksheetDialog.h index 6a31b5ac6..23b3f4bb6 100644 --- a/src/kdefrontend/worksheet/ExportWorksheetDialog.h +++ b/src/kdefrontend/worksheet/ExportWorksheetDialog.h @@ -1,74 +1,72 @@ /*************************************************************************** File : ExportWorksheetDialog.h Project : LabPlot Description : export worksheet dialog -------------------------------------------------------------------- - Copyright : (C) 2011-2016 by Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2011-2019 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 * * * ***************************************************************************/ #ifndef EXPORTWORKSHEETDIALOG_H #define EXPORTWORKSHEETDIALOG_H #include #include "commonfrontend/worksheet/WorksheetView.h" namespace Ui { class ExportWorksheetWidget; } class QPushButton; class QAbstractButton; class ExportWorksheetDialog : public QDialog { Q_OBJECT public: explicit ExportWorksheetDialog(QWidget*); ~ExportWorksheetDialog() override; QString path() const; void setFileName(const QString&); WorksheetView::ExportFormat exportFormat() const; WorksheetView::ExportArea exportArea() const; bool exportBackground() const; int exportResolution() const; private: Ui::ExportWorksheetWidget* ui; bool m_showOptions{true}; QPushButton* m_showOptionsButton; QPushButton* m_okButton; QPushButton* m_cancelButton; - private slots: void slotButtonClicked(QAbstractButton *); void okClicked(); void toggleOptions(); void selectFile(); void formatChanged(int); void fileNameChanged(const QString&); - void loadSettings(); }; #endif