aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/doc/tutorials
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/doc/tutorials')
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/clickablebutton.rst90
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/dialog.rst145
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/icons.pngbin0 -> 3202 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/icons/forward.pngbin0 -> 1113 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/icons/pause.pngbin0 -> 1001 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/icons/play.pngbin0 -> 970 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/icons/previous.pngbin0 -> 1050 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/icons/stop.pngbin0 -> 1064 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/player-new.pngbin0 -> 7818 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/player.pngbin0 -> 5835 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/qml.rst67
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/qrcfiles.rst169
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/style.qss23
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/uifiles.rst187
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/widgets.rst45
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/widgetstyling-no.pngbin0 -> 26444 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-no.pngbin0 -> 3834 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-yes.pngbin0 -> 4743 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/widgetstyling-yes.pngbin0 -> 32311 bytes
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/widgetstyling.py95
-rw-r--r--sources/pyside6/doc/tutorials/basictutorial/widgetstyling.rst169
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/add_chart.rst20
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst32
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst70
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/all_hour.csv8
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize.tar.bz2bin0 -> 6086 bytes
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize1/main.py55
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize2/main.py78
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main.py88
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py69
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main.py90
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py80
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py69
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py88
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main.py90
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py91
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py69
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py88
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main.py92
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.py131
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py70
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py88
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/filter_data.rst29
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/images/QMainWindow-layout.pngbin0 -> 17272 bytes
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/images/datavisualization_app.pngbin0 -> 40458 bytes
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/index.rst26
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst25
-rw-r--r--sources/pyside6/doc/tutorials/datavisualize/read_data.rst41
-rw-r--r--sources/pyside6/doc/tutorials/expenses/expenses.rst314
-rw-r--r--sources/pyside6/doc/tutorials/expenses/expenses_tool.pngbin0 -> 47826 bytes
-rw-r--r--sources/pyside6/doc/tutorials/expenses/main.py207
-rw-r--r--sources/pyside6/doc/tutorials/expenses/main_snake_prop.py210
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/01-expenses.py59
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/02-expenses.py71
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/03-expenses.py78
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/04-expenses.py90
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/05-expenses.py118
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/06-expenses.py138
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/07-expenses.py165
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/08-expenses.py178
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/09-expenses.py185
-rw-r--r--sources/pyside6/doc/tutorials/expenses/steps/10-expenses.py207
-rw-r--r--sources/pyside6/doc/tutorials/index.rst42
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter1/chapter1.rst89
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter1/createdb.py131
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter1/images/chapter1_books.pngbin0 -> 25391 bytes
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter1/initdb.h160
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter1/main.py59
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.cpp143
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.h83
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.py134
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter2/chapter2.rst93
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter2/createdb.py131
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter2/images/chapter2_books.pngbin0 -> 34658 bytes
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.pngbin0 -> 44122 bytes
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter2/images/star.pngbin0 -> 782 bytes
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter2/main.py63
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/bookdelegate-old.py134
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/bookdelegate.py133
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/books.qrc5
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.cpp171
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.py138
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.ui149
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/chapter3.rst121
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/createdb.py131
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/images/chapter3-books.pngbin0 -> 34624 bytes
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/images/star.pngbin0 -> 782 bytes
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/main-old.py52
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/chapter3/main.py53
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/hello_world_ex.py76
-rw-r--r--sources/pyside6/doc/tutorials/portingguide/index.rst194
-rw-r--r--sources/pyside6/doc/tutorials/qmlapp/logo.pngbin0 -> 6208 bytes
-rw-r--r--sources/pyside6/doc/tutorials/qmlapp/main.py82
-rw-r--r--sources/pyside6/doc/tutorials/qmlapp/newpyproject.pngbin0 -> 16091 bytes
-rw-r--r--sources/pyside6/doc/tutorials/qmlapp/projectsmode.pngbin0 -> 8848 bytes
-rw-r--r--sources/pyside6/doc/tutorials/qmlapp/pyprojname.pngbin0 -> 8068 bytes
-rw-r--r--sources/pyside6/doc/tutorials/qmlapp/pyprojxplor.pngbin0 -> 10062 bytes
-rw-r--r--sources/pyside6/doc/tutorials/qmlapp/qmlapplication.pngbin0 -> 10950 bytes
-rw-r--r--sources/pyside6/doc/tutorials/qmlapp/qmlapplication.rst132
-rw-r--r--sources/pyside6/doc/tutorials/qmlapp/view.qml102
-rw-r--r--sources/pyside6/doc/tutorials/qmlintegration/main.py113
-rw-r--r--sources/pyside6/doc/tutorials/qmlintegration/qmlintegration.rst111
-rw-r--r--sources/pyside6/doc/tutorials/qmlintegration/qtquickcontrols2.conf10
-rw-r--r--sources/pyside6/doc/tutorials/qmlintegration/style.qrc5
-rw-r--r--sources/pyside6/doc/tutorials/qmlintegration/textproperties_default.pngbin0 -> 19347 bytes
-rw-r--r--sources/pyside6/doc/tutorials/qmlintegration/textproperties_material.pngbin0 -> 21170 bytes
-rw-r--r--sources/pyside6/doc/tutorials/qmlintegration/view.qml183
-rw-r--r--sources/pyside6/doc/tutorials/qmlsqlintegration/chat.qml127
-rw-r--r--sources/pyside6/doc/tutorials/qmlsqlintegration/example_list_view.pngbin0 -> 6954 bytes
-rw-r--r--sources/pyside6/doc/tutorials/qmlsqlintegration/main.py85
-rw-r--r--sources/pyside6/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst225
-rw-r--r--sources/pyside6/doc/tutorials/qmlsqlintegration/sqlDialog.py146
112 files changed, 8403 insertions, 0 deletions
diff --git a/sources/pyside6/doc/tutorials/basictutorial/clickablebutton.rst b/sources/pyside6/doc/tutorials/basictutorial/clickablebutton.rst
new file mode 100644
index 000000000..bd45f1f64
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/clickablebutton.rst
@@ -0,0 +1,90 @@
+A Simple Button Tutorial
+************************
+
+In this tutorial, we'll show you how to handle **signals and slots**
+using Qt for Python. **Signals and slots** is a Qt feature that lets
+your graphical widgets communicate with other graphical widgets or
+your python code. Our application creates a button that logs the
+`Button clicked, Hello!` message to the python console each time you
+click it.
+
+Let's start by importing the necessary PySide6 classes and python
+`sys` module:
+::
+
+ import sys
+ from PySide6.QtWidgets import QApplication, QPushButton
+ from PySide6.QtCore import Slot
+
+Let's also create a python function that logs the message to the
+console:
+::
+
+ # Greetings
+ @Slot()
+ def say_hello():
+ print("Button clicked, Hello!")
+
+.. note:: The `@Slot()` is a decorator that identifies a function as
+ a slot. It is not important to understand why for now,
+ but use it always to avoid unexpected behavior.
+
+Now, as mentioned in previous examples you must create the
+`QApplication` to run your PySide6 code:
+::
+
+ # Create the Qt Application
+ app = QApplication(sys.argv)
+
+Let's create the clickable button, which is a `QPushButton` instance.
+To label the button, we pass a python string to the constructor:
+::
+
+ # Create a button
+ button = QPushButton("Click me")
+
+Before we show the button, we must connect it to the `say_hello()`
+function that we defined earlier. There are two ways of doing this;
+using the old style or the new style, which is more pythonic. Let's
+use the new style in this case. You can find more information about
+both these styles in the
+`Signals and Slots in PySide6 <https://wiki.qt.io/Qt_for_Python_Signals_and_Slots>`_
+wiki page.
+
+The `QPushButton` has a predefined signal called **clicked**, which
+is triggered every time the button is clicked. We'll connect this
+signal to the `say_hello()` function:
+::
+
+ # Connect the button to the function
+ button.clicked.connect(say_hello)
+
+Finally, we show the button and start the Qt main loop:
+::
+
+ # Show the button
+ button.show()
+ # Run the main Qt loop
+ app.exec_()
+
+Here is the complete code for this example:
+::
+
+ #!/usr/bin/python
+
+ import sys
+ from PySide6.QtWidgets import QApplication, QPushButton
+ from PySide6.QtCore import Slot
+
+ @Slot()
+ def say_hello():
+ print("Button clicked, Hello!")
+
+ # Create the Qt Application
+ app = QApplication(sys.argv)
+ # Create a button, connect it and show it
+ button = QPushButton("Click me")
+ button.clicked.connect(say_hello)
+ button.show()
+ # Run the main Qt loop
+ app.exec_()
diff --git a/sources/pyside6/doc/tutorials/basictutorial/dialog.rst b/sources/pyside6/doc/tutorials/basictutorial/dialog.rst
new file mode 100644
index 000000000..0ced571db
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/dialog.rst
@@ -0,0 +1,145 @@
+Creating a Simple PySide6 Dialog Application
+*********************************************
+
+This tutorial shows how to build a simple dialog with some
+basic widgets. The idea is to let users provide their name
+in a `QLineEdit`, and the dialog greets them on click of a
+`QPushButton`.
+
+Let us just start with a simple stub that creates and shows
+a dialog. This stub is updated during the course of this
+tutorial, but you can use this stub as is if you need to:
+::
+
+ import sys
+ from PySide6.QtWidgets import QApplication, QDialog, QLineEdit, QPushButton
+
+ class Form(QDialog):
+
+ def __init__(self, parent=None):
+ super(Form, self).__init__(parent)
+ self.setWindowTitle("My Form")
+
+
+ if __name__ == '__main__':
+ # Create the Qt Application
+ app = QApplication(sys.argv)
+ # Create and show the form
+ form = Form()
+ form.show()
+ # Run the main Qt loop
+ sys.exit(app.exec_())
+
+The imports aren't new to you, the same for the creation of the
+`QApplication` and the execution of the Qt main loop.
+The only novelty here is the **class definition**.
+
+You can create any class that subclasses PySide6 widgets.
+In this case, we are subclassing `QDialog` to define a custom
+dialog, which we name as **Form**. We have also implemented the
+`init()` method that calls the `QDialog`'s init method with the
+parent widget, if any. Also, the new `setWindowTitle()` method
+just sets the title of the dialog window. In `main()`, you can see
+that we are creating a *Form object* and showing it to the world.
+
+Create the Widgets
+===================
+
+We are going to create two widgets: a `QLineEdit` where users can
+enter their name, and a `QPushButton` that prints the contents of
+the `QLineEdit`.
+So, let's add the following code to the `init()` method of our Form:
+::
+
+ # Create widgets
+ self.edit = QLineEdit("Write my name here..")
+ self.button = QPushButton("Show Greetings")
+
+It's obvious from the code that both widgets will show the corresponding
+texts.
+
+Create a layout to organize the Widgets
+========================================
+
+Qt comes with layout-support that helps you organize the widgets
+in your application. In this case, let's use `QVBoxLayout` to lay out
+the widgets vertically. Add the following code to the `init()` method,
+after creating the widgets:
+::
+
+ # Create layout and add widgets
+ layout = QVBoxLayout()
+ layout.addWidget(self.edit)
+ layout.addWidget(self.button)
+ # Set dialog layout
+ self.setLayout(layout)
+
+So, we create the layout, add the widgets with `addWidget()`,
+and finally we say that our **Form** will have our `QVBoxLayout`
+as its layout.
+
+Create the function to greet and connect the Button
+====================================================
+
+Finally, we just have to add a function to our custom **Form**
+and *connect* our button to it. Our function will be a part of
+the Form, so you have to add it after the `init()` function:
+::
+
+ # Greets the user
+ def greetings(self):
+ print ("Hello {}".format(self.edit.text()))
+
+Our function just prints the contents of the `QLineEdit` to the
+python console. We have access to the text by means of the
+`QLineEdit.text()` method.
+
+Now that we have everything, we just need to *connect* the
+`QPushButton` to the `Form.greetings()` method. To do so, add the
+following line to the `init()` method:
+::
+
+ # Add button signal to greetings slot
+ self.button.clicked.connect(self.greetings)
+
+Once executed, you can enter your name in the `QLineEdit` and watch
+the console for greetings.
+
+Complete code
+=============
+
+Here is the complete code for this tutorial:
+::
+
+ import sys
+ from PySide6.QtWidgets import (QLineEdit, QPushButton, QApplication,
+ QVBoxLayout, QDialog)
+
+ class Form(QDialog):
+
+ def __init__(self, parent=None):
+ super(Form, self).__init__(parent)
+ # Create widgets
+ self.edit = QLineEdit("Write my name here")
+ self.button = QPushButton("Show Greetings")
+ # Create layout and add widgets
+ layout = QVBoxLayout()
+ layout.addWidget(self.edit)
+ layout.addWidget(self.button)
+ # Set dialog layout
+ self.setLayout(layout)
+ # Add button signal to greetings slot
+ self.button.clicked.connect(self.greetings)
+
+ # Greets the user
+ def greetings(self):
+ print ("Hello %s" % self.edit.text())
+
+ if __name__ == '__main__':
+ # Create the Qt Application
+ app = QApplication(sys.argv)
+ # Create and show the form
+ form = Form()
+ form.show()
+ # Run the main Qt loop
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons.png b/sources/pyside6/doc/tutorials/basictutorial/icons.png
new file mode 100644
index 000000000..0bcfd7d77
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/icons.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/forward.png b/sources/pyside6/doc/tutorials/basictutorial/icons/forward.png
new file mode 100644
index 000000000..c7a532dfe
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/icons/forward.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/pause.png b/sources/pyside6/doc/tutorials/basictutorial/icons/pause.png
new file mode 100644
index 000000000..d0beadb43
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/icons/pause.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/play.png b/sources/pyside6/doc/tutorials/basictutorial/icons/play.png
new file mode 100644
index 000000000..345685337
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/icons/play.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/previous.png b/sources/pyside6/doc/tutorials/basictutorial/icons/previous.png
new file mode 100644
index 000000000..979f18565
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/icons/previous.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/stop.png b/sources/pyside6/doc/tutorials/basictutorial/icons/stop.png
new file mode 100644
index 000000000..1e88ded3a
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/icons/stop.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/player-new.png b/sources/pyside6/doc/tutorials/basictutorial/player-new.png
new file mode 100644
index 000000000..e1f660e5f
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/player-new.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/player.png b/sources/pyside6/doc/tutorials/basictutorial/player.png
new file mode 100644
index 000000000..3060a990d
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/player.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/qml.rst b/sources/pyside6/doc/tutorials/basictutorial/qml.rst
new file mode 100644
index 000000000..c972daa00
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/qml.rst
@@ -0,0 +1,67 @@
+Your First Application Using PySide6 and QtQuick/QML
+*****************************************************
+
+QML is a declarative language that lets you develop applications
+faster than with traditional languages. It is ideal for designing the
+UI of your application because of its declarative nature. In QML, a
+user interface is specified as a tree of objects with properties. In
+this tutorial, we will show how to make a simple "Hello World"
+application with PySide6 and QML.
+
+A PySide6/QML application consists, at least, of two different files -
+a file with the QML description of the user interface, and a python file
+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 {
+ width: 200
+ height: 200
+ color: "green"
+
+ Text {
+ text: "Hello World"
+ anchors.centerIn: parent
+ }
+ }
+
+We start by importing `QtQuick 2.0`, which is a QML module.
+
+The rest of the QML code is pretty straightforward for those who
+have previously used HTML or XML files. Basically, we are creating
+a green rectangle with the size `200*200`, and adding a Text element
+that reads "Hello World". The code `anchors.centerIn: parent` makes
+the text appear centered in relation to its immediate parent, which
+is the Rectangle in this case.
+
+Now, let's see how the code looks on the PySide6.
+Let's call it `main.py`:
+
+.. code-block:: python
+
+ from PySide6.QtWidgets import QApplication
+ from PySide6.QtQuick import QQuickView
+ from PySide6.QtCore import QUrl
+
+ app = QApplication([])
+ view = QQuickView()
+ url = QUrl("view.qml")
+
+ view.setSource(url)
+ view.show()
+ app.exec_()
+
+If you are already familiar with PySide6 and have followed our
+tutorials, you have already seen much of this code.
+The only novelties are that you must `import QtQuick` and set the
+source of the `QQuickView` object to the URL of your QML file.
+Then, as any Qt widget, you call `QQuickView.show()`.
+
+.. note:: If you are programming for desktop, you should consider
+ adding `view.setResizeMode(QQuickView.SizeRootObjectToView)`
+ before showing the view.
diff --git a/sources/pyside6/doc/tutorials/basictutorial/qrcfiles.rst b/sources/pyside6/doc/tutorials/basictutorial/qrcfiles.rst
new file mode 100644
index 000000000..5ac560a8f
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/qrcfiles.rst
@@ -0,0 +1,169 @@
+Using `.qrc` Files (`pyside6-rcc`)
+**********************************
+
+The `Qt Resource System`_ is a mechanism for storing binary files
+in an application.
+
+The most common uses are for custom images, icons, fonts, among others.
+
+In this tutorial you will learn how to load custom images as button icons.
+
+For inspiration, we will try to adapt the multimedia player example
+from Qt.
+
+As you can see on the following image, the `QPushButton` that are used
+for the media actions (play, pause, stop, and so on) are using the
+default icons meant for such actions.
+
+.. image:: player.png
+ :alt: Multimedia Player Qt Example
+
+You could make the application more attractive by designing the icons,
+but in case you don't want to design them, `download the following set`_
+and use them.
+
+.. image:: icons.png
+ :alt: New Multimedia icons
+
+You can find more information about the `rcc` command, and `.qrc` file
+format, and the resource system in general in the `Qt Resource System`_
+site.
+
+.. _`download the following set`: icons/
+
+
+The `.qrc` file
+================
+
+Before running any command, add information about the resources to a `.qrc`
+file.
+In the following example, notice how the resources are listed in `icons.qrc`
+
+::
+
+ </ui>
+ <!DOCTYPE RCC><RCC version="1.0">
+ <qresource>
+ <file>icons/play.png</file>
+ <file>icons/pause.png</file>
+ <file>icons/stop.png</file>
+ <file>icons/previous.png</file>
+ <file>icons/forward.png</file>
+ </qresource>
+ </RCC>
+
+
+Generating a Python file
+=========================
+
+Now that the `icons.qrc` file is ready, use the `pyside6-rcc` tool to generate
+a Python class containing the binary information about the resources
+
+To do this, we need to run::
+
+ pyside6-rcc icons.rc -o rc_icons.py
+
+The `-o` option lets you specify the output filename,
+which is `rc_icons.py` in this case.
+
+To use the generated file, add the following import at the top of your main Python file::
+
+ import rc_icons
+
+
+Changes in the code
+===================
+
+As you are modifying an existing example, you need to modify the following
+lines:
+
+.. code-block:: python
+
+ from PySide6.QtGui import QIcon, QKeySequence
+ playIcon = self.style().standardIcon(QStyle.SP_MediaPlay)
+ previousIcon = self.style().standardIcon(QStyle.SP_MediaSkipBackward)
+ pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause)
+ nextIcon = self.style().standardIcon(QStyle.SP_MediaSkipForward)
+ stopIcon = self.style().standardIcon(QStyle.SP_MediaStop)
+
+and replace them with the following:
+
+.. code-block:: python
+
+ from PySide6.QtGui import QIcon, QKeySequence, QPixmap
+ playIcon = QIcon(QPixmap(":/icons/play.png"))
+ previousIcon = QIcon(QPixmap(":/icons/previous.png"))
+ pauseIcon = QIcon(QPixmap(":/icons/pause.png"))
+ nextIcon = QIcon(QPixmap(":/icons/forward.png"))
+ stopIcon = QIcon(QPixmap(":/icons/stop.png"))
+
+This ensures that the new icons are used instead of the default ones provided
+by the application theme.
+Notice that the lines are not consecutive, but are in different parts
+of the file.
+
+After all your imports, add the following
+
+.. code-block:: python
+
+ import rc_icons
+
+Now, the constructor of your class should look like this:
+
+.. code-block:: python
+
+ def __init__(self):
+ super(MainWindow, self).__init__()
+
+ self.playlist = QMediaPlaylist()
+ self.player = QMediaPlayer()
+
+ toolBar = QToolBar()
+ self.addToolBar(toolBar)
+
+ fileMenu = self.menuBar().addMenu("&File")
+ openAction = QAction(QIcon.fromTheme("document-open"),
+ "&Open...", self, shortcut=QKeySequence.Open,
+ triggered=self.open)
+ fileMenu.addAction(openAction)
+ exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit",
+ self, shortcut="Ctrl+Q", triggered=self.close)
+ fileMenu.addAction(exitAction)
+
+ playMenu = self.menuBar().addMenu("&Play")
+ playIcon = QIcon(QPixmap(":/icons/play.png"))
+ self.playAction = toolBar.addAction(playIcon, "Play")
+ self.playAction.triggered.connect(self.player.play)
+ playMenu.addAction(self.playAction)
+
+ previousIcon = QIcon(QPixmap(":/icons/previous.png"))
+ self.previousAction = toolBar.addAction(previousIcon, "Previous")
+ self.previousAction.triggered.connect(self.previousClicked)
+ playMenu.addAction(self.previousAction)
+
+ pauseIcon = QIcon(QPixmap(":/icons/pause.png"))
+ self.pauseAction = toolBar.addAction(pauseIcon, "Pause")
+ self.pauseAction.triggered.connect(self.player.pause)
+ playMenu.addAction(self.pauseAction)
+
+ nextIcon = QIcon(QPixmap(":/icons/forward.png"))
+ self.nextAction = toolBar.addAction(nextIcon, "Next")
+ self.nextAction.triggered.connect(self.playlist.next)
+ playMenu.addAction(self.nextAction)
+
+ stopIcon = QIcon(QPixmap(":/icons/stop.png"))
+ self.stopAction = toolBar.addAction(stopIcon, "Stop")
+ self.stopAction.triggered.connect(self.player.stop)
+ playMenu.addAction(self.stopAction)
+
+ # many lines were omitted
+
+Executing the example
+=====================
+
+Run the application by calling `python main.py` to checkout the new icon-set:
+
+.. image:: player-new.png
+ :alt: New Multimedia Player Qt Example
+
+.. _`Qt Resource System`: https://doc.qt.io/qt-5/resources.html
diff --git a/sources/pyside6/doc/tutorials/basictutorial/style.qss b/sources/pyside6/doc/tutorials/basictutorial/style.qss
new file mode 100644
index 000000000..b84b98f05
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/style.qss
@@ -0,0 +1,23 @@
+QListWidget {
+ color: #FFFFFF;
+ background-color: #33373B;
+}
+
+QListWidget::item {
+ height: 50px;
+}
+
+QListWidget::item:selected {
+ background-color: #2ABf9E;
+}
+
+QLabel {
+ background-color: #FFFFFF;
+ qproperty-alignment: AlignCenter;
+}
+
+QPushButton {
+ background-color: #2ABf9E;
+ padding: 20px;
+ font-size: 18px;
+}
diff --git a/sources/pyside6/doc/tutorials/basictutorial/uifiles.rst b/sources/pyside6/doc/tutorials/basictutorial/uifiles.rst
new file mode 100644
index 000000000..50bb2514c
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/uifiles.rst
@@ -0,0 +1,187 @@
+Using `.ui` files from Designer or QtCreator with `QUiLoader` and `pyside6-uic`
+*******************************************************************************
+
+This page describes the use of Qt Creator to create graphical
+interfaces for your Qt for Python project.
+You will need **Qt Creator** to design and modify your interface (UI file).
+
+If you don't know how to use Qt Creator, refer to the
+`Using Qt Designer <http://doc.qt.io/qtcreator/creator-using-qt-designer.html>`_
+documentation page.
+
+At Qt Creator, create a new Qt Design Form, choose "Main Window" for template.
+And save as `mainwindow.ui`.
+Add a `QPushButton` to the center of the centralwidget.
+
+Your file ``mainwindow.ui`` should look something like this:
+
+.. code-block:: xml
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralWidget">
+ <widget class="QPushButton" name="pushButton">
+ <property name="geometry">
+ <rect>
+ <x>110</x>
+ <y>80</y>
+ <width>201</width>
+ <height>81</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>PushButton</string>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QMenuBar" name="menuBar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>20</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QToolBar" name="mainToolBar">
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ <widget class="QStatusBar" name="statusBar"/>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+ </ui>
+
+Now we are ready to decide how to use the **UI file** from Python.
+
+Option A: 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 `pyside6-uic` tool.
+To use this tool, you need to run the following command on a console::
+
+ pyside6-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::
+
+ from ui_mainwindow import Ui_MainWindow
+
+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 PySide6.QtWidgets import QApplication, QMainWindow
+ from PySide6.QtCore import QFile
+ from ui_mainwindow import Ui_MainWindow
+
+ class MainWindow(QMainWindow):
+ def __init__(self):
+ super(MainWindow, self).__init__()
+ self.ui = Ui_MainWindow()
+ self.ui.setupUi(self)
+
+ if __name__ == "__main__":
+ app = QApplication(sys.argv)
+
+ window = MainWindow()
+ window.show()
+
+ sys.exit(app.exec_())
+
+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 `pyside6-uic` again every time you make changes
+ to the **UI file**.
+
+Option B: Loading it directly
+=============================
+
+To load the UI file directly, we will need a class from the **QtUiTools**
+module:
+
+.. code-block:: python
+
+ from PySide6.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)
+
+ loader = QUiLoader()
+ window = loader.load(ui_file)
+ window.show()
+
+The complete code of this example looks like this:
+
+.. code-block:: python
+
+ # File: main.py
+ import sys
+ from PySide6.QtUiTools import QUiLoader
+ from PySide6.QtWidgets import QApplication
+ from PySide6.QtCore import QFile, QIODevice
+
+ if __name__ == "__main__":
+ app = QApplication(sys.argv)
+
+ ui_file_name = "mainwindow.ui"
+ ui_file = QFile(ui_file_name)
+ if not ui_file.open(QIODevice.ReadOnly):
+ print("Cannot open {}: {}".format(ui_file_name, ui_file.errorString()))
+ sys.exit(-1)
+ loader = QUiLoader()
+ window = loader.load(ui_file)
+ ui_file.close()
+ if not window:
+ print(loader.errorString())
+ sys.exit(-1)
+ window.show()
+
+ sys.exit(app.exec_())
+
+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/pyside6/doc/tutorials/basictutorial/widgets.rst b/sources/pyside6/doc/tutorials/basictutorial/widgets.rst
new file mode 100644
index 000000000..d86ba2623
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/widgets.rst
@@ -0,0 +1,45 @@
+Your First QtWidgets Application
+*********************************
+
+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 PySide6:
+
+.. code-block:: python
+
+ import sys
+ from PySide6.QtWidgets import QApplication, QLabel
+
+ app = QApplication(sys.argv)
+ label = QLabel("Hello World!")
+ label.show()
+ app.exec_()
+
+
+For a widget application using PySide6, you must always start by
+importing the appropriate class from the `PySide6.QtWidgets` module.
+
+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>")
+
+.. note:: After creating the label, we call `show()` on it.
+
+Finally, we call `app.exec_()` to enter the Qt main loop and start
+to execute the Qt code. In reality, it is only here where the label
+is shown, but this can be ignored for now.
diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-no.png b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-no.png
new file mode 100644
index 000000000..c30dd621b
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-no.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-no.png b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-no.png
new file mode 100644
index 000000000..eb90e216d
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-no.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-yes.png b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-yes.png
new file mode 100644
index 000000000..5a714977e
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-yes.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-yes.png b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-yes.png
new file mode 100644
index 000000000..8ba49bd26
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-yes.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.py b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.py
new file mode 100644
index 000000000..94e44c5c5
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.py
@@ -0,0 +1,95 @@
+#############################################################################
+##
+## Copyright (C) 2020 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 PySide6.QtCore import Qt
+from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLabel, QListWidget,
+ QListWidgetItem, QPushButton, QVBoxLayout,
+ QWidget)
+
+_placeholder = """
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
+veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
+commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
+velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+occaecat cupidatat non proident, sunt in culpa qui officia deserunt
+mollit anim id est laborum
+"""
+
+
+class Widget(QWidget):
+ def __init__(self, parent=None):
+ super(Widget, self).__init__(parent)
+
+ menu_widget = QListWidget()
+ for i in range(10):
+ item = QListWidgetItem("Item {}".format(i))
+ item.setTextAlignment(Qt.AlignCenter)
+ menu_widget.addItem(item)
+
+ text_widget = QLabel(_placeholder)
+ button = QPushButton("Something")
+
+ content_layout = QVBoxLayout()
+ content_layout.addWidget(text_widget)
+ content_layout.addWidget(button)
+ main_widget = QWidget()
+ main_widget.setLayout(content_layout)
+
+ layout = QHBoxLayout()
+ layout.addWidget(menu_widget, 1)
+ layout.addWidget(main_widget, 4)
+ self.setLayout(layout)
+
+
+if __name__ == "__main__":
+ app = QApplication()
+
+ w = Widget()
+ w.show()
+
+ _style = None
+ with open("style.qss", "r") as f:
+ _style = f.read()
+ app.setStyleSheet(_style)
+
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.rst b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.rst
new file mode 100644
index 000000000..8deef1d7f
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.rst
@@ -0,0 +1,169 @@
+Widget Styling
+**************
+
+Qt Widgets application use a default theme depending on the platform.
+In some cases, there are system-wide configurations that modify the Qt theme,
+and applications are displayed differently.
+
+However, you can take care of your own widgets and provide a custom style
+to each component. As an example, look at the following simple snippet:
+
+.. code-block:: python
+
+ import sys
+ from PySide6.QtCore import Qt
+ from PySide6.QtWidgets import QApplication, QLabel
+
+ if __name__ == "__main__":
+ app = QApplication()
+ w = QLabel("This is a placeholder text")
+ w.setAlignment(Qt.AlignCenter)
+ w.show()
+ sys.exit(app.exec_())
+
+When you execute this code, you will see a simple `QLabel` aligned at the
+center, and with a placeholder text.
+
+.. image:: widgetstyling-simple-no.png
+ :alt: Simple Widget with no style
+
+You can style your application using the CSS-like syntax.
+For more information, see `Qt Style Sheets Reference`_.
+
+A `QLabel` can be styled differently by setting some of its CSS
+properties, such as `background-color` and `font-family`,
+so let's see how does the code look like with these changes:
+
+.. code-block:: python
+
+ import sys
+ from PySide6.QtCore import Qt
+ from PySide6.QtWidgets import QApplication, QLabel
+
+ if __name__ == "__main__":
+ app = QApplication()
+ w = QLabel("This is a placeholder text")
+ w.setAlignment(Qt.AlignCenter)
+ w.setStyleSheet("""
+ background-color: #262626;
+ color: #FFFFFF;
+ font-family: Titillium;
+ font-size: 18px;
+ """)
+ w.show()
+ sys.exit(app.exec_())
+
+Now when you run the code, notice that the `QLabel` looks different with your
+custom style:
+
+.. image:: widgetstyling-simple-yes.png
+ :alt: Simple Widget with Style
+
+
+.. note::
+
+ If you don't have the font `Titillium` installed, you can try with any
+ other you prefer.
+ Remember you can list your installed fonts using `QFontDatabase`,
+ specifically the `families()` method.
+
+
+Styling each UI element separately like you did in the previous snippet is a
+lot of work. The easier alternative for this is to use Qt Style Sheets,
+which is one or more `.qss` files defining the style for the UI elements in
+your application.
+
+More examples can be found in the `Qt Style Sheet Examples`_ documentation
+page.
+
+
+.. _`Qt Style Sheets Reference`: https://doc.qt.io/qt-5/stylesheet-reference.html
+.. _`Qt Style Sheet Examples`: https://doc.qt.io/qt-5/stylesheet-examples.html
+
+Qt Style Sheets
+===============
+
+.. warning::
+
+ Before starting modifying your application, keep in mind that you will be
+ responsible for all the graphical details of the application.
+ Altering margins, and sizes might end up looking strange or incorrect, so you
+ need to be careful when altering the style.
+ It's recommended to create a full new Qt style to cover all the possible
+ corner cases.
+
+A `qss` file is quite similar to a CSS file, but you need to specify the Widget
+component and optionally the name of the object::
+
+ QLabel {
+ background-color: red;
+ }
+
+ QLabel#title {
+ font-size: 20px;
+ }
+
+The first style defines a `background-color` for all `QLabel` objects in your
+application, whereas the later one styles the `title` object only.
+
+.. note::
+
+ You can set object names with the `setObjectName(str)` function to any Qt
+ object, for example: for a `label = QLabel("Test")`, you can write
+ `label.setObjectName("title")`
+
+
+Once you have a `qss` file for your application, you can apply it by reading
+the file and using the `QApplication.setStyleSheet(str)` function:
+
+.. code-block:: python
+
+ if __name__ == "__main__":
+ app = QApplication()
+
+ w = Widget()
+ w.show()
+
+ with open("style.qss", "r") as f:
+ _style = f.read()
+ app.setStyleSheet(_style)
+
+ sys.exit(app.exec_())
+
+Having a general `qss` file allows you to decouple the styling aspects of
+the code, without mixing it in the middle of the general functionality, and you
+can simply enable it or disable it.
+
+Look at this new example, with more widgets components:
+
+.. literalinclude:: widgetstyling.py
+ :linenos:
+ :lines: 59-81
+
+This displays a two column widget, with a `QListWidget` on the left and a
+`QLabel` and a `QPushButton` on the right. It looks like this when you run the
+code:
+
+.. image:: widgetstyling-no.png
+ :alt: Widget with no style
+
+If you add content to the previously described `style.qss` file, you can modify
+the look-n-feel of the previous example:
+
+.. literalinclude:: style.qss
+ :linenos:
+
+The style changes mainly the color of the different widgets, alter the
+alignment, and includes some spacing.
+You can also use state-based styling on the QListWidget *items* for example, to
+style them differently depending on whether they are *selected* or not.
+
+After applying all the styling alternatives you explored in this topic, notice
+that the `QLabel` example looks a lot different now.
+Try running the code to check its new look:
+
+.. image:: widgetstyling-yes.png
+ :alt: Widget with style
+
+You have the freedom to tune your style sheets and provide a really nice
+look-n-feel to all your applications.
diff --git a/sources/pyside6/doc/tutorials/datavisualize/add_chart.rst b/sources/pyside6/doc/tutorials/datavisualize/add_chart.rst
new file mode 100644
index 000000000..95b2092b3
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/add_chart.rst
@@ -0,0 +1,20 @@
+Chapter 5 - Add a chart view
+=============================
+
+A table is nice to present data, but a chart is even better. For this, you
+need the QtCharts module that provides many types of plots and options to
+graphically represent data.
+
+The placeholder for a plot is a QChartView, and inside that Widget you can
+place a QChart. As a first step, try including only this without any data to
+plot.
+
+Make the following highlighted changes to :code:`main_widget.py` from the
+previous chapter to add a QChartView:
+
+.. literalinclude:: datavisualize5/main_widget.py
+ :linenos:
+ :lines: 40-
+ :emphasize-lines: 2-3,6,22-36,48-50
+
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst b/sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst
new file mode 100644
index 000000000..a9ff38a30
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst
@@ -0,0 +1,32 @@
+Chapter 3 - Create an empty QMainWindow
+==========================================
+
+You can now think of presenting your data in a UI. A QMainWindow provides a
+convenient structure for GUI applications, such as a menu bar and status bar.
+The following image shows the layout that QMainWindow offers out-of-the box:
+
+.. image:: images/QMainWindow-layout.png
+ :alt: QMainWindow layout
+ :align: right
+
+In this case, let your application inherit from QMainWindow, and add the
+following UI elements:
+
+* A "File" menu to open a File dialog.
+* An "Exit" menu close the window.
+* A status message on the status bar when the application starts.
+
+In addition, you can define a fixed size for the window or adjust it based on
+the resolution you currently have. In the following snippet, you will see how
+window size is defined based on available screen width (80%) and height (70%).
+
+.. note:: You can achieve a similar structure using other Qt elements like
+ QMenuBar, QWidget, and QStatusBar. Refer the QMainWindow layout for
+ guidance.
+
+.. literalinclude:: datavisualize3/main_window.py
+ :language: python
+ :linenos:
+ :lines: 40-
+
+Try running the script to see what output you get with it.
diff --git a/sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst b/sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst
new file mode 100644
index 000000000..720918008
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst
@@ -0,0 +1,70 @@
+Chapter 4 - Add a QTableView
+=============================
+
+Now that you have a QMainWindow, you can include a centralWidget to your
+interface. Usually, a QWidget is used to display data in most data-driven
+applications. Use a table view to display your data.
+
+The first step is to add a horizontal layout with just a QTableView. You
+can create a QTableView object and place it inside a QHBoxLayout. Once the
+QWidget is properly built, pass the object to the QMainWindow as its central
+widget.
+
+Remember that a QTableView needs a model to display information. In this case,
+you can use a QAbstractTableModel instance.
+
+.. note:: You could also use the default item model that comes with a
+ QTableWidget instead. QTableWidget is a convenience class that reduces
+ your codebase considerably as you don't need to implement a data model.
+ However, it's less flexible than a QTableView, as QTableWidget cannot be
+ used with just any data. For more insight about Qt's model-view framework,
+ refer to the
+ `Model View Programming <http://doc.qt.io/qt-5/model-view-programming.html>`
+ documentation.
+
+Implementing the model for your QTableView, allows you to:
+- set the headers,
+- manipulate the formats of the cell values (remember we have UTC time and float
+numbers),
+- set style properties like text alignment,
+- and even set color properties for the cell or its content.
+
+To subclass the QAbstractTable, you must reimplement its virtual methods,
+rowCount(), columnCount(), and data(). This way, you can ensure that the data
+is handled properly. In addition, reimplement the headerData() method to
+provide the header information to the view.
+
+Here is a script that implements the CustomTableModel:
+
+.. literalinclude:: datavisualize4/table_model.py
+ :language: python
+ :linenos:
+ :lines: 40-
+
+Now, create a QWidget that has a QTableView, and connect it to your
+CustomTableModel.
+
+.. literalinclude:: datavisualize4/main_widget.py
+ :language: python
+ :linenos:
+ :emphasize-lines: 12-17
+ :lines: 40-
+
+You also need minor changes to the :code:`main_window.py` and
+:code:`main.py` from chapter 3 to include the Widget inside the
+MainWindow.
+
+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/pyside6/doc/tutorials/datavisualize/all_hour.csv b/sources/pyside6/doc/tutorials/datavisualize/all_hour.csv
new file mode 100644
index 000000000..400947c3c
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/all_hour.csv
@@ -0,0 +1,8 @@
+time,latitude,longitude,depth,mag,magType,nst,gap,dmin,rms,net,id,updated,place,type,horizontalError,depthError,magError,magNst,status,locationSource,magSource
+2019-01-10T12:11:24.810Z,34.1281662,-117.7754974,4.46,1.18,ml,22,69,0.04475,0.13,ci,ci38421072,2019-01-10T12:13:30.138Z,"3km NNW of La Verne, CA",earthquake,0.3,0.55,0.246,6,automatic,ci,ci
+2019-01-10T12:04:26.320Z,19.4433327,-155.6159973,0.72,1.79,md,22,99,0.04026,0.3,hv,hv70763571,2019-01-10T12:07:28.690Z,"26km E of Honaunau-Napoopoo, Hawaii",earthquake,0.6,1.79,0.28,6,automatic,hv,hv
+2019-01-10T11:57:48.980Z,33.3225,-116.3931667,4.84,0.62,ml,15,211,0.05776,0.16,ci,ci38421064,2019-01-10T12:01:29.166Z,"8km NNW of Borrego Springs, CA",earthquake,0.71,0.68,0.111,11,automatic,ci,ci
+2019-01-10T11:52:09.490Z,38.8356667,-122.8366699,1.28,2.74,md,25,77,0.003061,0.04,nc,nc73131566,2019-01-10T12:14:02.757Z,"10km NW of The Geysers, CA",earthquake,0.19,0.29,0.06,7,automatic,nc,nc
+2019-01-10T11:25:44.854Z,65.1082,-149.3701,20.6,2.1,ml,,,,1.02,ak,ak019gq2oer,2019-01-10T11:37:07.060Z,"60km NNW of North Nenana, Alaska",earthquake,,0.3,,,automatic,ak,ak
+2019-01-10T11:25:23.786Z,69.1518,-144.4977,10.4,3.7,ml,,,,0.74,ak,ak019gq2ndz,2019-01-10T11:47:11.284Z,"114km SSW of Kaktovik, Alaska",earthquake,,1.6,,,reviewed,ak,ak
+2019-01-10T11:16:11.761Z,61.3318,-150.0708,20.1,2.7,ml,,,,0.83,ak,ak019gq0ozj,2019-01-10T11:29:24.610Z,"15km NW of Anchorage, Alaska",earthquake,,0.4,,,automatic,ak,ak
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize.tar.bz2 b/sources/pyside6/doc/tutorials/datavisualize/datavisualize.tar.bz2
new file mode 100644
index 000000000..5fe12769a
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize.tar.bz2
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize1/main.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize1/main.py
new file mode 100644
index 000000000..8a8a1dfda
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize1/main.py
@@ -0,0 +1,55 @@
+#############################################################################
+##
+## 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 argparse
+import pandas as pd
+
+
+def read_data(fname):
+ return pd.read_csv(fname)
+
+
+if __name__ == "__main__":
+ options = argparse.ArgumentParser()
+ options.add_argument("-f", "--file", type=str, required=True)
+ args = options.parse_args()
+ data = read_data(args.file)
+ print(data)
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize2/main.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize2/main.py
new file mode 100644
index 000000000..864cb1a6c
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize2/main.py
@@ -0,0 +1,78 @@
+#############################################################################
+##
+## 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 argparse
+import pandas as pd
+
+from PySide6.QtCore import QDateTime, QTimeZone
+
+
+def transform_date(utc, timezone=None):
+ utc_fmt = "yyyy-MM-ddTHH:mm:ss.zzzZ"
+ new_date = QDateTime().fromString(utc, utc_fmt)
+ if timezone:
+ new_date.setTimeZone(timezone)
+ return new_date
+
+
+def read_data(fname):
+ # Read the CSV content
+ df = pd.read_csv(fname)
+
+ # Remove wrong magnitudes
+ df = df.drop(df[df.mag < 0].index)
+ magnitudes = df["mag"]
+
+ # My local timezone
+ timezone = QTimeZone(b"Europe/Berlin")
+
+ # Get timestamp transformed to our timezone
+ times = df["time"].apply(lambda x: transform_date(x, timezone))
+
+ return times, magnitudes
+
+
+if __name__ == "__main__":
+ options = argparse.ArgumentParser()
+ options.add_argument("-f", "--file", type=str, required=True)
+ args = options.parse_args()
+ data = read_data(args.file)
+ print(data)
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main.py
new file mode 100644
index 000000000..5dac460a5
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main.py
@@ -0,0 +1,88 @@
+#############################################################################
+##
+## 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 argparse
+import pandas as pd
+
+from PySide6.QtCore import QDateTime, QTimeZone
+from PySide6.QtWidgets import QApplication
+from main_window import MainWindow
+
+
+def transform_date(utc, timezone=None):
+ utc_fmt = "yyyy-MM-ddTHH:mm:ss.zzzZ"
+ new_date = QDateTime().fromString(utc, utc_fmt)
+ if timezone:
+ new_date.setTimeZone(timezone)
+ return new_date
+
+
+def read_data(fname):
+ # Read the CSV content
+ df = pd.read_csv(fname)
+
+ # Remove wrong magnitudes
+ df = df.drop(df[df.mag < 0].index)
+ magnitudes = df["mag"]
+
+ # My local timezone
+ timezone = QTimeZone(b"Europe/Berlin")
+
+ # Get timestamp transformed to our timezone
+ times = df["time"].apply(lambda x: transform_date(x, timezone))
+
+ return times, magnitudes
+
+
+if __name__ == "__main__":
+ options = argparse.ArgumentParser()
+ options.add_argument("-f", "--file", type=str, required=True)
+ args = options.parse_args()
+ data = read_data(args.file)
+
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ window = MainWindow()
+ window.show()
+
+ sys.exit(app.exec_())
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py
new file mode 100644
index 000000000..17fe0e141
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py
@@ -0,0 +1,69 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Slot
+from PySide6.QtGui import QAction, QKeySequence
+from PySide6.QtWidgets import QMainWindow
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Eartquakes information")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut(QKeySequence.Quit)
+ exit_action.triggered.connect(self.close)
+
+ self.file_menu.addAction(exit_action)
+
+ # Status Bar
+ self.status = self.statusBar()
+ self.status.showMessage("Data loaded and plotted")
+
+ # Window dimensions
+ geometry = qApp.desktop().availableGeometry(self)
+ self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main.py
new file mode 100644
index 000000000..ffb4d00b0
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main.py
@@ -0,0 +1,90 @@
+#############################################################################
+##
+## 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 argparse
+import pandas as pd
+
+from PySide6.QtCore import QDateTime, QTimeZone
+from PySide6.QtWidgets import QApplication
+from main_window import MainWindow
+from main_widget import Widget
+
+
+def transform_date(utc, timezone=None):
+ utc_fmt = "yyyy-MM-ddTHH:mm:ss.zzzZ"
+ new_date = QDateTime().fromString(utc, utc_fmt)
+ if timezone:
+ new_date.setTimeZone(timezone)
+ return new_date
+
+
+def read_data(fname):
+ # Read the CSV content
+ df = pd.read_csv(fname)
+
+ # Remove wrong magnitudes
+ df = df.drop(df[df.mag < 0].index)
+ magnitudes = df["mag"]
+
+ # My local timezone
+ timezone = QTimeZone(b"Europe/Berlin")
+
+ # Get timestamp transformed to our timezone
+ times = df["time"].apply(lambda x: transform_date(x, timezone))
+
+ return times, magnitudes
+
+
+if __name__ == "__main__":
+ options = argparse.ArgumentParser()
+ options.add_argument("-f", "--file", type=str, required=True)
+ args = options.parse_args()
+ data = read_data(args.file)
+
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ widget = Widget(data)
+ window = MainWindow(widget)
+ window.show()
+
+ sys.exit(app.exec_())
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py
new file mode 100644
index 000000000..5113ae0b8
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py
@@ -0,0 +1,80 @@
+#############################################################################
+##
+## 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 PySide6.QtWidgets import (QHBoxLayout, QHeaderView, QSizePolicy,
+ QTableView, QWidget)
+
+from table_model import CustomTableModel
+
+
+class Widget(QWidget):
+ def __init__(self, data):
+ QWidget.__init__(self)
+
+ # Getting the Model
+ self.model = CustomTableModel(data)
+
+ # Creating a QTableView
+ self.table_view = QTableView()
+ self.table_view.setModel(self.model)
+
+ # QTableView Headers
+ self.horizontal_header = self.table_view.horizontalHeader()
+ self.vertical_header = self.table_view.verticalHeader()
+ self.horizontal_header.setSectionResizeMode(
+ QHeaderView.ResizeToContents
+ )
+ self.vertical_header.setSectionResizeMode(
+ QHeaderView.ResizeToContents
+ )
+ self.horizontal_header.setStretchLastSection(True)
+
+ # QWidget Layout
+ self.main_layout = QHBoxLayout()
+ size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+
+ ## Left layout
+ size.setHorizontalStretch(1)
+ self.table_view.setSizePolicy(size)
+ self.main_layout.addWidget(self.table_view)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.main_layout)
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py
new file mode 100644
index 000000000..a60a4a1b1
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py
@@ -0,0 +1,69 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Slot
+from PySide6.QtGui import QAction, QKeySequence
+from PySide6.QtWidgets import QMainWindow
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Eartquakes information")
+ self.setCentralWidget(widget)
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ ## Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut(QKeySequence.Quit)
+ exit_action.triggered.connect(self.close)
+
+ self.file_menu.addAction(exit_action)
+
+ # Status Bar
+ self.status = self.statusBar()
+ self.status.showMessage("Data loaded and plotted")
+
+ # Window dimensions
+ geometry = qApp.desktop().availableGeometry(self)
+ self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py
new file mode 100644
index 000000000..d9c50acb3
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py
@@ -0,0 +1,88 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
+from PySide6.QtGui import QColor
+
+
+class CustomTableModel(QAbstractTableModel):
+ def __init__(self, data=None):
+ QAbstractTableModel.__init__(self)
+ self.load_data(data)
+
+ def load_data(self, data):
+ self.input_dates = data[0].values
+ self.input_magnitudes = data[1].values
+
+ self.column_count = 2
+ self.row_count = len(self.input_magnitudes)
+
+ def rowCount(self, parent=QModelIndex()):
+ return self.row_count
+
+ def columnCount(self, parent=QModelIndex()):
+ return self.column_count
+
+ def headerData(self, section, orientation, role):
+ if role != Qt.DisplayRole:
+ return None
+ if orientation == Qt.Horizontal:
+ return ("Date", "Magnitude")[section]
+ else:
+ return "{}".format(section)
+
+ def data(self, index, role=Qt.DisplayRole):
+ column = index.column()
+ row = index.row()
+
+ if role == Qt.DisplayRole:
+ if column == 0:
+ raw_date = self.input_dates[row]
+ date = "{}".format(raw_date.toPython())
+ return date[:-3]
+ elif column == 1:
+ return "{:.2f}".format(self.input_magnitudes[row])
+ elif role == Qt.BackgroundRole:
+ return QColor(Qt.white)
+ elif role == Qt.TextAlignmentRole:
+ return Qt.AlignRight
+
+ return None
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main.py
new file mode 100644
index 000000000..ffb4d00b0
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main.py
@@ -0,0 +1,90 @@
+#############################################################################
+##
+## 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 argparse
+import pandas as pd
+
+from PySide6.QtCore import QDateTime, QTimeZone
+from PySide6.QtWidgets import QApplication
+from main_window import MainWindow
+from main_widget import Widget
+
+
+def transform_date(utc, timezone=None):
+ utc_fmt = "yyyy-MM-ddTHH:mm:ss.zzzZ"
+ new_date = QDateTime().fromString(utc, utc_fmt)
+ if timezone:
+ new_date.setTimeZone(timezone)
+ return new_date
+
+
+def read_data(fname):
+ # Read the CSV content
+ df = pd.read_csv(fname)
+
+ # Remove wrong magnitudes
+ df = df.drop(df[df.mag < 0].index)
+ magnitudes = df["mag"]
+
+ # My local timezone
+ timezone = QTimeZone(b"Europe/Berlin")
+
+ # Get timestamp transformed to our timezone
+ times = df["time"].apply(lambda x: transform_date(x, timezone))
+
+ return times, magnitudes
+
+
+if __name__ == "__main__":
+ options = argparse.ArgumentParser()
+ options.add_argument("-f", "--file", type=str, required=True)
+ args = options.parse_args()
+ data = read_data(args.file)
+
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ widget = Widget(data)
+ window = MainWindow(widget)
+ window.show()
+
+ sys.exit(app.exec_())
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py
new file mode 100644
index 000000000..0f9b14df3
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py
@@ -0,0 +1,91 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import QDateTime, Qt
+from PySide6.QtGui import QPainter
+from PySide6.QtWidgets import (QWidget, QHeaderView, QHBoxLayout, QTableView,
+ QSizePolicy)
+from PySide6.QtCharts import QtCharts
+
+from table_model import CustomTableModel
+
+
+class Widget(QWidget):
+ def __init__(self, data):
+ QWidget.__init__(self)
+
+ # Getting the Model
+ self.model = CustomTableModel(data)
+
+ # Creating a QTableView
+ self.table_view = QTableView()
+ self.table_view.setModel(self.model)
+
+ # QTableView Headers
+ self.horizontal_header = self.table_view.horizontalHeader()
+ self.vertical_header = self.table_view.verticalHeader()
+ self.horizontal_header.setSectionResizeMode(QHeaderView.ResizeToContents)
+ self.vertical_header.setSectionResizeMode(QHeaderView.ResizeToContents)
+ self.horizontal_header.setStretchLastSection(True)
+
+ # Creating QChart
+ self.chart = QtCharts.QChart()
+ self.chart.setAnimationOptions(QtCharts.QChart.AllAnimations)
+
+ # Creating QChartView
+ self.chart_view = QtCharts.QChartView(self.chart)
+ self.chart_view.setRenderHint(QPainter.Antialiasing)
+
+ # QWidget Layout
+ self.main_layout = QHBoxLayout()
+ size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+
+ ## Left layout
+ size.setHorizontalStretch(1)
+ self.table_view.setSizePolicy(size)
+ self.main_layout.addWidget(self.table_view)
+
+ ## Right Layout
+ size.setHorizontalStretch(4)
+ self.chart_view.setSizePolicy(size)
+ self.main_layout.addWidget(self.chart_view)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.main_layout)
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py
new file mode 100644
index 000000000..4b0920133
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py
@@ -0,0 +1,69 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Slot
+from PySide6.QtGui import QAction, QKeySequence
+from PySide6.QtWidgets import QMainWindow
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Eartquakes information")
+ self.setCentralWidget(widget)
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut(QKeySequence.Quit)
+ exit_action.triggered.connect(self.close)
+
+ self.file_menu.addAction(exit_action)
+
+ # Status Bar
+ self.status = self.statusBar()
+ self.status.showMessage("Data loaded and plotted")
+
+ # Window dimensions
+ geometry = qApp.desktop().availableGeometry(self)
+ self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py
new file mode 100644
index 000000000..d9c50acb3
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py
@@ -0,0 +1,88 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
+from PySide6.QtGui import QColor
+
+
+class CustomTableModel(QAbstractTableModel):
+ def __init__(self, data=None):
+ QAbstractTableModel.__init__(self)
+ self.load_data(data)
+
+ def load_data(self, data):
+ self.input_dates = data[0].values
+ self.input_magnitudes = data[1].values
+
+ self.column_count = 2
+ self.row_count = len(self.input_magnitudes)
+
+ def rowCount(self, parent=QModelIndex()):
+ return self.row_count
+
+ def columnCount(self, parent=QModelIndex()):
+ return self.column_count
+
+ def headerData(self, section, orientation, role):
+ if role != Qt.DisplayRole:
+ return None
+ if orientation == Qt.Horizontal:
+ return ("Date", "Magnitude")[section]
+ else:
+ return "{}".format(section)
+
+ def data(self, index, role=Qt.DisplayRole):
+ column = index.column()
+ row = index.row()
+
+ if role == Qt.DisplayRole:
+ if column == 0:
+ raw_date = self.input_dates[row]
+ date = "{}".format(raw_date.toPython())
+ return date[:-3]
+ elif column == 1:
+ return "{:.2f}".format(self.input_magnitudes[row])
+ elif role == Qt.BackgroundRole:
+ return QColor(Qt.white)
+ elif role == Qt.TextAlignmentRole:
+ return Qt.AlignRight
+
+ return None
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main.py
new file mode 100644
index 000000000..3402ff856
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main.py
@@ -0,0 +1,92 @@
+#############################################################################
+##
+## 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 argparse
+import pandas as pd
+
+from PySide6.QtCore import QDateTime, QTimeZone
+from PySide6.QtWidgets import QApplication
+
+from main_window import MainWindow
+from main_widget import Widget
+
+
+def transform_date(utc, timezone=None):
+ utc_fmt = "yyyy-MM-ddTHH:mm:ss.zzzZ"
+ new_date = QDateTime().fromString(utc, utc_fmt)
+ if timezone:
+ new_date.setTimeZone(timezone)
+ return new_date
+
+
+def read_data(fname):
+ # Read the CSV content
+ df = pd.read_csv(fname)
+
+ # Remove wrong magnitudes
+ df = df.drop(df[df.mag < 0].index)
+ magnitudes = df["mag"]
+
+ # My local timezone
+ timezone = QTimeZone(b"Europe/Berlin")
+
+ # Get timestamp transformed to our timezone
+ times = df["time"].apply(lambda x: transform_date(x, timezone))
+
+ return times, magnitudes
+
+
+if __name__ == "__main__":
+ options = argparse.ArgumentParser()
+ options.add_argument("-f", "--file", type=str, required=True)
+ args = options.parse_args()
+ data = read_data(args.file)
+
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ # QWidget
+ widget = Widget(data)
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+
+ window.show()
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.py
new file mode 100644
index 000000000..2934a361f
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.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 PySide6.QtCore import QDateTime, Qt
+from PySide6.QtGui import QPainter
+from PySide6.QtWidgets import (QWidget, QHeaderView, QHBoxLayout, QTableView,
+ QSizePolicy)
+from PySide6.QtCharts import QtCharts
+
+from table_model import CustomTableModel
+
+
+class Widget(QWidget):
+ def __init__(self, data):
+ QWidget.__init__(self)
+
+ # Getting the Model
+ self.model = CustomTableModel(data)
+
+ # Creating a QTableView
+ self.table_view = QTableView()
+ self.table_view.setModel(self.model)
+
+ # QTableView Headers
+ resize = QHeaderView.ResizeToContents
+ self.horizontal_header = self.table_view.horizontalHeader()
+ self.vertical_header = self.table_view.verticalHeader()
+ self.horizontal_header.setSectionResizeMode(resize)
+ self.vertical_header.setSectionResizeMode(resize)
+ self.horizontal_header.setStretchLastSection(True)
+
+ # Creating QChart
+ self.chart = QtCharts.QChart()
+ self.chart.setAnimationOptions(QtCharts.QChart.AllAnimations)
+ self.add_series("Magnitude (Column 1)", [0, 1])
+
+ # Creating QChartView
+ self.chart_view = QtCharts.QChartView(self.chart)
+ self.chart_view.setRenderHint(QPainter.Antialiasing)
+
+ # QWidget Layout
+ self.main_layout = QHBoxLayout()
+ size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+
+ # Left layout
+ size.setHorizontalStretch(1)
+ self.table_view.setSizePolicy(size)
+ self.main_layout.addWidget(self.table_view)
+
+ # Right Layout
+ size.setHorizontalStretch(4)
+ self.chart_view.setSizePolicy(size)
+ self.main_layout.addWidget(self.chart_view)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.main_layout)
+
+ def add_series(self, name, columns):
+ # Create QLineSeries
+ self.series = QtCharts.QLineSeries()
+ self.series.setName(name)
+
+ # Filling QLineSeries
+ for i in range(self.model.rowCount()):
+ # Getting the data
+ t = self.model.index(i, 0).data()
+ date_fmt = "yyyy-MM-dd HH:mm:ss.zzz"
+
+ x = QDateTime().fromString(t, date_fmt).toSecsSinceEpoch()
+ y = float(self.model.index(i, 1).data())
+
+ if x > 0 and y > 0:
+ self.series.append(x, y)
+
+ self.chart.addSeries(self.series)
+
+ # Setting X-axis
+ self.axis_x = QtCharts.QDateTimeAxis()
+ self.axis_x.setTickCount(10)
+ self.axis_x.setFormat("dd.MM (h:mm)")
+ self.axis_x.setTitleText("Date")
+ self.chart.addAxis(self.axis_x, Qt.AlignBottom)
+ self.series.attachAxis(self.axis_x)
+ # Setting Y-axis
+ self.axis_y = QtCharts.QValueAxis()
+ self.axis_y.setTickCount(10)
+ self.axis_y.setLabelFormat("%.2f")
+ self.axis_y.setTitleText("Magnitude")
+ self.chart.addAxis(self.axis_y, Qt.AlignLeft)
+ self.series.attachAxis(self.axis_y)
+
+ # Getting the color from the QChart to use it on the QTableView
+ self.model.color = "{}".format(self.series.pen().color().name())
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py
new file mode 100644
index 000000000..450f6de41
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py
@@ -0,0 +1,70 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+from PySide6.QtCore import Slot
+from PySide6.QtGui import QAction, QKeySequence
+from PySide6.QtWidgets import QMainWindow
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Eartquakes information")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut(QKeySequence.Quit)
+ exit_action.triggered.connect(self.close)
+
+ self.file_menu.addAction(exit_action)
+
+ # Status Bar
+ self.status = self.statusBar()
+ self.status.showMessage("Data loaded and plotted")
+
+ # Window dimensions
+ geometry = qApp.desktop().availableGeometry(self)
+ self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
+ self.setCentralWidget(widget)
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py
new file mode 100644
index 000000000..abe3bf2ae
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py
@@ -0,0 +1,88 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
+from PySide6.QtGui import QColor
+
+
+class CustomTableModel(QAbstractTableModel):
+ def __init__(self, data=None):
+ QAbstractTableModel.__init__(self)
+ self.color = None
+ self.load_data(data)
+
+ def load_data(self, data):
+ self.input_dates = data[0].values
+ self.input_magnitudes = data[1].values
+
+ self.column_count = 2
+ self.row_count = len(self.input_magnitudes)
+
+ def rowCount(self, parent=QModelIndex()):
+ return self.row_count
+
+ def columnCount(self, parent=QModelIndex()):
+ return self.column_count
+
+ def headerData(self, section, orientation, role):
+ if role != Qt.DisplayRole:
+ return None
+ if orientation == Qt.Horizontal:
+ return ("Date", "Magnitude")[section]
+ else:
+ return "{}".format(section)
+
+ def data(self, index, role=Qt.DisplayRole):
+ column = index.column()
+ row = index.row()
+
+ if role == Qt.DisplayRole:
+ if column == 0:
+ raw_date = self.input_dates[row]
+ date = "{}".format(raw_date.toPython())
+ return date[:-3]
+ elif column == 1:
+ return "{:.2f}".format(self.input_magnitudes[row])
+ elif role == Qt.BackgroundRole:
+ return (QColor(Qt.white), QColor(self.color))[column]
+ elif role == Qt.TextAlignmentRole:
+ return Qt.AlignRight
+
+ return None
diff --git a/sources/pyside6/doc/tutorials/datavisualize/filter_data.rst b/sources/pyside6/doc/tutorials/datavisualize/filter_data.rst
new file mode 100644
index 000000000..b06b2fa15
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/filter_data.rst
@@ -0,0 +1,29 @@
+Chapter 2 - Filtering data
+===========================
+
+In the previous chapter, you learned how to read and print data that is a
+bit raw. Now, try to select a few columns and handle them properly.
+
+Start with these two columns: Time (time) and Magnitude (mag). After getting
+the information from these columns, filter and adapt the data. Try formatting
+the date to Qt types.
+
+There is not much to do for the Magnitude column, as it's just a floating point
+number. You could take special care to check if the data is correct. This could
+be done by filtering the data that follows the condition, "magnitude > 0", to
+avoid faulty data or unexpected behavior.
+
+The Date column provides data in UTC format (for example,
+2018-12-11T21:14:44.682Z), so you could easily map it to a QDateTime object
+defining the structure of the string. Additionally, you can adapt the time
+based on the timezone you are in, using QTimeZone.
+
+The following script filters and formats the CSV data as described earlier:
+
+.. literalinclude:: datavisualize2/main.py
+ :language: python
+ :linenos:
+ :lines: 40-
+
+Now that you have a tuple of QDateTime and float data, try improving the
+output further. That's what you'll learn in the following chapters.
diff --git a/sources/pyside6/doc/tutorials/datavisualize/images/QMainWindow-layout.png b/sources/pyside6/doc/tutorials/datavisualize/images/QMainWindow-layout.png
new file mode 100644
index 000000000..075d796b8
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/images/QMainWindow-layout.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/datavisualize/images/datavisualization_app.png b/sources/pyside6/doc/tutorials/datavisualize/images/datavisualization_app.png
new file mode 100644
index 000000000..ddac43fc3
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/images/datavisualization_app.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/datavisualize/index.rst b/sources/pyside6/doc/tutorials/datavisualize/index.rst
new file mode 100644
index 000000000..ff18c654e
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/index.rst
@@ -0,0 +1,26 @@
+Data Visualization Tool Tutorial
+*********************************
+
+In this tutorial, you'll learn about the data visualization capabilities
+of |project|. To start with, find some open data to visualize. For example,
+data about the magnitude of earthquakes during the last hour published on the
+US Geological Survey website. You could download the
+`All earthquakes <https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.csv>`_
+open data in a CSV format for this tutorial.
+
+In the following chapters of this tutorial you'll learn how to
+visualize data from a CSV in a line chart.
+
+.. toctree::
+ :glob:
+ :titlesonly:
+
+ read*
+ filter*
+ add_main*
+ add_tab*
+ add_chart*
+ plot*
+
+You can download the sources from :download:`here <datavisualize.tar.bz2>`.
+
diff --git a/sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst b/sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst
new file mode 100644
index 000000000..8ebdd2b08
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst
@@ -0,0 +1,25 @@
+Chapter 6 - Plot the data in the ChartView
+===========================================
+
+The last step of this tutorial is to plot the CSV data inside our QChart. For
+this, you need to go over our data and include the data on a QLineSeries.
+
+After adding the data to the series, you can modify the axis to properly
+display the QDateTime on the X-axis, and the magnitude values on the Y-axis.
+
+Here is the updated :code:`main_widget.py` that includes an additional
+function to plot data using a QLineSeries:
+
+.. literalinclude:: datavisualize6/main_widget.py
+ :language: python
+ :linenos:
+ :lines: 40-
+ :emphasize-lines: 33,56-91
+
+Now, run the application to visualize the earthquake magnitudes
+data at different times.
+
+.. image:: images/datavisualization_app.png
+
+Try modifying the sources to get different output. For example, you could try
+to plot more data from the CSV.
diff --git a/sources/pyside6/doc/tutorials/datavisualize/read_data.rst b/sources/pyside6/doc/tutorials/datavisualize/read_data.rst
new file mode 100644
index 000000000..f7bf9337a
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/datavisualize/read_data.rst
@@ -0,0 +1,41 @@
+Chapter 1 - Reading data from a CSV
+===================================
+
+There are several ways to read data from a CSV file. The following are the most
+common ways:
+
+- Native reading
+- the `CSV module <https://docs.python.org/3/library/csv.html>`_
+- the `numpy module <https://www.numpy.org>`_
+- the `pandas module <https://pandas.pydata.org/>`_
+
+In this chapter, you will learn to use pandas to read and filter CSV data.
+In addition, you could pass the data file through a command-line option to your
+script.
+
+The following python script, :code:`main.py`, demonstrates how to do it:
+
+.. literalinclude:: datavisualize1/main.py
+ :language: python
+ :linenos:
+ :lines: 40-
+
+The Python script uses the :code:`argparse` module to accept and parse input
+from the command line. It then uses the input, which in this case is the filename,
+to read and print data to the prompt.
+
+Try running the script in the following way to check if you get desired output:
+
+::
+
+ $python datavisualize1/main.py -f all_hour.csv
+ time latitude longitude depth ... magNst status locationSource magSource
+ 0 2019-01-10T12:11:24.810Z 34.128166 -117.775497 4.46 ... 6.0 automatic ci ci
+ 1 2019-01-10T12:04:26.320Z 19.443333 -155.615997 0.72 ... 6.0 automatic hv hv
+ 2 2019-01-10T11:57:48.980Z 33.322500 -116.393167 4.84 ... 11.0 automatic ci ci
+ 3 2019-01-10T11:52:09.490Z 38.835667 -122.836670 1.28 ... 7.0 automatic nc nc
+ 4 2019-01-10T11:25:44.854Z 65.108200 -149.370100 20.60 ... NaN automatic ak ak
+ 5 2019-01-10T11:25:23.786Z 69.151800 -144.497700 10.40 ... NaN reviewed ak ak
+ 6 2019-01-10T11:16:11.761Z 61.331800 -150.070800 20.10 ... NaN automatic ak ak
+
+ [7 rows x 22 columns]
diff --git a/sources/pyside6/doc/tutorials/expenses/expenses.rst b/sources/pyside6/doc/tutorials/expenses/expenses.rst
new file mode 100644
index 000000000..2aa3fb459
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/expenses.rst
@@ -0,0 +1,314 @@
+######################
+Expenses Tool Tutorial
+######################
+
+In this tutorial you will learn the following concepts:
+ * creating user interfaces programatically,
+ * layouts and widgets,
+ * overloading Qt classes,
+ * connecting signal and slots,
+ * interacting with QWidgets,
+ * and building your own application.
+
+The requirements:
+ * A simple window for the application
+ (`QMainWindow <https://doc.qt.io/qtforpython/PySide6/QtWidgets/QMainWindow.html>`_).
+ * A table to keep track of the expenses
+ (`QTableWidget <https://doc.qt.io/qtforpython/PySide6/QtWidgets/QTableWidget.html>`_).
+ * Two input fields to add expense information
+ (`QLineEdit <https://doc.qt.io/qtforpython/PySide6/QtWidgets/QLineEdit.html>`_).
+ * Buttons to add information to the table, plot data, clear table, and exit the application
+ (`QPushButton <https://doc.qt.io/qtforpython/PySide6/QtWidgets/QPushButton.html>`_).
+ * A verification step to avoid invalid data entry.
+ * A chart to visualize the expense data
+ (`QChart <https://doc.qt.io/qtforpython/PySide6/QtCharts/QtCharts.QChart.html>`_) that will
+ be embedded in a chart view
+ (`QChartView <https://doc.qt.io/qtforpython/PySide6/QtCharts/QtCharts.QChartView.html>`_).
+
+Empty window
+------------
+
+The base structure for a `QApplication` is located inside the `if __name__ == "__main__":`
+code block.
+
+.. code-block:: python
+ :linenos:
+
+ if __name__ == "__main__":
+ app = QApplication([])
+ # ...
+ sys.exit(app.exec_())
+
+Now, to start the development, create an empty window called `MainWindow`.
+You could do that by defining a class that inherits from `QMainWindow`.
+
+.. literalinclude:: steps/01-expenses.py
+ :linenos:
+ :lines: 45-59
+ :emphasize-lines: 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: 10-12
+
+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: 6
+
+Notice that the code snippet adds a *File* menu with the *Exit* option only.
+
+First signal/slot connection
+----------------------------
+
+The *Exit* option must be connected to a slot that triggers the application to exit. The main
+idea to achieve this, is the following:
+
+.. code-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-block:: python
+
+ exit_action.triggered.connect(slot_name)
+
+.. note:: Now a *slot* needs to be defined to exit the application, which can be done using
+ `QApplication.quit()`. If we put all these concepts together you will end up with the
+ following code:
+
+.. literalinclude:: steps/03-expenses.py
+ :linenos:
+ :lines: 56-65
+ :emphasize-lines: 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
+will be invokable from `Signals` of QObjects when connected.
+
+Empty widget and data
+---------------------
+
+The `QMainWindow` enables us to set a central widget that will be displayed when showing the window
+(`read more <https://doc.qt.io/qt-5/qmainwindow.html#details>`_).
+This central widget could be another class derived from `QWidget`.
+
+Additionally, you will define example data to visualize later.
+
+.. literalinclude:: steps/04-expenses.py
+ :linenos:
+ :lines: 46-53
+
+With the `Widget` class in place, modify `MainWindow`'s initialization code
+
+.. literalinclude:: steps/04-expenses.py
+ :linenos:
+ :lines: 80-84
+
+Window layout
+-------------
+
+Now that the main empty window is in place, you need to start adding widgets to achieve the main
+goal of creating an expenses application.
+
+After declaring the example data, you can visualize it on a simple `QTableWidget`. To do so, you
+will add this procedure to the `Widget` constructor.
+
+.. warning:: Only for the example purpose a QTableWidget will be used,
+ but for more performance-critical applications the combination
+ of a model and a QTableView is encouraged.
+
+.. literalinclude:: steps/05-expenses.py
+ :linenos:
+ :lines: 48-73
+
+As you can see, the code also includes a `QHBoxLayout` that provides the container to place widgets
+horizontally.
+
+Additionally, the `QTableWidget` allows for customizing it, like adding the labels for the two
+columns that will be used, and to *stretch* the content to use the whole `Widget` space.
+
+The last line of code refers to *filling the table**, and the code to perform that task is
+displayed below.
+
+.. literalinclude:: steps/05-expenses.py
+ :linenos:
+ :lines: 75-81
+
+Having this process on a separate method is a good practice to leave the constructor more readable,
+and to split the main functions of the class in independent processes.
+
+
+Right side layout
+-----------------
+
+Because the data that is being used is just an example, you are required to include a mechanism to
+input items to the table, and extra buttons to clear the table's content, and also quit the
+application.
+
+To distribute these input lines and buttons, you will use a `QVBoxLayout` that allows you to place
+elements vertically inside a layout.
+
+.. literalinclude:: steps/06-expenses.py
+ :linenos:
+ :lines: 64-80
+
+Leaving the table on the left side and these newly included widgets to the right side
+will be just a matter to add a layout to our main `QHBoxLayout` as you saw in the previous
+example:
+
+.. literalinclude:: steps/06-expenses.py
+ :linenos:
+ :lines: 42-47
+
+The next step will be connecting those new buttons to slots.
+
+
+Adding elements
+---------------
+
+Each `QPushButton` have a signal called *clicked*, that is emitted when you click on the button.
+This will be more than enough for this example, but you can see other signals in the `official
+documentation <https://doc.qt.io/qtforpython/PySide6/QtWidgets/QAbstractButton.html#signals>`_.
+
+.. literalinclude:: steps/07-expenses.py
+ :linenos:
+ :lines: 92-95
+
+As you can see on the previous lines, we are connecting each *clicked* signal to different slots.
+In this example slots are normal class methods in charge of perform a determined task associated
+with our buttons. It is really important to decorate each method declaration with a `@Slot()`, in
+that way PySide6 knows internally how to register them into Qt.
+
+.. 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.
+
+The mechanism to add elements into the table is described as the following:
+
+ * get the *description* and *price* from the fields,
+ * insert a new empty row to the table,
+ * set the values for the empty row in each column,
+ * clear the input text fields,
+ * include the global count of table rows.
+
+To exit the application you can use the `quit()` method of the unique `QApplication` instance, and
+to clear the content of the table you can just set the table *row count*, and the internal count to
+zero.
+
+Verification step
+-----------------
+
+Adding information to the table needs to be a critical action that require a verification step
+to avoid adding invalid information, for example, empty information.
+
+You can use a signal from `QLineEdit` called *textChanged[str]* which will be emitted every
+time something inside changes, i.e.: each key stroke.
+Notice that this time, there is a *[str]* section on the signal, this means that the signal
+will also emit the value of the text that was changed, which will be really useful to verify
+the current content of the `QLineEdit`.
+
+You can connect two different object's signal to the same slot, and this will be the case
+for your current application:
+
+.. literalinclude:: steps/08-expenses.py
+ :linenos:
+ :lines: 99-100
+
+The content of the *check_disable* slot will be really simple:
+
+.. literalinclude:: steps/08-expenses.py
+ :linenos:
+ :lines: 119-124
+
+You have two options, write a verification based on the current value
+of the string you retrieve, or manually get the whole content of both
+`QLineEdit`. The second is preferred in this case, so you can verify
+if the two inputs are not empty to enable the button *Add*.
+
+.. note:: Qt also provides a special class called
+ `QValidator <https://doc.qt.io/qtforpython/PySide6/QtGui/QValidator.html?highlight=qvalidator>`_
+ that you can use to validate any input.
+
+Empty chart view
+----------------
+
+New items can be added to the table, and the visualization is so far
+OK, but you can accomplish more by representing the data graphically.
+
+First you will include an empty `QChartView` placeholder into the right
+side of your application.
+
+.. literalinclude:: steps/09-expenses.py
+ :linenos:
+ :lines: 66-68
+
+Additionally the order of how you include widgets to the right
+`QVBoxLayout` will also change.
+
+.. literalinclude:: steps/09-expenses.py
+ :linenos:
+ :lines: 81-91
+ :emphasize-lines: 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,
+but now, with the `QChartView` it will not be necessary.
+
+Also, you need include a *Plot* button if you want to do it on-demand.
+
+Full application
+----------------
+
+For the final step, you will need to connect the *Plot* button
+to a slot that creates a chart and includes it into your `QChartView`.
+
+.. literalinclude:: steps/10-expenses.py
+ :linenos:
+ :lines: 103-109
+ :emphasize-lines: 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
+
+The following steps show how to fill a `QPieSeries`:
+
+ * create a `QPieSeries`,
+ * iterate over the table row IDs,
+ * get the items at the *i* position,
+ * add those values to the *series*.
+
+Once the series has been populated with our data, you create a new `QChart`,
+add the series on it, and optionally set an alignment for the legend.
+
+The final line `self.chart_view.setChart(chart)` is in charge of bringing
+your newly created chart to the `QChartView`.
+
+The application will look like this:
+
+.. image:: expenses_tool.png
+
+And now you can see the whole code:
+
+.. literalinclude:: main.py
+ :linenos:
diff --git a/sources/pyside6/doc/tutorials/expenses/expenses_tool.png b/sources/pyside6/doc/tutorials/expenses/expenses_tool.png
new file mode 100644
index 000000000..7a6f6d1f0
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/expenses_tool.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/expenses/main.py b/sources/pyside6/doc/tutorials/expenses/main.py
new file mode 100644
index 000000000..2eec5defb
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/main.py
@@ -0,0 +1,207 @@
+#############################################################################
+##
+## Copyright (C) 2020 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 PySide6.QtCore import Qt, Slot
+from PySide6.QtGui import QAction, QPainter
+from PySide6.QtWidgets import (QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+from PySide6.QtCharts import QtCharts
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Chart
+ self.chart_view = QtCharts.QChartView()
+ self.chart_view.setRenderHint(QPainter.Antialiasing)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+ self.plot = QPushButton("Plot")
+
+ # Disabling 'Add' button
+ self.add.setEnabled(False)
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addWidget(self.plot)
+ self.right.addWidget(self.chart_view)
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.plot.clicked.connect(self.plot_data)
+ self.clear.clicked.connect(self.clear_table)
+ self.description.textChanged[str].connect(self.check_disable)
+ self.price.textChanged[str].connect(self.check_disable)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ description_item = QTableWidgetItem(des)
+ price_item = QTableWidgetItem("{:.2f}".format(float(price)))
+ price_item.setTextAlignment(Qt.AlignRight)
+
+ self.table.setItem(self.items, 0, description_item)
+ self.table.setItem(self.items, 1, price_item)
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def check_disable(self, s):
+ if not self.description.text() or not self.price.text():
+ self.add.setEnabled(False)
+ else:
+ self.add.setEnabled(True)
+
+ @Slot()
+ def plot_data(self):
+ # Get table information
+ series = QtCharts.QPieSeries()
+ for i in range(self.table.rowCount()):
+ text = self.table.item(i, 0).text()
+ number = float(self.table.item(i, 1).text())
+ series.append(text, number)
+
+ chart = QtCharts.QChart()
+ chart.addSeries(series)
+ chart.legend().setAlignment(Qt.AlignLeft)
+ self.chart_view.setChart(chart)
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ description_item = QTableWidgetItem(desc)
+ price_item = QTableWidgetItem("{:.2f}".format(price))
+ price_item.setTextAlignment(Qt.AlignRight)
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, description_item)
+ self.table.setItem(self.items, 1, price_item)
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/main_snake_prop.py b/sources/pyside6/doc/tutorials/expenses/main_snake_prop.py
new file mode 100644
index 000000000..cea2f5767
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/main_snake_prop.py
@@ -0,0 +1,210 @@
+#############################################################################
+##
+## Copyright (C) 2020 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 PySide6.QtCore import QMargins, Qt, Slot, QSize
+from PySide6.QtGui import QPainter
+from PySide6.QtWidgets import (QAction, QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+from PySide6.QtCharts import QtCharts
+
+from __feature__ import snake_case, true_property
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.column_count = 2
+ self.table.horizontal_header_labels = ["Description", "Price"]
+ self.table.horizontal_header().section_resize_mode = QHeaderView.Stretch
+
+ # Chart
+ self.chart_view = QtCharts.QChartView()
+ self.chart_view.render_hint = QPainter.Antialiasing
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+ self.plot = QPushButton("Plot")
+
+ # Disabling 'Add' button
+ self.add.enabled = False
+
+ self.right = QVBoxLayout()
+
+ self.right.contents_margins = QMargins(10, 10, 10, 10)
+ self.right.add_widget(QLabel("Description"))
+ self.right.add_widget(self.description)
+ self.right.add_widget(QLabel("Price"))
+ self.right.add_widget(self.price)
+ self.right.add_widget(self.add)
+ self.right.add_widget(self.plot)
+ self.right.add_widget(self.chart_view)
+ self.right.add_widget(self.clear)
+ self.right.add_widget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.add_widget(self.table)
+ self.layout.add_layout(self.right)
+
+ # Set the layout to the QWidget
+ self.set_layout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.plot.clicked.connect(self.plot_data)
+ self.clear.clicked.connect(self.clear_table)
+ self.description.textChanged[str].connect(self.check_disable)
+ self.price.textChanged[str].connect(self.check_disable)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text
+ price = self.price.text
+
+ self.table.insert_row(self.items)
+ description_item = QTableWidgetItem(des)
+ price_item = QTableWidgetItem("{:.2f}".format(float(price)))
+ price_item.text_alignment = Qt.AlignRight
+
+ self.table.set_item(self.items, 0, description_item)
+ self.table.set_item(self.items, 1, price_item)
+
+ self.description.text = ""
+ self.price.text = ""
+
+ self.items += 1
+
+ @Slot()
+ def check_disable(self, s):
+ if not self.description.text or not self.price.text:
+ self.add.enabled = False
+ else:
+ self.add.enabled = True
+
+ @Slot()
+ def plot_data(self):
+ # Get table information
+ series = QtCharts.QPieSeries()
+ for i in range(self.table.row_count):
+ text = self.table.item(i, 0).text()
+ number = float(self.table.item(i, 1).text())
+ series.append(text, number)
+
+ chart = QtCharts.QChart()
+ chart.add_series(series)
+ chart.legend().alignment = Qt.AlignLeft
+ self.chart_view.set_chart(chart)
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ description_item = QTableWidgetItem(desc)
+ price_item = QTableWidgetItem("{:.2f}".format(price))
+ price_item.text_alignment = Qt.AlignRight
+ self.table.insert_row(self.items)
+ self.table.set_item(self.items, 0, description_item)
+ self.table.set_item(self.items, 1, price_item)
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.row_count = 0
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.window_title = "Tutorial"
+
+ # Menu
+ self.menu = self.menu_bar()
+ self.file_menu = self.menu.add_menu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.shortcut = "Ctrl+Q"
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.add_action(exit_action)
+ self.set_central_widget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.size = QSize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/01-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/01-expenses.py
new file mode 100644
index 000000000..efbbc59fc
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/01-expenses.py
@@ -0,0 +1,59 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide6.QtWidgets import QApplication, QMainWindow
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ window = MainWindow()
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/02-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/02-expenses.py
new file mode 100644
index 000000000..783283d4f
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/02-expenses.py
@@ -0,0 +1,71 @@
+#############################################################################
+##
+## 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 PySide6.QtGui import QAction
+from PySide6.QtWidgets import QApplication, QMainWindow
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+
+ self.file_menu.addAction(exit_action)
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ window = MainWindow()
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/03-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/03-expenses.py
new file mode 100644
index 000000000..bebf0b3ce
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/03-expenses.py
@@ -0,0 +1,78 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Slot
+from PySide6.QtGui import QAction
+from PySide6.QtWidgets import QApplication, QMainWindow
+from PySide6.QtCharts import QtCharts
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+
+ window = MainWindow()
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/04-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/04-expenses.py
new file mode 100644
index 000000000..a9a4c10a1
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/04-expenses.py
@@ -0,0 +1,90 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Slot
+from PySide6.QtGui import QAction
+from PySide6.QtWidgets import QApplication, QMainWindow, QWidget
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/05-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/05-expenses.py
new file mode 100644
index 000000000..3b22c2631
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/05-expenses.py
@@ -0,0 +1,118 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Slot
+from PySide6.QtGui import QAction
+from PySide6.QtWidgets import (QApplication, QHeaderView, QHBoxLayout, QMainWindow,
+ QTableWidget, QTableWidgetItem, QWidget)
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Fill example data
+ self.fill_table()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/06-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/06-expenses.py
new file mode 100644
index 000000000..5e6bcc0e9
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/06-expenses.py
@@ -0,0 +1,138 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Slot
+from PySide6.QtGui import QAction
+from PySide6.QtWidgets import (QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addStretch()
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Fill example data
+ self.fill_table()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/07-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/07-expenses.py
new file mode 100644
index 000000000..aa7ee6a3a
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/07-expenses.py
@@ -0,0 +1,165 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Slot
+from PySide6.QtGui import QAction
+from PySide6.QtWidgets import (QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addStretch()
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.clear.clicked.connect(self.clear_table)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(des))
+ self.table.setItem(self.items, 1, QTableWidgetItem(price))
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/08-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/08-expenses.py
new file mode 100644
index 000000000..0c5eb7c61
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/08-expenses.py
@@ -0,0 +1,178 @@
+#############################################################################
+##
+## 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 PySide6.QtCore import Slot
+from PySide6.QtGui import QAction
+from PySide6.QtWidgets import (QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+
+ # Disabling 'Add' button
+ self.add.setEnabled(False)
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addStretch()
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.clear.clicked.connect(self.clear_table)
+ self.description.textChanged[str].connect(self.check_disable)
+ self.price.textChanged[str].connect(self.check_disable)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(des))
+ self.table.setItem(self.items, 1, QTableWidgetItem(price))
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def check_disable(self, s):
+ if not self.description.text() or not self.price.text():
+ self.add.setEnabled(False)
+ else:
+ self.add.setEnabled(True)
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/09-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/09-expenses.py
new file mode 100644
index 000000000..30e1d7f3f
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/09-expenses.py
@@ -0,0 +1,185 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide6.QtCore import Slot
+from PySide6.QtGui import QAction, QPainter
+from PySide6.QtWidgets import (QApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+from PySide6.QtCharts import QtCharts
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Chart
+ self.chart_view = QtCharts.QChartView()
+ self.chart_view.setRenderHint(QPainter.Antialiasing)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+ self.plot = QPushButton("Plot")
+
+ # Disabling 'Add' button
+ self.add.setEnabled(False)
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addWidget(self.plot)
+ self.right.addWidget(self.chart_view)
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.clear.clicked.connect(self.clear_table)
+ self.description.textChanged[str].connect(self.check_disable)
+ self.price.textChanged[str].connect(self.check_disable)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(des))
+ self.table.setItem(self.items, 1, QTableWidgetItem(price))
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def check_disable(self, s):
+ if not self.description.text() or not self.price.text():
+ self.add.setEnabled(False)
+ else:
+ self.add.setEnabled(True)
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, QTableWidgetItem(desc))
+ self.table.setItem(self.items, 1, QTableWidgetItem(str(price)))
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/expenses/steps/10-expenses.py b/sources/pyside6/doc/tutorials/expenses/steps/10-expenses.py
new file mode 100644
index 000000000..2329805a7
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/expenses/steps/10-expenses.py
@@ -0,0 +1,207 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+from PySide6.QtCore import Qt, Slot
+from PySide6.QtGui import QAction, QPainter
+from PySide6.QtWidgets import (QQApplication, QHeaderView, QHBoxLayout, QLabel, QLineEdit,
+ QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
+ QVBoxLayout, QWidget)
+from PySide6.QtCharts import QtCharts
+
+
+class Widget(QWidget):
+ def __init__(self):
+ QWidget.__init__(self)
+ self.items = 0
+
+ # Example data
+ self._data = {"Water": 24.5, "Electricity": 55.1, "Rent": 850.0,
+ "Supermarket": 230.4, "Internet": 29.99, "Bars": 21.85,
+ "Public transportation": 60.0, "Coffee": 22.45, "Restaurants": 120}
+
+ # Left
+ self.table = QTableWidget()
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(["Description", "Price"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+
+ # Chart
+ self.chart_view = QtCharts.QChartView()
+ self.chart_view.setRenderHint(QPainter.Antialiasing)
+
+ # Right
+ self.description = QLineEdit()
+ self.price = QLineEdit()
+ self.add = QPushButton("Add")
+ self.clear = QPushButton("Clear")
+ self.quit = QPushButton("Quit")
+ self.plot = QPushButton("Plot")
+
+ # Disabling 'Add' button
+ self.add.setEnabled(False)
+
+ self.right = QVBoxLayout()
+ self.right.setMargin(10)
+ self.right.addWidget(QLabel("Description"))
+ self.right.addWidget(self.description)
+ self.right.addWidget(QLabel("Price"))
+ self.right.addWidget(self.price)
+ self.right.addWidget(self.add)
+ self.right.addWidget(self.plot)
+ self.right.addWidget(self.chart_view)
+ self.right.addWidget(self.clear)
+ self.right.addWidget(self.quit)
+
+ # QWidget Layout
+ self.layout = QHBoxLayout()
+
+ #self.table_view.setSizePolicy(size)
+ self.layout.addWidget(self.table)
+ self.layout.addLayout(self.right)
+
+ # Set the layout to the QWidget
+ self.setLayout(self.layout)
+
+ # Signals and Slots
+ self.add.clicked.connect(self.add_element)
+ self.quit.clicked.connect(self.quit_application)
+ self.plot.clicked.connect(self.plot_data)
+ self.clear.clicked.connect(self.clear_table)
+ self.description.textChanged[str].connect(self.check_disable)
+ self.price.textChanged[str].connect(self.check_disable)
+
+ # Fill example data
+ self.fill_table()
+
+ @Slot()
+ def add_element(self):
+ des = self.description.text()
+ price = self.price.text()
+
+ self.table.insertRow(self.items)
+ description_item = QTableWidgetItem(des)
+ price_item = QTableWidgetItem("{:.2f}".format(float(price)))
+ price_item.setTextAlignment(Qt.AlignRight)
+
+ self.table.setItem(self.items, 0, description_item)
+ self.table.setItem(self.items, 1, price_item)
+
+ self.description.setText("")
+ self.price.setText("")
+
+ self.items += 1
+
+ @Slot()
+ def check_disable(self, s):
+ if not self.description.text() or not self.price.text():
+ self.add.setEnabled(False)
+ else:
+ self.add.setEnabled(True)
+
+ @Slot()
+ def plot_data(self):
+ # Get table information
+ series = QtCharts.QPieSeries()
+ for i in range(self.table.rowCount()):
+ text = self.table.item(i, 0).text()
+ number = float(self.table.item(i, 1).text())
+ series.append(text, number)
+
+ chart = QtCharts.QChart()
+ chart.addSeries(series)
+ chart.legend().setAlignment(Qt.AlignLeft)
+ self.chart_view.setChart(chart)
+
+ @Slot()
+ def quit_application(self):
+ QApplication.quit()
+
+ def fill_table(self, data=None):
+ data = self._data if not data else data
+ for desc, price in data.items():
+ description_item = QTableWidgetItem(desc)
+ price_item = QTableWidgetItem("{:.2f}".format(price))
+ price_item.setTextAlignment(Qt.AlignRight)
+ self.table.insertRow(self.items)
+ self.table.setItem(self.items, 0, description_item)
+ self.table.setItem(self.items, 1, price_item)
+ self.items += 1
+
+ @Slot()
+ def clear_table(self):
+ self.table.setRowCount(0)
+ self.items = 0
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, widget):
+ QMainWindow.__init__(self)
+ self.setWindowTitle("Tutorial")
+
+ # Menu
+ self.menu = self.menuBar()
+ self.file_menu = self.menu.addMenu("File")
+
+ # Exit QAction
+ exit_action = QAction("Exit", self)
+ exit_action.setShortcut("Ctrl+Q")
+ exit_action.triggered.connect(self.exit_app)
+
+ self.file_menu.addAction(exit_action)
+ self.setCentralWidget(widget)
+
+ @Slot()
+ def exit_app(self, checked):
+ QApplication.quit()
+
+
+if __name__ == "__main__":
+ # Qt Application
+ app = QApplication(sys.argv)
+ # QWidget
+ widget = Widget()
+ # QMainWindow using QWidget as central widget
+ window = MainWindow(widget)
+ window.resize(800, 600)
+ window.show()
+
+ # Execute application
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/index.rst b/sources/pyside6/doc/tutorials/index.rst
new file mode 100644
index 000000000..a159725a7
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/index.rst
@@ -0,0 +1,42 @@
+|project| Tutorials
+====================
+
+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.
+
+Basic tutorials
+---------------
+
+.. toctree::
+ :maxdepth: 1
+
+ basictutorial/widgets.rst
+ basictutorial/qml.rst
+ basictutorial/clickablebutton.rst
+ basictutorial/dialog.rst
+ basictutorial/uifiles.rst
+ basictutorial/qrcfiles.rst
+ basictutorial/widgetstyling.rst
+
+Real use-cases applications
+---------------------------
+
+.. toctree::
+ :maxdepth: 1
+
+ datavisualize/index.rst
+ expenses/expenses.rst
+ qmlapp/qmlapplication.rst
+ qmlintegration/qmlintegration.rst
+ qmlsqlintegration/qmlsqlintegration.rst
+
+C++ and Python
+--------------
+
+.. toctree::
+ :maxdepth: 1
+
+ portingguide/index.rst
diff --git a/sources/pyside6/doc/tutorials/portingguide/chapter1/chapter1.rst b/sources/pyside6/doc/tutorials/portingguide/chapter1/chapter1.rst
new file mode 100644
index 000000000..20b11065a
--- /dev/null
+++ b/sources/pyside6/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/pyside6/doc/tutorials/portingguide/chapter1/createdb.py b/sources/pyside6/doc/tutorials/portingguide/chapter1/createdb.py
new file mode 100644
index 000000000..c3f27162d
--- /dev/null
+++ b/sources/pyside6/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 PySide6.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/pyside6/doc/tutorials/portingguide/chapter1/images/chapter1_books.png b/sources/pyside6/doc/tutorials/portingguide/chapter1/images/chapter1_books.png
new file mode 100644
index 000000000..164674220
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/portingguide/chapter1/images/chapter1_books.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/portingguide/chapter1/initdb.h b/sources/pyside6/doc/tutorials/portingguide/chapter1/initdb.h
new file mode 100644
index 000000000..773e3fb74
--- /dev/null
+++ b/sources/pyside6/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/pyside6/doc/tutorials/portingguide/chapter1/main.py b/sources/pyside6/doc/tutorials/portingguide/chapter1/main.py
new file mode 100644
index 000000000..5b42a61a1
--- /dev/null
+++ b/sources/pyside6/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 PySide6.QtSql import QSqlQueryModel
+from PySide6.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/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.cpp b/sources/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.cpp
new file mode 100644
index 000000000..4115f80cf
--- /dev/null
+++ b/sources/pyside6/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/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.h b/sources/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.h
new file mode 100644
index 000000000..f1b432699
--- /dev/null
+++ b/sources/pyside6/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/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.py b/sources/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.py
new file mode 100644
index 000000000..d6c16972b
--- /dev/null
+++ b/sources/pyside6/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 PySide6.QtSql import QSqlRelationalDelegate
+from PySide6.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate,
+ QStyle, QStyleOptionViewItem)
+from PySide6.QtGui import QMouseEvent, QPixmap, QPalette, QImage
+from PySide6.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/pyside6/doc/tutorials/portingguide/chapter2/chapter2.rst b/sources/pyside6/doc/tutorials/portingguide/chapter2/chapter2.rst
new file mode 100644
index 000000000..a574218fd
--- /dev/null
+++ b/sources/pyside6/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/pyside6/doc/tutorials/portingguide/chapter2/createdb.py b/sources/pyside6/doc/tutorials/portingguide/chapter2/createdb.py
new file mode 100644
index 000000000..c3f27162d
--- /dev/null
+++ b/sources/pyside6/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 PySide6.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/pyside6/doc/tutorials/portingguide/chapter2/images/chapter2_books.png b/sources/pyside6/doc/tutorials/portingguide/chapter2/images/chapter2_books.png
new file mode 100644
index 000000000..e456b7d8f
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/portingguide/chapter2/images/chapter2_books.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.png b/sources/pyside6/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.png
new file mode 100644
index 000000000..82a5f449c
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/portingguide/chapter2/images/star.png b/sources/pyside6/doc/tutorials/portingguide/chapter2/images/star.png
new file mode 100644
index 000000000..87f4464bd
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/portingguide/chapter2/images/star.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/portingguide/chapter2/main.py b/sources/pyside6/doc/tutorials/portingguide/chapter2/main.py
new file mode 100644
index 000000000..c9c907f90
--- /dev/null
+++ b/sources/pyside6/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 PySide6.QtCore import Qt
+from PySide6.QtSql import QSqlQueryModel
+from PySide6.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/pyside6/doc/tutorials/portingguide/chapter3/bookdelegate-old.py b/sources/pyside6/doc/tutorials/portingguide/chapter3/bookdelegate-old.py
new file mode 100644
index 000000000..0c0f1ee69
--- /dev/null
+++ b/sources/pyside6/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 PySide6.QtSql import QSqlRelationalDelegate
+from PySide6.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate,
+ QStyle, QStyleOptionViewItem)
+from PySide6.QtGui import QMouseEvent, QPixmap, QPalette, QImage
+from PySide6.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/pyside6/doc/tutorials/portingguide/chapter3/bookdelegate.py b/sources/pyside6/doc/tutorials/portingguide/chapter3/bookdelegate.py
new file mode 100644
index 000000000..4594508d5
--- /dev/null
+++ b/sources/pyside6/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 PySide6.QtSql import QSqlRelationalDelegate
+from PySide6.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate,
+ QStyle, QStyleOptionViewItem)
+from PySide6.QtGui import QMouseEvent, QPixmap, QPalette, QImage
+from PySide6.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/pyside6/doc/tutorials/portingguide/chapter3/books.qrc b/sources/pyside6/doc/tutorials/portingguide/chapter3/books.qrc
new file mode 100644
index 000000000..d6ad21337
--- /dev/null
+++ b/sources/pyside6/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/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.cpp b/sources/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.cpp
new file mode 100644
index 000000000..76f3c9da8
--- /dev/null
+++ b/sources/pyside6/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/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.py b/sources/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.py
new file mode 100644
index 000000000..0b20e8ad5
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.py
@@ -0,0 +1,138 @@
+#############################################################################
+##
+## 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 PySide6.QtGui import QAction
+from PySide6.QtWidgets import (QAbstractItemView, QDataWidgetMapper,
+ QHeaderView, QMainWindow, QMessageBox)
+from PySide6.QtGui import QKeySequence
+from PySide6.QtSql import (QSqlRelation, QSqlRelationalTableModel, QSqlTableModel,
+ QSqlError)
+from PySide6.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/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.ui b/sources/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.ui
new file mode 100644
index 000000000..e1668288f
--- /dev/null
+++ b/sources/pyside6/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/pyside6/doc/tutorials/portingguide/chapter3/chapter3.rst b/sources/pyside6/doc/tutorials/portingguide/chapter3/chapter3.rst
new file mode 100644
index 000000000..b2e06b144
--- /dev/null
+++ b/sources/pyside6/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::
+
+ pyside6-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: 57-140
+
+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 ``pyside6-rcc`` tool on the ``books.qrc`` file
+to generate ``rc_books.py``.
+
+.. code-block::
+
+ pyside6-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/pyside6/doc/tutorials/portingguide/chapter3/createdb.py b/sources/pyside6/doc/tutorials/portingguide/chapter3/createdb.py
new file mode 100644
index 000000000..c3f27162d
--- /dev/null
+++ b/sources/pyside6/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 PySide6.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/pyside6/doc/tutorials/portingguide/chapter3/images/chapter3-books.png b/sources/pyside6/doc/tutorials/portingguide/chapter3/images/chapter3-books.png
new file mode 100644
index 000000000..952cb14e8
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/portingguide/chapter3/images/chapter3-books.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/portingguide/chapter3/images/star.png b/sources/pyside6/doc/tutorials/portingguide/chapter3/images/star.png
new file mode 100644
index 000000000..87f4464bd
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/portingguide/chapter3/images/star.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/portingguide/chapter3/main-old.py b/sources/pyside6/doc/tutorials/portingguide/chapter3/main-old.py
new file mode 100644
index 000000000..675f8032b
--- /dev/null
+++ b/sources/pyside6/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 PySide6.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/pyside6/doc/tutorials/portingguide/chapter3/main.py b/sources/pyside6/doc/tutorials/portingguide/chapter3/main.py
new file mode 100644
index 000000000..e957869ac
--- /dev/null
+++ b/sources/pyside6/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 PySide6.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/pyside6/doc/tutorials/portingguide/hello_world_ex.py b/sources/pyside6/doc/tutorials/portingguide/hello_world_ex.py
new file mode 100644
index 000000000..81b63b7ea
--- /dev/null
+++ b/sources/pyside6/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 PySide6.QtWidgets import (QApplication, QLabel,
+ QPushButton, QVBoxLayout, QWidget)
+from PySide6.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/pyside6/doc/tutorials/portingguide/index.rst b/sources/pyside6/doc/tutorials/portingguide/index.rst
new file mode 100644
index 000000000..12f76d3fc
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/portingguide/index.rst
@@ -0,0 +1,194 @@
+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 PySide6.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. Python 3 uses
+ "bytes". QString is represented as an encoded human readable string,
+ which means it is a "str".
+
+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/pyside6/doc/tutorials/qmlapp/logo.png b/sources/pyside6/doc/tutorials/qmlapp/logo.png
new file mode 100644
index 000000000..30c621c9c
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlapp/logo.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/qmlapp/main.py b/sources/pyside6/doc/tutorials/qmlapp/main.py
new file mode 100644
index 000000000..5946538d9
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlapp/main.py
@@ -0,0 +1,82 @@
+#############################################################################
+##
+## 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$
+##
+#############################################################################
+
+#!/usr/bin/env python
+# -*- conding: utf-8 -*-
+
+import os, sys, urllib.request, json
+import PySide6.QtQml
+from PySide6.QtQuick import QQuickView
+from PySide6.QtCore import QStringListModel, Qt, QUrl
+from PySide6.QtGui import QGuiApplication
+
+if __name__ == '__main__':
+
+ #get our data
+ url = "http://country.io/names.json"
+ response = urllib.request.urlopen(url)
+ data = json.loads(response.read().decode('utf-8'))
+
+ #Format and sort the data
+ data_list = list(data.values())
+ data_list.sort()
+
+ #Set up the application window
+ app = QGuiApplication(sys.argv)
+ view = QQuickView()
+ view.setResizeMode(QQuickView.SizeRootObjectToView)
+
+ #Expose the list to the Qml code
+ my_model = QStringListModel()
+ my_model.setStringList(data_list)
+ view.rootContext().setContextProperty("myModel",my_model)
+
+ #Load the QML file
+ qml_file = os.path.join(os.path.dirname(__file__),"view.qml")
+ view.setSource(QUrl.fromLocalFile(os.path.abspath(qml_file)))
+
+ #Show the window
+ if view.status() == QQuickView.Error:
+ sys.exit(-1)
+ view.show()
+
+ #execute and cleanup
+ app.exec_()
+ del view
diff --git a/sources/pyside6/doc/tutorials/qmlapp/newpyproject.png b/sources/pyside6/doc/tutorials/qmlapp/newpyproject.png
new file mode 100644
index 000000000..93968a52d
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlapp/newpyproject.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/qmlapp/projectsmode.png b/sources/pyside6/doc/tutorials/qmlapp/projectsmode.png
new file mode 100644
index 000000000..c66d88723
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlapp/projectsmode.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/qmlapp/pyprojname.png b/sources/pyside6/doc/tutorials/qmlapp/pyprojname.png
new file mode 100644
index 000000000..98328074d
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlapp/pyprojname.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/qmlapp/pyprojxplor.png b/sources/pyside6/doc/tutorials/qmlapp/pyprojxplor.png
new file mode 100644
index 000000000..e01e2ebeb
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlapp/pyprojxplor.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/qmlapp/qmlapplication.png b/sources/pyside6/doc/tutorials/qmlapp/qmlapplication.png
new file mode 100644
index 000000000..ec0ad3dea
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlapp/qmlapplication.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/qmlapp/qmlapplication.rst b/sources/pyside6/doc/tutorials/qmlapp/qmlapplication.rst
new file mode 100644
index 000000000..082490f4b
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlapp/qmlapplication.rst
@@ -0,0 +1,132 @@
+#########################
+QML Application Tutorial
+#########################
+
+This tutorial provides a quick walk-through of a python application
+that loads a QML file. QML is a declarative language that lets you
+design UIs faster than a traditional language, such as C++. The
+QtQml and QtQuick modules provides the necessary infrastructure for
+QML-based UIs.
+
+In this tutorial, you'll also learn how to provide data from Python
+as a QML context property, which is then consumed by the ListView
+defined in the QML file.
+
+Before you begin, install the following prerequisites:
+
+* The `PySide6 <https://pypi.org/project/PySide6/>`_ Python packages.
+* Qt Creator v4.9 beta1 or later from
+ `http://download.qt.io
+ <http://download.qt.io/snapshots/qtcreator/4.9/4.9.0-beta1/>`_.
+
+
+The following step-by-step instructions guide you through application
+development process using Qt Creator:
+
+#. Open Qt Creator and select **File > New File or Project..** menu item
+ to open following dialog:
+
+ .. image:: newpyproject.png
+
+#. Select **Qt for Python - Empty** from the list of application templates
+ and select **Choose**.
+
+ .. image:: pyprojname.png
+
+#. Give a **Name** to your project, choose its location in the
+ filesystem, and select **Finish** to create an empty ``main.py``
+ and ``main.pyproject``.
+
+ .. image:: pyprojxplor.png
+
+ This should create a ``main.py`` and ```main.pyproject`` files
+ for the project.
+
+#. Download :download:`view.qml<view.qml>` and :download:`logo.png <logo.png>`
+ and move them to your project folder.
+
+#. Double-click on ``main.pyproject`` to open it in edit mode, and append
+ ``view.qml`` and ``logo.png`` to the **files** list. This is how your
+ project file should look after this change:
+
+ .. code::
+
+ {
+ "files": ["main.py", "view.qml", "logo.png"]
+ }
+
+#. Now that you have the necessary bits for the application, import the
+ Python modules in your ``main.py``, and download country data and
+ format it:
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 40-60
+ :emphasize-lines: 12-20
+
+#. Now, set up the application window using
+ :ref:`PySide6.QtGui.QGuiApplication<qguiapplication>`, which manages the application-wide
+ settings.
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 40-65
+ :emphasize-lines: 23-25
+
+ .. note:: Setting the resize policy is important if you want the
+ root item to resize itself to fit the window or vice-a-versa.
+ Otherwise, the root item will retain its original size on
+ resizing the window.
+
+#. You can now expose the ``data_list`` variable as a QML context
+ property, which will be consumed by the QML ListView item in ``view.qml``.
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 40-70
+ :emphasize-lines: 27-30
+
+#. Load the ``view.qml`` to the ``QQuickView`` and call ``show()`` to
+ display the application window.
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 40-79
+ :emphasize-lines: 33-39
+
+#. Finally, execute the application to start the event loop and clean up.
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 40-
+ :emphasize-lines: 41-43
+
+#. Your application is ready to be run now. Select **Projects** mode to
+ choose the Python version to run it.
+
+ .. image:: projectsmode.png
+
+Run the application by using the ``CTRL+R`` keyboard shortcut to see if it
+looks like this:
+
+.. image:: qmlapplication.png
+
+You could also watch the following video tutorial for guidance to develop
+this application:
+
+.. raw:: html
+
+ <div style="position: relative; padding-bottom: 56.25%; height: 0;
+ overflow: hidden; max-width: 100%; height: auto;">
+ <iframe src="https://www.youtube.com/embed/JxfiUx60Mbg" frameborder="0"
+ allowfullscreen style="position: absolute; top: 0; left: 0;
+ width: 100%; height: 100%;">
+ </iframe>
+ </div>
+
+********************
+Related information
+********************
+
+* `QML Reference <https://doc.qt.io/qt-5/qmlreference.html>`_
+* :doc:`../qmlintegration/qmlintegration`
diff --git a/sources/pyside6/doc/tutorials/qmlapp/view.qml b/sources/pyside6/doc/tutorials/qmlapp/view.qml
new file mode 100644
index 000000000..c75052b29
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlapp/view.qml
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qt for Python.
+**
+** $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$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+Page {
+ width: 640
+ height: 480
+
+ header: Label {
+ color: "#15af15"
+ text: qsTr("Where do people use Qt?")
+ font.pointSize: 17
+ font.bold: true
+ font.family: "Arial"
+ renderType: Text.NativeRendering
+ horizontalAlignment: Text.AlignHCenter
+ padding: 10
+ }
+ Rectangle {
+ id: root
+ width: parent.width
+ height: parent.height
+
+ Image {
+ id: image
+ fillMode: Image.PreserveAspectFit
+ anchors.centerIn: root
+ source: "./logo.png"
+ opacity: 0.5
+
+ }
+
+ ListView {
+ id: view
+ anchors.fill: root
+ anchors.margins: 25
+ model: myModel
+ delegate: Text {
+ anchors.leftMargin: 50
+ font.pointSize: 15
+ horizontalAlignment: Text.AlignHCenter
+ text: display
+ }
+ }
+ }
+ NumberAnimation {
+ id: anim
+ running: true
+ target: view
+ property: "contentY"
+ duration: 500
+ }
+}
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/main.py b/sources/pyside6/doc/tutorials/qmlintegration/main.py
new file mode 100644
index 000000000..884d239b3
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlintegration/main.py
@@ -0,0 +1,113 @@
+#############################################################################
+##
+## 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 os.path import abspath, dirname, join
+
+from PySide6.QtCore import QObject, Slot
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from style_rc import *
+
+
+class Bridge(QObject):
+
+ @Slot(str, result=str)
+ def getColor(self, color_name):
+ if color_name.lower() == "red":
+ return "#ef9a9a"
+ elif color_name.lower() == "green":
+ return "#a5d6a7"
+ elif color_name.lower() == "blue":
+ return "#90caf9"
+ else:
+ return "white"
+
+ @Slot(float, result=int)
+ def getSize(self, s):
+ size = int(s * 42) # Maximum font size
+ if size <= 0:
+ return 1
+ else:
+ return size
+
+ @Slot(str, result=bool)
+ def getItalic(self, s):
+ if s.lower() == "italic":
+ return True
+ else:
+ return False
+
+ @Slot(str, result=bool)
+ def getBold(self, s):
+ if s.lower() == "bold":
+ return True
+ else:
+ return False
+
+ @Slot(str, result=bool)
+ def getUnderline(self, s):
+ if s.lower() == "underline":
+ return True
+ else:
+ return False
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ engine = QQmlApplicationEngine()
+
+ # Instance of the Python object
+ bridge = Bridge()
+
+ # Expose the Python object to QML
+ context = engine.rootContext()
+ context.setContextProperty("con", bridge)
+
+ # Get the path of the current directory, and then add the name
+ # of the QML file, to load it.
+ qmlFile = join(dirname(__file__), 'view.qml')
+ engine.load(abspath(qmlFile))
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ sys.exit(app.exec_())
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/qmlintegration.rst b/sources/pyside6/doc/tutorials/qmlintegration/qmlintegration.rst
new file mode 100644
index 000000000..5bbb89609
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlintegration/qmlintegration.rst
@@ -0,0 +1,111 @@
+########################
+QML Integration Tutorial
+########################
+
+This tutorial provides a quick walk-through of a python application that loads, and interacts with
+a QML file. QML is a declarative language that lets you design UIs faster than a traditional
+language, such as C++. The QtQml and QtQuick modules provides the necessary infrastructure for
+QML-based UIs.
+
+In this tutorial, you will learn how to integrate Python with a QML application through a context
+property. This mechanism will help us to understand how to use Python as a backend for certain
+signals from the UI elements in the QML interface. Additionally, you will learn how to provide
+a modern look to your QML application using one of the features from Qt Quick Controls 2.
+
+The tutorial is based on an application that allow you to set many text properties, like increasing
+the font size, changing the color, changing the style, and so on. Before you begin, install the
+`PySide6 <https://pypi.org/project/PySide6/>`_ Python packages.
+
+The following step-by-step process will guide you through the key elements of the QML based
+application and PySide6 integration:
+
+#. First, let's start with the following QML-based UI:
+
+ .. image:: textproperties_default.png
+
+ The design is based on a `GridLayout`, containing two `ColumnLayout`.
+ Inside the UI you will find many `RadioButton`, `Button`, and a `Slider`.
+
+#. With the QML file in place, you can load it from Python:
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 98-108
+ :emphasize-lines: 6,9
+
+ Notice that we specify the name of the context property, **con**,
+ and also we explicitly load our QML file.
+
+#. Define the `Bridge` class, containing all the logic for the context property:
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 51-91
+
+#. Now, go back to the QML file and connect the signals to the slots defined in the `Bridge` class:
+
+ .. literalinclude:: view.qml
+ :linenos:
+ :lines: 85-93
+ :emphasize-lines: 5-7
+
+ The properties *Italic*, *Bold*, and *Underline* are mutually
+ exclusive, this means only one can be active at any time.
+ To achieve this each time we select one of these options, we
+ check the three properties via the context property as you can
+ see in the above snippet.
+ Only one of the three will return *True*, while the other two
+ will return *False*, that is how we make sure only one is being
+ applied to the text.
+
+#. Each slot verifies if the selected option contains the text associated
+ to the property:
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 79-84
+ :emphasize-lines: 4,6
+
+ Returning *True* or *False* allows you to activate and deactivate
+ the properties of the QML UI elements.
+
+ It is also possible to return other values that are not *Boolean*,
+ like the slot in charge of returning the font size:
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 64-70
+
+#. Now, for changing the look of our application, you have two options:
+
+ 1. Use the command line: execute the python file adding the option, `--style`::
+
+ python main.py --style material
+
+ 2. Use a `qtquickcontrols2.conf` file:
+
+ .. literalinclude:: qtquickcontrols2.conf
+ :linenos:
+
+ Then add it to your `.qrc` file:
+
+ .. literalinclude:: style.qrc
+ :linenos:
+
+ Generate the *rc* file running, `pyside6-rcc style.qrc > style_rc.py`
+ And finally import it from your `main.py` script.
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 41-48
+ :emphasize-lines: 8
+
+ 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
+:download:`main.py <main.py>` to try this example.
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/qtquickcontrols2.conf b/sources/pyside6/doc/tutorials/qmlintegration/qtquickcontrols2.conf
new file mode 100644
index 000000000..850646021
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlintegration/qtquickcontrols2.conf
@@ -0,0 +1,10 @@
+[Controls]
+Style=Material
+
+[Universal]
+Theme=System
+Accent=Red
+
+[Material]
+Theme=Dark
+Accent=Red
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/style.qrc b/sources/pyside6/doc/tutorials/qmlintegration/style.qrc
new file mode 100644
index 000000000..e313f5ed6
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlintegration/style.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+ <file>qtquickcontrols2.conf</file>
+</qresource>
+</RCC>
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/textproperties_default.png b/sources/pyside6/doc/tutorials/qmlintegration/textproperties_default.png
new file mode 100644
index 000000000..cfeac9368
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlintegration/textproperties_default.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/textproperties_material.png b/sources/pyside6/doc/tutorials/qmlintegration/textproperties_material.png
new file mode 100644
index 000000000..47866c10e
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlintegration/textproperties_material.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/view.qml b/sources/pyside6/doc/tutorials/qmlintegration/view.qml
new file mode 100644
index 000000000..97968d691
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlintegration/view.qml
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** 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 QtQuick 2.0
+import QtQuick.Layouts 1.12
+import QtQuick.Controls 2.12
+import QtQuick.Window 2.12
+import QtQuick.Controls.Material 2.12
+
+ApplicationWindow {
+ id: page
+ width: 800
+ height: 400
+ visible: true
+
+ GridLayout {
+ id: grid
+ columns: 2
+ rows: 3
+
+ ColumnLayout {
+ spacing: 2
+ Layout.preferredWidth: 400
+
+ Text {
+ id: leftlabel
+ Layout.alignment: Qt.AlignHCenter
+ color: "white"
+ font.pointSize: 16
+ text: "Qt for Python"
+ Layout.preferredHeight: 100
+ Material.accent: Material.Green
+ }
+
+ RadioButton {
+ id: italic
+ text: "Italic"
+ onToggled: {
+ leftlabel.font.italic = con.getItalic(italic.text)
+ leftlabel.font.bold = con.getBold(italic.text)
+ leftlabel.font.underline = con.getUnderline(italic.text)
+
+ }
+ }
+ RadioButton {
+ id: bold
+ text: "Bold"
+ onToggled: {
+ leftlabel.font.italic = con.getItalic(bold.text)
+ leftlabel.font.bold = con.getBold(bold.text)
+ leftlabel.font.underline = con.getUnderline(bold.text)
+ }
+ }
+ RadioButton {
+ id: underline
+ text: "Underline"
+ onToggled: {
+ leftlabel.font.italic = con.getItalic(underline.text)
+ leftlabel.font.bold = con.getBold(underline.text)
+ leftlabel.font.underline = con.getUnderline(underline.text)
+ }
+ }
+ RadioButton {
+ id: noneradio
+ text: "None"
+ checked: true
+ onToggled: {
+ leftlabel.font.italic = con.getItalic(noneradio.text)
+ leftlabel.font.bold = con.getBold(noneradio.text)
+ leftlabel.font.underline = con.getUnderline(noneradio.text)
+ }
+ }
+ }
+
+ ColumnLayout {
+ id: rightcolumn
+ spacing: 2
+ Layout.columnSpan: 1
+ Layout.preferredWidth: 400
+ Layout.preferredHeight: 400
+ Layout.fillWidth: true
+
+ RowLayout {
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+
+
+ Button {
+ id: red
+ text: "Red"
+ highlighted: true
+ Material.accent: Material.Red
+ onClicked: {
+ leftlabel.color = con.getColor(red.text)
+ }
+ }
+ Button {
+ id: green
+ text: "Green"
+ highlighted: true
+ Material.accent: Material.Green
+ onClicked: {
+ leftlabel.color = con.getColor(green.text)
+ }
+ }
+ Button {
+ id: blue
+ text: "Blue"
+ highlighted: true
+ Material.accent: Material.Blue
+ onClicked: {
+ leftlabel.color = con.getColor(blue.text)
+ }
+ }
+ Button {
+ id: nonebutton
+ text: "None"
+ highlighted: true
+ Material.accent: Material.BlueGrey
+ onClicked: {
+ leftlabel.color = con.getColor(nonebutton.text)
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ Text {
+ id: rightlabel
+ color: "white"
+ text: "Font size"
+ Material.accent: Material.White
+ }
+ Slider {
+ width: rightcolumn.width*0.6
+ Layout.alignment: Qt.AlignRight
+ id: slider
+ value: 0.5
+ onValueChanged: {
+ leftlabel.font.pointSize = con.getSize(value)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/sources/pyside6/doc/tutorials/qmlsqlintegration/chat.qml b/sources/pyside6/doc/tutorials/qmlsqlintegration/chat.qml
new file mode 100644
index 000000000..487f5b36c
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlsqlintegration/chat.qml
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Layouts 1.12
+import QtQuick.Controls 2.12
+
+ApplicationWindow {
+ id: window
+ title: qsTr("Chat")
+ width: 640
+ height: 960
+ visible: true
+ ColumnLayout {
+ anchors.fill: parent
+
+ ListView {
+ id: listView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: pane.leftPadding + messageField.leftPadding
+ displayMarginBeginning: 40
+ displayMarginEnd: 40
+ verticalLayoutDirection: ListView.BottomToTop
+ spacing: 12
+ model: chat_model
+ delegate: Column {
+ readonly property bool sentByMe: model.recipient !== "Me"
+ anchors.right: sentByMe ? parent.right : undefined
+ spacing: 6
+
+ Row {
+ id: messageRow
+ spacing: 6
+ anchors.right: sentByMe ? parent.right : undefined
+
+ Rectangle {
+ width: Math.min(messageText.implicitWidth + 24, listView.width - messageRow.spacing)
+ height: messageText.implicitHeight + 24
+ radius: 15
+ color: sentByMe ? "lightgrey" : "#ff627c"
+
+ Label {
+ id: messageText
+ text: model.message
+ color: sentByMe ? "black" : "white"
+ anchors.fill: parent
+ anchors.margins: 12
+ wrapMode: Label.Wrap
+ }
+ }
+ }
+
+ Label {
+ id: timestampText
+ text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm")
+ color: "lightgrey"
+ anchors.right: sentByMe ? parent.right : undefined
+ }
+ }
+
+ ScrollBar.vertical: ScrollBar {}
+ }
+
+ Pane {
+ id: pane
+ Layout.fillWidth: true
+
+ RowLayout {
+ width: parent.width
+
+ TextArea {
+ id: messageField
+ Layout.fillWidth: true
+ placeholderText: qsTr("Compose message")
+ wrapMode: TextArea.Wrap
+ }
+
+ Button {
+ id: sendButton
+ text: qsTr("Send")
+ enabled: messageField.length > 0
+ onClicked: {
+ chat_model.send_message("machine", messageField.text, "Me");
+ messageField.text = "";
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/sources/pyside6/doc/tutorials/qmlsqlintegration/example_list_view.png b/sources/pyside6/doc/tutorials/qmlsqlintegration/example_list_view.png
new file mode 100644
index 000000000..a0c189665
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlsqlintegration/example_list_view.png
Binary files differ
diff --git a/sources/pyside6/doc/tutorials/qmlsqlintegration/main.py b/sources/pyside6/doc/tutorials/qmlsqlintegration/main.py
new file mode 100644
index 000000000..50a3d43fe
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlsqlintegration/main.py
@@ -0,0 +1,85 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python project.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import logging
+
+from PySide6.QtCore import QDir, QFile, QUrl
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+from PySide6.QtSql import QSqlDatabase
+
+from sqlDialog import SqlConversationModel
+
+logging.basicConfig(filename="chat.log", level=logging.DEBUG)
+logger = logging.getLogger("logger")
+
+
+def connectToDatabase():
+ database = QSqlDatabase.database()
+ if not database.isValid():
+ database = QSqlDatabase.addDatabase("QSQLITE")
+ if not database.isValid():
+ logger.error("Cannot add database")
+
+ write_dir = QDir()
+ if not write_dir.mkpath("."):
+ logger.error("Failed to create writable directory")
+
+ # Ensure that we have a writable location on all devices.
+ filename = "{}/chat-database.sqlite3".format(write_dir.absolutePath())
+
+ # When using the SQLite driver, open() will create the SQLite
+ # database if it doesn't exist.
+ database.setDatabaseName(filename)
+ if not database.open():
+ logger.error("Cannot open database")
+ QFile.remove(filename)
+
+
+if __name__ == "__main__":
+ app = QGuiApplication()
+ connectToDatabase()
+ sql_conversation_model = SqlConversationModel()
+
+ engine = QQmlApplicationEngine()
+ # Export pertinent objects to QML
+ engine.rootContext().setContextProperty("chat_model", sql_conversation_model)
+ engine.load(QUrl("chat.qml"))
+
+ app.exec_()
diff --git a/sources/pyside6/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst b/sources/pyside6/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst
new file mode 100644
index 000000000..0df6dd30c
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst
@@ -0,0 +1,225 @@
+QML, SQL and PySide Integration Tutorial
+########################################
+
+This tutorial is very similar to the `Qt Chat Tutorial`_ one but it focuses on explaining how to
+integrate a SQL database into a PySide6 application using QML for its UI.
+
+.. _`Qt Chat Tutorial`: https://doc.qt.io/qt-5/qtquickcontrols-chattutorial-example.html
+
+sqlDialog.py
+------------
+
+We import the pertinent libraries to our program, define a global variable that hold the
+name of our table, and define the global function ``createTable()`` that creates a new table if it
+doesn't already exist.
+The database contains a single line to mock the beginning of a conversation.
+
+ .. literalinclude:: sqlDialog.py
+ :linenos:
+ :lines: 40-77
+
+The ``SqlConversationModel`` class offers the read-only data model required for the non-editable
+contacts list. It derives from the :ref:`QSqlQueryModel` class, which is the logical choice for
+this use case.
+Then, we proceed to create the table, set its name to the one defined previously with the
+:meth:`~.QSqlTableModel.setTable` method.
+We add the necessary attributes to the table, to have a program that reflects the idea
+of a chat application.
+
+ .. literalinclude:: sqlDialog.py
+ :linenos:
+ :lines: 80-91
+
+In ``setRecipient()``, you set a filter over the returned results from the database, and
+emit a signal every time the recipient of the message changes.
+
+ .. literalinclude:: sqlDialog.py
+ :linenos:
+ :lines: 93-103
+
+The ``data()`` function falls back to ``QSqlTableModel``'s implementation if the role is not a
+custom user role.
+If you get a user role, we can subtract :meth:`~.QtCore.Qt.UserRole` from it to get the index of
+that field, and then use that index to find the value to be returned.
+
+ .. literalinclude:: sqlDialog.py
+ :linenos:
+ :lines: 105-112
+
+
+In ``roleNames()``, we return a Python dictionary with our custom role and role names as key-values
+pairs, so we can use these roles in QML.
+Alternatively, it can be useful to declare an Enum to hold all of the role values.
+Note that ``names`` has to be a hash to be used as a dictionary key,
+and that's why we're using the ``hash`` function.
+
+ .. literalinclude:: sqlDialog.py
+ :linenos:
+ :lines: 114-128
+
+The ``send_message()`` function uses the given recipient and message to insert a new record into
+the database.
+Using :meth:`~.QSqlTableModel.OnManualSubmit` requires you to also call ``submitAll()``,
+since all the changes will be cached in the model until you do so.
+
+ .. literalinclude:: sqlDialog.py
+ :linenos:
+ :lines: 130-146
+
+chat.qml
+--------
+
+Let's look at the ``chat.qml`` file.
+
+ .. literalinclude:: chat.qml
+ :linenos:
+ :lines: 40-42
+
+First, import the Qt Quick module.
+This gives us access to graphical primitives such as Item, Rectangle, Text, and so on.
+For a full list of types, see the `Qt Quick QML Types`_ documentation.
+We then add QtQuick.Layouts import, which we'll cover shortly.
+
+Next, import the Qt Quick Controls module.
+Among other things, this provides access to ``ApplicationWindow``, which replaces the existing
+root type, Window:
+
+Let's step through the ``chat.qml`` file.
+
+ .. literalinclude:: chat.qml
+ :linenos:
+ :lines: 44-49
+
+``ApplicationWindow`` is a Window with some added convenience for creating a header and a footer.
+It also provides the foundation for popups and supports some basic styling, such as the background
+color.
+
+There are three properties that are almost always set when using ApplicationWindow: ``width``,
+``height``, and ``visible``.
+Once we've set these, we have a properly sized, empty window ready to be filled with content.
+
+There are two ways of laying out items in QML: `Item Positioners`_ and `Qt Quick Layouts`_.
+
+- Item positioners (`Row`_, `Column`_, and so on) are useful for situations where the size of items
+ is known or fixed, and all that is required is to neatly position them in a certain formation.
+- The layouts in Qt Quick Layouts can both position and resize items, making them well suited for
+ resizable user interfaces.
+ Below, we use `ColumnLayout`_ to vertically lay out a `ListView`_ and a `Pane`_.
+
+ .. literalinclude:: chat.qml
+ :linenos:
+ :lines: 50-53
+
+Pane is basically a rectangle whose color comes from the application's style.
+It's similar to `Frame`_, but it has no stroke around its border.
+
+Items that are direct children of a layout have various `attached properties`_ available to them.
+We use `Layout.fillWidth`_ and `Layout.fillHeight`_ on the `ListView`_ to ensure that it takes as
+much space within the `ColumnLayout`_ as it can, and the same is done for the Pane.
+As `ColumnLayout`_ is a vertical layout, there aren't any items to the left or right of each child,
+so this results in each item consuming the entire width of the layout.
+
+On the other hand, the `Layout.fillHeight`_ statement in the `ListView`_ enables it to occupy the
+remaining space that is left after accommodating the Pane.
+
+.. _Item Positioners: https://doc.qt.io/qt-5/qtquick-positioning-layouts.html
+.. _Qt Quick Layouts: https://doc.qt.io/qt-5/qtquicklayouts-index.html
+.. _Row: https://doc.qt.io/qt-5/qml-qtquick-row.html
+.. _Column: https://doc.qt.io/qt-5/qml-qtquick-column.html
+.. _ColumnLayout: https://doc.qt.io/qt-5/qml-qtquick-layouts-columnlayout.html
+.. _ListView: https://doc.qt.io/qt-5/qml-qtquick-listview.html
+.. _Pane: https://doc.qt.io/qt-5/qml-qtquick-controls2-pane.html
+.. _Frame: https://doc.qt.io/qt-5/qml-qtquick-controls2-frame.html
+.. _attached properties: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html
+.. _Layout.fillWidth: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillWidth-attached-prop
+.. _Layout.fillHeight: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillHeight-attached-prop
+.. _ListView: https://doc.qt.io/qt-5/qml-qtquick-listview.html
+.. _Qt Quick QML Types: https://doc.qt.io/qt-5/qtquick-qmlmodule.html
+
+Let's look at the ``Listview`` in detail:
+
+ .. literalinclude:: chat.qml
+ :linenos:
+ :lines: 53-99
+
+After filling the ``width`` and ``height`` of its parent, we also set some margins on the view.
+
+
+Next, we set `displayMarginBeginning`_ and `displayMarginEnd`_.
+These properties ensure that the delegates outside the view don't disappear when you
+scroll at the edges of the view.
+To get a better understanding, consider commenting out the properties and then rerun your code.
+Now watch what happens when you scroll the view.
+
+We then flip the vertical direction of the view, so that first items are at the bottom.
+
+Additionally, messages sent by the contact should be distinguished from those sent by a contact.
+For now, when a message is sent by you, we set a ``sentByMe`` property, to alternate between
+different contacts.
+Using this property, we distinguish between different contacts in two ways:
+
+* Messages sent by the contact are aligned to the right side of the screen by setting
+ ``anchors.right`` to ``parent.right``.
+* We change the color of the rectangle depending on the contact.
+ Since we don't want to display dark text on a dark background, and vice versa, we also set the
+ text color depending on who the contact is.
+
+At the bottom of the screen, we place a `TextArea`_ item to allow multi-line text input, and a
+button to send the message.
+We use Pane to cover the area under these two items:
+
+ .. literalinclude:: chat.qml
+ :linenos:
+ :lines: 101-125
+
+The `TextArea`_ should fill the available width of the screen.
+We assign some placeholder text to provide a visual cue to the contact as to where they should begin
+typing.
+The text within the input area is wrapped to ensure that it does not go outside of the screen.
+
+Lastly, we have a button that allows us to call the ``send_message`` method we defined on
+``sqlDialog.py``, since we're just having a mock up example here and there is only one possible
+recipient and one possible sender for this conversation we're just using strings here.
+
+.. _displayMarginBeginning: https://doc.qt.io/qt-5/qml-qtquick-listview.html#displayMarginBeginning-prop
+.. _displayMarginEnd: https://doc.qt.io/qt-5/qml-qtquick-listview.html#displayMarginEnd-prop
+.. _TextArea: https://doc.qt.io/qt-5/qml-qtquick-controls2-textarea.html
+
+
+main.py
+-------
+
+We use ``logging`` instead of Python's ``print()``, because it provides a better way to control the
+messages levels that our application will generate (errors, warnings, and information messages).
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 40-50
+
+``connectToDatabase()`` creates a connection with the SQLite database, creating the actual file
+if it doesn't already exist.
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 53-72
+
+
+
+A few interesting things happen in the ``main`` function:
+
+- Declaring a :ref:`QGuiApplication`.
+ You should use a :ref:`QGuiApplication` instead of :ref:`QApplication` because we're not
+ using the **QtWidgets** module.
+- Connecting to the database,
+- Declaring a :ref:`QQmlApplicationEngine`.
+ This allows you to access the QML context property to connect Python
+ and QML from the conversation model we built on ``sqlDialog.py``.
+- Loading the ``.qml`` file that defines the UI.
+
+Finally, the Qt application runs, and your program starts.
+
+ .. literalinclude:: main.py
+ :linenos:
+ :lines: 75-85
+
+.. image:: example_list_view.png
diff --git a/sources/pyside6/doc/tutorials/qmlsqlintegration/sqlDialog.py b/sources/pyside6/doc/tutorials/qmlsqlintegration/sqlDialog.py
new file mode 100644
index 000000000..a9745051b
--- /dev/null
+++ b/sources/pyside6/doc/tutorials/qmlsqlintegration/sqlDialog.py
@@ -0,0 +1,146 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python project.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import datetime
+import logging
+
+from PySide6.QtCore import Qt, Slot
+from PySide6.QtSql import QSqlDatabase, QSqlQuery, QSqlRecord, QSqlTableModel
+
+table_name = "Conversations"
+
+
+def createTable():
+ if table_name in QSqlDatabase.database().tables():
+ return
+
+ query = QSqlQuery()
+ if not query.exec_(
+ """
+ CREATE TABLE IF NOT EXISTS 'Conversations' (
+ 'author' TEXT NOT NULL,
+ 'recipient' TEXT NOT NULL,
+ 'timestamp' TEXT NOT NULL,
+ 'message' TEXT NOT NULL,
+ FOREIGN KEY('author') REFERENCES Contacts ( name ),
+ FOREIGN KEY('recipient') REFERENCES Contacts ( name )
+ )
+ """
+ ):
+ logging.error("Failed to query database")
+
+ # This adds the first message from the Bot
+ # and further development is required to make it interactive.
+ query.exec_(
+ """
+ INSERT INTO Conversations VALUES(
+ 'machine', 'Me', '2019-01-07T14:36:06', 'Hello!'
+ )
+ """
+ )
+ logging.info(query)
+
+
+class SqlConversationModel(QSqlTableModel):
+ def __init__(self, parent=None):
+ super(SqlConversationModel, self).__init__(parent)
+
+ createTable()
+ self.setTable(table_name)
+ self.setSort(2, Qt.DescendingOrder)
+ self.setEditStrategy(QSqlTableModel.OnManualSubmit)
+ self.recipient = ""
+
+ self.select()
+ logging.debug("Table was loaded successfully.")
+
+ def setRecipient(self, recipient):
+ if recipient == self.recipient:
+ pass
+
+ self.recipient = recipient
+
+ filter_str = (
+ "(recipient = '{}' AND author = 'Me') OR " "(recipient = 'Me' AND author='{}')"
+ ).format(self.recipient)
+ self.setFilter(filter_str)
+ self.select()
+
+ def data(self, index, role):
+ if role < Qt.UserRole:
+ return QSqlTableModel.data(self, index, role)
+
+ sql_record = QSqlRecord()
+ sql_record = self.record(index.row())
+
+ return sql_record.value(role - Qt.UserRole)
+
+ def roleNames(self):
+ """Converts dict to hash because that's the result expected
+ by QSqlTableModel"""
+ names = {}
+ author = "author".encode()
+ recipient = "recipient".encode()
+ timestamp = "timestamp".encode()
+ message = "message".encode()
+
+ names[hash(Qt.UserRole)] = author
+ names[hash(Qt.UserRole + 1)] = recipient
+ names[hash(Qt.UserRole + 2)] = timestamp
+ names[hash(Qt.UserRole + 3)] = message
+
+ return names
+
+ def send_message(self, recipient, message, author):
+ timestamp = datetime.datetime.now()
+
+ new_record = self.record()
+ new_record.setValue("author", author)
+ new_record.setValue("recipient", recipient)
+ new_record.setValue("timestamp", str(timestamp))
+ new_record.setValue("message", message)
+
+ logging.debug('Message: "{}" \n Received by: "{}"'.format(message, recipient))
+
+ if not self.insertRecord(self.rowCount(), new_record):
+ logging.error("Failed to send message: {}".format(self.lastError().text()))
+ return
+
+ self.submitAll()
+ self.select()