aboutsummaryrefslogtreecommitdiffstats
path: root/sources
diff options
context:
space:
mode:
Diffstat (limited to 'sources')
-rw-r--r--sources/pyside-tools/project.py10
-rw-r--r--sources/pyside-tools/project_lib/project_data.py13
-rw-r--r--sources/pyside-tools/project_lib/pyproject_parse_result.py2
-rw-r--r--sources/pyside-tools/project_lib/pyproject_toml.py9
-rw-r--r--sources/pyside6/PySide6/QtCore/typesystem_core_common.xml2
-rw-r--r--sources/pyside6/doc/tools/pyside-project.rst11
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/add_chart.rst14
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst8
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst33
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize3/datavisualize3.pyproject3
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py13
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize4/datavisualize4.pyproject3
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py8
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py13
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py16
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize5/datavisualize5.pyproject3
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py41
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py13
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py16
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize6/datavisualize6.pyproject3
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.py95
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py14
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py17
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/filter_data.rst7
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst12
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/read_data.rst2
-rw-r--r--sources/pyside6/tests/QtCore/qobject_property_test.py13
-rw-r--r--sources/shiboken6/doc/typesystem_specifying_types.rst19
-rw-r--r--sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py10
-rw-r--r--sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py62
-rw-r--r--sources/shiboken6/tests/libsmart/smart.cpp12
-rw-r--r--sources/shiboken6/tests/libsmart/smart_obj.h3
-rw-r--r--sources/shiboken6/tests/libsmart/smart_sharedptr.h6
-rw-r--r--sources/shiboken6/tests/smartbinding/CMakeLists.txt1
-rw-r--r--sources/shiboken6/tests/smartbinding/smart_pointer_test.py8
-rw-r--r--sources/shiboken6/tests/smartbinding/typesystem_smart.xml3
-rw-r--r--sources/shiboken6_generator/ApiExtractor/smartpointertypeentry.h3
-rw-r--r--sources/shiboken6_generator/ApiExtractor/typesystem.cpp14
-rw-r--r--sources/shiboken6_generator/ApiExtractor/typesystem_enums.h5
-rw-r--r--sources/shiboken6_generator/ApiExtractor/typesystemparser.cpp20
-rw-r--r--sources/shiboken6_generator/generator/shiboken/cppgenerator.cpp47
41 files changed, 431 insertions, 176 deletions
diff --git a/sources/pyside-tools/project.py b/sources/pyside-tools/project.py
index 762e76f31..fbb740c32 100644
--- a/sources/pyside-tools/project.py
+++ b/sources/pyside-tools/project.py
@@ -104,13 +104,19 @@ class Project:
"""Return path and command for a file's artifact"""
if file.suffix == ".ui": # Qt form files
py_file = f"{file.parent}/ui_{file.stem}.py"
- return [Path(py_file)], [UIC_CMD, os.fspath(file), "--rc-prefix", "-o", py_file]
+ cmd = [UIC_CMD]
+ cmd.extend(self.project.uic_options)
+ cmd.extend([os.fspath(file), "--rc-prefix", "-o", py_file])
+ return [Path(py_file)], cmd
if file.suffix == ".qrc": # Qt resources
if not output_path:
py_file = f"{file.parent}/rc_{file.stem}.py"
else:
py_file = str(output_path.resolve())
- return [Path(py_file)], [RCC_CMD, os.fspath(file), "-o", py_file]
+ cmd = [RCC_CMD]
+ cmd.extend(self.project.rcc_options)
+ cmd.extend([os.fspath(file), "-o", py_file])
+ return [Path(py_file)], cmd
# generate .qmltypes from sources with Qml decorators
if file.suffix == ".py" and file in self._qml_module_sources:
assert self._qml_module_dir
diff --git a/sources/pyside-tools/project_lib/project_data.py b/sources/pyside-tools/project_lib/project_data.py
index 9a219c957..928e79c7b 100644
--- a/sources/pyside-tools/project_lib/project_data.py
+++ b/sources/pyside-tools/project_lib/project_data.py
@@ -34,8 +34,10 @@ class ProjectData:
self._python_files: list[Path] = []
# ui files
self._ui_files: list[Path] = []
+ self._uic_options: list[str] = []
# qrc files
self._qrc_files: list[Path] = []
+ self._rcc_options: list[str] = []
# ts files
self._ts_files: list[Path] = []
@@ -53,6 +55,9 @@ class ProjectData:
print(f"{error}", file=sys.stderr)
sys.exit(1)
+ self._rcc_options = project_file_data.rcc_options
+ self._uic_options = project_file_data.uic_options
+
for f in project_file_data.files:
file = Path(project_file.parent / f)
if any(file.match(pattern) for pattern in PYPROJECT_FILE_PATTERNS):
@@ -101,10 +106,18 @@ class ProjectData:
return self._ui_files
@property
+ def uic_options(self):
+ return self._uic_options
+
+ @property
def qrc_files(self):
return self._qrc_files
@property
+ def rcc_options(self):
+ return self._rcc_options
+
+ @property
def qml_files(self):
return self._qml_files
diff --git a/sources/pyside-tools/project_lib/pyproject_parse_result.py b/sources/pyside-tools/project_lib/pyproject_parse_result.py
index 6a04bf5ce..4c3264b52 100644
--- a/sources/pyside-tools/project_lib/pyproject_parse_result.py
+++ b/sources/pyside-tools/project_lib/pyproject_parse_result.py
@@ -8,3 +8,5 @@ from pathlib import Path
class PyProjectParseResult:
errors: list[str] = field(default_factory=list)
files: list[Path] = field(default_factory=list)
+ rcc_options: list[str] = field(default_factory=list)
+ uic_options: list[str] = field(default_factory=list)
diff --git a/sources/pyside-tools/project_lib/pyproject_toml.py b/sources/pyside-tools/project_lib/pyproject_toml.py
index 6da7b455e..bc5a0f69d 100644
--- a/sources/pyside-tools/project_lib/pyproject_toml.py
+++ b/sources/pyside-tools/project_lib/pyproject_toml.py
@@ -91,11 +91,18 @@ def parse_pyproject_toml(pyproject_toml_file: Path) -> PyProjectParseResult:
result.errors.append(str(e))
return result
- pyside_table = root_table.get("tool", {}).get("pyside6-project", {})
+ tool_entry = root_table.get("tool", {})
+ pyside_table = tool_entry.get("pyside6-project", {})
if not pyside_table:
result.errors.append("Missing [tool.pyside6-project] table")
return result
+ if rcc_table := tool_entry.get("pyside6-rcc", {}):
+ result.rcc_options = rcc_table.get("options", [])
+
+ if uic_table := tool_entry.get("pyside6-uic", {}):
+ result.uic_options = uic_table.get("options", [])
+
files = pyside_table.get("files", [])
if not isinstance(files, list):
result.errors.append("Missing or invalid files list")
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
index f001178cc..bf04e04ed 100644
--- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
+++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
@@ -336,8 +336,6 @@
<add-conversion type="SbkObject" file="../glue/qtcore.cpp" snippet="conversion-sbkobject"/>
<add-conversion type="PyDict" check="PyDict_CheckExact(%in)" file="../glue/qtcore.cpp" snippet="conversion-pydict"/>
<add-conversion type="PyList" check="PyList_Check(%in)" file="../glue/qtcore.cpp" snippet="conversion-pylist"/>
- <add-conversion type="PyTuple" check="PyTuple_CheckExact(%in)"
- file="../glue/qtcore.cpp" snippet="conversion-pylist"/>
<add-conversion type="PyObject" file="../glue/qtcore.cpp" snippet="conversion-pyobject"/>
</target-to-native>
</conversion-rule>
diff --git a/sources/pyside6/doc/tools/pyside-project.rst b/sources/pyside6/doc/tools/pyside-project.rst
index c6913f363..41b5bc9af 100644
--- a/sources/pyside6/doc/tools/pyside-project.rst
+++ b/sources/pyside6/doc/tools/pyside-project.rst
@@ -34,6 +34,17 @@ files are listed in the ``tool.pyside6-project`` table. For example:
[tool.pyside6-project]
files = ["main.py", "main_window.py"]
+It is also possible to specify options for the :ref:`pyside6-rcc` and
+:ref:`pyside6-uic` tools:
+
+.. code-block:: toml
+
+ [tool.pyside6-rcc]
+ options = ["--compress-algo", "zlib"]
+
+ [tool.pyside6-uic]
+ options = [" --star-imports"]
+
More information about the ``pyproject.toml`` file format can be found in
`Python Packaging User Guide specification: "Writing your pyproject.toml"`_.
diff --git a/sources/pyside6/doc/tutorials/datavisualize/add_chart.rst b/sources/pyside6/doc/tutorials/datavisualize/add_chart.rst
index acffce40d..87e012021 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/add_chart.rst
+++ b/sources/pyside6/doc/tutorials/datavisualize/add_chart.rst
@@ -5,17 +5,17 @@ Chapter 5 - Add a chart view
=============================
A table is nice to present data, but a chart is even better. For this, you
-need the QtCharts module that provides many types of plots and options to
+need the QtGraphs module that provides many types of plots and options to
graphically represent data.
-The placeholder for a plot is a QChartView, and inside that Widget you can
-place a QChart. As a first step, try including only this without any data to
-plot.
+The relevant class for a plot is the GraphsView QML type, to which axes and
+data series can be added. As a first step, try including then without any data
+to plot.
Make the following highlighted changes to :code:`main_widget.py` from the
-previous chapter to add a QChartView:
+previous chapter to add a chart:
.. literalinclude:: datavisualize5/main_widget.py
:linenos:
- :lines: 3-
- :emphasize-lines: 2-3,6,22-36,47-49
+ :lines: 5-
+ :emphasize-lines: 4,27-39,53
diff --git a/sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst b/sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst
index ab5468cd6..9073e679a 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst
+++ b/sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst
@@ -4,7 +4,8 @@
Chapter 3 - Create an empty QMainWindow
==========================================
-You can now think of presenting your data in a UI. A QMainWindow provides a
+You can now think of presenting your data in a UI. A
+class:`~PySide6.QtWidgets.QMainWindow` provides a
convenient structure for GUI applications, such as a menu bar and status bar.
The following image shows the layout that QMainWindow offers out-of-the box:
@@ -24,12 +25,13 @@ the resolution you currently have. In the following snippet, you will see how
window size is defined based on available screen width (80%) and height (70%).
.. note:: You can achieve a similar structure using other Qt elements like
- QMenuBar, QWidget, and QStatusBar. Refer the QMainWindow layout for
+ class:`~PySide6.QtWidgets.QMenuBar`, class:`~PySide6.QtWidgets.QWidget`,
+ and class:`~PySide6.QtWidgets.QStatusBar`. Refer the QMainWindow layout for
guidance.
.. literalinclude:: datavisualize3/main_window.py
:language: python
:linenos:
- :lines: 4-
+ :lines: 5-
Try running the script to see what output you get with it.
diff --git a/sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst b/sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst
index 5b7e5e735..b3041349c 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst
+++ b/sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst
@@ -8,21 +8,22 @@ Now that you have a QMainWindow, you can include a centralWidget to your
interface. Usually, a QWidget is used to display data in most data-driven
applications. Use a table view to display your data.
-The first step is to add a horizontal layout with just a QTableView. You
-can create a QTableView object and place it inside a QHBoxLayout. Once the
+The first step is to add a horizontal layout with just a
+class:`~PySide6.QtWidgets.QTableView`. You can create a QTableView object
+and place it inside a class:`~PySide6.QtWidgets.QHBoxLayout`. Once the
QWidget is properly built, pass the object to the QMainWindow as its central
widget.
Remember that a QTableView needs a model to display information. In this case,
-you can use a QAbstractTableModel instance.
+you can use a class:`~PySide6.QtCore.QAbstractTableModel` instance.
.. note:: You could also use the default item model that comes with a
- QTableWidget instead. QTableWidget is a convenience class that reduces
- your codebase considerably as you don't need to implement a data model.
- However, it's less flexible than a QTableView, as QTableWidget cannot be
- used with just any data. For more insight about Qt's model-view framework,
- refer to the
- `Model View Programming <https://doc.qt.io/qt-5/model-view-programming.html>`
+ class:`~PySide6.QtWidgets.QTableWidget` instead. QTableWidget is a
+ convenience class that reduces your codebase considerably as you don't need
+ to implement a data model. However, it's less flexible than a QTableView,
+ as QTableWidget cannot be used with just any data. For more insight about
+ Qt's model-view framework, refer to the
+ `Model View Programming <https://doc.qt.io/qt-6/model-view-programming.html>`
documentation.
Implementing the model for your QTableView, allows you to:
@@ -42,7 +43,7 @@ Here is a script that implements the CustomTableModel:
.. literalinclude:: datavisualize4/table_model.py
:language: python
:linenos:
- :lines: 3-
+ :lines: 5-
Now, create a QWidget that has a QTableView, and connect it to your
CustomTableModel.
@@ -50,8 +51,8 @@ CustomTableModel.
.. literalinclude:: datavisualize4/main_widget.py
:language: python
:linenos:
- :emphasize-lines: 12-17
- :lines: 3-
+ :emphasize-lines: 12-12
+ :lines: 5-
You also need minor changes to the :code:`main_window.py` and
:code:`main.py` from chapter 3 to include the Widget inside the
@@ -62,11 +63,11 @@ In the following snippets you'll see those changes highlighted:
.. literalinclude:: datavisualize4/main_window.py
:language: python
:linenos:
- :lines: 3-
- :emphasize-lines: 8,11
+ :lines: 5-
+ :emphasize-lines: 9
.. literalinclude:: datavisualize4/main.py
:language: python
:linenos:
- :lines: 3-
- :emphasize-lines: 46-47
+ :lines: 5-
+ :emphasize-lines: 45-46
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/datavisualize3.pyproject b/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/datavisualize3.pyproject
new file mode 100644
index 000000000..1bd31f959
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/datavisualize3.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "main_window.py"]
+}
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py
index 6ee8fa61b..79a4afd36 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py
@@ -2,25 +2,22 @@
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
-from PySide6.QtGui import QAction, QKeySequence
+from PySide6.QtGui import QIcon, QKeySequence
from PySide6.QtWidgets import QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
- QMainWindow.__init__(self)
+ super().__init__()
self.setWindowTitle("Eartquakes information")
# Menu
self.menu = self.menuBar()
- self.file_menu = self.menu.addMenu("File")
+ file_menu = self.menu.addMenu("File")
# Exit QAction
- exit_action = QAction("Exit", self)
- exit_action.setShortcut(QKeySequence.Quit)
- exit_action.triggered.connect(self.close)
-
- self.file_menu.addAction(exit_action)
+ file_menu.addAction(QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit),
+ "Exit", QKeySequence.StandardKey.Quit, self.close)
# Status Bar
self.status = self.statusBar()
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/datavisualize4.pyproject b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/datavisualize4.pyproject
new file mode 100644
index 000000000..f54969728
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/datavisualize4.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "main_widget.py", "main_window.py", "table_model.py"]
+}
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py
index 85e24833f..5d8e6ade3 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py
@@ -10,7 +10,7 @@ from table_model import CustomTableModel
class Widget(QWidget):
def __init__(self, data):
- QWidget.__init__(self)
+ super().__init__()
# Getting the Model
self.model = CustomTableModel(data)
@@ -22,13 +22,13 @@ class Widget(QWidget):
# QTableView Headers
self.horizontal_header = self.table_view.horizontalHeader()
self.vertical_header = self.table_view.verticalHeader()
- self.horizontal_header.setSectionResizeMode(QHeaderView.ResizeToContents)
- self.vertical_header.setSectionResizeMode(QHeaderView.ResizeToContents)
+ self.horizontal_header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
+ self.vertical_header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
self.horizontal_header.setStretchLastSection(True)
# QWidget Layout
self.main_layout = QHBoxLayout()
- size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ size = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
# Left layout
size.setHorizontalStretch(1)
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py
index ded7fdf5c..600af6503 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py
@@ -2,25 +2,22 @@
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
-from PySide6.QtGui import QAction, QKeySequence
+from PySide6.QtGui import QIcon, QKeySequence
from PySide6.QtWidgets import QMainWindow
class MainWindow(QMainWindow):
def __init__(self, widget):
- QMainWindow.__init__(self)
+ super().__init__()
self.setWindowTitle("Eartquakes information")
self.setCentralWidget(widget)
# Menu
self.menu = self.menuBar()
- self.file_menu = self.menu.addMenu("File")
+ file_menu = self.menu.addMenu("File")
# Exit QAction
- exit_action = QAction("Exit", self)
- exit_action.setShortcut(QKeySequence.Quit)
- exit_action.triggered.connect(self.close)
-
- self.file_menu.addAction(exit_action)
+ file_menu.addAction(QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit),
+ "Exit", QKeySequence.StandardKey.Quit, self.close)
# Status Bar
self.status = self.statusBar()
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py
index cc2ac12ab..9a2871c22 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py
@@ -25,27 +25,27 @@ class CustomTableModel(QAbstractTableModel):
return self.column_count
def headerData(self, section, orientation, role):
- if role != Qt.DisplayRole:
+ if role != Qt.ItemDataRole.DisplayRole:
return None
- if orientation == Qt.Horizontal:
+ if orientation == Qt.Orientation.Horizontal:
return ("Date", "Magnitude")[section]
else:
return f"{section}"
- def data(self, index, role=Qt.DisplayRole):
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
column = index.column()
row = index.row()
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
if column == 0:
date = self.input_dates[row].toPython()
return str(date)[:-3]
elif column == 1:
magnitude = self.input_magnitudes[row]
return f"{magnitude:.2f}"
- elif role == Qt.BackgroundRole:
- return QColor(Qt.white)
- elif role == Qt.TextAlignmentRole:
- return Qt.AlignRight
+ elif role == Qt.ItemDataRole.BackgroundRole:
+ return QColor(Qt.GlobalColor.white)
+ elif role == Qt.ItemDataRole.TextAlignmentRole:
+ return Qt.AlignmentFlag.AlignRight
return None
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/datavisualize5.pyproject b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/datavisualize5.pyproject
new file mode 100644
index 000000000..f54969728
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/datavisualize5.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "main_widget.py", "main_window.py", "table_model.py"]
+}
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py
index 77ea4e776..fca09b059 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py
@@ -2,17 +2,17 @@
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
-from PySide6.QtGui import QPainter
from PySide6.QtWidgets import (QWidget, QHeaderView, QHBoxLayout, QTableView,
QSizePolicy)
-from PySide6.QtCharts import QChart, QChartView
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphs import QLineSeries, QDateTimeAxis, QValueAxis, QGraphsTheme
from table_model import CustomTableModel
class Widget(QWidget):
def __init__(self, data):
- QWidget.__init__(self)
+ super().__init__()
# Getting the Model
self.model = CustomTableModel(data)
@@ -24,21 +24,27 @@ class Widget(QWidget):
# QTableView Headers
self.horizontal_header = self.table_view.horizontalHeader()
self.vertical_header = self.table_view.verticalHeader()
- self.horizontal_header.setSectionResizeMode(QHeaderView.ResizeToContents)
- self.vertical_header.setSectionResizeMode(QHeaderView.ResizeToContents)
+ self.horizontal_header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
+ self.vertical_header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
self.horizontal_header.setStretchLastSection(True)
- # Creating QChart
- self.chart = QChart()
- self.chart.setAnimationOptions(QChart.AllAnimations)
-
- # Creating QChartView
- self.chart_view = QChartView(self.chart)
- self.chart_view.setRenderHint(QPainter.Antialiasing)
+ # Create QGraphView via QML
+ self.series = QLineSeries()
+ self.axis_x = QDateTimeAxis()
+ self.axis_y = QValueAxis()
+ self.quick_widget = QQuickWidget(self)
+ self.quick_widget.setResizeMode(QQuickWidget.ResizeMode.SizeRootObjectToView)
+ self.theme = QGraphsTheme()
+ initial_properties = {"theme": self.theme,
+ "axisX": self.axis_x,
+ "axisY": self.axis_y,
+ "seriesList": self.series}
+ self.quick_widget.setInitialProperties(initial_properties)
+ self.quick_widget.loadFromModule("QtGraphs", "GraphsView")
# QWidget Layout
- self.main_layout = QHBoxLayout()
- size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ self.main_layout = QHBoxLayout(self)
+ size = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
# Left layout
size.setHorizontalStretch(1)
@@ -47,8 +53,5 @@ class Widget(QWidget):
# Right Layout
size.setHorizontalStretch(4)
- self.chart_view.setSizePolicy(size)
- self.main_layout.addWidget(self.chart_view)
-
- # Set the layout to the QWidget
- self.setLayout(self.main_layout)
+ self.quick_widget.setSizePolicy(size)
+ self.main_layout.addWidget(self.quick_widget)
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py
index ded7fdf5c..600af6503 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py
@@ -2,25 +2,22 @@
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
-from PySide6.QtGui import QAction, QKeySequence
+from PySide6.QtGui import QIcon, QKeySequence
from PySide6.QtWidgets import QMainWindow
class MainWindow(QMainWindow):
def __init__(self, widget):
- QMainWindow.__init__(self)
+ super().__init__()
self.setWindowTitle("Eartquakes information")
self.setCentralWidget(widget)
# Menu
self.menu = self.menuBar()
- self.file_menu = self.menu.addMenu("File")
+ file_menu = self.menu.addMenu("File")
# Exit QAction
- exit_action = QAction("Exit", self)
- exit_action.setShortcut(QKeySequence.Quit)
- exit_action.triggered.connect(self.close)
-
- self.file_menu.addAction(exit_action)
+ file_menu.addAction(QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit),
+ "Exit", QKeySequence.StandardKey.Quit, self.close)
# Status Bar
self.status = self.statusBar()
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py
index cc2ac12ab..9a2871c22 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py
@@ -25,27 +25,27 @@ class CustomTableModel(QAbstractTableModel):
return self.column_count
def headerData(self, section, orientation, role):
- if role != Qt.DisplayRole:
+ if role != Qt.ItemDataRole.DisplayRole:
return None
- if orientation == Qt.Horizontal:
+ if orientation == Qt.Orientation.Horizontal:
return ("Date", "Magnitude")[section]
else:
return f"{section}"
- def data(self, index, role=Qt.DisplayRole):
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
column = index.column()
row = index.row()
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
if column == 0:
date = self.input_dates[row].toPython()
return str(date)[:-3]
elif column == 1:
magnitude = self.input_magnitudes[row]
return f"{magnitude:.2f}"
- elif role == Qt.BackgroundRole:
- return QColor(Qt.white)
- elif role == Qt.TextAlignmentRole:
- return Qt.AlignRight
+ elif role == Qt.ItemDataRole.BackgroundRole:
+ return QColor(Qt.GlobalColor.white)
+ elif role == Qt.ItemDataRole.TextAlignmentRole:
+ return Qt.AlignmentFlag.AlignRight
return None
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/datavisualize6.pyproject b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/datavisualize6.pyproject
new file mode 100644
index 000000000..f54969728
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/datavisualize6.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "main_widget.py", "main_window.py", "table_model.py"]
+}
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.py
index f987689ea..336afacd8 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.py
@@ -2,18 +2,20 @@
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
-from PySide6.QtCore import QDateTime, Qt
-from PySide6.QtGui import QPainter
+from math import floor, ceil
+
+from PySide6.QtCore import QDateTime, QTime, QTimeZone
from PySide6.QtWidgets import (QWidget, QHeaderView, QHBoxLayout, QTableView,
QSizePolicy)
-from PySide6.QtCharts import QChart, QChartView, QLineSeries, QDateTimeAxis, QValueAxis
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphs import QLineSeries, QDateTimeAxis, QValueAxis, QGraphsTheme
from table_model import CustomTableModel
class Widget(QWidget):
def __init__(self, data):
- QWidget.__init__(self)
+ super().__init__()
# Getting the Model
self.model = CustomTableModel(data)
@@ -23,25 +25,29 @@ class Widget(QWidget):
self.table_view.setModel(self.model)
# QTableView Headers
- resize = QHeaderView.ResizeToContents
+ resize = QHeaderView.ResizeMode.ResizeToContents
self.horizontal_header = self.table_view.horizontalHeader()
self.vertical_header = self.table_view.verticalHeader()
self.horizontal_header.setSectionResizeMode(resize)
self.vertical_header.setSectionResizeMode(resize)
self.horizontal_header.setStretchLastSection(True)
- # Creating QChart
- self.chart = QChart()
- self.chart.setAnimationOptions(QChart.AllAnimations)
- self.add_series("Magnitude (Column 1)", [0, 1])
-
- # Creating QChartView
- self.chart_view = QChartView(self.chart)
- self.chart_view.setRenderHint(QPainter.Antialiasing)
+ # Create QGraphView via QML
+ self.populate_series()
+ self.quick_widget = QQuickWidget(self)
+ self.quick_widget.setResizeMode(QQuickWidget.ResizeMode.SizeRootObjectToView)
+ self.theme = QGraphsTheme()
+ self.theme.setTheme(QGraphsTheme.Theme.BlueSeries)
+ initial_properties = {"theme": self.theme,
+ "axisX": self.axis_x,
+ "axisY": self.axis_y,
+ "seriesList": self.series}
+ self.quick_widget.setInitialProperties(initial_properties)
+ self.quick_widget.loadFromModule("QtGraphs", "GraphsView")
# QWidget Layout
- self.main_layout = QHBoxLayout()
- size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ self.main_layout = QHBoxLayout(self)
+ size = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
# Left layout
size.setHorizontalStretch(1)
@@ -50,46 +56,51 @@ class Widget(QWidget):
# Right Layout
size.setHorizontalStretch(4)
- self.chart_view.setSizePolicy(size)
- self.main_layout.addWidget(self.chart_view)
+ self.quick_widget.setSizePolicy(size)
+ self.main_layout.addWidget(self.quick_widget)
- # Set the layout to the QWidget
- self.setLayout(self.main_layout)
+ def populate_series(self):
+ def seconds(qtime: QTime):
+ return qtime.minute() * 60 + qtime.second()
- def add_series(self, name, columns):
- # Create QLineSeries
self.series = QLineSeries()
- self.series.setName(name)
+ self.series.setName("Magnitude (Column 1)")
# Filling QLineSeries
+ time_min = QDateTime(2100, 1, 1, 0, 0, 0)
+ time_max = QDateTime(1970, 1, 1, 0, 0, 0)
+ time_zone = QTimeZone(QTimeZone.Initialization.UTC)
+ y_min = 1e37
+ y_max = -1e37
+ date_fmt = "yyyy-MM-dd HH:mm:ss.zzz"
for i in range(self.model.rowCount()):
- # Getting the data
t = self.model.index(i, 0).data()
- date_fmt = "yyyy-MM-dd HH:mm:ss.zzz"
-
- x = QDateTime().fromString(t, date_fmt).toSecsSinceEpoch()
+ time = QDateTime.fromString(t, date_fmt)
+ time.setTimeZone(time_zone)
y = float(self.model.index(i, 1).data())
-
- if x > 0 and y > 0:
- self.series.append(x, y)
-
- self.chart.addSeries(self.series)
+ if time.isValid() and y > 0:
+ if time > time_max:
+ time_max = time
+ if time < time_min:
+ time_min = time
+ if y > y_max:
+ y_max = y
+ if y < y_min:
+ y_min = y
+ self.series.append(time.toMSecsSinceEpoch(), y)
# Setting X-axis
self.axis_x = QDateTimeAxis()
- self.axis_x.setTickCount(10)
- self.axis_x.setFormat("dd.MM (h:mm)")
+ self.axis_x.setLabelFormat("dd.MM (h:mm)")
self.axis_x.setTitleText("Date")
- self.chart.addAxis(self.axis_x, Qt.AlignBottom)
- self.series.attachAxis(self.axis_x)
+ self.axis_x.setMin(time_min.addSecs(-seconds(time_min.time())))
+ self.axis_x.setMax(time_max.addSecs(3600 - seconds(time_max.time())))
+ self.series.setAxisX(self.axis_x)
+
# Setting Y-axis
self.axis_y = QValueAxis()
- self.axis_y.setTickCount(10)
self.axis_y.setLabelFormat("%.2f")
self.axis_y.setTitleText("Magnitude")
- self.chart.addAxis(self.axis_y, Qt.AlignLeft)
- self.series.attachAxis(self.axis_y)
-
- # Getting the color from the QChart to use it on the QTableView
- color_name = self.series.pen().color().name()
- self.model.color = f"{color_name}"
+ self.axis_y.setMin(floor(y_min))
+ self.axis_y.setMax(ceil(y_max))
+ self.series.setAxisY(self.axis_y)
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py
index f37268df8..6a9eaea8e 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py
@@ -2,26 +2,22 @@
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
-from PySide6.QtGui import QAction, QKeySequence
+from PySide6.QtGui import QIcon, QKeySequence
from PySide6.QtWidgets import QMainWindow
class MainWindow(QMainWindow):
def __init__(self, widget):
- QMainWindow.__init__(self)
+ super().__init__()
self.setWindowTitle("Eartquakes information")
# Menu
self.menu = self.menuBar()
- self.file_menu = self.menu.addMenu("File")
+ file_menu = self.menu.addMenu("File")
# Exit QAction
- exit_action = QAction("Exit", self)
- exit_action.setShortcut(QKeySequence.Quit)
- exit_action.triggered.connect(self.close)
-
- self.file_menu.addAction(exit_action)
-
+ file_menu.addAction(QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit),
+ "Exit", QKeySequence.StandardKey.Quit, self.close)
# Status Bar
self.status = self.statusBar()
self.status.showMessage("Data loaded and plotted")
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py
index 3201e5887..9a2871c22 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py
@@ -9,7 +9,6 @@ from PySide6.QtGui import QColor
class CustomTableModel(QAbstractTableModel):
def __init__(self, data=None):
QAbstractTableModel.__init__(self)
- self.color = None
self.load_data(data)
def load_data(self, data):
@@ -26,27 +25,27 @@ class CustomTableModel(QAbstractTableModel):
return self.column_count
def headerData(self, section, orientation, role):
- if role != Qt.DisplayRole:
+ if role != Qt.ItemDataRole.DisplayRole:
return None
- if orientation == Qt.Horizontal:
+ if orientation == Qt.Orientation.Horizontal:
return ("Date", "Magnitude")[section]
else:
return f"{section}"
- def data(self, index, role=Qt.DisplayRole):
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
column = index.column()
row = index.row()
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
if column == 0:
date = self.input_dates[row].toPython()
return str(date)[:-3]
elif column == 1:
magnitude = self.input_magnitudes[row]
return f"{magnitude:.2f}"
- elif role == Qt.BackgroundRole:
- return (QColor(Qt.white), QColor(self.color))[column]
- elif role == Qt.TextAlignmentRole:
- return Qt.AlignRight
+ elif role == Qt.ItemDataRole.BackgroundRole:
+ return QColor(Qt.GlobalColor.white)
+ elif role == Qt.ItemDataRole.TextAlignmentRole:
+ return Qt.AlignmentFlag.AlignRight
return None
diff --git a/sources/pyside6/doc/tutorials/datavisualize/filter_data.rst b/sources/pyside6/doc/tutorials/datavisualize/filter_data.rst
index bef134e5b..4edde69c1 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/filter_data.rst
+++ b/sources/pyside6/doc/tutorials/datavisualize/filter_data.rst
@@ -17,7 +17,8 @@ be done by filtering the data that follows the condition, "magnitude > 0", to
avoid faulty data or unexpected behavior.
The Date column provides data in UTC format (for example,
-2018-12-11T21:14:44.682Z), so you could easily map it to a QDateTime object
+2018-12-11T21:14:44.682Z), so you could easily map it to a
+class:`~PySide6.QtCore.QDateTime` object
defining the structure of the string. Additionally, you can adapt the time
based on the timezone you are in, using QTimeZone.
@@ -26,7 +27,7 @@ The following script filters and formats the CSV data as described earlier:
.. literalinclude:: datavisualize2/main.py
:language: python
:linenos:
- :lines: 3-
+ :lines: 5-
-Now that you have a tuple of QDateTime and float data, try improving the
+Now that you have a tuple of ``QDateTime`` and float data, try improving the
output further. That's what you'll learn in the following chapters.
diff --git a/sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst b/sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst
index e4374e861..47d12a7c4 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst
+++ b/sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst
@@ -1,23 +1,23 @@
.. _tutorial_plot_datapoints:
-Chapter 6 - Plot the data in the ChartView
+Chapter 6 - Plot the data in the GraphsView
===========================================
-The last step of this tutorial is to plot the CSV data inside our QChart. For
-this, you need to go over our data and include the data on a QLineSeries.
+The last step of this tutorial is to plot the CSV data inside our GraphsView.
+For this, you need to go over our data and include the data on a QLineSeries.
After adding the data to the series, you can modify the axis to properly
display the QDateTime on the X-axis, and the magnitude values on the Y-axis.
Here is the updated :code:`main_widget.py` that includes an additional
-function to plot data using a QLineSeries:
+function to plot data using a :class:`~PySide6.QtGraphs.QLineSeries`:
.. literalinclude:: datavisualize6/main_widget.py
:language: python
:linenos:
- :lines: 3-
- :emphasize-lines: 33,56-91
+ :lines: 5-
+ :emphasize-lines: 31-42, 68-102
Now, run the application to visualize the earthquake magnitudes
data at different times.
diff --git a/sources/pyside6/doc/tutorials/datavisualize/read_data.rst b/sources/pyside6/doc/tutorials/datavisualize/read_data.rst
index 8be0e1c2f..d083a05ee 100644
--- a/sources/pyside6/doc/tutorials/datavisualize/read_data.rst
+++ b/sources/pyside6/doc/tutorials/datavisualize/read_data.rst
@@ -21,7 +21,7 @@ The following python script, :code:`main.py`, demonstrates how to do it:
.. literalinclude:: datavisualize1/main.py
:language: python
:linenos:
- :lines: 3-
+ :lines: 5-
The Python script uses the :code:`argparse` module to accept and parse input
from the command line. It then uses the input, which in this case is the filename,
diff --git a/sources/pyside6/tests/QtCore/qobject_property_test.py b/sources/pyside6/tests/QtCore/qobject_property_test.py
index 9d2bd2c56..e0a8044fe 100644
--- a/sources/pyside6/tests/QtCore/qobject_property_test.py
+++ b/sources/pyside6/tests/QtCore/qobject_property_test.py
@@ -106,9 +106,10 @@ class QObjectWithOtherClassPropertyTest(unittest.TestCase):
class VariantPropertyTest(unittest.TestCase):
- """Test QVariant conversion in properties and signals (PYSIDE-3206, PYSIDE-3244).
- It uses a property of list type that is passed a QVariantList
- with various element types when using QObject.setProperty()."""
+ """Test QVariant conversion in properties and signals (PYSIDE-3256,
+ PYSIDE-3244, PYSIDE-3206 [open]). It uses a property of list type
+ that is passed a QVariantList with various element types when
+ using QObject.setProperty()."""
def testIt(self):
to = TestVariantPropertyObject()
@@ -123,11 +124,11 @@ class VariantPropertyTest(unittest.TestCase):
to.setProperty("testProperty", [{"key": 42}])
self.assertEqual(type(to.get_property()[0]), dict)
- # PYSIDE-3206 (DBus): Convert a tuple to a list
+ # Tuple (PYSIDE-3256)
to.setProperty("testProperty", [(1, 2)])
- self.assertEqual(type(to.get_property()[0]), list)
+ self.assertEqual(type(to.get_property()[0]), tuple)
- # PYSIDE-324: The tuple conversion must not occur for named tuples
+ # Named Tuple (PYSIDE-3244)
to.setProperty("testProperty", [Point(1, 2)])
self.assertEqual(type(to.get_property()[0]), Point)
diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst
index 7fe4df392..7c3c1ae63 100644
--- a/sources/shiboken6/doc/typesystem_specifying_types.rst
+++ b/sources/shiboken6/doc/typesystem_specifying_types.rst
@@ -770,6 +770,7 @@ will be generated into specific modules.
reset-method="..."
instantiations="..."
excluded-instantiations="..."/>
+ to-python="default"
</typesystem>
@@ -797,6 +798,24 @@ comma-separated list of types to be excluded from instantiating. Typically,
this is used to exclude instantiations present in an underlying base module to
prevent symbol clashes.
+The *optional* **to-python** attribute specifies how a smart pointer
+instance is converted to Python:
+
+.. list-table::
+ :header-rows: 1
+
+ * - Value
+
+ - Meaning
+
+ * - ``default``
+
+ - A smart pointer instance is returned in all cases
+
+ * - ``null-as-none``
+
+ - ``None`` is returned if the smart pointer is null.
+
The *optional* attribute **type** specifies the type:
*shared*
diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py
index 6f86df8c8..b31b161a3 100644
--- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py
+++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py
@@ -53,6 +53,12 @@ def is_inconsistent_overload(signatures):
return count != 0 and count != len(signatures)
+def is_relevant_type(thing):
+ t = str(type(thing))
+ return (("PySide" in t or "getset_descriptor" in t)
+ and "QMetaObject" not in t)
+
+
class ExactEnumerator:
"""
ExactEnumerator enumerates all signatures in a module as they are.
@@ -178,7 +184,9 @@ class ExactEnumerator:
# Support attributes that have PySide types as values,
# but we skip the 'staticMetaObject' that needs
# to be defined at a QObject level.
- elif "PySide" in str(type(thing)) and "QMetaObject" not in str(type(thing)):
+ # PYSIDE-3034: added public variables, extracted helper function to
+ # avoid repetitive calls of str(type(thing))
+ elif is_relevant_type(thing):
if class_name not in attributes:
attributes[class_name] = {}
attributes[class_name][thing_name] = thing
diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py
index cdb4d9575..a84eb38dd 100644
--- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py
+++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py
@@ -32,6 +32,38 @@ from shibokensupport.signature.lib.tool import build_brace_pattern
indent = " " * 4
+TYPE_MAP = {
+ # Qt integer types
+ "qint64": "int",
+ "qint32": "int",
+ "qint16": "int",
+ "qsizetype": "int",
+ "quint32": "int",
+ "quint64": "int",
+ "size_t": "int",
+ "uint": "int",
+ "ushort": "int",
+ "ulong": "int",
+ "unsigned char": "int",
+ "unsigned int": "int",
+
+ # Qt floating types
+ "qreal": "float",
+
+ # Qt string-like
+ "QString": "str",
+ "QStringList": "typing.List[str]",
+ "QChar": "str",
+
+ # Qt containers (minimal)
+ "QList": "typing.List",
+ "QVariant": "typing.Any",
+
+ # C strings
+ "char*": "str",
+ "const char*": "str",
+}
+
class Writer:
def __init__(self, outfile, *args):
@@ -86,6 +118,29 @@ class Formatter(Writer):
inspect.formatannotation = cls.backup
return stringized
+ @classmethod
+ def normalize_type(cls, type_repr: str) -> str:
+ if not type_repr:
+ return "typing.Any"
+ if type_repr in {"void", "void*"}:
+ return "typing.Any"
+ if any(x in type_repr for x in ("QRhi", ".ComponentType", ".Semantic")):
+ return "int"
+ if ( " " in type_repr and
+ not any(x in type_repr for x in ("*", "::", "<", ">", "[", "]"))):
+ return "typing.Any"
+ if type_repr.startswith("QList["):
+ inner = type_repr[len("QList["):-1]
+ inner = cls.normalize_type(inner)
+ return f"typing.List[{inner}]"
+ if type_repr.startswith("QMap[") or type_repr.startswith("QHash["):
+ inner = type_repr[type_repr.find("[") + 1:-1]
+ key, value = map(str.strip, inner.split(",", 1))
+ key = cls.normalize_type(key)
+ value = cls.normalize_type(value)
+ return f"typing.Dict[{key}, {value}]"
+ return TYPE_MAP.get(type_repr, type_repr)
+
# Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]"
# I tried hard to replace typing.Optional by a simple override, but
# this became _way_ too much.
@@ -221,7 +276,12 @@ class Formatter(Writer):
spaces = indent * self.level
# PYSIDE-2903: Use a fully qualified name in the type comment.
full_name = f"{type(attr_value).__module__}.{type(attr_value).__qualname__}"
- self.print(f"{spaces}{attr_name:25} = ... # type: {full_name}")
+ if full_name == "builtins.getset_descriptor":
+ # PYSIDE-3034: Public variable types added to __doc__
+ type_repr = self.normalize_type(attr_value.__doc__)
+ else:
+ type_repr = full_name
+ self.print(f"{spaces}{attr_name:25} = ... # type: {type_repr}")
yield
@contextmanager
diff --git a/sources/shiboken6/tests/libsmart/smart.cpp b/sources/shiboken6/tests/libsmart/smart.cpp
index 2273040f9..6dd4c3c6b 100644
--- a/sources/shiboken6/tests/libsmart/smart.cpp
+++ b/sources/shiboken6/tests/libsmart/smart.cpp
@@ -147,6 +147,18 @@ SharedPtr<const Integer> Obj::createSharedPtrConstInteger()
return co;
}
+SharedPtr2<Integer> Obj::createNullSharedPtr2Integer()
+{
+ return {};
+}
+
+SharedPtr2<Integer> Obj::createSharedPtr2Integer(int value)
+{
+ auto *i = new Integer;
+ i->setValue(value);
+ return SharedPtr2<Integer>(i);
+}
+
int Obj::takeSharedPtrToConstInteger(SharedPtr<const Integer> pInt)
{
return pInt->m_int;
diff --git a/sources/shiboken6/tests/libsmart/smart_obj.h b/sources/shiboken6/tests/libsmart/smart_obj.h
index 9f4f8425d..fceca0b6d 100644
--- a/sources/shiboken6/tests/libsmart/smart_obj.h
+++ b/sources/shiboken6/tests/libsmart/smart_obj.h
@@ -38,6 +38,9 @@ public:
static SharedPtr<Integer> createSharedPtrInteger(int value);
static SharedPtr<Integer> createNullSharedPtrInteger();
+ static SharedPtr2<Integer> createNullSharedPtr2Integer();
+ static SharedPtr2<Integer> createSharedPtr2Integer(int value);
+
int m_integer; // public for testing member field access.
Integer *m_internalInteger;
};
diff --git a/sources/shiboken6/tests/libsmart/smart_sharedptr.h b/sources/shiboken6/tests/libsmart/smart_sharedptr.h
index dc665810a..7a77b3d6c 100644
--- a/sources/shiboken6/tests/libsmart/smart_sharedptr.h
+++ b/sources/shiboken6/tests/libsmart/smart_sharedptr.h
@@ -91,4 +91,10 @@ public:
std::shared_ptr<T> mPtr;
};
+template <class T>
+class SharedPtr2 : public SharedPtr<T> {
+public:
+ using SharedPtr<T>::SharedPtr;
+};
+
#endif // SMART_SHARED_PTR_H
diff --git a/sources/shiboken6/tests/smartbinding/CMakeLists.txt b/sources/shiboken6/tests/smartbinding/CMakeLists.txt
index 02c4e6596..5b3f4feda 100644
--- a/sources/shiboken6/tests/smartbinding/CMakeLists.txt
+++ b/sources/shiboken6/tests/smartbinding/CMakeLists.txt
@@ -13,6 +13,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/smart/obj_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/integer_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/sharedptr_obj_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/sharedptr_integer_wrapper.cpp
+${CMAKE_CURRENT_BINARY_DIR}/smart/sharedptr2_integer_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/registry_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/smart_integer2_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/sharedptr_integer2_wrapper.cpp
diff --git a/sources/shiboken6/tests/smartbinding/smart_pointer_test.py b/sources/shiboken6/tests/smartbinding/smart_pointer_test.py
index 64267fba7..761478df0 100644
--- a/sources/shiboken6/tests/smartbinding/smart_pointer_test.py
+++ b/sources/shiboken6/tests/smartbinding/smart_pointer_test.py
@@ -288,6 +288,14 @@ class SmartPointerTests(unittest.TestCase):
o.takeSharedPtrToInteger(None)
o.takeSharedPtrToIntegerByConstRef(None)
+ def testNoneConversion(self):
+ """PYSIDE-3253: SharedPtr2 is configured to convert to None."""
+ valid_ptr = Obj.createSharedPtr2Integer(42)
+ null_ptr = Obj.createNullSharedPtr2Integer()
+ self.assertEqual(valid_ptr.value(), 42)
+ self.assertFalse(valid_ptr is None)
+ self.assertTrue(null_ptr is None)
+
def testConstruction(self):
p1 = SharedPtr_Integer(integerFromValue(42))
self.assertEqual(p1.value(), 42)
diff --git a/sources/shiboken6/tests/smartbinding/typesystem_smart.xml b/sources/shiboken6/tests/smartbinding/typesystem_smart.xml
index 4024036fa..69c653286 100644
--- a/sources/shiboken6/tests/smartbinding/typesystem_smart.xml
+++ b/sources/shiboken6/tests/smartbinding/typesystem_smart.xml
@@ -13,6 +13,9 @@
<smart-pointer-type name="SharedPtr" type="shared" getter="data" ref-count-method="useCount"
null-check-method="isNull"
instantiations="Integer,Smart::Integer2=Test::SmartInteger2Ptr,Obj"/>
+ <smart-pointer-type name="SharedPtr2" type="shared" getter="data" ref-count-method="useCount"
+ null-check-method="isNull" to-python="null-as-none"
+ instantiations="Integer"/>
<object-type name="Obj" />
<value-type name="Integer" />
diff --git a/sources/shiboken6_generator/ApiExtractor/smartpointertypeentry.h b/sources/shiboken6_generator/ApiExtractor/smartpointertypeentry.h
index 7b67647b9..f3c4eef4e 100644
--- a/sources/shiboken6_generator/ApiExtractor/smartpointertypeentry.h
+++ b/sources/shiboken6_generator/ApiExtractor/smartpointertypeentry.h
@@ -50,6 +50,9 @@ public:
void setExcludedInstantiations(const TypeEntryCList &ex);
const TypeEntryCList &excludedInstantiations() const;
+ TypeSystem::SmartPointerToPythonConversion toPythonConversion() const;
+ void setToPythonConversion(TypeSystem::SmartPointerToPythonConversion c);
+
QString getTargetName(const AbstractMetaType &metaType) const;
bool hasCustomConversion() const;
diff --git a/sources/shiboken6_generator/ApiExtractor/typesystem.cpp b/sources/shiboken6_generator/ApiExtractor/typesystem.cpp
index c02ec0c93..7938ce64a 100644
--- a/sources/shiboken6_generator/ApiExtractor/typesystem.cpp
+++ b/sources/shiboken6_generator/ApiExtractor/typesystem.cpp
@@ -2182,6 +2182,8 @@ public:
TypeEntryCList m_excludedInstantiations;
CustomConversionPtr m_customConversion;
TypeSystem::SmartPointerType m_smartPointerType;
+ TypeSystem::SmartPointerToPythonConversion m_toPythonConversion =
+ TypeSystem::SmartPointerToPythonConversion::Default;
};
qsizetype SmartPointerTypeEntryPrivate::instantiationIndex(const TypeEntryCPtr &t) const
@@ -2293,6 +2295,18 @@ const TypeEntryCList &SmartPointerTypeEntry::excludedInstantiations() const
return d->m_excludedInstantiations;
}
+TypeSystem::SmartPointerToPythonConversion SmartPointerTypeEntry::toPythonConversion() const
+{
+ S_D(const SmartPointerTypeEntry);
+ return d->m_toPythonConversion;
+}
+
+void SmartPointerTypeEntry::setToPythonConversion(TypeSystem::SmartPointerToPythonConversion c)
+{
+ S_D(SmartPointerTypeEntry);
+ d->m_toPythonConversion = c;
+}
+
SmartPointerTypeEntry::SmartPointerTypeEntry(SmartPointerTypeEntryPrivate *d) :
ComplexTypeEntry(d)
{
diff --git a/sources/shiboken6_generator/ApiExtractor/typesystem_enums.h b/sources/shiboken6_generator/ApiExtractor/typesystem_enums.h
index bb2b5cd6f..4237e1593 100644
--- a/sources/shiboken6_generator/ApiExtractor/typesystem_enums.h
+++ b/sources/shiboken6_generator/ApiExtractor/typesystem_enums.h
@@ -106,6 +106,11 @@ enum class SmartPointerType : std::uint8_t {
ValueHandle
};
+enum class SmartPointerToPythonConversion : std::uint8_t {
+ Default,
+ NullAsNone,
+};
+
enum class PythonEnumType : std::uint8_t {
Unspecified,
Enum,
diff --git a/sources/shiboken6_generator/ApiExtractor/typesystemparser.cpp b/sources/shiboken6_generator/ApiExtractor/typesystemparser.cpp
index a4dfe5e6c..fc591e6bb 100644
--- a/sources/shiboken6_generator/ApiExtractor/typesystemparser.cpp
+++ b/sources/shiboken6_generator/ApiExtractor/typesystemparser.cpp
@@ -50,6 +50,7 @@ constexpr auto allowThreadAttribute = "allow-thread"_L1;
constexpr auto checkFunctionAttribute = "check-function"_L1;
constexpr auto defaultConstructibleAttribute = "default-constructible"_L1;
constexpr auto copyableAttribute = "copyable"_L1;
+constexpr auto smartPointerToPythonConversionAttribute = "to-python"_L1;
constexpr auto movableAttribute = "movable"_L1;
constexpr auto accessAttribute = "access"_L1;
constexpr auto actionAttribute = "action"_L1;
@@ -425,6 +426,14 @@ ENUM_LOOKUP_BEGIN(TypeSystem::SmartPointerType, Qt::CaseSensitive,
};
ENUM_LOOKUP_LINEAR_SEARCH
+ENUM_LOOKUP_BEGIN(TypeSystem::SmartPointerToPythonConversion, Qt::CaseSensitive,
+ smartPointerToPythonConversionFromAttribute)
+{
+ {u"default", TypeSystem::SmartPointerToPythonConversion::Default},
+ {u"null-as-none", TypeSystem::SmartPointerToPythonConversion::NullAsNone}
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
template <class EnumType>
static std::optional<EnumType>
lookupHashElement(const QHash<QStringView, EnumType> &hash,
@@ -1430,6 +1439,8 @@ SmartPointerTypeEntryPtr
TypeDatabaseParserContext::SmartPointerEntry entry;
QString instantiations;
QString excludedInstantiations;
+ TypeSystem::SmartPointerToPythonConversion conversion =
+ TypeSystem::SmartPointerToPythonConversion::Default;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
if (name == u"type") {
@@ -1454,6 +1465,14 @@ SmartPointerTypeEntryPtr
nullCheckMethod = attributes->takeAt(i).value().toString();
} else if (name == u"reset-method") {
resetMethod = attributes->takeAt(i).value().toString();
+ } else if (name == smartPointerToPythonConversionAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto convOpt = smartPointerToPythonConversionFromAttribute(attribute.value());
+ if (!convOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return nullptr;
+ }
+ conversion = convOpt.value();
}
}
@@ -1486,6 +1505,7 @@ SmartPointerTypeEntryPtr
currentParentTypeEntry());
if (!applyComplexTypeAttributes(reader, type, attributes))
return nullptr;
+ type->setToPythonConversion(conversion);
type->setNullCheckMethod(nullCheckMethod);
type->setValueCheckMethod(valueCheckMethod);
type->setResetMethod(resetMethod);
diff --git a/sources/shiboken6_generator/generator/shiboken/cppgenerator.cpp b/sources/shiboken6_generator/generator/shiboken/cppgenerator.cpp
index 4854ebf79..138ec8963 100644
--- a/sources/shiboken6_generator/generator/shiboken/cppgenerator.cpp
+++ b/sources/shiboken6_generator/generator/shiboken/cppgenerator.cpp
@@ -480,10 +480,17 @@ static QString BuildEnumFlagInfo(const AbstractMetaEnum &cppEnum)
}
static void writePyGetSetDefEntry(TextStream &s, const QString &name,
- const QString &getFunc, const QString &setFunc)
+ const QString &getFunc, const QString &setFunc, const QString &doc={})
{
- s << "{const_cast<char *>(\"" << mangleName(name) << "\"), " << getFunc << ", "
- << (setFunc.isEmpty() ? NULL_PTR : setFunc) << ", nullptr, nullptr},\n";
+ s << "{\"" << mangleName(name) << "\", " << getFunc << ", "
+ << (setFunc.isEmpty() ? NULL_PTR : setFunc) << ", ";
+
+ if (doc.isEmpty())
+ s << "nullptr";
+ else
+ s << "\"" << doc << "\"";
+
+ s << ", nullptr},\n";
}
static bool generateRichComparison(const GeneratorContext &c)
@@ -922,8 +929,9 @@ void CppGenerator::generateClass(TextStream &s,
const QString setter = canGenerateSetter
? cpythonSetterFunctionName(metaField) : QString();
const auto names = metaField.definitionNames();
+ const QString doc = metaField.type().pythonSignature();
for (const auto &name : names)
- writePyGetSetDefEntry(s, name, getter, setter);
+ writePyGetSetDefEntry(s, name, getter, setter, doc);
}
}
@@ -1769,6 +1777,19 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta
s << '\n';
}
+static void writeSmartPointerNoneReturnCheck(TextStream &c, QAnyStringView varName,
+ const SmartPointerTypeEntryCPtr &ste)
+{
+ c << "if (";
+ if (!ste->nullCheckMethod().isEmpty())
+ c << varName << "->" << ste->nullCheckMethod() << "()";
+ else if (!ste->valueCheckMethod().isEmpty())
+ c << '!' << varName << "->" << ste->valueCheckMethod() << "()";
+ else
+ c << "!*" << varName;
+ c << ")\n" << indent << "Py_RETURN_NONE;\n" << outdent;
+}
+
void CppGenerator::writePointerToPythonConverter(TextStream &c,
const GeneratorContext &context,
const QString &cpythonType)
@@ -1782,6 +1803,16 @@ void CppGenerator::writePointerToPythonConverter(TextStream &c,
QString instanceCast = "auto *tCppIn = reinterpret_cast<const "_L1 + getFullTypeName(context)
+ " *>(cppIn);\n"_L1;
+ if (context.forSmartPointer()) {
+ auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(context.metaClass()->typeEntry());
+ const auto toPythonConversion = ste->toPythonConversion();
+ if (toPythonConversion == TypeSystem::SmartPointerToPythonConversion::NullAsNone) {
+ c << instanceCast;
+ writeSmartPointerNoneReturnCheck(c, "tCppIn", ste);
+ instanceCast.clear();
+ }
+ }
+
const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction();
if (nameFunc.isEmpty() && !metaClass->hasVirtualDestructor()) {
c << "return Shiboken::Object::newObjectWithHeuristics("
@@ -1871,6 +1902,14 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas
} else {
c << "auto *source = reinterpret_cast<const " << typeName << " *>(cppIn);\n";
}
+
+ if (classContext.forSmartPointer()) {
+ auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(classContext.metaClass()->typeEntry());
+ const auto toPythonConversion = ste->toPythonConversion();
+ if (toPythonConversion == TypeSystem::SmartPointerToPythonConversion::NullAsNone)
+ writeSmartPointerNoneReturnCheck(c, "source", ste);
+ }
+
c << "return Shiboken::Object::newObject(" << cpythonType
<< ", new " << globalScopePrefix(classContext) << classContext.effectiveClassName() << '('
<< (needsMove ? "std::move(*source)" : "*source")