From 13a98f86d98bee60673cf08f105a3f9980a4f4d7 Mon Sep 17 00:00:00 2001 From: doluk <69309597+doluk@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:07:09 +0200 Subject: [PATCH 1/9] Add new table icon for csv export to US_Images Signed-off-by: doluk <69309597+doluk@users.noreply.github.com> --- gui/images.qrc | 19 +++++----- gui/us_images.cpp | 89 ++++++++++++++++++++++++----------------------- gui/us_images.h | 2 +- 3 files changed, 56 insertions(+), 54 deletions(-) diff --git a/gui/images.qrc b/gui/images.qrc index cdba3f0c5..b8bbca8d3 100644 --- a/gui/images.qrc +++ b/gui/images.qrc @@ -35,14 +35,15 @@ images/us3-icon-48x48.png images/arrow_left.png images/arrow_right.png - images/analysis.png - images/editing.png - images/import_1.png - images/import_2.png - images/analysis_2.png - images/report.png - images/live_update.png - images/setup.png - images/new_item2.png + images/analysis.png + images/editing.png + images/import_1.png + images/import_2.png + images/analysis_2.png + images/report.png + images/live_update.png + images/setup.png + images/new_item2.png + images/table.png diff --git a/gui/us_images.cpp b/gui/us_images.cpp index 88c5bdaaf..f78394898 100644 --- a/gui/us_images.cpp +++ b/gui/us_images.cpp @@ -37,50 +37,51 @@ const QString US_Images::image_name( int itype ) static const struct nameMap nameMaps[] = { - { US3_SPLASH, "us3-splash" }, - { CHECK, "check" }, - { ARROW_BLUE, "bluearrow" }, - { ARROW_GREEN, "greenarrow" }, - { ARROW_RED, "redarrow" }, - { AXES_BOX, "box" }, - { AXES_FRAME, "frame" }, - { AXES_NONE, "none" }, - { FILE_CELL, "filecell" }, - { FILE_OPEN, "fileopen" }, - { FLOOR_DATA, "floordata" }, - { FLOOR_EMPTY, "floorempty" }, - { FLOOR_ISO, "flooriso" }, - { FLOOR_MESH, "floormesh" }, - { GRID_NONE, "grid" }, - { GRID_BACK, "gridb" }, - { GRID_CEILING, "gridc" }, - { GRID_FLOOR, "gridf" }, - { GRID_FRONT, "gridfr" }, - { GRID_LEFT, "gridl" }, - { GRID_RIGHT, "gridr" }, - { MESH_FILLED, "filledmesh" }, - { MESH_HIDDEN, "hiddenline" }, - { MESH_NODATA, "nodata" }, - { MESH_NORMALS, "normals" }, - { MESH_POLYGON, "polygon" }, - { MESH_SCATTERED, "scattered" }, - { MESH_WIRE, "wireframe" }, - { MOVIE, "movie" }, - { SAVE_CONTENT, "savecontent" }, - { ICON_PROP, "icon" }, - { QWTPLOT, "qwtplot" }, - { US3_ICON, "us3-icon-48x48" }, - { ARROW_LEFT, "arrow_left" }, - { ARROW_RIGHT, "arrow_right" }, - { SETUP_COM, "setup" }, - { ANALYSIS_COM, "analysis" }, - { EDITING_COM, "editing" }, - { ANALYSIS_COM_2, "analysis_2" }, - { LIVE_UPDATE_COM, "live_update" }, - { IMPORT_COM_1, "import_1" }, - { IMPORT_COM_2, "import_2" }, - { REPORT_COM, "report" }, - { NEW_ITEM_COM, "new_item2" } + { US3_SPLASH, "us3-splash" }, + { CHECK, "check" }, + { ARROW_BLUE, "bluearrow" }, + { ARROW_GREEN, "greenarrow" }, + { ARROW_RED, "redarrow" }, + { AXES_BOX, "box" }, + { AXES_FRAME, "frame" }, + { AXES_NONE, "none" }, + { FILE_CELL, "filecell" }, + { FILE_OPEN, "fileopen" }, + { FLOOR_DATA, "floordata" }, + { FLOOR_EMPTY, "floorempty" }, + { FLOOR_ISO, "flooriso" }, + { FLOOR_MESH, "floormesh" }, + { GRID_NONE, "grid" }, + { GRID_BACK, "gridb" }, + { GRID_CEILING, "gridc" }, + { GRID_FLOOR, "gridf" }, + { GRID_FRONT, "gridfr" }, + { GRID_LEFT, "gridl" }, + { GRID_RIGHT, "gridr" }, + { MESH_FILLED, "filledmesh" }, + { MESH_HIDDEN, "hiddenline" }, + { MESH_NODATA, "nodata" }, + { MESH_NORMALS, "normals" }, + { MESH_POLYGON, "polygon" }, + { MESH_SCATTERED, "scattered" }, + { MESH_WIRE, "wireframe" }, + { MOVIE, "movie" }, + { SAVE_CONTENT, "savecontent" }, + { ICON_PROP, "icon" }, + { QWTPLOT, "qwtplot" }, + { US3_ICON, "us3-icon-48x48" }, + { ARROW_LEFT, "arrow_left" }, + { ARROW_RIGHT, "arrow_right" }, + { SETUP_COM, "setup" }, + { ANALYSIS_COM, "analysis" }, + { EDITING_COM, "editing" }, + { ANALYSIS_COM_2, "analysis_2" }, + { LIVE_UPDATE_COM, "live_update" }, + { IMPORT_COM_1, "import_1" }, + { IMPORT_COM_2, "import_2" }, + { REPORT_COM, "report" }, + { NEW_ITEM_COM, "new_item2" }, + { TABLE, "table" } }; static const int nimages = sizeof( nameMaps ) / sizeof( nameMaps[ 0 ] ); diff --git a/gui/us_images.h b/gui/us_images.h index ccef43b47..707b2d4a0 100644 --- a/gui/us_images.h +++ b/gui/us_images.h @@ -58,7 +58,7 @@ class US_GUI_EXTERN US_Images MOVIE, SAVE_CONTENT, ICON_PROP, QWTPLOT, US3_ICON, ARROW_LEFT, ARROW_RIGHT, SETUP_COM, ANALYSIS_COM, EDITING_COM, ANALYSIS_COM_2, - IMPORT_COM_1, IMPORT_COM_2, LIVE_UPDATE_COM, NEW_ITEM_COM, REPORT_COM + IMPORT_COM_1, IMPORT_COM_2, LIVE_UPDATE_COM, NEW_ITEM_COM, REPORT_COM, TABLE }; //! \brief Get image (embedded or from file) as a pixmap From 7903f54147a3423ef4e5f74d4db9fe81471e4332 Mon Sep 17 00:00:00 2001 From: doluk <69309597+doluk@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:08:09 +0200 Subject: [PATCH 2/9] Add US_GuiUtil::save_csv Signed-off-by: doluk <69309597+doluk@users.noreply.github.com> --- gui/us_gui_util.cpp | 162 ++++++++++++++++++++++++++++++++++++++++++++ gui/us_gui_util.h | 12 ++++ 2 files changed, 174 insertions(+) diff --git a/gui/us_gui_util.cpp b/gui/us_gui_util.cpp index 0e6d9423f..b9d2638a0 100644 --- a/gui/us_gui_util.cpp +++ b/gui/us_gui_util.cpp @@ -105,3 +105,165 @@ int US_GuiUtil::save_png( const QString& filename, const QwtPlot* plot ) return status; } +int US_GuiUtil::save_csv( const QString& filename, const QwtPlot* plot ) +{ + int status = 0; + + + if ( filename.endsWith( ".csv" ) ) + { // Save the file as a CSV + // iterate over all plot items and construct the output data + QVector> export_data; + export_data.clear(); + // determine axis title + QString x_axis_title = plot->axisTitle( QwtPlot::xBottom ).text(); + if ( x_axis_title.isEmpty() ) + { + x_axis_title = plot->axisTitle( QwtPlot::xTop ).text(); + } + if ( x_axis_title.isEmpty() ) + { + x_axis_title = QString("x"); + } + QString y_axis_title = plot->axisTitle( QwtPlot::yLeft ).text(); + if ( y_axis_title.isEmpty() ) + { + y_axis_title = plot->axisTitle( QwtPlot::yRight ).text(); + } + if ( y_axis_title.isEmpty() ) + { + y_axis_title = QString("y"); + } + int max_length = 0; + for ( QwtPlotItem* it: plot->itemList( QwtPlotItem::Rtti_PlotCurve ) ) + { + if ( it->rtti() != QwtPlotItem::Rtti_PlotCurve ) + { + continue; + } + QwtPlotCurve* item = static_cast(it); + // create two Vectors for x and y respective + QVector x_data; + x_data.clear(); + QVector y_data; + y_data.clear(); + x_data << item->title().text() + " " + x_axis_title; + y_data << item->title().text() + " " + y_axis_title; + const QwtSeriesData* data = item->data(); + for (size_t i = 0; i < data->size(); i++) + { + QPointF point = data->sample(i); + x_data << QString::number(point.x()); + y_data << QString::number(point.y()); + } + if ( max_length < x_data.size() ) + { + max_length = x_data.size(); + } + if ( max_length < y_data.size() ) + { + max_length = y_data.size(); + } + export_data << x_data << y_data; + } + QString z_axis_title; + if ( !plot->itemList( QwtPlotItem::Rtti_PlotSpectrogram ).isEmpty() ) + { + z_axis_title = plot->axisTitle( QwtPlot::yRight ).text(); + if ( !(!z_axis_title.isEmpty() && z_axis_title != y_axis_title) ) + { + z_axis_title = z_axis_title + " z"; + } + } + for ( QwtPlotItem* it: plot->itemList( QwtPlotItem::Rtti_PlotSpectrogram ) ) + { + if ( it->rtti() != QwtPlotItem::Rtti_PlotSpectrogram ) + { + continue; + } + QwtPlotSpectrogram* item = static_cast(it); + qDebug() << item->interval( Qt::XAxis ).minValue() << item->interval( Qt::XAxis ).maxValue(); + qDebug() << item->interval( Qt::YAxis ).minValue() << item->interval( Qt::YAxis ).maxValue(); + qDebug() << item->boundingRect().bottomLeft() << item->boundingRect().topRight(); + QwtRasterData* data = item->data(); + // create two Vectors for x and y respective + QVector x_data; + x_data.clear(); + QVector y_data; + y_data.clear(); + QVector z_data; + x_data << item->title().text() + " " + x_axis_title; + y_data << item->title().text() + " " + y_axis_title; + z_data << item->title().text() + " " + z_axis_title; + // Get the matrix data + int rows = 300; + int cols = 300; + QRectF rect = item->boundingRect(); + + double dx = rect.width() / (cols - 1); + double dy = rect.height() / (rows - 1); + + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols; ++col) { + double x = rect.left() + col * dx; + double y = rect.top() + row * dy; + double z = data->value(col, row); + x_data << QString::number(x); + y_data << QString::number(y); + z_data << QString::number(z); + } + } + + if ( max_length < x_data.size() ) + { + max_length = x_data.size(); + } + if ( max_length < y_data.size() ) + { + max_length = y_data.size(); + } + export_data << x_data << y_data << z_data; + } + // iterate over all entries to ensure a proper csv format + for (auto & i : export_data) + { + if ( i.size() < max_length ) + { + QVector vec = i; + for ( int jj = vec.size(); jj < max_length; jj++ ) + { + vec << QString(""); + } + i = vec; + } + } + // dump everything into a file + QFile myFile( filename ); + if ( !myFile.open( QIODevice::WriteOnly ) ) + { + qDebug() << "Could not write to file:" << filename << "Error string:" << myFile.errorString(); + } + else + { + QTextStream out(&myFile); + for ( int ii = 0; ii < max_length; ii++ ) + { + for ( int jj = 0; jj < export_data.size() - 1; jj++ ) + { + out << export_data[ jj ][ ii ] << ", "; + } + out << export_data.last()[ ii ] << Qt::endl; + } + myFile.flush(); + } + + myFile.close(); + } + + else + { // Mark error: filename does not end with ".csv" + status = 1; + } + + return status; +} \ No newline at end of file diff --git a/gui/us_gui_util.h b/gui/us_gui_util.h index 886391669..f2ce5e957 100644 --- a/gui/us_gui_util.h +++ b/gui/us_gui_util.h @@ -13,6 +13,11 @@ #define dataPlotClear(a) a->clear() #endif #include "qwt_plot.h" +#include "qwt_plot_curve.h" +#include "qwt_series_data.h" +#include +#include +#include #include "us_extern.h" //! \brief General GUI utilities for UltraScan @@ -44,5 +49,12 @@ class US_GUI_EXTERN US_GuiUtil //! \returns A status flag: 0 if all-ok static int save_png( const QString&, const QwtPlot* ); + //! \brief Save a plot to a CSV file + //! + //! \param filename Full path name of the file to produce + //! \param plot A pointer to the plot to save + //! \returns A status flag: 0 if all-ok + static int save_csv( const QString&, const QwtPlot* ); + }; #endif From 6faaa528e4dd023d57c6b3fa1289db1623e4d268 Mon Sep 17 00:00:00 2001 From: doluk <69309597+doluk@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:09:01 +0200 Subject: [PATCH 3/9] Add csv export option with icon to US_Plot Signed-off-by: doluk <69309597+doluk@users.noreply.github.com> --- gui/us_plot.cpp | 29 ++++++++++++++++++++++++++++- gui/us_plot.h | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/gui/us_plot.cpp b/gui/us_plot.cpp index 2506c781a..4c36a7cc8 100644 --- a/gui/us_plot.cpp +++ b/gui/us_plot.cpp @@ -3,6 +3,7 @@ #include #include #include "us_plot.h" +#include "us_images.h" #if QT_VERSION > 0x050000 #include #include "us_colorgradIO.h" @@ -81,6 +82,13 @@ US_Plot::US_Plot( QwtPlot*& parent_plot, const QString& title, btnZoom->setFont( buttonFont ); connect( btnZoom, SIGNAL( toggled( bool ) ), SLOT( zoom( bool ) ) ); + QToolButton* btnCSV = new QToolButton( toolBar ); + btnCSV->setText( "CSV" ); + btnCSV->setIcon( US_Images::getIcon( US_Images::TABLE ) ); + btnCSV->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + btnCSV->setFont( buttonFont ); + connect( btnCSV, SIGNAL( clicked() ), SLOT( csv() ) ); + QToolButton* btnPrint = new QToolButton( toolBar ); btnPrint->setText( "Print" ); btnPrint->setIcon( QIcon( QPixmap( print_xpm ) ) ); @@ -120,6 +128,7 @@ US_Plot::US_Plot( QwtPlot*& parent_plot, const QString& title, connect( btnCMap, SIGNAL( clicked() ), SLOT( colorMap() ) ); toolBar->addWidget( btnZoom ); + toolBar->addWidget( btnCSV ); toolBar->addWidget( btnPrint ); toolBar->addWidget( btnSVG ); toolBar->addWidget( btnPNG ); @@ -278,7 +287,7 @@ void US_Plot::zoom( bool on ) } } -void US_Plot::svg( void ) +void US_Plot::csv( void ) { QDir dir; QString reportDir = US_Settings::reportDir(); @@ -286,6 +295,24 @@ void US_Plot::svg( void ) QString fileName = QFileDialog::getSaveFileName( plot, tr( "Export File Name" ), reportDir, + tr( "CSV Documents (*.csv)" ) ); + + if ( ! fileName.isEmpty() ) + { + if ( fileName.right( 4 ) != ".csv" ) fileName += ".csv"; + + US_GuiUtil::save_csv( fileName, plot ); + } +} + +void US_Plot::svg( void ) +{ + QDir dir; + QString reportDir = US_Settings::reportDir(); + if ( ! dir.exists( reportDir ) ) dir.mkpath( reportDir ); + + QString fileName = QFileDialog::getSaveFileName( plot, + tr( "Export File Name" ), reportDir, tr( "SVG Documents (*.svgz)" ) ); if ( ! fileName.isEmpty() ) diff --git a/gui/us_plot.h b/gui/us_plot.h index 64b8cab33..775a3e58f 100644 --- a/gui/us_plot.h +++ b/gui/us_plot.h @@ -105,6 +105,7 @@ class US_GUI_EXTERN US_Plot : public QHBoxLayout void print ( void ); void svg ( void ); void png ( void ); + void csv ( void ); void config ( void ); void colorMap( void ); #if QT_VERSION < 0x050000 From b7024fa38e87b1593e7c15151cefb16d627f5c9b Mon Sep 17 00:00:00 2001 From: doluk <69309597+doluk@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:09:25 +0200 Subject: [PATCH 4/9] Fix QToolButton Size and alignment Signed-off-by: doluk <69309597+doluk@users.noreply.github.com> --- gui/us_plot.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gui/us_plot.cpp b/gui/us_plot.cpp index 4c36a7cc8..fec15b19c 100644 --- a/gui/us_plot.cpp +++ b/gui/us_plot.cpp @@ -80,6 +80,8 @@ US_Plot::US_Plot( QwtPlot*& parent_plot, const QString& title, btnZoom->setCheckable( true ); btnZoom->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); btnZoom->setFont( buttonFont ); + btnZoom->setIconSize ( QSize( 20, 20 ) ); + btnZoom->setFixedSize( QSize( 40, 50 ) ); connect( btnZoom, SIGNAL( toggled( bool ) ), SLOT( zoom( bool ) ) ); QToolButton* btnCSV = new QToolButton( toolBar ); @@ -87,6 +89,8 @@ US_Plot::US_Plot( QwtPlot*& parent_plot, const QString& title, btnCSV->setIcon( US_Images::getIcon( US_Images::TABLE ) ); btnCSV->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); btnCSV->setFont( buttonFont ); + btnCSV->setIconSize ( QSize( 20, 20 ) ); + btnCSV->setFixedSize( QSize( 40, 50 ) ); connect( btnCSV, SIGNAL( clicked() ), SLOT( csv() ) ); QToolButton* btnPrint = new QToolButton( toolBar ); @@ -94,6 +98,8 @@ US_Plot::US_Plot( QwtPlot*& parent_plot, const QString& title, btnPrint->setIcon( QIcon( QPixmap( print_xpm ) ) ); btnPrint->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); btnPrint->setFont( buttonFont ); + btnPrint->setIconSize ( QSize( 20, 20 ) ); + btnPrint->setFixedSize( QSize( 40, 50 ) ); connect( btnPrint, SIGNAL( clicked() ), SLOT( print() ) ); QToolButton* btnSVG = new QToolButton( toolBar ); @@ -101,6 +107,8 @@ US_Plot::US_Plot( QwtPlot*& parent_plot, const QString& title, btnSVG->setIcon( QIcon( QPixmap( vec_xpm ) ) ); btnSVG->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); btnSVG->setFont( buttonFont ); + btnSVG->setIconSize ( QSize( 20, 20 ) ); + btnSVG->setFixedSize( QSize( 40, 50 ) ); connect( btnSVG, SIGNAL( clicked() ), SLOT( svg() ) ); QToolButton* btnPNG = new QToolButton( toolBar ); @@ -108,6 +116,8 @@ US_Plot::US_Plot( QwtPlot*& parent_plot, const QString& title, btnPNG->setIcon( QIcon( QPixmap( ras_xpm ) ) ); btnPNG->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); btnPNG->setFont( buttonFont ); + btnPNG->setIconSize ( QSize( 20, 20 ) ); + btnPNG->setFixedSize( QSize( 40, 50 ) ); connect( btnPNG, SIGNAL( clicked() ), SLOT( png() ) ); QToolButton* btnConfig = new QToolButton( toolBar ); @@ -115,6 +125,8 @@ US_Plot::US_Plot( QwtPlot*& parent_plot, const QString& title, btnConfig->setIcon(QIcon( QPixmap( configure_32_xpm ) ) ); btnConfig->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); btnConfig->setFont( buttonFont ); + btnConfig->setIconSize ( QSize( 20, 20 ) ); + btnConfig->setFixedSize( QSize( 40, 50 ) ); connect( btnConfig, SIGNAL( clicked() ), SLOT( config() ) ); btnCMap = new QToolButton( toolBar ); From 0233d851eae29bec29447b459623e598afa8c400 Mon Sep 17 00:00:00 2001 From: doluk <69309597+doluk@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:10:27 +0200 Subject: [PATCH 5/9] Fix small bug in exporting 3D model plots to csv resulting in empty csv files Signed-off-by: doluk <69309597+doluk@users.noreply.github.com> --- gui/us_plot3d.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/gui/us_plot3d.cpp b/gui/us_plot3d.cpp index c6b591738..20ac84388 100644 --- a/gui/us_plot3d.cpp +++ b/gui/us_plot3d.cpp @@ -977,7 +977,11 @@ void US_Plot3D::replot() { unsigned int kcols = (unsigned int)ncols; unsigned int krows = (unsigned int)nrows; + double dx = (xmax - xmin) / (ncols - 1); + double dy = (ymax - ymin) / (nrows - 1); + double tmin = DBL_MAX; + double tmax = -DBL_MAX; double** wdata = new double* [ ncols ]; DbgLv(2) << "P3D: replot: ncols nrows" << ncols << nrows; @@ -993,6 +997,12 @@ if ((ii&63)==1) DbgLv(2) << "P3D: rp: row" << ii; { double zval = zdata[ ii ][ jj ]; wdata[ ii ][ jj ] = zval; + QVector temp; + temp.clear(); + temp << xmin + ii*dx << ymin + jj*dy << zdata[ii][jj]; + Triple t = Triple( xmin + ii*dx, ymin + jj*dy, zdata[ii][jj] ); + tdata[ ii ][ jj ] = t; + zdata << temp; zdmx = zdmx > zval ? zdmx : zval; if ((ii&63)==1&&(jj&63)==1) DbgLv(2) << "P3D: rp: col" << jj << " wdat" << zval; @@ -1147,6 +1157,7 @@ void US_Plot3D::replot( bool hold_color ) double yval = ( yroff - tdata[ kk ][ jj ].y ); double zval = tdata[ kk ][ jj ].z; wdata[ ii ][ jj ] = Triple( xval, yval, zval ); + tdata[ ii ][ jj ] = Triple( xval, yval, zval ); } } } @@ -1205,6 +1216,9 @@ void US_Plot3D::replot( bool hold_color ) { double zval = zdata[ ii ][ jj ]; wddat[ ii ][ jj ] = zval; + Triple t = tdata[ ii ][ jj ]; + t.z = zval; + tdata[ ii ][ jj ] = t; zdmx = qMax( zdmx, zval ); if ((ii&63)==1&&(jj&63)==1) DbgLv(2) << "P3D: rp: col" << jj << " wdat" << zval; @@ -2328,6 +2342,16 @@ void US_Plot3D::dump_contents() } } myFile.flush();} + else { + out << xatitle << ", " << yatitle << ", " << zatitle; + out << Qt::endl; + for ( int ii = 0; ii < tdata.size(); ii++){ + for ( int jj = 0; jj < tdata.first().size(); jj++){ + out << QString::number(tdata[ii][jj].x) << ", " << QString::number(tdata[ii][jj].y) << ", " << QString::number(tdata[ii][jj].z) << Qt::endl; + } + } + myFile.flush(); + } myFile.close(); ok = true;} From 6ccabe63e3f4b43f06ce3f1d48673a99b7db37b8 Mon Sep 17 00:00:00 2001 From: doluk <69309597+doluk@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:27:10 +0200 Subject: [PATCH 6/9] Add Table icon to images Signed-off-by: doluk <69309597+doluk@users.noreply.github.com> --- gui/images/table.png | Bin 0 -> 50571 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gui/images/table.png diff --git a/gui/images/table.png b/gui/images/table.png new file mode 100644 index 0000000000000000000000000000000000000000..4a043d622d19f7aa0f6a004e73f6afd5506a4d7e GIT binary patch literal 50571 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ⅇL&Wcio=sZFAYo8I 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 Date: Thu, 22 Aug 2024 13:38:57 +0200 Subject: [PATCH 7/9] exclude empty curves from csv export Signed-off-by: doluk <69309597+doluk@users.noreply.github.com> --- gui/us_gui_util.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/gui/us_gui_util.cpp b/gui/us_gui_util.cpp index b9d2638a0..3602a87ea 100644 --- a/gui/us_gui_util.cpp +++ b/gui/us_gui_util.cpp @@ -164,7 +164,10 @@ int US_GuiUtil::save_csv( const QString& filename, const QwtPlot* plot ) { max_length = y_data.size(); } - export_data << x_data << y_data; + if ( data->size() > 0 || (x_data.size() > 1 && y_data.size() > 1) ) + { // Only add data if there is data to add + export_data << x_data << y_data; + } } QString z_axis_title; if ( !plot->itemList( QwtPlotItem::Rtti_PlotSpectrogram ).isEmpty() ) @@ -222,7 +225,14 @@ int US_GuiUtil::save_csv( const QString& filename, const QwtPlot* plot ) { max_length = y_data.size(); } - export_data << x_data << y_data << z_data; + if ( max_length < z_data.size() ) + { + max_length = z_data.size(); + } + if ( x_data.size() > 1 && y_data.size() > 1 && z_data.size() > 1 ) + { // Only add data if there is data to add + export_data << x_data << y_data << z_data; + } } // iterate over all entries to ensure a proper csv format for (auto & i : export_data) From a5fbc1bea39535bce27a18c8bc158413134e9fbd Mon Sep 17 00:00:00 2001 From: doluk <69309597+doluk@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:22:59 +0200 Subject: [PATCH 8/9] Add direct read out for US_SpectrogramData Signed-off-by: doluk <69309597+doluk@users.noreply.github.com> --- gui/us_spectrodata.cpp | 17 +++++++++++++++++ gui/us_spectrodata.h | 8 ++++++++ 2 files changed, 25 insertions(+) diff --git a/gui/us_spectrodata.cpp b/gui/us_spectrodata.cpp index 9f4af8f10..a808c3770 100644 --- a/gui/us_spectrodata.cpp +++ b/gui/us_spectrodata.cpp @@ -370,3 +370,20 @@ qDebug() << "sRaDa: setBounding... zminr zmax" << zminr << zmax; qDebug() << "SD:sRaDa: RETURN:"; } +void US_SpectrogramData::value( int x, int y, double &x_out, double &y_out, double &z_out ) const { + x_out = (double)x / xinc + xmin ; // real x pixel position + y_out = ymax - (double)y / yinc; // real y pixel position + int jx = (int)x; // integral x pixel + int jy = (int)y; // integral y pixel + int jr1 = jy * nxpsc + jx; // overall raster position + int mxr = nxypt - 1; // max raster position index + + if ( jr1 < 0 || jr1 > mxr ) + { // for x,y outside raster, return z-minimum + z_out = 0.0; + return; + } + + z_out = rdata.at( jr1 ); // possible output value +} + diff --git a/gui/us_spectrodata.h b/gui/us_spectrodata.h index cfd64a024..2c11f14d3 100644 --- a/gui/us_spectrodata.h +++ b/gui/us_spectrodata.h @@ -95,6 +95,14 @@ class US_GUI_EXTERN US_SpectrogramData : public QwtRasterData //! \return The real Z value for raster location X,Y. virtual double value( double, double ) const; + //! \brief Called by QwtPlot to get the Z-value at each X,Y pixel location + //! \param x The X pixel location for which to fetch Z. + //! \param y The Y pixel location for which to fetch Z. + //! \param x_out The real X value for raster location XY + //! \param y_out The real Y value for raster location XY + //! \param z_out The real Z value for raster location XY + void value( int, int, double&, double&, double& ) const; + //! \brief Sets up the internal raster, based on a set of Solute points. //! \param solu Pointer to list of solution points for the current distribution. void setRaster( QList< S_Solute >* ); From 8ed6165abbf335ee813296edf7c015a173b08f10 Mon Sep 17 00:00:00 2001 From: doluk <69309597+doluk@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:23:09 +0200 Subject: [PATCH 9/9] utilize US_SpectrogramData Signed-off-by: doluk <69309597+doluk@users.noreply.github.com> --- gui/us_gui_util.cpp | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/gui/us_gui_util.cpp b/gui/us_gui_util.cpp index 3602a87ea..9f90132a3 100644 --- a/gui/us_gui_util.cpp +++ b/gui/us_gui_util.cpp @@ -6,6 +6,8 @@ #include "us_settings.h" #if QT_VERSION > 0x050000 #include "qwt_plot_renderer.h" +#include "us_spectrodata.h" + #endif // Save SVG+PNG or PNG file @@ -147,8 +149,8 @@ int US_GuiUtil::save_csv( const QString& filename, const QwtPlot* plot ) x_data.clear(); QVector y_data; y_data.clear(); - x_data << item->title().text() + " " + x_axis_title; - y_data << item->title().text() + " " + y_axis_title; + x_data << QString("\"") + item->title().text() + " " + x_axis_title + QString("\""); + y_data << QString("\"") + item->title().text() + " " + y_axis_title + QString("\""); const QwtSeriesData* data = item->data(); for (size_t i = 0; i < data->size(); i++) { @@ -188,29 +190,28 @@ int US_GuiUtil::save_csv( const QString& filename, const QwtPlot* plot ) qDebug() << item->interval( Qt::XAxis ).minValue() << item->interval( Qt::XAxis ).maxValue(); qDebug() << item->interval( Qt::YAxis ).minValue() << item->interval( Qt::YAxis ).maxValue(); qDebug() << item->boundingRect().bottomLeft() << item->boundingRect().topRight(); - QwtRasterData* data = item->data(); + US_SpectrogramData* data = dynamic_cast(item->data()); // create two Vectors for x and y respective + QSize raster_size; + QRectF rect; + // Get the raster data from US_SpectrogramData + data->initRaster( rect, raster_size ); QVector x_data; x_data.clear(); QVector y_data; y_data.clear(); QVector z_data; - x_data << item->title().text() + " " + x_axis_title; - y_data << item->title().text() + " " + y_axis_title; - z_data << item->title().text() + " " + z_axis_title; + x_data << QString("\"") + item->title().text() + " " + x_axis_title + QString("\""); + y_data << QString("\"") + item->title().text() + " " + y_axis_title + QString("\""); + z_data << QString("\"") + item->title().text() + " " + z_axis_title + QString("\""); // Get the matrix data - int rows = 300; - int cols = 300; - QRectF rect = item->boundingRect(); - - double dx = rect.width() / (cols - 1); - double dy = rect.height() / (rows - 1); + int rows = raster_size.width(); + int cols = raster_size.height(); for (int row = 0; row < rows; ++row) { for (int col = 0; col < cols; ++col) { - double x = rect.left() + col * dx; - double y = rect.top() + row * dy; - double z = data->value(col, row); + double x, y, z; + data->value( row, col, x, y, z ); x_data << QString::number(x); y_data << QString::number(y); z_data << QString::number(z); @@ -271,7 +272,7 @@ int US_GuiUtil::save_csv( const QString& filename, const QwtPlot* plot ) } else - { // Mark error: filename does not end with ".csv" + { // Mark error: filename does not end with ".csv" status = 1; }