diff options
-rw-r--r-- | coin/module_config.yaml | 3 | ||||
-rw-r--r-- | dist/changes-5.14.0 | 69 | ||||
-rw-r--r-- | examples/external/matplotlib/requirements.txt | 1 | ||||
-rw-r--r-- | examples/external/matplotlib/widget_3dplot.py | 241 | ||||
-rw-r--r-- | examples/external/opencv/requirements.txt | 1 | ||||
-rw-r--r-- | examples/external/opencv/webcam_pattern_detection.py | 206 | ||||
-rw-r--r-- | examples/external/scikit/requirements.txt | 1 | ||||
-rw-r--r-- | examples/external/scikit/staining_colors_separation.py | 183 | ||||
-rw-r--r-- | sources/pyside2/doc/api.rst | 94 | ||||
-rw-r--r-- | sources/pyside2/doc/index.rst | 2 | ||||
-rw-r--r-- | sources/pyside2/doc/modules.rst | 2 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/CMakeLists.txt | 3 | ||||
-rw-r--r-- | sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp | 4 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/qapp_macro.cpp | 65 | ||||
-rw-r--r-- | sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py | 4 |
15 files changed, 822 insertions, 57 deletions
diff --git a/coin/module_config.yaml b/coin/module_config.yaml index 71d4613b0..90df3cfed 100644 --- a/coin/module_config.yaml +++ b/coin/module_config.yaml @@ -287,6 +287,9 @@ run_test_instructions: &run_test_instructions property: features not_contains_value: LicenseCheck instructions: + - type: EnvironmentVariable + variableName: QTEST_ENVIRONMENT + variableValue: "ci" - type: ExecuteCommand command: python -u coin_test_instructions.py --os=MacOS --instdir=/Users/qt/work/install --targetOs=MacOS --hostArch=X86_64 --targetArch=X86_64 maxTimeInSeconds: 7200 diff --git a/dist/changes-5.14.0 b/dist/changes-5.14.0 new file mode 100644 index 000000000..46b4a6ba0 --- /dev/null +++ b/dist/changes-5.14.0 @@ -0,0 +1,69 @@ +Qt for Python 5.14.0 is a minor release. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qtforpython/ + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Important Source Incompatible/Behavior Changes * +**************************************************************************** + +- [PYSIDE-990] It is no longer possible to nest types in typesystem files by + by qualifying the namespace components with "::". The elements + need to be properly nested. + + +**************************************************************************** +* PySide2 * +**************************************************************************** + + - [PYSIDE-487] Add bindings for Qt 5.14 + - [PYSIDE-785] Release ownership in QAbstractVideoFilterWrapper::createFilterRunnable + - [PYSIDE-795] Create a framework for deprecated functions + - [PYSIDE-795] Make the function registry more usable in Python modules + - [PYSIDE-795] Improve the NumPy Support by iterables + - [PYSIDE-820] Fix booleans and empty list cases in QSettings + - [PYSIDE-849] Add inject-code for QVideoFrame::bits() + - [PYSIDE-939] Add support for Python 3.8 + - [PYSIDE-939] Fix refcount issues with QSettings glue code + - [PYSIDE-939] Finalize the Python 3.8 refcount fix + - [PYSIDE-951] Support Pointer Primitive Types by Arrays or Result Tuples + - [PYSIDE-1007] Remove extra ref on QDataStream::setDevice + - [PYSIDE-1033] CMake modularization: macros creation + - [PYSIDE-1041] Enable multimedia classes after API fixup + - [PYSIDE-1047] QtWinExtras: Add QtWin namespace + - [PYSIDE-1051] Fix heaptype conflict with QtCore.QObject.__new__in Python 2.7 + - [PYSIDE-1052] Add QtCore.Slot.__signature__ and much more manually + - [PYSIDE-1059] Documentation: update QInputDialog snippets + - [PYSIDE-1066] Fix Xcode sdk value embedded into PySide2 binaries + - [PYSIDE-1067] Update docs style + - [PYSIDE-1067] New documentation structure + - [PYSIDE-1068] Add designer to the pyside tools + - [PYSIDE-1073] Fix a typing bug in Python 2.7 and update + - [PYSIDE-1077] Fix wrong Python init return codes + - [PYSIDE-1079] signature: Support typing.Optional[T] and refine a bit + - [PYSIDE-1089] Fix formatting of the deployment documentation + - [PYSIDE-1093] Fix bad shutdown effect on QApplication.instance() + - [PYSIDE-1098] Replace pyside2-uic/pyside2-rcc by + uic/rcc which now have an option to generate Python + - [PYSIDE-1101] Remove QGraphicsItem::scroll from QtChart + - [PYSIDE-1140] Add python_requires to the python setup + - [QTBUG-66304] Blacklist failing QtPositioning test + + +**************************************************************************** +* Shiboken2 * +**************************************************************************** + + - [PYSIDE-454] Fix crash when smartptr template class cannot be found + - [PYSIDE-1037] Allow for "auto" as target of type for CONVERTTOCPP in injected code + - [PYSIDE-1095] Fix handling of modified default expressions diff --git a/examples/external/matplotlib/requirements.txt b/examples/external/matplotlib/requirements.txt new file mode 100644 index 000000000..6ccafc3f9 --- /dev/null +++ b/examples/external/matplotlib/requirements.txt @@ -0,0 +1 @@ +matplotlib diff --git a/examples/external/matplotlib/widget_3dplot.py b/examples/external/matplotlib/widget_3dplot.py new file mode 100644 index 000000000..8cc5293ed --- /dev/null +++ b/examples/external/matplotlib/widget_3dplot.py @@ -0,0 +1,241 @@ +############################################################################# +## +## Copyright (C) 2019 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:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $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 PySide2.QtCore import Qt, Slot +from PySide2.QtGui import QKeySequence +from PySide2.QtWidgets import (QAction, 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/opencv/requirements.txt b/examples/external/opencv/requirements.txt new file mode 100644 index 000000000..0dd006bbc --- /dev/null +++ b/examples/external/opencv/requirements.txt @@ -0,0 +1 @@ +opencv-python diff --git a/examples/external/opencv/webcam_pattern_detection.py b/examples/external/opencv/webcam_pattern_detection.py new file mode 100644 index 000000000..7c18a9da5 --- /dev/null +++ b/examples/external/opencv/webcam_pattern_detection.py @@ -0,0 +1,206 @@ +############################################################################# +## +## Copyright (C) 2019 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:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import os +import sys +import time + +import cv2 +from PySide2.QtCore import Qt, QThread, Signal, Slot +from PySide2.QtGui import QImage, QKeySequence, QPixmap +from PySide2.QtWidgets import (QAction, QApplication, QComboBox, QGroupBox, + QHBoxLayout, QLabel, QMainWindow, QPushButton, + QSizePolicy, QVBoxLayout, QWidget) + + +"""This example uses the video from a webcam to apply pattern +detection from the OpenCV module. e.g.: face, eyes, body, etc.""" + + +class Thread(QThread): + updateFrame = Signal(QImage) + + def __init__(self, parent=None): + QThread.__init__(self, parent) + self.trained_file = None + self.status = True + self.cap = True + + def set_file(self, fname): + # The data comes with the 'opencv-python' module + self.trained_file = os.path.join(cv2.data.haarcascades, fname) + + def run(self): + self.cap = cv2.VideoCapture(0) + while self.status: + cascade = cv2.CascadeClassifier(self.trained_file) + ret, frame = self.cap.read() + if not ret: + continue + + # Reading frame in gray scale to process the pattern + gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + + detections = cascade.detectMultiScale(gray_frame, scaleFactor=1.1, + minNeighbors=5, minSize=(30, 30)) + + # Drawing green rectangle around the pattern + for (x, y, w, h) in detections: + pos_ori = (x, y) + pos_end = (x + w, y + h) + color = (0, 255, 0) + cv2.rectangle(frame, pos_ori, pos_end, color, 2) + + # Reading the image in RGB to display it + color_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + # Creating and scaling QImage + h, w, ch = color_frame.shape + img = QImage(color_frame.data, w, h, ch * w, QImage.Format_RGB888) + scaled_img = img.scaled(640, 480, Qt.KeepAspectRatio) + + # Emit signal + self.updateFrame.emit(scaled_img) + sys.exit(-1) + + +class Window(QMainWindow): + def __init__(self): + QMainWindow.__init__(self) + # Title and dimensions + self.setWindowTitle("Patterns detection") + self.setGeometry(0, 0, 800, 500) + + # 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) + + # Create a label for the display camera + self.label = QLabel(self) + self.label.setFixedSize(640, 480) + + # Thread in charge of updating the image + self.th = Thread(self) + self.th.finished.connect(self.close) + self.th.updateFrame.connect(self.setImage) + + # Model group + self.group_model = QGroupBox("Trained model") + self.group_model.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + model_layout = QHBoxLayout() + + self.combobox = QComboBox() + for xml_file in os.listdir(cv2.data.haarcascades): + if xml_file.endswith(".xml"): + self.combobox.addItem(xml_file) + + model_layout.addWidget(QLabel("File:"), 10) + model_layout.addWidget(self.combobox, 90) + self.group_model.setLayout(model_layout) + + # Buttons layout + buttons_layout = QHBoxLayout() + self.button1 = QPushButton("Start") + self.button2 = QPushButton("Stop/Close") + self.button1.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + self.button2.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + buttons_layout.addWidget(self.button2) + buttons_layout.addWidget(self.button1) + + right_layout = QHBoxLayout() + right_layout.addWidget(self.group_model, 1) + right_layout.addLayout(buttons_layout, 1) + + # Main layout + layout = QVBoxLayout() + layout.addWidget(self.label) + layout.addLayout(right_layout) + + # Central widget + widget = QWidget(self) + widget.setLayout(layout) + self.setCentralWidget(widget) + + # Connections + self.button1.clicked.connect(self.start) + self.button2.clicked.connect(self.kill_thread) + self.button2.setEnabled(False) + self.combobox.currentTextChanged.connect(self.set_model) + + @Slot() + def set_model(self, text): + self.th.set_file(text) + + @Slot() + def kill_thread(self): + print("Finishing...") + self.button2.setEnabled(False) + self.button1.setEnabled(True) + self.th.cap.release() + cv2.destroyAllWindows() + self.status = False + self.th.terminate() + # Give time for the thread to finish + time.sleep(1) + + @Slot() + def start(self): + print("Starting...") + self.button2.setEnabled(True) + self.button1.setEnabled(False) + self.th.set_file(self.combobox.currentText()) + self.th.start() + + @Slot(QImage) + def setImage(self, image): + self.label.setPixmap(QPixmap.fromImage(image)) + + +if __name__ == "__main__": + app = QApplication() + w = Window() + w.show() + sys.exit(app.exec_()) diff --git a/examples/external/scikit/requirements.txt b/examples/external/scikit/requirements.txt new file mode 100644 index 000000000..391ca2f08 --- /dev/null +++ b/examples/external/scikit/requirements.txt @@ -0,0 +1 @@ +scikit-image diff --git a/examples/external/scikit/staining_colors_separation.py b/examples/external/scikit/staining_colors_separation.py new file mode 100644 index 000000000..91de1f63c --- /dev/null +++ b/examples/external/scikit/staining_colors_separation.py @@ -0,0 +1,183 @@ +############################################################################# +## +## Copyright (C) 2019 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:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +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 QSize, Qt, Slot +from PySide2.QtGui import QKeySequence +from PySide2.QtWidgets import (QAction, QApplication, QHBoxLayout, QLabel, + QMainWindow, QPushButton, QSizePolicy, + QVBoxLayout, QWidget) +from skimage import data +from skimage.color import rgb2hed +from skimage.exposure import rescale_intensity + + +class ApplicationWindow(QMainWindow): + """ + Example based on the example by 'scikit-image' gallery: + "Immunohistochemical staining colors separation" + https://scikit-image.org/docs/stable/auto_examples/color_exposure/plot_ihc_color_separation.html + """ + + def __init__(self, parent=None): + QMainWindow.__init__(self, parent) + 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) + + # Create an artificial color close to the original one + self.ihc_rgb = data.immunohistochemistry() + self.ihc_hed = rgb2hed(self.ihc_rgb) + + main_layout = QVBoxLayout(self._main) + plot_layout = QHBoxLayout() + button_layout = QHBoxLayout() + label_layout = QHBoxLayout() + + self.canvas1 = FigureCanvas(Figure(figsize=(5, 5))) + self.canvas2 = FigureCanvas(Figure(figsize=(5, 5))) + + self._ax1 = self.canvas1.figure.subplots() + self._ax2 = self.canvas2.figure.subplots() + + self._ax1.imshow(self.ihc_rgb) + + plot_layout.addWidget(self.canvas1) + plot_layout.addWidget(self.canvas2) + + self.button1 = QPushButton("Hematoxylin") + self.button2 = QPushButton("Eosin") + self.button3 = QPushButton("DAB") + self.button4 = QPushButton("Fluorescence") + + self.button1.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + self.button2.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + self.button3.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + self.button4.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + + self.button1.clicked.connect(self.plot_hematoxylin) + self.button2.clicked.connect(self.plot_eosin) + self.button3.clicked.connect(self.plot_dab) + self.button4.clicked.connect(self.plot_final) + + self.label1 = QLabel("Original", alignment=Qt.AlignCenter) + self.label2 = QLabel("", alignment=Qt.AlignCenter) + + font = self.label1.font() + font.setPointSize(16) + self.label1.setFont(font) + self.label2.setFont(font) + + label_layout.addWidget(self.label1) + label_layout.addWidget(self.label2) + + button_layout.addWidget(self.button1) + button_layout.addWidget(self.button2) + button_layout.addWidget(self.button3) + button_layout.addWidget(self.button4) + + main_layout.addLayout(label_layout, 2) + main_layout.addLayout(plot_layout, 88) + main_layout.addLayout(button_layout, 10) + + # Default image + self.plot_hematoxylin() + + def set_buttons_state(self, states): + self.button1.setEnabled(states[0]) + self.button2.setEnabled(states[1]) + self.button3.setEnabled(states[2]) + self.button4.setEnabled(states[3]) + + @Slot() + def plot_hematoxylin(self): + cmap_hema = LinearSegmentedColormap.from_list("mycmap", ["white", "navy"]) + self._ax2.imshow(self.ihc_hed[:, :, 0], cmap=cmap_hema) + self.canvas2.draw() + self.label2.setText("Hematoxylin") + self.set_buttons_state((False, True, True, True)) + + @Slot() + def plot_eosin(self): + cmap_eosin = LinearSegmentedColormap.from_list("mycmap", ["darkviolet", "white"]) + self._ax2.imshow(self.ihc_hed[:, :, 1], cmap=cmap_eosin) + self.canvas2.draw() + self.label2.setText("Eosin") + self.set_buttons_state((True, False, True, True)) + + @Slot() + def plot_dab(self): + cmap_dab = LinearSegmentedColormap.from_list("mycmap", ["white", "saddlebrown"]) + self._ax2.imshow(self.ihc_hed[:, :, 2], cmap=cmap_dab) + self.canvas2.draw() + self.label2.setText("DAB") + self.set_buttons_state((True, True, False, True)) + + @Slot() + def plot_final(self): + h = rescale_intensity(self.ihc_hed[:, :, 0], out_range=(0, 1)) + d = rescale_intensity(self.ihc_hed[:, :, 2], out_range=(0, 1)) + zdh = np.dstack((np.zeros_like(h), d, h)) + self._ax2.imshow(zdh) + self.canvas2.draw() + self.label2.setText("Stain separated image") + self.set_buttons_state((True, True, True, False)) + + +if __name__ == "__main__": + + app = QApplication(sys.argv) + w = ApplicationWindow() + w.show() + app.exec_() diff --git a/sources/pyside2/doc/api.rst b/sources/pyside2/doc/api.rst index 2cc258c75..337d383b4 100644 --- a/sources/pyside2/doc/api.rst +++ b/sources/pyside2/doc/api.rst @@ -5,64 +5,86 @@ Basic modules ------------- - These are the main modules that will help you build a Widget based UI. - :mod:`Qt Core <PySide2.QtCore>` - Provides core non-GUI functionality, like signal and slots, properties, base classes of item models, serialization, etc. - :mod:`Qt GUI <PySide2.QtGui>` - Extends QtCore with GUI functionality: Events, windows and screens, OpenGL and raster-based 2D painting, images. - :mod:`Qt Widgets <PySide2.QtWidgets>` - Ready to use Widgets for your application, including also graphical elements for your UI. +These are the main modules that help you build a Widget-based UI. + ++---------------------------------------+--------------------------------------------------------+ +| :mod:`Qt Core <PySide2.QtCore>` | Provides core non-GUI functionality, like signal and | +| | slots, properties, base classes of item models, | +| | serialization, and more. | ++---------------------------------------+--------------------------------------------------------+ +| :mod:`Qt GUI <PySide2.QtGui>` | Extends QtCore with GUI functionality: Events, windows | +| | and screens, OpenGL and raster-based 2D painting, as | +| | well as images. | ++---------------------------------------+--------------------------------------------------------+ +| :mod:`Qt Widgets <PySide2.QtWidgets>` | Provides ready to use Widgets for your application, | +| | including graphical elements for your UI. | ++---------------------------------------+--------------------------------------------------------+ QML and Qt Quick ---------------- - If you want to use the `QML Language <https://doc.qt.io/qt-5.qmlapplications>`, these - modules will help you interact with it from Python. - :mod:`Qt QML <PySide2.QtQml>` - Base Python API to interact with the QML module. - :mod:`Qt Quick <PySide2.QtQuick>` - Provides classes for embedding Qt Quick in Qt applications. - :mod:`Qt QuickWidgets <PySide2.QtQuickWidgets>` - Provides the QQuickWidget class for embedding Qt Quick in widget-based applications. +Use these modules to interact with the `QML Language <https://doc.qt.io/qt-5.qmlapplications>`, +from Python. + ++-------------------------------------------------+----------------------------------------------+ +| :mod:`Qt QML <PySide2.QtQml>` | The base Python API to interact with the | +| | module. | ++-------------------------------------------------+----------------------------------------------+ +| :mod:`Qt Quick <PySide2.QtQuick>` | Provides classes to embed Qt Quick in Qt | +| | applications. | ++-------------------------------------------------+----------------------------------------------+ +| :mod:`Qt QuickWidgets <PySide2.QtQuickWidgets>` | Provides the QQuickWidget class to embed Qt | +| | Quick in widget-based applications. | ++-------------------------------------------------+----------------------------------------------+ Data visualization ------------------ - Charts and diagrams: these modules provide a large amount - of classes that can help you include these elements in your UI. +Charts, diagrams, animations: these modules provide classes to help you include these elements in +your UI. - :mod:`Qt Charts <PySide2.QtCharts>` - Provides a set of easy to use chart components. - :mod:`Qt DataVisualization <PySide2.QtDataVisualization>` - Provides a way to visualize data in 3D as bar, scatter, and surface graphs. ++------------------------------------------------------------+-----------------------------------+ +| :mod:`Qt Charts <PySide2.QtCharts>` | Provides a set of easy to use | +| | chart components. | ++------------------------------------------------------------+-----------------------------------+ +| :mod:`Qt DataVisualization <PySide2.QtDataVisualization>` | Provides a way to visualize data | +| | in 3D as bar, scatter, or surface | +| | graphs. | ++------------------------------------------------------------+-----------------------------------+ Multimedia ----------- - Audio, video, and hardware interaction: check these modules if you are - looking for multimedia solutions. +Audio, video, and hardware interaction: use these modules for multimedia solutions. - :mod:`Qt Multimedia <PySide2.QtMultimedia>` - Provides low-level multimedia functionality. - :mod:`Qt MultimediaWidgets <PySide2.QtMultimediaWidgets>` - Provides the widget-based multimedia API. ++------------------------------------------------------------+-----------------------------------+ +| :mod:`Qt Multimedia <PySide2.QtMultimedia>` | Provides low-level multimedia | +| | functionality. | ++------------------------------------------------------------+-----------------------------------+ +| :mod:`Qt MultimediaWidgets <PySide2.QtMultimediaWidgets>` | Provides the widget-based | +| | multimedia API. | ++------------------------------------------------------------+-----------------------------------+ WebEngine --------- - If your project is based on a browser or the features around web - based applications, these modules will help you to interact with them. +If your project is based on a browser or the features around Web-based applications, use these +modules to interact with them. - :mod:`Qt WebEngineWidgets <PySide2.QtWebEngineWidgets>` - Provides widgets that can handle web content. - :mod:`Qt WebChannel <PySide2.QtWebChannel>` - Enables peer-to-peer communication between a server and a client - (HTML/JavaScript or QML application). ++---------------------------------------------------------+--------------------------------------+ +| :mod:`Qt WebEngineWidgets <PySide2.QtWebEngineWidgets>` | Provides widgets to handle Web | +| | content. | ++---------------------------------------------------------+--------------------------------------+ +| :mod:`Qt WebChannel <PySide2.QtWebChannel>` | Enables peer-to-peer communication | +| | between a server and a client | +| | (HTML/JavaScript or QML application).| ++---------------------------------------------------------+--------------------------------------+ All the modules --------------- - Here is a complete list of modules supported by |pymodname|. +There are many other modules currently supported by |pymodname|, here you can find a complete list +of them. - :doc:`Modules <modules>` + :doc:`Check all the modules <modules>` diff --git a/sources/pyside2/doc/index.rst b/sources/pyside2/doc/index.rst index 93e3451c5..96cbf2ab2 100644 --- a/sources/pyside2/doc/index.rst +++ b/sources/pyside2/doc/index.rst @@ -2,7 +2,7 @@ ********* **Qt for Python** offers the official Python bindings for `Qt`_ (`PySide2`_), -enabling the use of its APIs in Python applications, and a binding generator tool (`Shiboken2`_) +so that you can use Qt5 APIs in your Python applications, and a binding generator tool (`Shiboken2`_) which can be used to expose C++ projects into Python. |project| is available under the LGPLv3/GPLv3 and the Qt commercial license. diff --git a/sources/pyside2/doc/modules.rst b/sources/pyside2/doc/modules.rst index d9accd664..bb4b112a1 100644 --- a/sources/pyside2/doc/modules.rst +++ b/sources/pyside2/doc/modules.rst @@ -5,7 +5,7 @@ Qt Modules :hidden: :glob: - PySide2/Qt*/* + PySide2/Qt*/index .. list-table:: :widths: 150, 150 diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index c2a4c208e..65150eb92 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -46,6 +46,9 @@ if(NOT Qt5XmlPatterns_FOUND AND NOT HAS_LIBXSLT) "Documentation will not be built due to missing dependency (no Qt5XmlPatterns found).") endif() +# Export to parent scope so that generator/CMakeLists.txt gets it +set(DISABLE_DOCSTRINGS ${DISABLE_DOCSTRINGS} PARENT_SCOPE) + add_library(apiextractor STATIC ${apiextractor_SRC}) target_include_directories(apiextractor PRIVATE ${CLANG_EXTRA_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp index 2b7efd6e7..40cc255f0 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp @@ -1621,9 +1621,9 @@ void QtDocGenerator::generateClass(QTextStream &s, GeneratorContext &classContex m_docParser->setPackageName(metaClass->package()); m_docParser->fillDocumentation(const_cast<AbstractMetaClass*>(metaClass)); - s << ".. currentmodule:: " << metaClass->package() << Qt::endl; QString className = getClassTargetFullName(metaClass, false); - s << ".. _" << className << ":\n\n"; + s << ".. _" << className << ":" << "\n\n"; + s << ".. currentmodule:: " << metaClass->package() << "\n\n\n"; s << className << Qt::endl; s << Pad('*', className.count()) << Qt::endl << Qt::endl; diff --git a/sources/shiboken2/libshiboken/qapp_macro.cpp b/sources/shiboken2/libshiboken/qapp_macro.cpp index 306f53b74..827c240c5 100644 --- a/sources/shiboken2/libshiboken/qapp_macro.cpp +++ b/sources/shiboken2/libshiboken/qapp_macro.cpp @@ -53,19 +53,17 @@ extern "C" // variable that monitors Q*Application.instance(). // This variable is also able to destroy the app by deleting qApp. // +static const char *mod_names[3] = {"PySide2.QtCore", "PySide2.QtGui", "PySide2.QtWidgets"}; +static const char *app_names[3] = {"QCoreApplication", "QGuiApplication", "QApplication"}; + static int qApp_module_index(PyObject *module) { const char *name = PyModule_GetName(module); - int ret = 0; - - if (strcmp(name, "PySide2.QtCore") == 0) - ret = 1; - else if (strcmp(name, "PySide2.QtGui") == 0) - ret = 2; - else if (strcmp(name, "PySide2.QtWidgets") == 0) - ret = 3; - return ret; + for (int idx = 0; idx < 3; idx++) + if (strcmp(name, mod_names[idx]) == 0) + return idx + 1; + return 0; } #define PYTHON_IS_PYTHON3 (PY_VERSION_HEX >= 0x03000000) @@ -109,6 +107,8 @@ reset_qApp_var(void) return 0; } +static bool app_created = false; + /* * Note: * The PYSIDE-585 problem was that shutdown is called one more often @@ -120,7 +120,6 @@ reset_qApp_var(void) PyObject * MakeSingletonQAppWrapper(PyTypeObject *type) { - static bool app_created = false; if (type == nullptr) type = Py_NONE_TYPE; if (!(type == Py_NONE_TYPE || Py_TYPE(qApp_content) == Py_NONE_TYPE)) { @@ -163,7 +162,7 @@ MakeSingletonQAppWrapper(PyTypeObject *type) if (__moduleShutdown != nullptr) Py_XDECREF(PyObject_CallFunction(__moduleShutdown, const_cast<char *>("()"))); } else { - PyObject_INIT(qApp_content, type); + PyObject_Init(qApp_content, type); app_created = true; } Py_INCREF(qApp_content); @@ -245,13 +244,45 @@ NotifyModuleForQApp(PyObject *module, void *qApp) * Therefore, the implementation is very simple and just redirects the * qApp_contents variable and assigns the instance, instead of vice-versa. */ - PyObject *coreDict = qApp_moduledicts[1]; - if (qApp != nullptr && coreDict != nullptr) { - PyObject *coreApp = PyDict_GetItemString(coreDict, "QCoreApplication"); - if (coreApp != nullptr) { - qApp_content = PyObject_CallMethod(coreApp, "instance", ""); - reset_qApp_var(); + + // PYSIDE-1135: Make sure that at least QtCore gets imported. + // That problem exists when a derived instance is created in C++. + // PYSIDE-1164: Use the highest Q*Application module possible, + // because in embedded mode the instance() seems to be sticky. + static bool oneshot_active = false; + if (qApp == nullptr || app_created || oneshot_active) + return; + + // qApp exists without an application created. + // We assume that we are embedded, and we simply try to import all three modules. + oneshot_active = true; + int mod_found = 0; + const char *mod_name, *app_name; + const char *thismod_name = PyModule_GetName(module); + + // First go through all three modules, import and set qApp_moduledicts. + for (int idx = 0; idx < 3; idx++) { + // only import if it is not already the module + PyObject *mod = strcmp(thismod_name, mod_names[idx]) == 0 ? module + : PyImport_ImportModule(mod_names[idx]); + if (mod != nullptr) { + mod_found = idx + 1; + qApp_moduledicts[mod_found] = PyModule_GetDict(mod); + mod_name = PyModule_GetName(mod); + app_name = app_names[idx]; + continue; } + PyErr_Clear(); + } + + // Then take the highest module and call instance() on it. + if (mod_found) { + PyObject *mod_dict = qApp_moduledicts[mod_found]; + PyObject *app_class = PyDict_GetItemString(mod_dict, app_name); + qApp_content = PyObject_CallMethod(app_class, const_cast<char *>("instance"), + const_cast<char *>("")); + app_created = true; + reset_qApp_var(); } } diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index 2110ebe7a..0767e8fd4 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -489,6 +489,10 @@ def init_PySide2_QtCore(): PySide2.QtCore.QCborStringResultByteArray, "PySide2.QtCore.QCborStreamReader.StringResult[QString]": PySide2.QtCore.QCborStringResultString, + "PySide2.QtCore.QCborStreamReader.QCborStringResultByteArray": + PySide2.QtCore.QCborStringResultByteArray, # 5.14, why? + "PySide2.QtCore.QCborStreamReader.QCborStringResultString": + PySide2.QtCore.QCborStringResultString, # 5.14, why? "PySide2.QtCore.QUrl.ComponentFormattingOptions": PySide2.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why??? "PyUnicode": typing.Text, |