aboutsummaryrefslogtreecommitdiffstats
path: root/sources
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2019-06-07 23:20:59 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2019-06-07 23:20:59 +0200
commitb9a857b0fdcad47db958df635a5af934a8e90d62 (patch)
treea82bee72783db8f9063d857205972e71b1df3994 /sources
parent768541fb2e81a426daeffc2f81dcca42ecc43919 (diff)
parentd9f4a921ca88bf0bd340f42a874cf7c22b6b1a36 (diff)
Merge remote-tracking branch 'origin/5.13' into dev
Diffstat (limited to 'sources')
m---------sources/pyside2-tools0
-rw-r--r--sources/pyside2/CMakeLists.txt2
-rw-r--r--sources/pyside2/PySide2/Qt3DAnimation/CMakeLists.txt6
-rw-r--r--sources/pyside2/PySide2/Qt3DAnimation/typesystem_3danimation.xml12
-rw-r--r--sources/pyside2/doc/tutorials/expenses/expenses.rst313
-rw-r--r--sources/pyside2/doc/tutorials/expenses/expenses_tool.pngbin0 -> 47826 bytes
-rw-r--r--sources/pyside2/doc/tutorials/expenses/main.py207
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/01-expenses.py59
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/02-expenses.py70
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/03-expenses.py77
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/04-expenses.py89
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/05-expenses.py117
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/06-expenses.py137
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/07-expenses.py164
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/08-expenses.py177
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/09-expenses.py185
-rw-r--r--sources/pyside2/doc/tutorials/expenses/steps/10-expenses.py207
-rw-r--r--sources/pyside2/doc/tutorials/index.rst1
-rw-r--r--sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py2
-rw-r--r--sources/pyside2/tests/support/voidptr_test.py7
-rw-r--r--sources/shiboken2/ApiExtractor/CMakeLists.txt2
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp24
-rw-r--r--sources/shiboken2/ApiExtractor/parser/codemodel.cpp11
-rw-r--r--sources/shiboken2/ApiExtractor/parser/codemodel.h1
-rw-r--r--sources/shiboken2/CMakeLists.txt13
-rw-r--r--sources/shiboken2/data/shiboken_helpers.cmake2
-rw-r--r--sources/shiboken2/generator/generator.cpp2
-rw-r--r--sources/shiboken2/generator/shiboken2/headergenerator.cpp7
-rw-r--r--sources/shiboken2/generator/shiboken2/shibokengenerator.cpp5
-rw-r--r--sources/shiboken2/libshiboken/qapp_macro.cpp32
-rw-r--r--sources/shiboken2/libshiboken/signature.cpp106
-rw-r--r--sources/shiboken2/libshiboken/voidptr.cpp87
32 files changed, 2081 insertions, 43 deletions
diff --git a/sources/pyside2-tools b/sources/pyside2-tools
-Subproject 0681e65ffa4970c01247b705a6774a937756eb0
+Subproject 329b566e931d3f1b0a8dd319d244432cc740ae9
diff --git a/sources/pyside2/CMakeLists.txt b/sources/pyside2/CMakeLists.txt
index e5e5ab4e7..c5dbc623c 100644
--- a/sources/pyside2/CMakeLists.txt
+++ b/sources/pyside2/CMakeLists.txt
@@ -62,7 +62,7 @@ endif()
find_package(Shiboken2 2.0.0 REQUIRED)
if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE ${SHIBOKEN_BUILD_TYPE})
+ set(CMAKE_BUILD_TYPE "${SHIBOKEN_BUILD_TYPE}" CACHE STRING "Build Type")
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Release")
diff --git a/sources/pyside2/PySide2/Qt3DAnimation/CMakeLists.txt b/sources/pyside2/PySide2/Qt3DAnimation/CMakeLists.txt
index 73682b3bc..5875854d8 100644
--- a/sources/pyside2/PySide2/Qt3DAnimation/CMakeLists.txt
+++ b/sources/pyside2/PySide2/Qt3DAnimation/CMakeLists.txt
@@ -8,14 +8,20 @@ ${Qt3DAnimation_GEN_DIR}/qt3danimation_qabstractclipanimator_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qabstractclipblendnode_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qadditiveclipblend_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qanimationaspect_wrapper.cpp
+${Qt3DAnimation_GEN_DIR}/qt3danimation_qanimationcallback_wrapper.cpp
+${Qt3DAnimation_GEN_DIR}/qt3danimation_qanimationclip_wrapper.cpp
+${Qt3DAnimation_GEN_DIR}/qt3danimation_qanimationcliploader_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qanimationcontroller_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qanimationgroup_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qblendedclipanimator_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qclipanimator_wrapper.cpp
+${Qt3DAnimation_GEN_DIR}/qt3danimation_qclock_wrapper.cpp
+${Qt3DAnimation_GEN_DIR}/qt3danimation_qkeyframe_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qkeyframeanimation_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qlerpclipblend_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qmorphinganimation_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qmorphtarget_wrapper.cpp
+${Qt3DAnimation_GEN_DIR}/qt3danimation_qskeletonmapping_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qvertexblendanimation_wrapper.cpp
# module is always needed
${Qt3DAnimation_GEN_DIR}/qt3danimation_module_wrapper.cpp)
diff --git a/sources/pyside2/PySide2/Qt3DAnimation/typesystem_3danimation.xml b/sources/pyside2/PySide2/Qt3DAnimation/typesystem_3danimation.xml
index 567d7e25a..bba179e00 100644
--- a/sources/pyside2/PySide2/Qt3DAnimation/typesystem_3danimation.xml
+++ b/sources/pyside2/PySide2/Qt3DAnimation/typesystem_3danimation.xml
@@ -53,10 +53,21 @@
<object-type name="QAbstractClipBlendNode"/>
<object-type name="QAdditiveClipBlend"/>
<object-type name="QAnimationAspect"/>
+ <object-type name="QAnimationCallback">
+ <enum-type name="Flag"/>
+ </object-type>
+ <object-type name="QAnimationClip"/>
+ <object-type name="QAnimationClipLoader">
+ <enum-type name="Status"/>
+ </object-type>
<object-type name="QAnimationController"/>
<object-type name="QAnimationGroup"/>
<object-type name="QBlendedClipAnimator"/>
<object-type name="QClipAnimator"/>
+ <object-type name="QClock"/>
+ <object-type name="QKeyFrame">
+ <enum-type name="InterpolationType"/>
+ </object-type>
<object-type name="QKeyframeAnimation">
<enum-type name="RepeatMode"/>
</object-type>
@@ -65,6 +76,7 @@
<enum-type name="Method"/>
</object-type>
<object-type name="QMorphTarget"/>
+ <object-type name="QSkeletonMapping"/>
<object-type name="QVertexBlendAnimation"/>
</namespace-type>
</typesystem>
diff --git a/sources/pyside2/doc/tutorials/expenses/expenses.rst b/sources/pyside2/doc/tutorials/expenses/expenses.rst
new file mode 100644
index 000000000..c34d18669
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/expenses.rst
@@ -0,0 +1,313 @@
+######################
+Expenses Tool Tutorial
+######################
+
+In this tutorial you will learn the following concepts:
+* creating user interfaces programatically,
+* layouts and widgets,
+* overloading Qt classes,
+* connecting signal and slots,
+* interacting with QWidgets,
+* and building your own application.
+
+The requirements:
+ * A simple window for the application
+ (`QMainWindow <https://doc.qt.io/qtforpython/PySide2/QtWidgets/QMainWindow.html>`_).
+ * A table to keep track of the expenses
+ (`QTableWidget <https://doc.qt.io/qtforpython/PySide2/QtWidgets/QTableWidget.html>`_).
+ * Two input fields to add expense information
+ (`QLineEdit <https://doc.qt.io/qtforpython/PySide2/QtWidgets/QLineEdit.html>`_).
+ * Buttons to add information to the table, plot data, clear table, and exit the application
+ (`QPushButton <https://doc.qt.io/qtforpython/PySide2/QtWidgets/QPushButton.html>`_).
+ * A verification step to avoid invalid data entry.
+ * A chart to visualize the expense data
+ (`QChart <https://doc.qt.io/qtforpython/PySide2/QtCharts/QtCharts.QChart.html>`_) that will
+ be embedded in a chart view
+ (`QChartView <https://doc.qt.io/qtforpython/PySide2/QtCharts/QtCharts.QChartView.html>`_).
+
+Empty window
+------------
+
+The base structure for a `QApplication` is located inside the `if __name__ == "__main__":`
+code block.
+
+ .. code::
+
+ if __name__ == "__main__":
+ app = QApplication([])
+ # ...
+ sys.exit(app.exec_())
+
+Now, to start the development, create an empty window called `MainWindow`.
+You could do that by defining a class that inherits from `QMainWindow`.
+
+ .. literalinclude:: steps/01-expenses.py
+ :linenos:
+ :lines: 45-59
+ :emphasize-lines: 45-48
+
+Now that our class is defined, create an instance of it and call `show()`.
+
+ .. literalinclude:: steps/01-expenses.py
+ :linenos:
+ :lines: 45-59
+ :emphasize-lines: 54-56
+
+Menu bar
+--------
+
+Using a `QMainWindow` gives some features for free, among them a *menu bar*. To use it, you need
+to call the method `menuBar()` and populate it inside the `MainWindow` class.
+
+ .. literalinclude:: steps/02-expenses.py
+ :linenos:
+ :lines: 46-58
+ :emphasize-lines: 51
+
+Notice that the code snippet adds a *File* menu with the *Exit* option only.
+
+First signal/slot connection
+----------------------------
+
+The *Exit* option must be connected to a slot that triggers the application to exit. The main
+idea to achieve this, is the following:
+
+ .. code::
+
+ element.signal_name.connect(slot_name)
+
+All the interface's elements could be connected through signals to certain slots,
+in the case of a `QAction`, the signal `triggered` can be used:
+
+ .. code::
+
+ exit_action.triggered.connect(slot_name)
+
+.. note:: Now a *slot* needs to be defined to exit the application, which can be done using
+ `QApplication.quit()`. If we put all these concepts together you will end up with the
+ following code:
+
+ .. literalinclude:: steps/03-expenses.py
+ :linenos:
+ :lines: 56-65
+ :emphasize-lines: 59, 63-65
+
+Notice that the decorator `@Slot()` is required for each slot you declare to properly
+register them. Slots are normal functions, but the main difference is that they
+will be invokable from `Signals` of QObjects when connected.
+
+Empty widget and data
+---------------------
+
+The `QMainWindow` enables us to set a central widget that will be displayed when showing the window
+(`read more <https://doc.qt.io/qt-5/qmainwindow.html#details>`_).
+This central widget could be another class derived from `QWidget`.
+
+Additionally, you will define example data to visualize later.
+
+ .. literalinclude:: steps/04-expenses.py
+ :linenos:
+ :lines: 46-53
+
+With the `Widget` class in place, modify `MainWindow`'s initialization code
+
+ .. literalinclude:: steps/04-expenses.py
+ :linenos:
+ :lines: 80-84
+
+Window layout
+-------------
+
+Now that the main empty window is in place, you need to start adding widgets to achieve the main
+goal of creating an expenses application.
+
+After declaring the example data, you can visualize it on a simple `QTableWidget`. To do so, you
+will add this procedure to the `Widget` constructor.
+
+ .. warning:: Only for the example purpose a QTableWidget will be used,
+ but for more performance-critical applications the combination
+ of a model and a QTableView is encouraged.
+
+ .. literalinclude:: steps/05-expenses.py
+ :linenos:
+ :lines: 48-73
+
+As you can see, the code also includes a `QHBoxLayout` that provides the container to place widgets
+horizontally.
+
+Additionally, the `QTableWidget` allows for customizing it, like adding the labels for the two
+columns that will be used, and to *stretch* the content to use the whole `Widget` space.
+
+The last line of code refers to *filling the table**, and the code to perform that task is
+displayed below.
+
+ .. literalinclude:: steps/05-expenses.py
+ :linenos:
+ :lines: 75-81
+
+Having this process on a separate method is a good practice to leave the constructor more readable,
+and to split the main functions of the class in independent processes.
+
+
+Right side layout
+-----------------
+
+Because the data that is being used is just an example, you are required to include a mechanism to
+input items to the table, and extra buttons to clear the table's content, and also quit the
+application.
+
+To distribute these input lines and buttons, you will use a `QVBoxLayout` that allows you to place
+elements vertically inside a layout.
+
+ .. literalinclude:: steps/06-expenses.py
+ :linenos:
+ :lines: 64-80
+
+Leaving the table on the left side and these newly included widgets to the right side
+will be just a matter to add a layout to our main `QHBoxLayout` as you saw in the previous
+example:
+
+ .. literalinclude:: steps/06-expenses.py
+ :linenos:
+ :lines: 42-47
+
+The next step will be connecting those new buttons to slots.
+
+
+Adding elements
+---------------
+
+Each `QPushButton` have a signal called *clicked*, that is emitted when you click on the button.
+This will be more than enough for this example, but you can see other signals in the `official
+documentation <https://doc.qt.io/qtforpython/PySide2/QtWidgets/QAbstractButton.html#signals>`_.
+
+ .. literalinclude:: steps/07-expenses.py
+ :linenos:
+ :lines: 92-95
+
+As you can see on the previous lines, we are connecting each *clicked* signal to different slots.
+In this example slots are normal class methods in charge of perform a determined task associated
+with our buttons. It is really important to decorate each method declaration with a `@Slot()`, in
+that way PySide2 knows internally how to register them into Qt.
+
+ .. literalinclude:: steps/07-expenses.py
+ :linenos:
+ :lines: 1000-129
+ :emphasize-lines: 101,115,127
+
+Since these slots are methods, we can access the class variables, like our `QTableWidget` to
+interact with it.
+
+The mechanism to add elements into the table is described as the following:
+
+ * get the *description* and *price* from the fields,
+ * insert a new empty row to the table,
+ * set the values for the empty row in each column,
+ * clear the input text fields,
+ * include the global count of table rows.
+
+To exit the application you can use the `quit()` method of the unique `QApplication` instance, and
+to clear the content of the table you can just set the table *row count*, and the internal count to
+zero.
+
+Verification step
+-----------------
+
+Adding information to the table needs to be a critical action that require a verification step
+to avoid adding invalid information, for example, empty information.
+
+You can use a signal from `QLineEdit` called *textChanged[str]* which will be emitted every
+time something inside changes, i.e.: each key stroke.
+Notice that this time, there is a *[str]* section on the signal, this means that the signal
+will also emit the value of the text that was changed, which will be really useful to verify
+the current content of the `QLineEdit`.
+
+You can connect two different object's signal to the same slot, and this will be the case
+for your current application:
+
+ .. literalinclude:: steps/08-expenses.py
+ :linenos:
+ :lines: 99-100
+
+The content of the *check_disable* slot will be really simple:
+
+ .. literalinclude:: steps/08-expenses.py
+ :linenos:
+ :lines: 119-124
+
+You have two options, write a verification based on the current value
+of the string you retrieve, or manually get the whole content of both
+`QLineEdit`. The second is preferred in this case, so you can verify
+if the two inputs are not empty to enable the button *Add*.
+
+.. note:: Qt also provides a special class called
+ `QValidator <https://doc.qt.io/qtforpython/PySide2/QtGui/QValidator.html?highlight=qvalidator>`_
+ that you can use to validate any input.
+
+Empty chart view
+----------------
+
+New items can be added to the table, and the visualization is so far
+OK, but you can accomplish more by representing the data graphically.
+
+First you will include an empty `QChartView` placeholder into the right
+side of your application.
+
+ .. literalinclude:: steps/09-expenses.py
+ :linenos:
+ :lines: 66-68
+
+Additionally the order of how you include widgets to the right
+`QVBoxLayout` will also change.
+
+ .. literalinclude:: steps/09-expenses.py
+ :linenos:
+ :lines: 81-91
+ :emphasize-lines: 89
+
+Notice that before we had a line with `self.right.addStretch()`
+to fill up the vertical space between the *Add* and the *Clear* buttons,
+but now, with the `QChartView` it will not be necessary.
+
+Also, you need include a *Plot* button if you want to do it on-demand.
+
+Full application
+----------------
+
+For the final step, you will need to connect the *Plot* button
+to a slot that creates a chart and includes it into your `QChartView`.
+
+ .. literalinclude:: steps/10-expenses.py
+ :linenos:
+ :lines: 103-109
+ :emphasize-lines: 106
+
+That is nothing new, since you already did it for the other buttons,
+but now take a look at how to create a chart and include it into
+your `QChartView`.
+
+ .. literalinclude:: steps/10-expenses.py
+ :linenos:
+ :lines: 139-151
+
+The following steps show how to fill a `QPieSeries`:
+
+ * create a `QPieSeries`,
+ * iterate over the table row IDs,
+ * get the items at the *i* position,
+ * add those values to the *series*.
+
+Once the series has been populated with our data, you create a new `QChart`,
+add the series on it, and optionally set an alignment for the legend.
+
+The final line `self.chart_view.setChart(chart)` is in charge of bringing
+your newly created chart to the `QChartView`.
+
+The application will look like this:
+
+ .. image:: expenses_tool.png
+
+And now you can see the whole code:
+
+ .. literalinclude:: main.py
+ :linenos:
diff --git a/sources/pyside2/doc/tutorials/expenses/expenses_tool.png b/sources/pyside2/doc/tutorials/expenses/expenses_tool.png
new file mode 100644
index 000000000..7a6f6d1f0
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/expenses_tool.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/expenses/main.py b/sources/pyside2/doc/tutorials/expenses/main.py
new file mode 100644
index 000000000..6cc911671
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/main.py
@@ -0,0 +1,207 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtCore import Qt, Slot
+from PySide2.QtGui import QPainter
+from PySide2.QtWidgets import (QAction, QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+from PySide2.QtCharts import QtCharts
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Chart
+ self.chart_view = QtCharts.QChartView()
+ self.chart_view.setRenderHint(QPainter.Antialiasing)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+ self.plot = QPushButton("Plot")
+
+ # Disabling 'Add' button
+ self.add.setEnabled(False)
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addWidget(self.plot)
+ self.right.addWidget(self.chart_view)
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.plot.clicked.connect(self.plot_data)
+ self.clear.clicked.connect(self.clear_table)
+ self.description.textChanged[str].connect(self.check_disable)
+ self.price.textChanged[str].connect(self.check_disable)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ description_item = QTableWidgetItem(des)
+ price_item = QTableWidgetItem("{:.2f}".format(float(price)))
+ price_item.setTextAlignment(Qt.AlignRight)
+
+ self.table.setItem(self.items, 0, description_item)
+ self.table.setItem(self.items, 1, price_item)
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def check_disable(self, s):
+ if not self.description.text() or not self.price.text():
+ self.add.setEnabled(False)
+ else:
+ self.add.setEnabled(True)
+
+ @Slot()
+ def plot_data(self):
+ # Get table information
+ series = QtCharts.QPieSeries()
+ for i in range(self.table.rowCount()):
+ text = self.table.item(i, 0).text()
+ number = float(self.table.item(i, 1).text())
+ series.append(text, number)
+
+ chart = QtCharts.QChart()
+ chart.addSeries(series)
+ chart.legend().setAlignment(Qt.AlignLeft)
+ self.chart_view.setChart(chart)
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ description_item = QTableWidgetItem(desc)
+ price_item = QTableWidgetItem("{:.2f}".format(price))
+ price_item.setTextAlignment(Qt.AlignRight)
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, description_item)
+ self.table.setItem(self.items, 1, price_item)
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/01-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/01-expenses.py
new file mode 100644
index 000000000..12d5f0b1a
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/01-expenses.py
@@ -0,0 +1,59 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtWidgets import QApplication, QMainWindow
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ window = MainWindow()
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/02-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/02-expenses.py
new file mode 100644
index 000000000..0546bc9f3
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/02-expenses.py
@@ -0,0 +1,70 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtWidgets import QAction, QApplication, QMainWindow
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+
+ self.file_menu.addAction(exit_action)
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ window = MainWindow()
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/03-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/03-expenses.py
new file mode 100644
index 000000000..bc753309f
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/03-expenses.py
@@ -0,0 +1,77 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtCore import Slot
+from PySide2.QtWidgets import QAction, QApplication, QMainWindow
+from PySide2.QtCharts import QtCharts
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ window = MainWindow()
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/04-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/04-expenses.py
new file mode 100644
index 000000000..78e9b1d1d
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/04-expenses.py
@@ -0,0 +1,89 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtCore import Slot
+from PySide2.QtWidgets import QAction, QApplication, QMainWindow, QWidget
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/05-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/05-expenses.py
new file mode 100644
index 000000000..6fd515f40
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/05-expenses.py
@@ -0,0 +1,117 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtCore import Slot
+from PySide2.QtWidgets import (QAction, QApplication, QHeaderView, QHBoxLayout, QMainWindow,
+ QTableWidget, QTableWidgetItem, QWidget)
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Fill example data
+ self.fill_table()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/06-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/06-expenses.py
new file mode 100644
index 000000000..38fa881bf
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/06-expenses.py
@@ -0,0 +1,137 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtCore import Slot
+from PySide2.QtWidgets import (QAction, QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addStretch()
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Fill example data
+ self.fill_table()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/07-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/07-expenses.py
new file mode 100644
index 000000000..6b915806d
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/07-expenses.py
@@ -0,0 +1,164 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtCore import Slot
+from PySide2.QtWidgets import (QAction, QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addStretch()
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.clear.clicked.connect(self.clear_table)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(des))
+ self.table.setItem(self.items, 1, QTableWidgetItem(price))
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/08-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/08-expenses.py
new file mode 100644
index 000000000..2f18dff9f
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/08-expenses.py
@@ -0,0 +1,177 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtCore import Slot
+from PySide2.QtWidgets import (QAction, QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+
+ # Disabling 'Add' button
+ self.add.setEnabled(False)
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addStretch()
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.clear.clicked.connect(self.clear_table)
+ self.description.textChanged[str].connect(self.check_disable)
+ self.price.textChanged[str].connect(self.check_disable)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(des))
+ self.table.setItem(self.items, 1, QTableWidgetItem(price))
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def check_disable(self, s):
+ if not self.description.text() or not self.price.text():
+ self.add.setEnabled(False)
+ else:
+ self.add.setEnabled(True)
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/09-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/09-expenses.py
new file mode 100644
index 000000000..6b5d87e2e
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/09-expenses.py
@@ -0,0 +1,185 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtCore import Slot
+from PySide2.QtGui import QPainter
+from PySide2.QtWidgets import (QAction, QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+from PySide2.QtCharts import QtCharts
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Chart
+ self.chart_view = QtCharts.QChartView()
+ self.chart_view.setRenderHint(QPainter.Antialiasing)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+ self.plot = QPushButton("Plot")
+
+ # Disabling 'Add' button
+ self.add.setEnabled(False)
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addWidget(self.plot)
+ self.right.addWidget(self.chart_view)
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.clear.clicked.connect(self.clear_table)
+ self.description.textChanged[str].connect(self.check_disable)
+ self.price.textChanged[str].connect(self.check_disable)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(des))
+ self.table.setItem(self.items, 1, QTableWidgetItem(price))
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def check_disable(self, s):
+ if not self.description.text() or not self.price.text():
+ self.add.setEnabled(False)
+ else:
+ self.add.setEnabled(True)
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/expenses/steps/10-expenses.py b/sources/pyside2/doc/tutorials/expenses/steps/10-expenses.py
new file mode 100644
index 000000000..6cc911671
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/expenses/steps/10-expenses.py
@@ -0,0 +1,207 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide2.QtCore import Qt, Slot
+from PySide2.QtGui import QPainter
+from PySide2.QtWidgets import (QAction, QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+from PySide2.QtCharts import QtCharts
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Chart
+ self.chart_view = QtCharts.QChartView()
+ self.chart_view.setRenderHint(QPainter.Antialiasing)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+ self.plot = QPushButton("Plot")
+
+ # Disabling 'Add' button
+ self.add.setEnabled(False)
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addWidget(self.plot)
+ self.right.addWidget(self.chart_view)
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.plot.clicked.connect(self.plot_data)
+ self.clear.clicked.connect(self.clear_table)
+ self.description.textChanged[str].connect(self.check_disable)
+ self.price.textChanged[str].connect(self.check_disable)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ description_item = QTableWidgetItem(des)
+ price_item = QTableWidgetItem("{:.2f}".format(float(price)))
+ price_item.setTextAlignment(Qt.AlignRight)
+
+ self.table.setItem(self.items, 0, description_item)
+ self.table.setItem(self.items, 1, price_item)
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def check_disable(self, s):
+ if not self.description.text() or not self.price.text():
+ self.add.setEnabled(False)
+ else:
+ self.add.setEnabled(True)
+
+ @Slot()
+ def plot_data(self):
+ # Get table information
+ series = QtCharts.QPieSeries()
+ for i in range(self.table.rowCount()):
+ text = self.table.item(i, 0).text()
+ number = float(self.table.item(i, 1).text())
+ series.append(text, number)
+
+ chart = QtCharts.QChart()
+ chart.addSeries(series)
+ chart.legend().setAlignment(Qt.AlignLeft)
+ self.chart_view.setChart(chart)
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ description_item = QTableWidgetItem(desc)
+ price_item = QTableWidgetItem("{:.2f}".format(price))
+ price_item.setTextAlignment(Qt.AlignRight)
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, description_item)
+ self.table.setItem(self.items, 1, price_item)
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/index.rst b/sources/pyside2/doc/tutorials/index.rst
index af7e88873..5a97aecb9 100644
--- a/sources/pyside2/doc/tutorials/index.rst
+++ b/sources/pyside2/doc/tutorials/index.rst
@@ -27,5 +27,6 @@ Tutorials
basictutorial/dialog.rst
basictutorial/uifiles.rst
datavisualize/index.rst
+ expenses/expenses.rst
qmlapp/qmlapplication.rst
qmlintegration/qmlintegration.rst
diff --git a/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py b/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py
index b38cff68b..c58aba82e 100644
--- a/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py
+++ b/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py
@@ -40,7 +40,7 @@ class qAppMacroTest(unittest.TestCase):
except ImportError:
QtWidgets = QtGui = QtCore
# qApp is in the builtins
- qApp
+ self.assertEqual(bool(qApp), False)
# and also in certain PySide modules
QtCore.qApp, QtGui.qApp, QtWidgets.qApp
# and they are all the same
diff --git a/sources/pyside2/tests/support/voidptr_test.py b/sources/pyside2/tests/support/voidptr_test.py
index c04022489..f68217244 100644
--- a/sources/pyside2/tests/support/voidptr_test.py
+++ b/sources/pyside2/tests/support/voidptr_test.py
@@ -54,6 +54,11 @@ class PySide2Support(unittest.TestCase):
# Convert original and new to str
self.assertTrue(str(b), str(nba))
+ # Modify nba through a memoryview of vp
+ mv = memoryview(vp)
+ self.assertFalse(mv.readonly)
+ mv[6:11] = b'void*'
+ self.assertEqual(str(ba), str(b"Hello void*"))
+
if __name__ == '__main__':
unittest.main()
-
diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt
index 760cc6985..c55dba973 100644
--- a/sources/shiboken2/ApiExtractor/CMakeLists.txt
+++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt
@@ -63,6 +63,8 @@ if (NOT DISABLE_DOCSTRINGS)
endif()
endif()
+target_compile_definitions(apiextractor PRIVATE CMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}")
+
set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE)
if (BUILD_TESTS)
diff --git a/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp
index d3d5c8da8..3196c824e 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp
+++ b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp
@@ -189,7 +189,7 @@ static LinuxDistribution linuxDistribution()
const QString &productType = QSysInfo::productType();
if (productType == QLatin1String("rhel"))
return LinuxDistribution::RedHat;
- if (productType == QLatin1String("centos"))
+ if (productType.compare(QLatin1String("centos"), Qt::CaseInsensitive) == 0)
return LinuxDistribution::CentOs;
return LinuxDistribution::Other;
}
@@ -207,7 +207,7 @@ static inline bool needsGppInternalHeaders()
switch (distro) {
case LinuxDistribution::RedHat:
case LinuxDistribution::CentOs:
- return checkProductVersion(QVersionNumber(7), QVersionNumber(8));
+ return checkProductVersion(QVersionNumber(6, 10), QVersionNumber(8));
case LinuxDistribution::Other:
break;
}
@@ -288,6 +288,18 @@ static QString findClangBuiltInIncludesDir()
}
#endif // NEED_CLANG_BUILTIN_INCLUDES
+#if defined(Q_CC_CLANG) || defined(Q_CC_GNU)
+static QString compilerFromCMake(const QString &defaultCompiler)
+{
+# ifdef CMAKE_CXX_COMPILER
+ Q_UNUSED(defaultCompiler)
+ return QString::fromLocal8Bit(CMAKE_CXX_COMPILER);
+#else
+ return defaultCompiler;
+# endif
+}
+#endif // Q_CC_CLANG, Q_CC_GNU
+
// Returns clang options needed for emulating the host compiler
QByteArrayList emulatedCompilerOptions()
{
@@ -297,7 +309,7 @@ QByteArrayList emulatedCompilerOptions()
result.append(QByteArrayLiteral("-fms-compatibility-version=19"));
result.append(QByteArrayLiteral("-Wno-microsoft-enum-value"));
#elif defined(Q_CC_CLANG)
- HeaderPaths headerPaths = gppInternalIncludePaths(QStringLiteral("clang++"));
+ HeaderPaths headerPaths = gppInternalIncludePaths(compilerFromCMake(QStringLiteral("clang++")));
result.append(noStandardIncludeOption());
#elif defined(Q_CC_GNU)
HeaderPaths headerPaths;
@@ -322,10 +334,12 @@ QByteArrayList emulatedCompilerOptions()
// A fix for this has been added to Clang 5.0, so, the code can be removed
// once Clang 5.0 is the minimum version.
if (needsGppInternalHeaders()) {
- const HeaderPaths gppPaths = gppInternalIncludePaths(QStringLiteral("g++"));
+ const HeaderPaths gppPaths = gppInternalIncludePaths(compilerFromCMake(QStringLiteral("g++")));
for (const HeaderPath &h : gppPaths) {
- if (h.path.contains("c++"))
+ if (h.path.contains("c++")
+ || h.path.contains("sysroot")) { // centOS
headerPaths.append(h);
+ }
}
}
#else
diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp
index eb0f44689..7bb7e0a83 100644
--- a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp
+++ b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp
@@ -353,6 +353,17 @@ bool TypeInfo::stripLeadingQualifier(const QString &qualifier, QString *s)
return true;
}
+// Strip all const/volatile/*/&
+void TypeInfo::stripQualifiers(QString *s)
+{
+ stripLeadingConst(s);
+ stripLeadingVolatile(s);
+ while (s->endsWith(QLatin1Char('&')) || s->endsWith(QLatin1Char('*'))
+ || s->endsWith(QLatin1Char(' '))) {
+ s->chop(1);
+ }
+}
+
// Helper functionality to simplify a raw standard type as returned by
// clang_getCanonicalType() for g++ standard containers from
// "std::__cxx11::list<int, std::allocator<int> >" or
diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.h b/sources/shiboken2/ApiExtractor/parser/codemodel.h
index 3bce5e216..6f3c17613 100644
--- a/sources/shiboken2/ApiExtractor/parser/codemodel.h
+++ b/sources/shiboken2/ApiExtractor/parser/codemodel.h
@@ -213,6 +213,7 @@ public:
static bool stripLeadingConst(QString *s);
static bool stripLeadingVolatile(QString *s);
static bool stripLeadingQualifier(const QString &qualifier, QString *s);
+ static void stripQualifiers(QString *s);
void simplifyStdType();
diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt
index 950aa6215..be1b4cd7f 100644
--- a/sources/shiboken2/CMakeLists.txt
+++ b/sources/shiboken2/CMakeLists.txt
@@ -1,10 +1,13 @@
Include(icecc.cmake)
-project(shiboken2)
-include(CheckIncludeFileCXX)
cmake_minimum_required(VERSION 3.1)
cmake_policy(VERSION 3.1)
+set(CMAKE_BUILD_TYPE Release CACHE STRING "Build Type")
+
+project(shiboken2)
+include(CheckIncludeFileCXX)
+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake_helpers/")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/data/")
@@ -404,11 +407,7 @@ execute_process(
OUTPUT_VARIABLE PYTHON_WITH_COUNT_ALLOCS
OUTPUT_STRIP_TRAILING_WHITESPACE)
-if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE Release)
-endif()
-
-set(SHIBOKEN_BUILD_TYPE "Release")
+set(SHIBOKEN_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(SHIBOKEN_BUILD_TYPE "Debug")
diff --git a/sources/shiboken2/data/shiboken_helpers.cmake b/sources/shiboken2/data/shiboken_helpers.cmake
index c55ee89fe..f4dd4d5dc 100644
--- a/sources/shiboken2/data/shiboken_helpers.cmake
+++ b/sources/shiboken2/data/shiboken_helpers.cmake
@@ -157,7 +157,7 @@ macro(shiboken_compute_python_libraries)
# If the resulting variable
# contains a "debug;X;optimized;Y" list like described in shiboken_check_if_limited_api,
# make sure to pick just one, so that the final generator expressions are valid.
- shiboken_get_library_for_current_config("${SHIBOKEN_PYTHON_LIBRARIES}" ${CMAKE_BUILD_TYPE} "SHIBOKEN_PYTHON_LIBRARIES")
+ shiboken_get_library_for_current_config("${SHIBOKEN_PYTHON_LIBRARIES}" "${CMAKE_BUILD_TYPE}" "SHIBOKEN_PYTHON_LIBRARIES")
if(APPLE)
set(SHIBOKEN_PYTHON_LIBRARIES "-undefined dynamic_lookup")
diff --git a/sources/shiboken2/generator/generator.cpp b/sources/shiboken2/generator/generator.cpp
index 87758e533..fe03d3489 100644
--- a/sources/shiboken2/generator/generator.cpp
+++ b/sources/shiboken2/generator/generator.cpp
@@ -28,6 +28,7 @@
#include "generator.h"
#include "abstractmetalang.h"
+#include "parser/codemodel.h"
#include "messages.h"
#include "reporthandler.h"
#include "fileout.h"
@@ -911,6 +912,7 @@ QString getClassTargetFullName(const AbstractMetaType *metaType, bool includePac
QString getFilteredCppSignatureString(QString signature)
{
+ TypeInfo::stripQualifiers(&signature); // for const refs to smart pointers
signature.replace(QLatin1String("::"), QLatin1String("_"));
signature.replace(QLatin1Char('<'), QLatin1Char('_'));
signature.replace(QLatin1Char('>'), QLatin1Char('_'));
diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.cpp b/sources/shiboken2/generator/shiboken2/headergenerator.cpp
index 8881d71f4..17ebbcde9 100644
--- a/sources/shiboken2/generator/shiboken2/headergenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/headergenerator.cpp
@@ -31,6 +31,7 @@
#include <typedatabase.h>
#include <reporthandler.h>
#include <fileout.h>
+#include "parser/codemodel.h"
#include <algorithm>
@@ -592,8 +593,10 @@ void HeaderGenerator::writeSbkTypeFunction(QTextStream& s, const AbstractMetaCla
void HeaderGenerator::writeSbkTypeFunction(QTextStream &s, const AbstractMetaType *metaType)
{
- s << "template<> inline PyTypeObject* SbkType< ::" << metaType->cppSignature() << " >() "
- << "{ return reinterpret_cast<PyTypeObject*>(" << cpythonTypeNameExt(metaType) << "); }\n";
+ QString signature = metaType->cppSignature();
+ TypeInfo::stripQualifiers(&signature); // for const refs to smart pointers
+ s << "template<> inline PyTypeObject *SbkType< ::" << signature << " >() "
+ << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(metaType) << "); }\n";
}
void HeaderGenerator::writeInheritedOverloads(QTextStream& s)
diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
index 2b3b20c75..5d599fe95 100644
--- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
@@ -2681,8 +2681,11 @@ QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry* type)
QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType* type)
{
QString result = QLatin1String("SBK");
- if (type->typeEntry()->isContainer())
+ const auto *typeEntry = type->typeEntry();
+ if (typeEntry->isContainer()
+ || typeEntry->isSmartPointer()) { // PYSIDE-1024
result += QLatin1Char('_') + moduleName().toUpper();
+ }
result += processInstantiationsVariableName(type);
appendIndexSuffix(&result);
return result;
diff --git a/sources/shiboken2/libshiboken/qapp_macro.cpp b/sources/shiboken2/libshiboken/qapp_macro.cpp
index f69d0f937..19e985b20 100644
--- a/sources/shiboken2/libshiboken/qapp_macro.cpp
+++ b/sources/shiboken2/libshiboken/qapp_macro.cpp
@@ -67,7 +67,9 @@ qApp_module_index(PyObject *module)
return ret;
}
-#define Py_NONE_TYPE Py_TYPE(Py_None)
+#define PYTHON_IS_PYTHON3 (PY_VERSION_HEX >= 0x03000000)
+#define PYTHON_IS_PYTHON2 (!PYTHON_IS_PYTHON3)
+#define Py_NONE_TYPE Py_TYPE(Py_None)
#if PYTHON_IS_PYTHON3
# define BRACE_OPEN {
@@ -156,6 +158,31 @@ MakeSingletonQAppWrapper(PyTypeObject *type)
return qApp_content;
}
+#if PYTHON_IS_PYTHON2
+
+// Install support in Py_NONE_TYPE for Python 2: 'bool(qApp) == False'.
+static int
+none_bool(PyObject *v)
+{
+ return 0;
+}
+
+static PyNumberMethods none_as_number = {
+ nullptr, /* nb_add */
+ nullptr, /* nb_subtract */
+ nullptr, /* nb_multiply */
+ nullptr, /* nb_divide */
+ nullptr, /* nb_remainder */
+ nullptr, /* nb_divmod */
+ nullptr, /* nb_power */
+ nullptr, /* nb_negative */
+ nullptr, /* nb_positive */
+ nullptr, /* nb_absolute */
+ reinterpret_cast<inquiry>(none_bool), /* nb_nonzero */
+};
+
+#endif
+
static int
setup_qApp_var(PyObject *module)
{
@@ -163,6 +190,9 @@ setup_qApp_var(PyObject *module)
static int init_done = 0;
if (!init_done) {
+#if PYTHON_IS_PYTHON2
+ Py_NONE_TYPE->tp_as_number = &none_as_number;
+#endif
qApp_var = Py_BuildValue("s", "qApp");
if (qApp_var == NULL)
return -1;
diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp
index 3defca7d2..c83f90d64 100644
--- a/sources/shiboken2/libshiboken/signature.cpp
+++ b/sources/shiboken2/libshiboken/signature.cpp
@@ -71,6 +71,7 @@ typedef struct safe_globals_struc {
PyObject *helper_module;
PyObject *arg_dict;
PyObject *map_dict;
+ PyObject *value_dict; // for writing signatures
// init part 2: run module
PyObject *pyside_type_init_func;
PyObject *create_signature_func;
@@ -86,6 +87,7 @@ static PyObject *GetSignature_Function(PyObject *, const char *);
static PyObject *GetSignature_TypeMod(PyObject *, const char *);
static PyObject *GetSignature_Wrapper(PyObject *, const char *);
static PyObject *get_signature(PyObject *self, PyObject *args);
+static PyObject *get_signature_intern(PyObject *ob, const char *modifier);
static PyObject *PySide_BuildSignatureProps(PyObject *class_mod);
@@ -105,11 +107,36 @@ CreateSignature(PyObject *props, PyObject *key)
const_cast<char *>("(OO)"), props, key);
}
+typedef PyObject *(*signaturefunc)(PyObject *, const char *);
+
+static PyObject *
+_get_written_signature(signaturefunc sf, PyObject *ob, const char *modifier)
+{
+ /*
+ * Be a writable Attribute, but have a computed value.
+ *
+ * If a signature has not been written, call the signature function.
+ * If it has been written, return the written value.
+ * After __del__ was called, the function value re-appears.
+ *
+ * Note: This serves also for the new version that does not allow any
+ * assignment if we have a computed value. We only need to check if
+ * a computed value exists and then forbid writing.
+ * See pyside_set___signature
+ */
+ PyObject *ret = PyDict_GetItem(pyside_globals->value_dict, ob);
+ if (ret == nullptr) {
+ return ob == nullptr ? nullptr : sf(ob, modifier);
+ }
+ Py_INCREF(ret);
+ return ret;
+}
+
static PyObject *
pyside_cf_get___signature__(PyObject *func, const char *modifier)
{
init_module_2();
- return GetSignature_Function(func, modifier);
+ return _get_written_signature(GetSignature_Function, func, modifier);
}
static PyObject *
@@ -118,8 +145,8 @@ pyside_sm_get___signature__(PyObject *sm, const char *modifier)
init_module_2();
Shiboken::AutoDecRef func(PyObject_GetAttrString(sm, "__func__"));
if (Py_TYPE(func) == PepFunction_TypePtr)
- Py_RETURN_NONE;
- return GetSignature_Function(func, modifier);
+ return PyObject_GetAttrString(func, "__signature__");
+ return _get_written_signature(GetSignature_Function, func, modifier);
}
static PyObject *
@@ -270,14 +297,14 @@ static PyObject *
pyside_wd_get___signature__(PyObject *ob, const char *modifier)
{
init_module_2();
- return GetSignature_Wrapper(ob, modifier);
+ return _get_written_signature(GetSignature_Wrapper, ob, modifier);
}
static PyObject *
pyside_tp_get___signature__(PyObject *obtype_mod, const char *modifier)
{
init_module_2();
- return GetSignature_TypeMod(obtype_mod, modifier);
+ return _get_written_signature(GetSignature_TypeMod, obtype_mod, modifier);
}
// forward
@@ -510,6 +537,12 @@ init_phase_1(void)
if (p->arg_dict == nullptr
|| PyObject_SetAttrString(p->helper_module, "pyside_arg_dict", p->arg_dict) < 0)
goto error;
+
+ // build a dict for assigned signature values
+ p->value_dict = PyDict_New();
+ if (p->value_dict == nullptr)
+ goto error;
+
return p;
}
error:
@@ -682,33 +715,56 @@ pyside_wd_get___doc__(PyObject *wd) {
return handle_doc(wd, old_wd_doc_descr);
}
+// the default setter for all objects
+static int
+pyside_set___signature__(PyObject *op, PyObject *value)
+{
+ // By this additional check, this function refuses write access.
+ if (get_signature_intern(op, nullptr)) {
+ PyErr_Format(PyExc_AttributeError,
+ "Attribute '__signature__' of '%.50s' object is not writable",
+ Py_TYPE(op)->tp_name);
+ return -1;
+ }
+ int ret = value == nullptr
+ ? PyDict_DelItem(pyside_globals->value_dict, op)
+ : PyDict_SetItem(pyside_globals->value_dict, op, value);
+ Py_XINCREF(value);
+ return ret;
+}
+
static PyGetSetDef new_PyCFunction_getsets[] = {
- {const_cast<char *>("__signature__"), (getter)pyside_cf_get___signature__},
{const_cast<char *>("__doc__"), (getter)pyside_cf_get___doc__},
+ {const_cast<char *>("__signature__"), (getter)pyside_cf_get___signature__,
+ (setter)pyside_set___signature__},
{0}
};
static PyGetSetDef new_PyStaticMethod_getsets[] = {
- {const_cast<char *>("__signature__"), (getter)pyside_sm_get___signature__},
{const_cast<char *>("__doc__"), (getter)pyside_sm_get___doc__},
+ {const_cast<char *>("__signature__"), (getter)pyside_sm_get___signature__,
+ (setter)pyside_set___signature__},
{0}
};
static PyGetSetDef new_PyMethodDescr_getsets[] = {
- {const_cast<char *>("__signature__"), (getter)pyside_md_get___signature__},
{const_cast<char *>("__doc__"), (getter)pyside_md_get___doc__},
+ {const_cast<char *>("__signature__"), (getter)pyside_md_get___signature__,
+ (setter)pyside_set___signature__},
{0}
};
static PyGetSetDef new_PyType_getsets[] = {
- {const_cast<char *>("__signature__"), (getter)pyside_tp_get___signature__},
{const_cast<char *>("__doc__"), (getter)pyside_tp_get___doc__},
+ {const_cast<char *>("__signature__"), (getter)pyside_tp_get___signature__,
+ (setter)pyside_set___signature__},
{0}
};
static PyGetSetDef new_PyWrapperDescr_getsets[] = {
- {const_cast<char *>("__signature__"), (getter)pyside_wd_get___signature__},
{const_cast<char *>("__doc__"), (getter)pyside_wd_get___doc__},
+ {const_cast<char *>("__signature__"), (getter)pyside_wd_get___signature__,
+ (setter)pyside_set___signature__},
{0}
};
@@ -723,18 +779,8 @@ static PyGetSetDef new_PyWrapperDescr_getsets[] = {
//
static PyObject *
-get_signature(PyObject *self, PyObject *args)
+get_signature_intern(PyObject *ob, const char *modifier)
{
- PyObject *ob;
- const char *modifier = nullptr;
-
- init_module_1();
-
- if (!PyArg_ParseTuple(args, "O|s", &ob, &modifier))
- return nullptr;
- if (Py_TYPE(ob) == PepFunction_TypePtr)
- Py_RETURN_NONE;
-
if (PyType_IsSubtype(Py_TYPE(ob), &PyCFunction_Type))
return pyside_cf_get___signature__(ob, modifier);
if (Py_TYPE(ob) == PepStaticMethod_TypePtr)
@@ -745,6 +791,24 @@ get_signature(PyObject *self, PyObject *args)
return pyside_tp_get___signature__(ob, modifier);
if (Py_TYPE(ob) == &PyWrapperDescr_Type)
return pyside_wd_get___signature__(ob, modifier);
+ return nullptr;
+}
+
+static PyObject *
+get_signature(PyObject *self, PyObject *args)
+{
+ PyObject *ob;
+ const char *modifier = nullptr;
+
+ init_module_1();
+
+ if (!PyArg_ParseTuple(args, "O|s", &ob, &modifier))
+ return nullptr;
+ if (Py_TYPE(ob) == PepFunction_TypePtr)
+ Py_RETURN_NONE;
+ PyObject *ret = get_signature_intern(ob, modifier);
+ if (ret != nullptr)
+ return ret;
Py_RETURN_NONE;
}
diff --git a/sources/shiboken2/libshiboken/voidptr.cpp b/sources/shiboken2/libshiboken/voidptr.cpp
index e55ccfab5..5b0cb84ea 100644
--- a/sources/shiboken2/libshiboken/voidptr.cpp
+++ b/sources/shiboken2/libshiboken/voidptr.cpp
@@ -249,6 +249,83 @@ PyObject *SbkVoidPtrObject_str(PyObject *v)
}
+static int SbkVoidPtrObject_getbuffer(PyObject *obj, Py_buffer *view, int flags)
+{
+ if (view == NULL)
+ return -1;
+
+ SbkVoidPtrObject *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(obj);
+ if (sbkObject->size < 0)
+ return -1;
+
+ int readonly = sbkObject->isWritable ? 0 : 1;
+ if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) &&
+ (readonly == 1)) {
+ PyErr_SetString(PyExc_BufferError,
+ "Object is not writable.");
+ return -1;
+ }
+
+ view->obj = obj;
+ if (obj)
+ Py_XINCREF(obj);
+ view->buf = sbkObject->cptr;
+ view->len = sbkObject->size;
+ view->readonly = readonly;
+ view->itemsize = 1;
+ view->format = NULL;
+ if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
+ view->format = "B";
+ view->ndim = 1;
+ view->shape = NULL;
+ if ((flags & PyBUF_ND) == PyBUF_ND)
+ view->shape = &(view->len);
+ view->strides = NULL;
+ if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES)
+ view->strides = &(view->itemsize);
+ view->suboffsets = NULL;
+ view->internal = NULL;
+ return 0;
+}
+
+#if PY_VERSION_HEX < 0x03000000
+
+static Py_ssize_t SbkVoidPtrObject_readbufferproc(PyObject* self, Py_ssize_t segment, void** ptrptr)
+{
+ if (segment || !Shiboken::Object::isValid(self))
+ return -1;
+
+ SbkVoidPtrObject *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(self);
+ *ptrptr = reinterpret_cast<void*>(sbkObject->cptr);
+ return sbkObject->size;
+}
+
+static Py_ssize_t SbkVoidPtrObject_segcountproc(PyObject* self, Py_ssize_t* lenp)
+{
+ if (lenp) {
+ SbkVoidPtrObject *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(self);
+ *lenp = sbkObject->size;
+ }
+ return 1;
+}
+
+PyBufferProcs SbkVoidPtrObjectBufferProc = {
+ &SbkVoidPtrObject_readbufferproc, // bf_getreadbuffer
+ (writebufferproc)&SbkVoidPtrObject_readbufferproc, // bf_getwritebuffer
+ &SbkVoidPtrObject_segcountproc, // bf_getsegcount
+ (charbufferproc)&SbkVoidPtrObject_readbufferproc, // bf_getcharbuffer
+ (getbufferproc)SbkVoidPtrObject_getbuffer, // bf_getbuffer
+};
+
+#else
+
+static PyBufferProcs SbkVoidPtrObjectBufferProc = {
+ (getbufferproc)SbkVoidPtrObject_getbuffer, // bf_getbuffer
+ (releasebufferproc)0 // bf_releasebuffer
+};
+
+#endif
+
// Void pointer type definition.
static PyType_Slot SbkVoidPtrType_slots[] = {
{Py_tp_repr, (void *)SbkVoidPtrObject_repr},
@@ -278,6 +355,14 @@ PyTypeObject *SbkVoidPtrTypeF(void)
static PyTypeObject *type = nullptr;
if (!type)
type = (PyTypeObject *)PyType_FromSpec(&SbkVoidPtrType_spec);
+
+#if PY_VERSION_HEX < 0x03000000
+ type->tp_as_buffer = &SbkVoidPtrObjectBufferProc;
+ type->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
+#else
+ PepType_AS_BUFFER(type) = &SbkVoidPtrObjectBufferProc;
+#endif
+
return type;
}
@@ -394,5 +479,3 @@ SbkConverter *createConverter()
}
} // namespace VoidPtr
-
-