diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-10-18 08:45:00 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-10-18 08:45:41 +0200 |
commit | e66faec4ea11d394652a9cbf02dfe5b407d4c04d (patch) | |
tree | 88a49c37b178511fbc861d7de1403f1c2a7649bd /sources/pyside2 | |
parent | 66a0fa21efb94ac7cc16032213c91a33c0670e2f (diff) | |
parent | d9359a35ff059cc81c155764f4c234d03655bd45 (diff) |
Merge remote-tracking branch 'origin/5.13' into 5.14
Change-Id: I04187b70448808519c92bbb46b09d58e8babc3b3
Diffstat (limited to 'sources/pyside2')
41 files changed, 2593 insertions, 4 deletions
diff --git a/sources/pyside2/CMakeLists.txt b/sources/pyside2/CMakeLists.txt index 16038594a..1efcb53ed 100644 --- a/sources/pyside2/CMakeLists.txt +++ b/sources/pyside2/CMakeLists.txt @@ -179,6 +179,7 @@ string(REGEX MATCHALL "[0-9]+" qt_version_helper "${Qt5Core_VERSION}") list(GET qt_version_helper 0 QT_VERSION_MAJOR) list(GET qt_version_helper 1 QT_VERSION_MINOR) +list(GET qt_version_helper 2 QT_VERSION_PATCH) unset(qt_version_helper) set(PYSIDE_QT_VERSION "${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}" CACHE STRING "Qt version used to compile PySide" FORCE) diff --git a/sources/pyside2/doc/CMakeLists.txt b/sources/pyside2/doc/CMakeLists.txt index 36f770367..9d5a5a0a8 100644 --- a/sources/pyside2/doc/CMakeLists.txt +++ b/sources/pyside2/doc/CMakeLists.txt @@ -70,6 +70,11 @@ foreach(moduleIn ${all_module_shortnames}) endif() endforeach() +#Appending the additional qdocconf that describes the pyside-examples +#doc project. +configure_file("qtmodules/pyside-examples.qdocconf.in" "${CMAKE_CURRENT_LIST_DIR}/qtmodules/pyside-examples.qdocconf" @ONLY) +file(APPEND "pyside.qdocconf.in" "\@CMAKE_CURRENT_LIST_DIR\@/qtmodules/pyside-examples.qdocconf\n") + set(typeSystemDocXmlContents "${typeSystemDocXmlContents}\n</typesystem>\n") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/typesystem_doc.xml" "${typeSystemDocXmlContents}") @@ -82,7 +87,10 @@ set(QDOC_TYPESYSTEM_PATH "${pyside2_SOURCE_DIR}${PATH_SEP}${pyside2_BINARY_DIR}" add_custom_target(qdoc # Use dummy Qt version information, QDoc needs it but has no effect on WebXML output - COMMAND ${CMAKE_COMMAND} -E env BUILDDIR=${CMAKE_CURRENT_LIST_DIR}/src QT_INSTALL_DOCS=${QT_SRC_DIR}/doc QT_VERSION=1.0.0 QT_VER=1.0 QT_VERSION_TAG=100 + COMMAND ${CMAKE_COMMAND} -E env BUILDDIR=${CMAKE_CURRENT_LIST_DIR}/src QT_INSTALL_DOCS=${QT_SRC_DIR}/doc + QT_VERSION=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH} + QT_VER=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR} + QT_VERSION_TAG=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH} qdoc pyside.qdocconf -single-exec -installdir ${DOC_DATA_DIR} -outputdir ${DOC_DATA_DIR} COMMENT "Running qdoc against Qt source code..." SOURCE "pyside.qdocconf") diff --git a/sources/pyside2/doc/additionaldocs.lst b/sources/pyside2/doc/additionaldocs.lst index 037cb60f7..44c562424 100644 --- a/sources/pyside2/doc/additionaldocs.lst +++ b/sources/pyside2/doc/additionaldocs.lst @@ -51,6 +51,18 @@ # fi # done # A line enclosed in [] denotes a (relative) target directory +[pyside-examples] +all-pyside-examples.webxml +pyside2examples-widgets-dialogs-classwizard-classwizard-pyproject.webxml +pyside2examples-widgets-dialogs-classwizard-classwizard-py.webxml +pyside2examples-widgets-dialogs-classwizard-classwizard-qrc.webxml +pyside2examples-widgets-dialogs-classwizard-classwizard-rc-py.webxml +pyside2examples-widgets-dialogs-classwizard-example.webxml +pyside2examples-widgets-itemviews-stardelegate-example.webxml +pyside2examples-widgets-itemviews-stardelegate-stardelegate-pyproject.webxml +pyside2examples-widgets-itemviews-stardelegate-stardelegate-py.webxml +pyside2examples-widgets-itemviews-stardelegate-stareditor-py.webxml +pyside2examples-widgets-itemviews-stardelegate-starrating-py.webxml [overviews] animation-overview.webxml diff --git a/sources/pyside2/doc/pyside-examples/examples.qdoc b/sources/pyside2/doc/pyside-examples/examples.qdoc new file mode 100644 index 000000000..d82b33cf7 --- /dev/null +++ b/sources/pyside2/doc/pyside-examples/examples.qdoc @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group all-pyside-examples + \title All Qt for Python Examples + \brief A list of all the examples that are available with the Qt for Python package. +*/ diff --git a/sources/pyside2/doc/pyside-examples/images/pyside2example-classwizard.png b/sources/pyside2/doc/pyside-examples/images/pyside2example-classwizard.png Binary files differnew file mode 100644 index 000000000..1706772d8 --- /dev/null +++ b/sources/pyside2/doc/pyside-examples/images/pyside2example-classwizard.png diff --git a/sources/pyside2/doc/pyside-examples/images/pyside2example-stardelegate.png b/sources/pyside2/doc/pyside-examples/images/pyside2example-stardelegate.png Binary files differnew file mode 100644 index 000000000..343416397 --- /dev/null +++ b/sources/pyside2/doc/pyside-examples/images/pyside2example-stardelegate.png diff --git a/sources/pyside2/doc/pyside-examples/pyside2-classwizard.qdoc b/sources/pyside2/doc/pyside-examples/pyside2-classwizard.qdoc new file mode 100644 index 000000000..02560889b --- /dev/null +++ b/sources/pyside2/doc/pyside-examples/pyside2-classwizard.qdoc @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example widgets/dialogs/classwizard + \title PySide2.QtWidgets - Classwizard Example + \ingroup all-pyside-examples + \brief Demonstrates the use of QDialog in a wizard application + + This example demonstrates the use a custom QDialog in a wizard, + which generates necessary C++ class template code. + + \image pyside2example-classwizard.png + +*/ diff --git a/sources/pyside2/doc/pyside-examples/pyside2-stardelegate.qdoc b/sources/pyside2/doc/pyside-examples/pyside2-stardelegate.qdoc new file mode 100644 index 000000000..9df718335 --- /dev/null +++ b/sources/pyside2/doc/pyside-examples/pyside2-stardelegate.qdoc @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example widgets/itemviews/stardelegate + \title PySide2.QtWidgets - Star Delegate Example + \ingroup all-pyside-examples + \brief Demonstrates Qt's itemview architecture + + This example demonstrates the itemview architecture, which + is unique to Qt. + + \image pyside2example-stardelegate.png +*/ + diff --git a/sources/pyside2/doc/qtmodules/pyside-examples.qdocconf.in b/sources/pyside2/doc/qtmodules/pyside-examples.qdocconf.in new file mode 100644 index 000000000..14808f218 --- /dev/null +++ b/sources/pyside2/doc/qtmodules/pyside-examples.qdocconf.in @@ -0,0 +1,12 @@ +include(@QT_SRC_DIR@/doc/global/qt-module-defaults.qdocconf) + +project = Pyside2Examples +description = Qt for Python Examples +version = $QT_VERSION + +sourcedirs += @CMAKE_CURRENT_SOURCE_DIR@/pyside-examples +exampledirs = @CMAKE_CURRENT_SOURCE_DIR@/../../../examples +examples.fileextensions += *.py *.pyproject +imagedirs += @CMAKE_CURRENT_SOURCE_DIR@/pyside-examples/images +url.examples = "https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/\1?h=$QT_VER" +include(../pyside-config.qdocconf) diff --git a/sources/pyside2/doc/tutorials/datavisualize/add_chart.rst b/sources/pyside2/doc/tutorials/datavisualize/add_chart.rst index 0c9803269..95b2092b3 100644 --- a/sources/pyside2/doc/tutorials/datavisualize/add_chart.rst +++ b/sources/pyside2/doc/tutorials/datavisualize/add_chart.rst @@ -15,6 +15,6 @@ previous chapter to add a QChartView: .. literalinclude:: datavisualize5/main_widget.py :linenos: :lines: 40- - :emphasize-lines: 2-3,6,22-37,48-51 + :emphasize-lines: 2-3,6,22-36,48-50 diff --git a/sources/pyside2/doc/tutorials/datavisualize/add_tableview.rst b/sources/pyside2/doc/tutorials/datavisualize/add_tableview.rst index bbf27f2da..720918008 100644 --- a/sources/pyside2/doc/tutorials/datavisualize/add_tableview.rst +++ b/sources/pyside2/doc/tutorials/datavisualize/add_tableview.rst @@ -59,10 +59,12 @@ In the following snippets you'll see those changes highlighted: .. literalinclude:: datavisualize4/main_window.py :language: python :linenos: + :lines: 40- :emphasize-lines: 8,11 .. literalinclude:: datavisualize4/main.py :language: python :linenos: + :lines: 40- :emphasize-lines: 46-47 diff --git a/sources/pyside2/doc/tutorials/index.rst b/sources/pyside2/doc/tutorials/index.rst index 5a97aecb9..5b8fe9361 100644 --- a/sources/pyside2/doc/tutorials/index.rst +++ b/sources/pyside2/doc/tutorials/index.rst @@ -14,6 +14,7 @@ Examples and demos :maxdepth: 1 examples/tabbedbrowser.rst + ../pyside-examples/all-pyside-examples.rst Tutorials ========== @@ -30,3 +31,4 @@ Tutorials expenses/expenses.rst qmlapp/qmlapplication.rst qmlintegration/qmlintegration.rst + portingguide/index.rst diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/chapter1.rst b/sources/pyside2/doc/tutorials/portingguide/chapter1/chapter1.rst new file mode 100644 index 000000000..20b11065a --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/chapter1.rst @@ -0,0 +1,89 @@ +Chapter 1: ``initDb.h`` to ``createDb.py`` +******************************************* + +To begin with, port the C++ code that creates an SQLite +database and tables, and adds data to them. In this case, +all C++ code related to this lives in ``initdb.h``. The +code in this header file is divided into following parts: + +* ``initDb`` - Creates a db and the necessary tables +* ``addBooks`` - Adds data to the **books** table. +* ``addAuthor`` - Adds data to the **authors** table. +* ``addGenre`` - Adds data to the **genres** table. + +To start with, add these following ``import`` statements at +the beginning of ``createdb.py``: + +.. literalinclude:: createdb.py + :language: python + :linenos: + :lines: 40-44 + +The ``initDb`` function does most of the work needed to +set up the database, but it depends on the ``addAuthor``, +``addGenre``, and ``addBook`` helper functions to populate +the tables. Port these helper functions first. Here is how +the C++ and Python versions of these functions look like: + +C++ version +------------ + +.. literalinclude:: initdb.h + :language: c++ + :linenos: + :lines: 55-81 + +Python version +--------------- + +.. literalinclude:: createdb.py + :language: python + :linenos: + :lines: 44-65 + +Now that the helper functions are in place, port ``initDb``. +Here is how the C++ and Python versions of this function +looks like: + +C++ version +------------ + +.. literalinclude:: initdb.h + :language: c++ + :linenos: + :lines: 81-159 + +Python version +--------------- + +.. literalinclude:: createdb.py + :language: python + :linenos: + :lines: 65- + +.. note:: The Python version uses the ``check`` function to + execute the SQL statements instead of the ``if...else`` + block like in the C++ version. Although both are valid + approaches, the earlier one produces code that looks + cleaner and shorter. + +Your Python code to set up the database is ready now. To +test it, add the following code to ``main.py`` and run it: + +.. literalinclude:: main.py + :language: python + :linenos: + :lines: 40- + +Use the following command from the prompt to run: + +.. code-block:: + + python main.py + +Your table will look like this: + +.. image:: images/chapter1_books.png + +Try modifying the SQL statment in ``main.py`` to get data +from the ``genres`` or ``authors`` table. diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/createdb.py b/sources/pyside2/doc/tutorials/portingguide/chapter1/createdb.py new file mode 100644 index 000000000..8fb20cda1 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/createdb.py @@ -0,0 +1,131 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +from PySide2.QtSql import QSqlDatabase, QSqlError, QSqlQuery +from datetime import date + + +def add_book(q, title, year, authorId, genreId, rating): + q.addBindValue(title) + q.addBindValue(year) + q.addBindValue(authorId) + q.addBindValue(genreId) + q.addBindValue(rating) + q.exec_() + + +def add_genre(q, name): + q.addBindValue(name) + q.exec_() + return q.lastInsertId() + + +def add_author(q, name, birthdate): + q.addBindValue(name) + q.addBindValue(str(birthdate)) + q.exec_() + return q.lastInsertId() + +BOOKS_SQL = """ + create table books(id integer primary key, title varchar, author integer, + genre integer, year integer, rating integer) + """ +AUTHORS_SQL = """ + create table authors(id integer primary key, name varchar, birthdate text) + """ +GENRES_SQL = """ + create table genres(id integer primary key, name varchar) + """ +INSERT_AUTHOR_SQL = """ + insert into authors(name, birthdate) values(?, ?) + """ +INSERT_GENRE_SQL = """ + insert into genres(name) values(?) + """ +INSERT_BOOK_SQL = """ + insert into books(title, year, author, genre, rating) + values(?, ?, ?, ?, ?) + """ + +def init_db(): + """ + init_db() + Initializes the database. + If tables "books" and "authors" are already in the database, do nothing. + Return value: None or raises ValueError + The error value is the QtSql error instance. + """ + def check(func, *args): + if not func(*args): + raise ValueError(func.__self__.lastError()) + db = QSqlDatabase.addDatabase("QSQLITE") + db.setDatabaseName(":memory:") + + check(db.open) + + q = QSqlQuery() + check(q.exec_, BOOKS_SQL) + check(q.exec_, AUTHORS_SQL) + check(q.exec_, GENRES_SQL) + check(q.prepare, INSERT_AUTHOR_SQL) + + asimovId = add_author(q, "Isaac Asimov", date(1920, 2, 1)) + greeneId = add_author(q, "Graham Greene", date(1904, 10, 2)) + pratchettId = add_author(q, "Terry Pratchett", date(1948, 4, 28)) + + check(q.prepare,INSERT_GENRE_SQL) + sfiction = add_genre(q, "Science Fiction") + fiction = add_genre(q, "Fiction") + fantasy = add_genre(q, "Fantasy") + + check(q.prepare,INSERT_BOOK_SQL) + add_book(q, "Foundation", 1951, asimovId, sfiction, 3) + add_book(q, "Foundation and Empire", 1952, asimovId, sfiction, 4) + add_book(q, "Second Foundation", 1953, asimovId, sfiction, 3) + add_book(q, "Foundation's Edge", 1982, asimovId, sfiction, 3) + add_book(q, "Foundation and Earth", 1986, asimovId, sfiction, 4) + add_book(q, "Prelude to Foundation", 1988, asimovId, sfiction, 3) + add_book(q, "Forward the Foundation", 1993, asimovId, sfiction, 3) + add_book(q, "The Power and the Glory", 1940, greeneId, fiction, 4) + add_book(q, "The Third Man", 1950, greeneId, fiction, 5) + add_book(q, "Our Man in Havana", 1958, greeneId, fiction, 4) + add_book(q, "Guards! Guards!", 1989, pratchettId, fantasy, 3) + add_book(q, "Night Watch", 2002, pratchettId, fantasy, 3) + add_book(q, "Going Postal", 2004, pratchettId, fantasy, 3) diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/images/chapter1_books.png b/sources/pyside2/doc/tutorials/portingguide/chapter1/images/chapter1_books.png Binary files differnew file mode 100644 index 000000000..164674220 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/images/chapter1_books.png diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/initdb.h b/sources/pyside2/doc/tutorials/portingguide/chapter1/initdb.h new file mode 100644 index 000000000..773e3fb74 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/initdb.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef INITDB_H +#define INITDB_H + +#include <QtSql> + +void addBook(QSqlQuery &q, const QString &title, int year, const QVariant &authorId, + const QVariant &genreId, int rating) +{ + q.addBindValue(title); + q.addBindValue(year); + q.addBindValue(authorId); + q.addBindValue(genreId); + q.addBindValue(rating); + q.exec(); +} + +QVariant addGenre(QSqlQuery &q, const QString &name) +{ + q.addBindValue(name); + q.exec(); + return q.lastInsertId(); +} + +QVariant addAuthor(QSqlQuery &q, const QString &name, const QDate &birthdate) +{ + q.addBindValue(name); + q.addBindValue(birthdate); + q.exec(); + return q.lastInsertId(); +} + +const auto BOOKS_SQL = QLatin1String(R"( + create table books(id integer primary key, title varchar, author integer, + genre integer, year integer, rating integer) + )"); + +const auto AUTHORS_SQL = QLatin1String(R"( + create table authors(id integer primary key, name varchar, birthdate date) + )"); + +const auto GENRES_SQL = QLatin1String(R"( + create table genres(id integer primary key, name varchar) + )"); + +const auto INSERT_AUTHOR_SQL = QLatin1String(R"( + insert into authors(name, birthdate) values(?, ?) + )"); + +const auto INSERT_BOOK_SQL = QLatin1String(R"( + insert into books(title, year, author, genre, rating) + values(?, ?, ?, ?, ?) + )"); + +const auto INSERT_GENRE_SQL = QLatin1String(R"( + insert into genres(name) values(?) + )"); + +QSqlError initDb() +{ + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + db.setDatabaseName(":memory:"); + + if (!db.open()) + return db.lastError(); + + QStringList tables = db.tables(); + if (tables.contains("books", Qt::CaseInsensitive) + && tables.contains("authors", Qt::CaseInsensitive)) + return QSqlError(); + + QSqlQuery q; + if (!q.exec(BOOKS_SQL)) + return q.lastError(); + if (!q.exec(AUTHORS_SQL)) + return q.lastError(); + if (!q.exec(GENRES_SQL)) + return q.lastError(); + + if (!q.prepare(INSERT_AUTHOR_SQL)) + return q.lastError(); + QVariant asimovId = addAuthor(q, QLatin1String("Isaac Asimov"), QDate(1920, 2, 1)); + QVariant greeneId = addAuthor(q, QLatin1String("Graham Greene"), QDate(1904, 10, 2)); + QVariant pratchettId = addAuthor(q, QLatin1String("Terry Pratchett"), QDate(1948, 4, 28)); + + if (!q.prepare(INSERT_GENRE_SQL)) + return q.lastError(); + QVariant sfiction = addGenre(q, QLatin1String("Science Fiction")); + QVariant fiction = addGenre(q, QLatin1String("Fiction")); + QVariant fantasy = addGenre(q, QLatin1String("Fantasy")); + + if (!q.prepare(INSERT_BOOK_SQL)) + return q.lastError(); + addBook(q, QLatin1String("Foundation"), 1951, asimovId, sfiction, 3); + addBook(q, QLatin1String("Foundation and Empire"), 1952, asimovId, sfiction, 4); + addBook(q, QLatin1String("Second Foundation"), 1953, asimovId, sfiction, 3); + addBook(q, QLatin1String("Foundation's Edge"), 1982, asimovId, sfiction, 3); + addBook(q, QLatin1String("Foundation and Earth"), 1986, asimovId, sfiction, 4); + addBook(q, QLatin1String("Prelude to Foundation"), 1988, asimovId, sfiction, 3); + addBook(q, QLatin1String("Forward the Foundation"), 1993, asimovId, sfiction, 3); + addBook(q, QLatin1String("The Power and the Glory"), 1940, greeneId, fiction, 4); + addBook(q, QLatin1String("The Third Man"), 1950, greeneId, fiction, 5); + addBook(q, QLatin1String("Our Man in Havana"), 1958, greeneId, fiction, 4); + addBook(q, QLatin1String("Guards! Guards!"), 1989, pratchettId, fantasy, 3); + addBook(q, QLatin1String("Night Watch"), 2002, pratchettId, fantasy, 3); + addBook(q, QLatin1String("Going Postal"), 2004, pratchettId, fantasy, 3); + + return QSqlError(); +} + +#endif diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter1/main.py b/sources/pyside2/doc/tutorials/portingguide/chapter1/main.py new file mode 100644 index 000000000..7e94e4c14 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter1/main.py @@ -0,0 +1,59 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +from PySide2.QtSql import QSqlQueryModel +from PySide2.QtWidgets import QTableView, QApplication + +import createdb + +if __name__ == "__main__": + app = QApplication() + createdb.init_db() + + model = QSqlQueryModel() + model.setQuery("select * from books") + + table_view = QTableView() + table_view.setModel(model) + table_view.resize(800, 600) + table_view.show() + sys.exit(app.exec_()) diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.cpp b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.cpp new file mode 100644 index 000000000..4115f80cf --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bookdelegate.h" + +#include <QtWidgets> + +BookDelegate::BookDelegate(QObject *parent) + : QSqlRelationalDelegate(parent), star(QPixmap(":images/star.png")) +{ +} + +void BookDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (index.column() != 5) { + QStyleOptionViewItem opt = option; + // Since we draw the grid ourselves: + opt.rect.adjust(0, 0, -1, -1); + QSqlRelationalDelegate::paint(painter, opt, index); + } else { + const QAbstractItemModel *model = index.model(); + QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? + (option.state & QStyle::State_Active) ? + QPalette::Normal : + QPalette::Inactive : + QPalette::Disabled; + + if (option.state & QStyle::State_Selected) + painter->fillRect( + option.rect, + option.palette.color(cg, QPalette::Highlight)); + + int rating = model->data(index, Qt::DisplayRole).toInt(); + int width = star.width(); + int height = star.height(); + int x = option.rect.x(); + int y = option.rect.y() + (option.rect.height() / 2) - (height / 2); + for (int i = 0; i < rating; ++i) { + painter->drawPixmap(x, y, star); + x += width; + } + // Since we draw the grid ourselves: + drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1)); + } + + QPen pen = painter->pen(); + painter->setPen(option.palette.color(QPalette::Mid)); + painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight()); + painter->drawLine(option.rect.topRight(), option.rect.bottomRight()); + painter->setPen(pen); +} + +QSize BookDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (index.column() == 5) + return QSize(5 * star.width(), star.height()) + QSize(1, 1); + // Since we draw the grid ourselves: + return QSqlRelationalDelegate::sizeHint(option, index) + QSize(1, 1); +} + +bool BookDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, + const QModelIndex &index) +{ + if (index.column() != 5) + return QSqlRelationalDelegate::editorEvent(event, model, option, index); + + if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); + int stars = qBound(0, int(0.7 + qreal(mouseEvent->pos().x() + - option.rect.x()) / star.width()), 5); + model->setData(index, QVariant(stars)); + // So that the selection can change: + return false; + } + + return true; +} + +QWidget *BookDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (index.column() != 4) + return QSqlRelationalDelegate::createEditor(parent, option, index); + + // For editing the year, return a spinbox with a range from -1000 to 2100. + QSpinBox *sb = new QSpinBox(parent); + sb->setFrame(false); + sb->setMaximum(2100); + sb->setMinimum(-1000); + + return sb; +} diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.h b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.h new file mode 100644 index 000000000..f1b432699 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BOOKDELEGATE_H +#define BOOKDELEGATE_H + +#include <QModelIndex> +#include <QPixmap> +#include <QSize> +#include <QSqlRelationalDelegate> + +QT_FORWARD_DECLARE_CLASS(QPainter) + +class BookDelegate : public QSqlRelationalDelegate +{ +public: + BookDelegate(QObject *parent); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, + const QModelIndex &index) override; + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + +private: + QPixmap star; +}; + +#endif diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.py b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.py new file mode 100644 index 000000000..57d8f0f73 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/bookdelegate.py @@ -0,0 +1,134 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import copy, os +from PySide2.QtSql import QSqlRelationalDelegate +from PySide2.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate, + QStyle, QStyleOptionViewItem) +from PySide2.QtGui import QMouseEvent, QPixmap, QPalette, QImage +from PySide2.QtCore import QEvent, QSize, Qt, QUrl + +class BookDelegate(QSqlRelationalDelegate): + """Books delegate to rate the books""" + + def __init__(self, parent=None): + QSqlRelationalDelegate.__init__(self, parent) + star_png = os.path.dirname(__file__) + "\images\star.png" + self.star = QPixmap(star_png) + + def paint(self, painter, option, index): + """ Paint the items in the table. + + If the item referred to by <index> is a StarRating, we + handle the painting ourselves. For the other items, we + let the base class handle the painting as usual. + + In a polished application, we'd use a better check than + the column number to find out if we needed to paint the + stars, but it works for the purposes of this example. + """ + if index.column() != 5: + # Since we draw the grid ourselves: + opt = copy.copy(option) + opt.rect = option.rect.adjusted(0, 0, -1, -1) + QSqlRelationalDelegate.paint(self, painter, opt, index) + else: + model = index.model() + if option.state & QStyle.State_Enabled: + if option.state & QStyle.State_Active: + color_group = QPalette.Normal + else: + color_group = QPalette.Inactive + else: + color_group = QPalette.Disabled + + if option.state & QStyle.State_Selected: + painter.fillRect(option.rect, + option.palette.color(color_group, QPalette.Highlight)) + rating = model.data(index, Qt.DisplayRole) + width = self.star.width() + height = self.star.height() + x = option.rect.x() + y = option.rect.y() + (option.rect.height() / 2) - (height / 2) + for i in range(rating): + painter.drawPixmap(x, y, self.star) + x += width + + # Since we draw the grid ourselves: + self.drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1)) + + pen = painter.pen() + painter.setPen(option.palette.color(QPalette.Mid)) + painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight()) + painter.drawLine(option.rect.topRight(), option.rect.bottomRight()) + painter.setPen(pen) + + def sizeHint(self, option, index): + """ Returns the size needed to display the item in a QSize object. """ + if index.column() == 5: + size_hint = QSize(5 * self.star.width(), self.star.height()) + QSize(1, 1) + return size_hint + # Since we draw the grid ourselves: + return QSqlRelationalDelegate.sizeHint(self, option, index) + QSize(1, 1) + + def editorEvent(self, event, model, option, index): + if index.column() != 5: + return False + + if event.type() == QEvent.MouseButtonPress: + mouse_pos = event.pos() + new_stars = int(0.7 + (mouse_pos.x() - option.rect.x()) / self.star.width()) + stars = max(0, min(new_stars, 5)) + model.setData(index, stars) + # So that the selection can change + return False + + return True + + def createEditor(self, parent, option, index): + if index.column() != 4: + return QSqlRelationalDelegate.createEditor(self, parent, option, index) + + # For editing the year, return a spinbox with a range from -1000 to 2100. + spinbox = QSpinBox(parent) + spinbox.setFrame(False) + spinbox.setMaximum(2100) + spinbox.setMinimum(-1000) + return spinbox diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/chapter2.rst b/sources/pyside2/doc/tutorials/portingguide/chapter2/chapter2.rst new file mode 100644 index 000000000..a574218fd --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/chapter2.rst @@ -0,0 +1,93 @@ +Chapter 2: ``bookdelegate.cpp`` to ``bookdelegate.py`` +******************************************************* + +Now that your database is in place, port the C++ code for the +``BookDelegate`` class. This class offers a delegate to present +and edit the data in a ``QTableView``. It inherits +``QSqlRelationalDelegate`` interface, which offers features +specific for handling relational databases, such as a combobox +editor for foreign key fields. To begin with, create +``bookdelegate.py`` and add the following imports to it: + +.. literalinclude:: bookdelegate.py + :language: python + :linenos: + :lines: 40-47 + +After the necessary ``import`` statements, port the +constructor code for the ``BookDelegate`` class. Both +the C++ and Python versions of this code initialize a +``QSqlRelationalDelegate`` and ``QPixmap`` instance. +Here is how they look: + +C++ version +------------- + +.. literalinclude:: bookdelegate.cpp + :language: c++ + :linenos: + :lines: 54-59 + +Python version +--------------- + +.. literalinclude:: bookdelegate.py + :language: python + :linenos: + :lines: 47-54 + +.. note:: The Python version loads the ``QPixmap`` using + the absolute path of ``star.png`` in the local + filesystem. + +As the default functionality offered by the +``QSqlRelationalDelegate`` is not enough to present +the books data, you must reimplement a few functions. +For example, painting stars to represent the rating for +each book in the table. Here is how the reimplemented +code looks like: + +C++ version +------------ + +.. literalinclude:: bookdelegate.cpp + :language: c++ + :linenos: + :lines: 59- + +Python version +--------------- + +.. literalinclude:: bookdelegate.py + :language: python + :linenos: + :lines: 55- + +Now that the delegate is in place, run the following +``main.py`` to see how the data is presented: + +.. literalinclude:: main.py + :language: python + :linenos: + :lines: 40- + +Here is how the application will look when you run it: + +.. image:: images/chapter2_books.png + :alt: Books table data + +The only difference you'll notice now in comparison to +:doc:`chapter 1 <../chapter1/chapter1>` is that the +``rating`` column looks different. + +Try improving the table even further by adding these +features: + +* Title for each column +* SQL relation for the ``author_id`` and ``genre_id`` columns +* Set a title to the window + +With these features, this is how your table will look like: + +.. image:: images/chapter2_books_with_relation.png + :alt: Books table with SQL relation diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/createdb.py b/sources/pyside2/doc/tutorials/portingguide/chapter2/createdb.py new file mode 100644 index 000000000..8fb20cda1 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/createdb.py @@ -0,0 +1,131 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +from PySide2.QtSql import QSqlDatabase, QSqlError, QSqlQuery +from datetime import date + + +def add_book(q, title, year, authorId, genreId, rating): + q.addBindValue(title) + q.addBindValue(year) + q.addBindValue(authorId) + q.addBindValue(genreId) + q.addBindValue(rating) + q.exec_() + + +def add_genre(q, name): + q.addBindValue(name) + q.exec_() + return q.lastInsertId() + + +def add_author(q, name, birthdate): + q.addBindValue(name) + q.addBindValue(str(birthdate)) + q.exec_() + return q.lastInsertId() + +BOOKS_SQL = """ + create table books(id integer primary key, title varchar, author integer, + genre integer, year integer, rating integer) + """ +AUTHORS_SQL = """ + create table authors(id integer primary key, name varchar, birthdate text) + """ +GENRES_SQL = """ + create table genres(id integer primary key, name varchar) + """ +INSERT_AUTHOR_SQL = """ + insert into authors(name, birthdate) values(?, ?) + """ +INSERT_GENRE_SQL = """ + insert into genres(name) values(?) + """ +INSERT_BOOK_SQL = """ + insert into books(title, year, author, genre, rating) + values(?, ?, ?, ?, ?) + """ + +def init_db(): + """ + init_db() + Initializes the database. + If tables "books" and "authors" are already in the database, do nothing. + Return value: None or raises ValueError + The error value is the QtSql error instance. + """ + def check(func, *args): + if not func(*args): + raise ValueError(func.__self__.lastError()) + db = QSqlDatabase.addDatabase("QSQLITE") + db.setDatabaseName(":memory:") + + check(db.open) + + q = QSqlQuery() + check(q.exec_, BOOKS_SQL) + check(q.exec_, AUTHORS_SQL) + check(q.exec_, GENRES_SQL) + check(q.prepare, INSERT_AUTHOR_SQL) + + asimovId = add_author(q, "Isaac Asimov", date(1920, 2, 1)) + greeneId = add_author(q, "Graham Greene", date(1904, 10, 2)) + pratchettId = add_author(q, "Terry Pratchett", date(1948, 4, 28)) + + check(q.prepare,INSERT_GENRE_SQL) + sfiction = add_genre(q, "Science Fiction") + fiction = add_genre(q, "Fiction") + fantasy = add_genre(q, "Fantasy") + + check(q.prepare,INSERT_BOOK_SQL) + add_book(q, "Foundation", 1951, asimovId, sfiction, 3) + add_book(q, "Foundation and Empire", 1952, asimovId, sfiction, 4) + add_book(q, "Second Foundation", 1953, asimovId, sfiction, 3) + add_book(q, "Foundation's Edge", 1982, asimovId, sfiction, 3) + add_book(q, "Foundation and Earth", 1986, asimovId, sfiction, 4) + add_book(q, "Prelude to Foundation", 1988, asimovId, sfiction, 3) + add_book(q, "Forward the Foundation", 1993, asimovId, sfiction, 3) + add_book(q, "The Power and the Glory", 1940, greeneId, fiction, 4) + add_book(q, "The Third Man", 1950, greeneId, fiction, 5) + add_book(q, "Our Man in Havana", 1958, greeneId, fiction, 4) + add_book(q, "Guards! Guards!", 1989, pratchettId, fantasy, 3) + add_book(q, "Night Watch", 2002, pratchettId, fantasy, 3) + add_book(q, "Going Postal", 2004, pratchettId, fantasy, 3) diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books.png b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books.png Binary files differnew file mode 100644 index 000000000..e456b7d8f --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books.png diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.png b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.png Binary files differnew file mode 100644 index 000000000..82a5f449c --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/chapter2_books_with_relation.png diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/images/star.png b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/star.png Binary files differnew file mode 100644 index 000000000..87f4464bd --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/images/star.png diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter2/main.py b/sources/pyside2/doc/tutorials/portingguide/chapter2/main.py new file mode 100644 index 000000000..639ee2ca0 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter2/main.py @@ -0,0 +1,63 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +from PySide2.QtCore import Qt +from PySide2.QtSql import QSqlQueryModel +from PySide2.QtWidgets import QTableView, QApplication + +import createdb +from bookdelegate import BookDelegate + +if __name__ == "__main__": + app = QApplication() + createdb.init_db() + + model = QSqlQueryModel() + model.setQuery("select title, author, genre, year, rating from books") + + table = QTableView() + table.setModel(model) + table.setItemDelegate(BookDelegate()) + table.resize(800, 600) + table.show() + + sys.exit(app.exec_()) diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate-old.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate-old.py new file mode 100644 index 000000000..2e8670448 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate-old.py @@ -0,0 +1,134 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import copy, os +from PySide2.QtSql import QSqlRelationalDelegate +from PySide2.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate, + QStyle, QStyleOptionViewItem) +from PySide2.QtGui import QMouseEvent, QPixmap, QPalette, QImage +from PySide2.QtCore import QEvent, QSize, Qt, QUrl + +class BookDelegate(QSqlRelationalDelegate): + """Books delegate to rate the books""" + + def __init__(self, star_png, parent=None): + QSqlRelationalDelegate.__init__(self, parent) + star_png = os.path.dirname(__file__) + "\images\star.png" + self.star = QPixmap(star_png) + + def paint(self, painter, option, index): + """ Paint the items in the table. + + If the item referred to by <index> is a StarRating, we + handle the painting ourselves. For the other items, we + let the base class handle the painting as usual. + + In a polished application, we'd use a better check than + the column number to find out if we needed to paint the + stars, but it works for the purposes of this example. + """ + if index.column() != 5: + # Since we draw the grid ourselves: + opt = copy.copy(option) + opt.rect = option.rect.adjusted(0, 0, -1, -1) + QSqlRelationalDelegate.paint(self, painter, opt, index) + else: + model = index.model() + if option.state & QStyle.State_Enabled: + if option.state & QStyle.State_Active: + color_group = QPalette.Normal + else: + color_group = QPalette.Inactive + else: + color_group = QPalette.Disabled + + if option.state & QStyle.State_Selected: + painter.fillRect(option.rect, + option.palette.color(color_group, QPalette.Highlight)) + rating = model.data(index, Qt.DisplayRole) + width = self.star.width() + height = self.star.height() + x = option.rect.x() + y = option.rect.y() + (option.rect.height() / 2) - (height / 2) + for i in range(rating): + painter.drawPixmap(x, y, self.star) + x += width + + # Since we draw the grid ourselves: + self.drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1)) + + pen = painter.pen() + painter.setPen(option.palette.color(QPalette.Mid)) + painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight()) + painter.drawLine(option.rect.topRight(), option.rect.bottomRight()) + painter.setPen(pen) + + def sizeHint(self, option, index): + """ Returns the size needed to display the item in a QSize object. """ + if index.column() == 5: + size_hint = QSize(5 * self.star.width(), self.star.height()) + QSize(1, 1) + return size_hint + # Since we draw the grid ourselves: + return QSqlRelationalDelegate.sizeHint(self, option, index) + QSize(1, 1) + + def editorEvent(self, event, model, option, index): + if index.column() != 5: + return False + + if event.type() == QEvent.MouseButtonPress: + mouse_pos = event.pos() + new_stars = int(0.7 + (mouse_pos.x() - option.rect.x()) / self.star.width()) + stars = max(0, min(new_stars, 5)) + model.setData(index, stars) + # So that the selection can change + return False + + return True + + def createEditor(self, parent, option, index): + if index.column() != 4: + return QSqlRelationalDelegate.createEditor(self, parent, option, index) + + # For editing the year, return a spinbox with a range from -1000 to 2100. + spinbox = QSpinBox(parent) + spinbox.setFrame(False) + spinbox.setMaximum(2100) + spinbox.setMinimum(-1000) + return spinbox diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate.py new file mode 100644 index 000000000..087b0c262 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookdelegate.py @@ -0,0 +1,133 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import copy, os +from PySide2.QtSql import QSqlRelationalDelegate +from PySide2.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate, + QStyle, QStyleOptionViewItem) +from PySide2.QtGui import QMouseEvent, QPixmap, QPalette, QImage +from PySide2.QtCore import QEvent, QSize, Qt, QUrl + +class BookDelegate(QSqlRelationalDelegate): + """Books delegate to rate the books""" + + def __init__(self, star_png, parent=None): + QSqlRelationalDelegate.__init__(self, parent) + self.star = QPixmap(":/images/star.png") + + def paint(self, painter, option, index): + """ Paint the items in the table. + + If the item referred to by <index> is a StarRating, we + handle the painting ourselves. For the other items, we + let the base class handle the painting as usual. + + In a polished application, we'd use a better check than + the column number to find out if we needed to paint the + stars, but it works for the purposes of this example. + """ + if index.column() != 5: + # Since we draw the grid ourselves: + opt = copy.copy(option) + opt.rect = option.rect.adjusted(0, 0, -1, -1) + QSqlRelationalDelegate.paint(self, painter, opt, index) + else: + model = index.model() + if option.state & QStyle.State_Enabled: + if option.state & QStyle.State_Active: + color_group = QPalette.Normal + else: + color_group = QPalette.Inactive + else: + color_group = QPalette.Disabled + + if option.state & QStyle.State_Selected: + painter.fillRect(option.rect, + option.palette.color(color_group, QPalette.Highlight)) + rating = model.data(index, Qt.DisplayRole) + width = self.star.width() + height = self.star.height() + x = option.rect.x() + y = option.rect.y() + (option.rect.height() / 2) - (height / 2) + for i in range(rating): + painter.drawPixmap(x, y, self.star) + x += width + + # Since we draw the grid ourselves: + self.drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1)) + + pen = painter.pen() + painter.setPen(option.palette.color(QPalette.Mid)) + painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight()) + painter.drawLine(option.rect.topRight(), option.rect.bottomRight()) + painter.setPen(pen) + + def sizeHint(self, option, index): + """ Returns the size needed to display the item in a QSize object. """ + if index.column() == 5: + size_hint = QSize(5 * self.star.width(), self.star.height()) + QSize(1, 1) + return size_hint + # Since we draw the grid ourselves: + return QSqlRelationalDelegate.sizeHint(self, option, index) + QSize(1, 1) + + def editorEvent(self, event, model, option, index): + if index.column() != 5: + return False + + if event.type() == QEvent.MouseButtonPress: + mouse_pos = event.pos() + new_stars = int(0.7 + (mouse_pos.x() - option.rect.x()) / self.star.width()) + stars = max(0, min(new_stars, 5)) + model.setData(index, stars) + # So that the selection can change + return False + + return True + + def createEditor(self, parent, option, index): + if index.column() != 4: + return QSqlRelationalDelegate.createEditor(self, parent, option, index) + + # For editing the year, return a spinbox with a range from -1000 to 2100. + spinbox = QSpinBox(parent) + spinbox.setFrame(False) + spinbox.setMaximum(2100) + spinbox.setMinimum(-1000) + return spinbox diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/books.qrc b/sources/pyside2/doc/tutorials/portingguide/chapter3/books.qrc new file mode 100644 index 000000000..d6ad21337 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/books.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/star.png</file> +</qresource> +</RCC> diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.cpp b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.cpp new file mode 100644 index 000000000..76f3c9da8 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bookwindow.h" +#include "bookdelegate.h" +#include "initdb.h" + +#include <QtSql> + +BookWindow::BookWindow() +{ + ui.setupUi(this); + + if (!QSqlDatabase::drivers().contains("QSQLITE")) + QMessageBox::critical( + this, + "Unable to load database", + "This demo needs the SQLITE driver" + ); + + // Initialize the database: + QSqlError err = initDb(); + if (err.type() != QSqlError::NoError) { + showError(err); + return; + } + + // Create the data model: + model = new QSqlRelationalTableModel(ui.bookTable); + model->setEditStrategy(QSqlTableModel::OnManualSubmit); + model->setTable("books"); + + // Remember the indexes of the columns: + authorIdx = model->fieldIndex("author"); + genreIdx = model->fieldIndex("genre"); + + // Set the relations to the other database tables: + model->setRelation(authorIdx, QSqlRelation("authors", "id", "name")); + model->setRelation(genreIdx, QSqlRelation("genres", "id", "name")); + + // Set the localized header captions: + model->setHeaderData(authorIdx, Qt::Horizontal, tr("Author Name")); + model->setHeaderData(genreIdx, Qt::Horizontal, tr("Genre")); + model->setHeaderData(model->fieldIndex("title"), + Qt::Horizontal, tr("Title")); + model->setHeaderData(model->fieldIndex("year"), Qt::Horizontal, tr("Year")); + model->setHeaderData(model->fieldIndex("rating"), + Qt::Horizontal, tr("Rating")); + + // Populate the model: + if (!model->select()) { + showError(model->lastError()); + return; + } + + // Set the model and hide the ID column: + ui.bookTable->setModel(model); + ui.bookTable->setItemDelegate(new BookDelegate(ui.bookTable)); + ui.bookTable->setColumnHidden(model->fieldIndex("id"), true); + ui.bookTable->setSelectionMode(QAbstractItemView::SingleSelection); + + // Initialize the Author combo box: + ui.authorEdit->setModel(model->relationModel(authorIdx)); + ui.authorEdit->setModelColumn( + model->relationModel(authorIdx)->fieldIndex("name")); + + ui.genreEdit->setModel(model->relationModel(genreIdx)); + ui.genreEdit->setModelColumn( + model->relationModel(genreIdx)->fieldIndex("name")); + + // Lock and prohibit resizing of the width of the rating column: + ui.bookTable->horizontalHeader()->setSectionResizeMode( + model->fieldIndex("rating"), + QHeaderView::ResizeToContents); + + QDataWidgetMapper *mapper = new QDataWidgetMapper(this); + mapper->setModel(model); + mapper->setItemDelegate(new BookDelegate(this)); + mapper->addMapping(ui.titleEdit, model->fieldIndex("title")); + mapper->addMapping(ui.yearEdit, model->fieldIndex("year")); + mapper->addMapping(ui.authorEdit, authorIdx); + mapper->addMapping(ui.genreEdit, genreIdx); + mapper->addMapping(ui.ratingEdit, model->fieldIndex("rating")); + + connect(ui.bookTable->selectionModel(), + &QItemSelectionModel::currentRowChanged, + mapper, + &QDataWidgetMapper::setCurrentModelIndex + ); + + ui.bookTable->setCurrentIndex(model->index(0, 0)); + createMenuBar(); +} + +void BookWindow::showError(const QSqlError &err) +{ + QMessageBox::critical(this, "Unable to initialize Database", + "Error initializing database: " + err.text()); +} + +void BookWindow::createMenuBar() +{ + QAction *quitAction = new QAction(tr("&Quit"), this); + QAction *aboutAction = new QAction(tr("&About"), this); + QAction *aboutQtAction = new QAction(tr("&About Qt"), this); + + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + fileMenu->addAction(quitAction); + + QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); + helpMenu->addAction(aboutAction); + helpMenu->addAction(aboutQtAction); + + connect(quitAction, &QAction::triggered, this, &BookWindow::close); + connect(aboutAction, &QAction::triggered, this, &BookWindow::about); + connect(aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt); +} + +void BookWindow::about() +{ + QMessageBox::about(this, tr("About Books"), + tr("<p>The <b>Books</b> example shows how to use Qt SQL classes " + "with a model/view framework.")); +} diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.py new file mode 100644 index 000000000..4bc4cf48b --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.py @@ -0,0 +1,137 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +from PySide2.QtWidgets import (QAction, QAbstractItemView, qApp, QDataWidgetMapper, + QHeaderView, QMainWindow, QMessageBox) +from PySide2.QtGui import QKeySequence +from PySide2.QtSql import (QSqlRelation, QSqlRelationalTableModel, QSqlTableModel, + QSqlError) +from PySide2.QtCore import QAbstractItemModel, QObject, QSize, Qt, Slot +import createdb +from ui_bookwindow import Ui_BookWindow +from bookdelegate import BookDelegate + + +class BookWindow(QMainWindow, Ui_BookWindow): + # """A window to show the books available""" + + def __init__(self): + QMainWindow.__init__(self) + self.setupUi(self) + + #Initialize db + createdb.init_db() + + model = QSqlRelationalTableModel(self.bookTable) + model.setEditStrategy(QSqlTableModel.OnManualSubmit) + model.setTable("books") + + # Remember the indexes of the columns: + author_idx = model.fieldIndex("author") + genre_idx = model.fieldIndex("genre") + + # Set the relations to the other database tables: + model.setRelation(author_idx, QSqlRelation("authors", "id", "name")) + model.setRelation(genre_idx, QSqlRelation("genres", "id", "name")) + + # Set the localized header captions: + model.setHeaderData(author_idx, Qt.Horizontal, self.tr("Author Name")) + model.setHeaderData(genre_idx, Qt.Horizontal, self.tr("Genre")) + model.setHeaderData(model.fieldIndex("title"), Qt.Horizontal, self.tr("Title")) + model.setHeaderData(model.fieldIndex("year"), Qt.Horizontal, self.tr("Year")) + model.setHeaderData(model.fieldIndex("rating"), Qt.Horizontal, self.tr("Rating")) + + if not model.select(): + print(model.lastError()) + + # Set the model and hide the ID column: + self.bookTable.setModel(model) + self.bookTable.setItemDelegate(BookDelegate(self.bookTable)) + self.bookTable.setColumnHidden(model.fieldIndex("id"), True) + self.bookTable.setSelectionMode(QAbstractItemView.SingleSelection) + + # Initialize the Author combo box: + self.authorEdit.setModel(model.relationModel(author_idx)) + self.authorEdit.setModelColumn(model.relationModel(author_idx).fieldIndex("name")) + + self.genreEdit.setModel(model.relationModel(genre_idx)) + self.genreEdit.setModelColumn(model.relationModel(genre_idx).fieldIndex("name")) + + # Lock and prohibit resizing of the width of the rating column: + self.bookTable.horizontalHeader().setSectionResizeMode(model.fieldIndex("rating"), + QHeaderView.ResizeToContents) + + mapper = QDataWidgetMapper(self) + mapper.setModel(model) + mapper.setItemDelegate(BookDelegate(self)) + mapper.addMapping(self.titleEdit, model.fieldIndex("title")) + mapper.addMapping(self.yearEdit, model.fieldIndex("year")) + mapper.addMapping(self.authorEdit, author_idx) + mapper.addMapping(self.genreEdit, genre_idx) + mapper.addMapping(self.ratingEdit, model.fieldIndex("rating")) + + selection_model = self.bookTable.selectionModel() + selection_model.currentRowChanged.connect(mapper.setCurrentModelIndex) + + self.bookTable.setCurrentIndex(model.index(0, 0)) + self.create_menubar() + + def showError(err): + QMessageBox.critical(self, "Unable to initialize Database", + "Error initializing database: " + err.text()) + + def create_menubar(self): + file_menu = self.menuBar().addMenu(self.tr("&File")) + quit_action = file_menu.addAction(self.tr("&Quit")) + quit_action.triggered.connect(qApp.quit) + + help_menu = self.menuBar().addMenu(self.tr("&Help")) + about_action = help_menu.addAction(self.tr("&About")) + about_action.setShortcut(QKeySequence.HelpContents) + about_action.triggered.connect(self.about) + aboutQt_action = help_menu.addAction("&About Qt") + aboutQt_action.triggered.connect(qApp.aboutQt) + + def about(self): + QMessageBox.about(self, self.tr("About Books"), + self.tr("<p>The <b>Books</b> example shows how to use Qt SQL classes " + "with a model/view framework.")) diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.ui b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.ui new file mode 100644 index 000000000..e1668288f --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/bookwindow.ui @@ -0,0 +1,149 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>BookWindow</class> + <widget class="QMainWindow" name="BookWindow" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>601</width> + <height>420</height> + </rect> + </property> + <property name="windowTitle" > + <string>Books</string> + </property> + <widget class="QWidget" name="centralWidget" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Books</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QTableView" name="bookTable" > + <property name="selectionBehavior" > + <enum>QAbstractItemView::SelectRows</enum> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2" > + <property name="title" > + <string>Details</string> + </property> + <layout class="QFormLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string><b>Title:</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><b>Author: </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><b>Genre:</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><b>Year:</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><b>Rating:</b></string> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QSpinBox" name="ratingEdit" > + <property name="maximum" > + <number>5</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <tabstops> + <tabstop>bookTable</tabstop> + <tabstop>titleEdit</tabstop> + <tabstop>authorEdit</tabstop> + <tabstop>genreEdit</tabstop> + <tabstop>yearEdit</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/chapter3.rst b/sources/pyside2/doc/tutorials/portingguide/chapter3/chapter3.rst new file mode 100644 index 000000000..71b254811 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/chapter3.rst @@ -0,0 +1,121 @@ +Chapter 3: Port ``bookdwindow.cpp`` to ``bookwindow.py`` +********************************************************* + +After the bookdelegate, port the C++ code for the +``BookWindow`` class. It offers a QMainWindow, containing a +``QTableView`` to present the books data, and a **Details** +section with a set of input fields to edit the selected row +in the table. To begin with, create the ``bookwindow.py`` +and add the following imports to it: + +.. literalinclude:: bookwindow.py + :language: python + :linenos: + :lines: 40-53 + +.. note:: The imports include the ``BookDelegate`` you + ported earlier and the ``Ui_BookWindow``. The pyside-uic + tool generates the ``ui_bookwindow`` Python code based + on the ``bookwindow.ui`` XML file. + +To generate this Python code, run the following command on the +prompt: + +.. code-block:: + + pyside2-uic bookwindow.ui > ui_bookwindow.py + +Try porting the remaining code now. To begin with, here is +how both the versions of the constructor code looks: + +C++ version +------------ + +.. literalinclude:: bookwindow.cpp + :language: c++ + :linenos: + :lines: 47-115 + +Python version +--------------- + +.. literalinclude:: bookwindow.py + :language: python + :linenos: + :lines: 53-116 + +.. note:: The Python version of the ``BookWindow`` class + definition inherits from both ``QMainWindow`` and + ``Ui_BookWindow``, which is defined in the + ``ui_bookwindow.py`` file that you generated earlier. + +Here is how the rest of the code looks like: + +C++ version +------------ + +.. literalinclude:: bookwindow.cpp + :language: c++ + :linenos: + :lines: 115- + +Python version +--------------- + +.. literalinclude:: bookwindow.py + :language: python + :linenos: + :lines: 117- + +Now that all the necessary pieces are in place, try to put +them together in ``main.py``. + +.. literalinclude:: main.py + :language: python + :linenos: + :lines: 40- + +Try running this to see if you get the following output: + +.. image:: images/chapter3-books.png + :alt: BookWindow with a QTableView and a few input fields + +Now, if you look back at :doc:`chapter2 <../chapter2/chapter2>`, +you'll notice that the ``bookdelegate.py`` loads the +``star.png`` from the filesytem. Instead, you could add it +to a ``qrc`` file, and load from it. The later approach is +rececommended if your application is targeted for +different platforms, as most of the popular platforms +employ stricter file access policy these days. + +To add the ``star.png`` to a ``.qrc``, create a file called +``books.qrc`` and the following XML content to it: + +.. literalinclude:: books.qrc + :linenos: + +This is a simple XML file defining a list all resources that +your application needs. In this case, it is the ``star.png`` +image only. + +Now, run the ``pyside2-rcc`` tool on the ``books.qrc`` file +to generate ``rc_books.py``. + +.. code-block:: + + pyside2-rcc books.qrc > rc_books.py + +Once you have the Python script generated, make the +following changes to ``bookdelegate.py`` and ``main.py``: + +.. literalinclude:: bookdelegate.py + :diff: ../chapter2/bookdelegate.py + +.. literalinclude:: main.py + :diff: main-old.py + +Although there will be no noticeable difference in the UI +after these changes, using a ``.qrc`` is a better approach. + +Now that you have successfully ported the SQL Books example, +you know how easy it is. Try porting another C++ application. diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/createdb.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/createdb.py new file mode 100644 index 000000000..8fb20cda1 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/createdb.py @@ -0,0 +1,131 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +from PySide2.QtSql import QSqlDatabase, QSqlError, QSqlQuery +from datetime import date + + +def add_book(q, title, year, authorId, genreId, rating): + q.addBindValue(title) + q.addBindValue(year) + q.addBindValue(authorId) + q.addBindValue(genreId) + q.addBindValue(rating) + q.exec_() + + +def add_genre(q, name): + q.addBindValue(name) + q.exec_() + return q.lastInsertId() + + +def add_author(q, name, birthdate): + q.addBindValue(name) + q.addBindValue(str(birthdate)) + q.exec_() + return q.lastInsertId() + +BOOKS_SQL = """ + create table books(id integer primary key, title varchar, author integer, + genre integer, year integer, rating integer) + """ +AUTHORS_SQL = """ + create table authors(id integer primary key, name varchar, birthdate text) + """ +GENRES_SQL = """ + create table genres(id integer primary key, name varchar) + """ +INSERT_AUTHOR_SQL = """ + insert into authors(name, birthdate) values(?, ?) + """ +INSERT_GENRE_SQL = """ + insert into genres(name) values(?) + """ +INSERT_BOOK_SQL = """ + insert into books(title, year, author, genre, rating) + values(?, ?, ?, ?, ?) + """ + +def init_db(): + """ + init_db() + Initializes the database. + If tables "books" and "authors" are already in the database, do nothing. + Return value: None or raises ValueError + The error value is the QtSql error instance. + """ + def check(func, *args): + if not func(*args): + raise ValueError(func.__self__.lastError()) + db = QSqlDatabase.addDatabase("QSQLITE") + db.setDatabaseName(":memory:") + + check(db.open) + + q = QSqlQuery() + check(q.exec_, BOOKS_SQL) + check(q.exec_, AUTHORS_SQL) + check(q.exec_, GENRES_SQL) + check(q.prepare, INSERT_AUTHOR_SQL) + + asimovId = add_author(q, "Isaac Asimov", date(1920, 2, 1)) + greeneId = add_author(q, "Graham Greene", date(1904, 10, 2)) + pratchettId = add_author(q, "Terry Pratchett", date(1948, 4, 28)) + + check(q.prepare,INSERT_GENRE_SQL) + sfiction = add_genre(q, "Science Fiction") + fiction = add_genre(q, "Fiction") + fantasy = add_genre(q, "Fantasy") + + check(q.prepare,INSERT_BOOK_SQL) + add_book(q, "Foundation", 1951, asimovId, sfiction, 3) + add_book(q, "Foundation and Empire", 1952, asimovId, sfiction, 4) + add_book(q, "Second Foundation", 1953, asimovId, sfiction, 3) + add_book(q, "Foundation's Edge", 1982, asimovId, sfiction, 3) + add_book(q, "Foundation and Earth", 1986, asimovId, sfiction, 4) + add_book(q, "Prelude to Foundation", 1988, asimovId, sfiction, 3) + add_book(q, "Forward the Foundation", 1993, asimovId, sfiction, 3) + add_book(q, "The Power and the Glory", 1940, greeneId, fiction, 4) + add_book(q, "The Third Man", 1950, greeneId, fiction, 5) + add_book(q, "Our Man in Havana", 1958, greeneId, fiction, 4) + add_book(q, "Guards! Guards!", 1989, pratchettId, fantasy, 3) + add_book(q, "Night Watch", 2002, pratchettId, fantasy, 3) + add_book(q, "Going Postal", 2004, pratchettId, fantasy, 3) diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/images/chapter3-books.png b/sources/pyside2/doc/tutorials/portingguide/chapter3/images/chapter3-books.png Binary files differnew file mode 100644 index 000000000..952cb14e8 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/images/chapter3-books.png diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/images/star.png b/sources/pyside2/doc/tutorials/portingguide/chapter3/images/star.png Binary files differnew file mode 100644 index 000000000..87f4464bd --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/images/star.png diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/main-old.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/main-old.py new file mode 100644 index 000000000..4a8743c37 --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/main-old.py @@ -0,0 +1,52 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys +from PySide2.QtWidgets import QApplication +from bookwindow import BookWindow + +if __name__ == "__main__": + app = QApplication([]) + + window = BookWindow() + window.resize(800, 600) + window.show() + + sys.exit(app.exec_()) diff --git a/sources/pyside2/doc/tutorials/portingguide/chapter3/main.py b/sources/pyside2/doc/tutorials/portingguide/chapter3/main.py new file mode 100644 index 000000000..50d2c0d6b --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/chapter3/main.py @@ -0,0 +1,53 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys +from PySide2.QtWidgets import QApplication +from bookwindow import BookWindow +import rc_books + +if __name__ == "__main__": + app = QApplication([]) + + window = BookWindow() + window.resize(800, 600) + window.show() + + sys.exit(app.exec_()) diff --git a/sources/pyside2/doc/tutorials/portingguide/hello_world_ex.py b/sources/pyside2/doc/tutorials/portingguide/hello_world_ex.py new file mode 100644 index 000000000..c83dda55c --- /dev/null +++ b/sources/pyside2/doc/tutorials/portingguide/hello_world_ex.py @@ -0,0 +1,76 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys +import random + +from PySide2.QtWidgets import (QApplication, QLabel, + QPushButton, QVBoxLayout, QWidget) +from PySide2.QtCore import Qt, Slot + +class MyWidget(QWidget): + def __init__(self): + super().__init__() + + self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"] + + self.button = QPushButton("Click me!") + self.text = QLabel("Hello World") + self.text.setAlignment(Qt.AlignCenter) + + self.layout = QVBoxLayout() + self.layout.addWidget(self.text) + self.layout.addWidget(self.button) + self.setLayout(self.layout) + + self.button.clicked.connect(self.magic) + + @Slot() + def magic(self): + self.text.setText(random.choice(self.hello)) + +if __name__ == "__main__": + app = QApplication(sys.argv) + + widget = MyWidget() + widget.resize(800, 600) + widget.show() + + sys.exit(app.exec_()) diff --git a/sources/pyside2/doc/tutorials/portingguide/index.rst b/sources/pyside2/doc/tutorials/portingguide/index.rst new file mode 100644 index 000000000..8fd4c431a --- /dev/null +++ b/sources/pyside2/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 PySide2.QtWidgets import QApplication, QLabel + + app = QApplication(sys.argv) + label = QLabel("Hello World") + label.show() + sys.exit(app.exec_()) + +.. note:: Qt provides classes that are meant to manage + the application-specific requirements depending on + whether the application is console-only + (QCoreApplication), GUI with QtWidgets (QApplication), + or GUI without QtWidgets (QGuiApplication). These + classes load necessary plugins, such as the GUI + libraries required by an application. In this case, it is + QApplication that is initialized first as the application + has a GUI with QtWidgets. + +Qt in the C++ and Python context +--------------------------------- + +Qt behaves the same irrespective of whether it is used +in a C++ or a Python application. Considering that C++ +and Python use different language semantics, some +differences between the two variants of Qt are inevitable. +Here are a few important ones that you must be aware of: + +* **Qt Properties**: ``Q_PROPERTY`` macros are used in C++ to add a + public member variable with getter and setter functions. Python's + alternative for this is the ``@property`` decorator before the + getter and setter function definitions. +* **Qt Signals and Slots**: Qt offers a unique callback mechanism, + where a signal is emitted to notify the occurrence of an event, so + that slots connected to this signal can react to it. In C++, + the class definition must define the slots under the + ``public Q_SLOTS:`` and signals under ``Q_SIGNALS:`` + access specifier. You connect these two using one of the + several variants of the QObject::connect() function. Python's + equivalent for this is the `@Slot`` decorator just before the + function definition. This is necessary to register the slots + with the QtMetaObject. +* **QString, QVariant, and other types**: + - Qt for Python does not provide access to QString and + QVariant. You must use Python's native types instead. + - QChar and QStringRef are represented as Python strings, + and QStringList is converted to a list of strings. + - QDate, QDateTime, QTime, and QUrl's __hash__() methods + return a string representation so that identical dates + (and identical date/times or times or URLs) have + identical hash values. + - QTextStream's bin(), hex(), and oct() functions are + renamed to bin_(), hex_(), and oct_() respectively. This + should avoid name conflicts with Python's built-in + functions. +* **QByteArray**: A QByteArray is treated as a list of + bytes without encoding. The equivalent type in Python + varies; Python 2 uses "str" type, whereas Python 3 uses + "bytes". To avoid confusion, a QString is represented as + an encoded human readable string, which means it is + a "unicode" object in Python 2, and a "str" in Python 3. + +Here is the improved version of the Hello World example, +demonstrating some of these differences: + +.. literalinclude:: hello_world_ex.py + :linenos: + :lines: 40- + +.. note:: The ``if`` block is just a good practice when + developing a Python application. It lets the Python file + behave differently depending on whether it is imported + as a module in another file or run directly. The + ``__name__`` variable will have different values in + these two scenarios. It is ``__main__`` when the file is + run directly, and the module's file name + (``hello_world_ex`` in this case) when imported as a + module. In the later case, everything defined in the + module except the ``if`` block is available to the + importing file. + +Notice that the QPushButton's ``clicked`` signal is +connected to the ``magic`` function to randomly change the +QLabel's ``text`` property. The `@Slot`` decorator marks +the methods that are slots and informs the QtMetaObject about +them. + +Porting a Qt C++ example +========================= + +Qt offers several C++ examples to showcase its features and help +beginners learn. You can try porting one of these C++ examples to +Python. The +`books SQL example <https://code.qt.io/cgit/qt/qtbase.git/tree/examples/sql/books>`_ +is a good starting point as it does not require you to write UI-specific code in +Python, but can use its ``.ui`` file instead. + +The following chapters guides you through the porting process: + +.. toctree:: + :glob: + :titlesonly: + + chapter1/chapter1 + chapter2/chapter2 + chapter3/chapter3 diff --git a/sources/pyside2/doc/tutorials/qmlintegration/qmlintegration.rst b/sources/pyside2/doc/tutorials/qmlintegration/qmlintegration.rst index 36a12381d..d82e76246 100644 --- a/sources/pyside2/doc/tutorials/qmlintegration/qmlintegration.rst +++ b/sources/pyside2/doc/tutorials/qmlintegration/qmlintegration.rst @@ -107,5 +107,5 @@ application and PySide2 integration: .. image:: textproperties_material.png -You can download `view.qml <view.qml>`_ and `main.py <main.py>`_ -to try this example. +You can :download:`view.qml <view.qml>` and +:download:`main.py <main.py>` to try this example. |