8 #include <ui_openAsciiFileDlg.h>
19 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
20 #include <QDesktopWidget>
22 #include <QDialogButtonBox>
25 #include <QMessageBox>
26 #include <QPushButton>
29 #include <QTableWidget>
30 #include <QTableWidgetItem>
31 #include <QTextStream>
32 #include <QToolButton>
61 void save(Ui_AsciiOpenDialog* ui) {
63 ui->extractSFNamesFrom1stLineCheckBox->isChecked();
65 separator = ui->lineEditSeparator->text().at(0);
66 skipLines = ui->spinBoxSkipLines->value();
71 void load(Ui_AsciiOpenDialog* ui)
const {
72 ui->extractSFNamesFrom1stLineCheckBox->setChecked(
75 ui->lineEditSeparator->blockSignals(
true);
76 ui->lineEditSeparator->setText(
separator);
77 ui->lineEditSeparator->blockSignals(
false);
78 ui->spinBoxSkipLines->blockSignals(
true);
79 ui->spinBoxSkipLines->setValue(
skipLines);
80 ui->spinBoxSkipLines->blockSignals(
false);
81 ui->commaDecimalCheckBox->blockSignals(
true);
83 ui->commaDecimalCheckBox->setEnabled(
separator !=
',');
84 ui->commaDecimalCheckBox->blockSignals(
false);
101 m_ui(new Ui_AsciiOpenDialog),
104 m_averageLineSize(-1.0),
109 m_ui->commentLinesSkippedLabel->hide();
111 connect(
m_ui->applyButton, &QPushButton::clicked,
this,
113 connect(
m_ui->applyAllButton, &QPushButton::clicked,
this,
115 connect(
m_ui->cancelButton, &QPushButton::clicked,
this,
116 &AsciiOpenDlg::reject);
117 connect(
m_ui->lineEditSeparator, &QLineEdit::textChanged,
this,
119 connect(
m_ui->commaDecimalCheckBox, &QCheckBox::toggled,
this,
121 connect(
m_ui->spinBoxSkipLines,
122 static_cast<void (QSpinBox::*)(
int)
>(&QSpinBox::valueChanged),
this,
126 connect(
m_ui->toolButtonShortcutSpace, &QToolButton::clicked,
this,
128 connect(
m_ui->toolButtonShortcutComma, &QToolButton::clicked,
this,
130 connect(
m_ui->toolButtonShortcutSemicolon, &QToolButton::clicked,
this,
133 m_ui->maxCloudSizeDoubleSpinBox->setMaximum(
137 QSize screenSize = QGuiApplication::primaryScreen()->geometry().size();
139 setMaximumSize(screenSize);
148 QTextStream* stream ) {
161 const QList<QChar> separators{QChar(
' '), QChar(
','), QChar(
';')};
164 size_t maxValidColumnCount = 0;
165 QChar bestSep = separators.front();
166 for (QChar sep : separators) {
170 size_t validColumnCount = 0;
177 if (validColumnCount > 2) {
179 }
else if (validColumnCount > maxValidColumnCount) {
180 maxValidColumnCount = validColumnCount;
191 if (linesCount < 0)
return;
221 colHeader.contains(
"RED");
225 colHeader.contains(
"GREEN");
229 colHeader.contains(
"BLUE");
233 colHeader.contains(
"ALPHA");
237 (colHeader.contains(
"NORM") && colHeader.contains(
"X"));
241 (colHeader.contains(
"NORM") && colHeader.contains(
"Y"));
245 (colHeader.contains(
"NORM") && colHeader.contains(
"Z"));
258 return colHeader.contains(
"SCALAR");
261 return colHeader.contains(
"LABEL") || colHeader.contains(
"NAME");
288 assert(separator.size() == 1);
289 if (separator.length() < 1) {
290 m_ui->asciiCodeLabel->setText(
"Enter a valid character!");
291 m_ui->buttonWidget->setEnabled(
false);
292 m_ui->tableWidget->clear();
299 m_ui->asciiCodeLabel->setText(
300 QString(
"(ASCII code: %1)").arg(
m_separator.unicode()));
309 return m_ui->commaDecimalCheckBox->isEnabled() &&
310 m_ui->commaDecimalCheckBox->isChecked();
318 m_ui->tableWidget->setEnabled(
false);
327 m_ui->tableWidget->clear();
328 m_ui->extractSFNamesFrom1stLineCheckBox->setEnabled(
false);
336 if (!file.open(QFile::ReadOnly)) {
337 m_ui->tableWidget->clear();
339 m_ui->extractSFNamesFrom1stLineCheckBox->setEnabled(
false);
350 QString currentLine =
m_stream->readLine();
351 if (currentLine.isNull()) {
355 if (currentLine.isEmpty()) {
369 m_ui->tableWidget->clear();
376 unsigned lineCount = 0;
377 unsigned totalChars = 0;
378 unsigned columnsCount = 0;
379 unsigned commentLines = 0;
383 std::vector<bool> valueIsBelowOne;
387 std::vector<bool> valueIsBelow255;
391 QLocale locale(commaAsDecimal ? QLocale::French : QLocale::English);
392 QChar decimalPoint = commaAsDecimal ?
',' :
'.';
394 QString currentLine =
m_stream->readLine();
395 if (currentLine.isNull()) {
399 if (currentLine.isEmpty()) {
405 if (!currentLine.startsWith(
407 QStringList parts = currentLine.simplified().split(
411 unsigned partsCount = std::min(
413 bool columnCountHasIncreased = (partsCount > columnsCount);
416 if (columnCountHasIncreased) {
418 for (
unsigned i = columnsCount; i < partsCount; ++i) {
419 valueIsNumber.push_back(
true);
420 valueIsBelowOne.push_back(
true);
421 valueIsBelow255.push_back(
true);
422 valueIsInteger.push_back(
true);
425 if (
m_ui->tableWidget->columnCount() <
426 static_cast<int>(partsCount)) {
431 m_ui->tableWidget->setColumnCount(partsCount);
432 }
else if (
m_ui->tableWidget->columnCount() >
433 static_cast<int>(partsCount)) {
435 for (
int i =
static_cast<int>(partsCount);
436 i <
m_ui->tableWidget->columnCount(); ++i) {
437 m_ui->tableWidget->setItem(lineCount + 1, i,
441 columnsCount = partsCount;
445 for (
unsigned i = 0; i < partsCount; ++i) {
446 QTableWidgetItem* newItem =
new QTableWidgetItem(parts[i]);
449 bool isANumber =
false;
450 double value = locale.toDouble(parts[i], &isANumber);
452 valueIsNumber[i] =
false;
453 valueIsBelowOne[i] =
false;
454 valueIsInteger[i] =
false;
455 valueIsBelow255[i] =
false;
456 newItem->setBackground(QBrush(QColor(255, 160, 160)));
458 if (columnCountHasIncreased ||
459 (lineCount == 1 && !valueIsNumber[i])) {
463 valueIsNumber[i] =
true;
464 valueIsBelowOne[i] =
true;
465 valueIsInteger[i] =
true;
466 valueIsBelow255[i] =
true;
469 valueIsBelowOne[i] && (std::abs(value) <= 1.0);
470 valueIsInteger[i] = valueIsInteger[i] &&
471 !parts[i].contains(decimalPoint);
472 valueIsBelow255[i] = valueIsBelow255[i] &&
474 (value >= 0.0 && value <= 255.0);
477 m_ui->tableWidget->setItem(
483 totalChars += currentLine.size() + 1;
489 m_ui->spinBoxSkipLines->setMinimum(1);
490 m_ui->extractSFNamesFrom1stLineCheckBox->setEnabled(
false);
500 if (
m_ui->tableWidget->columnCount() >
static_cast<int>(columnsCount)) {
501 m_ui->tableWidget->setColumnCount(columnsCount);
519 m_ui->headerLabel->setText(QString(
"Header: ") + displayHeader);
520 m_ui->headerLabel->setVisible(
true);
522 m_ui->headerLabel->setVisible(
false);
525 m_ui->commentLinesSkippedLabel->setVisible(commentLines != 0);
527 m_ui->commentLinesSkippedLabel->setText(
528 QString(
"+ %1 comment line(s) skipped").arg(commentLines));
531 if (lineCount == 0 || columnsCount == 0) {
533 m_ui->tableWidget->clear();
540 unsigned approximateTotalLineCount =
544 QStringList propsText;
559 m_ui->tableWidget->removeRow(i);
565 static const QIcon xIcon(
566 QString::fromUtf8(
":/Resources/images/typeXCoordinate.png"));
567 static const QIcon yIcon(
568 QString::fromUtf8(
":/Resources/images/typeYCoordinate.png"));
569 static const QIcon zIcon(
570 QString::fromUtf8(
":/Resources/images/typeZCoordinate.png"));
571 static const QIcon NormIcon(
572 QString::fromUtf8(
":/Resources/images/typeNormal.png"));
573 static const QIcon RGBIcon(
574 QString::fromUtf8(
":/Resources/images/typeRgbCcolor.png"));
575 static const QIcon GreyIcon(
576 QString::fromUtf8(
":/Resources/images/typeGrayColor.png"));
577 static const QIcon ScalarIcon(
578 QString::fromUtf8(
":/Resources/images/typeSF.png"));
579 static const QIcon LabelIcon(
580 QString::fromUtf8(
":/Resources/images/dbLabelSymbol.png"));
583 (
m_ui->tableWidget->width() * 9) / (columnsCount * 10);
584 columnWidth = std::max(columnWidth, 80);
586 for (
unsigned i = 0; i < columnsCount; i++) {
587 QComboBox* columnHeaderWidget =
static_cast<QComboBox*
>(
588 m_ui->tableWidget->cellWidget(0, i));
589 QComboBox* _columnHeader = columnHeaderWidget;
590 if (!columnHeaderWidget) {
591 columnHeaderWidget =
new QComboBox();
592 columnHeaderWidget->addItems(propsText);
593 columnHeaderWidget->setMaxVisibleItems(
595 columnHeaderWidget->setCurrentIndex(0);
618 connect(columnHeaderWidget,
619 static_cast<void (QComboBox::*)(
int)
>(
620 &QComboBox::currentIndexChanged),
629 m_ui->tableWidget->setCellWidget(0, i, columnHeaderWidget);
630 m_ui->tableWidget->setColumnWidth(i, columnWidth);
633 if (!valueIsNumber[i]) {
660 unsigned assignedXYZFlags = 0;
661 unsigned assignedNormFlags = 0;
662 unsigned assignedRGBFlags = 0;
665 QStringList headerParts =
m_headerLine.simplified().split(
668 (headerParts.size() >=
static_cast<int>(columnsCount));
669 m_ui->extractSFNamesFrom1stLineCheckBox->setEnabled(
675 }
else if (!hadValidHeader) {
676 m_ui->extractSFNamesFrom1stLineCheckBox->setChecked(
true);
685 bool labelColumnAssigned =
false;
687 for (
unsigned i = 0; i < columnsCount; i++) {
691 QComboBox* columnHeaderWidget =
static_cast<QComboBox*
>(
692 m_ui->tableWidget->cellWidget(0, i));
693 assert(columnHeaderWidget);
695 columnHeaderWidget->blockSignals(
true);
697 QString colHeader = headerParts[i].toUpper();
699 if ((assignedXYZFlags &
X_BIT) == 0 &&
706 }
else if ((assignedXYZFlags &
Y_BIT) == 0 &&
713 }
else if ((assignedXYZFlags &
Z_BIT) == 0 &&
720 }
else if ((assignedRGBFlags &
X_BIT) == 0 &&
727 }
else if ((assignedRGBFlags &
Y_BIT) == 0 &&
734 }
else if ((assignedRGBFlags &
Z_BIT) == 0 &&
741 }
else if ((assignedRGBFlags &
X_BIT) == 0 &&
748 }
else if ((assignedRGBFlags &
Y_BIT) == 0 &&
755 }
else if ((assignedRGBFlags &
Z_BIT) == 0 &&
762 }
else if ((assignedRGBFlags &
W_BIT) == 0 &&
769 }
else if ((assignedNormFlags &
X_BIT) == 0 &&
776 }
else if ((assignedNormFlags &
Y_BIT) == 0 &&
783 }
else if ((assignedNormFlags &
Z_BIT) == 0 &&
792 columnHeaderWidget->setCurrentIndex(
797 columnHeaderWidget->setCurrentIndex(
802 columnHeaderWidget->setCurrentIndex(
807 columnHeaderWidget->setCurrentIndex(
810 }
else if (!labelColumnAssigned &&
817 columnHeaderWidget->setCurrentIndex(
820 labelColumnAssigned =
true;
823 columnHeaderWidget->blockSignals(
false);
830 for (
unsigned i = 0; i < columnsCount; i++) {
832 if (!labelColumnAssigned && columnsCount > 1 &&
834 QComboBox* columnHeaderWidget =
static_cast<QComboBox*
>(
835 m_ui->tableWidget->cellWidget(0, i));
836 assert(columnHeaderWidget);
837 columnHeaderWidget->blockSignals(
true);
838 columnHeaderWidget->setCurrentIndex(
840 columnHeaderWidget->blockSignals(
false);
842 labelColumnAssigned =
true;
848 assert(valueIsNumber[i]);
852 QComboBox* columnHeaderWidget =
static_cast<QComboBox*
>(
853 m_ui->tableWidget->cellWidget(0, i));
854 assert(columnHeaderWidget);
855 columnHeaderWidget->blockSignals(
true);
856 columnHeaderWidget->setCurrentIndex(-1);
862 if (columnsCount > 3 && i == 0 &&
864 valueIsInteger[i] && i + 1 < columnsCount &&
865 !valueIsInteger[i + 1]) {
867 columnHeaderWidget->setCurrentIndex(
870 if (!(assignedXYZFlags &
X_BIT)) {
871 columnHeaderWidget->setCurrentIndex(
875 }
else if (!(assignedXYZFlags &
Y_BIT)) {
876 columnHeaderWidget->setCurrentIndex(
880 }
else if (!(assignedXYZFlags &
Z_BIT)) {
881 columnHeaderWidget->setCurrentIndex(
889 if (valueIsBelow255[i] && assignedRGBFlags <
XYZ_BITS &&
895 (valueIsBelow255[i + 1] &&
896 valueIsBelow255[i + 2]))
899 if (!(assignedRGBFlags &
X_BIT)) {
900 columnHeaderWidget->setCurrentIndex(
904 }
else if (!(assignedRGBFlags &
Y_BIT)) {
905 columnHeaderWidget->setCurrentIndex(
909 }
else if (!(assignedRGBFlags &
Z_BIT)) {
910 columnHeaderWidget->setCurrentIndex(
914 }
else if (!(assignedRGBFlags &
W_BIT)) {
915 columnHeaderWidget->setCurrentIndex(
922 else if (valueIsBelowOne[i] &&
929 (valueIsBelowOne[i + 1] &&
930 valueIsBelowOne[i + 2]))
935 if (!(assignedNormFlags &
X_BIT)) {
936 columnHeaderWidget->setCurrentIndex(
940 }
else if (!(assignedNormFlags &
Y_BIT)) {
941 columnHeaderWidget->setCurrentIndex(
945 }
else if (!(assignedNormFlags &
Z_BIT)) {
946 columnHeaderWidget->setCurrentIndex(
953 columnHeaderWidget->setCurrentIndex(
958 if (columnHeaderWidget->currentIndex() >= 0) {
965 columnHeaderWidget->blockSignals(
false);
976 m_ui->tableWidget->setEnabled(
true);
977 m_ui->buttonWidget->setEnabled(
true);
983 m_ui->tableWidget->resizeColumnsToContents();
988 bool m_selectedInvalidColumns =
false;
991 assert(
m_ui->tableWidget->columnCount() >=
993 m_ui->show2DLabelsCheckBox->setEnabled(
false);
995 QComboBox* columnHeaderWidget =
static_cast<QComboBox*
>(
996 m_ui->tableWidget->cellWidget(0, i));
999 m_ui->show2DLabelsCheckBox->setEnabled(
true);
1001 columnHeaderWidget->currentIndex() != 0) {
1003 m_selectedInvalidColumns |=
true;
1008 m_ui->applyAllButton->setEnabled(!m_selectedInvalidColumns);
1009 m_ui->applyButton->setEnabled(!m_selectedInvalidColumns);
1013 QString& errorMessage) {
1019 for (
size_t i = 0; i < sequence.size(); i++)
1020 ++counters[sequence[i].
type];
1026 if (counters[i] > 1) {
1027 errorMessage = QString(
"'%1' defined at least twice!")
1038 if (coordIsDefined[0] + coordIsDefined[1] + coordIsDefined[2] < 2) {
1039 errorMessage =
"At least 2 vertex coordinates must be defined!";
1047 QString errorMessage;
1049 QMessageBox::warning(
nullptr,
"Error", errorMessage);
1053 m_ui->maxCloudSizeDoubleSpinBox->value();
1060 if (!
apply())
return;
1090 static_cast<QComboBox*
>(
m_ui->tableWidget->cellWidget(0, i));
1098 combo->setCurrentIndex(item.
type);
1101 QString errorMessage;
1113 QStringList headerParts;
1115 m_ui->extractSFNamesFrom1stLineCheckBox->isEnabled() &&
1116 m_ui->extractSFNamesFrom1stLineCheckBox->isChecked()) {
1123 const QComboBox* combo =
static_cast<QComboBox*
>(
1124 m_ui->tableWidget->cellWidget(0, i));
1129 headerParts.size() >
static_cast<int>(i) ? headerParts[i]
1146 QStringList headerParts =
m_headerLine.simplified().split(
1150 if (headerParts.size() <
static_cast<int>(seq.size()))
return false;
1152 for (
int i = 0; i < headerParts.size(); ++i) {
1154 QString colHeader = headerParts[i].toUpper();
1156 switch (seq[i].
type) {
1160 if (!
CouldBeX(colHeader))
return false;
1163 if (!
CouldBeY(colHeader))
return false;
1166 if (!
CouldBeZ(colHeader))
return false;
1169 if (!
CouldBeNx(colHeader))
return false;
1172 if (!
CouldBeNy(colHeader))
return false;
1175 if (!
CouldBeNz(colHeader))
return false;
1179 if (!
CouldBeR(colHeader))
return false;
1183 if (!
CouldBeG(colHeader))
return false;
1187 if (!
CouldBeB(colHeader))
return false;
1191 if (!
CouldBeA(colHeader))
return false;
1194 if (!
CouldBeGrey(colHeader) && !colHeader.contains(
"INT"))
1201 if (!
CouldBeRGBi(colHeader) && !colHeader.contains(
"RGB"))
1205 if (!
CouldBeRGBf(colHeader) && !colHeader.contains(
"RGB"))
1227 QObject* obj = sender();
1234 QComboBox* changedCombo = qobject_cast<QComboBox*>(obj);
1235 if (!changedCombo) {
1243 static_cast<QComboBox*
>(
m_ui->tableWidget->cellWidget(0, i));
1245 if (changedCombo == combo) {
1252 QComboBox* nextCombo =
static_cast<QComboBox*
>(
1253 m_ui->tableWidget->cellWidget(0, i + 1));
1254 QComboBox* nextNextCombo =
static_cast<QComboBox*
>(
1255 m_ui->tableWidget->cellWidget(0, i + 2));
1259 nextNextCombo->currentIndex() ==
1261 nextCombo->blockSignals(
true);
1262 nextNextCombo->blockSignals(
true);
1279 nextCombo->blockSignals(
false);
1280 nextNextCombo->blockSignals(
false);
1287 if (combo->currentIndex() == index) {
1288 combo->blockSignals(
true);
1290 combo->blockSignals(
false);
1303 QObject* obj = sender();
1307 QToolButton* shortcutButton =
static_cast<QToolButton*
>(obj);
1309 char newSeparator = 0;
1310 if (shortcutButton ==
m_ui->toolButtonShortcutSpace)
1312 else if (shortcutButton ==
m_ui->toolButtonShortcutComma)
1314 else if (shortcutButton ==
m_ui->toolButtonShortcutSemicolon)
1317 if (newSeparator != 0 &&
getSeparator() != newSeparator) {
1323 m_ui->commaDecimalCheckBox->blockSignals(
true);
1324 if (sep == QChar(
','))
1326 m_ui->commaDecimalCheckBox->setEnabled(
false);
1329 m_ui->commaDecimalCheckBox->setEnabled(
true);
1331 m_ui->commaDecimalCheckBox->blockSignals(
false);
1332 m_ui->lineEditSeparator->setText(sep);
1336 return static_cast<unsigned>(
1337 floor(
m_ui->maxCloudSizeDoubleSpinBox->value() * 1.0e6));
1341 return m_ui->show2DLabelsCheckBox->isEnabled() &&
1342 m_ui->show2DLabelsCheckBox->isChecked();
static bool CouldBeR(const QString &colHeader)
static bool CouldBeGf(const QString &colHeader)
static bool CouldBeRf(const QString &colHeader)
static bool CouldBeA(const QString &colHeader)
static bool CouldBeNy(const QString &colHeader)
static bool CouldBeY(const QString &colHeader)
static bool CouldBeNx(const QString &colHeader)
static unsigned XYZW_BITS
static double s_maxCloudSizeDoubleSpinBoxValue
static AsciiOpenContext s_asciiOpenContext
Semi-persistent loading context.
static bool CouldBeZ(const QString &colHeader)
static bool CouldBeG(const QString &colHeader)
static const unsigned MAX_COLUMNS
static bool CouldBeAf(const QString &colHeader)
static bool CouldBeB(const QString &colHeader)
static bool CouldBeRGBi(const QString &colHeader)
static bool CouldBeNz(const QString &colHeader)
static bool CouldBeBf(const QString &colHeader)
static int EnabledBits(unsigned bitField)
static bool CouldBeScal(const QString &colHeader)
static bool CouldBeRGBf(const QString &colHeader)
static const unsigned LINES_READ_FOR_STATS
static bool CouldBeLabel(const QString &colHeader)
static unsigned s_maxLabelCount
static const unsigned DISPLAYED_LINES
static bool CouldBeGrey(const QString &colHeader)
static bool CouldBeX(const QString &colHeader)
const unsigned ASCII_OPEN_DLG_TYPES_COUNT
const char ASCII_OPEN_DLG_TYPES_NAMES[ASCII_OPEN_DLG_TYPES_COUNT][20]
Ui_AsciiOpenDialog * m_ui
bool useCommaAsDecimal() const
Returns whether comma should be used as decimal point.
void checkSelectedColumnsValidity()
void setSkippedLines(int linesCount)
Sets the number of lines to skip.
unsigned char getSeparator() const
Returns user selected separator.
void columnsTypeHasChanged(int index)
void commaDecimalCheckBoxToggled(bool)
Slot called when the 'comma as decimal' checkbox is toggled.
void setInput(const QString &filename, QTextStream *stream=nullptr)
Sets the input filename or text stream.
bool showLabelsIn2D() const
Whether labels should be visible in 2D.
std::vector< ColumnType > m_columnType
Identifies columns with numbers only [mandatory].
unsigned getColumnsCount() const
Returns columns count per line.
std::vector< SequenceItem > Sequence
ASCII open sequence.
bool restorePreviousContext()
Restores the previous context ('Apply all' button)
void shortcutButtonPressed()
bool safeSequence() const
Returns whether the current sequence is 'safe'.
static void ResetApplyAll()
Resets the "apply all" flag (if set)
AsciiOpenDlg(QWidget *parent=nullptr)
Default constructor.
void autoFindBestSeparator()
Tries to guess the best separator automagically.
static bool CheckOpenSequence(const Sequence &sequence, QString &errorMessage)
Checks the "opening" sequence as set by the user.
void setSeparator(QChar)
Sets the current separator.
Sequence getOpenSequence() const
Returns the whole "opening" sequence as set by the user.
~AsciiOpenDlg() override
Default destructor.
void onSeparatorChange(const QString &separator)
Slot called when separator changes.
void updateTable()
Forces the table to update itself.
unsigned getMaxCloudSize() const
Returns the max number of points per cloud.
const unsigned CC_MAX_NUMBER_OF_POINTS_PER_CLOUD
constexpr Qt::SplitBehavior SkipEmptyParts
MiniVec< float, N > floor(const MiniVec< float, N > &a)
AsciiOpenDlg::Sequence sequence
bool extractSFNameFrom1stLine
void load(Ui_AsciiOpenDialog *ui) const
Restores state.
void save(Ui_AsciiOpenDialog *ui)
Saves state.
AsciiOpenContext()
Default initializer.
double maxPointCountPerCloud
ASCII open sequence item.
CC_ASCII_OPEN_DLG_TYPES type