第三章:将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):
注意:
导入包括了您之前移植的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#
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
类定义从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 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())
尝试运行以下内容,看是否能得到以下输出
现在,如果你回顾到第二章,你会注意到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.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++应用。