bookdelegate.cpp to bookdelegate.py — Qt for Python (original) (raw)
Now that your database is in place, port the C++ code for theBookDelegate
class. This class offers a delegate to present and edit the data in a QTableView
. It inheritsQSqlRelationalDelegate
interface, which offers features specific for handling relational databases, such as a combobox editor for foreign key fields. To begin with, createbookdelegate.py
and add the following imports to it:
1 2import copy, os 3from PySide2.QtSql import QSqlRelationalDelegate 4from PySide2.QtWidgets import (QItemDelegate, QSpinBox, QStyledItemDelegate, 5 QStyle, QStyleOptionViewItem) 6from PySide2.QtGui import QMouseEvent, QPixmap, QPalette, QImage 7from PySide2.QtCore import QEvent, QSize, Qt, QUrl 8
After the necessary import
statements, port the constructor code for the BookDelegate
class. Both the C++ and Python versions of this code initialize aQSqlRelationalDelegate
and QPixmap
instance. Here is how they look:
C++ version¶
1BookDelegate::BookDelegate(QObject *parent) 2 : QSqlRelationalDelegate(parent), star(QPixmap(":images/star.png")) 3{ 4}
Python version¶
1 2class BookDelegate(QSqlRelationalDelegate): 3 """Books delegate to rate the books""" 4 5 def init(self, parent=None): 6 QSqlRelationalDelegate.init(self, parent) 7 star_png = os.path.dirname(file) + "\images\star.png" 8 self.star = QPixmap(star_png)
Note
The Python version loads the QPixmap
using the absolute path of star.png
in the local filesystem.
As the default functionality offered by theQSqlRelationalDelegate
is not enough to present the books data, you must reimplement a few functions. For example, painting stars to represent the rating for each book in the table. Here is how the reimplemented code looks like:
C++ version¶
1void BookDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, 2 const QModelIndex &index) const 3{ 4 if (index.column() != 5) { 5 QStyleOptionViewItem opt = option; 6 // Since we draw the grid ourselves: 7 opt.rect.adjust(0, 0, -1, -1); 8 QSqlRelationalDelegate::paint(painter, opt, index); 9 } else { 10 const QAbstractItemModel *model = index.model(); 11 QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? 12 (option.state & QStyle::State_Active) ? 13 QPalette::Normal : 14 QPalette::Inactive : 15 QPalette::Disabled; 16 17 if (option.state & QStyle::State_Selected) 18 painter->fillRect( 19 option.rect, 20 option.palette.color(cg, QPalette::Highlight)); 21 22 int rating = model->data(index, Qt::DisplayRole).toInt(); 23 int width = star.width(); 24 int height = star.height(); 25 int x = option.rect.x(); 26 int y = option.rect.y() + (option.rect.height() / 2) - (height / 2); 27 for (int i = 0; i < rating; ++i) { 28 painter->drawPixmap(x, y, star); 29 x += width; 30 } 31 // Since we draw the grid ourselves: 32 drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1)); 33 } 34 35 QPen pen = painter->pen(); 36 painter->setPen(option.palette.color(QPalette::Mid)); 37 painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight()); 38 painter->drawLine(option.rect.topRight(), option.rect.bottomRight()); 39 painter->setPen(pen); 40} 41 42QSize BookDelegate::sizeHint(const QStyleOptionViewItem &option, 43 const QModelIndex &index) const 44{ 45 if (index.column() == 5) 46 return QSize(5 * star.width(), star.height()) + QSize(1, 1); 47 // Since we draw the grid ourselves: 48 return QSqlRelationalDelegate::sizeHint(option, index) + QSize(1, 1); 49} 50 51bool BookDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, 52 const QStyleOptionViewItem &option, 53 const QModelIndex &index) 54{ 55 if (index.column() != 5) 56 return QSqlRelationalDelegate::editorEvent(event, model, option, index); 57 58 if (event->type() == QEvent::MouseButtonPress) { 59 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); 60 int stars = qBound(0, int(0.7 + qreal(mouseEvent->pos().x() 61 - option.rect.x()) / star.width()), 5); 62 model->setData(index, QVariant(stars)); 63 // So that the selection can change: 64 return false; 65 } 66 67 return true; 68} 69 70QWidget *BookDelegate::createEditor(QWidget *parent, 71 const QStyleOptionViewItem &option, 72 const QModelIndex &index) const 73{ 74 if (index.column() != 4) 75 return QSqlRelationalDelegate::createEditor(parent, option, index); 76 77 // For editing the year, return a spinbox with a range from -1000 to 2100. 78 QSpinBox *sb = new QSpinBox(parent); 79 sb->setFrame(false); 80 sb->setMaximum(2100); 81 sb->setMinimum(-1000); 82 83 return sb; 84}
Python version¶
1 2 def paint(self, painter, option, index): 3 """ Paint the items in the table. 4 5 If the item referred to by is a StarRating, we 6 handle the painting ourselves. For the other items, we 7 let the base class handle the painting as usual. 8 9 In a polished application, we'd use a better check than 10 the column number to find out if we needed to paint the 11 stars, but it works for the purposes of this example. 12 """ 13 if index.column() != 5: 14 # Since we draw the grid ourselves: 15 opt = copy.copy(option) 16 opt.rect = option.rect.adjusted(0, 0, -1, -1) 17 QSqlRelationalDelegate.paint(self, painter, opt, index) 18 else: 19 model = index.model() 20 if option.state & QStyle.State_Enabled: 21 if option.state & QStyle.State_Active: 22 color_group = QPalette.Normal 23 else: 24 color_group = QPalette.Inactive 25 else: 26 color_group = QPalette.Disabled 27 28 if option.state & QStyle.State_Selected: 29 painter.fillRect(option.rect, 30 option.palette.color(color_group, QPalette.Highlight)) 31 rating = model.data(index, Qt.DisplayRole) 32 width = self.star.width() 33 height = self.star.height() 34 x = option.rect.x() 35 y = option.rect.y() + (option.rect.height() / 2) - (height / 2) 36 for i in range(rating): 37 painter.drawPixmap(x, y, self.star) 38 x += width 39 40 # Since we draw the grid ourselves: 41 self.drawFocus(painter, option, option.rect.adjusted(0, 0, -1, -1)) 42 43 pen = painter.pen() 44 painter.setPen(option.palette.color(QPalette.Mid)) 45 painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight()) 46 painter.drawLine(option.rect.topRight(), option.rect.bottomRight()) 47 painter.setPen(pen) 48 49 def sizeHint(self, option, index): 50 """ Returns the size needed to display the item in a QSize object. """ 51 if index.column() == 5: 52 size_hint = QSize(5 * self.star.width(), self.star.height()) + QSize(1, 1) 53 return size_hint 54 # Since we draw the grid ourselves: 55 return QSqlRelationalDelegate.sizeHint(self, option, index) + QSize(1, 1) 56 57 def editorEvent(self, event, model, option, index): 58 if index.column() != 5: 59 return False 60 61 if event.type() == QEvent.MouseButtonPress: 62 mouse_pos = event.pos() 63 new_stars = int(0.7 + (mouse_pos.x() - option.rect.x()) / self.star.width()) 64 stars = max(0, min(new_stars, 5)) 65 model.setData(index, stars) 66 # So that the selection can change 67 return False 68 69 return True 70 71 def createEditor(self, parent, option, index): 72 if index.column() != 4: 73 return QSqlRelationalDelegate.createEditor(self, parent, option, index) 74 75 # For editing the year, return a spinbox with a range from -1000 to 2100. 76 spinbox = QSpinBox(parent) 77 spinbox.setFrame(False) 78 spinbox.setMaximum(2100) 79 spinbox.setMinimum(-1000) 80 return spinbox
Now that the delegate is in place, run the followingmain.py
to see how the data is presented:
1 2import sys 3 4from PySide2.QtCore import Qt 5from PySide2.QtSql import QSqlQueryModel 6from PySide2.QtWidgets import QTableView, QApplication 7 8import createdb 9from bookdelegate import BookDelegate 10 11if name == "main": 12 app = QApplication() 13 createdb.init_db() 14 15 model = QSqlQueryModel() 16 model.setQuery("select title, author, genre, year, rating from books") 17 18 table = QTableView() 19 table.setModel(model) 20 table.setItemDelegate(BookDelegate()) 21 table.resize(800, 600) 22 table.show() 23 24 sys.exit(app.exec_())
Here is how the application will look when you run it:
The only difference you’ll notice now in comparison tochapter 1 is that therating
column looks different.
Try improving the table even further by adding these features:
- Title for each column
- SQL relation for the
author_id
andgenre_id
columns - Set a title to the window
With these features, this is how your table will look like:
© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.