REWORKED JKQTBasePlotter::saveData() with a more consistent interface and a bool return value to indicate success or failure + improved documentation

NEW: added two functions to JKQTPSaveDataAdapter
This commit is contained in:
jkriege2 2023-12-22 20:34:32 +01:00
parent 8c01738359
commit 36947a4bca
3 changed files with 84 additions and 23 deletions

View File

@ -29,8 +29,9 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
<li>BREAKING/REWORKED: Implement better/more access functions to the graphs (inlcuding sorting, moving up/down, appending/prepending, graphs-iterators ...), solves issue <a href="https://github.com/jkriege2/JKQtPlotter/pull/97">#97</a>, thanks to <a href="https://github.com/sim186">user:sim186</a> for bringing this up</li> <li>BREAKING/REWORKED: Implement better/more access functions to the graphs (inlcuding sorting, moving up/down, appending/prepending, graphs-iterators ...), solves issue <a href="https://github.com/jkriege2/JKQtPlotter/pull/97">#97</a>, thanks to <a href="https://github.com/sim186">user:sim186</a> for bringing this up</li>
<li>BREAKING/FIXED: fixed issue <a href="https://github.com/jkriege2/JKQtPlotter/pull/96">#96</a>: JKQTPlotter::saveAsPixelImage() does not add a border around the image any longer (can be reacivated by a new optional function parameter), thanks to <a href="https://github.com/nmielcarek">user:nmielcarek</a> for reporting</li> <li>BREAKING/FIXED: fixed issue <a href="https://github.com/jkriege2/JKQtPlotter/pull/96">#96</a>: JKQTPlotter::saveAsPixelImage() does not add a border around the image any longer (can be reacivated by a new optional function parameter), thanks to <a href="https://github.com/nmielcarek">user:nmielcarek</a> for reporting</li>
<li>BREAKING/FIXED: JKQTPXFunctionLineGraph and JKQTPYFunctionLineGraph do no longer contribute to auto-scaling, as that depends on the current plot-axes (hen-egg-problem).</li> <li>BREAKING/FIXED: JKQTPXFunctionLineGraph and JKQTPYFunctionLineGraph do no longer contribute to auto-scaling, as that depends on the current plot-axes (hen-egg-problem).</li>
<li>FIXED/BREAKING: graph symbols were not properly applied when reading styles (in fact they were shuffled under some circumstances)</li> <li>BREAKING/FIXED: graph symbols were not properly applied when reading styles (in fact they were shuffled under some circumstances)</li>
<li>NEW/BREAKING: rework layouting of keys/legends: new classes JKQTPBaseKey, JKQTPMainKey ... and removed several styling function for the main key from JKQTBasePlotter and JKQTPlotter (these are now accessible via JKQTBasePlotter::getMainKey() </li> <li>BREAKING/NEW: rework layouting of keys/legends: new classes JKQTPBaseKey, JKQTPMainKey ... and removed several styling function for the main key from JKQTBasePlotter and JKQTPlotter (these are now accessible via JKQTBasePlotter::getMainKey() </li>
<li>BREAKING/DEPRECATED: deprecated JKQTBasePlotter::zoom() and JKQTPlotter::zoom(), use JKQTBasePlotter::setXY() and JKQTPlotter::setXY() instead</li>
<li>FIXED issue described in <a href="https://github.com/jkriege2/JKQtPlotter/pull/62">#62: Fix custom labels draw, because giving exactly two label-strings did not display all of them</a>, thanks to <a href="https://github.com/FalsinSoft">user:FalsinSoft</a></li> <li>FIXED issue described in <a href="https://github.com/jkriege2/JKQtPlotter/pull/62">#62: Fix custom labels draw, because giving exactly two label-strings did not display all of them</a>, thanks to <a href="https://github.com/FalsinSoft">user:FalsinSoft</a></li>
<li>FIXED issue <a href="https://github.com/jkriege2/JKQtPlotter/pull/70">#70: Typo in jkqtplotter/CMakeLists.txt</a>, thanks to <a href="https://github.com/tedlinlab">user:tedlinlab</a></li> <li>FIXED issue <a href="https://github.com/jkriege2/JKQtPlotter/pull/70">#70: Typo in jkqtplotter/CMakeLists.txt</a>, thanks to <a href="https://github.com/tedlinlab">user:tedlinlab</a></li>
<li>FIXED issue <a href="https://github.com/jkriege2/JKQtPlotter/pull/80">#80: Bug with multiple inheritance with Q_GDAGET with CLANG</a>, thanks to <a href="https://github.com/igormironchik">user:igormironchik</a>, caused by <a href="https://bugreports.qt.io/browse/QTBUG-104874">QTBUG-104874</a></li> <li>FIXED issue <a href="https://github.com/jkriege2/JKQtPlotter/pull/80">#80: Bug with multiple inheritance with Q_GDAGET with CLANG</a>, thanks to <a href="https://github.com/igormironchik">user:igormironchik</a>, caused by <a href="https://bugreports.qt.io/browse/QTBUG-104874">QTBUG-104874</a></li>
@ -49,6 +50,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
<li>FIXED/IMPROVED issue <a href="https://github.com/jkriege2/JKQtPlotter/issues/100">#100: Add option to disable resize delay feature by setting the delay to zero</a> (thanks to <a href="https://github.com/fpalazzolo">user:fpalazzolo</a> for reporting)</li> <li>FIXED/IMPROVED issue <a href="https://github.com/jkriege2/JKQtPlotter/issues/100">#100: Add option to disable resize delay feature by setting the delay to zero</a> (thanks to <a href="https://github.com/fpalazzolo">user:fpalazzolo</a> for reporting)</li>
<li>FIXED/NEW: placement of plot-title (was not centerd in its box, but glued to the bottom) by adding a plotstyle parameter JKQTBasePlotterStyle::plotLabelOffset</li> <li>FIXED/NEW: placement of plot-title (was not centerd in its box, but glued to the bottom) by adding a plotstyle parameter JKQTBasePlotterStyle::plotLabelOffset</li>
<li>FIXED/REWORKED issue <a href="https://github.com/jkriege2/JKQtPlotter/issues/111">#111: Can't write to PDF files with JKQTPlotter::saveImage() when passing a filename ending in ".pdf"</a> (thanks to <a href="https://github.com/fpalazzolo">user:fpalazzolo/a> for reporting):<br/>While fixing this issue, the functions JKQTBasePlotter::saveImage() etc. gained a bool return value to indicate whether sacing was successful.</li> <li>FIXED/REWORKED issue <a href="https://github.com/jkriege2/JKQtPlotter/issues/111">#111: Can't write to PDF files with JKQTPlotter::saveImage() when passing a filename ending in ".pdf"</a> (thanks to <a href="https://github.com/fpalazzolo">user:fpalazzolo/a> for reporting):<br/>While fixing this issue, the functions JKQTBasePlotter::saveImage() etc. gained a bool return value to indicate whether sacing was successful.</li>
<li>REWORKED JKQTBasePlotter::saveData() with a more consistent interface and a bool return value to indicate success or failure + improved documentation, added methods to JKQTPSaveDataAdapter that allow to specify file extensions and an ID for the plugin.</li>
<li>REORGANIZED: separated line-graphs from jkqtpscatter.h/.cpp into jkqtplines.h/.cpp</li> <li>REORGANIZED: separated line-graphs from jkqtpscatter.h/.cpp into jkqtplines.h/.cpp</li>
<li>IMPROVED: QT6-compatibility by removing deprecated warnings</li> <li>IMPROVED: QT6-compatibility by removing deprecated warnings</li>
<li>IMPROVED: added missing override declarations</li> <li>IMPROVED: added missing override declarations</li>

View File

@ -3264,7 +3264,7 @@ void JKQTBasePlotter::copyDataMatlab() {
saveUserSettings(); saveUserSettings();
} }
void JKQTBasePlotter::saveData(const QString& filename, const QString &format) { bool JKQTBasePlotter::saveData(const QString& filename, const QString &format) {
loadUserSettings(); loadUserSettings();
QStringList fileformats; QStringList fileformats;
QStringList fileformatIDs; QStringList fileformatIDs;
@ -3283,20 +3283,27 @@ void JKQTBasePlotter::saveData(const QString& filename, const QString &format) {
fileformats<<tr("Matlab Script (*.m)"); fileformats<<tr("Matlab Script (*.m)");
fileformatIDs<<"m"; fileformatIDs<<"m";
QMap<QString, QStringList> saveAdapterFileExtensions;
{ {
JKQTPSynchronized<QList<JKQTPSaveDataAdapter*>>::Locker lock(jkqtpSaveDataAdapters); JKQTPSynchronized<QList<JKQTPSaveDataAdapter*>>::Locker lock(jkqtpSaveDataAdapters);
for (int i=0; i<jkqtpSaveDataAdapters.get().size(); i++) { for (int i=0; i<jkqtpSaveDataAdapters.get().size(); i++) {
const QString fid=jkqtpSaveDataAdapters.get()[i]->getFormatID();
fileformats<<jkqtpSaveDataAdapters.get()[i]->getFilter(); fileformats<<jkqtpSaveDataAdapters.get()[i]->getFilter();
fileformatIDs<<QString("custom%1").arg(i); fileformatIDs<<fid;
saveAdapterFileExtensions[fid]=jkqtpSaveDataAdapters.get()[i]->getFileExtension();
} }
} }
QString fn=filename; QString fn=filename;
QString fmt=format.toLower(); QString fmt=format.toLower().trimmed();
if (fmt=="sylk") fmt="slk";
else if (fmt=="ssv") fmt="sem";
if (fmt.isEmpty()) { if (fmt.isEmpty()) {
QString e=QFileInfo(filename).suffix().toLower();// jkqtp_tolower(extract_file_ext(fn.toStdString())); const QString e=QFileInfo(filename).suffix().toLower();
fmt=e;
if (e=="csv" || e=="dat") { if (e=="csv" || e=="dat") {
fmt="csv"; fmt="csv";
} else if (e=="txt") { } else if (e=="txt") {
@ -3307,6 +3314,16 @@ void JKQTBasePlotter::saveData(const QString& filename, const QString &format) {
fmt="dif"; fmt="dif";
} else if (e=="m") { } else if (e=="m") {
fmt="m"; fmt="m";
} else if (e=="ssv") {
fmt="sem";
}
if (!fileformatIDs.contains(fmt)) {
for (auto it= saveAdapterFileExtensions.begin(); it!=saveAdapterFileExtensions.end(); ++it) {
if (it.value().contains(e)) {
fmt=it.key();
break;
}
}
} }
} }
if (fn.isEmpty()) { if (fn.isEmpty()) {
@ -3322,13 +3339,8 @@ void JKQTBasePlotter::saveData(const QString& filename, const QString &format) {
} }
//qDebug()<<"after: currentSaveDirectory="<<currentSaveDirectory; //qDebug()<<"after: currentSaveDirectory="<<currentSaveDirectory;
fmt="csv"; fmt="csv";
for (int i=0; i<fileformats.size(); i++) { const int filtIdx=fileformats.indexOf(selectedFilter);
if (selectedFilter.contains(fileformats[i])) { if (filtIdx>=0) fmt=fileformatIDs[filtIdx] ;
fmt=fileformatIDs[i] ;
}
}
} }
saveUserSettings(); saveUserSettings();
@ -3336,30 +3348,49 @@ void JKQTBasePlotter::saveData(const QString& filename, const QString &format) {
if (fmt=="csv") { if (fmt=="csv") {
saveAsCSV(fn); saveAsCSV(fn);
return true;
} else if (fmt=="tab") { } else if (fmt=="tab") {
saveAsTabSV(fn); saveAsTabSV(fn);
return true;
} else if (fmt=="gex") { } else if (fmt=="gex") {
saveAsGerExcelCSV(fn); saveAsGerExcelCSV(fn);
return true;
} else if (fmt=="sem") { } else if (fmt=="sem") {
saveAsSemicolonSV(fn); saveAsSemicolonSV(fn);
return true;
} else if (fmt=="slk") { } else if (fmt=="slk") {
saveAsSYLK(fn); saveAsSYLK(fn);
return true;
} else if (fmt=="dif") { } else if (fmt=="dif") {
saveAsDIF(fn); saveAsDIF(fn);
return true;
} else if (fmt=="m") { } else if (fmt=="m") {
saveAsMatlab(fn); saveAsMatlab(fn);
} else if (fmt.startsWith("custom")) { return true;
} else if (fmt.startsWith("custom")) { // for backward compatibility!
QString fidx=fmt; QString fidx=fmt;
fidx=fidx.remove(0,6); fidx=fidx.remove(0,6);
int idx=fidx.toInt(); int idx=fidx.toInt();
JKQTPSynchronized<QList<JKQTPSaveDataAdapter*>>::Locker lock(jkqtpSaveDataAdapters); JKQTPSynchronized<QList<JKQTPSaveDataAdapter*>>::Locker lock(jkqtpSaveDataAdapters);
if (idx>=0 && idx<jkqtpSaveDataAdapters.get().size() && jkqtpSaveDataAdapters.get()[idx]) { if (idx>=0 && idx<jkqtpSaveDataAdapters.get().size() && jkqtpSaveDataAdapters.get()[idx]) {
QStringList cn; QStringList columnNames;
QList<QVector<double> > dataset=datastore->getData(&cn); const QList<QVector<double> > dataset=datastore->getData(&columnNames);
jkqtpSaveDataAdapters.get()[idx]->saveJKQTPData(fn, dataset, cn); jkqtpSaveDataAdapters.get()[idx]->saveJKQTPData(fn, dataset, columnNames);
return true;
}
} else {
JKQTPSynchronized<QList<JKQTPSaveDataAdapter*>>::Locker lock(jkqtpSaveDataAdapters);
for (int i=0; i<jkqtpSaveDataAdapters.get().size(); i++) {
if (fmt == jkqtpSaveDataAdapters.get()[i]->getFormatID()) {
QStringList columnNames;
const QList<QVector<double> > dataset=datastore->getData(&columnNames);
jkqtpSaveDataAdapters.get()[i]->saveJKQTPData(fn, dataset, columnNames);
return true;
} }
} }
} }
}
return false;
} }
void JKQTBasePlotter::saveAsCSV(const QString& filename) { void JKQTBasePlotter::saveAsCSV(const QString& filename) {

View File

@ -74,6 +74,11 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPSaveDataAdapter {
virtual ~JKQTPSaveDataAdapter() ; virtual ~JKQTPSaveDataAdapter() ;
/** \brief Filter-String for a Qt File-Dialog, e.g. <code>"CSV Files (*.csv)"</code> */ /** \brief Filter-String for a Qt File-Dialog, e.g. <code>"CSV Files (*.csv)"</code> */
virtual QString getFilter() const=0; virtual QString getFilter() const=0;
/** \brief a plugin-ID, i.e. a unique name for this format plugin, e.g. \c MyPluginExport_MATLABMAT */
virtual QString getFormatID() const=0;
/** \brief returns a list (in lower-case) of the file extensions supported by this plugin, e.g. \c {"mat"} */
virtual QStringList getFileExtension() const=0;
/** \brief actually save the table \a data into file \a filename . The parameter \a columnNames provides a name for each column */ /** \brief actually save the table \a data into file \a filename . The parameter \a columnNames provides a name for each column */
virtual void saveJKQTPData(const QString& filename, const QList<QVector<double> >& data, const QStringList& columnNames) const=0; virtual void saveJKQTPData(const QString& filename, const QList<QVector<double> >& data, const QStringList& columnNames) const=0;
}; };
@ -1823,19 +1828,32 @@ class JKQTPLOTTER_LIB_EXPORT JKQTBasePlotter: public QObject {
/** \brief save the data used for the current plot. The file format is extracted from the file extension (csv, ...) /** \brief save the data used for the current plot. The file format is extracted from the file extension (csv, ...)
* *
* The parameter \a format specifies the export format. if it is empty the format will be choosen according to the file extension, or * \param filename the filename to save to, if empty a file save dialog is displayed
* \param format The parameter \a format specifies the export format. if it is empty the format will be choosen according to the file extension, or
* if \a filename is also empty the format will be choosen according to what is selected in the file selection dialog. * if \a filename is also empty the format will be choosen according to what is selected in the file selection dialog.
* See below for a listing of supported values.
* \returns returns \c true if the data was exported successfully.
* *
* If \a format is \c "slk" the output will be in SYLK format, if \a format is \c "csv" or \a "dat" the output will be comma separated values * These values are supported for \a format (if \a format is not provided, the function tries to guess it from the file extensions liste below):
* and if \a format is \c "txt" the output will be tab separated values. * - \c "csv", Comma Separated Values, dot as decimal separator (see also <a href="https://en.wikipedia.org/wiki/Comma-separated_values">https://en.wikipedia.org/wiki/Comma-separated_values</a>, extensions: \c *.csv , \c *.dat , see JKQTBasePlotter::saveAsCSV()
* - \c "tab" Tab Separated Values, dot as decimal separator, extensions: \c *.txt , see JKQTBasePlotter::saveAsTabSV()
* - \c "sem" or \c "ssv", Semicolon Separated Values, dot as decimal separator, extensions: \c *.sem , \c *.ssv , see JKQTBasePlotter::saveAsSemicolonSV()
* - \c "gex", Semicolon Separated Values for German Excel, i.e. comma as decimal separator, extensions: \c *.gex , see JKQTBasePlotter::saveAsGerExcelCSV()
* - \c "slk" or \c "sylk" , SYmbolik LinK (SYLK) spreadsheet (see <a href="https://en.wikipedia.org/wiki/Symbolic_Link_(SYLK)">https://en.wikipedia.org/wiki/Symbolic_Link_(SYLK)</a> ), extensions: \c *.slk , \c *.sylk , see JKQTBasePlotter::saveAsSYLK()
* - \c "dif", Data Interchange Format (see <a href="https://en.wikipedia.org/wiki/Data_Interchange_Format">https://en.wikipedia.org/wiki/Data_Interchange_Format</a>), extensions: \c *.dif , see JKQTBasePlotter::saveAsDIF()
* - \c "m", Matlab Script, extensions: \c *.m , see JKQTBasePlotter::saveAsMatlab()
* .
*
* In addition you can use the custom exporters implemented as JKQTPSaveDataAdapter and registered using JKQTBasePlotter::registerSaveDataAdapter().
* For these you need to use \a format <code>= "customN"</code>, where N is the index of the exporter in the list of registered exporters.
*/ */
void saveData(const QString& filename=QString(""), const QString& jkqtp_format=QString("")); bool saveData(const QString& filename=QString(""), const QString& format=QString(""));
/** \brief copy the data used for the current plot to the clipboard /** \brief copy the data used for the current plot to the clipboard
* *
* copies data as tab separated data with the system-decimal point. * copies data as tab separated data with the system-decimal point.
*/ */
void copyData(); void copyData();
/** \brief copy the data used for the current plot to the clipboard in Matlab format /** \brief copy the data used for the current plot to the clipboard as a Matlab script
*/ */
void copyDataMatlab(); void copyDataMatlab();
/** \brief save the current plot data as a Comma Separated Values (CSV) file /** \brief save the current plot data as a Comma Separated Values (CSV) file
@ -1859,30 +1877,40 @@ class JKQTPLOTTER_LIB_EXPORT JKQTBasePlotter: public QObject {
* \param filename the file to save to, if \a filename is empty, a file open dialog will be shown * \param filename the file to save to, if \a filename is empty, a file open dialog will be shown
* *
* \note this function uses CSVdecimalSeparator as decimal separator and CSVcommentInitializer to initialize content lines * \note this function uses CSVdecimalSeparator as decimal separator and CSVcommentInitializer to initialize content lines
*
* \see saveData()
*/ */
void saveAsTabSV(const QString& filename=QString("")); void saveAsTabSV(const QString& filename=QString(""));
/** \brief save the current plot data as a DIF file /** \brief save the current plot data as a DIF file
* *
* \param filename the file to save to, if \a filename is empty, a file open dialog will be shown * \param filename the file to save to, if \a filename is empty, a file open dialog will be shown
*
* \see saveData()
*/ */
void saveAsDIF(const QString& filename=QString("")); void saveAsDIF(const QString& filename=QString(""));
/** \brief save the current plot data as a SYLK spreadsheet file /** \brief save the current plot data as a SYLK spreadsheet file
* *
* \param filename the file to save to, if \a filename is empty, a file open dialog will be shown * \param filename the file to save to, if \a filename is empty, a file open dialog will be shown
*
* \see saveData()
*/ */
void saveAsSYLK(const QString& filename=QString("")); void saveAsSYLK(const QString& filename=QString(""));
/** \brief save the current plot data as a Matlab Script /** \brief save the current plot data as a Matlab Script
* *
* \param filename the file to save to, if \a filename is empty, a file open dialog will be shown * \param filename the file to save to, if \a filename is empty, a file open dialog will be shown
*
* \see copyDataMatlab(), saveData()
*/ */
void saveAsMatlab(const QString& filename=QString("")); void saveAsMatlab(const QString& filename=QString(""));
/** \brief save the current plot data as a Semicolon Separated Values (CSV) file for german Excel, i.e. with comma as decimal separator /** \brief save the current plot data as a Semicolon Separated Values (CSV) file for german Excel, i.e. with comma as decimal separator
* *
* \param filename the file to save to, if \a filename is empty, a file open dialog will be shown * \param filename the file to save to, if \a filename is empty, a file open dialog will be shown
*
* \see saveData()
*/ */
void saveAsGerExcelCSV(const QString& filename=QString("")); void saveAsGerExcelCSV(const QString& filename=QString(""));