diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index 8eba3d8..ef6b7de 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev + sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev libqt6svg6-dev libgl-dev qtchooser -install qt6 $(which qmake6) - name: Get build timestamp diff --git a/.github/workflows/HIL_Tests.yml b/.github/workflows/HIL_Tests.yml index 4947f0f..4f1eee0 100644 --- a/.github/workflows/HIL_Tests.yml +++ b/.github/workflows/HIL_Tests.yml @@ -19,7 +19,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev + sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev qt6-svg-dev - name: Build application run: | diff --git a/.github/workflows/Release_tag_stable.yml b/.github/workflows/Release_tag_stable.yml index f92179c..700f3a3 100644 --- a/.github/workflows/Release_tag_stable.yml +++ b/.github/workflows/Release_tag_stable.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev zip + sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev libqt6svg6-dev libgl-dev zip qtchooser -install qt6 $(which qmake6) - name: Get app version @@ -72,7 +72,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev + sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev qt6-svg-dev - name: Get app version id: id_version diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index 074e728..7d8325b 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev + sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev libqt6svg6-dev libgl-dev qtchooser -install qt6 $(which qmake6) - name: Build Tests diff --git a/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro b/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro index 15b5b56..e8f086f 100644 --- a/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro +++ b/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro @@ -164,6 +164,7 @@ HEADERS += \ preferences.h \ savable.h \ scpi.h \ + screenshot.h \ streamingserver.h \ tcpserver.h \ touchstone.h \ @@ -320,6 +321,7 @@ SOURCES += \ preferences.cpp \ savable.cpp \ scpi.cpp \ + screenshot.cpp \ streamingserver.cpp \ tcpserver.cpp \ touchstone.cpp \ @@ -338,7 +340,7 @@ mac{ PKGCONFIG += libusb-1.0 } -QT += widgets network +QT += widgets network svg FORMS += \ Calibration/CalStandardLineEditDialog.ui \ diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/eyediagramplot.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/eyediagramplot.cpp index d686387..8b6b28e 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/eyediagramplot.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/eyediagramplot.cpp @@ -7,6 +7,7 @@ #include "fftcomplex.h" #include "preferences.h" #include "appwindow.h" +#include "screenshot.h" #include #include @@ -403,17 +404,7 @@ void EyeDiagramPlot::updateContextMenu() auto image = new QAction("Save image...", contextmenu); contextmenu->addAction(image); connect(image, &QAction::triggered, this, [=]() { - auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", Preferences::getInstance().UISettings.Paths.image, "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); - if(filename.isEmpty()) { - // aborted selection - return; - } - Preferences::getInstance().UISettings.Paths.image = QFileInfo(filename).path(); - if(filename.endsWith(".png")) { - filename.chop(4); - } - filename += ".png"; - grab().save(filename); + SaveScreenshot(this); }); contextmenu->addSection("Traces"); diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/tracepolar.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/tracepolar.cpp index 15b87c7..5f6269c 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/tracepolar.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/tracepolar.cpp @@ -3,6 +3,7 @@ #include "Marker/marker.h" #include "Util/util.h" #include "preferences.h" +#include "screenshot.h" #include @@ -253,17 +254,7 @@ void TracePolar::updateContextMenu() auto image = new QAction("Save image...", contextmenu); contextmenu->addAction(image); connect(image, &QAction::triggered, [=]() { - auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", Preferences::getInstance().UISettings.Paths.image, "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); - if(filename.isEmpty()) { - // aborted selection - return; - } - Preferences::getInstance().UISettings.Paths.image = QFileInfo(filename).path(); - if(filename.endsWith(".png")) { - filename.chop(4); - } - filename += ".png"; - grab().save(filename); + SaveScreenshot(this); }); auto createMarker = contextmenu->addAction("Add marker here"); diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/tracewaterfall.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/tracewaterfall.cpp index 043a6e3..2cc318b 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/tracewaterfall.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/tracewaterfall.cpp @@ -6,6 +6,7 @@ #include "waterfallaxisdialog.h" #include "appwindow.h" #include "tracexyplot.h" +#include "screenshot.h" #include #include @@ -213,17 +214,7 @@ void TraceWaterfall::updateContextMenu() auto image = new QAction("Save image...", contextmenu); contextmenu->addAction(image); connect(image, &QAction::triggered, [=]() { - auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", Preferences::getInstance().UISettings.Paths.image, "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); - if(filename.isEmpty()) { - // aborted selection - return; - } - Preferences::getInstance().UISettings.Paths.image = QFileInfo(filename).path(); - if(filename.endsWith(".png")) { - filename.chop(4); - } - filename += ".png"; - grab().save(filename); + SaveScreenshot(this); }); contextmenu->addSection("Traces"); diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp index 3547eea..3071c38 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp @@ -9,6 +9,7 @@ #include "preferences.h" #include "appwindow.h" #include "ui_XYPlotConstantLineEditDialog.h" +#include "screenshot.h" #include #include @@ -349,17 +350,7 @@ void TraceXYPlot::updateContextMenu() auto image = new QAction("Save image...", contextmenu); contextmenu->addAction(image); connect(image, &QAction::triggered, [=]() { - auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", Preferences::getInstance().UISettings.Paths.image, "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); - if(filename.isEmpty()) { - // aborted selection - return; - } - Preferences::getInstance().UISettings.Paths.image = QFileInfo(filename).path(); - if(filename.endsWith(".png")) { - filename.chop(4); - } - filename += ".png"; - grab().save(filename); + SaveScreenshot(this); }); auto createMarker = contextmenu->addAction("Add marker here"); diff --git a/Software/PC_Application/LibreVNA-GUI/mode.cpp b/Software/PC_Application/LibreVNA-GUI/mode.cpp index 3a9618b..515a86c 100644 --- a/Software/PC_Application/LibreVNA-GUI/mode.cpp +++ b/Software/PC_Application/LibreVNA-GUI/mode.cpp @@ -6,6 +6,7 @@ #include "CustomWidgets/informationbox.h" #include "ui_main.h" +#include "screenshot.h" #include #include @@ -141,17 +142,7 @@ Mode::Type Mode::TypeFromName(QString s) void Mode::saveSreenshot() { - auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", Preferences::getInstance().UISettings.Paths.image, "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); - if(filename.isEmpty()) { - // aborted selection - return; - } - Preferences::getInstance().UISettings.Paths.image = QFileInfo(filename).path(); - if(filename.endsWith(".png")) { - filename.chop(4); - } - filename += ".png"; - central->grab().save(filename); + SaveScreenshot(central); } void Mode::finalize(QWidget *centralWidget) diff --git a/Software/PC_Application/LibreVNA-GUI/preferences.h b/Software/PC_Application/LibreVNA-GUI/preferences.h index 44105e7..52bf1a2 100644 --- a/Software/PC_Application/LibreVNA-GUI/preferences.h +++ b/Software/PC_Application/LibreVNA-GUI/preferences.h @@ -229,6 +229,7 @@ public: QString pref; QString firmware; } Paths; + qsizetype saveImageFilterIndex; } UISettings; bool TCPoverride; // in case of manual port specification via command line @@ -413,6 +414,7 @@ private: {&UISettings.Paths.limitLines, "UISettings.Paths.limitLines", ""}, {&UISettings.Paths.pref, "UISettings.Paths.pref", ""}, {&UISettings.Paths.firmware, "UISettings.Paths.firmware", ""}, + {&UISettings.saveImageFilterIndex, "UISettings.saveImageFilterIndex", 0}, }}; }; diff --git a/Software/PC_Application/LibreVNA-GUI/screenshot.cpp b/Software/PC_Application/LibreVNA-GUI/screenshot.cpp new file mode 100644 index 0000000..b5dbc03 --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/screenshot.cpp @@ -0,0 +1,65 @@ +#include "screenshot.h" + +#include "preferences.h" + +#include +#include +#include + +void SaveScreenshot(QWidget *widget) +{ + Q_ASSERT(widget != nullptr); + + const QStringList extensions = QStringList() << "png" << "svg"; + QStringList filters; + + for (const QString& ext: extensions) { + filters << QString("%1 image files (*.%2)").arg(ext.toUpper(), ext); + } + + auto& settings = Preferences::getInstance().UISettings; + const QString filterString = filters.join(";;"); + qsizetype filterIndex = qBound(0, settings.saveImageFilterIndex, filters.size() - 1); + QString selectedFilter = filters[filterIndex]; + + auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", settings.Paths.image, filterString, &selectedFilter, Preferences::QFileDialogOptions()); + if(filename.isEmpty()) { + // aborted selection + return; + } + + filterIndex = filters.indexOf(selectedFilter); + const QString& extension = extensions[filterIndex]; + if(!filename.endsWith(extension)) { + filename += '.' + extension; + } + + settings.Paths.image = QFileInfo(filename).path(); + settings.saveImageFilterIndex = filterIndex; + + switch (filterIndex) + { + case 0: // PNG + widget->grab().save(filename); + break; + + case 1: // SVG + { + QSvgGenerator generator; + generator.setFileName(filename); + generator.setViewBox(QRect(QPoint(), widget->size())); + generator.setTitle(QCoreApplication::applicationName()); + generator.setDescription(QString("Created by %1 %2").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion())); + + QPainter painter; + painter.begin(&generator); + widget->render(&painter); + painter.end(); + break; + } + + default: + Q_ASSERT(false); + break; + } +} diff --git a/Software/PC_Application/LibreVNA-GUI/screenshot.h b/Software/PC_Application/LibreVNA-GUI/screenshot.h new file mode 100644 index 0000000..9dc7bfd --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/screenshot.h @@ -0,0 +1,8 @@ +#ifndef SCREENSHOT_H +#define SCREENSHOT_H + +class QWidget; + +void SaveScreenshot(QWidget *widget); + +#endif // SCREENSHOT_H diff --git a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro index 0b0e024..f156b6b 100644 --- a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro +++ b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro @@ -1,4 +1,4 @@ -QT += testlib widgets network +QT += testlib widgets network svg CONFIG += qt console warn_on depend_includepath testcase CONFIG -= app_bundle @@ -155,6 +155,7 @@ SOURCES += \ ../LibreVNA-GUI/preferences.cpp \ ../LibreVNA-GUI/savable.cpp \ ../LibreVNA-GUI/scpi.cpp \ + ../LibreVNA-GUI/screenshot.cpp \ ../LibreVNA-GUI/tcpserver.cpp \ ../LibreVNA-GUI/streamingserver.cpp \ ../LibreVNA-GUI/touchstone.cpp \ @@ -359,6 +360,7 @@ HEADERS += \ ../LibreVNA-GUI/preferences.h \ ../LibreVNA-GUI/savable.h \ ../LibreVNA-GUI/scpi.h \ + ../LibreVNA-GUI/screenshot.h \ ../LibreVNA-GUI/tcpserver.h \ ../LibreVNA-GUI/streamingserver.h \ ../LibreVNA-GUI/touchstone.h \