diff --git a/examples/sql/books/CMakeLists.txt b/examples/sql/books/CMakeLists.txt index 86ca051c3b8..36e6dca9e30 100644 --- a/examples/sql/books/CMakeLists.txt +++ b/examples/sql/books/CMakeLists.txt @@ -16,7 +16,7 @@ qt_standard_project_setup() qt_add_executable(books bookdelegate.cpp bookdelegate.h - bookwindow.cpp bookwindow.h bookwindow.ui + bookwindow.cpp bookwindow.h initdb.h main.cpp ) @@ -35,7 +35,8 @@ target_link_libraries(books PRIVATE # Resources: set(books_resource_files - "images/star.png" + "images/star.svg" + "images/star-filled.svg" ) qt_add_resources(books "books" diff --git a/examples/sql/books/bookdelegate.cpp b/examples/sql/books/bookdelegate.cpp index af2a2842985..6aac70b8600 100644 --- a/examples/sql/books/bookdelegate.cpp +++ b/examples/sql/books/bookdelegate.cpp @@ -6,9 +6,8 @@ #include BookDelegate::BookDelegate(QObject *parent) - : QSqlRelationalDelegate(parent), star(QPixmap(":images/star.png")) -{ -} + : QSqlRelationalDelegate(parent) +{} void BookDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const @@ -28,31 +27,34 @@ void BookDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, option.rect, option.palette.color(cg, QPalette::Highlight)); - int rating = model->data(index, Qt::DisplayRole).toInt(); - int width = star.width(); - int height = star.height(); - int x = option.rect.x(); + const int rating = model->data(index, Qt::DisplayRole).toInt(); + const int width = iconDimension; + const int height = width; + // add cellPadding / 2 to center the stars in the cell + int x = option.rect.x() + cellPadding / 2; int y = option.rect.y() + (option.rect.height() / 2) - (height / 2); - for (int i = 0; i < rating; ++i) { - painter->drawPixmap(x, y, star); + + QIcon starIcon(QStringLiteral(":images/star.svg")); + QIcon starFilledIcon(QStringLiteral(":images/star-filled.svg")); + + for (int i = 0; i < 5; ++i) { + if (i < rating) { + starFilledIcon.paint(painter, QRect(x, y, width, height)); + } else { + starIcon.paint(painter, QRect(x, y, width, height)); + } x += width; } } - - QPen pen = painter->pen(); - painter->setPen(option.palette.color(QPalette::Mid)); - painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight()); - painter->drawLine(option.rect.topRight(), option.rect.bottomRight()); - painter->setPen(pen); } QSize BookDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.column() == 5) - return QSize(5 * star.width(), star.height()) + QSize(1, 1); + return QSize(5 * iconDimension, iconDimension) + QSize(cellPadding, cellPadding); // Since we draw the grid ourselves: - return QSqlRelationalDelegate::sizeHint(option, index) + QSize(1, 1); + return QSqlRelationalDelegate::sizeHint(option, index) + QSize(cellPadding, cellPadding); } bool BookDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, @@ -65,7 +67,7 @@ bool BookDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *mouseEvent = static_cast(event); int stars = qBound(0, int(0.7 + qreal(mouseEvent->position().toPoint().x() - - option.rect.x()) / star.width()), 5); + - option.rect.x()) / iconDimension), 5); model->setData(index, QVariant(stars)); // So that the selection can change: return false; diff --git a/examples/sql/books/bookdelegate.h b/examples/sql/books/bookdelegate.h index d0b157b3951..5ddb635515e 100644 --- a/examples/sql/books/bookdelegate.h +++ b/examples/sql/books/bookdelegate.h @@ -30,7 +30,8 @@ public: const QModelIndex &index) const override; private: - QPixmap star; + const int cellPadding = 6; + const int iconDimension = 24; }; #endif diff --git a/examples/sql/books/books.pro b/examples/sql/books/books.pro index 870e4a6c007..953945912e5 100644 --- a/examples/sql/books/books.pro +++ b/examples/sql/books/books.pro @@ -4,7 +4,6 @@ INCLUDEPATH += . HEADERS = bookdelegate.h bookwindow.h initdb.h RESOURCES = books.qrc SOURCES = bookdelegate.cpp main.cpp bookwindow.cpp -FORMS = bookwindow.ui QT += sql widgets widgets requires(qtConfig(tableview)) diff --git a/examples/sql/books/books.qrc b/examples/sql/books/books.qrc index 342638ecb04..1a4dac32ccd 100644 --- a/examples/sql/books/books.qrc +++ b/examples/sql/books/books.qrc @@ -1,5 +1,6 @@ - images/star.png + images/star.svg + images/star-filled.svg diff --git a/examples/sql/books/bookwindow.cpp b/examples/sql/books/bookwindow.cpp index b7baad48cc0..8b34bd7a77b 100644 --- a/examples/sql/books/bookwindow.cpp +++ b/examples/sql/books/bookwindow.cpp @@ -9,8 +9,6 @@ BookWindow::BookWindow() { - ui.setupUi(this); - if (!QSqlDatabase::drivers().contains("QSQLITE")) QMessageBox::critical( this, @@ -25,71 +23,28 @@ BookWindow::BookWindow() return; } - // Create the data model: - model = new QSqlRelationalTableModel(ui.bookTable); - model->setEditStrategy(QSqlTableModel::OnManualSubmit); - model->setTable("books"); + // create the central widget for the window + window = new QWidget(this); + setCentralWidget(window); - // Remember the indexes of the columns: - authorIdx = model->fieldIndex("author"); - genreIdx = model->fieldIndex("genre"); + createLayout(); - // Set the relations to the other database tables: - model->setRelation(authorIdx, QSqlRelation("authors", "id", "name")); - model->setRelation(genreIdx, QSqlRelation("genres", "id", "name")); + createModel(); - // Set the localized header captions: - model->setHeaderData(authorIdx, Qt::Horizontal, tr("Author Name")); - model->setHeaderData(genreIdx, Qt::Horizontal, tr("Genre")); - model->setHeaderData(model->fieldIndex("title"), - Qt::Horizontal, tr("Title")); - model->setHeaderData(model->fieldIndex("year"), Qt::Horizontal, tr("Year")); - model->setHeaderData(model->fieldIndex("rating"), - Qt::Horizontal, tr("Rating")); - - // Populate the model: + // Populate the model if (!model->select()) { showError(model->lastError()); return; } - // Set the model and hide the ID column: - ui.bookTable->setModel(model); - ui.bookTable->setItemDelegate(new BookDelegate(ui.bookTable)); - ui.bookTable->setColumnHidden(model->fieldIndex("id"), true); - ui.bookTable->setSelectionMode(QAbstractItemView::SingleSelection); + configureWidgets(); - // Initialize the Author combo box: - ui.authorEdit->setModel(model->relationModel(authorIdx)); - ui.authorEdit->setModelColumn( - model->relationModel(authorIdx)->fieldIndex("name")); + // create the mappings between the UI elements and the SQL model + createMappings(); - ui.genreEdit->setModel(model->relationModel(genreIdx)); - ui.genreEdit->setModelColumn( - model->relationModel(genreIdx)->fieldIndex("name")); + tableView->setCurrentIndex(model->index(0, 0)); + tableView->selectRow(0); - // Lock and prohibit resizing of the width of the rating column: - ui.bookTable->horizontalHeader()->setSectionResizeMode( - model->fieldIndex("rating"), - QHeaderView::ResizeToContents); - - QDataWidgetMapper *mapper = new QDataWidgetMapper(this); - mapper->setModel(model); - mapper->setItemDelegate(new BookDelegate(this)); - mapper->addMapping(ui.titleEdit, model->fieldIndex("title")); - mapper->addMapping(ui.yearEdit, model->fieldIndex("year")); - mapper->addMapping(ui.authorEdit, authorIdx); - mapper->addMapping(ui.genreEdit, genreIdx); - mapper->addMapping(ui.ratingEdit, model->fieldIndex("rating")); - - connect(ui.bookTable->selectionModel(), - &QItemSelectionModel::currentRowChanged, - mapper, - &QDataWidgetMapper::setCurrentModelIndex - ); - - ui.bookTable->setCurrentIndex(model->index(0, 0)); - ui.bookTable->selectRow(0); createMenuBar(); } @@ -99,6 +54,140 @@ void BookWindow::showError(const QSqlError &err) "Error initializing database: " + err.text()); } +void BookWindow::createLayout() +{ + tableView = new QTableView(window); + + gridLayout = new QGridLayout(window); + + titleLabel = new QLabel(tr("Title:"), window); + titleLineEdit = new QLineEdit(window); + authorLabel = new QLabel(tr("Author:"), window); + authorComboBox = new QComboBox(window); + genreLabel = new QLabel(tr("Genre:"), window); + genreComboBox = new QComboBox(window); + yearLabel = new QLabel(tr("Year:"), window); + yearSpinBox = new QSpinBox(window); + ratingLabel = new QLabel(tr("Rating:"), window); + ratingComboBox = new QComboBox(window); + + gridLayout->addWidget(titleLabel, 0, 0, Qt::AlignRight); + gridLayout->addWidget(titleLineEdit, 0, 1, 1, 3); + gridLayout->addWidget(authorLabel, 1, 0, Qt::AlignRight); + gridLayout->addWidget(authorComboBox, 1, 1); + gridLayout->addWidget(yearLabel, 1, 2, Qt::AlignRight); + gridLayout->addWidget(yearSpinBox, 1, 3); + gridLayout->addWidget(genreLabel, 2, 0, Qt::AlignRight); + gridLayout->addWidget(genreComboBox, 2, 1); + gridLayout->addWidget(ratingLabel, 2, 2, Qt::AlignRight); + gridLayout->addWidget(ratingComboBox, 2, 3); + gridLayout->addWidget(tableView, 3, 0, 1, 4, Qt::AlignCenter); + gridLayout->setColumnStretch(1, 1000); + gridLayout->setColumnStretch(3, 1000); + + gridLayout->setContentsMargins(18, 18, 18, 18); + gridLayout->setSpacing(18); + gridLayout->setAlignment(Qt::AlignHCenter); +} + +void BookWindow::createModel() +{ + model = new QSqlRelationalTableModel(tableView); + model->setEditStrategy(QSqlTableModel::OnManualSubmit); + model->setTable("books"); + + authorIdx = model->fieldIndex("author"); + genreIdx = model->fieldIndex("genre"); + + // Set the relations to the other database tables + model->setRelation(authorIdx, QSqlRelation("authors", "id", "name")); + model->setRelation(genreIdx, QSqlRelation("genres", "id", "name")); + + // Set the localised header captions + model->setHeaderData(authorIdx, Qt::Horizontal, tr("Author Name")); + model->setHeaderData(genreIdx, Qt::Horizontal, tr("Genre")); + model->setHeaderData(model->fieldIndex("title"), + Qt::Horizontal, tr("Title")); + model->setHeaderData(model->fieldIndex("year"), Qt::Horizontal, tr("Year")); + model->setHeaderData(model->fieldIndex("rating"), + Qt::Horizontal, tr("Rating")); +} + +void BookWindow::configureWidgets() +{ + tableView->setModel(model); + tableView->setItemDelegate(new BookDelegate(tableView)); + tableView->setColumnHidden(model->fieldIndex("id"), true); + tableView->verticalHeader()->setVisible(false); + tableView->setSelectionMode(QAbstractItemView::ExtendedSelection); + tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + + // Lock and prohibit resizing of the width of the columns + tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + tableView->horizontalHeader()->setFixedHeight(tableView->rowHeight(0)); + + // increment by two to consider the frame + tableView->setFixedWidth(tableView->horizontalHeader()->length() + + tableView->verticalScrollBar()->sizeHint().width() + 2); + tableView->setMaximumHeight(tableView->verticalHeader()->length() + + tableView->horizontalHeader()->height() + 2); + + authorComboBox->setModel(model->relationModel(authorIdx)); + authorComboBox->setModelColumn(model->relationModel(authorIdx)->fieldIndex("name")); + + genreComboBox->setModel(model->relationModel(genreIdx)); + genreComboBox->setModelColumn(model->relationModel(genreIdx)->fieldIndex("name")); + + yearSpinBox->setMaximum(9999); + + const int width = 16; + const int height = width; + const int y = 2; + const int padding = 2; + + QSize iconSize = QSize(width * 5 + padding * 2, width + padding * 2); + QIcon starIcon(QStringLiteral(":images/star.svg")); + QIcon starFilledIcon(QStringLiteral(":images/star-filled.svg")); + + for (int row = 0; row < 6; ++row) { + QPixmap icon(iconSize); + icon.fill(Qt::transparent); + QPainter painter(&icon); + int x = 2; + + for (int col = 0; col < 5; ++col) { + if (col < row) { + starFilledIcon.paint(&painter, QRect(x, y, width, height)); + } else { + starIcon.paint(&painter, QRect(x, y, width, height)); + } + x += width; + } + ratingComboBox->addItem(icon, ""); + ratingComboBox->setItemData(row, QString::number(row + 1)); + } + + ratingComboBox->setIconSize(iconSize); +} + +void BookWindow::createMappings() +{ + QDataWidgetMapper *mapper = new QDataWidgetMapper(this); + mapper->setModel(model); + mapper->setItemDelegate(new BookDelegate(this)); + mapper->addMapping(titleLineEdit, model->fieldIndex("title")); + mapper->addMapping(yearSpinBox, model->fieldIndex("year")); + mapper->addMapping(authorComboBox, authorIdx); + mapper->addMapping(genreComboBox, genreIdx); + mapper->addMapping(ratingComboBox, model->fieldIndex("rating"), "currentIndex"); + connect(tableView->selectionModel(), + &QItemSelectionModel::currentRowChanged, + mapper, + &QDataWidgetMapper::setCurrentModelIndex + ); +} + void BookWindow::createMenuBar() { QAction *quitAction = new QAction(tr("&Quit"), this); diff --git a/examples/sql/books/bookwindow.h b/examples/sql/books/bookwindow.h index 8030516e5b6..03d83e219df 100644 --- a/examples/sql/books/bookwindow.h +++ b/examples/sql/books/bookwindow.h @@ -7,9 +7,6 @@ #include #include -#include "ui_bookwindow.h" - - class BookWindow: public QMainWindow { Q_OBJECT @@ -21,11 +18,29 @@ private slots: private: void showError(const QSqlError &err); - Ui::BookWindow ui; QSqlRelationalTableModel *model = nullptr; int authorIdx = 0, genreIdx = 0; + void createLayout(); + void createModel(); + void configureWidgets(); + void createMappings(); void createMenuBar(); + + QWidget *window = nullptr; + + QGridLayout *gridLayout = nullptr; + QTableView *tableView = nullptr; + QLabel *titleLabel = nullptr; + QLineEdit *titleLineEdit = nullptr; + QLabel *authorLabel = nullptr; + QComboBox *authorComboBox = nullptr; + QLabel *genreLabel = nullptr; + QComboBox *genreComboBox = nullptr; + QLabel *yearLabel = nullptr; + QSpinBox *yearSpinBox = nullptr; + QLabel *ratingLabel = nullptr; + QComboBox *ratingComboBox = nullptr; }; #endif diff --git a/examples/sql/books/bookwindow.ui b/examples/sql/books/bookwindow.ui deleted file mode 100644 index c93726c5532..00000000000 --- a/examples/sql/books/bookwindow.ui +++ /dev/null @@ -1,167 +0,0 @@ - - - BookWindow - - - - 0 - 0 - 601 - 420 - - - - Books - - - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - QAbstractItemView::SelectRows - - - - - - - Details - - - - QFormLayout::ExpandingFieldsGrow - - - - - <b>Title:</b> - - - - - - - true - - - - - - - <b>Author: </b> - - - - - - - true - - - - - - - <b>Genre:</b> - - - - - - - true - - - - - - - <b>Year:</b> - - - - - - - true - - - - - - -1000 - - - 2100 - - - - - - - <b>Rating:</b> - - - - - - - 5 - - - - - - - - - - - - - - bookTable - titleEdit - authorEdit - genreEdit - yearEdit - - - - diff --git a/examples/sql/books/images/star-filled.svg b/examples/sql/books/images/star-filled.svg new file mode 100644 index 00000000000..8a2aee27fb9 --- /dev/null +++ b/examples/sql/books/images/star-filled.svg @@ -0,0 +1 @@ + diff --git a/examples/sql/books/images/star.png b/examples/sql/books/images/star.png deleted file mode 100644 index 87f4464bd5e..00000000000 Binary files a/examples/sql/books/images/star.png and /dev/null differ diff --git a/examples/sql/books/images/star.svg b/examples/sql/books/images/star.svg new file mode 100644 index 00000000000..d959abc1820 --- /dev/null +++ b/examples/sql/books/images/star.svg @@ -0,0 +1 @@ + diff --git a/examples/sql/doc/images/books-demo.png b/examples/sql/doc/images/books-demo.png index 77b3b308c94..29f5c844730 100644 Binary files a/examples/sql/doc/images/books-demo.png and b/examples/sql/doc/images/books-demo.png differ