diff --git a/src/backend/datasources/projects/OriginProjectParser.cpp b/src/backend/datasources/projects/OriginProjectParser.cpp index 395f59c7f..cd8130762 100644 --- a/src/backend/datasources/projects/OriginProjectParser.cpp +++ b/src/backend/datasources/projects/OriginProjectParser.cpp @@ -1,2143 +1,2143 @@ /*************************************************************************** File : OriginProjectParser.h Project : LabPlot Description : parser for Origin projects -------------------------------------------------------------------- Copyright : (C) 2017-2018 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017-2019 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/datasources/projects/OriginProjectParser.h" #include "backend/core/column/Column.h" #include "backend/core/datatypes/Double2StringFilter.h" #include "backend/core/datatypes/DateTime2StringFilter.h" #include "backend/core/Project.h" #include "backend/core/Workbook.h" #include "backend/matrix/Matrix.h" #include "backend/note/Note.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/plots/cartesian/CartesianPlotLegend.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/plots/cartesian/XYEquationCurve.h" #include "backend/worksheet/TextLabel.h" #include #include #include #include #include #include /*! \class OriginProjectParser \brief parser for Origin projects. \ingroup datasources */ OriginProjectParser::OriginProjectParser() : ProjectParser() { m_topLevelClasses = {AspectType::Folder, AspectType::Workbook, AspectType::Spreadsheet, AspectType::Matrix, AspectType::Worksheet, AspectType::Note}; } bool OriginProjectParser::isOriginProject(const QString& fileName) { //TODO add opju later when liborigin supports it return fileName.endsWith(QLatin1String(".opj"), Qt::CaseInsensitive); } void OriginProjectParser::setImportUnusedObjects(bool importUnusedObjects) { m_importUnusedObjects = importUnusedObjects; } bool OriginProjectParser::hasUnusedObjects() { m_originFile = new OriginFile((const char*)m_projectFileName.toLocal8Bit()); if (!m_originFile->parse()) { delete m_originFile; m_originFile = nullptr; return false; } for (unsigned int i = 0; i < m_originFile->spreadCount(); i++) { const Origin::SpreadSheet& spread = m_originFile->spread(i); if (spread.objectID < 0) return true; } for (unsigned int i = 0; i < m_originFile->excelCount(); i++) { const Origin::Excel& excel = m_originFile->excel(i); if (excel.objectID < 0) return true; } for (unsigned int i = 0; i < m_originFile->matrixCount(); i++) { const Origin::Matrix& originMatrix = m_originFile->matrix(i); if (originMatrix.objectID < 0) return true; } delete m_originFile; m_originFile = nullptr; return false; } QString OriginProjectParser::supportedExtensions() { //TODO add opju later when liborigin supports it static const QString extensions = "*.opj *.OPJ"; return extensions; } unsigned int OriginProjectParser::findSpreadByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->spreadCount(); i++) { const Origin::SpreadSheet& spread = m_originFile->spread(i); if (spread.name == name.toStdString()) { m_spreadNameList << name; return i; } } return 0; } unsigned int OriginProjectParser::findMatrixByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->matrixCount(); i++) { const Origin::Matrix& originMatrix = m_originFile->matrix(i); if (originMatrix.name == name.toStdString()) { m_matrixNameList << name; return i; } } return 0; } unsigned int OriginProjectParser::findExcelByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->excelCount(); i++) { const Origin::Excel& excel = m_originFile->excel(i); if (excel.name == name.toStdString()) { m_excelNameList << name; return i; } } return 0; } unsigned int OriginProjectParser::findGraphByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->graphCount(); i++) { const Origin::Graph& graph = m_originFile->graph(i); if (graph.name == name.toStdString()) { m_graphNameList << name; return i; } } return 0; } unsigned int OriginProjectParser::findNoteByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->noteCount(); i++) { const Origin::Note& originNote = m_originFile->note(i); if (originNote.name == name.toStdString()) { m_noteNameList << name; return i; } } return 0; } //############################################################################## //############## Deserialization from Origin's project tree #################### //############################################################################## bool OriginProjectParser::load(Project* project, bool preview) { DEBUG("OriginProjectParser::load()"); //read and parse the m_originFile-file m_originFile = new OriginFile((const char*)m_projectFileName.toLocal8Bit()); if (!m_originFile->parse()) { delete m_originFile; m_originFile = nullptr; return false; } //Origin project tree and the iterator pointing to the root node const tree* projectTree = m_originFile->project(); tree::iterator projectIt = projectTree->begin(projectTree->begin()); m_spreadNameList.clear(); m_excelNameList.clear(); m_matrixNameList.clear(); m_graphNameList.clear(); m_noteNameList.clear(); //convert the project tree from liborigin's representation to LabPlot's project object project->setIsLoading(true); if (projectIt.node) { // only opj files from version >= 6.0 do have project tree DEBUG(" have a project tree"); QString name(QString::fromLatin1(projectIt->name.c_str())); project->setName(name); project->setCreationTime(creationTime(projectIt)); loadFolder(project, projectIt, preview); } else { // for lower versions put all windows on rootfolder DEBUG(" have no project tree"); int pos = m_projectFileName.lastIndexOf(QDir::separator()) + 1; project->setName((const char*)m_projectFileName.mid(pos).toLocal8Bit()); } // imports all loose windows (like prior version 6 which has no project tree) handleLooseWindows(project, preview); //restore column pointers: //1. extend the pathes to contain the parent structures first //2. restore the pointers from the pathes const QVector columns = project->children(AbstractAspect::Recursive); const QVector spreadsheets = project->children(AbstractAspect::Recursive); for (auto* curve : project->children(AbstractAspect::Recursive)) { curve->suppressRetransform(true); //x-column QString spreadsheetName = curve->xColumnPath().left(curve->xColumnPath().indexOf(QLatin1Char('/'))); for (const auto* spreadsheet : spreadsheets) { if (spreadsheet->name() == spreadsheetName) { const QString& newPath = spreadsheet->parentAspect()->path() + '/' + curve->xColumnPath(); curve->setXColumnPath(newPath); for (auto* column : columns) { if (!column) continue; if (column->path() == newPath) { curve->setXColumn(column); break; } } break; } } //x-column spreadsheetName = curve->yColumnPath().left(curve->yColumnPath().indexOf(QLatin1Char('/'))); for (const auto* spreadsheet : spreadsheets) { if (spreadsheet->name() == spreadsheetName) { const QString& newPath = spreadsheet->parentAspect()->path() + '/' + curve->yColumnPath(); curve->setYColumnPath(newPath); for (auto* column : columns) { if (!column) continue; if (column->path() == newPath) { curve->setYColumn(column); break; } } break; } } //TODO: error columns curve->suppressRetransform(false); } if (!preview) { for (auto* plot : project->children(AbstractAspect::Recursive)) { plot->setIsLoading(false); plot->retransform(); } } emit project->loaded(); project->setIsLoading(false); delete m_originFile; m_originFile = nullptr; return true; } bool OriginProjectParser::loadFolder(Folder* folder, tree::iterator baseIt, bool preview) { DEBUG("OriginProjectParser::loadFolder()") const tree* projectTree = m_originFile->project(); // do not skip anything if pathesToLoad() contains only root folder bool containsRootFolder = (folder->pathesToLoad().size() == 1 && folder->pathesToLoad().contains(folder->path())); if (containsRootFolder) { DEBUG(" pathesToLoad contains only folder path \"" << STDSTRING(folder->path()) << "\". Clearing pathes to load.") folder->setPathesToLoad(QStringList()); } //load folder's children: logic for reading the selected objects only is similar to Folder::readChildAspectElement for (tree::sibling_iterator it = projectTree->begin(baseIt); it != projectTree->end(baseIt); ++it) { QString name(QString::fromLatin1(it->name.c_str())); //name of the current child DEBUG(" * folder item name = " << STDSTRING(name)) //check whether we need to skip the loading of the current child if (!folder->pathesToLoad().isEmpty()) { //child's path is not available yet (child not added yet) -> construct the path manually const QString childPath = folder->path() + '/' + name; DEBUG(" path = " << STDSTRING(childPath)) //skip the current child aspect it is not in the list of aspects to be loaded if (folder->pathesToLoad().indexOf(childPath) == -1) { DEBUG(" skip it!") continue; } } //load top-level children AbstractAspect* aspect = nullptr; switch (it->type) { case Origin::ProjectNode::Folder: { DEBUG(" top level folder"); Folder* f = new Folder(name); if (!folder->pathesToLoad().isEmpty()) { //a child folder to be read -> provide the list of aspects to be loaded to the child folder, too. //since the child folder and all its children are not added yet (path() returns empty string), //we need to remove the path of the current child folder from the full pathes provided in pathesToLoad. //E.g. we want to import the path "Project/Folder/Spreadsheet" in the following project // Project // \Spreadsheet // \Folder // \Spreadsheet // //Here, we remove the part "Project/Folder/" and proceed for this child folder with "Spreadsheet" only. //With this the logic above where it is determined whether to import the child aspect or not works out. //manually construct the path of the child folder to be read const QString& curFolderPath = folder->path() + '/' + name; //remove the path of the current child folder QStringList pathesToLoadNew; for (const auto& path : folder->pathesToLoad()) { if (path.startsWith(curFolderPath)) pathesToLoadNew << path.right(path.length() - curFolderPath.length()); } f->setPathesToLoad(pathesToLoadNew); } loadFolder(f, it, preview); aspect = f; break; } case Origin::ProjectNode::SpreadSheet: { DEBUG(" top level spreadsheet"); Spreadsheet* spreadsheet = new Spreadsheet(name); loadSpreadsheet(spreadsheet, preview, name); aspect = spreadsheet; break; } case Origin::ProjectNode::Graph: { DEBUG(" top level graph"); Worksheet* worksheet = new Worksheet(name); worksheet->setIsLoading(true); worksheet->setTheme(QString()); loadWorksheet(worksheet, preview); aspect = worksheet; break; } case Origin::ProjectNode::Matrix: { DEBUG(" top level matrix"); const Origin::Matrix& originMatrix = m_originFile->matrix(findMatrixByName(name)); DEBUG(" matrix name = " << originMatrix.name); DEBUG(" number of sheets = " << originMatrix.sheets.size()); if (originMatrix.sheets.size() == 1) { // single sheet -> load into a matrix Matrix* matrix = new Matrix(name); loadMatrix(matrix, preview); aspect = matrix; } else { // multiple sheets -> load into a workbook Workbook* workbook = new Workbook(name); loadMatrixWorkbook(workbook, preview); aspect = workbook; } break; } case Origin::ProjectNode::Excel: { DEBUG(" top level excel"); Workbook* workbook = new Workbook(name); loadWorkbook(workbook, preview); aspect = workbook; break; } case Origin::ProjectNode::Note: { DEBUG("top level note"); Note* note = new Note(name); loadNote(note, preview); aspect = note; break; } case Origin::ProjectNode::Graph3D: default: //TODO: add UnsupportedAspect break; } if (aspect) { folder->addChildFast(aspect); aspect->setCreationTime(creationTime(it)); aspect->setIsLoading(false); } } // ResultsLog QString resultsLog = QString::fromLatin1(m_originFile->resultsLogString().c_str()); if (resultsLog.length() > 0) { DEBUG("Results log:\t\tyes"); Note* note = new Note("ResultsLog"); if (preview) folder->addChildFast(note); else { //only import the log if it is in the list of aspects to be loaded const QString childPath = folder->path() + '/' + note->name(); if (folder->pathesToLoad().indexOf(childPath) != -1) { note->setText(resultsLog); folder->addChildFast(note); } } } else DEBUG("Results log:\t\tno"); return folder; } void OriginProjectParser::handleLooseWindows(Folder* folder, bool preview) { DEBUG("OriginProjectParser::handleLooseWindows()"); QDEBUG("pathes to load:" << folder->pathesToLoad()); m_spreadNameList.removeDuplicates(); m_excelNameList.removeDuplicates(); m_matrixNameList.removeDuplicates(); m_graphNameList.removeDuplicates(); m_noteNameList.removeDuplicates(); QDEBUG(" spreads =" << m_spreadNameList); QDEBUG(" excels =" << m_excelNameList); QDEBUG(" matrices =" << m_matrixNameList); QDEBUG(" graphs =" << m_graphNameList); QDEBUG(" notes =" << m_noteNameList); DEBUG("Number of spreads loaded:\t" << m_spreadNameList.size() << ", in file: " << m_originFile->spreadCount()); DEBUG("Number of excels loaded:\t" << m_excelNameList.size() << ", in file: " << m_originFile->excelCount()); DEBUG("Number of matrices loaded:\t" << m_matrixNameList.size() << ", in file: " << m_originFile->matrixCount()); DEBUG("Number of graphs loaded:\t" << m_graphNameList.size() << ", in file: " << m_originFile->graphCount()); DEBUG("Number of notes loaded:\t\t" << m_noteNameList.size() << ", in file: " << m_originFile->noteCount()); // loop over all spreads to find loose ones for (unsigned int i = 0; i < m_originFile->spreadCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::SpreadSheet& spread = m_originFile->spread(i); QString name = QString::fromStdString(spread.name); DEBUG(" spread.objectId = " << spread.objectID); // skip unused spreads if selected if (spread.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose spread: " << STDSTRING(name)); continue; } const QString childPath = folder->path() + '/' + name; // we could also use spread.loose if (!m_spreadNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) { DEBUG(" Adding loose spread: " << STDSTRING(name)); Spreadsheet* spreadsheet = new Spreadsheet(name); loadSpreadsheet(spreadsheet, preview, name); aspect = spreadsheet; } if (aspect) { folder->addChildFast(aspect); DEBUG(" creation time as reported by liborigin: " << spread.creationDate); aspect->setCreationTime(QDateTime::fromTime_t(spread.creationDate)); } } // loop over all excels to find loose ones for (unsigned int i = 0; i < m_originFile->excelCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::Excel& excel = m_originFile->excel(i); QString name = QString::fromStdString(excel.name); DEBUG(" excel.objectId = " << excel.objectID); // skip unused data sets if selected if (excel.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose excel: " << STDSTRING(name)); continue; } const QString childPath = folder->path() + '/' + name; // we could also use excel.loose if (!m_excelNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) { DEBUG(" Adding loose excel: " << STDSTRING(name)); DEBUG(" containing number of sheets = " << excel.sheets.size()); Workbook* workbook = new Workbook(name); loadWorkbook(workbook, preview); aspect = workbook; } if (aspect) { folder->addChildFast(aspect); DEBUG(" creation time as reported by liborigin: " << excel.creationDate); aspect->setCreationTime(QDateTime::fromTime_t(excel.creationDate)); } } // loop over all matrices to find loose ones for (unsigned int i = 0; i < m_originFile->matrixCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::Matrix& originMatrix = m_originFile->matrix(i); QString name = QString::fromStdString(originMatrix.name); DEBUG(" originMatrix.objectId = " << originMatrix.objectID); // skip unused data sets if selected if (originMatrix.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose matrix: " << STDSTRING(name)); continue; } const QString childPath = folder->path() + '/' + name; if (!m_matrixNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) { DEBUG(" Adding loose matrix: " << STDSTRING(name)); DEBUG(" containing number of sheets = " << originMatrix.sheets.size()); if (originMatrix.sheets.size() == 1) { // single sheet -> load into a matrix Matrix* matrix = new Matrix(name); loadMatrix(matrix, preview); aspect = matrix; } else { // multiple sheets -> load into a workbook Workbook* workbook = new Workbook(name); loadMatrixWorkbook(workbook, preview); aspect = workbook; } } if (aspect) { folder->addChildFast(aspect); aspect->setCreationTime(QDateTime::fromTime_t(originMatrix.creationDate)); } } // handle loose graphs (is this even possible?) for (unsigned int i = 0; i < m_originFile->graphCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::Graph& graph = m_originFile->graph(i); QString name = QString::fromStdString(graph.name); DEBUG(" graph.objectId = " << graph.objectID); // skip unused graph if selected if (graph.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose graph: " << STDSTRING(name)); continue; } const QString childPath = folder->path() + '/' + name; if (!m_graphNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) { DEBUG(" Adding loose graph: " << STDSTRING(name)); Worksheet* worksheet = new Worksheet(name); loadWorksheet(worksheet, preview); aspect = worksheet; } if (aspect) { folder->addChildFast(aspect); aspect->setCreationTime(QDateTime::fromTime_t(graph.creationDate)); } } // handle loose notes (is this even possible?) for (unsigned int i = 0; i < m_originFile->noteCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::Note& originNote = m_originFile->note(i); QString name = QString::fromStdString(originNote.name); DEBUG(" originNote.objectId = " << originNote.objectID); // skip unused notes if selected if (originNote.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose note: " << STDSTRING(name)); continue; } const QString childPath = folder->path() + '/' + name; if (!m_noteNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) { DEBUG(" Adding loose note: " << STDSTRING(name)); Note* note = new Note(name); loadNote(note, preview); aspect = note; } if (aspect) { folder->addChildFast(aspect); aspect->setCreationTime(QDateTime::fromTime_t(originNote.creationDate)); } } } bool OriginProjectParser::loadWorkbook(Workbook* workbook, bool preview) { DEBUG("loadWorkbook()"); //load workbook sheets const Origin::Excel& excel = m_originFile->excel(findExcelByName(workbook->name())); DEBUG(" excel name = " << excel.name); DEBUG(" number of sheets = " << excel.sheets.size()); for (unsigned int s = 0; s < excel.sheets.size(); ++s) { Spreadsheet* spreadsheet = new Spreadsheet(QString::fromLatin1(excel.sheets[s].name.c_str())); loadSpreadsheet(spreadsheet, preview, workbook->name(), s); workbook->addChildFast(spreadsheet); } return true; } // load spreadsheet from spread (sheetIndex == -1) or from excel (only sheet sheetIndex) bool OriginProjectParser::loadSpreadsheet(Spreadsheet* spreadsheet, bool preview, const QString& name, int sheetIndex) { DEBUG("loadSpreadsheet() sheetIndex = " << sheetIndex); //load spreadsheet data Origin::SpreadSheet spread; Origin::Excel excel; if (sheetIndex == -1) // spread spread = m_originFile->spread(findSpreadByName(name)); else { excel = m_originFile->excel(findExcelByName(name)); spread = excel.sheets[sheetIndex]; } const size_t cols = spread.columns.size(); int rows = 0; for (size_t j = 0; j < cols; ++j) rows = std::max((int)spread.columns[j].data.size(), rows); // alternative: int rows = excel.maxRows; DEBUG("loadSpreadsheet() cols/maxRows = " << cols << "/" << rows); //TODO QLocale locale = mw->locale(); spreadsheet->setRowCount(rows); spreadsheet->setColumnCount((int)cols); if (sheetIndex == -1) spreadsheet->setComment(QString::fromLatin1(spread.label.c_str())); else spreadsheet->setComment(QString::fromLatin1(excel.label.c_str())); //in Origin column width is measured in characters, we need to convert to pixels //TODO: determine the font used in Origin in order to get the same column width as in Origin QFont font; QFontMetrics fm(font); const int scaling_factor = fm.maxWidth(); for (size_t j = 0; j < cols; ++j) { Origin::SpreadColumn column = spread.columns[j]; Column* col = spreadsheet->column((int)j); QString name(column.name.c_str()); col->setName(name.replace(QRegExp(".*_"), QString())); if (preview) continue; //TODO: we don't support any formulas for cells yet. // if (column.command.size() > 0) // col->setFormula(Interval(0, rows), QString(column.command.c_str())); col->setComment(QString::fromLatin1(column.comment.c_str())); col->setWidth((int)column.width * scaling_factor); //plot designation switch (column.type) { case Origin::SpreadColumn::X: col->setPlotDesignation(AbstractColumn::X); break; case Origin::SpreadColumn::Y: col->setPlotDesignation(AbstractColumn::Y); break; case Origin::SpreadColumn::Z: col->setPlotDesignation(AbstractColumn::Z); break; case Origin::SpreadColumn::XErr: col->setPlotDesignation(AbstractColumn::XError); break; case Origin::SpreadColumn::YErr: col->setPlotDesignation(AbstractColumn::YError); break; case Origin::SpreadColumn::Label: case Origin::SpreadColumn::NONE: default: col->setPlotDesignation(AbstractColumn::NoDesignation); } QString format; switch (column.valueType) { case Origin::Numeric: { for (unsigned int i = column.beginRow; i < column.endRow; ++i) { const double value = column.data.at(i).as_double(); if (value != _ONAN) col->setValueAt(i, value); } loadColumnNumericFormat(column, col); break; } case Origin::TextNumeric: { //A TextNumeric column can contain numeric and string values, there is no equivalent column mode in LabPlot. // -> Set the column mode as 'Numeric' or 'Text' depending on the type of first non-empty element in column. for (unsigned int i = column.beginRow; i < column.endRow; ++i) { const Origin::variant value(column.data.at(i)); if (value.type() == Origin::Variant::V_DOUBLE) { if (value.as_double() != _ONAN) break; } else { if (value.as_string() != nullptr) { col->setColumnMode(AbstractColumn::Text); break; } } } if (col->columnMode() == AbstractColumn::Numeric) { for (unsigned int i = column.beginRow; i < column.endRow; ++i) { const double value = column.data.at(i).as_double(); if (column.data.at(i).type() == Origin::Variant::V_DOUBLE && value != _ONAN) col->setValueAt(i, value); } loadColumnNumericFormat(column, col); } else { for (unsigned int i = column.beginRow; i < column.endRow; ++i) { const Origin::variant value(column.data.at(i)); if (value.type() == Origin::Variant::V_STRING) { if (value.as_string() != nullptr) col->setTextAt(i, value.as_string()); } else { if (value.as_double() != _ONAN) col->setTextAt(i, QString::number(value.as_double())); } } } break; } case Origin::Text: col->setColumnMode(AbstractColumn::Text); for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setTextAt(i, column.data[i].as_string()); break; case Origin::Time: { switch (column.valueTypeSpecification + 128) { case Origin::TIME_HH_MM: format = "hh:mm"; break; case Origin::TIME_HH: format = "hh"; break; case Origin::TIME_HH_MM_SS: format = "hh:mm:ss"; break; case Origin::TIME_HH_MM_SS_ZZ: format = "hh:mm:ss.zzz"; break; case Origin::TIME_HH_AP: format = "hh ap"; break; case Origin::TIME_HH_MM_AP: format = "hh:mm ap"; break; case Origin::TIME_MM_SS: format = "mm:ss"; break; case Origin::TIME_MM_SS_ZZ: format = "mm:ss.zzz"; break; case Origin::TIME_HHMM: format = "hhmm"; break; case Origin::TIME_HHMMSS: format = "hhmmss"; break; case Origin::TIME_HH_MM_SS_ZZZ: format = "hh:mm:ss.zzz"; break; } for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setValueAt(i, column.data[i].as_double()); col->setColumnMode(AbstractColumn::DateTime); DateTime2StringFilter *filter = static_cast(col->outputFilter()); filter->setFormat(format); break; } case Origin::Date: { switch (column.valueTypeSpecification) { case Origin::DATE_DD_MM_YYYY: format = "dd/MM/yyyy"; break; case Origin::DATE_DD_MM_YYYY_HH_MM: format = "dd/MM/yyyy HH:mm"; break; case Origin::DATE_DD_MM_YYYY_HH_MM_SS: format = "dd/MM/yyyy HH:mm:ss"; break; case Origin::DATE_DDMMYYYY: case Origin::DATE_DDMMYYYY_HH_MM: case Origin::DATE_DDMMYYYY_HH_MM_SS: format = "dd.MM.yyyy"; break; case Origin::DATE_MMM_D: format = "MMM d"; break; case Origin::DATE_M_D: format = "M/d"; break; case Origin::DATE_D: format = 'd'; break; case Origin::DATE_DDD: case Origin::DATE_DAY_LETTER: format = "ddd"; break; case Origin::DATE_YYYY: format = "yyyy"; break; case Origin::DATE_YY: format = "yy"; break; case Origin::DATE_YYMMDD: case Origin::DATE_YYMMDD_HH_MM: case Origin::DATE_YYMMDD_HH_MM_SS: case Origin::DATE_YYMMDD_HHMM: case Origin::DATE_YYMMDD_HHMMSS: format = "yyMMdd"; break; case Origin::DATE_MMM: case Origin::DATE_MONTH_LETTER: format = "MMM"; break; case Origin::DATE_M_D_YYYY: format = "M-d-yyyy"; break; default: format = "dd.MM.yyyy"; } for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setValueAt(i, column.data[i].as_double()); col->setColumnMode(AbstractColumn::DateTime); DateTime2StringFilter *filter = static_cast(col->outputFilter()); filter->setFormat(format); break; } case Origin::Month: { switch (column.valueTypeSpecification) { case Origin::MONTH_MMM: format = "MMM"; break; case Origin::MONTH_MMMM: format = "MMMM"; break; case Origin::MONTH_LETTER: format = 'M'; break; } for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setValueAt(i, column.data[i].as_double()); col->setColumnMode(AbstractColumn::Month); DateTime2StringFilter *filter = static_cast(col->outputFilter()); filter->setFormat(format); break; } case Origin::Day: { switch (column.valueTypeSpecification) { case Origin::DAY_DDD: format = "ddd"; break; case Origin::DAY_DDDD: format = "dddd"; break; case Origin::DAY_LETTER: format = 'd'; break; } for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setValueAt(i, column.data[i].as_double()); col->setColumnMode(AbstractColumn::Day); DateTime2StringFilter *filter = static_cast(col->outputFilter()); filter->setFormat(format); break; } case Origin::ColumnHeading: case Origin::TickIndexedDataset: case Origin::Categorical: break; } } //TODO: "hidden" not supported yet // if (spread.hidden || spread.loose) // mw->hideWindow(spreadsheet); return true; } void OriginProjectParser::loadColumnNumericFormat(const Origin::SpreadColumn& originColumn, Column* column) const { if (originColumn.numericDisplayType != 0) { int fi = 0; switch (originColumn.valueTypeSpecification) { case Origin::Decimal: fi = 1; break; case Origin::Scientific: fi = 2; break; case Origin::Engineering: case Origin::DecimalWithMarks: break; } Double2StringFilter* filter = static_cast(column->outputFilter()); filter->setNumericFormat(fi); filter->setNumDigits(originColumn.decimalPlaces); } } bool OriginProjectParser::loadMatrixWorkbook(Workbook* workbook, bool preview) { DEBUG("loadMatrixWorkbook()"); //load matrix workbook sheets const Origin::Matrix& originMatrix = m_originFile->matrix(findMatrixByName(workbook->name())); for (size_t s = 0; s < originMatrix.sheets.size(); ++s) { Matrix* matrix = new Matrix(QString::fromLatin1(originMatrix.sheets[s].name.c_str())); loadMatrix(matrix, preview, s, workbook->name()); workbook->addChildFast(matrix); } return true; } bool OriginProjectParser::loadMatrix(Matrix* matrix, bool preview, size_t sheetIndex, const QString& mwbName) { DEBUG("loadMatrix()"); //import matrix data const Origin::Matrix& originMatrix = m_originFile->matrix(findMatrixByName(mwbName)); if (preview) return true; //in Origin column width is measured in characters, we need to convert to pixels //TODO: determine the font used in Origin in order to get the same column width as in Origin QFont font; QFontMetrics fm(font); const int scaling_factor = fm.maxWidth(); const Origin::MatrixSheet& layer = originMatrix.sheets[sheetIndex]; const int colCount = layer.columnCount; const int rowCount = layer.rowCount; matrix->setRowCount(rowCount); matrix->setColumnCount(colCount); matrix->setFormula(layer.command.c_str()); //TODO: how to handle different widths for different columns? for (int j = 0; j < colCount; j++) matrix->setColumnWidth(j, layer.width * scaling_factor); //TODO: check column major vs. row major to improve the performance here for (int i = 0; i < rowCount; i++) { for (int j = 0; j < colCount; j++) matrix->setCell(i, j, layer.data[j + i*colCount]); } char format = 'g'; //TODO: prec not support by Matrix //int prec = 6; switch (layer.valueTypeSpecification) { case 0: //Decimal 1000 format = 'f'; // prec = layer.decimalPlaces; break; case 1: //Scientific format = 'e'; // prec = layer.decimalPlaces; break; case 2: //Engineering case 3: //Decimal 1,000 format = 'g'; // prec = layer.significantDigits; break; } matrix->setNumericFormat(format); return true; } bool OriginProjectParser::loadWorksheet(Worksheet* worksheet, bool preview) { DEBUG("OriginProjectParser::loadWorksheet()"); //load worksheet data const Origin::Graph& graph = m_originFile->graph(findGraphByName(worksheet->name())); DEBUG(" graph name = " << graph.name); worksheet->setComment(graph.label.c_str()); //TODO: width, height, view mode (print view, page view, window view, draft view) //Origin allows to freely resize the window and ajusts the size of the plot (layer) automatically //by keeping a certain width-to-height ratio. It's not clear what the actual size of the plot/layer is and how to handle this. //For now we simply create a new wokrsheet here with it's default size and make it using the whole view size. //Later we can decide to use one of the following properties: // 1) Window.frameRect gives Rect-corner coordinates (in pixels) of the Window object // 2) GraphLayer.clientRect gives Rect-corner coordinates (pixels) of the Layer inside the (printer?) page. // 3) Graph.width, Graph.height give the (printer?) page size in pixels. // const QRectF size(0, 0, // Worksheet::convertToSceneUnits(graph.width/600., Worksheet::Inch), // Worksheet::convertToSceneUnits(graph.height/600., Worksheet::Inch)); // worksheet->setPageRect(size); worksheet->setUseViewSize(true); QHash textLabelPositions; // worksheet background color const Origin::ColorGradientDirection bckgColorGradient = graph.windowBackgroundColorGradient; const Origin::Color bckgBaseColor = graph.windowBackgroundColorBase; const Origin::Color bckgEndColor = graph.windowBackgroundColorEnd; worksheet->setBackgroundColorStyle(backgroundColorStyle(bckgColorGradient)); switch (bckgColorGradient) { case Origin::ColorGradientDirection::NoGradient: case Origin::ColorGradientDirection::TopLeft: case Origin::ColorGradientDirection::Left: case Origin::ColorGradientDirection::BottomLeft: case Origin::ColorGradientDirection::Top: worksheet->setBackgroundFirstColor(color(bckgEndColor)); worksheet->setBackgroundSecondColor(color(bckgBaseColor)); break; case Origin::ColorGradientDirection::Center: break; case Origin::ColorGradientDirection::Bottom: case Origin::ColorGradientDirection::TopRight: case Origin::ColorGradientDirection::Right: case Origin::ColorGradientDirection::BottomRight: worksheet->setBackgroundFirstColor(color(bckgBaseColor)); worksheet->setBackgroundSecondColor(color(bckgEndColor)); } //TODO: do we need changes on the worksheet layout? //add plots int index = 1; for (const auto& layer : graph.layers) { if (!layer.is3D()) { CartesianPlot* plot = new CartesianPlot(i18n("Plot%1", QString::number(index))); worksheet->addChildFast(plot); plot->setIsLoading(true); //TODO: width, height //background color const Origin::Color& regColor = layer.backgroundColor; if (regColor.type == Origin::Color::None) plot->plotArea()->setBackgroundOpacity(0); else plot->plotArea()->setBackgroundFirstColor(color(regColor)); //border if (layer.borderType == Origin::BorderType::None) plot->plotArea()->setBorderPen(QPen(Qt::NoPen)); else plot->plotArea()->setBorderPen(QPen(Qt::SolidLine)); //ranges plot->setAutoScaleX(false); plot->setAutoScaleY(false); const Origin::GraphAxis& originXAxis = layer.xAxis; const Origin::GraphAxis& originYAxis = layer.yAxis; plot->setXMin(originXAxis.min); plot->setXMax(originXAxis.max); plot->setYMin(originYAxis.min); plot->setYMax(originYAxis.max); //scales switch (originXAxis.scale) { case Origin::GraphAxis::Linear: plot->setXScale(CartesianPlot::ScaleLinear); break; case Origin::GraphAxis::Log10: plot->setXScale(CartesianPlot::ScaleLog10); break; case Origin::GraphAxis::Ln: plot->setXScale(CartesianPlot::ScaleLn); break; case Origin::GraphAxis::Log2: plot->setXScale(CartesianPlot::ScaleLog2); break; case Origin::GraphAxis::Probability: case Origin::GraphAxis::Probit: case Origin::GraphAxis::Reciprocal: case Origin::GraphAxis::OffsetReciprocal: case Origin::GraphAxis::Logit: //TODO: plot->setXScale(CartesianPlot::ScaleLinear); break; } switch (originYAxis.scale) { case Origin::GraphAxis::Linear: plot->setYScale(CartesianPlot::ScaleLinear); break; case Origin::GraphAxis::Log10: plot->setYScale(CartesianPlot::ScaleLog10); break; case Origin::GraphAxis::Ln: plot->setYScale(CartesianPlot::ScaleLn); break; case Origin::GraphAxis::Log2: plot->setYScale(CartesianPlot::ScaleLog2); break; case Origin::GraphAxis::Probability: case Origin::GraphAxis::Probit: case Origin::GraphAxis::Reciprocal: case Origin::GraphAxis::OffsetReciprocal: case Origin::GraphAxis::Logit: //TODO: plot->setYScale(CartesianPlot::ScaleLinear); break; } //axes //x bottom if (layer.curves.size()) { Origin::GraphCurve originCurve = layer.curves[0]; QString xColumnName = QString::fromLatin1(originCurve.xColumnName.c_str()); //TODO: "Partikelgrö" DEBUG(" xColumnName = " << STDSTRING(xColumnName)); QDEBUG(" UTF8 xColumnName = " << xColumnName.toUtf8()); QString yColumnName = QString::fromLatin1(originCurve.yColumnName.c_str()); if (!originXAxis.formatAxis[0].hidden) { Axis* axis = new Axis("x", Axis::AxisHorizontal); axis->setSuppressRetransform(true); axis->setPosition(Axis::AxisBottom); plot->addChildFast(axis); loadAxis(originXAxis, axis, 0, xColumnName); axis->setSuppressRetransform(false); } //x top if (!originXAxis.formatAxis[1].hidden) { Axis* axis = new Axis("x top", Axis::AxisHorizontal); axis->setPosition(Axis::AxisTop); axis->setSuppressRetransform(true); plot->addChildFast(axis); loadAxis(originXAxis, axis, 1, xColumnName); axis->setSuppressRetransform(false); } //y left if (!originYAxis.formatAxis[0].hidden) { Axis* axis = new Axis("y", Axis::AxisVertical); axis->setSuppressRetransform(true); axis->setPosition(Axis::AxisLeft); plot->addChildFast(axis); loadAxis(originYAxis, axis, 0, yColumnName); axis->setSuppressRetransform(false); } //y right if (!originYAxis.formatAxis[1].hidden) { Axis* axis = new Axis("y right", Axis::AxisVertical); axis->setSuppressRetransform(true); axis->setPosition(Axis::AxisRight); plot->addChildFast(axis); loadAxis(originYAxis, axis, 1, yColumnName); axis->setSuppressRetransform(false); } } else { //TODO: ? } //range breaks //TODO //add legend if available const Origin::TextBox& originLegend = layer.legend; const QString& legendText = QString::fromLatin1(originLegend.text.c_str()); DEBUG(" legend text = " << STDSTRING(legendText)); if (!originLegend.text.empty()) { CartesianPlotLegend* legend = new CartesianPlotLegend(plot, i18n("legend")); //Origin's legend uses "\l(...)" or "\L(...)" string to format the legend symbol // and "%(...) to format the legend text for each curve //s. a. https://www.originlab.com/doc/Origin-Help/Legend-ManualControl //the text before these formatting tags, if available, is interpreted as the legend title QString legendTitle; //search for the first occurrence of the legend symbol substring int index = legendText.indexOf(QLatin1String("\\l("), 0, Qt::CaseInsensitive); if (index != -1) legendTitle = legendText.left(index); else { //check legend text index = legendText.indexOf(QLatin1String("%(")); if (index != -1) legendTitle = legendText.left(index); } legendTitle = legendTitle.trimmed(); if (!legendTitle.isEmpty()) legendTitle = parseOriginText(legendTitle); DEBUG(" legend title = " << STDSTRING(legendTitle)); legend->title()->setText(legendTitle); //TODO: text color //const Origin::Color& originColor = originLegend.color; //position //TODO: for the first release with OPJ support we put the legend to the bottom left corner, //in the next release we'll evaluate originLegend.clientRect giving the position inside of the whole page in Origin. //In Origin the legend can be placed outside of the plot which is not possible in LabPlot. //To achieve this we'll need to increase padding area in the plot and to place the legend outside of the plot area. CartesianPlotLegend::PositionWrapper position; position.horizontalPosition = CartesianPlotLegend::hPositionRight; position.verticalPosition = CartesianPlotLegend::vPositionBottom; legend->setPosition(position); //rotation legend->setRotationAngle(originLegend.rotation); //border line if (originLegend.borderType == Origin::BorderType::None) legend->setBorderPen(QPen(Qt::NoPen)); else legend->setBorderPen(QPen(Qt::SolidLine)); //background color, determine it with the help of the border type if (originLegend.borderType == Origin::BorderType::DarkMarble) legend->setBackgroundFirstColor(Qt::darkGray); else if (originLegend.borderType == Origin::BorderType::BlackOut) legend->setBackgroundFirstColor(Qt::black); else legend->setBackgroundFirstColor(Qt::white); plot->addLegend(legend); } //texts for (const auto& s : layer.texts) { DEBUG("EXTRA TEXT = " << s.text.c_str()); TextLabel* label = new TextLabel("text label"); label->setText(parseOriginText(QString::fromLatin1(s.text.c_str()))); plot->addChild(label); label->setParentGraphicsItem(plot->graphicsItem()); //position //determine the relative position inside of the layer rect const float horRatio = (float)(s.clientRect.left-layer.clientRect.left)/(layer.clientRect.right-layer.clientRect.left); const float vertRatio = (float)(s.clientRect.top-layer.clientRect.top)/(layer.clientRect.bottom-layer.clientRect.top); textLabelPositions[label] = QSizeF(horRatio, vertRatio); DEBUG("horizontal/vertical ratio = " << horRatio << ", " << vertRatio); //rotation label->setRotationAngle(s.rotation); //TODO: // Color color; // unsigned short fontSize; // int tab; // BorderType borderType; // Attach attach; } //curves int curveIndex = 1; for (const auto& originCurve : layer.curves) { QString data(originCurve.dataName.c_str()); switch (data[0].toLatin1()) { case 'T': case 'E': { if (originCurve.type == Origin::GraphCurve::Line || originCurve.type == Origin::GraphCurve::Scatter || originCurve.type == Origin::GraphCurve::LineSymbol || originCurve.type == Origin::GraphCurve::ErrorBar || originCurve.type == Origin::GraphCurve::XErrorBar) { // parse and use legend text // find substring between %c{curveIndex} and %c{curveIndex+1} int pos1 = legendText.indexOf(QString("\\c{%1}").arg(curveIndex)) + 5; int pos2 = legendText.indexOf(QString("\\c{%1}").arg(curveIndex+1)); QString curveText = legendText.mid(pos1, pos2 - pos1); // replace %(1), %(2), etc. with curve name curveText.replace(QString("%(%1)").arg(curveIndex), QString::fromLatin1(originCurve.yColumnName.c_str())); curveText = curveText.trimmed(); DEBUG(" curve " << curveIndex << " text = " << STDSTRING(curveText)); //XYCurve* xyCurve = new XYCurve(i18n("Curve%1", QString::number(curveIndex))); //TODO: curve (legend) does not support HTML text yet. //XYCurve* xyCurve = new XYCurve(curveText); XYCurve* curve = new XYCurve(QString::fromLatin1(originCurve.yColumnName.c_str())); const QString& tableName = data.right(data.length() - 2); curve->setXColumnPath(tableName + '/' + originCurve.xColumnName.c_str()); curve->setYColumnPath(tableName + '/' + originCurve.yColumnName.c_str()); curve->suppressRetransform(true); if (!preview) loadCurve(originCurve, curve); plot->addChildFast(curve); curve->suppressRetransform(false); } else if (originCurve.type == Origin::GraphCurve::Column) { //vertical bars } else if (originCurve.type == Origin::GraphCurve::Bar) { //horizontal bars } else if (originCurve.type == Origin::GraphCurve::Histogram) { } } break; case 'F': { Origin::Function function; const vector::difference_type funcIndex = m_originFile->functionIndex(data.right(data.length()-2).toStdString().c_str()); if (funcIndex < 0) { ++curveIndex; continue; } function = m_originFile->function(funcIndex); XYEquationCurve* xyEqCurve = new XYEquationCurve(function.name.c_str()); XYEquationCurve::EquationData eqData; eqData.count = function.totalPoints; eqData.expression1 = QString(function.formula.c_str()); if (function.type == Origin::Function::Polar) { eqData.type = XYEquationCurve::Polar; //replace 'x' by 'phi' eqData.expression1 = eqData.expression1.replace('x', "phi"); //convert from degrees to radians eqData.min = QString::number(function.begin/180) + QLatin1String("*pi"); eqData.max = QString::number(function.end/180) + QLatin1String("*pi"); } else { eqData.expression1 = QString(function.formula.c_str()); eqData.min = QString::number(function.begin); eqData.max = QString::number(function.end); } xyEqCurve->suppressRetransform(true); xyEqCurve->setEquationData(eqData); if (!preview) loadCurve(originCurve, xyEqCurve); plot->addChildFast(xyEqCurve); xyEqCurve->suppressRetransform(false); } } ++curveIndex; } } else { //no support for 3D plots yet //TODO: add an "UnsupportedAspect" here } ++index; } if (!preview) { worksheet->updateLayout(); //worksheet and plots got their sizes, //-> position all text labels inside the plots correctly by converting //the relative positions determined above to the absolute values QHash::const_iterator it = textLabelPositions.constBegin(); while (it != textLabelPositions.constEnd()) { TextLabel* label = it.key(); const QSizeF& ratios = it.value(); const CartesianPlot* plot = static_cast(label->parentAspect()); TextLabel::PositionWrapper position = label->position(); position.point.setX(plot->dataRect().width()*(ratios.width()-0.5)); position.point.setY(plot->dataRect().height()*(ratios.height()-0.5)); label->setPosition(position); ++it; } } return true; } /* * sets the axis properties (format and ticks) as defined in \c originAxis in \c axis, * \c index being 0 or 1 for "top" and "bottom" or "left" and "right" for horizontal or vertical axes, respectively. */ void OriginProjectParser::loadAxis(const Origin::GraphAxis& originAxis, Axis* axis, int index, const QString& axisTitle) const { // int axisPosition; // possible values: // 0: Axis is at default position // 1: Axis is at (axisPositionValue)% from standard position // 2: Axis is at (axisPositionValue) position of orthogonal axis // double axisPositionValue; // bool zeroLine; // bool oppositeLine; //ranges axis->setStart(originAxis.min); axis->setEnd(originAxis.max); //ticks - axis->setMajorTicksType(Axis::TicksIncrement); - axis->setMajorTicksIncrement(originAxis.step); + axis->setMajorTicksType(Axis::TicksSpacing); + axis->setMajorTicksSpacing(originAxis.step); axis->setMinorTicksType(Axis::TicksTotalNumber); axis->setMinorTicksNumber(originAxis.minorTicks); //scale switch (originAxis.scale) { case Origin::GraphAxis::Linear: axis->setScale(Axis::ScaleLinear); break; case Origin::GraphAxis::Log10: axis->setScale(Axis::ScaleLog10); break; case Origin::GraphAxis::Ln: axis->setScale(Axis::ScaleLn); break; case Origin::GraphAxis::Log2: axis->setScale(Axis::ScaleLog2); break; case Origin::GraphAxis::Probability: case Origin::GraphAxis::Probit: case Origin::GraphAxis::Reciprocal: case Origin::GraphAxis::OffsetReciprocal: case Origin::GraphAxis::Logit: //TODO: axis->setScale(Axis::ScaleLinear); break; } //major grid const Origin::GraphGrid& majorGrid = originAxis.majorGrid; QPen gridPen = axis->majorGridPen(); Qt::PenStyle penStyle(Qt::NoPen); if (!majorGrid.hidden) { switch (majorGrid.style) { case Origin::GraphCurve::Solid: penStyle = Qt::SolidLine; break; case Origin::GraphCurve::Dash: case Origin::GraphCurve::ShortDash: penStyle = Qt::DashLine; break; case Origin::GraphCurve::Dot: case Origin::GraphCurve::ShortDot: penStyle = Qt::DotLine; break; case Origin::GraphCurve::DashDot: case Origin::GraphCurve::ShortDashDot: penStyle = Qt::DashDotLine; break; case Origin::GraphCurve::DashDotDot: penStyle = Qt::DashDotDotLine; break; } } gridPen.setStyle(penStyle); Origin::Color gridColor; gridColor.type = Origin::Color::ColorType::Regular; gridColor.regular = majorGrid.color; gridPen.setColor(OriginProjectParser::color(gridColor)); gridPen.setWidthF(Worksheet::convertToSceneUnits(majorGrid.width, Worksheet::Point)); axis->setMajorGridPen(gridPen); //minor grid const Origin::GraphGrid& minorGrid = originAxis.minorGrid; gridPen = axis->minorGridPen(); penStyle = Qt::NoPen; if (!minorGrid.hidden) { switch (minorGrid.style) { case Origin::GraphCurve::Solid: penStyle = Qt::SolidLine; break; case Origin::GraphCurve::Dash: case Origin::GraphCurve::ShortDash: penStyle = Qt::DashLine; break; case Origin::GraphCurve::Dot: case Origin::GraphCurve::ShortDot: penStyle = Qt::DotLine; break; case Origin::GraphCurve::DashDot: case Origin::GraphCurve::ShortDashDot: penStyle = Qt::DashDotLine; break; case Origin::GraphCurve::DashDotDot: penStyle = Qt::DashDotDotLine; break; } } gridPen.setStyle(penStyle); gridColor.regular = minorGrid.color; gridPen.setColor(OriginProjectParser::color(gridColor)); gridPen.setWidthF(Worksheet::convertToSceneUnits(minorGrid.width, Worksheet::Point)); axis->setMinorGridPen(gridPen); //process Origin::GraphAxisFormat const Origin::GraphAxisFormat& axisFormat = originAxis.formatAxis[index]; QPen pen; Origin::Color color; color.type = Origin::Color::ColorType::Regular; color.regular = axisFormat.color; pen.setColor(OriginProjectParser::color(color)); pen.setWidthF(Worksheet::convertToSceneUnits(axisFormat.thickness, Worksheet::Point)); axis->setLinePen(pen); axis->setMajorTicksLength( Worksheet::convertToSceneUnits(axisFormat.majorTickLength, Worksheet::Point) ); axis->setMajorTicksDirection( (Axis::TicksFlags) axisFormat.majorTicksType); axis->setMajorTicksPen(pen); axis->setMinorTicksLength( axis->majorTicksLength()/2); // minorTicksLength is half of majorTicksLength axis->setMinorTicksDirection( (Axis::TicksFlags) axisFormat.minorTicksType); axis->setMinorTicksPen(pen); QString titleText = parseOriginText(QString::fromLatin1(axisFormat.label.text.c_str())); DEBUG(" axis title text = " << STDSTRING(titleText)); //TODO: parseOriginText() returns html formatted string. What is axisFormat.color used for? //TODO: use axisFormat.fontSize to override the global font size for the hmtl string? //TODO: convert special character here too DEBUG(" curve name = " << STDSTRING(axisTitle)); titleText.replace("%(?X)", axisTitle); titleText.replace("%(?Y)", axisTitle); DEBUG(" axis title = " << STDSTRING(titleText)); axis->title()->setText(titleText); axis->title()->setRotationAngle(axisFormat.label.rotation); axis->setLabelsPrefix(axisFormat.prefix.c_str()); axis->setLabelsSuffix(axisFormat.suffix.c_str()); //TODO: handle string factor member in GraphAxisFormat //process Origin::GraphAxisTick const Origin::GraphAxisTick& tickAxis = originAxis.tickAxis[index]; if (tickAxis.showMajorLabels) { color.type = Origin::Color::ColorType::Regular; color.regular = tickAxis.color; axis->setLabelsColor(OriginProjectParser::color(color)); //TODO: how to set labels position (top vs. bottom)? } else { axis->setLabelsPosition(Axis::LabelsPosition::NoLabels); } //TODO: handle ValueType valueType member in GraphAxisTick //TODO: handle int valueTypeSpecification in GraphAxisTick //precision if (tickAxis.decimalPlaces == -1) axis->setLabelsAutoPrecision(true); else { axis->setLabelsPrecision(tickAxis.decimalPlaces); axis->setLabelsAutoPrecision(false); } QFont font; //TODO: font family? font.setPixelSize( Worksheet::convertToSceneUnits(tickAxis.fontSize, Worksheet::Point) ); font.setBold(tickAxis.fontBold); axis->setLabelsFont(font); //TODO: handle string dataName member in GraphAxisTick //TODO: handle string columnName member in GraphAxisTick axis->setLabelsRotationAngle(tickAxis.rotation); } void OriginProjectParser::loadCurve(const Origin::GraphCurve& originCurve, XYCurve* curve) const { //line properties QPen pen = curve->linePen(); Qt::PenStyle penStyle(Qt::NoPen); if (originCurve.type == Origin::GraphCurve::Line || originCurve.type == Origin::GraphCurve::LineSymbol) { switch (originCurve.lineConnect) { case Origin::GraphCurve::NoLine: curve->setLineType(XYCurve::NoLine); break; case Origin::GraphCurve::Straight: curve->setLineType(XYCurve::Line); break; case Origin::GraphCurve::TwoPointSegment: curve->setLineType(XYCurve::Segments2); break; case Origin::GraphCurve::ThreePointSegment: curve->setLineType(XYCurve::Segments3); break; case Origin::GraphCurve::BSpline: case Origin::GraphCurve::Bezier: case Origin::GraphCurve::Spline: curve->setLineType(XYCurve::SplineCubicNatural); break; case Origin::GraphCurve::StepHorizontal: curve->setLineType(XYCurve::StartHorizontal); break; case Origin::GraphCurve::StepVertical: curve->setLineType(XYCurve::StartVertical); break; case Origin::GraphCurve::StepHCenter: curve->setLineType(XYCurve::MidpointHorizontal); break; case Origin::GraphCurve::StepVCenter: curve->setLineType(XYCurve::MidpointVertical); break; } switch (originCurve.lineStyle) { case Origin::GraphCurve::Solid: penStyle = Qt::SolidLine; break; case Origin::GraphCurve::Dash: case Origin::GraphCurve::ShortDash: penStyle = Qt::DashLine; break; case Origin::GraphCurve::Dot: case Origin::GraphCurve::ShortDot: penStyle = Qt::DotLine; break; case Origin::GraphCurve::DashDot: case Origin::GraphCurve::ShortDashDot: penStyle = Qt::DashDotLine; break; case Origin::GraphCurve::DashDotDot: penStyle = Qt::DashDotDotLine; break; } pen.setStyle(penStyle); pen.setWidthF( Worksheet::convertToSceneUnits(originCurve.lineWidth, Worksheet::Point) ); pen.setColor(color(originCurve.lineColor)); curve->setLineOpacity(1 - originCurve.lineTransparency/255); //TODO: handle unsigned char boxWidth of Origin::GraphCurve } pen.setStyle(penStyle); curve->setLinePen(pen); //symbol properties if (originCurve.type == Origin::GraphCurve::Scatter || originCurve.type == Origin::GraphCurve::LineSymbol) { //try to map the different symbols, mapping is not exact curve->setSymbolsRotationAngle(0); switch (originCurve.symbolShape) { case 0: //NoSymbol curve->setSymbolsStyle(Symbol::NoSymbols); break; case 1: //Rect curve->setSymbolsStyle(Symbol::Square); break; case 2: //Ellipse case 20://Sphere curve->setSymbolsStyle(Symbol::Circle); break; case 3: //UTriangle curve->setSymbolsStyle(Symbol::EquilateralTriangle); break; case 4: //DTriangle curve->setSymbolsStyle(Symbol::EquilateralTriangle); break; case 5: //Diamond curve->setSymbolsStyle(Symbol::Diamond); break; case 6: //Cross + curve->setSymbolsStyle(Symbol::Cross); break; case 7: //Cross x curve->setSymbolsStyle(Symbol::Cross); break; case 8: //Snow curve->setSymbolsStyle(Symbol::Star4); break; case 9: //Horizontal - curve->setSymbolsStyle(Symbol::Line); curve->setSymbolsRotationAngle(90); break; case 10: //Vertical | curve->setSymbolsStyle(Symbol::Line); break; case 15: //LTriangle curve->setSymbolsStyle(Symbol::EquilateralTriangle); break; case 16: //RTriangle curve->setSymbolsStyle(Symbol::EquilateralTriangle); break; case 17: //Hexagon case 19: //Pentagon curve->setSymbolsStyle(Symbol::Square); break; case 18: //Star curve->setSymbolsStyle(Symbol::Star5); break; default: curve->setSymbolsStyle(Symbol::NoSymbols); } //symbol size curve->setSymbolsSize(Worksheet::convertToSceneUnits(originCurve.symbolSize, Worksheet::Point)); //symbol fill color QBrush brush = curve->symbolsBrush(); if (originCurve.symbolFillColor.type == Origin::Color::ColorType::Automatic) { //"automatic" color -> the color of the line, if available, has to be used, black otherwise if (curve->lineType() != XYCurve::NoLine) brush.setColor(curve->linePen().color()); else brush.setColor(Qt::black); } else brush.setColor(color(originCurve.symbolFillColor)); curve->setSymbolsBrush(brush); //symbol border/edge color and width QPen pen = curve->symbolsPen(); if (originCurve.symbolColor.type == Origin::Color::ColorType::Automatic) { //"automatic" color -> the color of the line, if available, has to be used, black otherwise if (curve->lineType() != XYCurve::NoLine) pen.setColor(curve->linePen().color()); else pen.setColor(Qt::black); } else pen.setColor(color(originCurve.symbolColor)); //border width (edge thickness in Origin) is given by percentage of the symbol radius pen.setWidthF(originCurve.symbolThickness/100.*curve->symbolsSize()/2.); curve->setSymbolsPen(pen); //handle unsigned char pointOffset member //handle bool connectSymbols member } else { curve->setSymbolsStyle(Symbol::NoSymbols); } //filling properties if (originCurve.fillArea) { //TODO: handle unsigned char fillAreaType; //with 'fillAreaType'=0x10 the area between the curve and the x-axis is filled //with 'fillAreaType'=0x14 the area included inside the curve is filled. First and last curve points are joined by a line to close the otherwise open area. //with 'fillAreaType'=0x12 the area excluded outside the curve is filled. The inverse of fillAreaType=0x14 is filled. //At the moment we only support the first type, so set it to XYCurve::FillingBelow curve->setFillingPosition(XYCurve::FillingBelow); if (originCurve.fillAreaPattern == 0) { curve->setFillingType(PlotArea::Color); } else { curve->setFillingType(PlotArea::Pattern); //map different patterns in originCurve.fillAreaPattern (has the values of Origin::FillPattern) to Qt::BrushStyle; switch (originCurve.fillAreaPattern) { case 0: curve->setFillingBrushStyle(Qt::NoBrush); break; case 1: case 2: case 3: curve->setFillingBrushStyle(Qt::BDiagPattern); break; case 4: case 5: case 6: curve->setFillingBrushStyle(Qt::FDiagPattern); break; case 7: case 8: case 9: curve->setFillingBrushStyle(Qt::DiagCrossPattern); break; case 10: case 11: case 12: curve->setFillingBrushStyle(Qt::HorPattern); break; case 13: case 14: case 15: curve->setFillingBrushStyle(Qt::VerPattern); break; case 16: case 17: case 18: curve->setFillingBrushStyle(Qt::CrossPattern); break; } } curve->setFillingFirstColor(color(originCurve.fillAreaColor)); curve->setFillingOpacity(1 - originCurve.fillAreaTransparency/255); //Color fillAreaPatternColor - color for the pattern lines, not supported //double fillAreaPatternWidth - width of the pattern lines, not supported //bool fillAreaWithLineTransparency - transparency of the pattern lines independent of the area transparency, not supported //TODO: //unsigned char fillAreaPatternBorderStyle; //Color fillAreaPatternBorderColor; //double fillAreaPatternBorderWidth; //The Border properties are used only in "Column/Bar" (histogram) plots. Those properties are: //fillAreaPatternBorderStyle for the line style (use enum Origin::LineStyle here) //fillAreaPatternBorderColor for the line color //fillAreaPatternBorderWidth for the line width } else curve->setFillingPosition(XYCurve::NoFilling); } bool OriginProjectParser::loadNote(Note* note, bool preview) { DEBUG("OriginProjectParser::loadNote()"); //load note data const Origin::Note& originNote = m_originFile->note(findNoteByName(note->name())); if (preview) return true; note->setComment(originNote.label.c_str()); note->setNote(QString::fromLatin1(originNote.text.c_str())); return true; } //############################################################################## //########################### Helper functions ################################ //############################################################################## QDateTime OriginProjectParser::creationTime(tree::iterator it) const { //this logic seems to be correct only for the first node (project node). For other nodes the current time is returned. char time_str[21]; strftime(time_str, sizeof(time_str), "%F %T", gmtime(&(*it).creationDate)); return QDateTime::fromString(QString(time_str), Qt::ISODate); } QString OriginProjectParser::parseOriginText(const QString &str) const { DEBUG("parseOriginText()"); QStringList lines = str.split('\n'); QString text; for (int i = 0; i < lines.size(); ++i) { if (i > 0) text.append("
"); text.append(parseOriginTags(lines[i])); } DEBUG(" PARSED TEXT = " << STDSTRING(text)); return text; } QColor OriginProjectParser::color(Origin::Color color) const { switch (color.type) { case Origin::Color::ColorType::Regular: switch (color.regular) { case Origin::Color::Black: return QColor{Qt::black}; case Origin::Color::Red: return QColor{Qt::red}; case Origin::Color::Green: return QColor{Qt::green}; case Origin::Color::Blue: return QColor{Qt::blue}; case Origin::Color::Cyan: return QColor{Qt::cyan}; case Origin::Color::Magenta: return QColor{Qt::magenta}; case Origin::Color::Yellow: return QColor{Qt::yellow}; case Origin::Color::DarkYellow: return QColor{Qt::darkYellow}; case Origin::Color::Navy: return QColor{0, 0, 128}; case Origin::Color::Purple: return QColor{128, 0, 128}; case Origin::Color::Wine: return QColor{128, 0, 0}; case Origin::Color::Olive: return QColor{0, 128, 0}; case Origin::Color::DarkCyan: return QColor{Qt::darkCyan}; case Origin::Color::Royal: return QColor{0, 0, 160}; case Origin::Color::Orange: return QColor{255, 128, 0}; case Origin::Color::Violet: return QColor{128, 0, 255}; case Origin::Color::Pink: return QColor{255, 0, 128}; case Origin::Color::White: return QColor{Qt::white}; case Origin::Color::LightGray: return QColor{Qt::lightGray}; case Origin::Color::Gray: return QColor{Qt::gray}; case Origin::Color::LTYellow: return QColor{255, 0, 128}; case Origin::Color::LTCyan: return QColor{128, 255, 255}; case Origin::Color::LTMagenta: return QColor{255, 128, 255}; case Origin::Color::DarkGray: return QColor{Qt::darkGray}; case Origin::Color::SpecialV7Axis: return QColor{Qt::black}; } break; case Origin::Color::ColorType::Custom: return QColor{color.custom[0], color.custom[1], color.custom[2]}; case Origin::Color::ColorType::None: case Origin::Color::ColorType::Automatic: case Origin::Color::ColorType::Increment: case Origin::Color::ColorType::Indexing: case Origin::Color::ColorType::RGB: case Origin::Color::ColorType::Mapping: break; } return QColor(Qt::white); } PlotArea::BackgroundColorStyle OriginProjectParser::backgroundColorStyle(Origin::ColorGradientDirection colorGradient) const { switch (colorGradient) { case Origin::ColorGradientDirection::NoGradient: return PlotArea::BackgroundColorStyle::SingleColor; case Origin::ColorGradientDirection::TopLeft: return PlotArea::BackgroundColorStyle::TopLeftDiagonalLinearGradient; case Origin::ColorGradientDirection::Left: return PlotArea::BackgroundColorStyle::HorizontalLinearGradient; case Origin::ColorGradientDirection::BottomLeft: return PlotArea::BackgroundColorStyle::BottomLeftDiagonalLinearGradient; case Origin::ColorGradientDirection::Top: return PlotArea::BackgroundColorStyle::VerticalLinearGradient; case Origin::ColorGradientDirection::Center: return PlotArea::BackgroundColorStyle::RadialGradient; case Origin::ColorGradientDirection::Bottom: return PlotArea::BackgroundColorStyle::VerticalLinearGradient; case Origin::ColorGradientDirection::TopRight: return PlotArea::BackgroundColorStyle::BottomLeftDiagonalLinearGradient; case Origin::ColorGradientDirection::Right: return PlotArea::BackgroundColorStyle::HorizontalLinearGradient; case Origin::ColorGradientDirection::BottomRight: return PlotArea::BackgroundColorStyle::TopLeftDiagonalLinearGradient; } return PlotArea::BackgroundColorStyle::SingleColor; } QString strreverse(const QString &str) { //QString reversing QByteArray ba = str.toLocal8Bit(); std::reverse(ba.begin(), ba.end()); return QString(ba); } QList> OriginProjectParser::charReplacementList() const { QList> replacements; // TODO: probably missed some. Is there any generic method? replacements << qMakePair(QString("ä"), QString("ä")); replacements << qMakePair(QString("ö"), QString("ö")); replacements << qMakePair(QString("ü"), QString("ü")); replacements << qMakePair(QString("Ä"), QString("Ä")); replacements << qMakePair(QString("Ö"), QString("Ö")); replacements << qMakePair(QString("Ü"), QString("Ü")); replacements << qMakePair(QString("ß"), QString("ß")); replacements << qMakePair(QString("€"), QString("€")); replacements << qMakePair(QString("£"), QString("£")); replacements << qMakePair(QString("¥"), QString("¥")); replacements << qMakePair(QString("¤"), QString("¤")); // krazy:exclude=spelling replacements << qMakePair(QString("¦"), QString("¦")); replacements << qMakePair(QString("§"), QString("§")); replacements << qMakePair(QString("µ"), QString("µ")); replacements << qMakePair(QString("¹"), QString("¹")); replacements << qMakePair(QString("²"), QString("²")); replacements << qMakePair(QString("³"), QString("³")); replacements << qMakePair(QString("¶"), QString("¶")); replacements << qMakePair(QString("ø"), QString("ø")); replacements << qMakePair(QString("æ"), QString("æ")); replacements << qMakePair(QString("ð"), QString("ð")); replacements << qMakePair(QString("ħ"), QString("ℏ")); replacements << qMakePair(QString("ĸ"), QString("κ")); replacements << qMakePair(QString("¢"), QString("¢")); replacements << qMakePair(QString("¼"), QString("¼")); replacements << qMakePair(QString("½"), QString("½")); replacements << qMakePair(QString("¾"), QString("¾")); replacements << qMakePair(QString("¬"), QString("¬")); replacements << qMakePair(QString("©"), QString("©")); replacements << qMakePair(QString("®"), QString("®")); replacements << qMakePair(QString("ª"), QString("ª")); replacements << qMakePair(QString("º"), QString("º")); replacements << qMakePair(QString("±"), QString("±")); replacements << qMakePair(QString("¿"), QString("¿")); replacements << qMakePair(QString("×"), QString("×")); replacements << qMakePair(QString("°"), QString("°")); replacements << qMakePair(QString("«"), QString("«")); replacements << qMakePair(QString("»"), QString("»")); replacements << qMakePair(QString("¯"), QString("¯")); replacements << qMakePair(QString("¸"), QString("¸")); replacements << qMakePair(QString("À"), QString("À")); replacements << qMakePair(QString("Á"), QString("Á")); replacements << qMakePair(QString("Â"), QString("Â")); replacements << qMakePair(QString("Ã"), QString("Ã")); replacements << qMakePair(QString("Å"), QString("Å")); replacements << qMakePair(QString("Æ"), QString("Æ")); replacements << qMakePair(QString("Ç"), QString("Ç")); replacements << qMakePair(QString("È"), QString("È")); replacements << qMakePair(QString("É"), QString("É")); replacements << qMakePair(QString("Ê"), QString("Ê")); replacements << qMakePair(QString("Ë"), QString("Ë")); replacements << qMakePair(QString("Ì"), QString("Ì")); replacements << qMakePair(QString("Í"), QString("Í")); replacements << qMakePair(QString("Î"), QString("Î")); replacements << qMakePair(QString("Ï"), QString("Ï")); replacements << qMakePair(QString("Ð"), QString("Ð")); replacements << qMakePair(QString("Ñ"), QString("Ñ")); replacements << qMakePair(QString("Ò"), QString("Ò")); replacements << qMakePair(QString("Ó"), QString("Ó")); replacements << qMakePair(QString("Ô"), QString("Ô")); replacements << qMakePair(QString("Õ"), QString("Õ")); replacements << qMakePair(QString("Ù"), QString("Ù")); replacements << qMakePair(QString("Ú"), QString("Ú")); replacements << qMakePair(QString("Û"), QString("Û")); replacements << qMakePair(QString("Ý"), QString("Ý")); replacements << qMakePair(QString("Þ"), QString("Þ")); replacements << qMakePair(QString("à"), QString("à")); replacements << qMakePair(QString("á"), QString("á")); replacements << qMakePair(QString("â"), QString("â")); replacements << qMakePair(QString("ã"), QString("ã")); replacements << qMakePair(QString("å"), QString("å")); replacements << qMakePair(QString("ç"), QString("ç")); replacements << qMakePair(QString("è"), QString("è")); replacements << qMakePair(QString("é"), QString("é")); replacements << qMakePair(QString("ê"), QString("ê")); replacements << qMakePair(QString("ë"), QString("ë")); replacements << qMakePair(QString("ì"), QString("ì")); replacements << qMakePair(QString("í"), QString("í")); replacements << qMakePair(QString("î"), QString("î")); replacements << qMakePair(QString("ï"), QString("ï")); replacements << qMakePair(QString("ñ"), QString("ñ")); replacements << qMakePair(QString("ò"), QString("ò")); replacements << qMakePair(QString("ó"), QString("ó")); replacements << qMakePair(QString("ô"), QString("ô")); replacements << qMakePair(QString("õ"), QString("õ")); replacements << qMakePair(QString("÷"), QString("÷")); replacements << qMakePair(QString("ù"), QString("ù")); replacements << qMakePair(QString("ú"), QString("ú")); replacements << qMakePair(QString("û"), QString("û")); replacements << qMakePair(QString("ý"), QString("ý")); replacements << qMakePair(QString("þ"), QString("þ")); replacements << qMakePair(QString("ÿ"), QString("ÿ")); replacements << qMakePair(QString("Œ"), QString("Œ")); replacements << qMakePair(QString("œ"), QString("œ")); replacements << qMakePair(QString("Š"), QString("Š")); replacements << qMakePair(QString("š"), QString("š")); replacements << qMakePair(QString("Ÿ"), QString("Ÿ")); replacements << qMakePair(QString("†"), QString("†")); replacements << qMakePair(QString("‡"), QString("‡")); replacements << qMakePair(QString("…"), QString("…")); replacements << qMakePair(QString("‰"), QString("‰")); replacements << qMakePair(QString("™"), QString("™")); return replacements; } QString OriginProjectParser::replaceSpecialChars(const QString& text) const { QString t = text; for (const auto& r : charReplacementList()) t.replace(r.first, r.second); return t; } /*! * converts the string with Origin's syntax for text formatting/highlighting * to a string in the richtext/html format supported by Qt. * For the supported syntax, see: * https://www.originlab.com/doc/LabTalk/ref/Label-cmd * https://www.originlab.com/doc/Origin-Help/TextOb-Prop-Text-tab * https://doc.qt.io/qt-5/richtext-html-subset.html */ QString OriginProjectParser::parseOriginTags(const QString& str) const { DEBUG("parseOriginTags()"); DEBUG(" string: " << STDSTRING(str)); QDEBUG(" UTF8 string: " << str.toUtf8()); QString line = str; //replace %(...) tags // QRegExp rxcol("\\%\\(\\d+\\)"); // replace \l(x) (plot legend tags) with \\c{x}, where x is a digit line.replace(QRegularExpression(QStringLiteral("\\\\\\s*l\\s*\\(\\s*(\\d+)\\s*\\)")), QStringLiteral("\\c{\\1}")); // replace umlauts etc. line = replaceSpecialChars(line); // replace tabs (not really supported) line.replace('\t', "        "); // In PCRE2 (which is what QRegularExpression uses) variable-length lookbehind is supposed to be // exprimental in Perl 5.30; which means it doesn't work at the moment, i.e. using a variable-length // negative lookbehind isn't valid syntax from QRegularExpression POV. // Ultimately we have to reverse the string and use a negative _lookahead_ instead. // The goal is to temporatily replace '(' and ')' that don't denote tags; this is so that we // can handle parenthesis that are inside the tag, e.g. '\b(bold (cf))', we want the '(cf)' part // to remain as is. const QRegularExpression nonTagsRe("\\)([^)(]*)\\((?!\\s*([buigs\\+\\-]|\\d{1,3}\\s*[pc]|[\\w ]+\\s*:\\s*f)\\s*\\\\)"); QString linerev = strreverse(line); const QString lBracket = strreverse("&lbracket;"); const QString rBracket = strreverse("&rbracket;"); linerev.replace(nonTagsRe, rBracket + QStringLiteral("\\1") + lBracket); // change the line back to normal line = strreverse(linerev); //replace \-(...), \+(...), \b(...), \i(...), \u(...), \s(....), \g(...), \f:font(...), // \c'number'(...), \p'size'(...) tags with equivalent supported HTML syntax const QRegularExpression tagsRe(QStringLiteral("\\\\\\s*([-+bgisu]|f:(\\w[\\w ]+)|[pc]\\s*(\\d+))\\s*\\(([^()]+?)\\)")); QRegularExpressionMatch rmatch; while (line.contains(tagsRe, &rmatch)) { QString rep; const QString tagText = rmatch.captured(4); const QString marker = rmatch.captured(1); if (marker.startsWith(QLatin1Char('-'))) { rep = QStringLiteral("%1").arg(tagText); } else if (marker.startsWith(QLatin1Char('+'))) { rep = QStringLiteral("%1").arg(tagText); } else if (marker.startsWith(QLatin1Char('b'))) { rep = QStringLiteral("%1").arg(tagText); } else if (marker.startsWith(QLatin1Char('g'))) { // greek symbols e.g. α φ rep = QStringLiteral("%1").arg(tagText); } else if (marker.startsWith(QLatin1Char('i'))) { rep = QStringLiteral("%1").arg(tagText); } else if (marker.startsWith(QLatin1Char('s'))) { rep = QStringLiteral("%1").arg(tagText); } else if (marker.startsWith(QLatin1Char('u'))) { rep = QStringLiteral("%1").arg(tagText); } else if (marker.startsWith(QLatin1Char('f'))) { rep = QStringLiteral("%2").arg(rmatch.captured(2).trimmed(), tagText); } else if (marker.startsWith(QLatin1Char('p'))) { // e.g. \p200(...), means use font-size 200% rep = QStringLiteral("%2").arg(rmatch.captured(3), tagText); } else if (marker.startsWith(QLatin1Char('c'))) { // e.g. \c12(...), set the text color to the corresponding color from // the color drop-down list in OriginLab const int colorIndex = rmatch.captured(3).toInt(); Origin::Color c; c.type = Origin::Color::ColorType::Regular; c.regular = colorIndex <= 23 ? static_cast(colorIndex) : Origin::Color::RegularColor::Black; QColor color = OriginProjectParser::color(c); rep = QStringLiteral("%2").arg(color.name(), tagText); } line.replace(rmatch.capturedStart(0), rmatch.capturedLength(0), rep); } // put non-tag '(' and ')' back in their places line.replace("&lbracket;", "("); line.replace("&rbracket;", ")"); // special characters line.replace(QRegularExpression(QStringLiteral("\\\\\\((\\d+)\\)")), "&#\\1;"); DEBUG(" result: " << STDSTRING(line)); return line; } diff --git a/src/backend/nsl/nsl_math.c b/src/backend/nsl/nsl_math.c index b25f74ecb..5856d6c0a 100644 --- a/src/backend/nsl/nsl_math.c +++ b/src/backend/nsl/nsl_math.c @@ -1,66 +1,83 @@ /*************************************************************************** File : nsl_math.c Project : LabPlot Description : NSL math functions -------------------------------------------------------------------- - Copyright : (C) 2018 by Stefan Gerlach (stefan.gerlach@uni.kn) + Copyright : (C) 2018-2020 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 "nsl_math.h" #include +/* + * TODO: see Axis::determineDecimals() + */ +int nsl_math_decimals(double value) { + value /= 10.; // step one decimal before + double power10 = 1.; + for (int i = 0; i < 10; i++) { + double nearest = round(fabs(value) * power10) / power10; + if (nearest > 0) + return i; + + power10 *= 10.; + } + + return 10; +} + double nsl_math_round_places(double value, unsigned int n) { // no need to round if (value == 0. || fabs(value) > 1.e16 || fabs(value) < 1.e-16 || isnan(value) || isinf(value)) return value; double scale = gsl_pow_uint(10., n); double scaled_value = value*scale; if (fabs(scaled_value) > 1.e16) return value; if (fabs(scaled_value) < .5) return 0.; return round(scaled_value)/scale; } double nsl_math_round_precision(double value, unsigned int p) { // no need to round if (value == 0. || p > 16 || isnan(value) || isinf(value)) return value; int e = 0; while (fabs(value) > 10.) { value /= 10.; e++; } while (fabs(value) < 1.) { value *= 10.; e--; } double scale = gsl_pow_uint(10., p); double scaled_value = value*scale; return round(scaled_value)/scale * gsl_pow_int(10., e); } diff --git a/src/backend/nsl/nsl_math.h b/src/backend/nsl/nsl_math.h index 479132b23..137d884ea 100644 --- a/src/backend/nsl/nsl_math.h +++ b/src/backend/nsl/nsl_math.h @@ -1,38 +1,41 @@ /*************************************************************************** File : nsl_math.h Project : LabPlot Description : NSL math functions -------------------------------------------------------------------- Copyright : (C) 2018 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef NSL_MATH_H #define NSL_MATH_H +/* get number of decimals */ +int nsl_math_decimals(double value); + /* round double value to n decimal places */ double nsl_math_round_places(double value, unsigned int n); /* round double value to precision p */ double nsl_math_round_precision(double value, unsigned int p); #endif /* NSL_MATH_H */ diff --git a/src/backend/worksheet/plots/cartesian/Axis.cpp b/src/backend/worksheet/plots/cartesian/Axis.cpp index 55ee76526..44c35539a 100644 --- a/src/backend/worksheet/plots/cartesian/Axis.cpp +++ b/src/backend/worksheet/plots/cartesian/Axis.cpp @@ -1,2341 +1,2342 @@ /*************************************************************************** File : Axis.cpp Project : LabPlot Description : Axis for cartesian coordinate systems. -------------------------------------------------------------------- Copyright : (C) 2011-2018 Alexander Semke (alexander.semke@web.de) - Copyright : (C) 2013-2018 Stefan Gerlach (stefan.gerlach@uni.kn) + Copyright : (C) 2013-2020 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/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/plots/cartesian/AxisPrivate.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/TextLabel.h" #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/core/AbstractColumn.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/macros.h" // #include "backend/lib/trace.h" #include "kdefrontend/GuiTools.h" #include #include #include #include #include #include #include extern "C" { +#include #include "backend/nsl/nsl_math.h" } /** * \class AxisGrid * \brief Helper class to get the axis grid drawn with the z-Value=0. * * The painting of the grid lines is separated from the painting of the axis itself. * This allows to use a different z-values for the grid lines (z=0, drawn below all other objects ) * and for the axis (z=FLT_MAX, drawn on top of all other objects) * * \ingroup worksheet */ class AxisGrid : public QGraphicsItem { public: explicit AxisGrid(AxisPrivate* a) { axis = a; setFlag(QGraphicsItem::ItemIsSelectable, false); setFlag(QGraphicsItem::ItemIsFocusable, false); setAcceptHoverEvents(false); } QRectF boundingRect() const override { QPainterPath gridShape; gridShape.addPath(WorksheetElement::shapeFromPath(axis->majorGridPath, axis->majorGridPen)); gridShape.addPath(WorksheetElement::shapeFromPath(axis->minorGridPath, axis->minorGridPen)); QRectF boundingRectangle = gridShape.boundingRect(); return boundingRectangle; } void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override { Q_UNUSED(option) Q_UNUSED(widget) if (!axis->isVisible()) return; if (axis->linePath.isEmpty()) return; //draw major grid if (axis->majorGridPen.style() != Qt::NoPen) { painter->setOpacity(axis->majorGridOpacity); painter->setPen(axis->majorGridPen); painter->setBrush(Qt::NoBrush); painter->drawPath(axis->majorGridPath); } //draw minor grid if (axis->minorGridPen.style() != Qt::NoPen) { painter->setOpacity(axis->minorGridOpacity); painter->setPen(axis->minorGridPen); painter->setBrush(Qt::NoBrush); painter->drawPath(axis->minorGridPath); } } private: AxisPrivate* axis; }; /** * \class Axis * \brief Axis for cartesian coordinate systems. * * \ingroup worksheet */ Axis::Axis(const QString& name, AxisOrientation orientation) : WorksheetElement(name, AspectType::Axis), d_ptr(new AxisPrivate(this)) { d_ptr->orientation = orientation; init(); } Axis::Axis(const QString& name, AxisOrientation orientation, AxisPrivate* dd) : WorksheetElement(name, AspectType::Axis), d_ptr(dd) { d_ptr->orientation = orientation; init(); } void Axis::finalizeAdd() { Q_D(Axis); d->plot = dynamic_cast(parentAspect()); Q_ASSERT(d->plot); d->cSystem = dynamic_cast(d->plot->coordinateSystem()); } void Axis::init() { Q_D(Axis); KConfig config; KConfigGroup group = config.group("Axis"); d->autoScale = true; d->position = Axis::AxisCustom; d->offset = group.readEntry("PositionOffset", 0); d->scale = (Axis::AxisScale) group.readEntry("Scale", (int) Axis::ScaleLinear); d->autoScale = group.readEntry("AutoScale", true); d->start = group.readEntry("Start", 0); d->end = group.readEntry("End", 10); d->zeroOffset = group.readEntry("ZeroOffset", 0); d->scalingFactor = group.readEntry("ScalingFactor", 1.0); d->linePen.setStyle( (Qt::PenStyle) group.readEntry("LineStyle", (int) Qt::SolidLine) ); d->linePen.setWidthF( group.readEntry("LineWidth", Worksheet::convertToSceneUnits( 1.0, Worksheet::Point ) ) ); d->lineOpacity = group.readEntry("LineOpacity", 1.0); d->arrowType = (Axis::ArrowType) group.readEntry("ArrowType", (int)Axis::NoArrow); d->arrowPosition = (Axis::ArrowPosition) group.readEntry("ArrowPosition", (int)Axis::ArrowRight); d->arrowSize = group.readEntry("ArrowSize", Worksheet::convertToSceneUnits(10, Worksheet::Point)); // axis title d->title = new TextLabel(this->name(), TextLabel::AxisTitle); connect( d->title, &TextLabel::changed, this, &Axis::labelChanged); addChild(d->title); d->title->setHidden(true); d->title->graphicsItem()->setParentItem(d); d->title->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false); d->title->graphicsItem()->setFlag(QGraphicsItem::ItemIsFocusable, false); d->title->graphicsItem()->setAcceptHoverEvents(false); d->title->setText(this->name()); if (d->orientation == AxisVertical) d->title->setRotationAngle(90); d->titleOffsetX = Worksheet::convertToSceneUnits(2, Worksheet::Point); //distance to the axis tick labels d->titleOffsetY = Worksheet::convertToSceneUnits(2, Worksheet::Point); //distance to the axis tick labels d->majorTicksDirection = (Axis::TicksDirection) group.readEntry("MajorTicksDirection", (int) Axis::ticksOut); d->majorTicksType = (Axis::TicksType) group.readEntry("MajorTicksType", (int) Axis::TicksTotalNumber); d->majorTicksNumber = group.readEntry("MajorTicksNumber", 11); - d->majorTicksIncrement = group.readEntry("MajorTicksIncrement", -1.0); // set to negative value, so axisdocks determines the value to not to have to much labels the first time switched to TicksIncrement + d->majorTicksSpacing = group.readEntry("MajorTicksIncrement", 0.0); // set to 0, so axisdock determines the value to not have to many labels the first time switched to Spacing + d->majorTicksPen.setStyle((Qt::PenStyle) group.readEntry("MajorTicksLineStyle", (int)Qt::SolidLine) ); d->majorTicksPen.setColor( group.readEntry("MajorTicksColor", QColor(Qt::black) ) ); d->majorTicksPen.setWidthF( group.readEntry("MajorTicksWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point) ) ); d->majorTicksLength = group.readEntry("MajorTicksLength", Worksheet::convertToSceneUnits(6.0, Worksheet::Point)); d->majorTicksOpacity = group.readEntry("MajorTicksOpacity", 1.0); d->minorTicksDirection = (Axis::TicksDirection) group.readEntry("MinorTicksDirection", (int) Axis::ticksOut); d->minorTicksType = (Axis::TicksType) group.readEntry("MinorTicksType", (int) Axis::TicksTotalNumber); d->minorTicksNumber = group.readEntry("MinorTicksNumber", 1); - d->minorTicksIncrement = group.readEntry("MinorTicksIncrement", -1.0); + d->minorTicksIncrement = group.readEntry("MinorTicksIncrement", 0.0); // see MajorTicksIncrement d->minorTicksPen.setStyle((Qt::PenStyle) group.readEntry("MinorTicksLineStyle", (int)Qt::SolidLine) ); d->minorTicksPen.setColor( group.readEntry("MinorTicksColor", QColor(Qt::black) ) ); d->minorTicksPen.setWidthF( group.readEntry("MinorTicksWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point) ) ); d->minorTicksLength = group.readEntry("MinorTicksLength", Worksheet::convertToSceneUnits(3.0, Worksheet::Point)); d->minorTicksOpacity = group.readEntry("MinorTicksOpacity", 1.0); //Labels d->labelsFormat = (Axis::LabelsFormat) group.readEntry("LabelsFormat", (int)Axis::FormatDecimal); d->labelsAutoPrecision = group.readEntry("LabelsAutoPrecision", true); d->labelsPrecision = group.readEntry("LabelsPrecision", 1); d->labelsDateTimeFormat = group.readEntry("LabelsDateTimeFormat", "yyyy-MM-dd hh:mm:ss"); d->labelsPosition = (Axis::LabelsPosition) group.readEntry("LabelsPosition", (int) Axis::LabelsOut); d->labelsOffset = group.readEntry("LabelsOffset", Worksheet::convertToSceneUnits( 5.0, Worksheet::Point )); d->labelsRotationAngle = group.readEntry("LabelsRotation", 0); d->labelsFont = group.readEntry("LabelsFont", QFont()); d->labelsFont.setPixelSize( Worksheet::convertToSceneUnits( 10.0, Worksheet::Point ) ); d->labelsColor = group.readEntry("LabelsFontColor", QColor(Qt::black)); d->labelsPrefix = group.readEntry("LabelsPrefix", "" ); d->labelsSuffix = group.readEntry("LabelsSuffix", "" ); d->labelsOpacity = group.readEntry("LabelsOpacity", 1.0); //major grid d->majorGridPen.setStyle( (Qt::PenStyle) group.readEntry("MajorGridStyle", (int) Qt::NoPen) ); d->majorGridPen.setColor(group.readEntry("MajorGridColor", QColor(Qt::gray)) ); d->majorGridPen.setWidthF( group.readEntry("MajorGridWidth", Worksheet::convertToSceneUnits( 1.0, Worksheet::Point ) ) ); d->majorGridOpacity = group.readEntry("MajorGridOpacity", 1.0); //minor grid d->minorGridPen.setStyle( (Qt::PenStyle) group.readEntry("MinorGridStyle", (int) Qt::NoPen) ); d->minorGridPen.setColor(group.readEntry("MinorGridColor", QColor(Qt::gray)) ); d->minorGridPen.setWidthF( group.readEntry("MinorGridWidth", Worksheet::convertToSceneUnits( 1.0, Worksheet::Point ) ) ); d->minorGridOpacity = group.readEntry("MinorGridOpacity", 1.0); } /*! * For the most frequently edited properties, create Actions and ActionGroups for the context menu. * For some ActionGroups the actual actions are created in \c GuiTool, */ void Axis::initActions() { visibilityAction = new QAction(QIcon::fromTheme("view-visible"), i18n("Visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, &QAction::triggered, this, &Axis::visibilityChangedSlot); //Orientation orientationActionGroup = new QActionGroup(this); orientationActionGroup->setExclusive(true); connect(orientationActionGroup, &QActionGroup::triggered, this, &Axis::orientationChangedSlot); orientationHorizontalAction = new QAction(QIcon::fromTheme("labplot-axis-horizontal"), i18n("Horizontal"), orientationActionGroup); orientationHorizontalAction->setCheckable(true); orientationVerticalAction = new QAction(QIcon::fromTheme("labplot-axis-vertical"), i18n("Vertical"), orientationActionGroup); orientationVerticalAction->setCheckable(true); //Line lineStyleActionGroup = new QActionGroup(this); lineStyleActionGroup->setExclusive(true); connect(lineStyleActionGroup, &QActionGroup::triggered, this, &Axis::lineStyleChanged); lineColorActionGroup = new QActionGroup(this); lineColorActionGroup->setExclusive(true); connect(lineColorActionGroup, &QActionGroup::triggered, this, &Axis::lineColorChanged); //Ticks //TODO } void Axis::initMenus() { this->initActions(); //Orientation orientationMenu = new QMenu(i18n("Orientation")); orientationMenu->setIcon(QIcon::fromTheme("labplot-axis-horizontal")); orientationMenu->addAction(orientationHorizontalAction); orientationMenu->addAction(orientationVerticalAction); //Line lineMenu = new QMenu(i18n("Line")); lineMenu->setIcon(QIcon::fromTheme("draw-line")); lineStyleMenu = new QMenu(i18n("Style"), lineMenu); lineStyleMenu->setIcon(QIcon::fromTheme("object-stroke-style")); lineMenu->setIcon(QIcon::fromTheme("draw-line")); lineMenu->addMenu( lineStyleMenu ); lineColorMenu = new QMenu(i18n("Color"), lineMenu); lineColorMenu->setIcon(QIcon::fromTheme("fill-color")); GuiTools::fillColorMenu( lineColorMenu, lineColorActionGroup ); lineMenu->addMenu( lineColorMenu ); } QMenu* Axis::createContextMenu() { if (!orientationMenu) initMenus(); Q_D(const Axis); QMenu* menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action" visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); //Orientation if ( d->orientation == AxisHorizontal ) orientationHorizontalAction->setChecked(true); else orientationVerticalAction->setChecked(true); menu->insertMenu(firstAction, orientationMenu); //Line styles GuiTools::updatePenStyles( lineStyleMenu, lineStyleActionGroup, d->linePen.color() ); GuiTools::selectPenStyleAction(lineStyleActionGroup, d->linePen.style() ); GuiTools::selectColorAction(lineColorActionGroup, d->linePen.color() ); menu->insertMenu(firstAction, lineMenu); menu->insertSeparator(firstAction); return menu; } /*! Returns an icon to be used in the project explorer. */ QIcon Axis::icon() const{ Q_D(const Axis); QIcon ico; if (d->orientation == Axis::AxisHorizontal) ico = QIcon::fromTheme("labplot-axis-horizontal"); else ico = QIcon::fromTheme("labplot-axis-vertical"); return ico; } Axis::~Axis() { if (orientationMenu) { delete orientationMenu; delete lineMenu; } //no need to delete d->title, since it was added with addChild in init(); //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } QGraphicsItem *Axis::graphicsItem() const { return d_ptr; } /*! * overrides the implementation in WorksheetElement and sets the z-value to the maximal possible, * axes are drawn on top of all other object in the plot. */ void Axis::setZValue(qreal) { Q_D(Axis); d->setZValue(std::numeric_limits::max()); d->gridItem->setParentItem(d->parentItem()); d->gridItem->setZValue(0); } void Axis::retransform() { Q_D(Axis); d->retransform(); } void Axis::retransformTickLabelStrings() { Q_D(Axis); d->retransformTickLabelStrings(); } void Axis::setSuppressRetransform(bool value) { Q_D(Axis); d->suppressRetransform = value; } void Axis::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { Q_D(Axis); Q_UNUSED(pageResize); double ratio = 0; if (horizontalRatio > 1.0 || verticalRatio > 1.0) ratio = qMax(horizontalRatio, verticalRatio); else ratio = qMin(horizontalRatio, verticalRatio); QPen pen = d->linePen; pen.setWidthF(pen.widthF() * ratio); d->linePen = pen; d->majorTicksLength *= ratio; // ticks are perpendicular to axis line -> verticalRatio relevant d->minorTicksLength *= ratio; d->labelsFont.setPixelSize( d->labelsFont.pixelSize() * ratio ); //TODO: take into account rotated labels d->labelsOffset *= ratio; d->title->handleResize(horizontalRatio, verticalRatio, pageResize); } /* ============================ getter methods ================= */ BASIC_SHARED_D_READER_IMPL(Axis, bool, autoScale, autoScale) BASIC_SHARED_D_READER_IMPL(Axis, Axis::AxisOrientation, orientation, orientation) BASIC_SHARED_D_READER_IMPL(Axis, Axis::AxisPosition, position, position) BASIC_SHARED_D_READER_IMPL(Axis, Axis::AxisScale, scale, scale) BASIC_SHARED_D_READER_IMPL(Axis, double, offset, offset) BASIC_SHARED_D_READER_IMPL(Axis, double, start, start) BASIC_SHARED_D_READER_IMPL(Axis, double, end, end) BASIC_SHARED_D_READER_IMPL(Axis, qreal, scalingFactor, scalingFactor) BASIC_SHARED_D_READER_IMPL(Axis, qreal, zeroOffset, zeroOffset) BASIC_SHARED_D_READER_IMPL(Axis, TextLabel*, title, title) BASIC_SHARED_D_READER_IMPL(Axis, qreal, titleOffsetX, titleOffsetX) BASIC_SHARED_D_READER_IMPL(Axis, qreal, titleOffsetY, titleOffsetY) CLASS_SHARED_D_READER_IMPL(Axis, QPen, linePen, linePen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, lineOpacity, lineOpacity) BASIC_SHARED_D_READER_IMPL(Axis, Axis::ArrowType, arrowType, arrowType) BASIC_SHARED_D_READER_IMPL(Axis, Axis::ArrowPosition, arrowPosition, arrowPosition) BASIC_SHARED_D_READER_IMPL(Axis, qreal, arrowSize, arrowSize) BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksDirection, majorTicksDirection, majorTicksDirection) BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksType, majorTicksType, majorTicksType) BASIC_SHARED_D_READER_IMPL(Axis, int, majorTicksNumber, majorTicksNumber) -BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksIncrement, majorTicksIncrement) +BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksSpacing, majorTicksSpacing) BASIC_SHARED_D_READER_IMPL(Axis, const AbstractColumn*, majorTicksColumn, majorTicksColumn) QString& Axis::majorTicksColumnPath() const { return d_ptr->majorTicksColumnPath; } BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksLength, majorTicksLength) CLASS_SHARED_D_READER_IMPL(Axis, QPen, majorTicksPen, majorTicksPen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksOpacity, majorTicksOpacity) BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksDirection, minorTicksDirection, minorTicksDirection) BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksType, minorTicksType, minorTicksType) BASIC_SHARED_D_READER_IMPL(Axis, int, minorTicksNumber, minorTicksNumber) -BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksIncrement, minorTicksIncrement) +BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksSpacing, minorTicksIncrement) BASIC_SHARED_D_READER_IMPL(Axis, const AbstractColumn*, minorTicksColumn, minorTicksColumn) QString& Axis::minorTicksColumnPath() const { return d_ptr->minorTicksColumnPath; } BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksLength, minorTicksLength) CLASS_SHARED_D_READER_IMPL(Axis, QPen, minorTicksPen, minorTicksPen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksOpacity, minorTicksOpacity) BASIC_SHARED_D_READER_IMPL(Axis, Axis::LabelsFormat, labelsFormat, labelsFormat); BASIC_SHARED_D_READER_IMPL(Axis, bool, labelsAutoPrecision, labelsAutoPrecision); BASIC_SHARED_D_READER_IMPL(Axis, int, labelsPrecision, labelsPrecision); BASIC_SHARED_D_READER_IMPL(Axis, QString, labelsDateTimeFormat, labelsDateTimeFormat); BASIC_SHARED_D_READER_IMPL(Axis, Axis::LabelsPosition, labelsPosition, labelsPosition); BASIC_SHARED_D_READER_IMPL(Axis, qreal, labelsOffset, labelsOffset); BASIC_SHARED_D_READER_IMPL(Axis, qreal, labelsRotationAngle, labelsRotationAngle); CLASS_SHARED_D_READER_IMPL(Axis, QColor, labelsColor, labelsColor); CLASS_SHARED_D_READER_IMPL(Axis, QFont, labelsFont, labelsFont); CLASS_SHARED_D_READER_IMPL(Axis, QString, labelsPrefix, labelsPrefix); CLASS_SHARED_D_READER_IMPL(Axis, QString, labelsSuffix, labelsSuffix); BASIC_SHARED_D_READER_IMPL(Axis, qreal, labelsOpacity, labelsOpacity); CLASS_SHARED_D_READER_IMPL(Axis, QPen, majorGridPen, majorGridPen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorGridOpacity, majorGridOpacity) CLASS_SHARED_D_READER_IMPL(Axis, QPen, minorGridPen, minorGridPen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorGridOpacity, minorGridOpacity) /* ============================ setter methods and undo commands ================= */ STD_SETTER_CMD_IMPL_F_S(Axis, SetAutoScale, bool, autoScale, retransform); void Axis::setAutoScale(bool autoScale) { Q_D(Axis); if (autoScale != d->autoScale) { exec(new AxisSetAutoScaleCmd(d, autoScale, ki18n("%1: set axis auto scaling"))); if (autoScale) { auto* plot = qobject_cast(parentAspect()); if (!plot) return; if (d->orientation == Axis::AxisHorizontal) { d->end = plot->xMax(); d->start = plot->xMin(); } else { d->end = plot->yMax(); d->start = plot->yMin(); } retransform(); emit endChanged(d->end); emit startChanged(d->start); } } } STD_SWAP_METHOD_SETTER_CMD_IMPL(Axis, SetVisible, bool, swapVisible); void Axis::setVisible(bool on) { Q_D(Axis); exec(new AxisSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool Axis::isVisible() const { Q_D(const Axis); return d->isVisible(); } void Axis::setPrinting(bool on) { Q_D(Axis); d->setPrinting(on); } STD_SETTER_CMD_IMPL_F_S(Axis, SetOrientation, Axis::AxisOrientation, orientation, retransform); void Axis::setOrientation( AxisOrientation orientation) { Q_D(Axis); if (orientation != d->orientation) exec(new AxisSetOrientationCmd(d, orientation, ki18n("%1: set axis orientation"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetPosition, Axis::AxisPosition, position, retransform); void Axis::setPosition(AxisPosition position) { Q_D(Axis); if (position != d->position) exec(new AxisSetPositionCmd(d, position, ki18n("%1: set axis position"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetScaling, Axis::AxisScale, scale, retransformTicks); void Axis::setScale(AxisScale scale) { Q_D(Axis); if (scale != d->scale) exec(new AxisSetScalingCmd(d, scale, ki18n("%1: set axis scale"))); } STD_SETTER_CMD_IMPL_F(Axis, SetOffset, double, offset, retransform); void Axis::setOffset(double offset, bool undo) { Q_D(Axis); if (offset != d->offset) { if (undo) { exec(new AxisSetOffsetCmd(d, offset, ki18n("%1: set axis offset"))); } else { d->offset = offset; //don't need to call retransform() afterward //since the only usage of this call is in CartesianPlot, where retransform is called for all children anyway. } emit positionChanged(offset); } } STD_SETTER_CMD_IMPL_F_S(Axis, SetStart, double, start, retransform); void Axis::setStart(double start) { Q_D(Axis); if (start != d->start) exec(new AxisSetStartCmd(d, start, ki18n("%1: set axis start"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetEnd, double, end, retransform); void Axis::setEnd(double end) { Q_D(Axis); if (end != d->end) exec(new AxisSetEndCmd(d, end, ki18n("%1: set axis end"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetZeroOffset, qreal, zeroOffset, retransform); void Axis::setZeroOffset(qreal zeroOffset) { Q_D(Axis); if (zeroOffset != d->zeroOffset) exec(new AxisSetZeroOffsetCmd(d, zeroOffset, ki18n("%1: set axis zero offset"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetScalingFactor, qreal, scalingFactor, retransform); void Axis::setScalingFactor(qreal scalingFactor) { Q_D(Axis); if (scalingFactor != d->scalingFactor) exec(new AxisSetScalingFactorCmd(d, scalingFactor, ki18n("%1: set axis scaling factor"))); } //Title STD_SETTER_CMD_IMPL_F_S(Axis, SetTitleOffsetX, qreal, titleOffsetX, retransform); void Axis::setTitleOffsetX(qreal offset) { Q_D(Axis); if (offset != d->titleOffsetX) exec(new AxisSetTitleOffsetXCmd(d, offset, ki18n("%1: set title offset"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetTitleOffsetY, qreal, titleOffsetY, retransform); void Axis::setTitleOffsetY(qreal offset) { Q_D(Axis); if (offset != d->titleOffsetY) exec(new AxisSetTitleOffsetYCmd(d, offset, ki18n("%1: set title offset"))); } //Line STD_SETTER_CMD_IMPL_F_S(Axis, SetLinePen, QPen, linePen, recalcShapeAndBoundingRect); void Axis::setLinePen(const QPen &pen) { Q_D(Axis); if (pen != d->linePen) exec(new AxisSetLinePenCmd(d, pen, ki18n("%1: set line style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLineOpacity, qreal, lineOpacity, update); void Axis::setLineOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->lineOpacity) exec(new AxisSetLineOpacityCmd(d, opacity, ki18n("%1: set line opacity"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetArrowType, Axis::ArrowType, arrowType, retransformArrow); void Axis::setArrowType(ArrowType type) { Q_D(Axis); if (type != d->arrowType) exec(new AxisSetArrowTypeCmd(d, type, ki18n("%1: set arrow type"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetArrowPosition, Axis::ArrowPosition, arrowPosition, retransformArrow); void Axis::setArrowPosition(ArrowPosition position) { Q_D(Axis); if (position != d->arrowPosition) exec(new AxisSetArrowPositionCmd(d, position, ki18n("%1: set arrow position"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetArrowSize, qreal, arrowSize, retransformArrow); void Axis::setArrowSize(qreal arrowSize) { Q_D(Axis); if (arrowSize != d->arrowSize) exec(new AxisSetArrowSizeCmd(d, arrowSize, ki18n("%1: set arrow size"))); } //Major ticks STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksDirection, Axis::TicksDirection, majorTicksDirection, retransformTicks); void Axis::setMajorTicksDirection(TicksDirection majorTicksDirection) { Q_D(Axis); if (majorTicksDirection != d->majorTicksDirection) exec(new AxisSetMajorTicksDirectionCmd(d, majorTicksDirection, ki18n("%1: set major ticks direction"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksType, Axis::TicksType, majorTicksType, retransformTicks); void Axis::setMajorTicksType(TicksType majorTicksType) { Q_D(Axis); if (majorTicksType!= d->majorTicksType) exec(new AxisSetMajorTicksTypeCmd(d, majorTicksType, ki18n("%1: set major ticks type"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksNumber, int, majorTicksNumber, retransformTicks); void Axis::setMajorTicksNumber(int majorTicksNumber) { Q_D(Axis); if (majorTicksNumber != d->majorTicksNumber) exec(new AxisSetMajorTicksNumberCmd(d, majorTicksNumber, ki18n("%1: set the total number of the major ticks"))); } -STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksIncrement, qreal, majorTicksIncrement, retransformTicks); -void Axis::setMajorTicksIncrement(qreal majorTicksIncrement) { +STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksSpacing, qreal, majorTicksSpacing, retransformTicks); +void Axis::setMajorTicksSpacing(qreal majorTicksSpacing) { Q_D(Axis); - if (majorTicksIncrement != d->majorTicksIncrement) - exec(new AxisSetMajorTicksIncrementCmd(d, majorTicksIncrement, ki18n("%1: set the increment for the major ticks"))); + if (majorTicksSpacing != d->majorTicksSpacing) + exec(new AxisSetMajorTicksSpacingCmd(d, majorTicksSpacing, ki18n("%1: set the spacing of the major ticks"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksColumn, const AbstractColumn*, majorTicksColumn, retransformTicks) void Axis::setMajorTicksColumn(const AbstractColumn* column) { Q_D(Axis); if (column != d->majorTicksColumn) { exec(new AxisSetMajorTicksColumnCmd(d, column, ki18n("%1: assign major ticks' values"))); if (column) { connect(column, &AbstractColumn::dataChanged, this, &Axis::retransformTicks); connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, this, &Axis::majorTicksColumnAboutToBeRemoved); //TODO: add disconnect in the undo-function } } } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksPen, QPen, majorTicksPen, recalcShapeAndBoundingRect); void Axis::setMajorTicksPen(const QPen& pen) { Q_D(Axis); if (pen != d->majorTicksPen) exec(new AxisSetMajorTicksPenCmd(d, pen, ki18n("%1: set major ticks style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksLength, qreal, majorTicksLength, retransformTicks); void Axis::setMajorTicksLength(qreal majorTicksLength) { Q_D(Axis); if (majorTicksLength != d->majorTicksLength) exec(new AxisSetMajorTicksLengthCmd(d, majorTicksLength, ki18n("%1: set major ticks length"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksOpacity, qreal, majorTicksOpacity, update); void Axis::setMajorTicksOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->majorTicksOpacity) exec(new AxisSetMajorTicksOpacityCmd(d, opacity, ki18n("%1: set major ticks opacity"))); } //Minor ticks STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksDirection, Axis::TicksDirection, minorTicksDirection, retransformTicks); void Axis::setMinorTicksDirection(TicksDirection minorTicksDirection) { Q_D(Axis); if (minorTicksDirection != d->minorTicksDirection) exec(new AxisSetMinorTicksDirectionCmd(d, minorTicksDirection, ki18n("%1: set minor ticks direction"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksType, Axis::TicksType, minorTicksType, retransformTicks); void Axis::setMinorTicksType(TicksType minorTicksType) { Q_D(Axis); if (minorTicksType!= d->minorTicksType) exec(new AxisSetMinorTicksTypeCmd(d, minorTicksType, ki18n("%1: set minor ticks type"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksNumber, int, minorTicksNumber, retransformTicks); void Axis::setMinorTicksNumber(int minorTicksNumber) { Q_D(Axis); if (minorTicksNumber != d->minorTicksNumber) exec(new AxisSetMinorTicksNumberCmd(d, minorTicksNumber, ki18n("%1: set the total number of the minor ticks"))); } -STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksIncrement, qreal, minorTicksIncrement, retransformTicks); -void Axis::setMinorTicksIncrement(qreal minorTicksIncrement) { +STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksSpacing, qreal, minorTicksIncrement, retransformTicks); +void Axis::setMinorTicksSpacing(qreal minorTicksSpacing) { Q_D(Axis); - if (minorTicksIncrement != d->minorTicksIncrement) - exec(new AxisSetMinorTicksIncrementCmd(d, minorTicksIncrement, ki18n("%1: set the increment for the minor ticks"))); + if (minorTicksSpacing != d->minorTicksIncrement) + exec(new AxisSetMinorTicksSpacingCmd(d, minorTicksSpacing, ki18n("%1: set the spacing of the minor ticks"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksColumn, const AbstractColumn*, minorTicksColumn, retransformTicks) void Axis::setMinorTicksColumn(const AbstractColumn* column) { Q_D(Axis); if (column != d->minorTicksColumn) { exec(new AxisSetMinorTicksColumnCmd(d, column, ki18n("%1: assign minor ticks' values"))); if (column) { connect(column, &AbstractColumn::dataChanged, this, &Axis::retransformTicks); connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, this, &Axis::minorTicksColumnAboutToBeRemoved); //TODO: add disconnect in the undo-function } } } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksPen, QPen, minorTicksPen, recalcShapeAndBoundingRect); void Axis::setMinorTicksPen(const QPen& pen) { Q_D(Axis); if (pen != d->minorTicksPen) exec(new AxisSetMinorTicksPenCmd(d, pen, ki18n("%1: set minor ticks style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksLength, qreal, minorTicksLength, retransformTicks); void Axis::setMinorTicksLength(qreal minorTicksLength) { Q_D(Axis); if (minorTicksLength != d->minorTicksLength) exec(new AxisSetMinorTicksLengthCmd(d, minorTicksLength, ki18n("%1: set minor ticks length"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksOpacity, qreal, minorTicksOpacity, update); void Axis::setMinorTicksOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->minorTicksOpacity) exec(new AxisSetMinorTicksOpacityCmd(d, opacity, ki18n("%1: set minor ticks opacity"))); } //Labels STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsFormat, Axis::LabelsFormat, labelsFormat, retransformTicks); void Axis::setLabelsFormat(LabelsFormat labelsFormat) { Q_D(Axis); if (labelsFormat != d->labelsFormat) { //TODO: this part is not undo/redo-aware if (labelsFormat == Axis::FormatDecimal) d->labelsFormatDecimalOverruled = true; else d->labelsFormatDecimalOverruled = false; exec(new AxisSetLabelsFormatCmd(d, labelsFormat, ki18n("%1: set labels format"))); } } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsAutoPrecision, bool, labelsAutoPrecision, retransformTickLabelStrings); void Axis::setLabelsAutoPrecision(bool labelsAutoPrecision) { Q_D(Axis); if (labelsAutoPrecision != d->labelsAutoPrecision) exec(new AxisSetLabelsAutoPrecisionCmd(d, labelsAutoPrecision, ki18n("%1: set labels precision"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsPrecision, int, labelsPrecision, retransformTickLabelStrings); void Axis::setLabelsPrecision(int labelsPrecision) { Q_D(Axis); if (labelsPrecision != d->labelsPrecision) exec(new AxisSetLabelsPrecisionCmd(d, labelsPrecision, ki18n("%1: set labels precision"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsDateTimeFormat, QString, labelsDateTimeFormat, retransformTickLabelStrings); void Axis::setLabelsDateTimeFormat(const QString& format) { Q_D(Axis); if (format != d->labelsDateTimeFormat) exec(new AxisSetLabelsDateTimeFormatCmd(d, format, ki18n("%1: set labels datetime format"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsPosition, Axis::LabelsPosition, labelsPosition, retransformTickLabelPositions); void Axis::setLabelsPosition(LabelsPosition labelsPosition) { Q_D(Axis); if (labelsPosition != d->labelsPosition) exec(new AxisSetLabelsPositionCmd(d, labelsPosition, ki18n("%1: set labels position"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsOffset, double, labelsOffset, retransformTickLabelPositions); void Axis::setLabelsOffset(double offset) { Q_D(Axis); if (offset != d->labelsOffset) exec(new AxisSetLabelsOffsetCmd(d, offset, ki18n("%1: set label offset"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsRotationAngle, qreal, labelsRotationAngle, retransformTickLabelPositions); void Axis::setLabelsRotationAngle(qreal angle) { Q_D(Axis); if (angle != d->labelsRotationAngle) exec(new AxisSetLabelsRotationAngleCmd(d, angle, ki18n("%1: set label rotation angle"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsColor, QColor, labelsColor, update); void Axis::setLabelsColor(const QColor& color) { Q_D(Axis); if (color != d->labelsColor) exec(new AxisSetLabelsColorCmd(d, color, ki18n("%1: set label color"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsFont, QFont, labelsFont, retransformTickLabelStrings); void Axis::setLabelsFont(const QFont& font) { Q_D(Axis); if (font != d->labelsFont) exec(new AxisSetLabelsFontCmd(d, font, ki18n("%1: set label font"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsPrefix, QString, labelsPrefix, retransformTickLabelStrings); void Axis::setLabelsPrefix(const QString& prefix) { Q_D(Axis); if (prefix != d->labelsPrefix) exec(new AxisSetLabelsPrefixCmd(d, prefix, ki18n("%1: set label prefix"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsSuffix, QString, labelsSuffix, retransformTickLabelStrings); void Axis::setLabelsSuffix(const QString& suffix) { Q_D(Axis); if (suffix != d->labelsSuffix) exec(new AxisSetLabelsSuffixCmd(d, suffix, ki18n("%1: set label suffix"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsOpacity, qreal, labelsOpacity, update); void Axis::setLabelsOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->labelsOpacity) exec(new AxisSetLabelsOpacityCmd(d, opacity, ki18n("%1: set labels opacity"))); } //Major grid STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorGridPen, QPen, majorGridPen, retransformMajorGrid); void Axis::setMajorGridPen(const QPen& pen) { Q_D(Axis); if (pen != d->majorGridPen) exec(new AxisSetMajorGridPenCmd(d, pen, ki18n("%1: set major grid style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorGridOpacity, qreal, majorGridOpacity, updateGrid); void Axis::setMajorGridOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->majorGridOpacity) exec(new AxisSetMajorGridOpacityCmd(d, opacity, ki18n("%1: set major grid opacity"))); } //Minor grid STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorGridPen, QPen, minorGridPen, retransformMinorGrid); void Axis::setMinorGridPen(const QPen& pen) { Q_D(Axis); if (pen != d->minorGridPen) exec(new AxisSetMinorGridPenCmd(d, pen, ki18n("%1: set minor grid style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorGridOpacity, qreal, minorGridOpacity, updateGrid); void Axis::setMinorGridOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->minorGridOpacity) exec(new AxisSetMinorGridOpacityCmd(d, opacity, ki18n("%1: set minor grid opacity"))); } //############################################################################## //#################################### SLOTs ################################ //############################################################################## void Axis::labelChanged() { Q_D(Axis); d->recalcShapeAndBoundingRect(); } void Axis::retransformTicks() { Q_D(Axis); d->retransformTicks(); } void Axis::majorTicksColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(Axis); if (aspect == d->majorTicksColumn) { d->majorTicksColumn = nullptr; d->retransformTicks(); } } void Axis::minorTicksColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(Axis); if (aspect == d->minorTicksColumn) { d->minorTicksColumn = nullptr; d->retransformTicks(); } } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void Axis::orientationChangedSlot(QAction* action) { if (action == orientationHorizontalAction) this->setOrientation(AxisHorizontal); else this->setOrientation(AxisVertical); } void Axis::lineStyleChanged(QAction* action) { Q_D(const Axis); QPen pen = d->linePen; pen.setStyle(GuiTools::penStyleFromAction(lineStyleActionGroup, action)); this->setLinePen(pen); } void Axis::lineColorChanged(QAction* action) { Q_D(const Axis); QPen pen = d->linePen; pen.setColor(GuiTools::colorFromAction(lineColorActionGroup, action)); this->setLinePen(pen); } void Axis::visibilityChangedSlot() { Q_D(const Axis); this->setVisible(!d->isVisible()); } //##################################################################### //################### Private implementation ########################## //##################################################################### AxisPrivate::AxisPrivate(Axis* owner) : gridItem(new AxisGrid(this)), q(owner) { setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setAcceptHoverEvents(true); } QString AxisPrivate::name() const{ return q->name(); } bool AxisPrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); emit q->visibilityChanged(on); return oldValue; } QRectF AxisPrivate::boundingRect() const{ return boundingRectangle; } /*! Returns the shape of the XYCurve as a QPainterPath in local coordinates */ QPainterPath AxisPrivate::shape() const{ return axisShape; } /*! recalculates the position of the axis on the worksheet */ void AxisPrivate::retransform() { if (suppressRetransform || !plot) return; // PERFTRACE(name().toLatin1() + ", AxisPrivate::retransform()"); m_suppressRecalc = true; retransformLine(); m_suppressRecalc = false; recalcShapeAndBoundingRect(); } void AxisPrivate::retransformLine() { if (suppressRetransform) return; linePath = QPainterPath(); lines.clear(); QPointF startPoint; QPointF endPoint; if (orientation == Axis::AxisHorizontal) { if (position == Axis::AxisTop) offset = plot->yMax(); else if (position == Axis::AxisBottom) offset = plot->yMin(); else if (position == Axis::AxisCentered) offset = plot->yMin() + (plot->yMax()-plot->yMin())/2; startPoint.setX(start); startPoint.setY(offset); endPoint.setX(end); endPoint.setY(offset); } else { // vertical if (position == Axis::AxisLeft) offset = plot->xMin(); else if (position == Axis::AxisRight) offset = plot->xMax(); else if (position == Axis::AxisCentered) offset = plot->xMin() + (plot->xMax()-plot->xMin())/2; startPoint.setX(offset); startPoint.setY(start); endPoint.setY(end); endPoint.setX(offset); } lines.append(QLineF(startPoint, endPoint)); lines = cSystem->mapLogicalToScene(lines, AbstractCoordinateSystem::MarkGaps); for (const auto& line : lines) { linePath.moveTo(line.p1()); linePath.lineTo(line.p2()); } if (linePath.isEmpty()) { recalcShapeAndBoundingRect(); return; } else { retransformArrow(); retransformTicks(); } } void AxisPrivate::retransformArrow() { if (suppressRetransform) return; arrowPath = QPainterPath(); if (arrowType == Axis::NoArrow || lines.isEmpty()) { recalcShapeAndBoundingRect(); return; } if (arrowPosition == Axis::ArrowRight || arrowPosition == Axis::ArrowBoth) { const QPointF& endPoint = lines.at(lines.size()-1).p2(); this->addArrow(endPoint, 1); } if (arrowPosition == Axis::ArrowLeft || arrowPosition == Axis::ArrowBoth) { const QPointF& endPoint = lines.at(0).p1(); this->addArrow(endPoint, -1); } recalcShapeAndBoundingRect(); } void AxisPrivate::addArrow(QPointF startPoint, int direction) { static const double cos_phi = cos(M_PI/6.); if (orientation == Axis::AxisHorizontal) { QPointF endPoint = QPointF(startPoint.x() + direction*arrowSize, startPoint.y()); arrowPath.moveTo(startPoint); arrowPath.lineTo(endPoint); switch (arrowType) { case Axis::NoArrow: break; case Axis::SimpleArrowSmall: arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()-arrowSize/4*cos_phi)); arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()+arrowSize/4*cos_phi)); break; case Axis::SimpleArrowBig: arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()-arrowSize/2*cos_phi)); arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()+arrowSize/2*cos_phi)); break; case Axis::FilledArrowSmall: arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()-arrowSize/4*cos_phi)); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()+arrowSize/4*cos_phi)); arrowPath.lineTo(endPoint); break; case Axis::FilledArrowBig: arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()-arrowSize/2*cos_phi)); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()+arrowSize/2*cos_phi)); arrowPath.lineTo(endPoint); break; case Axis::SemiFilledArrowSmall: arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()-arrowSize/4*cos_phi)); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/8, endPoint.y())); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()+arrowSize/4*cos_phi)); arrowPath.lineTo(endPoint); break; case Axis::SemiFilledArrowBig: arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()-arrowSize/2*cos_phi)); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y())); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()+arrowSize/2*cos_phi)); arrowPath.lineTo(endPoint); break; } } else { //vertical orientation QPointF endPoint = QPointF(startPoint.x(), startPoint.y()-direction*arrowSize); arrowPath.moveTo(startPoint); arrowPath.lineTo(endPoint); switch (arrowType) { case Axis::NoArrow: break; case Axis::SimpleArrowSmall: arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); break; case Axis::SimpleArrowBig: arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); break; case Axis::FilledArrowSmall: arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(endPoint); break; case Axis::FilledArrowBig: arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.lineTo(endPoint); break; case Axis::SemiFilledArrowSmall: arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(QPointF(endPoint.x(), endPoint.y()+direction*arrowSize/8)); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(endPoint); break; case Axis::SemiFilledArrowBig: arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.lineTo(QPointF(endPoint.x(), endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.lineTo(endPoint); break; } } } //! helper function for retransformTicks() bool AxisPrivate::transformAnchor(QPointF* anchorPoint) { QVector points; points.append(*anchorPoint); points = cSystem->mapLogicalToScene(points); if (points.count() != 1) { // point is not mappable or in a coordinate gap return false; } else { *anchorPoint = points.at(0); return true; } } /*! recalculates the position of the axis ticks. */ void AxisPrivate::retransformTicks() { if (suppressRetransform) return; //TODO: check that start and end are > 0 for log and >=0 for sqrt, etc. majorTicksPath = QPainterPath(); minorTicksPath = QPainterPath(); majorTickPoints.clear(); minorTickPoints.clear(); tickLabelValues.clear(); if ( majorTicksNumber < 1 || (majorTicksDirection == Axis::noTicks && minorTicksDirection == Axis::noTicks) ) { retransformTickLabelPositions(); //this calls recalcShapeAndBoundingRect() return; } - //determine the spacing for the major ticks - double majorTicksSpacing = 0; + //determine the increment for the major ticks + double majorTicksIncrement = 0; int tmpMajorTicksNumber = 0; if (majorTicksType == Axis::TicksTotalNumber) { - //the total number of the major ticks is given - > determine the spacing + //the total number of major ticks is given - > determine the increment tmpMajorTicksNumber = majorTicksNumber; switch (scale) { case Axis::ScaleLinear: - majorTicksSpacing = (end-start)/(majorTicksNumber-1); + majorTicksIncrement = (end-start)/(majorTicksNumber-1); break; case Axis::ScaleLog10: - majorTicksSpacing = (log10(end)-log10(start))/(majorTicksNumber-1); + majorTicksIncrement = (log10(end)-log10(start))/(majorTicksNumber-1); break; case Axis::ScaleLog2: - majorTicksSpacing = (log(end)-log(start))/log(2)/(majorTicksNumber-1); + majorTicksIncrement = (log(end)-log(start))/log(2)/(majorTicksNumber-1); break; case Axis::ScaleLn: - majorTicksSpacing = (log(end)-log(start))/(majorTicksNumber-1); + majorTicksIncrement = (log(end)-log(start))/(majorTicksNumber-1); break; case Axis::ScaleSqrt: - majorTicksSpacing = (sqrt(end)-sqrt(start))/(majorTicksNumber-1); + majorTicksIncrement = (sqrt(end)-sqrt(start))/(majorTicksNumber-1); break; case Axis::ScaleX2: - majorTicksSpacing = (pow(end,2)-pow(start,2))/(majorTicksNumber-1); + majorTicksIncrement = (end*end - start*start)/(majorTicksNumber-1); } - } else if (majorTicksType == Axis::TicksIncrement) { - //the spacing (increment) of the major ticks is given - > determine the number - majorTicksSpacing = majorTicksIncrement; + } else if (majorTicksType == Axis::TicksSpacing) { + //the increment of the major ticks is given -> determine the number + majorTicksIncrement = majorTicksSpacing * GSL_SIGN(end-start); switch (scale) { case Axis::ScaleLinear: - tmpMajorTicksNumber = qRound((end-start)/majorTicksSpacing + 1); + tmpMajorTicksNumber = qRound((end-start)/majorTicksIncrement + 1); break; case Axis::ScaleLog10: - tmpMajorTicksNumber = qRound((log10(end)-log10(start))/majorTicksSpacing + 1); + tmpMajorTicksNumber = qRound((log10(end)-log10(start))/majorTicksIncrement + 1); break; case Axis::ScaleLog2: - tmpMajorTicksNumber = qRound((log(end)-log(start))/log(2)/majorTicksSpacing + 1); + tmpMajorTicksNumber = qRound((log(end)-log(start))/log(2)/majorTicksIncrement + 1); break; case Axis::ScaleLn: - tmpMajorTicksNumber = qRound((log(end)-log(start))/majorTicksSpacing + 1); + tmpMajorTicksNumber = qRound((log(end)-log(start))/majorTicksIncrement + 1); break; case Axis::ScaleSqrt: - tmpMajorTicksNumber = qRound((sqrt(end)-sqrt(start))/majorTicksSpacing + 1); + tmpMajorTicksNumber = qRound((sqrt(end)-sqrt(start))/majorTicksIncrement + 1); break; case Axis::ScaleX2: - tmpMajorTicksNumber = qRound((pow(end,2)-pow(start,2))/majorTicksSpacing + 1); + tmpMajorTicksNumber = qRound((end*end - start*start)/majorTicksIncrement + 1); } - } else { - //custom column was provided + } else { //custom column was provided if (majorTicksColumn) { tmpMajorTicksNumber = majorTicksColumn->rowCount(); } else { retransformTickLabelPositions(); //this calls recalcShapeAndBoundingRect() return; } } int tmpMinorTicksNumber; if (minorTicksType == Axis::TicksTotalNumber) tmpMinorTicksNumber = minorTicksNumber; - else if (minorTicksType == Axis::TicksIncrement) + else if (minorTicksType == Axis::TicksSpacing) tmpMinorTicksNumber = (end - start)/ (majorTicksNumber - 1)/minorTicksIncrement - 1; else (minorTicksColumn) ? tmpMinorTicksNumber = minorTicksColumn->rowCount() : tmpMinorTicksNumber = 0; QPointF anchorPoint; QPointF startPoint; QPointF endPoint; qreal majorTickPos = 0.0; qreal minorTickPos; qreal nextMajorTickPos = 0.0; const int xDirection = cSystem->xDirection(); const int yDirection = cSystem->yDirection(); const double middleX = plot->xMin() + (plot->xMax() - plot->xMin())/2; const double middleY = plot->yMin() + (plot->yMax() - plot->yMin())/2; bool valid; for (int iMajor = 0; iMajor < tmpMajorTicksNumber; iMajor++) { //calculate major tick's position if (majorTicksType != Axis::TicksCustomColumn) { switch (scale) { case Axis::ScaleLinear: - majorTickPos = start + majorTicksSpacing*iMajor; - nextMajorTickPos = start + majorTicksSpacing*(iMajor+1); + majorTickPos = start + majorTicksIncrement*iMajor; + nextMajorTickPos = start + majorTicksIncrement*(iMajor+1); break; case Axis::ScaleLog10: - majorTickPos = pow(10, log10(start) + majorTicksSpacing*iMajor); - nextMajorTickPos = pow(10, log10(start) + majorTicksSpacing*(iMajor+1)); + majorTickPos = pow(10, log10(start) + majorTicksIncrement*iMajor); + nextMajorTickPos = pow(10, log10(start) + majorTicksIncrement*(iMajor+1)); break; case Axis::ScaleLog2: - majorTickPos = pow(2, log(start)/log(2) + majorTicksSpacing*iMajor); - nextMajorTickPos = pow(2, log(start)/log(2) + majorTicksSpacing*(iMajor+1)); + majorTickPos = pow(2, log(start)/log(2) + majorTicksIncrement*iMajor); + nextMajorTickPos = pow(2, log(start)/log(2) + majorTicksIncrement*(iMajor+1)); break; case Axis::ScaleLn: - majorTickPos = exp(log(start) + majorTicksSpacing*iMajor); - nextMajorTickPos = exp(log(start) + majorTicksSpacing*(iMajor+1)); + majorTickPos = exp(log(start) + majorTicksIncrement*iMajor); + nextMajorTickPos = exp(log(start) + majorTicksIncrement*(iMajor+1)); break; case Axis::ScaleSqrt: - majorTickPos = pow(sqrt(start) + majorTicksSpacing*iMajor, 2); - nextMajorTickPos = pow(sqrt(start) + majorTicksSpacing*(iMajor+1), 2); + majorTickPos = pow(sqrt(start) + majorTicksIncrement*iMajor, 2); + nextMajorTickPos = pow(sqrt(start) + majorTicksIncrement*(iMajor+1), 2); break; case Axis::ScaleX2: - majorTickPos = sqrt(sqrt(start) + majorTicksSpacing*iMajor); - nextMajorTickPos = sqrt(sqrt(start) + majorTicksSpacing*(iMajor+1)); + majorTickPos = sqrt(sqrt(start) + majorTicksIncrement*iMajor); + nextMajorTickPos = sqrt(sqrt(start) + majorTicksIncrement*(iMajor+1)); break; } } else { if (!majorTicksColumn->isValid(iMajor) || majorTicksColumn->isMasked(iMajor)) continue; majorTickPos = majorTicksColumn->valueAt(iMajor); } //calculate start and end points for major tick's line if (majorTicksDirection != Axis::noTicks ) { if (orientation == Axis::AxisHorizontal) { anchorPoint.setX(majorTickPos); anchorPoint.setY(offset); valid = transformAnchor(&anchorPoint); if (valid) { if (offset < middleY) { startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? yDirection * majorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? -yDirection * majorTicksLength : 0); } else { startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? yDirection * majorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? -yDirection * majorTicksLength : 0); } } } else { // vertical anchorPoint.setY(majorTickPos); anchorPoint.setX(offset); valid = transformAnchor(&anchorPoint); if (valid) { if (offset < middleX) { startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? xDirection * majorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? -xDirection * majorTicksLength : 0, 0); } else { startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? xDirection * majorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? -xDirection * majorTicksLength : 0, 0); } } } double value = scalingFactor * majorTickPos + zeroOffset; //if custom column is used, we can have duplicated values in it and we need only unique values if (majorTicksType == Axis::TicksCustomColumn && tickLabelValues.indexOf(value) != -1) valid = false; //add major tick's line to the painter path if (valid) { majorTicksPath.moveTo(startPoint); majorTicksPath.lineTo(endPoint); majorTickPoints << anchorPoint; tickLabelValues << value; } } //minor ticks - if ((Axis::noTicks != minorTicksDirection) && (tmpMajorTicksNumber > 1) && (tmpMinorTicksNumber > 0) && (iMajor 1) && (tmpMinorTicksNumber > 0) && (iMajor < tmpMajorTicksNumber-1)) { //minor ticks are placed at equidistant positions independent of the selected scaling for the major ticks positions - double minorTicksSpacing = (nextMajorTickPos-majorTickPos)/(tmpMinorTicksNumber+1); + double minorTicksIncrement = (nextMajorTickPos-majorTickPos)/(tmpMinorTicksNumber+1); for (int iMinor = 0; iMinor < tmpMinorTicksNumber; iMinor++) { //calculate minor tick's position if (minorTicksType != Axis::TicksCustomColumn) { - minorTickPos = majorTickPos + (iMinor+1)*minorTicksSpacing; + minorTickPos = majorTickPos + (iMinor+1)*minorTicksIncrement; } else { if (!minorTicksColumn->isValid(iMinor) || minorTicksColumn->isMasked(iMinor)) continue; minorTickPos = minorTicksColumn->valueAt(iMinor); //in the case a custom column is used for the minor ticks, we draw them _once_ for the whole range of the axis. //execute the minor ticks loop only once. if (iMajor > 0) break; } //calculate start and end points for minor tick's line if (orientation == Axis::AxisHorizontal) { anchorPoint.setX(minorTickPos); anchorPoint.setY(offset); valid = transformAnchor(&anchorPoint); if (valid) { if (offset < middleY) { startPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksIn) ? yDirection * minorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksOut) ? -yDirection * minorTicksLength : 0); } else { startPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksOut) ? yDirection * minorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksIn) ? -yDirection * minorTicksLength : 0); } } } else { // vertical anchorPoint.setY(minorTickPos); anchorPoint.setX(offset); valid = transformAnchor(&anchorPoint); if (valid) { if (offset < middleX) { startPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksIn) ? xDirection * minorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksOut) ? -xDirection * minorTicksLength : 0, 0); } else { startPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksOut) ? xDirection * minorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksIn) ? -xDirection * minorTicksLength : 0, 0); } } } //add minor tick's line to the painter path if (valid) { minorTicksPath.moveTo(startPoint); minorTicksPath.lineTo(endPoint); minorTickPoints << anchorPoint; } } } } //tick positions where changed -> update the position of the tick labels and grid lines retransformTickLabelStrings(); retransformMajorGrid(); retransformMinorGrid(); } /*! creates the tick label strings starting with the most optimal (=the smallest possible number of float digits) precision for the floats */ void AxisPrivate::retransformTickLabelStrings() { if (suppressRetransform) return; -// DEBUG("AxisPrivate::retransformTickLabelStrings()"); + //DEBUG("AxisPrivate::retransformTickLabelStrings()"); if (labelsAutoPrecision) { //check, whether we need to increase the current precision int newPrecision = upperLabelsPrecision(labelsPrecision); - if (newPrecision!= labelsPrecision) { + if (newPrecision != labelsPrecision) { labelsPrecision = newPrecision; emit q->labelsPrecisionChanged(labelsPrecision); } else { //check, whether we can reduce the current precision newPrecision = lowerLabelsPrecision(labelsPrecision); - if (newPrecision!= labelsPrecision) { + if (newPrecision != labelsPrecision) { labelsPrecision = newPrecision; emit q->labelsPrecisionChanged(labelsPrecision); } } } -// DEBUG("labelsPrecision =" << labelsPrecision); + //DEBUG("labelsPrecision =" << labelsPrecision); //automatically switch from 'decimal' to 'scientific' format for big numbers (>10^4) //and back to decimal when the numbers get smaller after the auto-switch again if (labelsFormat == Axis::FormatDecimal && !labelsFormatDecimalOverruled) { for (auto value : tickLabelValues) { if (std::abs(value) > 1e4) { labelsFormat = Axis::FormatScientificE; emit q->labelsFormatChanged(labelsFormat); labelsFormatAutoChanged = true; break; } } } else if (labelsFormatAutoChanged ) { //check whether we still have big numbers bool changeBack = true; for (auto value : tickLabelValues) { if (std::abs(value) > 1e4) { changeBack = false; break; } } if (changeBack) { labelsFormatAutoChanged = false; labelsFormat = Axis::FormatDecimal; emit q->labelsFormatChanged(labelsFormat); } } tickLabelStrings.clear(); QString str; if ( (orientation == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (orientation == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ) { if (labelsFormat == Axis::FormatDecimal) { QString nullStr = QString::number(0, 'f', labelsPrecision); for (const auto value : tickLabelValues) { str = QString::number(value, 'f', labelsPrecision); if (str == "-" + nullStr) str = nullStr; str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatScientificE) { QString nullStr = QString::number(0, 'e', labelsPrecision); for (const auto value : tickLabelValues) { str = QString::number(value, 'e', labelsPrecision); if (str == "-" + nullStr) str = nullStr; str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatPowers10) { for (const auto value : tickLabelValues) { str = "10" + QString::number(log10(value), 'f', labelsPrecision) + ""; str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatPowers2) { for (const auto value : tickLabelValues) { str = "2" + QString::number(log2(value), 'f', labelsPrecision) + ""; str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatPowersE) { for (const auto value : tickLabelValues) { str = "e" + QString::number(log(value), 'f', labelsPrecision) + ""; str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatMultipliesPi) { for (const auto value : tickLabelValues) { str = "" + QString::number(value / M_PI, 'f', labelsPrecision) + "" + QChar(0x03C0); str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } } else { for (const auto value : tickLabelValues) { QDateTime dateTime; dateTime.setMSecsSinceEpoch(value); str = dateTime.toString(labelsDateTimeFormat); str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } //recalculate the position of the tick labels retransformTickLabelPositions(); } /*! returns the smallest upper limit for the precision where no duplicates for the tick label float occur. */ int AxisPrivate::upperLabelsPrecision(int precision) { -// DEBUG("AxisPrivate::upperLabelsPrecision() precision =" << precision); + //DEBUG("AxisPrivate::upperLabelsPrecision() precision =" << precision); //round float to the current precision and look for duplicates. //if there are duplicates, increase the precision. QVector tempValues; for (const auto value : tickLabelValues) tempValues.append( nsl_math_round_places(value, precision) ); for (int i = 0; i < tempValues.size(); ++i) { for (int j = 0; j < tempValues.size(); ++j) { if (i == j) continue; if (tempValues.at(i) == tempValues.at(j)) { //duplicate for the current precision found, increase the precision and check again return upperLabelsPrecision(precision + 1); } } } //no duplicates for the current precision found: return the current value -// DEBUG(" upper precision = " << precision); + //DEBUG(" upper precision = " << precision); return precision; } /*! returns highest lower limit for the precision where no duplicates for the tick label float occur. */ int AxisPrivate::lowerLabelsPrecision(int precision) { // DEBUG("AxisPrivate::lowerLabelsPrecision() precision =" << precision); //round float to the current precision and look for duplicates. //if there are duplicates, decrease the precision. QVector tempValues; for (const auto value : tickLabelValues) tempValues.append( nsl_math_round_places(value, precision-1) ); for (int i = 0; i < tempValues.size(); ++i) { for (int j = 0; j < tempValues.size(); ++j) { if (i == j) continue; if (tempValues.at(i) == tempValues.at(j)) { //duplicate found for the reduced precision //-> current precision cannot be reduced, return the current value // DEBUG(" lower precision = " << precision); return precision; } } } //no duplicates found, reduce further, and check again if (precision == 0) return 0; else return lowerLabelsPrecision(precision - 1); } /*! recalculates the position of the tick labels. Called when the geometry related properties (position, offset, font size, suffix, prefix) of the labels are changed. */ void AxisPrivate::retransformTickLabelPositions() { tickLabelPoints.clear(); if (majorTicksDirection == Axis::noTicks || labelsPosition == Axis::NoLabels) { recalcShapeAndBoundingRect(); return; } QFontMetrics fm(labelsFont); float width = 0; float height = fm.ascent(); QPointF pos; const double middleX = plot->xMin() + (plot->xMax() - plot->xMin())/2; const double middleY = plot->yMin() + (plot->yMax() - plot->yMin())/2; const int xDirection = cSystem->xDirection(); const int yDirection = cSystem->yDirection(); QPointF startPoint, endPoint, anchorPoint; QTextDocument td; td.setDefaultFont(labelsFont); double cosinus = cos(labelsRotationAngle * M_PI / 180); // calculate only one time double sinus = sin(labelsRotationAngle * M_PI / 180); // calculate only one time for ( int i = 0; i < majorTickPoints.size(); i++ ) { if ((orientation == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (orientation == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric)) { if (labelsFormat == Axis::FormatDecimal || labelsFormat == Axis::FormatScientificE) { width = fm.boundingRect(tickLabelStrings.at(i)).width(); } else { td.setHtml(tickLabelStrings.at(i)); width = td.size().width(); height = td.size().height(); } } else { // Datetime width = fm.boundingRect(tickLabelStrings.at(i)).width(); } double diffx = cosinus * width; double diffy = sinus * width; anchorPoint = majorTickPoints.at(i); //center align all labels with respect to the end point of the tick line if (orientation == Axis::AxisHorizontal) { if (offset < middleY) { startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? yDirection * majorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? -yDirection * majorTicksLength : 0); } else { startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? yDirection * majorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? -yDirection * majorTicksLength : 0); } // for rotated labels (angle is not zero), align label's corner at the position of the tick if (abs(labelsRotationAngle) > 179.999 && abs(labelsRotationAngle) < 180.009) { // +-180° if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() + width/2); pos.setY( endPoint.y() + labelsOffset ); } else { pos.setX( startPoint.x() + width/2); pos.setY( startPoint.y() - height + labelsOffset ); } } else if (labelsRotationAngle <= -0.01) { // [-0.01°, -180°) if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() + sinus * height/2); pos.setY( endPoint.y() + labelsOffset + cosinus * height/2); } else { pos.setX( startPoint.x() + sinus * height/2 - diffx); pos.setY( startPoint.y() + labelsOffset + cosinus * height/2 + diffy); } } else if (labelsRotationAngle >= 0.01) { // [0.01°, 180°) if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() - diffx + sinus * height/2); pos.setY( endPoint.y() + labelsOffset + diffy + cosinus * height/2); } else { pos.setX( startPoint.x() + sinus * height/2); pos.setY( startPoint.y() + labelsOffset + cosinus * height/2); } } else { // 0° if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() - width/2); pos.setY( endPoint.y() + height + labelsOffset ); } else { pos.setX( startPoint.x() - width/2); pos.setY( startPoint.y() + labelsOffset ); } } // ---------------------- vertical ------------------------- } else { if (offset < middleX) { startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? xDirection * majorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? -xDirection * majorTicksLength : 0, 0); } else { startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? xDirection * majorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? -xDirection * majorTicksLength : 0, 0); } if (labelsRotationAngle >= 89.999 && labelsRotationAngle <= 90.009) { // +90° if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() - labelsOffset); pos.setY( endPoint.y() + width/2 ); } else { pos.setX( startPoint.x() - labelsOffset); pos.setY( startPoint.y() + width/2); } } else if (labelsRotationAngle >= -90.999 && labelsRotationAngle <= -89.009) { // -90° if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() - labelsOffset - height); pos.setY( endPoint.y() - width/2 ); } else { pos.setX( startPoint.x() - labelsOffset); pos.setY( startPoint.y() - width/2 ); } } else if (abs(labelsRotationAngle) > 179.999 && abs(labelsRotationAngle) < 180.009) { // +-180° if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() - labelsOffset); pos.setY( endPoint.y() - height/2); } else { pos.setX( startPoint.x() - labelsOffset + width); pos.setY( startPoint.y() - height/2 ); } } else if (abs(labelsRotationAngle) >= 0.01 && abs(labelsRotationAngle) < 90.01) { // [0.01°, 90°) if (labelsPosition == Axis::LabelsOut) { // left pos.setX( endPoint.x() - labelsOffset - diffx + sinus * height/2); pos.setY( endPoint.y() + cosinus * height/2 + diffy); } else { pos.setX( startPoint.x() - labelsOffset + sinus * height/2); pos.setY( startPoint.y() + cosinus * height/2); } } else if (abs(labelsRotationAngle) >= 90.01 && abs(labelsRotationAngle) < 180) { // [90.01, 180) if (labelsPosition == Axis::LabelsOut) { // left pos.setX( endPoint.x() - labelsOffset + sinus * height/2); pos.setY( endPoint.y() + cosinus * height/2); } else { pos.setX( startPoint.x() - labelsOffset - diffx + sinus * height/2); pos.setY( startPoint.y() + diffy + cosinus * height/2); } } else { // 0° if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() - width - labelsOffset); pos.setY( endPoint.y() + height/2 ); } else { pos.setX( startPoint.x() - labelsOffset); pos.setY( startPoint.y() + height/2 ); } } } tickLabelPoints << pos; } recalcShapeAndBoundingRect(); } void AxisPrivate::retransformMajorGrid() { if (suppressRetransform) return; majorGridPath = QPainterPath(); if (majorGridPen.style() == Qt::NoPen || majorTickPoints.size() == 0) { recalcShapeAndBoundingRect(); return; } //major tick points are already in scene coordinates, convert them back to logical... //TODO: mapping should work without SuppressPageClipping-flag, check float comparisons in the map-function. //Currently, grid lines disappear somtimes without this flag QVector logicalMajorTickPoints = cSystem->mapSceneToLogical(majorTickPoints, AbstractCoordinateSystem::SuppressPageClipping); if (logicalMajorTickPoints.isEmpty()) return; //TODO: //when iterating over all grid lines, skip the first and the last points for auto scaled axes, //since we don't want to paint any grid lines at the plot boundaries bool skipLowestTick, skipUpperTick; if (orientation == Axis::AxisHorizontal) { //horizontal axis skipLowestTick = qFuzzyCompare(logicalMajorTickPoints.at(0).x(), plot->xMin()); skipUpperTick = qFuzzyCompare(logicalMajorTickPoints.at(logicalMajorTickPoints.size()-1).x(), plot->xMax()); } else { skipLowestTick = qFuzzyCompare(logicalMajorTickPoints.at(0).y(), plot->yMin()); skipUpperTick = qFuzzyCompare(logicalMajorTickPoints.at(logicalMajorTickPoints.size()-1).y(), plot->yMax()); } int start, end; if (skipLowestTick) { if (logicalMajorTickPoints.size() > 1) start = 1; else start = 0; } else { start = 0; } if (skipUpperTick) { if (logicalMajorTickPoints.size() > 1) end = logicalMajorTickPoints.size() - 1; else end = 0; } else { end = logicalMajorTickPoints.size(); } QVector lines; if (orientation == Axis::AxisHorizontal) { //horizontal axis double yMin = plot->yMin(); double yMax = plot->yMax(); for (int i = start; i < end; ++i) { const QPointF& point = logicalMajorTickPoints.at(i); lines.append( QLineF(point.x(), yMin, point.x(), yMax) ); } } else { //vertical axis double xMin = plot->xMin(); double xMax = plot->xMax(); //skip the first and the last points, since we don't want to paint any grid lines at the plot boundaries for (int i = start; i < end; ++i) { const QPointF& point = logicalMajorTickPoints.at(i); lines.append( QLineF(xMin, point.y(), xMax, point.y()) ); } } lines = cSystem->mapLogicalToScene(lines, AbstractCoordinateSystem::SuppressPageClipping); for (const auto& line : lines) { majorGridPath.moveTo(line.p1()); majorGridPath.lineTo(line.p2()); } recalcShapeAndBoundingRect(); } void AxisPrivate::retransformMinorGrid() { if (suppressRetransform) return; minorGridPath = QPainterPath(); if (minorGridPen.style() == Qt::NoPen) { recalcShapeAndBoundingRect(); return; } //minor tick points are already in scene coordinates, convert them back to logical... //TODO: mapping should work without SuppressPageClipping-flag, check float comparisons in the map-function. //Currently, grid lines disappear somtimes without this flag QVector logicalMinorTickPoints = cSystem->mapSceneToLogical(minorTickPoints, AbstractCoordinateSystem::SuppressPageClipping); QVector lines; if (orientation == Axis::AxisHorizontal) { //horizontal axis double yMin = plot->yMin(); double yMax = plot->yMax(); for (const auto point : logicalMinorTickPoints) lines.append( QLineF(point.x(), yMin, point.x(), yMax) ); } else { //vertical axis double xMin = plot->xMin(); double xMax = plot->xMax(); for (const auto point: logicalMinorTickPoints) lines.append( QLineF(xMin, point.y(), xMax, point.y()) ); } lines = cSystem->mapLogicalToScene(lines, AbstractCoordinateSystem::SuppressPageClipping); for (const auto& line : lines) { minorGridPath.moveTo(line.p1()); minorGridPath.lineTo(line.p2()); } recalcShapeAndBoundingRect(); } /*! * called when the opacity of the grid was changes, update the grid graphics item */ //TODO: this function is only needed for loaded projects where update() doesn't seem to be enough //and we have to call gridItem->update() explicitly. //This is not required for newly created plots/axes. Why is this difference? void AxisPrivate::updateGrid() { gridItem->update(); } void AxisPrivate::recalcShapeAndBoundingRect() { if (m_suppressRecalc) return; prepareGeometryChange(); if (linePath.isEmpty()) { axisShape = QPainterPath(); boundingRectangle = QRectF(); title->setPositionInvalid(true); if (plot) plot->prepareGeometryChange(); return; } else { title->setPositionInvalid(false); } axisShape = WorksheetElement::shapeFromPath(linePath, linePen); axisShape.addPath(WorksheetElement::shapeFromPath(arrowPath, linePen)); axisShape.addPath(WorksheetElement::shapeFromPath(majorTicksPath, majorTicksPen)); axisShape.addPath(WorksheetElement::shapeFromPath(minorTicksPath, minorTicksPen)); QPainterPath tickLabelsPath = QPainterPath(); if (labelsPosition != Axis::NoLabels) { QTransform trafo; QPainterPath tempPath; QFontMetrics fm(labelsFont); QTextDocument td; td.setDefaultFont(labelsFont); for (int i = 0; i < tickLabelPoints.size(); i++) { tempPath = QPainterPath(); if (labelsFormat == Axis::FormatDecimal || labelsFormat == Axis::FormatScientificE) { tempPath.addRect(fm.boundingRect(tickLabelStrings.at(i))); } else { td.setHtml(tickLabelStrings.at(i)); tempPath.addRect(QRectF(0, -td.size().height(), td.size().width(), td.size().height())); } trafo.reset(); trafo.translate( tickLabelPoints.at(i).x(), tickLabelPoints.at(i).y() ); trafo.rotate(-labelsRotationAngle); tempPath = trafo.map(tempPath); tickLabelsPath.addPath(WorksheetElement::shapeFromPath(tempPath, linePen)); } axisShape.addPath(WorksheetElement::shapeFromPath(tickLabelsPath, QPen())); } //add title label, if available if ( title->isVisible() && !title->text().text.isEmpty() ) { const QRectF& titleRect = title->graphicsItem()->boundingRect(); if (titleRect.width() != 0.0 && titleRect.height() != 0.0) { //determine the new position of the title label: //we calculate the new position here and not in retransform(), //since it depends on the size and position of the tick labels, tickLabelsPath, available here. QRectF rect = linePath.boundingRect(); qreal offsetX = titleOffsetX - labelsOffset; //the distance to the axis line qreal offsetY = titleOffsetY - labelsOffset; //the distance to the axis line if (orientation == Axis::AxisHorizontal) { offsetY -= titleRect.height()/2; if (labelsPosition == Axis::LabelsOut) offsetY -= tickLabelsPath.boundingRect().height(); title->setPosition( QPointF( (rect.topLeft().x() + rect.topRight().x())/2 + titleOffsetX, rect.bottomLeft().y() - offsetY ) ); } else { offsetX -= titleRect.width()/2; if (labelsPosition == Axis::LabelsOut) offsetX -= tickLabelsPath.boundingRect().width(); title->setPosition( QPointF( rect.topLeft().x() + offsetX, (rect.topLeft().y() + rect.bottomLeft().y())/2 - titleOffsetY) ); } axisShape.addPath(WorksheetElement::shapeFromPath(title->graphicsItem()->mapToParent(title->graphicsItem()->shape()), linePen)); } } boundingRectangle = axisShape.boundingRect(); //if the axis goes beyond the current bounding box of the plot (too high offset is used, too long labels etc.) //request a prepareGeometryChange() for the plot in order to properly keep track of geometry changes if (plot) plot->prepareGeometryChange(); } /*! paints the content of the axis. Reimplemented from \c QGraphicsItem. \sa QGraphicsItem::paint() */ void AxisPrivate::paint(QPainter *painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option) Q_UNUSED(widget) if (!isVisible()) return; if (linePath.isEmpty()) return; //draw the line if (linePen.style() != Qt::NoPen) { painter->setOpacity(lineOpacity); painter->setPen(linePen); painter->setBrush(Qt::SolidPattern); painter->drawPath(linePath); //draw the arrow if (arrowType != Axis::NoArrow) painter->drawPath(arrowPath); } //draw the major ticks if (majorTicksDirection != Axis::noTicks) { painter->setOpacity(majorTicksOpacity); painter->setPen(majorTicksPen); painter->setBrush(Qt::NoBrush); painter->drawPath(majorTicksPath); } //draw the minor ticks if (minorTicksDirection != Axis::noTicks) { painter->setOpacity(minorTicksOpacity); painter->setPen(minorTicksPen); painter->setBrush(Qt::NoBrush); painter->drawPath(minorTicksPath); } // draw tick labels if (labelsPosition != Axis::NoLabels) { painter->setOpacity(labelsOpacity); painter->setPen(QPen(labelsColor)); painter->setFont(labelsFont); QTextDocument td; td.setDefaultFont(labelsFont); if ((orientation == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (orientation == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric)) { for (int i = 0; i < tickLabelPoints.size(); i++) { painter->translate(tickLabelPoints.at(i)); painter->save(); painter->rotate(-labelsRotationAngle); if (labelsFormat == Axis::FormatDecimal || labelsFormat == Axis::FormatScientificE) { painter->drawText(QPoint(0,0), tickLabelStrings.at(i)); } else { td.setHtml(tickLabelStrings.at(i)); painter->translate(0, -td.size().height()); td.drawContents(painter); } painter->restore(); painter->translate(-tickLabelPoints.at(i)); } } else { // datetime for (int i = 0; i < tickLabelPoints.size(); i++) { painter->translate(tickLabelPoints.at(i)); painter->save(); painter->rotate(-labelsRotationAngle); painter->drawText(QPoint(0,0), tickLabelStrings.at(i)); painter->restore(); painter->translate(-tickLabelPoints.at(i)); } } } if (m_hovered && !isSelected() && !m_printing) { painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); painter->drawPath(axisShape); } if (isSelected() && !m_printing) { painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); painter->drawPath(axisShape); } } void AxisPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } void AxisPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { if (!isSelected()) { m_hovered = true; emit q->hovered(); update(axisShape.boundingRect()); } } void AxisPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { if (m_hovered) { m_hovered = false; emit q->unhovered(); update(axisShape.boundingRect()); } } void AxisPrivate::setPrinting(bool on) { m_printing = on; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void Axis::save(QXmlStreamWriter* writer) const{ Q_D(const Axis); writer->writeStartElement( "axis" ); writeBasicAttributes( writer ); writeCommentElement( writer ); //general writer->writeStartElement( "general" ); writer->writeAttribute( "autoScale", QString::number(d->autoScale) ); writer->writeAttribute( "orientation", QString::number(d->orientation) ); writer->writeAttribute( "position", QString::number(d->position) ); writer->writeAttribute( "scale", QString::number(d->scale) ); writer->writeAttribute( "offset", QString::number(d->offset) ); writer->writeAttribute( "start", QString::number(d->start) ); writer->writeAttribute( "end", QString::number(d->end) ); writer->writeAttribute( "scalingFactor", QString::number(d->scalingFactor) ); writer->writeAttribute( "zeroOffset", QString::number(d->zeroOffset) ); writer->writeAttribute( "titleOffsetX", QString::number(d->titleOffsetX) ); writer->writeAttribute( "titleOffsetY", QString::number(d->titleOffsetY) ); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); //label d->title->save( writer ); //line writer->writeStartElement( "line" ); WRITE_QPEN(d->linePen); writer->writeAttribute( "opacity", QString::number(d->lineOpacity) ); writer->writeAttribute( "arrowType", QString::number(d->arrowType) ); writer->writeAttribute( "arrowPosition", QString::number(d->arrowPosition) ); writer->writeAttribute( "arrowSize", QString::number(d->arrowSize) ); writer->writeEndElement(); //major ticks writer->writeStartElement( "majorTicks" ); writer->writeAttribute( "direction", QString::number(d->majorTicksDirection) ); writer->writeAttribute( "type", QString::number(d->majorTicksType) ); writer->writeAttribute( "number", QString::number(d->majorTicksNumber) ); - writer->writeAttribute( "increment", QString::number(d->majorTicksIncrement) ); + writer->writeAttribute( "increment", QString::number(d->majorTicksSpacing) ); WRITE_COLUMN(d->majorTicksColumn, majorTicksColumn); writer->writeAttribute( "length", QString::number(d->majorTicksLength) ); WRITE_QPEN(d->majorTicksPen); writer->writeAttribute( "opacity", QString::number(d->majorTicksOpacity) ); writer->writeEndElement(); //minor ticks writer->writeStartElement( "minorTicks" ); writer->writeAttribute( "direction", QString::number(d->minorTicksDirection) ); writer->writeAttribute( "type", QString::number(d->minorTicksType) ); writer->writeAttribute( "number", QString::number(d->minorTicksNumber) ); writer->writeAttribute( "increment", QString::number(d->minorTicksIncrement) ); WRITE_COLUMN(d->minorTicksColumn, minorTicksColumn); writer->writeAttribute( "length", QString::number(d->minorTicksLength) ); WRITE_QPEN(d->minorTicksPen); writer->writeAttribute( "opacity", QString::number(d->minorTicksOpacity) ); writer->writeEndElement(); //extra ticks //labels writer->writeStartElement( "labels" ); writer->writeAttribute( "position", QString::number(d->labelsPosition) ); writer->writeAttribute( "offset", QString::number(d->labelsOffset) ); writer->writeAttribute( "rotation", QString::number(d->labelsRotationAngle) ); writer->writeAttribute( "format", QString::number(d->labelsFormat) ); writer->writeAttribute( "precision", QString::number(d->labelsPrecision) ); writer->writeAttribute( "autoPrecision", QString::number(d->labelsAutoPrecision) ); writer->writeAttribute( "dateTimeFormat", d->labelsDateTimeFormat ); WRITE_QCOLOR(d->labelsColor); WRITE_QFONT(d->labelsFont); writer->writeAttribute( "prefix", d->labelsPrefix ); writer->writeAttribute( "suffix", d->labelsSuffix ); writer->writeAttribute( "opacity", QString::number(d->labelsOpacity) ); writer->writeEndElement(); //grid writer->writeStartElement( "majorGrid" ); WRITE_QPEN(d->majorGridPen); writer->writeAttribute( "opacity", QString::number(d->majorGridOpacity) ); writer->writeEndElement(); writer->writeStartElement( "minorGrid" ); WRITE_QPEN(d->minorGridPen); writer->writeAttribute( "opacity", QString::number(d->minorGridOpacity) ); writer->writeEndElement(); writer->writeEndElement(); // close "axis" section } //! Load from XML bool Axis::load(XmlStreamReader* reader, bool preview) { Q_D(Axis); if (!readBasicAttributes(reader)) return false; KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "axis") break; if (!reader->isStartElement()) continue; if (!preview && reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "general") { attribs = reader->attributes(); READ_INT_VALUE("autoScale", autoScale, bool); READ_INT_VALUE("orientation", orientation, Axis::AxisOrientation); READ_INT_VALUE("position", position, Axis::AxisPosition); READ_INT_VALUE("scale", scale, Axis::AxisScale); READ_DOUBLE_VALUE("offset", offset); READ_DOUBLE_VALUE("start", start); READ_DOUBLE_VALUE("end", end); READ_DOUBLE_VALUE("scalingFactor", scalingFactor); READ_DOUBLE_VALUE("zeroOffset", zeroOffset); READ_DOUBLE_VALUE("titleOffsetX", titleOffsetX); READ_DOUBLE_VALUE("titleOffsetY", titleOffsetY); str = attribs.value("visible").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (reader->name() == "textLabel") { d->title->load(reader, preview); } else if (!preview && reader->name() == "line") { attribs = reader->attributes(); READ_QPEN(d->linePen); READ_DOUBLE_VALUE("opacity", lineOpacity); READ_INT_VALUE("arrowType", arrowType, Axis::ArrowType); READ_INT_VALUE("arrowPosition", arrowPosition, Axis::ArrowPosition); READ_DOUBLE_VALUE("arrowSize", arrowSize); } else if (!preview && reader->name() == "majorTicks") { attribs = reader->attributes(); READ_INT_VALUE("direction", majorTicksDirection, Axis::TicksDirection); READ_INT_VALUE("type", majorTicksType, Axis::TicksType); READ_INT_VALUE("number", majorTicksNumber, int); - READ_DOUBLE_VALUE("increment", majorTicksIncrement); + READ_DOUBLE_VALUE("increment", majorTicksSpacing); READ_COLUMN(majorTicksColumn); READ_DOUBLE_VALUE("length", majorTicksLength); READ_QPEN(d->majorTicksPen); READ_DOUBLE_VALUE("opacity", majorTicksOpacity); } else if (!preview && reader->name() == "minorTicks") { attribs = reader->attributes(); READ_INT_VALUE("direction", minorTicksDirection, Axis::TicksDirection); READ_INT_VALUE("type", minorTicksType, Axis::TicksType); READ_INT_VALUE("number", minorTicksNumber, int); READ_DOUBLE_VALUE("increment", minorTicksIncrement); READ_COLUMN(minorTicksColumn); READ_DOUBLE_VALUE("length", minorTicksLength); READ_QPEN(d->minorTicksPen); READ_DOUBLE_VALUE("opacity", minorTicksOpacity); } else if (!preview && reader->name() == "labels") { attribs = reader->attributes(); READ_INT_VALUE("position", labelsPosition, Axis::LabelsPosition); READ_DOUBLE_VALUE("offset", labelsOffset); READ_DOUBLE_VALUE("rotation", labelsRotationAngle); READ_INT_VALUE("format", labelsFormat, Axis::LabelsFormat); READ_INT_VALUE("precision", labelsPrecision, int); READ_INT_VALUE("autoPrecision", labelsAutoPrecision, bool); d->labelsDateTimeFormat = attribs.value("dateTimeFormat").toString(); READ_QCOLOR(d->labelsColor); READ_QFONT(d->labelsFont); //don't produce any warning if no prefix or suffix is set (empty string is allowed here in xml) d->labelsPrefix = attribs.value("prefix").toString(); d->labelsSuffix = attribs.value("suffix").toString(); READ_DOUBLE_VALUE("opacity", labelsOpacity); } else if (!preview && reader->name() == "majorGrid") { attribs = reader->attributes(); READ_QPEN(d->majorGridPen); READ_DOUBLE_VALUE("opacity", majorGridOpacity); } else if (!preview && reader->name() == "minorGrid") { attribs = reader->attributes(); READ_QPEN(d->minorGridPen); READ_DOUBLE_VALUE("opacity", minorGridOpacity); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } return true; } //############################################################################## //######################### Theme management ################################## //############################################################################## void Axis::loadThemeConfig(const KConfig& config) { const KConfigGroup group = config.group("Axis"); //we don't want to show the major and minor grid lines for non-first horizontal/vertical axes //determine the index of the axis among other axes having the same orientation bool firstAxis = true; for (const auto* axis : parentAspect()->children()) { if (orientation() == axis->orientation()) { if (axis == this) { break; } else { firstAxis = false; break; } } } QPen p; // Tick label this->setLabelsColor(group.readEntry("LabelsFontColor",(QColor) this->labelsColor())); this->setLabelsOpacity(group.readEntry("LabelsOpacity",this->labelsOpacity())); //Line this->setLineOpacity(group.readEntry("LineOpacity",this->lineOpacity())); p.setColor(group.readEntry("LineColor", (QColor) this->linePen().color())); p.setStyle((Qt::PenStyle)group.readEntry("LineStyle",(int) this->linePen().style())); p.setWidthF(group.readEntry("LineWidth", this->linePen().widthF())); this->setLinePen(p); //Major ticks this->setMajorGridOpacity(group.readEntry("MajorGridOpacity", this->majorGridOpacity())); p.setColor(group.readEntry("MajorGridColor",(QColor) this->majorGridPen().color())); if (firstAxis) p.setStyle((Qt::PenStyle)group.readEntry("MajorGridStyle",(int) this->majorGridPen().style())); else p.setStyle(Qt::NoPen); p.setWidthF(group.readEntry("MajorGridWidth", this->majorGridPen().widthF())); this->setMajorGridPen(p); p.setColor(group.readEntry("MajorTicksColor",(QColor)this->majorTicksPen().color())); p.setStyle((Qt::PenStyle)group.readEntry("MajorTicksLineStyle",(int) this->majorTicksPen().style())); p.setWidthF(group.readEntry("MajorTicksWidth", this->majorTicksPen().widthF())); this->setMajorTicksPen(p); this->setMajorTicksOpacity(group.readEntry("MajorTicksOpacity",this->majorTicksOpacity())); //Minor ticks this->setMinorGridOpacity(group.readEntry("MinorGridOpacity", this->minorGridOpacity())); p.setColor(group.readEntry("MinorGridColor",(QColor) this->minorGridPen().color())); if (firstAxis) p.setStyle((Qt::PenStyle)group.readEntry("MinorGridStyle",(int) this->minorGridPen().style())); else p.setStyle(Qt::NoPen); p.setWidthF(group.readEntry("MinorGridWidth", this->minorGridPen().widthF())); this->setMinorGridPen(p); p.setColor(group.readEntry("MinorTicksColor",(QColor) this->minorTicksPen().color())); p.setWidthF(group.readEntry("MinorTicksWidth", this->minorTicksPen().widthF())); this->setMinorTicksPen(p); this->setMinorTicksOpacity(group.readEntry("MinorTicksOpacity",this->minorTicksOpacity())); const QVector& childElements = children(AbstractAspect::IncludeHidden); for (auto* child : childElements) child->loadThemeConfig(config); } void Axis::saveThemeConfig(const KConfig& config) { KConfigGroup group = config.group("Axis"); // Tick label group.writeEntry("LabelsFontColor", (QColor) this->labelsColor()); group.writeEntry("LabelsOpacity", this->labelsOpacity()); //Line group.writeEntry("LineOpacity", this->lineOpacity()); group.writeEntry("LineColor", (QColor) this->linePen().color()); group.writeEntry("LineStyle", (int) this->linePen().style()); group.writeEntry("LineWidth", this->linePen().widthF()); //Major ticks group.writeEntry("MajorGridOpacity", this->majorGridOpacity()); group.writeEntry("MajorGridColor", (QColor) this->majorGridPen().color()); group.writeEntry("MajorGridStyle", (int) this->majorGridPen().style()); group.writeEntry("MajorGridWidth", this->majorGridPen().widthF()); group.writeEntry("MajorTicksColor", (QColor)this->majorTicksPen().color()); group.writeEntry("MajorTicksLineStyle", (int) this->majorTicksPen().style()); group.writeEntry("MajorTicksWidth", this->majorTicksPen().widthF()); group.writeEntry("MajorTicksOpacity", this->majorTicksOpacity()); group.writeEntry("MajorTicksType", (int)this->majorTicksType()); //Minor ticks group.writeEntry("MinorGridOpacity", this->minorGridOpacity()); group.writeEntry("MinorGridColor",(QColor) this->minorGridPen().color()); group.writeEntry("MinorGridStyle", (int) this->minorGridPen().style()); group.writeEntry("MinorGridWidth", this->minorGridPen().widthF()); group.writeEntry("MinorTicksColor", (QColor) this->minorTicksPen().color()); group.writeEntry("MinorTicksLineStyle",( int) this->minorTicksPen().style()); group.writeEntry("MinorTicksWidth", this->minorTicksPen().widthF()); group.writeEntry("MinorTicksOpacity", this->minorTicksOpacity()); group.writeEntry("MinorTicksType", (int)this->minorTicksType()); const QVector& childElements = children(AbstractAspect::IncludeHidden); childElements.at(0)->saveThemeConfig(config); } diff --git a/src/backend/worksheet/plots/cartesian/Axis.h b/src/backend/worksheet/plots/cartesian/Axis.h index ba56ff5fa..debcec18e 100644 --- a/src/backend/worksheet/plots/cartesian/Axis.h +++ b/src/backend/worksheet/plots/cartesian/Axis.h @@ -1,249 +1,249 @@ /*************************************************************************** File : Axis.h Project : LabPlot Description : Axis for cartesian coordinate systems. -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2011-2018 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2013 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef AXISNEW_H #define AXISNEW_H #include "backend/worksheet/WorksheetElement.h" #include "backend/lib/macros.h" class CartesianPlot; class TextLabel; class AxisPrivate; class AbstractColumn; class QActionGroup; class Axis: public WorksheetElement { Q_OBJECT public: enum AxisOrientation {AxisHorizontal, AxisVertical}; enum AxisPosition {AxisTop, AxisBottom, AxisLeft, AxisRight, AxisCentered, AxisCustom}; enum LabelsFormat {FormatDecimal, FormatScientificE, FormatPowers10, FormatPowers2, FormatPowersE, FormatMultipliesPi}; enum TicksFlags { noTicks = 0x00, ticksIn = 0x01, ticksOut = 0x02, ticksBoth = 0x03, }; Q_DECLARE_FLAGS(TicksDirection, TicksFlags) - enum TicksType {TicksTotalNumber, TicksIncrement, TicksCustomColumn, TicksCustomValues}; + enum TicksType {TicksTotalNumber, TicksSpacing, TicksCustomColumn, TicksCustomValues}; enum ArrowType {NoArrow, SimpleArrowSmall, SimpleArrowBig, FilledArrowSmall, FilledArrowBig, SemiFilledArrowSmall, SemiFilledArrowBig}; enum ArrowPosition {ArrowLeft, ArrowRight, ArrowBoth}; enum AxisScale {ScaleLinear, ScaleLog10, ScaleLog2, ScaleLn, ScaleSqrt, ScaleX2}; enum LabelsPosition {NoLabels, LabelsIn, LabelsOut}; explicit Axis(const QString&, AxisOrientation orientation = AxisHorizontal); ~Axis() override; void finalizeAdd() override; QIcon icon() const override; QMenu* createContextMenu() override; QGraphicsItem* graphicsItem() const override; void setZValue(qreal) override; void save(QXmlStreamWriter*) const override; bool load(XmlStreamReader*, bool preview) override; void loadThemeConfig(const KConfig&) override; void saveThemeConfig(const KConfig&) override; BASIC_D_ACCESSOR_DECL(bool, autoScale, AutoScale) BASIC_D_ACCESSOR_DECL(AxisOrientation, orientation, Orientation) BASIC_D_ACCESSOR_DECL(AxisPosition, position, Position) BASIC_D_ACCESSOR_DECL(AxisScale, scale, Scale) BASIC_D_ACCESSOR_DECL(double, start, Start) BASIC_D_ACCESSOR_DECL(double, end, End) void setOffset(const double, const bool=true); double offset() const; BASIC_D_ACCESSOR_DECL(qreal, scalingFactor, ScalingFactor) BASIC_D_ACCESSOR_DECL(qreal, zeroOffset, ZeroOffset) POINTER_D_ACCESSOR_DECL(TextLabel, title, Title) BASIC_D_ACCESSOR_DECL(double, titleOffsetX, TitleOffsetX) BASIC_D_ACCESSOR_DECL(double, titleOffsetY, TitleOffsetY) CLASS_D_ACCESSOR_DECL(QPen, linePen, LinePen) BASIC_D_ACCESSOR_DECL(qreal, lineOpacity, LineOpacity) BASIC_D_ACCESSOR_DECL(ArrowType, arrowType, ArrowType) BASIC_D_ACCESSOR_DECL(ArrowPosition, arrowPosition, ArrowPosition) BASIC_D_ACCESSOR_DECL(double, arrowSize, ArrowSize) BASIC_D_ACCESSOR_DECL(TicksDirection, majorTicksDirection, MajorTicksDirection) BASIC_D_ACCESSOR_DECL(TicksType, majorTicksType, MajorTicksType) BASIC_D_ACCESSOR_DECL(int, majorTicksNumber, MajorTicksNumber) - BASIC_D_ACCESSOR_DECL(qreal, majorTicksIncrement, MajorTicksIncrement) + BASIC_D_ACCESSOR_DECL(qreal, majorTicksSpacing, MajorTicksSpacing) POINTER_D_ACCESSOR_DECL(const AbstractColumn, majorTicksColumn, MajorTicksColumn) QString& majorTicksColumnPath() const; CLASS_D_ACCESSOR_DECL(QPen, majorTicksPen, MajorTicksPen) BASIC_D_ACCESSOR_DECL(qreal, majorTicksLength, MajorTicksLength) BASIC_D_ACCESSOR_DECL(qreal, majorTicksOpacity, MajorTicksOpacity) BASIC_D_ACCESSOR_DECL(TicksDirection, minorTicksDirection, MinorTicksDirection) BASIC_D_ACCESSOR_DECL(TicksType, minorTicksType, MinorTicksType) BASIC_D_ACCESSOR_DECL(int, minorTicksNumber, MinorTicksNumber) - BASIC_D_ACCESSOR_DECL(qreal, minorTicksIncrement, MinorTicksIncrement) + BASIC_D_ACCESSOR_DECL(qreal, minorTicksSpacing, MinorTicksSpacing) POINTER_D_ACCESSOR_DECL(const AbstractColumn, minorTicksColumn, MinorTicksColumn) QString& minorTicksColumnPath() const; CLASS_D_ACCESSOR_DECL(QPen, minorTicksPen, MinorTicksPen) BASIC_D_ACCESSOR_DECL(qreal, minorTicksLength, MinorTicksLength) BASIC_D_ACCESSOR_DECL(qreal, minorTicksOpacity, MinorTicksOpacity) BASIC_D_ACCESSOR_DECL(LabelsFormat, labelsFormat, LabelsFormat) BASIC_D_ACCESSOR_DECL(bool, labelsAutoPrecision, LabelsAutoPrecision) BASIC_D_ACCESSOR_DECL(int, labelsPrecision, LabelsPrecision) CLASS_D_ACCESSOR_DECL(QString, labelsDateTimeFormat, LabelsDateTimeFormat) BASIC_D_ACCESSOR_DECL(LabelsPosition, labelsPosition, LabelsPosition) BASIC_D_ACCESSOR_DECL(qreal, labelsOffset, LabelsOffset) BASIC_D_ACCESSOR_DECL(qreal, labelsRotationAngle, LabelsRotationAngle) CLASS_D_ACCESSOR_DECL(QColor, labelsColor, LabelsColor) CLASS_D_ACCESSOR_DECL(QFont, labelsFont, LabelsFont) CLASS_D_ACCESSOR_DECL(QString, labelsPrefix, LabelsPrefix) CLASS_D_ACCESSOR_DECL(QString, labelsSuffix, LabelsSuffix) BASIC_D_ACCESSOR_DECL(qreal, labelsOpacity, LabelsOpacity) CLASS_D_ACCESSOR_DECL(QPen, majorGridPen, MajorGridPen) BASIC_D_ACCESSOR_DECL(qreal, majorGridOpacity, MajorGridOpacity) CLASS_D_ACCESSOR_DECL(QPen, minorGridPen, MinorGridPen) BASIC_D_ACCESSOR_DECL(qreal, minorGridOpacity, MinorGridOpacity) void setVisible(bool) override; bool isVisible() const override; void setPrinting(bool) override; void setSuppressRetransform(bool); void retransform() override; void retransformTickLabelStrings(); void handleResize(double horizontalRatio, double verticalRatio, bool pageResize) override; typedef AxisPrivate Private; protected: AxisPrivate* const d_ptr; Axis(const QString&, AxisOrientation, AxisPrivate*); TextLabel* m_title; private: Q_DECLARE_PRIVATE(Axis) void init(); void initActions(); void initMenus(); QAction* visibilityAction{nullptr}; QAction* orientationHorizontalAction{nullptr}; QAction* orientationVerticalAction{nullptr}; QActionGroup* orientationActionGroup{nullptr}; QActionGroup* lineStyleActionGroup{nullptr}; QActionGroup* lineColorActionGroup{nullptr}; QMenu* orientationMenu{nullptr}; QMenu* lineMenu{nullptr}; QMenu* lineStyleMenu{nullptr}; QMenu* lineColorMenu{nullptr}; private slots: void labelChanged(); void retransformTicks(); void majorTicksColumnAboutToBeRemoved(const AbstractAspect*); void minorTicksColumnAboutToBeRemoved(const AbstractAspect*); //SLOTs for changes triggered via QActions in the context menu void orientationChangedSlot(QAction*); void lineStyleChanged(QAction*); void lineColorChanged(QAction*); void visibilityChangedSlot(); signals: void orientationChanged(Axis::AxisOrientation); void positionChanged(Axis::AxisPosition); void positionChanged(double); void scaleChanged(Axis::AxisScale); void startChanged(double); void autoScaleChanged(bool); void endChanged(double); void zeroOffsetChanged(qreal); void scalingFactorChanged(qreal); //title void titleOffsetXChanged(qreal); void titleOffsetYChanged(qreal); // line void linePenChanged(const QPen&); void lineOpacityChanged(qreal); void arrowTypeChanged(Axis::ArrowType); void arrowPositionChanged(Axis::ArrowPosition); void arrowSizeChanged(qreal); // major ticks void majorTicksDirectionChanged(Axis::TicksDirection); void majorTicksTypeChanged(Axis::TicksType); void majorTicksNumberChanged(int); - void majorTicksIncrementChanged(qreal); + void majorTicksSpacingChanged(qreal); void majorTicksColumnChanged(const AbstractColumn*); void majorTicksPenChanged(QPen); void majorTicksLengthChanged(qreal); void majorTicksOpacityChanged(qreal); // minor ticks void minorTicksDirectionChanged(Axis::TicksDirection); void minorTicksTypeChanged(Axis::TicksType); void minorTicksNumberChanged(int); void minorTicksIncrementChanged(qreal); void minorTicksColumnChanged(const AbstractColumn*); void minorTicksPenChanged(QPen); void minorTicksLengthChanged(qreal); void minorTicksOpacityChanged(qreal); //labels void labelsFormatChanged(Axis::LabelsFormat); void labelsAutoPrecisionChanged(bool); void labelsPrecisionChanged(int); void labelsDateTimeFormatChanged(const QString&); void labelsPositionChanged(Axis::LabelsPosition); void labelsOffsetChanged(double); void labelsRotationAngleChanged(qreal); void labelsColorChanged(QColor); void labelsFontChanged(QFont); void labelsPrefixChanged(QString); void labelsSuffixChanged(QString); void labelsOpacityChanged(qreal); void majorGridPenChanged(QPen); void majorGridOpacityChanged(qreal); void minorGridPenChanged(QPen); void minorGridOpacityChanged(qreal); void visibilityChanged(bool); }; Q_DECLARE_OPERATORS_FOR_FLAGS(Axis::TicksDirection) #endif diff --git a/src/backend/worksheet/plots/cartesian/AxisPrivate.h b/src/backend/worksheet/plots/cartesian/AxisPrivate.h index cab4cde5d..c028d57bb 100644 --- a/src/backend/worksheet/plots/cartesian/AxisPrivate.h +++ b/src/backend/worksheet/plots/cartesian/AxisPrivate.h @@ -1,171 +1,171 @@ /*************************************************************************** File : AxisPrivate.h Project : LabPlot Description : Private members of Axis. -------------------------------------------------------------------- Copyright : (C) 2011-2018 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 AXISPRIVATE_H #define AXISPRIVATE_H #include #include #include #include "Axis.h" class QGraphicsSceneHoverEvent; class AxisGrid; class CartesianPlot; class CartesianCoordinateSystem; class TextLabel; class AxisPrivate: public QGraphicsItem { public: explicit AxisPrivate(Axis*); QRectF boundingRect() const override; QPainterPath shape() const override; QString name() const; void retransform(); void retransformLine(); void retransformArrow(); void retransformTicks(); void retransformTickLabelPositions(); void retransformTickLabelStrings(); void retransformMinorGrid(); void retransformMajorGrid(); void updateGrid(); bool swapVisible(bool); void recalcShapeAndBoundingRect(); void setPrinting(bool); //general bool autoScale; Axis::AxisOrientation orientation; //!< horizontal or vertical Axis::AxisPosition position; //!< left, right, bottom, top or custom (usually not changed after creation) Axis::AxisScale scale; double offset; //!< offset from zero in the direction perpendicular to the axis double start; //!< start coordinate of the axis line double end; //!< end coordinate of the axis line qreal scalingFactor; qreal zeroOffset; //line QVector lines; QPen linePen; qreal lineOpacity; Axis::ArrowType arrowType; Axis::ArrowPosition arrowPosition; qreal arrowSize; // Title TextLabel* title; qreal titleOffsetX; //distance to the axis line qreal titleOffsetY; //distance to the axis line // Ticks Axis::TicksDirection majorTicksDirection; //!< major ticks direction: inwards, outwards, both, or none Axis::TicksType majorTicksType; //!< the way how the number of major ticks is specified - either as a total number or an increment int majorTicksNumber; //!< number of major ticks - qreal majorTicksIncrement; //!< increment (step) for the major ticks + qreal majorTicksSpacing; //!< spacing (step) for the major ticks const AbstractColumn* majorTicksColumn{nullptr}; //!< column containing values for major ticks' positions QString majorTicksColumnPath; qreal majorTicksLength; //!< major tick length (in page units!) QPen majorTicksPen; qreal majorTicksOpacity; Axis::TicksDirection minorTicksDirection; //!< minor ticks direction: inwards, outwards, both, or none Axis::TicksType minorTicksType; //!< the way how the number of minor ticks is specified - either as a total number or an increment int minorTicksNumber; //!< number of minor ticks (between each two major ticks) - qreal minorTicksIncrement; //!< increment (step) for the minor ticks + qreal minorTicksIncrement; //!< spacing (step) for the minor ticks const AbstractColumn* minorTicksColumn{nullptr}; //!< column containing values for minor ticks' positions QString minorTicksColumnPath; qreal minorTicksLength; //!< minor tick length (in page units!) QPen minorTicksPen; qreal minorTicksOpacity; // Tick Label Axis::LabelsFormat labelsFormat; int labelsPrecision; bool labelsAutoPrecision; QString labelsDateTimeFormat; Axis::LabelsPosition labelsPosition; qreal labelsRotationAngle; QColor labelsColor; QFont labelsFont; qreal labelsOffset; //!< offset, distance to the end of the tick line (in page units) qreal labelsOpacity; QString labelsPrefix; QString labelsSuffix; //Grid AxisGrid* gridItem; QPen majorGridPen; qreal majorGridOpacity; QPen minorGridPen; qreal minorGridOpacity; Axis* const q; QPainterPath linePath; QPainterPath majorGridPath; QPainterPath minorGridPath; bool suppressRetransform{false}; bool labelsFormatDecimalOverruled{false}; bool labelsFormatAutoChanged{false}; CartesianPlot* plot{nullptr}; const CartesianCoordinateSystem* cSystem{nullptr}; private: void contextMenuEvent(QGraphicsSceneContextMenuEvent*) override; void hoverEnterEvent(QGraphicsSceneHoverEvent*) override; void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override; void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget* widget = nullptr) override; void addArrow(QPointF point, int direction); int upperLabelsPrecision(int); int lowerLabelsPrecision(int); bool transformAnchor(QPointF*); QPainterPath arrowPath; QPainterPath majorTicksPath; QPainterPath minorTicksPath; QRectF boundingRectangle; QPainterPath axisShape; QVector majorTickPoints;//!< position of the major ticks on the axis. QVector minorTickPoints;//!< position of the major ticks on the axis. QVector tickLabelPoints; //!< position of the major tick labels (left lower edge of label's bounding rect) QVector tickLabelValues; //!< major tick labels values QVector tickLabelStrings; //!< the actual text of the major tick labels bool m_hovered{false}; bool m_suppressRecalc{false}; bool m_printing{false}; }; #endif diff --git a/src/kdefrontend/dockwidgets/AxisDock.cpp b/src/kdefrontend/dockwidgets/AxisDock.cpp index 40e2cb37e..8b89af957 100644 --- a/src/kdefrontend/dockwidgets/AxisDock.cpp +++ b/src/kdefrontend/dockwidgets/AxisDock.cpp @@ -1,2244 +1,2248 @@ /*************************************************************************** File : AxisDock.cpp Project : LabPlot Description : axes widget class -------------------------------------------------------------------- Copyright : (C) 2011-2020 Alexander Semke (alexander.semke@web.de) - Copyright : (C) 2012-2013 Stefan Gerlach (stefan.gerlach@uni-konstanz.de) + Copyright : (C) 2012-2020 Stefan Gerlach (stefan.gerlach@uni-konstanz.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "AxisDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/GuiTools.h" #include "kdefrontend/TemplateHandler.h" #include "kdefrontend/widgets/LabelWidget.h" #include "commonfrontend/widgets/DateTimeSpinBox.h" #include #include #include #include #include /*! \class AxisDock \brief Provides a widget for editing the properties of the axes currently selected in the project explorer. \ingroup kdefrontend */ AxisDock::AxisDock(QWidget* parent) : BaseDock(parent) { ui.setupUi(this); m_leName = ui.leName; m_leComment = ui.leComment; //"Title"-tab auto* hboxLayout = new QHBoxLayout(ui.tabTitle); labelWidget = new LabelWidget(ui.tabTitle); labelWidget->setFixedLabelMode(true); hboxLayout->addWidget(labelWidget); hboxLayout->setContentsMargins(2,2,2,2); hboxLayout->setSpacing(2); //"Ticks"-tab auto* layout = static_cast(ui.tabTicks->layout()); cbMajorTicksColumn = new TreeViewComboBox(ui.tabTicks); layout->addWidget(cbMajorTicksColumn, 7, 2); cbMinorTicksColumn = new TreeViewComboBox(ui.tabTicks); layout->addWidget(cbMinorTicksColumn, 21, 2); dtsbMajorTicksIncrement = new DateTimeSpinBox(ui.tabTicks); layout->addWidget(dtsbMajorTicksIncrement, 6, 2); dtsbMinorTicksIncrement = new DateTimeSpinBox(ui.tabTicks); layout->addWidget(dtsbMinorTicksIncrement, 20, 2); //adjust layouts in the tabs for (int i = 0; i < ui.tabWidget->count(); ++i) { layout = dynamic_cast(ui.tabWidget->widget(i)->layout()); if (!layout) continue; layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); } //********************************** Slots ********************************************** //"General"-tab connect(ui.leName, &QLineEdit::textChanged, this, &AxisDock::nameChanged); connect(ui.leComment, &QLineEdit::textChanged, this, &AxisDock::commentChanged); connect(ui.chkVisible, &QCheckBox::clicked, this, &AxisDock::visibilityChanged); connect(ui.cbOrientation, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::orientationChanged); connect(ui.cbPosition, QOverload::of(&QComboBox::currentIndexChanged), this, QOverload::of(&AxisDock::positionChanged)); connect(ui.lePosition, &QLineEdit::textChanged, this, QOverload<>::of(&AxisDock::positionChanged)); connect(ui.cbScale, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::scaleChanged); connect(ui.chkAutoScale, &QCheckBox::stateChanged, this, &AxisDock::autoScaleChanged); connect(ui.leStart, &QLineEdit::textChanged, this, &AxisDock::startChanged); connect(ui.leEnd, &QLineEdit::textChanged, this, &AxisDock::endChanged); connect(ui.dateTimeEditStart, &QDateTimeEdit::dateTimeChanged, this, &AxisDock::startDateTimeChanged); connect(ui.dateTimeEditEnd, &QDateTimeEdit::dateTimeChanged, this, &AxisDock::endDateTimeChanged); connect(ui.leZeroOffset, &QLineEdit::textChanged, this, &AxisDock::zeroOffsetChanged); connect(ui.leScalingFactor, &QLineEdit::textChanged, this, &AxisDock::scalingFactorChanged); //"Line"-tab connect(ui.cbLineStyle, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::lineStyleChanged); connect(ui.kcbLineColor, &KColorButton::changed, this, &AxisDock::lineColorChanged); connect(ui.sbLineWidth, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AxisDock::lineWidthChanged); connect(ui.sbLineOpacity, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::lineOpacityChanged); connect(ui.cbArrowPosition, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::arrowPositionChanged); connect(ui.cbArrowType, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::arrowTypeChanged); connect(ui.sbArrowSize, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::arrowSizeChanged); //"Major ticks"-tab connect(ui.cbMajorTicksDirection, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::majorTicksDirectionChanged); connect(ui.cbMajorTicksType, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::majorTicksTypeChanged); connect(ui.sbMajorTicksNumber, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::majorTicksNumberChanged); - connect(ui.sbMajorTicksIncrementNumeric, QOverload::of(&QDoubleSpinBox::valueChanged), - this, &AxisDock::majorTicksIncrementChanged); + connect(ui.sbMajorTicksSpacingNumeric, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &AxisDock::majorTicksSpacingChanged); connect(dtsbMajorTicksIncrement, &DateTimeSpinBox::valueChanged, - this, &AxisDock::majorTicksIncrementChanged); + this, &AxisDock::majorTicksSpacingChanged); connect(cbMajorTicksColumn, &TreeViewComboBox::currentModelIndexChanged, this, &AxisDock::majorTicksColumnChanged); connect(ui.cbMajorTicksLineStyle, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::majorTicksLineStyleChanged); connect(ui.kcbMajorTicksColor, &KColorButton::changed, this, &AxisDock::majorTicksColorChanged); connect(ui.sbMajorTicksWidth, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AxisDock::majorTicksWidthChanged); connect(ui.sbMajorTicksLength, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AxisDock::majorTicksLengthChanged); connect(ui.sbMajorTicksOpacity, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::majorTicksOpacityChanged); //"Minor ticks"-tab connect(ui.cbMinorTicksDirection, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::minorTicksDirectionChanged); connect(ui.cbMinorTicksType, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::minorTicksTypeChanged); connect(ui.sbMinorTicksNumber, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::minorTicksNumberChanged); - connect(ui.sbMinorTicksIncrementNumeric, QOverload::of(&QDoubleSpinBox::valueChanged), - this, &AxisDock::minorTicksIncrementChanged); + connect(ui.sbMinorTicksSpacingNumeric, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &AxisDock::minorTicksSpacingChanged); connect(dtsbMinorTicksIncrement, &DateTimeSpinBox::valueChanged, - this, &AxisDock::minorTicksIncrementChanged); + this, &AxisDock::minorTicksSpacingChanged); connect(cbMinorTicksColumn, &TreeViewComboBox::currentModelIndexChanged, this, &AxisDock::minorTicksColumnChanged); connect(ui.cbMinorTicksLineStyle, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::minorTicksLineStyleChanged); connect(ui.kcbMinorTicksColor, &KColorButton::changed, this, &AxisDock::minorTicksColorChanged); connect(ui.sbMinorTicksWidth, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AxisDock::minorTicksWidthChanged); connect(ui.sbMinorTicksLength, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AxisDock::minorTicksLengthChanged); connect(ui.sbMinorTicksOpacity, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::minorTicksOpacityChanged); //"Extra ticks"-tab //"Tick labels"-tab connect(ui.cbLabelsFormat, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::labelsFormatChanged); connect(ui.sbLabelsPrecision, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::labelsPrecisionChanged); connect(ui.chkLabelsAutoPrecision, &QCheckBox::stateChanged, this, &AxisDock::labelsAutoPrecisionChanged); connect(ui.cbLabelsDateTimeFormat, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::labelsDateTimeFormatChanged); connect(ui.cbLabelsPosition, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::labelsPositionChanged); connect(ui.sbLabelsOffset, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AxisDock::labelsOffsetChanged); connect(ui.sbLabelsRotation, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::labelsRotationChanged); connect(ui.kfrLabelsFont, &KFontRequester::fontSelected, this, &AxisDock::labelsFontChanged); connect(ui.kcbLabelsFontColor, &KColorButton::changed, this, &AxisDock::labelsFontColorChanged); connect(ui.leLabelsPrefix, &QLineEdit::textChanged, this, &AxisDock::labelsPrefixChanged); connect(ui.leLabelsSuffix, &QLineEdit::textChanged, this, &AxisDock::labelsSuffixChanged); connect(ui.sbLabelsOpacity, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::labelsOpacityChanged); //"Grid"-tab connect(ui.cbMajorGridStyle, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::majorGridStyleChanged); connect(ui.kcbMajorGridColor, &KColorButton::changed, this, &AxisDock::majorGridColorChanged); connect(ui.sbMajorGridWidth, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AxisDock::majorGridWidthChanged); connect(ui.sbMajorGridOpacity, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::majorGridOpacityChanged); connect(ui.cbMinorGridStyle, QOverload::of(&QComboBox::currentIndexChanged), this, &AxisDock::minorGridStyleChanged); connect(ui.kcbMinorGridColor, &KColorButton::changed, this, &AxisDock::minorGridColorChanged); connect(ui.sbMinorGridWidth, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AxisDock::minorGridWidthChanged); connect(ui.sbMinorGridOpacity, QOverload::of(&QSpinBox::valueChanged), this, &AxisDock::minorGridOpacityChanged); //template handler auto* frame = new QFrame(this); auto* hlayout = new QHBoxLayout(frame); hlayout->setContentsMargins(0, 11, 0, 11); auto* templateHandler = new TemplateHandler(this, TemplateHandler::Axis); hlayout->addWidget(templateHandler); connect(templateHandler, &TemplateHandler::loadConfigRequested, this, &AxisDock::loadConfigFromTemplate); connect(templateHandler, &TemplateHandler::saveConfigRequested, this, &AxisDock::saveConfigAsTemplate); connect(templateHandler, &TemplateHandler::info, this, &AxisDock::info); ui.verticalLayout->addWidget(frame); init(); } AxisDock::~AxisDock() { if (m_aspectTreeModel) delete m_aspectTreeModel; } void AxisDock::init() { Lock lock(m_initializing); //Validators ui.lePosition->setValidator( new QDoubleValidator(ui.lePosition) ); ui.leStart->setValidator( new QDoubleValidator(ui.leStart) ); ui.leEnd->setValidator( new QDoubleValidator(ui.leEnd) ); ui.leZeroOffset->setValidator( new QDoubleValidator(ui.leZeroOffset) ); ui.leScalingFactor->setValidator( new QDoubleValidator(ui.leScalingFactor) ); //TODO move this stuff to retranslateUI() ui.cbPosition->addItem(i18n("Top")); ui.cbPosition->addItem(i18n("Bottom")); ui.cbPosition->addItem(i18n("Centered")); ui.cbPosition->addItem(i18n("Custom")); ui.cbScale->addItem( i18n("Linear") ); ui.cbScale->addItem( QLatin1String("log(x)") ); ui.cbScale->addItem( QLatin1String("log2(x)") ); ui.cbScale->addItem( QLatin1String("ln(x)") ); ui.cbScale->addItem( QLatin1String("sqrt(x)") ); ui.cbScale->addItem( QLatin1String("x^2") ); ui.cbOrientation->addItem( i18n("Horizontal") ); ui.cbOrientation->addItem( i18n("Vertical") ); //Arrows ui.cbArrowType->addItem( i18n("No arrow") ); ui.cbArrowType->addItem( i18n("Simple, Small") ); ui.cbArrowType->addItem( i18n("Simple, Big") ); ui.cbArrowType->addItem( i18n("Filled, Small") ); ui.cbArrowType->addItem( i18n("Filled, Big") ); ui.cbArrowType->addItem( i18n("Semi-filled, Small") ); ui.cbArrowType->addItem( i18n("Semi-filled, Big") ); QPainter pa; pa.setPen( QPen(Qt::SolidPattern, 0) ); QPixmap pm(20, 20); ui.cbArrowType->setIconSize( QSize(20,20) ); //no arrow pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); - pa.drawLine(3,10,17,10); + pa.drawLine(3, 10, 17, 10); pa.end(); ui.cbArrowType->setItemIcon(0, pm); //simple, small float cos_phi = cos(3.14159/6); pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); - pa.drawLine(3,10,17,10); + pa.drawLine(3, 10, 17, 10); pa.drawLine(17,10, 10, 10-5*cos_phi); pa.drawLine(17,10, 10, 10+5*cos_phi); pa.end(); ui.cbArrowType->setItemIcon(1, pm); //simple, big pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); - pa.drawLine(3,10,17,10); + pa.drawLine(3, 10, 17, 10); pa.drawLine(17,10, 10, 10-10*cos_phi); pa.drawLine(17,10, 10, 10+10*cos_phi); pa.end(); ui.cbArrowType->setItemIcon(2, pm); //filled, small pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); - pa.drawLine(3,10,17,10); + pa.drawLine(3, 10, 17, 10); QPointF points3[3] = {QPointF(17, 10), QPointF(10, 10-4*cos_phi), QPointF(10, 10+4*cos_phi) }; pa.drawPolygon(points3, 3); pa.end(); ui.cbArrowType->setItemIcon(3, pm); //filled, big pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); - pa.drawLine(3,10,17,10); + pa.drawLine(3, 10, 17, 10); QPointF points4[3] = {QPointF(17, 10), QPointF(10, 10-10*cos_phi), QPointF(10, 10+10*cos_phi) }; pa.drawPolygon(points4, 3); pa.end(); ui.cbArrowType->setItemIcon(4, pm); //semi-filled, small pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); - pa.drawLine(3,10,17,10); + pa.drawLine(3, 10, 17, 10); QPointF points5[4] = {QPointF(17, 10), QPointF(10, 10-4*cos_phi), QPointF(13, 10), QPointF(10, 10+4*cos_phi) }; pa.drawPolygon(points5, 4); pa.end(); ui.cbArrowType->setItemIcon(5, pm); //semi-filled, big pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); - pa.drawLine(3,10,17,10); + pa.drawLine(3, 10, 17, 10); QPointF points6[4] = {QPointF(17, 10), QPointF(10, 10-10*cos_phi), QPointF(13, 10), QPointF(10, 10+10*cos_phi) }; pa.drawPolygon(points6, 4); pa.end(); ui.cbArrowType->setItemIcon(6, pm); ui.cbArrowPosition->addItem( i18n("Left") ); ui.cbArrowPosition->addItem( i18n("Right") ); ui.cbArrowPosition->addItem( i18n("Both") ); ui.cbMajorTicksDirection->addItem( i18n("None") ); ui.cbMajorTicksDirection->addItem( i18n("In") ); ui.cbMajorTicksDirection->addItem( i18n("Out") ); ui.cbMajorTicksDirection->addItem( i18n("In and Out") ); ui.cbMajorTicksType->addItem( i18n("Number") ); - ui.cbMajorTicksType->addItem( i18n("Increment") ); + ui.cbMajorTicksType->addItem( i18n("Spacing") ); ui.cbMajorTicksType->addItem( i18n("Custom column") ); ui.cbMinorTicksDirection->addItem( i18n("None") ); ui.cbMinorTicksDirection->addItem( i18n("In") ); ui.cbMinorTicksDirection->addItem( i18n("Out") ); ui.cbMinorTicksDirection->addItem( i18n("In and Out") ); ui.cbMinorTicksType->addItem( i18n("Number") ); - ui.cbMinorTicksType->addItem( i18n("Increment") ); + ui.cbMinorTicksType->addItem( i18n("Spacing") ); ui.cbMinorTicksType->addItem( i18n("Custom column") ); GuiTools::updatePenStyles(ui.cbLineStyle, QColor(Qt::black)); GuiTools::updatePenStyles(ui.cbMajorTicksLineStyle, QColor(Qt::black)); GuiTools::updatePenStyles(ui.cbMinorTicksLineStyle, QColor(Qt::black)); GuiTools::updatePenStyles(ui.cbMajorGridStyle, QColor(Qt::black)); GuiTools::updatePenStyles(ui.cbMinorGridStyle, QColor(Qt::black)); //labels ui.cbLabelsPosition->addItem(i18n("No labels")); ui.cbLabelsPosition->addItem(i18n("Top")); ui.cbLabelsPosition->addItem(i18n("Bottom")); ui.cbLabelsFormat->addItem( i18n("Decimal notation") ); ui.cbLabelsFormat->addItem( i18n("Scientific notation") ); ui.cbLabelsFormat->addItem( i18n("Powers of 10") ); ui.cbLabelsFormat->addItem( i18n("Powers of 2") ); ui.cbLabelsFormat->addItem( i18n("Powers of e") ); ui.cbLabelsFormat->addItem( i18n("Multiples of π") ); ui.cbLabelsDateTimeFormat->addItems(AbstractColumn::dateTimeFormats()); } void AxisDock::setModel() { QList list{AspectType::Folder, AspectType::Spreadsheet, AspectType::Column}; cbMajorTicksColumn->setTopLevelClasses(list); cbMinorTicksColumn->setTopLevelClasses(list); list = {AspectType::Column}; m_aspectTreeModel->setSelectableAspects(list); cbMajorTicksColumn->setModel(m_aspectTreeModel); cbMinorTicksColumn->setModel(m_aspectTreeModel); } /*! sets the axes. The properties of the axes in the list \c list can be edited in this widget. */ void AxisDock::setAxes(QList list) { Lock lock(m_initializing); m_axesList = list; m_axis = list.first(); m_aspect = list.first(); Q_ASSERT(m_axis != nullptr); m_aspectTreeModel = new AspectTreeModel(m_axis->project()); this->setModel(); labelWidget->setAxes(list); //if there are more then one axis in the list, disable the tab "general" if (list.size() == 1) { ui.lName->setEnabled(true); ui.leName->setEnabled(true); ui.lComment->setEnabled(true); ui.leComment->setEnabled(true); ui.leName->setText(m_axis->name()); ui.leComment->setText(m_axis->comment()); this->setModelIndexFromColumn(cbMajorTicksColumn, m_axis->majorTicksColumn()); this->setModelIndexFromColumn(cbMinorTicksColumn, m_axis->minorTicksColumn()); } else { ui.lName->setEnabled(false); ui.leName->setEnabled(false); ui.lComment->setEnabled(false); ui.leComment->setEnabled(false); ui.leName->setText(QString()); ui.leComment->setText(QString()); cbMajorTicksColumn->setCurrentModelIndex(QModelIndex()); cbMinorTicksColumn->setCurrentModelIndex(QModelIndex()); } ui.leName->setStyleSheet(""); ui.leName->setToolTip(""); //show the properties of the first axis this->load(); // general connect(m_axis, &Axis::aspectDescriptionChanged, this, &AxisDock::axisDescriptionChanged); connect(m_axis, &Axis::orientationChanged, this, &AxisDock::axisOrientationChanged); connect(m_axis, QOverload::of(&Axis::positionChanged), this, QOverload::of(&AxisDock::axisPositionChanged)); connect(m_axis, &Axis::scaleChanged, this, &AxisDock::axisScaleChanged); connect(m_axis, &Axis::autoScaleChanged, this, &AxisDock::axisAutoScaleChanged); connect(m_axis, &Axis::startChanged, this, &AxisDock::axisStartChanged); connect(m_axis, &Axis::endChanged, this, &AxisDock::axisEndChanged); connect(m_axis, &Axis::zeroOffsetChanged, this, &AxisDock::axisZeroOffsetChanged); connect(m_axis, &Axis::scalingFactorChanged, this, &AxisDock::axisScalingFactorChanged); // line connect(m_axis, &Axis::linePenChanged, this, &AxisDock::axisLinePenChanged); connect(m_axis, &Axis::lineOpacityChanged, this, &AxisDock::axisLineOpacityChanged); connect(m_axis, &Axis::arrowTypeChanged, this, &AxisDock::axisArrowTypeChanged); connect(m_axis, &Axis::arrowPositionChanged, this, &AxisDock::axisArrowPositionChanged); connect(m_axis, &Axis::arrowSizeChanged, this, &AxisDock::axisArrowSizeChanged); // ticks connect(m_axis, &Axis::majorTicksDirectionChanged, this, &AxisDock::axisMajorTicksDirectionChanged); connect(m_axis, &Axis::majorTicksTypeChanged, this, &AxisDock::axisMajorTicksTypeChanged); connect(m_axis, &Axis::majorTicksNumberChanged, this, &AxisDock::axisMajorTicksNumberChanged); - connect(m_axis, &Axis::majorTicksIncrementChanged, this, &AxisDock::axisMajorTicksIncrementChanged); + connect(m_axis, &Axis::majorTicksSpacingChanged, this, &AxisDock::axisMajorTicksSpacingChanged); connect(m_axis, &Axis::majorTicksPenChanged, this, &AxisDock::axisMajorTicksPenChanged); connect(m_axis, &Axis::majorTicksLengthChanged, this, &AxisDock::axisMajorTicksLengthChanged); connect(m_axis, &Axis::majorTicksOpacityChanged, this, &AxisDock::axisMajorTicksOpacityChanged); connect(m_axis, &Axis::minorTicksDirectionChanged, this, &AxisDock::axisMinorTicksDirectionChanged); connect(m_axis, &Axis::minorTicksTypeChanged, this, &AxisDock::axisMinorTicksTypeChanged); connect(m_axis, &Axis::minorTicksNumberChanged, this, &AxisDock::axisMinorTicksNumberChanged); - connect(m_axis, &Axis::minorTicksIncrementChanged, this, &AxisDock::axisMinorTicksIncrementChanged); + connect(m_axis, &Axis::minorTicksIncrementChanged, this, &AxisDock::axisMinorTicksSpacingChanged); connect(m_axis, &Axis::minorTicksPenChanged, this, &AxisDock::axisMinorTicksPenChanged); connect(m_axis, &Axis::minorTicksLengthChanged, this, &AxisDock::axisMinorTicksLengthChanged); connect(m_axis, &Axis::minorTicksOpacityChanged, this, &AxisDock::axisMinorTicksOpacityChanged); // labels connect(m_axis, &Axis::labelsFormatChanged, this, &AxisDock::axisLabelsFormatChanged); connect(m_axis, &Axis::labelsAutoPrecisionChanged, this, &AxisDock::axisLabelsAutoPrecisionChanged); connect(m_axis, &Axis::labelsPrecisionChanged, this, &AxisDock::axisLabelsPrecisionChanged); connect(m_axis, &Axis::labelsDateTimeFormatChanged, this, &AxisDock::axisLabelsDateTimeFormatChanged); connect(m_axis, &Axis::labelsPositionChanged, this, &AxisDock::axisLabelsPositionChanged); connect(m_axis, &Axis::labelsOffsetChanged, this, &AxisDock::axisLabelsOffsetChanged); connect(m_axis, &Axis::labelsRotationAngleChanged, this, &AxisDock::axisLabelsRotationAngleChanged); connect(m_axis, &Axis::labelsFontChanged, this, &AxisDock::axisLabelsFontChanged); connect(m_axis, &Axis::labelsColorChanged, this, &AxisDock::axisLabelsFontColorChanged); connect(m_axis, &Axis::labelsPrefixChanged, this, &AxisDock::axisLabelsPrefixChanged); connect(m_axis, &Axis::labelsSuffixChanged, this, &AxisDock::axisLabelsSuffixChanged); connect(m_axis, &Axis::labelsOpacityChanged, this, &AxisDock::axisLabelsOpacityChanged); // grids connect(m_axis, &Axis::majorGridPenChanged, this, &AxisDock::axisMajorGridPenChanged); connect(m_axis, &Axis::majorGridOpacityChanged, this, &AxisDock::axisMajorGridOpacityChanged); connect(m_axis, &Axis::minorGridPenChanged, this, &AxisDock::axisMinorGridPenChanged); connect(m_axis, &Axis::minorGridOpacityChanged, this, &AxisDock::axisMinorGridOpacityChanged); connect(m_axis, &Axis::visibilityChanged, this, &AxisDock::axisVisibilityChanged); } void AxisDock::activateTitleTab() { ui.tabWidget->setCurrentWidget(ui.tabTitle); } void AxisDock::setModelIndexFromColumn(TreeViewComboBox* cb, const AbstractColumn* column) { if (column) cb->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(column)); else cb->setCurrentModelIndex(QModelIndex()); } //************************************************************* //********** SLOTs for changes triggered in AxisDock ********** //************************************************************* //"General"-tab void AxisDock::visibilityChanged(bool state) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setVisible(state); } /*! called if the orientation (horizontal or vertical) of the current axis is changed. */ void AxisDock::orientationChanged(int index) { auto orientation = (Axis::AxisOrientation)index; if (orientation == Axis::AxisHorizontal) { ui.cbPosition->setItemText(0, i18n("Top") ); ui.cbPosition->setItemText(1, i18n("Bottom") ); ui.cbLabelsPosition->setItemText(1, i18n("Top") ); ui.cbLabelsPosition->setItemText(2, i18n("Bottom") ); ui.cbScale->setItemText(1, QLatin1String("log(x)") ); ui.cbScale->setItemText(2, QLatin1String("log2(x)") ); ui.cbScale->setItemText(3, QLatin1String("ln(x)") ); ui.cbScale->setItemText(4, QLatin1String("sqrt(x)") ); ui.cbScale->setItemText(5, QLatin1String("x^2") ); } else { //vertical ui.cbPosition->setItemText(0, i18n("Left") ); ui.cbPosition->setItemText(1, i18n("Right") ); ui.cbLabelsPosition->setItemText(1, i18n("Right") ); ui.cbLabelsPosition->setItemText(2, i18n("Left") ); ui.cbScale->setItemText(1, QLatin1String("log(y)") ); ui.cbScale->setItemText(2, QLatin1String("log2(y)") ); ui.cbScale->setItemText(3, QLatin1String("ln(y)") ); ui.cbScale->setItemText(4, QLatin1String("sqrt(y)") ); ui.cbScale->setItemText(5, QLatin1String("y^2") ); } if (m_initializing) return; //depending on the current orientation we need to update axis position and labels position //axis position, map from the current index in the combobox to the enum value in Axis::AxisPosition Axis::AxisPosition axisPosition; int posIndex = ui.cbPosition->currentIndex(); if (orientation == Axis::AxisHorizontal) { if (posIndex > 1) posIndex += 2; axisPosition = Axis::AxisPosition(posIndex); } else axisPosition = Axis::AxisPosition(posIndex+2); //labels position posIndex = ui.cbLabelsPosition->currentIndex(); auto labelsPosition = Axis::LabelsPosition(posIndex); for (auto* axis : m_axesList) { axis->beginMacro(i18n("%1: set axis orientation", axis->name())); axis->setOrientation(orientation); axis->setPosition(axisPosition); axis->setLabelsPosition(labelsPosition); axis->endMacro(); } } /*! called if one of the predefined axis positions (top, bottom, left, right, center or custom) was changed. */ void AxisDock::positionChanged(int index) { if (index == -1) return; //we occasionally get -1 here, nothing to do in this case if (index == 3) ui.lePosition->setVisible(true); else ui.lePosition->setVisible(false); if (m_initializing) return; //map from the current index in the combo box to the enum value in Axis::AxisPosition, //depends on the current orientation Axis::AxisPosition position; if ( ui.cbOrientation->currentIndex() == 0 ) { if (index>1) index += 2; position = Axis::AxisPosition(index); } else position = Axis::AxisPosition(index+2); for (auto* axis : m_axesList) axis->setPosition(position); } /*! called when the custom position of the axis in the corresponding LineEdit is changed. */ void AxisDock::positionChanged() { if (m_initializing) return; double offset = ui.lePosition->text().toDouble(); for (auto* axis : m_axesList) axis->setOffset(offset); } void AxisDock::scaleChanged(int index) { if (m_initializing) return; auto scale = (Axis::AxisScale)index; for (auto* axis : m_axesList) axis->setScale(scale); } void AxisDock::autoScaleChanged(int index) { bool autoScale = index == Qt::Checked; ui.leStart->setEnabled(!autoScale); ui.leEnd->setEnabled(!autoScale); ui.dateTimeEditStart->setEnabled(!autoScale); ui.dateTimeEditEnd->setEnabled(!autoScale); if (m_initializing) return; for (auto* axis : m_axesList) axis->setAutoScale(autoScale); } void AxisDock::startChanged() { if (m_initializing) return; double value = ui.leStart->text().toDouble(); //check first, whether the value for the lower limit is valid for the log- and square root scaling. If not, set the default values. auto scale = Axis::AxisScale(ui.cbScale->currentIndex()); if (scale == Axis::ScaleLog10 || scale == Axis::ScaleLog2 || scale == Axis::ScaleLn) { if (value <= 0) { KMessageBox::sorry(this, i18n("The axes lower limit has a non-positive value. Default minimal value will be used."), i18n("Wrong lower limit value") ); ui.leStart->setText( "0.01" ); value = 0.01; } } else if (scale == Axis::ScaleSqrt) { if (value < 0) { KMessageBox::sorry(this, i18n("The axes lower limit has a negative value. Default minimal value will be used."), i18n("Wrong lower limit value") ); ui.leStart->setText( "0" ); value = 0; } } const Lock lock(m_initializing); for (auto* axis : m_axesList) axis->setStart(value); } void AxisDock::endChanged() { if (m_initializing) return; double value = ui.leEnd->text().toDouble(); const Lock lock(m_initializing); for (auto* axis : m_axesList) axis->setEnd(value); } void AxisDock::startDateTimeChanged(const QDateTime& dateTime) { if (m_initializing) return; quint64 value = dateTime.toMSecsSinceEpoch(); for (auto* axis : m_axesList) axis->setStart(value); } void AxisDock::endDateTimeChanged(const QDateTime& dateTime) { if (m_initializing) return; quint64 value = dateTime.toMSecsSinceEpoch(); for (auto* axis : m_axesList) axis->setEnd(value); } void AxisDock::zeroOffsetChanged() { if (m_initializing) return; double offset = ui.leZeroOffset->text().toDouble(); const Lock lock(m_initializing); for (auto* axis : m_axesList) axis->setZeroOffset(offset); } void AxisDock::scalingFactorChanged() { if (m_initializing) return; double scalingFactor = ui.leScalingFactor->text().toDouble(); if (scalingFactor != 0.0) { const Lock lock(m_initializing); for (auto* axis : m_axesList) axis->setScalingFactor(scalingFactor); } } // "Line"-tab void AxisDock::lineStyleChanged(int index) { auto penStyle = Qt::PenStyle(index); bool b = (penStyle != Qt::NoPen); ui.lLineColor->setEnabled(b); ui.kcbLineColor->setEnabled(b); ui.lLineWidth->setEnabled(b); ui.sbLineWidth->setEnabled(b); ui.lLineOpacity->setEnabled(b); ui.sbLineOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->linePen(); pen.setStyle(penStyle); axis->setLinePen(pen); } } void AxisDock::lineColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->linePen(); pen.setColor(color); axis->setLinePen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbLineStyle, color); m_initializing = false; } void AxisDock::lineWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->linePen(); pen.setWidthF(Worksheet::convertToSceneUnits(value, Worksheet::Point)); axis->setLinePen(pen); } } void AxisDock::lineOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setLineOpacity(opacity); } void AxisDock::arrowTypeChanged(int index) { auto type = (Axis::ArrowType)index; if (type == Axis::NoArrow) { ui.cbArrowPosition->setEnabled(false); ui.sbArrowSize->setEnabled(false); } else { ui.cbArrowPosition->setEnabled(true); ui.sbArrowSize->setEnabled(true); } if (m_initializing) return; for (auto* axis : m_axesList) axis->setArrowType(type); } void AxisDock::arrowPositionChanged(int index) { if (m_initializing) return; auto position = (Axis::ArrowPosition)index; for (auto* axis : m_axesList) axis->setArrowPosition(position); } void AxisDock::arrowSizeChanged(int value) { if (m_initializing) return; float v = Worksheet::convertToSceneUnits(value, Worksheet::Point); for (auto* axis : m_axesList) axis->setArrowSize(v); } //"Major ticks" tab void AxisDock::majorTicksDirectionChanged(int index) { Axis::TicksDirection direction = Axis::TicksDirection(index); bool b = (direction != Axis::noTicks); ui.lMajorTicksType->setEnabled(b); ui.cbMajorTicksType->setEnabled(b); ui.lMajorTicksType->setEnabled(b); ui.cbMajorTicksType->setEnabled(b); ui.lMajorTicksNumber->setEnabled(b); ui.sbMajorTicksNumber->setEnabled(b); - ui.lMajorTicksIncrementNumeric->setEnabled(b); - ui.sbMajorTicksIncrementNumeric->setEnabled(b); + ui.lMajorTicksSpacingNumeric->setEnabled(b); + ui.sbMajorTicksSpacingNumeric->setEnabled(b); ui.lMajorTicksIncrementDateTime->setEnabled(b); dtsbMajorTicksIncrement->setEnabled(b); ui.lMajorTicksLineStyle->setEnabled(b); ui.cbMajorTicksLineStyle->setEnabled(b); dtsbMinorTicksIncrement->setEnabled(b); if (b) { auto penStyle = Qt::PenStyle(ui.cbMajorTicksLineStyle->currentIndex()); b = (penStyle != Qt::NoPen); } ui.lMajorTicksColor->setEnabled(b); ui.kcbMajorTicksColor->setEnabled(b); ui.lMajorTicksWidth->setEnabled(b); ui.sbMajorTicksWidth->setEnabled(b); ui.lMajorTicksLength->setEnabled(b); ui.sbMajorTicksLength->setEnabled(b); ui.lMajorTicksOpacity->setEnabled(b); ui.sbMajorTicksOpacity->setEnabled(b); if (m_initializing) return; for (auto* axis : m_axesList) axis->setMajorTicksDirection(direction); } /*! called if the current style of the ticks (Number or Increment) is changed. Shows/hides the corresponding widgets. */ void AxisDock::majorTicksTypeChanged(int index) { if (!m_axis) // If elements are added to the combobox 'cbMajorTicksType' (at init of this class), then this function is called, which is a problem if no axis are available return; auto type = Axis::TicksType(index); if (type == Axis::TicksTotalNumber) { ui.lMajorTicksNumber->show(); ui.sbMajorTicksNumber->show(); - ui.lMajorTicksIncrementNumeric->hide(); - ui.sbMajorTicksIncrementNumeric->hide(); + ui.lMajorTicksSpacingNumeric->hide(); + ui.sbMajorTicksSpacingNumeric->hide(); ui.lMajorTicksIncrementDateTime->hide(); dtsbMajorTicksIncrement->hide(); ui.lMajorTicksColumn->hide(); cbMajorTicksColumn->hide(); - } else if (type == Axis::TicksIncrement) { + } else if (type == Axis::TicksSpacing) { ui.lMajorTicksNumber->hide(); ui.sbMajorTicksNumber->hide(); - ui.lMajorTicksIncrementNumeric->show(); + ui.lMajorTicksSpacingNumeric->show(); const auto* plot = static_cast(m_axis->parentAspect()); bool numeric = ( (m_axis->orientation() == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (m_axis->orientation() == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ); if (numeric) { ui.lMajorTicksIncrementDateTime->hide(); dtsbMajorTicksIncrement->hide(); - ui.lMajorTicksIncrementNumeric->show(); - ui.sbMajorTicksIncrementNumeric->show(); + ui.lMajorTicksSpacingNumeric->show(); + ui.sbMajorTicksSpacingNumeric->show(); } else { ui.lMajorTicksIncrementDateTime->show(); dtsbMajorTicksIncrement->show(); - ui.lMajorTicksIncrementNumeric->hide(); - ui.sbMajorTicksIncrementNumeric->hide(); + ui.lMajorTicksSpacingNumeric->hide(); + ui.sbMajorTicksSpacingNumeric->hide(); } ui.lMajorTicksColumn->hide(); cbMajorTicksColumn->hide(); - // Check if Increment is not to small - majorTicksIncrementChanged(); + // Check if spacing is not to small + majorTicksSpacingChanged(); } else { ui.lMajorTicksNumber->hide(); ui.sbMajorTicksNumber->hide(); - ui.lMajorTicksIncrementNumeric->hide(); - ui.sbMajorTicksIncrementNumeric->hide(); - dtsbMajorTicksIncrement->hide(); + ui.lMajorTicksSpacingNumeric->hide(); + ui.sbMajorTicksSpacingNumeric->hide(); + ui.lMajorTicksIncrementDateTime->hide(); dtsbMajorTicksIncrement->hide(); ui.lMajorTicksColumn->show(); cbMajorTicksColumn->show(); } if (m_initializing) return; for (auto* axis : m_axesList) axis->setMajorTicksType(type); } void AxisDock::majorTicksNumberChanged(int value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setMajorTicksNumber(value); } -void AxisDock::majorTicksIncrementChanged() { +void AxisDock::majorTicksSpacingChanged() { if (m_initializing) return; const auto* plot = static_cast(m_axis->parentAspect()); bool numeric = ( (m_axis->orientation() == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (m_axis->orientation() == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ); - double value = numeric ? ui.sbMajorTicksIncrementNumeric->value() : dtsbMajorTicksIncrement->value(); - double diff = m_axis->end() - m_axis->start(); - - if (value == 0 || diff / value > 100 || value < 0) { // maximum of 100 ticks + double value = numeric ? ui.sbMajorTicksSpacingNumeric->value() : dtsbMajorTicksIncrement->value(); + double diff = fabs(m_axis->end() - m_axis->start()); + DEBUG("value = " << value << ", diff = " << diff) + // fix spacing if incorrect + if (value == 0. || diff / value > 100.) { if (value == 0) value = diff / ui.sbMajorTicksNumber->value(); - if (diff / value > 100) - value = diff / 100; + if (diff / value > 100.) // maximum of 100 ticks + value = diff / 100.; + DEBUG("new value = " << value) // determine stepsize and number of decimals m_initializing = true; if (numeric) { - int decimal = determineDecimals(value * 10); - ui.sbMajorTicksIncrementNumeric->setDecimals(decimal); - ui.sbMajorTicksIncrementNumeric->setSingleStep(determineStep(diff, decimal)); - ui.sbMajorTicksIncrementNumeric->setValue(value); - } else + //TODO: check + int decimal = determineDecimals(value * 10.); + DEBUG("decimal = " << decimal) + ui.sbMajorTicksSpacingNumeric->setDecimals(decimal); + ui.sbMajorTicksSpacingNumeric->setSingleStep(determineStep(diff, decimal)); + ui.sbMajorTicksSpacingNumeric->setValue(value); + } else //TODO: check reversed axis dtsbMajorTicksIncrement->setValue(value); m_initializing = false; } for (auto* axis : m_axesList) - axis->setMajorTicksIncrement(value); + axis->setMajorTicksSpacing(value); } void AxisDock::majorTicksLineStyleChanged(int index) { auto penStyle = Qt::PenStyle(index); bool b = (penStyle != Qt::NoPen); ui.lMajorTicksColor->setEnabled(b); ui.kcbMajorTicksColor->setEnabled(b); ui.lMajorTicksWidth->setEnabled(b); ui.sbMajorTicksWidth->setEnabled(b); ui.lMajorTicksLength->setEnabled(b); ui.sbMajorTicksLength->setEnabled(b); ui.lMajorTicksOpacity->setEnabled(b); ui.sbMajorTicksOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->majorTicksPen(); pen.setStyle(penStyle); axis->setMajorTicksPen(pen); } } void AxisDock::majorTicksColumnChanged(const QModelIndex& index) { if (m_initializing) return; auto* aspect = static_cast(index.internalPointer()); AbstractColumn* column = nullptr; if (aspect) { column = dynamic_cast(aspect); Q_ASSERT(column != nullptr); } for (auto* axis : m_axesList) axis->setMajorTicksColumn(column); } void AxisDock::majorTicksColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->majorTicksPen(); pen.setColor(color); axis->setMajorTicksPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbMajorTicksLineStyle, color); m_initializing = false; } void AxisDock::majorTicksWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->majorTicksPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); axis->setMajorTicksPen(pen); } } void AxisDock::majorTicksLengthChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setMajorTicksLength( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void AxisDock::majorTicksOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setMajorTicksOpacity(opacity); } //"Minor ticks" tab void AxisDock::minorTicksDirectionChanged(int index) { Axis::TicksDirection direction = Axis::TicksDirection(index); bool b = (direction != Axis::noTicks); ui.lMinorTicksType->setEnabled(b); ui.cbMinorTicksType->setEnabled(b); ui.lMinorTicksType->setEnabled(b); ui.cbMinorTicksType->setEnabled(b); ui.lMinorTicksNumber->setEnabled(b); ui.sbMinorTicksNumber->setEnabled(b); - ui.lMinorTicksIncrementNumeric->setEnabled(b); - ui.sbMinorTicksIncrementNumeric->setEnabled(b); + ui.lMinorTicksSpacingNumeric->setEnabled(b); + ui.sbMinorTicksSpacingNumeric->setEnabled(b); ui.lMinorTicksIncrementDateTime->setEnabled(b); dtsbMinorTicksIncrement->setEnabled(b); ui.lMinorTicksLineStyle->setEnabled(b); ui.cbMinorTicksLineStyle->setEnabled(b); if (b) { auto penStyle = Qt::PenStyle(ui.cbMinorTicksLineStyle->currentIndex()); b = (penStyle != Qt::NoPen); } ui.lMinorTicksColor->setEnabled(b); ui.kcbMinorTicksColor->setEnabled(b); ui.lMinorTicksWidth->setEnabled(b); ui.sbMinorTicksWidth->setEnabled(b); ui.lMinorTicksLength->setEnabled(b); ui.sbMinorTicksLength->setEnabled(b); ui.lMinorTicksOpacity->setEnabled(b); ui.sbMinorTicksOpacity->setEnabled(b); if (m_initializing) return; for (auto* axis : m_axesList) axis->setMinorTicksDirection(direction); } void AxisDock::minorTicksTypeChanged(int index) { if (!m_axis) // If elements are added to the combobox 'cbMajorTicksType' (at init of this class), then this function is called, which is a problem if no axis are available return; auto type = Axis::TicksType(index); if (type == Axis::TicksTotalNumber) { ui.lMinorTicksNumber->show(); ui.sbMinorTicksNumber->show(); - ui.lMinorTicksIncrementNumeric->hide(); - ui.sbMinorTicksIncrementNumeric->hide(); + ui.lMinorTicksSpacingNumeric->hide(); + ui.sbMinorTicksSpacingNumeric->hide(); ui.lMinorTicksColumn->hide(); cbMinorTicksColumn->hide(); ui.lMinorTicksIncrementDateTime->hide(); dtsbMinorTicksIncrement->hide(); - } else if ( type == Axis::TicksIncrement) { + } else if ( type == Axis::TicksSpacing) { ui.lMinorTicksNumber->hide(); ui.sbMinorTicksNumber->hide(); const auto* plot = static_cast(m_axis->parentAspect()); bool numeric = ( (m_axis->orientation() == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (m_axis->orientation() == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ); if (numeric) { - ui.lMinorTicksIncrementNumeric->show(); - ui.sbMinorTicksIncrementNumeric->show(); + ui.lMinorTicksSpacingNumeric->show(); + ui.sbMinorTicksSpacingNumeric->show(); ui.lMinorTicksIncrementDateTime->hide(); dtsbMinorTicksIncrement->hide(); } else { - ui.lMinorTicksIncrementNumeric->hide(); - ui.sbMinorTicksIncrementNumeric->hide(); + ui.lMinorTicksSpacingNumeric->hide(); + ui.sbMinorTicksSpacingNumeric->hide(); ui.lMinorTicksIncrementDateTime->show(); dtsbMinorTicksIncrement->show(); } ui.lMinorTicksColumn->hide(); cbMinorTicksColumn->hide(); - // Check if Increment is not to small - minorTicksIncrementChanged(); + // Check if spacing is not to small + minorTicksSpacingChanged(); } else { ui.lMinorTicksNumber->hide(); ui.sbMinorTicksNumber->hide(); - ui.lMinorTicksIncrementNumeric->hide(); - ui.sbMinorTicksIncrementNumeric->hide(); + ui.lMinorTicksSpacingNumeric->hide(); + ui.sbMinorTicksSpacingNumeric->hide(); ui.lMinorTicksIncrementDateTime->hide(); dtsbMinorTicksIncrement->hide(); ui.lMinorTicksColumn->show(); cbMinorTicksColumn->show(); } if (m_initializing) return; for (auto* axis : m_axesList) axis->setMinorTicksType(type); } void AxisDock::minorTicksNumberChanged(int value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setMinorTicksNumber(value); } -void AxisDock::minorTicksIncrementChanged() { +void AxisDock::minorTicksSpacingChanged() { if (m_initializing) return; const auto* plot = static_cast(m_axis->parentAspect()); bool numeric = ( (m_axis->orientation() == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (m_axis->orientation() == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ); - double value = numeric ? ui.sbMinorTicksIncrementNumeric->value() : dtsbMinorTicksIncrement->value(); + double value = numeric ? ui.sbMinorTicksSpacingNumeric->value() : dtsbMinorTicksIncrement->value(); double numberTicks = 0.0; + //TODO: check if (value > 0) numberTicks = (m_axis->end() - m_axis->start()) / (m_axis->majorTicksNumber() - 1) / value -1; // recal - if (value == 0 || numberTicks > 100 || value < 0) { + if (value == 0. || numberTicks > 100 || value < 0) { if (value == 0) value = (m_axis->end() - m_axis->start()) / (m_axis->majorTicksNumber() - 1) / (ui.sbMinorTicksNumber->value() + 1); numberTicks = (m_axis->end() - m_axis->start()) / (m_axis->majorTicksNumber() - 1) / value -1; // recalculate number of ticks if (numberTicks > 100) // maximum 100 minor ticks value = (m_axis->end() - m_axis->start()) / (m_axis->majorTicksNumber() - 1) / (100 + 1); // determine stepsize and number of decimals m_initializing = true; if (numeric) { int decimal = determineDecimals(value * 10); - ui.sbMinorTicksIncrementNumeric->setDecimals(decimal); - ui.sbMinorTicksIncrementNumeric->setSingleStep(determineStep((m_axis->end() - m_axis->start()) / (m_axis->majorTicksNumber() - 1), decimal)); - ui.sbMinorTicksIncrementNumeric->setValue(value); + ui.sbMinorTicksSpacingNumeric->setDecimals(decimal); + ui.sbMinorTicksSpacingNumeric->setSingleStep(determineStep((m_axis->end() - m_axis->start()) / (m_axis->majorTicksNumber() - 1), decimal)); + ui.sbMinorTicksSpacingNumeric->setValue(value); } else dtsbMinorTicksIncrement->setValue(value); m_initializing = false; } for (auto* axis : m_axesList) - axis->setMinorTicksIncrement(value); + axis->setMinorTicksSpacing(value); } void AxisDock::minorTicksColumnChanged(const QModelIndex& index) { if (m_initializing) return; auto* aspect = static_cast(index.internalPointer()); auto* column = dynamic_cast(aspect); Q_ASSERT(column != nullptr); for (auto* axis : m_axesList) axis->setMinorTicksColumn(column); } void AxisDock::minorTicksLineStyleChanged(int index) { auto penStyle = Qt::PenStyle(index); bool b = (penStyle != Qt::NoPen); ui.lMinorTicksColor->setEnabled(b); ui.kcbMinorTicksColor->setEnabled(b); ui.lMinorTicksWidth->setEnabled(b); ui.sbMinorTicksWidth->setEnabled(b); ui.lMinorTicksLength->setEnabled(b); ui.sbMinorTicksLength->setEnabled(b); ui.lMinorTicksOpacity->setEnabled(b); ui.sbMinorTicksOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->minorTicksPen(); pen.setStyle(penStyle); axis->setMinorTicksPen(pen); } } void AxisDock::minorTicksColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->minorTicksPen(); pen.setColor(color); axis->setMinorTicksPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbMinorTicksLineStyle, color); m_initializing = false; } void AxisDock::minorTicksWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->minorTicksPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); axis->setMinorTicksPen(pen); } } void AxisDock::minorTicksLengthChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setMinorTicksLength( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void AxisDock::minorTicksOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setMinorTicksOpacity(opacity); } //"Tick labels"-tab void AxisDock::labelsFormatChanged(int index) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsFormat(Axis::LabelsFormat(index)); } void AxisDock::labelsPrecisionChanged(int value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsPrecision(value); } void AxisDock::labelsAutoPrecisionChanged(int state) { bool checked = (state == Qt::Checked); ui.sbLabelsPrecision->setEnabled(!checked); if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsAutoPrecision(checked); } void AxisDock::labelsDateTimeFormatChanged(int) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsDateTimeFormat(ui.cbLabelsDateTimeFormat->currentText()); } void AxisDock::labelsPositionChanged(int index) { auto position = Axis::LabelsPosition(index); bool b = (position != Axis::NoLabels); ui.lLabelsOffset->setEnabled(b); ui.sbLabelsOffset->setEnabled(b); ui.lLabelsRotation->setEnabled(b); ui.sbLabelsRotation->setEnabled(b); ui.lLabelsFont->setEnabled(b); ui.kfrLabelsFont->setEnabled(b); ui.lLabelsColor->setEnabled(b); ui.kcbLabelsFontColor->setEnabled(b); ui.lLabelsPrefix->setEnabled(b); ui.leLabelsPrefix->setEnabled(b); ui.lLabelsSuffix->setEnabled(b); ui.leLabelsSuffix->setEnabled(b); ui.lLabelsOpacity->setEnabled(b); ui.sbLabelsOpacity->setEnabled(b); if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsPosition(position); } void AxisDock::labelsOffsetChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsOffset( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void AxisDock::labelsRotationChanged(int value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsRotationAngle(value); } void AxisDock::labelsPrefixChanged() { if (m_initializing) return; QString prefix = ui.leLabelsPrefix->text(); for (auto* axis : m_axesList) axis->setLabelsPrefix(prefix); } void AxisDock::labelsSuffixChanged() { if (m_initializing) return; QString suffix = ui.leLabelsSuffix->text(); for (auto* axis : m_axesList) axis->setLabelsSuffix(suffix); } void AxisDock::labelsFontChanged(const QFont& font) { if (m_initializing) return; QFont labelsFont = font; labelsFont.setPixelSize( Worksheet::convertToSceneUnits(font.pointSizeF(), Worksheet::Point) ); for (auto* axis : m_axesList) axis->setLabelsFont( labelsFont ); } void AxisDock::labelsFontColorChanged(const QColor& color) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsColor(color); } void AxisDock::labelsOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setLabelsOpacity(opacity); } // "Grid"-tab //major grid void AxisDock::majorGridStyleChanged(int index) { auto penStyle = Qt::PenStyle(index); bool b = (penStyle != Qt::NoPen); ui.lMajorGridColor->setEnabled(b); ui.kcbMajorGridColor->setEnabled(b); ui.lMajorGridWidth->setEnabled(b); ui.sbMajorGridWidth->setEnabled(b); ui.lMajorGridOpacity->setEnabled(b); ui.sbMajorGridOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->majorGridPen(); pen.setStyle(penStyle); axis->setMajorGridPen(pen); } } void AxisDock::majorGridColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->majorGridPen(); pen.setColor(color); axis->setMajorGridPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbMajorGridStyle, color); m_initializing = false; } void AxisDock::majorGridWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->majorGridPen(); pen.setWidthF(Worksheet::convertToSceneUnits(value, Worksheet::Point)); axis->setMajorGridPen(pen); } } void AxisDock::majorGridOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setMajorGridOpacity(opacity); } //minor grid void AxisDock::minorGridStyleChanged(int index) { auto penStyle = Qt::PenStyle(index); bool b = (penStyle != Qt::NoPen); ui.lMinorGridColor->setEnabled(b); ui.kcbMinorGridColor->setEnabled(b); ui.lMinorGridWidth->setEnabled(b); ui.sbMinorGridWidth->setEnabled(b); ui.lMinorGridOpacity->setEnabled(b); ui.sbMinorGridOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->minorGridPen(); pen.setStyle(penStyle); axis->setMinorGridPen(pen); } } void AxisDock::minorGridColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->minorGridPen(); pen.setColor(color); axis->setMinorGridPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbMinorGridStyle, color); m_initializing = false; } void AxisDock::minorGridWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen = axis->minorGridPen(); pen.setWidthF(Worksheet::convertToSceneUnits(value, Worksheet::Point)); axis->setMinorGridPen(pen); } } void AxisDock::minorGridOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setMinorGridOpacity(opacity); } //************************************************************* //************ SLOTs for changes triggered in Axis ************ //************************************************************* void AxisDock::axisDescriptionChanged(const AbstractAspect* aspect) { if (m_axis != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) { ui.leName->setText(aspect->name()); } else if (aspect->comment() != ui.leComment->text()) { ui.leComment->setText(aspect->comment()); } m_initializing = false; } void AxisDock::axisOrientationChanged(Axis::AxisOrientation orientation) { m_initializing = true; ui.cbOrientation->setCurrentIndex( (int)orientation ); m_initializing = false; } void AxisDock::axisPositionChanged(Axis::AxisPosition position) { m_initializing = true; //map from the enum Axis::AxisOrientation to the index in the combo box int index(position); if (index > 1) ui.cbPosition->setCurrentIndex(index-2); else ui.cbPosition->setCurrentIndex(index); m_initializing = false; } void AxisDock::axisPositionChanged(float value) { m_initializing = true; ui.lePosition->setText( QString::number(value) ); m_initializing = false; } void AxisDock::axisScaleChanged(Axis::AxisScale scale) { m_initializing = true; ui.cbScale->setCurrentIndex( (int)scale ); m_initializing = false; } void AxisDock::axisAutoScaleChanged(bool on) { m_initializing = true; ui.chkAutoScale->setChecked(on); m_initializing = false; } void AxisDock::axisStartChanged(double value) { if (m_initializing) return; const Lock lock(m_initializing); ui.leStart->setText( QString::number(value) ); ui.dateTimeEditStart->setDateTime( QDateTime::fromMSecsSinceEpoch(value) ); // determine stepsize and number of decimals double diff = m_axis->end() - m_axis->start(); int decimal = determineDecimals(diff); - ui.sbMajorTicksIncrementNumeric->setDecimals(decimal); - ui.sbMajorTicksIncrementNumeric->setSingleStep(determineStep(diff, decimal)); + ui.sbMajorTicksSpacingNumeric->setDecimals(decimal); + ui.sbMajorTicksSpacingNumeric->setSingleStep(determineStep(diff, decimal)); } void AxisDock::axisEndChanged(double value) { if (m_initializing) return; const Lock lock(m_initializing); ui.leEnd->setText( QString::number(value) ); ui.dateTimeEditEnd->setDateTime( QDateTime::fromMSecsSinceEpoch(value) ); - ui.sbMajorTicksIncrementNumeric->setSingleStep(floor(m_axis->end() - m_axis->start())/10); + ui.sbMajorTicksSpacingNumeric->setSingleStep(floor(m_axis->end() - m_axis->start())/10); // determine stepsize and number of decimals double diff = m_axis->end() - m_axis->start(); int decimal = determineDecimals(diff); - ui.sbMajorTicksIncrementNumeric->setDecimals(decimal); - ui.sbMajorTicksIncrementNumeric->setSingleStep(determineStep(diff, decimal)); + ui.sbMajorTicksSpacingNumeric->setDecimals(decimal); + ui.sbMajorTicksSpacingNumeric->setSingleStep(determineStep(diff, decimal)); } void AxisDock::axisZeroOffsetChanged(qreal value) { if (m_initializing) return; const Lock lock(m_initializing); ui.leZeroOffset->setText( QString::number(value) ); } void AxisDock::axisScalingFactorChanged(qreal value) { if (m_initializing) return; const Lock lock(m_initializing); ui.leScalingFactor->setText( QString::number(value) ); } //line void AxisDock::axisLinePenChanged(const QPen& pen) { m_initializing = true; ui.cbLineStyle->setCurrentIndex( pen.style() ); ui.kcbLineColor->setColor( pen.color() ); GuiTools::updatePenStyles(ui.cbLineStyle, pen.color() ); ui.sbLineWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(), Worksheet::Point) ); m_initializing = false; } void AxisDock::axisArrowTypeChanged(Axis::ArrowType type) { m_initializing = true; ui.cbArrowType->setCurrentIndex((int)type); m_initializing = false; } void AxisDock::axisLineOpacityChanged(qreal opacity) { m_initializing = true; ui.sbLineOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void AxisDock::axisArrowPositionChanged(Axis::ArrowPosition position) { m_initializing = true; ui.cbArrowPosition->setCurrentIndex( (int)position ); m_initializing = false; } void AxisDock::axisArrowSizeChanged(qreal size) { m_initializing = true; ui.sbArrowSize->setValue( (int)Worksheet::convertFromSceneUnits(size, Worksheet::Point) ); m_initializing = false; } //major ticks void AxisDock::axisMajorTicksDirectionChanged(Axis::TicksDirection direction) { m_initializing = true; ui.cbMajorTicksDirection->setCurrentIndex(direction); m_initializing = false; } void AxisDock::axisMajorTicksTypeChanged(Axis::TicksType type) { m_initializing = true; ui.cbMajorTicksType->setCurrentIndex(type); m_initializing = false; } void AxisDock::axisMajorTicksNumberChanged(int number) { m_initializing = true; ui.sbMajorTicksNumber->setValue(number); m_initializing = false; } -void AxisDock::axisMajorTicksIncrementChanged(qreal increment) { +void AxisDock::axisMajorTicksSpacingChanged(qreal increment) { m_initializing = true; const auto* plot = dynamic_cast(m_axis->parentAspect()); if (plot) { bool numeric = ( (m_axis->orientation() == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (m_axis->orientation() == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ); if (numeric) - ui.sbMajorTicksIncrementNumeric->setValue(increment); + ui.sbMajorTicksSpacingNumeric->setValue(increment); else { dtsbMajorTicksIncrement->setValue(increment); } } m_initializing = false; } void AxisDock::axisMajorTicksPenChanged(const QPen& pen) { m_initializing = true; ui.cbMajorTicksLineStyle->setCurrentIndex(pen.style()); ui.kcbMajorTicksColor->setColor(pen.color()); ui.sbMajorTicksWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point) ); m_initializing = false; } void AxisDock::axisMajorTicksLengthChanged(qreal length) { m_initializing = true; ui.sbMajorTicksLength->setValue( Worksheet::convertFromSceneUnits(length,Worksheet::Point) ); m_initializing = false; } void AxisDock::axisMajorTicksOpacityChanged(qreal opacity) { m_initializing = true; ui.sbMajorTicksOpacity->setValue( round(opacity*100.0)); m_initializing = false; } //minor ticks void AxisDock::axisMinorTicksDirectionChanged(Axis::TicksDirection direction) { m_initializing = true; ui.cbMinorTicksDirection->setCurrentIndex(direction); m_initializing = false; } void AxisDock::axisMinorTicksTypeChanged(Axis::TicksType type) { m_initializing = true; ui.cbMinorTicksType->setCurrentIndex(type); m_initializing = false; } void AxisDock::axisMinorTicksNumberChanged(int number) { m_initializing = true; ui.sbMinorTicksNumber->setValue(number); m_initializing = false; } -void AxisDock::axisMinorTicksIncrementChanged(qreal increment) { +void AxisDock::axisMinorTicksSpacingChanged(qreal increment) { m_initializing = true; const auto* plot = dynamic_cast(m_axis->parentAspect()); if (plot) { bool numeric = ( (m_axis->orientation() == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (m_axis->orientation() == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ); if (numeric) - ui.sbMinorTicksIncrementNumeric->setValue(increment); + ui.sbMinorTicksSpacingNumeric->setValue(increment); else { dtsbMinorTicksIncrement->setValue(increment); } } m_initializing = false; } void AxisDock::axisMinorTicksPenChanged(const QPen& pen) { m_initializing = true; ui.cbMinorTicksLineStyle->setCurrentIndex(pen.style()); ui.kcbMinorTicksColor->setColor(pen.color()); ui.sbMinorTicksWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point) ); m_initializing = false; } void AxisDock::axisMinorTicksLengthChanged(qreal length) { m_initializing = true; ui.sbMinorTicksLength->setValue( Worksheet::convertFromSceneUnits(length,Worksheet::Point) ); m_initializing = false; } void AxisDock::axisMinorTicksOpacityChanged(qreal opacity) { m_initializing = true; ui.sbMinorTicksOpacity->setValue(round(opacity*100.0)); m_initializing = false; } //labels void AxisDock::axisLabelsFormatChanged(Axis::LabelsFormat format) { m_initializing = true; ui.cbLabelsFormat->setCurrentIndex(format); m_initializing = false; } void AxisDock::axisLabelsAutoPrecisionChanged(bool on) { m_initializing = true; ui.chkLabelsAutoPrecision->setChecked((int) on); m_initializing = false; } void AxisDock::axisLabelsPrecisionChanged(int precision) { m_initializing = true; ui.sbLabelsPrecision->setValue(precision); m_initializing = false; } void AxisDock::axisLabelsDateTimeFormatChanged(const QString& format) { m_initializing = true; ui.cbLabelsDateTimeFormat->setCurrentText(format); m_initializing = false; } void AxisDock::axisLabelsPositionChanged(Axis::LabelsPosition position) { m_initializing = true; ui.cbLabelsPosition->setCurrentIndex(position); m_initializing = false; } void AxisDock::axisLabelsOffsetChanged(double offset) { m_initializing = true; ui.sbLabelsOffset->setValue( Worksheet::convertFromSceneUnits(offset, Worksheet::Point) ); m_initializing = false; } void AxisDock::axisLabelsRotationAngleChanged(qreal rotation) { m_initializing = true; ui.sbLabelsRotation->setValue(rotation); m_initializing = false; } void AxisDock::axisLabelsFontChanged(const QFont& font) { m_initializing = true; //we need to set the font size in points for KFontRequester QFont newFont(font); newFont.setPointSizeF( round(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); ui.kfrLabelsFont->setFont(newFont); m_initializing = false; } void AxisDock::axisLabelsFontColorChanged(const QColor& color) { m_initializing = true; ui.kcbLabelsFontColor->setColor(color); m_initializing = false; } void AxisDock::axisLabelsPrefixChanged(const QString& prefix) { m_initializing = true; ui.leLabelsPrefix->setText(prefix); m_initializing = false; } void AxisDock::axisLabelsSuffixChanged(const QString& suffix) { m_initializing = true; ui.leLabelsSuffix->setText(suffix); m_initializing = false; } void AxisDock::axisLabelsOpacityChanged(qreal opacity) { m_initializing = true; ui.sbLabelsOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } //grid void AxisDock::axisMajorGridPenChanged(const QPen& pen) { m_initializing = true; ui.cbMajorGridStyle->setCurrentIndex((int) pen.style()); ui.kcbMajorGridColor->setColor(pen.color()); GuiTools::updatePenStyles(ui.cbMajorGridStyle, pen.color()); ui.sbMajorGridWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point)); m_initializing = false; } void AxisDock::axisMajorGridOpacityChanged(qreal opacity) { m_initializing = true; ui.sbMajorGridOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void AxisDock::axisMinorGridPenChanged(const QPen& pen) { m_initializing = true; ui.cbMinorGridStyle->setCurrentIndex((int) pen.style()); ui.kcbMinorGridColor->setColor(pen.color()); GuiTools::updatePenStyles(ui.cbMinorGridStyle, pen.color()); ui.sbMinorGridWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point)); m_initializing = false; } void AxisDock::axisMinorGridOpacityChanged(qreal opacity) { m_initializing = true; ui.sbMinorGridOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void AxisDock::axisVisibilityChanged(bool on) { m_initializing = true; ui.chkVisible->setChecked(on); m_initializing = false; } //************************************************************* //************************* Settings ************************** //************************************************************* void AxisDock::load() { //General ui.chkVisible->setChecked( m_axis->isVisible() ); ui.cbOrientation->setCurrentIndex( (int) m_axis->orientation() ); int index = (int)m_axis->position(); if (index > 1) ui.cbPosition->setCurrentIndex(index-2); else ui.cbPosition->setCurrentIndex(index); ui.lePosition->setText( QString::number( m_axis->offset()) ); ui.cbScale->setCurrentIndex( (int) m_axis->scale() ); ui.chkAutoScale->setChecked( m_axis->autoScale() ); ui.leStart->setText( QString::number(m_axis->start()) ); ui.leEnd->setText( QString::number(m_axis->end()) ); - - ui.sbMajorTicksIncrementNumeric->setDecimals(0); - ui.sbMajorTicksIncrementNumeric->setSingleStep(m_axis->majorTicksIncrement()); + ui.sbMajorTicksSpacingNumeric->setDecimals(0); + ui.sbMajorTicksSpacingNumeric->setSingleStep(m_axis->majorTicksSpacing()); //depending on range format of the axis (numeric vs. datetime), show/hide the corresponding widgets const auto* plot = dynamic_cast(m_axis->parentAspect()); if (plot) { bool numeric = ( (m_axis->orientation() == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (m_axis->orientation() == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ); //ranges ui.lStart->setVisible(numeric); ui.lEnd->setVisible(numeric); ui.leStart->setVisible(numeric); ui.leEnd->setVisible(numeric); ui.lStartDateTime->setVisible(!numeric); ui.dateTimeEditStart->setVisible(!numeric); ui.lEndDateTime->setVisible(!numeric); ui.dateTimeEditEnd->setVisible(!numeric); //tick labels format ui.lLabelsFormat->setVisible(numeric); ui.cbLabelsFormat->setVisible(numeric); ui.chkLabelsAutoPrecision->setVisible(numeric); ui.lLabelsPrecision->setVisible(numeric); ui.sbLabelsPrecision->setVisible(numeric); ui.cbLabelsDateTimeFormat->setVisible(numeric); ui.lLabelsDateTimeFormat->setVisible(!numeric); ui.cbLabelsDateTimeFormat->setVisible(!numeric); if (!numeric) { if (m_axis->orientation() == Axis::AxisHorizontal) { ui.dateTimeEditStart->setDisplayFormat(plot->xRangeDateTimeFormat()); ui.dateTimeEditEnd->setDisplayFormat(plot->xRangeDateTimeFormat()); } else { ui.dateTimeEditStart->setDisplayFormat(plot->yRangeDateTimeFormat()); ui.dateTimeEditEnd->setDisplayFormat(plot->yRangeDateTimeFormat()); } ui.dateTimeEditStart->setDateTime(QDateTime::fromMSecsSinceEpoch(m_axis->start())); ui.dateTimeEditEnd->setDateTime(QDateTime::fromMSecsSinceEpoch(m_axis->end())); } } ui.leZeroOffset->setText( QString::number(m_axis->zeroOffset()) ); ui.leScalingFactor->setText( QString::number(m_axis->scalingFactor()) ); //Line ui.cbLineStyle->setCurrentIndex( (int) m_axis->linePen().style() ); ui.kcbLineColor->setColor( m_axis->linePen().color() ); ui.sbLineWidth->setValue( Worksheet::convertFromSceneUnits(m_axis->linePen().widthF(),Worksheet::Point) ); ui.sbLineOpacity->setValue( round(m_axis->lineOpacity()*100.0) ); ui.cbArrowType->setCurrentIndex( (int)m_axis->arrowType() ); ui.cbArrowPosition->setCurrentIndex( (int)m_axis->arrowPosition() ); ui.sbArrowSize->setValue( (int)Worksheet::convertFromSceneUnits(m_axis->arrowSize(), Worksheet::Point) ); //Major ticks ui.cbMajorTicksDirection->setCurrentIndex( (int) m_axis->majorTicksDirection() ); ui.cbMajorTicksType->setCurrentIndex( (int) m_axis->majorTicksType() ); ui.sbMajorTicksNumber->setValue( m_axis->majorTicksNumber() ); ui.cbMajorTicksLineStyle->setCurrentIndex( (int) m_axis->majorTicksPen().style() ); ui.kcbMajorTicksColor->setColor( m_axis->majorTicksPen().color() ); ui.sbMajorTicksWidth->setValue( Worksheet::convertFromSceneUnits( m_axis->majorTicksPen().widthF(),Worksheet::Point) ); ui.sbMajorTicksLength->setValue( Worksheet::convertFromSceneUnits( m_axis->majorTicksLength(),Worksheet::Point) ); ui.sbMajorTicksOpacity->setValue( round(m_axis->majorTicksOpacity()*100.0) ); //Minor ticks ui.cbMinorTicksDirection->setCurrentIndex( (int) m_axis->minorTicksDirection() ); ui.cbMinorTicksType->setCurrentIndex( (int) m_axis->minorTicksType() ); ui.sbMinorTicksNumber->setValue( m_axis->minorTicksNumber() ); ui.cbMinorTicksLineStyle->setCurrentIndex( (int) m_axis->minorTicksPen().style() ); ui.kcbMinorTicksColor->setColor( m_axis->minorTicksPen().color() ); ui.sbMinorTicksWidth->setValue( Worksheet::convertFromSceneUnits(m_axis->minorTicksPen().widthF(),Worksheet::Point) ); ui.sbMinorTicksLength->setValue( Worksheet::convertFromSceneUnits(m_axis->minorTicksLength(),Worksheet::Point) ); ui.sbMinorTicksOpacity->setValue( round(m_axis->minorTicksOpacity()*100.0) ); //Extra ticks //TODO // Tick label ui.cbLabelsPosition->setCurrentIndex( (int) m_axis->labelsPosition() ); ui.sbLabelsOffset->setValue( Worksheet::convertFromSceneUnits(m_axis->labelsOffset(),Worksheet::Point) ); ui.sbLabelsRotation->setValue( m_axis->labelsRotationAngle() ); ui.cbLabelsFormat->setCurrentIndex( (int) m_axis->labelsFormat() ); ui.chkLabelsAutoPrecision->setChecked( (int) m_axis->labelsAutoPrecision() ); ui.sbLabelsPrecision->setValue( (int)m_axis->labelsPrecision() ); ui.cbLabelsDateTimeFormat->setCurrentText(m_axis->labelsDateTimeFormat()); //we need to set the font size in points for KFontRequester QFont font = m_axis->labelsFont(); font.setPointSizeF( round(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); ui.kfrLabelsFont->setFont( font ); ui.kcbLabelsFontColor->setColor( m_axis->labelsColor() ); ui.leLabelsPrefix->setText( m_axis->labelsPrefix() ); ui.leLabelsSuffix->setText( m_axis->labelsSuffix() ); ui.sbLabelsOpacity->setValue( round(m_axis->labelsOpacity()*100.0) ); //Grid ui.cbMajorGridStyle->setCurrentIndex( (int) m_axis->majorGridPen().style() ); ui.kcbMajorGridColor->setColor( m_axis->majorGridPen().color() ); ui.sbMajorGridWidth->setValue( Worksheet::convertFromSceneUnits(m_axis->majorGridPen().widthF(),Worksheet::Point) ); ui.sbMajorGridOpacity->setValue( round(m_axis->majorGridOpacity()*100.0) ); ui.cbMinorGridStyle->setCurrentIndex( (int) m_axis->minorGridPen().style() ); ui.kcbMinorGridColor->setColor( m_axis->minorGridPen().color() ); ui.sbMinorGridWidth->setValue( Worksheet::convertFromSceneUnits(m_axis->minorGridPen().widthF(),Worksheet::Point) ); ui.sbMinorGridOpacity->setValue( round(m_axis->minorGridOpacity()*100.0) ); GuiTools::updatePenStyles(ui.cbLineStyle, ui.kcbLineColor->color()); this->majorTicksTypeChanged(ui.cbMajorTicksType->currentIndex()); GuiTools::updatePenStyles(ui.cbMajorTicksLineStyle, ui.kcbMajorTicksColor->color()); this->minorTicksTypeChanged(ui.cbMinorTicksType->currentIndex()); GuiTools::updatePenStyles(ui.cbMinorTicksLineStyle, ui.kcbMinorTicksColor->color()); GuiTools::updatePenStyles(ui.cbMajorGridStyle, ui.kcbMajorGridColor->color()); GuiTools::updatePenStyles(ui.cbMinorGridStyle, ui.kcbMinorGridColor->color()); } /*! * Determine the number of decimals for using in a QDoubleSpinBox * \param diff * \return two relevant digits */ int AxisDock::determineDecimals(double diff) { diff /= 10.; // step one decimal before double power10 = 1.; for (int i = 0; i < 10; i++) { double nearest = round(fabs(diff) * power10) / power10; if (nearest > 0) return i+1; power10 *= 10.; } return 10; } /*! * Determine the step in a QDoubleSpinBox with specific decimals and diff * \param diff Difference between the largest value and smallest value * \param decimal * \return */ double AxisDock::determineStep(double diff, int decimal) { if (decimal == 0) { double ten = 1; for (unsigned int i = 1; i < 1000000000; i++) { if (diff/ten <= 10) { return ten/10; // use one decimal before } ten *= 10; } return 1; } return static_cast(1)/(pow(10,decimal)); } void AxisDock::loadConfigFromTemplate(KConfig& config) { //extract the name of the template from the file name QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index != -1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); int size = m_axesList.size(); if (size > 1) m_axis->beginMacro(i18n("%1 axes: template \"%2\" loaded", size, name)); else m_axis->beginMacro(i18n("%1: template \"%2\" loaded", m_axis->name(), name)); this->loadConfig(config); m_axis->endMacro(); } void AxisDock::loadConfig(KConfig& config) { KConfigGroup group = config.group( "Axis" ); bool numeric = false; const auto* plot = dynamic_cast(m_axis->parentAspect()); if (plot) { numeric = ( (m_axis->orientation() == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (m_axis->orientation() == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ); } //General ui.cbOrientation->setCurrentIndex( group.readEntry("Orientation", (int) m_axis->orientation()) ); int index = group.readEntry("Position", (int) m_axis->position()); if (index > 1) ui.cbPosition->setCurrentIndex(index-2); else ui.cbPosition->setCurrentIndex(index); ui.lePosition->setText( QString::number( group.readEntry("PositionOffset", m_axis->offset())) ); ui.cbScale->setCurrentIndex( group.readEntry("Scale", (int) m_axis->scale()) ); ui.chkAutoScale->setChecked(group.readEntry("AutoScale", m_axis->autoScale())); ui.leStart->setText( QString::number( group.readEntry("Start", m_axis->start())) ); ui.leEnd->setText( QString::number( group.readEntry("End", m_axis->end())) ); ui.leZeroOffset->setText( QString::number( group.readEntry("ZeroOffset", m_axis->zeroOffset())) ); ui.leScalingFactor->setText( QString::number( group.readEntry("ScalingFactor", m_axis->scalingFactor())) ); //Title KConfigGroup axisLabelGroup = config.group("AxisLabel"); labelWidget->loadConfig(axisLabelGroup); //Line ui.cbLineStyle->setCurrentIndex( group.readEntry("LineStyle", (int) m_axis->linePen().style()) ); ui.kcbLineColor->setColor( group.readEntry("LineColor", m_axis->linePen().color()) ); ui.sbLineWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("LineWidth", m_axis->linePen().widthF()),Worksheet::Point) ); ui.sbLineOpacity->setValue( round(group.readEntry("LineOpacity", m_axis->lineOpacity())*100.0) ); ui.cbArrowType->setCurrentIndex( group.readEntry("ArrowType", (int) m_axis->arrowType()) ); ui.cbArrowPosition->setCurrentIndex( group.readEntry("ArrowPosition", (int) m_axis->arrowPosition()) ); ui.sbArrowSize->setValue( Worksheet::convertFromSceneUnits(group.readEntry("ArrowSize", m_axis->arrowSize()), Worksheet::Point) ); //Major ticks ui.cbMajorTicksDirection->setCurrentIndex( group.readEntry("MajorTicksDirection", (int) m_axis->majorTicksDirection()) ); ui.cbMajorTicksType->setCurrentIndex( group.readEntry("MajorTicksType", (int) m_axis->majorTicksType()) ); ui.sbMajorTicksNumber->setValue( group.readEntry("MajorTicksNumber", m_axis->majorTicksNumber()) ); if (numeric) - ui.sbMajorTicksIncrementNumeric->setValue(group.readEntry("MajorTicksIncrement", m_axis->majorTicksIncrement())); + ui.sbMajorTicksSpacingNumeric->setValue(group.readEntry("MajorTicksIncrement", m_axis->majorTicksSpacing())); else - dtsbMajorTicksIncrement->setValue(group.readEntry("MajorTicksIncrement", m_axis->majorTicksIncrement())); + dtsbMajorTicksIncrement->setValue(group.readEntry("MajorTicksIncrement", m_axis->majorTicksSpacing())); ui.cbMajorTicksLineStyle->setCurrentIndex( group.readEntry("MajorTicksLineStyle", (int) m_axis->majorTicksPen().style()) ); ui.kcbMajorTicksColor->setColor( group.readEntry("MajorTicksColor", m_axis->majorTicksPen().color()) ); ui.sbMajorTicksWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MajorTicksWidth", m_axis->majorTicksPen().widthF()),Worksheet::Point) ); ui.sbMajorTicksLength->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MajorTicksLength", m_axis->majorTicksLength()),Worksheet::Point) ); ui.sbMajorTicksOpacity->setValue( round(group.readEntry("MajorTicksOpacity", m_axis->majorTicksOpacity())*100.0) ); //Minor ticks ui.cbMinorTicksDirection->setCurrentIndex( group.readEntry("MinorTicksDirection", (int) m_axis->minorTicksDirection()) ); ui.cbMinorTicksType->setCurrentIndex( group.readEntry("MinorTicksType", (int) m_axis->minorTicksType()) ); ui.sbMinorTicksNumber->setValue( group.readEntry("MinorTicksNumber", m_axis->minorTicksNumber()) ); if (numeric) - ui.sbMinorTicksIncrementNumeric->setValue(group.readEntry("MajorTicksIncrement", m_axis->majorTicksIncrement())); + ui.sbMinorTicksSpacingNumeric->setValue(group.readEntry("MinorTicksIncrement", m_axis->minorTicksSpacing())); else - dtsbMinorTicksIncrement->setValue(group.readEntry("MajorTicksIncrement", m_axis->majorTicksIncrement())); + dtsbMinorTicksIncrement->setValue(group.readEntry("MinorTicksIncrement", m_axis->minorTicksSpacing())); ui.cbMinorTicksLineStyle->setCurrentIndex( group.readEntry("MinorTicksLineStyle", (int) m_axis->minorTicksPen().style()) ); ui.kcbMinorTicksColor->setColor( group.readEntry("MinorTicksColor", m_axis->minorTicksPen().color()) ); ui.sbMinorTicksWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MinorTicksWidth", m_axis->minorTicksPen().widthF()),Worksheet::Point) ); ui.sbMinorTicksLength->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MinorTicksLength", m_axis->minorTicksLength()),Worksheet::Point) ); ui.sbMinorTicksOpacity->setValue( round(group.readEntry("MinorTicksOpacity", m_axis->minorTicksOpacity())*100.0) ); //Extra ticks //TODO // Tick label ui.cbLabelsFormat->setCurrentIndex( group.readEntry("LabelsFormat", (int) m_axis->labelsFormat()) ); ui.chkLabelsAutoPrecision->setChecked( group.readEntry("LabelsAutoPrecision", (int) m_axis->labelsAutoPrecision()) ); ui.sbLabelsPrecision->setValue( group.readEntry("LabelsPrecision", (int)m_axis->labelsPrecision()) ); ui.cbLabelsDateTimeFormat->setCurrentText( group.readEntry("LabelsDateTimeFormat", "yyyy-MM-dd hh:mm:ss") ); ui.cbLabelsPosition->setCurrentIndex( group.readEntry("LabelsPosition", (int) m_axis->labelsPosition()) ); ui.sbLabelsOffset->setValue( Worksheet::convertFromSceneUnits(group.readEntry("LabelsOffset", m_axis->labelsOffset()), Worksheet::Point) ); ui.sbLabelsRotation->setValue( group.readEntry("LabelsRotation", m_axis->labelsRotationAngle()) ); //we need to set the font size in points for KFontRequester QFont font = m_axis->labelsFont(); font.setPointSizeF( round(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); ui.kfrLabelsFont->setFont( group.readEntry("LabelsFont", font) ); ui.kcbLabelsFontColor->setColor( group.readEntry("LabelsFontColor", m_axis->labelsColor()) ); ui.leLabelsPrefix->setText( group.readEntry("LabelsPrefix", m_axis->labelsPrefix()) ); ui.leLabelsSuffix->setText( group.readEntry("LabelsSuffix", m_axis->labelsSuffix()) ); ui.sbLabelsOpacity->setValue( round(group.readEntry("LabelsOpacity", m_axis->labelsOpacity())*100.0) ); //Grid ui.cbMajorGridStyle->setCurrentIndex( group.readEntry("MajorGridStyle", (int) m_axis->majorGridPen().style()) ); ui.kcbMajorGridColor->setColor( group.readEntry("MajorGridColor", m_axis->majorGridPen().color()) ); ui.sbMajorGridWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MajorGridWidth", m_axis->majorGridPen().widthF()),Worksheet::Point) ); ui.sbMajorGridOpacity->setValue( round(group.readEntry("MajorGridOpacity", m_axis->majorGridOpacity())*100.0) ); ui.cbMinorGridStyle->setCurrentIndex( group.readEntry("MinorGridStyle", (int) m_axis->minorGridPen().style()) ); ui.kcbMinorGridColor->setColor( group.readEntry("MinorGridColor", m_axis->minorGridPen().color()) ); ui.sbMinorGridWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MinorGridWidth", m_axis->minorGridPen().widthF()),Worksheet::Point) ); ui.sbMinorGridOpacity->setValue( round(group.readEntry("MinorGridOpacity", m_axis->minorGridOpacity())*100.0) ); m_initializing = true; GuiTools::updatePenStyles(ui.cbLineStyle, ui.kcbLineColor->color()); this->majorTicksTypeChanged(ui.cbMajorTicksType->currentIndex()); GuiTools::updatePenStyles(ui.cbMajorTicksLineStyle, ui.kcbMajorTicksColor->color()); this->minorTicksTypeChanged(ui.cbMinorTicksType->currentIndex()); GuiTools::updatePenStyles(ui.cbMinorTicksLineStyle, ui.kcbMinorTicksColor->color()); GuiTools::updatePenStyles(ui.cbMajorGridStyle, ui.kcbMajorGridColor->color()); GuiTools::updatePenStyles(ui.cbMinorGridStyle, ui.kcbMinorGridColor->color()); m_initializing = false; } void AxisDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group( "Axis" ); bool numeric = false; const auto* plot = dynamic_cast(m_axis->parentAspect()); if (plot) { numeric = ( (m_axis->orientation() == Axis::AxisHorizontal && plot->xRangeFormat() == CartesianPlot::Numeric) || (m_axis->orientation() == Axis::AxisVertical && plot->yRangeFormat() == CartesianPlot::Numeric) ); } //General group.writeEntry("Orientation", ui.cbOrientation->currentIndex()); if (ui.cbPosition->currentIndex() == 2) { group.writeEntry("Position", (int)Axis::AxisCentered); } else if (ui.cbPosition->currentIndex() == 3) { group.writeEntry("Position", (int)Axis::AxisCustom); } else { if ( ui.cbOrientation->currentIndex() == Axis::AxisHorizontal ) group.writeEntry("Position", ui.cbPosition->currentIndex()); else group.writeEntry("Position", ui.cbPosition->currentIndex()+2); } group.writeEntry("PositionOffset", ui.lePosition->text()); group.writeEntry("Scale", ui.cbScale->currentIndex()); group.writeEntry("Start", ui.leStart->text()); group.writeEntry("End", ui.leEnd->text()); group.writeEntry("ZeroOffset", ui.leZeroOffset->text()); group.writeEntry("ScalingFactor", ui.leScalingFactor->text()); //Title KConfigGroup axisLabelGroup = config.group("AxisLabel"); labelWidget->saveConfig(axisLabelGroup); //Line group.writeEntry("LineStyle", ui.cbLineStyle->currentIndex()); group.writeEntry("LineColor", ui.kcbLineColor->color()); group.writeEntry("LineWidth", Worksheet::convertToSceneUnits(ui.sbLineWidth->value(), Worksheet::Point)); group.writeEntry("LineOpacity", ui.sbLineOpacity->value()/100.); //Major ticks group.writeEntry("MajorTicksDirection", ui.cbMajorTicksDirection->currentIndex()); group.writeEntry("MajorTicksType", ui.cbMajorTicksType->currentIndex()); group.writeEntry("MajorTicksNumber", ui.sbMajorTicksNumber->value()); if (numeric) - group.writeEntry("MajorTicksIncrement", QString::number(ui.sbMajorTicksIncrementNumeric->value())); + group.writeEntry("MajorTicksIncrement", QString::number(ui.sbMajorTicksSpacingNumeric->value())); else group.writeEntry("MajorTicksIncrement", QString::number(dtsbMajorTicksIncrement->value())); group.writeEntry("MajorTicksLineStyle", ui.cbMajorTicksLineStyle->currentIndex()); group.writeEntry("MajorTicksColor", ui.kcbMajorTicksColor->color()); group.writeEntry("MajorTicksWidth", Worksheet::convertToSceneUnits(ui.sbMajorTicksWidth->value(),Worksheet::Point)); group.writeEntry("MajorTicksLength", Worksheet::convertToSceneUnits(ui.sbMajorTicksLength->value(),Worksheet::Point)); group.writeEntry("MajorTicksOpacity", ui.sbMajorTicksOpacity->value()/100.); //Minor ticks group.writeEntry("MinorTicksDirection", ui.cbMinorTicksDirection->currentIndex()); group.writeEntry("MinorTicksType", ui.cbMinorTicksType->currentIndex()); group.writeEntry("MinorTicksNumber", ui.sbMinorTicksNumber->value()); if (numeric) - group.writeEntry("MinorTicksIncrement", QString::number(ui.sbMinorTicksIncrementNumeric->value())); + group.writeEntry("MinorTicksIncrement", QString::number(ui.sbMinorTicksSpacingNumeric->value())); else group.writeEntry("MinorTicksIncrement", QString::number(dtsbMinorTicksIncrement->value())); group.writeEntry("MinorTicksLineStyle", ui.cbMinorTicksLineStyle->currentIndex()); group.writeEntry("MinorTicksColor", ui.kcbMinorTicksColor->color()); group.writeEntry("MinorTicksWidth", Worksheet::convertFromSceneUnits(ui.sbMinorTicksWidth->value(),Worksheet::Point)); group.writeEntry("MinorTicksLength", Worksheet::convertFromSceneUnits(ui.sbMinorTicksLength->value(),Worksheet::Point)); group.writeEntry("MinorTicksOpacity", ui.sbMinorTicksOpacity->value()/100.); //Extra ticks // TODO // Tick label group.writeEntry("LabelsFormat", ui.cbLabelsFormat->currentIndex()); group.writeEntry("LabelsAutoPrecision", ui.chkLabelsAutoPrecision->isChecked()); group.writeEntry("LabelsPrecision", ui.sbLabelsPrecision->value()); group.writeEntry("LabelsPosition", ui.cbLabelsPosition->currentIndex()); group.writeEntry("LabelsOffset", Worksheet::convertToSceneUnits(ui.sbLabelsOffset->value(), Worksheet::Point)); group.writeEntry("LabelsRotation", ui.sbLabelsRotation->value()); group.writeEntry("LabelsFont", ui.kfrLabelsFont->font()); group.writeEntry("LabelsFontColor", ui.kcbLabelsFontColor->color()); group.writeEntry("LabelsPrefix", ui.leLabelsPrefix->text()); group.writeEntry("LabelsSuffix", ui.leLabelsSuffix->text()); group.writeEntry("LabelsOpacity", ui.sbLabelsOpacity->value()/100.); //Grid group.writeEntry("MajorGridStyle", ui.cbMajorGridStyle->currentIndex()); group.writeEntry("MajorGridColor", ui.kcbMajorGridColor->color()); group.writeEntry("MajorGridWidth", Worksheet::convertToSceneUnits(ui.sbMajorGridWidth->value(), Worksheet::Point)); group.writeEntry("MajorGridOpacity", ui.sbMajorGridOpacity->value()/100.); group.writeEntry("MinorGridStyle", ui.cbMinorGridStyle->currentIndex()); group.writeEntry("MinorGridColor", ui.kcbMinorGridColor->color()); group.writeEntry("MinorGridWidth", Worksheet::convertToSceneUnits(ui.sbMinorGridWidth->value(), Worksheet::Point)); group.writeEntry("MinorGridOpacity", ui.sbMinorGridOpacity->value()/100.); config.sync(); } diff --git a/src/kdefrontend/dockwidgets/AxisDock.h b/src/kdefrontend/dockwidgets/AxisDock.h index 4e27cfdd8..07893544a 100644 --- a/src/kdefrontend/dockwidgets/AxisDock.h +++ b/src/kdefrontend/dockwidgets/AxisDock.h @@ -1,221 +1,221 @@ /*************************************************************************** File : AxisDock.h Project : LabPlot Description : axes widget class -------------------------------------------------------------------- Copyright : (C) 2011-2018 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2013 by Stefan Gerlach (stefan.gerlach@uni-konstanz.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef AXISDOCK_H #define AXISDOCK_H #include "ui_axisdock.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "kdefrontend/dockwidgets/BaseDock.h" #include class AbstractAspect; class LabelWidget; class TreeViewComboBox; class AspectTreeModel; class AbstractColumn; class DateTimeSpinBox; class AxisDock : public BaseDock { Q_OBJECT public: explicit AxisDock(QWidget*); ~AxisDock() override; void setAxes(QList); void activateTitleTab(); private: Ui::AxisDock ui; QList m_axesList; Axis* m_axis{nullptr}; AspectTreeModel* m_aspectTreeModel{nullptr}; LabelWidget* labelWidget; TreeViewComboBox* cbMajorTicksColumn; TreeViewComboBox* cbMinorTicksColumn; bool m_dataChanged{false}; void setModel(); void setModelIndexFromColumn(TreeViewComboBox*, const AbstractColumn*); void load(); void loadConfig(KConfig&); // own created widgets DateTimeSpinBox* dtsbMajorTicksIncrement {nullptr}; DateTimeSpinBox* dtsbMinorTicksIncrement {nullptr}; int determineDecimals(double diff); double determineStep(double diff, int decimal); private slots: void init(); //SLOTs for changes triggered in AxisDock //"General"-tab void visibilityChanged(bool); void orientationChanged(int); void positionChanged(int); void positionChanged(); void scaleChanged(int); void autoScaleChanged(int); void startChanged(); void endChanged(); void startDateTimeChanged(const QDateTime&); void endDateTimeChanged(const QDateTime&); void zeroOffsetChanged(); void scalingFactorChanged(); //Line-Tab void lineStyleChanged(int); void lineColorChanged(const QColor&); void lineWidthChanged(double); void lineOpacityChanged(int); void arrowPositionChanged(int); void arrowTypeChanged(int); void arrowSizeChanged(int); //"Major ticks"-tab void majorTicksDirectionChanged(int); void majorTicksTypeChanged(int); void majorTicksNumberChanged(int); - void majorTicksIncrementChanged(); + void majorTicksSpacingChanged(); void majorTicksColumnChanged(const QModelIndex&); void majorTicksLineStyleChanged(int); void majorTicksColorChanged(const QColor&); void majorTicksWidthChanged(double); void majorTicksLengthChanged(double); void majorTicksOpacityChanged(int); //"Minor ticks"-tab void minorTicksDirectionChanged(int); void minorTicksTypeChanged(int); void minorTicksNumberChanged(int); - void minorTicksIncrementChanged(); + void minorTicksSpacingChanged(); void minorTicksColumnChanged(const QModelIndex&); void minorTicksLineStyleChanged(int); void minorTicksColorChanged(const QColor&); void minorTicksWidthChanged(double); void minorTicksLengthChanged(double); void minorTicksOpacityChanged(int); //"Extra ticks"-tab //"Tick labels"-tab void labelsFormatChanged(int); void labelsPrecisionChanged(int); void labelsAutoPrecisionChanged(int); void labelsDateTimeFormatChanged(int); void labelsPositionChanged(int); void labelsOffsetChanged(double); void labelsRotationChanged(int); void labelsFontChanged(const QFont&); void labelsFontColorChanged(const QColor&); void labelsPrefixChanged(); void labelsSuffixChanged(); void labelsOpacityChanged(int); //"Grid"-tab void majorGridStyleChanged(int); void majorGridColorChanged(const QColor&); void majorGridWidthChanged(double); void majorGridOpacityChanged(int); void minorGridStyleChanged(int); void minorGridColorChanged(const QColor&); void minorGridWidthChanged(double); void minorGridOpacityChanged(int); //SLOTs for changes triggered in Axis //General-Tab void axisDescriptionChanged(const AbstractAspect*); void axisOrientationChanged(Axis::AxisOrientation); void axisPositionChanged(Axis::AxisPosition); void axisPositionChanged(float); void axisScaleChanged(Axis::AxisScale); void axisAutoScaleChanged(bool); void axisStartChanged(double); void axisEndChanged(double); void axisZeroOffsetChanged(qreal); void axisScalingFactorChanged(qreal); //line void axisLinePenChanged(const QPen&); void axisLineOpacityChanged(qreal); void axisArrowTypeChanged(Axis::ArrowType); void axisArrowPositionChanged(Axis::ArrowPosition); void axisArrowSizeChanged(qreal); //ticks void axisMajorTicksDirectionChanged(Axis::TicksDirection); void axisMajorTicksTypeChanged(Axis::TicksType); void axisMajorTicksNumberChanged(int); - void axisMajorTicksIncrementChanged(qreal); + void axisMajorTicksSpacingChanged(qreal); void axisMajorTicksPenChanged(const QPen&); void axisMajorTicksLengthChanged(qreal); void axisMajorTicksOpacityChanged(qreal); void axisMinorTicksDirectionChanged(Axis::TicksDirection); void axisMinorTicksTypeChanged(Axis::TicksType); void axisMinorTicksNumberChanged(int); - void axisMinorTicksIncrementChanged(qreal); + void axisMinorTicksSpacingChanged(qreal); void axisMinorTicksPenChanged(const QPen&); void axisMinorTicksLengthChanged(qreal); void axisMinorTicksOpacityChanged(qreal); //labels void axisLabelsFormatChanged(Axis::LabelsFormat); void axisLabelsAutoPrecisionChanged(bool); void axisLabelsPrecisionChanged(int); void axisLabelsDateTimeFormatChanged(const QString&); void axisLabelsPositionChanged(Axis::LabelsPosition); void axisLabelsOffsetChanged(double); void axisLabelsRotationAngleChanged(qreal); void axisLabelsFontChanged(const QFont&); void axisLabelsFontColorChanged(const QColor&); void axisLabelsPrefixChanged(const QString&); void axisLabelsSuffixChanged(const QString&); void axisLabelsOpacityChanged(qreal); //grids void axisMajorGridPenChanged(const QPen&); void axisMajorGridOpacityChanged(qreal); void axisMinorGridPenChanged(const QPen&); void axisMinorGridOpacityChanged(qreal); void axisVisibilityChanged(bool); //save/load template void loadConfigFromTemplate(KConfig&); void saveConfigAsTemplate(KConfig&); signals: void info(const QString&); }; #endif diff --git a/src/kdefrontend/ui/dockwidgets/axisdock.ui b/src/kdefrontend/ui/dockwidgets/axisdock.ui index 5f3d9ec8b..1f809d18c 100644 --- a/src/kdefrontend/ui/dockwidgets/axisdock.ui +++ b/src/kdefrontend/ui/dockwidgets/axisdock.ui @@ -1,1522 +1,1522 @@ AxisDock 0 0 541 1406 16777215 16777215 true 16777215 16777215 3 General Name: Qt::Horizontal QSizePolicy::Fixed 10 20 0 0 Comment: 0 0 Qt::Vertical QSizePolicy::Fixed 20 18 Orientation: Position: true Position of the axis in the direction perpendicular to the axis in logical units. Scale: Qt::Vertical QSizePolicy::Fixed 20 18 Auto fit: Start: End: Start: true End: true Zero-offset: Scaling factor: Qt::Vertical 20 75 Visible Title Line 75 true Line Style: Qt::Horizontal QSizePolicy::Fixed 10 23 0 0 Color: 0 0 Width: pt 2 100.000000000000000 0.500000000000000 Opacity: 0 0 The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. % 0 100 10 100 Qt::Vertical QSizePolicy::Fixed 20 18 75 true Arrow Type: 0 0 Position: 0 0 Size: 0 0 The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. pt 0 100 10 50 Qt::Vertical 18 368 Ticks 75 true Minor ticks - + - Increment: + Spacing: Direction: Increment: Column: 0 0 The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. % 0 100 10 100 - - - -2147483647.000000000000000 + + + spacing between major ticks 999999999999999.000000000000000 pt 0.500000000000000 Length: Qt::Vertical 48 301 Width: Column: Number: Type: Opacity: - + - Increment: + Spacing: Color: Style: 0 0 Opacity: Number: 0 0 75 true Major ticks Direction: pt 0.500000000000000 Color: Qt::Vertical QSizePolicy::Fixed 20 18 Type: Style: pt 0.500000000000000 Qt::Vertical QSizePolicy::Fixed 20 18 Qt::Horizontal QSizePolicy::Fixed 17 24 Qt::Vertical QSizePolicy::Fixed 20 18 0 0 pt 0.500000000000000 0 0 The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. % 0 100 10 100 0 0 Length: Width: Increment: - + -2147483647.000000000000000 999999999999999.000000000000000 Labels 75 true Position Position: Qt::Horizontal QSizePolicy::Fixed 17 20 Offset: Rotation: Qt::Vertical QSizePolicy::Fixed 58 18 75 true Format Format: Number of digits after the decimal point Precision: 0 0 Number of digits after the decimal point Automatically determine the optimal number of digits after the decimal point Auto DateTime: Qt::Vertical QSizePolicy::Fixed 20 18 Font: Color: Prefix: Suffix: Opacity: Qt::Vertical 20 83 0 0 ° -180 180 10 0 0 0 pt -99.989999999999995 0.500000000000000 true 0 0 0 0 0 0 The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. % 0 100 10 100 true Grid Opacity: 75 true Major grid Style: Qt::Horizontal QSizePolicy::Fixed 13 23 0 0 Color: Width: pt 0.500000000000000 0 0 The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. % 0 100 10 100 Qt::Vertical QSizePolicy::Fixed 20 18 75 true Minor grid Color: Width: pt 0.500000000000000 Opacity: 0 0 The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. % 0 100 10 100 Qt::Vertical 20 40 Style: KColorButton QPushButton
kcolorbutton.h
KFontRequester QWidget
kfontrequester.h
KComboBox QComboBox
kcombobox.h