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