File indexing completed on 2024-05-05 17:19:07
0001 /*************************************************************************** 0002 * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr 0003 * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 ***************************************************************************/ 0006 /** @file 0007 * A table with graph with more features. 0008 * 0009 * @author Stephane MANKOWSKI / Guillaume DE BURE 0010 */ 0011 #include "skgtablewithgraph.h" 0012 0013 #include <kcolorscheme.h> 0014 #include <kstringhandler.h> 0015 0016 #include <qcollator.h> 0017 #include <qdesktopservices.h> 0018 #include <qdom.h> 0019 #include <qfileinfo.h> 0020 #include <qgraphicseffect.h> 0021 #include <qgraphicsitem.h> 0022 #include <qheaderview.h> 0023 #include <qmath.h> 0024 #include <qmenu.h> 0025 #include <qpainter.h> 0026 #include <qprinter.h> 0027 #include <qsavefile.h> 0028 #include <qscriptengine.h> 0029 #include <qscrollbar.h> 0030 #include <qtablewidget.h> 0031 #include <qtextcodec.h> 0032 #include <qtimer.h> 0033 #include <qwidgetaction.h> 0034 0035 #include <algorithm> 0036 0037 #include "skgcolorbutton.h" 0038 #include "skgcombobox.h" 0039 #include "skggraphicsscene.h" 0040 #include "skgmainpanel.h" 0041 #include "skgservices.h" 0042 #include "skgtraces.h" 0043 #include "skgtreemap.h" 0044 0045 /** 0046 * Data identifier for value 0047 */ 0048 static const int DATA_VALUE = 12; 0049 /** 0050 * Data identifier for color 0051 */ 0052 static const int DATA_COLOR_H = 11; 0053 /** 0054 * Data identifier for color 0055 */ 0056 static const int DATA_COLOR_S = 12; 0057 /** 0058 * Data identifier for color 0059 */ 0060 static const int DATA_COLOR_V = 13; 0061 /** 0062 * Data identifier for Z value 0063 */ 0064 static const int DATA_Z_VALUE = 14; 0065 /** 0066 * Data identifier for mode 0067 */ 0068 static const int DATA_MODE = 15; 0069 /** 0070 * Alpha value 0071 */ 0072 static const int ALPHA = 200; 0073 /** 0074 * Box size 0075 */ 0076 static const double BOX_SIZE = 120.0; 0077 /** 0078 * Box margin 0079 */ 0080 static const double BOX_MARGIN = 10.0; 0081 0082 #define cloneAction(MENU, ACTION, SLOTTOCALL) \ 0083 auto act = (MENU)->addAction((ACTION)->icon(), (ACTION)->text()); \ 0084 act->setCheckable(true); \ 0085 act->setChecked((ACTION)->isChecked()); \ 0086 connect(act, &QAction::triggered, this, SLOTTOCALL); \ 0087 connect(act, &QAction::toggled, ACTION, &QAction::setChecked); \ 0088 connect(ACTION, &QAction::toggled, act, &QAction::setChecked); 0089 0090 SKGTableWithGraph::SKGTableWithGraph() : SKGTableWithGraph(nullptr) 0091 {} 0092 0093 SKGTableWithGraph::SKGTableWithGraph(QWidget* iParent) 0094 : QWidget(iParent), m_scene(nullptr), m_additionalInformation(NONE), m_nbVirtualColumns(0), 0095 m_selectable(true), m_toolBarVisible(true), m_graphTypeVisible(true), m_limitVisible(true), m_averageVisible(true), 0096 m_linearRegressionVisible(true), m_paretoVisible(false), m_legendVisible(false), m_graphVisible(true), m_tableVisible(true), m_textVisible(false), m_zeroVisible(true), m_decimalsVisible(true), m_shadow(true), 0097 m_mainMenu(nullptr), 0098 m_indexSum(-1), m_indexAverage(-1), m_indexMin(-1), m_indexLinearRegression(-1), m_sortOrder(Qt::AscendingOrder), m_sortColumn(0) 0099 { 0100 m_axisColor = Qt::gray; 0101 m_gridColor = Qt::lightGray; 0102 m_minColor = Qt::red; 0103 m_maxColor = Qt::green; 0104 m_paretoColor = Qt::darkRed; 0105 m_averageColor = Qt::blue; 0106 m_tendencyColor = Qt::darkYellow; 0107 m_backgroundColor = Qt::white; 0108 m_textColor = Qt::black; 0109 m_NegativeColor = KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText); 0110 m_WhiteColor = QBrush(Qt::white); 0111 0112 ui.setupUi(this); 0113 ui.kTextEdit->hide(); 0114 0115 ui.kFilterEdit->setPlaceholderText(i18n("Search")); 0116 0117 m_displayMode = new SKGComboBox(this); 0118 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar-stacked")), i18nc("Noun, a type of graph, with bars stacked upon each other", "Stack of lines"), static_cast<int>(STACK)); 0119 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar-stacked")), i18nc("Noun, a type of graph, with bars stacked upon each other", "Stack of columns"), static_cast<int>(STACKCOLUMNS)); 0120 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar")), i18nc("Noun, a type of graph, with bars placed besides each other", "Histogram"), static_cast<int>(HISTOGRAM)); 0121 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-scatter")), i18nc("Noun, a type of graph with only points", "Point"), static_cast<int>(POINT)); 0122 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-line")), i18nc("Noun, a type of graph with only lines", "Line"), static_cast<int>(LINE)); 0123 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-area-stacked")), i18nc("Noun, a type of graph, with lines stacked upon each other", "Stacked area"), static_cast<int>(STACKAREA)); 0124 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("skg-chart-bubble")), i18nc("Noun, a type of graph, with bubbles", "Bubble"), static_cast<int>(BUBBLE)); 0125 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-pie")), i18nc("Noun, a type of graph that looks like a sliced pie", "Pie"), static_cast<int>(PIE)); 0126 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-ring")), i18nc("Noun, a type of graph that looks like concentric slices of a pie (a la filelight)", "Concentric pie"), static_cast<int>(CONCENTRICPIE)); 0127 m_displayMode->addItem(SKGServices::fromTheme(QStringLiteral("map-flat")), i18nc("Noun, a type of graph that looks treemap", "Treemap"), static_cast<int>(TREEMAP)); 0128 0129 ui.graphicView->addToolbarWidget(m_displayMode); 0130 0131 ui.kShow->addItem(QStringLiteral("table"), i18n("Table"), QStringLiteral("view-list-details"), QString(), QString(), QStringLiteral("text"), QStringLiteral("graph"), QString(), Qt::META + Qt::Key_T); 0132 ui.kShow->addItem(QStringLiteral("graph"), i18n("Graph"), QStringLiteral("office-chart-pie"), QString(), QString(), QStringLiteral("text"), QStringLiteral("table"), QString(), Qt::META + Qt::Key_G); 0133 ui.kShow->addItem(QStringLiteral("text"), i18n("Text"), QStringLiteral("view-list-text"), QString(), QString(), QStringLiteral("table;graph"), QStringLiteral("table;graph"), QString(), Qt::META + Qt::Key_R); 0134 0135 ui.kShow->setDefaultState(QStringLiteral("table;graph")); 0136 0137 connect(ui.kShow, &SKGShow::stateChanged, this, &SKGTableWithGraph::onDisplayModeChanged, Qt::QueuedConnection); 0138 0139 m_timer.setSingleShot(true); 0140 connect(&m_timer, &QTimer::timeout, this, &SKGTableWithGraph::refresh, Qt::QueuedConnection); 0141 0142 m_timerRedraw.setSingleShot(true); 0143 connect(&m_timerRedraw, &QTimer::timeout, this, &SKGTableWithGraph::redrawGraph, Qt::QueuedConnection); 0144 0145 // Build contextual menu 0146 ui.kTable->setContextMenuPolicy(Qt::CustomContextMenu); 0147 connect(ui.kTable, &SKGTableWidget::customContextMenuRequested, this, &SKGTableWithGraph::showMenu); 0148 0149 m_mainMenu = new QMenu(ui.kTable); 0150 0151 QAction* actExport = m_mainMenu->addAction(SKGServices::fromTheme(QStringLiteral("document-export")), i18nc("Noun, user action", "Export…")); 0152 connect(actExport, &QAction::triggered, this, &SKGTableWithGraph::onExport); 0153 0154 // Add graph mode in menu 0155 getGraphContextualMenu()->addSeparator(); 0156 auto displayModeMenu = new SKGComboBox(this); 0157 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar-stacked")), i18nc("Noun, a type of graph, with bars stacked upon each other", "Stack of lines"), static_cast<int>(STACK)); 0158 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar-stacked")), i18nc("Noun, a type of graph, with bars stacked upon each other", "Stack of columns"), static_cast<int>(STACKCOLUMNS)); 0159 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-bar")), i18nc("Noun, a type of graph, with bars placed besides each other", "Histogram"), static_cast<int>(HISTOGRAM)); 0160 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-scatter")), i18nc("Noun, a type of graph with only points", "Point"), static_cast<int>(POINT)); 0161 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-line")), i18nc("Noun, a type of graph with only lines", "Line"), static_cast<int>(LINE)); 0162 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-area-stacked")), i18nc("Noun, a type of graph, with lines stacked upon each other", "Stacked area"), static_cast<int>(STACKAREA)); 0163 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("skg-chart-bubble")), i18nc("Noun, a type of graph, with bubbles", "Bubble"), static_cast<int>(BUBBLE)); 0164 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-pie")), i18nc("Noun, a type of graph that looks like a sliced pie", "Pie"), static_cast<int>(PIE)); 0165 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("office-chart-ring")), i18nc("Noun, a type of graph that looks like concentric slices of a pie (a la filelight)", "Concentric pie"), static_cast<int>(CONCENTRICPIE)); 0166 displayModeMenu->addItem(SKGServices::fromTheme(QStringLiteral("map-flat")), i18nc("Noun, a type of graph that looks treemap", "Treemap"), static_cast<int>(TREEMAP)); 0167 0168 m_displayModeWidget = new QWidgetAction(this); 0169 m_displayModeWidget->setDefaultWidget(displayModeMenu); 0170 getGraphContextualMenu()->addAction(m_displayModeWidget); 0171 0172 connect(displayModeMenu, static_cast<void (SKGComboBox::*)(int)>(&SKGComboBox::currentIndexChanged), m_displayMode, &SKGComboBox::setCurrentIndex); 0173 connect(m_displayMode, static_cast<void (SKGComboBox::*)(int)>(&SKGComboBox::currentIndexChanged), displayModeMenu, &SKGComboBox::setCurrentIndex); 0174 0175 // Reset Colors 0176 QAction* resetColorsAction = m_mainMenu->addAction(SKGServices::fromTheme(QStringLiteral("format-fill-color")), i18nc("Noun, user action", "Reset default colors")); 0177 connect(resetColorsAction, &QAction::triggered, this, &SKGTableWithGraph::resetColors); 0178 getGraphContextualMenu()->addAction(resetColorsAction); 0179 0180 // Add item in menu of the graph 0181 m_allPositiveMenu = getGraphContextualMenu()->addAction(SKGServices::fromTheme(QStringLiteral("go-up-search")), i18nc("Noun, user action", "All values in positive")); 0182 if (m_allPositiveMenu != nullptr) { 0183 m_allPositiveMenu->setCheckable(true); 0184 } 0185 connect(m_allPositiveMenu, &QAction::toggled, this, &SKGTableWithGraph::redrawGraphDelayed, Qt::QueuedConnection); 0186 0187 // Add item in menu of the graph 0188 m_actShowLimits = getGraphContextualMenu()->addAction(i18nc("Noun, user action", "Show limits")); 0189 if (m_actShowLimits != nullptr) { 0190 m_actShowLimits->setCheckable(true); 0191 m_actShowLimits->setChecked(m_limitVisible); 0192 connect(m_actShowLimits, &QAction::triggered, this, &SKGTableWithGraph::switchLimitsVisibility); 0193 0194 // Clone action on table contextual menu 0195 cloneAction(m_mainMenu, m_actShowLimits, &SKGTableWithGraph::switchLimitsVisibility) 0196 } 0197 0198 // Add item in menu of the graph 0199 m_actShowAverage = getGraphContextualMenu()->addAction(i18nc("Noun, user action", "Show average")); 0200 if (m_actShowAverage != nullptr) { 0201 m_actShowAverage->setCheckable(true); 0202 m_actShowAverage->setChecked(m_averageVisible); 0203 connect(m_actShowAverage, &QAction::triggered, this, &SKGTableWithGraph::switchAverageVisibility); 0204 0205 // Clone action on table contextual menu 0206 cloneAction(m_mainMenu, m_actShowAverage, &SKGTableWithGraph::switchAverageVisibility) 0207 } 0208 0209 // Add item in menu of the graph 0210 m_actShowLinearRegression = getGraphContextualMenu()->addAction(i18nc("Noun, user action", "Show tendency line")); 0211 if (m_actShowLinearRegression != nullptr) { 0212 m_actShowLinearRegression->setCheckable(true); 0213 m_actShowLinearRegression->setChecked(m_linearRegressionVisible); 0214 connect(m_actShowLinearRegression, &QAction::triggered, this, &SKGTableWithGraph::switchLinearRegressionVisibility); 0215 0216 // Clone action on table contextual menu 0217 cloneAction(m_mainMenu, m_actShowLinearRegression, &SKGTableWithGraph::switchLinearRegressionVisibility) 0218 } 0219 0220 // Add item in menu of the graph 0221 m_actShowPareto = getGraphContextualMenu()->addAction(i18nc("Noun, user action", "Show Pareto curve")); 0222 if (m_actShowPareto != nullptr) { 0223 m_actShowPareto->setCheckable(true); 0224 m_actShowPareto->setChecked(m_paretoVisible); 0225 connect(m_actShowPareto, &QAction::triggered, this, &SKGTableWithGraph::switchParetoVisibility); 0226 } 0227 0228 // Add item in menu of the graph 0229 m_actShowLegend = getGraphContextualMenu()->addAction(SKGServices::fromTheme(QStringLiteral("help-contents")), i18nc("Noun, user action", "Show legend")); 0230 if (m_actShowLegend != nullptr) { 0231 m_actShowLegend->setCheckable(true); 0232 m_actShowLegend->setChecked(m_legendVisible); 0233 connect(m_actShowLegend, &QAction::triggered, this, &SKGTableWithGraph::switchLegendVisibility); 0234 } 0235 0236 // Add item in menu of the graph 0237 m_actShowZero = getGraphContextualMenu()->addAction(SKGServices::fromTheme(QStringLiteral("labplot-xy-plot-two-axes-centered-origin")), i18nc("Noun, user action", "Show origin")); 0238 if (m_actShowZero != nullptr) { 0239 m_actShowZero->setCheckable(true); 0240 m_actShowZero->setChecked(m_zeroVisible); 0241 connect(m_actShowZero, &QAction::triggered, this, &SKGTableWithGraph::swithOriginVisibility); 0242 } 0243 0244 // Add item in menu of the graph 0245 m_actShowDecimal = getGraphContextualMenu()->addAction(SKGServices::fromTheme(QStringLiteral("format-precision-less")), i18nc("Noun, user action", "Show decimals")); 0246 if (m_actShowDecimal != nullptr) { 0247 m_actShowDecimal->setCheckable(true); 0248 m_actShowDecimal->setChecked(m_decimalsVisible); 0249 connect(m_actShowDecimal, &QAction::triggered, this, &SKGTableWithGraph::swithDecimalsVisibility); 0250 0251 // Clone action on table contextual menu 0252 cloneAction(m_mainMenu, m_actShowDecimal, &SKGTableWithGraph::swithDecimalsVisibility) 0253 } 0254 0255 // Set headers parameters 0256 QHeaderView* verticalHeader = ui.kTable->verticalHeader(); 0257 if (verticalHeader != nullptr) { 0258 verticalHeader->hide(); 0259 verticalHeader->setDefaultSectionSize(verticalHeader->minimumSectionSize()); 0260 // verticalHeader->setSectionResizeMode(QHeaderView::ResizeToContents); 0261 } 0262 0263 ui.kTable->setSortingEnabled(false); // sort must be enable for refresh method 0264 QHeaderView* horizontalHeader = ui.kTable->horizontalHeader(); 0265 if (horizontalHeader != nullptr) { 0266 horizontalHeader->setSectionResizeMode(QHeaderView::ResizeToContents); 0267 horizontalHeader->show(); 0268 horizontalHeader->setSortIndicatorShown(true); 0269 horizontalHeader->setSortIndicator(m_sortColumn, m_sortOrder); 0270 connect(horizontalHeader, &QHeaderView::sortIndicatorChanged, this, &SKGTableWithGraph::refresh); 0271 } 0272 0273 connect(ui.kTable->horizontalScrollBar(), &QScrollBar::valueChanged, this, &SKGTableWithGraph::onHorizontalScrollBarChanged); 0274 connect(m_displayMode, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGTableWithGraph::redrawGraphDelayed, Qt::QueuedConnection); 0275 connect(ui.graphicView, &SKGGraphicsView::resized, this, &SKGTableWithGraph::redrawGraphDelayed, Qt::QueuedConnection); 0276 0277 connect(ui.kTable, &SKGTableWidget::cellDoubleClicked, this, &SKGTableWithGraph::onDoubleClick); 0278 connect(ui.kTable, &SKGTableWidget::itemSelectionChanged, this, &SKGTableWithGraph::onSelectionChanged); 0279 connect(ui.kFilterEdit, &QLineEdit::textChanged, this, &SKGTableWithGraph::onFilterModified); 0280 0281 #ifdef SKG_WEBENGINE 0282 connect(ui.kTextEdit, &SKGWebView::linkClicked, this, &SKGTableWithGraph::onLinkClicked); 0283 #endif 0284 #ifdef SKG_WEBKIT 0285 ui.kTextEdit->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); 0286 connect(ui.kTextEdit, &SKGWebView::linkClicked, this, &SKGTableWithGraph::onLinkClicked); 0287 #endif 0288 } 0289 0290 0291 SKGTableWithGraph::~SKGTableWithGraph() 0292 { 0293 delete m_scene; 0294 m_scene = nullptr; 0295 0296 m_mainMenu = nullptr; 0297 m_actShowLimits = nullptr; 0298 m_actShowLinearRegression = nullptr; 0299 m_actShowPareto = nullptr; 0300 m_displayModeWidget = nullptr; 0301 m_displayMode = nullptr; 0302 } 0303 0304 void SKGTableWithGraph::onHorizontalScrollBarChanged(int iValue) 0305 { 0306 QHeaderView* verticalHeader = ui.kTable->verticalHeader(); 0307 if (verticalHeader != nullptr) { 0308 verticalHeader->setVisible(iValue > 0); 0309 } 0310 } 0311 0312 bool SKGTableWithGraph::isGraphVisible() const 0313 { 0314 return m_graphVisible; 0315 } 0316 0317 bool SKGTableWithGraph::isTableVisible() const 0318 { 0319 return m_tableVisible; 0320 } 0321 0322 bool SKGTableWithGraph::isTextReportVisible() const 0323 { 0324 return m_textVisible; 0325 } 0326 0327 SKGShow* SKGTableWithGraph::getShowWidget() const 0328 { 0329 return ui.kShow; 0330 } 0331 void SKGTableWithGraph::setAverageColor(const QColor& iColor) 0332 { 0333 m_averageColor = iColor; 0334 } 0335 0336 void SKGTableWithGraph::setAxisColor(const QColor& iColor) 0337 { 0338 m_axisColor = iColor; 0339 } 0340 0341 void SKGTableWithGraph::setGridColor(const QColor& iColor) 0342 { 0343 m_gridColor = iColor; 0344 } 0345 0346 void SKGTableWithGraph::setMaxColor(const QColor& iColor) 0347 { 0348 m_maxColor = iColor; 0349 } 0350 0351 void SKGTableWithGraph::setParetoColor(const QColor& iColor) 0352 { 0353 m_paretoColor = iColor; 0354 } 0355 0356 void SKGTableWithGraph::setMinColor(const QColor& iColor) 0357 { 0358 m_minColor = iColor; 0359 } 0360 0361 void SKGTableWithGraph::setTendencyColor(const QColor& iColor) 0362 { 0363 m_tendencyColor = iColor; 0364 } 0365 0366 void SKGTableWithGraph::setOutlineColor(const QColor& iColor) 0367 { 0368 m_outlineColor = iColor; 0369 } 0370 0371 void SKGTableWithGraph::setBackgroundColor(const QColor& iColor) 0372 { 0373 m_backgroundColor = iColor; 0374 } 0375 0376 void SKGTableWithGraph::setTextColor(const QColor& iColor) 0377 { 0378 m_textColor = iColor; 0379 } 0380 0381 void SKGTableWithGraph::setAntialiasing(bool iAntialiasing) 0382 { 0383 ui.graphicView->setAntialiasing(iAntialiasing); 0384 } 0385 0386 void SKGTableWithGraph::setGraphTypeSelectorVisible(bool iVisible) 0387 { 0388 if (m_graphTypeVisible != iVisible) { 0389 m_graphTypeVisible = iVisible; 0390 if (m_displayMode != nullptr) { 0391 m_displayMode->setVisible(m_graphTypeVisible); 0392 } 0393 if (m_displayModeWidget != nullptr) { 0394 m_displayModeWidget->setVisible(m_graphTypeVisible); 0395 } 0396 Q_EMIT modified(); 0397 } 0398 } 0399 0400 bool SKGTableWithGraph::isGraphTypeSelectorVisible() const 0401 { 0402 return m_graphTypeVisible; 0403 } 0404 0405 bool SKGTableWithGraph::switchLimitsVisibility() 0406 { 0407 m_limitVisible = !m_limitVisible; 0408 refresh(); 0409 return m_limitVisible; 0410 } 0411 0412 bool SKGTableWithGraph::switchAverageVisibility() 0413 { 0414 m_averageVisible = !m_averageVisible; 0415 refresh(); 0416 return m_averageVisible; 0417 } 0418 0419 bool SKGTableWithGraph::switchLegendVisibility() 0420 { 0421 m_legendVisible = !m_legendVisible; 0422 redrawGraphDelayed(); 0423 return m_legendVisible; 0424 } 0425 0426 bool SKGTableWithGraph::swithOriginVisibility() 0427 { 0428 m_zeroVisible = !m_zeroVisible; 0429 redrawGraphDelayed(); 0430 return m_zeroVisible; 0431 } 0432 0433 bool SKGTableWithGraph::swithDecimalsVisibility() 0434 { 0435 m_decimalsVisible = !m_decimalsVisible; 0436 refresh(); 0437 return m_decimalsVisible; 0438 } 0439 0440 bool SKGTableWithGraph::switchLinearRegressionVisibility() 0441 { 0442 m_linearRegressionVisible = !m_linearRegressionVisible; 0443 refresh(); 0444 return m_linearRegressionVisible; 0445 } 0446 0447 bool SKGTableWithGraph::switchParetoVisibility() 0448 { 0449 m_paretoVisible = !m_paretoVisible; 0450 redrawGraphDelayed(); 0451 return m_paretoVisible; 0452 } 0453 0454 QMenu* SKGTableWithGraph::getTableContextualMenu() const 0455 { 0456 return m_mainMenu; 0457 } 0458 0459 QMenu* SKGTableWithGraph::getGraphContextualMenu() const 0460 { 0461 return ui.graphicView->getContextualMenu(); 0462 } 0463 0464 void SKGTableWithGraph::showMenu(const QPoint iPos) 0465 { 0466 if (m_mainMenu != nullptr) { 0467 m_mainMenu->popup(ui.kTable->mapToGlobal(iPos)); 0468 } 0469 } 0470 0471 QTableWidget* SKGTableWithGraph::table() const 0472 { 0473 return ui.kTable; 0474 } 0475 0476 SKGGraphicsView* SKGTableWithGraph::graph() const 0477 { 0478 return ui.graphicView; 0479 } 0480 0481 SKGWebView* SKGTableWithGraph::textReport() const 0482 { 0483 return ui.kTextEdit; 0484 } 0485 0486 SKGStringListList SKGTableWithGraph::getTable() 0487 { 0488 // Build table 0489 SKGStringListList output; 0490 0491 // Get header names 0492 int nb2 = ui.kTable->rowCount(); 0493 int nb = ui.kTable->columnCount(); 0494 output.reserve(nb2 + 1); 0495 QStringList cols; 0496 cols.reserve(2 * nb); 0497 for (int i = 0; i < nb; ++i) { 0498 cols.append(ui.kTable->horizontalHeaderItem(i)->text()); 0499 cols.append(i18n("%1 (raw)", ui.kTable->horizontalHeaderItem(i)->text())); 0500 } 0501 output.append(cols); 0502 0503 // Get content 0504 for (int i = 0; i < nb2; ++i) { 0505 QStringList row; 0506 row.reserve(nb); 0507 for (int j = 0; j < nb; j++) { 0508 auto* button = qobject_cast<SKGColorButton*>(ui.kTable->cellWidget(i, j)); 0509 if (button != nullptr) { 0510 row.append(button->text()); 0511 row.append(button->color().toRgb().name()); 0512 } else { 0513 row.append(ui.kTable->item(i, j)->text()); 0514 QString raw = ui.kTable->item(i, j)->data(DATA_VALUE).toString(); 0515 if (raw.isEmpty()) { 0516 raw = ui.kTable->item(i, j)->text(); 0517 } 0518 row.append(raw); 0519 } 0520 } 0521 output.append(row); 0522 } 0523 return output; 0524 } 0525 QString SKGTableWithGraph::getState() 0526 { 0527 SKGTRACEINFUNC(10) 0528 QDomDocument doc(QStringLiteral("SKGML")); 0529 QDomElement root = doc.createElement(QStringLiteral("parameters")); 0530 doc.appendChild(root); 0531 0532 if (ui.graphicView->isVisible() && ui.kTable->isVisible()) { 0533 root.setAttribute(QStringLiteral("splitterState"), QString(ui.splitter->saveState().toHex())); 0534 } 0535 root.setAttribute(QStringLiteral("graphMode"), SKGServices::intToString(static_cast<int>(getGraphType()))); 0536 root.setAttribute(QStringLiteral("allPositive"), m_allPositiveMenu->isChecked() ? QStringLiteral("Y") : QStringLiteral("N")); 0537 root.setAttribute(QStringLiteral("filter"), ui.kFilterEdit->text()); 0538 root.setAttribute(QStringLiteral("limitVisible"), m_limitVisible ? QStringLiteral("Y") : QStringLiteral("N")); 0539 root.setAttribute(QStringLiteral("averageVisible"), m_averageVisible ? QStringLiteral("Y") : QStringLiteral("N")); 0540 root.setAttribute(QStringLiteral("linearRegressionVisible"), m_linearRegressionVisible ? QStringLiteral("Y") : QStringLiteral("N")); 0541 root.setAttribute(QStringLiteral("paretoVisible"), m_paretoVisible ? QStringLiteral("Y") : QStringLiteral("N")); 0542 root.setAttribute(QStringLiteral("legendVisible"), m_legendVisible ? QStringLiteral("Y") : QStringLiteral("N")); 0543 root.setAttribute(QStringLiteral("zeroVisible"), m_zeroVisible ? QStringLiteral("Y") : QStringLiteral("N")); 0544 root.setAttribute(QStringLiteral("decimalsVisible"), m_decimalsVisible ? QStringLiteral("Y") : QStringLiteral("N")); 0545 QMapIterator<QString, QColor> i(m_mapTitleColor); 0546 while (i.hasNext()) { 0547 i.next(); 0548 QDomElement color = doc.createElement(QStringLiteral("color")); 0549 root.appendChild(color); 0550 color.setAttribute(QStringLiteral("key"), i.key()); 0551 color.setAttribute(QStringLiteral("value"), i.value().name()); 0552 } 0553 0554 QHeaderView* horizontalHeader = ui.kTable->horizontalHeader(); 0555 root.setAttribute(QStringLiteral("sortOrder"), SKGServices::intToString(horizontalHeader->sortIndicatorOrder())); 0556 root.setAttribute(QStringLiteral("sortColumn"), SKGServices::intToString(horizontalHeader->sortIndicatorSection())); 0557 root.setAttribute(QStringLiteral("graphicViewState"), ui.graphicView->getState()); 0558 root.setAttribute(QStringLiteral("web"), ui.kTextEdit->getState()); 0559 root.setAttribute(QStringLiteral("show"), ui.kShow->getState()); 0560 0561 if (ui.kTable->stickHorizontal()) { 0562 root.setAttribute(QStringLiteral("stickH"), QStringLiteral("Y")); 0563 } 0564 if (ui.kTable->stickVertical()) { 0565 root.setAttribute(QStringLiteral("stickV"), QStringLiteral("Y")); 0566 } 0567 0568 return doc.toString(); 0569 } 0570 0571 void SKGTableWithGraph::setState(const QString& iState) 0572 { 0573 SKGTRACEINFUNC(10) 0574 m_timer.stop(); 0575 m_timerRedraw.stop(); 0576 0577 QDomDocument doc(QStringLiteral("SKGML")); 0578 doc.setContent(iState); 0579 QDomElement root = doc.documentElement(); 0580 0581 m_mapTitleColor.clear(); 0582 0583 QDomNodeList colors = root.elementsByTagName(QStringLiteral("color")); 0584 int nb = colors.count(); 0585 for (int i = 0; i < nb; ++i) { 0586 QDomElement c = colors.at(i).toElement(); 0587 m_mapTitleColor[c.attribute(QStringLiteral("key"))] = QColor(c.attribute(QStringLiteral("value"))); 0588 } 0589 0590 QString splitterStateString = root.attribute(QStringLiteral("splitterState")); 0591 if (!splitterStateString.isEmpty()) { 0592 ui.splitter->restoreState(QByteArray::fromHex(splitterStateString.toLatin1())); 0593 } 0594 QString graphModeString = root.attribute(QStringLiteral("graphMode")); 0595 QString allPositiveString = root.attribute(QStringLiteral("allPositive")); 0596 QString limitVisibleString = root.attribute(QStringLiteral("limitVisible")); 0597 QString averageVisibleString = root.attribute(QStringLiteral("averageVisible")); 0598 QString legendVisibleString = root.attribute(QStringLiteral("legendVisible")); 0599 QString zeroVisibleString = root.attribute(QStringLiteral("zeroVisible")); 0600 QString decimalsVisibleString = root.attribute(QStringLiteral("decimalsVisible")); 0601 QString linearRegressionVisibleString = root.attribute(QStringLiteral("linearRegressionVisible")); 0602 QString paretorVisibleString = root.attribute(QStringLiteral("paretoVisible")); 0603 QString sortOrderString = root.attribute(QStringLiteral("sortOrder")); 0604 QString sortColumnString = root.attribute(QStringLiteral("sortColumn")); 0605 QString graphicViewStateString = root.attribute(QStringLiteral("graphicViewState")); 0606 QString webString = root.attribute(QStringLiteral("web")); 0607 QString showString = root.attribute(QStringLiteral("show")); 0608 ui.kTable->setStickHorizontal(root.attribute(QStringLiteral("stickH")) == QStringLiteral("Y")); 0609 ui.kTable->setStickVertical(root.attribute(QStringLiteral("stickV")) == QStringLiteral("Y")); 0610 0611 // Default value in case of reset 0612 if (graphModeString.isEmpty()) { 0613 graphModeString = SKGServices::intToString(LINE); 0614 } 0615 if (allPositiveString.isEmpty()) { 0616 allPositiveString = 'N'; 0617 } 0618 if (limitVisibleString.isEmpty()) { 0619 limitVisibleString = 'Y'; 0620 } 0621 if (averageVisibleString.isEmpty()) { 0622 averageVisibleString = 'Y'; 0623 } 0624 if (legendVisibleString.isEmpty()) { 0625 legendVisibleString = 'N'; 0626 } 0627 if (linearRegressionVisibleString.isEmpty()) { 0628 linearRegressionVisibleString = 'Y'; 0629 } 0630 if (paretorVisibleString.isEmpty()) { 0631 paretorVisibleString = 'N'; 0632 } 0633 if (sortOrderString.isEmpty()) { 0634 sortOrderString = '0'; 0635 } 0636 if (sortColumnString.isEmpty()) { 0637 sortColumnString = '0'; 0638 } 0639 0640 // Set 0641 setGraphType(SKGTableWithGraph::HISTOGRAM); 0642 if (m_displayMode != nullptr) { 0643 m_displayMode->setCurrentIndex(1); 0644 } 0645 setGraphType(static_cast<SKGTableWithGraph::GraphType>(SKGServices::stringToInt(graphModeString))); 0646 m_allPositiveMenu->setChecked(allPositiveString == QStringLiteral("Y")); 0647 ui.kFilterEdit->setText(root.attribute(QStringLiteral("filter"))); 0648 m_limitVisible = (limitVisibleString == QStringLiteral("Y")); 0649 if (m_actShowLimits != nullptr) { 0650 m_actShowLimits->setChecked(m_limitVisible); 0651 } 0652 m_averageVisible = (averageVisibleString == QStringLiteral("Y")); 0653 if (m_actShowAverage != nullptr) { 0654 m_actShowAverage->setChecked(m_averageVisible); 0655 } 0656 m_legendVisible = (legendVisibleString == QStringLiteral("Y")); 0657 if (m_actShowLegend != nullptr) { 0658 m_actShowLegend->setChecked(m_legendVisible); 0659 } 0660 m_zeroVisible = (zeroVisibleString != QStringLiteral("N")); 0661 if (m_actShowZero != nullptr) { 0662 m_actShowZero->setChecked(m_zeroVisible); 0663 } 0664 m_decimalsVisible = (decimalsVisibleString != QStringLiteral("N")); 0665 if (m_actShowDecimal != nullptr) { 0666 m_actShowDecimal->setChecked(m_decimalsVisible); 0667 } 0668 m_linearRegressionVisible = (linearRegressionVisibleString == QStringLiteral("Y")); 0669 if (m_actShowLinearRegression != nullptr) { 0670 m_actShowLinearRegression->setChecked(m_linearRegressionVisible); 0671 } 0672 m_paretoVisible = (paretorVisibleString == QStringLiteral("Y")); 0673 if (m_actShowPareto != nullptr) { 0674 m_actShowPareto->setChecked(m_paretoVisible); 0675 } 0676 0677 ui.kTable->setColumnCount(SKGServices::stringToInt(sortColumnString) + 1); 0678 QHeaderView* horizontalHeader = ui.kTable->horizontalHeader(); 0679 if (horizontalHeader != nullptr) { 0680 bool previous = horizontalHeader->blockSignals(true); 0681 horizontalHeader->setSortIndicator(SKGServices::stringToInt(sortColumnString), static_cast<Qt::SortOrder>(SKGServices::stringToInt(sortOrderString))); 0682 horizontalHeader->blockSignals(previous); 0683 } 0684 ui.graphicView->setState(graphicViewStateString); 0685 ui.kTextEdit->setState(webString); 0686 if (!showString.isEmpty()) { 0687 ui.kShow->setState(showString); 0688 } 0689 } 0690 0691 void SKGTableWithGraph::setFilterVisibility(bool iVisibility) const 0692 { 0693 ui.kToolbar->setVisible(iVisibility); 0694 } 0695 0696 void SKGTableWithGraph::onFilterModified() 0697 { 0698 m_timerRedraw.stop(); 0699 m_timer.start(300); 0700 } 0701 0702 void SKGTableWithGraph::onDisplayModeChanged() 0703 { 0704 QStringList mode = SKGServices::splitCSVLine(ui.kShow->getState()); 0705 0706 // Hide all 0707 if (m_scene != nullptr) { 0708 m_scene->clear(); 0709 delete m_scene; 0710 } 0711 m_scene = new SKGGraphicsScene(); 0712 ui.graphicView->setScene(m_scene); 0713 ui.graphicView->hide(); 0714 ui.kTextEdit->hide(); 0715 bool p = ui.kTable->blockSignals(true); 0716 ui.kTable->hide(); 0717 ui.kTable->blockSignals(p); 0718 m_graphVisible = false; 0719 m_tableVisible = false; 0720 m_textVisible = false; 0721 m_mapItemGraphic.clear(); 0722 0723 // Show needed widget 0724 if (mode.contains(QStringLiteral("table"))) { 0725 ui.kTable->show(); 0726 m_tableVisible = true; 0727 } 0728 if (mode.contains(QStringLiteral("graph"))) { 0729 ui.graphicView->show(); 0730 m_graphVisible = true; 0731 redrawGraphDelayed(); 0732 } 0733 if (mode.contains(QStringLiteral("text"))) { 0734 QTimer::singleShot(100, Qt::CoarseTimer, ui.kTextEdit, &SKGWebView::show); 0735 m_textVisible = true; 0736 redrawText(); 0737 } 0738 } 0739 0740 void SKGTableWithGraph::setData(const SKGStringListList& iData, 0741 const SKGServices::SKGUnitInfo& iPrimaryUnit, 0742 const SKGServices::SKGUnitInfo& iSecondaryUnit, 0743 SKGTableWithGraph::DisplayAdditionalFlag iAdditionalInformation, 0744 int iNbVirtualColumn) 0745 { 0746 SKGTRACEINFUNC(10) 0747 m_data = iData; 0748 m_primaryUnit = iPrimaryUnit; 0749 m_secondaryUnit = iSecondaryUnit; 0750 m_additionalInformation = iAdditionalInformation; 0751 m_nbVirtualColumns = iNbVirtualColumn; 0752 0753 onFilterModified(); 0754 } 0755 0756 SKGTableWithGraph::DisplayAdditionalFlag SKGTableWithGraph::getAdditionalDisplayMode() const 0757 { 0758 return m_additionalInformation; 0759 } 0760 0761 QStringList SKGTableWithGraph::getSumItems(const QString& iString) const 0762 { 0763 QStringList output; 0764 QString current = iString; 0765 int index = -1; 0766 do { 0767 output.insert(0, current); 0768 index = current.lastIndexOf(OBJECTSEPARATOR); 0769 if (index != -1) { 0770 current = current.left(index); 0771 } 0772 } while (index != -1); 0773 return output; 0774 } 0775 0776 void SKGTableWithGraph::addSums(SKGStringListList& ioTable, int& iNblines) 0777 { 0778 SKGTRACEINFUNC(10) 0779 int nbCols = -1; 0780 if (!ioTable.isEmpty()) { 0781 nbCols = ioTable.at(0).count(); 0782 } 0783 0784 // Create a list of sums lines associated to the index where to add them 0785 QMap<double, QStringList> sums; 0786 for (int i = 1; i < nbCols; ++i) { 0787 QStringList previousHeaderSum; 0788 QList<double> sum; 0789 QList<int> nbElem; 0790 0791 for (int j = 1; j < iNblines; ++j) { 0792 double indextoadd = j + 1 - 0.01; 0793 QStringList currentHeaderSum = getSumItems(ioTable.at(j).at(0)); 0794 if (previousHeaderSum.isEmpty()) { 0795 previousHeaderSum = currentHeaderSum; 0796 } 0797 0798 int nb = qMax(currentHeaderSum.count(), previousHeaderSum.count()); 0799 for (int k = 0; k < nb; ++k) { 0800 QString chString = currentHeaderSum.value(k); 0801 QString phString = previousHeaderSum.value(k); 0802 if (chString != phString) { 0803 // Add this sum 0804 if (nbElem.value(k) > 1) { 0805 QStringList thisSum; 0806 if (i == 1) { 0807 thisSum.push_back(phString); 0808 } else { 0809 thisSum = sums[indextoadd]; 0810 } 0811 thisSum.push_back(SKGServices::doubleToString(sum.value(k))); 0812 0813 sums[indextoadd] = thisSum; 0814 0815 indextoadd -= 0.01; 0816 } 0817 0818 // Reset sum 0819 if (k < sum.count()) { 0820 sum[k] = 0; 0821 } else { 0822 sum.push_back(0); 0823 } 0824 if (k < nbElem.count()) { 0825 nbElem[k] = 0; 0826 } else { 0827 nbElem.push_back(0); 0828 } 0829 } 0830 if (j < iNblines - 1) { 0831 sum = sum.mid(0, previousHeaderSum.count()); 0832 nbElem = nbElem.mid(0, previousHeaderSum.count()); 0833 0834 QString valstring = ioTable.at(j).at(i); 0835 double v = 0.0; 0836 if (!valstring.isEmpty()) { 0837 v = SKGServices::stringToDouble(valstring); 0838 } 0839 if (k < sum.count()) { 0840 sum[k] += v; 0841 } else { 0842 sum.push_back(v); 0843 } 0844 if (k < nbElem.count()) { 0845 nbElem[k] = nbElem[k] + 1; 0846 } else { 0847 nbElem.push_back(1); 0848 } 0849 } 0850 } 0851 0852 previousHeaderSum = currentHeaderSum; 0853 } 0854 } 0855 0856 // Add all sums in table 0857 QList<double> keys = sums.keys(); 0858 std::sort(keys.begin(), keys.end()); 0859 int nbkey = keys.count(); 0860 for (int i = nbkey - 1; i >= 0; --i) { 0861 double key = keys.at(i); 0862 ioTable.insert(key, sums[key]); 0863 m_sumRows.insert(key, true); 0864 ++iNblines; 0865 } 0866 } 0867 0868 void SKGTableWithGraph::refresh() 0869 { 0870 SKGTRACEINFUNC(10) 0871 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0872 0873 // Set parameters for static method 0874 QHeaderView* horizontalHeader = ui.kTable->horizontalHeader(); 0875 m_sortOrder = horizontalHeader->sortIndicatorOrder(); 0876 m_sortColumn = horizontalHeader->sortIndicatorSection(); 0877 0878 SKGStringListList groupedTable = m_data; 0879 int nbCols = -1; 0880 if (!groupedTable.isEmpty()) { 0881 nbCols = groupedTable.at(0).count(); 0882 } 0883 int nbRealCols = nbCols; 0884 0885 // Create filtered table 0886 { 0887 // Build list of criterias 0888 SKGServices::SKGSearchCriteriaList criterias = SKGServices::stringToSearchCriterias(ui.kFilterEdit->text()); 0889 0890 // Check all lines 0891 int nblists = criterias.count(); 0892 if ((nblists != 0) && (nbCols != 0)) { 0893 for (int i = groupedTable.count() - 1; i >= 1; --i) { // The first line is not filtered because it is the title 0894 QStringList line = groupedTable.at(i); 0895 0896 // Get title of the line 0897 const QString& val = line.at(0); 0898 0899 // Filtered 0900 bool ok = false; 0901 for (int l = 0; l < nblists; ++l) { 0902 QStringList words = criterias[l].words; 0903 QChar mode = criterias[l].mode; 0904 int nbwords = words.count(); 0905 0906 bool validateAllWords = true; 0907 for (int w = 0; validateAllWords && w < nbwords; ++w) { 0908 validateAllWords = val.contains(words.at(w), Qt::CaseInsensitive); 0909 } 0910 if (mode == '+') { 0911 ok |= validateAllWords; 0912 } else if (mode == '-' && validateAllWords) { 0913 ok = false; 0914 } 0915 } 0916 0917 if (!ok) { 0918 groupedTable.removeAt(i); 0919 } 0920 } 0921 } 0922 } 0923 0924 // Initialise sumRows 0925 int nblines = groupedTable.count(); 0926 m_sumRows.clear(); 0927 for (int j = 0; j < nblines; ++j) { 0928 m_sumRows.push_back(false); 0929 } 0930 0931 // Compute sums line 0932 if ((m_additionalInformation & SUM) != 0u) { 0933 QStringList sums; 0934 sums.reserve(nbCols + 1); 0935 sums.push_back(QString()); 0936 for (int i = 1; i < nbCols; ++i) { 0937 double sum = 0; 0938 for (int j = 1; j < nblines; ++j) { 0939 QString valstring = groupedTable.at(j).at(i); 0940 if (!valstring.isEmpty()) { 0941 sum += SKGServices::stringToDouble(valstring); 0942 } 0943 } 0944 sums.push_back(SKGServices::doubleToString(sum)); 0945 } 0946 groupedTable.push_back(sums); 0947 m_sumRows.push_back(true); 0948 ++nblines; 0949 } 0950 0951 // Compute sub sums lines 0952 if ((m_additionalInformation & SUM) != 0u && m_sortColumn == 0 && m_sortOrder == Qt::AscendingOrder) { 0953 addSums(groupedTable, nblines); 0954 } 0955 0956 // Compute sum and average column 0957 m_indexSum = -1; 0958 m_indexAverage = -1; 0959 m_indexMin = -1; 0960 m_indexLinearRegression = -1; 0961 if (nbCols > 2 && ((m_additionalInformation & SUM) != 0u || (m_averageVisible && (m_additionalInformation & AVERAGE) != 0u) || (m_limitVisible && (m_additionalInformation & LIMITS) != 0u))) { 0962 SKGTRACEINFUNC(10) 0963 // Add title 0964 QStringList newLine = groupedTable.at(0); 0965 if ((m_additionalInformation & SUM) != 0u) { 0966 m_indexSum = newLine.count(); 0967 newLine.push_back(i18nc("Noun, the numerical sum of a list of values", "Sum")); 0968 } 0969 if (m_averageVisible && (m_additionalInformation & AVERAGE) != 0u) { 0970 m_indexAverage = newLine.count(); 0971 newLine.push_back(i18nc("Noun, the numerical average of a list of values", "Average")); 0972 } 0973 if (m_limitVisible && (m_additionalInformation & LIMITS) != 0u) { 0974 m_indexMin = newLine.count(); 0975 newLine.push_back(i18nc("Noun, the minimum value of a list of values", "Min")); 0976 newLine.push_back(i18nc("Noun, the maximum value of a list of values", "Max")); 0977 } 0978 0979 if (m_linearRegressionVisible) { 0980 m_indexLinearRegression = newLine.count(); 0981 newLine.push_back(i18nc("Noun", "Tendency line")); 0982 } 0983 0984 groupedTable.replace(0, newLine); 0985 0986 for (int i = 1; i < nblines; ++i) { 0987 QStringList newLine2 = groupedTable.at(i); 0988 double sumy = 0; 0989 double sumx = 0; 0990 double sumx2 = 0; 0991 double sumxy = 0; 0992 double min = 10e20; 0993 double max = -10e20; 0994 int nbVals = 0; 0995 for (int j = 1; j < nbCols - m_nbVirtualColumns; ++j) { 0996 const QString& valstring = newLine2.at(j); 0997 if (!valstring.isEmpty()) { 0998 double v = SKGServices::stringToDouble(valstring); 0999 sumx += j; 1000 sumx2 += j * j; 1001 sumy += v; 1002 sumxy += j * v; 1003 min = qMin(min, v); 1004 max = qMax(max, v); 1005 ++nbVals; 1006 } 1007 } 1008 if ((m_additionalInformation & SUM) != 0u) { 1009 newLine2.push_back(SKGServices::doubleToString(sumy)); 1010 } 1011 if (m_averageVisible && (m_additionalInformation & AVERAGE) != 0u) { 1012 if (nbVals != 0) { 1013 newLine2.push_back(SKGServices::doubleToString(sumy / nbVals)); 1014 } else { 1015 newLine2.push_back(QStringLiteral("0")); 1016 } 1017 } 1018 if (m_limitVisible && (m_additionalInformation & LIMITS) != 0u) { 1019 if (nbVals != 0) { 1020 newLine2.push_back(SKGServices::doubleToString(min)); 1021 newLine2.push_back(SKGServices::doubleToString(max)); 1022 } else { 1023 newLine2.push_back(QStringLiteral("0")); 1024 newLine2.push_back(QStringLiteral("0")); 1025 } 1026 } 1027 1028 if (nbVals != 0) { 1029 double s2x = sumx2 / nbVals - sumx * sumx / (nbVals * nbVals); 1030 double sxy = sumxy / nbVals - (sumx / nbVals) * (sumy / nbVals); 1031 1032 double a = (s2x != 0.0 ? sxy / s2x : 0.0); 1033 double b = sumy / nbVals - a * sumx / nbVals; 1034 1035 newLine2.push_back("y=" % SKGServices::doubleToString(a) % "*x+" % SKGServices::doubleToString(b)); 1036 } else { 1037 newLine2.push_back(QStringLiteral("y=0")); 1038 } 1039 1040 groupedTable.replace(i, newLine2); 1041 } 1042 if (m_linearRegressionVisible) { 1043 ++nbCols; 1044 } 1045 if ((m_additionalInformation & SUM) != 0u) { 1046 ++nbCols; 1047 } 1048 if (m_averageVisible && (m_additionalInformation & AVERAGE) != 0u) { 1049 ++nbCols; 1050 } 1051 if (m_limitVisible && (m_additionalInformation & LIMITS) != 0u) { 1052 nbCols += 2; 1053 } 1054 } 1055 1056 // Sort lines 1057 if (m_sortColumn != 0 || m_sortOrder != Qt::AscendingOrder) { 1058 SKGTRACEINFUNC(10) 1059 // Extract title and sums 1060 if (!groupedTable.isEmpty()) { 1061 QStringList fist = groupedTable.takeFirst(); 1062 QStringList last; 1063 if ((m_additionalInformation & SUM) != 0u && !groupedTable.isEmpty()) { 1064 last = groupedTable.takeLast(); 1065 } 1066 1067 // Sort 1068 QCollator comp; 1069 comp.setCaseSensitivity(Qt::CaseInsensitive); 1070 std::sort(groupedTable.begin(), groupedTable.end(), [&](const QStringList & s1, const QStringList & s2) { 1071 if (m_sortColumn >= s1.count()) { 1072 m_sortColumn = s1.count() - 1; 1073 } 1074 if (m_sortColumn >= 0) { 1075 const QString& v1 = s1.at(m_sortColumn); 1076 const QString& v2 = s2.at(m_sortColumn); 1077 if (m_sortColumn == 0) { 1078 int v = comp.compare(v1, v2); 1079 return (m_sortOrder != 0u ? v > 0 : v < 0); 1080 } 1081 1082 double vd1 = SKGServices::stringToDouble(v1); 1083 double vd2 = SKGServices::stringToDouble(v2); 1084 return (m_sortOrder != 0u ? vd1 > vd2 : vd1 < vd2); 1085 } 1086 return false; 1087 }); 1088 1089 // Add title and sums 1090 groupedTable.insert(0, fist); 1091 if ((m_additionalInformation & SUM) != 0u) { 1092 groupedTable.push_back(last); 1093 } 1094 } 1095 } 1096 1097 IFSKGTRACEL(10) { 1098 QStringList dump = SKGServices::tableToDump(groupedTable, SKGServices::DUMP_TEXT); 1099 int nbl = dump.count(); 1100 for (int i = 0; i < nbl; ++i) { 1101 SKGTRACE << dump.at(i) << SKGENDL; 1102 } 1103 } 1104 1105 // Fill table 1106 { 1107 SKGTRACEINFUNC(10) 1108 1109 int nbRealColumns = nbRealCols - m_nbVirtualColumns; 1110 ui.kTable->hide(); 1111 QHeaderView* hHeader = ui.kTable->horizontalHeader(); 1112 if (hHeader != nullptr) { 1113 hHeader->setSectionResizeMode(QHeaderView::Fixed); // Needed to improve performances of setHorizontalHeaderLabels 1114 } 1115 ui.kTable->clear(); 1116 ui.kTable->setRowCount(nblines - 1); 1117 ui.kTable->setColumnCount(nbCols); 1118 for (int i = 0; i < nblines; ++i) { 1119 const QStringList& line = groupedTable.at(i); 1120 if (i == 0) { 1121 SKGTRACEINFUNC(10) 1122 // Set header 1123 ui.kTable->setHorizontalHeaderLabels(line); 1124 } else { 1125 for (int j = 0; j < nbCols; ++j) { 1126 const QString& val = line.at(j); 1127 1128 QTableWidgetItem* item; 1129 if (j == 0) { 1130 // Create the line header 1131 if (m_sumRows.at(i)) { 1132 item = new QTableWidgetItem(val.isEmpty() ? 1133 i18nc("Noun, the numerical sum of a list of values", "Sum") 1134 : i18nc("Noun, the numerical sum of a list of values", "Sum of '%1'", val)); 1135 item->setData(1, val); 1136 QFont f = item->font(); 1137 f.setBold(true); 1138 item->setFont(f); 1139 if (m_indexLinearRegression != -1) { 1140 item->setData(DATA_VALUE, line.at(m_indexLinearRegression)); 1141 } 1142 ui.kTable->setItem(i - 1, j, item); 1143 1144 // Set header 1145 ui.kTable->setVerticalHeaderItem(i - 1, new QTableWidgetItem(*item)); 1146 } else { 1147 // New color selector 1148 QColor defaultColor; 1149 int color_h = (240 + 360 * (i - 1) / nblines) % 360; // First color is blue to be compliant with min (red) and max (green) 1150 defaultColor = QColor::fromHsv(color_h, 255, 255); 1151 1152 QColor color; 1153 if (m_mapTitleColor.contains(val)) { 1154 color = m_mapTitleColor[val]; 1155 } else { 1156 color = defaultColor; 1157 m_mapTitleColor[val] = color; 1158 } 1159 1160 auto colorSelector = new SKGColorButton(this); 1161 colorSelector->setText(val); 1162 colorSelector->setToolTip(val); 1163 colorSelector->setColor(color); 1164 colorSelector->setDefaultColor(defaultColor); 1165 connect(colorSelector, &SKGColorButton::changed, this, &SKGTableWithGraph::onChangeColor); 1166 1167 ui.kTable->setCellWidget(i - 1, 0, colorSelector); 1168 1169 // Set header 1170 auto itemTmp = new QTableWidgetItem(val); 1171 itemTmp->setBackground(color); 1172 itemTmp->setToolTip(val); 1173 ui.kTable->setVerticalHeaderItem(i - 1, itemTmp); 1174 } 1175 } else { 1176 // Add a value 1177 QString tooltip = line.at(0) % '\n' % groupedTable.at(0).at(j); 1178 if (!val.isEmpty()) { 1179 if (j == m_indexLinearRegression) { 1180 // A linear regression value 1181 item = new QTableWidgetItem(val); 1182 } else { 1183 // A single value 1184 double vald = SKGServices::stringToDouble(val); 1185 QString vals = SKGServices::toCurrencyString(vald, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0); 1186 tooltip += '\n' % vals; 1187 1188 item = new QTableWidgetItem(vals); 1189 item->setToolTip(tooltip); 1190 if (!m_secondaryUnit.Symbol.isEmpty() && (m_secondaryUnit.Value != 0.0)) { 1191 item->setToolTip(tooltip % '\n' % SKGServices::toCurrencyString(vald / m_secondaryUnit.Value, m_secondaryUnit.Symbol, m_decimalsVisible ? m_secondaryUnit.NbDecimal : 0)); 1192 } 1193 1194 item->setData(DATA_VALUE, vald); 1195 item->setTextAlignment(Qt::AlignRight); 1196 if (vald < 0) { 1197 item->setForeground(m_NegativeColor); 1198 } 1199 if (m_sumRows.at(i)) { 1200 QFont f = item->font(); 1201 f.setBold(true); 1202 item->setFont(f); 1203 } 1204 } 1205 } else { 1206 // An empty value 1207 item = new QTableWidgetItem(QString()); 1208 item->setToolTip(tooltip); 1209 item->setData(DATA_VALUE, ""); 1210 } 1211 item->setFlags((j >= nbRealColumns && j != m_indexSum) || ui.kTable->horizontalHeaderItem(j)->text() == QStringLiteral("0000") ? Qt::NoItemFlags : Qt::ItemIsEnabled | Qt::ItemIsSelectable); 1212 ui.kTable->setItem(i - 1, j, item); 1213 } 1214 } 1215 } 1216 } 1217 1218 // Refresh graphic view 1219 redrawGraph(); 1220 1221 // Refresh text area 1222 redrawText(); 1223 1224 if (hHeader != nullptr) { 1225 hHeader->setSectionResizeMode(QHeaderView::ResizeToContents); 1226 } 1227 1228 if (m_tableVisible) { 1229 ui.kTable->show(); 1230 } 1231 1232 if ((hHeader != nullptr) && (hHeader->count() != 0)) { 1233 hHeader->resizeSection(0, 100); 1234 hHeader->setSectionResizeMode(0, QHeaderView::Interactive); 1235 } 1236 } 1237 1238 QApplication::restoreOverrideCursor(); 1239 } 1240 1241 void SKGTableWithGraph::onChangeColor() 1242 { 1243 auto* colorButton = qobject_cast<SKGColorButton*>(sender()); 1244 if (colorButton != nullptr) { 1245 m_mapTitleColor[colorButton->text()] = colorButton->color(); 1246 refresh(); 1247 } 1248 } 1249 1250 void SKGTableWithGraph::resetColors() 1251 { 1252 m_mapTitleColor.clear(); 1253 refresh(); 1254 } 1255 1256 void SKGTableWithGraph::onSelectionChanged() 1257 { 1258 _SKGTRACEINFUNC(10) 1259 if (m_graphVisible) { 1260 // Unset color on previous selection 1261 int nbRow = ui.kTable->rowCount(); 1262 int nbCol = ui.kTable->columnCount(); 1263 for (int r = 0; r < nbRow; ++r) { 1264 for (int c = 0; c < nbCol; ++c) { 1265 QTableWidgetItem* previous = ui.kTable->item(r, c); 1266 if (previous != nullptr) { 1267 QGraphicsItem* val = m_mapItemGraphic.value(previous); 1268 if (val != nullptr) { 1269 auto* graphicItem = qgraphicsitem_cast<QAbstractGraphicsShapeItem*>(val); 1270 if (graphicItem != nullptr) { 1271 QColor color = QColor::fromHsv(graphicItem->data(DATA_COLOR_H).toInt(), 1272 graphicItem->data(DATA_COLOR_S).toInt(), 1273 graphicItem->data(DATA_COLOR_V).toInt()); 1274 color.setAlpha(ALPHA); 1275 1276 if (graphicItem->data(DATA_MODE).toInt() == 1) { 1277 QPen pen = graphicItem->pen(); 1278 pen.setColor(color); 1279 graphicItem->setPen(pen); 1280 } else { 1281 graphicItem->setBrush(QBrush(color)); 1282 } 1283 graphicItem->setZValue(graphicItem->data(DATA_Z_VALUE).toReal()); 1284 if (graphicItem->isSelected()) { 1285 graphicItem->setSelected(false); 1286 } 1287 } 1288 } 1289 } 1290 } 1291 } 1292 1293 // Set highlight color on current selection 1294 QList<QTableWidgetItem*> selected = ui.kTable->selectedItems(); 1295 int nb = selected.count(); 1296 for (int i = 0; i < nb; ++i) { 1297 QTableWidgetItem* current = selected.at(i); 1298 if (current != nullptr) { 1299 QGraphicsItem* val = m_mapItemGraphic.value(current); 1300 auto* graphicItem = qgraphicsitem_cast<QAbstractGraphicsShapeItem*>(val); 1301 if (graphicItem != nullptr) { 1302 if (graphicItem->data(DATA_MODE).toInt() == 1) { 1303 QPen pen = graphicItem->pen(); 1304 pen.setColor(QApplication::palette().color(QPalette::Highlight)); 1305 graphicItem->setPen(pen); 1306 } else { 1307 graphicItem->setBrush(QBrush(QApplication::palette().color(QPalette::Highlight))); 1308 } 1309 graphicItem->setZValue(15); 1310 graphicItem->setSelected(true); 1311 graphicItem->ensureVisible(); 1312 } 1313 } 1314 } 1315 } 1316 1317 emit selectionChanged(); 1318 } 1319 1320 void SKGTableWithGraph::onDoubleClickGraph() 1321 { 1322 if (m_scene != nullptr) { 1323 // Get selection 1324 QList<QGraphicsItem*> selectedGraphItems = m_scene->selectedItems(); 1325 if (!selectedGraphItems.isEmpty()) { 1326 Q_EMIT cellDoubleClicked(selectedGraphItems[0]->data(1).toInt(), selectedGraphItems[0]->data(2).toInt()); 1327 } 1328 } 1329 } 1330 1331 void SKGTableWithGraph::onDoubleClick(int row, int column) 1332 { 1333 Q_EMIT cellDoubleClicked(row, column); 1334 } 1335 1336 void SKGTableWithGraph::onLinkClicked(const QUrl& url) 1337 { 1338 QString path = url.toString().remove(QStringLiteral("https://linkclicked/")); 1339 QStringList items = SKGServices::splitCSVLine(path, ','); 1340 if (items.count() == 2) { 1341 Q_EMIT cellDoubleClicked(SKGServices::stringToInt(items[0]), SKGServices::stringToInt(items[1])); 1342 } 1343 } 1344 1345 void SKGTableWithGraph::onSelectionChangedInGraph() 1346 { 1347 _SKGTRACEINFUNC(10) 1348 if (m_scene != nullptr) { 1349 bool previous = ui.kTable->blockSignals(true); 1350 ui.kTable->clearSelection(); 1351 1352 // Get selection 1353 QList<QGraphicsItem*> selectedGraphItems = m_scene->selectedItems(); 1354 int nb = selectedGraphItems.count(); 1355 for (int i = 0; i < nb; ++i) { 1356 ui.kTable->setCurrentCell(selectedGraphItems.at(i)->data(1).toInt(), selectedGraphItems.at(i)->data(2).toInt(), QItemSelectionModel::Select); 1357 } 1358 ui.kTable->blockSignals(previous); 1359 1360 previous = m_scene->blockSignals(true); 1361 onSelectionChanged(); 1362 m_scene->blockSignals(previous); 1363 } 1364 } 1365 1366 void SKGTableWithGraph::redrawGraphDelayed() 1367 { 1368 m_timerRedraw.start(300); 1369 } 1370 1371 void SKGTableWithGraph::redrawText() 1372 { 1373 if (!m_textVisible) { 1374 return; 1375 } 1376 SKGTRACEINFUNC(10) 1377 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 1378 1379 QString html = QStringLiteral("<? xml version = \"1.0\" encoding=\"utf-8\"?>" 1380 "<!DOCTYPE html PUBLIC \"-// W3C// DTD XHTML 1.0 Strict// EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" 1381 "<html xmlns=\"https://www.w3.org/1999/xhtml\">" 1382 "<head>" 1383 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\" />" 1384 "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\" />" 1385 "<style type=\"text/css\">" 1386 "body{background-color: #FFFFFF; font-size : small; display: inline;} a{color: inherit; text-decoration: inherit;} h1{text-decoration: underline; color: #FF3333;} h2{text-decoration: underline; color: #FF9933;} .table{border: thin solid #000000; border-collapse: collapse; background-color: #000000;} .tabletitle{background-color: #6495ed; color : #FFFF33; font-weight : bold; font-size : normal} .tabletotal{background-color: #D0E3FA;font-weight : bold;} tr{background-color: #FFFFFF;padding: 2px;} td{padding: 2px; white-space: nowrap;}" 1387 "</style>" 1388 "</head>" 1389 "<body>" 1390 "<table class=\"table\"><tr class=\"tabletitle\">"); 1391 // Dump header 1392 int nbCols = ui.kTable->columnCount(); 1393 for (int i = 0; i < nbCols; ++i) { 1394 QTableWidgetItem* item = ui.kTable->horizontalHeaderItem(i); 1395 if (item != nullptr) { 1396 html += R"(<td align="center" width="1000"><b>)" % item->text() % "</b></td>"; 1397 } 1398 } 1399 html += QStringLiteral("</tr>"); 1400 1401 // Dump values 1402 int nbLines = ui.kTable->rowCount(); 1403 for (int j = 0; j < nbLines; ++j) { 1404 html += QStringLiteral("<tr") % (m_sumRows.at(j + 1) ? " class=\"tabletotal\"" : "") % '>'; 1405 for (int i = 0; i < nbCols; ++i) { 1406 QTableWidgetItem* item = ui.kTable->item(j, i); 1407 if (item != nullptr) { 1408 bool red = (item->data(DATA_VALUE).toDouble() < 0); 1409 html += QStringLiteral("<td align=\"right\">") % (red ? "<font color=\"red\">" : ""); 1410 if ((item->flags()&Qt::ItemIsSelectable) != 0u) { 1411 html += "<a href=\"https://linkclicked/" % SKGServices::intToString(j) % "," % SKGServices::intToString(i) % "\">"; 1412 } 1413 html += item->text(); 1414 if ((item->flags()&Qt::ItemIsSelectable) != 0u) { 1415 html += QStringLiteral("</a>"); 1416 } 1417 html += QString(red ? QStringLiteral("</font>") : QString()) % "</td>"; 1418 } else { 1419 auto* colorButton = qobject_cast<SKGColorButton*>(ui.kTable->cellWidget(j, i)); 1420 if (colorButton != nullptr) { 1421 html += "<td><b>" % colorButton->text() % "</b></td>"; 1422 } 1423 } 1424 } 1425 html += QStringLiteral("</tr>"); 1426 } 1427 html += QStringLiteral("</table>"); 1428 html += QStringLiteral("</body></html>"); 1429 ui.kTextEdit->setHtml(html); 1430 QApplication::restoreOverrideCursor(); 1431 } 1432 1433 void SKGTableWithGraph::redrawGraph() 1434 { 1435 SKGTRACEINFUNC(10) 1436 m_mapItemGraphic.clear(); 1437 if (!m_graphVisible) { 1438 return; 1439 } 1440 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 1441 1442 ui.graphicView->hide(); 1443 ui.kTable->hide(); 1444 1445 // Recreate scene 1446 if (m_scene != nullptr) { 1447 SKGTRACEINFUNC(10) 1448 m_scene->clear(); 1449 delete m_scene; 1450 } 1451 1452 m_scene = new SKGGraphicsScene(); 1453 { 1454 SKGTRACEINFUNC(10) 1455 m_scene->setBackgroundBrush(m_backgroundColor); 1456 1457 // Get current selection 1458 int crow = ui.kTable->currentRow(); 1459 int ccolumn = ui.kTable->currentColumn(); 1460 1461 // Get nb columns and rows 1462 int nbRows = ui.kTable->rowCount(); 1463 int nbRealRows = nbRows; 1464 for (int posy = 0; posy < nbRows; ++posy) { 1465 if (m_sumRows.at(posy + 1)) { 1466 --nbRealRows; 1467 } 1468 } 1469 1470 int nbColumns = getNbColumns(false); 1471 int nbRealColumns = nbColumns - m_nbVirtualColumns; 1472 1473 // Get graphic mode 1474 GraphType mode = getGraphType(); 1475 1476 // Get in positive 1477 bool inPositive = false; 1478 if (mode == STACK || mode == STACKCOLUMNS || mode == HISTOGRAM || mode == POINT || mode == LINE || mode == STACKAREA) { 1479 m_allPositiveMenu->setEnabled(true); 1480 inPositive = (m_allPositiveMenu->isChecked()); 1481 } else { 1482 m_allPositiveMenu->setEnabled(false); 1483 if (mode == CONCENTRICPIE || mode == PIE || mode == TREEMAP) { 1484 inPositive = true; 1485 } 1486 } 1487 1488 // Get show origin 1489 bool showOrigin = true; 1490 if (mode == HISTOGRAM || mode == POINT || mode == LINE) { 1491 m_actShowZero->setEnabled(true); 1492 showOrigin = (m_actShowZero->isChecked()); 1493 } else { 1494 m_actShowZero->setEnabled(false); 1495 } 1496 1497 // Compute y limits 1498 double minLimit = (showOrigin ? 0 : 9999999); 1499 double maxLimit = (showOrigin ? 0 : -9999999); 1500 int nbLevel = 0; 1501 SKGTRACEL(3) << "mode=" << static_cast<unsigned int>(mode) << SKGENDL; 1502 SKGTRACEL(3) << "nb rows =" << nbRows << SKGENDL; 1503 SKGTRACEL(3) << "nb real rows =" << nbRealRows << SKGENDL; 1504 SKGTRACEL(3) << "nb columns =" << nbColumns << SKGENDL; 1505 SKGTRACEL(3) << "nb real columns=" << nbRealColumns << SKGENDL; 1506 SKGTRACEL(3) << "selected row =" << crow << SKGENDL; 1507 SKGTRACEL(3) << "selected column=" << ccolumn << SKGENDL; 1508 if (mode == STACK) { 1509 // STACK 1510 for (int posx = 0; posx < nbRows; ++posx) { 1511 if (!m_sumRows[posx + 1]) { 1512 double sumPositive = 0; 1513 double sumNegative = 0; 1514 for (int posy = 0; posy < nbColumns; ++posy) { 1515 QTableWidgetItem* tableItem = ui.kTable->item(posx, posy); 1516 if (tableItem != nullptr) { 1517 QVariant valQ = tableItem->data(DATA_VALUE); 1518 if (valQ.type() == QVariant::Double) { 1519 double val = valQ.toDouble(); 1520 if (inPositive || val >= 0) { 1521 sumPositive += qAbs(val); 1522 } else { 1523 sumNegative += val; 1524 } 1525 } 1526 } 1527 } 1528 1529 minLimit = qMin(minLimit, sumNegative); 1530 maxLimit = qMax(maxLimit, sumPositive); 1531 } 1532 } 1533 } else if (mode == STACKAREA || mode == STACKCOLUMNS || mode == PIE || mode == CONCENTRICPIE || mode == TREEMAP) { 1534 // STACKAREA or STACKCOLUMNS or PIE or CONCENTRICPIE 1535 for (int posy = 0; posy < nbColumns; ++posy) { 1536 double sumPositive = 0; 1537 double sumNegative = 0; 1538 for (int posx = 0; posx < nbRows; ++posx) { 1539 if (!m_sumRows[posx + 1]) { 1540 QTableWidgetItem* tableItem = ui.kTable->item(posx, posy); 1541 if (tableItem != nullptr) { 1542 QVariant valQ = tableItem->data(DATA_VALUE); 1543 if (valQ.type() == QVariant::Double) { 1544 double val = valQ.toDouble(); 1545 if (inPositive || val >= 0) { 1546 sumPositive += qAbs(val); 1547 } else { 1548 sumNegative += val; 1549 } 1550 } 1551 } 1552 } 1553 } 1554 if (mode == TREEMAP) { 1555 // TREEMAP 1556 minLimit = 0; 1557 maxLimit = qAbs(sumNegative) + sumPositive; 1558 } else { 1559 minLimit = qMin(minLimit, sumNegative); 1560 maxLimit = qMax(maxLimit, sumPositive); 1561 } 1562 } 1563 } else if (mode == HISTOGRAM || mode == POINT || mode == LINE) { 1564 // HISTOGRAM or POINTS or LINES 1565 for (int posx = 0; posx < nbRows; ++posx) { 1566 if (!m_sumRows[posx + 1]) { 1567 for (int posy = 0; posy < nbColumns; ++posy) { 1568 QTableWidgetItem* tableItem = ui.kTable->item(posx, posy); 1569 if (tableItem != nullptr) { 1570 QVariant valQ = tableItem->data(DATA_VALUE); 1571 if (valQ.type() == QVariant::Double) { 1572 double val = valQ.toDouble(); 1573 if (inPositive) { 1574 maxLimit = qMax(maxLimit, qAbs(val)); 1575 minLimit = qMin(minLimit, qAbs(val)); 1576 } else { 1577 minLimit = qMin(minLimit, val); 1578 maxLimit = qMax(maxLimit, val); 1579 } 1580 } 1581 } 1582 } 1583 } 1584 } 1585 } else if (mode == BUBBLE) { 1586 for (int posx = 0; posx < nbRows; ++posx) { 1587 if (!m_sumRows[posx + 1]) { 1588 for (int posy = 0; posy < nbColumns; ++posy) { 1589 QTableWidgetItem* tableItem = ui.kTable->item(posx, posy); 1590 if (tableItem != nullptr) { 1591 QVariant valQ = tableItem->data(DATA_VALUE); 1592 if (valQ.type() == QVariant::Double) { 1593 double val = valQ.toDouble(); 1594 maxLimit = qMax(maxLimit, qAbs(val)); 1595 } 1596 } 1597 } 1598 } 1599 } 1600 } 1601 1602 if (mode == CONCENTRICPIE) { 1603 for (int posx = 0; posx < nbRows; ++posx) { 1604 auto* btn = qobject_cast<SKGColorButton*>(ui.kTable->cellWidget(posx, 0)); 1605 if (btn != nullptr) { 1606 QString xname = btn->text(); 1607 QStringList vals = xname.split(OBJECTSEPARATOR); 1608 int nbvals = vals.count(); 1609 nbLevel = qMax(nbLevel, nbvals - 1); 1610 } 1611 } 1612 } 1613 1614 // Compute 1615 double yorigin = 0.0; 1616 double widthItem = 10; 1617 double maxX = 0; 1618 double margin = 0; 1619 double marginLeft = 0; 1620 double xstep = 0.0; 1621 double radius = 0.0; 1622 int jstep = qMax(computeStepSize(nbColumns, 10), static_cast<double>(1.0)); 1623 double ystep = computeStepSize(maxLimit - minLimit, 10); 1624 if (mode != BUBBLE && mode != PIE && mode != CONCENTRICPIE && mode != TREEMAP && ystep != 0) { 1625 double newMinLimit = ystep * qRound(minLimit / ystep); 1626 minLimit = (minLimit - newMinLimit < -EPSILON ? newMinLimit - ystep : newMinLimit); 1627 double newMaxLimit = ystep * qRound(maxLimit / ystep); 1628 maxLimit = (maxLimit - newMaxLimit > EPSILON ? newMaxLimit + ystep : newMaxLimit); 1629 } 1630 1631 if ((nbRealRows != 0) && ystep != 0) { 1632 QRect vSize = ui.graphicView->rect(); 1633 if (mode == STACK) { 1634 margin = (maxLimit - minLimit) * 0.3; 1635 1636 if (!showOrigin) { 1637 if (minLimit > 0) { 1638 yorigin = ystep * (qRound(minLimit / ystep)); 1639 } else if (maxLimit < 0) { 1640 yorigin = ystep * (qRound(maxLimit / ystep)); 1641 } 1642 } 1643 1644 widthItem = (maxLimit - minLimit) / (nbRealRows + 1); 1645 widthItem = widthItem * (static_cast<double>(vSize.width())) / (static_cast<double>(vSize.height())); 1646 1647 xstep = widthItem * (nbRealRows + 1); 1648 marginLeft = widthItem; 1649 1650 maxX = widthItem * nbRealRows + margin; 1651 } else if (mode == HISTOGRAM || mode == POINT || mode == LINE || mode == STACKAREA || mode == STACKCOLUMNS) { 1652 margin = (maxLimit - minLimit) * 0.3; 1653 1654 if (!showOrigin) { 1655 if (minLimit > 0) { 1656 yorigin = ystep * (qRound(minLimit / ystep)); 1657 } else if (maxLimit < 0) { 1658 yorigin = ystep * (qRound(maxLimit / ystep)); 1659 } 1660 } 1661 1662 widthItem = (maxLimit - minLimit) / ((nbRealRows + 1) * (nbColumns - 1)); 1663 widthItem = widthItem * (static_cast<double>(vSize.width())) / (static_cast<double>(vSize.height())); 1664 1665 xstep = widthItem * (nbRealRows + 1); 1666 marginLeft = qMin(jstep * xstep, widthItem * (nbColumns - 1)); 1667 1668 maxX = xstep * (nbColumns - 1); 1669 if (mode == POINT || mode == LINE || mode == STACKAREA) { 1670 maxX -= xstep; 1671 } 1672 // radius = qMax(margin / 100.0, qMin(width, qMin(ystep / 4, jstep * xstep / 4))); 1673 radius = qMax(margin / 200, qMin(widthItem, qMin(ystep / 8, jstep * xstep / 8))); 1674 } else if (mode == BUBBLE) { 1675 margin = ystep * nbRealRows * 0.3; 1676 1677 widthItem = ystep * nbRealRows / (nbRealRows * (nbColumns - 1)); 1678 widthItem = widthItem * (static_cast<double>(vSize.width())) / (static_cast<double>(vSize.height())); 1679 1680 xstep = widthItem * (nbRealRows + 1); 1681 marginLeft = jstep * xstep; 1682 1683 maxX = widthItem * (nbColumns - 1) * (nbRealRows + 1); 1684 } 1685 } 1686 1687 SKGTRACEL(3) << "minLimit=" << minLimit << SKGENDL; 1688 SKGTRACEL(3) << "maxLimit=" << maxLimit << SKGENDL; 1689 SKGTRACEL(3) << "ystep=" << ystep << SKGENDL; 1690 SKGTRACEL(3) << "yorigin=" << yorigin << SKGENDL; 1691 SKGTRACEL(3) << "width=" << widthItem << SKGENDL; 1692 SKGTRACEL(3) << "maxX=" << maxX << SKGENDL; 1693 SKGTRACEL(3) << "margin=" << margin << SKGENDL; 1694 SKGTRACEL(3) << "xstep=" << xstep << SKGENDL; 1695 SKGTRACEL(3) << "marginLeft=" << marginLeft << SKGENDL; 1696 SKGTRACEL(3) << "radius=" << radius << SKGENDL; 1697 1698 // Initialise pens 1699 QPen axisPen = QPen(Qt::SolidLine); 1700 axisPen.setColor(m_axisColor); 1701 axisPen.setWidthF(0); 1702 axisPen.setJoinStyle(Qt::RoundJoin); 1703 1704 QPen gridPen = QPen(Qt::SolidLine); 1705 gridPen.setColor(m_gridColor); 1706 gridPen.setWidthF(0); 1707 1708 QPen dotPen = QPen(Qt::SolidLine); // WARNING: Qt::DotLine is very bad for performances 1709 dotPen.setWidthF(0); 1710 1711 QPen outlinePen(m_outlineColor); 1712 outlinePen.setWidthF(0); 1713 1714 // Set rect 1715 int nbColInMode2 = (nbColumns > 2 ? sqrt(nbColumns - 1) + .5 : 1); 1716 m_scene->setItemIndexMethod(QGraphicsScene::NoIndex); 1717 SKGTRACEL(10) << "Items:" << nbRealRows << "x" << (nbColumns - 1) << "=" << nbRealRows*(nbColumns - 1) << SKGENDL; 1718 SKGTRACEL(10) << "nbColInMode2=" << nbColInMode2 << SKGENDL; 1719 1720 // Compute treemap 1721 QMap<QString, SKGTreeMap> treemap; 1722 if (mode == TREEMAP) { 1723 SKGTreeMap treemapobj(QString(), 0, 0, 0, BOX_SIZE, BOX_SIZE); 1724 for (int j = 1; j < nbColumns; ++j) { 1725 SKGTreeMap treemapobj2; 1726 for (int xx = 0; xx < nbRows; ++xx) { 1727 auto* colorButton = qobject_cast<SKGColorButton*>(ui.kTable->cellWidget(xx, 0)); 1728 if (colorButton != nullptr) { 1729 // Get cell value 1730 QTableWidgetItem* tableItem = ui.kTable->item(xx, j); 1731 if (tableItem != nullptr) { 1732 QVariant valQ = tableItem->data(DATA_VALUE); 1733 if (valQ.type() == QVariant::Double) { 1734 double val = qAbs(valQ.toDouble()); 1735 QString id = SKGServices::intToString(xx) % QStringLiteral("-") % SKGServices::intToString(j); 1736 treemapobj2.addChild(SKGTreeMap(id, val)); 1737 } 1738 } 1739 } 1740 } 1741 treemapobj.addChild(treemapobj2); 1742 } 1743 1744 treemapobj.compute(); 1745 treemap = treemapobj.getAllTilesById(); 1746 } 1747 1748 // Redraw scene 1749 double x0 = 0; 1750 double x1 = 0; 1751 double ymin = (showOrigin || mode == STACK || mode == STACKCOLUMNS ? 0 : 9999999); 1752 double ymax = (showOrigin || mode == STACK || mode == STACKCOLUMNS ? 0 : -9999999); 1753 int posx = 0; 1754 int nbRowsDrawed = 0; 1755 QMap<int, double> previousStackedPlus; 1756 QMap<int, double> previousStackedMoins; 1757 QMap<double, double> paretoPoints; 1758 for (int xx = 0; xx < nbRows; ++xx) { 1759 auto* colorButton = qobject_cast<SKGColorButton*>(ui.kTable->cellWidget(xx, 0)); 1760 if (colorButton != nullptr) { 1761 QString xname = colorButton->text(); 1762 QColor initialColor = colorButton->color(); 1763 double yPlus = 0; 1764 double yMoins = 0; 1765 int jprevious = -1; 1766 1767 ++nbRowsDrawed; 1768 for (int j = 1; j < nbColumns; ++j) { 1769 // Get cell value 1770 QTableWidgetItem* tableItem = ui.kTable->item(xx, j); 1771 if (tableItem != nullptr) { 1772 QVariant valQ = tableItem->data(DATA_VALUE); 1773 if (valQ.type() == QVariant::Double) { 1774 double val = valQ.toDouble(); 1775 QString valstring = tableItem->text(); 1776 1777 if (inPositive) { 1778 val = qAbs(val); 1779 } 1780 1781 int color_h; 1782 int color_s; 1783 int color_v; 1784 initialColor.getHsv(&color_h, &color_s, &color_v); 1785 color_s = color_s - color_s * 155 / 255 * (nbColumns - 1 - j) / nbColumns; 1786 color_v = color_v - color_v * 155 / 255 * (nbColumns - 1 - j) / nbColumns; 1787 if (j >= nbRealColumns) { 1788 // Yellow 1789 color_h = 60; 1790 color_s = 255; 1791 color_v = 255; 1792 } 1793 1794 QColor color; 1795 if (posx == crow && j == ccolumn) { 1796 color = QApplication::palette().color(QPalette::Highlight); 1797 } else { 1798 color = QColor::fromHsv(color_h, color_s, color_v); 1799 } 1800 1801 color.setAlpha(ALPHA); 1802 QBrush brush(color); 1803 1804 QGraphicsItem* graphItem = nullptr; 1805 if (mode == STACK) { 1806 // STACK 1807 if (val >= 0) { 1808 graphItem = m_scene->addRect(widthItem * posx, -yPlus - qAbs(val), widthItem, qAbs(val), outlinePen, brush); 1809 yPlus += qAbs(val); 1810 if (yPlus > ymax) { 1811 ymax = yPlus; 1812 } 1813 } else { 1814 graphItem = m_scene->addRect(widthItem * posx, -yMoins, widthItem, -val, outlinePen, brush); 1815 yMoins += val; 1816 if (yMoins < ymin) { 1817 ymin = yMoins; 1818 } 1819 } 1820 if (graphItem != nullptr) { 1821 graphItem->setZValue(5 + nbColumns - j); 1822 } 1823 } 1824 if (mode == STACKAREA) { 1825 // STACKAREA 1826 // Empty cells are ignored 1827 if (j == 1) { 1828 x0 = widthItem * (j - 1) * (nbRealRows + 1); 1829 } 1830 if (j == nbColumns - m_nbVirtualColumns - 1) { 1831 x1 = widthItem * (j - 1) * (nbRealRows + 1); 1832 } 1833 1834 if (!valstring.isEmpty()) { 1835 if (jprevious == -1) { 1836 jprevious = j; 1837 } 1838 QTableWidgetItem* tableItem2 = ui.kTable->item(xx, jprevious); 1839 if (tableItem2 != nullptr) { 1840 QString val2string = tableItem->text(); 1841 if (!val2string.isEmpty()) { 1842 double val2 = tableItem2->data(DATA_VALUE).toDouble(); 1843 if (j == jprevious) { 1844 val2 = 0; 1845 } 1846 if (inPositive) { 1847 val2 = qAbs(val2); 1848 } 1849 1850 if (val > EPSILON || (qAbs(val) <= EPSILON && val2 >= EPSILON)) { 1851 double xp = widthItem * (jprevious - 1) * (nbRealRows + 1) - (jprevious == j ? widthItem / 20.0 : 0); 1852 double xn = widthItem * (j - 1) * (nbRealRows + 1); 1853 double yp = (previousStackedPlus.contains(jprevious) ? previousStackedPlus[jprevious] : -val2); 1854 double yn = previousStackedPlus[j] - val; 1855 1856 QPolygonF polygon; 1857 polygon << QPointF(xp, yp + val2) << QPointF(xp, yp) 1858 << QPointF(xn, yn) << QPointF(xn, previousStackedPlus[j]); 1859 graphItem = m_scene->addPolygon(polygon, outlinePen, brush); 1860 previousStackedPlus[j] = yn; 1861 if (-yn > ymax) { 1862 ymax = -yn; 1863 } 1864 } else { 1865 double xp = widthItem * (jprevious - 1) * (nbRealRows + 1) - (jprevious == j ? widthItem / 20.0 : 0); 1866 double xn = widthItem * (j - 1) * (nbRealRows + 1); 1867 double yp = (previousStackedMoins.contains(jprevious) ? previousStackedMoins[jprevious] : -val2); 1868 double yn = previousStackedMoins[j] - val; 1869 1870 QPolygonF polygon; 1871 polygon << QPointF(xp, yp + val2) << QPointF(xp, yp) 1872 << QPointF(xn, yn) << QPointF(xn, previousStackedMoins[j]); 1873 graphItem = m_scene->addPolygon(polygon, outlinePen, brush); 1874 previousStackedMoins[j] = yn; 1875 if (-yn < ymin) { 1876 ymin = -yn; 1877 } 1878 } 1879 jprevious = j; 1880 } 1881 } 1882 } 1883 } 1884 if (mode == STACKCOLUMNS) { 1885 // STACKCOLUMNS 1886 if (val >= 0) { 1887 graphItem = m_scene->addRect(widthItem * (j - 1) * (nbRealRows + 1), -previousStackedPlus[j] - qAbs(val), widthItem * (nbRealRows + 1), qAbs(val), outlinePen, brush); 1888 previousStackedPlus[j] += qAbs(val); 1889 if (previousStackedPlus[j] > ymax) { 1890 ymax = previousStackedPlus[j]; 1891 } 1892 } else { 1893 graphItem = m_scene->addRect(widthItem * (j - 1) * (nbRealRows + 1), -previousStackedMoins[j], widthItem * (nbRealRows + 1), -val, outlinePen, brush); 1894 previousStackedMoins[j] += val; 1895 if (previousStackedMoins[j] < ymin) { 1896 ymin = previousStackedMoins[j]; 1897 } 1898 } 1899 if (graphItem != nullptr) { 1900 graphItem->setZValue(5 + nbRows - posx); 1901 } 1902 } else if (mode == HISTOGRAM) { 1903 // HISTOGRAM 1904 if (j == 1) { 1905 x0 = widthItem * ((j - 1) * (nbRealRows + 1) + posx) + widthItem; 1906 } 1907 if (j == nbColumns - m_nbVirtualColumns - 1) { 1908 x1 = widthItem * ((j - 1) * (nbRealRows + 1) + posx) + widthItem; 1909 } 1910 graphItem = m_scene->addRect(widthItem * ((j - 1) * (nbRealRows + 1) + posx) + widthItem / 2.0, (val < 0 ? -yorigin : -val), widthItem, (val < 0 ? yorigin - val : -yorigin + val), outlinePen, brush); 1911 if (val > ymax) { 1912 ymax = val; 1913 } 1914 if (val < ymin) { 1915 ymin = val; 1916 } 1917 } else if (mode == POINT) { 1918 // POINTS 1919 double xmin = widthItem * (j - 1) * (nbRealRows + 1) - radius; 1920 if (j == 1) { 1921 x0 = xmin + radius; 1922 } 1923 if (j == nbColumns - m_nbVirtualColumns - 1) { 1924 x1 = xmin + radius; 1925 } 1926 graphItem = drawPoint(xmin, -val, radius, posx, brush); 1927 if (val > ymax) { 1928 ymax = val; 1929 } 1930 if (val < ymin) { 1931 ymin = val; 1932 } 1933 } else if (mode == LINE) { 1934 // LINES 1935 // Empty cells are ignored 1936 if (j == 1) { 1937 x0 = widthItem * (j - 1) * (nbRealRows + 1); 1938 } 1939 if (j == nbColumns - m_nbVirtualColumns - 1) { 1940 x1 = widthItem * (j - 1) * (nbRealRows + 1); 1941 } 1942 1943 if (!valstring.isEmpty()) { 1944 if (jprevious == -1) { 1945 jprevious = j; 1946 } 1947 1948 // Create pen 1949 color.setAlpha(255); 1950 QPen pen = QPen(color); 1951 pen.setWidthF(radius >= (1 << 15) ? 0 : radius); 1952 pen.setCapStyle(Qt::RoundCap); 1953 pen.setJoinStyle(Qt::RoundJoin); 1954 1955 QTableWidgetItem* tableItem2 = ui.kTable->item(xx, jprevious); 1956 if (tableItem2 != nullptr) { 1957 QString val2string = tableItem->text(); 1958 if (!val2string.isEmpty()) { 1959 double val2 = tableItem2->data(DATA_VALUE).toDouble(); 1960 if (inPositive) { 1961 val2 = qAbs(val2); 1962 } 1963 1964 QGraphicsLineItem* line = m_scene->addLine(widthItem * (jprevious - 1) * (nbRealRows + 1) - (jprevious == j ? widthItem / 20.0 : 0), -val2, widthItem * (j - 1) * (nbRealRows + 1), -val, pen); 1965 line->setZValue(10); 1966 if (isShadowVisible()) { 1967 auto line_shadow = new QGraphicsDropShadowEffect(); 1968 line_shadow->setOffset(3); 1969 line->setGraphicsEffect(line_shadow); 1970 } 1971 graphItem = drawPoint(widthItem * (j - 1) * (nbRealRows + 1) - radius * 0.7, -val, radius * 0.7, posx % 5, brush); 1972 if (isShadowVisible()) { 1973 auto line_shadow = new QGraphicsDropShadowEffect(); 1974 line_shadow->setOffset(3); 1975 line->setGraphicsEffect(line_shadow); 1976 } 1977 graphItem->setZValue(20); 1978 if (val > ymax) { 1979 ymax = val; 1980 } 1981 if (val < ymin) { 1982 ymin = val; 1983 } 1984 jprevious = j; 1985 } 1986 } 1987 } 1988 } else if (mode == BUBBLE) { 1989 // BUBBLE 1990 ymin = 0; 1991 ymax = ystep * nbRealRows; 1992 1993 radius = sqrt(qAbs(val) / maxLimit) * qMin(ystep, jstep * xstep); 1994 double xmin = widthItem * (j - 1) * (nbRealRows + 1) - radius; 1995 1996 graphItem = m_scene->addEllipse(xmin, -ystep * posx - radius, 2 * radius, 2 * radius, outlinePen, brush); 1997 if (graphItem != nullptr) { 1998 graphItem->setZValue(5 + 5 * (maxLimit - qAbs(val)) / maxLimit); 1999 } 2000 } else if (mode == PIE || mode == CONCENTRICPIE) { 2001 // PIE 2002 val = qAbs(val); 2003 2004 // Compute absolute sum of the column 2005 double previousSum = 0; 2006 for (int x2 = 0; x2 < nbRows; ++x2) { 2007 if (!m_sumRows[x2 + 1]) { 2008 QTableWidgetItem* tabItem = ui.kTable->item(x2, j); 2009 if (tabItem != nullptr) { 2010 double absVal = qAbs(tabItem->data(DATA_VALUE).toDouble()); 2011 if (x2 < xx) { 2012 previousSum += absVal; 2013 } 2014 } 2015 } 2016 } 2017 2018 if (maxLimit != 0.0) { 2019 int nbvals = xname.split(OBJECTSEPARATOR).count(); 2020 double step = 0; 2021 double p = 0; 2022 if (mode == CONCENTRICPIE) { 2023 step = 100.0 / static_cast<double>(nbLevel + 1); 2024 p = step * (nbLevel + 1 - nbvals); 2025 } 2026 2027 QPainterPath path; 2028 path.moveTo(BOX_SIZE * ((j - 1) % nbColInMode2) + BOX_SIZE / 2.0, BOX_SIZE * floor((j - 1) / nbColInMode2) + BOX_SIZE / 2.0); 2029 path.arcTo(BOX_SIZE * ((j - 1) % nbColInMode2) + BOX_MARGIN + p / 2.0, BOX_SIZE * floor((j - 1) / nbColInMode2) + BOX_MARGIN + p / 2.0, BOX_SIZE - 2 * BOX_MARGIN - p, BOX_SIZE - 2 * BOX_MARGIN - p, 90.0 - 360.0 * previousSum / maxLimit, -360.0 * val / maxLimit); 2030 path.closeSubpath(); 2031 if (mode == CONCENTRICPIE && nbvals <= nbLevel + 1 && nbvals > 1) { 2032 p = step * (nbLevel + 1 - nbvals + 1); 2033 QPainterPath path2; 2034 path2.addEllipse(BOX_SIZE * ((j - 1) % nbColInMode2) + BOX_MARGIN + p / 2.0, 2035 BOX_SIZE * floor((j - 1) / nbColInMode2) + BOX_MARGIN + p / 2.0, 2036 BOX_SIZE - 2 * BOX_MARGIN - p, 2037 BOX_SIZE - 2 * BOX_MARGIN - p); 2038 path -= path2; 2039 } 2040 graphItem = m_scene->addPath(path, outlinePen, brush); 2041 } 2042 } else if (mode == TREEMAP) { 2043 // TREEMAP 2044 QString id = SKGServices::intToString(xx) % QStringLiteral("-") % SKGServices::intToString(j); 2045 SKGTreeMap tm = treemap[id]; 2046 graphItem = m_scene->addRect(tm.getX(), tm.getY(), tm.getW(), tm.getH(), outlinePen, brush); 2047 } 2048 2049 // Compute pareto points 2050 if (mode == POINT || mode == LINE) { 2051 paretoPoints[widthItem * (j - 1) * (nbRealRows + 1)] = val; 2052 } else if (mode == HISTOGRAM) { 2053 paretoPoints[widthItem * ((j - 1) * (nbRealRows + 1) + posx) + widthItem] = val; 2054 } 2055 2056 if (graphItem != nullptr) { 2057 if (graphItem->zValue() == 0) { 2058 graphItem->setZValue(5); 2059 } 2060 graphItem->setToolTip(tableItem->toolTip()); 2061 bool isSelect = (isSelectable() && ((tableItem->flags() & Qt::ItemIsEnabled) != 0u)); 2062 graphItem->setFlag(QGraphicsItem::ItemIsSelectable, isSelect); 2063 if (isSelect) { 2064 graphItem->setCursor(Qt::PointingHandCursor); 2065 } 2066 graphItem->setData(1, xx); 2067 graphItem->setData(2, j); 2068 graphItem->setData(DATA_COLOR_H, color_h); 2069 graphItem->setData(DATA_COLOR_S, color_s); 2070 graphItem->setData(DATA_COLOR_V, color_v); 2071 graphItem->setData(DATA_Z_VALUE, graphItem->zValue()); 2072 2073 m_mapItemGraphic[tableItem] = graphItem; 2074 } 2075 } 2076 } else { 2077 SKGTRACE << "WARNING: cell " << posx << "," << j << " null" << SKGENDL; 2078 } 2079 } 2080 2081 ++posx; 2082 } 2083 } 2084 2085 // Draw axis 2086 double scaleText = 0.0; 2087 auto nbRowInMode2 = static_cast<int>(((nbColumns - 1) / nbColInMode2)); 2088 if (nbRealRows != 0) { 2089 if (mode == TREEMAP) { 2090 // TREEMAP 2091 } else if (mode == PIE || mode == CONCENTRICPIE) { 2092 // PIE 2093 if (nbRowInMode2 * nbColInMode2 < nbColumns - 1) { 2094 ++nbRowInMode2; 2095 } 2096 for (int i = 0; i <= nbColInMode2; ++i) { 2097 QGraphicsLineItem* item = m_scene->addLine(BOX_SIZE * i, 0, BOX_SIZE * i, BOX_SIZE * nbRowInMode2); 2098 item->setPen(axisPen); 2099 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2100 } 2101 for (int i = 0; i <= nbRowInMode2; ++i) { 2102 QGraphicsLineItem* item = m_scene->addLine(0, BOX_SIZE * i, BOX_SIZE * nbColInMode2, BOX_SIZE * i); 2103 item->setPen(axisPen); 2104 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2105 } 2106 2107 for (int j = 1; j < nbColumns; ++j) { 2108 // Get column name 2109 QString yname = ui.kTable->horizontalHeaderItem(j)->text(); 2110 2111 QGraphicsTextItem* textItem = m_scene->addText(yname); 2112 textItem->setDefaultTextColor(m_textColor); 2113 textItem->setPos(BOX_SIZE * ((j - 1) % nbColInMode2) + 2, BOX_SIZE * floor((j - 1) / nbColInMode2) + 2); 2114 textItem->setScale(.5); 2115 textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2116 textItem->setZValue(20); 2117 } 2118 } else { 2119 // STACK & HISTOGRAMM 2120 QGraphicsLineItem* item; 2121 2122 // Compute scale text 2123 if (mode != BUBBLE) { 2124 QGraphicsTextItem* t = m_scene->addText(SKGServices::toCurrencyString(-qMax(qAbs(ymax), qAbs(ymin)), m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); 2125 QRectF bRect = t->boundingRect(); 2126 scaleText = 0.8 * qMin(marginLeft / bRect.width(), qMin(marginLeft / bRect.height(), ystep / bRect.height())); 2127 m_scene->removeItem(t); 2128 } else { 2129 maxLimit = ystep * nbRealRows; 2130 } 2131 2132 if (ystep > 0) { 2133 // Draw 2134 int i = 1; 2135 for (double posy = yorigin + ystep ; posy <= maxLimit ; posy = posy + ystep) { 2136 item = m_scene->addLine(0, -posy, maxX, -posy); 2137 item->setPen(gridPen); 2138 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2139 item->setZValue(1); 2140 QGraphicsTextItem* textItem; 2141 if (mode == BUBBLE) { 2142 auto* cel = qobject_cast<SKGColorButton*>(ui.kTable->cellWidget(i, 0)); 2143 ++i; 2144 if (cel == nullptr) { 2145 cel = qobject_cast<SKGColorButton*>(ui.kTable->cellWidget(i, 0)); 2146 ++i; 2147 } 2148 textItem = m_scene->addText(cel != nullptr ? cel->text() : QString()); 2149 } else { 2150 textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); 2151 } 2152 2153 QRectF textRect = textItem->boundingRect(); 2154 if (scaleText == 0) { 2155 scaleText = 0.8 * qMin(marginLeft / textRect.width(), qMin(marginLeft / textRect.height(), ystep / textRect.height())); 2156 } 2157 textItem->setDefaultTextColor(m_textColor); 2158 textItem->setScale(scaleText); 2159 textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2160 textItem->setZValue(20); 2161 double delta = scaleText * textRect.height() / 2.0; 2162 2163 textItem->setPos(-marginLeft, -posy - delta); 2164 } 2165 2166 i = 0; 2167 for (double posy = yorigin ; (posy == yorigin) || posy >= minLimit; posy = posy - ystep) { 2168 item = m_scene->addLine(0, -posy, maxX, -posy); 2169 item->setPen(gridPen); 2170 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2171 item->setZValue(1); 2172 2173 QGraphicsTextItem* textItem; 2174 if (mode == BUBBLE) { 2175 auto* cel = qobject_cast<SKGColorButton*>(ui.kTable->cellWidget(i, 0)); 2176 textItem = m_scene->addText(cel != nullptr ? cel->text() : QString()); 2177 } else { 2178 textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); 2179 } 2180 2181 QRectF textRect = textItem->boundingRect(); 2182 if (scaleText == 0) { 2183 scaleText = 0.8 * qMin(marginLeft / textRect.width(), qMin(marginLeft / textRect.height(), ystep / textRect.height())); 2184 } 2185 textItem->setDefaultTextColor(m_textColor); 2186 textItem->setScale(scaleText); 2187 textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2188 textItem->setZValue(20); 2189 double delta = scaleText * textRect.height() / 2.0; 2190 2191 textItem->setPos(-marginLeft, -posy - delta); 2192 } 2193 } 2194 2195 if (mode == HISTOGRAM || mode == POINT || mode == LINE || mode == BUBBLE || mode == STACKAREA || mode == STACKCOLUMNS) { 2196 // Line y 2197 for (int j = 1; j < nbColumns; j += jstep) { 2198 QString yname = ui.kTable->horizontalHeaderItem(j)->text(); 2199 double posx2 = xstep + (j - 2) * xstep; 2200 2201 QGraphicsTextItem* textItem = m_scene->addText(yname); 2202 2203 QRectF textRect = textItem->boundingRect(); 2204 if (scaleText == 0) { 2205 scaleText = 0.8 * qMin(marginLeft / textRect.width(), qMin(marginLeft / textRect.height(), ystep / textRect.height())); 2206 } 2207 textItem->setDefaultTextColor(m_textColor); 2208 textItem->setScale(scaleText); 2209 textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2210 textItem->setZValue(20); 2211 double delta = scaleText * textRect.height() / 2.0; 2212 textItem->setRotation(90); 2213 textItem->moveBy(textRect.height() *scaleText, 0); 2214 2215 textItem->setPos(posx2 + delta, -yorigin); 2216 2217 QGraphicsLineItem* graphicItem = m_scene->addLine(posx2, qMax(-yorigin, -minLimit), posx2, qMin(-yorigin, -maxLimit)); 2218 graphicItem->setPen(gridPen); 2219 graphicItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2220 } 2221 } 2222 2223 // Axis x 2224 if (yorigin == 0.0) { 2225 item = m_scene->addLine(0, -yorigin, maxX, -yorigin, axisPen); 2226 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2227 item->setZValue(2); 2228 } 2229 2230 // Rect 2231 { 2232 QGraphicsRectItem* graphicItem = m_scene->addRect(0, qMax(-yorigin, -minLimit), maxX, qMin(-yorigin, -maxLimit) - (qMax(-yorigin, -minLimit)), axisPen); 2233 graphicItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2234 graphicItem->setZValue(2); 2235 } 2236 } 2237 } 2238 2239 // Draw Average 2240 bool lineCondition = (mode == HISTOGRAM || mode == POINT || mode == LINE) && (scaleText != 0.0) && nbRowsDrawed == 1; 2241 int averageCol = getAverageColumnIndex(); 2242 if (m_averageVisible && lineCondition && averageCol != -1) { 2243 QTableWidgetItem* tableItem = ui.kTable->item(0, averageCol); 2244 if (tableItem != nullptr) { 2245 double posy = tableItem->data(DATA_VALUE).toDouble(); 2246 if (inPositive) { 2247 posy = qAbs(posy); 2248 } 2249 2250 QGraphicsLineItem* item = m_scene->addLine(0, -posy, maxX, -posy); 2251 dotPen.setColor(m_averageColor); 2252 item->setPen(dotPen); 2253 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2254 item->setZValue(1); 2255 item->setToolTip(tableItem->toolTip()); 2256 2257 QGraphicsTextItem* textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); 2258 QRectF textRect = textItem->boundingRect(); 2259 double delta = scaleText * textRect.height() / 2.0; 2260 textItem->setPos(maxX, -posy - delta); 2261 textItem->setDefaultTextColor(m_averageColor); 2262 textItem->setScale(scaleText); 2263 textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2264 textItem->setZValue(20); 2265 textItem->setToolTip(tableItem->toolTip()); 2266 } 2267 } 2268 2269 // Draw Min & Max limits 2270 int minCol = getMinColumnIndex(); 2271 if (lineCondition && minCol != -1) { 2272 // Min 2273 { 2274 QTableWidgetItem* tableItem = ui.kTable->item(0, minCol); 2275 if (tableItem != nullptr) { 2276 double posy = tableItem->data(DATA_VALUE).toDouble(); 2277 if (inPositive) { 2278 posy = qAbs(posy); 2279 } 2280 2281 QGraphicsLineItem* item = m_scene->addLine(0, -posy, maxX, -posy); 2282 dotPen.setColor(m_minColor); 2283 item->setPen(dotPen); 2284 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2285 item->setZValue(1); 2286 item->setToolTip(tableItem->toolTip()); 2287 2288 QGraphicsTextItem* textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); 2289 QRectF textRect = textItem->boundingRect(); 2290 double delta = scaleText * textRect.height() / 2.0; 2291 textItem->setPos(maxX, -posy - delta); 2292 textItem->setDefaultTextColor(m_minColor); 2293 textItem->setScale(scaleText); 2294 textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2295 textItem->setZValue(20); 2296 textItem->setToolTip(tableItem->toolTip()); 2297 } 2298 } 2299 2300 // Max 2301 { 2302 QTableWidgetItem* tableItem = ui.kTable->item(0, minCol + 1); 2303 if (tableItem != nullptr) { 2304 double posy = tableItem->data(DATA_VALUE).toDouble(); 2305 if (inPositive) { 2306 posy = qAbs(posy); 2307 } 2308 2309 QGraphicsLineItem* item = m_scene->addLine(0, -posy, maxX, -posy); 2310 dotPen.setColor(m_maxColor); 2311 item->setPen(dotPen); 2312 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2313 item->setZValue(1); 2314 item->setToolTip(tableItem->toolTip()); 2315 2316 QGraphicsTextItem* textItem = m_scene->addText(SKGServices::toCurrencyString(posy, m_primaryUnit.Symbol, m_decimalsVisible ? m_primaryUnit.NbDecimal : 0)); 2317 QRectF textRect = textItem->boundingRect(); 2318 double delta = scaleText * textRect.height() / 2.0; 2319 textItem->setPos(maxX, -posy - delta); 2320 textItem->setDefaultTextColor(m_maxColor); 2321 textItem->setScale(scaleText); 2322 textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2323 textItem->setZValue(20); 2324 textItem->setToolTip(tableItem->toolTip()); 2325 } 2326 } 2327 } 2328 2329 // Draw Linear Regression 2330 if (m_linearRegressionVisible && lineCondition && m_indexLinearRegression != -1) { 2331 QTableWidgetItem* tableItem = ui.kTable->item(0, m_indexLinearRegression); 2332 if (tableItem != nullptr) { 2333 QString f = tableItem->text(); 2334 2335 QScriptEngine myEngine; 2336 QString f0 = f; 2337 f0 = f0.remove(QStringLiteral("y=")); 2338 f0 = f0.replace('x', '1'); 2339 f0 = f0.replace(',', '.'); // Replace comma by a point in case of typo 2340 if (inPositive) { 2341 f0 = "Math.abs(" % f0 % ')'; 2342 } 2343 double y0 = myEngine.evaluate(f0).toNumber(); 2344 2345 QString f1 = f; 2346 f1 = f1.remove(QStringLiteral("y=")); 2347 f1 = f1.replace('x', SKGServices::intToString(nbRealColumns - 1)); 2348 f1 = f1.replace(',', '.'); // Replace comma by a point in case of typo 2349 if (inPositive) { 2350 f1 = "Math.abs(" % f1 % ')'; 2351 } 2352 double y1 = myEngine.evaluate(f1).toNumber(); 2353 2354 QGraphicsLineItem* item = m_scene->addLine(x0, -y0, x1, -y1); 2355 dotPen.setColor(m_tendencyColor); 2356 item->setPen(dotPen); 2357 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2358 item->setZValue(1); 2359 item->setToolTip(f); 2360 } 2361 } 2362 2363 // Draw pareto 2364 if (lineCondition && m_paretoVisible && !paretoPoints.isEmpty()) { 2365 // Compute the sum 2366 double sum = 0.0; 2367 QList<double> list = paretoPoints.keys(); 2368 for (auto xp1 : qAsConst(list)) { 2369 sum += paretoPoints[xp1]; 2370 } 2371 2372 // Draw the second axis 2373 for (int i = 0 ; i <= 100 ; i = i + 10) { 2374 double posy = (maxLimit - minLimit) * static_cast<double>(i) / 100.0 + minLimit; 2375 2376 QGraphicsTextItem* textItem = m_scene->addText(SKGServices::intToString(i) % "%"); 2377 QRectF textRect = textItem->boundingRect(); 2378 textItem->setDefaultTextColor(m_paretoColor); 2379 textItem->setScale(scaleText); 2380 textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2381 textItem->setZValue(19); 2382 2383 double delta = scaleText * textRect.height() / 2.0; 2384 textItem->setPos(maxX, -posy - delta); 2385 } 2386 2387 // Draw the curve 2388 double x00 = -1; 2389 double y00 = -1; 2390 double csum = 0.0; 2391 for (auto xp2 : qAsConst(list)) { 2392 csum += paretoPoints[xp2]; 2393 if (x00 != -1) { 2394 QGraphicsLineItem* item = m_scene->addLine(x00, -((maxLimit - minLimit) * y00 / sum + minLimit), xp2, -((maxLimit - minLimit) * csum / sum + minLimit)); 2395 dotPen.setColor(m_paretoColor); 2396 item->setPen(dotPen); 2397 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2398 item->setZValue(1); 2399 } 2400 x00 = xp2; 2401 y00 = csum; 2402 } 2403 } 2404 // Draw legend 2405 if (m_legendVisible) { 2406 QPointF legendPosition; 2407 double maxY; 2408 if (mode == TREEMAP) { 2409 scaleText = 0.2; 2410 margin = BOX_SIZE / 10; 2411 legendPosition = QPointF(BOX_SIZE + margin, 0); 2412 maxY = BOX_SIZE * nbRowInMode2; 2413 } else if (mode == PIE || mode == CONCENTRICPIE) { 2414 if (nbRowInMode2 * nbColInMode2 < nbColumns - 1) { 2415 ++nbRowInMode2; 2416 } 2417 2418 scaleText = 0.2; 2419 margin = BOX_SIZE / 10; 2420 legendPosition = QPointF(BOX_SIZE * nbColInMode2 + margin, 0); 2421 maxY = BOX_SIZE * nbRowInMode2; 2422 } else { 2423 legendPosition = QPointF(maxX + margin, qMin(-yorigin, -maxLimit) - margin / 6); 2424 maxY = qMax(-yorigin, -minLimit); 2425 } 2426 addLegend(legendPosition, margin / 4, scaleText, maxY); 2427 } 2428 } 2429 { 2430 SKGTRACEINFUNC(10) 2431 m_scene->setSceneRect(QRectF()); 2432 ui.graphicView->setScene(m_scene); 2433 ui.graphicView->show(); 2434 if (m_tableVisible) { 2435 ui.kTable->show(); 2436 } 2437 ui.graphicView->initializeZoom(); 2438 2439 // Add selection event on scene 2440 connect(m_scene, &SKGGraphicsScene::selectionChanged, this, &SKGTableWithGraph::onSelectionChangedInGraph, Qt::QueuedConnection); 2441 connect(m_scene, &SKGGraphicsScene::doubleClicked, this, &SKGTableWithGraph::onDoubleClickGraph, Qt::QueuedConnection); 2442 } 2443 QApplication::restoreOverrideCursor(); 2444 } 2445 2446 int SKGTableWithGraph::getNbColumns(bool iWithComputed) const 2447 { 2448 int nbColumns = ui.kTable->columnCount(); 2449 if (!iWithComputed) { 2450 if (m_indexMin != -1) { 2451 nbColumns -= 2; 2452 } 2453 if (m_indexAverage != -1) { 2454 --nbColumns; 2455 } 2456 if (m_indexSum != -1) { 2457 --nbColumns; 2458 } 2459 if (m_indexLinearRegression != -1) { 2460 --nbColumns; 2461 } 2462 } 2463 return nbColumns; 2464 } 2465 2466 int SKGTableWithGraph::getAverageColumnIndex() const 2467 { 2468 return m_indexAverage; 2469 } 2470 2471 int SKGTableWithGraph::getMinColumnIndex() const 2472 { 2473 return m_indexMin; 2474 } 2475 2476 double SKGTableWithGraph::computeStepSize(double iRange, double iTargetSteps) 2477 { 2478 // Calculate an initial guess at step size 2479 double tempStep = iRange / iTargetSteps; 2480 // Get the magnitude of the step size 2481 double mag = floor(log10(tempStep)); 2482 double magPow = pow(static_cast<double>(10.0), mag); 2483 // Calculate most significant digit of the new step size 2484 double magMsd = static_cast<int>(tempStep / magPow + .5); 2485 // promote the MSD to either 1, 2, or 5 2486 if (magMsd > 5.0) { 2487 magMsd = 10.0; 2488 } else if (magMsd > 2.0) { 2489 magMsd = 5.0; 2490 } else if (magMsd > 1.0) { 2491 magMsd = 2.0; 2492 } 2493 return magMsd * magPow; 2494 } 2495 2496 void SKGTableWithGraph::addLegend(const QPointF iPosition, double iSize, double iScaleText, double iMaxY) 2497 { 2498 SKGTRACEINFUNC(10) 2499 2500 QPen outlinePen(m_outlineColor); 2501 outlinePen.setWidthF((iScaleText * 4.0 / 500.0) >= (1 << 15) ? 0 : (iScaleText * 4.0 / 500.0)); 2502 2503 if (m_scene != nullptr) { 2504 GraphType mode = getGraphType(); 2505 int nbRows = ui.kTable->rowCount(); 2506 int nbRealRows = nbRows; 2507 for (int posy = 0; posy < nbRows; ++posy) { 2508 if (m_sumRows.at(posy + 1)) { 2509 --nbRealRows; 2510 } 2511 } 2512 int nbColumns = getNbColumns(false); 2513 if (nbColumns > 1) { 2514 double margin = 1.2; 2515 double currentYPos = iPosition.y(); 2516 double currentXPos = iPosition.x(); 2517 double currentXMaxSize = 0; 2518 for (int i = 0; i < nbRows; ++i) { 2519 auto* btn = qobject_cast<SKGColorButton*>(ui.kTable->cellWidget(i, 0)); 2520 if (btn != nullptr) { 2521 // Get title 2522 QString title = btn->text(); 2523 2524 // Build brush 2525 QColor color1; 2526 QTableWidgetItem* it = ui.kTable->item(i, 1); 2527 if (it != nullptr) { 2528 QGraphicsItem* graphicItem = m_mapItemGraphic.value(it); 2529 if (graphicItem != nullptr) { 2530 // Draw box 2531 color1 = QColor::fromHsv(graphicItem->data(DATA_COLOR_H).toInt(), 2532 graphicItem->data(DATA_COLOR_S).toInt(), 2533 graphicItem->data(DATA_COLOR_V).toInt()); 2534 color1.setAlpha(ALPHA); 2535 } 2536 } 2537 QColor color2; 2538 it = ui.kTable->item(i, nbColumns - 1 - m_nbVirtualColumns); 2539 if (it != nullptr) { 2540 QGraphicsItem* graphicItem = m_mapItemGraphic.value(it); 2541 if (graphicItem != nullptr) { 2542 // Draw box 2543 color2 = QColor::fromHsv(graphicItem->data(DATA_COLOR_H).toInt(), 2544 graphicItem->data(DATA_COLOR_S).toInt(), 2545 graphicItem->data(DATA_COLOR_V).toInt()); 2546 color2.setAlpha(ALPHA); 2547 } 2548 } 2549 2550 QLinearGradient grandient(currentXPos, currentYPos, currentXPos + 2.0 * iSize, currentYPos); 2551 grandient.setColorAt(0, color1); 2552 grandient.setColorAt(1, color2); 2553 2554 // Draw legend item 2555 QGraphicsItem* item = nullptr; 2556 if (mode == POINT || mode == LINE) { 2557 item = drawPoint(currentXPos, currentYPos + iSize / 2.0, iSize / 2.0, mode == POINT ? i : (i % 5), QBrush(grandient)); 2558 } else if (mode == BUBBLE) { 2559 item = m_scene->addEllipse(currentXPos, currentYPos, iSize, iSize, outlinePen, QBrush(grandient)); 2560 } else if (mode == PIE || mode == CONCENTRICPIE) { 2561 QPainterPath path; 2562 path.moveTo(currentXPos + iSize / 2.0, currentYPos + iSize / 2.0); 2563 path.arcTo(currentXPos, currentYPos, iSize, iSize, 45, 270); 2564 path.closeSubpath(); 2565 if (mode == CONCENTRICPIE) { 2566 QPainterPath path2; 2567 double p = iSize / 3.0; 2568 path2.addEllipse(currentXPos + p, currentYPos + p, iSize - 2.0 * p, iSize - 2.0 * p); 2569 path -= path2; 2570 } 2571 item = m_scene->addPath(path, outlinePen, QBrush(grandient)); 2572 } else { 2573 item = m_scene->addRect(currentXPos, currentYPos, iSize, iSize, outlinePen, QBrush(grandient)); 2574 } 2575 if (item != nullptr) { 2576 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2577 item->setToolTip(title); 2578 2579 // Set shadow 2580 if (isShadowVisible()) { 2581 auto ellipse_shadow = new QGraphicsDropShadowEffect(); 2582 ellipse_shadow->setOffset(3); 2583 item->setGraphicsEffect(ellipse_shadow); 2584 } 2585 } 2586 2587 // Draw text 2588 QGraphicsTextItem* textItem = m_scene->addText(title); 2589 textItem->setDefaultTextColor(m_textColor); 2590 textItem->setScale(iScaleText); 2591 textItem->setPos(currentXPos + margin * iSize, currentYPos + iSize / 2.0 - textItem->boundingRect().height()*iScaleText / 2.0); 2592 textItem->setFlag(QGraphicsItem::ItemIsSelectable, false); 2593 2594 // Compute next position 2595 currentYPos += margin * iSize; 2596 QRectF textRect = textItem->boundingRect(); 2597 currentXMaxSize = qMax(currentXMaxSize, static_cast<double>(iScaleText * textRect.width())); 2598 if (currentYPos > iMaxY) { 2599 currentYPos = iPosition.y(); 2600 currentXPos += currentXMaxSize + 2 * margin * iSize; 2601 currentXMaxSize = 0; 2602 } 2603 } 2604 } 2605 } 2606 } 2607 } 2608 2609 void SKGTableWithGraph::addArrow(const QPointF iPeak, double iSize, double iArrowAngle, double iDegree) 2610 { 2611 if (m_scene != nullptr) { 2612 QPolygonF pol; 2613 double radian = 3.14 * iArrowAngle / 360.0; 2614 pol << QPointF(0, 0) << QPointF(iSize * cos(radian), iSize * sin(radian)) << QPointF(iSize * cos(radian), -iSize * sin(radian)) << QPointF(0, 0); 2615 QGraphicsPolygonItem* item = m_scene->addPolygon(pol, QPen(m_axisColor, iSize / 20.0), QBrush(m_axisColor)); 2616 item->setRotation(iDegree); 2617 item->moveBy(iPeak.x(), iPeak.y()); 2618 item->setFlag(QGraphicsItem::ItemIsSelectable, false); 2619 item->setZValue(2); 2620 } 2621 } 2622 2623 QGraphicsItem* SKGTableWithGraph::drawPoint(qreal iX, qreal iY, qreal iRadius, int iMode, const QBrush& iBrush) 2624 { 2625 QGraphicsItem* graphItem = nullptr; 2626 int nbMode = 13; 2627 if (m_scene != nullptr) { 2628 QPen outlinePen(m_outlineColor); 2629 outlinePen.setWidthF((iRadius / 10) >= (1 << 15) ? 0.0 : iRadius / 10); 2630 2631 QPen pen; 2632 if ((iMode % nbMode) <= 4) { 2633 pen = QPen(iBrush.color()); 2634 const QGradient* grad = iBrush.gradient(); 2635 if (grad != nullptr) { 2636 auto stops = grad->stops(); 2637 auto stop = stops.last(); 2638 pen = QPen(stop.second); 2639 } 2640 pen.setWidthF((iRadius / 10) >= (1 << 15) ? 00.0 : iRadius / 10); 2641 } 2642 2643 switch (iMode % nbMode) { 2644 case 0: { 2645 graphItem = m_scene->addEllipse(iX, iY - iRadius, 2 * iRadius, 2 * iRadius, pen, m_WhiteColor); 2646 break; 2647 } 2648 case 1: { 2649 graphItem = m_scene->addRect(iX, iY - iRadius, 2 * iRadius, 2 * iRadius, pen, m_WhiteColor); 2650 break; 2651 } 2652 case 2: { 2653 QPolygonF polygon; 2654 polygon << QPointF(iX + iRadius, iY + iRadius) << QPointF(iX + 2 * iRadius, iY - iRadius) 2655 << QPointF(iX, iY - iRadius) << QPointF(iX + iRadius, iY + iRadius); 2656 graphItem = m_scene->addPolygon(polygon, pen, m_WhiteColor); 2657 break; 2658 } 2659 case 3: { 2660 QPolygonF polygon; 2661 polygon << QPointF(iX, iY) << QPointF(iX + iRadius, iY + iRadius) 2662 << QPointF(iX + 2 * iRadius, iY) << QPointF(iX + iRadius, iY - iRadius) << QPointF(iX, iY); 2663 graphItem = m_scene->addPolygon(polygon, pen, m_WhiteColor); 2664 break; 2665 } 2666 case 4: { 2667 QPolygonF polygon; 2668 polygon << QPointF(iX + iRadius, iY - iRadius) << QPointF(iX + 2 * iRadius, iY + iRadius) 2669 << QPointF(iX, iY + iRadius) << QPointF(iX + iRadius, iY - iRadius); 2670 graphItem = m_scene->addPolygon(polygon, pen, m_WhiteColor); 2671 break; 2672 } 2673 2674 2675 2676 case 5: { 2677 graphItem = m_scene->addEllipse(iX, iY - iRadius, 2 * iRadius, 2 * iRadius, outlinePen, iBrush); 2678 break; 2679 } 2680 case 6: { 2681 graphItem = m_scene->addRect(iX, iY - iRadius, 2 * iRadius, 2 * iRadius, outlinePen, iBrush); 2682 break; 2683 } 2684 case 7: { 2685 QPolygonF polygon; 2686 polygon << QPointF(iX, iY - iRadius) << QPointF(iX + 2 * iRadius, iY + iRadius) 2687 << QPointF(iX + 2 * iRadius, iY - iRadius) << QPointF(iX, iY + iRadius); 2688 graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); 2689 break; 2690 } 2691 case 8: { 2692 QPolygonF polygon; 2693 polygon << QPointF(iX + iRadius, iY + iRadius) << QPointF(iX + 2 * iRadius, iY - iRadius) 2694 << QPointF(iX, iY - iRadius) << QPointF(iX + iRadius, iY + iRadius); 2695 graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); 2696 break; 2697 } 2698 case 9: { 2699 QPolygonF polygon; 2700 polygon << QPointF(iX, iY) << QPointF(iX + iRadius, iY + iRadius) 2701 << QPointF(iX + 2 * iRadius, iY) << QPointF(iX + iRadius, iY - iRadius) << QPointF(iX, iY); 2702 graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); 2703 break; 2704 } 2705 case 10: { 2706 QPolygonF polygon; 2707 polygon << QPointF(iX, iY - iRadius) << QPointF(iX + 2 * iRadius, iY - iRadius) 2708 << QPointF(iX, iY + iRadius) << QPointF(iX + 2 * iRadius, iY + iRadius); 2709 graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); 2710 break; 2711 } 2712 case 11: { 2713 QPolygonF polygon; 2714 polygon << QPointF(iX + iRadius, iY - iRadius) << QPointF(iX + 2 * iRadius, iY + iRadius) 2715 << QPointF(iX, iY + iRadius) << QPointF(iX + iRadius, iY - iRadius); 2716 graphItem = m_scene->addPolygon(polygon, outlinePen, iBrush); 2717 break; 2718 } 2719 case 12: 2720 default: { 2721 QPainterPath path; 2722 path.addEllipse(iX, iY - iRadius, 2 * iRadius, 2 * iRadius); 2723 path.closeSubpath(); 2724 2725 QPainterPath path2; 2726 path2.addEllipse(iX + iRadius / 2.0, iY - iRadius / 2.0, iRadius, iRadius); 2727 path -= path2; 2728 graphItem = m_scene->addPath(path, outlinePen, iBrush); 2729 break; 2730 } 2731 } 2732 } 2733 if ((graphItem != nullptr) && (iMode % nbMode) <= 4) { 2734 graphItem->setData(DATA_MODE, 1); 2735 } 2736 return graphItem; 2737 } 2738 2739 SKGError SKGTableWithGraph::exportInFile(const QString& iFileName) 2740 { 2741 SKGError err; 2742 _SKGTRACEINFUNCRC(10, err) 2743 QString lastCodecUsed = QTextCodec::codecForLocale()->name(); 2744 2745 QString extension = QFileInfo(iFileName).suffix().toUpper(); 2746 if (extension == QStringLiteral("CSV")) { 2747 // Write file 2748 QSaveFile file(iFileName); 2749 if (!file.open(QIODevice::WriteOnly)) { 2750 err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", iFileName)); 2751 } else { 2752 QTextStream out(&file); 2753 out.setCodec(lastCodecUsed.toLatin1().constData()); 2754 QStringList dump = SKGServices::tableToDump(getTable(), SKGServices::DUMP_CSV); 2755 int nbl = dump.count(); 2756 for (int i = 0; i < nbl; ++i) { 2757 out << dump.at(i) << SKGENDL; 2758 } 2759 2760 // Close file 2761 file.commit(); 2762 } 2763 } else { 2764 // Write file 2765 QSaveFile file(iFileName); 2766 if (!file.open(QIODevice::WriteOnly)) { 2767 err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", iFileName)); 2768 } else { 2769 QTextStream out(&file); 2770 out.setCodec(lastCodecUsed.toLatin1().constData()); 2771 QStringList dump = SKGServices::tableToDump(getTable(), SKGServices::DUMP_TEXT); 2772 int nbl = dump.count(); 2773 for (int i = 0; i < nbl; ++i) { 2774 out << dump.at(i) << SKGENDL; 2775 } 2776 2777 // Close file 2778 file.commit(); 2779 } 2780 } 2781 return err; 2782 } 2783 2784 void SKGTableWithGraph::onExport() 2785 { 2786 _SKGTRACEINFUNC(10) 2787 SKGError err; 2788 QString fileName = SKGMainPanel::getSaveFileName(QStringLiteral("kfiledialog:///IMPEXP"), QStringLiteral("text/csv text/plain"), this); 2789 if (!fileName.isEmpty()) { 2790 err = exportInFile(fileName); 2791 2792 SKGMainPanel::displayErrorMessage(err); 2793 QDesktopServices::openUrl(QUrl(fileName)); 2794 } 2795 } 2796 2797 void SKGTableWithGraph::setGraphType(SKGTableWithGraph::GraphType iType) 2798 { 2799 if (m_displayMode != nullptr) { 2800 auto newIndex = m_displayMode->findData(static_cast<int>(iType)); 2801 if (m_displayMode->currentIndex() != newIndex) { 2802 m_displayMode->setCurrentIndex(newIndex); 2803 Q_EMIT modified(); 2804 } 2805 } 2806 } 2807 2808 SKGTableWithGraph::GraphType SKGTableWithGraph::getGraphType() const 2809 { 2810 GraphType mode = static_cast<GraphType>(m_displayMode->itemData(m_displayMode->currentIndex()).toInt()); 2811 return mode; 2812 } 2813 2814 void SKGTableWithGraph::setSelectable(bool iSelectable) 2815 { 2816 if (m_selectable != iSelectable) { 2817 m_selectable = iSelectable; 2818 Q_EMIT modified(); 2819 } 2820 } 2821 2822 bool SKGTableWithGraph::isSelectable() const 2823 { 2824 return m_selectable; 2825 } 2826 2827 void SKGTableWithGraph::setShadowVisible(bool iShadow) 2828 { 2829 if (m_shadow != iShadow) { 2830 m_shadow = iShadow; 2831 Q_EMIT modified(); 2832 } 2833 } 2834 2835 bool SKGTableWithGraph::isShadowVisible() const 2836 { 2837 return m_shadow; 2838 } 2839 2840