From cf82fcabfd5b7d4168c9088a2c3f0236231f68ab Mon Sep 17 00:00:00 2001 From: Sacha Schutz Date: Sun, 17 Jan 2021 19:36:08 +0100 Subject: Add new matplotlib example and rearrange dirs I add a widget showing a 2D gaussian with 2 inputs to adjust mu and sigma Pick-to: 6.0 Task-number: PYSIDE-841 Change-Id: I602b07943ebeb007332bc77c4372ef5a1db20422 Reviewed-by: Cristian Maureira-Fredes --- examples/external/matplotlib/requirements.txt | 1 - .../external/matplotlib/widget3d/requirements.txt | 2 + examples/external/matplotlib/widget3d/widget3d.py | 242 +++++++++++++++++++++ .../matplotlib/widget3d/widget3d.pyproject | 3 + examples/external/matplotlib/widget_3dplot.py | 242 --------------------- .../matplotlib/widget_gaussian/requirements.txt | 3 + .../matplotlib/widget_gaussian/widget_gaussian.py | 112 ++++++++++ .../widget_gaussian/widget_gaussian.pyproject | 3 + 8 files changed, 365 insertions(+), 243 deletions(-) delete mode 100644 examples/external/matplotlib/requirements.txt create mode 100644 examples/external/matplotlib/widget3d/requirements.txt create mode 100644 examples/external/matplotlib/widget3d/widget3d.py create mode 100644 examples/external/matplotlib/widget3d/widget3d.pyproject delete mode 100644 examples/external/matplotlib/widget_3dplot.py create mode 100644 examples/external/matplotlib/widget_gaussian/requirements.txt create mode 100644 examples/external/matplotlib/widget_gaussian/widget_gaussian.py create mode 100644 examples/external/matplotlib/widget_gaussian/widget_gaussian.pyproject diff --git a/examples/external/matplotlib/requirements.txt b/examples/external/matplotlib/requirements.txt deleted file mode 100644 index 6ccafc3f9..000000000 --- a/examples/external/matplotlib/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -matplotlib diff --git a/examples/external/matplotlib/widget3d/requirements.txt b/examples/external/matplotlib/widget3d/requirements.txt new file mode 100644 index 000000000..db5d81e01 --- /dev/null +++ b/examples/external/matplotlib/widget3d/requirements.txt @@ -0,0 +1,2 @@ +matplotlib +numpy diff --git a/examples/external/matplotlib/widget3d/widget3d.py b/examples/external/matplotlib/widget3d/widget3d.py new file mode 100644 index 000000000..874e7e439 --- /dev/null +++ b/examples/external/matplotlib/widget3d/widget3d.py @@ -0,0 +1,242 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +import numpy as np +from matplotlib.backends.backend_qt5agg import FigureCanvas +from matplotlib.figure import Figure +from mpl_toolkits.mplot3d import axes3d +from PySide6.QtCore import Qt, Slot +from PySide6.QtGui import QAction, QKeySequence +from PySide6.QtWidgets import (QApplication, QComboBox, QHBoxLayout, + QHeaderView, QLabel, QMainWindow, QSlider, + QTableWidget, QTableWidgetItem, QVBoxLayout, + QWidget) + + +"""This example implements the interaction between Qt Widgets and a 3D +matplotlib plot""" + + +class ApplicationWindow(QMainWindow): + def __init__(self, parent=None): + QMainWindow.__init__(self, parent) + + self.column_names = ["Column A", "Column B", "Column C"] + + # Central widget + self._main = QWidget() + self.setCentralWidget(self._main) + + # Main menu bar + self.menu = self.menuBar() + self.menu_file = self.menu.addMenu("File") + exit = QAction("Exit", self, triggered=qApp.quit) + self.menu_file.addAction(exit) + + self.menu_about = self.menu.addMenu("&About") + about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents), + triggered=qApp.aboutQt) + self.menu_about.addAction(about) + + # Figure (Left) + self.fig = Figure(figsize=(5, 3)) + self.canvas = FigureCanvas(self.fig) + + # Sliders (Left) + self.slider_azim = QSlider(minimum=0, maximum=360, orientation=Qt.Horizontal) + self.slider_elev = QSlider(minimum=0, maximum=360, orientation=Qt.Horizontal) + + self.slider_azim_layout = QHBoxLayout() + self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.minimum()))) + self.slider_azim_layout.addWidget(self.slider_azim) + self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.maximum()))) + + self.slider_elev_layout = QHBoxLayout() + self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.minimum()))) + self.slider_elev_layout.addWidget(self.slider_elev) + self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.maximum()))) + + # Table (Right) + self.table = QTableWidget() + header = self.table.horizontalHeader() + header.setSectionResizeMode(QHeaderView.Stretch) + + # ComboBox (Right) + self.combo = QComboBox() + self.combo.addItems(["Wired", "Surface", "Triangular Surface", "Sphere"]) + + # Right layout + rlayout = QVBoxLayout() + rlayout.setContentsMargins(1, 1, 1, 1) + rlayout.addWidget(QLabel("Plot type:")) + rlayout.addWidget(self.combo) + rlayout.addWidget(self.table) + + # Left layout + llayout = QVBoxLayout() + rlayout.setContentsMargins(1, 1, 1, 1) + llayout.addWidget(self.canvas, 88) + llayout.addWidget(QLabel("Azimuth:"), 1) + llayout.addLayout(self.slider_azim_layout, 5) + llayout.addWidget(QLabel("Elevation:"), 1) + llayout.addLayout(self.slider_elev_layout, 5) + + # Main layout + layout = QHBoxLayout(self._main) + layout.addLayout(llayout, 70) + layout.addLayout(rlayout, 30) + + # Signal and Slots connections + self.combo.currentTextChanged.connect(self.combo_option) + self.slider_azim.valueChanged.connect(self.rotate_azim) + self.slider_elev.valueChanged.connect(self.rotate_elev) + + # Initial setup + self.plot_wire() + self._ax.view_init(30, 30) + self.slider_azim.setValue(30) + self.slider_elev.setValue(30) + self.fig.canvas.mpl_connect("button_release_event", self.on_click) + + # Matplotlib slot method + def on_click(self, event): + azim, elev = self._ax.azim, self._ax.elev + self.slider_azim.setValue(azim + 180) + self.slider_elev.setValue(elev + 180) + + # Utils methods + + def set_table_data(self, X, Y, Z): + for i in range(len(X)): + self.table.setItem(i, 0, QTableWidgetItem("{:.2f}".format(X[i]))) + self.table.setItem(i, 1, QTableWidgetItem("{:.2f}".format(Y[i]))) + self.table.setItem(i, 2, QTableWidgetItem("{:.2f}".format(Z[i]))) + + def set_canvas_table_configuration(self, row_count, data): + self.fig.set_canvas(self.canvas) + self._ax = self.canvas.figure.add_subplot(projection="3d") + + self._ax.set_xlabel(self.column_names[0]) + self._ax.set_ylabel(self.column_names[1]) + self._ax.set_zlabel(self.column_names[2]) + + self.table.setRowCount(row_count) + self.table.setColumnCount(3) + self.table.setHorizontalHeaderLabels(self.column_names) + self.set_table_data(data[0], data[1], data[2]) + + # Plot methods + + def plot_wire(self): + # Data + self.X, self.Y, self.Z = axes3d.get_test_data(0.03) + + self.set_canvas_table_configuration(len(self.X[0]), (self.X[0], self.Y[0], self.Z[0])) + self._ax.plot_wireframe(self.X, self.Y, self.Z, rstride=10, cstride=10, cmap="viridis") + self.canvas.draw() + + def plot_surface(self): + # Data + self.X, self.Y = np.meshgrid(np.linspace(-6, 6, 30), np.linspace(-6, 6, 30)) + self.Z = np.sin(np.sqrt(self.X ** 2 + self.Y ** 2)) + + self.set_canvas_table_configuration(len(self.X[0]), (self.X[0], self.Y[0], self.Z[0])) + self._ax.plot_surface(self.X, self.Y, self.Z, + rstride=1, cstride=1, cmap="viridis", edgecolor="none") + self.canvas.draw() + + def plot_triangular_surface(self): + # Data + radii = np.linspace(0.125, 1.0, 8) + angles = np.linspace(0, 2 * np.pi, 36, endpoint=False)[..., np.newaxis] + self.X = np.append(0, (radii * np.cos(angles)).flatten()) + self.Y = np.append(0, (radii * np.sin(angles)).flatten()) + self.Z = np.sin(-self.X * self.Y) + + self.set_canvas_table_configuration(len(self.X), (self.X, self.Y, self.Z)) + self._ax.plot_trisurf(self.X, self.Y, self.Z, linewidth=0.2, antialiased=True) + self.canvas.draw() + + def plot_sphere(self): + # Data + u = np.linspace(0, 2 * np.pi, 100) + v = np.linspace(0, np.pi, 100) + self.X = 10 * np.outer(np.cos(u), np.sin(v)) + self.Y = 10 * np.outer(np.sin(u), np.sin(v)) + self.Z = 9 * np.outer(np.ones(np.size(u)), np.cos(v)) + + self.set_canvas_table_configuration(len(self.X), (self.X[0], self.Y[0], self.Z[0])) + self._ax.plot_surface(self.X, self.Y, self.Z) + self.canvas.draw() + + # Slots + + @Slot() + def combo_option(self, text): + if text == "Wired": + self.plot_wire() + elif text == "Surface": + self.plot_surface() + elif text == "Triangular Surface": + self.plot_triangular_surface() + elif text == "Sphere": + self.plot_sphere() + + @Slot() + def rotate_azim(self, value): + self._ax.view_init(self._ax.elev, value) + self.fig.set_canvas(self.canvas) + self.canvas.draw() + + @Slot() + def rotate_elev(self, value): + self._ax.view_init(value, self._ax.azim) + self.fig.set_canvas(self.canvas) + self.canvas.draw() + + +if __name__ == "__main__": + app = QApplication(sys.argv) + w = ApplicationWindow() + w.setFixedSize(1280, 720) + w.show() + app.exec_() diff --git a/examples/external/matplotlib/widget3d/widget3d.pyproject b/examples/external/matplotlib/widget3d/widget3d.pyproject new file mode 100644 index 000000000..6b25c5be7 --- /dev/null +++ b/examples/external/matplotlib/widget3d/widget3d.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["widget3d.py"] +} diff --git a/examples/external/matplotlib/widget_3dplot.py b/examples/external/matplotlib/widget_3dplot.py deleted file mode 100644 index 874e7e439..000000000 --- a/examples/external/matplotlib/widget_3dplot.py +++ /dev/null @@ -1,242 +0,0 @@ -############################################################################# -## -## Copyright (C) 2020 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# - -import sys - -import numpy as np -from matplotlib.backends.backend_qt5agg import FigureCanvas -from matplotlib.figure import Figure -from mpl_toolkits.mplot3d import axes3d -from PySide6.QtCore import Qt, Slot -from PySide6.QtGui import QAction, QKeySequence -from PySide6.QtWidgets import (QApplication, QComboBox, QHBoxLayout, - QHeaderView, QLabel, QMainWindow, QSlider, - QTableWidget, QTableWidgetItem, QVBoxLayout, - QWidget) - - -"""This example implements the interaction between Qt Widgets and a 3D -matplotlib plot""" - - -class ApplicationWindow(QMainWindow): - def __init__(self, parent=None): - QMainWindow.__init__(self, parent) - - self.column_names = ["Column A", "Column B", "Column C"] - - # Central widget - self._main = QWidget() - self.setCentralWidget(self._main) - - # Main menu bar - self.menu = self.menuBar() - self.menu_file = self.menu.addMenu("File") - exit = QAction("Exit", self, triggered=qApp.quit) - self.menu_file.addAction(exit) - - self.menu_about = self.menu.addMenu("&About") - about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents), - triggered=qApp.aboutQt) - self.menu_about.addAction(about) - - # Figure (Left) - self.fig = Figure(figsize=(5, 3)) - self.canvas = FigureCanvas(self.fig) - - # Sliders (Left) - self.slider_azim = QSlider(minimum=0, maximum=360, orientation=Qt.Horizontal) - self.slider_elev = QSlider(minimum=0, maximum=360, orientation=Qt.Horizontal) - - self.slider_azim_layout = QHBoxLayout() - self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.minimum()))) - self.slider_azim_layout.addWidget(self.slider_azim) - self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.maximum()))) - - self.slider_elev_layout = QHBoxLayout() - self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.minimum()))) - self.slider_elev_layout.addWidget(self.slider_elev) - self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.maximum()))) - - # Table (Right) - self.table = QTableWidget() - header = self.table.horizontalHeader() - header.setSectionResizeMode(QHeaderView.Stretch) - - # ComboBox (Right) - self.combo = QComboBox() - self.combo.addItems(["Wired", "Surface", "Triangular Surface", "Sphere"]) - - # Right layout - rlayout = QVBoxLayout() - rlayout.setContentsMargins(1, 1, 1, 1) - rlayout.addWidget(QLabel("Plot type:")) - rlayout.addWidget(self.combo) - rlayout.addWidget(self.table) - - # Left layout - llayout = QVBoxLayout() - rlayout.setContentsMargins(1, 1, 1, 1) - llayout.addWidget(self.canvas, 88) - llayout.addWidget(QLabel("Azimuth:"), 1) - llayout.addLayout(self.slider_azim_layout, 5) - llayout.addWidget(QLabel("Elevation:"), 1) - llayout.addLayout(self.slider_elev_layout, 5) - - # Main layout - layout = QHBoxLayout(self._main) - layout.addLayout(llayout, 70) - layout.addLayout(rlayout, 30) - - # Signal and Slots connections - self.combo.currentTextChanged.connect(self.combo_option) - self.slider_azim.valueChanged.connect(self.rotate_azim) - self.slider_elev.valueChanged.connect(self.rotate_elev) - - # Initial setup - self.plot_wire() - self._ax.view_init(30, 30) - self.slider_azim.setValue(30) - self.slider_elev.setValue(30) - self.fig.canvas.mpl_connect("button_release_event", self.on_click) - - # Matplotlib slot method - def on_click(self, event): - azim, elev = self._ax.azim, self._ax.elev - self.slider_azim.setValue(azim + 180) - self.slider_elev.setValue(elev + 180) - - # Utils methods - - def set_table_data(self, X, Y, Z): - for i in range(len(X)): - self.table.setItem(i, 0, QTableWidgetItem("{:.2f}".format(X[i]))) - self.table.setItem(i, 1, QTableWidgetItem("{:.2f}".format(Y[i]))) - self.table.setItem(i, 2, QTableWidgetItem("{:.2f}".format(Z[i]))) - - def set_canvas_table_configuration(self, row_count, data): - self.fig.set_canvas(self.canvas) - self._ax = self.canvas.figure.add_subplot(projection="3d") - - self._ax.set_xlabel(self.column_names[0]) - self._ax.set_ylabel(self.column_names[1]) - self._ax.set_zlabel(self.column_names[2]) - - self.table.setRowCount(row_count) - self.table.setColumnCount(3) - self.table.setHorizontalHeaderLabels(self.column_names) - self.set_table_data(data[0], data[1], data[2]) - - # Plot methods - - def plot_wire(self): - # Data - self.X, self.Y, self.Z = axes3d.get_test_data(0.03) - - self.set_canvas_table_configuration(len(self.X[0]), (self.X[0], self.Y[0], self.Z[0])) - self._ax.plot_wireframe(self.X, self.Y, self.Z, rstride=10, cstride=10, cmap="viridis") - self.canvas.draw() - - def plot_surface(self): - # Data - self.X, self.Y = np.meshgrid(np.linspace(-6, 6, 30), np.linspace(-6, 6, 30)) - self.Z = np.sin(np.sqrt(self.X ** 2 + self.Y ** 2)) - - self.set_canvas_table_configuration(len(self.X[0]), (self.X[0], self.Y[0], self.Z[0])) - self._ax.plot_surface(self.X, self.Y, self.Z, - rstride=1, cstride=1, cmap="viridis", edgecolor="none") - self.canvas.draw() - - def plot_triangular_surface(self): - # Data - radii = np.linspace(0.125, 1.0, 8) - angles = np.linspace(0, 2 * np.pi, 36, endpoint=False)[..., np.newaxis] - self.X = np.append(0, (radii * np.cos(angles)).flatten()) - self.Y = np.append(0, (radii * np.sin(angles)).flatten()) - self.Z = np.sin(-self.X * self.Y) - - self.set_canvas_table_configuration(len(self.X), (self.X, self.Y, self.Z)) - self._ax.plot_trisurf(self.X, self.Y, self.Z, linewidth=0.2, antialiased=True) - self.canvas.draw() - - def plot_sphere(self): - # Data - u = np.linspace(0, 2 * np.pi, 100) - v = np.linspace(0, np.pi, 100) - self.X = 10 * np.outer(np.cos(u), np.sin(v)) - self.Y = 10 * np.outer(np.sin(u), np.sin(v)) - self.Z = 9 * np.outer(np.ones(np.size(u)), np.cos(v)) - - self.set_canvas_table_configuration(len(self.X), (self.X[0], self.Y[0], self.Z[0])) - self._ax.plot_surface(self.X, self.Y, self.Z) - self.canvas.draw() - - # Slots - - @Slot() - def combo_option(self, text): - if text == "Wired": - self.plot_wire() - elif text == "Surface": - self.plot_surface() - elif text == "Triangular Surface": - self.plot_triangular_surface() - elif text == "Sphere": - self.plot_sphere() - - @Slot() - def rotate_azim(self, value): - self._ax.view_init(self._ax.elev, value) - self.fig.set_canvas(self.canvas) - self.canvas.draw() - - @Slot() - def rotate_elev(self, value): - self._ax.view_init(value, self._ax.azim) - self.fig.set_canvas(self.canvas) - self.canvas.draw() - - -if __name__ == "__main__": - app = QApplication(sys.argv) - w = ApplicationWindow() - w.setFixedSize(1280, 720) - w.show() - app.exec_() diff --git a/examples/external/matplotlib/widget_gaussian/requirements.txt b/examples/external/matplotlib/widget_gaussian/requirements.txt new file mode 100644 index 000000000..26a2b438f --- /dev/null +++ b/examples/external/matplotlib/widget_gaussian/requirements.txt @@ -0,0 +1,3 @@ +matplotlib +scipy +numpy diff --git a/examples/external/matplotlib/widget_gaussian/widget_gaussian.py b/examples/external/matplotlib/widget_gaussian/widget_gaussian.py new file mode 100644 index 000000000..ba790af4e --- /dev/null +++ b/examples/external/matplotlib/widget_gaussian/widget_gaussian.py @@ -0,0 +1,112 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +import numpy as np +from scipy.stats import norm +from matplotlib import pyplot as plt +from matplotlib.figure import Figure +from matplotlib.backends.backend_qt5agg import FigureCanvas +from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT +from PySide6.QtCore import Qt, Slot +from PySide6.QtWidgets import ( + QApplication, + QWidget, + QDoubleSpinBox, + QVBoxLayout, + QHBoxLayout, +) + + +"""This example implements the interaction between Qt Widgets and a 2D +matplotlib plot showing a gaussian curve with scipy""" + + +class PlotWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + + #  create widgets + self.view = FigureCanvas(Figure(figsize=(5, 3))) + self.axes = self.view.figure.subplots() + self.toolbar = NavigationToolbar2QT(self.view, self) + self.mu_input = QDoubleSpinBox() + self.std_input = QDoubleSpinBox() + self.mu_input.setPrefix("μ: ") + self.std_input.setPrefix("σ: ") + self.std_input.setValue(10) + + #  Create layout + input_layout = QHBoxLayout() + input_layout.addWidget(self.mu_input) + input_layout.addWidget(self.std_input) + vlayout = QVBoxLayout() + vlayout.addWidget(self.toolbar) + vlayout.addWidget(self.view) + vlayout.addLayout(input_layout) + self.setLayout(vlayout) + + # connect inputs with on_change method + self.mu_input.valueChanged.connect(self.on_change) + self.std_input.valueChanged.connect(self.on_change) + + self.on_change() + + @Slot() + def on_change(self): + """ Update the plot with the current input values """ + mu = self.mu_input.value() + std = self.std_input.value() + + x = np.linspace(-100, 100) + y = norm.pdf(x, mu, std) + + self.axes.clear() + self.axes.plot(x, y) + self.view.draw() + + +if __name__ == "__main__": + + app = QApplication(sys.argv) + w = PlotWidget() + w.show() + sys.exit(app.exec_()) diff --git a/examples/external/matplotlib/widget_gaussian/widget_gaussian.pyproject b/examples/external/matplotlib/widget_gaussian/widget_gaussian.pyproject new file mode 100644 index 000000000..72c5adc78 --- /dev/null +++ b/examples/external/matplotlib/widget_gaussian/widget_gaussian.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["widget_gaussian.py"] +} -- cgit v1.2.3