ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvColorScaleEditorDlg.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include "ui_colorScaleEditorDlg.h"
11 
12 // local
14 #include "ecvPersistentSettings.h"
15 
16 // common
17 #include <ecvMainAppInterface.h>
18 #include <ecvQtHelpers.h>
19 
20 // CV_DB_LIB
21 #include <ecvColorScalesManager.h>
22 #include <ecvFileUtils.h>
23 #include <ecvPointCloud.h>
24 #include <ecvScalarField.h>
25 
26 // Qt
27 #include <QColorDialog>
28 #include <QFileDialog>
29 #include <QHBoxLayout>
30 #include <QInputDialog>
31 
32 // Qt5/Qt6 Compatibility
33 #include <QtCompat.h>
34 
35 #include <QMessageBox>
36 #include <QPlainTextEdit>
37 #include <QSettings>
38 #include <QUuid>
39 
40 // System
41 #include <cassert>
42 
43 static char s_defaultEmptyCustomListText[] = "(auto)";
44 
46  ccColorScalesManager* manager,
47  ecvMainAppInterface* mainApp,
48  ccColorScale::Shared currentScale /*=0*/,
49  QWidget* parent /*=0*/)
50  : QDialog(parent),
51  m_manager(manager),
52  m_colorScale(currentScale),
53  m_scaleWidget(new ccColorScaleEditorWidget(this, Qt::Horizontal)),
54  m_associatedSF(nullptr),
55  m_modified(false),
56  m_minAbsoluteVal(0.0),
57  m_maxAbsoluteVal(1.0),
58  m_mainApp(mainApp),
59  m_ui(new Ui::ColorScaleEditorDlg) {
60  assert(m_manager);
61 
62  m_ui->setupUi(this);
63 
64  m_ui->colorScaleEditorFrame->setLayout(new QHBoxLayout());
65  m_ui->colorScaleEditorFrame->layout()->setContentsMargins(0, 0, 0, 0);
66  m_ui->colorScaleEditorFrame->layout()->addWidget(m_scaleWidget);
67 
68  // main combo box
69  connect(m_ui->rampComboBox, SIGNAL(activated(int)), this,
70  SLOT(colorScaleChanged(int)));
71 
72  // import/export buttons
73  connect(m_ui->exportToolButton, SIGNAL(clicked()), this,
74  SLOT(exportCurrentScale()));
75  connect(m_ui->importToolButton, SIGNAL(clicked()), this,
76  SLOT(importScale()));
77 
78  // upper buttons
79  connect(m_ui->renameToolButton, SIGNAL(clicked()), this,
80  SLOT(renameCurrentScale()));
81  connect(m_ui->saveToolButton, SIGNAL(clicked()), this,
82  SLOT(saveCurrentScale()));
83  connect(m_ui->deleteToolButton, SIGNAL(clicked()), this,
84  SLOT(deleteCurrentScale()));
85  connect(m_ui->copyToolButton, SIGNAL(clicked()), this,
86  SLOT(copyCurrentScale()));
87  connect(m_ui->newToolButton, SIGNAL(clicked()), this,
88  SLOT(createNewScale()));
89  connect(m_ui->scaleModeComboBox, SIGNAL(activated(int)), this,
90  SLOT(relativeModeChanged(int)));
91 
92  // scale widget
93  connect(m_scaleWidget, SIGNAL(stepSelected(int)), this,
94  SLOT(onStepSelected(int)));
95  connect(m_scaleWidget, SIGNAL(stepModified(int)), this,
96  SLOT(onStepModified(int)));
97 
98  // slider editor
99  connect(m_ui->deleteSliderToolButton, SIGNAL(clicked()), this,
100  SLOT(deletecSelectedStep()));
101  connect(m_ui->colorToolButton, SIGNAL(clicked()), this,
102  SLOT(changeSelectedStepColor()));
103  connect(m_ui->valueDoubleSpinBox, SIGNAL(valueChanged(double)), this,
104  SLOT(changeSelectedStepValue(double)));
105 
106  // labels list widget
107  connect(m_ui->customLabelsGroupBox, SIGNAL(toggled(bool)), this,
108  SLOT(toggleCustomLabelsList(bool)));
109  connect(m_ui->customLabelsPlainTextEdit, SIGNAL(textChanged()), this,
110  SLOT(onCustomLabelsListChanged()));
111 
112  // apply button
113  connect(m_ui->applyPushButton, SIGNAL(clicked()), this, SLOT(onApply()));
114  // close button
115  connect(m_ui->closePushButton, SIGNAL(clicked()), this, SLOT(onClose()));
116 
117  // populate main combox box with all known scales
119 
120  if (!m_colorScale)
122 
124 }
125 
127  m_associatedSF = sf;
128  if (m_associatedSF &&
129  (!m_colorScale ||
130  m_colorScale->isRelative())) // we only update those values if the
131  // current scale is not absolute!
132  {
135  }
136 }
137 
139  if (!m_manager) {
140  assert(false);
141  return;
142  }
143 
144  m_ui->rampComboBox->blockSignals(true);
145  m_ui->rampComboBox->clear();
146 
147  // populate combo box with scale names (and UUID)
148  assert(m_manager);
149  for (ccColorScalesManager::ScalesMap::const_iterator it =
150  m_manager->map().constBegin();
151  it != m_manager->map().constEnd(); ++it)
152  m_ui->rampComboBox->addItem((*it)->getName(), (*it)->getUuid());
153 
154  // find the currently selected scale in the new 'list'
155  int pos = -1;
156  if (m_colorScale) {
157  pos = m_ui->rampComboBox->findData(m_colorScale->getUuid());
158  if (pos < 0) // the current color scale has disappeared?!
160  }
161  m_ui->rampComboBox->setCurrentIndex(pos);
162 
163  m_ui->rampComboBox->blockSignals(false);
164 }
165 
167  QString UUID = m_ui->rampComboBox->itemData(pos).toString();
168  ccColorScale::Shared colorScale =
170 
171  setActiveScale(colorScale);
172 }
173 
175  setScaleModeToRelative(value == 0 ? true : false);
176 
177  setModified(true);
178 }
179 
181  m_modified = state;
182  m_ui->saveToolButton->setEnabled(m_modified);
183 }
184 
186  if (!m_colorScale || !m_modified) return true;
187 
188  if (m_colorScale->isLocked()) {
189  assert(false);
190  return true;
191  }
192 
194  QMessageBox::StandardButton button = QMessageBox::warning(
195  this, "Current scale has been modified",
196  "Do you want to save modifications?",
197  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
198  QMessageBox::Cancel);
199  if (button == QMessageBox::Yes) {
200  if (!saveCurrentScale()) {
201  return false;
202  }
203  } else if (button == QMessageBox::Cancel) {
204  return false;
205  }
206  return true;
207 }
208 
210  return (m_ui->scaleModeComboBox->currentIndex() == 0 ? true : false);
211 }
212 
214  ccColorScale::Shared currentScale) {
215  // the user wants to change the current scale while the it has been modified
216  // (and potentially not saved)
217  if (m_colorScale != currentScale) {
218  if (!canChangeCurrentScale()) {
219  // restore old combo-box state
220  int pos = m_ui->rampComboBox->findData(m_colorScale->getUuid());
221  if (pos >= 0) {
222  m_ui->rampComboBox->blockSignals(true);
223  m_ui->rampComboBox->setCurrentIndex(pos);
224  m_ui->rampComboBox->blockSignals(false);
225  } else {
226  assert(false);
227  }
228  // stop process
229  return;
230  }
231  }
232 
233  m_colorScale = currentScale;
234  setModified(false);
235 
236  // make sure combo-box is up to date
237  {
238  int pos = m_ui->rampComboBox->findData(m_colorScale->getUuid());
239  if (pos >= 0) {
240  m_ui->rampComboBox->blockSignals(true);
241  m_ui->rampComboBox->setCurrentIndex(pos);
242  m_ui->rampComboBox->blockSignals(false);
243  }
244  }
245 
246  // setup dialog components
247  {
248  // locked state
249  bool isLocked = !m_colorScale || m_colorScale->isLocked();
250  m_ui->colorScaleParametersFrame->setEnabled(!isLocked);
251  m_ui->exportToolButton->setEnabled(!isLocked);
252  m_ui->lockWarningLabel->setVisible(isLocked);
253  m_ui->selectedSliderGroupBox->setEnabled(!isLocked);
254  m_scaleWidget->setEnabled(!isLocked);
255  m_ui->customLabelsGroupBox->blockSignals(true);
256  m_ui->customLabelsGroupBox->setEnabled(!isLocked);
257  m_ui->customLabelsGroupBox->blockSignals(false);
258 
259  // absolute or relative mode
260  if (m_colorScale) {
261  bool isRelative = m_colorScale->isRelative();
262  if (!isRelative) {
263  // absolute color scales defines their own boundaries
264  m_colorScale->getAbsoluteBoundaries(m_minAbsoluteVal,
266  }
267  setScaleModeToRelative(isRelative);
268  } else {
269  // shouldn't be accessible anyway....
270  assert(isLocked == true);
271  setScaleModeToRelative(false);
272  }
273  }
274 
275  // custom labels
276  {
277  ccColorScale::LabelSet& customLabels = m_colorScale->customLabels();
278  if (customLabels.empty()) {
279  m_ui->customLabelsPlainTextEdit->blockSignals(true);
280  m_ui->customLabelsPlainTextEdit->setPlainText(
282  m_ui->customLabelsPlainTextEdit->blockSignals(false);
283  } else {
284  QString text;
285  size_t index = 0;
286  for (ccColorScale::LabelSet::const_iterator it =
287  customLabels.begin();
288  it != customLabels.end(); ++it, ++index) {
289  if (index != 0) text += QString("\n");
290  text += QString::number(it->value, 'f', 6);
291  if (!it->text.isEmpty()) {
292  text += " \"" + it->text + '\"';
293  }
294  }
295  m_ui->customLabelsPlainTextEdit->blockSignals(true);
296  m_ui->customLabelsPlainTextEdit->setPlainText(text);
297  m_ui->customLabelsPlainTextEdit->blockSignals(false);
298  }
299  m_ui->customLabelsGroupBox->blockSignals(true);
300  m_ui->customLabelsGroupBox->setChecked(!customLabels.empty());
301  m_ui->customLabelsGroupBox->blockSignals(false);
302  }
303 
305 
306  onStepSelected(-1);
307 }
308 
310  m_ui->scaleModeComboBox->setCurrentIndex(isRelative ? 0 : 1);
311  m_ui->valueDoubleSpinBox->setSuffix(isRelative ? QString(" %") : QString());
312  m_ui->valueDoubleSpinBox->blockSignals(true);
313  if (isRelative)
314  m_ui->valueDoubleSpinBox->setRange(0.0, 100.0); // between 0 and 100%
315  else
316  m_ui->valueDoubleSpinBox->setRange(-1.0e9, 1.0e9);
317  m_ui->valueDoubleSpinBox->blockSignals(false);
318 
319  // update selected slider frame
320  int selectedIndex =
322  onStepModified(selectedIndex);
323 }
324 
326  m_ui->selectedSliderGroupBox->setEnabled(
327  /*m_colorScale && !m_colorScale->isLocked() && */ index >= 0);
328  // don't delete the first and last steps!
329  m_ui->deleteSliderToolButton->setEnabled(
330  index >= 1 && index + 1 < m_scaleWidget->getStepCount());
331 
332  if (index < 0) {
333  m_ui->valueDoubleSpinBox->blockSignals(true);
334  m_ui->valueDoubleSpinBox->setValue(0.0);
335  m_ui->valueDoubleSpinBox->blockSignals(false);
336  ccQtHelpers::SetButtonColor(m_ui->colorToolButton, Qt::gray);
337  m_ui->valueLabel->setVisible(false);
338  } else {
339  bool modified =
340  m_modified; // save 'modified' state before calling
341  // onStepModified (which will force it to true)
342  onStepModified(index);
343  setModified(modified); // restore true 'modified' state
344  }
345 }
346 
348  if (index < 0 || index >= m_scaleWidget->getStepCount()) return;
349 
350  const ColorScaleElementSlider* slider = m_scaleWidget->getStep(index);
351  assert(slider);
352 
353  ccQtHelpers::SetButtonColor(m_ui->colorToolButton, slider->getColor());
354  if (m_colorScale) {
355  const double relativePos = slider->getRelativePos();
356  if (isRelativeMode()) {
357  m_ui->valueDoubleSpinBox->blockSignals(true);
358  m_ui->valueDoubleSpinBox->setValue(relativePos * 100.0);
359  m_ui->valueDoubleSpinBox->blockSignals(false);
360  if (m_associatedSF) {
361  // compute corresponding scalar value for associated SF
362  double actualValue = m_associatedSF->getMin() +
363  relativePos * (m_associatedSF->getMax() -
365  m_ui->valueLabel->setText(QString("(%1)").arg(actualValue));
366  m_ui->valueLabel->setVisible(true);
367  } else {
368  m_ui->valueLabel->setVisible(false);
369  }
370 
371  // can't change min and max boundaries in 'relative' mode!
372  m_ui->valueDoubleSpinBox->setEnabled(
373  index > 0 && index < m_scaleWidget->getStepCount() - 1);
374  } else {
375  // compute corresponding 'absolute' value from current dialog
376  // boundaries
377  double absoluteValue =
379  relativePos * (m_maxAbsoluteVal - m_minAbsoluteVal);
380 
381  m_ui->valueDoubleSpinBox->blockSignals(true);
382  m_ui->valueDoubleSpinBox->setValue(absoluteValue);
383  m_ui->valueDoubleSpinBox->blockSignals(false);
384  m_ui->valueDoubleSpinBox->setEnabled(true);
385 
386  // display corresponding relative position as well
387  m_ui->valueLabel->setText(
388  QString("(%1 %)").arg(relativePos * 100.0));
389  m_ui->valueLabel->setVisible(true);
390  }
391 
392  setModified(true);
393  }
394 }
395 
397  int selectedIndex = m_scaleWidget->getSelectedStepIndex();
398  if (selectedIndex >= 1 &&
399  selectedIndex + 1 <
400  m_scaleWidget->getStepCount()) // never delete the first and
401  // last steps!
402  {
403  m_scaleWidget->deleteStep(selectedIndex);
404  setModified(true);
405  }
406 }
407 
409  int selectedIndex = m_scaleWidget->getSelectedStepIndex();
410  if (selectedIndex < 0) return;
411 
412  const ColorScaleElementSlider* slider =
413  m_scaleWidget->getStep(selectedIndex);
414  assert(slider);
415 
416  QColor newCol = QColorDialog::getColor(slider->getColor(), this);
417  if (newCol.isValid()) {
418  // eventually onStepModified will be called (and thus m_modified will be
419  // updated)
420  m_scaleWidget->setStepColor(selectedIndex, newCol);
421  }
422 }
423 
425  if (!m_scaleWidget) return;
426 
427  int selectedIndex = m_scaleWidget->getSelectedStepIndex();
428  if (selectedIndex < 0) return;
429 
430  const ColorScaleElementSlider* slider =
431  m_scaleWidget->getStep(selectedIndex);
432  assert(slider);
433 
434  bool relativeMode = isRelativeMode();
435  if (relativeMode) {
436  assert(selectedIndex != 0 &&
437  selectedIndex + 1 < m_scaleWidget->getStepCount());
438 
439  value /= 100.0; // from percentage to relative position
440  assert(value >= 0.0 && value <= 1.0);
441 
442  // eventually onStepModified will be called (and thus m_modified will be
443  // updated)
444  m_scaleWidget->setStepRelativePosition(selectedIndex, value);
445  } else // absolute scale mode
446  {
447  // we build up the new list based on absolute values
450  {
451  for (int i = 0; i < m_scaleWidget->getStepCount(); ++i) {
452  const ColorScaleElementSlider* slider =
454  double absolutePos =
455  (i == selectedIndex
456  ? value
457  : m_minAbsoluteVal +
458  slider->getRelativePos() *
461  newSliders->push_back(new ColorScaleElementSlider(
462  absolutePos, slider->getColor()));
463  }
464  }
465 
466  // update min and max boundaries
467  {
468  newSliders->sort();
470  newSliders->front()->getRelativePos(); // absolute in fact!
472  newSliders->back()->getRelativePos(); // absolute in fact!
473  }
474 
475  // convert absolute pos to relative ones
476  int newSelectedIndex = -1;
477  {
478  double range = std::max(m_maxAbsoluteVal - m_minAbsoluteVal, 1e-12);
479  for (int i = 0; i < newSliders->size(); ++i) {
480  double absoluteVal = newSliders->at(i)->getRelativePos();
481  if (absoluteVal == value) newSelectedIndex = i;
482  double relativePos = (absoluteVal - m_minAbsoluteVal) / range;
483  newSliders->at(i)->setRelativePos(relativePos);
484  }
485  }
486 
487  // update the whole scale with new sliders
488  m_scaleWidget->setSliders(newSliders);
489 
490  m_scaleWidget->setSelectedStepIndex(newSelectedIndex, true);
491 
492  setModified(true);
493  }
494 }
495 
497  ccColorScale::LabelSet& labels) {
498  assert(m_ui->customLabelsGroupBox->isChecked());
499  labels.clear();
500 
501  QString text = m_ui->customLabelsPlainTextEdit->toPlainText();
502  QStringList items =
504  if (items.size() < 2) {
505  assert(false);
506  return false;
507  }
508 
509  try {
510  for (int i = 0; i < items.size(); ++i) {
511  bool ok;
512  double d = items[i].toDouble(&ok);
513  if (!ok) {
514  return false;
515  }
516  labels.insert(d);
517  }
518  } catch (const std::bad_alloc&) {
519  CVLog::Error("Not enough memory to save the custom labels!");
520  labels.clear();
521  return false;
522  }
523 
524  return true;
525 }
526 
528  QString text = m_ui->customLabelsPlainTextEdit->toPlainText();
529  QStringList items =
531  if (items.size() < 2) {
532  if (showWarnings)
533  CVLog::Error("Not enough labels defined (2 at least are required)");
534  return false;
535  }
536 
537  for (int i = 0; i < items.size(); ++i) {
538  bool ok;
539  items[i].toDouble(&ok);
540  if (!ok) {
541  if (showWarnings)
542  CVLog::Error(
543  QString("Invalid label value: '%1'").arg(items[i]));
544  return false;
545  }
546  }
547 
548  return true;
549 }
550 
552  setModified(true);
553 }
554 
556  // custom list enable
557  if (state) {
558  QString previousText = m_ui->customLabelsPlainTextEdit->toPlainText();
559  // if the previous list was 'empty', we clear its (fake) content
560  if (previousText == s_defaultEmptyCustomListText) {
561  m_ui->customLabelsPlainTextEdit->blockSignals(true);
562  m_ui->customLabelsPlainTextEdit->clear();
563  m_ui->customLabelsPlainTextEdit->blockSignals(false);
564  }
565  } else {
566  if (!checkCustomLabelsList(false)) {
567  // if the text is invalid
568  m_ui->customLabelsPlainTextEdit->setPlainText(
570  }
571  }
572  setModified(true);
573 }
574 
576  if (!m_colorScale) {
577  assert(false);
578  return;
579  }
580 
581  ccColorScale::Shared scale =
582  ccColorScale::Create(m_colorScale->getName() + QString("_copy"));
583  if (!m_colorScale->isRelative()) {
584  double minVal, maxVal;
585  m_colorScale->getAbsoluteBoundaries(minVal, maxVal);
586  scale->setAbsolute(minVal, maxVal);
587  }
589 
590  assert(m_manager);
591  if (m_manager) m_manager->addScale(scale);
592 
594 
595  setActiveScale(scale);
596 }
597 
599  if (!m_colorScale || m_colorScale->isLocked()) {
600  assert(false);
601  return false;
602  }
603 
604  // check the custom labels
605  if (m_ui->customLabelsGroupBox->isChecked() &&
606  !checkCustomLabelsList(true)) {
607  // error message already issued
608  return false;
609  }
610 
612  bool wasRelative = m_colorScale->isRelative();
613  bool isRelative = isRelativeMode();
614  if (isRelative)
615  m_colorScale->setRelative();
616  else
618 
619  // DGM: warning, if the relative state has changed
620  // we must update all the SFs currently relying on this scale!
621  if ((!isRelative || isRelative != wasRelative) && m_mainApp &&
622  m_mainApp->dbRootObject()) {
623  ccHObject::Container clouds;
624  m_mainApp->dbRootObject()->filterChildren(clouds, true,
625  CV_TYPES::POINT_CLOUD, true);
626  for (size_t i = 0; i < clouds.size(); ++i) {
627  ccPointCloud* cloud = static_cast<ccPointCloud*>(clouds[i]);
628  for (unsigned j = 0; j < cloud->getNumberOfScalarFields(); ++j) {
629  ccScalarField* sf =
630  static_cast<ccScalarField*>(cloud->getScalarField(j));
631  if (sf->getColorScale() == m_colorScale) {
632  // trick: we unlink then re-link the color scale to update
633  // everything automatically
634  sf->setColorScale(ccColorScale::Shared(nullptr));
636 
637  if (cloud->getCurrentDisplayedScalarField() == sf) {
638  // cloud->prepareDisplayForRefresh();
639  if (cloud->getParent() &&
640  cloud->getParent()->isKindOf(CV_TYPES::MESH)) {
641  // for mesh vertices (just in case)
642  // cloud->getParent()->prepareDisplayForRefresh();
643  }
644  }
645  }
646  }
647  }
648 
650  }
651 
652  // save the custom labels
653  if (m_ui->customLabelsGroupBox->isChecked()) {
654  exportCustomLabelsList(m_colorScale->customLabels());
655  } else {
656  m_colorScale->customLabels().clear();
657  }
658 
659  setModified(false);
660 
661  return true;
662 }
663 
665  if (!m_colorScale || m_colorScale->isLocked()) {
666  assert(false);
667  return;
668  }
669 
670  QString newName =
671  QInputDialog::getText(this, "Scale name", "Name", QLineEdit::Normal,
672  m_colorScale->getName());
673  if (!newName.isNull()) {
674  m_colorScale->setName(newName);
675  // position in combo box
676  int pos = m_ui->rampComboBox->findData(m_colorScale->getUuid());
677  if (pos >= 0)
678  // update combo box entry name
679  m_ui->rampComboBox->setItemText(pos, newName);
680  }
681 }
682 
684  if (!m_colorScale || m_colorScale->isLocked()) {
685  assert(false);
686  return;
687  }
688 
689  // ask for confirmation
690  if (QMessageBox::warning(this, "Delete scale", "Are you sure?",
691  QMessageBox::Yes | QMessageBox::No,
692  QMessageBox::No) == QMessageBox::No) {
693  return;
694  }
695 
696  // backup current scale
697  ccColorScale::Shared colorScaleToDelete = m_colorScale;
698  setModified(false); // cancel any modification
699 
700  int currentIndex = m_ui->rampComboBox->currentIndex();
701  if (currentIndex == 0)
702  currentIndex = 1;
703  else if (currentIndex > 0)
704  --currentIndex;
705 
706  assert(m_manager);
707  if (m_manager) {
708  // activate the neighbor scale in the list
710  m_ui->rampComboBox->itemData(currentIndex).toString());
711  setActiveScale(nextScale);
712 
713  m_manager->removeScale(colorScaleToDelete->getUuid());
714  }
715 
717 }
718 
720  ccColorScale::Shared scale = ccColorScale::Create("New scale");
721 
722  // add default min and max steps
723  scale->insert(ccColorScaleElement(0.0, Qt::blue), false);
724  scale->insert(ccColorScaleElement(1.0, Qt::red), true);
725 
726  assert(m_manager);
727  if (m_manager) m_manager->addScale(scale);
728 
730 
731  setActiveScale(scale);
732 }
733 
735  if (m_mainApp && canChangeCurrentScale()) {
738  }
739 }
740 
742  if (canChangeCurrentScale()) {
743  accept();
744  }
745 }
746 
748  if (!m_colorScale || m_colorScale->isLocked()) {
749  assert(false);
750  return;
751  }
752 
753  // persistent settings
754  QSettings settings;
755  settings.beginGroup(ecvPS::SaveFile());
756  QString currentPath =
758  .toString();
759 
760  // ask for a filename
761  QString filename = QFileDialog::getSaveFileName(this, "Select output file",
762  currentPath, "*.xml");
763  if (filename.isEmpty()) {
764  // process cancelled by user
765  return;
766  }
767 
768  // save last saving location
769  settings.setValue(ecvPS::CurrentPath(), QFileInfo(filename).absolutePath());
770  settings.endGroup();
771 
772  // try to save the file
773  if (m_colorScale->saveAsXML(filename)) {
774  CVLog::Print(
775  QString("[ColorScale] Scale '%1' successfully exported in '%2'")
776  .arg(m_colorScale->getName(), filename));
777  }
778 }
779 
781  // persistent settings
782  QSettings settings;
783  settings.beginGroup(ecvPS::LoadFile());
784  QString currentPath =
786  .toString();
787 
788  // ask for a filename
789  QString filename = QFileDialog::getOpenFileName(
790  this, "Select color scale file", currentPath, "*.xml");
791  if (filename.isEmpty()) {
792  // process cancelled by user
793  return;
794  }
795 
796  // save last loading parameters
797  settings.setValue(ecvPS::CurrentPath(), QFileInfo(filename).absolutePath());
798  settings.endGroup();
799 
800  // try to load the file
802  if (scale) {
803  assert(m_manager);
804  if (m_manager) {
805  ccColorScale::Shared otherScale =
806  m_manager->getScale(scale->getUuid());
807  if (otherScale) {
808  QString message = "A color scale with the same UUID";
809  if (otherScale->getName() == scale->getName())
810  message += QString(" and the same name (%1)")
811  .arg(scale->getName());
812  message += " is already in store!";
813  message += "\n";
814  message +=
815  "Do you want to force the importation of this new "
816  "scale? (a new UUID will be generated)";
817 
818  if (QMessageBox::question(this, "UUID conflict", message,
819  QMessageBox::Yes,
820  QMessageBox::No) == QMessageBox::No) {
822  "[ccColorScaleEditorDialog::importScale] "
823  "Importation cancelled due to a conflicting UUID "
824  "(color scale may already be in store)");
825  return;
826  }
827  // generate a new UUID
828  scale->setUuid(QUuid::createUuid().toString());
829  }
830  // now we can import the scale
831  m_manager->addScale(scale);
832  CVLog::Print(QString("[ccColorScaleEditorDialog::importScale] "
833  "Color scale '%1' successfully imported")
834  .arg(scale->getName()));
835  }
836 
838 
839  setActiveScale(scale);
840  }
841 }
std::string filename
QStringList qtCompatSplitRegex(const QString &str, const QString &pattern, Qt::SplitBehavior behavior=Qt::KeepEmptyParts)
Definition: QtCompat.h:308
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Color scale element as a widget.
Set of color scale elements (widgets)
void setModified(bool state)
Sets modification flag state.
void setAssociatedScalarField(ccScalarField *sf)
Sets associated scalar field (optional)
double m_maxAbsoluteVal
Current max boundary for absolute scales.
ccScalarField * m_associatedSF
Associated scalar field.
double m_minAbsoluteVal
Current min boundary for absolute scales.
ccColorScalesManager * m_manager
Color scale manager.
ecvMainAppInterface * m_mainApp
Associated application (interface)
ccColorScale::Shared m_colorScale
Current active color scale.
bool exportCustomLabelsList(ccColorScale::LabelSet &labels)
Exports the custom labels list.
bool checkCustomLabelsList(bool showWarnings)
Checks the custom labels list.
bool m_modified
Modification flag.
bool canChangeCurrentScale()
If the current scale has been modified, ask the user what to do.
void updateMainComboBox()
Updates main combox box with color scales manager.
void setScaleModeToRelative(bool isRelative)
ccColorScaleEditorWidget * m_scaleWidget
Color scale editor widget.
Ui::ColorScaleEditorDlg * m_ui
void setActiveScale(ccColorScale::Shared currentScale)
Sets active scale.
ccColorScaleEditorDialog(ccColorScalesManager *manager, ecvMainAppInterface *mainApp, ccColorScale::Shared currentScale=ccColorScale::Shared(nullptr), QWidget *parent=nullptr)
Default constructor.
Color scale editor dialog.
const ColorScaleElementSlider * getStep(int index)
Returns a given slider (pointer on)
void setSelectedStepIndex(int index, bool silent=false)
Sets currently selected step index.
void setSliders(SharedColorScaleElementSliders sliders) override
Sets associated sliders set.
int getSelectedStepIndex() const
Returns currently selected step index.
void exportColorScale(ccColorScale::Shared &destScale) const
Exports the current color scale.
void setStepRelativePosition(int index, double relativePos)
Sets a given slider relative position.
void importColorScale(ccColorScale::Shared scale)
Imports the current color scale.
void deleteStep(int index)
Deletes a given step.
void setStepColor(int index, QColor color)
Sets a given slider color.
int getStepCount() const
Returns the current number of color scale steps.
Color scale element: one value + one color.
Definition: ecvColorScale.h:22
const QColor & getColor() const
Returns color.
Definition: ecvColorScale.h:43
double getRelativePos() const
Returns step position (relative to scale boundaries)
Definition: ecvColorScale.h:38
static Shared LoadFromXML(QString filename)
Loads a color scale from an XML file.
static ccColorScale::Shared Create(const QString &name)
Creates a new color scale (with auto-generated unique id)
QSharedPointer< ccColorScale > Shared
Shared pointer type.
Definition: ecvColorScale.h:74
std::set< Label > LabelSet
Type of a list of custom labels.
Color scales manager/container.
ccColorScale::Shared getScale(QString UUID) const
Returns a color scale based on its UUID.
void addScale(ccColorScale::Shared scale)
Adds a new color scale.
ScalesMap & map()
Access to the internal map.
void removeScale(QString UUID)
Removes a color scale.
ccColorScale::Shared getDefaultScale(DEFAULT_SCALES scale) const
Returns a pre-defined color scale.
static ccColorScalesManager * GetUniqueInstance()
Returns unique instance.
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
ccScalarField * getCurrentDisplayedScalarField() const
Returns the currently displayed scalar (or 0 if none)
static void SetButtonColor(QAbstractButton *button, const QColor &col)
Sets a button background color.
Definition: ecvQtHelpers.h:17
A scalar field associated to display-related parameters.
const ccColorScale::Shared & getColorScale() const
Returns associated color scale.
void setColorScale(ccColorScale::Shared scale)
Sets associated color scale.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
ScalarType getMin() const
Returns the minimum value.
Definition: ScalarField.h:72
ScalarType getMax() const
Returns the maximum value.
Definition: ScalarField.h:74
Main application interface (for plugins)
virtual ccHObject * dbRootObject()=0
Returns DB root (as a ccHObject)
virtual void refreshAll(bool only2D=false, bool forceRedraw=true)=0
Redraws all GL windows that have the 'refresh' flag on.
static const QString CurrentPath()
static const QString SaveFile()
static const QString LoadFile()
int max(int a, int b)
Definition: cutil_math.h:48
static char s_defaultEmptyCustomListText[]
QSharedPointer< ColorScaleElementSliders > SharedColorScaleElementSliders
Shared set of color scale elements (widgets)
@ MESH
Definition: CVTypes.h:105
@ POINT_CLOUD
Definition: CVTypes.h:104
constexpr Qt::SplitBehavior SkipEmptyParts
Definition: QtCompat.h:302
std::string toString(T x)
Definition: Common.h:80
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb blue(0, 0, MAX)
QString defaultDocPath()
Shortcut for getting the documents location path.
Definition: ecvFileUtils.h:30