From ee5434173850f2ec45c36f0fe7c3cd19dd8f07a2 Mon Sep 17 00:00:00 2001 From: FBK Date: Mon, 30 Oct 2023 13:07:41 +0200 Subject: [PATCH 01/38] Remove a double of "Ctrl+Shift+S" action in a Plot menu --- src/asammdf/gui/widgets/main.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/asammdf/gui/widgets/main.py b/src/asammdf/gui/widgets/main.py index 2d41b7ca2..a650c193c 100644 --- a/src/asammdf/gui/widgets/main.py +++ b/src/asammdf/gui/widgets/main.py @@ -475,13 +475,6 @@ def __init__(self, files=None, *args, **kwargs): action.setShortcut(QtGui.QKeySequence("Ctrl+Shift+S")) plot_actions.addAction(action) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(":/save.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) - action = QtGui.QAction(icon, "{: <20}\tCtrl+Shift+S".format("Save all subplot channels"), menu) - action.triggered.connect(self.save_all_subplots) - action.setShortcut(QtGui.QKeySequence("Ctrl+Shift+S")) - plot_actions.addAction(action) - # channel shifting channel_shift_actions = QtGui.QActionGroup(self) From d5647fb7fca8e42ed4e0c856515c92a001b22dc7 Mon Sep 17 00:00:00 2001 From: FBK Date: Mon, 30 Oct 2023 13:18:58 +0200 Subject: [PATCH 02/38] Modify displayed text for QFleDialog in save_all_subplots() method --- src/asammdf/gui/widgets/mdi_area.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asammdf/gui/widgets/mdi_area.py b/src/asammdf/gui/widgets/mdi_area.py index 9acb99e4b..0e628baec 100644 --- a/src/asammdf/gui/widgets/mdi_area.py +++ b/src/asammdf/gui/widgets/mdi_area.py @@ -3872,7 +3872,7 @@ def remove_region(self, widget): def save_all_subplots(self): file_name, _ = QtWidgets.QFileDialog.getSaveFileName( - self, "Select output measurement file", "", "MDF version 4 files (*.mf4)" + self, "Save as measurement file", "", "MDF version 4 files (*.mf4)" ) if file_name: From 65e2842c0ee03eaae54281ea45985c5067ed9422 Mon Sep 17 00:00:00 2001 From: FBK Date: Mon, 30 Oct 2023 15:22:41 +0200 Subject: [PATCH 03/38] Select output measurement file -> Save as measurement file --- src/asammdf/gui/widgets/file.py | 4 ++-- src/asammdf/gui/widgets/numeric.py | 2 +- src/asammdf/gui/widgets/plot.py | 2 +- src/asammdf/gui/widgets/tabular_base.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/asammdf/gui/widgets/file.py b/src/asammdf/gui/widgets/file.py index bc778ea93..c9e44e15b 100644 --- a/src/asammdf/gui/widgets/file.py +++ b/src/asammdf/gui/widgets/file.py @@ -1767,7 +1767,7 @@ def extract_bus_logging(self, event): file_name, _ = QtWidgets.QFileDialog.getSaveFileName( self, - "Select output measurement file", + "Save as measurement file", "", f"{filter};;All files (*.*)", filter, @@ -2676,7 +2676,7 @@ def apply_processing(self, event): file_name, _ = QtWidgets.QFileDialog.getSaveFileName( self, - "Select output measurement file", + "Save as measurement file", "", f"{filter};;All files (*.*)", default, diff --git a/src/asammdf/gui/widgets/numeric.py b/src/asammdf/gui/widgets/numeric.py index c325ba655..e44d920fc 100644 --- a/src/asammdf/gui/widgets/numeric.py +++ b/src/asammdf/gui/widgets/numeric.py @@ -1602,7 +1602,7 @@ def keyPressEvent(self, event): ): file_name, _ = QtWidgets.QFileDialog.getSaveFileName( self, - "Select output measurement file", + "Save as measurement file", "", "MDF version 4 files (*.mf4)", ) diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index ce19145ae..7523c0110 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -4619,7 +4619,7 @@ def keyPressEvent(self, event): elif key == QtCore.Qt.Key.Key_S and modifier == QtCore.Qt.KeyboardModifier.ControlModifier: file_name, _ = QtWidgets.QFileDialog.getSaveFileName( self, - "Select output measurement file", + "Save as measurement file", "", "MDF version 4 files (*.mf4 *.mf4z)", ) diff --git a/src/asammdf/gui/widgets/tabular_base.py b/src/asammdf/gui/widgets/tabular_base.py index 05e870fe2..4a249b8cf 100644 --- a/src/asammdf/gui/widgets/tabular_base.py +++ b/src/asammdf/gui/widgets/tabular_base.py @@ -1696,7 +1696,7 @@ def keyPressEvent(self, event): elif key == QtCore.Qt.Key.Key_S and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: file_name, _ = QtWidgets.QFileDialog.getSaveFileName( self, - "Select output measurement file", + "Save as measurement file", "", "MDF version 4 files (*.mf4)", ) From e615c14e8ee6522261be8cb07ef6cdbbfa8d8575 Mon Sep 17 00:00:00 2001 From: Mattias Josefsson Date: Thu, 2 Nov 2023 18:21:37 +0100 Subject: [PATCH 04/38] Update signal.py unnecessary print statement removed --- src/asammdf/signal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/asammdf/signal.py b/src/asammdf/signal.py index 32fb1167b..da8da53c2 100644 --- a/src/asammdf/signal.py +++ b/src/asammdf/signal.py @@ -224,7 +224,6 @@ def plot(self, validate: bool = True, index_only: bool = False) -> None: return except: - print(format_exc()) try: import matplotlib.pyplot as plt from matplotlib.widgets import Slider From b217a8a122a103bfcc89638b1d69f879c7e889d4 Mon Sep 17 00:00:00 2001 From: FBK Date: Tue, 12 Dec 2023 15:56:06 +0200 Subject: [PATCH 05/38] Fix for fullScreen bug --- src/asammdf/gui/widgets/main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/asammdf/gui/widgets/main.py b/src/asammdf/gui/widgets/main.py index a650c193c..0dee4428a 100644 --- a/src/asammdf/gui/widgets/main.py +++ b/src/asammdf/gui/widgets/main.py @@ -1259,6 +1259,10 @@ def closeEvent(self, event): count = self.files.count() for i in range(count): self.files.widget(i).close() + if self.fullscreen: + widget, index = self.fullscreen + widget.close() + widget.deleteLater() event.accept() def dragEnterEvent(self, e): From 1b3f59441d5ca23ab0e09fe5acc7662c230c7ad8 Mon Sep 17 00:00:00 2001 From: FBK Date: Thu, 14 Dec 2023 17:09:01 +0200 Subject: [PATCH 06/38] Fix for Stats test error. Clear buffer for info (stats) --- src/asammdf/gui/widgets/channel_stats.py | 1 + src/asammdf/gui/widgets/plot.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/asammdf/gui/widgets/channel_stats.py b/src/asammdf/gui/widgets/channel_stats.py index e9d5cb6cf..f1d9dd91d 100644 --- a/src/asammdf/gui/widgets/channel_stats.py +++ b/src/asammdf/gui/widgets/channel_stats.py @@ -57,6 +57,7 @@ def __init__(self, xunit="s", precision=6, *args, **kwargs): def set_stats(self, stats): if not stats: + self.clear() return self._stats = deepcopy(stats) diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index 7f324902b..fa93d31f5 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -2305,6 +2305,7 @@ def channel_selection_reduced(self, deleted): if not count: self.info_uuid = None + self.info.set_stats(None) self.selected_channel_value.setText("") self.close_request.emit() From be2c5180fea05e19991f939d02d4013907d6076a Mon Sep 17 00:00:00 2001 From: FBK Date: Thu, 14 Dec 2023 17:25:20 +0200 Subject: [PATCH 07/38] Comment self.range_group --- src/asammdf/gui/widgets/channel_stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asammdf/gui/widgets/channel_stats.py b/src/asammdf/gui/widgets/channel_stats.py index f1d9dd91d..41b1a454e 100644 --- a/src/asammdf/gui/widgets/channel_stats.py +++ b/src/asammdf/gui/widgets/channel_stats.py @@ -114,7 +114,7 @@ def clear(self): for k, group in enumerate( ( self.cursor_group, - self.range_group, + # self.range_group, self.visible_group, self.overall_group, ) From 9c986f7730418d57d7c973c36047859e15232fea Mon Sep 17 00:00:00 2001 From: FBK Date: Thu, 14 Dec 2023 17:26:31 +0200 Subject: [PATCH 08/38] self.range_group -> self.region_group --- src/asammdf/gui/widgets/channel_stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asammdf/gui/widgets/channel_stats.py b/src/asammdf/gui/widgets/channel_stats.py index 41b1a454e..a22481db1 100644 --- a/src/asammdf/gui/widgets/channel_stats.py +++ b/src/asammdf/gui/widgets/channel_stats.py @@ -114,7 +114,7 @@ def clear(self): for k, group in enumerate( ( self.cursor_group, - # self.range_group, + self.region_group, self.visible_group, self.overall_group, ) From 0d087cbc49aba145dc2b3b2e32ea4541ac0170b1 Mon Sep 17 00:00:00 2001 From: Mattias Josefsson Date: Fri, 15 Dec 2023 10:20:19 +0100 Subject: [PATCH 09/38] black and ruff style checks --- .../dialogs/test_FunctionsManagerDialog.py | 4 +- test/test_mdf.py | 68 +++++++------ test/utils.py | 97 +++++++++---------- 3 files changed, 83 insertions(+), 86 deletions(-) diff --git a/test/asammdf/gui/dialogs/test_FunctionsManagerDialog.py b/test/asammdf/gui/dialogs/test_FunctionsManagerDialog.py index e7b80f9ae..3e6bd7d1c 100644 --- a/test/asammdf/gui/dialogs/test_FunctionsManagerDialog.py +++ b/test/asammdf/gui/dialogs/test_FunctionsManagerDialog.py @@ -220,7 +220,7 @@ def test_PushButton_SaveDefinitions(self): # Evaluate self.assertTrue(saved_file.exists()) - with open(saved_file, "r") as fpr: + with open(saved_file) as fpr: content = json.load(fpr) self.assertDictEqual(content, {"Function1": "def Function1(t=0):\n return 0"}) @@ -328,7 +328,7 @@ def test_PushButton_StoreFunctionChanges_0(self): # Evaluate self.assertTrue(saved_file.exists()) - with open(saved_file, "r") as fpr: + with open(saved_file) as fpr: content = json.load(fpr) self.assertIn(maximum.__name__, content) self.assertIn(content["maximum"], source) diff --git a/test/test_mdf.py b/test/test_mdf.py index 0080b639e..7844552d2 100644 --- a/test/test_mdf.py +++ b/test/test_mdf.py @@ -105,9 +105,7 @@ def test_read(self): equal = False elif i == 4: for j in range(1, 20): - target = np.array( - ["Channel {} sample {}".format(j, k).encode("ascii") for k in range(cycles)] - ) + target = np.array([f"Channel {j} sample {k}".encode("ascii") for k in range(cycles)]) vals = mdf.get(group=i, index=j + 1, samples_only=True)[0] cond = np.array_equal(vals, target) if not cond: @@ -158,13 +156,13 @@ def test_read_arrays(self): for j in range(1, 20): types = [ - ("Channel_{}".format(j), "(2, 3)= "4.00" else v3c.CONVERSION_TYPE_FORMULA, - "formula": "{} * sin(X)".format(i), + "formula": f"{i} * sin(X)", } sig = Signal( np.arange(cycles, dtype=np.int32) / 100.0, t, - name="Channel_{}".format(i), - unit="unit_{}".format(i), + name=f"Channel_{i}", + unit=f"unit_{i}", conversion=cls(**conversion), - comment="Sinus channel {} with algebraic conversion".format(i), + comment=f"Sinus channel {i} with algebraic conversion", raw=True, ) sigs.append(sig) @@ -107,10 +106,10 @@ def generate_test_file(tmpdir, version="4.10"): sig = Signal( np.ones(cycles, dtype=np.int64), t, - name="Channel_{}".format(i), - unit="unit_{}".format(i), + name=f"Channel_{i}", + unit=f"unit_{i}", conversion=cls(**conversion), - comment="Channel {} with rational conversion".format(i), + comment=f"Channel {i} with rational conversion", raw=True, ) sigs.append(sig) @@ -120,13 +119,13 @@ def generate_test_file(tmpdir, version="4.10"): sigs = [] encoding = "latin-1" if version < "4.00" else "utf-8" for i in range(channels_count): - sig = ["Channel {} sample {}".format(i, j).encode(encoding) for j in range(cycles)] + sig = [f"Channel {i} sample {j}".encode(encoding) for j in range(cycles)] sig = Signal( np.array(sig), t, - name="Channel_{}".format(i), - unit="unit_{}".format(i), - comment="String channel {}".format(i), + name=f"Channel_{i}", + unit=f"unit_{i}", + comment=f"String channel {i}", raw=True, encoding=encoding, ) @@ -140,9 +139,9 @@ def generate_test_file(tmpdir, version="4.10"): sig = Signal( ones * i, t, - name="Channel_{}".format(i), - unit="unit_{}".format(i), - comment="Byte array channel {}".format(i), + name=f"Channel_{i}", + unit=f"unit_{i}", + comment=f"Byte array channel {i}", raw=True, ) sigs.append(sig) @@ -153,24 +152,24 @@ def generate_test_file(tmpdir, version="4.10"): ones = np.ones(cycles, dtype=np.uint64) conversion = { "raw": np.arange(255, dtype=np.float64), - "phys": np.array(["Value {}".format(i).encode("ascii") for i in range(255)]), + "phys": np.array([f"Value {i}".encode("ascii") for i in range(255)]), "conversion_type": v4c.CONVERSION_TYPE_TABX if version >= "4.00" else v3c.CONVERSION_TYPE_TABX, "links_nr": 260, "ref_param_nr": 255, } for i in range(255): - conversion["val_{}".format(i)] = conversion["param_val_{}".format(i)] = conversion["raw"][i] - conversion["text_{}".format(i)] = conversion["phys"][i] - conversion["text_{}".format(255)] = "Default" + conversion[f"val_{i}"] = conversion[f"param_val_{i}"] = conversion["raw"][i] + conversion[f"text_{i}"] = conversion["phys"][i] + conversion[f"text_{255}"] = "Default" for i in range(channels_count): sig = Signal( ones * i, t, - name="Channel_{}".format(i), - unit="unit_{}".format(i), - comment="Value to text channel {}".format(i), + name=f"Channel_{i}", + unit=f"unit_{i}", + comment=f"Value to text channel {i}", conversion=cls(**conversion), raw=True, ) @@ -201,18 +200,18 @@ def generate_arrays_test_file(tmpdir): ] types = [ - ("Channel_{}".format(i), "(2, 3) Date: Fri, 15 Dec 2023 12:40:00 +0200 Subject: [PATCH 10/38] active all group channels in focused mode when the user clicks on the group --- src/asammdf/gui/widgets/plot.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index 7f324902b..a11fa7450 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -2181,12 +2181,21 @@ def channel_item_to_config(self, item): return channel def channel_selection_changed(self, update=False): + def set_focused(item): + if item.type() == item.Channel: + item.signal.enable = True + + elif item.type() == item.Group: + for i in range(item.childCount()): + set_focused(item.child(i)) + if self.focused_mode: for signal in self.plot.signals: signal.enable = False + for item in self.channel_selection.selectedItems(): - if item.type() == item.Channel: - item.signal.enable = True + set_focused(item) + self.plot.update() else: if update: From 13a23c16a3fbc6c85d023b2f07d94ced09506ad4 Mon Sep 17 00:00:00 2001 From: Mattias Josefsson Date: Sat, 16 Dec 2023 09:20:01 +0100 Subject: [PATCH 11/38] style, removed unused import --- src/asammdf/signal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/asammdf/signal.py b/src/asammdf/signal.py index da8da53c2..bb761c89e 100644 --- a/src/asammdf/signal.py +++ b/src/asammdf/signal.py @@ -5,7 +5,6 @@ from collections.abc import Iterator import logging from textwrap import fill -from traceback import format_exc from typing import Any import numpy as np From 5035a6b71786edcde92856eb6ba1ed2010f9c0f2 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Mon, 18 Dec 2023 10:11:13 +0200 Subject: [PATCH 12/38] painter error --- src/asammdf/blocks/cutils.c | 46 +++++++++++++++++++++++++++++++++ src/asammdf/gui/plot.py | 1 - src/asammdf/gui/widgets/plot.py | 43 +++++++++++++++++++++++++----- src/asammdf/version.py | 2 +- 4 files changed, 83 insertions(+), 9 deletions(-) diff --git a/src/asammdf/blocks/cutils.c b/src/asammdf/blocks/cutils.c index a0c08f3e0..0023223e4 100644 --- a/src/asammdf/blocks/cutils.c +++ b/src/asammdf/blocks/cutils.c @@ -6,6 +6,7 @@ #include "numpy/ndarrayobject.h" #include #include +#include #include #define PY_PRINTF(o) \ @@ -1251,6 +1252,49 @@ static PyObject* data_block_from_arrays(PyObject* self, PyObject* args) } } +static PyObject* get_idx_with_edges(PyObject* self, PyObject* args) +{ + int i = 0; + PyObject *idx; + PyArrayObject *result; + PyArray_Descr *descr; + + uint8_t *out_array, * idx_array, previous=1, current=0; + + + if (!PyArg_ParseTuple(args, "O", &idx)) + { + return 0; + } + else + { + npy_intp dims[1], count; + count = PyArray_SIZE(idx); + dims[0] = count; + descr = PyArray_DescrFromType(NPY_BOOL); + + result = (PyArrayObject *) PyArray_Zeros(1, dims, descr, 0); + + idx_array = (uint8_t *)PyArray_GETPTR1(idx, 0); + out_array = (uint8_t *)PyArray_GETPTR1(result, 0); + + for (i = 0; i < count; i++, idx_array++, out_array++) + { + current = *idx_array; + if (current) { + if (current != previous) *(out_array-1) = 1; + *out_array = 1; + } + else { + if (current != previous) *(out_array-1) = 0; + } + previous = current; + } + } + + return (PyObject *) result; +} + // Our Module's Function Definition struct // We require this `NULL` to signal the end of our method @@ -1265,6 +1309,8 @@ static PyMethodDef myMethods[] = { "positions", positions, METH_VARARGS, "positions" }, { "get_channel_raw_bytes", get_channel_raw_bytes, METH_VARARGS, "get_channel_raw_bytes" }, { "data_block_from_arrays", data_block_from_arrays, METH_VARARGS, "data_block_from_arrays" }, + { "get_idx_with_edges", get_idx_with_edges, METH_VARARGS, "get_idx_with_edges" }, + { NULL, NULL, 0, NULL } }; diff --git a/src/asammdf/gui/plot.py b/src/asammdf/gui/plot.py index 0b498da8b..bb24065dc 100644 --- a/src/asammdf/gui/plot.py +++ b/src/asammdf/gui/plot.py @@ -6,7 +6,6 @@ try: os.environ["QT_API"] = "pyside6" os.environ["PYQTGRAPH_QT_LIB"] = "PySide6" - os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "0" from PySide6 import QtWidgets diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index a11fa7450..204c5416b 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -24,10 +24,7 @@ from ..utils import FONT_SIZE, value_as_str from .viewbox import ViewBoxWithCursor -try: - from ...blocks.cutils import positions -except: - pass +from ...blocks.cutils import positions, get_idx_with_edges @lru_cache(maxsize=1024) @@ -4254,6 +4251,7 @@ def edit_computation(self, item): self.edit_channel_request.emit(computed_channel, item) def generatePath(self, x, y, sig=None): + print(["GENERATE", x, y, sig]) if sig is None or sig.path is None: if x is None or len(x) == 0 or y is None or len(y) == 0: path = QtGui.QPainterPath() @@ -4264,6 +4262,8 @@ def generatePath(self, x, y, sig=None): else: path = sig.path + print(path, id(path)) + return path def get_axis(self, index): @@ -5174,6 +5174,9 @@ def paintEvent(self, ev): ): continue + print("==========\n" * 2) + print(sig.name) + y = sig.plot_samples x = sig.plot_timestamps @@ -5213,6 +5216,7 @@ def paintEvent(self, ev): if ranges: for range_info in ranges: + print(range_info) val = range_info["value1"] if val is not None and isinstance(val, float): op = range_info["op1"] @@ -5260,20 +5264,44 @@ def paintEvent(self, ev): if not np.any(idx): continue + idx_with_edges = get_idx_with_edges(idx) + + print(idx, idx_with_edges, sep="\n") + y = sig.plot_samples.astype("f8") - y[~idx] = np.inf + y[~idx_with_edges] = np.inf x = sig.plot_timestamps + print(len(x), len(y)) + x, y = self.scale_curve_to_pixmap(x, y, y_range=sig.y_range, x_start=x_start, delta=delta) + print(x.tolist(), y.tolist()) + color = range_info["font_color"] pen = fn.mkPen(color.name()) pen.setWidth(pen_width) paint.setPen(pen) - paint.drawPath(self.generatePath(x, y)) + print("gen", x, y, sep="\n") + pp = self.generatePath(x, y) + print(pp) + print("draw") + pp.clear() + del pp + # paint.drawPath(pp) + print("dots") + + if with_dots and 0: + y = sig.plot_samples.astype("f8") + y[~idx] = np.inf + + x = sig.plot_timestamps + + x, y = self.scale_curve_to_pixmap( + x, y, y_range=sig.y_range, x_start=x_start, delta=delta + ) - if with_dots: paint.setRenderHints(paint.RenderHint.Antialiasing, True) pen.setWidth(dots_with) pen.setCapStyle(cap_style) @@ -5289,6 +5317,7 @@ def paintEvent(self, ev): arr[:, 1] = y paint.drawPoints(poly) paint.setRenderHints(paint.RenderHint.Antialiasing, False) + print("done\n") paint.end() paint = QtGui.QPainter() diff --git a/src/asammdf/version.py b/src/asammdf/version.py index fe4adae62..e360c79da 100644 --- a/src/asammdf/version.py +++ b/src/asammdf/version.py @@ -1,3 +1,3 @@ """ asammdf version module """ -__version__ = "7.4.0.dev9" +__version__ = "7.4.0.dev10" From 381be47e2f572061114cc4f046565bfc592f7ace Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Mon, 18 Dec 2023 12:51:41 +0200 Subject: [PATCH 13/38] color the edges as well when the plot signal has ranges --- src/asammdf/blocks/cutils.c | 134 ++++++++++++++++---------------- src/asammdf/gui/widgets/plot.py | 58 +++++++------- 2 files changed, 99 insertions(+), 93 deletions(-) diff --git a/src/asammdf/blocks/cutils.c b/src/asammdf/blocks/cutils.c index 0023223e4..aad6cba12 100644 --- a/src/asammdf/blocks/cutils.c +++ b/src/asammdf/blocks/cutils.c @@ -253,6 +253,8 @@ static PyObject* extract(PyObject* self, PyObject* args) descr->elsize = max; vals = (PyArrayObject *) PyArray_Zeros(1, dims, descr, 0); + + Py_XDECREF(descr); if (offsets == Py_None) { @@ -273,9 +275,9 @@ static PyObject* extract(PyObject* self, PyObject* args) size = calc_size(&buf[offset]); memcpy(addr2, &buf[offset+4], size); } + Py_XDECREF(offsets_list); } } - Py_XDECREF(offsets_list); } return (PyObject *) vals; @@ -353,8 +355,7 @@ static PyObject* get_vlsd_max_sample_size(PyObject* self, PyObject* args) { int i = 0; Py_ssize_t count = 0; - PyObject* data, * offsets, * result; - npy_intp dim[1]; + PyObject* data, * offsets; unsigned long long max_size = 0; unsigned long vlsd_size = 0; char* inptr=NULL, *data_end=NULL, *current_position=NULL; @@ -368,7 +369,7 @@ static PyObject* get_vlsd_max_sample_size(PyObject* self, PyObject* args) } else { - offsets_array = (unsigned long long*)PyArray_GETPTR1(offsets, 0); + offsets_array = (unsigned long long*)PyArray_GETPTR1((PyArrayObject *)offsets, 0); inptr = PyBytes_AsString(data); data_end = inptr + PyBytes_GET_SIZE(data); @@ -395,15 +396,15 @@ void positions_char(PyObject* samples, PyObject* timestamps, PyObject* plot_samp long* outdata; int pos_min = 0, pos_max = 0; - indata = (char*)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (char*)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); char * ps; double tmin, tmax, * ts, *pt; - ps = (char*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (char*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index=count-1; for (int i = 0; i < (int)count; i++) { @@ -465,15 +466,15 @@ void positions_short(PyObject* samples, PyObject* timestamps, PyObject* plot_sam long* outdata; int pos_min = 0, pos_max = 0; - indata = (short*)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (short*)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); short * ps; double tmin, tmax, * ts, *pt; - ps = (short*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (short*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index = count - 1; for (int i = 0; i < (int)count; i++) { @@ -535,15 +536,15 @@ void positions_long(PyObject* samples, PyObject* timestamps, PyObject* plot_samp long* outdata; int pos_min = 0, pos_max = 0; - indata = (long*)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (long*)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); long * ps; double tmin, tmax, * ts, *pt; - ps = (long*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (long*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index = count - 1; for (int i = 0; i < (int)count; i++) { @@ -605,15 +606,15 @@ void positions_long_long(PyObject* samples, PyObject* timestamps, PyObject* plot long* outdata; int pos_min = 0, pos_max = 0; - indata = (long long*)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (long long*)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); long long * ps; double tmin, tmax, * ts, * pt; - ps = (long long*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (long long*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index = count - 1; for (int i = 0; i < (int)count; i++) { @@ -675,15 +676,15 @@ void positions_unsigned_char(PyObject* samples, PyObject* timestamps, PyObject* long* outdata; int pos_min = 0, pos_max = 0; - indata = (unsigned char*)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (unsigned char*)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); unsigned char* ps; double tmin, tmax, * ts, * pt; - ps = (unsigned char*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (unsigned char*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index = count - 1; for (int i = 0; i < (int)count; i++) { @@ -745,15 +746,15 @@ void positions_unsigned_short(PyObject* samples, PyObject* timestamps, PyObject* long* outdata; int pos_min = 0, pos_max = 0; - indata = (unsigned short *)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (unsigned short *)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); unsigned short* ps; double tmin, tmax, * ts, * pt; - ps = (unsigned short*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (unsigned short*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index = count - 1; for (int i = 0; i < (int)count; i++) { @@ -815,15 +816,15 @@ void positions_unsigned_long(PyObject* samples, PyObject* timestamps, PyObject* long* outdata; int pos_min = 0, pos_max = 0; - indata = (unsigned long*)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (unsigned long*)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); unsigned long* ps; double tmin, tmax, * ts, * pt; - ps = (unsigned long*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (unsigned long*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index = count - 1; for (int i = 0; i < (int)count; i++) { @@ -885,15 +886,15 @@ void positions_unsigned_long_long(PyObject* samples, PyObject* timestamps, PyObj long* outdata; int pos_min = 0, pos_max = 0; - indata = (unsigned long long *)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (unsigned long long *)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); unsigned long long* ps; double tmin, tmax, * ts, * pt; - ps = (unsigned long long*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (unsigned long long*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index = count - 1; for (int i = 0; i < (int)count; i++) { @@ -955,15 +956,15 @@ void positions_float(PyObject* samples, PyObject* timestamps, PyObject* plot_sam long* outdata= NULL; int pos_min = 0, pos_max = 0; - indata = (float*)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (float*)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); float* ps; double tmin, tmax, * ts, * pt; - ps = (float*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (float*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index = count - 1; for (int i = 0; i < (int)count; i++) { @@ -1021,19 +1022,19 @@ void positions_float(PyObject* samples, PyObject* timestamps, PyObject* plot_sam void positions_double(PyObject* samples, PyObject* timestamps, PyObject* plot_samples, PyObject* plot_timestamps, PyObject* result, long step, long count, long last) { - double min, max, val, * indata=NULL; + double min, max, * indata=NULL; long* outdata = NULL; int pos_min = 0, pos_max = 0; - indata = (double*)PyArray_GETPTR1(samples, 0); - outdata = (long*)PyArray_GETPTR1(result, 0); + indata = (double*)PyArray_GETPTR1((PyArrayObject *)samples, 0); + outdata = (long*)PyArray_GETPTR1((PyArrayObject *)result, 0); double* ps = NULL; double tmin, tmax, * ts = NULL, * pt = NULL; - ps = (double*)PyArray_GETPTR1(plot_samples, 0); - pt = (double*)PyArray_GETPTR1(plot_timestamps, 0); - ts = (double*)PyArray_GETPTR1(timestamps, 0); + ps = (double*)PyArray_GETPTR1((PyArrayObject *)plot_samples, 0); + pt = (double*)PyArray_GETPTR1((PyArrayObject *)plot_timestamps, 0); + ts = (double*)PyArray_GETPTR1((PyArrayObject *)timestamps, 0); int current_pos = 0, stop_index = count - 1; for (int i = 0; i < (int)count; i++) { @@ -1255,9 +1256,8 @@ static PyObject* data_block_from_arrays(PyObject* self, PyObject* args) static PyObject* get_idx_with_edges(PyObject* self, PyObject* args) { int i = 0; - PyObject *idx; - PyArrayObject *result; - PyArray_Descr *descr; + PyObject *idx=NULL; + PyArrayObject *result=NULL; uint8_t *out_array, * idx_array, previous=1, current=0; @@ -1269,24 +1269,26 @@ static PyObject* get_idx_with_edges(PyObject* self, PyObject* args) else { npy_intp dims[1], count; - count = PyArray_SIZE(idx); - dims[0] = count; - descr = PyArray_DescrFromType(NPY_BOOL); - - result = (PyArrayObject *) PyArray_Zeros(1, dims, descr, 0); + count = PyArray_SIZE((PyArrayObject *)idx); + dims[0] = count; + result = PyArray_ZEROS(1, dims, NPY_BOOL, 0); - idx_array = (uint8_t *)PyArray_GETPTR1(idx, 0); + idx_array = (uint8_t *)PyArray_GETPTR1((PyArrayObject *)idx, 0); out_array = (uint8_t *)PyArray_GETPTR1(result, 0); for (i = 0; i < count; i++, idx_array++, out_array++) { current = *idx_array; if (current) { - if (current != previous) *(out_array-1) = 1; + if (current != previous) { + *(out_array-1) = 1; + } *out_array = 1; } else { - if (current != previous) *(out_array-1) = 0; + if (current != previous && i) { + *(out_array-1) = 0; + } } previous = current; } diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index 204c5416b..54c408b6a 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -19,13 +19,12 @@ from ... import tool as Tool from ...blocks.conversion_utils import from_dict, to_dict +from ...blocks.cutils import get_idx_with_edges, positions from ...blocks.utils import target_byte_order from ..dialogs.messagebox import MessageBox from ..utils import FONT_SIZE, value_as_str from .viewbox import ViewBoxWithCursor -from ...blocks.cutils import positions, get_idx_with_edges - @lru_cache(maxsize=1024) def polygon_and_ndarray(size): @@ -4251,7 +4250,6 @@ def edit_computation(self, item): self.edit_channel_request.emit(computed_channel, item) def generatePath(self, x, y, sig=None): - print(["GENERATE", x, y, sig]) if sig is None or sig.path is None: if x is None or len(x) == 0 or y is None or len(y) == 0: path = QtGui.QPainterPath() @@ -4262,8 +4260,6 @@ def generatePath(self, x, y, sig=None): else: path = sig.path - print(path, id(path)) - return path def get_axis(self, index): @@ -5165,6 +5161,10 @@ def paintEvent(self, ev): else: cap_style = QtCore.Qt.PenCapStyle.RoundCap + curve = self._curve + step_mode = self.line_interconnect + default_connect = curve.opts["connect"] + for i, sig in enumerate(self.signals): if ( not sig.enable @@ -5174,9 +5174,6 @@ def paintEvent(self, ev): ): continue - print("==========\n" * 2) - print(sig.name) - y = sig.plot_samples x = sig.plot_timestamps @@ -5216,7 +5213,6 @@ def paintEvent(self, ev): if ranges: for range_info in ranges: - print(range_info) val = range_info["value1"] if val is not None and isinstance(val, float): op = range_info["op1"] @@ -5264,35 +5260,43 @@ def paintEvent(self, ev): if not np.any(idx): continue - idx_with_edges = get_idx_with_edges(idx) + if step_mode == "right": + idx_with_edges = get_idx_with_edges(idx) + _connect = "finite" + y = sig.plot_samples.astype("f8") + y[~idx_with_edges] = np.inf + x = sig.plot_timestamps - print(idx, idx_with_edges, sep="\n") + elif step_mode == "": + last = idx[-1] + idx_with_edges = np.roll(idx, -1) + idx_with_edges[-1] = last + _connect = idx_with_edges.view("u1") + y = sig.plot_samples.astype("f8") + x = sig.plot_timestamps - y = sig.plot_samples.astype("f8") - y[~idx_with_edges] = np.inf - x = sig.plot_timestamps + if step_mode == "left": + idx_with_edges = np.repeat(get_idx_with_edges(idx), 2) + idx_with_edges = np.insert(idx_with_edges, 0, idx_with_edges[0]) + _connect = idx_with_edges[:-1].view("u1") + y = sig.plot_samples.astype("f8") + x = sig.plot_timestamps - print(len(x), len(y)) + curve.opts["connect"] = _connect x, y = self.scale_curve_to_pixmap(x, y, y_range=sig.y_range, x_start=x_start, delta=delta) - print(x.tolist(), y.tolist()) - color = range_info["font_color"] pen = fn.mkPen(color.name()) pen.setWidth(pen_width) paint.setPen(pen) - print("gen", x, y, sep="\n") pp = self.generatePath(x, y) - print(pp) - print("draw") - pp.clear() - del pp - # paint.drawPath(pp) - print("dots") - - if with_dots and 0: + paint.drawPath(pp) + + curve.opts["connect"] = default_connect + + if with_dots: y = sig.plot_samples.astype("f8") y[~idx] = np.inf @@ -5317,7 +5321,7 @@ def paintEvent(self, ev): arr[:, 1] = y paint.drawPoints(poly) paint.setRenderHints(paint.RenderHint.Antialiasing, False) - print("done\n") + paint.end() paint = QtGui.QPainter() From bd2051ce88a6b6b81419b26b67a7d9c7d3b732b9 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Mon, 18 Dec 2023 13:04:19 +0200 Subject: [PATCH 14/38] segfault --- src/asammdf/blocks/cutils.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/asammdf/blocks/cutils.c b/src/asammdf/blocks/cutils.c index aad6cba12..9e1da4022 100644 --- a/src/asammdf/blocks/cutils.c +++ b/src/asammdf/blocks/cutils.c @@ -253,8 +253,6 @@ static PyObject* extract(PyObject* self, PyObject* args) descr->elsize = max; vals = (PyArrayObject *) PyArray_Zeros(1, dims, descr, 0); - - Py_XDECREF(descr); if (offsets == Py_None) { From 9907528097caa19c7e5af1fe4de32994b9a5f2b6 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Mon, 18 Dec 2023 13:17:01 +0200 Subject: [PATCH 15/38] cannot mix yield and return --- src/asammdf/mdf.py | 433 ++++++++++++++++++++++----------------------- 1 file changed, 213 insertions(+), 220 deletions(-) diff --git a/src/asammdf/mdf.py b/src/asammdf/mdf.py index 2010a6d86..a8956a4f7 100644 --- a/src/asammdf/mdf.py +++ b/src/asammdf/mdf.py @@ -3929,291 +3929,284 @@ def iter_to_dataframe( yield df mdf.close() - return - - # else: channels is None + else: + # channels is None - df = {} - self._set_temporary_master(None) + df = {} + self._set_temporary_master(None) - masters = {index: self.get_master(index) for index in self.virtual_groups} + masters = {index: self.get_master(index) for index in self.virtual_groups} - if raster is not None: - try: - raster = float(raster) - assert raster > 0 - except (TypeError, ValueError): - if isinstance(raster, str): - raster = self.get(raster, raw=True, ignore_invalidation_bits=True).timestamps + if raster is not None: + try: + raster = float(raster) + assert raster > 0 + except (TypeError, ValueError): + if isinstance(raster, str): + raster = self.get(raster, raw=True, ignore_invalidation_bits=True).timestamps + else: + raster = np.array(raster) else: - raster = np.array(raster) - else: - raster = master_using_raster(self, raster) - master = raster - else: - if masters: - master = reduce(np.union1d, masters.values()) + raster = master_using_raster(self, raster) + master = raster else: - master = np.array([], dtype="= "4.00" else "latin-1" - signal.raw = False - signal.conversion = None - if signal.samples.dtype.kind == "S": - signal.encoding = "utf-8" if self.version >= "4.00" else "latin-1" + for s_index, sig in enumerate(signals): + sig = sig.validate(copy=False) - for s_index, sig in enumerate(signals): - sig = sig.validate(copy=False) + if len(sig) == 0: + if empty_channels == "zeros": + sig.samples = np.zeros( + len(master) if virtual_group.cycles_nr == 0 else virtual_group.cycles_nr, + dtype=sig.samples.dtype, + ) + sig.timestamps = master if virtual_group.cycles_nr == 0 else group_master - if len(sig) == 0: - if empty_channels == "zeros": - sig.samples = np.zeros( - len(master) if virtual_group.cycles_nr == 0 else virtual_group.cycles_nr, - dtype=sig.samples.dtype, - ) - sig.timestamps = master if virtual_group.cycles_nr == 0 else group_master + signals[s_index] = sig - signals[s_index] = sig + if use_interpolation: + same_master = np.array_equal(master, group_master) - if use_interpolation: - same_master = np.array_equal(master, group_master) + if not same_master and interpolate_outwards_with_nan: + idx = np.argwhere((master >= group_master[0]) & (master <= group_master[-1])).flatten() - if not same_master and interpolate_outwards_with_nan: - idx = np.argwhere((master >= group_master[0]) & (master <= group_master[-1])).flatten() + cycles = len(group_master) - cycles = len(group_master) + signals = [ + signal.interp( + master, + integer_interpolation_mode=self._integer_interpolation, + float_interpolation_mode=self._float_interpolation, + ) + if not same_master or len(signal) != cycles + else signal + for signal in signals + ] - signals = [ - signal.interp( - master, - integer_interpolation_mode=self._integer_interpolation, - float_interpolation_mode=self._float_interpolation, - ) - if not same_master or len(signal) != cycles - else signal - for signal in signals - ] + if not same_master and interpolate_outwards_with_nan: + for sig in signals: + sig.timestamps = sig.timestamps[idx] + sig.samples = sig.samples[idx] - if not same_master and interpolate_outwards_with_nan: - for sig in signals: - sig.timestamps = sig.timestamps[idx] - sig.samples = sig.samples[idx] + group_master = master - group_master = master + signals = [sig for sig in signals if len(sig)] - signals = [sig for sig in signals if len(sig)] + if signals: + diffs = np.diff(group_master, prepend=-np.inf) > 0 - if signals: - diffs = np.diff(group_master, prepend=-np.inf) > 0 + if group_master.dtype.byteorder not in target_byte_order: + group_master = group_master.byteswap().newbyteorder() - if group_master.dtype.byteorder not in target_byte_order: - group_master = group_master.byteswap().newbyteorder() + if np.all(diffs): + index = pd.Index(group_master, tupleize_cols=False) - if np.all(diffs): - index = pd.Index(group_master, tupleize_cols=False) + else: + idx = np.argwhere(diffs).flatten() + group_master = group_master[idx] - else: - idx = np.argwhere(diffs).flatten() - group_master = group_master[idx] + index = pd.Index(group_master, tupleize_cols=False) - index = pd.Index(group_master, tupleize_cols=False) + for sig in signals: + sig.samples = sig.samples[idx] + sig.timestamps = sig.timestamps[idx] - for sig in signals: - sig.samples = sig.samples[idx] - sig.timestamps = sig.timestamps[idx] + size = len(index) + for k, sig in enumerate(signals): + if sig.timestamps.dtype.byteorder not in target_byte_order: + sig.timestamps = sig.timestamps.byteswap().newbyteorder() - size = len(index) - for k, sig in enumerate(signals): - if sig.timestamps.dtype.byteorder not in target_byte_order: - sig.timestamps = sig.timestamps.byteswap().newbyteorder() + sig_index = index if len(sig) == size else pd.Index(sig.timestamps, tupleize_cols=False) - sig_index = index if len(sig) == size else pd.Index(sig.timestamps, tupleize_cols=False) + # byte arrays + if len(sig.samples.shape) > 1: + if use_display_names: + channel_name = list(sig.display_names)[0] if sig.display_names else sig.name + else: + channel_name = sig.name - # byte arrays - if len(sig.samples.shape) > 1: - if use_display_names: - channel_name = list(sig.display_names)[0] if sig.display_names else sig.name - else: - channel_name = sig.name + channel_name = used_names.get_unique_name(channel_name) - channel_name = used_names.get_unique_name(channel_name) + if sig.samples.dtype.byteorder not in target_byte_order: + sig.samples = sig.samples.byteswap().newbyteorder() - if sig.samples.dtype.byteorder not in target_byte_order: - sig.samples = sig.samples.byteswap().newbyteorder() + df[channel_name] = pd.Series( + list(sig.samples), + index=sig_index, + ) - df[channel_name] = pd.Series( - list(sig.samples), - index=sig_index, - ) + # arrays and structures + elif sig.samples.dtype.names: + for name, series in components( + sig.samples, + sig.name, + used_names, + master=sig_index, + only_basenames=only_basenames, + ): + df[name] = series - # arrays and structures - elif sig.samples.dtype.names: - for name, series in components( - sig.samples, - sig.name, - used_names, - master=sig_index, - only_basenames=only_basenames, - ): - df[name] = series - - # scalars - else: - if use_display_names: - channel_name = list(sig.display_names)[0] if sig.display_names else sig.name + # scalars else: - channel_name = sig.name + if use_display_names: + channel_name = list(sig.display_names)[0] if sig.display_names else sig.name + else: + channel_name = sig.name - channel_name = used_names.get_unique_name(channel_name) + channel_name = used_names.get_unique_name(channel_name) - if reduce_memory_usage and sig.samples.dtype.kind in "SU": - unique = np.unique(sig.samples) + if reduce_memory_usage and sig.samples.dtype.kind in "SU": + unique = np.unique(sig.samples) - if sig.samples.dtype.byteorder not in target_byte_order: - sig.samples = sig.samples.byteswap().newbyteorder() + if sig.samples.dtype.byteorder not in target_byte_order: + sig.samples = sig.samples.byteswap().newbyteorder() - if len(sig.samples) / len(unique) >= 2: - df[channel_name] = pd.Series( - sig.samples, - index=sig_index, - dtype="category", - ) + if len(sig.samples) / len(unique) >= 2: + df[channel_name] = pd.Series( + sig.samples, + index=sig_index, + dtype="category", + ) + else: + df[channel_name] = pd.Series( + sig.samples, + index=sig_index, + fastpath=True, + ) else: + if reduce_memory_usage: + sig.samples = downcast(sig.samples) + + if sig.samples.dtype.byteorder not in target_byte_order: + sig.samples = sig.samples.byteswap().newbyteorder() + df[channel_name] = pd.Series( sig.samples, index=sig_index, fastpath=True, ) - else: - if reduce_memory_usage: - sig.samples = downcast(sig.samples) - if sig.samples.dtype.byteorder not in target_byte_order: - sig.samples = sig.samples.byteswap().newbyteorder() + if progress is not None: + if callable(progress): + progress(group_index + 1, groups_nr) + else: + progress.signals.setValue.emit(group_index + 1) - df[channel_name] = pd.Series( - sig.samples, - index=sig_index, - fastpath=True, - ) + strings, nonstrings = {}, {} - if progress is not None: - if callable(progress): - progress(group_index + 1, groups_nr) + for col, series in df.items(): + if series.dtype.kind == "S": + strings[col] = series else: - progress.signals.setValue.emit(group_index + 1) + nonstrings[col] = series - if progress.stop: - return TERMINATED - - strings, nonstrings = {}, {} - - for col, series in df.items(): - if series.dtype.kind == "S": - strings[col] = series - else: - nonstrings[col] = series - - if numeric_1D_only: - nonstrings = {col: series for col, series in nonstrings.items() if series.dtype.kind in "uif"} - strings = {} + if numeric_1D_only: + nonstrings = {col: series for col, series in nonstrings.items() if series.dtype.kind in "uif"} + strings = {} - df = pd.DataFrame(nonstrings, index=master) + df = pd.DataFrame(nonstrings, index=master) - if strings: - df_strings = pd.DataFrame(strings, index=master) - df = pd.concat([df, df_strings], axis=1) + if strings: + df_strings = pd.DataFrame(strings, index=master) + df = pd.concat([df, df_strings], axis=1) - df.index.name = "timestamps" + df.index.name = "timestamps" - if time_as_date: - delta = pd.to_timedelta(df.index, unit="s") + if time_as_date: + delta = pd.to_timedelta(df.index, unit="s") - new_index = self.header.start_time + delta - df.set_index(new_index, inplace=True) - elif time_from_zero and len(master): - df.set_index(df.index - df.index[0], inplace=True) + new_index = self.header.start_time + delta + df.set_index(new_index, inplace=True) + elif time_from_zero and len(master): + df.set_index(df.index - df.index[0], inplace=True) - yield df + yield df def to_dataframe( self, From b438294895434811aedf85d69c8a9e890ad9f248 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Mon, 18 Dec 2023 15:16:57 +0200 Subject: [PATCH 16/38] fix x_range padding --- src/asammdf/gui/widgets/mdi_area.py | 11 ++++++++++- src/asammdf/gui/widgets/plot.py | 3 +-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/asammdf/gui/widgets/mdi_area.py b/src/asammdf/gui/widgets/mdi_area.py index a780191bd..181f3eaa3 100644 --- a/src/asammdf/gui/widgets/mdi_area.py +++ b/src/asammdf/gui/widgets/mdi_area.py @@ -3773,11 +3773,20 @@ def set_x_range(self, widget, x_range): if not self.subplots_link: return + if not isinstance(x_range, (tuple, list)): + return + + if not len(x_range) == 2: + return + + if np.any(np.isnan(x_range)) or not np.all(np.isfinite(x_range)): + return + for mdi in self.mdi_area.subWindowList(): wid = mdi.widget() if wid is not widget and isinstance(wid, Plot): wid._inhibit_x_range_changed_signal = True - wid.plot.viewbox.setXRange(*x_range, update=True) + wid.plot.viewbox.setXRange(*x_range, padding=0, update=True) wid._inhibit_x_range_changed_signal = False def set_region(self, widget, region): diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index 54c408b6a..041b089a6 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -5291,8 +5291,7 @@ def paintEvent(self, ev): pen.setWidth(pen_width) paint.setPen(pen) - pp = self.generatePath(x, y) - paint.drawPath(pp) + paint.drawPath(self.generatePath(x, y)) curve.opts["connect"] = default_connect From 27dbbf16b56bfeff39dbd7baac3b236622f4b022 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Tue, 19 Dec 2023 09:05:08 +0200 Subject: [PATCH 17/38] delete later --- src/asammdf/gui/dialogs/advanced_search.py | 44 +++++++++++----------- src/asammdf/gui/dialogs/define_channel.py | 1 + src/asammdf/gui/ui/search_dialog.py | 2 +- src/asammdf/gui/ui/search_dialog.ui | 2 +- src/asammdf/gui/widgets/file.py | 3 ++ src/asammdf/gui/widgets/main.py | 1 + src/asammdf/gui/widgets/mdi_area.py | 18 ++++++++- src/asammdf/gui/widgets/plot.py | 1 + src/asammdf/gui/widgets/tree.py | 2 +- src/asammdf/gui/widgets/viewbox.py | 1 + 10 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/asammdf/gui/dialogs/advanced_search.py b/src/asammdf/gui/dialogs/advanced_search.py index c07b2628e..705e20255 100644 --- a/src/asammdf/gui/dialogs/advanced_search.py +++ b/src/asammdf/gui/dialogs/advanced_search.py @@ -125,6 +125,9 @@ def __init__( self.pattern.editingFinished.connect(self.update_pattern_matches) self.case_sensitivity_pattern.currentIndexChanged.connect(self.update_pattern_matches) self.pattern_match_type.currentIndexChanged.connect(self.update_pattern_matches) + self.filter_type.currentIndexChanged.connect(self.update_pattern_matches) + self.filter_value.valueChanged.connect(self.update_pattern_matches) + self.raw.stateChanged.connect(self.update_pattern_matches) self.update_pattern_matches() self.showMaximized() @@ -421,35 +424,30 @@ def section_resized(self, index, old_size, new_size): self.matches.setColumnWidth(index, new_size) def update_pattern_matches(self, *args): + from ..widgets.mdi_area import extract_signals_using_pattern + self.pattern_matches.clear() if not self.channels_db: return - pattern = self.pattern.text().strip() - case_sensitive = self.case_sensitivity_pattern.currentText() == "Case sensitive" - match_type = self.pattern_match_type.currentText() - - if match_type == "Wildcard": - wild = f"__{os.urandom(3).hex()}WILDCARD{os.urandom(3).hex()}__" - pattern = pattern.replace("*", wild) - pattern = re.escape(pattern) - pattern = pattern.replace(wild, ".*") - - if case_sensitive: - pattern = re.compile(pattern) - else: - pattern = re.compile(f"(?i){pattern}") - - matches = {} + pattern_info = { + "pattern": self.pattern.text().strip(), + "case_sensitive": self.case_sensitivity_pattern.currentText() == "Case sensitive", + "match_type": self.pattern_match_type.currentText(), + "filter_value": self.filter_value.value(), + "filter_type": self.filter_type.currentText(), + "raw": self.raw.isChecked(), + "integer_format": self.integer_format.currentText(), + } - for name, entries in self.channels_db.items(): - if pattern.fullmatch(name): - for entry in entries: - if entry in matches: - continue - matches[entry] = name + signals = extract_signals_using_pattern( + mdf=self.mdf or None, + pattern_info=pattern_info, + ignore_value2text_conversions=True, + as_names=True, + ) - items = [QtWidgets.QTreeWidgetItem([name]) for entry, name in matches.items()] + items = [QtWidgets.QTreeWidgetItem([name]) for name in signals] self.pattern_matches.addTopLevelItems(items) diff --git a/src/asammdf/gui/dialogs/define_channel.py b/src/asammdf/gui/dialogs/define_channel.py index 9684a93b3..a776aa0ff 100644 --- a/src/asammdf/gui/dialogs/define_channel.py +++ b/src/asammdf/gui/dialogs/define_channel.py @@ -164,6 +164,7 @@ def function_changed(self, *args): for widget in widgets: self.arg_layout.removeWidget(widget) widget.setParent(None) + widget.deleteLater() self.arg_layout.removeItem(self.arg_widgets[-1]) diff --git a/src/asammdf/gui/ui/search_dialog.py b/src/asammdf/gui/ui/search_dialog.py index e61c25ef1..100fbdb5c 100644 --- a/src/asammdf/gui/ui/search_dialog.py +++ b/src/asammdf/gui/ui/search_dialog.py @@ -427,7 +427,7 @@ def retranslateUi(self, SearchDialog): self.label_3.setText(QCoreApplication.translate("SearchDialog", "Filter type", None)) ___qtreewidgetitem2 = self.pattern_matches.headerItem() ___qtreewidgetitem2.setText( - 0, QCoreApplication.translate("SearchDialog", "Channels matching the name pattern", None) + 0, QCoreApplication.translate("SearchDialog", "Channels matching the pattern conditions", None) ) self.tabs.setTabText( self.tabs.indexOf(self.tab_2), QCoreApplication.translate("SearchDialog", "Pattern definition", None) diff --git a/src/asammdf/gui/ui/search_dialog.ui b/src/asammdf/gui/ui/search_dialog.ui index 640564cac..45fe281f3 100644 --- a/src/asammdf/gui/ui/search_dialog.ui +++ b/src/asammdf/gui/ui/search_dialog.ui @@ -548,7 +548,7 @@ - Channels matching the name pattern + Channels matching the pattern conditions diff --git a/src/asammdf/gui/widgets/file.py b/src/asammdf/gui/widgets/file.py index c1d326f35..eda425304 100644 --- a/src/asammdf/gui/widgets/file.py +++ b/src/asammdf/gui/widgets/file.py @@ -1135,6 +1135,7 @@ def load_channel_list(self, event=None, file_name=None, manually=False): self.mdi_area.removeSubWindow(window) widget.setParent(None) widget.close() + widget.deleteLater() window.close() suffix = original_file_name.suffix.lower() @@ -2865,6 +2866,7 @@ def apply_processing_thread(self, file_name, opts, version, needs_filter, channe self.mdi_area.removeSubWindow(window) widget.setParent(None) widget.close() + widget.deleteLater() window.close() result = mdf.save( @@ -3016,6 +3018,7 @@ def embed_display_file(self, event=None): self.mdi_area.removeSubWindow(window) widget.setParent(None) widget.close() + widget.deleteLater() window.close() suffix = original_file_name.suffix.lower() diff --git a/src/asammdf/gui/widgets/main.py b/src/asammdf/gui/widgets/main.py index 08a85e964..deb1788c9 100644 --- a/src/asammdf/gui/widgets/main.py +++ b/src/asammdf/gui/widgets/main.py @@ -1245,6 +1245,7 @@ def close_file(self, index): if widget: widget.close() widget.setParent(None) + widget.deleteLater() if self.files.count(): self.files.setCurrentIndex(0) diff --git a/src/asammdf/gui/widgets/mdi_area.py b/src/asammdf/gui/widgets/mdi_area.py index 181f3eaa3..23ea5825e 100644 --- a/src/asammdf/gui/widgets/mdi_area.py +++ b/src/asammdf/gui/widgets/mdi_area.py @@ -182,7 +182,13 @@ def build_mime_from_config( return mime, descriptions, found, not_found, computed -def extract_signals_using_pattern(mdf, pattern_info, ignore_value2text_conversions, uuid): +def extract_signals_using_pattern(mdf, pattern_info, ignore_value2text_conversions, uuid=None, as_names=False): + if not mdf: + if as_names: + return set() + else: + return dict() + pattern = pattern_info["pattern"] match_type = pattern_info["match_type"] case_sensitive = pattern_info.get("case_sensitive", False) @@ -217,6 +223,9 @@ def extract_signals_using_pattern(mdf, pattern_info, ignore_value2text_conversio print(format_exc()) signals = [] else: + if as_names and filter_type == "Unspecified": + return {match[0] for match in matches} + psignals = mdf.select( matches, ignore_value2text_conversions=ignore_value2text_conversions, @@ -273,7 +282,10 @@ def extract_signals_using_pattern(mdf, pattern_info, ignore_value2text_conversio sig.ranges = [] output_signals[uuid] = sig - return output_signals + if as_names: + return {sig.name for sig in signals} + else: + return output_signals def generate_window_title(mdi, window_name="", title=""): @@ -2657,6 +2669,7 @@ def clear_windows(self): self.mdi_area.removeSubWindow(window) widget.setParent(None) window.close() + widget.deleteLater() widget.close() def delete_functions(self, deleted_functions): @@ -4024,6 +4037,7 @@ def verify_bookmarks(self, bookmarks, plot): self.mdi_area.removeSubWindow(window) widget.setParent(None) widget.close() + widget.deleteLater() window.close() suffix = original_file_name.suffix.lower() diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index 041b089a6..65e5e52e6 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -4620,6 +4620,7 @@ def keyPressEvent(self, event): self.region_lock = None self.region.setParent(None) self.region.hide() + self.region.deleteLater() self.region = None self.range_removed.emit() diff --git a/src/asammdf/gui/widgets/tree.py b/src/asammdf/gui/widgets/tree.py index 45d9f0b50..f574d558d 100644 --- a/src/asammdf/gui/widgets/tree.py +++ b/src/asammdf/gui/widgets/tree.py @@ -578,7 +578,7 @@ def keyPressEvent(self, event): elif key == QtCore.Qt.Key.Key_Insert and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: dlg = AdvancedSearch( - None, + self.plot.mdf if self.plot else None, show_add_window=False, show_apply=True, show_search=False, diff --git a/src/asammdf/gui/widgets/viewbox.py b/src/asammdf/gui/widgets/viewbox.py index 7d899fc5d..69dc68654 100644 --- a/src/asammdf/gui/widgets/viewbox.py +++ b/src/asammdf/gui/widgets/viewbox.py @@ -152,6 +152,7 @@ def __init__(self, plot, *args, **kwargs): super().__init__(*args, **kwargs) self.menu.setParent(None) + self.menu.deleteLater() self.menu = None self.menu = ViewBoxMenu(self) From 640a786fafb8d81c3590de8b19d0f37cc782db42 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Tue, 19 Dec 2023 09:23:09 +0200 Subject: [PATCH 18/38] style --- src/asammdf/gui/widgets/mdi_area.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asammdf/gui/widgets/mdi_area.py b/src/asammdf/gui/widgets/mdi_area.py index 23ea5825e..bff431206 100644 --- a/src/asammdf/gui/widgets/mdi_area.py +++ b/src/asammdf/gui/widgets/mdi_area.py @@ -187,7 +187,7 @@ def extract_signals_using_pattern(mdf, pattern_info, ignore_value2text_conversio if as_names: return set() else: - return dict() + return {} pattern = pattern_info["pattern"] match_type = pattern_info["match_type"] From 70bacf76e4c40b941590319249909937e34265db Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Tue, 19 Dec 2023 09:43:35 +0200 Subject: [PATCH 19/38] copy display properties from group --- src/asammdf/gui/widgets/tree.py | 103 ++++++++++++++++---------------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/src/asammdf/gui/widgets/tree.py b/src/asammdf/gui/widgets/tree.py index f574d558d..0b250ff29 100644 --- a/src/asammdf/gui/widgets/tree.py +++ b/src/asammdf/gui/widgets/tree.py @@ -4,7 +4,6 @@ import json import os import re -from traceback import format_exc import numpy as np from pyqtgraph import functions as fn @@ -774,41 +773,41 @@ def keyPressEvent(self, event): if not selected_items: return - try: - info = json.loads(info) + info = json.loads(info) + if info["type"] == "channel": info["color"] = fn.mkColor(info["color"]) - except: - print(format_exc()) - else: + for item in selected_items: - try: - item.color = info["color"] - item.precision = info["precision"] - item.format = info["format"] - - item.setCheckState( - self.IndividualAxisColumn, - QtCore.Qt.CheckState.Checked if info["individual_axis"] else QtCore.Qt.CheckState.Unchecked, - ) - item.setCheckState( - self.CommonAxisColumn, - QtCore.Qt.CheckState.Checked if info["ylink"] else QtCore.Qt.CheckState.Unchecked, - ) + item.color = info["color"] + item.precision = info["precision"] + item.format = info["format"] - if item.type() == ChannelsTreeItem.Channel: - plot = item.treeWidget().plot.plot - sig, index = plot.signal_by_uuid(item.uuid) - sig.y_range = info["y_range"] + item.setCheckState( + self.IndividualAxisColumn, + QtCore.Qt.CheckState.Checked if info["individual_axis"] else QtCore.Qt.CheckState.Unchecked, + ) + item.setCheckState( + self.CommonAxisColumn, + QtCore.Qt.CheckState.Checked if info["ylink"] else QtCore.Qt.CheckState.Unchecked, + ) - item.set_ranges(info["ranges"]) + if item.type() == ChannelsTreeItem.Channel: + plot = item.treeWidget().plot.plot + sig, index = plot.signal_by_uuid(item.uuid) + sig.y_range = info["y_range"] + + item.set_ranges(info["ranges"]) - if "conversion" in info: - item.set_conversion(from_dict(info["conversion"])) - elif item.type() == ChannelsTreeItem.Group: - item.set_ranges(info["ranges"]) + if "conversion" in info: + item.set_conversion(from_dict(info["conversion"])) - except: - print(format_exc()) + elif item.type() == ChannelsTreeItem.Group: + item.set_ranges(info["ranges"]) + + elif info["type"] == "group": + for item in selected_items: + if item.type() in (ChannelsTreeItem.Channel, ChannelsTreeItem.Group): + item.set_ranges(info["ranges"]) elif modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier and key in ( QtCore.Qt.Key.Key_Up, @@ -1905,30 +1904,34 @@ def _get_color_using_ranges(self, value, pen=False): def get_display_properties(self): if self.type() == ChannelsTreeItem.Group: - if self.childCount(): - return self.child(0).get_display_properties() - return "" - info = { - "color": self.color.name(), - "precision": self.precision, - "ylink": self.checkState(self.CommonAxisColumn) == QtCore.Qt.CheckState.Checked, - "individual_axis": self.checkState(self.IndividualAxisColumn) == QtCore.Qt.CheckState.Checked, - "format": self.format, - "ranges": copy_ranges(self.ranges), - } - - if self.signal.flags & Signal.Flags.user_defined_conversion: - info["conversion"] = to_dict(self.signal.conversion) + info = { + "type": "group", + "ranges": copy_ranges(self.ranges), + } - for range_info in info["ranges"]: - range_info["background_color"] = range_info["background_color"].name() - range_info["font_color"] = range_info["font_color"].name() + elif self.type() == ChannelsTreeItem.Channel: + info = { + "type": "channel", + "color": self.color.name(), + "precision": self.precision, + "ylink": self.checkState(self.CommonAxisColumn) == QtCore.Qt.CheckState.Checked, + "individual_axis": self.checkState(self.IndividualAxisColumn) == QtCore.Qt.CheckState.Checked, + "format": self.format, + "ranges": copy_ranges(self.ranges), + } - plot = self.treeWidget().plot.plot + if self.signal.flags & Signal.Flags.user_defined_conversion: + info["conversion"] = to_dict(self.signal.conversion) - sig, index = plot.signal_by_uuid(self.uuid) + plot = self.treeWidget().plot.plot - info["y_range"] = tuple(float(e) for e in sig.y_range) + sig, index = plot.signal_by_uuid(self.uuid) + + info["y_range"] = tuple(float(e) for e in sig.y_range) + + for range_info in info["ranges"]: + range_info["background_color"] = range_info["background_color"].name() + range_info["font_color"] = range_info["font_color"].name() return json.dumps(info) From 4988573db6bfd23ea1e4d30b9027ae1378a1a3a3 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Tue, 19 Dec 2023 10:04:36 +0200 Subject: [PATCH 20/38] adapt tests --- .../widgets/test_PlotWidget_ContextMenu.py | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py b/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py index 78f526eb2..3c80dbbf3 100644 --- a/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py +++ b/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py @@ -313,8 +313,13 @@ def test_Action_CopyDisplayProperties_Group(self): position = self.plot.channel_selection.visualItemRect(group_channel_a).center() self.context_menu(action_text="Copy display properties [Ctrl+Shift+C]", position=position) clipboard = QtWidgets.QApplication.instance().clipboard().text() - with self.assertRaises(JSONDecodeError): - json.loads(clipboard) + try: + content = json.loads(clipboard) + except JSONDecodeError: + self.fail("Clipboard Content cannot be decoded as JSON content.") + else: + self.assertIsInstance(content, dict) + self.assertTrue(content["type"] == "group") with self.subTest("PopulatedGroup"): # Add Channels to Group @@ -330,6 +335,7 @@ def test_Action_CopyDisplayProperties_Group(self): self.fail("Clipboard Content cannot be decoded as JSON content.") else: self.assertIsInstance(content, dict) + self.assertTrue(content["type"] == "channel") with self.subTest("EmptyGroup_1"): group_channel_a.removeChild(self.plot_channel_a) @@ -337,8 +343,13 @@ def test_Action_CopyDisplayProperties_Group(self): self.context_menu(action_text="Copy display properties [Ctrl+Shift+C]", position=position) clipboard = QtWidgets.QApplication.instance().clipboard().text() print(clipboard) - with self.assertRaises(JSONDecodeError): - json.loads(clipboard) + try: + content = json.loads(clipboard) + except JSONDecodeError: + self.fail("Clipboard Content cannot be decoded as JSON content.") + else: + self.assertIsInstance(content, dict) + self.assertTrue(content["type"] == "group") with self.subTest("EmptyGroup_2"): # Add Channels to Group @@ -354,8 +365,13 @@ def test_Action_CopyDisplayProperties_Group(self): self.context_menu(action_text="Copy display properties [Ctrl+Shift+C]", position=position) clipboard = QtWidgets.QApplication.instance().clipboard().text() print(clipboard) - with self.assertRaises(JSONDecodeError): - json.loads(clipboard) + try: + content = json.loads(clipboard) + except JSONDecodeError: + self.fail("Clipboard Content cannot be decoded as JSON content.") + else: + self.assertIsInstance(content, dict) + self.assertTrue(content["type"] == "group") @unittest.skipIf(sys.platform != "win32", "Timers cannot be started/stopped from another thread.") def test_Action_PasteDisplayProperties_Group(self): @@ -423,16 +439,15 @@ def test_Action_PasteDisplayProperties_Group(self): group_channel_properties = QtWidgets.QApplication.instance().clipboard().text() group_channel_properties = json.loads(group_channel_properties) - del group_channel_properties["y_range"] # Evaluate - self.assertEqual(channel_a_properties, group_channel_properties) + self.assertEqual(channel_a_properties["ranges"], group_channel_properties["ranges"]) with self.subTest("FromGroup_ToChannel"): position_src = self.plot.channel_selection.visualItemRect(self.plot_channel_c).center() self.context_menu(action_text=action_copy, position=position_src) channel_c_properties = QtWidgets.QApplication.instance().clipboard().text() - self.assertNotEqual(channel_c_properties, group_channel_properties) + self.assertNotEqual(channel_c_properties["ranges"], group_channel_properties["ranges"]) position_src = self.plot.channel_selection.visualItemRect(group_channel_a).center() self.context_menu(action_text=action_copy, position=position_src) @@ -447,10 +462,9 @@ def test_Action_PasteDisplayProperties_Group(self): channel_c_properties = QtWidgets.QApplication.instance().clipboard().text() channel_c_properties = json.loads(channel_c_properties) - del channel_c_properties["y_range"] # Evaluate - self.assertEqual(channel_c_properties, group_channel_properties) + self.assertEqual(channel_c_properties["ranges"], group_channel_properties["ranges"]) with self.subTest("FromGroup_ToGroup"): self.move_channel_to_group(src=self.plot_channel_a, dst=group_channel_a) @@ -459,7 +473,6 @@ def test_Action_PasteDisplayProperties_Group(self): self.context_menu(action_text=action_copy, position=position_src) group_channel_b_properties = QtWidgets.QApplication.instance().clipboard().text() group_channel_b_properties = json.loads(group_channel_b_properties) - del group_channel_b_properties["y_range"] # Paste position_dst = self.plot.channel_selection.visualItemRect(group_channel_a).center() @@ -471,7 +484,6 @@ def test_Action_PasteDisplayProperties_Group(self): group_channel_a_properties = QtWidgets.QApplication.instance().clipboard().text() group_channel_a_properties = json.loads(group_channel_a_properties) - del group_channel_a_properties["y_range"] # Evaluate self.assertEqual(group_channel_a_properties, group_channel_b_properties) From 27ab648cfe35a5a796621a84376d02f008ecee06 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Tue, 19 Dec 2023 10:15:14 +0200 Subject: [PATCH 21/38] fix set_fmt on channel tree --- src/asammdf/gui/widgets/tree.py | 21 ++++++++++--------- .../widgets/test_PlotWidget_ContextMenu.py | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/asammdf/gui/widgets/tree.py b/src/asammdf/gui/widgets/tree.py index 0b250ff29..fb8b82d33 100644 --- a/src/asammdf/gui/widgets/tree.py +++ b/src/asammdf/gui/widgets/tree.py @@ -2114,17 +2114,18 @@ def set_disabled(self, disabled, preserve_subgroup_state=True): child.set_disabled(disabled, preserve_subgroup_state=False) def set_fmt(self, fmt): - if self.kind in "SUV": - self.fmt = "{}" - elif self.kind == "f": - self.fmt = f"{{:.{self.precision}f}}" - else: - if fmt == "hex": - self.fmt = "0x{:x}" - elif fmt == "bin": - self.fmt = "0b{:b}" - elif fmt == "phys": + if self.type() == self.Channel: + if self.kind in "SUV": self.fmt = "{}" + elif self.kind == "f": + self.fmt = f"{{:.{self.precision}f}}" + else: + if fmt == "hex": + self.fmt = "0x{:x}" + elif fmt == "bin": + self.fmt = "0b{:b}" + elif fmt == "phys": + self.fmt = "{}" def set_pattern(self, pattern): if pattern: diff --git a/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py b/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py index 3c80dbbf3..40836d53b 100644 --- a/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py +++ b/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py @@ -427,7 +427,6 @@ def test_Action_PasteDisplayProperties_Group(self): channel_a_properties = QtWidgets.QApplication.instance().clipboard().text() channel_a_properties = json.loads(channel_a_properties) - del channel_a_properties["y_range"] # Paste position_dst = self.plot.channel_selection.visualItemRect(group_channel_a).center() @@ -447,6 +446,7 @@ def test_Action_PasteDisplayProperties_Group(self): position_src = self.plot.channel_selection.visualItemRect(self.plot_channel_c).center() self.context_menu(action_text=action_copy, position=position_src) channel_c_properties = QtWidgets.QApplication.instance().clipboard().text() + channel_c_properties = json.loads(channel_c_properties) self.assertNotEqual(channel_c_properties["ranges"], group_channel_properties["ranges"]) position_src = self.plot.channel_selection.visualItemRect(group_channel_a).center() From ca2b65e4e65a7b38151a16ff2e3f29e7c83433ac Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Tue, 19 Dec 2023 10:23:01 +0200 Subject: [PATCH 22/38] fix test --- test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py b/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py index 40836d53b..1af0bfa5c 100644 --- a/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py +++ b/test/asammdf/gui/widgets/test_PlotWidget_ContextMenu.py @@ -443,12 +443,6 @@ def test_Action_PasteDisplayProperties_Group(self): self.assertEqual(channel_a_properties["ranges"], group_channel_properties["ranges"]) with self.subTest("FromGroup_ToChannel"): - position_src = self.plot.channel_selection.visualItemRect(self.plot_channel_c).center() - self.context_menu(action_text=action_copy, position=position_src) - channel_c_properties = QtWidgets.QApplication.instance().clipboard().text() - channel_c_properties = json.loads(channel_c_properties) - self.assertNotEqual(channel_c_properties["ranges"], group_channel_properties["ranges"]) - position_src = self.plot.channel_selection.visualItemRect(group_channel_a).center() self.context_menu(action_text=action_copy, position=position_src) From 0c73d6ae74321e903c37a5a2222dffdad6b193b6 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Wed, 20 Dec 2023 08:47:06 +0200 Subject: [PATCH 23/38] names arguments for extract_signals_using_pattern --- src/asammdf/gui/dialogs/advanced_search.py | 7 ++- src/asammdf/gui/widgets/mdi_area.py | 65 +++++++++++++--------- src/asammdf/gui/widgets/tree.py | 10 +++- src/asammdf/version.py | 2 +- 4 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/asammdf/gui/dialogs/advanced_search.py b/src/asammdf/gui/dialogs/advanced_search.py index 705e20255..b8cfb4c57 100644 --- a/src/asammdf/gui/dialogs/advanced_search.py +++ b/src/asammdf/gui/dialogs/advanced_search.py @@ -37,6 +37,7 @@ def __init__( *args, **kwargs, ): + channels_db = kwargs.pop("channels_db", None) super().__init__(*args, **kwargs) self.setupUi(self) @@ -53,7 +54,8 @@ def __init__( self.channels_db = mdf.channels_db self.mdf = mdf else: - self.mdf = self.channels_db = None + self.mdf = None + self.channels_db = channels_db self.apply_btn.clicked.connect(self._apply) self.add_btn.clicked.connect(self._add) @@ -442,7 +444,8 @@ def update_pattern_matches(self, *args): } signals = extract_signals_using_pattern( - mdf=self.mdf or None, + mdf=self.mdf, + channels_db=self.channels_db, pattern_info=pattern_info, ignore_value2text_conversions=True, as_names=True, diff --git a/src/asammdf/gui/widgets/mdi_area.py b/src/asammdf/gui/widgets/mdi_area.py index bff431206..363c0f45d 100644 --- a/src/asammdf/gui/widgets/mdi_area.py +++ b/src/asammdf/gui/widgets/mdi_area.py @@ -182,13 +182,18 @@ def build_mime_from_config( return mime, descriptions, found, not_found, computed -def extract_signals_using_pattern(mdf, pattern_info, ignore_value2text_conversions, uuid=None, as_names=False): +def extract_signals_using_pattern( + mdf, channels_db, pattern_info, ignore_value2text_conversions, uuid=None, as_names=False +): if not mdf: if as_names: return set() else: return {} + elif not channels_db: + channels_db = mdf.channels_db + pattern = pattern_info["pattern"] match_type = pattern_info["match_type"] case_sensitive = pattern_info.get("case_sensitive", False) @@ -211,7 +216,7 @@ def extract_signals_using_pattern(mdf, pattern_info, ignore_value2text_conversio matches = {} - for name, entries in mdf.channels_db.items(): + for name, entries in channels_db.items(): if pattern.fullmatch(name): for entry in entries: if entry in matches: @@ -223,7 +228,7 @@ def extract_signals_using_pattern(mdf, pattern_info, ignore_value2text_conversio print(format_exc()) signals = [] else: - if as_names and filter_type == "Unspecified": + if (as_names and filter_type == "Unspecified") or not mdf: return {match[0] for match in matches} psignals = mdf.select( @@ -650,10 +655,11 @@ def __init__(self, comparison=False, *args, **kwargs): def add_pattern_group(self, plot, group): signals = extract_signals_using_pattern( - self.mdf, - group.pattern, - self.ignore_value2text_conversions, - self.uuid, + mdf=self.mdf, + channels_db=None, + pattern_info=group.pattern, + ignore_value2text_conversions=self.ignore_value2text_conversions, + uuid=self.uuid, ) signals = { @@ -1973,10 +1979,11 @@ def _add_numeric_window(self, names): signals.extend( extract_signals_using_pattern( - file.mdf, - pattern_group["pattern"], - file.ignore_value2text_conversions, - file.uuid, + mdf=file.mdf, + channels_db=None, + pattern_info=pattern_group["pattern"], + ignore_value2text_conversions=file.ignore_value2text_conversions, + uuid=file.uuid, ).values() ) @@ -2581,10 +2588,11 @@ def _add_tabular_window(self, names): [ (sig.name, sig.group_index, sig.channel_index) for sig in extract_signals_using_pattern( - file.mdf, - pattern_group["pattern"], - file.ignore_value2text_conversions, - file.uuid, + mdf=file.mdf, + channels_db=None, + pattern_info=pattern_group["pattern"], + ignore_value2text_conversions=file.ignore_value2text_conversions, + uuid=file.uuid, ).values() ] ) @@ -2835,10 +2843,11 @@ def _load_numeric_window(self, window_info): pattern_info = window_info["configuration"].get("pattern", {}) if pattern_info: signals = extract_signals_using_pattern( - self.mdf, - pattern_info, - self.ignore_value2text_conversions, - self.uuid, + mdf=self.mdf, + channels_db=None, + pattern_info=pattern_info, + ignore_value2text_conversions=self.ignore_value2text_conversions, + uuid=self.uuid, ) signals = list(signals.values()) @@ -3054,10 +3063,11 @@ def _load_plot_window(self, window_info): pattern_info = window_info["configuration"].get("pattern", {}) if pattern_info: plot_signals = extract_signals_using_pattern( - self.mdf, - pattern_info, - self.ignore_value2text_conversions, - self.uuid, + mdf=self.mdf, + channels_db=None, + pattern_info=pattern_info, + ignore_value2text_conversions=self.ignore_value2text_conversions, + uuid=self.uuid, ) mime_data = None @@ -3479,10 +3489,11 @@ def _load_tabular_window(self, window_info): found_signals = [] signals_ = extract_signals_using_pattern( - self.mdf, - pattern_info, - self.ignore_value2text_conversions, - self.uuid, + mdf=self.mdf, + channels_db=None, + pattern_info=pattern_info, + ignore_value2text_conversions=self.ignore_value2text_conversions, + uuid=self.uuid, ).values() try: diff --git a/src/asammdf/gui/widgets/tree.py b/src/asammdf/gui/widgets/tree.py index fb8b82d33..1d9942e12 100644 --- a/src/asammdf/gui/widgets/tree.py +++ b/src/asammdf/gui/widgets/tree.py @@ -576,14 +576,22 @@ def keyPressEvent(self, event): self.update_channel_groups_count() elif key == QtCore.Qt.Key.Key_Insert and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + if hasattr(self.plot.owner, "mdf"): + mdf = self.plot.owner.mdf + channels_db = None + else: + mdf = None + channels_db = self.plot.owner.channels_db + dlg = AdvancedSearch( - self.plot.mdf if self.plot else None, + mdf=mdf, show_add_window=False, show_apply=True, show_search=False, show_pattern=True, window_title="Add pattern based group", parent=self, + channels_db=channels_db, ) dlg.setModal(True) dlg.exec_() diff --git a/src/asammdf/version.py b/src/asammdf/version.py index e360c79da..da328658f 100644 --- a/src/asammdf/version.py +++ b/src/asammdf/version.py @@ -1,3 +1,3 @@ """ asammdf version module """ -__version__ = "7.4.0.dev10" +__version__ = "7.4.0.dev11" From fb0d711a57f306f2ccb63d21db97d45b2dc09524 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Wed, 20 Dec 2023 09:08:37 +0200 Subject: [PATCH 24/38] check channels_db in extract_signals_using_pattern --- src/asammdf/gui/widgets/mdi_area.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asammdf/gui/widgets/mdi_area.py b/src/asammdf/gui/widgets/mdi_area.py index 363c0f45d..e6d27efc0 100644 --- a/src/asammdf/gui/widgets/mdi_area.py +++ b/src/asammdf/gui/widgets/mdi_area.py @@ -185,7 +185,7 @@ def build_mime_from_config( def extract_signals_using_pattern( mdf, channels_db, pattern_info, ignore_value2text_conversions, uuid=None, as_names=False ): - if not mdf: + if not mdf and not channels_db: if as_names: return set() else: From ea04e78d40265ebaec6c29bb2eb77f8c22ef7487 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Thu, 21 Dec 2023 11:46:19 +0200 Subject: [PATCH 25/38] set env vars for the GUI --- src/asammdf/gui/__init__.py | 4 ++++ src/asammdf/gui/plot.py | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/asammdf/gui/__init__.py b/src/asammdf/gui/__init__.py index 8b1378917..ea242eb25 100644 --- a/src/asammdf/gui/__init__.py +++ b/src/asammdf/gui/__init__.py @@ -1 +1,5 @@ +import os +os.environ["QT_API"] = "pyside6" +os.environ["PYQTGRAPH_QT_LIB"] = "PySide6" +os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1" diff --git a/src/asammdf/gui/plot.py b/src/asammdf/gui/plot.py index bb24065dc..8b2e0a9a8 100644 --- a/src/asammdf/gui/plot.py +++ b/src/asammdf/gui/plot.py @@ -1,12 +1,8 @@ import logging -import os from ..blocks.utils import plausible_timestamps try: - os.environ["QT_API"] = "pyside6" - os.environ["PYQTGRAPH_QT_LIB"] = "PySide6" - from PySide6 import QtWidgets from .widgets.plot_standalone import PlotWindow From 116a518b580082acdb79c482ee80696970d1f143 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Thu, 21 Dec 2023 14:57:06 +0200 Subject: [PATCH 26/38] fixes for cpython 3.12 --- src/asammdf/gui/widgets/python_highlighter.py | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/asammdf/gui/widgets/python_highlighter.py b/src/asammdf/gui/widgets/python_highlighter.py index b5779447a..92ee2ae14 100644 --- a/src/asammdf/gui/widgets/python_highlighter.py +++ b/src/asammdf/gui/widgets/python_highlighter.py @@ -53,45 +53,45 @@ class PythonHighlighter(QtGui.QSyntaxHighlighter): # Python operators operators = [ - "=", + r"=", # Comparison - "==", + r"==", "!=", "<", "<=", ">", ">=", # Arithmetic - "\+", + r"\+", "-", - "\*", + r"\*", "/", "//", - "\%", - "\*\*", + r"\%", + r"\*\*", # In-place - "\+=", + r"\+=", "-=", - "\*=", + r"\*=", "/=", - "\%=", + r"\%=", # Bitwise - "\^", - "\|", - "\&", - "\~", + r"\^", + r"\|", + r"\&", + r"\~", ">>", "<<", ] # Python braces braces = [ - "\{", - "\}", - "\(", - "\)", - "\[", - "\]", + r"\{", + r"\}", + r"\(", + r"\)", + r"\[", + r"\]", ] def __init__(self, parent: QtGui.QTextDocument) -> None: From 201e9fe8a60b4fc1ecc728e972b7eeb53f21b315 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Wed, 3 Jan 2024 14:20:24 +0200 Subject: [PATCH 27/38] accept keyboard events --- src/asammdf/gui/dialogs/error_dialog.py | 1 + src/asammdf/gui/dialogs/messagebox.py | 1 + src/asammdf/gui/utils.py | 1 + src/asammdf/gui/widgets/file.py | 7 +++++++ src/asammdf/gui/widgets/list.py | 12 +++++++++++- src/asammdf/gui/widgets/main.py | 3 +++ src/asammdf/gui/widgets/numeric.py | 18 +++++++++++++++++- src/asammdf/gui/widgets/plot.py | 20 +++++++++++++++++++- src/asammdf/gui/widgets/signal_scale.py | 6 ++++++ src/asammdf/gui/widgets/tabular_base.py | 14 ++++++++++---- src/asammdf/gui/widgets/tree.py | 12 ++++++++++++ src/asammdf/version.py | 2 +- 12 files changed, 89 insertions(+), 8 deletions(-) diff --git a/src/asammdf/gui/dialogs/error_dialog.py b/src/asammdf/gui/dialogs/error_dialog.py index ff759266a..d7198113f 100644 --- a/src/asammdf/gui/dialogs/error_dialog.py +++ b/src/asammdf/gui/dialogs/error_dialog.py @@ -79,6 +79,7 @@ def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key.Key_F1: self.timer.stop() self.status.clear() + event.accept() else: super().keyPressEvent(event) diff --git a/src/asammdf/gui/dialogs/messagebox.py b/src/asammdf/gui/dialogs/messagebox.py index 34953016d..9743a6315 100644 --- a/src/asammdf/gui/dialogs/messagebox.py +++ b/src/asammdf/gui/dialogs/messagebox.py @@ -72,6 +72,7 @@ def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key.Key_F1: self.timer.stop() self.setText(self.original_text) + event.accept() else: super().keyPressEvent(event) diff --git a/src/asammdf/gui/utils.py b/src/asammdf/gui/utils.py index 6d7d63131..039e67411 100644 --- a/src/asammdf/gui/utils.py +++ b/src/asammdf/gui/utils.py @@ -316,6 +316,7 @@ def close(self): def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key.Key_Escape and event.modifiers() == QtCore.Qt.KeyboardModifier.NoModifier: + event.accept() self.close() else: super().keyPressEvent(event) diff --git a/src/asammdf/gui/widgets/file.py b/src/asammdf/gui/widgets/file.py index eda425304..a696f4e10 100644 --- a/src/asammdf/gui/widgets/file.py +++ b/src/asammdf/gui/widgets/file.py @@ -2136,9 +2136,11 @@ def keyPressEvent(self, event): if key == QtCore.Qt.Key.Key_F and modifier == QtCore.Qt.KeyboardModifier.ControlModifier: self.search() + event.accept() elif key == QtCore.Qt.Key.Key_F11: self.full_screen_toggled.emit() + event.accept() elif ( key in (QtCore.Qt.Key.Key_V, QtCore.Qt.Key.Key_H, QtCore.Qt.Key.Key_C, QtCore.Qt.Key.Key_T) @@ -2162,10 +2164,13 @@ def keyPressEvent(self, event): elif mode == "tile horizontally": self.mdi_area.tile_horizontally() + event.accept() + elif key == QtCore.Qt.Key.Key_F and modifier == ( QtCore.Qt.KeyboardModifier.ShiftModifier | QtCore.Qt.KeyboardModifier.AltModifier ): self.toggle_frames() + event.accept() elif key == QtCore.Qt.Key.Key_L and modifier == QtCore.Qt.KeyboardModifier.ShiftModifier: if self.channel_view.isVisible(): @@ -2190,9 +2195,11 @@ def keyPressEvent(self, event): self.splitter.setSizes(self._splitter_sizes) self.splitter.handle(0).setEnabled(True) self.splitter.handle(1).setEnabled(True) + event.accept() elif key == QtCore.Qt.Key.Key_Period and modifier == QtCore.Qt.KeyboardModifier.NoModifier: self.set_line_style() + event.accept() else: widget = self.get_current_widget() diff --git a/src/asammdf/gui/widgets/list.py b/src/asammdf/gui/widgets/list.py index 106ba87e6..74fd8aceb 100644 --- a/src/asammdf/gui/widgets/list.py +++ b/src/asammdf/gui/widgets/list.py @@ -64,8 +64,10 @@ def keyPressEvent(self, event): self.takeItem(row) if deleted: self.itemsDeleted.emit(deleted) + event.accept() elif key == QtCore.Qt.Key.Key_Space and modifiers == QtCore.Qt.KeyboardModifier.NoModifier: + event.accept() selected_items = self.selectedItems() if not selected_items: return @@ -81,6 +83,7 @@ def keyPressEvent(self, event): wid.display.setCheckState(state) elif key == QtCore.Qt.Key.Key_Space and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + event.accept() selected_items = self.selectedItems() if not selected_items: return @@ -98,7 +101,9 @@ def keyPressEvent(self, event): elif modifiers == QtCore.Qt.KeyboardModifier.ControlModifier and key == QtCore.Qt.Key.Key_C: selected_items = self.selectedItems() if not selected_items: + event.accept() return + self.itemWidget(selected_items[0]).keyPressEvent(event) elif modifiers == ( @@ -109,12 +114,12 @@ def keyPressEvent(self, event): ): selected_items = self.selectedItems() if not selected_items: + event.accept() return self.itemWidget(selected_items[0]).keyPressEvent(event) else: super().keyPressEvent(event) - self.parent().keyPressEvent(event) def startDrag(self, supportedActions): selected_items = self.selectedItems() @@ -457,6 +462,8 @@ def keyPressEvent(self, event): if deleted: self.itemsDeleted.emit(deleted) + event.accept() + elif key == QtCore.Qt.Key.Key_C and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: text = [] for item in self.selectedItems(): @@ -471,6 +478,8 @@ def keyPressEvent(self, event): text = "" QtWidgets.QApplication.instance().clipboard().setText(text) + event.accept() + elif ( key == QtCore.Qt.Key.Key_V and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier @@ -483,6 +492,7 @@ def keyPressEvent(self, event): self.itemsPasted.emit() except: pass + event.accept() else: super().keyPressEvent(event) diff --git a/src/asammdf/gui/widgets/main.py b/src/asammdf/gui/widgets/main.py index ab155a638..468a9e191 100644 --- a/src/asammdf/gui/widgets/main.py +++ b/src/asammdf/gui/widgets/main.py @@ -1318,6 +1318,7 @@ def keyPressEvent(self, event): if self.files.count() and self.stackedWidget.currentIndex() == 0: self.files.currentWidget().keyPressEvent(event) elif self.files.count() and self.stackedWidget.currentIndex() == 2: + event.accept() count = self.files.count() channels_dbs = [self.files.widget(i).mdf.channels_db for i in range(count)] measurements = [str(self.files.widget(i).mdf.name) for i in range(count)] @@ -1362,6 +1363,7 @@ def keyPressEvent(self, event): elif key == QtCore.Qt.Key.Key_F11: self.toggle_fullscreen() + event.accept() elif key in (QtCore.Qt.Key.Key_F2, QtCore.Qt.Key.Key_F3, QtCore.Qt.Key.Key_F4): if self.files.count() and self.stackedWidget.currentIndex() == 0: @@ -1372,6 +1374,7 @@ def keyPressEvent(self, event): elif key == QtCore.Qt.Key.Key_F4: window_type = "Tabular" self.files.currentWidget()._create_window(None, window_type) + event.accept() else: super().keyPressEvent(event) diff --git a/src/asammdf/gui/widgets/numeric.py b/src/asammdf/gui/widgets/numeric.py index 856491f22..1f6b1b479 100644 --- a/src/asammdf/gui/widgets/numeric.py +++ b/src/asammdf/gui/widgets/numeric.py @@ -648,6 +648,7 @@ def keyPressEvent(self, event): modifiers = event.modifiers() if key == QtCore.Qt.Key.Key_Delete and modifiers == QtCore.Qt.KeyboardModifier.NoModifier: + event.accept() selected_items = {index.row() for index in self.selectedIndexes() if index.isValid()} for row in reversed(list(selected_items)): @@ -657,6 +658,8 @@ def keyPressEvent(self, event): self.backend.update() elif key == QtCore.Qt.Key.Key_R and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + event.accept() + selected_items = {index.row() for index in self.selectedIndexes() if index.isValid()} if selected_items: @@ -687,6 +690,8 @@ def keyPressEvent(self, event): modifiers == (QtCore.Qt.KeyboardModifier.ControlModifier | QtCore.Qt.KeyboardModifier.ShiftModifier) and key == QtCore.Qt.Key.Key_C ): + event.accept() + selected_items = {index.row() for index in self.selectedIndexes() if index.isValid()} if not selected_items: @@ -710,6 +715,8 @@ def keyPressEvent(self, event): modifiers == (QtCore.Qt.KeyboardModifier.ControlModifier | QtCore.Qt.KeyboardModifier.ShiftModifier) and key == QtCore.Qt.Key.Key_V ): + event.accept() + info = QtWidgets.QApplication.instance().clipboard().text() selected_items = {index.row() for index in self.selectedIndexes() if index.isValid()} @@ -1574,6 +1581,8 @@ def keyPressEvent(self, event): key in (QtCore.Qt.Key.Key_H, QtCore.Qt.Key.Key_B, QtCore.Qt.Key.Key_P, QtCore.Qt.Key.Key_T) and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier ): + event.accept() + if key == QtCore.Qt.Key.Key_H: self.set_format("Hex") elif key == QtCore.Qt.Key.Key_B: @@ -1582,12 +1591,13 @@ def keyPressEvent(self, event): self.set_format("Ascii") else: self.set_format("Physical") - event.accept() + elif ( key == QtCore.Qt.Key.Key_Right and modifiers == QtCore.Qt.KeyboardModifier.NoModifier and self.mode == "offline" ): + event.accept() self.timestamp_slider.setValue(self.timestamp_slider.value() + 1) elif ( @@ -1595,6 +1605,7 @@ def keyPressEvent(self, event): and modifiers == QtCore.Qt.KeyboardModifier.NoModifier and self.mode == "offline" ): + event.accept() self.timestamp_slider.setValue(self.timestamp_slider.value() - 1) elif ( @@ -1602,6 +1613,7 @@ def keyPressEvent(self, event): and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier and self.mode == "offline" ): + event.accept() file_name, _ = QtWidgets.QFileDialog.getSaveFileName( self, "Save as measurement file", @@ -1632,9 +1644,11 @@ def keyPressEvent(self, event): mdf.save(file_name, overwrite=True) elif key == QtCore.Qt.Key.Key_BracketLeft and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + event.accept() self.decrease_font() elif key == QtCore.Qt.Key.Key_BracketRight and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + event.accept() self.increase_font() elif ( @@ -1642,6 +1656,8 @@ def keyPressEvent(self, event): and modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier and self.mode == "offline" ): + event.accept() + value, ok = QtWidgets.QInputDialog.getDouble( self, "Go to time stamp", diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index 30b4a9920..e50b81eaa 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -2625,6 +2625,7 @@ def keyPressEvent(self, event): modifiers = event.modifiers() if key == QtCore.Qt.Key.Key_M and modifiers == QtCore.Qt.KeyboardModifier.NoModifier: + event.accept() ch_size, plt_size, info_size = self.splitter.sizes() if self.info.isVisible(): @@ -2653,6 +2654,8 @@ def keyPressEvent(self, event): self.focused_mode_btn.setFlat(True) self.channel_selection_changed(update=True) + event.accept() + elif ( key in (QtCore.Qt.Key.Key_B, QtCore.Qt.Key.Key_H, QtCore.Qt.Key.Key_P, QtCore.Qt.Key.Key_T) and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier @@ -2713,6 +2716,8 @@ def keyPressEvent(self, event): self.current_uuid_changed(self.plot.current_uuid) self.plot.update() + event.accept() + elif ( key in (QtCore.Qt.Key.Key_R, QtCore.Qt.Key.Key_S) and modifiers == QtCore.Qt.KeyboardModifier.AltModifier @@ -2792,6 +2797,8 @@ def keyPressEvent(self, event): if self.plot.cursor1: self.plot.cursor_moved.emit(self.plot.cursor1) + event.accept() + elif key == QtCore.Qt.Key.Key_I and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: if self.plot.cursor1: position = self.plot.cursor1.value() @@ -2824,6 +2831,8 @@ def keyPressEvent(self, event): self.update() + event.accept() + elif key == QtCore.Qt.Key.Key_I and modifiers == QtCore.Qt.KeyboardModifier.AltModifier: self.show_bookmarks = not self.show_bookmarks if self.show_bookmarks: @@ -2835,6 +2844,7 @@ def keyPressEvent(self, event): bookmark.visible = self.show_bookmarks self.plot.update() + event.accept() elif key == QtCore.Qt.Key.Key_G and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: selected_items = [ @@ -2873,6 +2883,8 @@ def keyPressEvent(self, event): self.plot.update() + event.accept() + elif key == QtCore.Qt.Key.Key_R and modifiers == QtCore.Qt.KeyboardModifier.NoModifier: iterator = QtWidgets.QTreeWidgetItemIterator(self.channel_selection) while item := iterator.value(): @@ -2922,6 +2934,8 @@ def keyPressEvent(self, event): else: self.undo_zoom() + event.accept() + elif key == QtCore.Qt.Key.Key_W and modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier: if self.enable_zoom_history and self.zoom_history: self.zoom_history_index = 0 @@ -2946,8 +2960,9 @@ def keyPressEvent(self, event): self.plot.block_zoom_signal = False + event.accept() else: - super().keyPressEvent(event) + event.ignore() def mousePressEvent(self, event): self.clicked.emit() @@ -5090,7 +5105,10 @@ def keyPressEvent(self, event): handled = False if not handled: + event.ignore() self.parent().keyPressEvent(event) + else: + event.accept() def open_scale_editor(self, uuid): uuid = uuid or self.current_uuid diff --git a/src/asammdf/gui/widgets/signal_scale.py b/src/asammdf/gui/widgets/signal_scale.py index f14d2dcc1..05c23a85f 100644 --- a/src/asammdf/gui/widgets/signal_scale.py +++ b/src/asammdf/gui/widgets/signal_scale.py @@ -213,17 +213,23 @@ def keyPressEvent(self, event): QtCore.Qt.KeyboardModifier.NoModifier, QtCore.Qt.KeyboardModifier.ShiftModifier, ): + event.accept() self.zoom_in() + elif key == QtCore.Qt.Key.Key_O and modifiers in ( QtCore.Qt.KeyboardModifier.NoModifier, QtCore.Qt.KeyboardModifier.ShiftModifier, ): + event.accept() self.zoom_out() + elif key == QtCore.Qt.Key.Key_F and modifiers in ( QtCore.Qt.KeyboardModifier.NoModifier, QtCore.Qt.KeyboardModifier.ShiftModifier, ): + event.accept() self.fit() + else: super().keyPressEvent(event) diff --git a/src/asammdf/gui/widgets/tabular_base.py b/src/asammdf/gui/widgets/tabular_base.py index 7b8aa6e49..1eecc5f3f 100644 --- a/src/asammdf/gui/widgets/tabular_base.py +++ b/src/asammdf/gui/widgets/tabular_base.py @@ -462,6 +462,7 @@ def keyPressEvent(self, event): modifiers = event.modifiers() if key == QtCore.Qt.Key.Key_R and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + event.accept() selected_items = {index.column() for index in self.selectedIndexes() if index.isValid()} if selected_items: @@ -1681,6 +1682,7 @@ def keyPressEvent(self, event): key in (QtCore.Qt.Key.Key_H, QtCore.Qt.Key.Key_B, QtCore.Qt.Key.Key_P) and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier ): + event.accept() if key == QtCore.Qt.Key.Key_H: self.format_selection.setCurrentText("hex") elif key == QtCore.Qt.Key.Key_B: @@ -1688,9 +1690,8 @@ def keyPressEvent(self, event): else: self.format_selection.setCurrentText("phys") - event.accept() - elif key == QtCore.Qt.Key.Key_S and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + event.accept() file_name, _ = QtWidgets.QFileDialog.getSaveFileName( self, "Save as measurement file", @@ -1704,12 +1705,15 @@ def keyPressEvent(self, event): mdf.save(file_name, overwrite=True) elif key == QtCore.Qt.Key.Key_BracketLeft and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + event.accept() self.decrease_font() elif key == QtCore.Qt.Key.Key_BracketRight and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + event.accept() self.increase_font() elif key == QtCore.Qt.Key.Key_G and modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier: + event.accept() value, ok = QtWidgets.QInputDialog.getDouble( self, "Go to time stamp", @@ -2037,16 +2041,18 @@ def keyPressEvent(self, event): QtWidgets.QWidget.keyPressEvent(self, event) mods = event.modifiers() - # Ctrl+C if event.key() == Qt.Key.Key_C and (mods & Qt.KeyboardModifier.ControlModifier): + event.accept() self.copy(header=True) - # Ctrl+Shift+C + elif ( event.key() == Qt.Key.Key_C and (mods & Qt.KeyboardModifier.ShiftModifier) and (mods & Qt.KeyboardModifier.ControlModifier) ): + event.accept() self.copy(header=True) + else: self.dataView.keyPressEvent(event) diff --git a/src/asammdf/gui/widgets/tree.py b/src/asammdf/gui/widgets/tree.py index 1d9942e12..2c00644cd 100644 --- a/src/asammdf/gui/widgets/tree.py +++ b/src/asammdf/gui/widgets/tree.py @@ -217,6 +217,7 @@ def __init__(self, *args, **kwargs): def keyPressEvent(self, event): key = event.key() if key == QtCore.Qt.Key.Key_Space: + event.accept() selected_items = self.selectedItems() if not selected_items: return @@ -557,6 +558,7 @@ def keyPressEvent(self, event): modifiers = event.modifiers() if key == QtCore.Qt.Key.Key_Delete and self.can_delete_items: + event.accept() selected_items = self.selectedItems() deleted = list(set(get_data(self.plot, selected_items, uuids_only=True))) @@ -576,6 +578,7 @@ def keyPressEvent(self, event): self.update_channel_groups_count() elif key == QtCore.Qt.Key.Key_Insert and modifiers == QtCore.Qt.KeyboardModifier.ControlModifier: + event.accept() if hasattr(self.plot.owner, "mdf"): mdf = self.plot.owner.mdf channels_db = None @@ -631,6 +634,7 @@ def keyPressEvent(self, event): self.update_channel_groups_count() elif key == QtCore.Qt.Key.Key_Insert and modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier: + event.accept() text, ok = QtWidgets.QInputDialog.getText(self, "Channel group name", "New channel group name:") if ok: group = ChannelsTreeItem(ChannelsTreeItem.Group, name=text) @@ -652,6 +656,7 @@ def keyPressEvent(self, event): self.update_channel_groups_count() elif key == QtCore.Qt.Key.Key_Space: + event.accept() selected_items = self.selectedItems() if not selected_items: return @@ -675,6 +680,7 @@ def keyPressEvent(self, event): item.setHidden(True) elif modifiers == QtCore.Qt.KeyboardModifier.NoModifier and key == QtCore.Qt.Key.Key_C: + event.accept() selected_items = self.selectedItems() if not selected_items: return @@ -693,12 +699,14 @@ def keyPressEvent(self, event): item.color = color elif modifiers == QtCore.Qt.KeyboardModifier.ControlModifier and key == QtCore.Qt.Key.Key_C: + event.accept() selected_items = validate_drag_items(self.invisibleRootItem(), self.selectedItems(), []) data = get_data(self.plot, selected_items, uuids_only=False) data = substitude_mime_uuids(data, None, force=True) QtWidgets.QApplication.instance().clipboard().setText(json.dumps(data)) elif modifiers == QtCore.Qt.KeyboardModifier.ControlModifier and key == QtCore.Qt.Key.Key_V: + event.accept() try: data = QtWidgets.QApplication.instance().clipboard().text() data = json.loads(data) @@ -708,6 +716,7 @@ def keyPressEvent(self, event): pass elif modifiers == QtCore.Qt.KeyboardModifier.ControlModifier and key == QtCore.Qt.Key.Key_N: + event.accept() selected_items = self.selectedItems() if not selected_items: return @@ -717,6 +726,7 @@ def keyPressEvent(self, event): QtWidgets.QApplication.instance().clipboard().setText(text) elif modifiers == QtCore.Qt.KeyboardModifier.ControlModifier and key == QtCore.Qt.Key.Key_R: + event.accept() selected_items = self.selectedItems() if not selected_items: return @@ -762,6 +772,7 @@ def keyPressEvent(self, event): modifiers == (QtCore.Qt.KeyboardModifier.ControlModifier | QtCore.Qt.KeyboardModifier.ShiftModifier) and key == QtCore.Qt.Key.Key_C ): + event.accept() selected_items = [ item for item in self.selectedItems() @@ -776,6 +787,7 @@ def keyPressEvent(self, event): modifiers == (QtCore.Qt.KeyboardModifier.ControlModifier | QtCore.Qt.KeyboardModifier.ShiftModifier) and key == QtCore.Qt.Key.Key_V ): + event.accept() info = QtWidgets.QApplication.instance().clipboard().text() selected_items = self.selectedItems() if not selected_items: diff --git a/src/asammdf/version.py b/src/asammdf/version.py index da328658f..1eedd2625 100644 --- a/src/asammdf/version.py +++ b/src/asammdf/version.py @@ -1,3 +1,3 @@ """ asammdf version module """ -__version__ = "7.4.0.dev11" +__version__ = "7.4.0.dev12" From 628fb24147381b53125af9156e28b422322ceb39 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Thu, 4 Jan 2024 14:57:14 +0200 Subject: [PATCH 28/38] fix path handling in the batch widget --- src/asammdf/gui/widgets/batch.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/asammdf/gui/widgets/batch.py b/src/asammdf/gui/widgets/batch.py index dd9b8ee25..b3c76f982 100644 --- a/src/asammdf/gui/widgets/batch.py +++ b/src/asammdf/gui/widgets/batch.py @@ -845,6 +845,7 @@ def _as_mdf(self, file_name): try: from mfile import BSIG, DL3, ERG, TDMS except ImportError: + print(format_exc()) from cmerg import BSIG, ERG if suffix == ".erg": @@ -857,6 +858,7 @@ def _as_mdf(self, file_name): cls = DL3 mdf = cls(file_name).export_mdf() + mdf.original_name = file_name elif suffix in (".mdf", ".mf4", ".mf4z"): mdf = MDF(file_name) @@ -878,7 +880,11 @@ def _prepare_files(self, files=None, progress=None): mdf_files = [] for i, file_name in enumerate(files): progress.signals.setLabelText.emit(f"Preparing the file {i+1} of {count}\n{file_name}") - mdf = self._as_mdf(file_name) + try: + mdf = self._as_mdf(file_name) + except: + print(format_exc()) + mdf = None mdf_files.append(mdf) progress.signals.setValue.emit(i + 1) @@ -1400,7 +1406,7 @@ def apply_processing_thread(self, progress): output_folder = None try: - root = Path(os.path.commonprefix(source_files)).parent + root = Path(os.path.commonpath(source_files)) except ValueError: root = None @@ -1413,6 +1419,9 @@ def apply_processing_thread(self, progress): files = self._prepare_files(list(source_files), progress) for mdf_index, (mdf_file, source_file) in enumerate(zip(files, source_files)): + if mdf_file is None: + continue + mdf_file.configure( read_fragment_size=split_size, integer_interpolation=self.integer_interpolation, @@ -1557,7 +1566,7 @@ def apply_processing_thread(self, progress): if root is None: file_name = output_folder / Path(mdf_file.original_name).name else: - file_name = output_folder / Path(mdf_file.name).relative_to(root) + file_name = output_folder / Path(mdf_file.original_name).relative_to(root) if not file_name.parent.exists(): os.makedirs(file_name.parent, exist_ok=True) From 9565c1e7203df44998d5866ba8863fa72e180911 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Thu, 4 Jan 2024 14:58:41 +0200 Subject: [PATCH 29/38] bump version --- src/asammdf/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asammdf/version.py b/src/asammdf/version.py index 1eedd2625..96d9f6047 100644 --- a/src/asammdf/version.py +++ b/src/asammdf/version.py @@ -1,3 +1,3 @@ """ asammdf version module """ -__version__ = "7.4.0.dev12" +__version__ = "7.4.0.dev13" From 84f254d1feb824f068ad9f46caff56edea87ca94 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Fri, 5 Jan 2024 12:05:13 +0200 Subject: [PATCH 30/38] fix overflow errors when editing very high ranges --- src/asammdf/gui/widgets/signal_scale.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/asammdf/gui/widgets/signal_scale.py b/src/asammdf/gui/widgets/signal_scale.py index 05c23a85f..8109252ea 100644 --- a/src/asammdf/gui/widgets/signal_scale.py +++ b/src/asammdf/gui/widgets/signal_scale.py @@ -195,8 +195,12 @@ def draw_plot(self, *args): pen.setWidth(2) pen.setStyle(QtCore.Qt.PenStyle.DashDotDotLine) painter.setPen(pen) - offset = int(PLOT_HEIGTH - self.offset.value() * PLOT_HEIGTH / 100) - painter.drawLine(0, offset, PLOT_HEIGTH + 2 * TEXT_WIDTH, offset) + offset = PLOT_HEIGTH - self.offset.value() * PLOT_HEIGTH / 100 + + p1 = QtCore.QPointF(0.0, offset) + p2 = QtCore.QPointF(float(PLOT_HEIGTH + 2 * TEXT_WIDTH), offset) + + painter.drawLine(p1, p2) painter.end() From e4852638471602a5a55cc8558d2f1392ab9640d7 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Fri, 5 Jan 2024 12:09:49 +0200 Subject: [PATCH 31/38] bump --- src/asammdf/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asammdf/version.py b/src/asammdf/version.py index 96d9f6047..e1c7248cf 100644 --- a/src/asammdf/version.py +++ b/src/asammdf/version.py @@ -1,3 +1,3 @@ """ asammdf version module """ -__version__ = "7.4.0.dev13" +__version__ = "7.4.0.dev14" From 43852ffec3707e835a5bf401851e65561620c944 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Mon, 8 Jan 2024 10:43:39 +0200 Subject: [PATCH 32/38] use numeric values when computing virtual channels --- src/asammdf/gui/utils.py | 10 ++++++++++ src/asammdf/gui/widgets/mdi_area.py | 6 ------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/asammdf/gui/utils.py b/src/asammdf/gui/utils.py index 039e67411..3fb69867f 100644 --- a/src/asammdf/gui/utils.py +++ b/src/asammdf/gui/utils.py @@ -388,6 +388,16 @@ def compute_signal( all_timebase, functions, ): + required_channels = {} + for key, sig in measured_signals.items(): + signal = sig.physical(copy=False) + if signal.samples.dtype.kind in "fui": + required_channels[key] = signal + else: + required_channels[key] = sig + + measured_signals = required_channels + type_ = description["type"] try: diff --git a/src/asammdf/gui/widgets/mdi_area.py b/src/asammdf/gui/widgets/mdi_area.py index e6d27efc0..156204f8f 100644 --- a/src/asammdf/gui/widgets/mdi_area.py +++ b/src/asammdf/gui/widgets/mdi_area.py @@ -988,8 +988,6 @@ def add_new_channels(self, names, widget, mime_data=None): required_channels.update(measured_signals) - required_channels = {key: sig.physical(copy=False) for key, sig in required_channels.items()} - if required_channels: all_timebase = np.unique( np.concatenate( @@ -2722,8 +2720,6 @@ def edit_channel(self, channel, item, widget): else: all_timebase = [] - required_channels = {key: sig.physical(copy=False) for key, sig in required_channels.items()} - computation = channel["computation"] signal = compute_signal( @@ -3197,8 +3193,6 @@ def _load_plot_window(self, window_info): required_channels.update(measured_signals) - required_channels = {key: sig.physical(copy=False) for key, sig in required_channels.items()} - for sig_uuid, channel in computed.items(): computation = channel["computation"] From 4dfa20b0af6c924993cf5e1b99a073d7d3fdda45 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Wed, 10 Jan 2024 09:51:30 +0200 Subject: [PATCH 33/38] prepare for numpy 2.0 --- src/asammdf/blocks/v4_blocks.py | 8 ++++---- src/asammdf/mdf.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/asammdf/blocks/v4_blocks.py b/src/asammdf/blocks/v4_blocks.py index b28d3831f..a6ac0a037 100644 --- a/src/asammdf/blocks/v4_blocks.py +++ b/src/asammdf/blocks/v4_blocks.py @@ -3129,7 +3129,7 @@ def convert(self, values, as_object=False, as_bytes=False, ignore_value2text_con vals = vals * a if b: vals += b - values = np.core.records.fromarrays( + values = np.rec.fromarrays( [vals] + [values[name] for name in names[1:]], dtype=[(name, vals.dtype, vals.shape[1:])] + [(name, values[name].dtype, values[name].shape[1:]) for name in names[1:]], @@ -3167,7 +3167,7 @@ def convert(self, values, as_object=False, as_bytes=False, ignore_value2text_con except TypeError: vals = (P1 * X**2 + P2 * X + P3) / (P4 * X**2 + P5 * X + P6) - values = np.core.records.fromarrays( + values = np.rec.fromarrays( [vals] + [values[name] for name in names[1:]], dtype=[(name, vals.dtype, vals.shape[1:])] + [(name, values[name].dtype, values[name].shape[1:]) for name in names[1:]], @@ -3349,7 +3349,7 @@ def convert(self, values, as_object=False, as_bytes=False, ignore_value2text_con ret = ret.astype(bytes) ret = ret.reshape(shape) - values = np.core.records.fromarrays( + values = np.rec.fromarrays( [ret] + [values[name] for name in names[1:]], dtype=[(name, ret.dtype, ret.shape[1:])] + [(name, values[name].dtype, values[name].shape[1:]) for name in names[1:]], @@ -3521,7 +3521,7 @@ def convert(self, values, as_object=False, as_bytes=False, ignore_value2text_con ret = np.array(ret, dtype=bytes) ret = ret.reshape(shape) - values = np.core.records.fromarrays( + values = np.rec.fromarrays( [ret] + [values[name] for name in names[1:]], dtype=[(name, ret.dtype, ret.shape[1:])] + [(name, values[name].dtype, values[name].shape[1:]) for name in names[1:]], diff --git a/src/asammdf/mdf.py b/src/asammdf/mdf.py index a8956a4f7..ae3d34c66 100644 --- a/src/asammdf/mdf.py +++ b/src/asammdf/mdf.py @@ -5159,7 +5159,7 @@ def _extract_lin_logging( bus_data_bytes = data_bytes original_msg_ids = original_ids - unique_ids = np.unique(np.core.records.fromarrays([bus_msg_ids, bus_msg_ids])) + unique_ids = np.unique(np.rec.fromarrays([bus_msg_ids, bus_msg_ids])) total_unique_ids = total_unique_ids | {tuple(int(e) for e in f) for f in unique_ids} From 49b0bb5a4b963070ea410fd87e086e4b27cd9bdc Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Wed, 10 Jan 2024 12:42:12 +0200 Subject: [PATCH 34/38] more numpy 2 changes --- src/asammdf/blocks/utils.py | 6 +++--- src/asammdf/gui/widgets/plot.py | 4 ++-- src/asammdf/mdf.py | 18 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/asammdf/blocks/utils.py b/src/asammdf/blocks/utils.py index fdbf8cb54..b4df8d26e 100644 --- a/src/asammdf/blocks/utils.py +++ b/src/asammdf/blocks/utils.py @@ -1387,7 +1387,7 @@ def components( values = channel[name] if values.dtype.byteorder not in target_byte_order: - values = values.byteswap().newbyteorder() + values = values.view(values.dtype.newbyteorder()) if len(values.shape) > 1: values = Series( @@ -1406,7 +1406,7 @@ def components( values = channel[name] if values.dtype.byteorder not in target_byte_order: - values = values.byteswap().newbyteorder() + values = values.view(values.dtype.newbyteorder()) if not only_basenames: axis_name = unique_names.get_unique_name(f"{name_}.{name}") @@ -1442,7 +1442,7 @@ def components( else: if values.dtype.byteorder not in target_byte_order: - values = values.byteswap().newbyteorder() + values = values.view(values.dtype.newbyteorder()) if not only_basenames: name_ = unique_names.get_unique_name( diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index e50b81eaa..b2bc93fb6 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -220,10 +220,10 @@ def __init__(self, signal, index=0, trim_info=None, duplication=1, allow_trim=Tr self.timestamps = self.timestamps[~nans] if self.samples.dtype.byteorder not in target_byte_order: - self.samples = self.samples.byteswap().newbyteorder() + self.samples = self.samples.view(self.samples.dtype.newbyteorder()) if self.timestamps.dtype.byteorder not in target_byte_order: - self.timestamps = self.timestamps.byteswap().newbyteorder() + self.timestamps = self.timestamps.view(self.timestamps.dtype.newbyteorder()) if self.timestamps.dtype != float64: self.timestamps = self.timestamps.astype(float64) diff --git a/src/asammdf/mdf.py b/src/asammdf/mdf.py index ae3d34c66..e7e369b52 100644 --- a/src/asammdf/mdf.py +++ b/src/asammdf/mdf.py @@ -4082,7 +4082,7 @@ def iter_to_dataframe( diffs = np.diff(group_master, prepend=-np.inf) > 0 if group_master.dtype.byteorder not in target_byte_order: - group_master = group_master.byteswap().newbyteorder() + group_master = group_master.view(group_master.dtype.newbyteorder()) if np.all(diffs): index = pd.Index(group_master, tupleize_cols=False) @@ -4100,7 +4100,7 @@ def iter_to_dataframe( size = len(index) for k, sig in enumerate(signals): if sig.timestamps.dtype.byteorder not in target_byte_order: - sig.timestamps = sig.timestamps.byteswap().newbyteorder() + sig.timestamps = sig.timestamps.view(sig.timestamps.dtype.newbyteorder()) sig_index = index if len(sig) == size else pd.Index(sig.timestamps, tupleize_cols=False) @@ -4114,7 +4114,7 @@ def iter_to_dataframe( channel_name = used_names.get_unique_name(channel_name) if sig.samples.dtype.byteorder not in target_byte_order: - sig.samples = sig.samples.byteswap().newbyteorder() + sig.samples = sig.samples.view(sig.samples.dtype.newbyteorder()) df[channel_name] = pd.Series( list(sig.samples), @@ -4145,7 +4145,7 @@ def iter_to_dataframe( unique = np.unique(sig.samples) if sig.samples.dtype.byteorder not in target_byte_order: - sig.samples = sig.samples.byteswap().newbyteorder() + sig.samples = sig.samples.view(sig.samples.dtype.newbyteorder()) if len(sig.samples) / len(unique) >= 2: df[channel_name] = pd.Series( @@ -4164,7 +4164,7 @@ def iter_to_dataframe( sig.samples = downcast(sig.samples) if sig.samples.dtype.byteorder not in target_byte_order: - sig.samples = sig.samples.byteswap().newbyteorder() + sig.samples = sig.samples.view(sig.samples.dtype.newbyteorder()) df[channel_name] = pd.Series( sig.samples, @@ -4458,7 +4458,7 @@ def to_dataframe( signals = [sig for sig in signals if len(sig)] if group_master.dtype.byteorder not in target_byte_order: - group_master = group_master.byteswap().newbyteorder() + ggroup_master = group_master.view(group_master.dtype.newbyteorder()) if signals: diffs = np.diff(group_master, prepend=-np.inf) > 0 @@ -4480,7 +4480,7 @@ def to_dataframe( size = len(index) for k, sig in enumerate(signals): if sig.timestamps.dtype.byteorder not in target_byte_order: - sig.timestamps = sig.timestamps.byteswap().newbyteorder() + sig.timestamps = sig.timestamps.view(sig.timestamps.dtype.newbyteorder()) sig_index = index if len(sig) == size else pd.Index(sig.timestamps, tupleize_cols=False) @@ -4494,7 +4494,7 @@ def to_dataframe( channel_name = used_names.get_unique_name(channel_name) if sig.samples.dtype.byteorder not in target_byte_order: - sig.samples = sig.samples.byteswap().newbyteorder() + sig.samples = sig.samples.view(sig.samples.dtype.newbyteorder()) df[channel_name] = pd.Series( list(sig.samples), @@ -4526,7 +4526,7 @@ def to_dataframe( sig.samples = downcast(sig.samples) if sig.samples.dtype.byteorder not in target_byte_order: - sig.samples = sig.samples.byteswap().newbyteorder() + sig.samples = sig.samples.view(sig.samples.dtype.newbyteorder()) df[channel_name] = pd.Series(sig.samples, index=sig_index, fastpath=True) From 8e223d9cb94a62ee0b18e63aa82c65ddb7ab0757 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Thu, 11 Jan 2024 15:08:26 +0200 Subject: [PATCH 35/38] correctly restore the tabular window filter --- src/asammdf/gui/widgets/mdi_area.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/asammdf/gui/widgets/mdi_area.py b/src/asammdf/gui/widgets/mdi_area.py index 156204f8f..e2ea4e356 100644 --- a/src/asammdf/gui/widgets/mdi_area.py +++ b/src/asammdf/gui/widgets/mdi_area.py @@ -3585,6 +3585,8 @@ def _load_tabular_window(self, window_info): w.setWindowTitle(generate_window_title(w, window_info["type"], window_info["title"])) + mode = tabular.format_selection.currentText() + filter_count = 0 available_columns = [signals.index.name, *signals.columns] for filter_info in window_info["configuration"]["filters"]: @@ -3597,7 +3599,12 @@ def _load_tabular_window(self, window_info): filter.relation.setCurrentText(filter_info["relation"]) filter.column.setCurrentText(filter_info["column"]) filter.op.setCurrentText(filter_info["op"]) - filter.target.setText(str(filter_info["target"]).strip('"')) + if mode == "phys": + filter.target.setText(str(filter_info["target"]).strip('"')) + elif mode == "hex": + filter.target.setText(hex(filter_info["target"]).strip('"')) + elif mode == "bin": + filter.target.setText(bin(filter_info["target"]).strip('"')) filter.validate_target() filter_count += 1 From 84693affaeb13a021a8f8f4a220d2ec2429b49c6 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Thu, 11 Jan 2024 15:07:04 +0200 Subject: [PATCH 36/38] consider the DPI when drawing --- src/asammdf/gui/__init__.py | 4 +- src/asammdf/gui/asammdfgui.py | 1 - src/asammdf/gui/widgets/cursor.py | 22 ++++++- src/asammdf/gui/widgets/formated_axis.py | 78 +++++++++++++++++++++--- src/asammdf/gui/widgets/plot.py | 70 +++++++++++++++------ 5 files changed, 145 insertions(+), 30 deletions(-) diff --git a/src/asammdf/gui/__init__.py b/src/asammdf/gui/__init__.py index ea242eb25..b11fce72e 100644 --- a/src/asammdf/gui/__init__.py +++ b/src/asammdf/gui/__init__.py @@ -2,4 +2,6 @@ os.environ["QT_API"] = "pyside6" os.environ["PYQTGRAPH_QT_LIB"] = "PySide6" -os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1" + +if "QT_ENABLE_HIGHDPI_SCALING" not in os.environ: + os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1" diff --git a/src/asammdf/gui/asammdfgui.py b/src/asammdf/gui/asammdfgui.py index 651bd4a72..bf21cc5df 100644 --- a/src/asammdf/gui/asammdfgui.py +++ b/src/asammdf/gui/asammdfgui.py @@ -4,7 +4,6 @@ os.environ["QT_API"] = "pyside6" os.environ["PYQTGRAPH_QT_LIB"] = "PySide6" -os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "0" os.environ["PYSIDE6_OPTION_PYTHON_ENUM"] = "2" alternative_sitepacakges = os.environ.get("ASAMMDF_PYTHONPATH", "") diff --git a/src/asammdf/gui/widgets/cursor.py b/src/asammdf/gui/widgets/cursor.py index 7a804940a..f2ed1247c 100644 --- a/src/asammdf/gui/widgets/cursor.py +++ b/src/asammdf/gui/widgets/cursor.py @@ -138,7 +138,11 @@ def paint(self, paint, *args, plot=None, uuid=None): rect = plot.viewbox.sceneBoundingRect() delta = rect.x() height = rect.height() - width = rect.x() + rect.width() + + px, py = plot.px, plot.py + + plot.px = (plot.x_range[1] - plot.x_range[0]) / rect.width() + plot.py = rect.height() x, y = plot.scale_curve_to_pixmap( position, @@ -191,6 +195,8 @@ def paint(self, paint, *args, plot=None, uuid=None): pix = QtGui.QPixmap(":/erase.png").scaled(16, 16) paint.drawPixmap(QtCore.QPointF(rect.x() + rect.width() - 17, rect.y() + 1), pix) + plot.px, plot.py = px, py + def set_value(self, value): self.setPos(value) @@ -296,6 +302,11 @@ def paint(self, paint, *args, plot=None, uuid=None): height = rect.height() width = rect.x() + rect.width() + px, py = plot.px, plot.py + + plot.px = (plot.x_range[1] - plot.x_range[0]) / rect.width() + plot.py = rect.height() + if not self.show_circle and not self.show_horizontal_line or not uuid: x, y = plot.scale_curve_to_pixmap( position, @@ -356,6 +367,8 @@ def paint(self, paint, *args, plot=None, uuid=None): ) paint.drawLine(QtCore.QPointF(x, 0), QtCore.QPointF(x, height)) + plot.px, plot.py = px, py + def _computeBoundingRect(self): # br = UIGraphicsItem.boundingRect(self) vr = self.viewRect() # bounds of containing ViewBox mapped to local coords. @@ -467,6 +480,11 @@ def paint(self, p, *args, plot=None, uuid=None): delta = rect.x() height = rect.height() + px, py = plot.px, plot.py + + plot.px = (plot.x_range[1] - plot.x_range[0]) / rect.width() + plot.py = rect.height() + x1, y1 = plot.scale_curve_to_pixmap( self.lines[0].value(), 0, @@ -495,3 +513,5 @@ def paint(self, p, *args, plot=None, uuid=None): p.drawRect(rect) for line in self.lines: line.paint(p, *args, plot=plot, uuid=uuid) + + plot.px, plot.py = px, py diff --git a/src/asammdf/gui/widgets/formated_axis.py b/src/asammdf/gui/widgets/formated_axis.py index 38dbe639a..775befce9 100644 --- a/src/asammdf/gui/widgets/formated_axis.py +++ b/src/asammdf/gui/widgets/formated_axis.py @@ -1,4 +1,5 @@ from datetime import datetime, timedelta, timezone +from traceback import format_exc from PySide6 import QtCore, QtGui, QtWidgets @@ -492,13 +493,40 @@ def wheelEvent(self, event): event.accept() + def drawPicture(self, p, axisSpec, tickSpecs, textSpecs, ratio=1.0): + p.setRenderHint(p.RenderHint.Antialiasing, False) + p.setRenderHint(p.RenderHint.TextAntialiasing, True) + + ## draw long line along axis + pen, p1, p2 = axisSpec + p.setPen(pen) + p.drawLine(p1, p2) + # p.translate(0.5,0) ## resolves some damn pixel ambiguity + + ## draw ticks + for pen, p1, p2 in tickSpecs: + p.setPen(pen) + p.drawLine(p1, p2) + + # Draw all text + if self.style["tickFont"] is not None: + p.setFont(self.style["tickFont"]) + p.setPen(self.textPen()) + bounding = self.boundingRect().toAlignedRect() + bounding.setSize(bounding.size() * ratio) + bounding.moveTo(bounding.topLeft() * ratio) + p.setClipRect(bounding) + for rect, flags, text in textSpecs: + p.drawText(rect, int(flags), text) + def paint(self, p, opt, widget): rect = self.boundingRect() width = rect.width() + ratio = widget.devicePixelRatio() if widget else 1.0 if self.picture is None: try: - picture = QtGui.QPixmap(width, rect.height()) + picture = QtGui.QPixmap(int(width * ratio), int(rect.height() * ratio)) picture.fill(self.background) painter = QtGui.QPainter() @@ -508,26 +536,31 @@ def paint(self, p, opt, widget): painter.setCompositionMode(QtGui.QPainter.CompositionMode.CompositionMode_SourceOver) if self.style["tickFont"]: painter.setFont(self.style["tickFont"]) - specs = self.generateDrawSpecs(painter) + specs = self.generateDrawSpecs(painter, ratio) if specs is not None: - self.drawPicture(painter, *specs) + self.drawPicture(painter, *specs, ratio) if self.minus is not None: painter.drawPixmap( - QtCore.QPoint(int(rect.x()) + 5, 6), - self.minus.pixmap.scaled(BUTTON_SIZE, BUTTON_SIZE), + QtCore.QPoint(int(rect.x() * ratio) + 5 * ratio, 6 * ratio), + self.minus.pixmap.scaled(BUTTON_SIZE * ratio, BUTTON_SIZE * ratio), ) painter.drawPixmap( - QtCore.QPoint(int(rect.x()) + 5, 27), - self.plus.pixmap.scaled(BUTTON_SIZE, BUTTON_SIZE), + QtCore.QPoint((int(rect.x()) + 5) * ratio, 27 * ratio), + self.plus.pixmap.scaled(BUTTON_SIZE * ratio, BUTTON_SIZE * ratio), ) if self.orientation in ("left", "right"): painter.setPen(self._pen) - label_rect = QtCore.QRectF(1, 1, rect.height() - (28 + BUTTON_SIZE), rect.width()) - painter.translate(rect.bottomLeft()) + label_rect = QtCore.QRectF( + 1 * ratio, + 1 * ratio, + rect.height() * ratio - (28 + BUTTON_SIZE) * ratio, + rect.width() * ratio, + ) + painter.translate(rect.bottomLeft() * ratio) painter.rotate(-90) painter.setRenderHint(painter.RenderHint.TextAntialiasing, True) @@ -539,11 +572,17 @@ def paint(self, p, opt, widget): painter.rotate(90) painter.resetTransform() + except: + print(format_exc()) + finally: painter.end() + + picture.setDevicePixelRatio(widget.devicePixelRatio() if widget else 1.0) + self.picture = picture - def generateDrawSpecs(self, p): + def generateDrawSpecs(self, p, ratio): """ Calls tickValues() and tickStrings() to determine where and how ticks should be drawn, then generates from this a set of drawing commands to be @@ -804,4 +843,23 @@ def generateDrawSpecs(self, p): self.tickSpecs = tickSpecs + axisSpec = ( + axisSpec[0], + axisSpec[1] * ratio, + axisSpec[2] * ratio, + ) + + bounds.setSize(bounds.size() * ratio) + + for spec in textSpecs: + spec[0].setSize(spec[0].size() * ratio) + spec[0].moveTo(spec[0].topLeft() * ratio) + + for i, spec in enumerate(tickSpecs): + tickSpecs[i] = ( + spec[0], + spec[1] * ratio, + spec[2] * ratio, + ) + return (axisSpec, tickSpecs, textSpecs) diff --git a/src/asammdf/gui/widgets/plot.py b/src/asammdf/gui/widgets/plot.py index b2bc93fb6..64fa3f970 100644 --- a/src/asammdf/gui/widgets/plot.py +++ b/src/asammdf/gui/widgets/plot.py @@ -4206,6 +4206,7 @@ def dragEnterEvent(self, e): super().dragEnterEvent(e) def draw_grids(self, paint, event_rect): + ratio = self.devicePixelRatio() if self.y_axis.grid or self.x_axis.grid: rect = self.viewbox.sceneBoundingRect() y_delta = rect.y() @@ -4215,7 +4216,7 @@ def draw_grids(self, paint, event_rect): for pen, p1, p2 in self.y_axis.tickSpecs: pen2 = fn.mkPen(pen) pen2.setStyle(QtCore.Qt.PenStyle.DashLine) - y_pos = p1.y() + y_delta + y_pos = p1.y() / ratio + y_delta paint.setPen(pen2) paint.drawLine( QtCore.QPointF(0, y_pos), @@ -4226,7 +4227,7 @@ def draw_grids(self, paint, event_rect): for pen, p1, p2 in self.x_axis.tickSpecs: pen2 = fn.mkPen(pen) pen2.setStyle(QtCore.Qt.PenStyle.DashLine) - x_pos = p1.x() + x_delta + x_pos = p1.x() / ratio + x_delta paint.setPen(pen2) paint.drawLine( QtCore.QPointF(x_pos, 0), @@ -4515,15 +4516,23 @@ def keyPressEvent(self, event): elif key == QtCore.Qt.Key.Key_G: if modifier == QtCore.Qt.KeyboardModifier.NoModifier: - y = self.plotItem.ctrl.yGridCheck.isChecked() - x = self.plotItem.ctrl.xGridCheck.isChecked() + y = self.y_axis.grid + x = self.x_axis.grid + # y = self.plotItem.ctrl.yGridCheck.isChecked() + # x = self.plotItem.ctrl.xGridCheck.isChecked() if x and y: - self.plotItem.showGrid(x=False, y=False) + self.y_axis.grid = False + self.x_axis.grid = False elif x: - self.plotItem.showGrid(x=True, y=True) + self.y_axis.grid = True + self.x_axis.grid = True else: - self.plotItem.showGrid(x=True, y=False) + self.y_axis.grid = False + self.x_axis.grid = True + + self.x_axis.picture = None + self.y_axis.picture = None self.update() @@ -5142,7 +5151,9 @@ def paintEvent(self, ev): super().paintEvent(ev) if self._pixmap is None: - self._pixmap = QtGui.QPixmap(event_rect.width(), event_rect.height()) + ratio = self.devicePixelRatio() + + self._pixmap = QtGui.QPixmap(int(event_rect.width() * ratio), int(event_rect.height() * ratio)) self._pixmap.fill(self.backgroundBrush().color()) paint = QtGui.QPainter() @@ -5153,16 +5164,14 @@ def paintEvent(self, ev): self.x_range, self.y_range = self.viewbox.viewRange() rect = self.viewbox.sceneBoundingRect() + rect.setSize(rect.size() * ratio) + rect.moveTo(rect.topLeft() * ratio) self.px = (self.x_range[1] - self.x_range[0]) / rect.width() self.py = rect.height() with_dots = self.with_dots - self.auto_clip_rect(paint) - - rect = self.viewbox.sceneBoundingRect() - delta = rect.x() x_start = self.x_range[0] @@ -5343,6 +5352,8 @@ def paintEvent(self, ev): paint.end() + self._pixmap.setDevicePixelRatio(self.devicePixelRatio()) + paint = QtGui.QPainter() vp = self.viewport() paint.begin(vp) @@ -5354,30 +5365,55 @@ def paintEvent(self, ev): if self.x_axis.picture is None: self.x_axis.paint(paint, None, None) + ratio = self.devicePixelRatio() + + r = self.y_axis.boundingRect() + r.setSize(self.y_axis.picture.size()) + r.moveTo(r.topLeft() * ratio) paint.drawPixmap( self.y_axis.sceneBoundingRect(), self.y_axis.picture, - self.y_axis.boundingRect(), + r, ) + + r = self.x_axis.boundingRect() + r.setSize(self.x_axis.picture.size()) + r.moveTo(r.topLeft() * ratio) paint.drawPixmap( self.x_axis.sceneBoundingRect(), self.x_axis.picture, - self.x_axis.boundingRect(), + r, ) for ax in self.axes: if isinstance(ax, FormatedAxis) and ax.isVisible(): if ax.picture is None: ax.paint(paint, None, None) + + r = ax.boundingRect() + r.setSize(ax.picture.size()) + r.moveTo(r.topLeft() * ratio) + paint.drawPixmap( ax.sceneBoundingRect(), ax.picture, - ax.boundingRect(), + r, ) - self.auto_clip_rect(paint) + r = self.viewbox.sceneBoundingRect() + r.setLeft(r.left() + 5) + r.setSize(r.size() * ratio) + r.moveTo(r.topLeft() * ratio) - paint.drawPixmap(event_rect, self._pixmap, event_rect) + t = self.viewbox.sceneBoundingRect() + t = self.viewbox.sceneBoundingRect() + t.setLeft(t.left() + 5) + + paint.setClipping(False) + paint.drawPixmap(t.toRect(), self._pixmap, r.toRect()) + paint.setClipping(True) + + self.auto_clip_rect(paint) self.draw_grids(paint, event_rect) From de1d0bff7518ca151af409e00781ddee6c7029e8 Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Fri, 12 Jan 2024 10:08:33 +0200 Subject: [PATCH 37/38] bump version --- src/asammdf/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asammdf/version.py b/src/asammdf/version.py index e1c7248cf..88280dfc5 100644 --- a/src/asammdf/version.py +++ b/src/asammdf/version.py @@ -1,3 +1,3 @@ """ asammdf version module """ -__version__ = "7.4.0.dev14" +__version__ = "7.4.0.dev15" From 23c6ae1f934327e899a3e5d57a10da4c83e1288b Mon Sep 17 00:00:00 2001 From: danielhrisca Date: Fri, 12 Jan 2024 11:02:51 +0200 Subject: [PATCH 38/38] ruff catches bug? --- src/asammdf/blocks/mdf_v4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asammdf/blocks/mdf_v4.py b/src/asammdf/blocks/mdf_v4.py index ba0b3999f..4862c85ab 100644 --- a/src/asammdf/blocks/mdf_v4.py +++ b/src/asammdf/blocks/mdf_v4.py @@ -7989,7 +7989,7 @@ def _yield_selected_signals( if group_index == index: master_index = idx - encodings = {group_index: [None] for groups_index in groups} + encodings = {group_index: [None] for group_index in groups} self._set_temporary_master(None) idx = 0