第三章:将bookdwindow.cpp移植到bookwindow.py#

在将BookDelegate移植之后,再移植BookWindow类的C++代码。它提供了一个QMainWindow,包含一个QTableView用于显示书籍数据,以及一个详细信息部分,包含一组输入字段用于编辑表格中选定的行。首先,创建bookwindow.py并将以下导入添加到其中

 1
 2from PySide6.QtGui import QAction
 3from PySide6.QtWidgets import (QAbstractItemView, QDataWidgetMapper,
 4    QHeaderView, QMainWindow, QMessageBox)
 5from PySide6.QtGui import QKeySequence
 6from PySide6.QtSql import (QSqlRelation, QSqlRelationalTableModel, QSqlTableModel,
 7    QSqlError)
 8from PySide6.QtCore import QAbstractItemModel, QObject, QSize, Qt, Slot
 9import createdb
10from ui_bookwindow import Ui_BookWindow
11from bookdelegate import BookDelegate
12
13
14class BookWindow(QMainWindow, Ui_BookWindow):

注意:

导入包括了您之前移植的BookDelegateUi_BookWindow。 pyside-uic工具根据bookwindow.ui XML文件生成ui_bookwindow Python代码。

要生成此Python代码,请在提示符下运行以下命令

pyside6-uic bookwindow.ui -o ui_bookwindow.py

现在尝试移植剩余的代码。首先,下面是构造函数代码的两个版本的外观

版本C++#

 1    // Initialize the database:
 2    QSqlError err = initDb();
 3    if (err.type() != QSqlError::NoError) {
 4        showError(err);
 5        return;
 6    }
 7
 8    // Create the data model:
 9    model = new QSqlRelationalTableModel(ui.bookTable);
10    model->setEditStrategy(QSqlTableModel::OnManualSubmit);
11    model->setTable("books");
12
13    // Remember the indexes of the columns:
14    authorIdx = model->fieldIndex("author");
15    genreIdx = model->fieldIndex("genre");
16
17    // Set the relations to the other database tables:
18    model->setRelation(authorIdx, QSqlRelation("authors", "id", "name"));
19    model->setRelation(genreIdx, QSqlRelation("genres", "id", "name"));
20
21    // Set the localized header captions:
22    model->setHeaderData(authorIdx, Qt::Horizontal, tr("Author Name"));
23    model->setHeaderData(genreIdx, Qt::Horizontal, tr("Genre"));
24    model->setHeaderData(model->fieldIndex("title"),
25                         Qt::Horizontal, tr("Title"));
26    model->setHeaderData(model->fieldIndex("year"), Qt::Horizontal, tr("Year"));
27    model->setHeaderData(model->fieldIndex("rating"),
28                         Qt::Horizontal, tr("Rating"));
29
30    // Populate the model:
31    if (!model->select()) {
32        showError(model->lastError());
33        return;
34    }
35
36    // Set the model and hide the ID column:
37    ui.bookTable->setModel(model);
38    ui.bookTable->setItemDelegate(new BookDelegate(ui.bookTable));
39    ui.bookTable->setColumnHidden(model->fieldIndex("id"), true);
40    ui.bookTable->setSelectionMode(QAbstractItemView::SingleSelection);
41
42    // Initialize the Author combo box:
43    ui.authorEdit->setModel(model->relationModel(authorIdx));
44    ui.authorEdit->setModelColumn(
45                model->relationModel(authorIdx)->fieldIndex("name"));
46
47    ui.genreEdit->setModel(model->relationModel(genreIdx));
48    ui.genreEdit->setModelColumn(
49                model->relationModel(genreIdx)->fieldIndex("name"));
50
51    // Lock and prohibit resizing of the width of the rating column:
52    ui.bookTable->horizontalHeader()->setSectionResizeMode(
53                model->fieldIndex("rating"),
54                QHeaderView::ResizeToContents);
55
56    QDataWidgetMapper *mapper = new QDataWidgetMapper(this);
57    mapper->setModel(model);
58    mapper->setItemDelegate(new BookDelegate(this));
59    mapper->addMapping(ui.titleEdit, model->fieldIndex("title"));
60    mapper->addMapping(ui.yearEdit, model->fieldIndex("year"));
61    mapper->addMapping(ui.authorEdit, authorIdx);
62    mapper->addMapping(ui.genreEdit, genreIdx);
63    mapper->addMapping(ui.ratingEdit, model->fieldIndex("rating"));
64
65    connect(ui.bookTable->selectionModel(),
66            &QItemSelectionModel::currentRowChanged,
67            mapper,
68            &QDataWidgetMapper::setCurrentModelIndex
69            );
70
71    ui.bookTable->setCurrentIndex(model->index(0, 0));
72    createMenuBar();
73}
74
75void BookWindow::showError(const QSqlError &err)
76{
77    QMessageBox::critical(this, "Unable to initialize Database",
78                "Error initializing database: " + err.text());
79}
80
81void BookWindow::createMenuBar()
82{
83    QAction *quitAction = new QAction(tr("&Quit"), this);

版本Python#

 1class BookWindow(QMainWindow, Ui_BookWindow):
 2    # """A window to show the books available"""
 3
 4    def __init__(self):
 5        QMainWindow.__init__(self)
 6        self.setupUi(self)
 7
 8        #Initialize db
 9        createdb.init_db()
10
11        model = QSqlRelationalTableModel(self.bookTable)
12        model.setEditStrategy(QSqlTableModel.OnManualSubmit)
13        model.setTable("books")
14
15        # Remember the indexes of the columns:
16        author_idx = model.fieldIndex("author")
17        genre_idx = model.fieldIndex("genre")
18
19        # Set the relations to the other database tables:
20        model.setRelation(author_idx, QSqlRelation("authors", "id", "name"))
21        model.setRelation(genre_idx, QSqlRelation("genres", "id", "name"))
22
23        # Set the localized header captions:
24        model.setHeaderData(author_idx, Qt.Horizontal, self.tr("Author Name"))
25        model.setHeaderData(genre_idx, Qt.Horizontal, self.tr("Genre"))
26        model.setHeaderData(model.fieldIndex("title"), Qt.Horizontal, self.tr("Title"))
27        model.setHeaderData(model.fieldIndex("year"), Qt.Horizontal, self.tr("Year"))
28        model.setHeaderData(model.fieldIndex("rating"), Qt.Horizontal, self.tr("Rating"))
29
30        if not model.select():
31            print(model.lastError())
32
33        # Set the model and hide the ID column:
34        self.bookTable.setModel(model)
35        self.bookTable.setItemDelegate(BookDelegate(self.bookTable))
36        self.bookTable.setColumnHidden(model.fieldIndex("id"), True)
37        self.bookTable.setSelectionMode(QAbstractItemView.SingleSelection)
38
39        # Initialize the Author combo box:
40        self.authorEdit.setModel(model.relationModel(author_idx))
41        self.authorEdit.setModelColumn(model.relationModel(author_idx).fieldIndex("name"))
42
43        self.genreEdit.setModel(model.relationModel(genre_idx))
44        self.genreEdit.setModelColumn(model.relationModel(genre_idx).fieldIndex("name"))
45
46        # Lock and prohibit resizing of the width of the rating column:
47        self.bookTable.horizontalHeader().setSectionResizeMode(model.fieldIndex("rating"),
48            QHeaderView.ResizeToContents)
49
50        mapper = QDataWidgetMapper(self)
51        mapper.setModel(model)
52        mapper.setItemDelegate(BookDelegate(self))
53        mapper.addMapping(self.titleEdit, model.fieldIndex("title"))
54        mapper.addMapping(self.yearEdit, model.fieldIndex("year"))
55        mapper.addMapping(self.authorEdit, author_idx)
56        mapper.addMapping(self.genreEdit, genre_idx)
57        mapper.addMapping(self.ratingEdit, model.fieldIndex("rating"))
58
59        selection_model = self.bookTable.selectionModel()
60        selection_model.currentRowChanged.connect(mapper.setCurrentModelIndex)
61
62        self.bookTable.setCurrentIndex(model.index(0, 0))
63        self.create_menubar()
64

注意:

Python版本的BookWindow类定义从QMainWindowUi_BookWindow继承,后者定义在您之前生成的ui_bookwindow.py文件中。

以下是其余代码的外观

C++版本#

 1    mapper->setItemDelegate(new BookDelegate(this));
 2    mapper->addMapping(ui.titleEdit, model->fieldIndex("title"));
 3    mapper->addMapping(ui.yearEdit, model->fieldIndex("year"));
 4    mapper->addMapping(ui.authorEdit, authorIdx);
 5    mapper->addMapping(ui.genreEdit, genreIdx);
 6    mapper->addMapping(ui.ratingEdit, model->fieldIndex("rating"));
 7
 8    connect(ui.bookTable->selectionModel(),
 9            &QItemSelectionModel::currentRowChanged,
10            mapper,
11            &QDataWidgetMapper::setCurrentModelIndex
12            );
13
14    ui.bookTable->setCurrentIndex(model->index(0, 0));
15    createMenuBar();
16}
17
18void BookWindow::showError(const QSqlError &err)
19{
20    QMessageBox::critical(this, "Unable to initialize Database",
21                "Error initializing database: " + err.text());
22}
23
24void BookWindow::createMenuBar()
25{
26    QAction *quitAction = new QAction(tr("&Quit"), this);
27    QAction *aboutAction = new QAction(tr("&About"), this);
28    QAction *aboutQtAction = new QAction(tr("&About Qt"), this);
29
30    QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
31    fileMenu->addAction(quitAction);
32
33    QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
34    helpMenu->addAction(aboutAction);
35    helpMenu->addAction(aboutQtAction);
36
37    connect(quitAction, &QAction::triggered, this, &BookWindow::close);
38    connect(aboutAction, &QAction::triggered, this, &BookWindow::about);
39    connect(aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
40}
41
42void BookWindow::about()
43{
44    QMessageBox::about(this, tr("About Books"),
45            tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
46               "with a model/view framework."));
47}

Python版本#

 1    def showError(err):
 2        QMessageBox.critical(self, "Unable to initialize Database",
 3                    "Error initializing database: " + err.text())
 4
 5    def create_menubar(self):
 6        file_menu = self.menuBar().addMenu(self.tr("&File"))
 7        quit_action = file_menu.addAction(self.tr("&Quit"))
 8        quit_action.triggered.connect(qApp.quit)
 9
10        help_menu = self.menuBar().addMenu(self.tr("&Help"))
11        about_action = help_menu.addAction(self.tr("&About"))
12        about_action.setShortcut(QKeySequence.HelpContents)
13        about_action.triggered.connect(self.about)
14        aboutQt_action = help_menu.addAction("&About Qt")
15        aboutQt_action.triggered.connect(qApp.aboutQt)
16
17    def about(self):
18        QMessageBox.about(self, self.tr("About Books"),
19            self.tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
20                "with a model/view framework."))

现在所有必要的组件都准备好了,尝试在main.py中将它们组合在一起。

 1
 2import sys
 3from PySide6.QtWidgets import QApplication
 4from bookwindow import BookWindow
 5import rc_books
 6
 7if __name__ == "__main__":
 8    app = QApplication([])
 9
10    window = BookWindow()
11    window.resize(800, 600)
12    window.show()
13
14    sys.exit(app.exec())

尝试运行以下内容,看是否能得到以下输出

BookWindow with a QTableView and a few input fields

现在,如果你回顾到第二章,你会注意到bookdelegate.py从文件系统中加载了star.png。相反,你可以在一个qrc文件中添加它,并从中加载。如果你的应用程序针对不同的平台,后者方法更为推荐,因为如今大多数流行的平台都采用了更严格的文件访问策略。

要将star.png添加到.qrc中,创建一个名为books.qrc的文件,并按照以下XML内容添加到其中

1<!DOCTYPE RCC><RCC version="1.0">
2<qresource>
3  <file>images/star.png</file>
4</qresource>
5</RCC>

这是一个简单的XML文件,定义了一个包含应用程序需要的所有资源的列表。在这种情况下,它只是star.png图像。

现在,在books.qrc文件上运行pyside6-rcc工具,以生成rc_books.py

pyside6-rcc books.qrc -o rc_books.py

一旦你生成了Python脚本,就在bookdelegate.pymain.py中进行以下修改

--- /data/snapshot-pyside-6.7-rel/tqtc-pyside-setup/build/testenv-tqtc_6.7/build/pyside6/doc/base/tutorials/portingguide/chapter2/bookdelegate.py
+++ /data/snapshot-pyside-6.7-rel/tqtc-pyside-setup/build/testenv-tqtc_6.7/build/pyside6/doc/base/tutorials/portingguide/chapter3/bookdelegate.py
@@ -1,24 +1,19 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial
 
-import copy
-import os
-from pathlib import Path
-
+import copy, os
 from PySide6.QtSql import QSqlRelationalDelegate
 from PySide6.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate,
     QStyle, QStyleOptionViewItem)
 from PySide6.QtGui import QMouseEvent, QPixmap, QPalette, QImage
 from PySide6.QtCore import QEvent, QSize, Qt, QUrl
 
-
 class BookDelegate(QSqlRelationalDelegate):
     """Books delegate to rate the books"""
 
-    def __init__(self, parent=None):
+    def __init__(self, star_png, parent=None):
         QSqlRelationalDelegate.__init__(self, parent)
-        star_png = Path(__file__).parent / "images" / "star.png"
-        self.star = QPixmap(star_png)
+        self.star = QPixmap(":/images/star.png")
 
     def paint(self, painter, option, index):
         """ Paint the items in the table.
--- /data/snapshot-pyside-6.7-rel/tqtc-pyside-setup/build/testenv-tqtc_6.7/build/pyside6/doc/base/tutorials/portingguide/chapter3/main-old.py
+++ /data/snapshot-pyside-6.7-rel/tqtc-pyside-setup/build/testenv-tqtc_6.7/build/pyside6/doc/base/tutorials/portingguide/chapter3/main.py
@@ -4,6 +4,7 @@
 import sys
 from PySide6.QtWidgets import QApplication
 from bookwindow import BookWindow
+import rc_books
 
 if __name__ == "__main__":
     app = QApplication([])

尽管这些修改在UI上不会有明显的变化,但使用.qrc是一个更好的方法。

现在你已经成功移植了SQL Books示例,你知道这样做有多简单。尝试移植另一个C++应用。