diff --git a/QtPMbrowser/DlgExportMetadata.ui b/QtPMbrowser/DlgExportMetadata.ui
index fcaa30b..5c8b1e3 100644
--- a/QtPMbrowser/DlgExportMetadata.ui
+++ b/QtPMbrowser/DlgExportMetadata.ui
@@ -6,15 +6,57 @@
0
0
- 358
- 252
+ 383
+ 279
+
+
+ 0
+ 0
+
+
Export metadata as table
-
- -
+
+
-
+
+
+ <html><head/><body><p>Only parameters marked <span style=" font-style:italic;">export</span> in the <span style=" font-style:italic;">Select Parameters</span> dialog will be included in export.</p></body></html>
+
+
+ false
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Select Parameters...
+
+
+
+ -
+
+
+ Export one line per:
+
+
+ Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
+
+
+
+ -
-
@@ -38,27 +80,21 @@
- -
-
+
-
+
- copy to clipboard
+ use system default formatting
- -
-
-
- Qt::Orientation::Vertical
-
-
-
- 20
- 40
-
+
-
+
+
+ Copy to Clipboard
-
+
- -
+
-
Qt::Orientation::Horizontal
@@ -71,39 +107,18 @@
- -
-
-
- use system default formatting
-
-
-
- -
-
-
- <html><head/><body><p>About to export tab delimited file. Only parameters marked <span style=" font-style:italic;">export</span> in the <span style=" font-style:italic;">Select Parameters</span> dialog will be included in export.</p></body></html>
-
-
- false
-
-
- true
-
-
-
- -
-
-
- One line per:
+
-
+
+
+ Qt::Orientation::Vertical
-
-
- -
-
-
- Select Parameters...
+
+
+ 20
+ 40
+
-
+
diff --git a/QtPMbrowser/renderarea.cpp b/QtPMbrowser/renderarea.cpp
index eb6dacc..101d1a3 100644
--- a/QtPMbrowser/renderarea.cpp
+++ b/QtPMbrowser/renderarea.cpp
@@ -17,8 +17,8 @@
along with PMbrowser. If not, see .
*/
-#include
#include
+#include
#include
#include
#include
@@ -49,7 +49,7 @@ RenderArea::RenderArea(QWidget* parent) :
xTrace{}, yTrace{}, tracebuffer{}, background_traces_hidden{ false },
clipped{ false },
x_min{ 0.0 }, x_max{ 0.0 },
- y_min{}, y_max{}, a_x{}, b_x{}, a_y{}, b_y{}, numtraces{ 10 },
+ a_x{}, b_x{}, a_y{}, b_y{}, numtraces{ 10 },
do_autoscale_on_load{ true }, isTraceDragging{ false }, isPinching{false},
isSelecting{ false }, selStart{}, selEnd{}, tempPixMap{ nullptr },
settings_modified{ false }
@@ -145,17 +145,21 @@ void RenderArea::paint(QPainter& painter, const QRect& rectangle)
"x- and y-trace: numbers of datapoints\nnot equal\n(required for YX-mode)");
}
else {
+ const double y_min = currentYscale->y_min;
+ const double y_max = currentYscale->y_max;
setScaling(x_min, x_max, y_min, y_max);
drawGrid(painter, show_grid_horz, show_grid_vert);
if (!background_traces_hidden) {
// paint traces in persistance buffer
painter.setPen(color_bktrace);
for (auto trace : std::as_const(tracebuffer)) {
+ priv_Scale ys = yScales[trace->getYUnit()];
+ setScaling(x_min, x_max, ys.y_min, ys.y_max);
trace->render(painter, this);
}
painter.setPen(color_trace);
}
-
+ setScaling(x_min, x_max, y_min, y_max);
yTrace.render(painter, this);
font = painter.font();
@@ -228,15 +232,15 @@ void RenderArea::drawGrid(QPainter& painter, bool horizontal, bool vertical)
QPointF(zero_point.x(), static_cast(height())));
}
}
- if (horizontal && y_min < y_max) {
+ if (horizontal && currentYscale->y_min < currentYscale->y_max) {
double d_width = width();
- double horz_step = std::pow(10.0, std::floor(std::log10(y_max - y_min)));
+ double horz_step = std::pow(10.0, std::floor(std::log10(currentYscale->y_max - currentYscale->y_min)));
if (info.length() > 0) {
info.append(" | ");
}
info.append(QString("y: %L1/div").arg(horz_step));
- int horz_divs = static_cast((y_max - y_min) / horz_step) + 1;
- auto line_0 = std::ceil(y_min / horz_step) * horz_step;
+ int horz_divs = static_cast((currentYscale->y_max - currentYscale->y_min) / horz_step) + 1;
+ auto line_0 = std::ceil(currentYscale->y_min / horz_step) * horz_step;
painter.setPen(penDashed);
for (int i = 0; i < horz_divs; ++i) {
double y = line_0 + i * horz_step;
@@ -244,7 +248,7 @@ void RenderArea::drawGrid(QPainter& painter, bool horizontal, bool vertical)
auto py = scaleToQPF(0.0, y).y();
painter.drawLine(QPointF(0.0, py), QPointF(d_width, py));
}
- if (y_min < 0.0 && y_max>0.0) {
+ if (currentYscale->y_min < 0.0 && currentYscale->y_max>0.0) {
// draw zero line
painter.setPen(penSolid);
painter.drawLine(QPointF(0.0, zero_point.y()),
@@ -476,8 +480,8 @@ void RenderArea::mouseReleaseEvent(QMouseEvent* event)
scaleFromPixToXY(selStart.x(), selStart.y(), xs, ys);
x_min = std::min(x, xs);
x_max = std::max(x, xs);
- y_min = std::min(y, ys);
- y_max = std::max(y, ys);
+ currentYscale->y_min = std::min(y, ys);
+ currentYscale->y_max = std::max(y, ys);
}
update();
event->accept();
@@ -541,15 +545,14 @@ static void find_min_max(std::vector::const_iterator first,
double& min_val, double& max_val)
{
min_val = max_val = *first;
- auto p = first;
- for (; p != last; ++p) {
- if (!std::isnan(*p)) {
- min_val = max_val = *p;
+ for (; first != last; ++first) {
+ if (!std::isnan(*first)) {
+ min_val = max_val = *first;
break;
}
}
- for (; p != last; ++p) {
- auto val = *p;
+ for (; first != last; ++first) {
+ auto val = *first;
if (!std::isnan(val)) {
min_val = std::min(min_val, val);
max_val = std::max(max_val, val);
@@ -571,16 +574,16 @@ void RenderArea::autoScale()
x_min = yTrace.x0;
x_max = yTrace.x0 + static_cast(yTrace.data.size() - 1) * yTrace.deltax;
}
- find_min_max(yTrace.data.cbegin(), yTrace.data.cend(), y_min, y_max);
+ find_min_max(yTrace.data.cbegin(), yTrace.data.cend(), currentYscale->y_min, currentYscale->y_max);
update();
}
void RenderArea::verticalShrink()
{
- double nymin = 1.5 * y_min - 0.5 * y_max,
- nymax = 1.5 * y_max - 0.5 * y_min;
- y_min = nymin;
- y_max = nymax;
+ double nymin = 1.5 * currentYscale->y_min - 0.5 * currentYscale->y_max,
+ nymax = 1.5 * currentYscale->y_max - 0.5 * currentYscale->y_min;
+ currentYscale->y_min = nymin;
+ currentYscale->y_max = nymax;
update();
}
@@ -650,11 +653,11 @@ void RenderArea::copyToClipboard()
void RenderArea::showSettingsDialog()
{
DlgGraphSettings dlg(this);
- dlg.setValues(do_autoscale_on_load, x_min, x_max, y_min, y_max, numtraces,
+ dlg.setValues(do_autoscale_on_load, x_min, x_max, currentYscale->y_min, currentYscale->y_max, numtraces,
show_grid_horz, show_grid_vert, color_grid, color_trace, color_bktrace);
if (dlg.exec()) {
settings_modified = true;
- dlg.getValues(do_autoscale_on_load, x_min, x_max, y_min, y_max, numtraces,
+ dlg.getValues(do_autoscale_on_load, x_min, x_max, currentYscale->y_min, currentYscale->y_max, numtraces,
show_grid_horz, show_grid_vert, color_grid, color_trace, color_bktrace);
// if numtraces has been reduced we want to get rid of excess traces
while (tracebuffer.size() > numtraces) {
@@ -673,40 +676,36 @@ void RenderArea::zoomIn(double x_center, double y_center, double factor)
{
x_min = x_center - (x_center - x_min) / factor;
x_max = x_center + (x_max - x_center) / factor;
- y_min = y_center - (y_center - y_min) / factor;
- y_max = y_center + (y_max - y_center) / factor;
+ currentYscale->y_min = y_center - (y_center - currentYscale->y_min) / factor;
+ currentYscale->y_max = y_center + (currentYscale->y_max - y_center) / factor;
update();
}
void RenderArea::renderTrace(hkLib::hkTreeNode* TrRecord, std::istream& infile)
{
using namespace hkLib;
- if (yTrace.isValid()) {
- tracebuffer.enqueue(new DisplayTrace(std::move(yTrace)));
- while (tracebuffer.size() > numtraces) {
- delete tracebuffer.dequeue();
- }
- }
+ DisplayTrace newYtrace{};
char dataformat = TrRecord->getChar(TrDataFormat);
uint16_t tracedatakind = TrRecord->extractUInt16(TrDataKind);
clipped = tracedatakind & ClipBit;
- yTrace.y_unit = qs_from_sv(TrRecord->getString<8>(TrYUnit));
- yTrace.x_unit = qs_from_sv(TrRecord->getString<8>(TrXUnit));
- yTrace.x0 = TrRecord->extractLongReal(TrXStart), yTrace.deltax = TrRecord->extractLongReal(TrXInterval);
+ newYtrace.y_unit = qs_from_sv(TrRecord->getString<8>(TrYUnit));
+ newYtrace.x_unit = qs_from_sv(TrRecord->getString<8>(TrXUnit));
+ newYtrace.x0 = TrRecord->extractLongReal(TrXStart);
+ newYtrace.deltax = TrRecord->extractLongReal(TrXInterval);
ndatapoints = TrRecord->extractValue(TrDataPoints);
- yTrace.data.resize(ndatapoints);
+ newYtrace.data.resize(ndatapoints);
try {
if (dataformat == DFT_int16) {
- ReadScaleAndConvert(infile, *TrRecord, ndatapoints, yTrace.data.data());
+ ReadScaleAndConvert(infile, *TrRecord, ndatapoints, newYtrace.data.data());
}
else if (dataformat == DFT_int32) {
- ReadScaleAndConvert(infile, *TrRecord, ndatapoints, yTrace.data.data());
+ ReadScaleAndConvert(infile, *TrRecord, ndatapoints, newYtrace.data.data());
}
else if (dataformat == DFT_float) {
- ReadScaleAndConvert(infile, *TrRecord, ndatapoints, yTrace.data.data());
+ ReadScaleAndConvert(infile, *TrRecord, ndatapoints, newYtrace.data.data());
}
else if (dataformat == DFT_double) {
- ReadScaleAndConvert(infile, *TrRecord, ndatapoints, yTrace.data.data());
+ ReadScaleAndConvert(infile, *TrRecord, ndatapoints, newYtrace.data.data());
}
else {
QMessageBox::warning(this, QString("Data Format Error"), QString("Unknown Dataformat"));
@@ -714,10 +713,11 @@ void RenderArea::renderTrace(hkLib::hkTreeNode* TrRecord, std::istream& infile)
}
}
catch (const std::exception& e) {
- yTrace.data.clear();
+// newYtrace.data.clear();
QMessageBox::warning(nullptr, "File Error", e.what());
return;
}
+ addTrace(std::move(newYtrace));
if (do_autoscale_on_load) { autoScale(); }
else { update(); } // update is usually done within autoScale()
setMouseTracking(true);
@@ -732,6 +732,7 @@ void RenderArea::addTrace(DisplayTrace&& dt)
}
}
yTrace = std::move(dt);
+ currentYscale = &yScales[yTrace.getYUnit()];
if (do_autoscale_on_load) { autoScale(); }
setMouseTracking(true);
update();
@@ -803,7 +804,7 @@ QPointF RenderArea::scaleToQPF(double x, double y)
void RenderArea::scaleFromPixToXY(double px, double py, double& x, double& y)
{
x = x_min + px / double(width()) * (x_max - x_min);
- y = y_max - (py - button_row_height) / double(height() - button_row_height) * (y_max - y_min);
+ y = currentYscale->y_max - (py - button_row_height) / double(height() - button_row_height) * (currentYscale->y_max - currentYscale->y_min);
}
void RenderArea::scaleFromPixToXY(const QPointF& p, double& x, double& y)
@@ -820,9 +821,20 @@ void RenderArea::shiftByPixel(QPoint shift)
update();
}
if (shift.y() != 0) {
- auto dy = -(y_max - y_min) / height() * double(shift.y());
- y_max += dy;
- y_min += dy;
+ if (shift_all_y_scales) {
+ for (auto& ys : yScales) {
+ //shift each y scale
+ auto dy = -(ys.y_max - ys.y_min) / height() * double(shift.y());
+ ys.y_max += dy;
+ ys.y_min += dy;
+ }
+ }
+ else {
+ auto& ys = *currentYscale;
+ auto dy = -(ys.y_max - ys.y_min) / height() * double(shift.y());
+ ys.y_max += dy;
+ ys.y_min += dy;
+ }
update();
}
}
@@ -832,6 +844,7 @@ void RenderArea::loadSettings()
QSettings s;
s.beginGroup("renderarea");
do_autoscale_on_load = s.value("do_autoscale_on_load", int(do_autoscale_on_load)).toInt();
+ shift_all_y_scales = s.value("shift_all_y_scales", int(shift_all_y_scales)).toInt();
show_grid_horz = s.value("show_grid_horz", int(show_grid_horz)).toInt();
show_grid_vert = s.value("show_grid_vert", int(show_grid_vert)).toInt();
background_traces_hidden = !s.value("overlay", 1).toInt();
@@ -849,6 +862,7 @@ void RenderArea::saveSettings()
QSettings s;
s.beginGroup("renderarea");
s.setValue("do_autoscale_on_load", int(do_autoscale_on_load));
+ s.setValue("shift_all_y_scales", int(shift_all_y_scales));
s.setValue("show_grid_horz", int(show_grid_horz));
s.setValue("show_grid_vert", int(show_grid_vert));
s.setValue("overlay", int(!background_traces_hidden));
diff --git a/QtPMbrowser/renderarea.h b/QtPMbrowser/renderarea.h
index 25a437e..326cc49 100644
--- a/QtPMbrowser/renderarea.h
+++ b/QtPMbrowser/renderarea.h
@@ -21,6 +21,7 @@
#define RENDERAREA_H
#include
+#include
#include
#include
#include
@@ -123,6 +124,8 @@ public slots:
QPushButton btnWipe, btnAutoScale, btnVertShrink, btnHrzShrink;
QCheckBox chkAutoScale, chkOverlay;
+ struct priv_Scale { double y_min{}; double y_max{}; };
+ QMap yScales{};
QGridLayout* my_layout{};
int button_row_height{};
@@ -130,8 +133,10 @@ public slots:
DisplayTrace xTrace, yTrace; // TODO at least yTrace should be a pointer?
QQueue tracebuffer;
bool background_traces_hidden;
+ bool shift_all_y_scales{ true };
bool clipped; // Amp. was clipping
- double x_min, x_max, y_min, y_max;
+ double x_min, x_max;
+ priv_Scale* currentYscale{};
double a_x, b_x, a_y, b_y; // for scaling
int numtraces; // number of traces in persistance buffer
bool do_autoscale_on_load;
diff --git a/hekatoolslib/time_handling.cpp b/hekatoolslib/time_handling.cpp
index 11ba376..a5db55b 100644
--- a/hekatoolslib/time_handling.cpp
+++ b/hekatoolslib/time_handling.cpp
@@ -19,15 +19,18 @@
#include
#include
-#include
#include
#include
#include
#include "time_handling.h"
constexpr std::time_t EPOCHDIFF_MAC_UNIX = 2082844800;
-constexpr double JanFirst1990MACTime = 1580970496.0; //1580947200.0; // better value?
-constexpr auto HIGH_DWORD = 4294967296.0;
+
+// the following values are from file format documentation
+// Strangly, the actual time difference in time_t seconds
+// is 126144000, but PM seems to caculate with the given difference.
+constexpr double JanFirst1990MACTime = 1580970496.0;
+constexpr auto HIGH_DWORD = 4294967296.0; // = 2^32
namespace hkLib {
///
@@ -42,7 +45,7 @@ namespace hkLib {
if (t < 0.0) {
t += HIGH_DWORD; // why is this necessary?
}
- return std::time_t(std::floor(t)) - EPOCHDIFF_MAC_UNIX;
+ return static_cast(std::floor(t)) - EPOCHDIFF_MAC_UNIX;
}
constexpr std::size_t BUFF_SIZE = 128;
@@ -69,6 +72,6 @@ namespace hkLib {
std::string formatPMtimeUTC(double t)
{
- return formatPMtime(t, "%FT%T UTC");
+ return formatPMtime(t, "%FT%T");
}
}