第3章:将bookdwindow.cpp
移植到bookwindow.py
¶
在bookdelegate之后,移植BookWindow
类的C++代码。它提供了一个QMainWindow,包含一个QTableView
来展示书籍数据,以及一个详细信息部分,其中包含一组输入字段,用于编辑表格中选定的行。首先,创建bookwindow.py
并向其中添加以下导入:
1from __future__ import annotations
2
3from PySide6.QtGui import QAction
4from PySide6.QtWidgets import (QAbstractItemView, QDataWidgetMapper,
5 QHeaderView, QMainWindow, QMessageBox)
6from PySide6.QtGui import QKeySequence
7from PySide6.QtSql import (QSqlRelation, QSqlRelationalTableModel, QSqlTableModel,
8 QSqlError)
9from PySide6.QtCore import QAbstractItemModel, QObject, QSize, Qt, Slot
10import createdb
11from ui_bookwindow import Ui_BookWindow
12from bookdelegate import BookDelegate
13
14
注意
导入包括您之前移植的BookDelegate
和Ui_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 版本¶
1
2class BookWindow(QMainWindow, Ui_BookWindow):
3 # """A window to show the books available"""
4
5 def __init__(self):
6 QMainWindow.__init__(self)
7 self.setupUi(self)
8
9 #Initialize db
10 createdb.init_db()
11
12 model = QSqlRelationalTableModel(self.bookTable)
13 model.setEditStrategy(QSqlTableModel.OnManualSubmit)
14 model.setTable("books")
15
16 # Remember the indexes of the columns:
17 author_idx = model.fieldIndex("author")
18 genre_idx = model.fieldIndex("genre")
19
20 # Set the relations to the other database tables:
21 model.setRelation(author_idx, QSqlRelation("authors", "id", "name"))
22 model.setRelation(genre_idx, QSqlRelation("genres", "id", "name"))
23
24 # Set the localized header captions:
25 model.setHeaderData(author_idx, Qt.Horizontal, self.tr("Author Name"))
26 model.setHeaderData(genre_idx, Qt.Horizontal, self.tr("Genre"))
27 model.setHeaderData(model.fieldIndex("title"), Qt.Horizontal, self.tr("Title"))
28 model.setHeaderData(model.fieldIndex("year"), Qt.Horizontal, self.tr("Year"))
29 model.setHeaderData(model.fieldIndex("rating"), Qt.Horizontal, self.tr("Rating"))
30
31 if not model.select():
32 print(model.lastError())
33
34 # Set the model and hide the ID column:
35 self.bookTable.setModel(model)
36 self.bookTable.setItemDelegate(BookDelegate(self.bookTable))
37 self.bookTable.setColumnHidden(model.fieldIndex("id"), True)
38 self.bookTable.setSelectionMode(QAbstractItemView.SingleSelection)
39
40 # Initialize the Author combo box:
41 self.authorEdit.setModel(model.relationModel(author_idx))
42 self.authorEdit.setModelColumn(model.relationModel(author_idx).fieldIndex("name"))
43
44 self.genreEdit.setModel(model.relationModel(genre_idx))
45 self.genreEdit.setModelColumn(model.relationModel(genre_idx).fieldIndex("name"))
46
47 # Lock and prohibit resizing of the width of the rating column:
48 self.bookTable.horizontalHeader().setSectionResizeMode(model.fieldIndex("rating"),
49 QHeaderView.ResizeToContents)
50
51 mapper = QDataWidgetMapper(self)
52 mapper.setModel(model)
53 mapper.setItemDelegate(BookDelegate(self))
54 mapper.addMapping(self.titleEdit, model.fieldIndex("title"))
55 mapper.addMapping(self.yearEdit, model.fieldIndex("year"))
56 mapper.addMapping(self.authorEdit, author_idx)
57 mapper.addMapping(self.genreEdit, genre_idx)
58 mapper.addMapping(self.ratingEdit, model.fieldIndex("rating"))
59
60 selection_model = self.bookTable.selectionModel()
61 selection_model.currentRowChanged.connect(mapper.setCurrentModelIndex)
62
63 self.bookTable.setCurrentIndex(model.index(0, 0))
64 self.create_menubar()
注意
Python 版本的 BookWindow
类定义继承自 QMainWindow
和 Ui_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
2 def showError(err):
3 QMessageBox.critical(self, "Unable to initialize Database",
4 "Error initializing database: " + err.text())
5
6 def create_menubar(self):
7 file_menu = self.menuBar().addMenu(self.tr("&File"))
8 quit_action = file_menu.addAction(self.tr("&Quit"))
9 quit_action.triggered.connect(qApp.quit)
10
11 help_menu = self.menuBar().addMenu(self.tr("&Help"))
12 about_action = help_menu.addAction(self.tr("&About"))
13 about_action.setShortcut(QKeySequence.HelpContents)
14 about_action.triggered.connect(self.about)
15 aboutQt_action = help_menu.addAction("&About Qt")
16 aboutQt_action.triggered.connect(qApp.aboutQt)
17
18 def about(self):
19 QMessageBox.about(self, self.tr("About Books"),
20 self.tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
21 "with a model/view framework."))
现在所有必要的部分都已就位,尝试将它们整合到main.py
中。
1from __future__ import annotations
2
3import sys
4from PySide6.QtWidgets import QApplication
5from bookwindow import BookWindow
6import rc_books
7
8if __name__ == "__main__":
9 app = QApplication([])
10
11 window = BookWindow()
12 window.resize(800, 600)
13 window.show()
14
15 sys.exit(app.exec())
尝试运行此操作以查看是否获得以下输出:

现在,如果你回头看chapter2,
你会注意到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.py
和main.py
进行以下更改:
--- /data/snapshot-pyside-6.8-rel/tqtc-pyside-setup/build/testenv-tqtc_6.8/build/pyside6/doc/base/tutorials/portingguide/chapter2/bookdelegate.py
+++ /data/snapshot-pyside-6.8-rel/tqtc-pyside-setup/build/testenv-tqtc_6.8/build/pyside6/doc/base/tutorials/portingguide/chapter3/bookdelegate.py
@@ -2,24 +2,19 @@
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
-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.8-rel/tqtc-pyside-setup/build/testenv-tqtc_6.8/build/pyside6/doc/base/tutorials/portingguide/chapter3/main-old.py
+++ /data/snapshot-pyside-6.8-rel/tqtc-pyside-setup/build/testenv-tqtc_6.8/build/pyside6/doc/base/tutorials/portingguide/chapter3/main.py
@@ -5,6 +5,7 @@
import sys
from PySide6.QtWidgets import QApplication
from bookwindow import BookWindow
+import rc_books
if __name__ == "__main__":
app = QApplication([])
尽管这些更改后用户界面不会有明显差异,但使用.qrc
是一种更好的方法。
现在你已经成功移植了SQL Books示例,你知道这有多容易。尝试移植另一个C++应用程序。