Resolved issue #804. New feature. Import measurements from CSV file in Tape app.

--HG--
branch : develop
This commit is contained in:
Roman Telezhynskyi 2018-01-31 14:34:50 +02:00
parent 90fe5d379e
commit cd1ebf434a
9 changed files with 344 additions and 27 deletions

View File

@ -40,6 +40,7 @@
- [#794] Better control over scale value. - [#794] Better control over scale value.
- [#392] Improve feature: Show progress bar while opening pattern file. - [#392] Improve feature: Show progress bar while opening pattern file.
- [#732] Tape app. Improve Database dialog. - [#732] Tape app. Improve Database dialog.
- [#804] New feature. Import measurements from CSV file in Tape app.
# Version 0.5.1 # Version 0.5.1
- [#683] Tool Seam allowance's dialog is off screen on small resolutions. - [#683] Tool Seam allowance's dialog is off screen on small resolutions.

View File

@ -34,6 +34,7 @@
#include "dialogs/dialogtapepreferences.h" #include "dialogs/dialogtapepreferences.h"
#include "../vpatterndb/calculator.h" #include "../vpatterndb/calculator.h"
#include "../vpatterndb/pmsystems.h" #include "../vpatterndb/pmsystems.h"
#include "../vpatterndb/measurements.h"
#include "../ifc/ifcdef.h" #include "../ifc/ifcdef.h"
#include "../ifc/xml/vvitconverter.h" #include "../ifc/xml/vvitconverter.h"
#include "../ifc/xml/vvstconverter.h" #include "../ifc/xml/vvstconverter.h"
@ -41,6 +42,7 @@
#include "../vmisc/vlockguard.h" #include "../vmisc/vlockguard.h"
#include "../vmisc/vsysexits.h" #include "../vmisc/vsysexits.h"
#include "../vmisc/qxtcsvmodel.h" #include "../vmisc/qxtcsvmodel.h"
#include "../vmisc/dialogs/dialogexporttocsv.h"
#include "vlitepattern.h" #include "vlitepattern.h"
#include "../qmuparser/qmudef.h" #include "../qmuparser/qmudef.h"
#include "../vtools/dialogs/support/dialogeditwrongformula.h" #include "../vtools/dialogs/support/dialogeditwrongformula.h"
@ -308,6 +310,8 @@ bool TMainWindow::LoadFile(const QString &path)
} }
MeasurementGUI(); MeasurementGUI();
ui->actionImportFromCSV->setEnabled(true);
} }
catch (VException &e) catch (VException &e)
{ {
@ -387,6 +391,8 @@ void TMainWindow::FileNew()
InitWindow(); InitWindow();
MeasurementGUI(); MeasurementGUI();
ui->actionImportFromCSV->setEnabled(true);
} }
else else
{ {
@ -946,6 +952,56 @@ void TMainWindow::ShowWindow() const
} }
} }
//---------------------------------------------------------------------------------------------------------------------
void TMainWindow::ImportDataFromCSV()
{
if (m == nullptr || m->Type() == MeasurementsType::Unknown)
{
return;
}
const QString filters = tr("Comma-Separated Values") + QLatin1String(" (*.csv)");
const QString suffix("csv");
QString fileName = QFileDialog::getOpenFileName(this, tr("Import from CSV"), QDir::homePath(), filters, nullptr,
QFileDialog::DontUseNativeDialog);
if (fileName.isEmpty())
{
return;
}
QFileInfo f( fileName );
if (f.suffix().isEmpty() && f.suffix() != suffix)
{
fileName += QLatin1String(".") + suffix;
}
DialogExportToCSV dialog(this);
dialog.SetWithHeader(qApp->Settings()->GetCSVWithHeader());
dialog.SetSelectedMib(qApp->Settings()->GetCSVCodec());
dialog.SetSeparator(qApp->Settings()->GetCSVSeparator());
if (dialog.exec() == QDialog::Accepted)
{
qApp->Settings()->SetCSVSeparator(dialog.GetSeparator());
qApp->Settings()->SetCSVCodec(dialog.GetSelectedMib());
qApp->Settings()->SetCSVWithHeader(dialog.IsWithHeader());
QxtCsvModel csv(fileName, nullptr, dialog.IsWithHeader(), dialog.GetSeparator(),
QTextCodec::codecForMib(dialog.GetSelectedMib()));
if (m->Type() == MeasurementsType::Individual)
{
ImportIndividualMeasurements(csv);
}
else
{
ImportMultisizeMeasurements(csv);
}
}
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
void TMainWindow::AboutToShowDockMenu() void TMainWindow::AboutToShowDockMenu()
@ -1880,6 +1936,7 @@ void TMainWindow::SetupMenu()
ui->actionSaveAs->setShortcuts(QKeySequence::SaveAs); ui->actionSaveAs->setShortcuts(QKeySequence::SaveAs);
connect(ui->actionExportToCSV, &QAction::triggered, this, &TMainWindow::ExportDataToCSV); connect(ui->actionExportToCSV, &QAction::triggered, this, &TMainWindow::ExportDataToCSV);
connect(ui->actionImportFromCSV, &QAction::triggered, this, &TMainWindow::ImportDataFromCSV);
connect(ui->actionReadOnly, &QAction::triggered, this, [this](bool ro) connect(ui->actionReadOnly, &QAction::triggered, this, [this](bool ro)
{ {
if (not mIsReadOnly) if (not mIsReadOnly)
@ -2440,10 +2497,7 @@ void TMainWindow::RefreshTable(bool freshCall)
ui->tableWidget->horizontalHeader()->setStretchLastSection(true); ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
ui->tableWidget->blockSignals(false); ui->tableWidget->blockSignals(false);
if (ui->tableWidget->rowCount() > 0) ui->actionExportToCSV->setEnabled(ui->tableWidget->rowCount() > 0);
{
ui->actionExportToCSV->setEnabled(true);
}
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
@ -3011,6 +3065,243 @@ bool TMainWindow::IgnoreLocking(int error, const QString &path)
return true; return true;
} }
//---------------------------------------------------------------------------------------------------------------------
QString TMainWindow::CheckMName(const QString &name) const
{
if (name.isEmpty())
{
throw VException(tr("Measurement name in is empty."));
}
if (name.indexOf(CustomMSign) == 0)
{
QRegularExpression rx(NameRegExp());
if (not rx.match(name).hasMatch())
{
throw VException(tr("Merasurement '%1' doesn't match regex pattern.").arg(name));
}
if (not data->IsUnique(name))
{
throw VException(tr("Merasurement '%1' already used in file.").arg(name));
}
}
else
{
if (not AllGroupNames().toSet().contains(name))
{
throw VException(tr("Measurement '%1' is not one of known measurements.").arg(name));
}
if (not data->IsUnique(name))
{
throw VException(tr("Merasurement '%1' already used in file.").arg(name));
}
}
return name;
}
//---------------------------------------------------------------------------------------------------------------------
void TMainWindow::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 TMainWindow::RefreshDataAfterImport()
{
const int currentRow = ui->tableWidget->currentRow();
search->AddRow(currentRow);
RefreshData();
search->RefreshList(ui->lineEditFind->text());
ui->tableWidget->selectRow(currentRow);
ui->actionExportToCSV->setEnabled(true);
MeasurementsWasSaved(false);
}
//---------------------------------------------------------------------------------------------------------------------
void TMainWindow::ImportIndividualMeasurements(const QxtCsvModel &csv)
{
const int columns = csv.columnCount();
const int rows = csv.rowCount();
if (columns < 2)
{
ShowError(tr("Individual measurements require at least 2 columns."));
return;
}
struct IndividualMeasurement
{
IndividualMeasurement()
: name(),
value("0"),
fullName(),
description()
{}
QString name;
QString value;
QString fullName;
QString description;
};
QVector<IndividualMeasurement> measurements;
for(int i=0; i < rows; ++i)
{
try
{
IndividualMeasurement measurement;
measurement.name = CheckMName(qApp->TrVars()->MFromUser(csv.text(i, 0).simplified()));
measurement.value = VTranslateVars::TryFormulaFromUser(csv.text(i, 1), qApp->Settings()->GetOsSeparator());
if (columns >= 3)
{
measurement.fullName = csv.text(i, 2).simplified();
}
if (columns >= 4)
{
measurement.description = csv.text(i, 3).simplified();
}
measurements.append(measurement);
}
catch (VException &e)
{
ShowError(tr("Error in row %1.").arg(i) + QLatin1Char(' ') + e.ErrorMessage());
return;
}
}
for(int i=0; i < measurements.size(); ++i)
{
m->AddEmpty(measurements.at(i).name, measurements.at(i).value);
if (not measurements.at(i).fullName.isEmpty())
{
m->SetMFullName(measurements.at(i).name, measurements.at(i).fullName);
}
if (not measurements.at(i).description.isEmpty())
{
m->SetMDescription(measurements.at(i).name, measurements.at(i).description);
}
}
RefreshDataAfterImport();
}
//---------------------------------------------------------------------------------------------------------------------
void TMainWindow::ImportMultisizeMeasurements(const QxtCsvModel &csv)
{
const int columns = csv.columnCount();
const int rows = csv.rowCount();
if (columns < 4)
{
ShowError(tr("Multisize measurements require at least 4 columns."));
return;
}
auto ConverToDouble = [this](QString text, const QString &error)
{
text = VTranslateVars::TryFormulaFromUser(text, qApp->Settings()->GetOsSeparator());
bool ok = false;
QLocale::c();
const qreal value = QLocale::c().toDouble(text, &ok);
if (not ok)
{
throw VException(error);
}
return value;
};
struct MultisizeMeasurement
{
MultisizeMeasurement()
: name(),
base(0),
heightIncrease(0),
sizeIncrease(0),
fullName(),
description()
{}
QString name;
qreal base;
qreal heightIncrease;
qreal sizeIncrease;
QString fullName;
QString description;
};
QVector<MultisizeMeasurement> measurements;
for(int i=0; i < rows; ++i)
{
try
{
MultisizeMeasurement measurement;
measurement.name = CheckMName(qApp->TrVars()->MFromUser(csv.text(i, 0).simplified()));
measurement.base = ConverToDouble(csv.text(i, 1),
tr("Cannot convert base size value to double in column 2."));
measurement.heightIncrease = ConverToDouble(csv.text(i, 2),
tr("Cannot convert height increase value to double in column 3."));
measurement.sizeIncrease = ConverToDouble(csv.text(i, 3),
tr("Cannot convert size increase value to double in column 4."));
if (columns >= 5)
{
measurement.fullName = csv.text(i, 4).simplified();
}
if (columns >= 6)
{
measurement.description = csv.text(i, 5).simplified();
}
measurements.append(measurement);
}
catch (VException &e)
{
ShowError(tr("Error in row %1.").arg(i) + QLatin1Char(' ') + e.ErrorMessage());
return;
}
}
for(int i=0; i < measurements.size(); ++i)
{
m->AddEmpty(measurements.at(i).name);
m->SetMBaseValue(measurements.at(i).name, measurements.at(i).base);
m->SetMSizeIncrease(measurements.at(i).name, measurements.at(i).sizeIncrease);
m->SetMHeightIncrease(measurements.at(i).name, measurements.at(i).heightIncrease);
if (not measurements.at(i).fullName.isEmpty())
{
m->SetMFullName(measurements.at(i).name, measurements.at(i).fullName);
}
if (not measurements.at(i).description.isEmpty())
{
m->SetMDescription(measurements.at(i).name, measurements.at(i).description);
}
}
RefreshDataAfterImport();
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void TMainWindow::SetDecimals() void TMainWindow::SetDecimals()
{ {

View File

@ -43,6 +43,7 @@ namespace Ui
} }
class QLabel; class QLabel;
class QxtCsvModel;
class TMainWindow : public VAbstractMainWindow class TMainWindow : public VAbstractMainWindow
{ {
@ -86,6 +87,7 @@ private slots:
bool FileSaveAs(); bool FileSaveAs();
void AboutToShowWindowMenu(); void AboutToShowWindowMenu();
void ShowWindow() const; void ShowWindow() const;
void ImportDataFromCSV();
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
void AboutToShowDockMenu(); void AboutToShowDockMenu();
@ -213,6 +215,13 @@ private:
template <class T> template <class T>
void HackWidget(T **widget); void HackWidget(T **widget);
QString CheckMName(const QString &name) const;
void ShowError(const QString &text);
void RefreshDataAfterImport();
void ImportIndividualMeasurements(const QxtCsvModel &csv);
void ImportMultisizeMeasurements(const QxtCsvModel &csv);
}; };
#endif // TMAINWINDOW_H #endif // TMAINWINDOW_H

View File

@ -844,6 +844,7 @@
<addaction name="actionSave"/> <addaction name="actionSave"/>
<addaction name="actionSaveAs"/> <addaction name="actionSaveAs"/>
<addaction name="actionExportToCSV"/> <addaction name="actionExportToCSV"/>
<addaction name="actionImportFromCSV"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionReadOnly"/> <addaction name="actionReadOnly"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -1182,6 +1183,17 @@
<enum>QAction::NoRole</enum> <enum>QAction::NoRole</enum>
</property> </property>
</action> </action>
<action name="actionImportFromCSV">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Import from CSV</string>
</property>
<property name="toolTip">
<string>Import from CSV</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

View File

@ -811,14 +811,7 @@ QDomElement VMeasurements::MakeEmpty(const QString &name, const QString &formula
} }
else else
{ {
if (formula.isEmpty()) SetAttribute(element, AttrValue, formula.isEmpty() ? QString("0") : formula);
{
SetAttribute(element, AttrValue, QString("0"));
}
else
{
SetAttribute(element, AttrValue, formula);
}
} }
return element; return element;

View File

@ -78,11 +78,11 @@ QxtCsvModel::QxtCsvModel(QObject *parent) : QAbstractTableModel(parent)
\sa setSource \sa setSource
*/ */
QxtCsvModel::QxtCsvModel(QIODevice *file, QObject *parent, bool withHeader, QChar separator) QxtCsvModel::QxtCsvModel(QIODevice *file, QObject *parent, bool withHeader, QChar separator, QTextCodec* codec)
: QAbstractTableModel(parent) : QAbstractTableModel(parent)
{ {
QXT_INIT_PRIVATE(QxtCsvModel) QXT_INIT_PRIVATE(QxtCsvModel)
setSource(file, withHeader, separator); setSource(file, withHeader, separator, codec);
} }
/*! /*!
@ -95,12 +95,12 @@ QxtCsvModel::QxtCsvModel(QIODevice *file, QObject *parent, bool withHeader, QCha
\sa setSource \sa setSource
*/ */
QxtCsvModel::QxtCsvModel(const QString &filename, QObject *parent, bool withHeader, QChar separator) QxtCsvModel::QxtCsvModel(const QString &filename, QObject *parent, bool withHeader, QChar separator, QTextCodec* codec)
: QAbstractTableModel(parent) : QAbstractTableModel(parent)
{ {
QXT_INIT_PRIVATE(QxtCsvModel) QXT_INIT_PRIVATE(QxtCsvModel)
QFile src(filename); QFile src(filename);
setSource(&src, withHeader, separator); setSource(&src, withHeader, separator, codec);
} }
QT_WARNING_POP QT_WARNING_POP
@ -214,14 +214,7 @@ void QxtCsvModel::setSource(QIODevice *file, bool withHeader, QChar separator, Q
QChar ch, buffer(0); QChar ch, buffer(0);
bool readCR = false; bool readCR = false;
QTextStream stream(file); QTextStream stream(file);
if (codec) codec ? stream.setCodec(codec) : stream.setAutoDetectUnicode(true);
{
stream.setCodec(codec);
}
else
{
stream.setAutoDetectUnicode(true);
}
while (not stream.atEnd()) while (not stream.atEnd())
{ {
if (buffer != QChar(0)) if (buffer != QChar(0))

View File

@ -55,9 +55,10 @@ class QxtCsvModel : public QAbstractTableModel
Q_OBJECT Q_OBJECT
public: public:
explicit QxtCsvModel(QObject *parent = nullptr); explicit QxtCsvModel(QObject *parent = nullptr);
explicit QxtCsvModel(QIODevice *file, QObject *parent = nullptr, bool withHeader = false, QChar separator = ','); explicit QxtCsvModel(QIODevice *file, QObject *parent = nullptr, bool withHeader = false, QChar separator = ',',
QTextCodec *codec = nullptr);
explicit QxtCsvModel(const QString &filename, QObject *parent = nullptr, bool withHeader = false, explicit QxtCsvModel(const QString &filename, QObject *parent = nullptr, bool withHeader = false,
QChar separator = ','); QChar separator = ',', QTextCodec *codec = nullptr);
virtual ~QxtCsvModel() Q_DECL_EQ_DEFAULT; virtual ~QxtCsvModel() Q_DECL_EQ_DEFAULT;
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;

View File

@ -73,6 +73,22 @@ bool VTranslateMeasurements::MeasurementsFromUser(QString &newFormula, int posit
return false; return false;
} }
//---------------------------------------------------------------------------------------------------------------------
QString VTranslateMeasurements::MFromUser(const QString &measurement) const
{
QMap<QString, qmu::QmuTranslation>::const_iterator i = measurements.constBegin();
while (i != measurements.constEnd())
{
const QString translated = i.value().translate(qApp->Settings()->GetLocale());
if (measurement == translated)
{
return i.key();
}
++i;
}
return measurement;
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
QString VTranslateMeasurements::MToUser(const QString &measurement) const QString VTranslateMeasurements::MToUser(const QString &measurement) const
{ {

View File

@ -43,6 +43,7 @@ public:
bool MeasurementsFromUser(QString &newFormula, int position, const QString &token, int &bias) const; bool MeasurementsFromUser(QString &newFormula, int position, const QString &token, int &bias) const;
QString MFromUser(const QString &measurement) const;
QString MToUser(const QString &measurement) const; QString MToUser(const QString &measurement) const;
QString MNumber(const QString &measurement) const; QString MNumber(const QString &measurement) const;
QString MFormula(const QString &measurement) const; QString MFormula(const QString &measurement) const;