diff options
Diffstat (limited to 'sources/pyside2/doc/tutorials/qmlsqlintegration')
-rw-r--r-- | sources/pyside2/doc/tutorials/qmlsqlintegration/chat.qml | 127 | ||||
-rw-r--r-- | sources/pyside2/doc/tutorials/qmlsqlintegration/example_list_view.png | bin | 6954 -> 0 bytes | |||
-rw-r--r-- | sources/pyside2/doc/tutorials/qmlsqlintegration/main.py | 85 | ||||
-rw-r--r-- | sources/pyside2/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst | 223 | ||||
-rw-r--r-- | sources/pyside2/doc/tutorials/qmlsqlintegration/sqlDialog.py | 146 |
5 files changed, 0 insertions, 581 deletions
diff --git a/sources/pyside2/doc/tutorials/qmlsqlintegration/chat.qml b/sources/pyside2/doc/tutorials/qmlsqlintegration/chat.qml deleted file mode 100644 index 487f5b36c..000000000 --- a/sources/pyside2/doc/tutorials/qmlsqlintegration/chat.qml +++ /dev/null @@ -1,127 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.12 -import QtQuick.Layouts 1.12 -import QtQuick.Controls 2.12 - -ApplicationWindow { - id: window - title: qsTr("Chat") - width: 640 - height: 960 - visible: true - ColumnLayout { - anchors.fill: parent - - ListView { - id: listView - Layout.fillWidth: true - Layout.fillHeight: true - Layout.margins: pane.leftPadding + messageField.leftPadding - displayMarginBeginning: 40 - displayMarginEnd: 40 - verticalLayoutDirection: ListView.BottomToTop - spacing: 12 - model: chat_model - delegate: Column { - readonly property bool sentByMe: model.recipient !== "Me" - anchors.right: sentByMe ? parent.right : undefined - spacing: 6 - - Row { - id: messageRow - spacing: 6 - anchors.right: sentByMe ? parent.right : undefined - - Rectangle { - width: Math.min(messageText.implicitWidth + 24, listView.width - messageRow.spacing) - height: messageText.implicitHeight + 24 - radius: 15 - color: sentByMe ? "lightgrey" : "#ff627c" - - Label { - id: messageText - text: model.message - color: sentByMe ? "black" : "white" - anchors.fill: parent - anchors.margins: 12 - wrapMode: Label.Wrap - } - } - } - - Label { - id: timestampText - text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm") - color: "lightgrey" - anchors.right: sentByMe ? parent.right : undefined - } - } - - ScrollBar.vertical: ScrollBar {} - } - - Pane { - id: pane - Layout.fillWidth: true - - RowLayout { - width: parent.width - - TextArea { - id: messageField - Layout.fillWidth: true - placeholderText: qsTr("Compose message") - wrapMode: TextArea.Wrap - } - - Button { - id: sendButton - text: qsTr("Send") - enabled: messageField.length > 0 - onClicked: { - chat_model.send_message("machine", messageField.text, "Me"); - messageField.text = ""; - } - } - } - } - } -} diff --git a/sources/pyside2/doc/tutorials/qmlsqlintegration/example_list_view.png b/sources/pyside2/doc/tutorials/qmlsqlintegration/example_list_view.png Binary files differdeleted file mode 100644 index a0c189665..000000000 --- a/sources/pyside2/doc/tutorials/qmlsqlintegration/example_list_view.png +++ /dev/null diff --git a/sources/pyside2/doc/tutorials/qmlsqlintegration/main.py b/sources/pyside2/doc/tutorials/qmlsqlintegration/main.py deleted file mode 100644 index c710e019a..000000000 --- a/sources/pyside2/doc/tutorials/qmlsqlintegration/main.py +++ /dev/null @@ -1,85 +0,0 @@ -############################################################################# -## -## Copyright (C) 2019 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the Qt for Python project. -## -## $QT_BEGIN_LICENSE:LGPL$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU Lesser General Public License Usage -## Alternatively, this file may be used under the terms of the GNU Lesser -## General Public License version 3 as published by the Free Software -## Foundation and appearing in the file LICENSE.LGPL3 included in the -## packaging of this file. Please review the following information to -## ensure the GNU Lesser General Public License version 3 requirements -## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 2.0 or (at your option) the GNU General -## Public license version 3 or any later version approved by the KDE Free -## Qt Foundation. The licenses are as published by the Free Software -## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-2.0.html and -## https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# - -import logging - -from PySide2.QtCore import QDir, QFile, QUrl -from PySide2.QtGui import QGuiApplication -from PySide2.QtQml import QQmlApplicationEngine -from PySide2.QtSql import QSqlDatabase - -from sqlDialog import SqlConversationModel - -logging.basicConfig(filename="chat.log", level=logging.DEBUG) -logger = logging.getLogger("logger") - - -def connectToDatabase(): - database = QSqlDatabase.database() - if not database.isValid(): - database = QSqlDatabase.addDatabase("QSQLITE") - if not database.isValid(): - logger.error("Cannot add database") - - write_dir = QDir() - if not write_dir.mkpath("."): - logger.error("Failed to create writable directory") - - # Ensure that we have a writable location on all devices. - filename = "{}/chat-database.sqlite3".format(write_dir.absolutePath()) - - # When using the SQLite driver, open() will create the SQLite - # database if it doesn't exist. - database.setDatabaseName(filename) - if not database.open(): - logger.error("Cannot open database") - QFile.remove(filename) - - -if __name__ == "__main__": - app = QGuiApplication() - connectToDatabase() - sql_conversation_model = SqlConversationModel() - - engine = QQmlApplicationEngine() - # Export pertinent objects to QML - engine.rootContext().setContextProperty("chat_model", sql_conversation_model) - engine.load(QUrl("chat.qml")) - - app.exec_() diff --git a/sources/pyside2/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst b/sources/pyside2/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst deleted file mode 100644 index c26d154ac..000000000 --- a/sources/pyside2/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst +++ /dev/null @@ -1,223 +0,0 @@ -QML, SQL and PySide Integration Tutorial -######################################## - -This tutorial is very similar to the `Qt Chat Tutorial`_ one but it focuses on explaining how to -integrate a SQL database into a PySide2 application using QML for its UI. - -.. _`Qt Chat Tutorial`: https://doc.qt.io/qt-5/qtquickcontrols-chattutorial-example.html - -sqlDialog.py ------------- - -We import the pertinent libraries to our program, define a global variable that hold the -name of our table, and define the global function ``createTable()`` that creates a new table if it -doesn't already exist. -The database contains a single line to mock the beginning of a conversation. - - .. literalinclude:: sqlDialog.py - :linenos: - :lines: 40-77 - -The ``SqlConversationModel`` class offers the read-only data model required for the non-editable -contacts list. It derives from the :ref:`QSqlQueryModel` class, which is the logical choice for -this use case. -Then, we proceed to create the table, set its name to the one defined previously with the -:meth:`~.QSqlTableModel.setTable` method. -We add the necessary attributes to the table, to have a program that reflects the idea -of a chat application. - - .. literalinclude:: sqlDialog.py - :linenos: - :lines: 80-91 - -In ``setRecipient()``, you set a filter over the returned results from the database, and -emit a signal every time the recipient of the message changes. - - .. literalinclude:: sqlDialog.py - :linenos: - :lines: 93-103 - -The ``data()`` function falls back to ``QSqlTableModel``'s implementation if the role is not a -custom user role. -If you get a user role, we can subtract :meth:`~.QtCore.Qt.UserRole` from it to get the index of -that field, and then use that index to find the value to be returned. - - .. literalinclude:: sqlDialog.py - :linenos: - :lines: 105-112 - - -In ``roleNames()``, we return a Python dictionary with our custom role and role names as key-values -pairs, so we can use these roles in QML. -Alternatively, it can be useful to declare an Enum to hold all of the role values. -Note that ``names`` has to be a hash to be used as a dictionary key, -and that's why we're using the ``hash`` function. - - .. literalinclude:: sqlDialog.py - :linenos: - :lines: 114-128 - -The ``send_message()`` function uses the given recipient and message to insert a new record into -the database. -Using :meth:`~.QSqlTableModel.OnManualSubmit` requires you to also call ``submitAll()``, -since all the changes will be cached in the model until you do so. - - .. literalinclude:: sqlDialog.py - :linenos: - :lines: 130-146 - -chat.qml --------- - -Let's look at the ``chat.qml`` file. - - .. literalinclude:: chat.qml - :linenos: - :lines: 40-42 - -First, import the Qt Quick module. -This gives us access to graphical primitives such as Item, Rectangle, Text, and so on. -For a full list of types, see the `Qt Quick QML Types`_ documentation. -We then add QtQuick.Layouts import, which we'll cover shortly. - -Next, import the Qt Quick Controls module. -Among other things, this provides access to ``ApplicationWindow``, which replaces the existing -root type, Window: - -Let's step through the ``chat.qml`` file. - - .. literalinclude:: chat.qml - :linenos: - :lines: 44-49 - -``ApplicationWindow`` is a Window with some added convenience for creating a header and a footer. -It also provides the foundation for popups and supports some basic styling, such as the background -color. - -There are three properties that are almost always set when using ApplicationWindow: ``width``, -``height``, and ``visible``. -Once we've set these, we have a properly sized, empty window ready to be filled with content. - -There are two ways of laying out items in QML: `Item Positioners`_ and `Qt Quick Layouts`_. -* Item positioners (`Row`_, `Column`_, and so on) are useful for situations where the size of items - is known or fixed, and all that is required is to neatly position them in a certain formation. -* The layouts in Qt Quick Layouts can both position and resize items, making them well suited for - resizable user interfaces. - Below, we use `ColumnLayout`_ to vertically lay out a `ListView`_ and a `Pane`_. - - .. literalinclude:: chat.qml - :linenos: - :lines: 50-53 - -Pane is basically a rectangle whose color comes from the application's style. -It's similar to `Frame`_, but it has no stroke around its border. - -Items that are direct children of a layout have various `attached properties`_ available to them. -We use `Layout.fillWidth`_ and `Layout.fillHeight`_ on the `ListView`_ to ensure that it takes as -much space within the `ColumnLayout`_ as it can, and the same is done for the Pane. -As `ColumnLayout`_ is a vertical layout, there aren't any items to the left or right of each child, -so this results in each item consuming the entire width of the layout. - -On the other hand, the `Layout.fillHeight`_ statement in the `ListView`_ enables it to occupy the -remaining space that is left after accommodating the Pane. - -.. _Item Positioners: https://doc.qt.io/qt-5/qtquick-positioning-layouts.html -.. _Qt Quick Layouts: https://doc.qt.io/qt-5/qtquicklayouts-index.html -.. _Row: https://doc.qt.io/qt-5/qml-qtquick-row.html -.. _Column: https://doc.qt.io/qt-5/qml-qtquick-column.html -.. _ColumnLayout: https://doc.qt.io/qt-5/qml-qtquick-layouts-columnlayout.html -.. _ListView: https://doc.qt.io/qt-5/qml-qtquick-listview.html -.. _Pane: https://doc.qt.io/qt-5/qml-qtquick-controls2-pane.html -.. _Frame: https://doc.qt.io/qt-5/qml-qtquick-controls2-frame.html -.. _attached properties: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html -.. _Layout.fillWidth: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillWidth-attached-prop -.. _Layout.fillHeight: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillHeight-attached-prop -.. _ListView: https://doc.qt.io/qt-5/qml-qtquick-listview.html -.. _Qt Quick QML Types: https://doc.qt.io/qt-5/qtquick-qmlmodule.html - -Let's look at the ``Listview`` in detail: - - .. literalinclude:: chat.qml - :linenos: - :lines: 53-99 - -After filling the ``width`` and ``height`` of its parent, we also set some margins on the view. - - -Next, we set `displayMarginBeginning`_ and `displayMarginEnd`_. -These properties ensure that the delegates outside the view don't disappear when you -scroll at the edges of the view. -To get a better understanding, consider commenting out the properties and then rerun your code. -Now watch what happens when you scroll the view. - -We then flip the vertical direction of the view, so that first items are at the bottom. - -Additionally, messages sent by the contact should be distinguished from those sent by a contact. -For now, when a message is sent by you, we set a ``sentByMe`` property, to alternate between -different contacts. -Using this property, we distinguish between different contacts in two ways: - -* Messages sent by the contact are aligned to the right side of the screen by setting - ``anchors.right`` to ``parent.right``. -* We change the color of the rectangle depending on the contact. - Since we don't want to display dark text on a dark background, and vice versa, we also set the - text color depending on who the contact is. - -At the bottom of the screen, we place a `TextArea`_ item to allow multi-line text input, and a -button to send the message. -We use Pane to cover the area under these two items: - - .. literalinclude:: chat.qml - :linenos: - :lines: 101-125 - -The `TextArea`_ should fill the available width of the screen. -We assign some placeholder text to provide a visual cue to the contact as to where they should begin -typing. -The text within the input area is wrapped to ensure that it does not go outside of the screen. - -Lastly, we have a button that allows us to call the ``send_message`` method we defined on -``sqlDialog.py``, since we're just having a mock up example here and there is only one possible -recipient and one possible sender for this conversation we're just using strings here. - -.. _displayMarginBeginning: https://doc.qt.io/qt-5/qml-qtquick-listview.html#displayMarginBeginning-prop -.. _displayMarginEnd: https://doc.qt.io/qt-5/qml-qtquick-listview.html#displayMarginEnd-prop -.. _TextArea: https://doc.qt.io/qt-5/qml-qtquick-controls2-textarea.html - - -main.py -------- - -We use ``logging`` instead of Python's ``print()``, because it provides a better way to control the -messages levels that our application will generate (errors, warnings, and information messages). - - .. literalinclude:: main.py - :linenos: - :lines: 40-50 - -``connectToDatabase()`` creates a connection with the SQLite database, creating the actual file -if it doesn't already exist. - - .. literalinclude:: main.py - :linenos: - :lines: 53-72 - - - -A few interesting things happen in the ``main`` function: -* Declaring a :ref:`QGuiApplication`. - You should use a :ref:`QGuiApplication` instead of :ref:`QApplication` because we're not - using the **QtWidgets** module. -* Connecting to the database, -* Declaring a :ref:`QQmlApplicationEngine`. - This allows you to access the QML context property to connect Python - and QML from the conversation model we built on ``sqlDialog.py``. -* Loading the ``.qml`` file that defines the UI. - -Finally, the Qt application runs, and your program starts. - - .. literalinclude:: main.py - :linenos: - :lines: 75-85 - -.. image:: example_list_view.png diff --git a/sources/pyside2/doc/tutorials/qmlsqlintegration/sqlDialog.py b/sources/pyside2/doc/tutorials/qmlsqlintegration/sqlDialog.py deleted file mode 100644 index 6a9ff8234..000000000 --- a/sources/pyside2/doc/tutorials/qmlsqlintegration/sqlDialog.py +++ /dev/null @@ -1,146 +0,0 @@ -############################################################################# -## -## Copyright (C) 2019 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the Qt for Python project. -## -## $QT_BEGIN_LICENSE:LGPL$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU Lesser General Public License Usage -## Alternatively, this file may be used under the terms of the GNU Lesser -## General Public License version 3 as published by the Free Software -## Foundation and appearing in the file LICENSE.LGPL3 included in the -## packaging of this file. Please review the following information to -## ensure the GNU Lesser General Public License version 3 requirements -## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 2.0 or (at your option) the GNU General -## Public license version 3 or any later version approved by the KDE Free -## Qt Foundation. The licenses are as published by the Free Software -## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-2.0.html and -## https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# - -import datetime -import logging - -from PySide2.QtCore import Qt, Slot -from PySide2.QtSql import QSqlDatabase, QSqlQuery, QSqlRecord, QSqlTableModel - -table_name = "Conversations" - - -def createTable(): - if table_name in QSqlDatabase.database().tables(): - return - - query = QSqlQuery() - if not query.exec_( - """ - CREATE TABLE IF NOT EXISTS 'Conversations' ( - 'author' TEXT NOT NULL, - 'recipient' TEXT NOT NULL, - 'timestamp' TEXT NOT NULL, - 'message' TEXT NOT NULL, - FOREIGN KEY('author') REFERENCES Contacts ( name ), - FOREIGN KEY('recipient') REFERENCES Contacts ( name ) - ) - """ - ): - logging.error("Failed to query database") - - # This adds the first message from the Bot - # and further development is required to make it interactive. - query.exec_( - """ - INSERT INTO Conversations VALUES( - 'machine', 'Me', '2019-01-07T14:36:06', 'Hello!' - ) - """ - ) - logging.info(query) - - -class SqlConversationModel(QSqlTableModel): - def __init__(self, parent=None): - super(SqlConversationModel, self).__init__(parent) - - createTable() - self.setTable(table_name) - self.setSort(2, Qt.DescendingOrder) - self.setEditStrategy(QSqlTableModel.OnManualSubmit) - self.recipient = "" - - self.select() - logging.debug("Table was loaded successfully.") - - def setRecipient(self, recipient): - if recipient == self.recipient: - pass - - self.recipient = recipient - - filter_str = ( - "(recipient = '{}' AND author = 'Me') OR " "(recipient = 'Me' AND author='{}')" - ).format(self.recipient) - self.setFilter(filter_str) - self.select() - - def data(self, index, role): - if role < Qt.UserRole: - return QSqlTableModel.data(self, index, role) - - sql_record = QSqlRecord() - sql_record = self.record(index.row()) - - return sql_record.value(role - Qt.UserRole) - - def roleNames(self): - """Converts dict to hash because that's the result expected - by QSqlTableModel""" - names = {} - author = "author".encode() - recipient = "recipient".encode() - timestamp = "timestamp".encode() - message = "message".encode() - - names[hash(Qt.UserRole)] = author - names[hash(Qt.UserRole + 1)] = recipient - names[hash(Qt.UserRole + 2)] = timestamp - names[hash(Qt.UserRole + 3)] = message - - return names - - def send_message(self, recipient, message, author): - timestamp = datetime.datetime.now() - - new_record = self.record() - new_record.setValue("author", author) - new_record.setValue("recipient", recipient) - new_record.setValue("timestamp", str(timestamp)) - new_record.setValue("message", message) - - logging.debug('Message: "{}" \n Received by: "{}"'.format(message, recipient)) - - if not self.insertRecord(self.rowCount(), new_record): - logging.error("Failed to send message: {}".format(self.lastError().text())) - return - - self.submitAll() - self.select() |