diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-06-07 23:20:59 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-06-07 23:20:59 +0200 |
commit | b9a857b0fdcad47db958df635a5af934a8e90d62 (patch) | |
tree | a82bee72783db8f9063d857205972e71b1df3994 /sources | |
parent | 768541fb2e81a426daeffc2f81dcca42ecc43919 (diff) | |
parent | d9f4a921ca88bf0bd340f42a874cf7c22b6b1a36 (diff) |
Merge remote-tracking branch 'origin/5.13' into dev
Change-Id: I920a77a3a8c79bba0fd7e5e4f990facf4081ca08
Diffstat (limited to 'sources')
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 Binary files differnew file mode 100644 index 000000000..7a6f6d1f0 --- /dev/null +++ b/sources/pyside2/doc/tutorials/expenses/expenses_tool.png 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 - - |