diff options
Diffstat (limited to 'examples/external')
29 files changed, 769 insertions, 146 deletions
diff --git a/examples/external/matplotlib/widget3d/doc/widget3d.png b/examples/external/matplotlib/widget3d/doc/widget3d.png Binary files differnew file mode 100644 index 000000000..fa2ed5043 --- /dev/null +++ b/examples/external/matplotlib/widget3d/doc/widget3d.png diff --git a/examples/external/matplotlib/widget3d/doc/widget3d.rst b/examples/external/matplotlib/widget3d/doc/widget3d.rst new file mode 100644 index 000000000..b5c9fd8fd --- /dev/null +++ b/examples/external/matplotlib/widget3d/doc/widget3d.rst @@ -0,0 +1,9 @@ +Matplotlib Widget 3D Example +============================ + +A Python application that demonstrates how to combine matplotlib +with Qt Widget-based functionality. + +.. image:: widget3d.png + :width: 400 + :alt: Matplotlib Widget 3D Screenshot diff --git a/examples/external/matplotlib/requirements.txt b/examples/external/matplotlib/widget3d/requirements.txt index 6ccafc3f9..db5d81e01 100644 --- a/examples/external/matplotlib/requirements.txt +++ b/examples/external/matplotlib/widget3d/requirements.txt @@ -1 +1,2 @@ matplotlib +numpy diff --git a/examples/external/matplotlib/widget_3dplot.py b/examples/external/matplotlib/widget3d/widget3d.py index 6f47da31b..8bfcc4ca2 100644 --- a/examples/external/matplotlib/widget_3dplot.py +++ b/examples/external/matplotlib/widget3d/widget3d.py @@ -1,52 +1,15 @@ -############################################################################# -## -## 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$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import sys import numpy as np -from matplotlib.backends.backend_qt5agg import FigureCanvas +from matplotlib.backends.backend_qtagg import FigureCanvas from matplotlib.figure import Figure from mpl_toolkits.mplot3d import axes3d -from PySide2.QtCore import Qt, Slot -from PySide2.QtGui import QAction, QKeySequence -from PySide2.QtWidgets import (QApplication, QComboBox, QHBoxLayout, +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) @@ -69,12 +32,12 @@ class ApplicationWindow(QMainWindow): # Main menu bar self.menu = self.menuBar() self.menu_file = self.menu.addMenu("File") - exit = QAction("Exit", self, triggered=qApp.quit) + exit = QAction("Exit", self, triggered=qApp.quit) # noqa: F821 self.menu_file.addAction(exit) self.menu_about = self.menu.addMenu("&About") about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents), - triggered=qApp.aboutQt) + triggered=qApp.aboutQt) # noqa: F821 self.menu_about.addAction(about) # Figure (Left) @@ -82,18 +45,20 @@ class ApplicationWindow(QMainWindow): 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) + min = 0 + max = 360 + self.slider_azim = QSlider(minimum=min, maximum=max, orientation=Qt.Horizontal) + self.slider_elev = QSlider(minimum=min, maximum=max, orientation=Qt.Horizontal) self.slider_azim_layout = QHBoxLayout() - self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.minimum()))) + self.slider_azim_layout.addWidget(QLabel(f"{min}")) self.slider_azim_layout.addWidget(self.slider_azim) - self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.maximum()))) + self.slider_azim_layout.addWidget(QLabel(f"{max}")) self.slider_elev_layout = QHBoxLayout() - self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.minimum()))) + self.slider_elev_layout.addWidget(QLabel(f"{min}")) self.slider_elev_layout.addWidget(self.slider_elev) - self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.maximum()))) + self.slider_elev_layout.addWidget(QLabel(f"{max}")) # Table (Right) self.table = QTableWidget() @@ -147,9 +112,9 @@ class ApplicationWindow(QMainWindow): 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]))) + self.table.setItem(i, 0, QTableWidgetItem(f"{X[i]:.2f}")) + self.table.setItem(i, 1, QTableWidgetItem(f"{Y[i]:.2f}")) + self.table.setItem(i, 2, QTableWidgetItem(f"{Z[i]:.2f}")) def set_canvas_table_configuration(self, row_count, data): self.fig.set_canvas(self.canvas) @@ -239,4 +204,4 @@ if __name__ == "__main__": w = ApplicationWindow() w.setFixedSize(1280, 720) w.show() - app.exec_() + 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_gaussian/doc/widget_gaussian.png b/examples/external/matplotlib/widget_gaussian/doc/widget_gaussian.png Binary files differnew file mode 100644 index 000000000..e9fe16354 --- /dev/null +++ b/examples/external/matplotlib/widget_gaussian/doc/widget_gaussian.png diff --git a/examples/external/matplotlib/widget_gaussian/doc/widget_gaussian.rst b/examples/external/matplotlib/widget_gaussian/doc/widget_gaussian.rst new file mode 100644 index 000000000..467fb42c2 --- /dev/null +++ b/examples/external/matplotlib/widget_gaussian/doc/widget_gaussian.rst @@ -0,0 +1,9 @@ +Matplotlib Widget Gaussian Example +================================== + +A Python application that demonstrates how to interact with +matplotlib and scipy, combined with Qt Widgets. + +.. image:: widget_gaussian.png + :width: 400 + :alt: Matplotlib Widget Gaussian Screenshot 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..2423e496a --- /dev/null +++ b/examples/external/matplotlib/widget_gaussian/widget_gaussian.py @@ -0,0 +1,74 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import sys + +import numpy as np +from scipy.stats import norm +from matplotlib.figure import Figure +from matplotlib.backends.backend_qtagg import FigureCanvas +from matplotlib.backends.backend_qtagg import NavigationToolbar2QT +from PySide6.QtCore import 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"] +} diff --git a/examples/external/networkx/doc/networkx.png b/examples/external/networkx/doc/networkx.png Binary files differnew file mode 100644 index 000000000..d3264f8ee --- /dev/null +++ b/examples/external/networkx/doc/networkx.png diff --git a/examples/external/networkx/doc/networkx.rst b/examples/external/networkx/doc/networkx.rst new file mode 100644 index 000000000..58897d3b7 --- /dev/null +++ b/examples/external/networkx/doc/networkx.rst @@ -0,0 +1,8 @@ +Networkx viewer Example +======================= + +A Python application that demonstrates how to display networkx graph into a QGraphicsView. + +.. image:: networkx.png + :width: 400 + :alt: Networkx viewer Screenshot diff --git a/examples/external/networkx/main.py b/examples/external/networkx/main.py new file mode 100644 index 000000000..8cd7e7903 --- /dev/null +++ b/examples/external/networkx/main.py @@ -0,0 +1,346 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + + +import math +import sys + +from PySide6.QtCore import (QEasingCurve, QLineF, + QParallelAnimationGroup, QPointF, + QPropertyAnimation, QRectF, Qt) +from PySide6.QtGui import QBrush, QColor, QPainter, QPen, QPolygonF +from PySide6.QtWidgets import (QApplication, QComboBox, QGraphicsItem, + QGraphicsObject, QGraphicsScene, QGraphicsView, + QStyleOptionGraphicsItem, QVBoxLayout, QWidget) + +import networkx as nx + + +class Node(QGraphicsObject): + + """A QGraphicsItem representing node in a graph""" + + def __init__(self, name: str, parent=None): + """Node constructor + + Args: + name (str): Node label + """ + super().__init__(parent) + self._name = name + self._edges = [] + self._color = "#5AD469" + self._radius = 30 + self._rect = QRectF(0, 0, self._radius * 2, self._radius * 2) + + self.setFlag(QGraphicsItem.ItemIsMovable) + self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) + self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) + + def boundingRect(self) -> QRectF: + """Override from QGraphicsItem + + Returns: + QRect: Return node bounding rect + """ + return self._rect + + def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: QWidget = None): + """Override from QGraphicsItem + + Draw node + + Args: + painter (QPainter) + option (QStyleOptionGraphicsItem) + """ + painter.setRenderHints(QPainter.Antialiasing) + painter.setPen( + QPen( + QColor(self._color).darker(), + 2, + Qt.SolidLine, + Qt.RoundCap, + Qt.RoundJoin, + ) + ) + painter.setBrush(QBrush(QColor(self._color))) + painter.drawEllipse(self.boundingRect()) + painter.setPen(QPen(QColor("white"))) + painter.drawText(self.boundingRect(), Qt.AlignCenter, self._name) + + def add_edge(self, edge): + """Add an edge to this node + + Args: + edge (Edge) + """ + self._edges.append(edge) + + def itemChange(self, change: QGraphicsItem.GraphicsItemChange, value): + """Override from QGraphicsItem + + Args: + change (QGraphicsItem.GraphicsItemChange) + value (Any) + + Returns: + Any + """ + if change == QGraphicsItem.ItemPositionHasChanged: + for edge in self._edges: + edge.adjust() + + return super().itemChange(change, value) + + +class Edge(QGraphicsItem): + def __init__(self, source: Node, dest: Node, parent: QGraphicsItem = None): + """Edge constructor + + Args: + source (Node): source node + dest (Node): destination node + """ + super().__init__(parent) + self._source = source + self._dest = dest + + self._tickness = 2 + self._color = "#2BB53C" + self._arrow_size = 20 + + self._source.add_edge(self) + self._dest.add_edge(self) + + self._line = QLineF() + self.setZValue(-1) + self.adjust() + + def boundingRect(self) -> QRectF: + """Override from QGraphicsItem + + Returns: + QRect: Return node bounding rect + """ + return ( + QRectF(self._line.p1(), self._line.p2()) + .normalized() + .adjusted( + -self._tickness - self._arrow_size, + -self._tickness - self._arrow_size, + self._tickness + self._arrow_size, + self._tickness + self._arrow_size, + ) + ) + + def adjust(self): + """ + Update edge position from source and destination node. + This method is called from Node::itemChange + """ + self.prepareGeometryChange() + self._line = QLineF( + self._source.pos() + self._source.boundingRect().center(), + self._dest.pos() + self._dest.boundingRect().center(), + ) + + def _draw_arrow(self, painter: QPainter, start: QPointF, end: QPointF): + """Draw arrow from start point to end point. + + Args: + painter (QPainter) + start (QPointF): start position + end (QPointF): end position + """ + painter.setBrush(QBrush(self._color)) + + line = QLineF(end, start) + + angle = math.atan2(-line.dy(), line.dx()) + arrow_p1 = line.p1() + QPointF( + math.sin(angle + math.pi / 3) * self._arrow_size, + math.cos(angle + math.pi / 3) * self._arrow_size, + ) + arrow_p2 = line.p1() + QPointF( + math.sin(angle + math.pi - math.pi / 3) * self._arrow_size, + math.cos(angle + math.pi - math.pi / 3) * self._arrow_size, + ) + + arrow_head = QPolygonF() + arrow_head.clear() + arrow_head.append(line.p1()) + arrow_head.append(arrow_p1) + arrow_head.append(arrow_p2) + painter.drawLine(line) + painter.drawPolygon(arrow_head) + + def _arrow_target(self) -> QPointF: + """Calculate the position of the arrow taking into account the size of the destination node + + Returns: + QPointF + """ + target = self._line.p1() + center = self._line.p2() + radius = self._dest._radius + vector = target - center + length = math.sqrt(vector.x() ** 2 + vector.y() ** 2) + if length == 0: + return target + normal = vector / length + target = QPointF(center.x() + (normal.x() * radius), center.y() + (normal.y() * radius)) + + return target + + def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget=None): + """Override from QGraphicsItem + + Draw Edge. This method is called from Edge.adjust() + + Args: + painter (QPainter) + option (QStyleOptionGraphicsItem) + """ + + if self._source and self._dest: + painter.setRenderHints(QPainter.Antialiasing) + + painter.setPen( + QPen( + QColor(self._color), + self._tickness, + Qt.SolidLine, + Qt.RoundCap, + Qt.RoundJoin, + ) + ) + painter.drawLine(self._line) + self._draw_arrow(painter, self._line.p1(), self._arrow_target()) + self._arrow_target() + + +class GraphView(QGraphicsView): + def __init__(self, graph: nx.DiGraph, parent=None): + """GraphView constructor + + This widget can display a directed graph + + Args: + graph (nx.DiGraph): a networkx directed graph + """ + super().__init__() + self._graph = graph + self._scene = QGraphicsScene() + self.setScene(self._scene) + + # Used to add space between nodes + self._graph_scale = 200 + + # Map node name to Node object {str=>Node} + self._nodes_map = {} + + # List of networkx layout function + self._nx_layout = { + "circular": nx.circular_layout, + "planar": nx.planar_layout, + "random": nx.random_layout, + "shell_layout": nx.shell_layout, + "kamada_kawai_layout": nx.kamada_kawai_layout, + "spring_layout": nx.spring_layout, + "spiral_layout": nx.spiral_layout, + } + + self._load_graph() + self.set_nx_layout("circular") + + def get_nx_layouts(self) -> list: + """Return all layout names + + Returns: + list: layout name (str) + """ + return self._nx_layout.keys() + + def set_nx_layout(self, name: str): + """Set networkx layout and start animation + + Args: + name (str): Layout name + """ + if name in self._nx_layout: + self._nx_layout_function = self._nx_layout[name] + + # Compute node position from layout function + positions = self._nx_layout_function(self._graph) + + # Change position of all nodes using an animation + self.animations = QParallelAnimationGroup() + for node, pos in positions.items(): + x, y = pos + x *= self._graph_scale + y *= self._graph_scale + item = self._nodes_map[node] + + animation = QPropertyAnimation(item, b"pos") + animation.setDuration(1000) + animation.setEndValue(QPointF(x, y)) + animation.setEasingCurve(QEasingCurve.OutExpo) + self.animations.addAnimation(animation) + + self.animations.start() + + def _load_graph(self): + """Load graph into QGraphicsScene using Node class and Edge class""" + + self.scene().clear() + self._nodes_map.clear() + + # Add nodes + for node in self._graph: + item = Node(node) + self.scene().addItem(item) + self._nodes_map[node] = item + + # Add edges + for a, b in self._graph.edges: + source = self._nodes_map[a] + dest = self._nodes_map[b] + self.scene().addItem(Edge(source, dest)) + + +class MainWindow(QWidget): + def __init__(self, parent=None): + super().__init__() + + self.graph = nx.DiGraph() + self.graph.add_edges_from( + [ + ("1", "2"), + ("2", "3"), + ("3", "4"), + ("1", "5"), + ("1", "6"), + ("1", "7"), + ] + ) + + self.view = GraphView(self.graph) + self.choice_combo = QComboBox() + self.choice_combo.addItems(self.view.get_nx_layouts()) + v_layout = QVBoxLayout(self) + v_layout.addWidget(self.choice_combo) + v_layout.addWidget(self.view) + self.choice_combo.currentTextChanged.connect(self.view.set_nx_layout) + + +if __name__ == "__main__": + + app = QApplication(sys.argv) + + # Create a networkx graph + + widget = MainWindow() + widget.show() + widget.resize(800, 600) + sys.exit(app.exec()) diff --git a/examples/external/networkx/networkx.pyproject b/examples/external/networkx/networkx.pyproject new file mode 100644 index 000000000..cc7a74a34 --- /dev/null +++ b/examples/external/networkx/networkx.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["main.py"] +} diff --git a/examples/external/networkx/requirements.txt b/examples/external/networkx/requirements.txt new file mode 100644 index 000000000..370ba57d9 --- /dev/null +++ b/examples/external/networkx/requirements.txt @@ -0,0 +1,3 @@ +networkx +numpy +scipy diff --git a/examples/external/opencv/doc/opencv.png b/examples/external/opencv/doc/opencv.png Binary files differnew file mode 100644 index 000000000..e3edcab47 --- /dev/null +++ b/examples/external/opencv/doc/opencv.png diff --git a/examples/external/opencv/doc/opencv.rst b/examples/external/opencv/doc/opencv.rst new file mode 100644 index 000000000..ff2c72501 --- /dev/null +++ b/examples/external/opencv/doc/opencv.rst @@ -0,0 +1,9 @@ +OpenCV Face Detection Example +============================= + +A Python application that demonstrates how to use OpenCV +and a trained model to detect faces detected from a webcam. + +.. image:: opencv.png + :width: 400 + :alt: OpenCV Face Detection Screenshot diff --git a/examples/external/opencv/opencv.pyproject b/examples/external/opencv/opencv.pyproject new file mode 100644 index 000000000..dd9a73e24 --- /dev/null +++ b/examples/external/opencv/opencv.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["webcam_pattern_detection.py"] +} diff --git a/examples/external/opencv/webcam_pattern_detection.py b/examples/external/opencv/webcam_pattern_detection.py index 664ba2111..0c55a1333 100644 --- a/examples/external/opencv/webcam_pattern_detection.py +++ b/examples/external/opencv/webcam_pattern_detection.py @@ -1,51 +1,14 @@ -############################################################################# -## -## 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$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import os import sys import time import cv2 -from PySide2.QtCore import Qt, QThread, Signal, Slot -from PySide2.QtGui import QAction, QImage, QKeySequence, QPixmap -from PySide2.QtWidgets import (QApplication, QComboBox, QGroupBox, +from PySide6.QtCore import Qt, QThread, Signal, Slot +from PySide6.QtGui import QAction, QImage, QKeySequence, QPixmap +from PySide6.QtWidgets import (QApplication, QComboBox, QGroupBox, QHBoxLayout, QLabel, QMainWindow, QPushButton, QSizePolicy, QVBoxLayout, QWidget) @@ -103,7 +66,7 @@ class Thread(QThread): class Window(QMainWindow): def __init__(self): - QMainWindow.__init__(self) + super().__init__() # Title and dimensions self.setWindowTitle("Patterns detection") self.setGeometry(0, 0, 800, 500) @@ -111,12 +74,12 @@ class Window(QMainWindow): # Main menu bar self.menu = self.menuBar() self.menu_file = self.menu.addMenu("File") - exit = QAction("Exit", self, triggered=qApp.quit) + exit = QAction("Exit", self, triggered=qApp.quit) # noqa: F821 self.menu_file.addAction(exit) self.menu_about = self.menu.addMenu("&About") about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents), - triggered=qApp.aboutQt) + triggered=qApp.aboutQt) # noqa: F821 self.menu_about.addAction(about) # Create a label for the display camera @@ -204,4 +167,4 @@ if __name__ == "__main__": app = QApplication() w = Window() w.show() - sys.exit(app.exec_()) + sys.exit(app.exec()) diff --git a/examples/external/pandas/dataframe_model.py b/examples/external/pandas/dataframe_model.py new file mode 100644 index 000000000..b3d9e81fe --- /dev/null +++ b/examples/external/pandas/dataframe_model.py @@ -0,0 +1,82 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import pandas as pd + +from PySide6.QtWidgets import QTableView, QApplication +from PySide6.QtCore import QAbstractTableModel, Qt, QModelIndex +import sys + + +class PandasModel(QAbstractTableModel): + """A model to interface a Qt view with pandas dataframe """ + + def __init__(self, dataframe: pd.DataFrame, parent=None): + QAbstractTableModel.__init__(self, parent) + self._dataframe = dataframe + + def rowCount(self, parent=QModelIndex()) -> int: + """ Override method from QAbstractTableModel + + Return row count of the pandas DataFrame + """ + if parent == QModelIndex(): + return len(self._dataframe) + + return 0 + + def columnCount(self, parent=QModelIndex()) -> int: + """Override method from QAbstractTableModel + + Return column count of the pandas DataFrame + """ + if parent == QModelIndex(): + return len(self._dataframe.columns) + return 0 + + def data(self, index: QModelIndex, role=Qt.ItemDataRole): + """Override method from QAbstractTableModel + + Return data cell from the pandas DataFrame + """ + if not index.isValid(): + return None + + if role == Qt.DisplayRole: + return str(self._dataframe.iloc[index.row(), index.column()]) + + return None + + def headerData( + self, section: int, orientation: Qt.Orientation, role: Qt.ItemDataRole + ): + """Override method from QAbstractTableModel + + Return dataframe index as vertical header data and columns as horizontal header data. + """ + if role == Qt.DisplayRole: + if orientation == Qt.Horizontal: + return str(self._dataframe.columns[section]) + + if orientation == Qt.Vertical: + return str(self._dataframe.index[section]) + + return None + + +if __name__ == "__main__": + + app = QApplication(sys.argv) + + df = pd.read_csv("iris.csv") + + view = QTableView() + view.resize(800, 500) + view.horizontalHeader().setStretchLastSection(True) + view.setAlternatingRowColors(True) + view.setSelectionBehavior(QTableView.SelectRows) + + model = PandasModel(df) + view.setModel(model) + view.show() + app.exec() diff --git a/examples/external/pandas/doc/pandas.rst b/examples/external/pandas/doc/pandas.rst new file mode 100644 index 000000000..8e75eead4 --- /dev/null +++ b/examples/external/pandas/doc/pandas.rst @@ -0,0 +1,9 @@ +Pandas Simple Example +===================== + +A Python application that demonstrates how to visualize +a Pandas DataFrame. + +.. image:: pandas_simple.png + :width: 400 + :alt: Pandas Simple Screenshot diff --git a/examples/external/pandas/doc/pandas_simple.png b/examples/external/pandas/doc/pandas_simple.png Binary files differnew file mode 100644 index 000000000..ea5d240bd --- /dev/null +++ b/examples/external/pandas/doc/pandas_simple.png diff --git a/examples/external/pandas/iris.csv b/examples/external/pandas/iris.csv new file mode 100644 index 000000000..bf14e161b --- /dev/null +++ b/examples/external/pandas/iris.csv @@ -0,0 +1,151 @@ +"sepal.length","sepal.width","petal.length","petal.width","variety" +5.1,3.5,1.4,.2,"Setosa" +4.9,3,1.4,.2,"Setosa" +4.7,3.2,1.3,.2,"Setosa" +4.6,3.1,1.5,.2,"Setosa" +5,3.6,1.4,.2,"Setosa" +5.4,3.9,1.7,.4,"Setosa" +4.6,3.4,1.4,.3,"Setosa" +5,3.4,1.5,.2,"Setosa" +4.4,2.9,1.4,.2,"Setosa" +4.9,3.1,1.5,.1,"Setosa" +5.4,3.7,1.5,.2,"Setosa" +4.8,3.4,1.6,.2,"Setosa" +4.8,3,1.4,.1,"Setosa" +4.3,3,1.1,.1,"Setosa" +5.8,4,1.2,.2,"Setosa" +5.7,4.4,1.5,.4,"Setosa" +5.4,3.9,1.3,.4,"Setosa" +5.1,3.5,1.4,.3,"Setosa" +5.7,3.8,1.7,.3,"Setosa" +5.1,3.8,1.5,.3,"Setosa" +5.4,3.4,1.7,.2,"Setosa" +5.1,3.7,1.5,.4,"Setosa" +4.6,3.6,1,.2,"Setosa" +5.1,3.3,1.7,.5,"Setosa" +4.8,3.4,1.9,.2,"Setosa" +5,3,1.6,.2,"Setosa" +5,3.4,1.6,.4,"Setosa" +5.2,3.5,1.5,.2,"Setosa" +5.2,3.4,1.4,.2,"Setosa" +4.7,3.2,1.6,.2,"Setosa" +4.8,3.1,1.6,.2,"Setosa" +5.4,3.4,1.5,.4,"Setosa" +5.2,4.1,1.5,.1,"Setosa" +5.5,4.2,1.4,.2,"Setosa" +4.9,3.1,1.5,.2,"Setosa" +5,3.2,1.2,.2,"Setosa" +5.5,3.5,1.3,.2,"Setosa" +4.9,3.6,1.4,.1,"Setosa" +4.4,3,1.3,.2,"Setosa" +5.1,3.4,1.5,.2,"Setosa" +5,3.5,1.3,.3,"Setosa" +4.5,2.3,1.3,.3,"Setosa" +4.4,3.2,1.3,.2,"Setosa" +5,3.5,1.6,.6,"Setosa" +5.1,3.8,1.9,.4,"Setosa" +4.8,3,1.4,.3,"Setosa" +5.1,3.8,1.6,.2,"Setosa" +4.6,3.2,1.4,.2,"Setosa" +5.3,3.7,1.5,.2,"Setosa" +5,3.3,1.4,.2,"Setosa" +7,3.2,4.7,1.4,"Versicolor" +6.4,3.2,4.5,1.5,"Versicolor" +6.9,3.1,4.9,1.5,"Versicolor" +5.5,2.3,4,1.3,"Versicolor" +6.5,2.8,4.6,1.5,"Versicolor" +5.7,2.8,4.5,1.3,"Versicolor" +6.3,3.3,4.7,1.6,"Versicolor" +4.9,2.4,3.3,1,"Versicolor" +6.6,2.9,4.6,1.3,"Versicolor" +5.2,2.7,3.9,1.4,"Versicolor" +5,2,3.5,1,"Versicolor" +5.9,3,4.2,1.5,"Versicolor" +6,2.2,4,1,"Versicolor" +6.1,2.9,4.7,1.4,"Versicolor" +5.6,2.9,3.6,1.3,"Versicolor" +6.7,3.1,4.4,1.4,"Versicolor" +5.6,3,4.5,1.5,"Versicolor" +5.8,2.7,4.1,1,"Versicolor" +6.2,2.2,4.5,1.5,"Versicolor" +5.6,2.5,3.9,1.1,"Versicolor" +5.9,3.2,4.8,1.8,"Versicolor" +6.1,2.8,4,1.3,"Versicolor" +6.3,2.5,4.9,1.5,"Versicolor" +6.1,2.8,4.7,1.2,"Versicolor" +6.4,2.9,4.3,1.3,"Versicolor" +6.6,3,4.4,1.4,"Versicolor" +6.8,2.8,4.8,1.4,"Versicolor" +6.7,3,5,1.7,"Versicolor" +6,2.9,4.5,1.5,"Versicolor" +5.7,2.6,3.5,1,"Versicolor" +5.5,2.4,3.8,1.1,"Versicolor" +5.5,2.4,3.7,1,"Versicolor" +5.8,2.7,3.9,1.2,"Versicolor" +6,2.7,5.1,1.6,"Versicolor" +5.4,3,4.5,1.5,"Versicolor" +6,3.4,4.5,1.6,"Versicolor" +6.7,3.1,4.7,1.5,"Versicolor" +6.3,2.3,4.4,1.3,"Versicolor" +5.6,3,4.1,1.3,"Versicolor" +5.5,2.5,4,1.3,"Versicolor" +5.5,2.6,4.4,1.2,"Versicolor" +6.1,3,4.6,1.4,"Versicolor" +5.8,2.6,4,1.2,"Versicolor" +5,2.3,3.3,1,"Versicolor" +5.6,2.7,4.2,1.3,"Versicolor" +5.7,3,4.2,1.2,"Versicolor" +5.7,2.9,4.2,1.3,"Versicolor" +6.2,2.9,4.3,1.3,"Versicolor" +5.1,2.5,3,1.1,"Versicolor" +5.7,2.8,4.1,1.3,"Versicolor" +6.3,3.3,6,2.5,"Virginica" +5.8,2.7,5.1,1.9,"Virginica" +7.1,3,5.9,2.1,"Virginica" +6.3,2.9,5.6,1.8,"Virginica" +6.5,3,5.8,2.2,"Virginica" +7.6,3,6.6,2.1,"Virginica" +4.9,2.5,4.5,1.7,"Virginica" +7.3,2.9,6.3,1.8,"Virginica" +6.7,2.5,5.8,1.8,"Virginica" +7.2,3.6,6.1,2.5,"Virginica" +6.5,3.2,5.1,2,"Virginica" +6.4,2.7,5.3,1.9,"Virginica" +6.8,3,5.5,2.1,"Virginica" +5.7,2.5,5,2,"Virginica" +5.8,2.8,5.1,2.4,"Virginica" +6.4,3.2,5.3,2.3,"Virginica" +6.5,3,5.5,1.8,"Virginica" +7.7,3.8,6.7,2.2,"Virginica" +7.7,2.6,6.9,2.3,"Virginica" +6,2.2,5,1.5,"Virginica" +6.9,3.2,5.7,2.3,"Virginica" +5.6,2.8,4.9,2,"Virginica" +7.7,2.8,6.7,2,"Virginica" +6.3,2.7,4.9,1.8,"Virginica" +6.7,3.3,5.7,2.1,"Virginica" +7.2,3.2,6,1.8,"Virginica" +6.2,2.8,4.8,1.8,"Virginica" +6.1,3,4.9,1.8,"Virginica" +6.4,2.8,5.6,2.1,"Virginica" +7.2,3,5.8,1.6,"Virginica" +7.4,2.8,6.1,1.9,"Virginica" +7.9,3.8,6.4,2,"Virginica" +6.4,2.8,5.6,2.2,"Virginica" +6.3,2.8,5.1,1.5,"Virginica" +6.1,2.6,5.6,1.4,"Virginica" +7.7,3,6.1,2.3,"Virginica" +6.3,3.4,5.6,2.4,"Virginica" +6.4,3.1,5.5,1.8,"Virginica" +6,3,4.8,1.8,"Virginica" +6.9,3.1,5.4,2.1,"Virginica" +6.7,3.1,5.6,2.4,"Virginica" +6.9,3.1,5.1,2.3,"Virginica" +5.8,2.7,5.1,1.9,"Virginica" +6.8,3.2,5.9,2.3,"Virginica" +6.7,3.3,5.7,2.5,"Virginica" +6.7,3,5.2,2.3,"Virginica" +6.3,2.5,5,1.9,"Virginica" +6.5,3,5.2,2,"Virginica" +6.2,3.4,5.4,2.3,"Virginica" +5.9,3,5.1,1.8,"Virginica" diff --git a/examples/external/pandas/pandas.pyproject b/examples/external/pandas/pandas.pyproject new file mode 100644 index 000000000..4731cf7d9 --- /dev/null +++ b/examples/external/pandas/pandas.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["dataframe_model.py"] +} diff --git a/examples/external/pandas/requirements.txt b/examples/external/pandas/requirements.txt new file mode 100644 index 000000000..fb6c7ed7e --- /dev/null +++ b/examples/external/pandas/requirements.txt @@ -0,0 +1 @@ +pandas diff --git a/examples/external/scikit/doc/scikit.png b/examples/external/scikit/doc/scikit.png Binary files differnew file mode 100644 index 000000000..930a1c7c9 --- /dev/null +++ b/examples/external/scikit/doc/scikit.png diff --git a/examples/external/scikit/doc/scikit.rst b/examples/external/scikit/doc/scikit.rst new file mode 100644 index 000000000..29ebbddbf --- /dev/null +++ b/examples/external/scikit/doc/scikit.rst @@ -0,0 +1,9 @@ +Scikit Image Example +==================== + +A Python application that demonstrates how to use Scikit Image +to apply filters to images based on a Qt Widgets. + +.. image:: scikit.png + :width: 400 + :alt: Scikit Image Screenshot diff --git a/examples/external/scikit/scikit.pyproject b/examples/external/scikit/scikit.pyproject new file mode 100644 index 000000000..5026c86e3 --- /dev/null +++ b/examples/external/scikit/scikit.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["staining_colors_separation.py"] +} diff --git a/examples/external/scikit/staining_colors_separation.py b/examples/external/scikit/staining_colors_separation.py index d21453927..94fdc3bdc 100644 --- a/examples/external/scikit/staining_colors_separation.py +++ b/examples/external/scikit/staining_colors_separation.py @@ -1,42 +1,5 @@ -############################################################################# -## -## 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$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import sys @@ -44,9 +7,9 @@ import numpy as np from matplotlib.backends.backend_qt5agg import FigureCanvas from matplotlib.colors import LinearSegmentedColormap from matplotlib.figure import Figure -from PySide2.QtCore import Qt, Slot -from PySide2.QtGui import QAction, QKeySequence -from PySide2.QtWidgets import (QApplication, QHBoxLayout, QLabel, +from PySide6.QtCore import Qt, Slot +from PySide6.QtGui import QAction, QKeySequence +from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLabel, QMainWindow, QPushButton, QSizePolicy, QVBoxLayout, QWidget) from skimage import data @@ -69,12 +32,12 @@ class ApplicationWindow(QMainWindow): # Main menu bar self.menu = self.menuBar() self.menu_file = self.menu.addMenu("File") - exit = QAction("Exit", self, triggered=qApp.quit) + exit = QAction("Exit", self, triggered=qApp.quit) # noqa: F821 self.menu_file.addAction(exit) self.menu_about = self.menu.addMenu("&About") about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents), - triggered=qApp.aboutQt) + triggered=qApp.aboutQt) # noqa: F821 self.menu_about.addAction(about) # Create an artificial color close to the original one @@ -181,4 +144,4 @@ if __name__ == "__main__": app = QApplication(sys.argv) w = ApplicationWindow() w.show() - app.exec_() + app.exec() |