From eba4e30773f531d1d296f2441dde3df02ae636bf Mon Sep 17 00:00:00 2001
From: Petter <petter.kallstrom@liu.se>
Date: Fri, 28 Apr 2023 17:55:32 +0200
Subject: [PATCH] PlotWindow: Added `add_result(...)`. Refactored slightly

---
 b_asic/gui_utils/plot_window.py | 102 +++++++++++++++++++++++++++-----
 test/test_simulation_gui.py     |   9 +++
 2 files changed, 96 insertions(+), 15 deletions(-)

diff --git a/b_asic/gui_utils/plot_window.py b/b_asic/gui_utils/plot_window.py
index 755adada..ae1e5df6 100644
--- a/b_asic/gui_utils/plot_window.py
+++ b/b_asic/gui_utils/plot_window.py
@@ -38,7 +38,7 @@ class PlotWindow(QWidget):
     sim_result : dict
         Simulation results of the form obtained from :attr:`~b_asic.simulation.Simulation.results`.
         Alternative, sim_result can be a list, ['name1', sim_result1, 'name2', sim_result2, ...]
-    sfg_name : str, optional
+    figure_name : str, optional
         The name of the SFG.
     parent : optional
         The parent window.
@@ -47,7 +47,7 @@ class PlotWindow(QWidget):
     def __init__(
         self,
         sim_result: Mapping[ResultKey, Sequence[Num]],
-        sfg_name: Optional[str] = None,
+        figure_name: Optional[str] = None,
     ):
         super().__init__()
         self.setWindowFlags(
@@ -57,8 +57,8 @@ class PlotWindow(QWidget):
             | Qt.WindowMaximizeButtonHint
         )
         title = (
-            f"Simulation results: {sfg_name}"
-            if sfg_name is not None
+            f"Simulation results: {figure_name}"
+            if figure_name is not None
             else "Simulation results"
         )
         self.setWindowTitle(title)
@@ -238,8 +238,8 @@ class PlotWindow(QWidget):
         # Add additional checkboxes
         self._legend = self._plot_axes.legend()
         self._legend_checkbox = QCheckBox("&Legend")
-        self._legend_checkbox.stateChanged.connect(self._legend_checkbox_change)
         self._legend_checkbox.setCheckState(Qt.CheckState.Checked)
+        self._legend_checkbox.stateChanged.connect(self._legend_checkbox_change)
         self._legend_checkbox.setIcon(get_icon('legend'))
         listlayout.addWidget(self._legend_checkbox)
         # self.ontop_checkbox = QCheckBox("&On top")
@@ -261,12 +261,63 @@ class PlotWindow(QWidget):
         # self.plotcanvas.draw()
         self._auto_redraw = True
 
+    def add_result(self, name, result) -> None:
+        """
+        Add another result to the plot.
+
+        The new signals are added in natural order without sorting them.
+        ---
+        name: str
+          The name of the result
+        result: dict
+          The result from one simulation.
+        """
+        markers = ".ov<^>s+*xd|_"
+        ix = 0
+        self._auto_redraw = False
+        for key, res in result.items():
+            key2 = name + "." + key
+            ischecked = Qt.CheckState.Unchecked
+            if re.fullmatch(r"[0-9]+", key):
+                key2 = name + '.out' + key
+                ischecked = Qt.CheckState.Checked
+
+            if len(res) <= 100:
+                fmt = markers[ix] + '-'
+                ix = (ix + 1) % len(markers)
+            else:
+                fmt = '-'
+
+            def addline(key, vector, checked):
+                line = self._plot_axes.plot(np.real(res), fmt, label=key)
+                self._lines[key] = line[0]
+                list_item = QListWidgetItem(key)
+                list_item.setCheckState(
+                    Qt.CheckState.Unchecked
+                )  # will add it if checked
+                self._checklist.addItem(list_item)
+                self._lines[
+                    key
+                ].remove()  # remove the line from plot. Keep it in _lines.
+                list_item.setCheckState(checked)  # will add it if checked
+
+            if all(np.imag(np.real_if_close(res)) == 0):
+                # real: add one line with corresponding checkbox
+                addline(key2, np.real(res), ischecked)
+            else:
+                # complex: add '_re', '_im', '_mag', '_ang'
+                addline(key2 + "_re", np.real(res), ischecked)
+                addline(key2 + "_im", np.imag(res), ischecked)
+                addline(key2 + "_mag", np.absolute(res), Qt.CheckState.Unchecked)
+                addline(key2 + "_ang", np.angle(res), Qt.CheckState.Unchecked)
+        self._auto_redraw = True
+        self._update_legend()
+        self._plot_canvas.draw()
+
     def _legend_checkbox_change(self, check_state):
         self._legend.set(visible=(check_state == Qt.CheckState.Checked))
-        if self._auto_redraw:
-            if check_state == Qt.CheckState.Checked:
-                self._legend = self._plot_axes.legend()
-            self._plot_canvas.draw()
+        self._update_legend()
+        self._plot_canvas.draw()
 
     # def _ontop_checkbox_change(self, checkState):
     # Bugg: It seems the window closes if you change the WindowStaysOnTopHint.
@@ -281,10 +332,12 @@ class PlotWindow(QWidget):
             self._checklist.item(x).setCheckState(Qt.CheckState.Checked)
         self._auto_redraw = True
         self._update_legend()
+        self._plot_canvas.draw()
 
     def _update_legend(self):
-        self._legend = self._plot_axes.legend()
-        self._plot_canvas.draw()
+        # if self._legend_checkbox.checkState == Qt.CheckState.Checked:
+        if self._legend_checkbox.isChecked():
+            self._legend = self._plot_axes.legend()
 
     def _button_none_click(self, event):
         self._auto_redraw = False
@@ -292,6 +345,7 @@ class PlotWindow(QWidget):
             self._checklist.item(x).setCheckState(Qt.CheckState.Unchecked)
         self._auto_redraw = True
         self._update_legend()
+        self._plot_canvas.draw()
 
     def _item_change(self, listitem):
         key = listitem.text()
@@ -301,6 +355,7 @@ class PlotWindow(QWidget):
             self._lines[key].remove()
         if self._auto_redraw:
             self._update_legend()
+            self._plot_canvas.draw()
 
     def _relim(self, event=None):
         self._plot_axes.relim(True)
@@ -312,6 +367,13 @@ class PlotWindow(QWidget):
 
 def start_simulation_dialog(
     sim_results: Dict[str, List[complex]], sfg_name: Optional[str] = None
+):
+    """Deprecated. Use `show_simulation_result` instead."""
+    show_simulation_result(sim_results, sfg_name)
+
+
+def show_simulation_result(
+    sim_results: Dict[str, List[complex]], figure_name: Optional[str] = None
 ):
     """
     Display the simulation results window.
@@ -321,14 +383,14 @@ def start_simulation_dialog(
     sim_results : dict
         Simulation results of the form obtained from :attr:`~b_asic.simulation.Simulation.results`.
         Alternative, sim_result can be a list, ['name1', sim_result1, 'name2', sim_result2, ...]
-    sfg_name : str, optional
+    figure_name : str, optional
         The name of the SFG.
     """
     if not QApplication.instance():
         app = QApplication(sys.argv)
     else:
         app = QApplication.instance()
-    win = PlotWindow(sim_result=sim_results, sfg_name=sfg_name)
+    win = PlotWindow(sim_result=sim_results, figure_name=figure_name)
     win.show()
     app.exec_()
 
@@ -360,8 +422,18 @@ if __name__ == "__main__":
         't3': [0, 0, 0, 1],
     }
     res3 = {
-        '0': np.random.rand(200).tolist(),
+        #'0': np.random.rand(50).tolist(),
+        '0': np.random.rand(50),
         '1': np.random.rand(200).tolist(),
     }
+    res4 = {
+        '0': np.random.rand(60).tolist(),
+        '1': np.random.rand(220).tolist(),
+        't4': np.random.rand(50).tolist(),
+    }
     # start_simulation_dialog(res3)
-    start_simulation_dialog(['Real', res1, 'Cpx', res2, res3], "Test data")
+    app = QApplication(sys.argv)
+    win2 = PlotWindow(['Real', res1, 'Cpx', res2, res3], "Test data")
+    win2.add_result('res4', res4)
+    win2.show()
+    app.exec_()
diff --git a/test/test_simulation_gui.py b/test/test_simulation_gui.py
index 2f5ee5b1..158e68b4 100644
--- a/test/test_simulation_gui.py
+++ b/test/test_simulation_gui.py
@@ -67,12 +67,21 @@ def test_start_with_multi_data(qtbot):
         'in1': [1, 0, 0, 0],
         'in2': [0.1, 2, 0, 0],
     }
+    sim_res3 = {
+        '1': [1, 2, 3, 4],
+        't4': [2, 3, 3, 2],
+    }
     widget = PlotWindow(['simReal', sim_res1, 'simCpx', sim_res2], "Test vector")
     qtbot.addWidget(widget)
 
     assert widget._checklist.count() == 15
     assert len(widget._plot_axes.lines) == 4
 
+    widget.add_result('foo', sim_res3)
+
+    assert widget._checklist.count() == 17
+    assert len(widget._plot_axes.lines) == 5
+
 
 def test_click_buttons(qtbot):
     sim_res = {
-- 
GitLab