aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside2/doc/tutorials
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside2/doc/tutorials')
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/qml.rst8
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/uifiles.rst31
-rw-r--r--sources/pyside2/doc/tutorials/basictutorial/widgets.rst12
-rw-r--r--sources/pyside2/doc/tutorials/datavisualize/add_chart.rst2
-rw-r--r--sources/pyside2/doc/tutorials/datavisualize/add_tableview.rst2
-rw-r--r--sources/pyside2/doc/tutorials/datavisualize/filter_data.rst1
-rw-r--r--sources/pyside2/doc/tutorials/examples/images/tabbedbrowser.pngbin37147 -> 0 bytes
-rw-r--r--sources/pyside2/doc/tutorials/examples/tabbedbrowser.rst58
-rw-r--r--sources/pyside2/doc/tutorials/expenses/expenses.rst141
-rw-r--r--sources/pyside2/doc/tutorials/index.rst17
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter1/chapter1.rst89
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter1/createdb.py131
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter1/images/chapter1_books.pngbin0 -> 25391 bytes
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter1/initdb.h160
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter1/main.py59
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.cpp143
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.h83
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.py134
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter2/chapter2.rst93
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter2/createdb.py131
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books.pngbin0 -> 34658 bytes
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.pngbin0 -> 44122 bytes
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter2/images/star.pngbin0 -> 782 bytes
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter2/main.py63
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate-old.py134
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate.py133
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/books.qrc5
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.cpp171
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.py137
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.ui149
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/chapter3.rst121
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/createdb.py131
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/images/chapter3-books.pngbin0 -> 34624 bytes
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/images/star.pngbin0 -> 782 bytes
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/main-old.py52
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/chapter3/main.py53
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/hello_world_ex.py76
-rw-r--r--sources/pyside2/doc/tutorials/portingguide/index.rst196
-rw-r--r--sources/pyside2/doc/tutorials/qmlintegration/qmlintegration.rst16
39 files changed, 2564 insertions, 168 deletions
diff --git a/sources/pyside2/doc/tutorials/basictutorial/qml.rst b/sources/pyside2/doc/tutorials/basictutorial/qml.rst
index 81583096b..fb168410a 100644
--- a/sources/pyside2/doc/tutorials/basictutorial/qml.rst
+++ b/sources/pyside2/doc/tutorials/basictutorial/qml.rst
@@ -14,7 +14,9 @@ that loads the QML file. To make things easier, let's save both files in
the same directory.
Here is a simple QML file called `view.qml`:
-::
+
+.. code-block:: javascript
+
import QtQuick 2.0
Rectangle {
@@ -39,7 +41,9 @@ is the Rectangle in this case.
Now, let's see how the code looks on the PySide2.
Let's call it `main.py`:
-::
+
+.. code-block:: python
+
from PySide2.QtWidgets import QApplication
from PySide2.QtQuick import QQuickView
from PySide2.QtCore import QUrl
diff --git a/sources/pyside2/doc/tutorials/basictutorial/uifiles.rst b/sources/pyside2/doc/tutorials/basictutorial/uifiles.rst
index b00437bcb..a45bfc18c 100644
--- a/sources/pyside2/doc/tutorials/basictutorial/uifiles.rst
+++ b/sources/pyside2/doc/tutorials/basictutorial/uifiles.rst
@@ -78,14 +78,12 @@ Generating a Python class
Another option to interact with a **UI file** is to generate a Python
class from it. This is possible thanks to the `pyside2-uic` tool.
-To use this tool, you need to run the following command on a console:
-::
+To use this tool, you need to run the following command on a console::
pyside2-uic mainwindow.ui > ui_mainwindow.py
We redirect all the output of the command to a file called `ui_mainwindow.py`,
-which will be imported directly:
-::
+which will be imported directly::
from ui_mainwindow import Ui_MainWindow
@@ -93,7 +91,8 @@ Now to use it, we should create a personalized class for our widget
to **setup** this generated design.
To understand the idea, let's take a look at the whole code:
-::
+
+.. code-block:: python
import sys
from PySide2.QtWidgets import QApplication, QMainWindow
@@ -118,26 +117,32 @@ What is inside the *if* statement is already known from the previous
examples, and our new basic class contains only two new lines
that are in charge of loading the generated python class from the UI
file:
-::
+
+.. code-block:: python
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
-.. note:: You must run `pyside2-uic` again every time you make changes
-to the **UI file**.
+.. note::
+
+ You must run `pyside2-uic` again every time you make changes
+ to the **UI file**.
Loading it directly
====================
To load the UI file directly, we will need a class from the **QtUiTools**
module:
-::
+
+.. code-block:: python
from PySide2.QtUiTools import QUiLoader
The `QUiLoader` lets us load the **ui file** dynamically
and use it right away:
-::
+
+.. code-block:: python
+
ui_file = QFile("mainwindow.ui")
ui_file.open(QFile.ReadOnly)
@@ -146,7 +151,8 @@ and use it right away:
window.show()
The complete code of this example looks like this:
-::
+
+.. code-block:: python
# File: main.py
import sys
@@ -169,6 +175,7 @@ The complete code of this example looks like this:
Then to execute it we just need to run the following on a
command prompt:
-::
+
+.. code-block:: python
python main.py
diff --git a/sources/pyside2/doc/tutorials/basictutorial/widgets.rst b/sources/pyside2/doc/tutorials/basictutorial/widgets.rst
index c864e3d47..41e474227 100644
--- a/sources/pyside2/doc/tutorials/basictutorial/widgets.rst
+++ b/sources/pyside2/doc/tutorials/basictutorial/widgets.rst
@@ -5,7 +5,9 @@ As with any other programming framework,
you start with the traditional "Hello World" program.
Here is a simple example of a Hello World application in PySide2:
-::
+
+.. code-block:: python
+
import sys
from PySide2.QtWidgets import QApplication, QLabel
@@ -22,13 +24,17 @@ After the imports, you create a `QApplication` instance. As Qt can
receive arguments from command line, you may pass any argument to
the QApplication object. Usually, you don't need to pass any
arguments so you can leave it as is, or use the following approach:
-::
+
+.. code-block:: python
+
app = QApplication([])
After the creation of the application object, we have created a
`QLabel` object. A `QLabel` is a widget that can present text
(simple or rich, like html), and images:
-::
+
+.. code-block:: python
+
# This HTML approach will be valid too!
label = QLabel("<font color=red size=40>Hello World!</font>")
diff --git a/sources/pyside2/doc/tutorials/datavisualize/add_chart.rst b/sources/pyside2/doc/tutorials/datavisualize/add_chart.rst
index 0c9803269..95b2092b3 100644
--- a/sources/pyside2/doc/tutorials/datavisualize/add_chart.rst
+++ b/sources/pyside2/doc/tutorials/datavisualize/add_chart.rst
@@ -15,6 +15,6 @@ previous chapter to add a QChartView:
.. literalinclude:: datavisualize5/main_widget.py
:linenos:
:lines: 40-
- :emphasize-lines: 2-3,6,22-37,48-51
+ :emphasize-lines: 2-3,6,22-36,48-50
diff --git a/sources/pyside2/doc/tutorials/datavisualize/add_tableview.rst b/sources/pyside2/doc/tutorials/datavisualize/add_tableview.rst
index bbf27f2da..720918008 100644
--- a/sources/pyside2/doc/tutorials/datavisualize/add_tableview.rst
+++ b/sources/pyside2/doc/tutorials/datavisualize/add_tableview.rst
@@ -59,10 +59,12 @@ In the following snippets you'll see those changes highlighted:
.. literalinclude:: datavisualize4/main_window.py
:language: python
:linenos:
+ :lines: 40-
:emphasize-lines: 8,11
.. literalinclude:: datavisualize4/main.py
:language: python
:linenos:
+ :lines: 40-
:emphasize-lines: 46-47
diff --git a/sources/pyside2/doc/tutorials/datavisualize/filter_data.rst b/sources/pyside2/doc/tutorials/datavisualize/filter_data.rst
index f54ba217c..b06b2fa15 100644
--- a/sources/pyside2/doc/tutorials/datavisualize/filter_data.rst
+++ b/sources/pyside2/doc/tutorials/datavisualize/filter_data.rst
@@ -23,7 +23,6 @@ The following script filters and formats the CSV data as described earlier:
.. literalinclude:: datavisualize2/main.py
:language: python
:linenos:
- :emphasize-lines: 44,47-69
:lines: 40-
Now that you have a tuple of QDateTime and float data, try improving the
diff --git a/sources/pyside2/doc/tutorials/examples/images/tabbedbrowser.png b/sources/pyside2/doc/tutorials/examples/images/tabbedbrowser.png
deleted file mode 100644
index 27c3daa09..000000000
--- a/sources/pyside2/doc/tutorials/examples/images/tabbedbrowser.png
+++ /dev/null
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/examples/tabbedbrowser.rst b/sources/pyside2/doc/tutorials/examples/tabbedbrowser.rst
deleted file mode 100644
index c34c50647..000000000
--- a/sources/pyside2/doc/tutorials/examples/tabbedbrowser.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-**********************
-Web Browser Example
-**********************
-
-The example demonstrates the power and simplicity offered by |project| to developers.
-It uses several |pymodname| submodules to offer a fluid and modern-looking UI that
-is apt for a web browser. The application offers the following features:
-
- * Tab-based browsing experience using QTabWidget.
- * Download manager using a QProgressBar and QWebEngineDownloadItem.
- * Bookmark manager using QTreeView.
-
-.. image:: images/tabbedbrowser.png
-
-The application's code is organized in several parts for ease of maintenance. For example,
-:code:`DownloadWidget` provides a widget to track progress of a download item. In the following
-sections, these different parts are discussed briefly to help you understand the Python code behind
-them a little better.
-
-BookmarkWidget or :code:`bookmarkwidget.py`
-===========================================
-
-This widget docks to the left of the main window by default. It inherits QTreeView and
-loads a default set of bookmarks using a QStandardItemModel. The model is populated at startup
-from a JSON file, which is updated when you add or remove bookmarks from the tree view.
-
-.. automodule:: bookmarkwidget
- :members:
-
-DownloadWidget or :code:`downloadwidget.py`
-=============================================
-
-The widget tracks progress of the download item. It inherits QProgressBar to display
-progress of the QWebEngineDownloadItem instance, and offers a context-menu with actions such as Launch,
-Show in folder, Cancel, and Remove.
-
-.. automodule:: downloadwidget
- :members:
-
-BrowserTabWidget or :code:`browsertabwidget.py`
-===============================================
-
-The widget includes a QWebEngineView to enable viewing web content. It docks to the right
-of BookmarkWidget in the main window.
-
-.. automodule:: browsertabwidget
- :members:
-
-MainWindow or :code:`main.py`
-=============================
-
-This is the parent window that collates all the other widgets together to offer the complete package.
-
-.. automodule:: main
- :members:
-
-
-Try running the example to explore it further.
diff --git a/sources/pyside2/doc/tutorials/expenses/expenses.rst b/sources/pyside2/doc/tutorials/expenses/expenses.rst
index c34d18669..a19cec5c3 100644
--- a/sources/pyside2/doc/tutorials/expenses/expenses.rst
+++ b/sources/pyside2/doc/tutorials/expenses/expenses.rst
@@ -31,7 +31,8 @@ Empty window
The base structure for a `QApplication` is located inside the `if __name__ == "__main__":`
code block.
- .. code::
+.. code-block:: python
+ :dedent: 4
if __name__ == "__main__":
app = QApplication([])
@@ -41,17 +42,17 @@ code block.
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
+.. literalinclude:: steps/01-expenses.py
+ :linenos:
+ :lines: 45-59
+ :emphasize-lines: 1-4
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
+.. literalinclude:: steps/01-expenses.py
+ :linenos:
+ :lines: 45-59
+ :emphasize-lines: 10-12
Menu bar
--------
@@ -59,10 +60,10 @@ 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
+.. literalinclude:: steps/02-expenses.py
+ :linenos:
+ :lines: 46-58
+ :emphasize-lines: 6
Notice that the code snippet adds a *File* menu with the *Exit* option only.
@@ -72,14 +73,14 @@ 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::
+.. code-block:: python
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::
+.. code-block:: python
exit_action.triggered.connect(slot_name)
@@ -87,10 +88,10 @@ in the case of a `QAction`, the signal `triggered` can be used:
`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
+.. literalinclude:: steps/03-expenses.py
+ :linenos:
+ :lines: 56-65
+ :emphasize-lines: 4, 8-10
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
@@ -105,15 +106,15 @@ 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
+.. 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
+.. literalinclude:: steps/04-expenses.py
+ :linenos:
+ :lines: 80-84
Window layout
-------------
@@ -124,13 +125,13 @@ 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.
+.. 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
+.. 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.
@@ -141,9 +142,9 @@ columns that will be used, and to *stretch* the content to use the whole `Widget
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
+.. 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.
@@ -159,17 +160,17 @@ 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
+.. 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
+.. literalinclude:: steps/06-expenses.py
+ :linenos:
+ :lines: 42-47
The next step will be connecting those new buttons to slots.
@@ -181,19 +182,19 @@ Each `QPushButton` have a signal called *clicked*, that is emitted when you clic
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
+.. 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
+.. literalinclude:: steps/07-expenses.py
+ :linenos:
+ :lines: 100-129
+ :emphasize-lines: 2,16,28
Since these slots are methods, we can access the class variables, like our `QTableWidget` to
interact with it.
@@ -225,15 +226,15 @@ 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
+.. 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
+.. 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
@@ -253,17 +254,17 @@ 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
+.. 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
+.. literalinclude:: steps/09-expenses.py
+ :linenos:
+ :lines: 81-91
+ :emphasize-lines: 9
Notice that before we had a line with `self.right.addStretch()`
to fill up the vertical space between the *Add* and the *Clear* buttons,
@@ -277,18 +278,18 @@ 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
+.. literalinclude:: steps/10-expenses.py
+ :linenos:
+ :lines: 103-109
+ :emphasize-lines: 6
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
+.. literalinclude:: steps/10-expenses.py
+ :linenos:
+ :lines: 139-151
The following steps show how to fill a `QPieSeries`:
@@ -305,9 +306,9 @@ your newly created chart to the `QChartView`.
The application will look like this:
- .. image:: expenses_tool.png
+.. image:: expenses_tool.png
And now you can see the whole code:
- .. literalinclude:: main.py
- :linenos:
+.. literalinclude:: main.py
+ :linenos:
diff --git a/sources/pyside2/doc/tutorials/index.rst b/sources/pyside2/doc/tutorials/index.rst
index 5a97aecb9..7486554f9 100644
--- a/sources/pyside2/doc/tutorials/index.rst
+++ b/sources/pyside2/doc/tutorials/index.rst
@@ -1,22 +1,12 @@
-Qt for Python examples and tutorials
-*************************************
+|project| Tutorials
+====================
-A collection of examples and tutorials with "walkthrough" guides are
+A collection of tutorials with "walkthrough" guides are
provided with |project| to help new users get started. These
documents were ported from C++ to Python and cover a range of topics,
from basic use of widgets to step-by-step tutorials that show how an
application is put together.
-Examples and demos
-===================
-
-.. toctree::
- :maxdepth: 1
-
- examples/tabbedbrowser.rst
-
-Tutorials
-==========
.. toctree::
:maxdepth: 2
@@ -30,3 +20,4 @@ Tutorials
expenses/expenses.rst
qmlapp/qmlapplication.rst
qmlintegration/qmlintegration.rst
+ portingguide/index.rst
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/chapter1.rst b/sources/pyside2/doc/tutorials/portingguide/chapter1/chapter1.rst
new file mode 100644
index 000000000..20b11065a
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/chapter1.rst
@@ -0,0 +1,89 @@
+Chapter 1: ``initDb.h`` to ``createDb.py``
+*******************************************
+
+To begin with, port the C++ code that creates an SQLite
+database and tables, and adds data to them. In this case,
+all C++ code related to this lives in ``initdb.h``. The
+code in this header file is divided into following parts:
+
+* ``initDb`` - Creates a db and the necessary tables
+* ``addBooks`` - Adds data to the **books** table.
+* ``addAuthor`` - Adds data to the **authors** table.
+* ``addGenre`` - Adds data to the **genres** table.
+
+To start with, add these following ``import`` statements at
+the beginning of ``createdb.py``:
+
+.. literalinclude:: createdb.py
+ :language: python
+ :linenos:
+ :lines: 40-44
+
+The ``initDb`` function does most of the work needed to
+set up the database, but it depends on the ``addAuthor``,
+``addGenre``, and ``addBook`` helper functions to populate
+the tables. Port these helper functions first. Here is how
+the C++ and Python versions of these functions look like:
+
+C++ version
+------------
+
+.. literalinclude:: initdb.h
+ :language: c++
+ :linenos:
+ :lines: 55-81
+
+Python version
+---------------
+
+.. literalinclude:: createdb.py
+ :language: python
+ :linenos:
+ :lines: 44-65
+
+Now that the helper functions are in place, port ``initDb``.
+Here is how the C++ and Python versions of this function
+looks like:
+
+C++ version
+------------
+
+.. literalinclude:: initdb.h
+ :language: c++
+ :linenos:
+ :lines: 81-159
+
+Python version
+---------------
+
+.. literalinclude:: createdb.py
+ :language: python
+ :linenos:
+ :lines: 65-
+
+.. note:: The Python version uses the ``check`` function to
+ execute the SQL statements instead of the ``if...else``
+ block like in the C++ version. Although both are valid
+ approaches, the earlier one produces code that looks
+ cleaner and shorter.
+
+Your Python code to set up the database is ready now. To
+test it, add the following code to ``main.py`` and run it:
+
+.. literalinclude:: main.py
+ :language: python
+ :linenos:
+ :lines: 40-
+
+Use the following command from the prompt to run:
+
+.. code-block::
+
+ python main.py
+
+Your table will look like this:
+
+.. image:: images/chapter1_books.png
+
+Try modifying the SQL statment in ``main.py`` to get data
+from the ``genres`` or ``authors`` table.
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/createdb.py b/sources/pyside2/doc/tutorials/portingguide/chapter1/createdb.py
new file mode 100644
index 000000000..8fb20cda1
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/createdb.py
@@ -0,0 +1,131 @@
+#############################################################################
+##
+## 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$
+##
+#############################################################################
+
+from PySide2.QtSql import QSqlDatabase, QSqlError, QSqlQuery
+from datetime import date
+
+
+def add_book(q, title, year, authorId, genreId, rating):
+ q.addBindValue(title)
+ q.addBindValue(year)
+ q.addBindValue(authorId)
+ q.addBindValue(genreId)
+ q.addBindValue(rating)
+ q.exec_()
+
+
+def add_genre(q, name):
+ q.addBindValue(name)
+ q.exec_()
+ return q.lastInsertId()
+
+
+def add_author(q, name, birthdate):
+ q.addBindValue(name)
+ q.addBindValue(str(birthdate))
+ q.exec_()
+ return q.lastInsertId()
+
+BOOKS_SQL = """
+ create table books(id integer primary key, title varchar, author integer,
+ genre integer, year integer, rating integer)
+ """
+AUTHORS_SQL = """
+ create table authors(id integer primary key, name varchar, birthdate text)
+ """
+GENRES_SQL = """
+ create table genres(id integer primary key, name varchar)
+ """
+INSERT_AUTHOR_SQL = """
+ insert into authors(name, birthdate) values(?, ?)
+ """
+INSERT_GENRE_SQL = """
+ insert into genres(name) values(?)
+ """
+INSERT_BOOK_SQL = """
+ insert into books(title, year, author, genre, rating)
+ values(?, ?, ?, ?, ?)
+ """
+
+def init_db():
+ """
+ init_db()
+ Initializes the database.
+ If tables "books" and "authors" are already in the database, do nothing.
+ Return value: None or raises ValueError
+ The error value is the QtSql error instance.
+ """
+ def check(func, *args):
+ if not func(*args):
+ raise ValueError(func.__self__.lastError())
+ db = QSqlDatabase.addDatabase("QSQLITE")
+ db.setDatabaseName(":memory:")
+
+ check(db.open)
+
+ q = QSqlQuery()
+ check(q.exec_, BOOKS_SQL)
+ check(q.exec_, AUTHORS_SQL)
+ check(q.exec_, GENRES_SQL)
+ check(q.prepare, INSERT_AUTHOR_SQL)
+
+ asimovId = add_author(q, "Isaac Asimov", date(1920, 2, 1))
+ greeneId = add_author(q, "Graham Greene", date(1904, 10, 2))
+ pratchettId = add_author(q, "Terry Pratchett", date(1948, 4, 28))
+
+ check(q.prepare,INSERT_GENRE_SQL)
+ sfiction = add_genre(q, "Science Fiction")
+ fiction = add_genre(q, "Fiction")
+ fantasy = add_genre(q, "Fantasy")
+
+ check(q.prepare,INSERT_BOOK_SQL)
+ add_book(q, "Foundation", 1951, asimovId, sfiction, 3)
+ add_book(q, "Foundation and Empire", 1952, asimovId, sfiction, 4)
+ add_book(q, "Second Foundation", 1953, asimovId, sfiction, 3)
+ add_book(q, "Foundation's Edge", 1982, asimovId, sfiction, 3)
+ add_book(q, "Foundation and Earth", 1986, asimovId, sfiction, 4)
+ add_book(q, "Prelude to Foundation", 1988, asimovId, sfiction, 3)
+ add_book(q, "Forward the Foundation", 1993, asimovId, sfiction, 3)
+ add_book(q, "The Power and the Glory", 1940, greeneId, fiction, 4)
+ add_book(q, "The Third Man", 1950, greeneId, fiction, 5)
+ add_book(q, "Our Man in Havana", 1958, greeneId, fiction, 4)
+ add_book(q, "Guards! Guards!", 1989, pratchettId, fantasy, 3)
+ add_book(q, "Night Watch", 2002, pratchettId, fantasy, 3)
+ add_book(q, "Going Postal", 2004, pratchettId, fantasy, 3)
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/images/chapter1_books.png b/sources/pyside2/doc/tutorials/portingguide/chapter1/images/chapter1_books.png
new file mode 100644
index 000000000..164674220
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/images/chapter1_books.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/initdb.h b/sources/pyside2/doc/tutorials/portingguide/chapter1/initdb.h
new file mode 100644
index 000000000..773e3fb74
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/initdb.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+
+#ifndef INITDB_H
+#define INITDB_H
+
+#include <QtSql>
+
+void addBook(QSqlQuery &q, const QString &title, int year, const QVariant &authorId,
+ const QVariant &genreId, int rating)
+{
+ q.addBindValue(title);
+ q.addBindValue(year);
+ q.addBindValue(authorId);
+ q.addBindValue(genreId);
+ q.addBindValue(rating);
+ q.exec();
+}
+
+QVariant addGenre(QSqlQuery &q, const QString &name)
+{
+ q.addBindValue(name);
+ q.exec();
+ return q.lastInsertId();
+}
+
+QVariant addAuthor(QSqlQuery &q, const QString &name, const QDate &birthdate)
+{
+ q.addBindValue(name);
+ q.addBindValue(birthdate);
+ q.exec();
+ return q.lastInsertId();
+}
+
+const auto BOOKS_SQL = QLatin1String(R"(
+ create table books(id integer primary key, title varchar, author integer,
+ genre integer, year integer, rating integer)
+ )");
+
+const auto AUTHORS_SQL = QLatin1String(R"(
+ create table authors(id integer primary key, name varchar, birthdate date)
+ )");
+
+const auto GENRES_SQL = QLatin1String(R"(
+ create table genres(id integer primary key, name varchar)
+ )");
+
+const auto INSERT_AUTHOR_SQL = QLatin1String(R"(
+ insert into authors(name, birthdate) values(?, ?)
+ )");
+
+const auto INSERT_BOOK_SQL = QLatin1String(R"(
+ insert into books(title, year, author, genre, rating)
+ values(?, ?, ?, ?, ?)
+ )");
+
+const auto INSERT_GENRE_SQL = QLatin1String(R"(
+ insert into genres(name) values(?)
+ )");
+
+QSqlError initDb()
+{
+ QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
+ db.setDatabaseName(":memory:");
+
+ if (!db.open())
+ return db.lastError();
+
+ QStringList tables = db.tables();
+ if (tables.contains("books", Qt::CaseInsensitive)
+ && tables.contains("authors", Qt::CaseInsensitive))
+ return QSqlError();
+
+ QSqlQuery q;
+ if (!q.exec(BOOKS_SQL))
+ return q.lastError();
+ if (!q.exec(AUTHORS_SQL))
+ return q.lastError();
+ if (!q.exec(GENRES_SQL))
+ return q.lastError();
+
+ if (!q.prepare(INSERT_AUTHOR_SQL))
+ return q.lastError();
+ QVariant asimovId = addAuthor(q, QLatin1String("Isaac Asimov"), QDate(1920, 2, 1));
+ QVariant greeneId = addAuthor(q, QLatin1String("Graham Greene"), QDate(1904, 10, 2));
+ QVariant pratchettId = addAuthor(q, QLatin1String("Terry Pratchett"), QDate(1948, 4, 28));
+
+ if (!q.prepare(INSERT_GENRE_SQL))
+ return q.lastError();
+ QVariant sfiction = addGenre(q, QLatin1String("Science Fiction"));
+ QVariant fiction = addGenre(q, QLatin1String("Fiction"));
+ QVariant fantasy = addGenre(q, QLatin1String("Fantasy"));
+
+ if (!q.prepare(INSERT_BOOK_SQL))
+ return q.lastError();
+ addBook(q, QLatin1String("Foundation"), 1951, asimovId, sfiction, 3);
+ addBook(q, QLatin1String("Foundation and Empire"), 1952, asimovId, sfiction, 4);
+ addBook(q, QLatin1String("Second Foundation"), 1953, asimovId, sfiction, 3);
+ addBook(q, QLatin1String("Foundation's Edge"), 1982, asimovId, sfiction, 3);
+ addBook(q, QLatin1String("Foundation and Earth"), 1986, asimovId, sfiction, 4);
+ addBook(q, QLatin1String("Prelude to Foundation"), 1988, asimovId, sfiction, 3);
+ addBook(q, QLatin1String("Forward the Foundation"), 1993, asimovId, sfiction, 3);
+ addBook(q, QLatin1String("The Power and the Glory"), 1940, greeneId, fiction, 4);
+ addBook(q, QLatin1String("The Third Man"), 1950, greeneId, fiction, 5);
+ addBook(q, QLatin1String("Our Man in Havana"), 1958, greeneId, fiction, 4);
+ addBook(q, QLatin1String("Guards! Guards!"), 1989, pratchettId, fantasy, 3);
+ addBook(q, QLatin1String("Night Watch"), 2002, pratchettId, fantasy, 3);
+ addBook(q, QLatin1String("Going Postal"), 2004, pratchettId, fantasy, 3);
+
+ return QSqlError();
+}
+
+#endif
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/main.py b/sources/pyside2/doc/tutorials/portingguide/chapter1/main.py
new file mode 100644
index 000000000..7e94e4c14
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/main.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.QtSql import QSqlQueryModel
+from PySide2.QtWidgets import QTableView, QApplication
+
+import createdb
+
+if __name__ == "__main__":
+ app = QApplication()
+ createdb.init_db()
+
+ model = QSqlQueryModel()
+ model.setQuery("select * from books")
+
+ table_view = QTableView()
+ table_view.setModel(model)
+ table_view.resize(800, 600)
+ table_view.show()
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.cpp b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.cpp
new file mode 100644
index 000000000..4115f80cf
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+
+#include "bookdelegate.h"
+
+#include <QtWidgets>
+
+BookDelegate::BookDelegate(QObject *parent)
+ : QSqlRelationalDelegate(parent), star(QPixmap(":images/star.png"))
+{
+}
+
+void BookDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ if (index.column() != 5) {
+ QStyleOptionViewItem opt = option;
+ // Since we draw the grid ourselves:
+ opt.rect.adjust(0, 0, -1, -1);
+ QSqlRelationalDelegate::paint(painter, opt, index);
+ } else {
+ const QAbstractItemModel *model = index.model();
+ QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ?
+ (option.state & QStyle::State_Active) ?
+ QPalette::Normal :
+ QPalette::Inactive :
+ QPalette::Disabled;
+
+ if (option.state & QStyle::State_Selected)
+ painter->fillRect(
+ option.rect,
+ option.palette.color(cg, QPalette::Highlight));
+
+ int rating = model->data(index, Qt::DisplayRole).toInt();
+ int width = star.width();
+ int height = star.height();
+ int x = option.rect.x();
+ int y = option.rect.y() + (option.rect.height() / 2) - (height / 2);
+ for (int i = 0; i < rating; ++i) {
+ painter->drawPixmap(x, y, star);
+ x += width;
+ }
+ // Since we draw the grid ourselves:
+ drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1));
+ }
+
+ QPen pen = painter->pen();
+ painter->setPen(option.palette.color(QPalette::Mid));
+ painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
+ painter->drawLine(option.rect.topRight(), option.rect.bottomRight());
+ painter->setPen(pen);
+}
+
+QSize BookDelegate::sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ if (index.column() == 5)
+ return QSize(5 * star.width(), star.height()) + QSize(1, 1);
+ // Since we draw the grid ourselves:
+ return QSqlRelationalDelegate::sizeHint(option, index) + QSize(1, 1);
+}
+
+bool BookDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index)
+{
+ if (index.column() != 5)
+ return QSqlRelationalDelegate::editorEvent(event, model, option, index);
+
+ if (event->type() == QEvent::MouseButtonPress) {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
+ int stars = qBound(0, int(0.7 + qreal(mouseEvent->pos().x()
+ - option.rect.x()) / star.width()), 5);
+ model->setData(index, QVariant(stars));
+ // So that the selection can change:
+ return false;
+ }
+
+ return true;
+}
+
+QWidget *BookDelegate::createEditor(QWidget *parent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ if (index.column() != 4)
+ return QSqlRelationalDelegate::createEditor(parent, option, index);
+
+ // For editing the year, return a spinbox with a range from -1000 to 2100.
+ QSpinBox *sb = new QSpinBox(parent);
+ sb->setFrame(false);
+ sb->setMaximum(2100);
+ sb->setMinimum(-1000);
+
+ return sb;
+}
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.h b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.h
new file mode 100644
index 000000000..f1b432699
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+
+#ifndef BOOKDELEGATE_H
+#define BOOKDELEGATE_H
+
+#include <QModelIndex>
+#include <QPixmap>
+#include <QSize>
+#include <QSqlRelationalDelegate>
+
+QT_FORWARD_DECLARE_CLASS(QPainter)
+
+class BookDelegate : public QSqlRelationalDelegate
+{
+public:
+ BookDelegate(QObject *parent);
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+
+ bool editorEvent(QEvent *event, QAbstractItemModel *model,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) override;
+
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+
+private:
+ QPixmap star;
+};
+
+#endif
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.py b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.py
new file mode 100644
index 000000000..57d8f0f73
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.py
@@ -0,0 +1,134 @@
+#############################################################################
+##
+## 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 copy, os
+from PySide2.QtSql import QSqlRelationalDelegate
+from PySide2.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate,
+ QStyle, QStyleOptionViewItem)
+from PySide2.QtGui import QMouseEvent, QPixmap, QPalette, QImage
+from PySide2.QtCore import QEvent, QSize, Qt, QUrl
+
+class BookDelegate(QSqlRelationalDelegate):
+ """Books delegate to rate the books"""
+
+ def __init__(self, parent=None):
+ QSqlRelationalDelegate.__init__(self, parent)
+ star_png = os.path.dirname(__file__) + "\images\star.png"
+ self.star = QPixmap(star_png)
+
+ def paint(self, painter, option, index):
+ """ Paint the items in the table.
+
+ If the item referred to by <index> is a StarRating, we
+ handle the painting ourselves. For the other items, we
+ let the base class handle the painting as usual.
+
+ In a polished application, we'd use a better check than
+ the column number to find out if we needed to paint the
+ stars, but it works for the purposes of this example.
+ """
+ if index.column() != 5:
+ # Since we draw the grid ourselves:
+ opt = copy.copy(option)
+ opt.rect = option.rect.adjusted(0, 0, -1, -1)
+ QSqlRelationalDelegate.paint(self, painter, opt, index)
+ else:
+ model = index.model()
+ if option.state & QStyle.State_Enabled:
+ if option.state & QStyle.State_Active:
+ color_group = QPalette.Normal
+ else:
+ color_group = QPalette.Inactive
+ else:
+ color_group = QPalette.Disabled
+
+ if option.state & QStyle.State_Selected:
+ painter.fillRect(option.rect,
+ option.palette.color(color_group, QPalette.Highlight))
+ rating = model.data(index, Qt.DisplayRole)
+ width = self.star.width()
+ height = self.star.height()
+ x = option.rect.x()
+ y = option.rect.y() + (option.rect.height() / 2) - (height / 2)
+ for i in range(rating):
+ painter.drawPixmap(x, y, self.star)
+ x += width
+
+ # Since we draw the grid ourselves:
+ self.drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1))
+
+ pen = painter.pen()
+ painter.setPen(option.palette.color(QPalette.Mid))
+ painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight())
+ painter.drawLine(option.rect.topRight(), option.rect.bottomRight())
+ painter.setPen(pen)
+
+ def sizeHint(self, option, index):
+ """ Returns the size needed to display the item in a QSize object. """
+ if index.column() == 5:
+ size_hint = QSize(5 * self.star.width(), self.star.height()) + QSize(1, 1)
+ return size_hint
+ # Since we draw the grid ourselves:
+ return QSqlRelationalDelegate.sizeHint(self, option, index) + QSize(1, 1)
+
+ def editorEvent(self, event, model, option, index):
+ if index.column() != 5:
+ return False
+
+ if event.type() == QEvent.MouseButtonPress:
+ mouse_pos = event.pos()
+ new_stars = int(0.7 + (mouse_pos.x() - option.rect.x()) / self.star.width())
+ stars = max(0, min(new_stars, 5))
+ model.setData(index, stars)
+ # So that the selection can change
+ return False
+
+ return True
+
+ def createEditor(self, parent, option, index):
+ if index.column() != 4:
+ return QSqlRelationalDelegate.createEditor(self, parent, option, index)
+
+ # For editing the year, return a spinbox with a range from -1000 to 2100.
+ spinbox = QSpinBox(parent)
+ spinbox.setFrame(False)
+ spinbox.setMaximum(2100)
+ spinbox.setMinimum(-1000)
+ return spinbox
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/chapter2.rst b/sources/pyside2/doc/tutorials/portingguide/chapter2/chapter2.rst
new file mode 100644
index 000000000..a574218fd
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/chapter2.rst
@@ -0,0 +1,93 @@
+Chapter 2: ``bookdelegate.cpp`` to ``bookdelegate.py``
+*******************************************************
+
+Now that your database is in place, port the C++ code for the
+``BookDelegate`` class. This class offers a delegate to present
+and edit the data in a ``QTableView``. It inherits
+``QSqlRelationalDelegate`` interface, which offers features
+specific for handling relational databases, such as a combobox
+editor for foreign key fields. To begin with, create
+``bookdelegate.py`` and add the following imports to it:
+
+.. literalinclude:: bookdelegate.py
+ :language: python
+ :linenos:
+ :lines: 40-47
+
+After the necessary ``import`` statements, port the
+constructor code for the ``BookDelegate`` class. Both
+the C++ and Python versions of this code initialize a
+``QSqlRelationalDelegate`` and ``QPixmap`` instance.
+Here is how they look:
+
+C++ version
+-------------
+
+.. literalinclude:: bookdelegate.cpp
+ :language: c++
+ :linenos:
+ :lines: 54-59
+
+Python version
+---------------
+
+.. literalinclude:: bookdelegate.py
+ :language: python
+ :linenos:
+ :lines: 47-54
+
+.. note:: The Python version loads the ``QPixmap`` using
+ the absolute path of ``star.png`` in the local
+ filesystem.
+
+As the default functionality offered by the
+``QSqlRelationalDelegate`` is not enough to present
+the books data, you must reimplement a few functions.
+For example, painting stars to represent the rating for
+each book in the table. Here is how the reimplemented
+code looks like:
+
+C++ version
+------------
+
+.. literalinclude:: bookdelegate.cpp
+ :language: c++
+ :linenos:
+ :lines: 59-
+
+Python version
+---------------
+
+.. literalinclude:: bookdelegate.py
+ :language: python
+ :linenos:
+ :lines: 55-
+
+Now that the delegate is in place, run the following
+``main.py`` to see how the data is presented:
+
+.. literalinclude:: main.py
+ :language: python
+ :linenos:
+ :lines: 40-
+
+Here is how the application will look when you run it:
+
+.. image:: images/chapter2_books.png
+ :alt: Books table data
+
+The only difference you'll notice now in comparison to
+:doc:`chapter 1 <../chapter1/chapter1>` is that the
+``rating`` column looks different.
+
+Try improving the table even further by adding these
+features:
+
+* Title for each column
+* SQL relation for the ``author_id`` and ``genre_id`` columns
+* Set a title to the window
+
+With these features, this is how your table will look like:
+
+.. image:: images/chapter2_books_with_relation.png
+ :alt: Books table with SQL relation
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/createdb.py b/sources/pyside2/doc/tutorials/portingguide/chapter2/createdb.py
new file mode 100644
index 000000000..8fb20cda1
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/createdb.py
@@ -0,0 +1,131 @@
+#############################################################################
+##
+## 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$
+##
+#############################################################################
+
+from PySide2.QtSql import QSqlDatabase, QSqlError, QSqlQuery
+from datetime import date
+
+
+def add_book(q, title, year, authorId, genreId, rating):
+ q.addBindValue(title)
+ q.addBindValue(year)
+ q.addBindValue(authorId)
+ q.addBindValue(genreId)
+ q.addBindValue(rating)
+ q.exec_()
+
+
+def add_genre(q, name):
+ q.addBindValue(name)
+ q.exec_()
+ return q.lastInsertId()
+
+
+def add_author(q, name, birthdate):
+ q.addBindValue(name)
+ q.addBindValue(str(birthdate))
+ q.exec_()
+ return q.lastInsertId()
+
+BOOKS_SQL = """
+ create table books(id integer primary key, title varchar, author integer,
+ genre integer, year integer, rating integer)
+ """
+AUTHORS_SQL = """
+ create table authors(id integer primary key, name varchar, birthdate text)
+ """
+GENRES_SQL = """
+ create table genres(id integer primary key, name varchar)
+ """
+INSERT_AUTHOR_SQL = """
+ insert into authors(name, birthdate) values(?, ?)
+ """
+INSERT_GENRE_SQL = """
+ insert into genres(name) values(?)
+ """
+INSERT_BOOK_SQL = """
+ insert into books(title, year, author, genre, rating)
+ values(?, ?, ?, ?, ?)
+ """
+
+def init_db():
+ """
+ init_db()
+ Initializes the database.
+ If tables "books" and "authors" are already in the database, do nothing.
+ Return value: None or raises ValueError
+ The error value is the QtSql error instance.
+ """
+ def check(func, *args):
+ if not func(*args):
+ raise ValueError(func.__self__.lastError())
+ db = QSqlDatabase.addDatabase("QSQLITE")
+ db.setDatabaseName(":memory:")
+
+ check(db.open)
+
+ q = QSqlQuery()
+ check(q.exec_, BOOKS_SQL)
+ check(q.exec_, AUTHORS_SQL)
+ check(q.exec_, GENRES_SQL)
+ check(q.prepare, INSERT_AUTHOR_SQL)
+
+ asimovId = add_author(q, "Isaac Asimov", date(1920, 2, 1))
+ greeneId = add_author(q, "Graham Greene", date(1904, 10, 2))
+ pratchettId = add_author(q, "Terry Pratchett", date(1948, 4, 28))
+
+ check(q.prepare,INSERT_GENRE_SQL)
+ sfiction = add_genre(q, "Science Fiction")
+ fiction = add_genre(q, "Fiction")
+ fantasy = add_genre(q, "Fantasy")
+
+ check(q.prepare,INSERT_BOOK_SQL)
+ add_book(q, "Foundation", 1951, asimovId, sfiction, 3)
+ add_book(q, "Foundation and Empire", 1952, asimovId, sfiction, 4)
+ add_book(q, "Second Foundation", 1953, asimovId, sfiction, 3)
+ add_book(q, "Foundation's Edge", 1982, asimovId, sfiction, 3)
+ add_book(q, "Foundation and Earth", 1986, asimovId, sfiction, 4)
+ add_book(q, "Prelude to Foundation", 1988, asimovId, sfiction, 3)
+ add_book(q, "Forward the Foundation", 1993, asimovId, sfiction, 3)
+ add_book(q, "The Power and the Glory", 1940, greeneId, fiction, 4)
+ add_book(q, "The Third Man", 1950, greeneId, fiction, 5)
+ add_book(q, "Our Man in Havana", 1958, greeneId, fiction, 4)
+ add_book(q, "Guards! Guards!", 1989, pratchettId, fantasy, 3)
+ add_book(q, "Night Watch", 2002, pratchettId, fantasy, 3)
+ add_book(q, "Going Postal", 2004, pratchettId, fantasy, 3)
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books.png b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books.png
new file mode 100644
index 000000000..e456b7d8f
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.png b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.png
new file mode 100644
index 000000000..82a5f449c
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/images/star.png b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/star.png
new file mode 100644
index 000000000..87f4464bd
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/star.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/main.py b/sources/pyside2/doc/tutorials/portingguide/chapter2/main.py
new file mode 100644
index 000000000..639ee2ca0
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/main.py
@@ -0,0 +1,63 @@
+#############################################################################
+##
+## 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
+from PySide2.QtSql import QSqlQueryModel
+from PySide2.QtWidgets import QTableView, QApplication
+
+import createdb
+from bookdelegate import BookDelegate
+
+if __name__ == "__main__":
+ app = QApplication()
+ createdb.init_db()
+
+ model = QSqlQueryModel()
+ model.setQuery("select title, author, genre, year, rating from books")
+
+ table = QTableView()
+ table.setModel(model)
+ table.setItemDelegate(BookDelegate())
+ table.resize(800, 600)
+ table.show()
+
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate-old.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate-old.py
new file mode 100644
index 000000000..2e8670448
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate-old.py
@@ -0,0 +1,134 @@
+#############################################################################
+##
+## 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 copy, os
+from PySide2.QtSql import QSqlRelationalDelegate
+from PySide2.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate,
+ QStyle, QStyleOptionViewItem)
+from PySide2.QtGui import QMouseEvent, QPixmap, QPalette, QImage
+from PySide2.QtCore import QEvent, QSize, Qt, QUrl
+
+class BookDelegate(QSqlRelationalDelegate):
+ """Books delegate to rate the books"""
+
+ def __init__(self, star_png, parent=None):
+ QSqlRelationalDelegate.__init__(self, parent)
+ star_png = os.path.dirname(__file__) + "\images\star.png"
+ self.star = QPixmap(star_png)
+
+ def paint(self, painter, option, index):
+ """ Paint the items in the table.
+
+ If the item referred to by <index> is a StarRating, we
+ handle the painting ourselves. For the other items, we
+ let the base class handle the painting as usual.
+
+ In a polished application, we'd use a better check than
+ the column number to find out if we needed to paint the
+ stars, but it works for the purposes of this example.
+ """
+ if index.column() != 5:
+ # Since we draw the grid ourselves:
+ opt = copy.copy(option)
+ opt.rect = option.rect.adjusted(0, 0, -1, -1)
+ QSqlRelationalDelegate.paint(self, painter, opt, index)
+ else:
+ model = index.model()
+ if option.state & QStyle.State_Enabled:
+ if option.state & QStyle.State_Active:
+ color_group = QPalette.Normal
+ else:
+ color_group = QPalette.Inactive
+ else:
+ color_group = QPalette.Disabled
+
+ if option.state & QStyle.State_Selected:
+ painter.fillRect(option.rect,
+ option.palette.color(color_group, QPalette.Highlight))
+ rating = model.data(index, Qt.DisplayRole)
+ width = self.star.width()
+ height = self.star.height()
+ x = option.rect.x()
+ y = option.rect.y() + (option.rect.height() / 2) - (height / 2)
+ for i in range(rating):
+ painter.drawPixmap(x, y, self.star)
+ x += width
+
+ # Since we draw the grid ourselves:
+ self.drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1))
+
+ pen = painter.pen()
+ painter.setPen(option.palette.color(QPalette.Mid))
+ painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight())
+ painter.drawLine(option.rect.topRight(), option.rect.bottomRight())
+ painter.setPen(pen)
+
+ def sizeHint(self, option, index):
+ """ Returns the size needed to display the item in a QSize object. """
+ if index.column() == 5:
+ size_hint = QSize(5 * self.star.width(), self.star.height()) + QSize(1, 1)
+ return size_hint
+ # Since we draw the grid ourselves:
+ return QSqlRelationalDelegate.sizeHint(self, option, index) + QSize(1, 1)
+
+ def editorEvent(self, event, model, option, index):
+ if index.column() != 5:
+ return False
+
+ if event.type() == QEvent.MouseButtonPress:
+ mouse_pos = event.pos()
+ new_stars = int(0.7 + (mouse_pos.x() - option.rect.x()) / self.star.width())
+ stars = max(0, min(new_stars, 5))
+ model.setData(index, stars)
+ # So that the selection can change
+ return False
+
+ return True
+
+ def createEditor(self, parent, option, index):
+ if index.column() != 4:
+ return QSqlRelationalDelegate.createEditor(self, parent, option, index)
+
+ # For editing the year, return a spinbox with a range from -1000 to 2100.
+ spinbox = QSpinBox(parent)
+ spinbox.setFrame(False)
+ spinbox.setMaximum(2100)
+ spinbox.setMinimum(-1000)
+ return spinbox
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate.py
new file mode 100644
index 000000000..087b0c262
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate.py
@@ -0,0 +1,133 @@
+#############################################################################
+##
+## 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 copy, os
+from PySide2.QtSql import QSqlRelationalDelegate
+from PySide2.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate,
+ QStyle, QStyleOptionViewItem)
+from PySide2.QtGui import QMouseEvent, QPixmap, QPalette, QImage
+from PySide2.QtCore import QEvent, QSize, Qt, QUrl
+
+class BookDelegate(QSqlRelationalDelegate):
+ """Books delegate to rate the books"""
+
+ def __init__(self, star_png, parent=None):
+ QSqlRelationalDelegate.__init__(self, parent)
+ self.star = QPixmap(":/images/star.png")
+
+ def paint(self, painter, option, index):
+ """ Paint the items in the table.
+
+ If the item referred to by <index> is a StarRating, we
+ handle the painting ourselves. For the other items, we
+ let the base class handle the painting as usual.
+
+ In a polished application, we'd use a better check than
+ the column number to find out if we needed to paint the
+ stars, but it works for the purposes of this example.
+ """
+ if index.column() != 5:
+ # Since we draw the grid ourselves:
+ opt = copy.copy(option)
+ opt.rect = option.rect.adjusted(0, 0, -1, -1)
+ QSqlRelationalDelegate.paint(self, painter, opt, index)
+ else:
+ model = index.model()
+ if option.state & QStyle.State_Enabled:
+ if option.state & QStyle.State_Active:
+ color_group = QPalette.Normal
+ else:
+ color_group = QPalette.Inactive
+ else:
+ color_group = QPalette.Disabled
+
+ if option.state & QStyle.State_Selected:
+ painter.fillRect(option.rect,
+ option.palette.color(color_group, QPalette.Highlight))
+ rating = model.data(index, Qt.DisplayRole)
+ width = self.star.width()
+ height = self.star.height()
+ x = option.rect.x()
+ y = option.rect.y() + (option.rect.height() / 2) - (height / 2)
+ for i in range(rating):
+ painter.drawPixmap(x, y, self.star)
+ x += width
+
+ # Since we draw the grid ourselves:
+ self.drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1))
+
+ pen = painter.pen()
+ painter.setPen(option.palette.color(QPalette.Mid))
+ painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight())
+ painter.drawLine(option.rect.topRight(), option.rect.bottomRight())
+ painter.setPen(pen)
+
+ def sizeHint(self, option, index):
+ """ Returns the size needed to display the item in a QSize object. """
+ if index.column() == 5:
+ size_hint = QSize(5 * self.star.width(), self.star.height()) + QSize(1, 1)
+ return size_hint
+ # Since we draw the grid ourselves:
+ return QSqlRelationalDelegate.sizeHint(self, option, index) + QSize(1, 1)
+
+ def editorEvent(self, event, model, option, index):
+ if index.column() != 5:
+ return False
+
+ if event.type() == QEvent.MouseButtonPress:
+ mouse_pos = event.pos()
+ new_stars = int(0.7 + (mouse_pos.x() - option.rect.x()) / self.star.width())
+ stars = max(0, min(new_stars, 5))
+ model.setData(index, stars)
+ # So that the selection can change
+ return False
+
+ return True
+
+ def createEditor(self, parent, option, index):
+ if index.column() != 4:
+ return QSqlRelationalDelegate.createEditor(self, parent, option, index)
+
+ # For editing the year, return a spinbox with a range from -1000 to 2100.
+ spinbox = QSpinBox(parent)
+ spinbox.setFrame(False)
+ spinbox.setMaximum(2100)
+ spinbox.setMinimum(-1000)
+ return spinbox
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/books.qrc b/sources/pyside2/doc/tutorials/portingguide/chapter3/books.qrc
new file mode 100644
index 000000000..d6ad21337
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/books.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>images/star.png</file>
+</qresource>
+</RCC>
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.cpp b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.cpp
new file mode 100644
index 000000000..76f3c9da8
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+
+#include "bookwindow.h"
+#include "bookdelegate.h"
+#include "initdb.h"
+
+#include <QtSql>
+
+BookWindow::BookWindow()
+{
+ ui.setupUi(this);
+
+ if (!QSqlDatabase::drivers().contains("QSQLITE"))
+ QMessageBox::critical(
+ this,
+ "Unable to load database",
+ "This demo needs the SQLITE driver"
+ );
+
+ // Initialize the database:
+ QSqlError err = initDb();
+ if (err.type() != QSqlError::NoError) {
+ showError(err);
+ return;
+ }
+
+ // Create the data model:
+ model = new QSqlRelationalTableModel(ui.bookTable);
+ model->setEditStrategy(QSqlTableModel::OnManualSubmit);
+ model->setTable("books");
+
+ // Remember the indexes of the columns:
+ authorIdx = model->fieldIndex("author");
+ genreIdx = model->fieldIndex("genre");
+
+ // Set the relations to the other database tables:
+ model->setRelation(authorIdx, QSqlRelation("authors", "id", "name"));
+ model->setRelation(genreIdx, QSqlRelation("genres", "id", "name"));
+
+ // Set the localized header captions:
+ model->setHeaderData(authorIdx, Qt::Horizontal, tr("Author Name"));
+ model->setHeaderData(genreIdx, Qt::Horizontal, tr("Genre"));
+ model->setHeaderData(model->fieldIndex("title"),
+ Qt::Horizontal, tr("Title"));
+ model->setHeaderData(model->fieldIndex("year"), Qt::Horizontal, tr("Year"));
+ model->setHeaderData(model->fieldIndex("rating"),
+ Qt::Horizontal, tr("Rating"));
+
+ // Populate the model:
+ if (!model->select()) {
+ showError(model->lastError());
+ return;
+ }
+
+ // Set the model and hide the ID column:
+ ui.bookTable->setModel(model);
+ ui.bookTable->setItemDelegate(new BookDelegate(ui.bookTable));
+ ui.bookTable->setColumnHidden(model->fieldIndex("id"), true);
+ ui.bookTable->setSelectionMode(QAbstractItemView::SingleSelection);
+
+ // Initialize the Author combo box:
+ ui.authorEdit->setModel(model->relationModel(authorIdx));
+ ui.authorEdit->setModelColumn(
+ model->relationModel(authorIdx)->fieldIndex("name"));
+
+ ui.genreEdit->setModel(model->relationModel(genreIdx));
+ ui.genreEdit->setModelColumn(
+ model->relationModel(genreIdx)->fieldIndex("name"));
+
+ // Lock and prohibit resizing of the width of the rating column:
+ ui.bookTable->horizontalHeader()->setSectionResizeMode(
+ model->fieldIndex("rating"),
+ QHeaderView::ResizeToContents);
+
+ QDataWidgetMapper *mapper = new QDataWidgetMapper(this);
+ mapper->setModel(model);
+ mapper->setItemDelegate(new BookDelegate(this));
+ mapper->addMapping(ui.titleEdit, model->fieldIndex("title"));
+ mapper->addMapping(ui.yearEdit, model->fieldIndex("year"));
+ mapper->addMapping(ui.authorEdit, authorIdx);
+ mapper->addMapping(ui.genreEdit, genreIdx);
+ mapper->addMapping(ui.ratingEdit, model->fieldIndex("rating"));
+
+ connect(ui.bookTable->selectionModel(),
+ &QItemSelectionModel::currentRowChanged,
+ mapper,
+ &QDataWidgetMapper::setCurrentModelIndex
+ );
+
+ ui.bookTable->setCurrentIndex(model->index(0, 0));
+ createMenuBar();
+}
+
+void BookWindow::showError(const QSqlError &err)
+{
+ QMessageBox::critical(this, "Unable to initialize Database",
+ "Error initializing database: " + err.text());
+}
+
+void BookWindow::createMenuBar()
+{
+ QAction *quitAction = new QAction(tr("&Quit"), this);
+ QAction *aboutAction = new QAction(tr("&About"), this);
+ QAction *aboutQtAction = new QAction(tr("&About Qt"), this);
+
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+ fileMenu->addAction(quitAction);
+
+ QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+ helpMenu->addAction(aboutAction);
+ helpMenu->addAction(aboutQtAction);
+
+ connect(quitAction, &QAction::triggered, this, &BookWindow::close);
+ connect(aboutAction, &QAction::triggered, this, &BookWindow::about);
+ connect(aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
+}
+
+void BookWindow::about()
+{
+ QMessageBox::about(this, tr("About Books"),
+ tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
+ "with a model/view framework."));
+}
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.py
new file mode 100644
index 000000000..4bc4cf48b
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.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$
+##
+#############################################################################
+
+from __future__ import print_function, absolute_import
+
+from PySide2.QtWidgets import (QAction, QAbstractItemView, qApp, QDataWidgetMapper,
+ QHeaderView, QMainWindow, QMessageBox)
+from PySide2.QtGui import QKeySequence
+from PySide2.QtSql import (QSqlRelation, QSqlRelationalTableModel, QSqlTableModel,
+ QSqlError)
+from PySide2.QtCore import QAbstractItemModel, QObject, QSize, Qt, Slot
+import createdb
+from ui_bookwindow import Ui_BookWindow
+from bookdelegate import BookDelegate
+
+
+class BookWindow(QMainWindow, Ui_BookWindow):
+ # """A window to show the books available"""
+
+ def __init__(self):
+ QMainWindow.__init__(self)
+ self.setupUi(self)
+
+ #Initialize db
+ createdb.init_db()
+
+ model = QSqlRelationalTableModel(self.bookTable)
+ model.setEditStrategy(QSqlTableModel.OnManualSubmit)
+ model.setTable("books")
+
+ # Remember the indexes of the columns:
+ author_idx = model.fieldIndex("author")
+ genre_idx = model.fieldIndex("genre")
+
+ # Set the relations to the other database tables:
+ model.setRelation(author_idx, QSqlRelation("authors", "id", "name"))
+ model.setRelation(genre_idx, QSqlRelation("genres", "id", "name"))
+
+ # Set the localized header captions:
+ model.setHeaderData(author_idx, Qt.Horizontal, self.tr("Author Name"))
+ model.setHeaderData(genre_idx, Qt.Horizontal, self.tr("Genre"))
+ model.setHeaderData(model.fieldIndex("title"), Qt.Horizontal, self.tr("Title"))
+ model.setHeaderData(model.fieldIndex("year"), Qt.Horizontal, self.tr("Year"))
+ model.setHeaderData(model.fieldIndex("rating"), Qt.Horizontal, self.tr("Rating"))
+
+ if not model.select():
+ print(model.lastError())
+
+ # Set the model and hide the ID column:
+ self.bookTable.setModel(model)
+ self.bookTable.setItemDelegate(BookDelegate(self.bookTable))
+ self.bookTable.setColumnHidden(model.fieldIndex("id"), True)
+ self.bookTable.setSelectionMode(QAbstractItemView.SingleSelection)
+
+ # Initialize the Author combo box:
+ self.authorEdit.setModel(model.relationModel(author_idx))
+ self.authorEdit.setModelColumn(model.relationModel(author_idx).fieldIndex("name"))
+
+ self.genreEdit.setModel(model.relationModel(genre_idx))
+ self.genreEdit.setModelColumn(model.relationModel(genre_idx).fieldIndex("name"))
+
+ # Lock and prohibit resizing of the width of the rating column:
+ self.bookTable.horizontalHeader().setSectionResizeMode(model.fieldIndex("rating"),
+ QHeaderView.ResizeToContents)
+
+ mapper = QDataWidgetMapper(self)
+ mapper.setModel(model)
+ mapper.setItemDelegate(BookDelegate(self))
+ mapper.addMapping(self.titleEdit, model.fieldIndex("title"))
+ mapper.addMapping(self.yearEdit, model.fieldIndex("year"))
+ mapper.addMapping(self.authorEdit, author_idx)
+ mapper.addMapping(self.genreEdit, genre_idx)
+ mapper.addMapping(self.ratingEdit, model.fieldIndex("rating"))
+
+ selection_model = self.bookTable.selectionModel()
+ selection_model.currentRowChanged.connect(mapper.setCurrentModelIndex)
+
+ self.bookTable.setCurrentIndex(model.index(0, 0))
+ self.create_menubar()
+
+ def showError(err):
+ QMessageBox.critical(self, "Unable to initialize Database",
+ "Error initializing database: " + err.text())
+
+ def create_menubar(self):
+ file_menu = self.menuBar().addMenu(self.tr("&File"))
+ quit_action = file_menu.addAction(self.tr("&Quit"))
+ quit_action.triggered.connect(qApp.quit)
+
+ help_menu = self.menuBar().addMenu(self.tr("&Help"))
+ about_action = help_menu.addAction(self.tr("&About"))
+ about_action.setShortcut(QKeySequence.HelpContents)
+ about_action.triggered.connect(self.about)
+ aboutQt_action = help_menu.addAction("&About Qt")
+ aboutQt_action.triggered.connect(qApp.aboutQt)
+
+ def about(self):
+ QMessageBox.about(self, self.tr("About Books"),
+ self.tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
+ "with a model/view framework."))
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.ui b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.ui
new file mode 100644
index 000000000..e1668288f
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.ui
@@ -0,0 +1,149 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>BookWindow</class>
+ <widget class="QMainWindow" name="BookWindow" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>601</width>
+ <height>420</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Books</string>
+ </property>
+ <widget class="QWidget" name="centralWidget" >
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>Books</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTableView" name="bookTable" >
+ <property name="selectionBehavior" >
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="title" >
+ <string>Details</string>
+ </property>
+ <layout class="QFormLayout" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_5" >
+ <property name="text" >
+ <string>&lt;b>Title:&lt;/b></string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLineEdit" name="titleEdit" >
+ <property name="enabled" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_2_2_2_2" >
+ <property name="text" >
+ <string>&lt;b>Author: &lt;/b></string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QComboBox" name="authorEdit" >
+ <property name="enabled" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>&lt;b>Genre:&lt;/b></string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QComboBox" name="genreEdit" >
+ <property name="enabled" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_4" >
+ <property name="text" >
+ <string>&lt;b>Year:&lt;/b></string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" >
+ <widget class="QSpinBox" name="yearEdit" >
+ <property name="enabled" >
+ <bool>true</bool>
+ </property>
+ <property name="prefix" >
+ <string/>
+ </property>
+ <property name="maximum" >
+ <number>2100</number>
+ </property>
+ <property name="minimum" >
+ <number>-1000</number>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>&lt;b>Rating:&lt;/b></string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" >
+ <widget class="QSpinBox" name="ratingEdit" >
+ <property name="maximum" >
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <tabstops>
+ <tabstop>bookTable</tabstop>
+ <tabstop>titleEdit</tabstop>
+ <tabstop>authorEdit</tabstop>
+ <tabstop>genreEdit</tabstop>
+ <tabstop>yearEdit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/chapter3.rst b/sources/pyside2/doc/tutorials/portingguide/chapter3/chapter3.rst
new file mode 100644
index 000000000..71b254811
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/chapter3.rst
@@ -0,0 +1,121 @@
+Chapter 3: Port ``bookdwindow.cpp`` to ``bookwindow.py``
+*********************************************************
+
+After the bookdelegate, port the C++ code for the
+``BookWindow`` class. It offers a QMainWindow, containing a
+``QTableView`` to present the books data, and a **Details**
+section with a set of input fields to edit the selected row
+in the table. To begin with, create the ``bookwindow.py``
+and add the following imports to it:
+
+.. literalinclude:: bookwindow.py
+ :language: python
+ :linenos:
+ :lines: 40-53
+
+.. note:: The imports include the ``BookDelegate`` you
+ ported earlier and the ``Ui_BookWindow``. The pyside-uic
+ tool generates the ``ui_bookwindow`` Python code based
+ on the ``bookwindow.ui`` XML file.
+
+To generate this Python code, run the following command on the
+prompt:
+
+.. code-block::
+
+ pyside2-uic bookwindow.ui > ui_bookwindow.py
+
+Try porting the remaining code now. To begin with, here is
+how both the versions of the constructor code looks:
+
+C++ version
+------------
+
+.. literalinclude:: bookwindow.cpp
+ :language: c++
+ :linenos:
+ :lines: 47-115
+
+Python version
+---------------
+
+.. literalinclude:: bookwindow.py
+ :language: python
+ :linenos:
+ :lines: 53-116
+
+.. note:: The Python version of the ``BookWindow`` class
+ definition inherits from both ``QMainWindow`` and
+ ``Ui_BookWindow``, which is defined in the
+ ``ui_bookwindow.py`` file that you generated earlier.
+
+Here is how the rest of the code looks like:
+
+C++ version
+------------
+
+.. literalinclude:: bookwindow.cpp
+ :language: c++
+ :linenos:
+ :lines: 115-
+
+Python version
+---------------
+
+.. literalinclude:: bookwindow.py
+ :language: python
+ :linenos:
+ :lines: 117-
+
+Now that all the necessary pieces are in place, try to put
+them together in ``main.py``.
+
+.. literalinclude:: main.py
+ :language: python
+ :linenos:
+ :lines: 40-
+
+Try running this to see if you get the following output:
+
+.. image:: images/chapter3-books.png
+ :alt: BookWindow with a QTableView and a few input fields
+
+Now, if you look back at :doc:`chapter2 <../chapter2/chapter2>`,
+you'll notice that the ``bookdelegate.py`` loads the
+``star.png`` from the filesytem. Instead, you could add it
+to a ``qrc`` file, and load from it. The later approach is
+rececommended if your application is targeted for
+different platforms, as most of the popular platforms
+employ stricter file access policy these days.
+
+To add the ``star.png`` to a ``.qrc``, create a file called
+``books.qrc`` and the following XML content to it:
+
+.. literalinclude:: books.qrc
+ :linenos:
+
+This is a simple XML file defining a list all resources that
+your application needs. In this case, it is the ``star.png``
+image only.
+
+Now, run the ``pyside2-rcc`` tool on the ``books.qrc`` file
+to generate ``rc_books.py``.
+
+.. code-block::
+
+ pyside2-rcc books.qrc > rc_books.py
+
+Once you have the Python script generated, make the
+following changes to ``bookdelegate.py`` and ``main.py``:
+
+.. literalinclude:: bookdelegate.py
+ :diff: ../chapter2/bookdelegate.py
+
+.. literalinclude:: main.py
+ :diff: main-old.py
+
+Although there will be no noticeable difference in the UI
+after these changes, using a ``.qrc`` is a better approach.
+
+Now that you have successfully ported the SQL Books example,
+you know how easy it is. Try porting another C++ application.
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/createdb.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/createdb.py
new file mode 100644
index 000000000..8fb20cda1
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/createdb.py
@@ -0,0 +1,131 @@
+#############################################################################
+##
+## 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$
+##
+#############################################################################
+
+from PySide2.QtSql import QSqlDatabase, QSqlError, QSqlQuery
+from datetime import date
+
+
+def add_book(q, title, year, authorId, genreId, rating):
+ q.addBindValue(title)
+ q.addBindValue(year)
+ q.addBindValue(authorId)
+ q.addBindValue(genreId)
+ q.addBindValue(rating)
+ q.exec_()
+
+
+def add_genre(q, name):
+ q.addBindValue(name)
+ q.exec_()
+ return q.lastInsertId()
+
+
+def add_author(q, name, birthdate):
+ q.addBindValue(name)
+ q.addBindValue(str(birthdate))
+ q.exec_()
+ return q.lastInsertId()
+
+BOOKS_SQL = """
+ create table books(id integer primary key, title varchar, author integer,
+ genre integer, year integer, rating integer)
+ """
+AUTHORS_SQL = """
+ create table authors(id integer primary key, name varchar, birthdate text)
+ """
+GENRES_SQL = """
+ create table genres(id integer primary key, name varchar)
+ """
+INSERT_AUTHOR_SQL = """
+ insert into authors(name, birthdate) values(?, ?)
+ """
+INSERT_GENRE_SQL = """
+ insert into genres(name) values(?)
+ """
+INSERT_BOOK_SQL = """
+ insert into books(title, year, author, genre, rating)
+ values(?, ?, ?, ?, ?)
+ """
+
+def init_db():
+ """
+ init_db()
+ Initializes the database.
+ If tables "books" and "authors" are already in the database, do nothing.
+ Return value: None or raises ValueError
+ The error value is the QtSql error instance.
+ """
+ def check(func, *args):
+ if not func(*args):
+ raise ValueError(func.__self__.lastError())
+ db = QSqlDatabase.addDatabase("QSQLITE")
+ db.setDatabaseName(":memory:")
+
+ check(db.open)
+
+ q = QSqlQuery()
+ check(q.exec_, BOOKS_SQL)
+ check(q.exec_, AUTHORS_SQL)
+ check(q.exec_, GENRES_SQL)
+ check(q.prepare, INSERT_AUTHOR_SQL)
+
+ asimovId = add_author(q, "Isaac Asimov", date(1920, 2, 1))
+ greeneId = add_author(q, "Graham Greene", date(1904, 10, 2))
+ pratchettId = add_author(q, "Terry Pratchett", date(1948, 4, 28))
+
+ check(q.prepare,INSERT_GENRE_SQL)
+ sfiction = add_genre(q, "Science Fiction")
+ fiction = add_genre(q, "Fiction")
+ fantasy = add_genre(q, "Fantasy")
+
+ check(q.prepare,INSERT_BOOK_SQL)
+ add_book(q, "Foundation", 1951, asimovId, sfiction, 3)
+ add_book(q, "Foundation and Empire", 1952, asimovId, sfiction, 4)
+ add_book(q, "Second Foundation", 1953, asimovId, sfiction, 3)
+ add_book(q, "Foundation's Edge", 1982, asimovId, sfiction, 3)
+ add_book(q, "Foundation and Earth", 1986, asimovId, sfiction, 4)
+ add_book(q, "Prelude to Foundation", 1988, asimovId, sfiction, 3)
+ add_book(q, "Forward the Foundation", 1993, asimovId, sfiction, 3)
+ add_book(q, "The Power and the Glory", 1940, greeneId, fiction, 4)
+ add_book(q, "The Third Man", 1950, greeneId, fiction, 5)
+ add_book(q, "Our Man in Havana", 1958, greeneId, fiction, 4)
+ add_book(q, "Guards! Guards!", 1989, pratchettId, fantasy, 3)
+ add_book(q, "Night Watch", 2002, pratchettId, fantasy, 3)
+ add_book(q, "Going Postal", 2004, pratchettId, fantasy, 3)
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/images/chapter3-books.png b/sources/pyside2/doc/tutorials/portingguide/chapter3/images/chapter3-books.png
new file mode 100644
index 000000000..952cb14e8
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/images/chapter3-books.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/images/star.png b/sources/pyside2/doc/tutorials/portingguide/chapter3/images/star.png
new file mode 100644
index 000000000..87f4464bd
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/images/star.png
Binary files differ
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/main-old.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/main-old.py
new file mode 100644
index 000000000..4a8743c37
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/main-old.py
@@ -0,0 +1,52 @@
+#############################################################################
+##
+## 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
+from bookwindow import BookWindow
+
+if __name__ == "__main__":
+ app = QApplication([])
+
+ window = BookWindow()
+ window.resize(800, 600)
+ window.show()
+
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/main.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/main.py
new file mode 100644
index 000000000..50d2c0d6b
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/main.py
@@ -0,0 +1,53 @@
+#############################################################################
+##
+## 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
+from bookwindow import BookWindow
+import rc_books
+
+if __name__ == "__main__":
+ app = QApplication([])
+
+ window = BookWindow()
+ window.resize(800, 600)
+ window.show()
+
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/portingguide/hello_world_ex.py b/sources/pyside2/doc/tutorials/portingguide/hello_world_ex.py
new file mode 100644
index 000000000..c83dda55c
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/hello_world_ex.py
@@ -0,0 +1,76 @@
+#############################################################################
+##
+## 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
+import random
+
+from PySide2.QtWidgets import (QApplication, QLabel,
+ QPushButton, QVBoxLayout, QWidget)
+from PySide2.QtCore import Qt, Slot
+
+class MyWidget(QWidget):
+ def __init__(self):
+ super().__init__()
+
+ self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
+
+ self.button = QPushButton("Click me!")
+ self.text = QLabel("Hello World")
+ self.text.setAlignment(Qt.AlignCenter)
+
+ self.layout = QVBoxLayout()
+ self.layout.addWidget(self.text)
+ self.layout.addWidget(self.button)
+ self.setLayout(self.layout)
+
+ self.button.clicked.connect(self.magic)
+
+ @Slot()
+ def magic(self):
+ self.text.setText(random.choice(self.hello))
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+
+ widget = MyWidget()
+ widget.resize(800, 600)
+ widget.show()
+
+ sys.exit(app.exec_())
diff --git a/sources/pyside2/doc/tutorials/portingguide/index.rst b/sources/pyside2/doc/tutorials/portingguide/index.rst
new file mode 100644
index 000000000..aabf4b19f
--- /dev/null
+++ b/sources/pyside2/doc/tutorials/portingguide/index.rst
@@ -0,0 +1,196 @@
+Porting a C++ Application to Python
+*************************************
+
+Qt for Python lets you use Qt APIs in a Python application.
+So the next question is: What does it take to port an
+existing C++ application? Try porting a Qt C++ application
+to Python to understand this.
+
+Before you start, ensure that all the prerequisites for
+Qt for Python are met. See
+:doc:`Getting Started <../../gettingstarted>` for more
+information. In addition, familiarize yourself with the
+basic differences between Qt in C++ and in Python.
+
+Basic differences
+==================
+
+This section highlights some of the basic differences
+between C++ and Python, and how Qt differs between these
+two contexts.
+
+C++ vs Python
+--------------
+
+* In the interest of code reuse, both C++ and Python
+ provide ways for one file of code to use facilities
+ provided by another. In C++, this is done using the
+ ``#include`` directive to access the API definition of
+ the reused code. The Python equivalent is an ``import``
+ statement.
+* The constructor of a C++ class shares the name of its
+ class and automatically calls the constructor of any
+ base-classes (in a predefined order) before it runs.
+ In Python, the ``__init__()`` method is the constructor
+ of the class, and it can explicitly call base-class
+ constructors in any order.
+* C++ uses the keyword, ``this``, to implicitly refer to
+ the current object. In python, you need to explicitly
+ mention the current object as the first parameter
+ to each instance method of the class; it is conventionally
+ named ``self``.
+* And more importantly, forget about curly braces, {}, and
+ semi-colon, ;.
+* Precede variable definitions with the ``global`` keyword,
+ only if they need global scope.
+
+.. code:: python
+
+ var = None
+ def func(key, value = None):
+ """Does stuff with a key and an optional value.
+
+ If value is omitted or None, the value from func()'s
+ last call is reused.
+ """
+ global var
+ if value is None:
+ if var is None:
+ raise ValueError("Must pass a value on first call", key, value)
+ value = var
+ else:
+ var = value
+ doStuff(key, value)
+
+In this example, ``func()`` would treat ``var`` as a local
+name without the ``global`` statement. This would lead to
+a ``NameError`` in the ``value is None`` handling, on
+accessing ``var``. For more information about this, see
+`Python refernce documentation <python refdoc>`_.
+
+.. _python refdoc: https://docs.python.org/3/reference/simple_stmts.html#the-global-statement
+
+.. tip:: Python being an interpreted language, most often
+ the easiest way is to try your idea in the interperter.
+ You could call the ``help()`` function in the
+ interpreter on any built-in function or keyword in
+ Python. For example, a call to ``help(import)`` should
+ provide documentation about the ``import`` statment
+
+Last but not the least, try out a few examples to
+familiarize yourself with the Python coding style and
+follow the guidelines outlined in the
+`PEP8 - Style Guide <pep8>`_.
+
+.. _pep8: <https://www.python.org/dev/peps/pep-0008/#naming-conventions>
+
+.. code-block:: python
+
+ import sys
+
+ from PySide2.QtWidgets import QApplication, QLabel
+
+ app = QApplication(sys.argv)
+ label = QLabel("Hello World")
+ label.show()
+ sys.exit(app.exec_())
+
+.. note:: Qt provides classes that are meant to manage
+ the application-specific requirements depending on
+ whether the application is console-only
+ (QCoreApplication), GUI with QtWidgets (QApplication),
+ or GUI without QtWidgets (QGuiApplication). These
+ classes load necessary plugins, such as the GUI
+ libraries required by an application. In this case, it is
+ QApplication that is initialized first as the application
+ has a GUI with QtWidgets.
+
+Qt in the C++ and Python context
+---------------------------------
+
+Qt behaves the same irrespective of whether it is used
+in a C++ or a Python application. Considering that C++
+and Python use different language semantics, some
+differences between the two variants of Qt are inevitable.
+Here are a few important ones that you must be aware of:
+
+* **Qt Properties**: ``Q_PROPERTY`` macros are used in C++ to add a
+ public member variable with getter and setter functions. Python's
+ alternative for this is the ``@property`` decorator before the
+ getter and setter function definitions.
+* **Qt Signals and Slots**: Qt offers a unique callback mechanism,
+ where a signal is emitted to notify the occurrence of an event, so
+ that slots connected to this signal can react to it. In C++,
+ the class definition must define the slots under the
+ ``public Q_SLOTS:`` and signals under ``Q_SIGNALS:``
+ access specifier. You connect these two using one of the
+ several variants of the QObject::connect() function. Python's
+ equivalent for this is the `@Slot`` decorator just before the
+ function definition. This is necessary to register the slots
+ with the QtMetaObject.
+* **QString, QVariant, and other types**
+
+ - Qt for Python does not provide access to QString and
+ QVariant. You must use Python's native types instead.
+ - QChar and QStringRef are represented as Python strings,
+ and QStringList is converted to a list of strings.
+ - QDate, QDateTime, QTime, and QUrl's __hash__() methods
+ return a string representation so that identical dates
+ (and identical date/times or times or URLs) have
+ identical hash values.
+ - QTextStream's bin(), hex(), and oct() functions are
+ renamed to bin_(), hex_(), and oct_() respectively. This
+ should avoid name conflicts with Python's built-in
+ functions.
+
+* **QByteArray**: A QByteArray is treated as a list of
+ bytes without encoding. The equivalent type in Python
+ varies; Python 2 uses "str" type, whereas Python 3 uses
+ "bytes". To avoid confusion, a QString is represented as
+ an encoded human readable string, which means it is
+ a "unicode" object in Python 2, and a "str" in Python 3.
+
+Here is the improved version of the Hello World example,
+demonstrating some of these differences:
+
+.. literalinclude:: hello_world_ex.py
+ :linenos:
+ :lines: 40-
+
+.. note:: The ``if`` block is just a good practice when
+ developing a Python application. It lets the Python file
+ behave differently depending on whether it is imported
+ as a module in another file or run directly. The
+ ``__name__`` variable will have different values in
+ these two scenarios. It is ``__main__`` when the file is
+ run directly, and the module's file name
+ (``hello_world_ex`` in this case) when imported as a
+ module. In the later case, everything defined in the
+ module except the ``if`` block is available to the
+ importing file.
+
+Notice that the QPushButton's ``clicked`` signal is
+connected to the ``magic`` function to randomly change the
+QLabel's ``text`` property. The `@Slot`` decorator marks
+the methods that are slots and informs the QtMetaObject about
+them.
+
+Porting a Qt C++ example
+=========================
+
+Qt offers several C++ examples to showcase its features and help
+beginners learn. You can try porting one of these C++ examples to
+Python. The
+`books SQL example <https://code.qt.io/cgit/qt/qtbase.git/tree/examples/sql/books>`_
+is a good starting point as it does not require you to write UI-specific code in
+Python, but can use its ``.ui`` file instead.
+
+The following chapters guides you through the porting process:
+
+.. toctree::
+ :glob:
+ :titlesonly:
+
+ chapter1/chapter1
+ chapter2/chapter2
+ chapter3/chapter3
diff --git a/sources/pyside2/doc/tutorials/qmlintegration/qmlintegration.rst b/sources/pyside2/doc/tutorials/qmlintegration/qmlintegration.rst
index 36a12381d..62336ee81 100644
--- a/sources/pyside2/doc/tutorials/qmlintegration/qmlintegration.rst
+++ b/sources/pyside2/doc/tutorials/qmlintegration/qmlintegration.rst
@@ -31,7 +31,7 @@ application and PySide2 integration:
.. literalinclude:: main.py
:linenos:
:lines: 98-108
- :emphasize-lines: 103,107
+ :emphasize-lines: 6,9
Notice that we specify the name of the context property, **con**,
and also we explicitly load our QML file.
@@ -47,7 +47,7 @@ application and PySide2 integration:
.. literalinclude:: view.qml
:linenos:
:lines: 85-93
- :emphasize-lines: 89-91
+ :emphasize-lines: 5-7
The properties *Italic*, *Bold*, and *Underline* are mutually
exclusive, this means only one can be active at any time.
@@ -64,7 +64,7 @@ application and PySide2 integration:
.. literalinclude:: main.py
:linenos:
:lines: 79-84
- :emphasize-lines: 82,84
+ :emphasize-lines: 4,6
Returning *True* or *False* allows you to activate and deactivate
the properties of the QML UI elements.
@@ -98,14 +98,14 @@ application and PySide2 integration:
.. literalinclude:: main.py
:linenos:
:lines: 41-48
- :emphasize-lines: 48
+ :emphasize-lines: 8
- You can read more about this configuration file
- `here <https://doc.qt.io/qt-5/qtquickcontrols2-configuration.html>`_.
+ You can read more about this configuration file
+ `here <https://doc.qt.io/qt-5/qtquickcontrols2-configuration.html>`_.
The final look of your application will be:
.. image:: textproperties_material.png
-You can download `view.qml <view.qml>`_ and `main.py <main.py>`_
-to try this example.
+You can :download:`view.qml <view.qml>` and
+:download:`main.py <main.py>` to try this example.