aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorVenugopal Shivashankar <Venugopal.Shivashankar@qt.io>2019-04-29 11:32:51 +0200
committerVenugopal Shivashankar <Venugopal.Shivashankar@qt.io>2019-05-20 10:32:54 +0000
commit6d5ca98cad443cc3d10e28262d9a05ff47ba660a (patch)
treefe3bf9e5480127ecdebe7caa2d75de0267f889d9 /examples
parenta00a511884299cae4bbad18a709b3c07e2aa4790 (diff)
Example: Port the QtSql Books example
Change-Id: Ie8b0378f6f8de406700cf8ace381e0022b641617 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'examples')
-rw-r--r--examples/sql/books/bookdelegate.py134
-rw-r--r--examples/sql/books/books.pyproject5
-rw-r--r--examples/sql/books/books.qrc5
-rw-r--r--examples/sql/books/bookwindow.py140
-rw-r--r--examples/sql/books/bookwindow.ui164
-rw-r--r--examples/sql/books/createdb.py117
-rw-r--r--examples/sql/books/images/star.pngbin0 -> 782 bytes
-rw-r--r--examples/sql/books/main.py53
-rw-r--r--examples/sql/books/rc_books.py88
-rw-r--r--examples/sql/books/ui_bookwindow.py129
10 files changed, 835 insertions, 0 deletions
diff --git a/examples/sql/books/bookdelegate.py b/examples/sql/books/bookdelegate.py
new file mode 100644
index 000000000..a0bd92334
--- /dev/null
+++ b/examples/sql/books/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
+from PySide2.QtSql import QSqlRelationalDelegate
+from PySide2.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate,
+ QStyle, QStyleOptionViewItem)
+from PySide2.QtGui import QMouseEvent, QPixmap, QPalette
+from PySide2.QtCore import QEvent, QSize, Qt
+
+
+class BookDelegate(QSqlRelationalDelegate):
+ """Books delegate to rate the books"""
+
+ def __init__(self, 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/examples/sql/books/books.pyproject b/examples/sql/books/books.pyproject
new file mode 100644
index 000000000..44a1ef219
--- /dev/null
+++ b/examples/sql/books/books.pyproject
@@ -0,0 +1,5 @@
+{
+ "files": ["main.py", "bookdelegate.py", "bookwindow.py",
+ "createdb.py", "books.qrc", "bookwindow.ui",
+ "images/star.png"]
+}
diff --git a/examples/sql/books/books.qrc b/examples/sql/books/books.qrc
new file mode 100644
index 000000000..d6ad21337
--- /dev/null
+++ b/examples/sql/books/books.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>images/star.png</file>
+</qresource>
+</RCC>
diff --git a/examples/sql/books/bookwindow.py b/examples/sql/books/bookwindow.py
new file mode 100644
index 000000000..500acf2ef
--- /dev/null
+++ b/examples/sql/books/bookwindow.py
@@ -0,0 +1,140 @@
+#############################################################################
+##
+## 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)
+
+ #check for SQL errors
+ err = createdb.init_db()
+ if err.type() is not QSqlError.NoError:
+ showError(err)
+ return
+
+ 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/examples/sql/books/bookwindow.ui b/examples/sql/books/bookwindow.ui
new file mode 100644
index 000000000..ce8f9f933
--- /dev/null
+++ b/examples/sql/books/bookwindow.ui
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <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="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string/>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QTableView" name="bookTable">
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Details</string>
+ </property>
+ <layout class="QFormLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>&lt;b&gt;Title:&lt;/b&gt;</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">
+ <property name="text">
+ <string>&lt;b&gt;Author: &lt;/b&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="authorEdit">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>&lt;b&gt;Genre:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="genreEdit">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>&lt;b&gt;Year:&lt;/b&gt;</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="minimum">
+ <number>-1000</number>
+ </property>
+ <property name="maximum">
+ <number>2100</number>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>&lt;b&gt;Rating:&lt;/b&gt;</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>
+ <tabstops>
+ <tabstop>bookTable</tabstop>
+ <tabstop>titleEdit</tabstop>
+ <tabstop>authorEdit</tabstop>
+ <tabstop>genreEdit</tabstop>
+ <tabstop>yearEdit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/sql/books/createdb.py b/examples/sql/books/createdb.py
new file mode 100644
index 000000000..d662cacd1
--- /dev/null
+++ b/examples/sql/books/createdb.py
@@ -0,0 +1,117 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+from PySide2.QtSql import QSqlDatabase, QSqlError, QSqlQuery
+from datetime import datetime
+
+
+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(birthdate)
+ q.exec_()
+ return q.lastInsertId()
+
+
+def init_db():
+ db = QSqlDatabase.addDatabase("QSQLITE")
+ db.setDatabaseName(":memory:")
+
+ if not db.open():
+ return db.lastError()
+
+ tables = db.tables()
+ for table in tables:
+ if table == "books" and table == "authors":
+ return QSqlError()
+
+ q = QSqlQuery()
+ if not q.exec_("create table books(id integer primary key, title varchar, author integer, "
+ "genre integer, year integer, rating integer)"):
+ return q.lastError()
+ if not q.exec_("create table authors(id integer primary key, name varchar, birthdate date)"):
+ return q.lastError()
+ if not q.exec_("create table genres(id integer primary key, name varchar)"):
+ return q.lastError()
+
+ if not q.prepare("insert into authors(name, birthdate) values(?, ?)"):
+ return q.lastError()
+ asimovId = add_author(q, "Isaac Asimov", datetime(1920, 2, 1))
+ greeneId = add_author(q, "Graham Greene", datetime(1904, 10, 2))
+ pratchettId = add_author(q, "Terry Pratchett", datetime(1948, 4, 28))
+
+ if not q.prepare("insert into genres(name) values(?)"):
+ return q.lastError()
+ sfiction = add_genre(q, "Science Fiction")
+ fiction = add_genre(q, "Fiction")
+ fantasy = add_genre(q, "Fantasy")
+
+ if not q.prepare("insert into books(title, year, author, genre, rating) "
+ "values(?, ?, ?, ?, ?)"):
+ return q.lastError()
+ 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)
+
+ return QSqlError()
diff --git a/examples/sql/books/images/star.png b/examples/sql/books/images/star.png
new file mode 100644
index 000000000..87f4464bd
--- /dev/null
+++ b/examples/sql/books/images/star.png
Binary files differ
diff --git a/examples/sql/books/main.py b/examples/sql/books/main.py
new file mode 100644
index 000000000..50d2c0d6b
--- /dev/null
+++ b/examples/sql/books/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/examples/sql/books/rc_books.py b/examples/sql/books/rc_books.py
new file mode 100644
index 000000000..6f2cbbeb6
--- /dev/null
+++ b/examples/sql/books/rc_books.py
@@ -0,0 +1,88 @@
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 5.14.0
+# WARNING! All changes made in this file will be lost!
+
+from PySide2 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x03\x0e\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\
+\x00\x00\x00\x09pHYs\x00\x00\x0b\x11\x00\x00\x0b\x11\
+\x01\x7fd_\x91\x00\x00\x00\x07tIME\x07\xd4\x09\
+\x03\x12\x11\x08\x18~\xe5:\x00\x00\x00\x06bKGD\
+\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x02\x9bID\
+AT8\xcbc\x98:c\x1e#:\xe6\xe5d\xcf\x17\
+\x12\x12\x16\xc4&\x87\x8e\x19\xb0\x09v\xc6\x18\xb7x\xea\
+\x8b\xcd\x9c=o\x09i\x06,X4\x8f\xf1\xd2\xa5\x99\
+L\xb9\xa1\x16\xc5\xc7\xbb\xed\xff\x0a\xf2\xb2;M\x9f\xb5\
+\x908\x03\x16,\x9a\xcb\xf8\xe0\xde\x04\x96\xc7\x0f\xdby\
+\xe7MO\xc8\xfbv\xbf\xe5\xff\xb4\x0a\x9b\x9by\x851\
+\xdc\xd3g-\x82k\x983\x7f)\xe3l F1`\
+\xca\xf4y\x8c\xd7\xaeMg\x02i~\xf2\xa8Y\xe1\xd2\
+\xa5\xfa\xdc_\x9f7\xfd\xffx\xbf\xea\x7fE\x96m\x97\
+\x81\x81>'33\x8b\xa5\x9e8gi\xb8\x9e\xc0f\
+&&\xa6D\x14\x03&N\x9d\xc7x\xef\xdeD\x96'\
+\x0f[E\x9f>j\xd6\xbdu\xb3\x22\xef\xd7\xb7=\xff\
+\xbe\x7f\xe8\xfb\x7f~S\xcc\xef\x05\xc5\xea\x9fNOQ\
+\xfb\x7f\xbaM\xed\xbf\x87\x1a\xefn5-\x1dV\x14\x03\
+f\xcf[\xce\xa8\xa4\xa9![W\xed\x9b}\xefJ\xcb\
+\xcew\xaf&\x7f\xfa\xfee\xc9\xff\xef\x1f\xfa\xff\xbf\xbf\
+\x95\xf2\xff\xc9^\x83\xffW\x17\xaa\xfdot\x12{\xc4\
+\xc7\xc7/\x8e\x12\x06Y\xb9\x85\xcc\xb2\x82\x1c\xf3\xa7D\
+\xab\xfe\xfa\xbe%\xe2\xff\x8fgm\xff\x7f|\x9a\x08\xd6\
+\xfc\xf5Y\xcd\xff\xd7\xe7\xfc\xfe\xdf\xde\xa0\xf5\x7fE\x94\
+\xecO\x16\x16V\xebi3\xe7\xa3\x06\xe2\xe4is\x18\
+\xe7/Z\xc1\xc8\xce\xc1i\x10\xe5\xa8\xd2\xbe\xa6\xcd\xe7\
+\xf6\xc3m\x99\xff~^(\xf8\xff\xe1j\xe0\xff\x17G\
+L\xff\xdf\xdf\xae\xf6\xbf\xc2]\xf4\xba\x9a\x9a\x06\x1bF\
+,\x00\x01#2\xe6\xe6\xe6`Q\xd6\x941_\xde\xe4\
+q\xfb\xc3y\xd3\xff\x1b\x8aT\xff\xbf?`\xff\xff\xdc\
+l\xe5\xff\xea\xc2\x1c9\xd3g-\xc0i\x00\x13\x10\x8b\
+\x03\xb1?\x10\xe7\xf5\x16\xd8\xde\xf8p\xc6\xe4\xbf\x9d<\
+\xf7t\x7fC\xe9\x95\xb7\x96\xd9\xff\x9b\x9c,\xfdN@\
+@H\x14\x9b\x01LP,\x06\xc4\x19@|\x22;\xca\
+\xf0\xe7\xe9\xf9\x06\xff\x81\xec\x03@\xbc^\x82\x9f\xf3\xf6\
+\x9e\x1a\xf3_az\x823P\xd2\x01T#3\x10\x0b\
+\x00\xb1\x1e\x10\x17\x03\xf1\xd1\xa8@\xdd\x9f\xad\x09J \
+\x03\xfe\x00\xf17 >\x0f\xb4kf\xb9\xa7\xea\x0d}\
+i>#d\x03\xb4\x808\x08\x88k\x81x\x09\xd4\xc6\
+\x1b\x11a\x06\xdf\xec\x94\xb8\xdf\x03\xd9;\x81x\x1a\x10\
+\xf7\x82\xd4\xb0\xb2\xb1G\xf9\xda\x99:L\x9d9\x9f\x09\
+f\x80\x0e\x10;\x02\xb1\x13\x10[\x00\xb1\x01\x10\x07\x06\
+{h\x9c\x02\xd2k\xa0\x86\x8b\x001\x17\x10\xf3\x80\xb0\
+\x88\x88(\xcb,hFC\xf6\x02\x08\xb3\x001+\x10\
+K122\xe4\x01i7 \x96\x01b6\xa88\x0b\
+T=cW\xef$\xb0\x01\x00\xceo{\xf5UL\xf0\
+\xac\x00\x00\x00\x00IEND\xaeB`\x82\
+"
+
+qt_resource_name = b"\
+\x00\x06\
+\x07\x03}\xc3\
+\x00i\
+\x00m\x00a\x00g\x00e\x00s\
+\x00\x08\
+\x0a\x85X\x07\
+\x00s\
+\x00t\x00a\x00r\x00.\x00p\x00n\x00g\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01j\x965\xd3\xea\
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
diff --git a/examples/sql/books/ui_bookwindow.py b/examples/sql/books/ui_bookwindow.py
new file mode 100644
index 000000000..5eb412d92
--- /dev/null
+++ b/examples/sql/books/ui_bookwindow.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+
+################################################################################
+## Form generated from reading UI file 'bookwindow.ui'
+##
+## Created by: Qt User Interface Compiler version 5.14.0
+##
+## WARNING! All changes made in this file will be lost when recompiling UI file!
+################################################################################
+
+from PySide2.QtCore import (QCoreApplication, QMetaObject, QObject, QPoint,
+ QRect, QSize, QUrl, Qt)
+from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QFont,
+ QFontDatabase, QIcon, QLinearGradient, QPalette, QPainter, QPixmap,
+ QRadialGradient)
+from PySide2.QtWidgets import *
+
+class Ui_BookWindow(object):
+ def setupUi(self, BookWindow):
+ if BookWindow.objectName():
+ BookWindow.setObjectName(u"BookWindow")
+ BookWindow.resize(601, 420)
+ self.centralWidget = QWidget(BookWindow)
+ self.centralWidget.setObjectName(u"centralWidget")
+ self.vboxLayout = QVBoxLayout(self.centralWidget);
+ self.vboxLayout.setSpacing(6)
+ self.vboxLayout.setObjectName(u"vboxLayout")
+ self.vboxLayout.setContentsMargins(9, 9, 9, 9);
+ self.groupBox = QGroupBox(self.centralWidget)
+ self.groupBox.setObjectName(u"groupBox")
+ self.vboxLayout1 = QVBoxLayout(self.groupBox);
+ self.vboxLayout1.setSpacing(6)
+ self.vboxLayout1.setObjectName(u"vboxLayout1")
+ self.vboxLayout1.setContentsMargins(9, 9, 9, 9);
+ self.bookTable = QTableView(self.groupBox)
+ self.bookTable.setObjectName(u"bookTable")
+ self.bookTable.setSelectionBehavior(QAbstractItemView.SelectRows)
+
+ self.vboxLayout1.addWidget(self.bookTable);
+
+ self.groupBox_2 = QGroupBox(self.groupBox)
+ self.groupBox_2.setObjectName(u"groupBox_2")
+ self.formLayout = QFormLayout(self.groupBox_2);
+ self.formLayout.setObjectName(u"formLayout")
+ self.label_5 = QLabel(self.groupBox_2)
+ self.label_5.setObjectName(u"label_5")
+
+ self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label_5);
+
+ self.titleEdit = QLineEdit(self.groupBox_2)
+ self.titleEdit.setObjectName(u"titleEdit")
+ self.titleEdit.setEnabled(True)
+
+ self.formLayout.setWidget(0, QFormLayout.FieldRole, self.titleEdit);
+
+ self.label_2 = QLabel(self.groupBox_2)
+ self.label_2.setObjectName(u"label_2")
+
+ self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label_2);
+
+ self.authorEdit = QComboBox(self.groupBox_2)
+ self.authorEdit.setObjectName(u"authorEdit")
+ self.authorEdit.setEnabled(True)
+
+ self.formLayout.setWidget(1, QFormLayout.FieldRole, self.authorEdit);
+
+ self.label_3 = QLabel(self.groupBox_2)
+ self.label_3.setObjectName(u"label_3")
+
+ self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_3);
+
+ self.genreEdit = QComboBox(self.groupBox_2)
+ self.genreEdit.setObjectName(u"genreEdit")
+ self.genreEdit.setEnabled(True)
+
+ self.formLayout.setWidget(2, QFormLayout.FieldRole, self.genreEdit);
+
+ self.label_4 = QLabel(self.groupBox_2)
+ self.label_4.setObjectName(u"label_4")
+
+ self.formLayout.setWidget(3, QFormLayout.LabelRole, self.label_4);
+
+ self.yearEdit = QSpinBox(self.groupBox_2)
+ self.yearEdit.setObjectName(u"yearEdit")
+ self.yearEdit.setEnabled(True)
+ self.yearEdit.setMinimum(-1000)
+ self.yearEdit.setMaximum(2100)
+
+ self.formLayout.setWidget(3, QFormLayout.FieldRole, self.yearEdit);
+
+ self.label = QLabel(self.groupBox_2)
+ self.label.setObjectName(u"label")
+
+ self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label);
+
+ self.ratingEdit = QSpinBox(self.groupBox_2)
+ self.ratingEdit.setObjectName(u"ratingEdit")
+ self.ratingEdit.setMaximum(5)
+
+ self.formLayout.setWidget(4, QFormLayout.FieldRole, self.ratingEdit);
+
+
+ self.vboxLayout1.addWidget(self.groupBox_2);
+
+
+ self.vboxLayout.addWidget(self.groupBox);
+
+ BookWindow.setCentralWidget(self.centralWidget)
+ QWidget.setTabOrder(self.bookTable, self.titleEdit)
+ QWidget.setTabOrder(self.titleEdit, self.authorEdit)
+ QWidget.setTabOrder(self.authorEdit, self.genreEdit)
+ QWidget.setTabOrder(self.genreEdit, self.yearEdit)
+
+ self.retranslateUi(BookWindow)
+
+ QMetaObject.connectSlotsByName(BookWindow)
+ # setupUi
+
+ def retranslateUi(self, BookWindow):
+ BookWindow.setWindowTitle(QCoreApplication.translate("BookWindow", u"Books", None))
+ self.groupBox.setTitle("")
+ self.groupBox_2.setTitle(QCoreApplication.translate("BookWindow", u"Details", None))
+ self.label_5.setText(QCoreApplication.translate("BookWindow", u"<b>Title:</b>", None))
+ self.label_2.setText(QCoreApplication.translate("BookWindow", u"<b>Author: </b>", None))
+ self.label_3.setText(QCoreApplication.translate("BookWindow", u"<b>Genre:</b>", None))
+ self.label_4.setText(QCoreApplication.translate("BookWindow", u"<b>Year:</b>", None))
+ self.yearEdit.setPrefix("")
+ self.label.setText(QCoreApplication.translate("BookWindow", u"<b>Rating:</b>", None))
+ # retranslateUi