From 84371ad2eb9dc69f604bb12e093a9afc88d128e9 Mon Sep 17 00:00:00 2001 From: David Graeff Date: Thu, 11 Jan 2018 22:51:01 +0100 Subject: [PATCH] Move exporter to own directory. There will come another raw-sample exporter at some time. Move export settings there as well. --- openhantek/src/exporter.cpp | 483 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openhantek/src/exporter.h | 42 ------------------------------------------ openhantek/src/exporting/exporter.cpp | 487 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openhantek/src/exporting/exporter.h | 38 ++++++++++++++++++++++++++++++++++++++ openhantek/src/exporting/exportsettings.h | 13 +++++++++++++ openhantek/src/scopesettings.h | 32 +++++++++++++++++--------------- openhantek/src/settings.cpp | 20 +++++++++++--------- openhantek/src/settings.h | 17 +++-------------- 8 files changed, 569 insertions(+), 563 deletions(-) delete mode 100644 openhantek/src/exporter.cpp delete mode 100644 openhantek/src/exporter.h create mode 100644 openhantek/src/exporting/exporter.cpp create mode 100644 openhantek/src/exporting/exporter.h create mode 100644 openhantek/src/exporting/exportsettings.h diff --git a/openhantek/src/exporter.cpp b/openhantek/src/exporter.cpp deleted file mode 100644 index a8a5d75..0000000 --- a/openhantek/src/exporter.cpp +++ /dev/null @@ -1,483 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "exporter.h" - -#include "analyse/dataanalyzerresult.h" -#include "glgenerator.h" -#include "settings.h" -#include "viewconstants.h" -#include "utils/dsoStrings.h" -#include "utils/printutils.h" - -#define tr(msg) QCoreApplication::translate("Exporter", msg) - -Exporter::Exporter(DsoSettings *settings, const QString &filename, ExportFormat format) - : settings(settings), filename(filename), format(format) {} - -Exporter *Exporter::createPrintExporter(DsoSettings *settings) { - std::unique_ptr printer = printPaintDevice(settings); - // Show the printing dialog - QPrintDialog dialog(printer.get()); - dialog.setWindowTitle(tr("Print oscillograph")); - if (dialog.exec() != QDialog::Accepted) { return nullptr; } - - Exporter *exporter = new Exporter(settings, QString(), EXPORT_FORMAT_PRINTER); - exporter->selectedPrinter = std::move(printer); - return exporter; -} - -Exporter *Exporter::createSaveToFileExporter(DsoSettings *settings) { - QStringList filters; - filters << tr("Portable Document Format (*.pdf)") << tr("Image (*.png *.xpm *.jpg)") - << tr("Comma-Separated Values (*.csv)"); - - QFileDialog fileDialog(nullptr, tr("Export file..."), QString(), filters.join(";;")); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - if (fileDialog.exec() != QDialog::Accepted) return nullptr; - - return new Exporter(settings, fileDialog.selectedFiles().first(), - (ExportFormat)(EXPORT_FORMAT_PDF + filters.indexOf(fileDialog.selectedNameFilter()))); -} - -std::unique_ptr Exporter::printPaintDevice(DsoSettings *settings) { - // We need a QPrinter for printing, pdf- and ps-export - std::unique_ptr printer = std::unique_ptr(new QPrinter(QPrinter::HighResolution)); - printer->setOrientation(settings->view.zoom ? QPrinter::Portrait : QPrinter::Landscape); - printer->setPageMargins(20, 20, 20, 20, QPrinter::Millimeter); - return printer; -} - -bool Exporter::exportSamples(const DataAnalyzerResult *result) { - if (this->format == EXPORT_FORMAT_CSV) { return exportCSV(result); } - - // Choose the color values we need - DsoSettingsColorValues *colorValues; - if (this->format == EXPORT_FORMAT_IMAGE && settings->view.screenColorImages) - colorValues = &(settings->view.screen); - else - colorValues = &(settings->view.print); - - std::unique_ptr paintDevice; - - if (this->format == EXPORT_FORMAT_IMAGE) { - // We need a QPixmap for image-export - QPixmap *qPixmap = new QPixmap(settings->options.imageSize); - qPixmap->fill(colorValues->background); - paintDevice = std::unique_ptr(qPixmap); - } else if (this->format == EXPORT_FORMAT_PRINTER) { - paintDevice = std::move(selectedPrinter); - } else { - std::unique_ptr printer = printPaintDevice(settings); - printer->setOutputFileName(this->filename); - printer->setOutputFormat((this->format == EXPORT_FORMAT_PDF) ? QPrinter::PdfFormat : QPrinter::NativeFormat); - paintDevice = std::move(printer); - } - - if (!paintDevice) return false; - - // Create a painter for our device - QPainter painter(paintDevice.get()); - - // Get line height - QFont font; - QFontMetrics fontMetrics(font, paintDevice.get()); - double lineHeight = fontMetrics.height(); - - painter.setBrush(Qt::SolidPattern); - - // Draw the settings table - double stretchBase = (double)(paintDevice->width() - lineHeight * 10) / 4; - - // Print trigger details - painter.setPen(colorValues->voltage[settings->scope.trigger.source]); - QString levelString = valueToString(settings->scope.voltage[settings->scope.trigger.source].trigger, UNIT_VOLTS, 3); - QString pretriggerString = tr("%L1%").arg((int)(settings->scope.trigger.position * 100 + 0.5)); - painter.drawText(QRectF(0, 0, lineHeight * 10, lineHeight), - tr("%1 %2 %3 %4") - .arg(settings->scope.voltage[settings->scope.trigger.source].name, - Dso::slopeString(settings->scope.trigger.slope), levelString, pretriggerString)); - - double scopeHeight; - - { // DataAnalyser mutex lock - // Print sample count - painter.setPen(colorValues->text); - painter.drawText(QRectF(lineHeight * 10, 0, stretchBase, lineHeight), tr("%1 S").arg(result->sampleCount()), - QTextOption(Qt::AlignRight)); - // Print samplerate - painter.drawText(QRectF(lineHeight * 10 + stretchBase, 0, stretchBase, lineHeight), - valueToString(settings->scope.horizontal.samplerate, UNIT_SAMPLES) + tr("/s"), - QTextOption(Qt::AlignRight)); - // Print timebase - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 2, 0, stretchBase, lineHeight), - valueToString(settings->scope.horizontal.timebase, UNIT_SECONDS, 0) + tr("/div"), - QTextOption(Qt::AlignRight)); - // Print frequencybase - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 3, 0, stretchBase, lineHeight), - valueToString(settings->scope.horizontal.frequencybase, UNIT_HERTZ, 0) + tr("/div"), - QTextOption(Qt::AlignRight)); - - // Draw the measurement table - stretchBase = (double)(paintDevice->width() - lineHeight * 6) / 10; - int channelCount = 0; - for (int channel = settings->scope.voltage.size() - 1; channel >= 0; channel--) { - if ((settings->scope.voltage[channel].used || settings->scope.spectrum[channel].used) && - result->data(channel)) { - ++channelCount; - double top = (double)paintDevice->height() - channelCount * lineHeight; - - // Print label - painter.setPen(colorValues->voltage[channel]); - painter.drawText(QRectF(0, top, lineHeight * 4, lineHeight), settings->scope.voltage[channel].name); - // Print coupling/math mode - if ((unsigned int)channel < settings->deviceSpecification->channels) - painter.drawText(QRectF(lineHeight * 4, top, lineHeight * 2, lineHeight), - Dso::couplingString(settings->scope.coupling(channel, settings->deviceSpecification))); - else - painter.drawText(QRectF(lineHeight * 4, top, lineHeight * 2, lineHeight), - Dso::mathModeString(settings->scope.voltage[channel].math)); - - // Print voltage gain - painter.drawText(QRectF(lineHeight * 6, top, stretchBase * 2, lineHeight), - valueToString(settings->scope.gain(channel), UNIT_VOLTS, 0) + tr("/div"), - QTextOption(Qt::AlignRight)); - // Print spectrum magnitude - if (settings->scope.spectrum[channel].used) { - painter.setPen(colorValues->spectrum[channel]); - painter.drawText(QRectF(lineHeight * 6 + stretchBase * 2, top, stretchBase * 2, lineHeight), - valueToString(settings->scope.spectrum[channel].magnitude, UNIT_DECIBEL, 0) + - tr("/div"), - QTextOption(Qt::AlignRight)); - } - - // Amplitude string representation (4 significant digits) - painter.setPen(colorValues->text); - painter.drawText(QRectF(lineHeight * 6 + stretchBase * 4, top, stretchBase * 3, lineHeight), - valueToString(result->data(channel)->amplitude, UNIT_VOLTS, 4), - QTextOption(Qt::AlignRight)); - // Frequency string representation (5 significant digits) - painter.drawText(QRectF(lineHeight * 6 + stretchBase * 7, top, stretchBase * 3, lineHeight), - valueToString(result->data(channel)->frequency, UNIT_HERTZ, 5), - QTextOption(Qt::AlignRight)); - } - } - - // Draw the marker table - stretchBase = (double)(paintDevice->width() - lineHeight * 10) / 4; - painter.setPen(colorValues->text); - - // Calculate variables needed for zoomed scope - double divs = fabs(settings->scope.horizontal.marker[1] - settings->scope.horizontal.marker[0]); - double time = divs * settings->scope.horizontal.timebase; - double zoomFactor = DIVS_TIME / divs; - double zoomOffset = (settings->scope.horizontal.marker[0] + settings->scope.horizontal.marker[1]) / 2; - - if (settings->view.zoom) { - scopeHeight = (double)(paintDevice->height() - (channelCount + 5) * lineHeight) / 2; - double top = 2.5 * lineHeight + scopeHeight; - - painter.drawText(QRectF(0, top, stretchBase, lineHeight), - tr("Zoom x%L1").arg(DIVS_TIME / divs, -1, 'g', 3)); - - painter.drawText(QRectF(lineHeight * 10, top, stretchBase, lineHeight), - valueToString(time, UNIT_SECONDS, 4), QTextOption(Qt::AlignRight)); - painter.drawText(QRectF(lineHeight * 10 + stretchBase, top, stretchBase, lineHeight), - valueToString(1.0 / time, UNIT_HERTZ, 4), QTextOption(Qt::AlignRight)); - - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 2, top, stretchBase, lineHeight), - valueToString(time / DIVS_TIME, UNIT_SECONDS, 3) + tr("/div"), - QTextOption(Qt::AlignRight)); - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 3, top, stretchBase, lineHeight), - valueToString(divs * settings->scope.horizontal.frequencybase / DIVS_TIME, UNIT_HERTZ, 3) + - tr("/div"), - QTextOption(Qt::AlignRight)); - } else { - scopeHeight = (double)paintDevice->height() - (channelCount + 4) * lineHeight; - double top = 2.5 * lineHeight + scopeHeight; - - painter.drawText(QRectF(0, top, stretchBase, lineHeight), tr("Marker 1/2")); - - painter.drawText(QRectF(lineHeight * 10, top, stretchBase * 2, lineHeight), - valueToString(time, UNIT_SECONDS, 4), QTextOption(Qt::AlignRight)); - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 2, top, stretchBase * 2, lineHeight), - valueToString(1.0 / time, UNIT_HERTZ, 4), QTextOption(Qt::AlignRight)); - } - - // Set DIVS_TIME x DIVS_VOLTAGE matrix for oscillograph - painter.setMatrix(QMatrix((paintDevice->width() - 1) / DIVS_TIME, 0, 0, -(scopeHeight - 1) / DIVS_VOLTAGE, - (double)(paintDevice->width() - 1) / 2, (scopeHeight - 1) / 2 + lineHeight * 1.5), - false); - - // Draw the graphs - painter.setRenderHint(QPainter::Antialiasing); - painter.setBrush(Qt::NoBrush); - - for (int zoomed = 0; zoomed < (settings->view.zoom ? 2 : 1); ++zoomed) { - switch (settings->scope.horizontal.format) { - case Dso::GraphFormat::TY: - // Add graphs for channels - for (ChannelID channel = 0; channel < settings->scope.voltage.size(); ++channel) { - if (settings->scope.voltage[channel].used && result->data(channel)) { - painter.setPen(QPen(colorValues->voltage[channel], 0)); - - // What's the horizontal distance between sampling points? - double horizontalFactor = - result->data(channel)->voltage.interval / settings->scope.horizontal.timebase; - // How many samples are visible? - double centerPosition, centerOffset; - if (zoomed) { - centerPosition = (zoomOffset + DIVS_TIME / 2) / horizontalFactor; - centerOffset = DIVS_TIME / horizontalFactor / zoomFactor / 2; - } else { - centerPosition = DIVS_TIME / 2 / horizontalFactor; - centerOffset = DIVS_TIME / horizontalFactor / 2; - } - unsigned int firstPosition = qMax((int)(centerPosition - centerOffset), 0); - unsigned int lastPosition = qMin((int)(centerPosition + centerOffset), - (int)result->data(channel)->voltage.sample.size() - 1); - - // Draw graph - QPointF *graph = new QPointF[lastPosition - firstPosition + 1]; - - for (unsigned int position = firstPosition; position <= lastPosition; ++position) - graph[position - firstPosition] = QPointF(position * horizontalFactor - DIVS_TIME / 2, - result->data(channel)->voltage.sample[position] / - settings->scope.gain(channel) + - settings->scope.voltage[channel].offset); - - painter.drawPolyline(graph, lastPosition - firstPosition + 1); - delete[] graph; - } - } - - // Add spectrum graphs - for (ChannelID channel = 0; channel < settings->scope.spectrum.size(); ++channel) { - if (settings->scope.spectrum[channel].used && result->data(channel)) { - painter.setPen(QPen(colorValues->spectrum[channel], 0)); - - // What's the horizontal distance between sampling points? - double horizontalFactor = - result->data(channel)->spectrum.interval / settings->scope.horizontal.frequencybase; - // How many samples are visible? - double centerPosition, centerOffset; - if (zoomed) { - centerPosition = (zoomOffset + DIVS_TIME / 2) / horizontalFactor; - centerOffset = DIVS_TIME / horizontalFactor / zoomFactor / 2; - } else { - centerPosition = DIVS_TIME / 2 / horizontalFactor; - centerOffset = DIVS_TIME / horizontalFactor / 2; - } - unsigned int firstPosition = qMax((int)(centerPosition - centerOffset), 0); - unsigned int lastPosition = qMin((int)(centerPosition + centerOffset), - (int)result->data(channel)->spectrum.sample.size() - 1); - - // Draw graph - QPointF *graph = new QPointF[lastPosition - firstPosition + 1]; - - for (unsigned int position = firstPosition; position <= lastPosition; ++position) - graph[position - firstPosition] = - QPointF(position * horizontalFactor - DIVS_TIME / 2, - result->data(channel)->spectrum.sample[position] / - settings->scope.spectrum[channel].magnitude + - settings->scope.spectrum[channel].offset); - - painter.drawPolyline(graph, lastPosition - firstPosition + 1); - delete[] graph; - } - } - break; - - case Dso::GraphFormat::XY: - break; - - default: - break; - } - - // Set DIVS_TIME / zoomFactor x DIVS_VOLTAGE matrix for zoomed - // oscillograph - painter.setMatrix(QMatrix((paintDevice->width() - 1) / DIVS_TIME * zoomFactor, 0, 0, - -(scopeHeight - 1) / DIVS_VOLTAGE, - (double)(paintDevice->width() - 1) / 2 - - zoomOffset * zoomFactor * (paintDevice->width() - 1) / DIVS_TIME, - (scopeHeight - 1) * 1.5 + lineHeight * 4), - false); - } - } // dataanalyser mutex release - - drawGrids(painter, colorValues, lineHeight, scopeHeight, paintDevice->width()); - painter.end(); - - if (this->format == EXPORT_FORMAT_IMAGE) static_cast(paintDevice.get())->save(this->filename); - - return true; -} - -bool Exporter::exportCSV(const DataAnalyzerResult *result) { - QFile csvFile(this->filename); - if (!csvFile.open(QIODevice::WriteOnly | QIODevice::Text)) return false; - - QTextStream csvStream(&csvFile); - - size_t chCount = settings->scope.voltage.size(); - std::vector voltageData(size_t(chCount), nullptr); - std::vector spectrumData(size_t(chCount), nullptr); - size_t maxRow = 0; - bool isSpectrumUsed = false; - double timeInterval = 0; - double freqInterval = 0; - - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (result->data(channel)) { - if (settings->scope.voltage[channel].used) { - voltageData[channel] = &(result->data(channel)->voltage); - maxRow = std::max(maxRow, voltageData[channel]->sample.size()); - timeInterval = result->data(channel)->voltage.interval; - } - if (settings->scope.spectrum[channel].used) { - spectrumData[channel] = &(result->data(channel)->spectrum); - maxRow = std::max(maxRow, spectrumData[channel]->sample.size()); - freqInterval = result->data(channel)->spectrum.interval; - isSpectrumUsed = true; - } - } - } - - // Start with channel names - csvStream << "\"t\""; - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (voltageData[channel] != nullptr) { csvStream << ",\"" << settings->scope.voltage[channel].name << "\""; } - } - if (isSpectrumUsed) { - csvStream << ",\"f\""; - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (spectrumData[channel] != nullptr) { - csvStream << ",\"" << settings->scope.spectrum[channel].name << "\""; - } - } - } - csvStream << "\n"; - - for (unsigned int row = 0; row < maxRow; ++row) { - - csvStream << timeInterval * row; - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (voltageData[channel] != nullptr) { - csvStream << ","; - if (row < voltageData[channel]->sample.size()) { csvStream << voltageData[channel]->sample[row]; } - } - } - - if (isSpectrumUsed) { - csvStream << "," << freqInterval * row; - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (spectrumData[channel] != nullptr) { - csvStream << ","; - if (row < spectrumData[channel]->sample.size()) { csvStream << spectrumData[channel]->sample[row]; } - } - } - } - csvStream << "\n"; - } - - csvFile.close(); - - return true; -} - -void Exporter::drawGrids(QPainter &painter, DsoSettingsColorValues *colorValues, double lineHeight, double scopeHeight, - int scopeWidth) { - painter.setRenderHint(QPainter::Antialiasing, false); - for (int zoomed = 0; zoomed < (settings->view.zoom ? 2 : 1); ++zoomed) { - // Set DIVS_TIME x DIVS_VOLTAGE matrix for oscillograph - painter.setMatrix(QMatrix((scopeWidth - 1) / DIVS_TIME, 0, 0, -(scopeHeight - 1) / DIVS_VOLTAGE, - (double)(scopeWidth - 1) / 2, - (scopeHeight - 1) * (zoomed + 0.5) + lineHeight * 1.5 + lineHeight * 2.5 * zoomed), - false); - - // Grid lines - painter.setPen(QPen(colorValues->grid, 0)); - - if (this->format < EXPORT_FORMAT_IMAGE) { - // Draw vertical lines - for (int div = 1; div < DIVS_TIME / 2; ++div) { - for (int dot = 1; dot < DIVS_VOLTAGE / 2 * 5; ++dot) { - painter.drawLine(QPointF((double)-div - 0.02, (double)-dot / 5), - QPointF((double)-div + 0.02, (double)-dot / 5)); - painter.drawLine(QPointF((double)-div - 0.02, (double)dot / 5), - QPointF((double)-div + 0.02, (double)dot / 5)); - painter.drawLine(QPointF((double)div - 0.02, (double)-dot / 5), - QPointF((double)div + 0.02, (double)-dot / 5)); - painter.drawLine(QPointF((double)div - 0.02, (double)dot / 5), - QPointF((double)div + 0.02, (double)dot / 5)); - } - } - // Draw horizontal lines - for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) { - for (int dot = 1; dot < DIVS_TIME / 2 * 5; ++dot) { - painter.drawLine(QPointF((double)-dot / 5, (double)-div - 0.02), - QPointF((double)-dot / 5, (double)-div + 0.02)); - painter.drawLine(QPointF((double)dot / 5, (double)-div - 0.02), - QPointF((double)dot / 5, (double)-div + 0.02)); - painter.drawLine(QPointF((double)-dot / 5, (double)div - 0.02), - QPointF((double)-dot / 5, (double)div + 0.02)); - painter.drawLine(QPointF((double)dot / 5, (double)div - 0.02), - QPointF((double)dot / 5, (double)div + 0.02)); - } - } - } else { - // Draw vertical lines - for (int div = 1; div < DIVS_TIME / 2; ++div) { - for (int dot = 1; dot < DIVS_VOLTAGE / 2 * 5; ++dot) { - painter.drawPoint(QPointF(-div, (double)-dot / 5)); - painter.drawPoint(QPointF(-div, (double)dot / 5)); - painter.drawPoint(QPointF(div, (double)-dot / 5)); - painter.drawPoint(QPointF(div, (double)dot / 5)); - } - } - // Draw horizontal lines - for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) { - for (int dot = 1; dot < DIVS_TIME / 2 * 5; ++dot) { - if (dot % 5 == 0) continue; // Already done by vertical lines - painter.drawPoint(QPointF((double)-dot / 5, -div)); - painter.drawPoint(QPointF((double)dot / 5, -div)); - painter.drawPoint(QPointF((double)-dot / 5, div)); - painter.drawPoint(QPointF((double)dot / 5, div)); - } - } - } - - // Axes - painter.setPen(QPen(colorValues->axes, 0)); - painter.drawLine(QPointF(-DIVS_TIME / 2, 0), QPointF(DIVS_TIME / 2, 0)); - painter.drawLine(QPointF(0, -DIVS_VOLTAGE / 2), QPointF(0, DIVS_VOLTAGE / 2)); - for (double div = 0.2; div <= DIVS_TIME / 2; div += 0.2) { - painter.drawLine(QPointF(div, -0.05), QPointF(div, 0.05)); - painter.drawLine(QPointF(-div, -0.05), QPointF(-div, 0.05)); - } - for (double div = 0.2; div <= DIVS_VOLTAGE / 2; div += 0.2) { - painter.drawLine(QPointF(-0.05, div), QPointF(0.05, div)); - painter.drawLine(QPointF(-0.05, -div), QPointF(0.05, -div)); - } - - // Borders - painter.setPen(QPen(colorValues->border, 0)); - painter.drawRect(QRectF(-DIVS_TIME / 2, -DIVS_VOLTAGE / 2, DIVS_TIME, DIVS_VOLTAGE)); - } -} diff --git a/openhantek/src/exporter.h b/openhantek/src/exporter.h deleted file mode 100644 index 63fd0aa..0000000 --- a/openhantek/src/exporter.h +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once - -#include -#include -#include -#include - -class DsoSettings; -class DataAnalyzerResult; -struct DsoSettingsColorValues; - -//////////////////////////////////////////////////////////////////////////////// -/// \enum ExportFormat exporter.h -/// \brief Possible file formats for the export. -enum ExportFormat { EXPORT_FORMAT_PRINTER, EXPORT_FORMAT_PDF, EXPORT_FORMAT_IMAGE, EXPORT_FORMAT_CSV }; - -//////////////////////////////////////////////////////////////////////////////// -/// \class Exporter exporter.h -/// \brief Exports the oscilloscope screen to a file or prints it. -class Exporter { - public: - static Exporter *createPrintExporter(DsoSettings *settings); - static Exporter *createSaveToFileExporter(DsoSettings *settings); - - /// \brief Print the document (May be a file too) - bool exportSamples(const DataAnalyzerResult *result); - - private: - Exporter(DsoSettings *settings, const QString &filename, ExportFormat format); - void setFormat(ExportFormat format); - bool exportCSV(const DataAnalyzerResult *result); - static std::unique_ptr printPaintDevice(DsoSettings *settings); - void drawGrids(QPainter &painter, DsoSettingsColorValues *colorValues, double lineHeight, double scopeHeight, - int scopeWidth); - DsoSettings *settings; - std::unique_ptr selectedPrinter; - - QString filename; - ExportFormat format; -}; diff --git a/openhantek/src/exporting/exporter.cpp b/openhantek/src/exporting/exporter.cpp new file mode 100644 index 0000000..6d1fcd0 --- /dev/null +++ b/openhantek/src/exporting/exporter.cpp @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exporter.h" + +#include "controlspecification.h" +#include "post/graphgenerator.h" +#include "post/ppresult.h" +#include "settings.h" +#include "utils/dsoStrings.h" +#include "utils/printutils.h" +#include "viewconstants.h" + +#define tr(msg) QCoreApplication::translate("Exporter", msg) + +Exporter::Exporter(const Dso::ControlSpecification *deviceSpecification, DsoSettings *settings, const QString &filename, + ExportFormat format) + : deviceSpecification(deviceSpecification), settings(settings), filename(filename), format(format) {} + +Exporter *Exporter::createPrintExporter(const Dso::ControlSpecification *deviceSpecification, DsoSettings *settings) { + std::unique_ptr printer = printPaintDevice(settings); + // Show the printing dialog + QPrintDialog dialog(printer.get()); + dialog.setWindowTitle(tr("Print oscillograph")); + if (dialog.exec() != QDialog::Accepted) { return nullptr; } + + Exporter *exporter = new Exporter(deviceSpecification, settings, QString(), EXPORT_FORMAT_PRINTER); + exporter->selectedPrinter = std::move(printer); + return exporter; +} + +Exporter *Exporter::createSaveToFileExporter(const Dso::ControlSpecification *deviceSpecification, + DsoSettings *settings) { + QStringList filters; + filters << tr("Portable Document Format (*.pdf)") << tr("Image (*.png *.xpm *.jpg)") + << tr("Comma-Separated Values (*.csv)"); + + QFileDialog fileDialog(nullptr, tr("Export file..."), QString(), filters.join(";;")); + fileDialog.setFileMode(QFileDialog::AnyFile); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + if (fileDialog.exec() != QDialog::Accepted) return nullptr; + + return new Exporter(deviceSpecification, settings, fileDialog.selectedFiles().first(), + (ExportFormat)(EXPORT_FORMAT_PDF + filters.indexOf(fileDialog.selectedNameFilter()))); +} + +std::unique_ptr Exporter::printPaintDevice(DsoSettings *settings) { + // We need a QPrinter for printing, pdf- and ps-export + std::unique_ptr printer = std::unique_ptr(new QPrinter(QPrinter::HighResolution)); + printer->setOrientation(settings->view.zoom ? QPrinter::Portrait : QPrinter::Landscape); + printer->setPageMargins(20, 20, 20, 20, QPrinter::Millimeter); + return printer; +} + +bool Exporter::exportSamples(const PPresult *result) { + if (this->format == EXPORT_FORMAT_CSV) { return exportCSV(result); } + + // Choose the color values we need + DsoSettingsColorValues *colorValues; + if (this->format == EXPORT_FORMAT_IMAGE && settings->view.screenColorImages) + colorValues = &(settings->view.screen); + else + colorValues = &(settings->view.print); + + std::unique_ptr paintDevice; + + if (this->format == EXPORT_FORMAT_IMAGE) { + // We need a QPixmap for image-export + QPixmap *qPixmap = new QPixmap(settings->exporting.imageSize); + qPixmap->fill(colorValues->background); + paintDevice = std::unique_ptr(qPixmap); + } else if (this->format == EXPORT_FORMAT_PRINTER) { + paintDevice = std::move(selectedPrinter); + } else { + std::unique_ptr printer = printPaintDevice(settings); + printer->setOutputFileName(this->filename); + printer->setOutputFormat((this->format == EXPORT_FORMAT_PDF) ? QPrinter::PdfFormat : QPrinter::NativeFormat); + paintDevice = std::move(printer); + } + + if (!paintDevice) return false; + + // Create a painter for our device + QPainter painter(paintDevice.get()); + + // Get line height + QFont font; + QFontMetrics fontMetrics(font, paintDevice.get()); + double lineHeight = fontMetrics.height(); + + painter.setBrush(Qt::SolidPattern); + + // Draw the settings table + double stretchBase = (double)(paintDevice->width() - lineHeight * 10) / 4; + + // Print trigger details + painter.setPen(colorValues->voltage[settings->scope.trigger.source]); + QString levelString = valueToString(settings->scope.voltage[settings->scope.trigger.source].trigger, UNIT_VOLTS, 3); + QString pretriggerString = tr("%L1%").arg((int)(settings->scope.trigger.position * 100 + 0.5)); + painter.drawText(QRectF(0, 0, lineHeight * 10, lineHeight), + tr("%1 %2 %3 %4") + .arg(settings->scope.voltage[settings->scope.trigger.source].name, + Dso::slopeString(settings->scope.trigger.slope), levelString, pretriggerString)); + + double scopeHeight; + + { // DataAnalyser mutex lock + // Print sample count + painter.setPen(colorValues->text); + painter.drawText(QRectF(lineHeight * 10, 0, stretchBase, lineHeight), tr("%1 S").arg(result->sampleCount()), + QTextOption(Qt::AlignRight)); + // Print samplerate + painter.drawText(QRectF(lineHeight * 10 + stretchBase, 0, stretchBase, lineHeight), + valueToString(settings->scope.horizontal.samplerate, UNIT_SAMPLES) + tr("/s"), + QTextOption(Qt::AlignRight)); + // Print timebase + painter.drawText(QRectF(lineHeight * 10 + stretchBase * 2, 0, stretchBase, lineHeight), + valueToString(settings->scope.horizontal.timebase, UNIT_SECONDS, 0) + tr("/div"), + QTextOption(Qt::AlignRight)); + // Print frequencybase + painter.drawText(QRectF(lineHeight * 10 + stretchBase * 3, 0, stretchBase, lineHeight), + valueToString(settings->scope.horizontal.frequencybase, UNIT_HERTZ, 0) + tr("/div"), + QTextOption(Qt::AlignRight)); + + // Draw the measurement table + stretchBase = (double)(paintDevice->width() - lineHeight * 6) / 10; + int channelCount = 0; + for (int channel = settings->scope.voltage.size() - 1; channel >= 0; channel--) { + if ((settings->scope.voltage[channel].used || settings->scope.spectrum[channel].used) && + result->data(channel)) { + ++channelCount; + double top = (double)paintDevice->height() - channelCount * lineHeight; + + // Print label + painter.setPen(colorValues->voltage[channel]); + painter.drawText(QRectF(0, top, lineHeight * 4, lineHeight), settings->scope.voltage[channel].name); + // Print coupling/math mode + if ((unsigned int)channel < deviceSpecification->channels) + painter.drawText( + QRectF(lineHeight * 4, top, lineHeight * 2, lineHeight), + Dso::couplingString(settings->scope.coupling(channel, deviceSpecification))); + else + painter.drawText(QRectF(lineHeight * 4, top, lineHeight * 2, lineHeight), + Dso::mathModeString(settings->scope.voltage[channel].math)); + + // Print voltage gain + painter.drawText(QRectF(lineHeight * 6, top, stretchBase * 2, lineHeight), + valueToString(settings->scope.gain(channel), UNIT_VOLTS, 0) + tr("/div"), + QTextOption(Qt::AlignRight)); + // Print spectrum magnitude + if (settings->scope.spectrum[channel].used) { + painter.setPen(colorValues->spectrum[channel]); + painter.drawText(QRectF(lineHeight * 6 + stretchBase * 2, top, stretchBase * 2, lineHeight), + valueToString(settings->scope.spectrum[channel].magnitude, UNIT_DECIBEL, 0) + + tr("/div"), + QTextOption(Qt::AlignRight)); + } + + // Amplitude string representation (4 significant digits) + painter.setPen(colorValues->text); + painter.drawText(QRectF(lineHeight * 6 + stretchBase * 4, top, stretchBase * 3, lineHeight), + valueToString(result->data(channel)->computeAmplitude(), UNIT_VOLTS, 4), + QTextOption(Qt::AlignRight)); + // Frequency string representation (5 significant digits) + painter.drawText(QRectF(lineHeight * 6 + stretchBase * 7, top, stretchBase * 3, lineHeight), + valueToString(result->data(channel)->frequency, UNIT_HERTZ, 5), + QTextOption(Qt::AlignRight)); + } + } + + // Draw the marker table + stretchBase = (double)(paintDevice->width() - lineHeight * 10) / 4; + painter.setPen(colorValues->text); + + // Calculate variables needed for zoomed scope + double divs = fabs(settings->scope.horizontal.marker[1] - settings->scope.horizontal.marker[0]); + double time = divs * settings->scope.horizontal.timebase; + double zoomFactor = DIVS_TIME / divs; + double zoomOffset = (settings->scope.horizontal.marker[0] + settings->scope.horizontal.marker[1]) / 2; + + if (settings->view.zoom) { + scopeHeight = (double)(paintDevice->height() - (channelCount + 5) * lineHeight) / 2; + double top = 2.5 * lineHeight + scopeHeight; + + painter.drawText(QRectF(0, top, stretchBase, lineHeight), + tr("Zoom x%L1").arg(DIVS_TIME / divs, -1, 'g', 3)); + + painter.drawText(QRectF(lineHeight * 10, top, stretchBase, lineHeight), + valueToString(time, UNIT_SECONDS, 4), QTextOption(Qt::AlignRight)); + painter.drawText(QRectF(lineHeight * 10 + stretchBase, top, stretchBase, lineHeight), + valueToString(1.0 / time, UNIT_HERTZ, 4), QTextOption(Qt::AlignRight)); + + painter.drawText(QRectF(lineHeight * 10 + stretchBase * 2, top, stretchBase, lineHeight), + valueToString(time / DIVS_TIME, UNIT_SECONDS, 3) + tr("/div"), + QTextOption(Qt::AlignRight)); + painter.drawText(QRectF(lineHeight * 10 + stretchBase * 3, top, stretchBase, lineHeight), + valueToString(divs * settings->scope.horizontal.frequencybase / DIVS_TIME, UNIT_HERTZ, 3) + + tr("/div"), + QTextOption(Qt::AlignRight)); + } else { + scopeHeight = (double)paintDevice->height() - (channelCount + 4) * lineHeight; + double top = 2.5 * lineHeight + scopeHeight; + + painter.drawText(QRectF(0, top, stretchBase, lineHeight), tr("Marker 1/2")); + + painter.drawText(QRectF(lineHeight * 10, top, stretchBase * 2, lineHeight), + valueToString(time, UNIT_SECONDS, 4), QTextOption(Qt::AlignRight)); + painter.drawText(QRectF(lineHeight * 10 + stretchBase * 2, top, stretchBase * 2, lineHeight), + valueToString(1.0 / time, UNIT_HERTZ, 4), QTextOption(Qt::AlignRight)); + } + + // Set DIVS_TIME x DIVS_VOLTAGE matrix for oscillograph + painter.setMatrix(QMatrix((paintDevice->width() - 1) / DIVS_TIME, 0, 0, -(scopeHeight - 1) / DIVS_VOLTAGE, + (double)(paintDevice->width() - 1) / 2, (scopeHeight - 1) / 2 + lineHeight * 1.5), + false); + + // Draw the graphs + painter.setRenderHint(QPainter::Antialiasing); + painter.setBrush(Qt::NoBrush); + + for (int zoomed = 0; zoomed < (settings->view.zoom ? 2 : 1); ++zoomed) { + switch (settings->scope.horizontal.format) { + case Dso::GraphFormat::TY: + // Add graphs for channels + for (ChannelID channel = 0; channel < settings->scope.voltage.size(); ++channel) { + if (settings->scope.voltage[channel].used && result->data(channel)) { + painter.setPen(QPen(colorValues->voltage[channel], 0)); + + // What's the horizontal distance between sampling points? + double horizontalFactor = + result->data(channel)->voltage.interval / settings->scope.horizontal.timebase; + // How many samples are visible? + double centerPosition, centerOffset; + if (zoomed) { + centerPosition = (zoomOffset + DIVS_TIME / 2) / horizontalFactor; + centerOffset = DIVS_TIME / horizontalFactor / zoomFactor / 2; + } else { + centerPosition = DIVS_TIME / 2 / horizontalFactor; + centerOffset = DIVS_TIME / horizontalFactor / 2; + } + unsigned int firstPosition = qMax((int)(centerPosition - centerOffset), 0); + unsigned int lastPosition = qMin((int)(centerPosition + centerOffset), + (int)result->data(channel)->voltage.sample.size() - 1); + + // Draw graph + QPointF *graph = new QPointF[lastPosition - firstPosition + 1]; + + for (unsigned int position = firstPosition; position <= lastPosition; ++position) + graph[position - firstPosition] = QPointF(position * horizontalFactor - DIVS_TIME / 2, + result->data(channel)->voltage.sample[position] / + settings->scope.gain(channel) + + settings->scope.voltage[channel].offset); + + painter.drawPolyline(graph, lastPosition - firstPosition + 1); + delete[] graph; + } + } + + // Add spectrum graphs + for (ChannelID channel = 0; channel < settings->scope.spectrum.size(); ++channel) { + if (settings->scope.spectrum[channel].used && result->data(channel)) { + painter.setPen(QPen(colorValues->spectrum[channel], 0)); + + // What's the horizontal distance between sampling points? + double horizontalFactor = + result->data(channel)->spectrum.interval / settings->scope.horizontal.frequencybase; + // How many samples are visible? + double centerPosition, centerOffset; + if (zoomed) { + centerPosition = (zoomOffset + DIVS_TIME / 2) / horizontalFactor; + centerOffset = DIVS_TIME / horizontalFactor / zoomFactor / 2; + } else { + centerPosition = DIVS_TIME / 2 / horizontalFactor; + centerOffset = DIVS_TIME / horizontalFactor / 2; + } + unsigned int firstPosition = qMax((int)(centerPosition - centerOffset), 0); + unsigned int lastPosition = qMin((int)(centerPosition + centerOffset), + (int)result->data(channel)->spectrum.sample.size() - 1); + + // Draw graph + QPointF *graph = new QPointF[lastPosition - firstPosition + 1]; + + for (unsigned int position = firstPosition; position <= lastPosition; ++position) + graph[position - firstPosition] = + QPointF(position * horizontalFactor - DIVS_TIME / 2, + result->data(channel)->spectrum.sample[position] / + settings->scope.spectrum[channel].magnitude + + settings->scope.spectrum[channel].offset); + + painter.drawPolyline(graph, lastPosition - firstPosition + 1); + delete[] graph; + } + } + break; + + case Dso::GraphFormat::XY: + break; + + default: + break; + } + + // Set DIVS_TIME / zoomFactor x DIVS_VOLTAGE matrix for zoomed + // oscillograph + painter.setMatrix(QMatrix((paintDevice->width() - 1) / DIVS_TIME * zoomFactor, 0, 0, + -(scopeHeight - 1) / DIVS_VOLTAGE, + (double)(paintDevice->width() - 1) / 2 - + zoomOffset * zoomFactor * (paintDevice->width() - 1) / DIVS_TIME, + (scopeHeight - 1) * 1.5 + lineHeight * 4), + false); + } + } // dataanalyser mutex release + + drawGrids(painter, colorValues, lineHeight, scopeHeight, paintDevice->width()); + painter.end(); + + if (this->format == EXPORT_FORMAT_IMAGE) static_cast(paintDevice.get())->save(this->filename); + + return true; +} + +bool Exporter::exportCSV(const PPresult *result) { + QFile csvFile(this->filename); + if (!csvFile.open(QIODevice::WriteOnly | QIODevice::Text)) return false; + + QTextStream csvStream(&csvFile); + + size_t chCount = settings->scope.voltage.size(); + std::vector voltageData(size_t(chCount), nullptr); + std::vector spectrumData(size_t(chCount), nullptr); + size_t maxRow = 0; + bool isSpectrumUsed = false; + double timeInterval = 0; + double freqInterval = 0; + + for (ChannelID channel = 0; channel < chCount; ++channel) { + if (result->data(channel)) { + if (settings->scope.voltage[channel].used) { + voltageData[channel] = &(result->data(channel)->voltage); + maxRow = std::max(maxRow, voltageData[channel]->sample.size()); + timeInterval = result->data(channel)->voltage.interval; + } + if (settings->scope.spectrum[channel].used) { + spectrumData[channel] = &(result->data(channel)->spectrum); + maxRow = std::max(maxRow, spectrumData[channel]->sample.size()); + freqInterval = result->data(channel)->spectrum.interval; + isSpectrumUsed = true; + } + } + } + + // Start with channel names + csvStream << "\"t\""; + for (ChannelID channel = 0; channel < chCount; ++channel) { + if (voltageData[channel] != nullptr) { csvStream << ",\"" << settings->scope.voltage[channel].name << "\""; } + } + if (isSpectrumUsed) { + csvStream << ",\"f\""; + for (ChannelID channel = 0; channel < chCount; ++channel) { + if (spectrumData[channel] != nullptr) { + csvStream << ",\"" << settings->scope.spectrum[channel].name << "\""; + } + } + } + csvStream << "\n"; + + for (unsigned int row = 0; row < maxRow; ++row) { + + csvStream << timeInterval * row; + for (ChannelID channel = 0; channel < chCount; ++channel) { + if (voltageData[channel] != nullptr) { + csvStream << ","; + if (row < voltageData[channel]->sample.size()) { csvStream << voltageData[channel]->sample[row]; } + } + } + + if (isSpectrumUsed) { + csvStream << "," << freqInterval * row; + for (ChannelID channel = 0; channel < chCount; ++channel) { + if (spectrumData[channel] != nullptr) { + csvStream << ","; + if (row < spectrumData[channel]->sample.size()) { csvStream << spectrumData[channel]->sample[row]; } + } + } + } + csvStream << "\n"; + } + + csvFile.close(); + + return true; +} + +void Exporter::drawGrids(QPainter &painter, DsoSettingsColorValues *colorValues, double lineHeight, double scopeHeight, + int scopeWidth) { + painter.setRenderHint(QPainter::Antialiasing, false); + for (int zoomed = 0; zoomed < (settings->view.zoom ? 2 : 1); ++zoomed) { + // Set DIVS_TIME x DIVS_VOLTAGE matrix for oscillograph + painter.setMatrix(QMatrix((scopeWidth - 1) / DIVS_TIME, 0, 0, -(scopeHeight - 1) / DIVS_VOLTAGE, + (double)(scopeWidth - 1) / 2, + (scopeHeight - 1) * (zoomed + 0.5) + lineHeight * 1.5 + lineHeight * 2.5 * zoomed), + false); + + // Grid lines + painter.setPen(QPen(colorValues->grid, 0)); + + if (this->format < EXPORT_FORMAT_IMAGE) { + // Draw vertical lines + for (int div = 1; div < DIVS_TIME / 2; ++div) { + for (int dot = 1; dot < DIVS_VOLTAGE / 2 * 5; ++dot) { + painter.drawLine(QPointF((double)-div - 0.02, (double)-dot / 5), + QPointF((double)-div + 0.02, (double)-dot / 5)); + painter.drawLine(QPointF((double)-div - 0.02, (double)dot / 5), + QPointF((double)-div + 0.02, (double)dot / 5)); + painter.drawLine(QPointF((double)div - 0.02, (double)-dot / 5), + QPointF((double)div + 0.02, (double)-dot / 5)); + painter.drawLine(QPointF((double)div - 0.02, (double)dot / 5), + QPointF((double)div + 0.02, (double)dot / 5)); + } + } + // Draw horizontal lines + for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) { + for (int dot = 1; dot < DIVS_TIME / 2 * 5; ++dot) { + painter.drawLine(QPointF((double)-dot / 5, (double)-div - 0.02), + QPointF((double)-dot / 5, (double)-div + 0.02)); + painter.drawLine(QPointF((double)dot / 5, (double)-div - 0.02), + QPointF((double)dot / 5, (double)-div + 0.02)); + painter.drawLine(QPointF((double)-dot / 5, (double)div - 0.02), + QPointF((double)-dot / 5, (double)div + 0.02)); + painter.drawLine(QPointF((double)dot / 5, (double)div - 0.02), + QPointF((double)dot / 5, (double)div + 0.02)); + } + } + } else { + // Draw vertical lines + for (int div = 1; div < DIVS_TIME / 2; ++div) { + for (int dot = 1; dot < DIVS_VOLTAGE / 2 * 5; ++dot) { + painter.drawPoint(QPointF(-div, (double)-dot / 5)); + painter.drawPoint(QPointF(-div, (double)dot / 5)); + painter.drawPoint(QPointF(div, (double)-dot / 5)); + painter.drawPoint(QPointF(div, (double)dot / 5)); + } + } + // Draw horizontal lines + for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) { + for (int dot = 1; dot < DIVS_TIME / 2 * 5; ++dot) { + if (dot % 5 == 0) continue; // Already done by vertical lines + painter.drawPoint(QPointF((double)-dot / 5, -div)); + painter.drawPoint(QPointF((double)dot / 5, -div)); + painter.drawPoint(QPointF((double)-dot / 5, div)); + painter.drawPoint(QPointF((double)dot / 5, div)); + } + } + } + + // Axes + painter.setPen(QPen(colorValues->axes, 0)); + painter.drawLine(QPointF(-DIVS_TIME / 2, 0), QPointF(DIVS_TIME / 2, 0)); + painter.drawLine(QPointF(0, -DIVS_VOLTAGE / 2), QPointF(0, DIVS_VOLTAGE / 2)); + for (double div = 0.2; div <= DIVS_TIME / 2; div += 0.2) { + painter.drawLine(QPointF(div, -0.05), QPointF(div, 0.05)); + painter.drawLine(QPointF(-div, -0.05), QPointF(-div, 0.05)); + } + for (double div = 0.2; div <= DIVS_VOLTAGE / 2; div += 0.2) { + painter.drawLine(QPointF(-0.05, div), QPointF(0.05, div)); + painter.drawLine(QPointF(-0.05, -div), QPointF(0.05, -div)); + } + + // Borders + painter.setPen(QPen(colorValues->border, 0)); + painter.drawRect(QRectF(-DIVS_TIME / 2, -DIVS_VOLTAGE / 2, DIVS_TIME, DIVS_VOLTAGE)); + } +} diff --git a/openhantek/src/exporting/exporter.h b/openhantek/src/exporting/exporter.h new file mode 100644 index 0000000..154ee4b --- /dev/null +++ b/openhantek/src/exporting/exporter.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#pragma once + +#include +#include +#include +#include +#include "exportsettings.h" + +class DsoSettings; +class PPresult; +struct DsoSettingsColorValues; +namespace Dso { struct ControlSpecification; } + +/// \brief Exports the oscilloscope screen to a file or prints it. +class Exporter { + public: + static Exporter *createPrintExporter(const Dso::ControlSpecification* deviceSpecification, DsoSettings *settings); + static Exporter *createSaveToFileExporter(const Dso::ControlSpecification* deviceSpecification, DsoSettings *settings); + + /// \brief Print the document (May be a file too) + bool exportSamples(const PPresult *result); + + private: + Exporter(const Dso::ControlSpecification* deviceSpecification, DsoSettings *settings, const QString &filename, ExportFormat format); + void setFormat(ExportFormat format); + bool exportCSV(const PPresult *result); + static std::unique_ptr printPaintDevice(DsoSettings *settings); + void drawGrids(QPainter &painter, DsoSettingsColorValues *colorValues, double lineHeight, double scopeHeight, + int scopeWidth); + const Dso::ControlSpecification* deviceSpecification; + DsoSettings *settings; + std::unique_ptr selectedPrinter; + + QString filename; + ExportFormat format; +}; diff --git a/openhantek/src/exporting/exportsettings.h b/openhantek/src/exporting/exportsettings.h new file mode 100644 index 0000000..586bc35 --- /dev/null +++ b/openhantek/src/exporting/exportsettings.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#pragma once + +#include + +/// \brief Holds the general options of the program. +struct DsoSettingsExport { + QSize imageSize = QSize(640, 480); ///< Size of exported images in pixels +}; + +/// \brief Possible file formats for the export. +enum ExportFormat { EXPORT_FORMAT_PRINTER, EXPORT_FORMAT_PDF, EXPORT_FORMAT_IMAGE, EXPORT_FORMAT_CSV }; diff --git a/openhantek/src/scopesettings.h b/openhantek/src/scopesettings.h index b26a431..00e844a 100644 --- a/openhantek/src/scopesettings.h +++ b/openhantek/src/scopesettings.h @@ -4,10 +4,10 @@ #include -#include "analyse/enums.h" #include "hantekdso/controlspecification.h" #include "hantekdso/enums.h" #include "hantekprotocol/definitions.h" +#include "post/enums.h" #include #define MARKER_COUNT 2 ///< Number of markers @@ -21,9 +21,9 @@ struct DsoSettingsScopeHorizontal { unsigned int recordLength = 0; ///< Sample count - ///TODO Use ControlSettingsSamplerateTarget - double timebase = 1e-3; ///< Timebase in s/div - double samplerate = 1e6; ///< The samplerate of the oscilloscope in S + /// TODO Use ControlSettingsSamplerateTarget + double timebase = 1e-3; ///< Timebase in s/div + double samplerate = 1e6; ///< The samplerate of the oscilloscope in S enum SamplerateSource { Samplerrate, Duration } samplerateSource = Samplerrate; }; @@ -31,12 +31,12 @@ struct DsoSettingsScopeHorizontal { /// TODO Use ControlSettingsTrigger struct DsoSettingsScopeTrigger { Dso::TriggerMode mode = Dso::TriggerMode::HARDWARE_SOFTWARE; ///< Automatic, normal or single trigger - double position = 0.0; ///< Horizontal position for pretrigger - Dso::Slope slope = Dso::Slope::Positive; ///< Rising or falling edge causes trigger - unsigned int source = 0; ///< Channel that is used as trigger source - bool special = false; ///< true if the trigger source is not a standard channel - unsigned swTriggerThreshold = 7; ///< Software trigger, threshold - unsigned swTriggerSampleSet = 11; ///< Software trigger, sample set + double position = 0.0; ///< Horizontal position for pretrigger + Dso::Slope slope = Dso::Slope::Positive; ///< Rising or falling edge causes trigger + unsigned int source = 0; ///< Channel that is used as trigger source + bool special = false; ///< true if the trigger source is not a standard channel + unsigned swTriggerThreshold = 7; ///< Software trigger, threshold + unsigned swTriggerSampleSet = 11; ///< Software trigger, sample set }; /// \brief Holds the settings for the spectrum analysis. @@ -51,17 +51,17 @@ struct DsoSettingsScopeSpectrum { /// \brief Holds the settings for the normal voltage graphs. /// TODO Use ControlSettingsVoltage struct DsoSettingsScopeVoltage { + double offset = 0.0; ///< Vertical offset in divs + double trigger = 0.0; ///< Trigger level in V unsigned gainStepIndex = 6; ///< The vertical resolution in V/div (default = 1.0) - bool inverted = false; ///< true if the channel is inverted (mirrored on cross-axis) union { ///< Different enums, coupling for real- and mode for math-channels Dso::MathMode math; unsigned couplingIndex = 0; int rawValue; }; - QString name; ///< Name of this channel - double offset = 0.0; ///< Vertical offset in divs - double trigger = 0.0; ///< Trigger level in V - bool used = false; ///< true if this channel is enabled + QString name; ///< Name of this channel + bool inverted = false; ///< true if the channel is inverted (mirrored on cross-axis) + bool used = false; ///< true if this channel is enabled }; /// \brief Holds the settings for the oscilloscope. @@ -82,4 +82,6 @@ struct DsoSettingsScope { Dso::Coupling coupling(ChannelID channel, const Dso::ControlSpecification *deviceSpecification) { return deviceSpecification->couplings[voltage[channel].couplingIndex]; } + // Channels, including math channels + unsigned countChannels() { return (unsigned)voltage.size(); } }; diff --git a/openhantek/src/settings.cpp b/openhantek/src/settings.cpp index 1361189..bb8fc99 100644 --- a/openhantek/src/settings.cpp +++ b/openhantek/src/settings.cpp @@ -11,9 +11,7 @@ /// \brief Set the number of channels. /// \param channels The new channel count, that will be applied to lists. -DsoSettings::DsoSettings(const Dso::ControlSpecification* deviceSpecification) - : deviceSpecification(deviceSpecification) { - +DsoSettings::DsoSettings(const Dso::ControlSpecification* deviceSpecification) { // Add new channels to the list while (scope.spectrum.size() < deviceSpecification->channels) { // Spectrum @@ -63,10 +61,11 @@ bool DsoSettings::setFilename(const QString &filename) { void DsoSettings::load() { // General options store->beginGroup("options"); - if (store->contains("alwaysSave")) options.alwaysSave = store->value("alwaysSave").toBool(); - if (store->contains("imageSize")) options.imageSize = store->value("imageSize").toSize(); - // If the window/* keys were found in this group, remove them from settings - store->remove("window"); + if (store->contains("alwaysSave")) alwaysSave = store->value("alwaysSave").toBool(); + store->endGroup(); + + store->beginGroup("exporting"); + if (store->contains("imageSize")) exporting.imageSize = store->value("imageSize").toSize(); store->endGroup(); // Oscilloscope settings @@ -170,8 +169,11 @@ void DsoSettings::load() { void DsoSettings::save() { // Main window layout and other general options store->beginGroup("options"); - store->setValue("alwaysSave", options.alwaysSave); - store->setValue("imageSize", options.imageSize); + store->setValue("alwaysSave", alwaysSave); + store->endGroup(); + + store->beginGroup("exporting"); + store->setValue("imageSize", exporting.imageSize); store->endGroup(); // Oszilloskope settings diff --git a/openhantek/src/settings.h b/openhantek/src/settings.h index b6f8801..ff1cbeb 100644 --- a/openhantek/src/settings.h +++ b/openhantek/src/settings.h @@ -9,31 +9,20 @@ #include "scopesettings.h" #include "viewsettings.h" +#include "exporting/exportsettings.h" #include "hantekdso/controlspecification.h" #include "hantekdso/controlsettings.h" -//////////////////////////////////////////////////////////////////////////////// -/// \struct DsoSettingsOptions -/// \brief Holds the general options of the program. -struct DsoSettingsOptions { - bool alwaysSave = true; ///< Always save the settings on exit - QSize imageSize = QSize(640, 480); ///< Size of exported images in pixels -}; - -//////////////////////////////////////////////////////////////////////////////// -/// \class DsoSettings /// \brief Holds the settings of the program. class DsoSettings { public: explicit DsoSettings(const Dso::ControlSpecification* deviceSpecification); bool setFilename(const QString &filename); - DsoSettingsOptions options; ///< General options of the program + DsoSettingsExport exporting; ///< General options of the program DsoSettingsScope scope; ///< All oscilloscope related settings DsoSettingsView view; ///< All view related settings - - // Read only access to device settings and device specification - const Dso::ControlSpecification* deviceSpecification; + bool alwaysSave = true; ///< Always save the settings on exit QByteArray mainWindowGeometry; ///< Geometry of the main window QByteArray mainWindowState; ///< State of docking windows and toolbars -- libgit2 0.21.4