diff --git a/src/backend/core/Project.cpp b/src/backend/core/Project.cpp index 80990e477..3b0ca881c 100644 --- a/src/backend/core/Project.cpp +++ b/src/backend/core/Project.cpp @@ -1,475 +1,475 @@ /*************************************************************************** File : Project.cpp Project : LabPlot Description : Represents a LabPlot project. -------------------------------------------------------------------- Copyright : (C) 2011-2014 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2007-2008 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2007 Knut Franke (knut.franke@gmx.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 "backend/core/Project.h" #include "backend/lib/XmlStreamReader.h" #include "backend/datasources/LiveDataSource.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/plots/cartesian/XYEquationCurve.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" #include "backend/worksheet/plots/cartesian/XYFourierTransformCurve.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/datapicker/DatapickerCurve.h" #include #include #include #include #include #include #include #include #include #include /** * \class Project * \brief Represents a project. * \ingroup core * Project represents the root node of all objects created during the runtime of the program. * Manages also the undo stack. */ /** * \enum Project::MdiWindowVisibility * \brief MDI subwindow visibility setting */ /** * \var Project::folderOnly * \brief only show MDI windows corresponding to Parts in the current folder */ /** * \var Project::foldAndSubfolders * \brief show MDI windows corresponding to Parts in the current folder and its subfolders */ /** * \var Project::allMdiWindows * \brief show MDI windows for all Parts in the project simultaneously */ class Project::Private { public: Private() : mdiWindowVisibility(Project::folderOnly), scriptingEngine(0), version(LVERSION), author(QString(qgetenv("USER"))), modificationTime(QDateTime::currentDateTime()), changed(false), loading(false) { } QUndoStack undo_stack; MdiWindowVisibility mdiWindowVisibility; AbstractScriptingEngine* scriptingEngine; QString fileName; QString version; QString author; QDateTime modificationTime; bool changed; bool loading; }; Project::Project() : Folder(i18n("Project")), d(new Private()) { //load default values for name, comment and author from config KConfig config; KConfigGroup group = config.group("Project"); d->author = group.readEntry("Author", QString()); //we don't have direct access to the members name and comment //->temporaly disable the undo stack and call the setters setUndoAware(false); d->loading = true; setName(group.readEntry("Name", i18n("Project"))); setComment(group.readEntry("Comment", QString())); setUndoAware(true); d->loading = false; d->changed = false; // TODO: intelligent engine choosing // Q_ASSERT(ScriptingEngineManager::instance()->engineNames().size() > 0); // QString engine_name = ScriptingEngineManager::instance()->engineNames()[0]; // d->scriptingEngine = ScriptingEngineManager::instance()->engine(engine_name); - connect(this, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)),this, SLOT(descriptionChanged(const AbstractAspect*))); + connect(this, &Project::aspectDescriptionChanged,this, &Project::descriptionChanged); } Project::~Project() { //if the project is being closed and the live data sources still continue reading the data, //the dependend objects (columns, etc.), which are already deleted maybe here, are still being notified about the changes. //->stop reading the live data sources prior to deleting all objects. for (auto* lds : children()) lds->pauseReading(); //if the project is being closed, in Worksheet the scene items are being removed and the selection in the view can change. //don't react on these changes since this can lead crashes (worksheet object is already in the destructor). //->notify all worksheets about the project being closed. for (auto* w : children()) w->setIsClosing(); d->undo_stack.clear(); delete d; } QUndoStack* Project::undoStack() const { return &d->undo_stack; } QMenu* Project::createContextMenu() { QMenu* menu = new QMenu(); // no remove action from AbstractAspect in the project context menu emit requestProjectContextMenu(menu); return menu; } QMenu* Project::createFolderContextMenu(const Folder* folder) { QMenu* menu = const_cast(folder)->AbstractAspect::createContextMenu(); Q_ASSERT(menu); emit requestFolderContextMenu(folder, menu); return menu; } void Project::setMdiWindowVisibility(MdiWindowVisibility visibility) { d->mdiWindowVisibility = visibility; emit mdiWindowVisibilityChanged(); } Project::MdiWindowVisibility Project::mdiWindowVisibility() const { return d->mdiWindowVisibility; } AbstractScriptingEngine* Project::scriptingEngine() const { return d->scriptingEngine; } CLASS_D_ACCESSOR_IMPL(Project, QString, fileName, FileName, fileName) BASIC_D_ACCESSOR_IMPL(Project, QString, version, Version, version) CLASS_D_ACCESSOR_IMPL(Project, QString, author, Author, author) CLASS_D_ACCESSOR_IMPL(Project, QDateTime, modificationTime, ModificationTime, modificationTime) void Project::setChanged(const bool value) { if (d->loading) return; if (value) emit changed(); d->changed = value; } bool Project ::hasChanged() const { return d->changed ; } void Project::descriptionChanged(const AbstractAspect* aspect) { if (d->loading) return; if (this!=aspect) return; d->changed = true; emit changed(); } void Project::navigateTo(const QString& path) { requestNavigateTo(path); } bool Project::isLoading() const { return d->loading; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /** * \brief Save as XML */ void Project::save(QXmlStreamWriter* writer) const { //set the version and the modification time to the current values d->version = LVERSION; d->modificationTime = QDateTime::currentDateTime(); writer->setAutoFormatting(true); writer->writeStartDocument(); writer->writeDTD(""); writer->writeStartElement("project"); writer->writeAttribute("version", version()); writer->writeAttribute("fileName", fileName()); writer->writeAttribute("modificationTime", modificationTime().toString("yyyy-dd-MM hh:mm:ss:zzz")); writer->writeAttribute("author", author()); writeBasicAttributes(writer); writeCommentElement(writer); //save all children for (auto* child : children(IncludeHidden)) { writer->writeStartElement("child_aspect"); child->save(writer); writer->writeEndElement(); } //save the state of the views (visible, maximized/minimized/geometry) //and the state of the project explorer (expanded items, currently selected item) emit requestSaveState(writer); writer->writeEndElement(); writer->writeEndDocument(); } bool Project::load(const QString& filename, bool preview) { QIODevice *file; // first try gzip compression, because projects can be gzipped and end with .lml if (filename.endsWith(QLatin1String(".lml"), Qt::CaseInsensitive)) file = new KCompressionDevice(filename,KFilterDev::compressionTypeForMimeType("application/x-gzip")); else // opens filename using file ending file = new KFilterDev(filename); if (file == 0) file = new QFile(filename); if (!file->open(QIODevice::ReadOnly)) { KMessageBox::error(0, i18n("Sorry. Could not open file for reading.")); return false; } char c; const bool rc = file->getChar(&c); if (!rc) { KMessageBox::error(0, i18n("The project file is empty."), i18n("Error opening project")); file->close(); delete file; return false; } file->seek(0); //parse XML XmlStreamReader reader(file); if (this->load(&reader, preview) == false) { RESET_CURSOR; QString msg_text = reader.errorString(); KMessageBox::error(0, msg_text, i18n("Error when opening the project")); return false; } if (reader.hasWarnings()) { QString msg = i18n("The following problems occurred when loading the project file:\n"); const QStringList& warnings = reader.warningStrings(); foreach (const QString& str, warnings) msg += str + '\n'; qWarning() << msg; //TODO: show warnings in a kind of "log window" but not in message box // KMessageBox::error(this, msg, i18n("Project loading partly failed")); } file->close(); delete file; return true; } /** * \brief Load from XML */ bool Project::load(XmlStreamReader* reader, bool preview) { d->loading = true; while (!(reader->isStartDocument() || reader->atEnd())) reader->readNext(); if(!(reader->atEnd())) { if (!reader->skipToNextTag()) return false; if (reader->name() == "project") { QString version = reader->attributes().value("version").toString(); if(version.isEmpty()) reader->raiseWarning(i18n("Attribute 'version' is missing.")); else d->version = version; if (!readBasicAttributes(reader)) return false; if (!readProjectAttributes(reader)) return false; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement()) break; if (reader->isStartElement()) { if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if(reader->name() == "child_aspect") { if (!readChildAspectElement(reader, preview)) return false; } else if(reader->name() == "state") { //load the state of the views (visible, maximized/minimized/geometry) //and the state of the project explorer (expanded items, currently selected item) emit requestLoadState(reader); } else { reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } } //wait until all columns are decoded from base64-encoded data QThreadPool::globalInstance()->waitForDone(); //everything is read now. //restore the pointer to the data sets (columns) in xy-curves etc. QVector curves = children(AbstractAspect::Recursive); QVector axes = children(AbstractAspect::Recursive); QVector dataPickerCurves = children(AbstractAspect::Recursive); if (!curves.isEmpty() || !axes.isEmpty()) { QVector columns = children(AbstractAspect::Recursive); //XY-curves for (auto* curve : curves) { if (!curve) continue; curve->suppressRetransform(true); XYEquationCurve* equationCurve = dynamic_cast(curve); XYDataReductionCurve* dataReductionCurve = dynamic_cast(curve); XYDifferentiationCurve* differentiationCurve = dynamic_cast(curve); XYIntegrationCurve* integrationCurve = dynamic_cast(curve); XYInterpolationCurve* interpolationCurve = dynamic_cast(curve); XYSmoothCurve* smoothCurve = dynamic_cast(curve); XYFitCurve* fitCurve = dynamic_cast(curve); XYFourierFilterCurve* filterCurve = dynamic_cast(curve); XYFourierTransformCurve* dftCurve = dynamic_cast(curve); if (equationCurve) { //curves defined by a mathematical equations recalculate their own columns on load again. if (!preview) equationCurve->recalculate(); } else if (dataReductionCurve) { RESTORE_COLUMN_POINTER(dataReductionCurve, xDataColumn, XDataColumn); RESTORE_COLUMN_POINTER(dataReductionCurve, yDataColumn, YDataColumn); } else if (differentiationCurve) { RESTORE_COLUMN_POINTER(differentiationCurve, xDataColumn, XDataColumn); RESTORE_COLUMN_POINTER(differentiationCurve, yDataColumn, YDataColumn); } else if (integrationCurve) { RESTORE_COLUMN_POINTER(integrationCurve, xDataColumn, XDataColumn); RESTORE_COLUMN_POINTER(integrationCurve, yDataColumn, YDataColumn); } else if (interpolationCurve) { RESTORE_COLUMN_POINTER(interpolationCurve, xDataColumn, XDataColumn); RESTORE_COLUMN_POINTER(interpolationCurve, yDataColumn, YDataColumn); } else if (smoothCurve) { RESTORE_COLUMN_POINTER(smoothCurve, xDataColumn, XDataColumn); RESTORE_COLUMN_POINTER(smoothCurve, yDataColumn, YDataColumn); } else if (fitCurve) { RESTORE_COLUMN_POINTER(fitCurve, xDataColumn, XDataColumn); RESTORE_COLUMN_POINTER(fitCurve, yDataColumn, YDataColumn); RESTORE_COLUMN_POINTER(fitCurve, xErrorColumn, XErrorColumn); RESTORE_COLUMN_POINTER(fitCurve, yErrorColumn, YErrorColumn); } else if (filterCurve) { RESTORE_COLUMN_POINTER(filterCurve, xDataColumn, XDataColumn); RESTORE_COLUMN_POINTER(filterCurve, yDataColumn, YDataColumn); } else if (dftCurve) { RESTORE_COLUMN_POINTER(dftCurve, xDataColumn, XDataColumn); RESTORE_COLUMN_POINTER(dftCurve, yDataColumn, YDataColumn); } else { RESTORE_COLUMN_POINTER(curve, xColumn, XColumn); RESTORE_COLUMN_POINTER(curve, yColumn, YColumn); RESTORE_COLUMN_POINTER(curve, valuesColumn, ValuesColumn); RESTORE_COLUMN_POINTER(curve, xErrorPlusColumn, XErrorPlusColumn); RESTORE_COLUMN_POINTER(curve, xErrorMinusColumn, XErrorMinusColumn); RESTORE_COLUMN_POINTER(curve, yErrorPlusColumn, YErrorPlusColumn); RESTORE_COLUMN_POINTER(curve, yErrorMinusColumn, YErrorMinusColumn); } RESTORE_POINTER(curve, dataSourceCurve, DataSourceCurve, XYCurve, curves); curve->suppressRetransform(false); } //Axes for (auto* axis : axes) { if (!axis) continue; RESTORE_COLUMN_POINTER(axis, majorTicksColumn, MajorTicksColumn); RESTORE_COLUMN_POINTER(axis, minorTicksColumn, MinorTicksColumn); } for (auto* dataPickerCurve : dataPickerCurves) { if (!dataPickerCurve) continue; RESTORE_COLUMN_POINTER(dataPickerCurve, posXColumn, PosXColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, posYColumn, PosYColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, plusDeltaXColumn, PlusDeltaXColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, minusDeltaXColumn, MinusDeltaXColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, plusDeltaYColumn, PlusDeltaYColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, minusDeltaYColumn, MinusDeltaYColumn); } } } else // no project element reader->raiseError(i18n("no project element found")); } else // no start document reader->raiseError(i18n("no valid XML document found")); if (!preview) { for (auto* plot : children(AbstractAspect::Recursive)) plot->retransform(); } d->loading = false; emit loaded(); return !reader->hasError(); } bool Project::readProjectAttributes(XmlStreamReader* reader) { QXmlStreamAttributes attribs = reader->attributes(); QString str = attribs.value(reader->namespaceUri().toString(), "fileName").toString(); if(str.isEmpty()) { reader->raiseError(i18n("Project file name missing.")); return false; } d->fileName = str; str = attribs.value(reader->namespaceUri().toString(), "modificationTime").toString(); QDateTime modificationTime = QDateTime::fromString(str, "yyyy-dd-MM hh:mm:ss:zzz"); if(str.isEmpty() || !modificationTime.isValid()) { reader->raiseWarning(i18n("Invalid project modification time. Using current time.")); d->modificationTime = QDateTime::currentDateTime(); } else d->modificationTime = modificationTime; str = attribs.value(reader->namespaceUri().toString(), "author").toString(); d->author = str; return true; } diff --git a/src/backend/core/column/Column.cpp b/src/backend/core/column/Column.cpp index 80e81c7b1..8da67df53 100644 --- a/src/backend/core/column/Column.cpp +++ b/src/backend/core/column/Column.cpp @@ -1,1186 +1,1186 @@ /*************************************************************************** File : Column.cpp Project : LabPlot Description : Aspect that manages a column -------------------------------------------------------------------- Copyright : (C) 2007-2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2013-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 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 "backend/core/column/Column.h" #include "backend/core/column/ColumnPrivate.h" #include "backend/core/column/ColumnStringIO.h" #include "backend/core/column/columncommands.h" #include "backend/core/Project.h" #include "backend/lib/XmlStreamReader.h" #include "backend/core/datatypes/String2DateTimeFilter.h" #include "backend/core/datatypes/DateTime2StringFilter.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" extern "C" { #include } #include #include #include #include #include #include /** * \class Column * \brief Aspect that manages a column * * This class represents a column, i.e., (mathematically) a 1D vector of * values with a header. It provides a public reading and (undo aware) writing * interface as defined in AbstractColumn. A column * can have one of currently three data types: double, QString, or * QDateTime. The string representation of the values can differ depending * on the mode of the column. * * Column inherits from AbstractAspect and is intended to be a child * of the corresponding Spreadsheet in the aspect hierarchy. Columns don't * have a view as they are intended to be displayed inside a spreadsheet. */ Column::Column(const QString& name, AbstractColumn::ColumnMode mode) : AbstractColumn(name), d(new ColumnPrivate(this, mode)) { init(); } /** * \brief Common part of ctors */ void Column::init() { m_string_io = new ColumnStringIO(this); d->inputFilter()->input(0, m_string_io); d->outputFilter()->input(0, this); d->inputFilter()->setHidden(true); d->outputFilter()->setHidden(true); addChild(d->inputFilter()); addChild(d->outputFilter()); m_suppressDataChangedSignal = false; m_usedInActionGroup = new QActionGroup(this); - connect(m_usedInActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(navigateTo(QAction*))); + connect(m_usedInActionGroup, &QActionGroup::triggered, this, &Column::navigateTo); } Column::~Column() { delete m_string_io; delete d; } QMenu* Column::createContextMenu() { QMenu* menu = AbstractAspect::createContextMenu(); QAction* firstAction = menu->actions().at(1); //add actions available in SpreadsheetView emit requestProjectContextMenu(menu); //"Used in" menu containing all curves where the column is used QMenu* usedInMenu = new QMenu(i18n("Used in")); usedInMenu->setIcon(QIcon::fromTheme("go-next-view")); //remove previously added actions for (auto* action: m_usedInActionGroup->actions()) m_usedInActionGroup->removeAction(action); //add curves where the column is currently in use QVector curves = project()->children(AbstractAspect::Recursive); for (const auto* curve: curves) { if (curve->dataSourceType() == XYCurve::DataSourceSpreadsheet && (curve->xColumn() == this || curve->yColumn() == this) ) { QAction* action = new QAction(curve->icon(), curve->name(), m_usedInActionGroup); action->setData(curve->path()); usedInMenu->addAction(action); } } menu->insertSeparator(firstAction); menu->insertMenu(firstAction, usedInMenu); menu->insertSeparator(firstAction); return menu; } void Column::navigateTo(QAction* action) { project()->navigateTo(action->data().toString()); } /*! * */ void Column::setSuppressDataChangedSignal(bool b) { m_suppressDataChangedSignal = b; } /** * \brief Set the column mode * * This sets the column mode and, if * necessary, converts it to another datatype. */ void Column::setColumnMode(AbstractColumn::ColumnMode mode) { if (mode == columnMode()) return; DEBUG("Column::setColumnMode()"); beginMacro(i18n("%1: change column type", name())); auto* old_input_filter = d->inputFilter(); auto* old_output_filter = d->outputFilter(); exec(new ColumnSetModeCmd(d, mode)); if (d->inputFilter() != old_input_filter) { removeChild(old_input_filter); addChild(d->inputFilter()); d->inputFilter()->input(0, m_string_io); } if (d->outputFilter() != old_output_filter) { removeChild(old_output_filter); addChild(d->outputFilter()); d->outputFilter()->input(0, this); } endMacro(); DEBUG("Column::setColumnMode() DONE"); } void Column::setColumnModeFast(AbstractColumn::ColumnMode mode) { if (mode == columnMode()) return; auto* old_input_filter = d->inputFilter(); auto* old_output_filter = d->outputFilter(); exec(new ColumnSetModeCmd(d, mode)); if (d->inputFilter() != old_input_filter) { removeChild(old_input_filter); addChildFast(d->inputFilter()); d->inputFilter()->input(0, m_string_io); } if (d->outputFilter() != old_output_filter) { removeChild(old_output_filter); addChildFast(d->outputFilter()); d->outputFilter()->input(0, this); } } /** * \brief Copy another column of the same type * * This function will return false if the data type * of 'other' is not the same as the type of 'this'. * Use a filter to convert a column to another type. */ bool Column::copy(const AbstractColumn* other) { Q_CHECK_PTR(other); if (other->columnMode() != columnMode()) return false; exec(new ColumnFullCopyCmd(d, other)); return true; } /** * \brief Copies a part of another column of the same type * * This function will return false if the data type * of 'other' is not the same as the type of 'this'. * \param other pointer to the column to copy * \param src_start first row to copy in the column to copy * \param dest_start first row to copy in * \param num_rows the number of rows to copy */ bool Column::copy(const AbstractColumn* source, int source_start, int dest_start, int num_rows) { Q_CHECK_PTR(source); if (source->columnMode() != columnMode()) return false; exec(new ColumnPartialCopyCmd(d, source, source_start, dest_start, num_rows)); return true; } /** * \brief Insert some empty (or initialized with zero) rows */ void Column::handleRowInsertion(int before, int count) { AbstractColumn::handleRowInsertion(before, count); exec(new ColumnInsertRowsCmd(d, before, count)); if (!m_suppressDataChangedSignal) emit dataChanged(this); setStatisticsAvailable(false); } /** * \brief Remove 'count' rows starting from row 'first' */ void Column::handleRowRemoval(int first, int count) { AbstractColumn::handleRowRemoval(first, count); exec(new ColumnRemoveRowsCmd(d, first, count)); if (!m_suppressDataChangedSignal) emit dataChanged(this); setStatisticsAvailable(false); } /** * \brief Set the column plot designation */ void Column::setPlotDesignation(AbstractColumn::PlotDesignation pd) { if (pd != plotDesignation()) exec(new ColumnSetPlotDesignationCmd(d, pd)); } /** * \brief Get width */ int Column::width() const { return d->width(); } /** * \brief Set width */ void Column::setWidth(int value) { d->setWidth(value); } /** * \brief Clear the whole column */ void Column::clear() { exec(new ColumnClearCmd(d)); } //////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //! \name Formula related functions //@{ //////////////////////////////////////////////////////////////////////////////// /** * \brief Returns the formula used to generate column values */ QString Column:: formula() const { return d->formula(); } const QStringList& Column::formulaVariableNames() const { return d->formulaVariableNames(); } const QStringList& Column::formulaVariableColumnPathes() const { return d->formulaVariableColumnPathes(); } /** * \brief Sets the formula used to generate column values */ void Column::setFormula(const QString& formula, const QStringList& variableNames, const QStringList& columnPathes) { exec(new ColumnSetGlobalFormulaCmd(d, formula, variableNames, columnPathes)); } /** * \brief Set a formula string for an interval of rows */ void Column::setFormula(Interval i, QString formula) { exec(new ColumnSetFormulaCmd(d, i, formula)); } /** * \brief Overloaded function for convenience */ void Column::setFormula(int row, QString formula) { setFormula(Interval(row, row), formula); } /** * \brief Clear all formulas */ void Column::clearFormulas() { exec(new ColumnClearFormulasCmd(d)); } //////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //! \name type specific functions //@{ //////////////////////////////////////////////////////////////////////////////// /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Text */ void Column::setTextAt(int row, const QString& new_value) { DEBUG("Column::setTextAt()"); setStatisticsAvailable(false); exec(new ColumnSetTextCmd(d, row, new_value)); } /** * \brief Replace a range of values * * Use this only when columnMode() is Text */ void Column::replaceTexts(int first, const QVector& new_values) { DEBUG("Column::replaceTexts()"); if (!new_values.isEmpty()) { //TODO: do we really need this check? setStatisticsAvailable(false); exec(new ColumnReplaceTextsCmd(d, first, new_values)); } } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void Column::setDateAt(int row, const QDate& new_value) { setStatisticsAvailable(false); setDateTimeAt(row, QDateTime(new_value, timeAt(row))); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void Column::setTimeAt(int row, const QTime& new_value) { setStatisticsAvailable(false); setDateTimeAt(row, QDateTime(dateAt(row), new_value)); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void Column::setDateTimeAt(int row, const QDateTime& new_value) { setStatisticsAvailable(false); exec(new ColumnSetDateTimeCmd(d, row, new_value)); } /** * \brief Replace a range of values * * Use this only when columnMode() is DateTime, Month or Day */ void Column::replaceDateTimes(int first, const QVector& new_values) { if (!new_values.isEmpty()) { setStatisticsAvailable(false); exec(new ColumnReplaceDateTimesCmd(d, first, new_values)); } } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Numeric */ void Column::setValueAt(int row, double new_value) { DEBUG("Column::setValueAt()"); setStatisticsAvailable(false); exec(new ColumnSetValueCmd(d, row, new_value)); } /** * \brief Replace a range of values * * Use this only when columnMode() is Numeric */ void Column::replaceValues(int first, const QVector& new_values) { DEBUG("Column::replaceValues()"); if (!new_values.isEmpty()) { setStatisticsAvailable(false); exec(new ColumnReplaceValuesCmd(d, first, new_values)); } } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Integer */ void Column::setIntegerAt(int row, int new_value) { DEBUG("Column::setIntegerAt()"); setStatisticsAvailable(false); exec(new ColumnSetIntegerCmd(d, row, new_value)); } /** * \brief Replace a range of values * * Use this only when columnMode() is Integer */ void Column::replaceInteger(int first, const QVector& new_values) { DEBUG("Column::replaceInteger()"); if (!new_values.isEmpty()) { setStatisticsAvailable(false); exec(new ColumnReplaceIntegersCmd(d, first, new_values)); } } void Column::setStatisticsAvailable(bool available) { d->statisticsAvailable = available; } bool Column::statisticsAvailable() const { return d->statisticsAvailable; } const Column::ColumnStatistics& Column::statistics() { if (!statisticsAvailable()) calculateStatistics(); return d->statistics; } void Column::calculateStatistics() { d->statistics = ColumnStatistics(); ColumnStatistics& statistics = d->statistics; // TODO: support other data types? QVector* rowValues = reinterpret_cast*>(data()); int notNanCount = 0; double val; double columnSum = 0.0; double columnProduct = 1.0; double columnSumNeg = 0.0; double columnSumSquare = 0.0; statistics.minimum = INFINITY; statistics.maximum = -INFINITY; QMap frequencyOfValues; QVector rowData; rowData.reserve(rowValues->size()); for (int row = 0; row < rowValues->size(); ++row) { val = rowValues->value(row); if (std::isnan(val) || isMasked(row)) continue; if (val < statistics.minimum) statistics.minimum = val; if (val > statistics.maximum) statistics.maximum = val; columnSum+= val; columnSumNeg += (1.0 / val); columnSumSquare += pow(val, 2.0); columnProduct *= val; if (frequencyOfValues.contains(val)) frequencyOfValues.operator [](val)++; else frequencyOfValues.insert(val, 1); ++notNanCount; rowData.push_back(val); } if (notNanCount == 0) { setStatisticsAvailable(true); return; } if (rowData.size() < rowValues->size()) rowData.squeeze(); statistics.arithmeticMean = columnSum / notNanCount; statistics.geometricMean = pow(columnProduct, 1.0 / notNanCount); statistics.harmonicMean = notNanCount / columnSumNeg; statistics.contraharmonicMean = columnSumSquare / columnSum; double columnSumVariance = 0; double columnSumMeanDeviation = 0.0; double columnSumMedianDeviation = 0.0; double sumForCentralMoment_r3 = 0.0; double sumForCentralMoment_r4 = 0.0; gsl_sort(rowData.data(), 1, notNanCount); statistics.median = (notNanCount%2) ? rowData.at((notNanCount-1)/2) : (rowData.at((notNanCount-1)/2) + rowData.at(notNanCount/2))/2.0; QVector absoluteMedianList; absoluteMedianList.reserve(notNanCount); absoluteMedianList.resize(notNanCount); int idx = 0; for(int row = 0; row < rowValues->size(); ++row) { val = rowValues->value(row); if (std::isnan(val) || isMasked(row) ) continue; columnSumVariance += pow(val - statistics.arithmeticMean, 2.0); sumForCentralMoment_r3 += pow(val - statistics.arithmeticMean, 3.0); sumForCentralMoment_r4 += pow(val - statistics.arithmeticMean, 4.0); columnSumMeanDeviation += fabs( val - statistics.arithmeticMean ); absoluteMedianList[idx] = fabs(val - statistics.median); columnSumMedianDeviation += absoluteMedianList[idx]; idx++; } statistics.meanDeviationAroundMedian = columnSumMedianDeviation / notNanCount; statistics.medianDeviation = (notNanCount%2) ? absoluteMedianList.at((notNanCount-1)/2) : (absoluteMedianList.at((notNanCount-1)/2) + absoluteMedianList.at(notNanCount/2))/2.0; const double centralMoment_r3 = sumForCentralMoment_r3 / notNanCount; const double centralMoment_r4 = sumForCentralMoment_r4 / notNanCount; statistics.variance = columnSumVariance / notNanCount; statistics.standardDeviation = sqrt(statistics.variance); statistics.skewness = centralMoment_r3 / pow(statistics.standardDeviation, 3.0); statistics.kurtosis = (centralMoment_r4 / pow(statistics.standardDeviation, 4.0)) - 3.0; statistics.meanDeviation = columnSumMeanDeviation / notNanCount; double entropy = 0.0; for (const auto& v: frequencyOfValues.values()) { const double frequencyNorm = static_cast(v) / notNanCount; entropy += (frequencyNorm * log2(frequencyNorm)); } statistics.entropy = -entropy; setStatisticsAvailable(true); } ////////////////////////////////////////////////////////////////////////////////////////////// void* Column::data() const { return d->data(); } //TODO: support all data types /** * \brief Return the content of row 'row'. * * Use this only when columnMode() is Text */ QString Column::textAt(int row) const { return d->textAt(row); } /** * \brief Return the date part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDate Column::dateAt(int row) const { return d->dateAt(row); } /** * \brief Return the time part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QTime Column::timeAt(int row) const { return d->timeAt(row); } /** * \brief Return the QDateTime in row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDateTime Column::dateTimeAt(int row) const { return d->dateTimeAt(row); } /** * \brief Return the double value in row 'row' */ double Column::valueAt(int row) const { return d->valueAt(row); } /** * \brief Return the int value in row 'row' */ int Column::integerAt(int row) const { return d->integerAt(row); } /* * call this function if the data of the column was changed directly via the data()-pointer * and not via the setValueAt() in order to emit the dataChanged-signal. * This is used e.g. in \c XYFitCurvePrivate::recalculate() */ void Column::setChanged() { if (!m_suppressDataChangedSignal) emit dataChanged(this); setStatisticsAvailable(false); } //////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////// /** * \brief Return an icon to be used for decorating the views and spreadsheet column headers */ QIcon Column::icon() const { return iconForMode(columnMode()); } //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name serialize/deserialize //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Save the column as XML */ void Column::save(QXmlStreamWriter* writer) const { writer->writeStartElement("column"); writeBasicAttributes(writer); writer->writeAttribute("designation", QString::number(plotDesignation())); writer->writeAttribute("mode", QString::number(columnMode())); writer->writeAttribute("width", QString::number(width())); //save the formula used to generate column values, if available if (!formula().isEmpty() ) { writer->writeStartElement("formula"); writer->writeTextElement("text", formula()); writer->writeStartElement("variableNames"); for (auto name: formulaVariableNames()) writer->writeTextElement("name", name); writer->writeEndElement(); writer->writeStartElement("columnPathes"); for (auto path: formulaVariableColumnPathes()) writer->writeTextElement("path", path); writer->writeEndElement(); writer->writeEndElement(); } writeCommentElement(writer); writer->writeStartElement("input_filter"); d->inputFilter()->save(writer); writer->writeEndElement(); writer->writeStartElement("output_filter"); d->outputFilter()->save(writer); writer->writeEndElement(); XmlWriteMask(writer); //TODO: formula in cells is not implemented yet // QList< Interval > formulas = formulaIntervals(); // foreach(const Interval& interval, formulas) { // writer->writeStartElement("formula"); // writer->writeAttribute("start_row", QString::number(interval.start())); // writer->writeAttribute("end_row", QString::number(interval.end())); // writer->writeCharacters(formula(interval.start())); // writer->writeEndElement(); // } int i; switch(columnMode()) { case AbstractColumn::Numeric: { const char* data = reinterpret_cast(static_cast< QVector* >(d->data())->constData()); int size = d->rowCount() * sizeof(double); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); break; } case AbstractColumn::Integer: { const char* data = reinterpret_cast(static_cast< QVector* >(d->data())->constData()); int size = d->rowCount() * sizeof(int); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); break; } case AbstractColumn::Text: for (i = 0; i < rowCount(); ++i) { writer->writeStartElement("row"); writer->writeAttribute("index", QString::number(i)); writer->writeCharacters(textAt(i)); writer->writeEndElement(); } break; case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: for (i = 0; i < rowCount(); ++i) { writer->writeStartElement("row"); writer->writeAttribute("index", QString::number(i)); writer->writeCharacters(dateTimeAt(i).toString("yyyy-dd-MM hh:mm:ss:zzz")); writer->writeEndElement(); } break; } writer->writeEndElement(); // "column" } //TODO: extra header class DecodeColumnTask : public QRunnable { public: DecodeColumnTask(ColumnPrivate* priv, const QString& content) { m_private = priv; m_content = content; }; void run() { QByteArray bytes = QByteArray::fromBase64(m_content.toAscii()); if (m_private->columnMode() == AbstractColumn::Numeric) { QVector* data = new QVector(bytes.size()/sizeof(double)); memcpy(data->data(), bytes.data(), bytes.size()); m_private->replaceData(data); } else { QVector* data = new QVector(bytes.size()/sizeof(int)); memcpy(data->data(), bytes.data(), bytes.size()); m_private->replaceData(data); } } private: ColumnPrivate* m_private; QString m_content; }; /** * \brief Load the column from XML */ bool Column::load(XmlStreamReader* reader, bool preview) { if (reader->isStartElement() && reader->name() != "column") { reader->raiseError(i18n("no column element found")); return false; } if (!readBasicAttributes(reader)) return false; QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs = reader->attributes(); QString str = attribs.value("designation").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'designation'")); else d->setPlotDesignation( AbstractColumn::PlotDesignation(str.toInt()) ); str = attribs.value("mode").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'mode'")); else setColumnModeFast( AbstractColumn::ColumnMode(str.toInt()) ); str = attribs.value("width").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'width'")); else d->setWidth(str.toInt()); // read child elements while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement()) break; if (reader->isStartElement()) { bool ret_val = true; if (reader->name() == "comment") ret_val = readCommentElement(reader); else if (reader->name() == "input_filter") ret_val = XmlReadInputFilter(reader); else if (reader->name() == "output_filter") ret_val = XmlReadOutputFilter(reader); else if (reader->name() == "mask") ret_val = XmlReadMask(reader); else if (reader->name() == "formula") ret_val = XmlReadFormula(reader); else if (reader->name() == "row") ret_val = XmlReadRow(reader); else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } if (!ret_val) return false; } if (!preview) { QString content = reader->text().toString().trimmed(); if (!content.isEmpty() && ( columnMode() == AbstractColumn::Numeric || columnMode() == AbstractColumn::Integer)) { DecodeColumnTask* task = new DecodeColumnTask(d, content); QThreadPool::globalInstance()->start(task); } } } return !reader->error(); } /** * \brief Read XML input filter element */ bool Column::XmlReadInputFilter(XmlStreamReader* reader) { Q_ASSERT(reader->isStartElement() && reader->name() == "input_filter"); if (!reader->skipToNextTag()) return false; if (!d->inputFilter()->load(reader, false)) return false; if (!reader->skipToNextTag()) return false; Q_ASSERT(reader->isEndElement() && reader->name() == "input_filter"); return true; } /** * \brief Read XML output filter element */ bool Column::XmlReadOutputFilter(XmlStreamReader* reader) { Q_ASSERT(reader->isStartElement() && reader->name() == "output_filter"); if (!reader->skipToNextTag()) return false; if (!d->outputFilter()->load(reader, false)) return false; if (!reader->skipToNextTag()) return false; Q_ASSERT(reader->isEndElement() && reader->name() == "output_filter"); return true; } /** * \brief Read XML formula element */ bool Column::XmlReadFormula(XmlStreamReader* reader) { QString formula; QStringList variableNames; QStringList columnPathes; while (reader->readNext()) { if (reader->isEndElement()) break; if (reader->name() == "text") formula = reader->readElementText(); else if (reader->name() == "variableNames") { while (reader->readNext()) { if (reader->name() == "variableNames" && reader->isEndElement()) break; if (reader->isStartElement()) variableNames << reader->readElementText(); } } else if (reader->name() == "columnPathes") { while (reader->readNext()) { if (reader->name() == "columnPathes" && reader->isEndElement()) break; if (reader->isStartElement()) columnPathes << reader->readElementText(); } } } setFormula(formula, variableNames, columnPathes); return true; } //TODO: read cell formula, not implemented yet // bool Column::XmlReadFormula(XmlStreamReader* reader) // { // Q_ASSERT(reader->isStartElement() && reader->name() == "formula"); // // bool ok1, ok2; // int start, end; // start = reader->readAttributeInt("start_row", &ok1); // end = reader->readAttributeInt("end_row", &ok2); // if(!ok1 || !ok2) // { // reader->raiseError(i18n("invalid or missing start or end row")); // return false; // } // setFormula(Interval(start,end), reader->readElementText()); // // return true; // } /** * \brief Read XML row element */ bool Column::XmlReadRow(XmlStreamReader* reader) { Q_ASSERT(reader->isStartElement() && reader->name() == "row"); // QXmlStreamAttributes attribs = reader->attributes(); bool ok; int index = reader->readAttributeInt("index", &ok); if (!ok) { reader->raiseError(i18n("invalid or missing row index")); return false; } QString str = reader->readElementText(); switch (columnMode()) { case AbstractColumn::Numeric: { double value = str.toDouble(&ok); if(!ok) { reader->raiseError(i18n("invalid row value")); return false; } setValueAt(index, value); break; } case AbstractColumn::Integer: { int value = str.toInt(&ok); if(!ok) { reader->raiseError(i18n("invalid row value")); return false; } setIntegerAt(index, value); break; } case AbstractColumn::Text: setTextAt(index, str); break; case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: QDateTime date_time = QDateTime::fromString(str,"yyyy-dd-MM hh:mm:ss:zzz"); setDateTimeAt(index, date_time); break; } return true; } //////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////// /** * \brief Return whether the object is read-only */ bool Column::isReadOnly() const { return false; } /** * \brief Return the column mode * * This function is mostly used by spreadsheets but can also be used * by plots. The column mode specifies how to interpret * the values in the column additional to the data type. */ AbstractColumn::ColumnMode Column::columnMode() const { return d->columnMode(); } /** * \brief Return the data vector size * * This returns the number of rows that actually contain data. * Rows beyond this can be masked etc. but should be ignored by filters, * plots etc. */ int Column::rowCount() const { return d->rowCount(); } /** * \brief Return the column plot designation */ AbstractColumn::PlotDesignation Column::plotDesignation() const { return d->plotDesignation(); } AbstractSimpleFilter* Column::outputFilter() const { return d->outputFilter(); } /** * \brief Return a wrapper column object used for String I/O. */ ColumnStringIO* Column::asStringColumn() const { return m_string_io; } //////////////////////////////////////////////////////////////////////////////// //! \name IntervalAttribute related functions //@{ //////////////////////////////////////////////////////////////////////////////// /** * \brief Return the formula associated with row 'row' */ QString Column::formula(int row) const { return d->formula(row); } /** * \brief Return the intervals that have associated formulas * * This can be used to make a list of formulas with their intervals. * Here is some example code: * * \code * QStringList list; * QList< Interval > intervals = my_column.formulaIntervals(); * foreach(Interval interval, intervals) * list << QString(interval.toString() + ": " + my_column.formula(interval.start())); * \endcode */ QList< Interval > Column::formulaIntervals() const { return d->formulaIntervals(); } void Column::handleFormatChange() { DEBUG("Column::handleFormatChange() mode = " << ENUM_TO_STRING(AbstractColumn, ColumnMode, columnMode())); if (columnMode() == AbstractColumn::DateTime) { auto* input_filter = static_cast(d->inputFilter()); auto* output_filter = static_cast(d->outputFilter()); DEBUG("change format " << input_filter->format().toStdString() << " to " << output_filter->format().toStdString()); input_filter->setFormat(output_filter->format()); } emit aspectDescriptionChanged(this); // the icon for the type changed if (!m_suppressDataChangedSignal) emit dataChanged(this); // all cells must be repainted setStatisticsAvailable(false); DEBUG("Column::handleFormatChange() DONE"); } /*! * calculates the minimal value in the column. * for \c count = 0, the minimum of all elements is returned. * for \c count > 0, the minimum of the first \count elements is returned. * for \c count = 0, the minimum of the last \count elements is returned. */ double Column::minimum(int count) const { double min = INFINITY; if (count == 0 && statisticsAvailable()) min = const_cast(this)->statistics().minimum; else { ColumnMode mode = columnMode(); int start, end; if (count == 0) { start = 0; end = rowCount(); } else if (count > 0) { start = 0; end = qMin(rowCount(), count); } else { start = qMax(rowCount() + count, 0); end = rowCount(); } switch (mode) { case Numeric: { QVector* vec = static_cast*>(data()); for (int row = start; row < end; ++row) { const double val = vec->at(row); if (std::isnan(val)) continue; if (val < min) min = val; } break; } case Integer: { QVector* vec = static_cast*>(data()); for (int row = start; row < end; ++row) { const int val = vec->at(row); if (std::isnan(val)) continue; if (val < min) min = val; } break; } case Text: case DateTime: case Day: case Month: default: break; } } return min; } /*! * calculates the maximal value in the column. * for \c count = 0, the maximum of all elements is returned. * for \c count > 0, the maximum of the first \count elements is returned. * for \c count = 0, the maximum of the last \count elements is returned. */ double Column::maximum(int count) const { double max = -INFINITY; if (count == 0 && statisticsAvailable()) max = const_cast(this)->statistics().maximum; else { ColumnMode mode = columnMode(); int start, end; if (count == 0) { start = 0; end = rowCount(); } else if (count > 0) { start = 0; end = qMin(rowCount(), count); } else { start = qMax(rowCount() + count, 0); end = rowCount(); } switch (mode) { case Numeric: { QVector* vec = static_cast*>(data()); for (int row = start; row < end; ++row) { const double val = vec->at(row); if (std::isnan(val)) continue; if (val > max) max = val; } break; } case Integer: { QVector* vec = static_cast*>(data()); for (int row = start; row < end; ++row) { const int val = vec->at(row); if (std::isnan(val)) continue; if (val > max) max = val; } break; } case Text: case DateTime: case Day: case Month: default: break; } } return max; } diff --git a/src/backend/datasources/LiveDataSource.cpp b/src/backend/datasources/LiveDataSource.cpp index c024c3c42..2b3dc9150 100644 --- a/src/backend/datasources/LiveDataSource.cpp +++ b/src/backend/datasources/LiveDataSource.cpp @@ -1,1032 +1,1032 @@ /*************************************************************************** File : LiveDataSource.cpp Project : LabPlot Description : Represents live data source -------------------------------------------------------------------- Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/datasources/filters/BinaryFilter.h" #include "backend/core/Project.h" #include "kdefrontend/spreadsheet/PlotDataDialog.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class LiveDataSource \brief Represents data stored in a file. Reading and writing is done with the help of appropriate I/O-filters. \ingroup datasources */ LiveDataSource::LiveDataSource(AbstractScriptingEngine* engine, const QString& name, bool loading) : Spreadsheet(engine, name, loading), m_fileType(Ascii), m_fileWatched(false), m_fileLinked(false), m_paused(false), m_prepared(false), m_keepLastValues(false), m_bytesRead(0), m_filter(nullptr), m_updateTimer(new QTimer(this)), m_fileSystemWatcher(nullptr), m_file(nullptr), m_localSocket(nullptr), m_tcpSocket(nullptr), m_udpSocket(nullptr), m_serialPort(nullptr), m_device(nullptr) { initActions(); - connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(read())); + connect(m_updateTimer, &QTimer::timeout, this, &LiveDataSource::read); } LiveDataSource::~LiveDataSource() { if (m_filter) delete m_filter; if (m_fileSystemWatcher) delete m_fileSystemWatcher; if (m_file) delete m_file; if (m_localSocket) delete m_localSocket; if (m_tcpSocket) delete m_tcpSocket; if (m_serialPort) delete m_serialPort; delete m_updateTimer; } /*! * depending on the update type, periodically or on data changes, starts the timer or activates the file watchers, respectively. */ void LiveDataSource::ready() { if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else watch(); } void LiveDataSource::initActions() { m_reloadAction = new QAction(QIcon::fromTheme("view-refresh"), i18n("Reload"), this); - connect(m_reloadAction, SIGNAL(triggered()), this, SLOT(read())); + connect(m_reloadAction, &QAction::triggered, this, &LiveDataSource::read); m_toggleLinkAction = new QAction(i18n("Link the file"), this); m_toggleLinkAction->setCheckable(true); - connect(m_toggleLinkAction, SIGNAL(triggered()), this, SLOT(linkToggled())); + connect(m_toggleLinkAction, &QAction::triggered, this, &LiveDataSource::linkToggled); m_plotDataAction = new QAction(QIcon::fromTheme("office-chart-line"), i18n("Plot data"), this); - connect(m_plotDataAction, SIGNAL(triggered()), this, SLOT(plotData())); + connect(m_plotDataAction, &QAction::triggered, this, &LiveDataSource::plotData); } QWidget* LiveDataSource::view() const { if (!m_view) m_view = new SpreadsheetView(const_cast(this), true); return m_view; } /*! * \brief Returns a list with the names of the available ports */ QStringList LiveDataSource::availablePorts() { QStringList ports; qDebug() << "available ports count:" << QSerialPortInfo::availablePorts().size(); for(const QSerialPortInfo& sp : QSerialPortInfo::availablePorts()) { ports.append(sp.portName()); qDebug() << sp.description(); qDebug() << sp.manufacturer(); qDebug() << sp.portName(); qDebug() << sp.serialNumber(); qDebug() << sp.systemLocation(); } return ports; } /*! * \brief Returns a list with the supported baud rates */ QStringList LiveDataSource::supportedBaudRates() { QStringList baudRates; for(const auto& baud : QSerialPortInfo::standardBaudRates()) baudRates.append(QString::number(baud)); return baudRates; } /*! * \brief Updates this data source at this moment */ void LiveDataSource::updateNow() { m_updateTimer->stop(); read(); //restart the timer after update if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); } /*! * \brief Continue reading from the live data source after it was paused. */ void LiveDataSource::continueReading() { m_paused = false; if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else if (m_updateType == NewData) - connect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); + connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } /*! * \brief Pause the reading of the live data source. */ void LiveDataSource::pauseReading() { m_paused = true; if (m_updateType == TimeInterval) m_updateTimer->stop(); else if (m_updateType == NewData) disconnect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); } /*! returns the list with all supported data file formats. */ QStringList LiveDataSource::fileTypes() { // see LiveDataSource::FileType return (QStringList()<< i18n("ASCII data") << i18n("Binary data") << i18n("Image") << i18n("Hierarchical Data Format (HDF)") << i18n("Network Common Data Format (NetCDF)") // << "CDF" << i18n("Flexible Image Transport System Data Format (FITS)") // << i18n("Sound") ); } void LiveDataSource::setFileName(const QString& name) { m_fileName = name; } QString LiveDataSource::fileName() const { return m_fileName; } /*! * \brief Sets the local socket's server name to name * \param name */ void LiveDataSource::setLocalSocketName(const QString & name) { m_localSocketName = name; } QString LiveDataSource::localSocketName() const { return m_localSocketName; } void LiveDataSource::setFileType(const FileType type) { m_fileType = type; } LiveDataSource::FileType LiveDataSource::fileType() const { return m_fileType; } void LiveDataSource::setFilter(AbstractFileFilter* f) { m_filter = f; } AbstractFileFilter* LiveDataSource::filter() const { return m_filter; } /*! sets whether the file should be watched or not. In the first case the data source will be automatically updated on file changes. */ void LiveDataSource::setFileWatched(const bool b) { m_fileWatched = b; } bool LiveDataSource::isFileWatched() const { return m_fileWatched; } /*! * \brief Sets whether we'll keep the last values or append it to the previous ones * \param keepLastValues */ void LiveDataSource::setKeepLastValues(const bool keepLastValues) { m_keepLastValues = keepLastValues; } bool LiveDataSource::keepLastValues() const { return m_keepLastValues; } /*! * \brief Sets the serial port's baud rate * \param baudrate */ void LiveDataSource::setBaudRate(const int baudrate) { m_baudRate = baudrate; } int LiveDataSource::baudRate() const { return m_baudRate; } /*! * \brief Sets the source's update interval to \c interval * \param interval */ void LiveDataSource::setUpdateInterval(const int interval) { m_updateInterval = interval; m_updateTimer->start(m_updateInterval); } int LiveDataSource::updateInterval() const { return m_updateInterval; } /*! * \brief Sets how many values we should store * \param keepnvalues */ void LiveDataSource::setKeepNvalues(const int keepnvalues) { m_keepNvalues = keepnvalues; } int LiveDataSource::keepNvalues() const { return m_keepNvalues; } /*! * \brief Sets the network socket's port to port * \param port */ void LiveDataSource::setPort(const int port) { m_port = port; } void LiveDataSource::setBytesRead(const qint64 bytes) { m_bytesRead = bytes; } int LiveDataSource::bytesRead() const { return m_bytesRead; } int LiveDataSource::port() const { return m_port; } /*! * \brief Sets the serial port's name to name * \param name */ void LiveDataSource::setSerialPort(const QString &name) { m_serialPortName = name; } QString LiveDataSource::serialPortName() const { return m_serialPortName; } bool LiveDataSource::isPaused() const { return m_paused; } /*! * \brief Sets the sample rate to samplerate * \param samplerate */ void LiveDataSource::setSampleRate(const int samplerate) { m_sampleRate = samplerate; } int LiveDataSource::sampleRate() const { return m_sampleRate; } /*! * \brief Sets the source's type to sourcetype * \param sourcetype */ void LiveDataSource::setSourceType(const SourceType sourcetype) { m_sourceType = sourcetype; } LiveDataSource::SourceType LiveDataSource::sourceType() const { return m_sourceType; } /*! * \brief Sets the source's reading type to readingType * \param readingType */ void LiveDataSource::setReadingType(const ReadingType readingType) { m_readingType = readingType; } LiveDataSource::ReadingType LiveDataSource::readingType() const { return m_readingType; } /*! * \brief Sets the source's update type to updatetype and handles this change * \param updatetype */ void LiveDataSource::setUpdateType(const UpdateType updatetype) { if (updatetype == NewData) { m_updateTimer->stop(); if (m_fileSystemWatcher == nullptr) watch(); else - connect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); + connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } else { if (m_fileSystemWatcher) - disconnect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); + disconnect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } m_updateType = updatetype; } LiveDataSource::UpdateType LiveDataSource::updateType() const { return m_updateType; } /*! * \brief Sets the network socket's host * \param host */ void LiveDataSource::setHost(const QString & host) { m_host = host; } QString LiveDataSource::host() const { return m_host; } /*! sets whether only a link to the file is saved in the project file (\c b=true) or the whole content of the file (\c b=false). */ void LiveDataSource::setFileLinked(const bool b) { m_fileLinked = b; } /*! returns \c true if only a link to the file is saved in the project file. \c false otherwise. */ bool LiveDataSource::isFileLinked() const { return m_fileLinked; } QIcon LiveDataSource::icon() const { QIcon icon; if (m_fileType == LiveDataSource::Ascii) icon = QIcon::fromTheme("text-plain"); else if (m_fileType == LiveDataSource::Binary) icon = QIcon::fromTheme("application-octet-stream"); else if (m_fileType == LiveDataSource::Image) icon = QIcon::fromTheme("image-x-generic"); // TODO: HDF, NetCDF, FITS, etc. return icon; } QMenu* LiveDataSource::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertAction(firstAction, m_plotDataAction); menu->insertSeparator(firstAction); //TODO: doesnt' always make sense... // if (!m_fileWatched) // menu->insertAction(firstAction, m_reloadAction); // // m_toggleWatchAction->setChecked(m_fileWatched); // menu->insertAction(firstAction, m_toggleWatchAction); // // m_toggleLinkAction->setChecked(m_fileLinked); // menu->insertAction(firstAction, m_toggleLinkAction); return menu; } //############################################################################## //################################# SLOTS #################################### //############################################################################## /* * called periodically or on new data changes (file changed, new data in the socket, etc.) */ void LiveDataSource::read() { if (m_filter == nullptr) return; //initialize the device (file, socket, serial port), when calling this function for the first time if (!m_prepared) { switch (m_sourceType) { case FileOrPipe: m_file = new QFile(m_fileName); m_device = m_file; break; case NetworkTcpSocket: m_tcpSocket = new QTcpSocket(this); m_tcpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); m_device = m_tcpSocket; qDebug() << "socket state before preparing: " << m_tcpSocket->state(); - connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); - connect(m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(tcpSocketError(QAbstractSocket::SocketError))); + connect(m_tcpSocket, &QTcpSocket::readyRead, this, &LiveDataSource::readyRead); + connect(m_tcpSocket, static_cast(&QTcpSocket::error), this, &LiveDataSource::tcpSocketError); qDebug() << "socket state after preparing: " << m_tcpSocket->state(); break; case NetworkUdpSocket: m_udpSocket = new QUdpSocket(this); m_udpSocket->bind(QHostAddress(m_host), m_port); qDebug() << "socket state before preparing: " << m_udpSocket->state(); m_udpSocket->connectToHost(m_host, m_port); m_device = m_udpSocket; - connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); - connect(m_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(tcpSocketError(QAbstractSocket::SocketError))); + connect(m_udpSocket, &QUdpSocket::readyRead, this, &LiveDataSource::readyRead); + connect(m_udpSocket, static_cast(&QUdpSocket::error), this, &LiveDataSource::tcpSocketError); qDebug() << "socket state after preparing: " << m_udpSocket->state(); break; case LocalSocket: m_localSocket = new QLocalSocket(this); qDebug() << "socket state before preparing: " << m_localSocket->state(); m_localSocket->connectToServer(m_localSocketName, QLocalSocket::ReadOnly); qDebug() << "socket state after preparing: " << m_localSocket->state(); m_device = m_localSocket; - connect(m_localSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); - connect(m_localSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError))); + connect(m_localSocket, &QLocalSocket::readyRead, this, &LiveDataSource::readyRead); + connect(m_localSocket, static_cast(&QLocalSocket::error), this, &LiveDataSource::localSocketError); break; case SerialPort: m_serialPort = new QSerialPort; m_device = m_serialPort; m_serialPort->setBaudRate(m_baudRate); m_serialPort->setPortName(m_serialPortName); - connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(serialPortError(QSerialPort::SerialPortError))); - connect(m_serialPort, SIGNAL(readyRead()), this, SLOT(readyRead())); + connect(m_serialPort, static_cast(&QSerialPort::error), this, &LiveDataSource::serialPortError); + connect(m_serialPort, &QSerialPort::readyRead, this, &LiveDataSource::readyRead); break; } m_prepared = true; } qint64 bytes = 0; switch (m_sourceType) { case FileOrPipe: switch (m_fileType) { case Ascii: qDebug() << "Reading live ascii file.." ; bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); m_bytesRead += bytes; qDebug() << "Read " << bytes << " bytes, in total: " << m_bytesRead; break; case Binary: //bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); m_bytesRead += bytes; case Image: case HDF: case NETCDF: case FITS: break; } break; case NetworkTcpSocket: DEBUG("reading from a TCP socket"); qDebug() << "reading from a TCP socket before abort: " << m_tcpSocket->state(); m_tcpSocket->abort(); m_tcpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); qDebug() << "reading from a TCP socket after reconnect: " << m_tcpSocket->state(); break; case NetworkUdpSocket: DEBUG("reading from a UDP socket"); qDebug() << "reading from a UDP socket before abort: " << m_udpSocket->state(); m_udpSocket->abort(); m_udpSocket->bind(QHostAddress(m_host), m_port); m_udpSocket->connectToHost(m_host, m_port); qDebug() << "reading from a UDP socket after reconnect: " << m_udpSocket->state(); break; case LocalSocket: DEBUG("reading from a local socket"); qDebug() << "reading from a local socket before abort: " << m_localSocket->state(); m_localSocket->abort(); m_localSocket->connectToServer(m_localSocketName, QLocalSocket::ReadOnly); qDebug() << "reading from a local socket after reconnect: " << m_localSocket->state(); break; case SerialPort: DEBUG("reading from the serial port"); m_serialPort->setBaudRate(m_baudRate); m_serialPort->setPortName(m_serialPortName); m_device = m_serialPort; //TODO break; } } /*! * Slot for the signal that is emitted once every time new data is available for reading from the device. * It will only be emitted again once new data is available, such as when a new payload of network data has arrived on the network socket, * or when a new block of data has been appended to your device. */ void LiveDataSource::readyRead() { DEBUG("Got new data from the device"); qDebug()<< "Got new data from the device"; if (m_fileType == Ascii) dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_device, this); // else if (m_fileType == Binary) // dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_device, this); //since we won't have the timer to call read() where we create new connections //for sequencial devices in read() we just request data/connect to servers if (m_updateType == NewData) read(); } void LiveDataSource::localSocketError(QLocalSocket::LocalSocketError socketError) { Q_UNUSED(socketError); /*disconnect(m_localSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError))); disconnect(m_localSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));*/ /*switch (socketError) { case QLocalSocket::ServerNotFoundError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket was not found. Please check the socket name.")); break; case QLocalSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The connection was refused by the peer")); break; case QLocalSocket::PeerClosedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket has closed the connection.")); break; default: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The following error occurred: %1.").arg(m_localSocket->errorString())); }*/ } void LiveDataSource::tcpSocketError(QAbstractSocket::SocketError socketError) { Q_UNUSED(socketError); /*switch (socketError) { case QAbstractSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The connection was refused by the peer. Make sure the server is running and check the host name and port settings.")); break; case QAbstractSocket::RemoteHostClosedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The remote host closed the connection.")); break; case QAbstractSocket::HostNotFoundError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The host was not found. Please check the host name and port settings.")); break; default: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The following error occurred: %1.").arg(m_tcpSocket->errorString())); }*/ } void LiveDataSource::serialPortError(QSerialPort::SerialPortError serialPortError) { switch (serialPortError) { case QSerialPort::DeviceNotFoundError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to open the device.")); break; case QSerialPort::PermissionError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to open the device. Please check your permissions on this device.")); break; case QSerialPort::OpenError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Device already opened.")); break; case QSerialPort::NotOpenError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device is not opened.")); break; case QSerialPort::ReadError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data.")); break; case QSerialPort::ResourceError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data. The device is removed.")); break; case QSerialPort::TimeoutError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device timed out.")); break; case QSerialPort::ParityError: case QSerialPort::FramingError: case QSerialPort::BreakConditionError: case QSerialPort::WriteError: case QSerialPort::UnsupportedOperationError: case QSerialPort::UnknownError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The following error occurred: %1.").arg(m_serialPort->errorString())); break; case QSerialPort::NoError: break; } } void LiveDataSource::watchToggled() { m_fileWatched = !m_fileWatched; watch(); project()->setChanged(true); } void LiveDataSource::linkToggled() { m_fileLinked = !m_fileLinked; project()->setChanged(true); } //watch the file upon reading for changes if required void LiveDataSource::watch() { if (m_fileWatched) { if (!m_fileSystemWatcher) { m_fileSystemWatcher = new QFileSystemWatcher; - connect (m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); + connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } if ( !m_fileSystemWatcher->files().contains(m_fileName) ) m_fileSystemWatcher->addPath(m_fileName); } else { if (m_fileSystemWatcher) m_fileSystemWatcher->removePath(m_fileName); } } /*! 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 LiveDataSource::fileInfoString(const QString &name) { QString infoString; QFileInfo fileInfo; QString fileTypeString; QIODevice *file = new QFile(name); QString fileName; if (name.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + name; else fileName = name; if(file==0) file = new QFile(fileName); if (file->open(QIODevice::ReadOnly)) { QStringList infoStrings; //general information about the file infoStrings << "" + fileName + "
"; fileInfo.setFile(fileName); 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())); #ifdef HAVE_FITS if (fileName.endsWith(QLatin1String(".fits"))) { infoStrings << i18n("Images: %1", QString::number(FITSFilter::imagesCount(fileName) )); infoStrings << i18n("Tables: %1", QString::number(FITSFilter::tablesCount(fileName) )); } #endif // file type and type specific information about the file #ifdef Q_OS_LINUX QProcess *proc = new QProcess(); QStringList args; args<<"-b"<start( "file", args); if(proc->waitForReadyRead(1000) == false) infoStrings << i18n("Could not open file %1 for reading.", 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 //TODO depending on the file type, generate additional information about the file: //Number of lines for ASCII, color-depth for images etc. Use the specific filters here. // port the old labplot1.6 code. if( fileTypeString.contains("ASCII")) { infoStrings << "
"; //TODO: consider choosen separator infoStrings << i18n("Number of columns: %1", AsciiFilter::columnNumber(fileName)); infoStrings << i18n("Number of lines: %1", AsciiFilter::lineNumber(fileName)); } infoString += infoStrings.join("
"); } else infoString += i18n("Could not open file %1 for reading.", fileName); return infoString; } void LiveDataSource::plotData() { PlotDataDialog* dlg = new PlotDataDialog(this); dlg->exec(); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void LiveDataSource::save(QXmlStreamWriter* writer) const { writer->writeStartElement("liveDataSource"); writeBasicAttributes(writer); writeCommentElement(writer); //general writer->writeStartElement("general"); writer->writeAttribute("fileName", m_fileName); writer->writeAttribute("fileType", QString::number(m_fileType)); writer->writeAttribute("fileWatched", QString::number(m_fileWatched)); writer->writeAttribute("fileLinked", QString::number(m_fileLinked)); writer->writeAttribute("updateType", QString::number(m_updateType)); writer->writeAttribute("readingType", QString::number(m_readingType)); writer->writeAttribute("sourceType", QString::number(m_sourceType)); writer->writeAttribute("keepValues", QString::number(m_keepNvalues)); if (m_updateType == TimeInterval) writer->writeAttribute("updateInterval", QString::number(m_updateInterval)); if (m_readingType != TillEnd) writer->writeAttribute("sampleRate", QString::number(m_sampleRate)); switch (m_sourceType) { case SerialPort: writer->writeAttribute("baudRate", QString::number(m_baudRate)); writer->writeAttribute("serialPortName", m_serialPortName); break; case NetworkTcpSocket: case NetworkUdpSocket: writer->writeAttribute("host", m_host); writer->writeAttribute("port", QString::number(m_port)); break; case FileOrPipe: break; case LocalSocket: break; default: break; } writer->writeEndElement(); //filter m_filter->save(writer); //columns if (!m_fileLinked) { foreach (Column * col, children(IncludeHidden)) col->save(writer); } writer->writeEndElement(); // "liveDataSource" } /*! Loads from XML. */ bool LiveDataSource::load(XmlStreamReader* reader, bool preview) { if(!reader->isStartElement() || reader->name() != "liveDataSource") { reader->raiseError(i18n("no liveDataSource element found")); return false; } if (!readBasicAttributes(reader)) return false; QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "LiveDataSource") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (reader->name() == "general") { attribs = reader->attributes(); str = attribs.value("fileName").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileName'")); else m_fileName = str; str = attribs.value("fileType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileType'")); else m_fileType = (FileType)str.toInt(); str = attribs.value("fileWatched").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileWatched'")); else m_fileWatched = str.toInt(); str = attribs.value("fileLinked").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileLinked'")); else m_fileLinked = str.toInt(); str = attribs.value("updateType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'updateType'")); else m_updateType = static_cast(str.toInt()); str = attribs.value("sourceType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'sourceType'")); else m_sourceType = static_cast(str.toInt()); str = attribs.value("readingType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'readingType'")); else m_readingType = static_cast(str.toInt()); if (m_updateType == TimeInterval) { str = attribs.value("updateInterval").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'updateInterval'")); else m_updateInterval = str.toInt(); } if (m_readingType != TillEnd) { str = attribs.value("sampleRate").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'sampleRate'")); else m_sampleRate = str.toInt(); } switch (m_sourceType) { case SerialPort: str = attribs.value("baudRate").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'baudRate'")); else m_baudRate = str.toInt(); str = attribs.value("serialPortName").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'serialPortName'")); else m_serialPortName = str; break; case NetworkTcpSocket: case NetworkUdpSocket: str = attribs.value("host").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'host'")); else m_host = str; str = attribs.value("port").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'port'")); else m_host = str; break; case FileOrPipe: break; case LocalSocket: break; default: break; } } else if (reader->name() == "asciiFilter") { m_filter = new AsciiFilter(); if (!m_filter->load(reader)) return false; } else if(reader->name() == "column") { Column* column = new Column("", AbstractColumn::Text); if (!column->load(reader, preview)) { delete column; setColumnCount(0); return false; } addChild(column); } else {// unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } //read the content of the file if it was only linked if (m_fileLinked) this->read(); return !reader->hasError(); } diff --git a/src/backend/matrix/Matrix.cpp b/src/backend/matrix/Matrix.cpp index 62daeb80f..90eb440a6 100644 --- a/src/backend/matrix/Matrix.cpp +++ b/src/backend/matrix/Matrix.cpp @@ -1,1272 +1,1272 @@ /*************************************************************************** File : Matrix.cpp Project : Matrix Description : Spreadsheet with a MxN matrix data model -------------------------------------------------------------------- Copyright : (C) 2008-2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2015-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 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 "Matrix.h" #include "MatrixPrivate.h" #include "matrixcommands.h" #include "backend/matrix/MatrixModel.h" #include "backend/core/Folder.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include "commonfrontend/matrix/MatrixView.h" #include "kdefrontend/spreadsheet/ExportSpreadsheetDialog.h" #include #include #include #include #include #include /*! This class manages matrix based data (i.e., mathematically a MxN matrix with M rows, N columns). This data is typically used to for 3D plots. The values of the matrix are stored as generic values. Each column of the matrix is stored in a QVector objects. \ingroup backend */ Matrix::Matrix(AbstractScriptingEngine* engine, int rows, int cols, const QString& name, const AbstractColumn::ColumnMode mode) : AbstractDataSource(engine, name), d(new MatrixPrivate(this, mode)), m_model(nullptr) { //set initial number of rows and columns appendColumns(cols); appendRows(rows); init(); } Matrix::Matrix(AbstractScriptingEngine* engine, const QString& name, bool loading, const AbstractColumn::ColumnMode mode) : AbstractDataSource(engine, name), d(new MatrixPrivate(this, mode)), m_model(nullptr) { if (!loading) init(); } Matrix::~Matrix() { delete d; } void Matrix::init() { KConfig config; KConfigGroup group = config.group("Matrix"); //matrix dimension int rows = group.readEntry("RowCount", 10); int cols = group.readEntry("ColumnCount", 10); appendRows(rows); appendColumns(cols); //mapping to logical x- and y-coordinates d->xStart = group.readEntry("XStart", 0.0); d->xEnd = group.readEntry("XEnd", 1.0); d->yStart = group.readEntry("YStart", 0.0); d->yEnd = group.readEntry("YEnd", 1.0); //format QByteArray formatba = group.readEntry("NumericFormat", "f").toLatin1(); d->numericFormat = *formatba.data(); d->precision = group.readEntry("Precision", 3); d->headerFormat = (Matrix::HeaderFormat)group.readEntry("HeaderFormat", (int)Matrix::HeaderRowsColumns); } /*! Returns an icon to be used for decorating my views. */ QIcon Matrix::icon() const { return QIcon::fromTheme("labplot-matrix"); } /*! Returns a new context menu. The caller takes ownership of the menu. */ QMenu* Matrix::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); emit requestProjectContextMenu(menu); return menu; } QWidget* Matrix::view() const { if (!m_view) { MatrixView* view = new MatrixView(const_cast(this)); m_model = view->model(); m_view = view; } return m_view; } bool Matrix::exportView() const { ExportSpreadsheetDialog* dlg = new ExportSpreadsheetDialog(m_view); dlg->setFileName(name()); dlg->setMatrixMode(true); //TODO FITS filter to decide if it can be exported to both dlg->setExportTo(QStringList() << i18n("FITS image") << i18n("FITS table")); if (reinterpret_cast(m_view)->selectedColumnCount() == 0) { dlg->setExportSelection(false); } bool ret; if ( (ret = (dlg->exec() == QDialog::Accepted)) ) { const QString path = dlg->path(); const MatrixView* view = reinterpret_cast(m_view); WAIT_CURSOR; if (dlg->format() == ExportSpreadsheetDialog::LaTeX) { const bool verticalHeader = dlg->matrixVerticalHeader(); const bool horizontalHeader = dlg->matrixHorizontalHeader(); const bool latexHeader = dlg->exportHeader(); const bool gridLines = dlg->gridLines(); const bool entire = dlg->entireSpreadheet(); const bool captions = dlg->captions(); view->exportToLaTeX(path, verticalHeader, horizontalHeader, latexHeader, gridLines, entire, captions); } else if (dlg->format() == ExportSpreadsheetDialog::FITS) { const int exportTo = dlg->exportToFits(); view->exportToFits(path, exportTo ); } else { const QString separator = dlg->separator(); view->exportToFile(path, separator); } RESET_CURSOR; } delete dlg; return ret; } bool Matrix::printView() { QPrinter printer; QPrintDialog* dlg = new QPrintDialog(&printer, m_view); bool ret; dlg->setWindowTitle(i18n("Print Matrix")); if ( (ret = (dlg->exec() == QDialog::Accepted)) ) { const MatrixView* view = reinterpret_cast(m_view); view->print(&printer); } delete dlg; return ret; } bool Matrix::printPreview() const { const MatrixView* view = reinterpret_cast(m_view); QPrintPreviewDialog* dlg = new QPrintPreviewDialog(m_view); - connect(dlg, SIGNAL(paintRequested(QPrinter*)), view, SLOT(print(QPrinter*))); + connect(dlg, &QPrintPreviewDialog::paintRequested, view, &MatrixView::print); return dlg->exec(); } //############################################################################## //########################## getter methods ################################## //############################################################################## void* Matrix::data() const { return d->data; } BASIC_D_READER_IMPL(Matrix, AbstractColumn::ColumnMode, mode, mode) BASIC_D_READER_IMPL(Matrix, int, rowCount, rowCount) BASIC_D_READER_IMPL(Matrix, int, columnCount, columnCount) BASIC_D_READER_IMPL(Matrix, double, xStart, xStart) BASIC_D_READER_IMPL(Matrix, double, xEnd, xEnd) BASIC_D_READER_IMPL(Matrix, double, yStart, yStart) BASIC_D_READER_IMPL(Matrix, double, yEnd, yEnd) BASIC_D_READER_IMPL(Matrix, char, numericFormat, numericFormat) BASIC_D_READER_IMPL(Matrix, int, precision, precision) BASIC_D_READER_IMPL(Matrix, Matrix::HeaderFormat, headerFormat, headerFormat) CLASS_D_READER_IMPL(Matrix, QString, formula, formula) void Matrix::setSuppressDataChangedSignal(bool b) { if (m_model) m_model->setSuppressDataChangedSignal(b); } void Matrix::setChanged() { if (m_model) m_model->setChanged(); } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## void Matrix::setRowCount(int count) { if (count == d->rowCount) return; const int diff = count - d->rowCount; if (diff > 0) appendRows(diff); else if (diff < 0) removeRows(rowCount() + diff, -diff); } void Matrix::setColumnCount(int count) { if (count == d->columnCount) return; const int diff = count - columnCount(); if (diff > 0) appendColumns(diff); else if (diff < 0) removeColumns(columnCount() + diff, -diff); } STD_SETTER_CMD_IMPL_F_S(Matrix, SetXStart, double, xStart, updateViewHeader) void Matrix::setXStart(double xStart) { if (xStart != d->xStart) exec(new MatrixSetXStartCmd(d, xStart, i18n("%1: x-start changed"))); } STD_SETTER_CMD_IMPL_F_S(Matrix, SetXEnd, double, xEnd, updateViewHeader) void Matrix::setXEnd(double xEnd) { if (xEnd != d->xEnd) exec(new MatrixSetXEndCmd(d, xEnd, i18n("%1: x-end changed"))); } STD_SETTER_CMD_IMPL_F_S(Matrix, SetYStart, double, yStart, updateViewHeader) void Matrix::setYStart(double yStart) { if (yStart != d->yStart) exec(new MatrixSetYStartCmd(d, yStart, i18n("%1: y-start changed"))); } STD_SETTER_CMD_IMPL_F_S(Matrix, SetYEnd, double, yEnd, updateViewHeader) void Matrix::setYEnd(double yEnd) { if (yEnd != d->yEnd) exec(new MatrixSetYEndCmd(d, yEnd, i18n("%1: y-end changed"))); } STD_SETTER_CMD_IMPL_S(Matrix, SetNumericFormat, char, numericFormat) void Matrix::setNumericFormat(char format) { if (format != d->numericFormat) exec(new MatrixSetNumericFormatCmd(d, format, i18n("%1: numeric format changed"))); } STD_SETTER_CMD_IMPL_S(Matrix, SetPrecision, int, precision) void Matrix::setPrecision(int precision) { if (precision != d->precision) exec(new MatrixSetPrecisionCmd(d, precision, i18n("%1: precision changed"))); } //TODO: make this undoable? void Matrix::setHeaderFormat(Matrix::HeaderFormat format) { d->headerFormat = format; m_model->updateHeader(); if (m_view) (reinterpret_cast(m_view))->resizeHeaders(); emit headerFormatChanged(format); } //columns void Matrix::insertColumns(int before, int count) { if (count < 1 || before < 0 || before > columnCount()) return; WAIT_CURSOR; exec(new MatrixInsertColumnsCmd(d, before, count)); RESET_CURSOR; } void Matrix::appendColumns(int count) { insertColumns(columnCount(), count); } void Matrix::removeColumns(int first, int count) { if (count < 1 || first < 0 || first+count > columnCount()) return; WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixRemoveColumnsCmd(d, first, count)); break; case AbstractColumn::Text: exec(new MatrixRemoveColumnsCmd(d, first, count)); break; case AbstractColumn::Integer: exec(new MatrixRemoveColumnsCmd(d, first, count)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixRemoveColumnsCmd(d, first, count)); break; } RESET_CURSOR; } void Matrix::clearColumn(int c) { WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixClearColumnCmd(d, c)); break; case AbstractColumn::Text: exec(new MatrixClearColumnCmd(d, c)); break; case AbstractColumn::Integer: exec(new MatrixClearColumnCmd(d, c)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixClearColumnCmd(d, c)); break; } RESET_CURSOR; } //rows void Matrix::insertRows(int before, int count) { if (count < 1 || before < 0 || before > rowCount()) return; WAIT_CURSOR; exec(new MatrixInsertRowsCmd(d, before, count)); RESET_CURSOR; } void Matrix::appendRows(int count) { insertRows(rowCount(), count); } void Matrix::removeRows(int first, int count) { if (count < 1 || first < 0 || first+count > rowCount()) return; WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixRemoveRowsCmd(d, first, count)); break; case AbstractColumn::Text: exec(new MatrixRemoveRowsCmd(d, first, count)); break; case AbstractColumn::Integer: exec(new MatrixRemoveRowsCmd(d, first, count)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixRemoveRowsCmd(d, first, count)); break; } RESET_CURSOR; } void Matrix::clearRow(int r) { switch (d->mode) { case AbstractColumn::Numeric: for (int c = 0; c < columnCount(); ++c) exec(new MatrixSetCellValueCmd(d, r, c, 0.0)); break; case AbstractColumn::Text: for (int c = 0; c < columnCount(); ++c) exec(new MatrixSetCellValueCmd(d, r, c, QString())); break; case AbstractColumn::Integer: for (int c = 0; c < columnCount(); ++c) exec(new MatrixSetCellValueCmd(d, r, c, 0)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int c = 0; c < columnCount(); ++c) exec(new MatrixSetCellValueCmd(d, r, c, QDateTime())); break; } } //! Return the value in the given cell (needs explicit instantiation) template T Matrix::cell(int row, int col) const { return d->cell(row, col); } template double Matrix::cell(int row, int col) const; //! Return the text displayed in the given cell (needs explicit instantiation) template QString Matrix::text(int row, int col) { return QLocale().toString(cell(row,col), d->numericFormat, d->precision); } template QString Matrix::text(int row, int col); //! Set the value of the cell (needs explicit instantiation) template void Matrix::setCell(int row, int col, T value) { if(row < 0 || row >= rowCount()) return; if(col < 0 || col >= columnCount()) return; exec(new MatrixSetCellValueCmd(d, row, col, value)); } template void Matrix::setCell(int row, int col, double value); void Matrix::clearCell(int row, int col) { switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixSetCellValueCmd(d, row, col, 0.0)); break; case AbstractColumn::Text: exec(new MatrixSetCellValueCmd(d, row, col, QString())); break; case AbstractColumn::Integer: exec(new MatrixSetCellValueCmd(d, row, col, 0)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixSetCellValueCmd(d, row, col, QDateTime())); break; } } void Matrix::setDimensions(int rows, int cols) { if( (rows < 0) || (cols < 0 ) || (rows == rowCount() && cols == columnCount()) ) return; WAIT_CURSOR; beginMacro(i18n("%1: set matrix size to %2x%3", name(), rows, cols)); int col_diff = cols - columnCount(); if (col_diff > 0) insertColumns(columnCount(), col_diff); else if (col_diff < 0) removeColumns(columnCount() + col_diff, -col_diff); int row_diff = rows - rowCount(); if(row_diff > 0) appendRows(row_diff); else if (row_diff < 0) removeRows(rowCount() + row_diff, -row_diff); endMacro(); RESET_CURSOR; } void Matrix::copy(Matrix* other) { WAIT_CURSOR; beginMacro(i18n("%1: copy %2", name(), other->name())); int rows = other->rowCount(); int columns = other->columnCount(); setDimensions(rows, columns); for (int i=0; irowHeight(i)); for (int i=0; icolumnWidth(i)); d->suppressDataChange = true; switch (d->mode) { case AbstractColumn::Numeric: for (int i = 0; i < columns; i++) setColumnCells(i, 0, rows-1, other->columnCells(i, 0, rows-1)); break; case AbstractColumn::Text: for (int i = 0; i < columns; i++) setColumnCells(i, 0, rows-1, other->columnCells(i, 0, rows-1)); break; case AbstractColumn::Integer: for (int i = 0; i < columns; i++) setColumnCells(i, 0, rows-1, other->columnCells(i, 0, rows-1)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int i = 0; i < columns; i++) setColumnCells(i, 0, rows-1, other->columnCells(i, 0, rows-1)); break; } setCoordinates(other->xStart(), other->xEnd(), other->yStart(), other->yEnd()); setNumericFormat(other->numericFormat()); setPrecision(other->precision()); d->formula = other->formula(); d->suppressDataChange = false; emit dataChanged(0, 0, rows-1, columns-1); if (m_view) reinterpret_cast(m_view)->adjustHeaders(); endMacro(); RESET_CURSOR; } //! Duplicate the matrix inside its folder void Matrix::duplicate() { Matrix* matrix = new Matrix(0, rowCount(), columnCount(), name()); matrix->copy(this); if (folder()) folder()->addChild(matrix); } void Matrix::addRows() { if (!m_view) return; WAIT_CURSOR; int count = reinterpret_cast(m_view)->selectedRowCount(false); beginMacro(i18np("%1: add %2 rows", "%1: add %2 rows", name(), count)); exec(new MatrixInsertRowsCmd(d, rowCount(), count)); endMacro(); RESET_CURSOR; } void Matrix::addColumns() { if (!m_view) return; WAIT_CURSOR; int count = reinterpret_cast(m_view)->selectedRowCount(false); beginMacro(i18np("%1: add %2 column", "%1: add %2 columns", name(), count)); exec(new MatrixInsertColumnsCmd(d, columnCount(), count)); endMacro(); RESET_CURSOR; } void Matrix::setCoordinates(double x1, double x2, double y1, double y2) { exec(new MatrixSetCoordinatesCmd(d, x1, x2, y1, y2)); } void Matrix::setFormula(const QString& formula) { exec(new MatrixSetFormulaCmd(d, formula)); } //! This method should only be called by the view. /** This method does not change the view, it only changes the * values that are saved when the matrix is saved. The view * has to take care of reading and applying these values */ void Matrix::setRowHeight(int row, int height) { d->setRowHeight(row, height); } //! This method should only be called by the view. /** This method does not change the view, it only changes the * values that are saved when the matrix is saved. The view * has to take care of reading and applying these values */ void Matrix::setColumnWidth(int col, int width) { d->setColumnWidth(col, width); } int Matrix::rowHeight(int row) const { return d->rowHeight(row); } int Matrix::columnWidth(int col) const { return d->columnWidth(col); } //! Return the values in the given cells as vector template QVector Matrix::columnCells(int col, int first_row, int last_row) { return d->columnCells(col, first_row, last_row); } //! Set the values in the given cells from a double vector template void Matrix::setColumnCells(int col, int first_row, int last_row, const QVector& values) { WAIT_CURSOR; exec(new MatrixSetColumnCellsCmd(d, col, first_row, last_row, values)); RESET_CURSOR; } //! Return the values in the given cells as vector (needs explicit instantiation) template QVector Matrix::rowCells(int row, int first_column, int last_column) { return d->rowCells(row, first_column, last_column); } template QVector Matrix::rowCells(int row, int first_column, int last_column); template QVector Matrix::rowCells(int row, int first_column, int last_column); template QVector Matrix::rowCells(int row, int first_column, int last_column); template QVector Matrix::rowCells(int row, int first_column, int last_column); //! Set the values in the given cells from a double vector template void Matrix::setRowCells(int row, int first_column, int last_column, const QVector& values) { WAIT_CURSOR; exec(new MatrixSetRowCellsCmd(d, row, first_column, last_column, values)); RESET_CURSOR; } void Matrix::setData(void* data) { bool isEmpty = false; switch (d->mode) { case AbstractColumn::Numeric: if (static_cast>*>(data)->isEmpty()) isEmpty = true; break; case AbstractColumn::Text: if (static_cast>*>(data)->isEmpty()) isEmpty = true; break; case AbstractColumn::Integer: if (static_cast>*>(data)->isEmpty()) isEmpty = true; break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: if (static_cast>*>(data)->isEmpty()) isEmpty = true; break; } if (!isEmpty) exec(new MatrixReplaceValuesCmd(d, data)); } //############################################################################## //######################### Public slots ##################################### //############################################################################## //! Clear the whole matrix (i.e. reset all cells) void Matrix::clear() { WAIT_CURSOR; beginMacro(i18n("%1: clear", name())); switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixClearCmd(d)); break; case AbstractColumn::Text: exec(new MatrixClearCmd(d)); break; case AbstractColumn::Integer: exec(new MatrixClearCmd(d)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixClearCmd(d)); break; } endMacro(); RESET_CURSOR; } void Matrix::transpose() { WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixTransposeCmd(d)); break; case AbstractColumn::Text: exec(new MatrixTransposeCmd(d)); break; case AbstractColumn::Integer: exec(new MatrixTransposeCmd(d)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixTransposeCmd(d)); break; } RESET_CURSOR; } void Matrix::mirrorHorizontally() { WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixMirrorHorizontallyCmd(d)); break; case AbstractColumn::Text: exec(new MatrixMirrorHorizontallyCmd(d)); break; case AbstractColumn::Integer: exec(new MatrixMirrorHorizontallyCmd(d)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixMirrorHorizontallyCmd(d)); break; } RESET_CURSOR; } void Matrix::mirrorVertically() { WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixMirrorVerticallyCmd(d)); break; case AbstractColumn::Text: exec(new MatrixMirrorVerticallyCmd(d)); break; case AbstractColumn::Integer: exec(new MatrixMirrorVerticallyCmd(d)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixMirrorVerticallyCmd(d)); break; } RESET_CURSOR; } //############################################################################## //###################### Private implementation ############################### //############################################################################## MatrixPrivate::MatrixPrivate(Matrix* owner, const AbstractColumn::ColumnMode m) : q(owner), data(0), mode(m), rowCount(0), columnCount(0), suppressDataChange(false) { switch (mode) { case AbstractColumn::Numeric: data = new QVector>(); break; case AbstractColumn::Text: data = new QVector>(); break; case AbstractColumn::Month: case AbstractColumn::Day: case AbstractColumn::DateTime: data = new QVector>(); break; case AbstractColumn::Integer: data = new QVector>(); break; } } MatrixPrivate::~MatrixPrivate() { if (data) { switch (mode) { case AbstractColumn::Numeric: delete static_cast>*>(data); break; case AbstractColumn::Text: delete static_cast>*>(data); break; case AbstractColumn::Integer: delete static_cast>*>(data); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: delete static_cast>*>(data); break; } } } void MatrixPrivate::updateViewHeader() { reinterpret_cast(q->m_view)->model()->updateHeader(); } /*! Insert \count columns before column number \c before */ void MatrixPrivate::insertColumns(int before, int count) { Q_ASSERT(before >= 0); Q_ASSERT(before <= columnCount); emit q->columnsAboutToBeInserted(before, count); switch (mode) { case AbstractColumn::Numeric: for (int i = 0; i < count; i++) { static_cast>*>(data)->insert(before+i, QVector(rowCount)); columnWidths.insert(before+i, 0); } break; case AbstractColumn::Text: for (int i = 0; i < count; i++) { static_cast>*>(data)->insert(before+i, QVector(rowCount)); columnWidths.insert(before+i, 0); } break; case AbstractColumn::Integer: for (int i = 0; i < count; i++) { static_cast>*>(data)->insert(before+i, QVector(rowCount)); columnWidths.insert(before+i, 0); } break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int i = 0; i < count; i++) { static_cast>*>(data)->insert(before+i, QVector(rowCount)); columnWidths.insert(before+i, 0); } break; } columnCount += count; emit q->columnsInserted(before, count); } /*! Remove \c count columns starting with column index \c first */ void MatrixPrivate::removeColumns(int first, int count) { emit q->columnsAboutToBeRemoved(first, count); Q_ASSERT(first >= 0); Q_ASSERT(first + count <= columnCount); switch (mode) { case AbstractColumn::Numeric: (static_cast>*>(data))->remove(first, count); break; case AbstractColumn::Text: (static_cast>*>(data))->remove(first, count); break; case AbstractColumn::Integer: (static_cast>*>(data))->remove(first, count); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: (static_cast>*>(data))->remove(first, count); break; } for (int i = 0; i < count; i++) columnWidths.remove(first); columnCount -= count; emit q->columnsRemoved(first, count); } /*! Insert \c count rows before row with the index \c before */ void MatrixPrivate::insertRows(int before, int count) { emit q->rowsAboutToBeInserted(before, count); Q_ASSERT(before >= 0); Q_ASSERT(before <= rowCount); switch (mode) { case AbstractColumn::Numeric: for (int col = 0; col < columnCount; col++) for (int i = 0; i < count; i++) (static_cast>*>(data))->operator[](col).insert(before+i, 0.0); break; case AbstractColumn::Text: for (int col = 0; col < columnCount; col++) for (int i = 0; i < count; i++) (static_cast>*>(data))->operator[](col).insert(before+i, QString()); break; case AbstractColumn::Integer: for (int col = 0; col < columnCount; col++) for (int i = 0; i < count; i++) (static_cast>*>(data))->operator[](col).insert(before+i, 0); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int col = 0; col < columnCount; col++) for (int i = 0; i < count; i++) (static_cast>*>(data))->operator[](col).insert(before+i, QDateTime()); } for(int i=0; irowsInserted(before, count); } /*! Remove \c count columns starting from the column with index \c first */ void MatrixPrivate::removeRows(int first, int count) { emit q->rowsAboutToBeRemoved(first, count); Q_ASSERT(first >= 0); Q_ASSERT(first+count <= rowCount); switch (mode) { case AbstractColumn::Numeric: for (int col = 0; col < columnCount; col++) (static_cast>*>(data))->operator[](col).remove(first, count); break; case AbstractColumn::Text: for (int col = 0; col < columnCount; col++) (static_cast>*>(data))->operator[](col).remove(first, count); break; case AbstractColumn::Integer: for (int col = 0; col < columnCount; col++) (static_cast>*>(data))->operator[](col).remove(first, count); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int col = 0; col < columnCount; col++) (static_cast>*>(data))->operator[](col).remove(first, count); break; } for (int i = 0; i < count; i++) rowHeights.remove(first); rowCount -= count; emit q->rowsRemoved(first, count); } //! Fill column with zeroes void MatrixPrivate::clearColumn(int col) { switch (mode) { case AbstractColumn::Numeric: static_cast>*>(data)->operator[](col).fill(0.0); break; case AbstractColumn::Text: static_cast>*>(data)->operator[](col).fill(QString()); break; case AbstractColumn::Integer: static_cast>*>(data)->operator[](col).fill(0); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: static_cast>*>(data)->operator[](col).fill(QDateTime()); break; } if (!suppressDataChange) emit q->dataChanged(0, col, rowCount-1, col); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## void Matrix::save(QXmlStreamWriter* writer) const { writer->writeStartElement("matrix"); writeBasicAttributes(writer); writeCommentElement(writer); //formula writer->writeStartElement("formula"); writer->writeCharacters(d->formula); writer->writeEndElement(); //format writer->writeStartElement("format"); writer->writeAttribute("mode", QString::number(d->mode)); writer->writeAttribute("headerFormat", QString::number(d->headerFormat)); writer->writeAttribute("numericFormat", QString(QChar(d->numericFormat))); writer->writeAttribute("precision", QString::number(d->precision)); writer->writeEndElement(); //dimensions writer->writeStartElement("dimension"); writer->writeAttribute("columns", QString::number(d->columnCount)); writer->writeAttribute("rows", QString::number(d->rowCount)); writer->writeAttribute("x_start", QString::number(d->xStart)); writer->writeAttribute("x_end", QString::number(d->xEnd)); writer->writeAttribute("y_start", QString::number(d->yStart)); writer->writeAttribute("y_end", QString::number(d->yEnd)); writer->writeEndElement(); //vector with row heights writer->writeStartElement("row_heights"); const char* data = reinterpret_cast(d->rowHeights.constData()); int size = d->rowHeights.size() * sizeof(int); writer->writeCharacters(QByteArray::fromRawData(data,size).toBase64()); writer->writeEndElement(); //vector with column widths writer->writeStartElement("column_widths"); data = reinterpret_cast(d->columnWidths.constData()); size = d->columnWidths.size()*sizeof(int); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); //columns switch (d->mode) { case AbstractColumn::Numeric: size = d->rowCount*sizeof(double); for (int i = 0; i < d->columnCount; ++i) { data = reinterpret_cast(static_cast>*>(d->data)->at(i).constData()); writer->writeStartElement("column"); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); } break; case AbstractColumn::Text: size = d->rowCount*sizeof(QString); for (int i = 0; i < d->columnCount; ++i) { data = reinterpret_cast(static_cast>*>(d->data)->at(i).constData()); writer->writeStartElement("column"); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); } break; case AbstractColumn::Integer: size = d->rowCount*sizeof(int); for (int i = 0; i < d->columnCount; ++i) { data = reinterpret_cast(static_cast>*>(d->data)->at(i).constData()); writer->writeStartElement("column"); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); } break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: size = d->rowCount*sizeof(QDateTime); for (int i = 0; i < d->columnCount; ++i) { data = reinterpret_cast(static_cast>*>(d->data)->at(i).constData()); writer->writeStartElement("column"); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); } break; } writer->writeEndElement(); // "matrix" } bool Matrix::load(XmlStreamReader* reader, bool preview) { if(!reader->isStartElement() || reader->name() != "matrix") { reader->raiseError(i18n("no matrix element found")); return false; } if (!readBasicAttributes(reader)) return false; QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; // read child elements while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "matrix") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if(!preview && reader->name() == "formula") { d->formula = reader->text().toString().trimmed(); } else if (!preview && reader->name() == "format") { attribs = reader->attributes(); str = attribs.value("mode").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'mode'")); else d->mode = AbstractColumn::ColumnMode(str.toInt()); str = attribs.value("headerFormat").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'headerFormat'")); else d->headerFormat = Matrix::HeaderFormat(str.toInt()); str = attribs.value("numericFormat").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'numericFormat'")); else { QByteArray formatba = str.toLatin1(); d->numericFormat = *formatba.data(); } str = attribs.value("precision").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'precision'")); else d->precision = str.toInt(); } else if (!preview && reader->name() == "dimension") { attribs = reader->attributes(); str = attribs.value("columns").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'columns'")); else d->columnCount = str.toInt(); str = attribs.value("rows").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'rows'")); else d->rowCount = str.toInt(); str = attribs.value("x_start").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'x_start'")); else d->xStart = str.toDouble(); str = attribs.value("x_end").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'x_end'")); else d->xEnd = str.toDouble(); str = attribs.value("y_start").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'y_start'")); else d->yStart = str.toDouble(); str = attribs.value("y_end").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'y_end'")); else d->yEnd = str.toDouble(); } else if (!preview && reader->name() == "row_heights") { reader->readNext(); QString content = reader->text().toString().trimmed(); QByteArray bytes = QByteArray::fromBase64(content.toAscii()); int count = bytes.size()/sizeof(int); d->rowHeights.resize(count); memcpy(d->rowHeights.data(), bytes.data(), count*sizeof(int)); } else if (!preview && reader->name() == "column_widths") { reader->readNext(); QString content = reader->text().toString().trimmed(); QByteArray bytes = QByteArray::fromBase64(content.toAscii()); int count = bytes.size()/sizeof(int); d->columnWidths.resize(count); memcpy(d->columnWidths.data(), bytes.data(), count*sizeof(int)); } else if (!preview && reader->name() == "column") { //TODO: parallelize reading of columns? reader->readNext(); QString content = reader->text().toString().trimmed(); QByteArray bytes = QByteArray::fromBase64(content.toAscii()); switch (d->mode) { case AbstractColumn::Numeric: { int count = bytes.size()/sizeof(double); QVector column; column.resize(count); memcpy(column.data(), bytes.data(), count*sizeof(double)); static_cast>*>(d->data)->append(column); break; } case AbstractColumn::Text: { int count = bytes.size()/sizeof(QString); QVector column; column.resize(count); memcpy(column.data(), bytes.data(), count*sizeof(QString)); static_cast>*>(d->data)->append(column); break; } case AbstractColumn::Integer: { int count = bytes.size()/sizeof(int); QVector column; column.resize(count); memcpy(column.data(), bytes.data(), count*sizeof(int)); static_cast>*>(d->data)->append(column); break; } case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: { int count = bytes.size()/sizeof(QDateTime); QVector column; column.resize(count); memcpy(column.data(), bytes.data(), count*sizeof(QDateTime)); static_cast>*>(d->data)->append(column); break; } } } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } return true; } //############################################################################## //######################## Data Import ####################################### //############################################################################## int Matrix::prepareImport(QVector& dataContainer, AbstractFileFilter::ImportMode mode, int actualRows, int actualCols, QStringList colNameList, QVector columnMode) { QDEBUG("prepareImport() rows =" << actualRows << " cols =" << actualCols); Q_UNUSED(colNameList); int columnOffset = 0; setUndoAware(false); setSuppressDataChangedSignal(true); // resize the matrix if (mode == AbstractFileFilter::Replace) { clear(); setDimensions(actualRows, actualCols); } else { if (rowCount() < actualRows) setDimensions(actualRows, actualCols); else setDimensions(rowCount(), actualCols); } // data() returns a void* which is a pointer to a matrix of any data type (see ColumnPrivate.cpp) dataContainer.resize(actualCols); switch (columnMode[0]) { // only columnMode[0] is used case AbstractColumn::Numeric: for (int n = 0; n < actualCols; n++) { QVector* vector = &(static_cast>*>(data())->operator[](n)); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); } break; case AbstractColumn::Integer: for (int n = 0; n < actualCols; n++) { QVector* vector = &(static_cast>*>(data())->operator[](n)); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); } break; case AbstractColumn::Text: for (int n = 0; n < actualCols; n++) { QVector* vector = &(static_cast>*>(data())->operator[](n)); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); } break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int n = 0; n < actualCols; n++) { QVector* vector = &(static_cast>*>(data())->operator[](n)); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); } break; } return columnOffset; } void Matrix::finalizeImport(int columnOffset, int startColumn, int endColumn, const QString& dateTimeFormat, AbstractFileFilter::ImportMode importMode) { Q_UNUSED(columnOffset); Q_UNUSED(startColumn); Q_UNUSED(endColumn); Q_UNUSED(dateTimeFormat); Q_UNUSED(importMode); setSuppressDataChangedSignal(false); setChanged(); setUndoAware(true); } diff --git a/src/backend/note/Note.cpp b/src/backend/note/Note.cpp index fc1c2f496..d73b6dcfc 100644 --- a/src/backend/note/Note.cpp +++ b/src/backend/note/Note.cpp @@ -1,181 +1,181 @@ /*************************************************************************** File : Notes.cpp Project : LabPlot Description : Notes Widget for taking notes -------------------------------------------------------------------- Copyright : (C) 2009-2015 Garvit Khatri (garvitdelhi@gmail.com) Copyright : (C) 2016 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 "Note.h" #include "commonfrontend/note/NoteView.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/macros.h" #include #include #include #include #include #include #include Note::Note(const QString& name) : AbstractPart(name) { KConfig config; KConfigGroup group = config.group("Notes"); m_backgroundColor = group.readEntry("BackgroundColor", QColor(Qt::yellow)); m_textColor = group.readEntry("TextColor", QColor(Qt::black)); m_textFont = group.readEntry("TextFont", QFont()); } QIcon Note::icon() const { return QIcon::fromTheme("document-new"); } bool Note::printView() { QPrinter printer; QPrintDialog* dlg = new QPrintDialog(&printer, m_view); dlg->setWindowTitle(i18n("Print Worksheet")); bool ret; if ( (ret = (dlg->exec() == QDialog::Accepted)) ) { NoteView* view = reinterpret_cast(m_view); view->print(&printer); } delete dlg; return ret; } bool Note::printPreview() const { const NoteView* view = reinterpret_cast(m_view); QPrintPreviewDialog* dlg = new QPrintPreviewDialog(m_view); - connect(dlg, SIGNAL(paintRequested(QPrinter*)), view, SLOT(print(QPrinter*))); + connect(dlg, &QPrintPreviewDialog::paintRequested, view, &NoteView::print); return dlg->exec(); } bool Note::exportView() const { return false; } void Note::setNote(const QString& note) { m_note = note; } const QString& Note::note() const { return m_note; } void Note::setBackgroundColor(const QColor& color) { m_backgroundColor = color; emit (backgroundColorChanged(color)); } const QColor& Note::backgroundColor() const { return m_backgroundColor; } void Note::setTextColor(const QColor& color) { m_textColor = color; emit (textColorChanged(color)); } const QColor& Note::textColor() const{ return m_textColor; } void Note::setTextFont(const QFont& font) { m_textFont = font; emit (textFontChanged(font)); } const QFont& Note::textFont() const { return m_textFont; } QWidget* Note::view() const { if (!m_view) m_view = new NoteView(const_cast(this)); return m_view; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void Note::save(QXmlStreamWriter* writer) const { writer->writeStartElement("note"); writeBasicAttributes(writer); writeCommentElement(writer); writer->writeStartElement("background"); WRITE_QCOLOR(m_backgroundColor); writer->writeEndElement(); writer->writeStartElement("text"); WRITE_QCOLOR(m_textColor); WRITE_QFONT(m_textFont); writer->writeAttribute("text", m_note); writer->writeEndElement(); writer->writeEndElement(); // close "note" section } bool Note::load(XmlStreamReader* reader, bool preview) { if (!reader->isStartElement() || reader->name() != "note") { reader->raiseError(i18n("no note element found")); return false; } if (!readBasicAttributes(reader)) return false; QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "note") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "background") { attribs = reader->attributes(); READ_QCOLOR(m_backgroundColor); } else if (!preview && reader->name() == "text") { attribs = reader->attributes(); READ_QCOLOR(m_textColor); READ_QFONT(m_textFont); m_note = attribs.value("text").toString(); } } return true; } diff --git a/src/kdefrontend/HistoryDialog.cpp b/src/kdefrontend/HistoryDialog.cpp index 04a270f95..7be37fa01 100644 --- a/src/kdefrontend/HistoryDialog.cpp +++ b/src/kdefrontend/HistoryDialog.cpp @@ -1,102 +1,102 @@ /*************************************************************************** File : HistoryDialog.cpp Project : LabPlot Description : history dialog -------------------------------------------------------------------- Copyright : (C) 2012-2016 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 "HistoryDialog.h" #include #include #include #include #include #include #include #include /*! \class HistoryDialog \brief Display the content of project's undo stack. \ingroup kdefrontend */ HistoryDialog::HistoryDialog(QWidget* parent, QUndoStack* stack, const QString& emptyLabel) : QDialog(parent), m_undoStack(stack), m_clearUndoStackButton(nullptr) { QUndoView* undoView = new QUndoView(stack, this); undoView->setCleanIcon( QIcon::fromTheme("edit-clear-history") ); undoView->setEmptyLabel(emptyLabel); undoView->setMinimumWidth(350); undoView->setWhatsThis(i18n("List of all performed steps/actions.\n" "Select an item in the list to navigate to the corresponding step.")); setWindowIcon( QIcon::fromTheme("view-history") ); setWindowTitle(i18n("Undo/Redo History")); setAttribute(Qt::WA_DeleteOnClose); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - m_okButton = btnBox->button(QDialogButtonBox::Ok); - QPushButton* cancelButton = btnBox->button(QDialogButtonBox::Cancel); - connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close())); + connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &HistoryDialog::close); + connect(btnBox, &QDialogButtonBox::accepted, this, &HistoryDialog::accept); + connect(btnBox, &QDialogButtonBox::rejected, this, &HistoryDialog::reject); if (stack->count()) { m_clearUndoStackButton = new QPushButton; btnBox->addButton(m_clearUndoStackButton, QDialogButtonBox::ActionRole); m_clearUndoStackButton->setText(i18n("&Clear")); m_clearUndoStackButton->setToolTip(i18n("Clears the undo history. Commands are not undone or redone; the state of the project remains unchanged.")); m_clearUndoStackButton->setIcon(QIcon::fromTheme("edit-clear")); - connect(m_clearUndoStackButton, SIGNAL(clicked(bool)), this, SLOT(clearUndoStack())); + connect(m_clearUndoStackButton, &QPushButton::clicked, this, &HistoryDialog::clearUndoStack); } QFrame* line = new QFrame; line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(undoView); layout->addWidget(line); layout->addWidget(btnBox); setLayout(layout); //restore saved dialog size if available KConfigGroup conf(KSharedConfig::openConfig(), "HistoryDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize( QSize(500, 300).expandedTo(minimumSize()) ); } HistoryDialog::~HistoryDialog() { //save dialog size KConfigGroup conf(KSharedConfig::openConfig(), "HistoryDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void HistoryDialog::clearUndoStack() { if (KMessageBox::questionYesNo( this, i18n("Do you really want to clear the undo history?"), i18n("Clear history") ) == KMessageBox::Yes) m_undoStack->clear(); } diff --git a/src/kdefrontend/datasources/DatabaseManagerDialog.cpp b/src/kdefrontend/datasources/DatabaseManagerDialog.cpp index 65bfc03f5..ab94abbe2 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) ***************************************************************************/ /*************************************************************************** * * * 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 /*! \class DatabaseManagerDialog \brief dialog for managing database connections \ingroup kdefrontend */ DatabaseManagerDialog::DatabaseManagerDialog(QWidget* parent, const QString& conn) : QDialog(parent), mainWidget(new DatabaseManagerWidget(this, conn)), m_changed(false) { setWindowIcon(QIcon::fromTheme("network-server-database")); setWindowTitle(i18n("SQL Database Connections")); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(mainWidget); layout->addWidget(buttonBox); connect(mainWidget, SIGNAL(changed()), this, SLOT(changed())); - connect(buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked(bool)), this, SLOT(save())); - connect(buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked(bool)), this, SLOT(close())); + 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); KConfigGroup conf(KSharedConfig::openConfig(), "DatabaseManagerDialog"); KWindowConfig::restoreWindowSize(windowHandle(), conf); } 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(i18n("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/FITSOptionsWidget.cpp b/src/kdefrontend/datasources/FITSOptionsWidget.cpp index 349f85ac3..ad8f124ad 100644 --- a/src/kdefrontend/datasources/FITSOptionsWidget.cpp +++ b/src/kdefrontend/datasources/FITSOptionsWidget.cpp @@ -1,177 +1,177 @@ /*************************************************************************** File : FITSOptionsWidget.cpp Project : LabPlot Description : Widget providing options for the import of FITS data -------------------------------------------------------------------- Copyright : (C) 2016 Fabian Kristof (fkristofszabolcs@gmail.com) Copyright : (C) 2017 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 "FITSOptionsWidget.h" #include "ImportFileWidget.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/lib/macros.h" FITSOptionsWidget::FITSOptionsWidget(QWidget* parent, ImportFileWidget* fileWidget) : QWidget(parent), m_fileWidget(fileWidget) { ui.setupUi(parent); ui.twExtensions->headerItem()->setText(0, i18n("Content")); ui.twExtensions->setSelectionMode(QAbstractItemView::SingleSelection); ui.twExtensions->setAlternatingRowColors(true); ui.twExtensions->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui.twPreview->setEditTriggers(QAbstractItemView::NoEditTriggers); - connect( ui.twExtensions, SIGNAL(itemSelectionChanged()), SLOT(fitsTreeWidgetSelectionChanged())); - connect( ui.bRefreshPreview, SIGNAL(clicked()), fileWidget, SLOT(refreshPreview()) ); + connect(ui.twExtensions, &QTreeWidget::itemSelectionChanged, this, &FITSOptionsWidget::fitsTreeWidgetSelectionChanged); + connect(ui.bRefreshPreview, &QPushButton::clicked, fileWidget, &ImportFileWidget::refreshPreview); } void FITSOptionsWidget::clear() { ui.twExtensions->clear(); ui.twPreview->clear(); } QString FITSOptionsWidget::currentExtensionName() { QString name; if (ui.twExtensions->currentItem() != 0 && ui.twExtensions->currentItem()->text(0) != i18n("Primary header")) name = ui.twExtensions->currentItem()->text(ui.twExtensions->currentColumn()); return name; } void FITSOptionsWidget::updateContent(FITSFilter *filter, const QString& fileName) { ui.twExtensions->clear(); filter->parseExtensions(fileName, ui.twExtensions, true); } /*! updates the selected var name of a NetCDF file when the tree widget item is selected */ //TODO void FITSOptionsWidget::fitsTreeWidgetSelectionChanged() { DEBUG("fitsTreeWidgetSelectionChanges()"); QDEBUG("SELECTED ITEMS =" << ui.twExtensions->selectedItems()); if (ui.twExtensions->selectedItems().isEmpty()) return; QTreeWidgetItem* item = ui.twExtensions->selectedItems().first(); int column = ui.twExtensions->currentColumn(); WAIT_CURSOR; const QString& itemText = item->text(column); QString selectedExtension; int extType = 0; if (itemText.contains(QLatin1String("IMAGE #")) || itemText.contains(QLatin1String("ASCII_TBL #")) || itemText.contains(QLatin1String("BINARY_TBL #"))) extType = 1; else if (!itemText.compare(i18n("Primary header"))) extType = 2; if (extType == 0) { if (item->parent() != 0) { if (item->parent()->parent() != 0) selectedExtension = item->parent()->parent()->text(0) + QLatin1String("[") + item->text(column) + QLatin1String("]"); } } else if (extType == 1) { if (item->parent() != 0) { if (item->parent()->parent() != 0) { bool ok; int hduNum = itemText.right(1).toInt(&ok); selectedExtension = item->parent()->parent()->text(0) + QLatin1String("[") + QString::number(hduNum-1) + QLatin1String("]"); } } } else { if (item->parent()->parent() != 0) selectedExtension = item->parent()->parent()->text(column); } if (!selectedExtension.isEmpty()) { FITSFilter* filter = dynamic_cast(m_fileWidget->currentFileFilter()); bool readFitsTableToMatrix; const QVector importedStrings = filter->readChdu(selectedExtension, &readFitsTableToMatrix, ui.sbPreviewLines->value()); emit m_fileWidget->checkedFitsTableToMatrix(readFitsTableToMatrix); const int rows = importedStrings.size(); ui.twPreview->clear(); ui.twPreview->setRowCount(rows); int colCount = 0; const int maxColumns = 300; for (int i = 0; i < rows; i++) { QStringList lineString = importedStrings[i]; if (i == 0) { colCount = lineString.size() > maxColumns ? maxColumns : lineString.size(); ui.twPreview->setColumnCount(colCount); } colCount = lineString.size() > maxColumns ? maxColumns : lineString.size(); for (int j = 0; j < colCount; j++) { QTableWidgetItem* item = new QTableWidgetItem(lineString[j]); ui.twPreview->setItem(i, j, item); } } ui.twPreview->resizeColumnsToContents(); } RESET_CURSOR; } /*! return list of selected FITS extension names */ const QStringList FITSOptionsWidget::selectedFITSExtensions() const { QStringList names; for (const auto* item: ui.twExtensions->selectedItems()) names << item->text(0); return names; } const QString FITSOptionsWidget::extensionName(bool* ok) { if (ui.twExtensions->currentItem() != 0) { const QTreeWidgetItem* item = ui.twExtensions->currentItem(); const int currentColumn = ui.twExtensions->currentColumn(); QString itemText = item->text(currentColumn); int extType = 0; if (itemText.contains(QLatin1String("IMAGE #")) || itemText.contains(QLatin1String("ASCII_TBL #")) || itemText.contains(QLatin1String("BINARY_TBL #"))) extType = 1; else if (!itemText.compare(i18n("Primary header"))) extType = 2; if (extType == 0) { if (item->parent() != 0 && item->parent()->parent() != 0) return item->parent()->parent()->text(0) + QLatin1String("[")+ item->text(currentColumn) + QLatin1String("]"); } else if (extType == 1) { if (item->parent() != 0 && item->parent()->parent() != 0) { int hduNum = itemText.right(1).toInt(ok); return item->parent()->parent()->text(0) + QLatin1String("[") + QString::number(hduNum-1) + QLatin1String("]"); } } else { if (item->parent()->parent() != 0) return item->parent()->parent()->text(currentColumn); } } return QString(); } diff --git a/src/kdefrontend/datasources/FileInfoDialog.cpp b/src/kdefrontend/datasources/FileInfoDialog.cpp index eab7d6278..8a865827d 100644 --- a/src/kdefrontend/datasources/FileInfoDialog.cpp +++ b/src/kdefrontend/datasources/FileInfoDialog.cpp @@ -1,84 +1,84 @@ /*************************************************************************** File : FileInfoDialog.cpp Project : LabPlot Description : import file data dialog -------------------------------------------------------------------- Copyright : (C) 2009-2017 by 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 * * * ***************************************************************************/ #include "FileInfoDialog.h" #include "backend/datasources/LiveDataSource.h" #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); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(&m_textEditWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, &QDialogButtonBox::rejected, this, &FileInfoDialog::reject); + connect(buttonBox, &QDialogButtonBox::accepted, this, &FileInfoDialog::accept); layout->addWidget(buttonBox); setWindowIcon(QIcon::fromTheme("help-about")); setWindowTitle(i18n("File info")); setAttribute(Qt::WA_DeleteOnClose); setLayout(layout); resize( QSize(500,300) ); } void FileInfoDialog::setFiles(QStringList& files) { QString infoString; for (const auto fileName: files) { if(fileName.isEmpty()) continue; if (!infoString.isEmpty()) infoString += "


"; infoString += LiveDataSource::fileInfoString(fileName); } m_textEditWidget.document()->setHtml(infoString); } diff --git a/src/kdefrontend/datasources/ImportFileWidget.h b/src/kdefrontend/datasources/ImportFileWidget.h index b5b5a55c8..796e082c7 100644 --- a/src/kdefrontend/datasources/ImportFileWidget.h +++ b/src/kdefrontend/datasources/ImportFileWidget.h @@ -1,114 +1,115 @@ /*************************************************************************** File : ImportFileWidget.h Project : LabPlot Description : import file data widget -------------------------------------------------------------------- Copyright : (C) 2009-2017 by Stefan Gerlach (stefan.gerlach@uni-konstanz.de) Copyright : (C) 2009-2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef IMPORTFILEWIDGET_H #define IMPORTFILEWIDGET_H #include "ui_importfilewidget.h" #include "backend/datasources/LiveDataSource.h" #include class AbstractFileFilter; class AsciiOptionsWidget; class BinaryOptionsWidget; class HDFOptionsWidget; class ImageOptionsWidget; class NetCDFOptionsWidget; class FITSOptionsWidget; class QTableWidget; class ImportFileWidget : public QWidget { Q_OBJECT public: explicit ImportFileWidget(QWidget*, const QString& fileName = QString()); ~ImportFileWidget(); void showOptions(bool); void saveSettings(LiveDataSource*) const; LiveDataSource::FileType currentFileType() const; LiveDataSource::SourceType currentSourceType() const; AbstractFileFilter* currentFileFilter() const; QString fileName() const; bool isFileEmpty() const; const QStringList selectedHDFNames() const; const QStringList selectedNetCDFNames() const; const QStringList selectedFITSExtensions() const; void hideDataSource(); void showAsciiHeaderOptions(bool); QString host() const; QString port() const; QString serialPort() const; int baudRate() const; void initializeAndFillPortsAndBaudRates(); private: Ui::ImportFileWidget ui; std::unique_ptr m_asciiOptionsWidget; std::unique_ptr m_binaryOptionsWidget; std::unique_ptr m_hdfOptionsWidget; std::unique_ptr m_imageOptionsWidget; std::unique_ptr m_netcdfOptionsWidget; std::unique_ptr m_fitsOptionsWidget; QTableWidget* m_twPreview; const QString& m_fileName; bool m_fileEmpty; bool m_liveDataSource; private slots: void fileNameChanged(const QString&); void fileTypeChanged(int); void updateTypeChanged(int); void sourceTypeChanged(int); void readingTypeChanged(int); void saveFilter(); void manageFilters(); void filterChanged(int); void selectFile(); void fileInfoDialog(); void refreshPreview(); void loadSettings(); signals: void fileNameChanged(); void sourceTypeChanged(); void hostChanged(); void portChanged(); void checkedFitsTableToMatrix(const bool enable); friend class HDFOptionsWidget; // to access refreshPreview() friend class NetCDFOptionsWidget; // to access refreshPreview() and others + friend class FITSOptionsWidget; }; #endif diff --git a/src/kdefrontend/dockwidgets/LiveDataDock.cpp b/src/kdefrontend/dockwidgets/LiveDataDock.cpp index 0f3aafe26..6567ac1ac 100644 --- a/src/kdefrontend/dockwidgets/LiveDataDock.cpp +++ b/src/kdefrontend/dockwidgets/LiveDataDock.cpp @@ -1,199 +1,201 @@ /*************************************************************************** File : LiveDataDock.cpp Project : LabPlot Description : Dock widget for live data properties -------------------------------------------------------------------- Copyright : (C) 2017 by Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "LiveDataDock.h" #include LiveDataDock::LiveDataDock(QWidget* parent) : QWidget(parent), m_paused(false) { ui.setupUi(this); ui.bUpdateNow->setIcon(QIcon::fromTheme(QLatin1String("view-refresh"))); - connect(ui.bPausePlayReading, SIGNAL(clicked(bool)), this, SLOT(pauseContinueReading())); - connect(ui.bUpdateNow, SIGNAL(clicked(bool)), this, SLOT(updateNow())); - connect(ui.sbUpdateInterval, SIGNAL(valueChanged(int)), this, SLOT(updateIntervalChanged(int))); - connect(ui.leKeepNValues, SIGNAL(textChanged(QString)), this, SLOT(keepNvaluesChanged(QString))); - connect(ui.sbSampleRate, SIGNAL(valueChanged(int)), this, SLOT(sampleRateChanged(int))); - connect(ui.cbUpdateType, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypeChanged(int))); - connect(ui.cbReadingType, SIGNAL(currentIndexChanged(int)), this, SLOT(readingTypeChanged(int))); + connect(ui.bPausePlayReading, &QPushButton::clicked, this, &LiveDataDock::pauseContinueReading); + connect(ui.bUpdateNow, &QPushButton::clicked, this, &LiveDataDock::updateNow); + connect(ui.sbUpdateInterval, static_cast(&QSpinBox::valueChanged), this, &LiveDataDock::updateIntervalChanged); + + connect(ui.leKeepNValues, &QLineEdit::textChanged, this, &LiveDataDock::keepNvaluesChanged); + connect(ui.sbSampleRate, static_cast(&QSpinBox::valueChanged), this, &LiveDataDock::sampleRateChanged); + connect(ui.cbUpdateType, static_cast(&QComboBox::currentIndexChanged), this, &LiveDataDock::updateTypeChanged); + connect(ui.cbReadingType, static_cast(&QComboBox::currentIndexChanged), this, &LiveDataDock::readingTypeChanged); + } LiveDataDock::~LiveDataDock() { } /*! * \brief Sets the live data sources of this dock widget * \param sources */ void LiveDataDock::setLiveDataSources(const QList& sources) { m_liveDataSources = sources; const LiveDataSource* const fds = sources.at(0); ui.sbUpdateInterval->setValue(fds->updateInterval()); ui.cbUpdateType->setCurrentIndex(static_cast(fds->updateType())); ui.cbReadingType->setCurrentIndex(static_cast(fds->readingType())); if (fds->updateType() == LiveDataSource::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); } if (fds->isPaused()) { ui.bPausePlayReading->setText(i18n("Continue reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); } else { ui.bPausePlayReading->setText(i18n("Pause reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); } if(!fds->keepLastValues()) { ui.leKeepNValues->hide(); ui.lKeepNvalues->hide(); } else { ui.leKeepNValues->setValidator(new QIntValidator(2, 100000)); ui.leKeepNValues->setText(QString::number(fds->keepNvalues())); } if (fds->readingType() == LiveDataSource::ReadingType::TillEnd) { ui.lSampleRate->hide(); ui.sbSampleRate->hide(); } else ui.sbSampleRate->setValue(fds->sampleRate()); } /*! * \brief Modifies the sample rate of the live data sources * \param sampleRate */ void LiveDataDock::sampleRateChanged(int sampleRate) { for (auto* source : m_liveDataSources) source->setSampleRate(sampleRate); } /*! * \brief Updates the live data sources now */ void LiveDataDock::updateNow() { for (auto* source : m_liveDataSources) source->updateNow(); } /*! * \brief LiveDataDock::updateTypeChanged * \param idx */ void LiveDataDock::updateTypeChanged(int idx) { LiveDataSource::UpdateType type = static_cast(idx); if (type == LiveDataSource::UpdateType::TimeInterval) { ui.lUpdateInterval->show(); ui.sbUpdateInterval->show(); for (auto* source: m_liveDataSources) { source->setUpdateType(type); source->setUpdateInterval(ui.sbUpdateInterval->value()); source->setFileWatched(false); } } else if (type == LiveDataSource::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); for (auto* source: m_liveDataSources) { source->setFileWatched(true); source->setUpdateType(type); } } } /*! * \brief Handles the change of the reading type in the dock widget * \param idx */ void LiveDataDock::readingTypeChanged(int idx) { LiveDataSource::ReadingType type = static_cast(idx); if (type == LiveDataSource::ReadingType::TillEnd) { ui.lSampleRate->hide(); ui.sbSampleRate->hide(); } else { ui.lSampleRate->show(); ui.sbSampleRate->show(); } for (auto* source : m_liveDataSources) source->setReadingType(type); } /*! * \brief Modifies the update interval of the live data sources * \param updateInterval */ void LiveDataDock::updateIntervalChanged(int updateInterval) { for (auto* source : m_liveDataSources) source->setUpdateInterval(updateInterval); } /*! * \brief Modifies the number of samples to keep in each of the live data sources * \param keepNvalues */ void LiveDataDock::keepNvaluesChanged(QString keepNvalues) { for (auto* source : m_liveDataSources) source->setKeepNvalues(keepNvalues.toInt()); } /*! * \brief Pauses the reading of the live data source */ void LiveDataDock::pauseReading() { for (auto* source: m_liveDataSources) source->pauseReading(); } /*! * \brief Continues the reading of the live data source */ void LiveDataDock::continueReading() { for (auto* source: m_liveDataSources) source->continueReading(); } /*! * \brief Handles the pausing/continuing of reading of the live data source */ void LiveDataDock::pauseContinueReading() { m_paused = !m_paused; if (m_paused) { pauseReading(); ui.bPausePlayReading->setText(i18n("Continue reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); } else { continueReading(); ui.bPausePlayReading->setText(i18n("Pause reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); } } diff --git a/src/kdefrontend/matrix/MatrixFunctionDialog.cpp b/src/kdefrontend/matrix/MatrixFunctionDialog.cpp index 044641f04..230c54409 100644 --- a/src/kdefrontend/matrix/MatrixFunctionDialog.cpp +++ b/src/kdefrontend/matrix/MatrixFunctionDialog.cpp @@ -1,258 +1,259 @@ /*************************************************************************** File : MatrixFunctionDialog.cpp Project : LabPlot Description : Dialog for generating matrix values from a mathematical function ------------------------------------------------------------------------ Copyright : (C) 2015-2016 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "MatrixFunctionDialog.h" #include "backend/lib/macros.h" #include "backend/matrix/Matrix.h" #include "kdefrontend/widgets/ConstantsWidget.h" #include "kdefrontend/widgets/FunctionsWidget.h" extern "C" { #include "backend/gsl/parser.h" } #include #include #include #include #include #include #include #ifndef NDEBUG #include #include #endif /*! \class MatrixFunctionDialog \brief Dialog for generating matrix values from a mathematical function. \ingroup kdefrontend */ MatrixFunctionDialog::MatrixFunctionDialog(Matrix* m, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), m_matrix(m) { Q_ASSERT(m_matrix); setWindowTitle(i18n("Function values")); ui.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui.tbConstants->setIcon( QIcon::fromTheme("labplot-format-text-symbol") ); ui.tbFunctions->setIcon( QIcon::fromTheme("preferences-desktop-font") ); QStringList vars; vars << "x" << "y"; ui.teEquation->setVariables(vars); ui.teEquation->setFocus(); ui.teEquation->setMaximumHeight(QLineEdit().sizeHint().height()*2); QString info = '[' + QString::number(m_matrix->xStart()) + ", " + QString::number(m_matrix->xEnd()) + "], " + i18np("%1 value", "%1 values", m_matrix->columnCount()); ui.lXInfo->setText(info); info = '[' + QString::number(m_matrix->yStart()) + ", " + QString::number(m_matrix->yEnd()) + "], " + i18np("%1 value", "%1 values", m_matrix->rowCount()); ui.lYInfo->setText(info); ui.teEquation->setPlainText(m_matrix->formula()); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.gridLayout_2->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); - QPushButton* cancelButton = btnBox->button(QDialogButtonBox::Cancel); - connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close())); + connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &MatrixFunctionDialog::close); + connect(btnBox, &QDialogButtonBox::accepted, this, &MatrixFunctionDialog::accept); + connect(btnBox, &QDialogButtonBox::rejected, this, &MatrixFunctionDialog::reject); m_okButton->setText(i18n("&Generate")); m_okButton->setToolTip(i18n("Generate function values")); - connect(ui.teEquation, SIGNAL(expressionChanged()), this, SLOT(checkValues())); - connect(ui.tbConstants, SIGNAL(clicked()), this, SLOT(showConstants())); - connect(ui.tbFunctions, SIGNAL(clicked()), this, SLOT(showFunctions())); - connect(m_okButton, SIGNAL(clicked(bool)), this, SLOT(generate())); + connect(ui.teEquation, &ExpressionTextEdit::expressionChanged, this, &MatrixFunctionDialog::checkValues); + connect(ui.tbConstants, &QToolButton::clicked, this, &MatrixFunctionDialog::showConstants); + connect(ui.tbFunctions, &QToolButton::clicked, this, &MatrixFunctionDialog::showFunctions); + connect(m_okButton, &QPushButton::clicked, this, &MatrixFunctionDialog::generate); resize(QSize(300,0).expandedTo(minimumSize())); } void MatrixFunctionDialog::checkValues() { if (!ui.teEquation->isValid()) { m_okButton->setEnabled(false); return; } m_okButton->setEnabled(true); } void MatrixFunctionDialog::showConstants() { QMenu menu; ConstantsWidget constants(&menu); - connect(&constants, SIGNAL(constantSelected(QString)), this, SLOT(insertConstant(QString))); - connect(&constants, SIGNAL(constantSelected(QString)), &menu, SLOT(close())); - connect(&constants, SIGNAL(canceled()), &menu, SLOT(close())); + connect(&constants, &ConstantsWidget::constantSelected, this, &MatrixFunctionDialog::insertConstant); + connect(&constants, &ConstantsWidget::constantSelected, &menu, &QMenu::close); + connect(&constants, &ConstantsWidget::canceled, &menu, &QMenu::close); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&constants); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbConstants->width(),-menu.sizeHint().height()); menu.exec(ui.tbConstants->mapToGlobal(pos)); } void MatrixFunctionDialog::showFunctions() { QMenu menu; FunctionsWidget functions(&menu); - connect(&functions, SIGNAL(functionSelected(QString)), this, SLOT(insertFunction(QString))); - connect(&functions, SIGNAL(functionSelected(QString)), &menu, SLOT(close())); - connect(&functions, SIGNAL(canceled()), &menu, SLOT(close())); + connect(&functions, &FunctionsWidget::functionSelected, this, &MatrixFunctionDialog::insertFunction); + connect(&functions, &FunctionsWidget::functionSelected, &menu, &QMenu::close); + connect(&functions, &FunctionsWidget::canceled, &menu, &QMenu::close); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&functions); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbFunctions->width(),-menu.sizeHint().height()); menu.exec(ui.tbFunctions->mapToGlobal(pos)); } void MatrixFunctionDialog::insertFunction(const QString& str) { ui.teEquation->insertPlainText(str + "(x)"); } void MatrixFunctionDialog::insertConstant(const QString& str) { ui.teEquation->insertPlainText(str); } /* task class for parallel fill (not used) */ class GenerateValueTask : public QRunnable { public: GenerateValueTask(int startCol, int endCol, QVector>& matrixData, double xStart, double yStart, double xStep, double yStep, char* func): m_startCol(startCol), m_endCol(endCol), m_matrixData(matrixData), m_xStart(xStart), m_yStart(yStart), m_xStep(xStep), m_yStep(yStep), m_func(func) { }; void run() { const int rows = m_matrixData[m_startCol].size(); double x = m_xStart; double y = m_yStart; #ifndef NDEBUG qDebug()<<"FILL col"<name())); //TODO: data types QVector>* new_data = static_cast>*>(m_matrix->data()); QByteArray funcba = ui.teEquation->toPlainText().toLocal8Bit(); char* func = funcba.data(); // check if rows or cols == 1 double diff = m_matrix->xEnd() - m_matrix->xStart(); double xStep = 0.0; if (m_matrix->columnCount() > 1) xStep = diff/double(m_matrix->columnCount() - 1); diff = m_matrix->yEnd() - m_matrix->yStart(); double yStep = 0.0; if (m_matrix->rowCount() > 1) yStep = diff/double(m_matrix->rowCount() - 1); #ifndef NDEBUG QElapsedTimer timer; timer.start(); #endif //TODO: too slow because every parser thread needs an own symbol_table // idea: use pool->maxThreadCount() symbol tables and reuse them? /* double yStart = m_matrix->yStart(); const int cols = m_matrix->columnCount(); QThreadPool* pool = QThreadPool::globalInstance(); int range = ceil(double(cols)/pool->maxThreadCount()); #ifndef NDEBUG qDebug() << "Starting" << pool->maxThreadCount() << "threads. cols =" << cols << ": range =" << range; #endif for (int i = 0; i < pool->maxThreadCount(); ++i) { const int start = i*range; int end = (i+1)*range; if (end > cols) end = cols; qDebug() << "start/end: " << start << end; const double xStart = m_matrix->xStart() + xStep*start; GenerateValueTask* task = new GenerateValueTask(start, end, new_data, xStart, yStart, xStep, yStep, func); task->setAutoDelete(false); pool->start(task); } pool->waitForDone(); */ double x = 0, y = 0; parser_var vars[] = {{"x", x}, {"y", y}}; for (int col = 0; col < m_matrix->columnCount(); col++) { vars[0].value = x; for (int row = 0; row < m_matrix->rowCount(); row++) { vars[1].value = y; (new_data->operator[](col))[row] = parse_with_vars(func, vars, 2); y += yStep; } y = m_matrix->yStart(); x += xStep; } // Timing #ifndef NDEBUG qDebug() << "elapsed time =" << timer.elapsed() << "ms"; #endif m_matrix->setFormula(ui.teEquation->toPlainText()); m_matrix->setData(new_data); m_matrix->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/DropValuesDialog.cpp b/src/kdefrontend/spreadsheet/DropValuesDialog.cpp index 39855fa2a..9c1f50fb0 100644 --- a/src/kdefrontend/spreadsheet/DropValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/DropValuesDialog.cpp @@ -1,345 +1,346 @@ /*************************************************************************** File : DropValuesDialog.cpp Project : LabPlot Description : Dialog for droping and masking values in columns -------------------------------------------------------------------- Copyright : (C) 2015 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "DropValuesDialog.h" #include "backend/core/column/Column.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include #include #include #include #include /*! \class DropValuesDialog \brief Dialog for generating values from a mathematical function. \ingroup kdefrontend */ DropValuesDialog::DropValuesDialog(Spreadsheet* s, bool mask, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), m_spreadsheet(s), m_mask(mask) { setWindowTitle(i18n("Drop values")); ui.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui.cbOperator->addItem(i18n("equal to")); ui.cbOperator->addItem(i18n("between (including end points)")); ui.cbOperator->addItem(i18n("between (excluding end points)")); ui.cbOperator->addItem(i18n("greater then")); ui.cbOperator->addItem(i18n("greater then or equal to")); ui.cbOperator->addItem(i18n("lesser then")); ui.cbOperator->addItem(i18n("lesser then or equal to")); ui.leValue1->setValidator( new QDoubleValidator(ui.leValue1) ); ui.leValue2->setValidator( new QDoubleValidator(ui.leValue2) ); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.horizontalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); - QPushButton* cancelButton = btnBox->button(QDialogButtonBox::Cancel); - connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close())); + connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &DropValuesDialog::close); if (m_mask) { m_okButton->setText(i18n("&Mask")); m_okButton->setToolTip(i18n("Mask values in the specified region")); ui.lMode->setText(i18n("Mask values")); setWindowTitle(i18n("Mask values")); } else { m_okButton->setText(i18n("&Drop")); m_okButton->setToolTip(i18n("Drop values in the specified region")); } - connect(ui.cbOperator, SIGNAL(currentIndexChanged(int)), this, SLOT(operatorChanged(int)) ); - connect(m_okButton, SIGNAL(clicked(bool)), this, SLOT(okClicked())); + connect(ui.cbOperator, static_cast(&QComboBox::currentIndexChanged), this, &DropValuesDialog::operatorChanged ); + connect(m_okButton, &QPushButton::clicked, this, &DropValuesDialog::okClicked); + connect(btnBox, &QDialogButtonBox::accepted, this, &DropValuesDialog::accept); + connect(btnBox, &QDialogButtonBox::rejected, this, &DropValuesDialog::reject); resize( QSize(400,0).expandedTo(minimumSize()) ); operatorChanged(0); } void DropValuesDialog::setColumns(QVector columns) { m_columns = columns; } void DropValuesDialog::operatorChanged(int index) const { bool value2 = (index==1) || (index==2); ui.lMin->setVisible(value2); ui.lMax->setVisible(value2); ui.lAnd->setVisible(value2); ui.leValue2->setVisible(value2); } void DropValuesDialog::okClicked() const { if (m_mask) maskValues(); else dropValues(); } //TODO: m_column->setMasked() is slow, we need direct access to the masked-container -> redesign class MaskValuesTask : public QRunnable { public: MaskValuesTask(Column* col, int op, double value1, double value2){ m_column = col; m_operator = op; m_value1 = value1; m_value2 = value2; } void run() { m_column->setSuppressDataChangedSignal(true); bool changed = false; QVector* data = static_cast* >(m_column->data()); //equal to if (m_operator == 0) { for (int i=0; isize(); ++i) { if (data->at(i) == m_value1) { m_column->setMasked(i, true); changed = true; } } } //between (including end points) else if (m_operator == 1) { for (int i=0; isize(); ++i) { if (data->at(i) >= m_value1 && data->at(i) <= m_value2) { m_column->setMasked(i, true); changed = true; } } } //between (excluding end points) else if (m_operator == 2) { for (int i=0; isize(); ++i) { if (data->at(i) > m_value1 && data->at(i) < m_value2) { m_column->setMasked(i, true); changed = true; } } } //greater than else if (m_operator == 3) { for (int i=0; isize(); ++i) { if (data->at(i) > m_value1) { m_column->setMasked(i, true); changed = true; } } } //greater than or equal to else if (m_operator == 4) { for (int i=0; isize(); ++i) { if (data->at(i) >= m_value1) { m_column->setMasked(i, true); changed = true; } } } //lesser than else if (m_operator == 5) { for (int i=0; isize(); ++i) { if (data->at(i) < m_value1) { m_column->setMasked(i, true); changed = true; } } } //lesser than or equal to else if (m_operator == 6) { for (int i=0; isize(); ++i) { if (data->at(i) <= m_value1) { m_column->setMasked(i, true); changed = true; } } } m_column->setSuppressDataChangedSignal(false); if (changed) m_column->setChanged(); } private: Column* m_column; int m_operator; double m_value1; double m_value2; }; class DropValuesTask : public QRunnable { public: DropValuesTask(Column* col, int op, double value1, double value2){ m_column = col; m_operator = op; m_value1 = value1; m_value2 = value2; } void run() { bool changed = false; QVector* data = static_cast* >(m_column->data()); QVector new_data(*data); //equal to if (m_operator == 0) { for (int i=0; i= m_value1 && new_data[i] <= m_value2) { new_data[i] = NAN; changed = true; } } } //between (excluding end points) else if (m_operator == 2) { for (int i=0; i m_value1 && new_data[i] < m_value2) { new_data[i] = NAN; changed = true; } } } //greater than else if (m_operator == 3) { for (int i=0; i m_value1) { new_data[i] = NAN; changed = true; } } } //greater than or equal to else if (m_operator == 4) { for (int i=0; i= m_value1) { new_data[i] = NAN; changed = true; } } } //lesser than else if (m_operator == 5) { for (int i=0; ireplaceValues(0, new_data); } private: Column* m_column; int m_operator; double m_value1; double m_value2; }; void DropValuesDialog::maskValues() const { Q_ASSERT(m_spreadsheet); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: mask values", m_spreadsheet->name())); const int op = ui.cbOperator->currentIndex(); const double value1 = ui.leValue1->text().toDouble(); const double value2 = ui.leValue2->text().toDouble(); foreach(Column* col, m_columns) { MaskValuesTask* task = new MaskValuesTask(col, op, value1, value2); task->run(); //TODO: writing to the undo-stack in Column::setMasked() is not tread-safe -> redesign // QThreadPool::globalInstance()->start(task); } //wait until all columns were processed // QThreadPool::globalInstance()->waitForDone(); m_spreadsheet->endMacro(); RESET_CURSOR; } void DropValuesDialog::dropValues() const { Q_ASSERT(m_spreadsheet); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: drop values", m_spreadsheet->name())); const int op = ui.cbOperator->currentIndex(); const double value1 = ui.leValue1->text().toDouble(); const double value2 = ui.leValue2->text().toDouble(); foreach(Column* col, m_columns) { DropValuesTask* task = new DropValuesTask(col, op, value1, value2); QThreadPool::globalInstance()->start(task); } //wait until all columns were processed QThreadPool::globalInstance()->waitForDone(); m_spreadsheet->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/EquidistantValuesDialog.cpp b/src/kdefrontend/spreadsheet/EquidistantValuesDialog.cpp index 60a7a0a85..185f4f037 100644 --- a/src/kdefrontend/spreadsheet/EquidistantValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/EquidistantValuesDialog.cpp @@ -1,180 +1,181 @@ /*************************************************************************** File : EquidistantValuesDialog.cpp Project : LabPlot Description : Dialog for generating equidistant numbers -------------------------------------------------------------------- Copyright : (C) 2014 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 "EquidistantValuesDialog.h" #include "backend/core/column/Column.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include #include #include /*! \class EquidistantValuesDialog \brief Dialog for equidistant values. \ingroup kdefrontend */ EquidistantValuesDialog::EquidistantValuesDialog(Spreadsheet* s, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), m_spreadsheet(s) { setWindowTitle(i18n("Equidistant values")); ui.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui.cbType->addItem(i18n("Number")); ui.cbType->addItem(i18n("Increment")); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.gridLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); - QPushButton* cancelButton = btnBox->button(QDialogButtonBox::Cancel); - connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close())); + connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &EquidistantValuesDialog::close); + connect(btnBox, &QDialogButtonBox::accepted, this, &EquidistantValuesDialog::accept); + connect(btnBox, &QDialogButtonBox::rejected, this, &EquidistantValuesDialog::reject); m_okButton->setText(i18n("&Generate")); m_okButton->setToolTip(i18n("Generate equidistant values")); ui.leFrom->setClearButtonEnabled(true); ui.leTo->setClearButtonEnabled(true); ui.leIncrement->setClearButtonEnabled(true); ui.leNumber->setClearButtonEnabled(true); ui.leFrom->setValidator( new QDoubleValidator(ui.leFrom) ); ui.leTo->setValidator( new QDoubleValidator(ui.leTo) ); ui.leIncrement->setValidator( new QDoubleValidator(ui.leIncrement) ); ui.leNumber->setValidator( new QIntValidator(ui.leNumber) ); ui.leFrom->setText("1"); ui.leTo->setText("100"); ui.leIncrement->setText("1"); - connect( ui.cbType, SIGNAL(currentIndexChanged(int)), SLOT(typeChanged(int)) ); - connect( ui.leFrom, SIGNAL(textChanged(QString)), this, SLOT(checkValues()) ); - connect( ui.leTo, SIGNAL(textChanged(QString)), this, SLOT(checkValues()) ); - connect( ui.leNumber, SIGNAL(textChanged(QString)), this, SLOT(checkValues()) ); - connect( ui.leIncrement, SIGNAL(textChanged(QString)), this, SLOT(checkValues()) ); - connect(m_okButton, SIGNAL(clicked(bool)), this, SLOT(generate())); + connect( ui.cbType, static_cast(&QComboBox::currentIndexChanged), this, &EquidistantValuesDialog::typeChanged); + connect( ui.leFrom, &QLineEdit::textChanged, this, &EquidistantValuesDialog::checkValues); + connect( ui.leTo, &QLineEdit::textChanged, this, &EquidistantValuesDialog::checkValues); + connect( ui.leNumber, &QLineEdit::textChanged, this, &EquidistantValuesDialog::checkValues); + connect( ui.leIncrement, &QLineEdit::textChanged, this, &EquidistantValuesDialog::checkValues); + connect(m_okButton, &QPushButton::clicked, this, &EquidistantValuesDialog::generate); //generated data the default this->typeChanged(0); resize( QSize(300,0).expandedTo(minimumSize()) ); } void EquidistantValuesDialog::setColumns(QVector columns) { m_columns = columns; ui.leNumber->setText( QString::number(m_columns.first()->rowCount()) ); } void EquidistantValuesDialog::typeChanged(int index) { if (index==0) { //fixed number ui.lIncrement->hide(); ui.leIncrement->hide(); ui.lNumber->show(); ui.leNumber->show(); } else { //fixed increment ui.lIncrement->show(); ui.leIncrement->show(); ui.lNumber->hide(); ui.leNumber->hide(); } } void EquidistantValuesDialog::checkValues() { if (ui.leFrom->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } if (ui.leTo->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } if (ui.cbType->currentIndex() == 0) { if (ui.leNumber->text().simplified().isEmpty() || ui.leNumber->text().simplified().toInt()==0) { m_okButton->setEnabled(false); return; } } else { if (ui.leIncrement->text().simplified().isEmpty() || qFuzzyIsNull(ui.leIncrement->text().simplified().toDouble())) { m_okButton->setEnabled(false); return; } } m_okButton->setEnabled(true); } void EquidistantValuesDialog::generate() { Q_ASSERT(m_spreadsheet); WAIT_CURSOR; m_spreadsheet->beginMacro(i18np("%1: fill column with equidistant numbers", "%1: fill columns with equidistant numbers", m_spreadsheet->name(), m_columns.size())); double start = ui.leFrom->text().toDouble(); double end = ui.leTo->text().toDouble(); int number; double dist; if (ui.cbType->currentIndex()==0) { //fixed number number = ui.leNumber->text().toInt(); if (number!=1) dist = (end - start)/ (number - 1); else dist = 0; } else { //fixed increment dist = ui.leIncrement->text().toDouble(); number = (end-start)/dist + 1; } if (m_spreadsheet->rowCount()setRowCount(number); for (auto* col : m_columns) { col->setSuppressDataChangedSignal(true); if (m_spreadsheet->rowCount()>number) col->clear(); for (int i=0; isetValueAt(i, start + dist*i); } col->setSuppressDataChangedSignal(false); col->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/ExportSpreadsheetDialog.cpp b/src/kdefrontend/spreadsheet/ExportSpreadsheetDialog.cpp index f05052ba9..45ad073b6 100644 --- a/src/kdefrontend/spreadsheet/ExportSpreadsheetDialog.cpp +++ b/src/kdefrontend/spreadsheet/ExportSpreadsheetDialog.cpp @@ -1,454 +1,457 @@ /*************************************************************************** File : ExportSpreadsheetDialog.cpp Project : LabPlot Description : export spreadsheet dialog -------------------------------------------------------------------- Copyright : (C) 2014-2016 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 "ExportSpreadsheetDialog.h" #include "ui_exportspreadsheetwidget.h" #include #include #include #include #include #include #include #include #include #include /*! \class ExportSpreadsheetDialog \brief Dialog for exporting a spreadsheet to a file. \ingroup kdefrontend */ ExportSpreadsheetDialog::ExportSpreadsheetDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ExportSpreadsheetWidget()), m_matrixMode(false), m_format(Format::ASCII) { ui->setupUi(this); ui->gbOptions->hide(); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_showOptionsButton = new QPushButton; connect(btnBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonClicked(QAbstractButton*))); btnBox->addButton(m_showOptionsButton, QDialogButtonBox::ActionRole); ui->verticalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_cancelButton = btnBox->button(QDialogButtonBox::Cancel); QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui->leFileName->setCompleter(completer); ui->cbFormat->addItem("ASCII"); ui->cbFormat->addItem("Binary"); ui->cbFormat->addItem("LaTeX"); ui->cbFormat->addItem("FITS"); ui->cbSeparator->addItem("TAB"); ui->cbSeparator->addItem("SPACE"); ui->cbSeparator->addItem(","); ui->cbSeparator->addItem(";"); ui->cbSeparator->addItem(":"); ui->cbSeparator->addItem(",TAB"); ui->cbSeparator->addItem(";TAB"); ui->cbSeparator->addItem(":TAB"); ui->cbSeparator->addItem(",SPACE"); ui->cbSeparator->addItem(";SPACE"); ui->cbSeparator->addItem(":SPACE"); ui->cbLaTeXExport->addItem(i18n("Export spreadsheet")); ui->cbLaTeXExport->addItem(i18n("Export selection")); ui->bOpen->setIcon( QIcon::fromTheme("document-open") ); - connect(ui->bOpen, SIGNAL(clicked()), this, SLOT (selectFile()) ); - connect(ui->leFileName, SIGNAL(textChanged(QString)), this, SLOT(fileNameChanged(QString)) ); - connect(m_showOptionsButton,SIGNAL(clicked(bool)), this, SLOT(toggleOptions())); - connect(ui->cbFormat, SIGNAL(currentIndexChanged(int)), this, SLOT(formatChanged(int))); - connect(ui->cbExportToFITS, SIGNAL(currentIndexChanged(int)), this, SLOT(fitsExportToChanged(int))); + connect(btnBox, &QDialogButtonBox::accepted, this, &ExportSpreadsheetDialog::accept); + connect(btnBox, &QDialogButtonBox::rejected, this, &ExportSpreadsheetDialog::reject); + + connect(ui->bOpen, &QPushButton::clicked, this, &ExportSpreadsheetDialog::selectFile); + connect(ui->leFileName, &QLineEdit::textChanged, this, &ExportSpreadsheetDialog::fileNameChanged ); + connect(m_showOptionsButton, &QPushButton::clicked, this, &ExportSpreadsheetDialog::toggleOptions); + //connect(ui->cbFormat, &KComboBox::currentIndexChanged, this, &ExportSpreadsheetDialog::formatChanged); + //connect(ui->cbExportToFITS, &QComboBox::currentIndexChanged, this, &ExportSpreadsheetDialog::fitsExportToChanged); setWindowTitle(i18n("Export spreadsheet")); setWindowIcon(QIcon::fromTheme("document-export-database")); //restore saved settings KConfigGroup conf(KSharedConfig::openConfig(), "ExportSpreadsheetDialog"); ui->cbFormat->setCurrentIndex(conf.readEntry("Format", 0)); ui->chkExportHeader->setChecked(conf.readEntry("Header", true)); ui->cbSeparator->setCurrentItem(conf.readEntry("Separator", "TAB")); ui->chkHeaders->setChecked(conf.readEntry("LaTeXHeaders", true)); ui->chkGridLines->setChecked(conf.readEntry("LaTeXGridLines", true)); ui->chkCaptions->setChecked(conf.readEntry("LaTeXCaptions", true)); ui->chkEmptyRows->setChecked(conf.readEntry("LaTeXSkipEmpty", false)); ui->cbLaTeXExport->setCurrentIndex(conf.readEntry("ExportOnly", 0)); ui->chkMatrixHHeader->setChecked(conf.readEntry("MatrixHorizontalHeader", true)); ui->chkMatrixVHeader->setChecked(conf.readEntry("MatrixVerticalHeader", true)); ui->chkMatrixVHeader->setChecked(conf.readEntry("FITSSpreadsheetColumnsUnits", true)); ui->cbExportToFITS->setCurrentIndex(conf.readEntry("FITSTo", 0)); m_showOptions = conf.readEntry("ShowOptions", false); ui->gbOptions->setVisible(m_showOptions); m_showOptions ? m_showOptionsButton->setText(i18n("Hide Options")) : m_showOptionsButton->setText(i18n("Show Options")); KWindowConfig::restoreWindowSize(windowHandle(), conf); } ExportSpreadsheetDialog::~ExportSpreadsheetDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ExportSpreadsheetDialog"); conf.writeEntry("Format", ui->cbFormat->currentIndex()); conf.writeEntry("Header", ui->chkExportHeader->isChecked()); conf.writeEntry("Separator", ui->cbSeparator->currentIndex()); conf.writeEntry("ShowOptions", m_showOptions); conf.writeEntry("LaTeXHeaders", ui->chkHeaders->isChecked()); conf.writeEntry("LaTeXGridLines", ui->chkGridLines->isChecked()); conf.writeEntry("LaTeXCaptions", ui->chkCaptions->isChecked()); conf.writeEntry("LaTeXSkipEmpty", ui->chkEmptyRows->isChecked()); conf.writeEntry("ExportOnly", ui->cbLaTeXExport->currentIndex()); conf.writeEntry("MatrixVerticalHeader", ui->chkMatrixVHeader->isChecked()); conf.writeEntry("MatrixHorizontalHeader", ui->chkMatrixHHeader->isChecked()); conf.writeEntry("FITSTo", ui->cbExportToFITS->currentIndex()); conf.writeEntry("FITSSpreadsheetColumnsUnits", ui->chkColumnsAsUnits->isChecked()); KWindowConfig::saveWindowSize(windowHandle(), conf); } void ExportSpreadsheetDialog::setFileName(const QString& name) { KConfigGroup conf(KSharedConfig::openConfig(), "ExportSpreadsheetDialog"); QString dir = conf.readEntry("LastDir", ""); if (dir.isEmpty()) dir = QDir::homePath(); ui->leFileName->setText(dir + QDir::separator() + name); this->formatChanged(ui->cbFormat->currentIndex()); } void ExportSpreadsheetDialog::fitsExportToChanged(int idx) { if (idx == 0) { ui->chkColumnsAsUnits->hide(); ui->lColumnAsUnits->hide(); } else { if (!m_matrixMode) { ui->chkColumnsAsUnits->show(); ui->lColumnAsUnits->show(); } } } void ExportSpreadsheetDialog::setMatrixMode(bool b) { if (b) { setWindowTitle(i18n("Export matrix")); ui->lExportHeader->hide(); ui->chkExportHeader->hide(); ui->lEmptyRows->hide(); ui->chkEmptyRows->hide(); if (ui->cbFormat->currentIndex() != 3) { ui->chkMatrixHHeader->show(); ui->chkMatrixVHeader->show(); ui->lMatrixHHeader->show(); ui->lMatrixVHeader->show(); } ui->lHeader->hide(); ui->chkHeaders->hide(); ui->cbLaTeXExport->setItemText(0,i18n("Export matrix")); ui->cbExportToFITS->setCurrentIndex(0); ui->lColumnAsUnits->hide(); ui->chkColumnsAsUnits->hide(); m_matrixMode = b; } } QString ExportSpreadsheetDialog::path() const { return ui->leFileName->text(); } int ExportSpreadsheetDialog::exportToFits() const { return ui->cbExportToFITS->currentIndex(); } bool ExportSpreadsheetDialog::exportHeader() const { return ui->chkExportHeader->isChecked(); } bool ExportSpreadsheetDialog::captions() const { return ui->chkCaptions->isChecked(); } bool ExportSpreadsheetDialog::exportLatexHeader() const { return ui->chkHeaders->isChecked(); } bool ExportSpreadsheetDialog::gridLines() const { return ui->chkGridLines->isChecked(); } bool ExportSpreadsheetDialog::skipEmptyRows() const { return ui->chkEmptyRows->isChecked(); } bool ExportSpreadsheetDialog::exportSelection() const { return ui->cbLaTeXExport->currentIndex() == 1; } bool ExportSpreadsheetDialog::entireSpreadheet() const { return ui->cbLaTeXExport->currentIndex() == 0; } bool ExportSpreadsheetDialog::matrixHorizontalHeader() const { return ui->chkMatrixHHeader->isChecked(); } bool ExportSpreadsheetDialog::matrixVerticalHeader() const { return ui->chkMatrixVHeader->isChecked(); } bool ExportSpreadsheetDialog::commentsAsUnitsFits() const { return ui->chkColumnsAsUnits->isChecked(); } QString ExportSpreadsheetDialog::separator() const { return ui->cbSeparator->currentText(); } void ExportSpreadsheetDialog::slotButtonClicked(QAbstractButton* button) { if (button == m_okButton) okClicked(); else if (button == m_cancelButton) { reject(); } } void ExportSpreadsheetDialog::setExportToImage(bool possible) { if (!possible) { ui->cbExportToFITS->setCurrentIndex(1); ui->cbExportToFITS->setItemData(0, 0, Qt::UserRole - 1); } } //SLOTS void ExportSpreadsheetDialog::okClicked() { if (format() != FITS) 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(), "ExportSpreadsheetDialog"); conf.writeEntry("Format", ui->cbFormat->currentIndex()); conf.writeEntry("Header", ui->chkExportHeader->isChecked()); conf.writeEntry("Separator", ui->cbSeparator->currentText()); 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 ExportSpreadsheetDialog::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 ExportSpreadsheetDialog::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "ExportSpreadsheetDialog"); QString dir = conf.readEntry("LastDir", ""); QString path = QFileDialog::getOpenFileName(this, i18n("Export to file"), dir); if (!path.isEmpty()) { 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); } } } /*! called when the output format was changed. Adjusts the extension for the specified file. */ void ExportSpreadsheetDialog::formatChanged(int index) { QStringList extensions; extensions << ".txt" << ".bin" << ".tex" << ".fits"; QString path = ui->leFileName->text(); int i = path.indexOf("."); if (index != 1) { if (i==-1) path = path + extensions.at(index); else path=path.left(i) + extensions.at(index); } if (ui->cbFormat->currentIndex() == 2) { ui->cbSeparator->hide(); ui->lSeparator->hide(); ui->chkCaptions->show(); ui->chkGridLines->show(); ui->lExportArea->show(); ui->lGridLines->show(); ui->lCaptions->show(); ui->cbLaTeXExport->show(); if (!m_matrixMode) { ui->lHeader->show(); ui->chkHeaders->show(); ui->lEmptyRows->show(); ui->chkEmptyRows->show(); ui->lMatrixHHeader->hide(); ui->lMatrixVHeader->hide(); ui->chkMatrixHHeader->hide(); ui->chkMatrixVHeader->hide(); } else { ui->lMatrixHHeader->show(); ui->lMatrixVHeader->show(); ui->chkMatrixHHeader->show(); ui->chkMatrixVHeader->show(); } ui->cbExportToFITS->hide(); ui->lExportToFITS->hide(); ui->lColumnAsUnits->hide(); ui->chkColumnsAsUnits->hide(); //FITS } else if(ui->cbFormat->currentIndex() == 3) { ui->lCaptions->hide(); ui->lEmptyRows->hide(); ui->lExportArea->hide(); ui->lGridLines->hide(); ui->lMatrixHHeader->hide(); ui->lMatrixVHeader->hide(); ui->lSeparator->hide(); ui->lHeader->hide(); ui->chkEmptyRows->hide(); ui->chkHeaders->hide(); ui->chkExportHeader->hide(); ui->lExportHeader->hide(); ui->chkGridLines->hide(); ui->chkMatrixHHeader->hide(); ui->chkMatrixVHeader->hide(); ui->chkCaptions->hide(); ui->cbLaTeXExport->hide(); ui->cbSeparator->hide(); ui->cbExportToFITS->show(); ui->lExportToFITS->show(); if (!m_matrixMode) { ui->lColumnAsUnits->show(); ui->chkColumnsAsUnits->show(); } } else { ui->cbSeparator->show(); ui->lSeparator->show(); ui->chkCaptions->hide(); ui->chkEmptyRows->hide(); ui->chkGridLines->hide(); ui->lEmptyRows->hide(); ui->lExportArea->hide(); ui->lGridLines->hide(); ui->lCaptions->hide(); ui->cbLaTeXExport->hide(); ui->lMatrixHHeader->hide(); ui->lMatrixVHeader->hide(); ui->chkMatrixHHeader->hide(); ui->chkMatrixVHeader->hide(); ui->lHeader->hide(); ui->chkHeaders->hide(); ui->cbExportToFITS->hide(); ui->lExportToFITS->hide(); ui->lColumnAsUnits->hide(); ui->chkColumnsAsUnits->hide(); } if (!m_matrixMode) { ui->chkExportHeader->show(); ui->lExportHeader->show(); } else { ui->chkExportHeader->hide(); ui->lExportHeader->hide(); } if (ui->cbFormat->currentIndex() == 3) { ui->chkExportHeader->hide(); ui->lExportHeader->hide(); } setFormat(static_cast(index)); ui->leFileName->setText(path); } void ExportSpreadsheetDialog::setExportSelection(bool enable) { if (!enable) { const QStandardItemModel* areaToExportModel = qobject_cast(ui->cbLaTeXExport->model()); QStandardItem* item = areaToExportModel->item(1); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); } } void ExportSpreadsheetDialog::setFormat(Format format) { m_format = format; } void ExportSpreadsheetDialog::setExportTo(const QStringList &to) { ui->cbExportToFITS->addItems(to); } ExportSpreadsheetDialog::Format ExportSpreadsheetDialog::format() const { return m_format; } void ExportSpreadsheetDialog::fileNameChanged(const QString& name) { m_okButton->setEnabled(!name.simplified().isEmpty()); } diff --git a/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp b/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp index 360e8f54f..f71b1521f 100644 --- a/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp @@ -1,356 +1,356 @@ /*************************************************************************** File : FunctionValuesDialog.cpp Project : LabPlot Description : Dialog for generating values from a mathematical function -------------------------------------------------------------------- Copyright : (C) 2014-2015 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "FunctionValuesDialog.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" #include "backend/gsl/ExpressionParser.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/widgets/ConstantsWidget.h" #include "kdefrontend/widgets/FunctionsWidget.h" #include #include #include #include #include #include #include #include /*! \class FunctionValuesDialog \brief Dialog for generating values from a mathematical function. \ingroup kdefrontend */ FunctionValuesDialog::FunctionValuesDialog(Spreadsheet* s, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), m_spreadsheet(s) { Q_ASSERT(s); setWindowTitle(i18n("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"; #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); ui.bAddVariable->setIcon(QIcon::fromTheme("list-add")); ui.bAddVariable->setToolTip(i18n("Add new variable")); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.gridLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); - QPushButton* cancelButton = btnBox->button(QDialogButtonBox::Cancel); - - connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close())); + connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &FunctionValuesDialog::close); + 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, SIGNAL(clicked(bool)), this, SLOT(generate())); + connect(m_okButton, &QPushButton::clicked, this, &FunctionValuesDialog::generate); //restore saved settings if available KConfigGroup conf(KSharedConfig::openConfig(), "FunctionValuesDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize(QSize(300, 0).expandedTo(minimumSize())); } FunctionValuesDialog::~FunctionValuesDialog() { KConfigGroup conf(KSharedConfig::openConfig(), "FunctionValuesDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void FunctionValuesDialog::setColumns(QVector columns) { m_columns = columns; ui.teEquation->setPlainText(m_columns.first()->formula()); const QStringList& variableNames = m_columns.first()->formulaVariableNames(); if (!variableNames.size()) { //no formular 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 columns = m_spreadsheet->project()->children("Column", AbstractAspect::Recursive); for (int i = 0; i < variableNames.size(); ++i) { addVariable(); m_variableNames[i]->setText(variableNames.at(i)); foreach (const AbstractAspect* aspect, columns) { if (aspect->path() == columnPathes.at(i)) { const AbstractColumn* column = dynamic_cast(aspect); if (column) m_variableDataColumns[i]->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(column)); else m_variableDataColumns[i]->setCurrentModelIndex(QModelIndex()); break; } } } } } /*! check the user input and enables/disables the Ok-button depending on the correctness of the input */ void FunctionValuesDialog::checkValues() { //check whether the formulr 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; } } m_okButton->setEnabled(true); } void FunctionValuesDialog::showConstants() { QMenu menu; ConstantsWidget constants(&menu); connect(&constants, SIGNAL(constantSelected(QString)), this, SLOT(insertConstant(QString))); connect(&constants, SIGNAL(constantSelected(QString)), &menu, SLOT(close())); connect(&constants, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&constants); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbConstants->width(),-menu.sizeHint().height()); menu.exec(ui.tbConstants->mapToGlobal(pos)); } void FunctionValuesDialog::showFunctions() { QMenu menu; FunctionsWidget functions(&menu); connect(&functions, SIGNAL(functionSelected(QString)), this, SLOT(insertFunction(QString))); connect(&functions, SIGNAL(functionSelected(QString)), &menu, SLOT(close())); connect(&functions, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); 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) { ui.teEquation->insertPlainText(str + "(x)"); } void FunctionValuesDialog::insertConstant(const QString& str) { ui.teEquation->insertPlainText(str); } void FunctionValuesDialog::addVariable() { QGridLayout* layout = dynamic_cast(ui.frameVariables->layout()); int row = m_variableNames.size(); //text field for the variable name QLineEdit* le = new QLineEdit(); le->setMaximumWidth(30); connect(le, SIGNAL(textChanged(QString)), this, SLOT(variableNameChanged())); layout->addWidget(le, row, 0, 1, 1); m_variableNames<addWidget(l, row, 1, 1, 1); m_variableLabels<setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); connect( cb, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(checkValues()) ); layout->addWidget(cb, row, 2, 1, 1); m_variableDataColumns<setTopLevelClasses(m_topLevelClasses); cb->setModel(m_aspectTreeModel.get()); cb->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(m_spreadsheet->column(0))); //move the add-button to the next row layout->removeWidget(ui.bAddVariable); layout->addWidget(ui.bAddVariable, row+1,3, 1, 1); //add delete-button for the just added variable if (row != 0) { QToolButton* b = new QToolButton(); 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 (int i = 0; i < m_variableNames.size(); ++i) { QString name = m_variableNames.at(i)->text().simplified(); if (!name.isEmpty()) { vars<name(), m_columns.size())); //determine variable names and the data vectors of the specified columns QStringList variableNames; QStringList columnPathes; QVector*> xVectors; QVector xColumns; int maxRowCount = m_spreadsheet->rowCount(); for (int i=0; itext().simplified(); AbstractAspect* aspect = static_cast(m_variableDataColumns.at(i)->currentModelIndex().internalPointer()); Q_ASSERT(aspect); Column* column = dynamic_cast(aspect); Q_ASSERT(column); columnPathes << column->path(); xColumns << column; xVectors << static_cast* >(column->data()); if (column->rowCount()>maxRowCount) maxRowCount = column->rowCount(); } //resize the spreadsheet if one of the data vectors from other spreadsheet(s) has more elements then the current spreadsheet. if (m_spreadsheet->rowCount()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 (int i=0; itoPlainText(); parser->evaluateCartesian(expression, variableNames, xVectors, &new_data); //set the new values and store the expression, variable names and the used data columns foreach(Column* col, m_columns) { col->setFormula(expression, variableNames, columnPathes); col->replaceValues(0, new_data); } m_spreadsheet->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/PlotDataDialog.cpp b/src/kdefrontend/spreadsheet/PlotDataDialog.cpp index 6a199d2a5..87387b676 100644 --- a/src/kdefrontend/spreadsheet/PlotDataDialog.cpp +++ b/src/kdefrontend/spreadsheet/PlotDataDialog.cpp @@ -1,551 +1,551 @@ /*************************************************************************** File : PlotDataDialog.cpp 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 * * * ***************************************************************************/ #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/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" #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 "ui_plotdatawidget.h" /*! \class PlotDataDialog \brief Dialog for generating plots for the spreadsheet data. \ingroup kdefrontend */ PlotDataDialog::PlotDataDialog(Spreadsheet* s, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), ui(new Ui::PlotDataWidget()), m_spreadsheet(s), m_plotsModel(new AspectTreeModel(m_spreadsheet->project())), m_worksheetsModel(new AspectTreeModel(m_spreadsheet->project())), m_analysisAction(Differentiation), m_analysisMode(false) { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle(i18n("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")); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(mainWidget); layout->addWidget(buttonBox); setLayout(layout); //create combox boxes for the existing plots and worksheets QGridLayout* 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); list.clear(); list<<"Folder"<<"Worksheet"; cbExistingWorksheets->setTopLevelClasses(list); list.clear(); list<<"Worksheet"; m_worksheetsModel->setSelectableAspects(list); cbExistingWorksheets->setModel(m_worksheetsModel); //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, SIGNAL(accepted()), this, SLOT(plot())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - connect(ui->rbCurvePlacement1, SIGNAL(toggled(bool)), this, SLOT(curvePlacementChanged())); - connect(ui->rbCurvePlacement2, SIGNAL(toggled(bool)), this, SLOT(curvePlacementChanged())); - connect(ui->rbPlotPlacement1, SIGNAL(toggled(bool)), this, SLOT(plotPlacementChanged())); - connect(ui->rbPlotPlacement2, SIGNAL(toggled(bool)), this, SLOT(plotPlacementChanged())); - connect(ui->rbPlotPlacement3, SIGNAL(toggled(bool)), this, SLOT(plotPlacementChanged())); - connect(cbExistingPlots, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(checkOkButton())); - connect(cbExistingWorksheets, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(checkOkButton())); + connect(buttonBox, &QDialogButtonBox::accepted, this, &PlotDataDialog::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"); if (conf.exists()) { 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); plotPlacementChanged(); KWindowConfig::restoreWindowSize(windowHandle(), conf); } else resize( QSize(500,0).expandedTo(minimumSize()) ); this->processColumns(); } 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 SpreadsheetView* view = reinterpret_cast(m_spreadsheet->view()); m_columns = view->selectedColumns(true); //use all spreadsheet columns if no columns are selected if (!m_columns.size()) { m_columns = m_spreadsheet->children(); //disable everything if the spreadsheet doesn't have any columns if (!m_columns.size()) { ui->gbData->setEnabled(false); ui->gbCurvePlacement->setEnabled(false); ui->gbPlotPlacement->setEnabled(false); return; } } 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) { QGridLayout* gridLayout = dynamic_cast(ui->scrollAreaYColumns->widget()->layout()); for (int i = 2; i < m_columns.size(); ++i) { QLabel* label = new QLabel(i18n("Y-data")); QComboBox* 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")); } //determine the column names and the name of the first column having "X" as the plot designation QList columnNames; QString xColumnName; for(const Column* column : m_columns) { columnNames << column->name(); if (xColumnName.isEmpty() && column->plotDesignation() == AbstractColumn::X) xColumnName = column->name(); } //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::plot() { DEBUG("PlotDataDialog::plot()"); WAIT_CURSOR; if (ui->rbPlotPlacement1->isChecked()) { //add curves to an existing plot AbstractAspect* aspect = static_cast(cbExistingPlots->currentModelIndex().internalPointer()); CartesianPlot* 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 AbstractAspect* aspect = static_cast(cbExistingWorksheets->currentModelIndex().internalPointer()); Worksheet* 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 befor 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 Project* project = m_spreadsheet->project(); project->beginMacro( i18n("Plot data from %1", m_spreadsheet->name()) ); Worksheet* worksheet = new Worksheet(0, i18n("Plot data from %1", m_spreadsheet->name())); project->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 befor 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); } project->endMacro(); } RESET_CURSOR; } Column* PlotDataDialog::columnFromName(const QString& name) const { for(auto* column : m_columns) { if (column->name() == name) return column; } return 0; } /*! * * 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); Column* xColumn = columnFromName(ui->cbXColumn->currentText()); for (int i = 1; i < m_columnComboBoxes.size(); ++i) { QComboBox* comboBox = m_columnComboBoxes[i]; const QString& name = comboBox->currentText(); Column* yColumn = columnFromName(name); addCurve(name, xColumn, yColumn, plot); } 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); const QString& xColumnName = ui->cbXColumn->currentText(); Column* xColumn = columnFromName(xColumnName); for (int i = 1; i < m_columnComboBoxes.size(); ++i) { QComboBox* comboBox = m_columnComboBoxes[i]; 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(); } 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) { XYCurve* curve = new XYCurve(name); curve->setXColumn(xColumn); curve->setYColumn(yColumn); plot->addChild(curve); } else { bool createDataCurve = ui->chkCreateDataCurve->isChecked(); if (createDataCurve) { XYCurve* curve = new XYCurve(name); curve->setXColumn(xColumn); curve->setYColumn(yColumn); plot->addChild(curve); } //TODO: introduce a base class for all analysis curves and refactor this part switch (m_analysisAction) { case DataReduction: { XYDataReductionCurve* analysisCurve = new XYDataReductionCurve(i18n("Reduction of '%1'", name)); analysisCurve->setXDataColumn(xColumn); analysisCurve->setYDataColumn(yColumn); analysisCurve->recalculate(); plot->addChild(analysisCurve); break; } case Differentiation: { XYDifferentiationCurve* analysisCurve = new XYDifferentiationCurve(i18n("Derivative of '%1'", name)); analysisCurve->setXDataColumn(xColumn); analysisCurve->setYDataColumn(yColumn); analysisCurve->recalculate(); plot->addChild(analysisCurve); break; } case Integration: { XYIntegrationCurve* analysisCurve = new XYIntegrationCurve(i18n("Integral of '%1'", name)); analysisCurve->setXDataColumn(xColumn); analysisCurve->setYDataColumn(yColumn); analysisCurve->recalculate(); plot->addChild(analysisCurve); break; } case Interpolation: { XYInterpolationCurve* analysisCurve = new XYInterpolationCurve(i18n("Interpolation of '%1'", name)); analysisCurve->setXDataColumn(xColumn); analysisCurve->setYDataColumn(yColumn); analysisCurve->recalculate(); plot->addChild(analysisCurve); break; } case Smoothing: { XYSmoothCurve* analysisCurve = new XYSmoothCurve(i18n("Smoothing of '%1'", name)); analysisCurve->setXDataColumn(xColumn); analysisCurve->setYDataColumn(yColumn); analysisCurve->recalculate(); plot->addChild(analysisCurve); break; } case FitLinear: case FitPower: case FitExp1: case FitExp2: case FitInvExp: case FitGauss: case FitCauchyLorentz: case FitTan: case FitTanh: case FitErrFunc: case FitCustom: { XYFitCurve* analysisCurve = new XYFitCurve(i18n("Fit to '%1'", name)); analysisCurve->setXDataColumn(xColumn); analysisCurve->setYDataColumn(yColumn); analysisCurve->initFitData(m_analysisAction); analysisCurve->recalculate(); plot->addChild(analysisCurve); break; } case FourierFilter: { XYFourierFilterCurve* analysisCurve = new XYFourierFilterCurve(i18n("Fourier Filter of '%1'", name)); analysisCurve->setXDataColumn(xColumn); analysisCurve->setYDataColumn(yColumn); analysisCurve->recalculate(); plot->addChild(analysisCurve); break; } } } } //################################################################ //########################## 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; if (ui->rbPlotPlacement1->isChecked()) { AbstractAspect* aspect = static_cast(cbExistingPlots->currentModelIndex().internalPointer()); enable = (aspect!=NULL); } else if (ui->rbPlotPlacement2->isChecked()) { AbstractAspect* aspect = static_cast(cbExistingWorksheets->currentModelIndex().internalPointer()); enable = (aspect!=NULL); } else enable = true; m_okButton->setEnabled(enable); } diff --git a/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp b/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp index 3b5191e0b..8b6dbe5e4 100644 --- a/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp @@ -1,722 +1,725 @@ /*************************************************************************** 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) 2016-2017 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 extern "C" { #include #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, Qt::WFlags fl) : QDialog(parent, fl), m_spreadsheet(s) { setWindowTitle(i18n("Random values")); QWidget* mainWidget = new QWidget(this); ui.setupUi(mainWidget); QVBoxLayout *layout = new QVBoxLayout(this); QDialogButtonBox *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, SIGNAL(currentIndexChanged(int)), SLOT(distributionChanged(int)) ); - connect( ui.leParameter1, SIGNAL(textChanged(QString)), this, SLOT(checkValues()) ); - connect( ui.leParameter2, SIGNAL(textChanged(QString)), this, SLOT(checkValues()) ); - connect( ui.leParameter3, SIGNAL(textChanged(QString)), this, SLOT(checkValues()) ); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(generate())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + 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 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 emmited 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); } 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(QString::fromUtf8("\u03c3 =")); ui.lParameter2->setText(QString::fromUtf8("\u03bc =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_gaussian_tail: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(QString::fromUtf8("\u03bc =")); ui.lParameter2->setText(QString::fromUtf8("\u03c3 =")); 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(QString::fromUtf8("\u03bb =")); ui.leParameter1->setText("1.0"); ui.lParameter2->setText(QString::fromUtf8("\u03bc =")); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_laplace: ui.lParameter1->setText(QString::fromUtf8("\u03c3 =")); ui.lParameter2->setText(QString::fromUtf8("\u03bc =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_exponential_power: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(QString::fromUtf8("\u03bc =")); ui.lParameter2->setText(QString::fromUtf8("\u03c3 =")); 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(QString::fromUtf8("\u03b3 =")); ui.lParameter2->setText(QString::fromUtf8("\u03bc =")); 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(QString::fromUtf8("\u03c3 =")); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_rayleigh_tail: ui.lParameter1->setText(QString::fromUtf8("\u03bc =")); ui.lParameter2->setText(QString::fromUtf8("\u03c3 =")); 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(QString::fromUtf8("\u03b1 =")); 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(QString::fromUtf8("c =")); ui.lParameter2->setText(QString::fromUtf8("\u03b1 =")); ui.lParameter3->setText(QString::fromUtf8("\u03b2 =")); 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(QString::fromUtf8("\u03b8 =")); 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(QString::fromUtf8("\u03bb =")); ui.lParameter3->setText(QString::fromUtf8("\u03bc =")); 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(QString::fromUtf8("\u03c3 =")); ui.lParameter2->setText(QString::fromUtf8("\u03b2 =")); ui.lParameter3->setText(QString::fromUtf8("\u03bc =")); 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(QString::fromUtf8("\u03bc =")); 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(QString::fromUtf8("\u03c3 =")); ui.lParameter2->setText(QString::fromUtf8("\u03bc =")); 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(QString::fromUtf8("\u03bd\u2081 =")); ui.lParameter2->setText(QString::fromUtf8("\u03bd\u2082 =")); 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(QString::fromUtf8("\u03bd =")); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_logistic: ui.lParameter1->setText(QString::fromUtf8("\u03c3 =")); ui.lParameter2->setText(QString::fromUtf8("\u03bc =")); 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(QString::fromUtf8("\u03bb =")); 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.lParameter1->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]) + ".jpg"); 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; foreach (Column* 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(); const int rows = m_spreadsheet->rowCount(); QVector new_data(rows); switch (dist) { case nsl_sf_stats_gaussian: { double mu = ui.leParameter1->text().toDouble(); double sigma = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gaussian(r, sigma) + mu; col->replaceValues(0, new_data); } 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(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gaussian_tail(r, a, sigma) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_exponential: { double l = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { //GSL uses the inverse for exp. distrib. for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_exponential(r, 1./l) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_laplace: { double s = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_laplace(r, s) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_exponential_power: { double mu = ui.leParameter1->text().toDouble(); double a = ui.leParameter2->text().toDouble(); double b = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_exppow(r, a, b) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_cauchy_lorentz: { double gamma = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_cauchy(r, gamma) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_rayleigh: { double s = ui.leParameter1->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_rayleigh(r, s); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_rayleigh_tail: { double mu = ui.leParameter1->text().toDouble(); double sigma = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_rayleigh_tail(r, mu, sigma); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_landau: foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_landau(r); col->replaceValues(0, new_data); } break; case nsl_sf_stats_levy_alpha_stable: { double c = ui.leParameter1->text().toDouble(); double alpha = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_levy(r, c, alpha); col->replaceValues(0, new_data); } 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(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_levy_skew(r, c, alpha, beta); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_gamma: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gamma(r, a, b); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_flat: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_flat(r, a, b); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_lognormal: { double s = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_lognormal(r, mu, s); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_chi_squared: { double n = ui.leParameter1->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_chisq(r, n); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_fdist: { double nu1 = ui.leParameter1->text().toDouble(); double nu2 = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_fdist(r, nu1, nu2); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_tdist: { double nu = ui.leParameter1->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_tdist(r, nu); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_beta: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_beta(r, a, b); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_logistic: { double s = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_logistic(r, s) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_pareto: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_pareto(r, a, b); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_weibull: { double k = ui.leParameter1->text().toDouble(); double l = ui.leParameter2->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_weibull(r, l, k) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_gumbel1: { double s = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); double mu = ui.leParameter3->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gumbel1(r, 1./s, b) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_gumbel2: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); double mu = ui.leParameter3->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gumbel2(r, a, b) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_poisson: { double l = ui.leParameter1->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_poisson(r, l); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_bernoulli: { double p = ui.leParameter1->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_bernoulli(r, p); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_binomial: { double p = ui.leParameter1->text().toDouble(); double n = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_binomial(r, p, n); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_negative_binomial: { double p = ui.leParameter1->text().toDouble(); double n = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_negative_binomial(r, p, n); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_pascal: { double p = ui.leParameter1->text().toDouble(); double n = ui.leParameter2->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_pascal(r, p, n); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_geometric: { double p = ui.leParameter1->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_geometric(r, p); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_hypergeometric: { double n1 = ui.leParameter1->text().toDouble(); double n2 = ui.leParameter2->text().toDouble(); double t = ui.leParameter3->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_hypergeometric(r, n1, n2, t); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_logarithmic: { double p = ui.leParameter1->text().toDouble(); foreach (Column* col, m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_logarithmic(r, p); col->replaceValues(0, new_data); } 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; } foreach (Column* col, m_columns) { col->setSuppressDataChangedSignal(false); col->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; gsl_rng_free(r); } diff --git a/src/kdefrontend/spreadsheet/SortDialog.cpp b/src/kdefrontend/spreadsheet/SortDialog.cpp index 68a11e595..31fc82600 100644 --- a/src/kdefrontend/spreadsheet/SortDialog.cpp +++ b/src/kdefrontend/spreadsheet/SortDialog.cpp @@ -1,129 +1,128 @@ /*************************************************************************** File : SortDialog.h Project : LabPlot Description : Sorting options dialog -------------------------------------------------------------------- Copyright : (C) 2011 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 "SortDialog.h" #include #include #include #include #include #include #include #include #include /*! \class SortDialog \brief Dialog for sorting the columns in a spreadsheet. \ingroup kdefrontend */ SortDialog::SortDialog( QWidget* parent, Qt::WFlags fl ) : QDialog( parent, fl ){ setWindowIcon(QIcon::fromTheme("view-sort-ascending")); setWindowTitle(i18n("Sort columns")); setSizeGripEnabled(true); setAttribute(Qt::WA_DeleteOnClose); QGroupBox* widget = new QGroupBox(i18n("Options")); QGridLayout* layout = new QGridLayout(widget); layout->setSpacing(4); layout->setContentsMargins(4,4,4,4); layout->addWidget( new QLabel( i18n("Order")), 0, 0 ); m_cbOrdering = new QComboBox(); m_cbOrdering->addItem(QIcon::fromTheme("view-sort-ascending"), i18n("Ascending")); m_cbOrdering->addItem(QIcon::fromTheme("view-sort-descending"), i18n("Descending")); layout->addWidget(m_cbOrdering, 0, 1 ); m_lType = new QLabel(i18n("Sort columns")); layout->addWidget( m_lType, 1, 0 ); m_cbType = new QComboBox(); m_cbType->addItem(i18n("Separately")); m_cbType->addItem(i18n("Together")); layout->addWidget(m_cbType, 1, 1 ); m_cbType->setCurrentIndex(Together); m_lColumns = new QLabel(i18n("Leading column")); layout->addWidget( m_lColumns, 2, 0 ); m_cbColumns = new QComboBox(); layout->addWidget(m_cbColumns, 2, 1); layout->setRowStretch(3, 1); - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok - | QDialogButtonBox::Cancel); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("Sort")); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(sort())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, &QDialogButtonBox::accepted, this, &SortDialog::sortColumns); + connect(buttonBox, &QDialogButtonBox::rejected, this, &SortDialog::reject); + connect(buttonBox, &QDialogButtonBox::accepted, this, &SortDialog::accept); layout->addWidget(buttonBox); setLayout(layout); connect(m_cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(changeType(int))); this->resize(400,0); } -void SortDialog::sort(){ +void SortDialog::sortColumns(){ Column* leading; if(m_cbType->currentIndex() == Together) leading = m_columns.at(m_cbColumns->currentIndex()); else leading = 0; emit sort(leading, m_columns, m_cbOrdering->currentIndex() == Ascending ); } void SortDialog::setColumns(QVector columns){ m_columns = columns; for(int i=0; iaddItem( m_columns.at(i)->name() ); m_cbColumns->setCurrentIndex(0); if (m_columns.size() == 1){ m_lType->hide(); m_cbType->hide(); m_lColumns->hide(); m_cbColumns->hide(); } } void SortDialog::changeType(int Type){ if(Type == Together) m_cbColumns->setEnabled(true); else m_cbColumns->setEnabled(false); } diff --git a/src/kdefrontend/spreadsheet/SortDialog.h b/src/kdefrontend/spreadsheet/SortDialog.h index cf4e12967..5cf6530cf 100644 --- a/src/kdefrontend/spreadsheet/SortDialog.h +++ b/src/kdefrontend/spreadsheet/SortDialog.h @@ -1,65 +1,65 @@ /*************************************************************************** File : SortDialog.h Project : LabPlot Description : Sorting options dialog -------------------------------------------------------------------- Copyright : (C) 2011 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 SORTDIALOG_H #define SORTDIALOG_H #include "backend/core/column/Column.h" #include class QPushButton; class QComboBox; class QLabel; class SortDialog : public QDialog { Q_OBJECT public: explicit SortDialog( QWidget* parent = 0, Qt::WFlags fl = 0 ); void setColumns(QVector); enum { Separately=0, Together=1 }; enum { Ascending=0, Descending=1 }; private slots: - void sort(); + void sortColumns(); void changeType(int index); signals: void sort(Column*, QVector, bool ascending); private: QVector m_columns; QComboBox* m_cbOrdering; QLabel* m_lType; QComboBox* m_cbType; QLabel* m_lColumns; QComboBox* m_cbColumns; }; #endif diff --git a/src/kdefrontend/spreadsheet/StatisticsDialog.cpp b/src/kdefrontend/spreadsheet/StatisticsDialog.cpp index 3c8f26b8f..c697531d9 100644 --- a/src/kdefrontend/spreadsheet/StatisticsDialog.cpp +++ b/src/kdefrontend/spreadsheet/StatisticsDialog.cpp @@ -1,237 +1,238 @@ /*************************************************************************** File : StatisticsDialog.cpp Project : LabPlot Description : Dialog showing statistics for column values -------------------------------------------------------------------- Copyright : (C) 2016-2017 by Fabian Kristof (fkristofszabolcs@gmail.com)) Copyright : (C) 2016-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 * * * ***************************************************************************/ #include "StatisticsDialog.h" #include "backend/core/column/Column.h" #include #include #include #include #include #include #include #include #include StatisticsDialog::StatisticsDialog(const QString& title, QWidget* parent) : QDialog(parent) { m_twStatistics = new QTabWidget; QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok); QPushButton* btnOk = btnBox->button(QDialogButtonBox::Ok); btnOk->setFocus(); - connect(btnOk, SIGNAL(clicked(bool)), this, SLOT(close())); + connect(btnOk, &QPushButton::clicked, this, &StatisticsDialog::close); + connect(btnBox, &QDialogButtonBox::accepted, this, &StatisticsDialog::accept); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(m_twStatistics); layout->addWidget(btnBox); setLayout(layout); setWindowTitle(title); setAttribute(Qt::WA_DeleteOnClose); const QString htmlColor = (palette().color(QPalette::Base).lightness() < 128) ? QLatin1String("#5f5f5f") : QLatin1String("#D1D1D1"); m_htmlText = QString("" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "
" + i18n("Location measures")+ "
" + i18n("Minimum")+ "%1
" + i18n("Maximum")+ "%2
" + i18n("Arithmetic mean")+ "%3
" + i18n("Geometric mean")+ "%4
" + i18n("Harmonic mean")+ "%5
" + i18n("Contraharmonic mean")+ "%6
" + i18n("Median")+ "%7
" + i18n("Dispersion measures")+ "
" + i18n("Variance")+ "%8
" + i18n("Standard deviation")+ "%9
" + i18n("Mean absolute deviation around mean")+ "%10
" + i18n("Mean absolute deviation around median")+ "%11
" + i18n("Median absolute deviation")+ "%12
" + i18n("Shape measures")+ "
" + i18n("Skewness")+ "%13
" + i18n("Kurtosis")+ "%14
" + i18n("Entropy")+ "%15
"); - connect(m_twStatistics, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int))); + connect(m_twStatistics, &QTabWidget::currentChanged, this, &StatisticsDialog::currentTabChanged); QTimer::singleShot(0, this, &StatisticsDialog::loadSettings); } void StatisticsDialog::loadSettings() { //restore saved settings if available QApplication::processEvents(QEventLoop::AllEvents, 0); KConfigGroup conf(KSharedConfig::openConfig(), "StatisticsDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize(QSize(490, 520)); } StatisticsDialog::~StatisticsDialog() { KConfigGroup conf(KSharedConfig::openConfig(), "StatisticsDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void StatisticsDialog::setColumns(const QVector& columns) { if (!columns.size()) return; m_columns = columns; for (int i = 0; i < m_columns.size(); ++i) { QTextEdit* textEdit = new QTextEdit; textEdit->setReadOnly(true); m_twStatistics->addTab(textEdit, m_columns[i]->name()); } currentTabChanged(0); } const QString StatisticsDialog::isNanValue(const double value) { return (std::isnan(value) ? QLatin1String("-") : QString::number(value,'g', 10)); } void StatisticsDialog::currentTabChanged(int index) { WAIT_CURSOR; const Column::ColumnStatistics& statistics = m_columns[index]->statistics(); RESET_CURSOR; QTextEdit* const textEdit = static_cast(m_twStatistics->currentWidget()); textEdit->setHtml(m_htmlText.arg(isNanValue(statistics.minimum == INFINITY ? NAN : statistics.minimum), isNanValue(statistics.maximum == -INFINITY ? NAN : statistics.maximum), isNanValue(statistics.arithmeticMean), isNanValue(statistics.geometricMean), isNanValue(statistics.harmonicMean), isNanValue(statistics.contraharmonicMean), isNanValue(statistics.median), isNanValue(statistics.variance), isNanValue(statistics.standardDeviation)). arg(isNanValue(statistics.meanDeviation), isNanValue(statistics.meanDeviationAroundMedian), isNanValue(statistics.medianDeviation), isNanValue(statistics.skewness), isNanValue(statistics.kurtosis), isNanValue(statistics.entropy))); } diff --git a/src/kdefrontend/widgets/ExpressionTextEdit.cpp b/src/kdefrontend/widgets/ExpressionTextEdit.cpp index 59ce5c92f..04a42e506 100644 --- a/src/kdefrontend/widgets/ExpressionTextEdit.cpp +++ b/src/kdefrontend/widgets/ExpressionTextEdit.cpp @@ -1,218 +1,218 @@ /*************************************************************************** File : ExpressionTextEdit.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2014-2016 Alexander Semke (alexander.semke@web.de) Description : widget for defining mathematical expressions modified version of http://qt-project.org/doc/qt-4.8/tools-customcompleter.html ***************************************************************************/ /*************************************************************************** * * * 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 * * * ***************************************************************************/ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "ExpressionTextEdit.h" #include "backend/gsl/ExpressionParser.h" #include "tools/EquationHighlighter.h" #include #include #include #include /*! \class ExpressionTextEdit \brief Provides a widget for defining mathematical expressions Supports syntax-highlighting and completion. Modified version of http://qt-project.org/doc/qt-4.8/tools-customcompleter.html \ingroup kdefrontend */ ExpressionTextEdit::ExpressionTextEdit(QWidget *parent) : KTextEdit(parent), m_highlighter(new EquationHighlighter(this)), m_expressionType(XYEquationCurve::Neutral), m_isValid(false) { QStringList list = ExpressionParser::getInstance()->functions(); list.append(ExpressionParser::getInstance()->constants()); setTabChangesFocus(true); m_completer = new QCompleter(list, this); m_completer->setWidget(this); m_completer->setCompletionMode(QCompleter::PopupCompletion); m_completer->setCaseSensitivity(Qt::CaseInsensitive); - connect(m_completer, SIGNAL(activated(QString)),this, SLOT(insertCompletion(QString))); + connect(m_completer, static_cast(&QCompleter::activated),this, &ExpressionTextEdit::insertCompletion); connect(this, SIGNAL(textChanged()), this, SLOT(validateExpression()) ); connect(this, SIGNAL(cursorPositionChanged()), m_highlighter, SLOT(rehighlight()) ); } EquationHighlighter* ExpressionTextEdit::highlighter() { return m_highlighter; } bool ExpressionTextEdit::isValid() const { return (!document()->toPlainText().trimmed().isEmpty() && m_isValid); } void ExpressionTextEdit::setExpressionType(XYEquationCurve::EquationType type) { m_expressionType = type; m_variables.clear(); if (type==XYEquationCurve::Cartesian) m_variables<<"x"; else if (type==XYEquationCurve::Polar) m_variables<<"phi"; else if (type==XYEquationCurve::Parametric) m_variables<<"t"; else if (type==XYEquationCurve::Implicit) m_variables<<"x"<<"y"; m_highlighter->setVariables(m_variables); } void ExpressionTextEdit::setVariables(const QStringList& vars) { m_variables = vars; m_highlighter->setVariables(m_variables); validateExpression(true); } void ExpressionTextEdit::insertCompletion(const QString& completion) { QTextCursor tc = textCursor(); int extra = completion.length() - m_completer->completionPrefix().length(); tc.movePosition(QTextCursor::Left); tc.movePosition(QTextCursor::EndOfWord); tc.insertText(completion.right(extra)); setTextCursor(tc); } QString ExpressionTextEdit::textUnderCursor() const { QTextCursor tc = textCursor(); tc.select(QTextCursor::WordUnderCursor); return tc.selectedText(); } void ExpressionTextEdit::focusInEvent(QFocusEvent *e) { m_completer->setWidget(this); QTextEdit::focusInEvent(e); } void ExpressionTextEdit::focusOutEvent(QFocusEvent *e) { //when loosing focus, rehighlight to remove potential highlighting of openning and closing brackets m_highlighter->rehighlight(); QTextEdit::focusOutEvent(e); } void ExpressionTextEdit::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: e->ignore(); return; default: break; } bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E if (!isShortcut) // do not process the shortcut when we have a completer QTextEdit::keyPressEvent(e); const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier); if ((ctrlOrShift && e->text().isEmpty())) return; static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift; QString completionPrefix = textUnderCursor(); if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 1 || eow.contains(e->text().right(1)))) { m_completer->popup()->hide(); return; } if (completionPrefix != m_completer->completionPrefix()) { m_completer->setCompletionPrefix(completionPrefix); m_completer->popup()->setCurrentIndex(m_completer->completionModel()->index(0, 0)); } QRect cr = cursorRect(); cr.setWidth(m_completer->popup()->sizeHintForColumn(0) + m_completer->popup()->verticalScrollBar()->sizeHint().width()); m_completer->complete(cr); // popup it up! } /*! * \brief Validates the current expression if the text was changed and highlights the text field red if the expression is invalid. * \param force forces the validation and highlighting when no text changes were made, used when new parameters/variables were provided */ void ExpressionTextEdit::validateExpression(bool force) { //check whether the expression was changed or only the formating QString text = toPlainText(); if (text != m_currentExpression || force) { m_isValid = ExpressionParser::getInstance()->isValid(text, m_variables); if (!m_isValid) setStyleSheet("QTextEdit{background: red;}"); else setStyleSheet(""); m_currentExpression = text; emit expressionChanged(); } } diff --git a/src/kdefrontend/widgets/FITSHeaderEditAddUnitDialog.cpp b/src/kdefrontend/widgets/FITSHeaderEditAddUnitDialog.cpp index 5aabfde4e..9b41e87c7 100644 --- a/src/kdefrontend/widgets/FITSHeaderEditAddUnitDialog.cpp +++ b/src/kdefrontend/widgets/FITSHeaderEditAddUnitDialog.cpp @@ -1,68 +1,69 @@ /*************************************************************************** File : FITSHeaderEditAddUnitDialog.cpp Project : LabPlot Description : Widget for adding or modifying FITS header keyword units -------------------------------------------------------------------- Copyright : (C) 2016-2017 by Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "FITSHeaderEditAddUnitDialog.h" #include "backend/datasources/filters/FITSFilter.h" #include #include #include FITSHeaderEditAddUnitDialog::FITSHeaderEditAddUnitDialog(const QString& unit, QWidget* parent) : QDialog(parent) { ui.setupUi(this); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.horizontalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_okButton->setText(i18n("&Add")); setWindowTitle(i18n("Add New Unit")); setWindowIcon(QIcon::fromTheme("document-new")); m_okButton->setEnabled(false); QCompleter* keyCompleter = new QCompleter(FITSFilter::units(), this); ui.leUnit->setCompleter(keyCompleter); ui.leUnit->setPlaceholderText(i18n("Enter unit name here")); - connect(ui.leUnit, SIGNAL(textChanged(QString)), this, SLOT(unitChanged())); - connect(m_okButton, SIGNAL(clicked(bool)), this, SLOT(accept())); - QPushButton* btnCancel = btnBox->button(QDialogButtonBox::Cancel); - connect(btnCancel, SIGNAL(clicked(bool)), this, SLOT(reject())); + connect(ui.leUnit, &QLineEdit::textChanged, this, &FITSHeaderEditAddUnitDialog::unitChanged); + connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &FITSHeaderEditAddUnitDialog::close); + + connect(btnBox, &QDialogButtonBox::accepted, this, &FITSHeaderEditAddUnitDialog::accept); + connect(btnBox, &QDialogButtonBox::rejected, this, &FITSHeaderEditAddUnitDialog::reject); ui.leUnit->setText(unit); } QString FITSHeaderEditAddUnitDialog::unit() const { QString unit = ui.leUnit->text(); if (unit.contains(QLatin1Char('('))) unit = unit.left(unit.indexOf(QLatin1Char('('))-1); return unit; } void FITSHeaderEditAddUnitDialog::unitChanged() { m_okButton->setEnabled(!ui.leUnit->text().isEmpty()); } diff --git a/src/kdefrontend/widgets/FITSHeaderEditDialog.cpp b/src/kdefrontend/widgets/FITSHeaderEditDialog.cpp index 1336cc71f..e8bd11f3a 100644 --- a/src/kdefrontend/widgets/FITSHeaderEditDialog.cpp +++ b/src/kdefrontend/widgets/FITSHeaderEditDialog.cpp @@ -1,108 +1,108 @@ /*************************************************************************** File : FITSHeaderEditDialog.h Project : LabPlot Description : Dialog for listing/editing FITS header keywords -------------------------------------------------------------------- Copyright : (C) 2016-2017 by Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "FITSHeaderEditDialog.h" #include #include #include #include #include /*! \class FITSHeaderEditDialog * \brief Dialog class for editing FITS header units. * \since 2.4.0 * \ingroup widgets */ FITSHeaderEditDialog::FITSHeaderEditDialog(QWidget* parent) : QDialog(parent), m_saved(false) { m_headerEditWidget = new FITSHeaderEditWidget(this); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(m_headerEditWidget); layout->addWidget(btnBox); setLayout(layout); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_okButton->setText(i18n("&Save")); m_okButton->setEnabled(false); - QPushButton* cancelButton = btnBox->button(QDialogButtonBox::Cancel); - - connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(reject())); + connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &FITSHeaderEditDialog::reject); + connect(btnBox, &QDialogButtonBox::accepted, this, &FITSHeaderEditDialog::accept); + connect(btnBox, &QDialogButtonBox::rejected, this, &FITSHeaderEditDialog::reject); setWindowTitle(i18n("FITS Metadata Editor")); setWindowIcon(QIcon::fromTheme("document-edit")); - connect(m_okButton, SIGNAL(clicked(bool)), this, SLOT(save())); - connect(m_headerEditWidget, SIGNAL(changed(bool)), this, SLOT(headersChanged(bool))); + connect(m_okButton, &QPushButton::clicked, this, &FITSHeaderEditDialog::save); + connect(m_headerEditWidget, &FITSHeaderEditWidget::changed, this, &FITSHeaderEditDialog::headersChanged); setAttribute(Qt::WA_DeleteOnClose); //restore saved settings if available KConfigGroup conf(KSharedConfig::openConfig(), "FITSHeaderEditDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize( QSize(400,0).expandedTo(minimumSize()) ); } /*! * \brief FITSHeaderEditDialog::~FITSHeaderEditDialog */ FITSHeaderEditDialog::~FITSHeaderEditDialog() { KConfigGroup conf(KSharedConfig::openConfig(), "FITSHeaderEditDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); delete m_headerEditWidget; } void FITSHeaderEditDialog::headersChanged(bool changed) { if (changed) { setWindowTitle(i18n("FITS Metadata Editor [Changed]")); m_okButton->setEnabled(true); } else { setWindowTitle(i18n("FITS Metadata Editor")); m_okButton->setEnabled(false); } } /*! * \brief This slot is triggered when the Save button was clicked in the ui. */ void FITSHeaderEditDialog::save() { m_saved = m_headerEditWidget->save(); } /*! * \brief Returns whether there were changes saved. * \return */ bool FITSHeaderEditDialog::saved() const { return m_saved; } diff --git a/src/kdefrontend/widgets/FITSHeaderEditNewKeywordDialog.cpp b/src/kdefrontend/widgets/FITSHeaderEditNewKeywordDialog.cpp index 49209b9d6..ec7a4872c 100644 --- a/src/kdefrontend/widgets/FITSHeaderEditNewKeywordDialog.cpp +++ b/src/kdefrontend/widgets/FITSHeaderEditNewKeywordDialog.cpp @@ -1,114 +1,114 @@ /*************************************************************************** File : FITSHeaderEditNewKeywordDialog.cpp Project : LabPlot Description : Widget for adding new keyword in the FITS edit widget -------------------------------------------------------------------- Copyright : (C) 2016-2017 by Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "FITSHeaderEditNewKeywordDialog.h" #include #include #include #include #include #include #define FLEN_KEYWORD 75 /* max length of a keyword (HIERARCH convention) */ #define FLEN_VALUE 71 /* max length of a keyword value string */ #define FLEN_COMMENT 73 /* max length of a keyword comment string */ /*! \class FITSHeaderEditNewKeywordDialog * \brief Dialog class for adding new keywords to the FITSHeaderEditDialog's table. * \since 2.4.0 * \ingroup widgets */ FITSHeaderEditNewKeywordDialog::FITSHeaderEditNewKeywordDialog(QWidget *parent) : QDialog(parent) { ui.setupUi(this); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.gridLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_cancelButton = btnBox->button(QDialogButtonBox::Cancel); m_okButton->setText(i18n("&Add keyword")); - connect(btnBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonClicked(QAbstractButton*))); + connect(btnBox, &QDialogButtonBox::clicked, this, &FITSHeaderEditNewKeywordDialog::slotButtonClicked); setWindowTitle(i18n("Specify the new keyword")); setWindowIcon(QIcon::fromTheme("document-new")); QCompleter* keyCompleter = new QCompleter(FITSFilter::standardKeywords(), this); keyCompleter->setCaseSensitivity(Qt::CaseInsensitive); ui.leKey->setCompleter(keyCompleter); ui.leKey->setPlaceholderText(i18n("Specify the name")); ui.leValue->setPlaceholderText(i18n("Specify the value")); ui.leComment->setPlaceholderText(i18n("Specify the comment")); ui.leKey->setMaxLength(FLEN_KEYWORD); ui.leValue->setMaxLength(FLEN_VALUE); ui.leComment->setMaxLength(FLEN_COMMENT); } /*! * \brief Decides whether the keyword can be used, messagebox pops up if the keywords key is empty. * \return Whether the keyword was "Ok" or not. */ int FITSHeaderEditNewKeywordDialog::okClicked() { if (!ui.leKey->text().isEmpty()) { m_newKeyword = FITSFilter::Keyword(ui.leKey->text(), ui.leValue->text(), ui.leComment->text()); return QMessageBox::Ok; } else { const int yesNo = KMessageBox::warningYesNo(this, i18n("Can't add new keyword without key, would you like to try again?"), i18n("Cannot add empty key")); if (yesNo == KMessageBox::No) return QMessageBox::Cancel; return yesNo; } } /*! * \brief Returns the new keyword. * \return The newly constructed keyword from the line edits. */ FITSFilter::Keyword FITSHeaderEditNewKeywordDialog::newKeyword() const { return m_newKeyword; } /*! * \brief Decides whether the dialog should move in an accepted state or canceled. * \param button the button clicked */ void FITSHeaderEditNewKeywordDialog::slotButtonClicked(QAbstractButton* button) { if (button == m_okButton) { int okClickedBtn = okClicked(); if (okClickedBtn == QMessageBox::Ok) accept(); else if (okClickedBtn == QMessageBox::Cancel) reject(); } else if (button == m_cancelButton) reject(); } diff --git a/src/kdefrontend/widgets/FunctionsWidget.cpp b/src/kdefrontend/widgets/FunctionsWidget.cpp index 66e9aeb47..ea76cebc0 100644 --- a/src/kdefrontend/widgets/FunctionsWidget.cpp +++ b/src/kdefrontend/widgets/FunctionsWidget.cpp @@ -1,105 +1,105 @@ /*************************************************************************** File : FunctionsWidget.cc Project : LabPlot Description : widget for selecting functions -------------------------------------------------------------------- Copyright : (C) 2014 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 "FunctionsWidget.h" #include "backend/gsl/ExpressionParser.h" /*! \class FunctionsWidget \brief Widget for selecting supported mathematical functions that can be used in expressions in \c ExpressionTextEdit. \ingroup kdefrontend */ FunctionsWidget::FunctionsWidget(QWidget *parent): QWidget(parent) { ui.setupUi(this); ui.bInsert->setIcon(QIcon::fromTheme("edit-paste")); ui.bCancel->setIcon(QIcon::fromTheme("dialog-cancel")); m_expressionParser = ExpressionParser::getInstance(); ui.cbGroup->addItems(m_expressionParser->functionsGroups()); //SLOTS - connect( ui.leFilter, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)) ); - connect( ui.cbGroup, SIGNAL(currentIndexChanged(int)), this, SLOT(groupChanged(int)) ); - connect( ui.bInsert, SIGNAL(clicked(bool)), this, SLOT(insertClicked()) ); - connect( ui.bCancel, SIGNAL(clicked(bool)), this, SIGNAL(canceled()) ); - connect( ui.lwFunctions, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(insertClicked()) ); + connect(ui.leFilter, &QLineEdit::textChanged, this, &FunctionsWidget::filterChanged); + connect(ui.cbGroup, static_cast(&QComboBox::currentIndexChanged), this, &FunctionsWidget::groupChanged); + connect(ui.bInsert, &QPushButton::clicked, this, &FunctionsWidget::insertClicked); + connect(ui.bCancel, &QPushButton::clicked, this, &FunctionsWidget::canceled); + connect(ui.lwFunctions, &QListWidget::itemDoubleClicked, this, &FunctionsWidget::insertClicked); this->groupChanged(0); } /*! * shows all functions of the selected group and selects the first one in the QStringList */ void FunctionsWidget::groupChanged(int index) { static const QStringList& functions = m_expressionParser->functions(); static const QStringList& names = m_expressionParser->functionsNames(); static const QVector& indices = m_expressionParser->functionsGroupIndices(); ui.lwFunctions->clear(); for (int i=0; iaddItem( names.at(i) + " (" + functions.at(i) + ')' ); } ui.lwFunctions->setCurrentRow(0); } void FunctionsWidget::filterChanged(const QString& filter) { if ( !filter.isEmpty() ) { ui.cbGroup->setEnabled(false); static const QStringList& names = m_expressionParser->functionsNames(); static const QStringList& functions = m_expressionParser->functions(); ui.lwFunctions->clear(); for (int i=0; iaddItem( names.at(i) + " (" + functions.at(i) + ')' ); } if (ui.lwFunctions->count()) { ui.lwFunctions->setCurrentRow(0); ui.bInsert->setEnabled(true); } else { ui.bInsert->setEnabled(false); } } else { ui.cbGroup->setEnabled(true); groupChanged(ui.cbGroup->currentIndex()); } } void FunctionsWidget::insertClicked() { static const QStringList& functions = m_expressionParser->functions(); static const QStringList& names = m_expressionParser->functionsNames(); //determine the currently selected constant const QString& text = ui.lwFunctions->currentItem()->text(); const QString& name = text.left( text.indexOf(" (") ); int index = names.indexOf(name); emit functionSelected(functions.at(index)); } diff --git a/src/kdefrontend/widgets/ThemesComboBox.cpp b/src/kdefrontend/widgets/ThemesComboBox.cpp index 73d6247e3..f2fabe394 100644 --- a/src/kdefrontend/widgets/ThemesComboBox.cpp +++ b/src/kdefrontend/widgets/ThemesComboBox.cpp @@ -1,91 +1,91 @@ /*************************************************************************** File : ThemesComboBox.cpp Project : LabPlot Description : Preview of all themes in a QComboBox -------------------------------------------------------------------- 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 * * * ***************************************************************************/ #include "kdefrontend/widgets/ThemesComboBox.h" #include "kdefrontend/widgets/ThemesWidget.h" #include #include #include /*! \class ThemesComboBox \brief Preview of all themes in a QComboBox. \ingroup backend/widgets */ ThemesComboBox::ThemesComboBox(QWidget* parent) : QComboBox(parent) { QVBoxLayout* layout = new QVBoxLayout; m_view = new ThemesWidget(this); m_groupBox = new QGroupBox; layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(m_view); m_groupBox->setLayout(layout); m_groupBox->setParent(parent, Qt::Popup); m_groupBox->hide(); m_groupBox->installEventFilter(this); addItem(""); setCurrentIndex(0); - connect(m_view, SIGNAL(themeSelected(QString)), this, SLOT(handleThemeChanged(QString))); + connect(m_view, &ThemesWidget::themeSelected, this, &ThemesComboBox::handleThemeChanged); } void ThemesComboBox::showPopup() { m_groupBox->show(); m_groupBox->resize(this->width(), 250); m_groupBox->move(mapToGlobal( this->rect().topLeft() )); } void ThemesComboBox::hidePopup() { m_groupBox->hide(); } /*! catches the MouseButtonPress-event and hides the tree view on mouse clicking. */ bool ThemesComboBox::eventFilter(QObject* object, QEvent* event) { if ( (object == m_groupBox) && event->type() == QEvent::MouseButtonPress ) { m_groupBox->hide(); this->setFocus(); return true; } return false; } void ThemesComboBox::handleThemeChanged(const QString& theme) { if (theme!=currentText()) { setItemText(0, theme); emit currentThemeChanged(theme); } m_groupBox->hide(); } diff --git a/src/kdefrontend/widgets/ThemesWidget.cpp b/src/kdefrontend/widgets/ThemesWidget.cpp index e997eebbf..1214facee 100644 --- a/src/kdefrontend/widgets/ThemesWidget.cpp +++ b/src/kdefrontend/widgets/ThemesWidget.cpp @@ -1,137 +1,137 @@ /*************************************************************************** File : ThemesWidget.cpp Project : LabPlot Description : widget for selecting themes -------------------------------------------------------------------- Copyright : (C) 2016 Prakriti Bhardwaj (p_bhardwaj14@informatik.uni-kl.de) Copyright : (C) 2016 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 "ThemesWidget.h" #include "kdefrontend/ThemeHandler.h" #include #include #include #include #include #include #include #include #include #include // #include /*! \class ThemesWidget \brief Widget for showing theme previews and for selecting a theme. \ingroup kdefrontend */ ThemesWidget::ThemesWidget(QWidget* parent) : QListView(parent) { setSelectionMode(QAbstractItemView::SingleSelection); setWordWrap(true); setViewMode(QListWidget::IconMode); setResizeMode(QListWidget::Adjust); //make the icon 3x3cm big and show two of them in the height int size = 3.0/2.54 * QApplication::desktop()->physicalDpiX(); setIconSize(QSize(size, size)); setMinimumSize(1.1*size, 2.1*size); //add some offset here to take care of potential scrollbars, etc. (not very precise...) setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //show preview pixmaps QStandardItemModel* mContentItemModel = new QStandardItemModel(this); QStringList themeList = ThemeHandler::themes(); QStringList themeImgPathList = QStandardPaths::locateAll(QStandardPaths::DataLocation, "themes/screenshots/", QStandardPaths::LocateDirectory); if (themeImgPathList.isEmpty()) return; const QString& themeImgPath = themeImgPathList.first(); QString tempPath; for (int i = 0; i < themeList.size(); ++i) { QStandardItem* listItem = new QStandardItem(); tempPath = themeImgPath + themeList.at(i) + ".png"; if (!QFile::exists(tempPath)) tempPath = themeImgPath + "Unavailable.png"; listItem->setIcon(QIcon(QPixmap(tempPath))); listItem->setText(themeList.at(i)); listItem->setData(themeList.at(i), Qt::UserRole); mContentItemModel->appendRow(listItem); } //create and add the icon for "None" QPixmap pm(size, size); QPen pen(Qt::SolidPattern, 1); const QColor& color = (palette().color(QPalette::Base).lightness() < 128) ? Qt::white : Qt::black; pen.setColor(color); QPainter pa; pa.setPen(pen); pm.fill(Qt::transparent); pa.begin(&pm); pa.setPen(pen); pa.drawRect(5, 5, size-10, size-10); pa.end(); QStandardItem* listItem = new QStandardItem(); listItem->setIcon(pm); listItem->setText(i18n("None")); listItem->setData("", Qt::UserRole); mContentItemModel->appendRow(listItem); //adding download themes option //TODO: activate this later // QStandardItem* listItem = new QStandardItem(); // listItem->setIcon(QIcon::fromTheme("get-hot-new-stuff")); // listItem->setText("Download Themes"); // listItem->setData("file_download_theme", Qt::UserRole); // mContentItemModel->appendRow(listItem); setModel(mContentItemModel); //SLOTS - connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(applyClicked(QModelIndex))); + connect(this, &ThemesWidget::clicked, this, &ThemesWidget::applyClicked); } void ThemesWidget::applyClicked(const QModelIndex& index) { QString themeName = index.data(Qt::UserRole).toString(); //TODO: activate this later // if(themeName=="file_download_theme") // this->downloadThemes(); // else emit themeSelected(themeName); } //TODO: activate this later // void ThemesWidget::downloadThemes() { // KNS3::DownloadDialog dialog("labplot2_themes.knsrc", this); // dialog.exec(); // foreach (const KNS3::Entry& e, dialog.changedEntries()) { // kDebug() << "Changed Entry: " << e.name(); // } // } diff --git a/src/kdefrontend/worksheet/DynamicPresenterWidget.cpp b/src/kdefrontend/worksheet/DynamicPresenterWidget.cpp index 491b7314a..30acc2f42 100644 --- a/src/kdefrontend/worksheet/DynamicPresenterWidget.cpp +++ b/src/kdefrontend/worksheet/DynamicPresenterWidget.cpp @@ -1,119 +1,119 @@ /*************************************************************************** File : DynamicPresenterWidget.cpp Project : LabPlot Description : Widget for dynamic presenting of worksheets -------------------------------------------------------------------- Copyright : (C) 2016 by Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "DynamicPresenterWidget.h" #include "commonfrontend/worksheet/WorksheetView.h" #include "SlidingPanel.h" #include #include #include #include #include DynamicPresenterWidget::DynamicPresenterWidget(Worksheet *worksheet, QWidget *parent) : QWidget(parent), m_view(new WorksheetView(worksheet)), m_timeLine(new QTimeLine(600)) { setAttribute(Qt::WA_DeleteOnClose); setFocus(); m_view->setParent(this); m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_view->fitInView(m_view->sceneRect(), Qt::KeepAspectRatio); m_view->adjustSize(); QDesktopWidget* const dw = QApplication::desktop(); const int primaryScreenIdx = dw->primaryScreen(); const QRect& screenSize = dw->availableGeometry(primaryScreenIdx); const int moveRight = (screenSize.width() - m_view->width()) / 2.0; const int moveDown = (screenSize.height() - m_view->height()) / 2.0; m_view->move(moveRight, moveDown); m_view->show(); m_panel = new SlidingPanel(this, worksheet->name()); qApp->installEventFilter(this); - connect(m_timeLine, SIGNAL(valueChanged(qreal)), m_panel, SLOT(movePanel(qreal))); - connect(m_panel->quitButton(), SIGNAL(clicked(bool)), this, SLOT(close())); + connect(m_timeLine, &QTimeLine::valueChanged, m_panel, &SlidingPanel::movePanel); + connect(m_panel->quitButton(), &QPushButton::clicked, this, &DynamicPresenterWidget::close); grabMouse(); slideUp(); } DynamicPresenterWidget::~DynamicPresenterWidget() { delete m_timeLine; delete m_view; } bool DynamicPresenterWidget::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::MouseMove) { for (const auto* const ob : m_panel->children()) { if (watched == ob) return false; } if (qobject_cast(watched) == m_panel) return false; if (!m_panel->shouldHide()) slideDown(); else if (m_panel->y() == 0) { if (m_panel->shouldHide()) slideUp(); } } return false; } void DynamicPresenterWidget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) close(); } void DynamicPresenterWidget::slideDown() { m_timeLine->setDirection(QTimeLine::Forward); startTimeline(); } void DynamicPresenterWidget::slideUp() { m_timeLine->setDirection(QTimeLine::Backward); startTimeline(); } void DynamicPresenterWidget::startTimeline() { if (m_timeLine->state() != QTimeLine::Running) m_timeLine->start(); } void DynamicPresenterWidget::focusOutEvent(QFocusEvent *e) { if (m_view->hasFocus()) setFocus(); if (e->reason() & Qt::BacktabFocusReason) close(); } diff --git a/src/kdefrontend/worksheet/GridDialog.cpp b/src/kdefrontend/worksheet/GridDialog.cpp index d87877d6f..51ec21f49 100644 --- a/src/kdefrontend/worksheet/GridDialog.cpp +++ b/src/kdefrontend/worksheet/GridDialog.cpp @@ -1,113 +1,113 @@ /*************************************************************************** File : GridDialog.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2011 by Alexander Semke Email (use @ for *) : alexander.semke*web.de Description : dialog for editing the grid properties for the worksheet view ***************************************************************************/ /*************************************************************************** * * * 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 "GridDialog.h" #include #include #include #include #include #include /** * @brief Provides a dialog for editing the grid properties for the worksheet view * \ingroup kdefrontend */ GridDialog::GridDialog(QWidget* parent) : KDialog(parent){ setCaption(i18n("Custom grid")); QFrame* widget = new QFrame( this ); widget->setFrameShape(QFrame::Box); widget->setFrameShadow(QFrame::Raised); QGridLayout* layout = new QGridLayout(widget); QLabel* label = new QLabel(i18n("Style"), widget); layout->addWidget(label, 0, 0); cbStyle = new QComboBox(this); cbStyle->addItem(i18n("lines")); cbStyle->addItem(i18n("dots")); cbStyle->setCurrentIndex(0); layout->addWidget(cbStyle, 0, 1); label = new QLabel(i18n("Horizontal spacing"), widget); layout->addWidget(label, 1, 0); sbHorizontalSpacing = new QSpinBox(widget); sbHorizontalSpacing->setRange(1,100); sbHorizontalSpacing->setValue(10); layout->addWidget(sbHorizontalSpacing, 1, 1); label = new QLabel(i18n("Vertical spacing"), widget); layout->addWidget(label, 2, 0); sbVerticalSpacing = new QSpinBox(widget); sbVerticalSpacing->setRange(1,100); sbVerticalSpacing->setValue(10); layout->addWidget(sbVerticalSpacing, 2, 1); label = new QLabel(i18n("Color"), widget); layout->addWidget(label, 3, 0); kcbColor = new KColorButton(widget); kcbColor->setColor(Qt::gray); layout->addWidget(kcbColor , 3, 1); label = new QLabel(i18n("Opacity"), widget); layout->addWidget(label, 4, 0); sbOpacity = new QSpinBox(widget); sbOpacity->setRange(1,100); sbOpacity->setValue(100); layout->addWidget(sbOpacity, 4, 1); label = new QLabel("%", widget); layout->addWidget(label, 4, 2); setMainWidget( widget ); setButtons( KDialog::Ok | KDialog::Cancel); - connect(this,SIGNAL(applyClicked()),SLOT(apply())); - connect(this,SIGNAL(okClicked()),SLOT(apply())); + connect(this, SIGNAL(applyClicked()) ,SLOT(apply())); + connect(this, SIGNAL(okClicked()),SLOT(apply())); } void GridDialog::save(WorksheetView::GridSettings& settings){ if (cbStyle->currentIndex() == 0) settings.style = WorksheetView::LineGrid; else settings.style = WorksheetView::DotGrid; settings.horizontalSpacing = sbHorizontalSpacing->value(); settings.verticalSpacing = sbVerticalSpacing->value(); settings.color = kcbColor->color(); settings.opacity = (float)sbOpacity->value()/100; } diff --git a/src/kdefrontend/worksheet/PresenterWidget.cpp b/src/kdefrontend/worksheet/PresenterWidget.cpp index 887585718..8a4ac399d 100644 --- a/src/kdefrontend/worksheet/PresenterWidget.cpp +++ b/src/kdefrontend/worksheet/PresenterWidget.cpp @@ -1,108 +1,108 @@ /*************************************************************************** File : PresenterWidget.cpp Project : LabPlot Description : Widget for static presenting of worksheets -------------------------------------------------------------------- Copyright : (C) 2016 by Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "PresenterWidget.h" #include "SlidingPanel.h" #include #include #include #include #include #include #include PresenterWidget::PresenterWidget(const QPixmap &pixmap, const QString& worksheetName, QWidget *parent) : QWidget(parent), m_imageLabel(new QLabel(this)), m_timeLine(new QTimeLine(600)) { setAttribute(Qt::WA_DeleteOnClose); m_imageLabel->setPixmap(pixmap); m_imageLabel->adjustSize(); QDesktopWidget* const dw = QApplication::desktop(); const int primaryScreenIdx = dw->primaryScreen(); const QRect& screenSize = dw->availableGeometry(primaryScreenIdx); const int moveRight = (screenSize.width() - m_imageLabel->width()) / 2.0; const int moveDown = (screenSize.height() - m_imageLabel->height()) / 2.0; m_imageLabel->move(moveRight, moveDown); m_panel = new SlidingPanel(this, worksheetName); qApp->installEventFilter(this); - connect(m_timeLine, SIGNAL(valueChanged(qreal)), m_panel, SLOT(movePanel(qreal))); - connect(m_panel->quitButton(), SIGNAL(clicked(bool)), this, SLOT(close())); + connect(m_timeLine, &QTimeLine::valueChanged, m_panel, &SlidingPanel::movePanel); + connect(m_panel->quitButton(), &QPushButton::clicked, this, &PresenterWidget::close); slideUp(); setFocus(); } PresenterWidget::~PresenterWidget() { delete m_imageLabel; delete m_timeLine; } bool PresenterWidget::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::MouseMove) { for (const auto* ob : m_panel->children()) { if (watched == ob) return false; } if (qobject_cast(watched) == m_panel) return false; if (!m_panel->shouldHide()) slideDown(); else if (m_panel->y() == 0) { if (m_panel->shouldHide()) slideUp(); } } return false; } void PresenterWidget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) close(); } void PresenterWidget::focusOutEvent(QFocusEvent*) { close(); } void PresenterWidget::slideDown() { m_timeLine->setDirection(QTimeLine::Forward); startTimeline(); } void PresenterWidget::slideUp() { m_timeLine->setDirection(QTimeLine::Backward); startTimeline(); } void PresenterWidget::startTimeline() { if (m_timeLine->state() != QTimeLine::Running) m_timeLine->start(); }