Import/Export known measurements to CSV.

This commit is contained in:
Roman Telezhynskyi 2023-11-07 17:38:20 +02:00
parent 84cc3a1393
commit 6336a94bd5
13 changed files with 1454 additions and 165 deletions

View File

@ -0,0 +1,550 @@
/************************************************************************
**
** @file dialogknownmeasurementscsvcolumns.cpp
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 7 11, 2023
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2023 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Valentina is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#include "dialogknownmeasurementscsvcolumns.h"
#include "ui_dialogknownmeasurementscsvcolumns.h"
#include "../vmisc/qxtcsvmodel.h"
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
#include "../vmisc/backport/qoverload.h"
#endif // QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
#include "../vtools/dialogs/dialogtoolbox.h"
#include <QPushButton>
#include <QShowEvent>
#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
#include "../vmisc/compatibility.h"
#endif
using namespace Qt::Literals::StringLiterals;
//---------------------------------------------------------------------------------------------------------------------
DialogKnownMeasurementsCSVColumns::DialogKnownMeasurementsCSVColumns(const QString &filename, QWidget *parent)
: QDialog(parent),
ui(new Ui::DialogKnownMeasurementsCSVColumns),
m_fileName{filename}
{
ui->setupUi(this);
}
//---------------------------------------------------------------------------------------------------------------------
DialogKnownMeasurementsCSVColumns::~DialogKnownMeasurementsCSVColumns()
{
delete ui;
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange)
{
// retranslate designer form (single inheritance approach)
ui->retranslateUi(this);
RetranslateLabels();
InitColumnsControls();
InitImportHeaders();
ShowImportPreview();
CheckStatus();
}
// remember to call base class implementation
QDialog::changeEvent(event);
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::showEvent(QShowEvent *event)
{
QDialog::showEvent(event);
if (event->spontaneous())
{
return;
}
if (m_isInitialized)
{
return;
}
// do your init stuff here
if (not m_fileName.isEmpty())
{
InitColumnsMap();
ShowInputPreview();
InitColumnsControls();
RetranslateLabels();
SetDefaultColumns();
InitImportHeaders();
ShowImportPreview();
connect(ui->comboBoxName, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&DialogKnownMeasurementsCSVColumns::ColumnChanged);
connect(ui->comboBoxGroup, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&DialogKnownMeasurementsCSVColumns::ColumnChanged);
connect(ui->comboBoxFullName, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&DialogKnownMeasurementsCSVColumns::ColumnChanged);
connect(ui->comboBoxFormula, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&DialogKnownMeasurementsCSVColumns::ColumnChanged);
connect(ui->comboBoxDescription, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&DialogKnownMeasurementsCSVColumns::ColumnChanged);
CheckStatus();
}
m_isInitialized = true; // first show windows are held
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::ColumnChanged()
{
auto *control = qobject_cast<QComboBox *>(sender());
if (control == ui->comboBoxName)
{
SaveColum(control, KnownMeasurementsColumns::Name);
}
else if (control == ui->comboBoxGroup)
{
SaveColum(control, KnownMeasurementsColumns::Group);
}
else if (control == ui->comboBoxFullName)
{
SaveColum(control, KnownMeasurementsColumns::FullName);
}
else if (control == ui->comboBoxFormula)
{
SaveColum(control, KnownMeasurementsColumns::Formula);
}
else if (control == ui->comboBoxDescription)
{
SaveColum(control, KnownMeasurementsColumns::Description);
}
}
//---------------------------------------------------------------------------------------------------------------------
bool DialogKnownMeasurementsCSVColumns::ColumnMandatory(int column) const
{
return column < static_cast<int>(KnownMeasurementsColumns::Group);
}
//---------------------------------------------------------------------------------------------------------------------
QString DialogKnownMeasurementsCSVColumns::ColumnHeader(int column) const
{
const auto individualColumn = static_cast<KnownMeasurementsColumns>(column);
switch (individualColumn)
{
case KnownMeasurementsColumns::Name:
return tr("Name", "measurement column");
case KnownMeasurementsColumns::Group:
return tr("Group", "measurement column");
case KnownMeasurementsColumns::FullName:
return tr("Full name", "measurement column");
case KnownMeasurementsColumns::Formula:
return tr("Formula", "measurement column");
case KnownMeasurementsColumns::Description:
return tr("Description", "measurement column");
default:
return {};
}
}
//---------------------------------------------------------------------------------------------------------------------
int DialogKnownMeasurementsCSVColumns::ImportColumnCount() const
{
return static_cast<int>(KnownMeasurementsColumns::LAST_DO_NOT_USE);
}
//---------------------------------------------------------------------------------------------------------------------
int DialogKnownMeasurementsCSVColumns::MinimumColumns() const
{
return 1;
}
//---------------------------------------------------------------------------------------------------------------------
bool DialogKnownMeasurementsCSVColumns::ColumnsValid()
{
ClearColumnCollor();
bool columnNameFlag = true;
bool columnGroupFlag = true;
bool columnFullNameFlag = true;
bool columnFormulaFlag = true;
bool columnDescriptionFlag = true;
const QColor errorColor = Qt::red;
auto ChangeColumnColor = [this, errorColor](QLabel *label, bool &columnFlag, KnownMeasurementsColumns column)
{
if (not ColumnValid(column))
{
ChangeColor(label, errorColor);
columnFlag = false;
}
};
ChangeColumnColor(ui->labelName, columnNameFlag, KnownMeasurementsColumns::Name);
ChangeColumnColor(ui->labelGroup, columnGroupFlag, KnownMeasurementsColumns::Group);
ChangeColumnColor(ui->labelFullName, columnFullNameFlag, KnownMeasurementsColumns::FullName);
ChangeColumnColor(ui->labelFormula, columnFormulaFlag, KnownMeasurementsColumns::Formula);
ChangeColumnColor(ui->labelDescription, columnDescriptionFlag, KnownMeasurementsColumns::Description);
return columnNameFlag && columnGroupFlag && columnFullNameFlag && columnFormulaFlag && columnDescriptionFlag;
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::ClearColumnCollor()
{
ChangeColor(ui->labelName, OkColor(this));
ChangeColor(ui->labelGroup, OkColor(this));
ChangeColor(ui->labelFullName, OkColor(this));
ChangeColor(ui->labelFormula, OkColor(this));
ChangeColor(ui->labelDescription, OkColor(this));
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::InitColumnsMap()
{
QSharedPointer<QxtCsvModel> csv = DialogKnownMeasurementsCSVColumns::CSVModel();
m_columnsMap.clear();
auto InitColumn = [this, csv](int column, int &index)
{
++index;
if (ColumnMandatory(column))
{
m_columnsMap[column] = index;
}
else
{
m_columnsMap[column] = csv->columnCount() >= index ? index : -1;
}
};
m_columnsMap.resize(static_cast<int>(KnownMeasurementsColumns::LAST_DO_NOT_USE));
int index = -1;
for (int column = 0; column < static_cast<int>(KnownMeasurementsColumns::LAST_DO_NOT_USE); ++column)
{
InitColumn(column, index);
}
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::InitColumnsControls()
{
const int inputColumnCount = CSVModel()->columnCount();
auto InitControl = [this, inputColumnCount](QComboBox *control, int column)
{
SCASSERT(control != nullptr)
int currentColumn = -2;
int index = control->currentIndex();
if (index != -1)
{
currentColumn = control->currentData().toInt();
}
control->blockSignals(true);
control->clear();
for (int i = 0; i < inputColumnCount; ++i)
{
control->addItem(QString::number(i + 1), i);
}
if (not ColumnMandatory(column))
{
control->addItem(tr("Skip"), -1);
}
control->setCurrentIndex(-1);
index = control->findData(currentColumn);
if (index != -1)
{
control->setCurrentIndex(index);
control->blockSignals(false);
}
else
{
control->blockSignals(false);
if (not ColumnMandatory(column))
{
control->setCurrentIndex(control->findData(-1));
}
else
{
control->setCurrentIndex(0);
}
}
};
InitControl(ui->comboBoxName, static_cast<int>(KnownMeasurementsColumns::Name));
InitControl(ui->comboBoxGroup, static_cast<int>(KnownMeasurementsColumns::Group));
InitControl(ui->comboBoxFullName, static_cast<int>(KnownMeasurementsColumns::FullName));
InitControl(ui->comboBoxFormula, static_cast<int>(KnownMeasurementsColumns::Formula));
InitControl(ui->comboBoxDescription, static_cast<int>(KnownMeasurementsColumns::Description));
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::InitImportHeaders()
{
if (m_fileName.isEmpty())
{
return;
}
const int columns = ImportColumnCount();
ui->tableWidgetImport->clear();
ui->tableWidgetImport->setColumnCount(columns);
auto AddHeader = [this](int column, bool visible = true)
{
auto *header = new QTableWidgetItem(ColumnHeader(column));
ui->tableWidgetImport->setHorizontalHeaderItem(column, header);
ui->tableWidgetImport->setColumnHidden(column, not visible);
};
AddHeader(static_cast<int>(KnownMeasurementsColumns::Name));
AddHeader(static_cast<int>(KnownMeasurementsColumns::Group));
AddHeader(static_cast<int>(KnownMeasurementsColumns::FullName));
AddHeader(static_cast<int>(KnownMeasurementsColumns::Formula));
AddHeader(static_cast<int>(KnownMeasurementsColumns::Description));
}
//---------------------------------------------------------------------------------------------------------------------
QSharedPointer<QxtCsvModel> DialogKnownMeasurementsCSVColumns::CSVModel() const
{
return QSharedPointer<QxtCsvModel>::create(m_fileName, nullptr, m_withHeader, m_separator, m_codec);
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::ShowInputPreview()
{
if (m_fileName.isEmpty())
{
return;
}
QSharedPointer<QxtCsvModel> csv = DialogKnownMeasurementsCSVColumns::CSVModel();
const int columns = csv->columnCount();
const int rows = csv->rowCount();
ui->tableWidgetInput->clear();
ui->tableWidgetInput->setColumnCount(columns);
ui->tableWidgetInput->setRowCount(rows);
ui->tableWidgetInput->horizontalHeader()->setVisible(m_withHeader);
if (m_withHeader)
{
for (int column = 0; column < columns; ++column)
{
auto *header = new QTableWidgetItem(csv->headerText(column));
header->setToolTip(QString::number(column + 1));
ui->tableWidgetInput->setHorizontalHeaderItem(column, header);
}
}
for (int row = 0; row < rows; ++row)
{
for (int column = 0; column < columns; ++column)
{
const QString text = csv->text(row, column);
auto *item = new QTableWidgetItem(text);
item->setToolTip(text);
// set the item non-editable (view only), and non-selectable
Qt::ItemFlags flags = item->flags();
flags &= ~(Qt::ItemIsEditable); // reset/clear the flag
item->setFlags(flags);
ui->tableWidgetInput->setItem(row, column, item);
}
}
ui->tableWidgetInput->resizeColumnsToContents();
ui->tableWidgetInput->resizeRowsToContents();
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::ShowImportPreview()
{
if (m_fileName.isEmpty())
{
return;
}
QSharedPointer<QxtCsvModel> csv = DialogKnownMeasurementsCSVColumns::CSVModel();
const int importColumns = ImportColumnCount();
const int columns = csv->columnCount();
const int rows = csv->rowCount();
ui->tableWidgetImport->clearContents();
ui->tableWidgetImport->setRowCount(rows);
for (int row = 0; row < rows; ++row)
{
for (int column = 0; column < importColumns; ++column)
{
const int tableColumn = m_columnsMap.at(column);
if (tableColumn >= 0 && tableColumn < columns)
{
const QString text = csv->text(row, tableColumn);
auto *item = new QTableWidgetItem(text);
item->setToolTip(text);
// set the item non-editable (view only), and non-selectable
Qt::ItemFlags flags = item->flags();
flags &= ~(Qt::ItemIsEditable); // reset/clear the flag
item->setFlags(flags);
ui->tableWidgetImport->setItem(row, column, item);
}
}
}
ui->tableWidgetImport->resizeColumnsToContents();
ui->tableWidgetImport->resizeRowsToContents();
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::RetranslateLabels()
{
ui->labelName->setText(tr("Name") + "*:"_L1);
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::SetDefaultColumns()
{
auto SetDefault = [this](QComboBox *control, int column)
{
SCASSERT(control != nullptr)
int index = control->findData(m_columnsMap.at(column));
if (index != -1)
{
control->setCurrentIndex(index);
}
};
SetDefault(ui->comboBoxName, static_cast<int>(KnownMeasurementsColumns::Name));
SetDefault(ui->comboBoxGroup, static_cast<int>(KnownMeasurementsColumns::Group));
SetDefault(ui->comboBoxFullName, static_cast<int>(KnownMeasurementsColumns::FullName));
SetDefault(ui->comboBoxFormula, static_cast<int>(KnownMeasurementsColumns::Formula));
SetDefault(ui->comboBoxDescription, static_cast<int>(KnownMeasurementsColumns::Description));
}
//---------------------------------------------------------------------------------------------------------------------
void DialogKnownMeasurementsCSVColumns::CheckStatus()
{
auto SetStatus = [this](bool status)
{
QPushButton *bOk = ui->buttonBox->button(QDialogButtonBox::Ok);
// cppcheck-suppress unknownMacro
SCASSERT(bOk != nullptr)
bOk->setEnabled(status);
};
if (m_fileName.isEmpty())
{
SetStatus(false);
ui->labelStatus->setText(tr("File path is empty"));
return;
}
QSharedPointer<QxtCsvModel> csv = DialogKnownMeasurementsCSVColumns::CSVModel();
const int columns = csv->columnCount();
if (columns < MinimumColumns())
{
SetStatus(false);
ui->labelStatus->setText(tr("Not enough columns"));
return;
}
const int rows = csv->rowCount();
if (rows < 1)
{
SetStatus(false);
ui->labelStatus->setText(tr("Not enough data to import"));
return;
}
if (not ColumnsValid())
{
SetStatus(false);
ui->labelStatus->setText(tr("Please, select unique number for each column"));
return;
}
SetStatus(true);
ui->labelStatus->setText(tr("Ready"));
}
//---------------------------------------------------------------------------------------------------------------------
template <class T> bool DialogKnownMeasurementsCSVColumns::ColumnValid(T column) const
{
const int columnNumber = static_cast<int>(column);
int value = m_columnsMap.at(columnNumber);
if (value == -1 && not ColumnMandatory(columnNumber))
{
return true;
}
for (int c = 0; c < m_columnsMap.size(); ++c)
{
if (c == columnNumber)
{
continue;
}
if (value == m_columnsMap.at(c))
{
return false;
}
}
return true;
}
//---------------------------------------------------------------------------------------------------------------------
template <class T> void DialogKnownMeasurementsCSVColumns::SaveColum(QComboBox *control, T column)
{
SCASSERT(control != nullptr)
const int columnNumber = static_cast<int>(column);
m_columnsMap[columnNumber] = control->currentData().toInt();
ShowImportPreview();
CheckStatus();
}

View File

@ -0,0 +1,138 @@
/************************************************************************
**
** @file dialogknownmeasurementscsvcolumns.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 7 11, 2023
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2023 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Valentina is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef DIALOGKNOWNMEASUREMENTSCSVCOLUMNS_H
#define DIALOGKNOWNMEASUREMENTSCSVCOLUMNS_H
#include <QDialog>
class QxtCsvModel;
class QComboBox;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
class VTextCodec;
#endif
enum class KnownMeasurementsColumns : qint8
{
Name = 0,
Group = 1, // optional
FullName = 2, // optional
Formula = 3, // optional
Description = 4, // optional
LAST_DO_NOT_USE = 5
};
namespace Ui
{
class DialogKnownMeasurementsCSVColumns;
}
class DialogKnownMeasurementsCSVColumns : public QDialog
{
Q_OBJECT // NOLINT
public:
explicit DialogKnownMeasurementsCSVColumns(const QString &filename, QWidget *parent = nullptr);
~DialogKnownMeasurementsCSVColumns() override;
auto ColumnsMap() const -> QVector<int>;
void SetWithHeader(bool withHeader);
void SetSeparator(const QChar &separator);
void SetCodec(VTextCodec *codec);
protected:
void changeEvent(QEvent *event) override;
void showEvent(QShowEvent *event) override;
private slots:
void ColumnChanged();
private:
// cppcheck-suppress unknownMacro
Q_DISABLE_COPY_MOVE(DialogKnownMeasurementsCSVColumns) // NOLINT
Ui::DialogKnownMeasurementsCSVColumns *ui;
bool m_isInitialized{false};
QString m_fileName;
bool m_withHeader{false};
QChar m_separator{','};
VTextCodec *m_codec{nullptr};
QVector<int> m_columnsMap{};
auto ColumnMandatory(int column) const -> bool;
auto ColumnHeader(int column) const -> QString;
auto ImportColumnCount() const -> int;
auto MinimumColumns() const -> int;
auto ColumnsValid() -> bool;
void ClearColumnCollor();
void InitColumnsMap();
void InitColumnsControls();
void InitImportHeaders();
auto CSVModel() const -> QSharedPointer<QxtCsvModel>;
void ShowInputPreview();
void ShowImportPreview();
void RetranslateLabels();
void SetDefaultColumns();
void CheckStatus();
template <class T> auto ColumnValid(T column) const -> bool;
template <class T> void SaveColum(QComboBox *control, T column);
};
//---------------------------------------------------------------------------------------------------------------------
inline auto DialogKnownMeasurementsCSVColumns::ColumnsMap() const -> QVector<int>
{
return m_columnsMap;
}
//---------------------------------------------------------------------------------------------------------------------
inline void DialogKnownMeasurementsCSVColumns::SetWithHeader(bool withHeader)
{
m_withHeader = withHeader;
}
//---------------------------------------------------------------------------------------------------------------------
inline void DialogKnownMeasurementsCSVColumns::SetSeparator(const QChar &separator)
{
m_separator = separator;
}
//---------------------------------------------------------------------------------------------------------------------
inline void DialogKnownMeasurementsCSVColumns::SetCodec(VTextCodec *codec)
{
m_codec = codec;
}
#endif // DIALOGKNOWNMEASUREMENTSCSVCOLUMNS_H

View File

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DialogKnownMeasurementsCSVColumns</class>
<widget class="QDialog" name="DialogKnownMeasurementsCSVColumns">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1036</width>
<height>713</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Preview</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Input</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTableWidget" name="tableWidgetInput">
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Import</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableWidget" name="tableWidgetImport">
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Columns</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelName">
<property name="text">
<string notr="true">Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxName"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelGroup">
<property name="text">
<string notr="true">Group:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxGroup"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelFullName">
<property name="text">
<string>Full name:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBoxFullName"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelFormula">
<property name="text">
<string notr="true">Formula:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboBoxFormula"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelDescription">
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="comboBoxDescription"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="labelStatus">
<property name="text">
<string>Ready</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DialogKnownMeasurementsCSVColumns</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DialogKnownMeasurementsCSVColumns</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -37,10 +37,16 @@
#include <QPushButton> #include <QPushButton>
#include <QShowEvent> #include <QShowEvent>
#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
#include "../vmisc/compatibility.h"
#endif
using namespace Qt::Literals::StringLiterals;
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
DialogMeasurementsCSVColumns::DialogMeasurementsCSVColumns(const QString &filename, MeasurementsType type, DialogMeasurementsCSVColumns::DialogMeasurementsCSVColumns(const QString &filename, MeasurementsType type,
QWidget *parent) : QWidget *parent)
QDialog(parent), : QDialog(parent),
ui(new Ui::DialogMeasurementsCSVColumns), ui(new Ui::DialogMeasurementsCSVColumns),
m_fileName{filename}, m_fileName{filename},
m_type(type) m_type(type)
@ -53,8 +59,8 @@ DialogMeasurementsCSVColumns::DialogMeasurementsCSVColumns(const QString &filena
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
DialogMeasurementsCSVColumns::DialogMeasurementsCSVColumns(const QString &filename, MeasurementsType type, DialogMeasurementsCSVColumns::DialogMeasurementsCSVColumns(const QString &filename, MeasurementsType type,
const QList<MeasurementDimension_p> &dimensions, const QList<MeasurementDimension_p> &dimensions,
QWidget *parent) : QWidget *parent)
QDialog(parent), : QDialog(parent),
ui(new Ui::DialogMeasurementsCSVColumns), ui(new Ui::DialogMeasurementsCSVColumns),
m_fileName{filename}, m_fileName{filename},
m_type(type), m_type(type),
@ -116,37 +122,36 @@ void DialogMeasurementsCSVColumns::showEvent(QShowEvent *event)
InitImportHeaders(); InitImportHeaders();
ShowImportPreview(); ShowImportPreview();
connect(ui->comboBoxName, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(ui->comboBoxName, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
this, &DialogMeasurementsCSVColumns::ColumnChanged); &DialogMeasurementsCSVColumns::ColumnChanged);
connect(ui->comboBoxValue, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(ui->comboBoxValue, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
this, &DialogMeasurementsCSVColumns::ColumnChanged); &DialogMeasurementsCSVColumns::ColumnChanged);
if (m_type == MeasurementsType::Multisize) if (m_type == MeasurementsType::Multisize)
{ {
if (not m_dimensions.empty()) if (not m_dimensions.empty())
{ {
connect(ui->comboBoxShiftA, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(ui->comboBoxShiftA, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
this, &DialogMeasurementsCSVColumns::ColumnChanged); &DialogMeasurementsCSVColumns::ColumnChanged);
} }
if (m_dimensions.size() > 1) if (m_dimensions.size() > 1)
{ {
connect(ui->comboBoxShiftB, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(ui->comboBoxShiftB, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
this, &DialogMeasurementsCSVColumns::ColumnChanged); &DialogMeasurementsCSVColumns::ColumnChanged);
} }
if (m_dimensions.size() > 2) if (m_dimensions.size() > 2)
{ {
connect(ui->comboBoxShiftC, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(ui->comboBoxShiftC, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
this, &DialogMeasurementsCSVColumns::ColumnChanged); &DialogMeasurementsCSVColumns::ColumnChanged);
} }
} }
connect(ui->comboBoxFullName, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
connect(ui->comboBoxFullName, QOverload<int>::of(&QComboBox::currentIndexChanged), &DialogMeasurementsCSVColumns::ColumnChanged);
this, &DialogMeasurementsCSVColumns::ColumnChanged); connect(ui->comboBoxDescription, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
connect(ui->comboBoxDescription, QOverload<int>::of(&QComboBox::currentIndexChanged), &DialogMeasurementsCSVColumns::ColumnChanged);
this, &DialogMeasurementsCSVColumns::ColumnChanged);
CheckStatus(); CheckStatus();
} }
@ -245,6 +250,7 @@ auto DialogMeasurementsCSVColumns::ColumnHeader(int column) const -> QString
} }
else else
{ {
const QString suffix = QStringLiteral(" (%1):");
const auto multisizeColumn = static_cast<MultisizeMeasurementsColumns>(column); const auto multisizeColumn = static_cast<MultisizeMeasurementsColumns>(column);
switch (multisizeColumn) switch (multisizeColumn)
{ {
@ -256,7 +262,7 @@ auto DialogMeasurementsCSVColumns::ColumnHeader(int column) const -> QString
if (not m_dimensions.empty()) if (not m_dimensions.empty())
{ {
MeasurementDimension_p dimension = m_dimensions.at(0); MeasurementDimension_p dimension = m_dimensions.at(0);
return tr("Shift (%1):", "measurement column").arg(dimension->Name()); return tr("Shift", "measurement column") + suffix.arg(dimension->Name());
} }
else else
{ {
@ -266,7 +272,7 @@ auto DialogMeasurementsCSVColumns::ColumnHeader(int column) const -> QString
if (m_dimensions.size() > 1) if (m_dimensions.size() > 1)
{ {
MeasurementDimension_p dimension = m_dimensions.at(1); MeasurementDimension_p dimension = m_dimensions.at(1);
return tr("Shift (%1):", "measurement column").arg(dimension->Name()); return tr("Shift", "measurement column") + suffix.arg(dimension->Name());
} }
else else
{ {
@ -276,7 +282,7 @@ auto DialogMeasurementsCSVColumns::ColumnHeader(int column) const -> QString
if (m_dimensions.size() > 2) if (m_dimensions.size() > 2)
{ {
MeasurementDimension_p dimension = m_dimensions.at(2); MeasurementDimension_p dimension = m_dimensions.at(2);
return tr("Shift (%1):", "measurement column").arg(dimension->Name()); return tr("Shift", "measurement column") + suffix.arg(dimension->Name());
} }
else else
{ {
@ -336,8 +342,8 @@ auto DialogMeasurementsCSVColumns::ColumnsValid() -> bool
const QColor errorColor = Qt::red; const QColor errorColor = Qt::red;
auto ChangeColumnColor = [this, errorColor] auto ChangeColumnColor = [this, errorColor](QLabel *label, bool &columnFlag,
(QLabel *label, bool &columnFlag, MultisizeMeasurementsColumns multisizeColumn, MultisizeMeasurementsColumns multisizeColumn,
IndividualMeasurementsColumns individualColumn) IndividualMeasurementsColumns individualColumn)
{ {
if (m_type == MeasurementsType::Multisize ? not ColumnValid(multisizeColumn) if (m_type == MeasurementsType::Multisize ? not ColumnValid(multisizeColumn)
@ -721,32 +727,34 @@ void DialogMeasurementsCSVColumns::HackColumnControls()
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void DialogMeasurementsCSVColumns::RetranslateLabels() void DialogMeasurementsCSVColumns::RetranslateLabels()
{ {
ui->labelName->setText(tr("Name") + "*:"); ui->labelName->setText(tr("Name", "measurement column") + "*:"_L1);
if (m_type == MeasurementsType::Individual) if (m_type == MeasurementsType::Individual)
{ {
ui->labelValue->setText(tr("Value") + "*:"); ui->labelValue->setText(tr("Value", "measurement column") + "*:"_L1);
} }
else else
{ {
ui->labelValue->setText(tr("Base value") + "*:"); ui->labelValue->setText(tr("Base value", "measurement column") + "*:"_L1);
const QString suffix = QStringLiteral(" (%1)");
if (not m_dimensions.empty()) if (not m_dimensions.empty())
{ {
MeasurementDimension_p dimension = m_dimensions.at(0); MeasurementDimension_p dimension = m_dimensions.at(0);
ui->labelShiftA->setText(tr("Shift (%1)*:").arg(dimension->Name())); ui->labelShiftA->setText(tr("Shift", "measurement column") + suffix.arg(dimension->Name()));
} }
if (m_dimensions.size() > 1) if (m_dimensions.size() > 1)
{ {
MeasurementDimension_p dimension = m_dimensions.at(1); MeasurementDimension_p dimension = m_dimensions.at(1);
ui->labelShiftB->setText(tr("Shift (%1)*:").arg(dimension->Name())); ui->labelShiftB->setText(tr("Shift", "measurement column") + suffix.arg(dimension->Name()));
} }
if (m_dimensions.size() > 2) if (m_dimensions.size() > 2)
{ {
MeasurementDimension_p dimension = m_dimensions.at(2); MeasurementDimension_p dimension = m_dimensions.at(2);
ui->labelShiftC->setText(tr("Shift (%1)*:").arg(dimension->Name())); ui->labelShiftC->setText(tr("Shift", "measurement column") + suffix.arg(dimension->Name()));
} }
} }
} }
@ -845,8 +853,7 @@ void DialogMeasurementsCSVColumns::CheckStatus()
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
template<class T> template <class T> void DialogMeasurementsCSVColumns::HackWidget(T **widget)
void DialogMeasurementsCSVColumns::HackWidget(T **widget)
{ {
delete *widget; delete *widget;
*widget = new T(); *widget = new T();
@ -854,8 +861,7 @@ void DialogMeasurementsCSVColumns::HackWidget(T **widget)
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
template<class T> template <class T> auto DialogMeasurementsCSVColumns::ColumnValid(T column) const -> bool
auto DialogMeasurementsCSVColumns::ColumnValid(T column) const -> bool
{ {
const int columnNumber = static_cast<int>(column); const int columnNumber = static_cast<int>(column);
int value = m_columnsMap.at(columnNumber); int value = m_columnsMap.at(columnNumber);
@ -882,8 +888,7 @@ auto DialogMeasurementsCSVColumns::ColumnValid(T column) const -> bool
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
template<class T> template <class T> void DialogMeasurementsCSVColumns::SaveColum(QComboBox *control, T column)
void DialogMeasurementsCSVColumns::SaveColum(QComboBox *control, T column)
{ {
SCASSERT(control != nullptr) SCASSERT(control != nullptr)

View File

@ -19,7 +19,8 @@ SOURCES += \
$$PWD/vtapesettings.cpp \ $$PWD/vtapesettings.cpp \
$$PWD/dialogs/dialogsetupmultisize.cpp \ $$PWD/dialogs/dialogsetupmultisize.cpp \
$$PWD/vtapeshortcutmanager.cpp \ $$PWD/vtapeshortcutmanager.cpp \
$$PWD/tkmmainwindow.cpp $$PWD/tkmmainwindow.cpp \
$$PWD/dialogs/dialogknownmeasurementscsvcolumns.cpp
*msvc*:SOURCES += $$PWD/stable.cpp *msvc*:SOURCES += $$PWD/stable.cpp
@ -42,7 +43,8 @@ HEADERS += \
$$PWD/vtapesettings.h \ $$PWD/vtapesettings.h \
$$PWD/dialogs/dialogsetupmultisize.h \ $$PWD/dialogs/dialogsetupmultisize.h \
$$PWD/vtapeshortcutmanager.h \ $$PWD/vtapeshortcutmanager.h \
$$PWD/tkmmainwindow.h $$PWD/tkmmainwindow.h \
$$PWD/dialogs/dialogknownmeasurementscsvcolumns.h
FORMS += \ FORMS += \
$$PWD/dialogs/dialogdimensioncustomnames.ui \ $$PWD/dialogs/dialogdimensioncustomnames.ui \
@ -57,5 +59,6 @@ FORMS += \
$$PWD/dialogs/configpages/tapepreferencesconfigurationpage.ui \ $$PWD/dialogs/configpages/tapepreferencesconfigurationpage.ui \
$$PWD/dialogs/configpages/tapepreferencespathpage.ui \ $$PWD/dialogs/configpages/tapepreferencespathpage.ui \
$$PWD/dialogs/dialogsetupmultisize.ui \ $$PWD/dialogs/dialogsetupmultisize.ui \
$$PWD/tkmmainwindow.ui $$PWD/tkmmainwindow.ui \
$$PWD/dialogs/dialogknownmeasurementscsvcolumns.ui

View File

@ -78,6 +78,9 @@ VToolApp {
"configpages/tapepreferencespathpage.ui", "configpages/tapepreferencespathpage.ui",
"dialogdimensioncustomnames.cpp", "dialogdimensioncustomnames.cpp",
"dialogdimensionlabels.cpp", "dialogdimensionlabels.cpp",
"dialogknownmeasurementscsvcolumns.cpp",
"dialogknownmeasurementscsvcolumns.h",
"dialogknownmeasurementscsvcolumns.ui",
"dialogmeasurementscsvcolumns.cpp", "dialogmeasurementscsvcolumns.cpp",
"dialogrestrictdimension.cpp", "dialogrestrictdimension.cpp",
"dialogabouttape.cpp", "dialogabouttape.cpp",

View File

@ -41,6 +41,7 @@
#include "../vmisc/theme/vtheme.h" #include "../vmisc/theme/vtheme.h"
#include "../vmisc/vsysexits.h" #include "../vmisc/vsysexits.h"
#include "dialogs/dialogabouttape.h" #include "dialogs/dialogabouttape.h"
#include "dialogs/dialogknownmeasurementscsvcolumns.h"
#include "knownmeasurements/vknownmeasurements.h" #include "knownmeasurements/vknownmeasurements.h"
#include "mapplication.h" // Should be last because of definning qApp #include "mapplication.h" // Should be last because of definning qApp
#include "quuid.h" #include "quuid.h"
@ -468,54 +469,35 @@ auto TKMMainWindow::eventFilter(QObject *object, QEvent *event) -> bool
void TKMMainWindow::ExportToCSVData(const QString &fileName, bool withHeader, int mib, const QChar &separator) void TKMMainWindow::ExportToCSVData(const QString &fileName, bool withHeader, int mib, const QChar &separator)
{ {
QxtCsvModel csv; QxtCsvModel csv;
const int columns = ui->tableWidget->columnCount();
{ int columns = 5;
int colCount = 0; int colCount = 0;
for (int column = 0; column < columns; ++column) for (int column = 0; column <= columns; ++column)
{
if (not ui->tableWidget->isColumnHidden(column))
{ {
csv.insertColumn(colCount++); csv.insertColumn(colCount++);
} }
}
}
if (withHeader) if (withHeader)
{ {
int colCount = 0; for (int column = 0; column <= columns; ++column)
for (int column = 0; column < columns; ++column)
{ {
if (not ui->tableWidget->isColumnHidden(column)) csv.setHeaderText(column, CSVColumnHeader(column));
{
QString text;
if (QTableWidgetItem *header = ui->tableWidget->horizontalHeaderItem(column))
{
text = header->text();
}
csv.setHeaderText(colCount, text);
++colCount;
}
} }
} }
const int rows = ui->tableWidget->rowCount(); const QMap<int, VKnownMeasurement> orderedTable = m_known.OrderedMeasurments();
for (int row = 0; row < rows; ++row) int row = 0;
for (auto iMap = orderedTable.constBegin(); iMap != orderedTable.constEnd(); ++iMap)
{ {
const VKnownMeasurement &m = iMap.value();
csv.insertRow(row); csv.insertRow(row);
int colCount = 0; csv.setText(row, 0, m.name);
for (int column = 0; column < columns; ++column) csv.setText(row, 1, m.fullName);
{ csv.setText(row, 2, m.group);
if (not ui->tableWidget->isColumnHidden(column)) csv.setText(row, 3, m.formula);
{ csv.setText(row, 4, m.description);
QString text; ++row;
if (QTableWidgetItem *item = ui->tableWidget->item(row, column))
{
text = item->text();
}
csv.setText(row, colCount, text);
++colCount;
}
}
} }
QString error; QString error;
@ -729,6 +711,53 @@ void TKMMainWindow::ShowWindow() const
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void TKMMainWindow::ImportDataFromCSV() void TKMMainWindow::ImportDataFromCSV()
{ {
if (m_m == nullptr)
{
return;
}
const QString filters = tr("Comma-Separated Values") + QStringLiteral(" (*.csv)");
const QString suffix = QStringLiteral("csv");
QString fileName = QFileDialog::getOpenFileName(this, tr("Import from CSV"), QDir::homePath(), filters, nullptr,
VAbstractApplication::VApp()->NativeFileDialog());
if (fileName.isEmpty())
{
return;
}
QFileInfo f(fileName);
if (f.suffix().isEmpty() && f.suffix() != suffix)
{
fileName += '.'_L1 + suffix;
}
DialogExportToCSV dialog(this);
dialog.SetWithHeader(VAbstractApplication::VApp()->Settings()->GetCSVWithHeader());
dialog.SetSelectedMib(VAbstractApplication::VApp()->Settings()->GetCSVCodec());
dialog.SetSeparator(VAbstractApplication::VApp()->Settings()->GetCSVSeparator());
dialog.ShowFilePreview(fileName);
if (dialog.exec() == QDialog::Accepted)
{
VAbstractApplication::VApp()->Settings()->SetCSVSeparator(dialog.GetSeparator());
VAbstractApplication::VApp()->Settings()->SetCSVCodec(dialog.GetSelectedMib());
VAbstractApplication::VApp()->Settings()->SetCSVWithHeader(dialog.IsWithHeader());
auto columns = QSharedPointer<DialogKnownMeasurementsCSVColumns>::create(fileName, this);
columns->SetWithHeader(dialog.IsWithHeader());
columns->SetSeparator(dialog.GetSeparator());
columns->SetCodec(VTextCodec::codecForMib(dialog.GetSelectedMib()));
if (columns->exec() == QDialog::Accepted)
{
QxtCsvModel csv(fileName, nullptr, dialog.IsWithHeader(), dialog.GetSeparator(),
VTextCodec::codecForMib(dialog.GetSelectedMib()));
const QVector<int> map = columns->ColumnsMap();
ImportKnownMeasurements(csv, map, dialog.IsWithHeader());
}
}
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
@ -2429,3 +2458,184 @@ void TKMMainWindow::InitMeasurementDiagramList()
ui->comboBoxDiagram->addItem(title, i.key()); ui->comboBoxDiagram->addItem(title, i.key());
} }
} }
//---------------------------------------------------------------------------------------------------------------------
void TKMMainWindow::ShowError(const QString &text)
{
QMessageBox messageBox(this);
messageBox.setIcon(QMessageBox::Critical);
messageBox.setText(text);
messageBox.setStandardButtons(QMessageBox::Ok);
messageBox.setDefaultButton(QMessageBox::Ok);
messageBox.exec();
}
//---------------------------------------------------------------------------------------------------------------------
void TKMMainWindow::RefreshDataAfterImport()
{
const int currentRow = ui->tableWidget->currentRow();
m_search->AddRow(currentRow);
m_known = VKnownMeasurements();
RefreshTable();
m_search->RefreshList(ui->lineEditFind->text());
ui->tableWidget->selectRow(currentRow);
ui->actionExportToCSV->setEnabled(ui->tableWidget->rowCount() > 0);
MeasurementsWereSaved(false);
}
//---------------------------------------------------------------------------------------------------------------------
auto TKMMainWindow::CheckMName(const QString &name, const QSet<QString> &importedNames) const -> QString
{
if (name.isEmpty())
{
throw VException(tr("Measurement name is empty."));
}
if (importedNames.contains(name))
{
throw VException(tr("Imported file must not contain the same name twice."));
}
QRegularExpression rx(NameRegExp(VariableRegex::KnownMeasurement));
if (not rx.match(name).hasMatch())
{
throw VException(tr("Measurement '%1' doesn't match regex pattern.").arg(name));
}
if (m_known.Measurments().contains(name))
{
throw VException(tr("Measurement '%1' already used in the file.").arg(name));
}
return name;
}
//---------------------------------------------------------------------------------------------------------------------
auto TKMMainWindow::CSVColumnHeader(int column) const -> QString
{
switch (column)
{
case 0: // name
return tr("Name", "measurement column");
case 1: // full name
return tr("Full name", "measurement column");
case 2: // group
return tr("Group", "measurement column");
case 3: // formula
return tr("Formula", "measurement column");
case 4: // description
return tr("Description", "measurement column");
default:
return {};
}
}
//---------------------------------------------------------------------------------------------------------------------
auto TKMMainWindow::ReadCSV(const QxtCsvModel &csv, const QVector<int> &map, bool withHeader)
-> QVector<VKnownMeasurement>
{
QVector<VKnownMeasurement> measurements;
QSet<QString> importedNames;
const int rows = csv.rowCount();
for (int i = 0; i < rows; ++i)
{
try
{
const int nameColumn = map.at(static_cast<int>(KnownMeasurementsColumns::Name));
const QString name = csv.text(i, nameColumn).simplified();
if (name.isEmpty())
{
ShowError(tr("Error in row %1. The measurement name is empty.").arg(i));
continue;
}
VKnownMeasurement measurement;
const QString mName = CheckMName(name, importedNames);
importedNames.insert(mName);
measurement.name = mName;
const int nameGroup = map.at(static_cast<int>(KnownMeasurementsColumns::Group));
if (nameGroup >= 0)
{
measurement.group = csv.text(i, nameGroup).simplified();
}
const int nameFullName = map.at(static_cast<int>(KnownMeasurementsColumns::FullName));
if (nameFullName >= 0)
{
measurement.fullName = csv.text(i, nameFullName);
}
const int nameFormula = map.at(static_cast<int>(KnownMeasurementsColumns::Formula));
if (nameFormula >= 0)
{
measurement.formula = csv.text(i, nameFormula);
}
const int nameDescription = map.at(static_cast<int>(KnownMeasurementsColumns::Description));
if (nameDescription >= 0)
{
measurement.description = csv.text(i, nameDescription);
}
measurements.append(measurement);
}
catch (VException &e)
{
int rowIndex = i + 1;
if (withHeader)
{
++rowIndex;
}
ShowError(tr("Error in row %1. %2").arg(rowIndex).arg(e.ErrorMessage()));
return {};
}
}
return measurements;
}
//---------------------------------------------------------------------------------------------------------------------
void TKMMainWindow::ImportKnownMeasurements(const QxtCsvModel &csv, const QVector<int> &map, bool withHeader)
{
if (csv.columnCount() < 2)
{
ShowError(tr("Individual measurements require at least 2 columns."));
return;
}
QVector<VKnownMeasurement> measurements = ReadCSV(csv, map, withHeader);
if (measurements.isEmpty())
{
return;
}
for (const auto &im : qAsConst(measurements))
{
m_m->AddEmptyMeasurement(im.name);
if (not im.group.isEmpty())
{
m_m->SetMGroup(im.name, im.group);
}
if (not im.fullName.isEmpty())
{
m_m->SetMFullName(im.name, im.fullName);
}
if (not im.formula.isEmpty())
{
m_m->SetMFormula(im.name, im.formula);
}
if (not im.description.isEmpty())
{
m_m->SetMDescription(im.name, im.description);
}
}
RefreshDataAfterImport();
}

View File

@ -45,6 +45,7 @@ class TKMMainWindow;
class VKnownMeasurementsDocument; class VKnownMeasurementsDocument;
class VPatternImage; class VPatternImage;
class QCompleter; class QCompleter;
class QxtCsvModel;
class TKMMainWindow : public VAbstractMainWindow class TKMMainWindow : public VAbstractMainWindow
{ {
@ -181,6 +182,14 @@ private:
void InitMeasurementUnits(); void InitMeasurementUnits();
void InitMeasurementDiagramList(); void InitMeasurementDiagramList();
void ShowError(const QString &text);
void RefreshDataAfterImport();
auto CheckMName(const QString &name, const QSet<QString> &importedNames) const -> QString;
auto CSVColumnHeader(int column) const -> QString;
auto ReadCSV(const QxtCsvModel &csv, const QVector<int> &map, bool withHeader) -> QVector<VKnownMeasurement>;
void ImportKnownMeasurements(const QxtCsvModel &csv, const QVector<int> &map, bool withHeader);
}; };
#endif // TKMMAINWINDOW_H #endif // TKMMAINWINDOW_H

View File

@ -47,7 +47,7 @@
<string/> <string/>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tabMeasurements"> <widget class="QWidget" name="tabMeasurements">
<attribute name="icon"> <attribute name="icon">
@ -947,6 +947,8 @@
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionSave"/> <addaction name="actionSave"/>
<addaction name="actionSaveAs"/> <addaction name="actionSaveAs"/>
<addaction name="actionExportToCSV"/>
<addaction name="actionImportFromCSV"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionReadOnly"/> <addaction name="actionReadOnly"/>
<addaction name="separator"/> <addaction name="separator"/>

View File

@ -38,9 +38,12 @@
#include "../vformat/knownmeasurements/vknownmeasurementsdatabase.h" #include "../vformat/knownmeasurements/vknownmeasurementsdatabase.h"
#include "../vganalytics/vganalytics.h" #include "../vganalytics/vganalytics.h"
#include "../vmisc/compatibility.h" #include "../vmisc/compatibility.h"
#include "../vmisc/def.h"
#include "../vmisc/defglobal.h"
#include "../vmisc/dialogs/dialogaskcollectstatistic.h" #include "../vmisc/dialogs/dialogaskcollectstatistic.h"
#include "../vmisc/dialogs/dialogexporttocsv.h" #include "../vmisc/dialogs/dialogexporttocsv.h"
#include "../vmisc/dialogs/dialogselectlanguage.h" #include "../vmisc/dialogs/dialogselectlanguage.h"
#include "../vmisc/literals.h"
#include "../vmisc/qxtcsvmodel.h" #include "../vmisc/qxtcsvmodel.h"
#include "../vmisc/theme/vtheme.h" #include "../vmisc/theme/vtheme.h"
#include "../vmisc/vsysexits.h" #include "../vmisc/vsysexits.h"
@ -49,7 +52,6 @@
#include "../vpatterndb/vcontainer.h" #include "../vpatterndb/vcontainer.h"
#include "../vtools/dialogs/support/dialogeditwrongformula.h" #include "../vtools/dialogs/support/dialogeditwrongformula.h"
#include "../vwidgets/vaspectratiopixmaplabel.h" #include "../vwidgets/vaspectratiopixmaplabel.h"
#include "def.h"
#include "dialogs/dialogabouttape.h" #include "dialogs/dialogabouttape.h"
#include "dialogs/dialogdimensioncustomnames.h" #include "dialogs/dialogdimensioncustomnames.h"
#include "dialogs/dialogdimensionlabels.h" #include "dialogs/dialogdimensionlabels.h"
@ -138,6 +140,7 @@ struct IndividualMeasurement
QString value{'0'}; // NOLINT(misc-non-private-member-variables-in-classes) QString value{'0'}; // NOLINT(misc-non-private-member-variables-in-classes)
QString fullName{}; // NOLINT(misc-non-private-member-variables-in-classes) QString fullName{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString description{}; // NOLINT(misc-non-private-member-variables-in-classes) QString description{}; // NOLINT(misc-non-private-member-variables-in-classes)
bool specialUnits{false}; // NOLINT(misc-non-private-member-variables-in-classes)
}; };
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
@ -878,54 +881,46 @@ auto TMainWindow::eventFilter(QObject *object, QEvent *event) -> bool
void TMainWindow::ExportToCSVData(const QString &fileName, bool withHeader, int mib, const QChar &separator) void TMainWindow::ExportToCSVData(const QString &fileName, bool withHeader, int mib, const QChar &separator)
{ {
QxtCsvModel csv; QxtCsvModel csv;
const int columns = ui->tableWidget->columnCount();
int columns = 2;
if (m_mType == MeasurementsType::Individual)
{ {
columns = 5;
}
else
{
columns = 5;
const QList<MeasurementDimension_p> dimensions = m_m->Dimensions().values();
if (dimensions.size() > 1)
{
columns += qMin(static_cast<int>(dimensions.size()), 2);
}
}
int colCount = 0; int colCount = 0;
for (int column = 0; column < columns; ++column) for (int column = 0; column <= columns; ++column)
{
if (not ui->tableWidget->isColumnHidden(column))
{ {
csv.insertColumn(colCount++); csv.insertColumn(colCount++);
} }
}
}
if (withHeader) if (withHeader)
{ {
int colCount = 0; for (int column = 0; column <= columns; ++column)
for (int column = 0; column < columns; ++column)
{ {
if (not ui->tableWidget->isColumnHidden(column)) csv.setHeaderText(column, CSVColumnHeader(column));
{
QString text;
if (QTableWidgetItem *header = ui->tableWidget->horizontalHeaderItem(column))
{
text = header->text();
}
csv.setHeaderText(colCount, text);
++colCount;
}
} }
} }
const int rows = ui->tableWidget->rowCount(); VKnownMeasurementsDatabase *db = MApplication::VApp()->KnownMeasurementsDatabase();
for (int row = 0; row < rows; ++row) VKnownMeasurements knownDB = db->KnownMeasurements(m_m->KnownMeasurements());
const QMap<int, QSharedPointer<VMeasurement>> orderedTable = OrderedMeasurments();
int row = 0;
for (auto iMap = orderedTable.constBegin(); iMap != orderedTable.constEnd(); ++iMap)
{ {
csv.insertRow(row); ExportRowToCSV(csv, row, iMap.value(), knownDB);
int colCount = 0; ++row;
for (int column = 0; column < columns; ++column)
{
if (not ui->tableWidget->isColumnHidden(column))
{
QString text;
if (QTableWidgetItem *item = ui->tableWidget->item(row, column))
{
text = item->text();
}
csv.setText(row, colCount, text);
++colCount;
}
}
} }
QString error; QString error;
@ -4314,8 +4309,6 @@ auto TMainWindow::CheckMName(const QString &name, const QSet<QString> &importedN
throw VException(tr("Imported file must not contain the same name twice.")); throw VException(tr("Imported file must not contain the same name twice."));
} }
if (name.indexOf(CustomMSign) == 0)
{
QRegularExpression rx(NameRegExp()); QRegularExpression rx(NameRegExp());
if (not rx.match(name).hasMatch()) if (not rx.match(name).hasMatch())
{ {
@ -4326,14 +4319,6 @@ auto TMainWindow::CheckMName(const QString &name, const QSet<QString> &importedN
{ {
throw VException(tr("Measurement '%1' already used in the file.").arg(name)); throw VException(tr("Measurement '%1' already used in the file.").arg(name));
} }
}
else
{
if (not m_data->IsUnique(name))
{
throw VException(tr("Measurement '%1' already used in file.").arg(name));
}
}
return name; return name;
} }
@ -4395,8 +4380,16 @@ void TMainWindow::ImportIndividualMeasurements(const QxtCsvModel &csv, const QVe
measurement.name = mName; measurement.name = mName;
const int valueColumn = map.at(static_cast<int>(IndividualMeasurementsColumns::Value)); const int valueColumn = map.at(static_cast<int>(IndividualMeasurementsColumns::Value));
QString rawValue = csv.text(i, valueColumn);
if (rawValue.endsWith(degreeSymbol))
{
measurement.specialUnits = true;
RemoveLast(rawValue);
}
measurement.value = VTranslateVars::TryFormulaFromUser( measurement.value = VTranslateVars::TryFormulaFromUser(
csv.text(i, valueColumn), VAbstractApplication::VApp()->Settings()->GetOsSeparator()); rawValue, VAbstractApplication::VApp()->Settings()->GetOsSeparator());
SetIndividualMeasurementFullName(i, name, csv, map, measurement); SetIndividualMeasurementFullName(i, name, csv, map, measurement);
SetIndividualMeasurementDescription(i, name, csv, map, measurement); SetIndividualMeasurementDescription(i, name, csv, map, measurement);
@ -4424,6 +4417,11 @@ void TMainWindow::ImportIndividualMeasurements(const QxtCsvModel &csv, const QVe
m_m->SetMFullName(im.name, im.fullName); m_m->SetMFullName(im.name, im.fullName);
} }
if (im.specialUnits)
{
m_m->SetMSpecialUnits(im.name, im.specialUnits);
}
if (not im.description.isEmpty()) if (not im.description.isEmpty())
{ {
m_m->SetMDescription(im.name, im.description); m_m->SetMDescription(im.name, im.description);
@ -4489,6 +4487,11 @@ void TMainWindow::ImportMultisizeMeasurements(const QxtCsvModel &csv, const QVec
m_m->SetMFullName(mm.name, mm.fullName); m_m->SetMFullName(mm.name, mm.fullName);
} }
if (mm.specialUnits)
{
m_m->SetMSpecialUnits(mm.name, mm.specialUnits);
}
if (not mm.description.isEmpty()) if (not mm.description.isEmpty())
{ {
m_m->SetMDescription(mm.name, mm.description); m_m->SetMDescription(mm.name, mm.description);
@ -4517,8 +4520,15 @@ auto TMainWindow::ImportMultisizeMeasurement(const QxtCsvModel &csv, int i, cons
measurement.name = mName; measurement.name = mName;
const auto baseValueColumn = map.at(static_cast<int>(MultisizeMeasurementsColumns::BaseValue)); const auto baseValueColumn = map.at(static_cast<int>(MultisizeMeasurementsColumns::BaseValue));
measurement.base = QString rawBaseValue = csv.text(i, baseValueColumn);
ConverToDouble(csv.text(i, baseValueColumn), tr("Cannot convert base value to double in column 2."));
if (rawBaseValue.endsWith(degreeSymbol))
{
measurement.specialUnits = true;
RemoveLast(rawBaseValue);
}
measurement.base = ConverToDouble(rawBaseValue, tr("Cannot convert base value to double in column 2."));
const auto shiftAColumn = map.at(static_cast<int>(MultisizeMeasurementsColumns::ShiftA)); const auto shiftAColumn = map.at(static_cast<int>(MultisizeMeasurementsColumns::ShiftA));
measurement.shiftA = ConverToDouble(csv.text(i, shiftAColumn), measurement.shiftA = ConverToDouble(csv.text(i, shiftAColumn),
@ -4831,6 +4841,159 @@ auto TMainWindow::KnownMeasurementsRegistred(const QUuid &id) -> bool
return known.contains(id); return known.contains(id);
} }
//---------------------------------------------------------------------------------------------------------------------
auto TMainWindow::CSVColumnHeader(int column) const -> QString
{
if (m_mType == MeasurementsType::Individual)
{
switch (column)
{
case 0: // name
return tr("Name", "measurement column");
case 1: // full name
return tr("Full name", "measurement column");
case 2: // calculated value
return tr("Calculated value", "measurement column") + " ("_L1 + UnitsToStr(m_pUnit) + ')'_L1;
case 3: // formula
return tr("Formula", "measurement column");
case 4: // description
return tr("Description", "measurement column");
default:
return {};
}
}
if (column == 0)
{
return tr("Name", "measurement column");
}
if (column == 1)
{
return tr("Full name", "measurement column");
}
if (column == 2)
{
return tr("Calculated value", "measurement column") + " ("_L1 + UnitsToStr(m_pUnit) + ')'_L1;
}
if (column == 3)
{
return tr("Base value", "measurement column");
}
const QList<MeasurementDimension_p> dimensions = m_m->Dimensions().values();
const QString suffix = QStringLiteral(" (%1):");
if (column == 4)
{
if (not dimensions.empty())
{
const MeasurementDimension_p &dimension = dimensions.at(0);
return tr("Shift", "measurement column") + suffix.arg(dimension->Name());
}
return QStringLiteral("Shift A");
}
if (column == 5 && dimensions.size() > 1)
{
const MeasurementDimension_p &dimension = dimensions.at(1);
return tr("Shift", "measurement column") + suffix.arg(dimension->Name());
}
if (column == 6 && dimensions.size() > 2)
{
const MeasurementDimension_p &dimension = dimensions.at(2);
return tr("Shift", "measurement column") + suffix.arg(dimension->Name());
}
if ((column == 5 && dimensions.size() <= 1) || (column == 6 && dimensions.size() <= 2) || column == 7)
{
return tr("Description", "measurement column");
}
return {};
}
//---------------------------------------------------------------------------------------------------------------------
void TMainWindow::ExportRowToCSV(QxtCsvModel &csv, int row, const QSharedPointer<VMeasurement> &meash,
const VKnownMeasurements &knownDB) const
{
csv.insertRow(row);
VKnownMeasurement known = knownDB.Measurement(meash->GetName());
csv.setText(row, 0, meash->GetName());
if (meash->IsCustom())
{
csv.setText(row, 1, meash->GetGuiText());
}
else
{
csv.setText(row, 1, known.fullName);
}
QString calculatedValue;
if (meash->IsSpecialUnits())
{
calculatedValue = locale().toString(*meash->GetValue()) + degreeSymbol;
}
else
{
calculatedValue = locale().toString(UnitConvertor(*meash->GetValue(), m_mUnit, m_pUnit));
}
csv.setText(row, 2, calculatedValue);
if (m_mType == MeasurementsType::Individual)
{
QString formula = VTranslateVars::TryFormulaToUser(meash->GetFormula(),
VAbstractApplication::VApp()->Settings()->GetOsSeparator());
csv.setText(row, 3, formula);
if (meash->IsCustom())
{
csv.setText(row, 4, meash->GetDescription());
}
else
{
csv.setText(row, 4, known.description);
}
}
else
{
csv.setText(row, 3, locale().toString(meash->GetBase()));
const QList<MeasurementDimension_p> dimensions = m_m->Dimensions().values();
csv.setText(row, 4, locale().toString(meash->GetShiftA()));
int column = 5;
if (dimensions.size() > 1)
{
csv.setText(row, column, locale().toString(meash->GetShiftB()));
++column;
}
if (dimensions.size() > 2)
{
csv.setText(row, column, locale().toString(meash->GetShiftC()));
++column;
}
if (meash->IsCustom())
{
csv.setText(row, column, meash->GetDescription());
}
else
{
csv.setText(row, column, known.description);
}
}
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void TMainWindow::SetDecimals() void TMainWindow::SetDecimals()
{ {

View File

@ -50,6 +50,7 @@ class QxtCsvModel;
class VMeasurement; class VMeasurement;
class QAbstractButton; class QAbstractButton;
class QUuid; class QUuid;
class VKnownMeasurements;
class TMainWindow : public VAbstractMainWindow class TMainWindow : public VAbstractMainWindow
{ {
@ -205,6 +206,7 @@ private:
qreal shiftC{0}; // NOLINT(misc-non-private-member-variables-in-classes) qreal shiftC{0}; // NOLINT(misc-non-private-member-variables-in-classes)
QString fullName{}; // NOLINT(misc-non-private-member-variables-in-classes) QString fullName{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString description{}; // NOLINT(misc-non-private-member-variables-in-classes) QString description{}; // NOLINT(misc-non-private-member-variables-in-classes)
bool specialUnits{false}; // NOLINT(misc-non-private-member-variables-in-classes)
}; };
QMultiHash<VShortcutAction, QAction *> m_actionShortcuts{}; QMultiHash<VShortcutAction, QAction *> m_actionShortcuts{};
@ -306,6 +308,10 @@ private:
void InitKnownMeasurementsDescription(); void InitKnownMeasurementsDescription();
static auto KnownMeasurementsRegistred(const QUuid &id) -> bool; static auto KnownMeasurementsRegistred(const QUuid &id) -> bool;
auto CSVColumnHeader(int column) const -> QString;
void ExportRowToCSV(QxtCsvModel &csv, int row, const QSharedPointer<VMeasurement> &meash,
const VKnownMeasurements &knownDB) const;
}; };
#endif // TMAINWINDOW_H #endif // TMAINWINDOW_H

View File

@ -53,7 +53,7 @@
<string/> <string/>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tabMeasurements"> <widget class="QWidget" name="tabMeasurements">
<attribute name="icon"> <attribute name="icon">

View File

@ -475,4 +475,17 @@ template <typename T> inline void SetTextAlignment(T *item, Qt::Alignment alignm
#endif #endif
} }
//---------------------------------------------------------------------------------------------------------------------
inline void RemoveLast(QString &str)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
str.removeLast();
#else
if (!str.isEmpty())
{
str.remove(str.size() - 1, 1);
}
#endif
}
#endif // COMPATIBILITY_H #endif // COMPATIBILITY_H