Resolved issue #409. New feature: Export measurement file to Excel .csv.

--HG--
branch : develop
This commit is contained in:
Roman Telezhynskyi 2016-06-01 16:37:42 +03:00
parent 8e3f3a4cd8
commit 32d391c587
15 changed files with 1508 additions and 6 deletions

View File

@ -24,6 +24,7 @@
- [#472] Add 'Full Name' column to Formula dialog.
- [#487] True dart point always goes to origin when the label is moved.
- [#128] New Tool: Slash and Spread.
- [#409] New feature: Export measurement file to Excel .csv.
# Version 0.4.5
- [#435] Valentina doesn't change the cursor.

View File

@ -41,7 +41,7 @@ class DialogAboutTape : public QDialog
public:
explicit DialogAboutTape(QWidget *parent = 0);
~DialogAboutTape();
virtual ~DialogAboutTape();
protected:
virtual void changeEvent(QEvent* event) Q_DECL_OVERRIDE;

View File

@ -0,0 +1,154 @@
/************************************************************************
**
** @file dialogexporttocsv.cpp
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 1 6, 2016
**
** @brief
** @copyright
** This source code is part of the Valentine project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2016 Valentina project
** <https://bitbucket.org/dismine/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 "dialogexporttocsv.h"
#include "ui_dialogexporttocsv.h"
#include "../vmisc/vtapesettings.h"
#include "../mapplication.h"
#include <QShowEvent>
#include <QTextCodec>
//---------------------------------------------------------------------------------------------------------------------
DialogExportToCSV::DialogExportToCSV(QWidget *parent)
: QDialog(parent),
ui(new Ui::DialogExportToCSV),
isInitialized(false)
{
ui->setupUi(this);
ui->checkBoxWithHeader->setChecked(qApp->TapeSettings()->GetCSVWithHeader());
foreach (int mib, QTextCodec::availableMibs())
{
ui->comboBoxCodec->addItem(QTextCodec::codecForMib(mib)->name(), mib);
}
ui->comboBoxCodec->setCurrentIndex(ui->comboBoxCodec->findData(qApp->TapeSettings()->GetCSVCodec()));
SetSeparator(qApp->TapeSettings()->GetCSVSeparator());
}
//---------------------------------------------------------------------------------------------------------------------
DialogExportToCSV::~DialogExportToCSV()
{
delete ui;
}
//---------------------------------------------------------------------------------------------------------------------
bool DialogExportToCSV::WithHeader() const
{
return ui->checkBoxWithHeader->isChecked();
}
//---------------------------------------------------------------------------------------------------------------------
int DialogExportToCSV::SelectedMib() const
{
#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
return ui->comboBoxCodec->itemData(ui->comboBoxCodec->currentIndex()).toInt();
#else
return ui->comboBoxCodec->currentData().toInt();
#endif
}
//---------------------------------------------------------------------------------------------------------------------
QChar DialogExportToCSV::Separator() const
{
if (ui->radioButtonTab->isChecked())
{
return QChar('\t');
}
else if (ui->radioButtonSemicolon->isChecked())
{
return QChar(';');
}
else if (ui->radioButtonSpace->isChecked())
{
return QChar(' ');
}
else
{
return QChar(',');
}
}
//---------------------------------------------------------------------------------------------------------------------
void DialogExportToCSV::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange)
{
// retranslate designer form (single inheritance approach)
ui->retranslateUi(this);
}
// remember to call base class implementation
QDialog::changeEvent(event);
}
//---------------------------------------------------------------------------------------------------------------------
void DialogExportToCSV::showEvent(QShowEvent *event)
{
QDialog::showEvent( event );
if ( event->spontaneous() )
{
return;
}
if (isInitialized)
{
return;
}
// do your init stuff here
setMaximumSize(size());
setMinimumSize(size());
isInitialized = true;//first show windows are held
}
//---------------------------------------------------------------------------------------------------------------------
void DialogExportToCSV::SetSeparator(const QChar &separator)
{
switch(separator.toLatin1())
{
case '\t':
ui->radioButtonTab->setChecked(true);
break;
case ';':
ui->radioButtonSemicolon->setChecked(true);
break;
case ' ':
ui->radioButtonSpace->setChecked(true);
break;
case ',':
default:
ui->radioButtonComma->setChecked(true);
break;
}
}

View File

@ -0,0 +1,62 @@
/************************************************************************
**
** @file dialogexporttocsv.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 1 6, 2016
**
** @brief
** @copyright
** This source code is part of the Valentine project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2016 Valentina project
** <https://bitbucket.org/dismine/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 DIALOGEXPORTTOCSV_H
#define DIALOGEXPORTTOCSV_H
#include <QDialog>
namespace Ui {
class DialogExportToCSV;
}
class DialogExportToCSV : public QDialog
{
Q_OBJECT
public:
explicit DialogExportToCSV(QWidget *parent = nullptr);
virtual ~DialogExportToCSV();
bool WithHeader() const;
int SelectedMib() const;
QChar Separator() const;
protected:
virtual void changeEvent(QEvent* event) Q_DECL_OVERRIDE;
virtual void showEvent(QShowEvent *event) Q_DECL_OVERRIDE;
private:
Q_DISABLE_COPY(DialogExportToCSV)
Ui::DialogExportToCSV *ui;
bool isInitialized;
void SetSeparator(const QChar &separator);
};
#endif // DIALOGEXPORTTOCSV_H

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DialogExportToCSV</class>
<widget class="QDialog" name="DialogExportToCSV">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>298</width>
<height>292</height>
</rect>
</property>
<property name="windowTitle">
<string>Export options</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>:/tapeicon/64x64/logo.png</normaloff>:/tapeicon/64x64/logo.png</iconset>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBoxExport">
<property name="title">
<string>Export</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="checkBoxWithHeader">
<property name="text">
<string>With header</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Codec:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxCodec"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBoxSeparator">
<property name="title">
<string>Separator</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="radioButtonTab">
<property name="text">
<string>Tab</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonComma">
<property name="text">
<string>Comma</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonSemicolon">
<property name="text">
<string>Semicolon</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonSpace">
<property name="text">
<string>Space</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
</layout>
</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>DialogExportToCSV</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>DialogExportToCSV</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>
<buttongroups>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui>

View File

@ -11,6 +11,7 @@ SOURCES += \
$$PWD/dialogs/tapeconfigdialog.cpp \
$$PWD/dialogs/configpages/tapeconfigurationpage.cpp \
$$PWD/dialogs/configpages/tapepathpage.cpp \
$$PWD/dialogs/dialogexporttocsv.cpp \
$$PWD/vlitepattern.cpp \
$$PWD/vtablesearch.cpp
@ -27,6 +28,7 @@ HEADERS += \
$$PWD/dialogs/tapeconfigdialog.h \
$$PWD/dialogs/configpages/tapeconfigurationpage.h \
$$PWD/dialogs/configpages/tapepathpage.h \
$$PWD/dialogs/dialogexporttocsv.h \
$$PWD/vlitepattern.h \
$$PWD/vtablesearch.h
@ -34,4 +36,5 @@ FORMS += \
$$PWD/tmainwindow.ui \
$$PWD/dialogs/dialogabouttape.ui \
$$PWD/dialogs/dialognewmeasurements.ui \
$$PWD/dialogs/dialogmdatabase.ui
$$PWD/dialogs/dialogmdatabase.ui \
$$PWD/dialogs/dialogexporttocsv.ui

View File

@ -32,6 +32,7 @@
#include "dialogs/dialognewmeasurements.h"
#include "dialogs/dialogmdatabase.h"
#include "dialogs/tapeconfigdialog.h"
#include "dialogs/dialogexporttocsv.h"
#include "../vpatterndb/calculator.h"
#include "../ifc/ifcdef.h"
#include "../ifc/xml/vvitconverter.h"
@ -39,6 +40,7 @@
#include "../ifc/xml/vpatternconverter.h"
#include "../vmisc/vlockguard.h"
#include "../vmisc/vsysexits.h"
#include "../vmisc/qxtcsvmodel.h"
#include "vlitepattern.h"
#include "../qmuparser/qmudef.h"
#include "../vtools/dialogs/support/dialogeditwrongformula.h"
@ -51,6 +53,7 @@
#include <QComboBox>
#include <QProcess>
#include <QtNumeric>
#include <QTextCodec>
#if defined(Q_OS_MAC)
#include <QMimeData>
@ -679,6 +682,80 @@ void TMainWindow::FileSaveAs()
}
}
//---------------------------------------------------------------------------------------------------------------------
void TMainWindow::ExportToCSV()
{
const QString filters = tr("Comma-Separated Values (*.cvs)");
const QString suffix("csv");
const QString path = QDir::homePath() + "/" + tr("measurements"); + "." + suffix;
QString fileName = QFileDialog::getSaveFileName(this, tr("Export to CSV"), path, filters);
if (fileName.isEmpty())
{
return;
}
QFileInfo f( fileName );
if (f.suffix().isEmpty() && f.suffix() != suffix)
{
fileName += "." + suffix;
}
DialogExportToCSV dialog(this);
if (dialog.exec() == QDialog::Accepted)
{
QxtCsvModel csv;
const int columns = ui->tableWidget->columnCount();
{
int colCount = 0;
for (int column = 0; column < columns; ++column)
{
if (not ui->tableWidget->isColumnHidden(column))
{
csv.insertColumn(colCount++);
}
}
}
if (dialog.WithHeader())
{
int colCount = 0;
for (int column = 0; column < columns; ++column)
{
if (not ui->tableWidget->isColumnHidden(column))
{
QTableWidgetItem *header = ui->tableWidget->horizontalHeaderItem(colCount);
csv.setHeaderText(colCount, header->text());
++colCount;
}
}
}
const int rows = ui->tableWidget->rowCount();
for (int row = 0; row < rows; ++row)
{
csv.insertRow(row);
int colCount = 0;
for (int column = 0; column < columns; ++column)
{
if (not ui->tableWidget->isColumnHidden(column))
{
QTableWidgetItem *item = ui->tableWidget->item(row, column);
csv.setText(row, colCount, item->text());
++colCount;
}
}
}
csv.toCSV(fileName, dialog.WithHeader(), dialog.Separator(), QTextCodec::codecForMib(dialog.SelectedMib()));
qApp->TapeSettings()->SetCSVSeparator(dialog.Separator());
qApp->TapeSettings()->SetCSVCodec(dialog.SelectedMib());
qApp->TapeSettings()->SetCSVWithHeader(dialog.WithHeader());
}
}
//---------------------------------------------------------------------------------------------------------------------
void TMainWindow::AboutToShowWindowMenu()
{
@ -863,6 +940,8 @@ void TMainWindow::Remove()
{
MFields(false);
ui->actionExportToCSV->setEnabled(false);
ui->lineEditName->blockSignals(true);
ui->lineEditName->setText("");
ui->lineEditName->blockSignals(false);
@ -1060,13 +1139,15 @@ void TMainWindow::AddCustom()
ui->tableWidget->selectRow(currentRow);
ui->actionExportToCSV->setEnabled(true);
MeasurementsWasSaved(false);
}
//---------------------------------------------------------------------------------------------------------------------
void TMainWindow::AddKnown()
{
DialogMDataBase *dialog = new DialogMDataBase(m->ListKnown(), this);
QScopedPointer<DialogMDataBase> dialog (new DialogMDataBase(m->ListKnown(), this));
if (dialog->exec() == QDialog::Accepted)
{
qint32 currentRow;
@ -1114,9 +1195,10 @@ void TMainWindow::AddKnown()
ui->tableWidget->selectRow(currentRow);
ui->actionExportToCSV->setEnabled(true);
MeasurementsWasSaved(false);
}
delete dialog;
}
//---------------------------------------------------------------------------------------------------------------------
@ -1732,6 +1814,7 @@ void TMainWindow::SetupMenu()
connect(ui->actionSaveAs, &QAction::triggered, this, &TMainWindow::FileSaveAs);
ui->actionSaveAs->setShortcuts(QKeySequence::SaveAs);
connect(ui->actionExportToCSV, &QAction::triggered, this, &TMainWindow::ExportToCSV);
connect(ui->actionReadOnly, &QAction::triggered, this, &TMainWindow::ReadOnly);
connect(ui->actionPreferences, &QAction::triggered, this, &TMainWindow::Preferences);
@ -2302,6 +2385,11 @@ void TMainWindow::RefreshTable()
ui->tableWidget->resizeRowsToContents();
ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
ui->tableWidget->blockSignals(false);
if (ui->tableWidget->rowCount() > 0)
{
ui->actionExportToCSV->setEnabled(true);
}
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -79,6 +79,7 @@ protected:
private slots:
void FileSave();
void FileSaveAs();
void ExportToCSV();
void AboutToShowWindowMenu();
void ShowWindow();
void AboutApplication();

View File

@ -913,6 +913,7 @@
<addaction name="separator"/>
<addaction name="actionSave"/>
<addaction name="actionSaveAs"/>
<addaction name="actionExportToCSV"/>
<addaction name="separator"/>
<addaction name="actionReadOnly"/>
<addaction name="separator"/>
@ -1249,6 +1250,14 @@
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="actionExportToCSV">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Export to CSV</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View File

@ -632,4 +632,95 @@ static inline bool VFuzzyComparePossibleNulls(double p1, double p2)
}
}
/****************************************************************************
** This file is derived from code bearing the following notice:
** The sole author of this file, Adam Higerd, has explicitly disclaimed all
** copyright interest and protection for the content within. This file has
** been placed in the public domain according to United States copyright
** statute and case law. In jurisdictions where this public domain dedication
** is not legally recognized, anyone who receives a copy of this file is
** permitted to use, modify, duplicate, and redistribute this file, in whole
** or in part, with no restrictions or conditions. In these jurisdictions,
** this file shall be copyright (C) 2006-2008 by Adam Higerd.
****************************************************************************/
#define QXT_DECLARE_PRIVATE(PUB) friend class PUB##Private; QxtPrivateInterface<PUB, PUB##Private> qxt_d;
#define QXT_DECLARE_PUBLIC(PUB) friend class PUB;
#define QXT_INIT_PRIVATE(PUB) qxt_d.setPublic(this);
#define QXT_D(PUB) PUB##Private& d = qxt_d()
#define QXT_P(PUB) PUB& p = qxt_p()
template <typename PUB>
class QxtPrivate
{
public:
QxtPrivate(): qxt_p_ptr(nullptr)
{}
virtual ~QxtPrivate()
{}
inline void QXT_setPublic(PUB* pub)
{
qxt_p_ptr = pub;
}
protected:
inline PUB& qxt_p()
{
return *qxt_p_ptr;
}
inline const PUB& qxt_p() const
{
return *qxt_p_ptr;
}
inline PUB* qxt_ptr()
{
return qxt_p_ptr;
}
inline const PUB* qxt_ptr() const
{
return qxt_p_ptr;
}
private:
Q_DISABLE_COPY(QxtPrivate)
PUB* qxt_p_ptr;
};
template <typename PUB, typename PVT>
class QxtPrivateInterface
{
friend class QxtPrivate<PUB>;
public:
QxtPrivateInterface() : pvt(new PVT)
{}
~QxtPrivateInterface()
{
delete pvt;
}
inline void setPublic(PUB* pub)
{
pvt->QXT_setPublic(pub);
}
inline PVT& operator()()
{
return *static_cast<PVT*>(pvt);
}
inline const PVT& operator()() const
{
return *static_cast<PVT*>(pvt);
}
inline PVT * operator->()
{
return static_cast<PVT*>(pvt);
}
inline const PVT * operator->() const
{
return static_cast<PVT*>(pvt);
}
private:
Q_DISABLE_COPY(QxtPrivateInterface)
QxtPrivate<PUB>* pvt;
};
#endif // DEF_H

View File

@ -0,0 +1,729 @@
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
/*!
\class QxtCsvModel
\brief The QxtCsvModel class provides a QAbstractTableModel for CSV Files
*/
#include "qxtcsvmodel.h"
#include <QFile>
#include <QTextStream>
#include <QDebug>
class QxtCsvModelPrivate : public QxtPrivate<QxtCsvModel>
{
public:
QxtCsvModelPrivate() : csvData(), header(), maxColumn(0), quoteMode(QxtCsvModel::DefaultQuoteMode)
{}
QXT_DECLARE_PUBLIC(QxtCsvModel)
QList<QStringList> csvData;
QStringList header;
int maxColumn;
QxtCsvModel::QuoteMode quoteMode;
};
#ifdef Q_CC_GNU
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#endif
/*!
Creates an empty QxtCsvModel with parent \a parent.
*/
QxtCsvModel::QxtCsvModel(QObject *parent) : QAbstractTableModel(parent)
{
QXT_INIT_PRIVATE(QxtCsvModel);
}
/*!
Creates a QxtCsvModel with the parent \a parent and content loaded from \a file.
See \a setSource for information on the \a withHeader and \a separator properties, or
if you need control over the quoting method or codec used to parse the file.
\sa setSource
*/
QxtCsvModel::QxtCsvModel(QIODevice *file, QObject *parent, bool withHeader, QChar separator)
: QAbstractTableModel(parent)
{
QXT_INIT_PRIVATE(QxtCsvModel);
setSource(file, withHeader, separator);
}
/*!
\overload
Creates a QxtCsvModel with the parent \a parent and content loaded from \a file.
See \a setSource for information on the \a withHeader and \a separator properties, or
if you need control over the quoting method or codec used to parse the file.
\sa setSource
*/
QxtCsvModel::QxtCsvModel(const QString filename, QObject *parent, bool withHeader, QChar separator)
: QAbstractTableModel(parent)
{
QXT_INIT_PRIVATE(QxtCsvModel);
QFile src(filename);
setSource(&src, withHeader, separator);
}
#ifdef Q_CC_GNU
#pragma GCC diagnostic pop
#endif
QxtCsvModel::~QxtCsvModel()
{}
/*!
\reimp
*/
int QxtCsvModel::rowCount(const QModelIndex& parent) const
{
if (parent.row() != -1 && parent.column() != -1)
{
return 0;
}
return qxt_d().csvData.count();
}
/*!
\reimp
*/
int QxtCsvModel::columnCount(const QModelIndex& parent) const
{
if (parent.row() != -1 && parent.column() != -1)
{
return 0;
}
return qxt_d().maxColumn;
}
/*!
\reimp
*/
QVariant QxtCsvModel::data(const QModelIndex& index, int role) const
{
if (index.parent() != QModelIndex())
{
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole)
{
if (index.row() < 0 || index.column() < 0 || index.row() >= rowCount())
{
return QVariant();
}
const QStringList& row = qxt_d().csvData[index.row()];
if (index.column() >= row.length())
{
return QVariant();
}
return row[index.column()];
}
return QVariant();
}
/*!
\reimp
*/
QVariant QxtCsvModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (section < qxt_d().header.count() && orientation == Qt::Horizontal && (role == Qt::DisplayRole
|| role == Qt::EditRole
|| role == Qt::UserRole))
{
return qxt_d().header[section];
}
else
{
return QAbstractTableModel::headerData(section, orientation, role);
}
}
/*!
\overload
Reads in a CSV file from the provided \a file using \a codec.
*/
void QxtCsvModel::setSource(const QString filename, bool withHeader, QChar separator, QTextCodec* codec)
{
QFile src(filename);
setSource(&src, withHeader, separator, codec);
}
/*!
Reads in a CSV file from the provided \a file using \a codec.
The value of \a separator will be used to delimit fields, subject to the specified \a quoteMode.
If \a withHeader is set to true, the first line of the file will be used to populate the model's
horizontal header.
\sa quoteMode
*/
void QxtCsvModel::setSource(QIODevice *file, bool withHeader, QChar separator, QTextCodec* codec)
{
QxtCsvModelPrivate* d_ptr = &qxt_d();
bool headerSet = !withHeader;
if (not file->isOpen())
{
file->open(QIODevice::ReadOnly);
}
if (withHeader)
{
d_ptr->maxColumn = 0;
}
else
{
d_ptr->maxColumn = d_ptr->header.size();
}
d_ptr->csvData.clear();
QStringList row;
QString field;
QChar quote;
QChar ch, buffer(0);
bool readCR = false;
QTextStream stream(file);
if (codec)
{
stream.setCodec(codec);
}
else
{
stream.setAutoDetectUnicode(true);
}
while (not stream.atEnd())
{
if (buffer != QChar(0))
{
ch = buffer;
buffer = QChar(0);
}
else
{
stream >> ch;
}
if (ch == '\n' && readCR)
{
continue;
}
else if (ch == '\r')
{
readCR = true;
}
else
{
readCR = false;
}
if (ch != separator && (ch.category() == QChar::Separator_Line || ch.category() == QChar::Separator_Paragraph
|| ch.category() == QChar::Other_Control))
{
row << field;
field.clear();
if (not row.isEmpty())
{
if (not headerSet)
{
d_ptr->header = row;
headerSet = true;
}
else
{
d_ptr->csvData.append(row);
}
if (row.length() > d_ptr->maxColumn)
{
d_ptr->maxColumn = row.length();
}
}
row.clear();
}
else if ((d_ptr->quoteMode & DoubleQuote && ch == '"') || (d_ptr->quoteMode & SingleQuote && ch == '\''))
{
quote = ch;
do
{
stream >> ch;
if (ch == '\\' && d_ptr->quoteMode & BackslashEscape)
{
stream >> ch;
}
else if (ch == quote)
{
if (d_ptr->quoteMode & TwoQuoteEscape)
{
stream >> buffer;
if (buffer == quote)
{
buffer = QChar(0);
field.append(ch);
continue;
}
}
break;
}
field.append(ch);
} while (!stream.atEnd());
}
else if (ch == separator)
{
row << field;
field.clear();
}
else
{
field.append(ch);
}
}
if (not field.isEmpty())
{
row << field;
}
if (not row.isEmpty())
{
if (not headerSet)
{
d_ptr->header = row;
}
else
{
d_ptr->csvData.append(row);
}
}
file->close();
}
/*!
Sets the horizontal headers of the model to the values provided in \a data.
*/
void QxtCsvModel::setHeaderData(const QStringList& data)
{
qxt_d().header = data;
emit headerDataChanged(Qt::Horizontal, 0, data.count());
}
/*!
\reimp
*/
bool QxtCsvModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant& value, int role)
{
if (orientation != Qt::Horizontal)
{
return false; // We don't support the vertical header
}
if (role != Qt::DisplayRole && role != Qt::EditRole)
{
return false; // We don't support any other roles
}
if (section < 0)
{
return false; // Bogus input
}
while (section > qxt_d().header.size())
{
qxt_d().header << QString();
}
qxt_d().header[section] = value.toString();
emit headerDataChanged(Qt::Horizontal, section, section);
return true;
}
/*!
\reimp
*/
bool QxtCsvModel::setData(const QModelIndex& index, const QVariant& data, int role)
{
if (index.parent() != QModelIndex())
{
return false;
}
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole)
{
if (index.row() >= rowCount() || index.column() >= columnCount() || index.row() < 0 || index.column() < 0)
{
return false;
}
QStringList& row = qxt_d().csvData[index.row()];
while (row.length() <= index.column())
{
row << QString();
}
row[index.column()] = data.toString();
emit dataChanged(index, index);
return true;
}
return false;
}
/*!
\reimp
*/
bool QxtCsvModel::insertRow(int row, const QModelIndex& parent)
{
return insertRows(row, 1, parent);
}
/*!
\reimp
*/
bool QxtCsvModel::insertRows(int row, int count, const QModelIndex& parent)
{
if (parent != QModelIndex() || row < 0)
{
return false;
}
emit beginInsertRows(parent, row, row + count);
QxtCsvModelPrivate& d_ptr = qxt_d();
if (row >= rowCount())
{
for(int i = 0; i < count; i++)
{
d_ptr.csvData << QStringList();
}
}
else
{
for(int i = 0; i < count; i++)
{
d_ptr.csvData.insert(row, QStringList());
}
}
emit endInsertRows();
return true;
}
/*!
\reimp
*/
bool QxtCsvModel::removeRow(int row, const QModelIndex& parent)
{
return removeRows(row, 1, parent);
}
/*!
\reimp
*/
bool QxtCsvModel::removeRows(int row, int count, const QModelIndex& parent)
{
if (parent != QModelIndex() || row < 0)
{
return false;
}
if (row >= rowCount())
{
return false;
}
if (row + count >= rowCount())
{
count = rowCount() - row;
}
emit beginRemoveRows(parent, row, row + count);
QxtCsvModelPrivate& d_ptr = qxt_d();
for (int i = 0;i < count;i++)
{
d_ptr.csvData.removeAt(row);
}
emit endRemoveRows();
return true;
}
/*!
\reimp
*/
bool QxtCsvModel::insertColumn(int col, const QModelIndex& parent)
{
return insertColumns(col, 1, parent);
}
/*!
\reimp
*/
bool QxtCsvModel::insertColumns(int col, int count, const QModelIndex& parent)
{
if (parent != QModelIndex() || col < 0)
{
return false;
}
beginInsertColumns(parent, col, col + count - 1);
QxtCsvModelPrivate& d_ptr = qxt_d();
for (int i = 0; i < rowCount(); i++)
{
QStringList& row = d_ptr.csvData[i];
while (col >= row.length())
{
row.append(QString());
}
for (int j = 0; j < count; j++)
{
row.insert(col, QString());
}
}
for (int i = 0; i < count ;i++)
{
d_ptr.header.insert(col, QString());
}
d_ptr.maxColumn += count;
endInsertColumns();
return true;
}
/*!
\reimp
*/
bool QxtCsvModel::removeColumn(int col, const QModelIndex& parent)
{
return removeColumns(col, 1, parent);
}
/*!
\reimp
*/
bool QxtCsvModel::removeColumns(int col, int count, const QModelIndex& parent)
{
if (parent != QModelIndex() || col < 0)
{
return false;
}
if (col >= columnCount())
{
return false;
}
if (col + count >= columnCount())
{
count = columnCount() - col;
}
emit beginRemoveColumns(parent, col, col + count);
QxtCsvModelPrivate& d_ptr = qxt_d();
QString before, after;
for (int i = 0; i < rowCount(); i++)
{
for (int j = 0; j < count; j++)
{
d_ptr.csvData[i].removeAt(col);
}
}
for (int i = 0; i < count; i++)
{
d_ptr.header.removeAt(col);
}
emit endRemoveColumns();
return true;
}
static QString qxt_addCsvQuotes(QxtCsvModel::QuoteMode mode, QString field)
{
bool addDoubleQuotes = ((mode & QxtCsvModel::DoubleQuote) && field.contains('"'));
bool addSingleQuotes = ((mode & QxtCsvModel::SingleQuote) && field.contains('\''));
bool quoteField = (mode & QxtCsvModel::AlwaysQuoteOutput) || addDoubleQuotes || addSingleQuotes;
if (quoteField && !addDoubleQuotes && !addSingleQuotes)
{
if (mode & QxtCsvModel::DoubleQuote)
{
addDoubleQuotes = true;
}
else if(mode & QxtCsvModel::SingleQuote)
{
addSingleQuotes = true;
}
}
if (mode & QxtCsvModel::BackslashEscape)
{
if (addDoubleQuotes)
{
return '"' + field.replace("\\", "\\\\").replace("\"", "\\\"") + '"';
}
if (addSingleQuotes)
{
return '\'' + field.replace("\\", "\\\\").replace("'", "\\'") + '\'';
}
}
else
{
if (addDoubleQuotes)
{
return '"' + field.replace("\"", "\"\"") + '"';
}
if (addSingleQuotes)
{
return '\'' + field.replace("'", "''") + '\'';
}
}
return field;
}
/*!
Outputs the content of the model as a CSV file to the device \a dest using \a codec.
Fields in the output file will be separated by \a separator. Set \a withHeader to true
to output a row of headers at the top of the file.
*/
void QxtCsvModel::toCSV(QIODevice* dest, bool withHeader, QChar separator, QTextCodec* codec) const
{
const QxtCsvModelPrivate& d_ptr = qxt_d();
int row, col, rows, cols;
rows = rowCount();
cols = columnCount();
QString data;
if (not dest->isOpen())
{
dest->open(QIODevice::WriteOnly | QIODevice::Truncate);
}
QTextStream stream(dest);
if (codec)
{
stream.setCodec(codec);
}
if (withHeader)
{
data = "";
for (col = 0; col < cols; ++col)
{
if (col > 0)
{
data += separator;
}
data += qxt_addCsvQuotes(d_ptr.quoteMode, d_ptr.header.at(col));
}
stream << data << endl;
}
for (row = 0; row < rows; ++row)
{
const QStringList& rowData = d_ptr.csvData[row];
data = "";
for (col = 0; col < cols; ++col)
{
if (col > 0)
{
data += separator;
}
if (col < rowData.length())
{
data += qxt_addCsvQuotes(d_ptr.quoteMode, rowData.at(col));
}
else
{
data += qxt_addCsvQuotes(d_ptr.quoteMode, QString());
}
}
stream << data << endl;
}
stream << flush;
dest->close();
}
/*!
\overload
Outputs the content of the model as a CSV file to the file specified by \a filename using \a codec.
Fields in the output file will be separated by \a separator. Set \a withHeader to true
to output a row of headers at the top of the file.
*/
void QxtCsvModel::toCSV(const QString filename, bool withHeader, QChar separator, QTextCodec* codec) const
{
QFile dest(filename);
toCSV(&dest, withHeader, separator, codec);
}
/*!
\reimp
*/
Qt::ItemFlags QxtCsvModel::flags(const QModelIndex& index) const
{
return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
}
/*!
* Returns the current quoting mode.
* \sa setQuoteMode
*/
QxtCsvModel::QuoteMode QxtCsvModel::quoteMode() const
{
return qxt_d().quoteMode;
}
/*!
* Sets the current quoting mode. The default quoting mode is BothQuotes | BackslashEscape.
*
* The quoting mode determines what kinds of quoting is used for reading and writing CSV files.
* \sa quoteMode
* \sa QuoteOption
*/
void QxtCsvModel::setQuoteMode(QuoteMode mode)
{
qxt_d().quoteMode = mode;
}
/*!
Sets the content of the cell at row \a row and column \a column to \a value.
\sa text
*/
void QxtCsvModel::setText(int row, int column, const QString& value)
{
setData(index(row, column), value);
}
/*!
Fetches the content of the cell at row \a row and column \a column.
\sa setText
*/
QString QxtCsvModel::text(int row, int column) const
{
return data(index(row, column)).toString();
}
/*!
Sets the content of the header for column \a column to \a value.
\sa headerText
*/
void QxtCsvModel::setHeaderText(int column, const QString& value)
{
setHeaderData(column, Qt::Horizontal, value);
}
/*!
Fetches the content of the cell at row \a row and column \a column.
\sa setText
*/
QString QxtCsvModel::headerText(int column) const
{
return headerData(column, Qt::Horizontal).toString();
}

View File

@ -0,0 +1,113 @@
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#ifndef QXTCSVMODEL_H
#define QXTCSVMODEL_H
#include <QAbstractTableModel>
#include <QVariant>
#include <QIODevice>
#include <QChar>
#include <QString>
#include <QStringList>
#include <QModelIndex>
#include <def.h>
class QTextCodec;
class QxtCsvModelPrivate;
class QxtCsvModel : public QAbstractTableModel
{
Q_OBJECT
public:
QxtCsvModel(QObject *parent = nullptr);
explicit QxtCsvModel(QIODevice *file, QObject *parent = nullptr, bool withHeader = false, QChar separator = ',');
explicit QxtCsvModel(const QString filename, QObject *parent = nullptr, bool withHeader = false,
QChar separator = ',');
virtual ~QxtCsvModel();
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
virtual bool setData(const QModelIndex& index, const QVariant& data, int role = Qt::EditRole) Q_DECL_OVERRIDE;
virtual QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant& value,
int role = Qt::DisplayRole) Q_DECL_OVERRIDE;
void setHeaderData(const QStringList& data);
QString text(int row, int column) const;
void setText(int row, int column, const QString& value);
QString headerText(int column) const;
void setHeaderText(int column, const QString& value);
bool insertRow(int row, const QModelIndex& parent = QModelIndex());
virtual bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) Q_DECL_OVERRIDE;
bool removeRow(int row, const QModelIndex& parent = QModelIndex());
virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) Q_DECL_OVERRIDE;
bool insertColumn(int col, const QModelIndex& parent = QModelIndex());
virtual bool insertColumns(int col, int count, const QModelIndex& parent = QModelIndex()) Q_DECL_OVERRIDE;
bool removeColumn(int col, const QModelIndex& parent = QModelIndex());
virtual bool removeColumns(int col, int count, const QModelIndex& parent = QModelIndex()) Q_DECL_OVERRIDE;
void setSource(QIODevice *file, bool withHeader = false, QChar separator = ',', QTextCodec* codec = nullptr);
void setSource(const QString filename, bool withHeader = false, QChar separator = ',', QTextCodec* codec = nullptr);
void toCSV(QIODevice *file, bool withHeader = false, QChar separator = ',', QTextCodec* codec = nullptr) const;
void toCSV(const QString filename, bool withHeader = false, QChar separator = ',',
QTextCodec* codec = nullptr) const;
enum QuoteOption { NoQuotes = 0,
SingleQuote = 1,
DoubleQuote = 2,
BothQuotes = 3,
NoEscape = 0,
TwoQuoteEscape = 4,
BackslashEscape = 8,
AlwaysQuoteOutput = 16,
DefaultQuoteMode = BothQuotes | BackslashEscape | AlwaysQuoteOutput };
Q_DECLARE_FLAGS(QuoteMode, QuoteOption)
QuoteMode quoteMode() const;
void setQuoteMode(QuoteMode mode);
virtual Qt::ItemFlags flags(const QModelIndex& index) const Q_DECL_OVERRIDE;
private:
Q_DISABLE_COPY(QxtCsvModel)
QXT_DECLARE_PRIVATE(QxtCsvModel)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QxtCsvModel::QuoteMode)
#endif // QXTCSVMODEL_H

View File

@ -9,7 +9,8 @@ SOURCES += \
$$PWD/projectversion.cpp \
$$PWD/vcommonsettings.cpp \
$$PWD/vtapesettings.cpp \
$$PWD/commandoptions.cpp
$$PWD/commandoptions.cpp \
$$PWD/qxtcsvmodel.cpp
win32-msvc*:SOURCES += $$PWD/stable.cpp
@ -27,7 +28,8 @@ HEADERS += \
$$PWD/debugbreak.h \
$$PWD/vlockguard.h \
$$PWD/vsysexits.h \
$$PWD/commandoptions.h
$$PWD/commandoptions.h \
$$PWD/qxtcsvmodel.h
# Qt's versions
# 5.0.0, 5.0.1, 5.0.2

View File

@ -30,12 +30,17 @@
#include <QApplication>
#include <QDir>
#include <QTextCodec>
const QString VTapeSettings::SettingDataBaseGeometry = QStringLiteral("database/geometry");
const QString VTapeSettings::SettingDefHeight = QStringLiteral("gradation/defHeight");
const QString VTapeSettings::SettingDefSize = QStringLiteral("gradation/defHeight");
const QString VTapeSettings::SettingCSVWithHeader = QStringLiteral("csv/withHeader");
const QString VTapeSettings::SettingCSVCodec = QStringLiteral("csv/withCodec");
const QString VTapeSettings::SettingCSVSeparator = QStringLiteral("csv/withSeparator");
//---------------------------------------------------------------------------------------------------------------------
VTapeSettings::VTapeSettings(Format format, Scope scope, const QString &organization, const QString &application,
QObject *parent)
@ -78,3 +83,70 @@ int VTapeSettings::GetDefSize() const
{
return value(SettingDefSize, 50).toInt();
}
//---------------------------------------------------------------------------------------------------------------------
void VTapeSettings::SetCSVWithHeader(bool withHeader)
{
setValue(SettingCSVWithHeader, withHeader);
}
//---------------------------------------------------------------------------------------------------------------------
bool VTapeSettings::GetCSVWithHeader() const
{
return value(SettingCSVWithHeader, false).toBool();
}
//---------------------------------------------------------------------------------------------------------------------
void VTapeSettings::SetCSVCodec(int mib)
{
setValue(SettingCSVCodec, mib);
}
//---------------------------------------------------------------------------------------------------------------------
int VTapeSettings::GetCSVCodec() const
{
return value(SettingCSVCodec, QTextCodec::codecForLocale()->mibEnum()).toInt();
}
//---------------------------------------------------------------------------------------------------------------------
void VTapeSettings::SetCSVSeparator(const QChar &separator)
{
switch(separator.toLatin1())
{
case '\t':
setValue(SettingCSVSeparator, 0);
break;
case ';':
setValue(SettingCSVSeparator, 1);
break;
case ' ':
setValue(SettingCSVSeparator, 2);
break;
case ',':
default:
setValue(SettingCSVSeparator, 3);
break;
}
}
//---------------------------------------------------------------------------------------------------------------------
QChar VTapeSettings::GetCSVSeparator() const
{
const quint8 separator = static_cast<quint8>(value(SettingCSVSeparator, 3).toUInt());
switch(separator)
{
case 0:
return QChar('\t');
break;
case 1:
return QChar(';');
break;
case 2:
return QChar(' ');
break;
case 3:
default:
return QChar(',');
break;
}
}

View File

@ -47,12 +47,24 @@ public:
void SetDefSize(int value);
int GetDefSize() const;
void SetCSVWithHeader(bool withHeader);
bool GetCSVWithHeader() const;
void SetCSVCodec(int mib);
int GetCSVCodec() const;
void SetCSVSeparator(const QChar &separator);
QChar GetCSVSeparator() const;
private:
Q_DISABLE_COPY(VTapeSettings)
static const QString SettingDataBaseGeometry;
static const QString SettingDefHeight;
static const QString SettingDefSize;
static const QString SettingCSVWithHeader;
static const QString SettingCSVCodec;
static const QString SettingCSVSeparator;
};
#endif // VTAPESETTINGS_H