qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,20 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(NOT TARGET Qt6::Widgets)
return()
endif()
qt_internal_add_example(books)
qt_internal_add_example(drilldown)
qt_internal_add_example(cachedtable)
qt_internal_add_example(querymodel)
qt_internal_add_example(relationaltablemodel)
qt_internal_add_example(sqlwidgetmapper)
qt_internal_add_example(tablemodel)
if(TARGET Qt6::Xml)
qt_internal_add_example(masterdetail)
endif()
if(NOT CMAKE_CROSSCOMPILING)
qt_internal_add_example(sqlbrowser)
endif()

9
examples/sql/README Normal file
View File

@ -0,0 +1,9 @@
Qt provides extensive database interoperability, with support for products
from both open source and proprietary vendors.
SQL support is integrated with Qt's model/view architecture, making it easier
to provide GUI integration for your database applications.
Documentation for these examples can be found via the Examples
link in the main Qt documentation.

View File

@ -0,0 +1,52 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(books LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/sql/books")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Sql Widgets)
qt_standard_project_setup()
qt_add_executable(books
bookdelegate.cpp bookdelegate.h
bookwindow.cpp bookwindow.h bookwindow.ui
initdb.h
main.cpp
)
set_target_properties(books PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(books PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Sql
Qt6::Widgets
)
# Resources:
set(books_resource_files
"images/star.png"
)
qt_add_resources(books "books"
PREFIX
"/"
FILES
${books_resource_files}
)
install(TARGETS books
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,91 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "bookdelegate.h"
#include <QtWidgets>
BookDelegate::BookDelegate(QObject *parent)
: QSqlRelationalDelegate(parent), star(QPixmap(":images/star.png"))
{
}
void BookDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (index.column() != 5) {
QSqlRelationalDelegate::paint(painter, option, index);
} else {
const QAbstractItemModel *model = index.model();
QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ?
(option.state & QStyle::State_Active) ?
QPalette::Normal :
QPalette::Inactive :
QPalette::Disabled;
if (option.state & QStyle::State_Selected)
painter->fillRect(
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();
int y = option.rect.y() + (option.rect.height() / 2) - (height / 2);
for (int i = 0; i < rating; ++i) {
painter->drawPixmap(x, y, star);
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);
// Since we draw the grid ourselves:
return QSqlRelationalDelegate::sizeHint(option, index) + QSize(1, 1);
}
bool BookDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index)
{
if (index.column() != 5)
return QSqlRelationalDelegate::editorEvent(event, model, option, index);
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
int stars = qBound(0, int(0.7 + qreal(mouseEvent->position().toPoint().x()
- option.rect.x()) / star.width()), 5);
model->setData(index, QVariant(stars));
// So that the selection can change:
return false;
}
return true;
}
QWidget *BookDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (index.column() != 4)
return QSqlRelationalDelegate::createEditor(parent, option, index);
// For editing the year, return a spinbox with a range from -1000 to 2100.
QSpinBox *sb = new QSpinBox(parent);
sb->setFrame(false);
sb->setMaximum(2100);
sb->setMinimum(-1000);
return sb;
}

View File

@ -0,0 +1,36 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef BOOKDELEGATE_H
#define BOOKDELEGATE_H
#include <QModelIndex>
#include <QPixmap>
#include <QSize>
#include <QSqlRelationalDelegate>
QT_FORWARD_DECLARE_CLASS(QPainter)
class BookDelegate : public QSqlRelationalDelegate
{
public:
BookDelegate(QObject *parent);
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index) override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
private:
QPixmap star;
};
#endif

View File

@ -0,0 +1,13 @@
TEMPLATE = app
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))
target.path = $$[QT_INSTALL_EXAMPLES]/sql/books
INSTALLS += target

View File

@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
<file>images/star.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,124 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "bookwindow.h"
#include "bookdelegate.h"
#include "initdb.h"
#include <QtSql>
BookWindow::BookWindow()
{
ui.setupUi(this);
if (!QSqlDatabase::drivers().contains("QSQLITE"))
QMessageBox::critical(
this,
"Unable to load database",
"This demo needs the SQLITE driver"
);
// Initialize the database:
QSqlError err = initDb();
if (err.type() != QSqlError::NoError) {
showError(err);
return;
}
// Create the data model:
model = new QSqlRelationalTableModel(ui.bookTable);
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->setTable("books");
// Remember the indexes of the columns:
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 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:
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);
// Initialize the Author combo box:
ui.authorEdit->setModel(model->relationModel(authorIdx));
ui.authorEdit->setModelColumn(
model->relationModel(authorIdx)->fieldIndex("name"));
ui.genreEdit->setModel(model->relationModel(genreIdx));
ui.genreEdit->setModelColumn(
model->relationModel(genreIdx)->fieldIndex("name"));
// 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));
createMenuBar();
}
void BookWindow::showError(const QSqlError &err)
{
QMessageBox::critical(this, "Unable to initialize Database",
"Error initializing database: " + err.text());
}
void BookWindow::createMenuBar()
{
QAction *quitAction = new QAction(tr("&Quit"), this);
QAction *aboutAction = new QAction(tr("&About"), this);
QAction *aboutQtAction = new QAction(tr("&About Qt"), this);
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(quitAction);
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(aboutAction);
helpMenu->addAction(aboutQtAction);
connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit);
connect(aboutAction, &QAction::triggered, this, &BookWindow::about);
connect(aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
}
void BookWindow::about()
{
QMessageBox::about(this, tr("About Books"),
tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
"with a model/view framework."));
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef BOOKWINDOW_H
#define BOOKWINDOW_H
#include <QtWidgets>
#include <QtSql>
#include "ui_bookwindow.h"
class BookWindow: public QMainWindow
{
Q_OBJECT
public:
BookWindow();
private slots:
void about();
private:
void showError(const QSqlError &err);
Ui::BookWindow ui;
QSqlRelationalTableModel *model;
int authorIdx, genreIdx;
void createMenuBar();
};
#endif

View File

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BookWindow</class>
<widget class="QMainWindow" name="BookWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>601</width>
<height>420</height>
</rect>
</property>
<property name="windowTitle">
<string>Books</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QTableView" name="bookTable">
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Details</string>
</property>
<layout class="QFormLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>&lt;b&gt;Title:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="titleEdit">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&lt;b&gt;Author: &lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="authorEdit">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&lt;b&gt;Genre:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="genreEdit">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;b&gt;Year:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="yearEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>-1000</number>
</property>
<property name="maximum">
<number>2100</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;b&gt;Rating:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="ratingEdit">
<property name="maximum">
<number>5</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>bookTable</tabstop>
<tabstop>titleEdit</tabstop>
<tabstop>authorEdit</tabstop>
<tabstop>genreEdit</tabstop>
<tabstop>yearEdit</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

113
examples/sql/books/initdb.h Normal file
View File

@ -0,0 +1,113 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef INITDB_H
#define INITDB_H
#include <QtSql>
void addBook(QSqlQuery &q, const QString &title, int year, const QVariant &authorId,
const QVariant &genreId, int rating)
{
q.addBindValue(title);
q.addBindValue(year);
q.addBindValue(authorId);
q.addBindValue(genreId);
q.addBindValue(rating);
q.exec();
}
QVariant addGenre(QSqlQuery &q, const QString &name)
{
q.addBindValue(name);
q.exec();
return q.lastInsertId();
}
QVariant addAuthor(QSqlQuery &q, const QString &name, QDate birthdate)
{
q.addBindValue(name);
q.addBindValue(birthdate);
q.exec();
return q.lastInsertId();
}
const auto BOOKS_SQL = QLatin1String(R"(
create table books(id integer primary key, title varchar, author integer,
genre integer, year integer, rating integer)
)");
const auto AUTHORS_SQL = QLatin1String(R"(
create table authors(id integer primary key, name varchar, birthdate date)
)");
const auto GENRES_SQL = QLatin1String(R"(
create table genres(id integer primary key, name varchar)
)");
const auto INSERT_AUTHOR_SQL = QLatin1String(R"(
insert into authors(name, birthdate) values(?, ?)
)");
const auto INSERT_BOOK_SQL = QLatin1String(R"(
insert into books(title, year, author, genre, rating)
values(?, ?, ?, ?, ?)
)");
const auto INSERT_GENRE_SQL = QLatin1String(R"(
insert into genres(name) values(?)
)");
QSqlError initDb()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if (!db.open())
return db.lastError();
QStringList tables = db.tables();
if (tables.contains("books", Qt::CaseInsensitive)
&& tables.contains("authors", Qt::CaseInsensitive))
return QSqlError();
QSqlQuery q;
if (!q.exec(BOOKS_SQL))
return q.lastError();
if (!q.exec(AUTHORS_SQL))
return q.lastError();
if (!q.exec(GENRES_SQL))
return q.lastError();
if (!q.prepare(INSERT_AUTHOR_SQL))
return q.lastError();
QVariant asimovId = addAuthor(q, QLatin1String("Isaac Asimov"), QDate(1920, 2, 1));
QVariant greeneId = addAuthor(q, QLatin1String("Graham Greene"), QDate(1904, 10, 2));
QVariant pratchettId = addAuthor(q, QLatin1String("Terry Pratchett"), QDate(1948, 4, 28));
if (!q.prepare(INSERT_GENRE_SQL))
return q.lastError();
QVariant sfiction = addGenre(q, QLatin1String("Science Fiction"));
QVariant fiction = addGenre(q, QLatin1String("Fiction"));
QVariant fantasy = addGenre(q, QLatin1String("Fantasy"));
if (!q.prepare(INSERT_BOOK_SQL))
return q.lastError();
addBook(q, QLatin1String("Foundation"), 1951, asimovId, sfiction, 3);
addBook(q, QLatin1String("Foundation and Empire"), 1952, asimovId, sfiction, 4);
addBook(q, QLatin1String("Second Foundation"), 1953, asimovId, sfiction, 3);
addBook(q, QLatin1String("Foundation's Edge"), 1982, asimovId, sfiction, 3);
addBook(q, QLatin1String("Foundation and Earth"), 1986, asimovId, sfiction, 4);
addBook(q, QLatin1String("Prelude to Foundation"), 1988, asimovId, sfiction, 3);
addBook(q, QLatin1String("Forward the Foundation"), 1993, asimovId, sfiction, 3);
addBook(q, QLatin1String("The Power and the Glory"), 1940, greeneId, fiction, 4);
addBook(q, QLatin1String("The Third Man"), 1950, greeneId, fiction, 5);
addBook(q, QLatin1String("Our Man in Havana"), 1958, greeneId, fiction, 4);
addBook(q, QLatin1String("Guards! Guards!"), 1989, pratchettId, fantasy, 3);
addBook(q, QLatin1String("Night Watch"), 2002, pratchettId, fantasy, 3);
addBook(q, QLatin1String("Going Postal"), 2004, pratchettId, fantasy, 3);
return QSqlError();
}
#endif

View File

@ -0,0 +1,18 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "bookwindow.h"
#include <QtWidgets>
int main(int argc, char * argv[])
{
Q_INIT_RESOURCE(books);
QApplication app(argc, argv);
BookWindow win;
win.show();
return app.exec();
}

View File

@ -0,0 +1,39 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(cachedtable LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/sql/cachedtable")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Sql Widgets)
qt_standard_project_setup()
qt_add_executable(cachedtable
../connection.h
main.cpp
tableeditor.cpp tableeditor.h
)
set_target_properties(cachedtable PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(cachedtable PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Sql
Qt6::Widgets
)
install(TARGETS cachedtable
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,11 @@
HEADERS = ../connection.h \
tableeditor.h
SOURCES = main.cpp \
tableeditor.cpp
QT += sql widgets
requires(qtConfig(tableview))
# install
target.path = $$[QT_INSTALL_EXAMPLES]/sql/cachedtable
INSTALLS += target

View File

@ -0,0 +1,20 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "../connection.h"
#include "tableeditor.h"
//! [0]
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
if (!createConnection())
return 1;
TableEditor editor("person");
editor.show();
return app.exec();
}
//! [0]

View File

@ -0,0 +1,69 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtSql>
#include "tableeditor.h"
//! [0]
TableEditor::TableEditor(const QString &tableName, QWidget *parent)
: QWidget(parent)
{
model = new QSqlTableModel(this);
model->setTable(tableName);
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
model->setHeaderData(0, Qt::Horizontal, tr("ID"));
model->setHeaderData(1, Qt::Horizontal, tr("First name"));
model->setHeaderData(2, Qt::Horizontal, tr("Last name"));
//! [0] //! [1]
QTableView *view = new QTableView;
view->setModel(model);
view->resizeColumnsToContents();
//! [1]
//! [2]
submitButton = new QPushButton(tr("Submit"));
submitButton->setDefault(true);
revertButton = new QPushButton(tr("&Revert"));
quitButton = new QPushButton(tr("Quit"));
buttonBox = new QDialogButtonBox(Qt::Vertical);
buttonBox->addButton(submitButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(revertButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
//! [2]
//! [3]
connect(submitButton, &QPushButton::clicked, this, &TableEditor::submit);
connect(revertButton, &QPushButton::clicked, model, &QSqlTableModel::revertAll);
connect(quitButton, &QPushButton::clicked, this, &TableEditor::close);
//! [3]
//! [4]
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(view);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setWindowTitle(tr("Cached Table"));
}
//! [4]
//! [5]
void TableEditor::submit()
{
model->database().transaction();
if (model->submitAll()) {
model->database().commit();
} else {
model->database().rollback();
QMessageBox::warning(this, tr("Cached Table"),
tr("The database reported an error: %1")
.arg(model->lastError().text()));
}
}
//! [5]

View File

@ -0,0 +1,35 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef TABLEEDITOR_H
#define TABLEEDITOR_H
#include <QDialog>
QT_BEGIN_NAMESPACE
class QDialogButtonBox;
class QPushButton;
class QSqlTableModel;
QT_END_NAMESPACE
//! [0]
class TableEditor : public QWidget
{
Q_OBJECT
public:
explicit TableEditor(const QString &tableName, QWidget *parent = nullptr);
private slots:
void submit();
private:
QPushButton *submitButton;
QPushButton *revertButton;
QPushButton *quitButton;
QDialogButtonBox *buttonBox;
QSqlTableModel *model;
};
//! [0]
#endif

81
examples/sql/connection.h Normal file
View File

@ -0,0 +1,81 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
/*
This file defines a helper function to open a connection to an
in-memory SQLITE database and to create a test table.
If you want to use another database, simply modify the code
below. All the examples in this directory use this function to
connect to a database.
*/
//! [0]
static bool createConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if (!db.open()) {
QMessageBox::critical(nullptr, QObject::tr("Cannot open database"),
QObject::tr("Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information how "
"to build it.\n\n"
"Click Cancel to exit."), QMessageBox::Cancel);
return false;
}
QSqlQuery query;
query.exec("create table person (id int primary key, "
"firstname varchar(20), lastname varchar(20))");
query.exec("insert into person values(101, 'Danny', 'Young')");
query.exec("insert into person values(102, 'Christine', 'Holand')");
query.exec("insert into person values(103, 'Lars', 'Gordon')");
query.exec("insert into person values(104, 'Roberto', 'Robitaille')");
query.exec("insert into person values(105, 'Maria', 'Papadopoulos')");
query.exec("create table items (id int primary key,"
"imagefile int,"
"itemtype varchar(20),"
"description varchar(100))");
query.exec("insert into items "
"values(0, 0, 'Qt',"
"'Qt is a full development framework with tools designed to "
"streamline the creation of stunning applications and "
"amazing user interfaces for desktop, embedded and mobile "
"platforms.')");
query.exec("insert into items "
"values(1, 1, 'Qt Quick',"
"'Qt Quick is a collection of techniques designed to help "
"developers create intuitive, modern-looking, and fluid "
"user interfaces using a CSS & JavaScript like language.')");
query.exec("insert into items "
"values(2, 2, 'Qt Creator',"
"'Qt Creator is a powerful cross-platform integrated "
"development environment (IDE), including UI design tools "
"and on-device debugging.')");
query.exec("insert into items "
"values(3, 3, 'Qt Project',"
"'The Qt Project governs the open source development of Qt, "
"allowing anyone wanting to contribute to join the effort "
"through a meritocratic structure of approvers and "
"maintainers.')");
query.exec("create table images (itemid int, file varchar(20))");
query.exec("insert into images values(0, 'images/qt-logo.png')");
query.exec("insert into images values(1, 'images/qt-quick.png')");
query.exec("insert into images values(2, 'images/qt-creator.png')");
query.exec("insert into images values(3, 'images/qt-project.png')");
return true;
}
//! [0]
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,24 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example books
\title Books
\ingroup sql_examples
\brief Shows how to use Qt SQL classes with a model/view framework.
The Books example shows how Qt's SQL classes can be used with the model/view
framework to create rich user interfaces for information stored in a database.
\borderedimage books-demo.png
Information about a collection of books is held in a database. The books are
catalogued by author, title, genre, and year of publication. Although each of
these fields can be displayed and edited using standard widgets, an additional
field describing an arbitrary rating for the book needs something extra.
Books are rated using a system where each is allocated a number of stars; the
more a book has, the better it is supposed to be. By clicking on a cell
containing the rating, the number of stars can be modified, and the rating in
the database is updated.
*/

View File

@ -0,0 +1,174 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example cachedtable
\title Cached Table Example
\ingroup sql_examples
\brief The Cached Table example shows how a table view can be used to access a database,
caching any changes to the data until the user explicitly submits them using a
push button.
\borderedimage cachedtable-example.png
The example consists of a single class, \c TableEditor, which is a
custom dialog widget that allows the user to modify data stored in
a database. We will first review the class definition and how to
use the class, then we will take a look at the implementation.
\section1 TableEditor Class Definition
The \c TableEditor class inherits QWidget making the table editor
widget a top-level dialog window.
\snippet cachedtable/tableeditor.h 0
The \c TableEditor constructor takes two arguments: The first is a
reference to the database table the \c TableEditor object will operate
on. The other is a pointer to the parent widget and is passed on to the
base class constructor.
Note the QSqlTableModel variable declaration: As we will see in
this example, the QSqlTableModel class can be used to provide data
to view classes such as QTableView. The QSqlTableModel class
provides an editable data model making it possible to read and
write database records from a single table. It is build on top of
the lower-level QSqlQuery class which provides means of executing
and manipulating SQL statements.
We are also going to show how a table view can be used to cache
any changes to the data until the user explicitly requests to
submit them. For that reason we need to declare a \c submit() slot
in addition to the model and the editor's buttons.
\table 100%
\header \li Connecting to a Database
\row
\li
Before we can use the \c TableEditor class, we must create a
connection to the database containing the table we want to edit:
\snippet cachedtable/main.cpp 0
The \c createConnection() function is a helper function provided
for convenience. It is defined in the \c connection.h file which
is located in the \c sql example directory (all the examples in
the \c sql directory use this function to connect to a database).
\snippet connection.h 0
The \c createConnection function opens a connection to an
in-memory SQLITE database and creates a test table. If you want
to use another database, simply modify this function's code.
\endtable
\section1 TableEditor Class Implementation
The class implementation consists of only two functions, the
constructor and the \c submit() slot. In the constructor we create
and customize the data model and the various window elements:
\snippet cachedtable/tableeditor.cpp 0
First we create the data model and set the SQL database table we
want the model to operate on. Note that the
QSqlTableModel::setTable() function does not select data from the
table; it only fetches its field information. For that reason we
call the QSqlTableModel::select() function later on, populating
the model with data from the table. The selection can be
customized by specifying filters and sort conditions (see the
QSqlTableModel class documentation for more details).
We also set the model's edit strategy. The edit strategy dictates
when the changes done by the user in the view, are actually
applied to the database. Since we want to cache the changes in the
table view (i.e. in the model) until the user explicitly submits
them, we choose the QSqlTableModel::OnManualSubmit strategy. The
alternatives are QSqlTableModel::OnFieldChange and
QSqlTableModel::OnRowChange.
Finally, we set up the labels displayed in the view header using
the \l {QSqlQueryModel::setHeaderData()}{setHeaderData()} function
that the model inherits from the QSqlQueryModel class.
\snippet cachedtable/tableeditor.cpp 1
Then we create a table view. The QTableView class provides a
default model/view implementation of a table view, i.e. it
implements a table view that displays items from a model. It also
allows the user to edit the items, storing the changes in the
model. To create a read only view, set the proper flag using the
\l {QAbstractItemView::editTriggers}{editTriggers} property the
view inherits from the QAbstractItemView class.
To make the view present our data, we pass our model to the view
using the \l {QAbstractItemView::setModel()}{setModel()} function.
\snippet cachedtable/tableeditor.cpp 2
The \c {TableEditor}'s buttons are regular QPushButton objects. We
add them to a button box to ensure that the buttons are presented
in a layout that is appropriate to the current widget style. The
rationale for this is that dialogs and message boxes typically
present buttons in a layout that conforms to the interface
guidelines for that platform. Invariably, different platforms have
different layouts for their dialogs. QDialogButtonBox allows a
developer to add buttons to it and will automatically use the
appropriate layout for the user's desktop environment.
Most buttons for a dialog follow certain roles. When adding a
button to a button box using the \l
{QDialogButtonBox}{addButton()} function, the button's role must
be specified using the QDialogButtonBox::ButtonRole
enum. Alternatively, QDialogButtonBox provides several standard
buttons (e.g. \uicontrol OK, \uicontrol Cancel, \uicontrol Save) that you can
use. They exist as flags so you can OR them together in the
constructor.
\snippet cachedtable/tableeditor.cpp 3
We connect the \uicontrol Quit button to the table editor's \l
{QWidget::close()}{close()} slot, and the \uicontrol Submit button to
our private \c submit() slot. The latter slot will take care of
the data transactions. Finally, we connect the \uicontrol Revert button
to our model's \l {QSqlTableModel::revertAll()}{revertAll()} slot,
reverting all pending changes (i.e., restoring the original data).
\snippet cachedtable/tableeditor.cpp 4
In the end we add the button box and the table view to a layout,
install the layout on the table editor widget, and set the
editor's window title.
\snippet cachedtable/tableeditor.cpp 5
The \c submit() slot is called whenever the users hit the \uicontrol
Submit button to save their changes.
First, we begin a transaction on the database using the
QSqlDatabase::transaction() function. A database transaction is a
unit of interaction with a database management system or similar
system that is treated in a coherent and reliable way independent
of other transactions. A pointer to the used database can be
obtained using the QSqlTableModel::database() function.
Then, we try to submit all the pending changes, i.e. the model's
modified items. If no error occurs, we commit the transaction to
the database using the QSqlDatabase::commit() function (note that
on some databases, this function will not work if there is an
active QSqlQuery on the database). Otherwise we perform a rollback
of the transaction using the QSqlDatabase::rollback() function and
post a warning to the user.
\table 100%
\row
\li
\b {See also:}
A complete list of Qt's SQL \l {Database Classes}, and the \l
{Model/View Programming} documentation.
\endtable
*/

View File

@ -0,0 +1,522 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example drilldown
\title Drill Down Example
\ingroup sql_examples
\brief The Drill Down example shows how to read data from a database as
well as submit changes, using the QSqlRelationalTableModel and
QDataWidgetMapper classes.
\borderedimage drilldown-example.png Screenshot of the Drill Down Example
When running the example application, a user can retrieve
information about each item by clicking the corresponding image.
The application pops up an information window displaying the data,
and allows the users to alter the description as well as the image.
The main view will be updated when the users submit their changes.
The example consists of three classes:
\list
\li \c ImageItem is a custom graphics item class used to
display the images.
\li \c View is the main application widget allowing the user to
browse through the various items.
\li \c InformationWindow displays the requested information,
allowing the users to alter it and submit their changes to the
database.
\endlist
We will first take a look at the \c InformationWindow class to see
how you can read and modify data from a database. Then we will
review the main application widget, i.e., the \c View class, and
the associated \c ImageItem class.
\section1 InformationWindow Class Definition
The \c InformationWindow class is a custom widget inheriting
QWidget:
\snippet drilldown/informationwindow.h 0
When we create an information window, we pass the associated
item ID, a parent, and a pointer to the database, to the
constructor. We will use the database pointer to populate our
window with data, while passing the parent parameter on to the
base class. The ID is stored for future reference.
Once a window is created, we will use the public \c id() function
to locate it whenever information for the given location is
requested. We will also use the ID to update the main application
widget when the users submit their changes to the database, i.e.,
we will emit a signal carrying the ID and file name as parameters
whenever the users changes the associated image.
\snippet drilldown/informationwindow.h 1
Since we allow the users to alter some of the data, we
must provide functionality for reverting and submitting their
changes. The \c enableButtons() slot is provided for convenience
to enable and disable the various buttons when required.
\snippet drilldown/informationwindow.h 2
The \c createButtons() function is also a convenience function,
provided to simplify the constructor. As mentioned above we store
the item ID for future reference. We also store the name of
the currently displayed image file to be able to determine when to
emit the \c imageChanged() signal.
The information window uses the QLabel class to display the name of
an item. The associated image file is displayed using a QComboBox
instance while the description is displayed using QTextEdit. In
addition, the window has three buttons to control the data flow and
whether the window is shown or not.
Finally, we declare a \e mapper. The QDataWidgetMapper class
provides mapping between a section of a data model to widgets. We
will use the mapper to extract data from the given database,
updating the database whenever the user modifies the data.
\section1 InformationWindow Class Implementation
The constructor takes three arguments: an item ID, a database
pointer and a parent widget. The database pointer is actually a
pointer to a QSqlRelationalTableModel object providing an editable
data model (with foreign key support) for our database table.
\snippet drilldown/informationwindow.cpp 0
\snippet drilldown/informationwindow.cpp 1
First we create the various widgets required to display the data
contained in the database. Most of the widgets are created in a
straight forward manner. But note the combobox displaying the
name of the image file:
\snippet drilldown/informationwindow.cpp 2
In this example, information about the items are stored in a
database table called "items". When creating the model,
we will use a foreign key to establish a relation between this
table and a second data base table, "images", containing the names
of the available image files. We will get back to how this is done
when reviewing the \c View class. The rationale for creating such
a relation though, is that we want to ensure that the user only
can choose between predefined image files.
The model corresponding to the "images" database table, is
available through the QSqlRelationalTableModel's \l
{QSqlRelationalTableModel::}{relationModel()} function, requiring
the foreign key (in this case the "imagefile" column number) as
argument. We use QComboBox's \l {QComboBox::}{setModel()} function
to make the combobox use the "images" model. And, since this model
has two columns ("itemid" and "file"), we also specify which
column we want to be visible using the QComboBox::setModelColumn()
function.
\snippet drilldown/informationwindow.cpp 3
Then we create the mapper. The QDataWidgetMapper class allows us
to create data-aware widgets by mapping them to sections of an
item model.
The \l {QDataWidgetMapper::}{addMapping()} function adds a mapping
between the given widget and the specified section of the
model. If the mapper's orientation is horizontal (the default) the
section is a column in the model, otherwise it is a row. We call
the \l {QDataWidgetMapper::}{setCurrentIndex()} function to
initialize the widgets with the data associated with the given
item ID. Every time the current index changes, all the widgets
are updated with the contents from the model.
We also set the mapper's submit policy to
QDataWidgetMapper::ManualSubmit. This means that no data is
submitted to the database until the user expliclity requests a
submit (the alternative is QDataWidgetMapper::AutoSubmit,
automatically submitting changes when the corresponding widget
loses focus). Finally, we specify the item delegate the mapper
view should use for its items. The QSqlRelationalDelegate class
represents a delegate that unlike the default delegate, enables
combobox functionality for fields that are foreign keys into other
tables (like "imagefile" in our "items" table).
\snippet drilldown/informationwindow.cpp 4
Finally, we connect the "something's changed" signals in the
editors to our custom \c enableButtons slot, enabling the users
to either submit or revert their changes.
We need to use lambdas for connecting the \c enableButtons slot
because its signature does not match \c QTextEdit::textChanged
and \c QComboBox::currentIndexChanged.
Since the latter has another overload with the signature
\c {const QString &} and the selected signal would be ambiguous,
we need to use \c QOverload<int>::of to select a specific overload
for \c currentIndexChanged.
We add all the widgets into a layout, store the item ID and the
name of the displayed image file for future reference, and set
the window title and initial size.
Note that we also set the Qt::Window window flag to indicate that
our widget is in fact a window, with a window system frame and a
title bar.
\snippet drilldown/informationwindow.cpp 5
When a window is created, it is not deleted until the main
application exits (i.e., if the user closes the information
window, it is only hidden). For this reason we do not want to
create more than one \c InformationWindow object for each
item, and we provide the public \c id() function to be able to
determine whether a window already exists for a given location
when the user requests information about it.
\snippet drilldown/informationwindow.cpp 6
The \c revert() slot is triggered whenever the user hits the \uicontrol
Revert button.
Since we set the QDataWidgetMapper::ManualSubmit submit policy,
none of the user's changes are written back to the model unless
the user expliclity choose to submit all of them. Nevertheless, we
can use the QDataWidgetMapper's \l {QDataWidgetMapper::}{revert()}
slot to reset the editor widgets, repopulating all widgets with
the current data of the model.
\snippet drilldown/informationwindow.cpp 7
Likewise, the \c submit() slot is triggered whenever the users
decide to submit their changes by pressing the \uicontrol Submit button.
We use QDataWidgetMapper's \l {QDataWidgetMapper::}{submit()} slot
to submit all changes from the mapped widgets to the model,
i.e. to the database. For every mapped section, the item delegate
will then read the current value from the widget and set it in the
model. Finally, the \e model's \l {QAbstractItemModel::}{submit()}
function is invoked to let the model know that it should submit
whatever it has cached to the permanent storage.
Note that before any data is submitted, we check if the user has
chosen another image file using the previously stored \c
displayedImage variable as reference. If the current and stored
file names differ, we store the new file name and emit the \c
imageChanged() signal.
\snippet drilldown/informationwindow.cpp 8
The \c createButtons() function is provided for convenience, i.e.,
to simplify the constructor.
We make the \uicontrol Close button the default button, i.e., the button
that is pressed when the user presses \uicontrol Enter, and connect its
\l {QPushButton::}{clicked()} signal to the widget's \l
{QWidget::}{close()} slot. As mentioned above closing the window
only hides the widget; it is not deleted. We also connect the \uicontrol
Submit and \uicontrol Revert buttons to the corresponding \c submit()
and \c revert() slots.
\snippet drilldown/informationwindow.cpp 9
The QDialogButtonBox class is a widget that presents buttons in a
layout that is appropriate to the current widget style. Dialogs
like our information window, typically present buttons in a layout
that conforms to the interface guidelines for that
platform. Invariably, different platforms have different layouts
for their dialogs. QDialogButtonBox allows us to add buttons,
automatically using the appropriate layout for the user's desktop
environment.
Most buttons for a dialog follow certain roles. We give the \uicontrol
Submit and \uicontrol Revert buttons the \l
{QDialogButtonBox::ButtonRole}{reset} role, i.e., indicating that
pressing the button resets the fields to the default values (in
our case the information contained in the database). The \l
{QDialogButtonBox::ButtonRole}{reject} role indicates that
clicking the button causes the dialog to be rejected. On the other
hand, since we only hide the information window, any changes that
the user has made will be preserved until the user explicitly
reverts or submits them.
\snippet drilldown/informationwindow.cpp 10
The \c enableButtons() slot is called to enable the buttons
whenever the user changes the presented data. Likewise, when the
user chooses to submit the changes, the buttons are disabled to
indicate that the current data is stored in the database.
This completes the \c InformationWindow class. Let's take a look
at how we have used it in our example application.
\section1 View Class Definition
The \c View class represents the main application window and
inherits QGraphicsView:
\snippet drilldown/view.h 0
\codeline
\snippet drilldown/view.h 1
The QGraphicsView class is part of the \l {Graphics View
Framework} which we will use to display the images. To be able to
respond to user interaction by displaying the appropriate
information window when the image is clicked, we reimplement
QGraphicsView's \l{QGraphicsView::}{mouseReleaseEvent()} function.
Note that the constructor expects the names of two database
tables: One containing the detailed information about the items,
and another containing the names of the available image files. We
also provide a private \c updateImage() slot to catch \c
{InformationWindow}'s \c imageChanged() signal that is emitted
whenever the user changes an image associated with the item.
\snippet drilldown/view.h 2
The \c addItems() function is a convenience function provided to
simplify the constructor. It is called only once, creating the
various items and adding them to the view.
The \c findWindow() function, on the other hand, is frequently
used. It is called from the \c showInformation() function to
determine whether a window is already created for the given
item (whenever we create an \c InformationWindow object, we
store a reference to it in the \c informationWindows list). The
latter function is in turn called from our custom \c
mouseReleaseEvent() implementation.
\snippet drilldown/view.h 3
Finally, we declare a QSqlRelationalTableModel pointer. As
previously mentioned, the QSqlRelationalTableModel class provides
an editable data model with foreign key support. There are a
couple of things you should keep in mind when using the
QSqlRelationalTableModel class: The table must have a primary key
declared and this key cannot contain a relation to another table,
that is, it cannot be a foreign key. Also note that if a relational
table contains keys that refer to non-existent rows in the
referenced table, the rows containing the invalid keys will not be
exposed through the model. It is the user's or the database's
responsibility to maintain referential integrity.
\section1 View Class Implementation
Although the constructor requests the names of both the table
containing office details as well as the table containing the
names of the available image files, we only have to create a
QSqlRelationalTableModel object for the "items" table:
\snippet drilldown/view.cpp 0
The reason is that once we have a model with the item details,
we can create a relation to the available image files using
QSqlRelationalTableModel's \l
{QSqlRelationalTableModel::}{setRelation()} function. This
function creates a foreign key for the given model column. The key
is specified by the provided QSqlRelation object constructed by
the name of the table the key refers to, the field the key is
mapping to and the field that should be presented to the user.
Note that setting the table only specifies which table the model
operates on, i.e., we must explicitly call the model's \l
{QSqlRelationalTableModel::}{select()} function to populate our
model.
\snippet drilldown/view.cpp 1
Then we create the contents of our view, i.e., the scene and its
items. The labels are regular QGraphicsTextItem objects, whereas
the images are instances of the \c ImageItem class, derived from
QGraphicsPixmapItem. We will get back to this shortly when reviewing
the \c addItems() function.
Finally, we set the main application widget's size constraints and
window title.
\snippet drilldown/view.cpp 3
The \c addItems() function is called only once when creating the main
application window. For each row in the database table, we first
extract the corresponding record using the model's
\l {QSqlRelationalTableModel::}{record()} function. The QSqlRecord
class encapsulates both the functionality and characteristics of a
database record, and supports adding and removing fields as well
as setting and retrieving field values. The QSqlRecord::value()
function returns the value of the field with the given name or
index as a QVariant object.
For each record, we create a label item as well as an image item,
calculate their position and add them to the scene. The image
items are represented by instances of the \c ImageItem class. The
reason we must create a custom item class is that we want to catch
the item's hover events, animating the item when the mouse cursor
is hovering over the image (by default, no items accept hover
events). Please see the \l{Graphics View Framework} documentation
and the \l{Graphics View Examples} for more details.
\snippet drilldown/view.cpp 5
We reimplement QGraphicsView's \l
{QGraphicsView::}{mouseReleaseEvent()} event handler to respond to
user interaction. If the user clicks any of the image items, this
function calls the private \c showInformation() function to pop up
the associated information window.
The \l {Graphics View Framework} provides the qgraphicsitem_cast()
function to determine whether the given QGraphicsItem instance is
of a given type. Note that if the event is not related to any of
our image items, we pass it on to the base class implementation.
\snippet drilldown/view.cpp 6
The \c showInformation() function is given an \c ImageItem object
as argument, and starts off by extracting the item's item ID.
Then it determines if there already is created an information
window for this location.
If no window for the given location exists, we create one by
passing the item ID, a pointer to the model, and our view as a
parent, to the \c InformationWindow constructor. Note that we
connect the information window's \c imageChanged() signal to \e
this widget's \c updateImage() slot, before we give it a suitable
position and add it to the list of existing windows.
If there is a window for the given location, and that window is
visible, it ensures that the window is raised to the top of the
widget stack and activated. If it is hidden, calling its \l
{QWidget::}{show()} slot gives the same result.
\snippet drilldown/view.cpp 7
The \c updateImage() slot takes an item ID and the name of an
image file as arguments. It filters out the image items, and
updates the one that correspond to the given item ID, with the
provided image file.
\snippet drilldown/view.cpp 8
The \c findWindow() function simply searches through the list of
existing windows, returning a pointer to the window that matches
the given item ID, or \nullptr if the window doesn't exists.
Finally, let's take a quick look at our custom \c ImageItem class:
\section1 ImageItem Class Definition
The \c ImageItem class is provided to facilitate animation of the
image items. It inherits QGraphicsPixmapItem and reimplements its
hover event handlers:
\snippet drilldown/imageitem.h 0
We declare a \c Type enum value for our custom item and reimplement
\l{QGraphicsItem::}{type()}. This is done so we can safely use
qgraphicsitem_cast().
In addition, we implement a public \c id() function to be able to
identify the associated location and a public \c adjust() function
that can be called to ensure that the image item is given the
preferred size regardless of the original image file.
The animation is implemented using the QTimeLine class together
with the event handlers and the private \c setFrame() slot: The
image item will expand when the mouse cursor hovers over it,
returning back to its original size when the cursor leaves its
borders.
Finally, we store the item ID that this particular record is
associated with as well as a z-value. In the \l {Graphics View
Framework}, an item's z-value determines its position in the item
stack. An item of high z-value will be drawn on top of an item
with a lower z-value if they share the same parent item. We also
provide an \c updateItemPosition() function to refresh the view
when required.
\section1 ImageItem Class Implementation
The \c ImageItem class is really only a QGraphicsPixmapItem with
some additional features, i.e., we can pass most of the
constructor's arguments (the pixmap, parent and scene) on to the
base class constructor:
\snippet drilldown/imageitem.cpp 0
Then we store the ID for future reference, and ensure that our
image item will accept hover events. Hover events are delivered
when there is no current mouse grabber item. They are sent when the
mouse cursor enters an item, when it moves around inside the item,
and when the cursor leaves an item. As we mentioned earlier, none
of the \l {Graphics View Framework}'s items accept hover
event's by default.
The QTimeLine class provides a timeline for controlling
animations. Its \l {QTimeLine::}{duration} property holds the
total duration of the timeline in milliseconds. By default, the
time line runs once from the beginning and towards the end. The
QTimeLine::setFrameRange() function sets the timeline's frame
counter; when the timeline is running, the \l
{QTimeLine::}{frameChanged()} signal is emitted each time the
frame changes. We set the duration and frame range for our
animation, and connect the time line's \l
{QTimeLine::}{frameChanged()} and \l {QTimeLine::}{finished()}
signals to our private \c setFrame() and \c updateItemPosition()
slots.
Finally, we call \c adjust() to ensure that the item is given the
preferred size.
\snippet drilldown/imageitem.cpp 1
\codeline
\snippet drilldown/imageitem.cpp 2
Whenever the mouse cursor enters or leaves the image item, the
corresponding event handlers are triggered: We first set the time
line's direction, making the item expand or shrink,
respectively. Then we alter the item's z-value if it is not already
set to the expected value.
In the case of hover \e enter events, we immediately update the
item's position since we want the item to appear on top of all
other items as soon as it starts expanding. In the case of hover
\e leave events, on the other hand, we postpone the actual update
to achieve the same result. But remember that when we constructed
our item, we connected the time line's \l
{QTimeLine::}{finished()} signal to the \c updateItemPosition()
slot. In this way the item is given the correct position in the
item stack once the animation is completed. Finally, if the time
line is not already running, we start it.
\snippet drilldown/imageitem.cpp 3
When the time line is running, it triggers the \c setFrame() slot
whenever the current frame changes due to the connection we
created in the item constructor. It is this slot that controls the
animation, expanding or shrinking the image item step by step.
We first call the \c adjust() function to ensure that we start off
with the item's original size. Then we scale the item with a
factor depending on the animation's progress (using the \c frame
parameter). Note that by default, the transformation will be
relative to the item's top-left corner. Since we want the item to
be transformed relative to its center, we must translate the
coordinate system before we scale the item.
In the end, only the following convenience functions remain:
\snippet drilldown/imageitem.cpp 4
\codeline
\snippet drilldown/imageitem.cpp 5
\codeline
\snippet drilldown/imageitem.cpp 6
The \c adjust() function defines and applies a transformation
matrix, ensuring that our image item appears with the preferred
size regardless of the size of the source image. The \c id()
function is trivial, and is simply provided to be able to identify
the item. In the \c updateItemPosition() slot we call the
QGraphicsItem::setZValue() function, setting the elevation of the
item.
*/

View File

@ -0,0 +1,20 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example masterdetail
\title Master Detail Example
\ingroup sql_examples
\brief The Master Detail Example shows how to present data from different
data sources in the same application. The album titles, and the
corresponding artists and release dates, are kept in a
database, while each album's tracks are stored in an XML
file.
The example also shows how to add as well as remove data from both
the database and the associated XML file using the API provided by
the Qt SQL and Qt XML modules, respectively.
\borderedimage masterdetail-example.png
*/

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example querymodel
\title Query Model Example
\ingroup sql_examples
\brief The Query Model example shows how to make customized versions of
data obtained from a SQL query, using a model that encapsulates
the query and table views to display the results.
\borderedimage querymodel-example.png
*/

View File

@ -0,0 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example relationaltablemodel
\title Relational Table Model Example
\ingroup sql_examples
\brief The Relational Table Model example shows how to use table views with a relational
model to visualize the relations between items in a database.
\borderedimage relationaltablemodel-example.png
*/

View File

@ -0,0 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example sqlbrowser
\title SQL Browser
\ingroup sql_examples
\brief The SQL Browser example shows how a data browser can be used to visualize
the results of SQL statements on a live database.
\borderedimage sqlbrowser-demo.png
*/

View File

@ -0,0 +1,162 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example sqlwidgetmapper
\title SQL Widget Mapper Example
\ingroup sql_examples
\brief The SQL Widget Mapper example shows how to use a map information from a
database to widgets on a form.
\borderedimage sql-widget-mapper.png
In the \l{Combo Widget Mapper Example}, we showed how to use a named
mapping between a widget mapper and a QComboBox widget with a special
purpose model to relate values in the model to a list of choices.
Again, we create a \c Window class with an almost identical user interface,
providing a combo box to allow their addresses to be classified as "Home",
"Work" or "Other". However, instead of using a separate model to hold these
address types, we use one database table to hold the example data and
another to hold the address types. In this way, we store all the
information in the same place.
\section1 Window Class Definition
The class provides a constructor, a slot to keep the buttons up to date,
and a private function to set up the model:
\snippet sqlwidgetmapper/window.h Window definition
In addition to the QDataWidgetMapper object and the controls used to make
up the user interface, we use a QStandardItemModel to hold our data and
a QStringListModel to hold information about the types of address that
can be applied to each person's data.
\section1 Window Class Implementation
The first act performed by the \c Window class constructor is to set up
the model used to hold the example data. Since this is a key part of the
example, we will look at this first.
The model is initialized in the window's \c{setupModel()} function. Here,
we create a SQLite database containing a "person" table with primary key,
name, address and type fields.
\snippet sqlwidgetmapper/window.cpp Set up the main table
On each row of the table, we insert default values for these fields,
including values for the address types that correspond to the address
types are stored in a separate table.
\borderedimage widgetmapper-sql-mapping-table.png
We create an "addresstype" table containing the identifiers used in the
"person" table and the corresponding strings:
\snippet sqlwidgetmapper/window.cpp Set up the address type table
The "typeid" field in the "person" table is related to the contents of
the "addresstype" table via a relation in a QSqlRelationalTableModel.
This kind of model performs all the necessary work to store the data in
a database and also allows any relations to be used as models in their
own right.
In this case, we have defined a relation for the "typeid" field in the
"person" table that relates it to the "id" field in the "addresstype"
table and which causes the contents of the "description" field to be
used wherever the "typeid" is presented to the user. (See the
QSqlRelationalTableModel::setRelation() documentation for details.)
\borderedimage widgetmapper-sql-mapping.png
The constructor of the \c Window class can be explained in three parts.
In the first part, we set up the model used to hold the data, then we set
up the widgets used for the user interface:
\snippet sqlwidgetmapper/window.cpp Set up widgets
We obtain a model for the combo box from the main model, based on the
relation we set up for the "typeid" field. The call to the combo box's
\l{QComboBox::}{setModelColumn()} selects the field in the field in the
model to display.
Note that this approach is similar to the one used in the
\l{Combo Widget Mapper Example} in that we set up a model for the
combo box. However, in this case, we obtain a model based on a relation
in the QSqlRelationalTableModel rather than create a separate one.
Next, we set up the widget mapper, relating each input widget to a field
in the model:
\snippet sqlwidgetmapper/window.cpp Set up the mapper
For the combo box, we already know the index of the field in the model
from the \c{setupModel()} function. We use a QSqlRelationalDelegate as
a proxy between the mapper and the input widgets to match up the "typeid"
values in the model with those in the combo box's model and populate the
combo box with descriptions rather than integer values.
As a result, the user is able to select an item from the combo box,
and the associated value is written back to the model.
The rest of the constructor is very similar to that of the
\l{Simple Widget Mapper Example}:
\snippet sqlwidgetmapper/window.cpp Set up connections and layouts
We show the implementation of the \c{updateButtons()} slot for
completeness:
\snippet sqlwidgetmapper/window.cpp Slot for updating the buttons
\omit
\section1 Delegate Class Definition and Implementation
The delegate we use to mediate interaction between the widget mapper and
the input widgets is a small QItemDelegate subclass:
\snippet sqlwidgetmapper/delegate.h Delegate class definition
This provides implementations of the two standard functions used to pass
data between editor widgets and the model (see the \l{Delegate Classes}
documentation for a more general description of these functions).
Since we only provide an empty implementation of the constructor, we
concentrate on the other two functions.
The \l{QItemDelegate::}{setEditorData()} implementation takes the data
referred to by the model index supplied and processes it according to
the presence of a \c currentIndex property in the editor widget:
\snippet sqlwidgetmapper/delegate.cpp setEditorData implementation
If, like QComboBox, the editor widget has this property, it is set using
the value from the model. Since we are passing around QVariant values,
the strings stored in the model are automatically converted to the integer
values needed for the \c currentIndex property.
As a result, instead of showing "0", "1" or "2" in the combo box, one of
its predefined set of items is shown. We call QItemDelegate::setEditorData()
for widgets without the \c currentIndex property.
The \l{QItemDelegate::}{setModelData()} implementation performs the reverse
process, taking the value stored in the widget's \c currentIndex property
and storing it back in the model:
\snippet sqlwidgetmapper/delegate.cpp setModelData implementation
\endomit
\section1 Summary and Further Reading
The use of a separate model for the combo box and a special delegate for the
widget mapper allows us to present a menu of choices to the user. Although
the choices are stored in the same database as the user's data, they are held
in a separate table. Using this approach, we can reconstructed complete records
at a later time while using database features appropriately.
If SQL models are not being used, it is still possible to use more than
one model to present choices to the user. This is covered by the
\l{Combo Widget Mapper Example}.
*/

View File

@ -0,0 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tablemodel
\title Table Model Example
\ingroup sql_examples
\brief The Table Model example shows how to use a specialized SQL table model with table
views to edit information in a database.
\borderedimage tablemodel-example.png
*/

View File

@ -0,0 +1,56 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(drilldown LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/sql/drilldown")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Sql Widgets)
qt_standard_project_setup()
qt_add_executable(drilldown
../connection.h
imageitem.cpp imageitem.h
informationwindow.cpp informationwindow.h
main.cpp
view.cpp view.h
)
set_target_properties(drilldown PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(drilldown PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Sql
Qt6::Widgets
)
# Resources:
set(drilldown_resource_files
"images/qt-creator.png"
"images/qt-logo.png"
"images/qt-project.png"
"images/qt-quick.png"
)
qt_add_resources(drilldown "drilldown"
PREFIX
"/"
FILES
${drilldown_resource_files}
)
install(TARGETS drilldown
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,15 @@
HEADERS = ../connection.h \
imageitem.h \
informationwindow.h \
view.h
RESOURCES = drilldown.qrc
SOURCES = imageitem.cpp \
informationwindow.cpp \
main.cpp \
view.cpp
QT += sql widgets
requires(qtConfig(combobox))
# install
target.path = $$[QT_INSTALL_EXAMPLES]/sql/drilldown
INSTALLS += target

View File

@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file>images/qt-logo.png</file>
<file>images/qt-quick.png</file>
<file>images/qt-creator.png</file>
<file>images/qt-project.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,84 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "imageitem.h"
//! [0]
ImageItem::ImageItem(int id, const QPixmap &pixmap, QGraphicsItem *parent)
: QGraphicsPixmapItem(pixmap, parent)
{
recordId = id;
setAcceptHoverEvents(true);
timeLine.setDuration(150);
timeLine.setFrameRange(0, 150);
connect(&timeLine, &QTimeLine::frameChanged, this, &ImageItem::setFrame);
connect(&timeLine, &QTimeLine::finished, this, &ImageItem::updateItemPosition);
adjust();
}
//! [0]
//! [1]
void ImageItem::hoverEnterEvent(QGraphicsSceneHoverEvent * /*event*/)
{
timeLine.setDirection(QTimeLine::Forward);
if (z != 1.0) {
z = 1.0;
updateItemPosition();
}
if (timeLine.state() == QTimeLine::NotRunning)
timeLine.start();
}
//! [1]
//! [2]
void ImageItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
{
timeLine.setDirection(QTimeLine::Backward);
if (z != 0.0)
z = 0.0;
if (timeLine.state() == QTimeLine::NotRunning)
timeLine.start();
}
//! [2]
//! [3]
void ImageItem::setFrame(int frame)
{
adjust();
QPointF center = boundingRect().center();
setTransform(QTransform::fromTranslate(center.x(), center.y()), true);
setTransform(QTransform::fromScale(1 + frame / 330.0, 1 + frame / 330.0), true);
setTransform(QTransform::fromTranslate(-center.x(), -center.y()), true);
}
//! [3]
//! [4]
void ImageItem::adjust()
{
setTransform(QTransform::fromScale(120 / boundingRect().width(),
120 / boundingRect().height()));
}
//! [4]
//! [5]
int ImageItem::id() const
{
return recordId;
}
//! [5]
//! [6]
void ImageItem::updateItemPosition()
{
setZValue(z);
}
//! [6]

View File

@ -0,0 +1,39 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef IMAGEITEM_H
#define IMAGEITEM_H
#include <QtCore>
#include <QtWidgets/QGraphicsPixmapItem>
//! [0]
class ImageItem : public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
public:
enum { Type = UserType + 1 };
ImageItem(int id, const QPixmap &pixmap, QGraphicsItem *parent = nullptr);
int type() const override { return Type; }
void adjust();
int id() const;
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
private slots:
void setFrame(int frame);
void updateItemPosition();
private:
QTimeLine timeLine;
int recordId;
double z;
};
//! [0]
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,128 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "informationwindow.h"
//! [0]
InformationWindow::InformationWindow(int id, QSqlRelationalTableModel *items,
QWidget *parent)
: QDialog(parent)
{
//! [0] //! [1]
QLabel *itemLabel = new QLabel(tr("Item: "));
QLabel *descriptionLabel = new QLabel(tr("Description: "));
QLabel *imageFileLabel = new QLabel(tr("Image file: "));
createButtons();
itemText = new QLabel;
descriptionEditor = new QTextEdit;
//! [1]
//! [2]
imageFileEditor = new QComboBox;
imageFileEditor->setModel(items->relationModel(1));
imageFileEditor->setModelColumn(items->relationModel(1)->fieldIndex("file"));
//! [2]
//! [3]
mapper = new QDataWidgetMapper(this);
mapper->setModel(items);
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
mapper->setItemDelegate(new QSqlRelationalDelegate(mapper));
mapper->addMapping(imageFileEditor, 1);
mapper->addMapping(itemText, 2, "text");
mapper->addMapping(descriptionEditor, 3);
mapper->setCurrentIndex(id);
//! [3]
//! [4]
connect(descriptionEditor, &QTextEdit::textChanged, [=]() {
enableButtons();
});
connect(imageFileEditor, &QComboBox::currentIndexChanged, [=]() {
enableButtons();
});
QFormLayout *formLayout = new QFormLayout;
formLayout->addRow(itemLabel, itemText);
formLayout->addRow(imageFileLabel, imageFileEditor);
formLayout->addRow(descriptionLabel, descriptionEditor);
QVBoxLayout *layout = new QVBoxLayout;
layout->addLayout(formLayout);
layout->addWidget(buttonBox);
setLayout(layout);
itemId = id;
displayedImage = imageFileEditor->currentText();
setWindowFlags(Qt::Window);
enableButtons(false);
setWindowTitle(itemText->text());
}
//! [4]
//! [5]
int InformationWindow::id() const
{
return itemId;
}
//! [5]
//! [6]
void InformationWindow::revert()
{
mapper->revert();
enableButtons(false);
}
//! [6]
//! [7]
void InformationWindow::submit()
{
QString newImage(imageFileEditor->currentText());
if (displayedImage != newImage) {
displayedImage = newImage;
emit imageChanged(itemId, newImage);
}
mapper->submit();
mapper->setCurrentIndex(itemId);
enableButtons(false);
}
//! [7]
//! [8]
void InformationWindow::createButtons()
{
closeButton = new QPushButton(tr("&Close"));
revertButton = new QPushButton(tr("&Revert"));
submitButton = new QPushButton(tr("&Submit"));
closeButton->setDefault(true);
connect(closeButton, &QPushButton::clicked, this, &InformationWindow::close);
connect(revertButton, &QPushButton::clicked, this, &InformationWindow::revert);
connect(submitButton, &QPushButton::clicked, this, &InformationWindow::submit);
//! [8]
//! [9]
buttonBox = new QDialogButtonBox(this);
buttonBox->addButton(submitButton, QDialogButtonBox::AcceptRole);
buttonBox->addButton(revertButton, QDialogButtonBox::ResetRole);
buttonBox->addButton(closeButton, QDialogButtonBox::RejectRole);
}
//! [9]
//! [10]
void InformationWindow::enableButtons(bool enable)
{
revertButton->setEnabled(enable);
submitButton->setEnabled(enable);
}
//! [10]

View File

@ -0,0 +1,52 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef INFORMATIONWINDOW_H
#define INFORMATIONWINDOW_H
#include <QtWidgets>
#include <QtSql>
//! [0]
class InformationWindow : public QDialog
{
Q_OBJECT
public:
InformationWindow(int id, QSqlRelationalTableModel *items,
QWidget *parent = nullptr);
int id() const;
signals:
void imageChanged(int id, const QString &fileName);
//! [0]
//! [1]
private slots:
void revert();
void submit();
void enableButtons(bool enable = true);
//! [1]
//! [2]
private:
void createButtons();
int itemId;
QString displayedImage;
QComboBox *imageFileEditor = nullptr;
QLabel *itemText = nullptr;
QTextEdit *descriptionEditor = nullptr;
QPushButton *closeButton = nullptr;
QPushButton *submitButton = nullptr;
QPushButton *revertButton = nullptr;
QDialogButtonBox *buttonBox = nullptr;
QDataWidgetMapper *mapper = nullptr;
};
//! [2]
#endif

View File

@ -0,0 +1,27 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "view.h"
#include "../connection.h"
#include <QApplication>
#include <stdlib.h>
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(drilldown);
QApplication app(argc, argv);
if (!createConnection())
return EXIT_FAILURE;
View view("items", "images");
view.show();
#ifdef QT_KEYPAD_NAVIGATION
QApplication::setNavigationMode(Qt::NavigationModeCursorAuto);
#endif
return app.exec();
}

View File

@ -0,0 +1,135 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "informationwindow.h"
#include "imageitem.h"
#include "view.h"
//! [0]
View::View(const QString &items, const QString &images, QWidget *parent)
: QGraphicsView(parent)
{
itemTable = new QSqlRelationalTableModel(this);
itemTable->setTable(items);
itemTable->setRelation(1, QSqlRelation(images, "itemid", "file"));
itemTable->select();
//! [0]
//! [1]
scene = new QGraphicsScene(this);
scene->setSceneRect(0, 0, 465, 365);
setScene(scene);
addItems();
setMinimumSize(470, 370);
setMaximumSize(470, 370);
QLinearGradient gradient(QPointF(0, 0), QPointF(0, 370));
gradient.setColorAt(0, QColor("#868482"));
gradient.setColorAt(1, QColor("#5d5b59"));
setBackgroundBrush(gradient);
}
//! [1]
//! [3]
void View::addItems()
{
int itemCount = itemTable->rowCount();
int imageOffset = 150;
int leftMargin = 70;
int topMargin = 40;
for (int i = 0; i < itemCount; i++) {
QSqlRecord record = itemTable->record(i);
int id = record.value("id").toInt();
QString file = record.value("file").toString();
QString item = record.value("itemtype").toString();
int columnOffset = ((i % 2) * 37);
int x = ((i % 2) * imageOffset) + leftMargin + columnOffset;
int y = ((i / 2) * imageOffset) + topMargin;
ImageItem *image = new ImageItem(id, QPixmap(":/" + file));
image->setData(0, i);
image->setPos(x, y);
scene->addItem(image);
QGraphicsTextItem *label = scene->addText(item);
label->setDefaultTextColor(QColor("#d7d6d5"));
QPointF labelOffset((120 - label->boundingRect().width()) / 2, 120.0);
label->setPos(QPointF(x, y) + labelOffset);
}
}
//! [3]
//! [5]
void View::mouseReleaseEvent(QMouseEvent *event)
{
if (QGraphicsItem *item = itemAt(event->position().toPoint())) {
if (ImageItem *image = qgraphicsitem_cast<ImageItem *>(item))
showInformation(image);
}
QGraphicsView::mouseReleaseEvent(event);
}
//! [5]
//! [6]
void View::showInformation(ImageItem *image)
{
int id = image->id();
if (id < 0 || id >= itemTable->rowCount())
return;
InformationWindow *window = findWindow(id);
if (!window) {
window = new InformationWindow(id, itemTable, this);
connect(window, QOverload<int,const QString &>::of(&InformationWindow::imageChanged),
this, QOverload<int,const QString &>::of(&View::updateImage));
window->move(pos() + QPoint(20, 40));
window->show();
informationWindows.append(window);
}
if (window->isVisible()) {
window->raise();
window->activateWindow();
} else
window->show();
}
//! [6]
//! [7]
void View::updateImage(int id, const QString &fileName)
{
QList<QGraphicsItem *> items = scene->items();
while(!items.empty()) {
QGraphicsItem *item = items.takeFirst();
if (ImageItem *image = qgraphicsitem_cast<ImageItem *>(item)) {
if (image->id() == id){
image->setPixmap(QPixmap(":/" +fileName));
image->adjust();
break;
}
}
}
}
//! [7]
//! [8]
InformationWindow *View::findWindow(int id) const
{
for (auto window : informationWindows) {
if (window && (window->id() == id))
return window;
}
return nullptr;
}
//! [8]

View File

@ -0,0 +1,43 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef VIEW_H
#define VIEW_H
#include <QtWidgets>
#include <QtSql>
class ImageItem;
class InformationWindow;
//! [0]
class View : public QGraphicsView
{
Q_OBJECT
public:
View(const QString &items, const QString &images, QWidget *parent = nullptr);
protected:
void mouseReleaseEvent(QMouseEvent *event) override;
//! [0]
//! [1]
private slots:
void updateImage(int id, const QString &fileName);
//! [1]
//! [2]
private:
void addItems();
InformationWindow *findWindow(int id) const;
void showInformation(ImageItem *image);
QGraphicsScene *scene;
QList<InformationWindow *> informationWindows;
//! [2] //! [3]
QSqlRelationalTableModel *itemTable;
};
//! [3]
#endif

View File

@ -0,0 +1,54 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(masterdetail LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/sql/masterdetail")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Sql Widgets Xml)
qt_standard_project_setup()
qt_add_executable(masterdetail
database.h
dialog.cpp dialog.h
main.cpp
mainwindow.cpp mainwindow.h
)
set_target_properties(masterdetail PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(masterdetail PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Sql
Qt6::Widgets
Qt6::Xml
)
# Resources:
set(masterdetail_resource_files
"images/icon.png"
"images/image.png"
)
qt_add_resources(masterdetail "masterdetail"
PREFIX
"/"
FILES
${masterdetail_resource_files}
)
install(TARGETS masterdetail
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,98 @@
<archive>
<album id="1" >
<track number="01" >Humming one of your songs</track>
<track number="02" >Are they saying goodbye</track>
<track number="03" >On off</track>
<track number="04" >I shot my heart</track>
<track number="05" >Drowning in Those Eyes</track>
<track number="06" >So you did it again</track>
<track number="07" >One more time</track>
<track number="08" >Headphone silence</track>
<track number="09" >What I want</track>
<track number="10" >Sleeping by the Fyris River</track>
<track number="11" >Wooden Body</track>
<track number="12" >Humming one of your songs (encore)</track>
</album>
<album id="2" >
<track number="01" >To let myself go</track>
<track number="02" >Rubber and Soul</track>
<track number="03" >Balloon ranger</track>
<track number="04" >My lover will go</track>
<track number="05" >Temporary dive</track>
<track number="06" >Laid in earth</track>
<track number="07" >This voice</track>
<track number="08" >Where friend rhymes with end</track>
<track number="09" >Song No.6 feat Ron Sexsmith</track>
<track number="10" >The Fight Song</track>
</album>
<album id="3" >
<track number="01" >From Grace</track>
<track number="02" >All's not last</track>
<track number="03" >That Great October Sound</track>
<track number="04" >Life Here Is Gold</track>
<track number="05" >Tomorrow Stays The Same</track>
<track number="06" >Postulate</track>
<track number="07" >Adelaide</track>
<track number="08" >John Wayne</track>
<track number="09" >Love's Lost</track>
<track number="10" >Dreamweaver</track>
<track number="11" >Outro</track>
</album>
<album id="4" >
<track number="01" >Rain down on me</track>
<track number="02" >Cecilia</track>
<track number="03" >Make a mess of yourself</track>
<track number="04" >Pale green eyes</track>
<track number="05" >Either way I'm gone</track>
<track number="06" >Honey</track>
<track number="07" >Rise in shame</track>
<track number="08" >Stray dogs</track>
<track number="09" >The willow</track>
<track number="10" >Stay home</track>
<track number="11" >Outro</track>
</album>
<album id="5" >
</album>
<album id="6" >
<track number="01" >Kontroll på kontinentet</track>
<track number="02" >Ompa til du dør</track>
<track number="03" >Bøn fra helvete</track>
<track number="04" >170</track>
<track number="05" >Rullett</track>
<track number="06" >Dr. Mowinckel</track>
<track number="07" >Fra sjåfør til passasjer</track>
<track number="08" >Resistansen</track>
<track number="09" >Dekk bord</track>
<track number="10" >Bak et halleluja</track>
<track number="11" >Bris</track>
<track number="12" >Mr. Kaizer, hans Constanze og meg</track>
</album>
<album id="7" >
<track number="01" >Di grind</track>
<track number="02" >Hevnervals</track>
<track number="03" >Evig pint</track>
<track number="04" >De involverte</track>
<track number="05" >Djevelens orkester</track>
<track number="06" >Container</track>
<track number="07" >Naade</track>
<track number="08" >Min kvite russer</track>
<track number="09" >Veterans klage</track>
<track number="10" >Til depotet</track>
<track number="11" >Salt og pepper</track>
<track number="12" >Drøm Hardt (Requiem Part I)</track>
</album>
<album id="8" >
<track number="01" >KGB</track>
<track number="02" >Maestro</track>
<track number="03" >Knekker deg til sist</track>
<track number="04" >Senor Flamingos Adieu</track>
<track number="05" >Blitzregn baby</track>
<track number="06" >Dieter Meyers Inst.</track>
<track number="07" >Christiania</track>
<track number="08" >Delikatessen</track>
<track number="09" >Jævel av en tango</track>
<track number="10" >Papa har lov</track>
<track number="11" >Auksjon (i Dieter Meyers hall)</track>
<track number="12" >På ditt skift</track>
</album>
</archive>

View File

@ -0,0 +1,59 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef DATABASE_H
#define DATABASE_H
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
static bool createConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if (!db.open()) {
QMessageBox::critical(nullptr, QObject::tr("Cannot open database"),
QObject::tr("Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information how "
"to build it.\n\n"
"Click Cancel to exit."), QMessageBox::Cancel);
return false;
}
QSqlQuery query;
query.exec("create table artists (id int primary key, "
"artist varchar(40), "
"albumcount int)");
query.exec("insert into artists values(0, '<all>', 0)");
query.exec("insert into artists values(1, 'Ane Brun', 2)");
query.exec("insert into artists values(2, 'Thomas Dybdahl', 3)");
query.exec("insert into artists values(3, 'Kaizers Orchestra', 3)");
query.exec("create table albums (albumid int primary key, "
"title varchar(50), "
"artistid int, "
"year int)");
query.exec("insert into albums values(1, 'Spending Time With Morgan', 1, "
"2003)");
query.exec("insert into albums values(2, 'A Temporary Dive', 1, 2005)");
query.exec("insert into albums values(3, '...The Great October Sound', 2, "
"2002)");
query.exec("insert into albums values(4, 'Stray Dogs', 2, 2003)");
query.exec("insert into albums values(5, "
"'One day you`ll dance for me, New York City', 2, 2004)");
query.exec("insert into albums values(6, 'Ompa Til Du D\xc3\xb8r', 3, 2001)");
query.exec("insert into albums values(7, 'Evig Pint', 3, 2002)");
query.exec("insert into albums values(8, 'Maestro', 3, 2005)");
return true;
}
#endif

View File

@ -0,0 +1,245 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "dialog.h"
int uniqueAlbumId;
int uniqueArtistId;
Dialog::Dialog(QSqlRelationalTableModel *albums, QDomDocument details,
QFile *output, QWidget *parent)
: QDialog(parent)
{
model = albums;
albumDetails = details;
outputFile = output;
QGroupBox *inputWidgetBox = createInputWidgets();
QDialogButtonBox *buttonBox = createButtons();
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(inputWidgetBox);
layout->addWidget(buttonBox);
setLayout(layout);
setWindowTitle(tr("Add Album"));
}
void Dialog::submit()
{
QString artist = artistEditor->text();
QString title = titleEditor->text();
if (artist.isEmpty() || title.isEmpty()) {
QString message(tr("Please provide both the name of the artist "
"and the title of the album."));
QMessageBox::information(this, tr("Add Album"), message);
} else {
int artistId = findArtistId(artist);
int albumId = addNewAlbum(title, artistId);
QStringList tracks;
tracks = tracksEditor->text().split(QLatin1Char(','), Qt::SkipEmptyParts);
addTracks(albumId, tracks);
increaseAlbumCount(indexOfArtist(artist));
accept();
}
}
int Dialog::findArtistId(const QString &artist)
{
QSqlTableModel *artistModel = model->relationModel(2);
int row = 0;
while (row < artistModel->rowCount()) {
QSqlRecord record = artistModel->record(row);
if (record.value("artist") == artist)
return record.value("id").toInt();
else
row++;
}
return addNewArtist(artist);
}
int Dialog::addNewArtist(const QString &name)
{
QSqlTableModel *artistModel = model->relationModel(2);
QSqlRecord record;
int id = generateArtistId();
QSqlField f1("id", QMetaType(QMetaType::Int));
QSqlField f2("artist", QMetaType(QMetaType::QString));
QSqlField f3("albumcount", QMetaType(QMetaType::Int));
f1.setValue(QVariant(id));
f2.setValue(QVariant(name));
f3.setValue(QVariant(0));
record.append(f1);
record.append(f2);
record.append(f3);
artistModel->insertRecord(-1, record);
return id;
}
int Dialog::addNewAlbum(const QString &title, int artistId)
{
int id = generateAlbumId();
QSqlRecord record;
QSqlField f1("albumid", QMetaType(QMetaType::Int));
QSqlField f2("title", QMetaType(QMetaType::QString));
QSqlField f3("artistid", QMetaType(QMetaType::Int));
QSqlField f4("year", QMetaType(QMetaType::Int));
f1.setValue(QVariant(id));
f2.setValue(QVariant(title));
f3.setValue(QVariant(artistId));
f4.setValue(QVariant(yearEditor->value()));
record.append(f1);
record.append(f2);
record.append(f3);
record.append(f4);
model->insertRecord(-1, record);
return id;
}
void Dialog::addTracks(int albumId, const QStringList &tracks)
{
QDomElement albumNode = albumDetails.createElement("album");
albumNode.setAttribute("id", albumId);
for (int i = 0; i < tracks.count(); ++i) {
QString trackNumber = QString::number(i);
if (i < 10)
trackNumber.prepend('0');
QDomText textNode = albumDetails.createTextNode(tracks.at(i));
QDomElement trackNode = albumDetails.createElement("track");
trackNode.setAttribute("number", trackNumber);
trackNode.appendChild(textNode);
albumNode.appendChild(trackNode);
}
QDomNodeList archive = albumDetails.elementsByTagName("archive");
archive.item(0).appendChild(albumNode);
/*
The following code is commented out since the example uses an in
memory database, i.e., altering the XML file will bring the data
out of sync.
if (!outputFile->open(QIODevice::WriteOnly)) {
return;
} else {
QTextStream stream(outputFile);
archive.item(0).save(stream, 4);
outputFile->close();
}
*/
}
void Dialog::increaseAlbumCount(QModelIndex artistIndex)
{
QSqlTableModel *artistModel = model->relationModel(2);
QModelIndex albumCountIndex;
albumCountIndex = artistIndex.sibling(artistIndex.row(), 2);
int albumCount = albumCountIndex.data().toInt();
artistModel->setData(albumCountIndex, QVariant(albumCount + 1));
}
void Dialog::revert()
{
artistEditor->clear();
titleEditor->clear();
yearEditor->setValue(QDate::currentDate().year());
tracksEditor->clear();
}
QGroupBox *Dialog::createInputWidgets()
{
QGroupBox *box = new QGroupBox(tr("Add Album"));
QLabel *artistLabel = new QLabel(tr("Artist:"));
QLabel *titleLabel = new QLabel(tr("Title:"));
QLabel *yearLabel = new QLabel(tr("Year:"));
QLabel *tracksLabel = new QLabel(tr("Tracks (separated by comma):"));
artistEditor = new QLineEdit;
titleEditor = new QLineEdit;
yearEditor = new QSpinBox;
yearEditor->setMinimum(1900);
yearEditor->setMaximum(QDate::currentDate().year());
yearEditor->setValue(yearEditor->maximum());
yearEditor->setReadOnly(false);
tracksEditor = new QLineEdit;
QGridLayout *layout = new QGridLayout;
layout->addWidget(artistLabel, 0, 0);
layout->addWidget(artistEditor, 0, 1);
layout->addWidget(titleLabel, 1, 0);
layout->addWidget(titleEditor, 1, 1);
layout->addWidget(yearLabel, 2, 0);
layout->addWidget(yearEditor, 2, 1);
layout->addWidget(tracksLabel, 3, 0, 1, 2);
layout->addWidget(tracksEditor, 4, 0, 1, 2);
box->setLayout(layout);
return box;
}
QDialogButtonBox *Dialog::createButtons()
{
QPushButton *closeButton = new QPushButton(tr("&Close"));
QPushButton *revertButton = new QPushButton(tr("&Revert"));
QPushButton *submitButton = new QPushButton(tr("&Submit"));
closeButton->setDefault(true);
connect(closeButton, &QPushButton::clicked, this, &Dialog::close);
connect(revertButton, &QPushButton::clicked, this, &Dialog::revert);
connect(submitButton, &QPushButton::clicked, this, &Dialog::submit);
QDialogButtonBox *buttonBox = new QDialogButtonBox;
buttonBox->addButton(submitButton, QDialogButtonBox::ResetRole);
buttonBox->addButton(revertButton, QDialogButtonBox::ResetRole);
buttonBox->addButton(closeButton, QDialogButtonBox::RejectRole);
return buttonBox;
}
QModelIndex Dialog::indexOfArtist(const QString &artist)
{
QSqlTableModel *artistModel = model->relationModel(2);
for (int i = 0; i < artistModel->rowCount(); ++i) {
QSqlRecord record = artistModel->record(i);
if (record.value("artist") == artist)
return artistModel->index(i, 1);
}
return QModelIndex();
}
int Dialog::generateArtistId()
{
uniqueArtistId += 1;
return uniqueArtistId;
}
int Dialog::generateAlbumId()
{
uniqueAlbumId += 1;
return uniqueAlbumId;
}

View File

@ -0,0 +1,45 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef DIALOG_H
#define DIALOG_H
#include <QtWidgets>
#include <QtSql>
#include <QtXml>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QSqlRelationalTableModel *albums, QDomDocument details,
QFile *output, QWidget *parent = nullptr);
private slots:
void revert();
void submit();
private:
int addNewAlbum(const QString &title, int artistId);
int addNewArtist(const QString &name);
void addTracks(int albumId, const QStringList &tracks);
QDialogButtonBox *createButtons();
QGroupBox *createInputWidgets();
int findArtistId(const QString &artist);
static int generateAlbumId();
static int generateArtistId();
void increaseAlbumCount(QModelIndex artistIndex);
QModelIndex indexOfArtist(const QString &artist);
QSqlRelationalTableModel *model;
QDomDocument albumDetails;
QFile *outputFile;
QLineEdit *artistEditor;
QLineEdit *titleEditor;
QSpinBox *yearEditor;
QLineEdit *tracksEditor;
};
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -0,0 +1,25 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "database.h"
#include "mainwindow.h"
#include <QApplication>
#include <QFile>
#include <stdlib.h>
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(masterdetail);
QApplication app(argc, argv);
if (!createConnection())
return EXIT_FAILURE;
QFile albumDetails("albumdetails.xml");
MainWindow window("artists", "albums", &albumDetails);
window.show();
return app.exec();
}

View File

@ -0,0 +1,397 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwindow.h"
#include "dialog.h"
#include <QtWidgets>
#include <QtSql>
#include <QtXml>
extern int uniqueAlbumId;
extern int uniqueArtistId;
MainWindow::MainWindow(const QString &artistTable, const QString &albumTable,
QFile *albumDetails, QWidget *parent)
: QMainWindow(parent)
{
file = albumDetails;
readAlbumData();
model = new QSqlRelationalTableModel(this);
model->setTable(albumTable);
model->setRelation(2, QSqlRelation(artistTable, "id", "artist"));
model->select();
QGroupBox *artists = createArtistGroupBox();
QGroupBox *albums = createAlbumGroupBox();
QGroupBox *details = createDetailsGroupBox();
artistView->setCurrentIndex(0);
uniqueAlbumId = model->rowCount();
uniqueArtistId = artistView->count();
connect(model, &QSqlRelationalTableModel::rowsInserted,
this, &MainWindow::updateHeader);
connect(model, &QSqlRelationalTableModel::rowsRemoved,
this, &MainWindow::updateHeader);
QGridLayout *layout = new QGridLayout;
layout->addWidget(artists, 0, 0);
layout->addWidget(albums, 1, 0);
layout->addWidget(details, 0, 1, 2, 1);
layout->setColumnStretch(1, 1);
layout->setColumnMinimumWidth(0, 500);
QWidget *widget = new QWidget;
widget->setLayout(layout);
setCentralWidget(widget);
createMenuBar();
showImageLabel();
resize(850, 400);
setWindowTitle(tr("Music Archive"));
}
void MainWindow::changeArtist(int row)
{
if (row > 0) {
QModelIndex index = model->relationModel(2)->index(row, 1);
model->setFilter("artist = '" + index.data().toString() + '\'') ;
showArtistProfile(index);
} else if (row == 0) {
model->setFilter(QString());
showImageLabel();
} else {
return;
}
}
void MainWindow::showArtistProfile(QModelIndex index)
{
QSqlRecord record = model->relationModel(2)->record(index.row());
QString name = record.value("artist").toString();
QString count = record.value("albumcount").toString();
profileLabel->setText(tr("Artist : %1 \n" \
"Number of Albums: %2").arg(name).arg(count));
profileLabel->show();
iconLabel->show();
titleLabel->hide();
trackList->hide();
imageLabel->hide();
}
void MainWindow::showAlbumDetails(QModelIndex index)
{
QSqlRecord record = model->record(index.row());
QString artist = record.value("artist").toString();
QString title = record.value("title").toString();
QString year = record.value("year").toString();
QString albumId = record.value("albumid").toString();
showArtistProfile(indexOfArtist(artist));
titleLabel->setText(tr("Title: %1 (%2)").arg(title).arg(year));
titleLabel->show();
QDomNodeList albums = albumData.elementsByTagName("album");
for (int i = 0; i < albums.count(); ++i) {
QDomNode album = albums.item(i);
if (album.toElement().attribute("id") == albumId) {
getTrackList(album.toElement());
break;
}
}
if (trackList->count() != 0)
trackList->show();
}
void MainWindow::getTrackList(QDomNode album)
{
trackList->clear();
QDomNodeList tracks = album.childNodes();
QDomNode track;
QString trackNumber;
for (int i = 0; i < tracks.count(); ++i) {
track = tracks.item(i);
trackNumber = track.toElement().attribute("number");
QListWidgetItem *item = new QListWidgetItem(trackList);
item->setText(trackNumber + ": " + track.toElement().text());
}
}
void MainWindow::addAlbum()
{
Dialog *dialog = new Dialog(model, albumData, file, this);
int accepted = dialog->exec();
if (accepted == 1) {
int lastRow = model->rowCount() - 1;
albumView->selectRow(lastRow);
albumView->scrollToBottom();
showAlbumDetails(model->index(lastRow, 0));
}
}
void MainWindow::deleteAlbum()
{
QModelIndexList selection = albumView->selectionModel()->selectedRows(0);
if (!selection.empty()) {
QModelIndex idIndex = selection.at(0);
int id = idIndex.data().toInt();
QString title = idIndex.sibling(idIndex.row(), 1).data().toString();
QString artist = idIndex.sibling(idIndex.row(), 2).data().toString();
QMessageBox::StandardButton button;
button = QMessageBox::question(this, tr("Delete Album"),
tr("Are you sure you want to "
"delete '%1' by '%2'?")
.arg(title, artist),
QMessageBox::Yes | QMessageBox::No);
if (button == QMessageBox::Yes) {
removeAlbumFromFile(id);
removeAlbumFromDatabase(idIndex);
decreaseAlbumCount(indexOfArtist(artist));
showImageLabel();
}
} else {
QMessageBox::information(this, tr("Delete Album"),
tr("Select the album you want to delete."));
}
}
void MainWindow::removeAlbumFromFile(int id)
{
QDomNodeList albums = albumData.elementsByTagName("album");
for (int i = 0; i < albums.count(); ++i) {
QDomNode node = albums.item(i);
if (node.toElement().attribute("id").toInt() == id) {
albumData.elementsByTagName("archive").item(0).removeChild(node);
break;
}
}
/*
The following code is commented out since the example uses an in
memory database, i.e., altering the XML file will bring the data
out of sync.
if (!file->open(QIODevice::WriteOnly)) {
return;
} else {
QTextStream stream(file);
albumData.elementsByTagName("archive").item(0).save(stream, 4);
file->close();
}
*/
}
void MainWindow::removeAlbumFromDatabase(QModelIndex index)
{
model->removeRow(index.row());
}
void MainWindow::decreaseAlbumCount(QModelIndex artistIndex)
{
int row = artistIndex.row();
QModelIndex albumCountIndex = artistIndex.sibling(row, 2);
int albumCount = albumCountIndex.data().toInt();
QSqlTableModel *artists = model->relationModel(2);
if (albumCount == 1) {
artists->removeRow(row);
showImageLabel();
} else {
artists->setData(albumCountIndex, QVariant(albumCount - 1));
}
}
void MainWindow::readAlbumData()
{
if (!file->open(QIODevice::ReadOnly))
return;
if (!albumData.setContent(file)) {
file->close();
return;
}
file->close();
}
QGroupBox* MainWindow::createArtistGroupBox()
{
artistView = new QComboBox;
artistView->setModel(model->relationModel(2));
artistView->setModelColumn(1);
connect(artistView, &QComboBox::currentIndexChanged,
this, &MainWindow::changeArtist);
QGroupBox *box = new QGroupBox(tr("Artist"));
QGridLayout *layout = new QGridLayout;
layout->addWidget(artistView, 0, 0);
box->setLayout(layout);
return box;
}
QGroupBox* MainWindow::createAlbumGroupBox()
{
QGroupBox *box = new QGroupBox(tr("Album"));
albumView = new QTableView;
albumView->setEditTriggers(QAbstractItemView::NoEditTriggers);
albumView->setSortingEnabled(true);
albumView->setSelectionBehavior(QAbstractItemView::SelectRows);
albumView->setSelectionMode(QAbstractItemView::SingleSelection);
albumView->setShowGrid(false);
albumView->verticalHeader()->hide();
albumView->setAlternatingRowColors(true);
albumView->setModel(model);
adjustHeader();
QLocale locale = albumView->locale();
locale.setNumberOptions(QLocale::OmitGroupSeparator);
albumView->setLocale(locale);
connect(albumView, &QTableView::clicked,
this, &MainWindow::showAlbumDetails);
connect(albumView, &QTableView::activated,
this, &MainWindow::showAlbumDetails);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(albumView, 0, { });
box->setLayout(layout);
return box;
}
QGroupBox* MainWindow::createDetailsGroupBox()
{
QGroupBox *box = new QGroupBox(tr("Details"));
profileLabel = new QLabel;
profileLabel->setWordWrap(true);
profileLabel->setAlignment(Qt::AlignBottom);
titleLabel = new QLabel;
titleLabel->setWordWrap(true);
titleLabel->setAlignment(Qt::AlignBottom);
iconLabel = new QLabel();
iconLabel->setAlignment(Qt::AlignBottom | Qt::AlignRight);
iconLabel->setPixmap(QPixmap(":/images/icon.png"));
imageLabel = new QLabel;
imageLabel->setWordWrap(true);
imageLabel->setAlignment(Qt::AlignCenter);
imageLabel->setPixmap(QPixmap(":/images/image.png"));
trackList = new QListWidget;
QGridLayout *layout = new QGridLayout;
layout->addWidget(imageLabel, 0, 0, 3, 2);
layout->addWidget(profileLabel, 0, 0);
layout->addWidget(iconLabel, 0, 1);
layout->addWidget(titleLabel, 1, 0, 1, 2);
layout->addWidget(trackList, 2, 0, 1, 2);
layout->setRowStretch(2, 1);
box->setLayout(layout);
return box;
}
void MainWindow::createMenuBar()
{
QAction *addAction = new QAction(tr("&Add album..."), this);
QAction *deleteAction = new QAction(tr("&Delete album..."), this);
QAction *quitAction = new QAction(tr("&Quit"), this);
QAction *aboutAction = new QAction(tr("&About"), this);
QAction *aboutQtAction = new QAction(tr("About &Qt"), this);
addAction->setShortcut(tr("Ctrl+A"));
deleteAction->setShortcut(tr("Ctrl+D"));
quitAction->setShortcuts(QKeySequence::Quit);
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(addAction);
fileMenu->addAction(deleteAction);
fileMenu->addSeparator();
fileMenu->addAction(quitAction);
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(aboutAction);
helpMenu->addAction(aboutQtAction);
connect(addAction, &QAction::triggered,
this, &MainWindow::addAlbum);
connect(deleteAction, &QAction::triggered,
this, &MainWindow::deleteAlbum);
connect(quitAction, &QAction::triggered,
qApp, &QCoreApplication::quit);
connect(aboutAction, &QAction::triggered,
this, &MainWindow::about);
connect(aboutQtAction, &QAction::triggered,
qApp, &QApplication::aboutQt);
}
void MainWindow::showImageLabel()
{
profileLabel->hide();
titleLabel->hide();
iconLabel->hide();
trackList->hide();
imageLabel->show();
}
QModelIndex MainWindow::indexOfArtist(const QString &artist)
{
QSqlTableModel *artistModel = model->relationModel(2);
for (int i = 0; i < artistModel->rowCount(); i++) {
QSqlRecord record = artistModel->record(i);
if (record.value("artist") == artist)
return artistModel->index(i, 1);
}
return QModelIndex();
}
void MainWindow::updateHeader(QModelIndex, int, int)
{
adjustHeader();
}
void MainWindow::adjustHeader()
{
albumView->hideColumn(0);
albumView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
albumView->resizeColumnToContents(2);
albumView->resizeColumnToContents(3);
}
void MainWindow::about()
{
QMessageBox::about(this, tr("About Music Archive"),
tr("<p>The <b>Music Archive</b> example shows how to present "
"data from different data sources in the same application. "
"The album titles, and the corresponding artists and release dates, "
"are kept in a database, while each album's tracks are stored "
"in an XML file. </p><p>The example also shows how to add as "
"well as remove data from both the database and the "
"associated XML file using the API provided by the Qt SQL and "
"Qt XML modules, respectively.</p>"));
}

View File

@ -0,0 +1,66 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QDomDocument>
#include <QMainWindow>
#include <QModelIndex>
QT_BEGIN_NAMESPACE
class QComboBox;
class QFile;
class QGroupBox;
class QLabel;
class QListWidget;
class QSqlRelationalTableModel;
class QTableView;
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(const QString &artistTable, const QString &albumTable,
QFile *albumDetails, QWidget *parent = nullptr);
private slots:
void about();
void addAlbum();
void changeArtist(int row);
void deleteAlbum();
void showAlbumDetails(QModelIndex index);
void showArtistProfile(QModelIndex index);
void updateHeader(QModelIndex, int, int);
private:
void adjustHeader();
QGroupBox *createAlbumGroupBox();
QGroupBox *createArtistGroupBox();
QGroupBox *createDetailsGroupBox();
void createMenuBar();
void decreaseAlbumCount(QModelIndex artistIndex);
void getTrackList(QDomNode album);
QModelIndex indexOfArtist(const QString &artist);
void readAlbumData();
void removeAlbumFromDatabase(QModelIndex album);
void removeAlbumFromFile(int id);
void showImageLabel();
QTableView *albumView;
QComboBox *artistView;
QListWidget *trackList;
QLabel *iconLabel;
QLabel *imageLabel;
QLabel *profileLabel;
QLabel *titleLabel;
QDomDocument albumData;
QFile *file;
QSqlRelationalTableModel *model;
};
#endif

View File

@ -0,0 +1,17 @@
HEADERS = database.h \
dialog.h \
mainwindow.h
RESOURCES = masterdetail.qrc
SOURCES = dialog.cpp \
main.cpp \
mainwindow.cpp
QT += sql widgets
QT += xml widgets
requires(qtConfig(tableview))
EXAMPLE_FILES = albumdetails.xml
# install
target.path = $$[QT_INSTALL_EXAMPLES]/sql/masterdetail
INSTALLS += target

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>images/icon.png</file>
<file>images/image.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,40 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(querymodel LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/sql/querymodel")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Sql Widgets)
qt_standard_project_setup()
qt_add_executable(querymodel
../connection.h
customsqlmodel.cpp customsqlmodel.h
editablesqlmodel.cpp editablesqlmodel.h
main.cpp
)
set_target_properties(querymodel PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(querymodel PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Sql
Qt6::Widgets
)
install(TARGETS querymodel
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,27 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include "customsqlmodel.h"
CustomSqlModel::CustomSqlModel(QObject *parent)
: QSqlQueryModel(parent)
{
}
//! [0]
QVariant CustomSqlModel::data(const QModelIndex &index, int role) const
{
QVariant value = QSqlQueryModel::data(index, role);
if (value.isValid() && role == Qt::DisplayRole) {
if (index.column() == 0)
return value.toString().prepend('#');
else if (index.column() == 2)
return value.toString().toUpper();
}
if (role == Qt::ForegroundRole && index.column() == 1)
return QVariant::fromValue(QColor(Qt::blue));
return value;
}
//! [0]

View File

@ -0,0 +1,21 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CUSTOMSQLMODEL_H
#define CUSTOMSQLMODEL_H
#include <QSqlQueryModel>
//! [0]
class CustomSqlModel : public QSqlQueryModel
{
Q_OBJECT
public:
CustomSqlModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &item, int role) const override;
};
//! [0]
#endif

View File

@ -0,0 +1,72 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtSql>
#include "editablesqlmodel.h"
EditableSqlModel::EditableSqlModel(QObject *parent)
: QSqlQueryModel(parent)
{
}
//! [0]
Qt::ItemFlags EditableSqlModel::flags(
const QModelIndex &index) const
{
Qt::ItemFlags flags = QSqlQueryModel::flags(index);
if (index.column() == 1 || index.column() == 2)
flags |= Qt::ItemIsEditable;
return flags;
}
//! [0]
//! [1]
bool EditableSqlModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
if (index.column() < 1 || index.column() > 2)
return false;
QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
int id = data(primaryKeyIndex).toInt();
clear();
bool ok;
if (index.column() == 1) {
ok = setFirstName(id, value.toString());
} else {
ok = setLastName(id, value.toString());
}
refresh();
return ok;
}
//! [1]
void EditableSqlModel::refresh()
{
setQuery("select * from person");
setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
setHeaderData(1, Qt::Horizontal, QObject::tr("First name"));
setHeaderData(2, Qt::Horizontal, QObject::tr("Last name"));
}
//! [2]
bool EditableSqlModel::setFirstName(int personId, const QString &firstName)
{
QSqlQuery query;
query.prepare("update person set firstname = ? where id = ?");
query.addBindValue(firstName);
query.addBindValue(personId);
return query.exec();
}
//! [2]
bool EditableSqlModel::setLastName(int personId, const QString &lastName)
{
QSqlQuery query;
query.prepare("update person set lastname = ? where id = ?");
query.addBindValue(lastName);
query.addBindValue(personId);
return query.exec();
}

View File

@ -0,0 +1,25 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef EDITABLESQLMODEL_H
#define EDITABLESQLMODEL_H
#include <QSqlQueryModel>
class EditableSqlModel : public QSqlQueryModel
{
Q_OBJECT
public:
EditableSqlModel(QObject *parent = nullptr);
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
private:
bool setFirstName(int personId, const QString &firstName);
bool setLastName(int personId, const QString &lastName);
void refresh();
};
#endif

View File

@ -0,0 +1,54 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../connection.h"
#include "customsqlmodel.h"
#include "editablesqlmodel.h"
#include <QApplication>
#include <QTableView>
#include <stdlib.h>
void initializeModel(QSqlQueryModel *model)
{
model->setQuery("select * from person");
model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("First name"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Last name"));
}
QTableView* createView(QSqlQueryModel *model, const QString &title = "")
{
QTableView *view = new QTableView;
view->setModel(model);
static int offset = 0;
view->setWindowTitle(title);
view->move(100 + offset, 100 + offset);
offset += 20;
view->show();
return view;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
if (!createConnection())
return EXIT_FAILURE;
QSqlQueryModel plainModel;
EditableSqlModel editableModel;
CustomSqlModel customModel;
initializeModel(&plainModel);
initializeModel(&editableModel);
initializeModel(&customModel);
createView(&plainModel, QObject::tr("Plain Query Model"));
createView(&editableModel, QObject::tr("Editable Query Model"));
createView(&customModel, QObject::tr("Custom Query Model"));
return app.exec();
}

View File

@ -0,0 +1,13 @@
HEADERS = ../connection.h \
customsqlmodel.h \
editablesqlmodel.h
SOURCES = customsqlmodel.cpp \
editablesqlmodel.cpp \
main.cpp
QT += sql widgets
requires(qtConfig(tableview))
# install
target.path = $$[QT_INSTALL_EXAMPLES]/sql/querymodel
INSTALLS += target

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(relationaltablemodel LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/sql/relationaltablemodel")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Sql Widgets)
qt_standard_project_setup()
qt_add_executable(relationaltablemodel
../connection.h
relationaltablemodel.cpp
)
set_target_properties(relationaltablemodel PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(relationaltablemodel PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Sql
Qt6::Widgets
)
install(TARGETS relationaltablemodel
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,80 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtSql>
#include "../connection.h"
#include <memory>
void initializeModel(QSqlRelationalTableModel *model)
{
//! [0]
model->setTable("employee");
//! [0]
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
//! [1]
model->setRelation(2, QSqlRelation("city", "id", "name"));
//! [1] //! [2]
model->setRelation(3, QSqlRelation("country", "id", "name"));
//! [2]
//! [3]
model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Name"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("City"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Country"));
//! [3]
model->select();
}
std::unique_ptr<QTableView> createView(const QString &title, QSqlTableModel *model)
{
//! [4]
std::unique_ptr<QTableView> view{new QTableView};
view->setModel(model);
view->setItemDelegate(new QSqlRelationalDelegate(view.get()));
//! [4]
view->setWindowTitle(title);
return view;
}
void createRelationalTables()
{
QSqlQuery query;
query.exec("create table employee(id int primary key, name varchar(20), city int, country int)");
query.exec("insert into employee values(1, 'Espen', 5000, 47)");
query.exec("insert into employee values(2, 'Harald', 80000, 49)");
query.exec("insert into employee values(3, 'Sam', 100, 1)");
query.exec("create table city(id int, name varchar(20))");
query.exec("insert into city values(100, 'San Jose')");
query.exec("insert into city values(5000, 'Oslo')");
query.exec("insert into city values(80000, 'Munich')");
query.exec("create table country(id int, name varchar(20))");
query.exec("insert into country values(1, 'USA')");
query.exec("insert into country values(47, 'Norway')");
query.exec("insert into country values(49, 'Germany')");
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
if (!createConnection())
return EXIT_FAILURE;
createRelationalTables();
QSqlRelationalTableModel model;
initializeModel(&model);
std::unique_ptr<QTableView> view = createView(QObject::tr("Relational Table Model"), &model);
view->show();
return app.exec();
}

View File

@ -0,0 +1,9 @@
HEADERS = ../connection.h
SOURCES = relationaltablemodel.cpp
QT += sql widgets
requires(qtConfig(tableview))
# install
target.path = $$[QT_INSTALL_EXAMPLES]/sql/relationaltablemodel
INSTALLS += target

21
examples/sql/sql.pro Normal file
View File

@ -0,0 +1,21 @@
requires(qtHaveModule(widgets))
TEMPLATE = subdirs
SUBDIRS = books \
drilldown \
cachedtable \
querymodel \
relationaltablemodel \
sqlwidgetmapper \
tablemodel
qtHaveModule(xml): SUBDIRS += masterdetail
!cross_compile:{
contains(QT_BUILD_PARTS, tools):{
SUBDIRS += sqlbrowser
}
}
EXAMPLE_FILES = connection.h

View File

@ -0,0 +1,43 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(sqlbrowser LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/sql/sqlbrowser")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Sql Widgets)
qt_standard_project_setup()
qt_add_executable(sqlbrowser
browser.cpp browser.h
browserwidget.ui
connectionwidget.cpp connectionwidget.h
main.cpp
qsqlconnectiondialog.cpp qsqlconnectiondialog.h qsqlconnectiondialog.ui
)
set_target_properties(sqlbrowser PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(sqlbrowser PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Sql
Qt6::Widgets
)
install(TARGETS sqlbrowser
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,268 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "browser.h"
#include "qsqlconnectiondialog.h"
#include <QtWidgets>
#include <QtSql>
Browser::Browser(QWidget *parent)
: QWidget(parent)
{
setupUi(this);
table->addAction(insertRowAction);
table->addAction(deleteRowAction);
table->addAction(fieldStrategyAction);
table->addAction(rowStrategyAction);
table->addAction(manualStrategyAction);
table->addAction(submitAction);
table->addAction(revertAction);
table->addAction(selectAction);
if (QSqlDatabase::drivers().isEmpty())
QMessageBox::information(this, tr("No database drivers found"),
tr("This demo requires at least one Qt database driver. "
"Please check the documentation how to build the "
"Qt SQL plugins."));
emit statusMessage(tr("Ready."));
}
Browser::~Browser()
{
}
void Browser::exec()
{
QSqlQueryModel *model = new QSqlQueryModel(table);
model->setQuery(QSqlQuery(sqlEdit->toPlainText(), connectionWidget->currentDatabase()));
table->setModel(model);
if (model->lastError().type() != QSqlError::NoError)
emit statusMessage(model->lastError().text());
else if (model->query().isSelect())
emit statusMessage(tr("Query OK."));
else
emit statusMessage(tr("Query OK, number of affected rows: %1").arg(
model->query().numRowsAffected()));
updateActions();
}
QSqlError Browser::addConnection(const QString &driver, const QString &dbName, const QString &host,
const QString &user, const QString &passwd, int port)
{
static int cCount = 0;
QSqlError err;
QSqlDatabase db = QSqlDatabase::addDatabase(driver, QString("Browser%1").arg(++cCount));
db.setDatabaseName(dbName);
db.setHostName(host);
db.setPort(port);
if (!db.open(user, passwd)) {
err = db.lastError();
db = QSqlDatabase();
QSqlDatabase::removeDatabase(QString("Browser%1").arg(cCount));
}
connectionWidget->refresh();
return err;
}
void Browser::addConnection()
{
QSqlConnectionDialog dialog(this);
if (dialog.exec() != QDialog::Accepted)
return;
if (dialog.useInMemoryDatabase()) {
QSqlDatabase::database("in_mem_db", false).close();
QSqlDatabase::removeDatabase("in_mem_db");
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "in_mem_db");
db.setDatabaseName(":memory:");
if (!db.open())
QMessageBox::warning(this, tr("Unable to open database"), tr("An error occurred while "
"opening the connection: ") + db.lastError().text());
QSqlQuery q("", db);
q.exec("drop table Movies");
q.exec("drop table Names");
q.exec("create table Movies (id integer primary key, Title varchar, Director varchar, Rating number)");
q.exec("insert into Movies values (0, 'Metropolis', 'Fritz Lang', '8.4')");
q.exec("insert into Movies values (1, 'Nosferatu, eine Symphonie des Grauens', 'F.W. Murnau', '8.1')");
q.exec("insert into Movies values (2, 'Bis ans Ende der Welt', 'Wim Wenders', '6.5')");
q.exec("insert into Movies values (3, 'Hardware', 'Richard Stanley', '5.2')");
q.exec("insert into Movies values (4, 'Mitchell', 'Andrew V. McLaglen', '2.1')");
q.exec("create table Names (id integer primary key, FirstName varchar, LastName varchar, City varchar)");
q.exec("insert into Names values (0, 'Sala', 'Palmer', 'Morristown')");
q.exec("insert into Names values (1, 'Christopher', 'Walker', 'Morristown')");
q.exec("insert into Names values (2, 'Donald', 'Duck', 'Andeby')");
q.exec("insert into Names values (3, 'Buck', 'Rogers', 'Paris')");
q.exec("insert into Names values (4, 'Sherlock', 'Holmes', 'London')");
connectionWidget->refresh();
} else {
QSqlError err = addConnection(dialog.driverName(), dialog.databaseName(), dialog.hostName(),
dialog.userName(), dialog.password(), dialog.port());
if (err.type() != QSqlError::NoError)
QMessageBox::warning(this, tr("Unable to open database"), tr("An error occurred while "
"opening the connection: ") + err.text());
}
}
void Browser::showTable(const QString &t)
{
QSqlTableModel *model = new CustomModel(table, connectionWidget->currentDatabase());
model->setEditStrategy(QSqlTableModel::OnRowChange);
model->setTable(connectionWidget->currentDatabase().driver()->escapeIdentifier(t, QSqlDriver::TableName));
model->select();
if (model->lastError().type() != QSqlError::NoError)
emit statusMessage(model->lastError().text());
table->setModel(model);
table->setEditTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed);
connect(table->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &Browser::currentChanged);
updateActions();
}
void Browser::showMetaData(const QString &t)
{
QSqlRecord rec = connectionWidget->currentDatabase().record(t);
QStandardItemModel *model = new QStandardItemModel(table);
model->insertRows(0, rec.count());
model->insertColumns(0, 7);
model->setHeaderData(0, Qt::Horizontal, "Fieldname");
model->setHeaderData(1, Qt::Horizontal, "Type");
model->setHeaderData(2, Qt::Horizontal, "Length");
model->setHeaderData(3, Qt::Horizontal, "Precision");
model->setHeaderData(4, Qt::Horizontal, "Required");
model->setHeaderData(5, Qt::Horizontal, "AutoValue");
model->setHeaderData(6, Qt::Horizontal, "DefaultValue");
for (int i = 0; i < rec.count(); ++i) {
QSqlField fld = rec.field(i);
model->setData(model->index(i, 0), fld.name());
model->setData(model->index(i, 1), fld.typeID() == -1
? QString(fld.metaType().name())
: QString("%1 (%2)").arg(fld.metaType().name()).arg(fld.typeID()));
model->setData(model->index(i, 2), fld.length());
model->setData(model->index(i, 3), fld.precision());
model->setData(model->index(i, 4), fld.requiredStatus() == -1 ? QVariant("?")
: QVariant(bool(fld.requiredStatus())));
model->setData(model->index(i, 5), fld.isAutoValue());
model->setData(model->index(i, 6), fld.defaultValue());
}
table->setModel(model);
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
updateActions();
}
void Browser::insertRow()
{
QSqlTableModel *model = qobject_cast<QSqlTableModel *>(table->model());
if (!model)
return;
QModelIndex insertIndex = table->currentIndex();
int row = insertIndex.row() == -1 ? 0 : insertIndex.row();
model->insertRow(row);
insertIndex = model->index(row, 0);
table->setCurrentIndex(insertIndex);
table->edit(insertIndex);
}
void Browser::deleteRow()
{
QSqlTableModel *model = qobject_cast<QSqlTableModel *>(table->model());
if (!model)
return;
QModelIndexList currentSelection = table->selectionModel()->selectedIndexes();
for (int i = 0; i < currentSelection.count(); ++i) {
if (currentSelection.at(i).column() != 0)
continue;
model->removeRow(currentSelection.at(i).row());
}
updateActions();
}
void Browser::updateActions()
{
QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
bool enableIns = tm;
bool enableDel = enableIns && table->currentIndex().isValid();
insertRowAction->setEnabled(enableIns);
deleteRowAction->setEnabled(enableDel);
fieldStrategyAction->setEnabled(tm);
rowStrategyAction->setEnabled(tm);
manualStrategyAction->setEnabled(tm);
submitAction->setEnabled(tm);
revertAction->setEnabled(tm);
selectAction->setEnabled(tm);
if (tm) {
QSqlTableModel::EditStrategy es = tm->editStrategy();
fieldStrategyAction->setChecked(es == QSqlTableModel::OnFieldChange);
rowStrategyAction->setChecked(es == QSqlTableModel::OnRowChange);
manualStrategyAction->setChecked(es == QSqlTableModel::OnManualSubmit);
}
}
void Browser::about()
{
QMessageBox::about(this, tr("About"), tr("The SQL Browser demonstration "
"shows how a data browser can be used to visualize the results of SQL"
"statements on a live database"));
}
void Browser::on_fieldStrategyAction_triggered()
{
QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
if (tm)
tm->setEditStrategy(QSqlTableModel::OnFieldChange);
}
void Browser::on_rowStrategyAction_triggered()
{
QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
if (tm)
tm->setEditStrategy(QSqlTableModel::OnRowChange);
}
void Browser::on_manualStrategyAction_triggered()
{
QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
if (tm)
tm->setEditStrategy(QSqlTableModel::OnManualSubmit);
}
void Browser::on_submitAction_triggered()
{
QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
if (tm)
tm->submitAll();
}
void Browser::on_revertAction_triggered()
{
QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
if (tm)
tm->revertAll();
}
void Browser::on_selectAction_triggered()
{
QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
if (tm)
tm->select();
}

View File

@ -0,0 +1,83 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef BROWSER_H
#define BROWSER_H
#include <QWidget>
#include <QSqlTableModel>
#include "ui_browserwidget.h"
class ConnectionWidget;
QT_FORWARD_DECLARE_CLASS(QTableView)
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QTextEdit)
QT_FORWARD_DECLARE_CLASS(QSqlError)
class Browser: public QWidget, private Ui::Browser
{
Q_OBJECT
public:
Browser(QWidget *parent = nullptr);
virtual ~Browser();
QSqlError addConnection(const QString &driver, const QString &dbName, const QString &host,
const QString &user, const QString &passwd, int port = -1);
void insertRow();
void deleteRow();
void updateActions();
public slots:
void exec();
void showTable(const QString &table);
void showMetaData(const QString &table);
void addConnection();
void currentChanged() { updateActions(); }
void about();
void on_insertRowAction_triggered()
{ insertRow(); }
void on_deleteRowAction_triggered()
{ deleteRow(); }
void on_fieldStrategyAction_triggered();
void on_rowStrategyAction_triggered();
void on_manualStrategyAction_triggered();
void on_submitAction_triggered();
void on_revertAction_triggered();
void on_selectAction_triggered();
void on_connectionWidget_tableActivated(const QString &table)
{ showTable(table); }
void on_connectionWidget_metaDataRequested(const QString &table)
{ showMetaData(table); }
void on_submitButton_clicked()
{
exec();
sqlEdit->setFocus();
}
void on_clearButton_clicked()
{
sqlEdit->clear();
sqlEdit->setFocus();
}
signals:
void statusMessage(const QString &message);
};
class CustomModel: public QSqlTableModel
{
Q_OBJECT
public:
explicit CustomModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase())
: QSqlTableModel(parent, db) {}
QVariant data(const QModelIndex &idx, int role) const override
{
if (role == Qt::BackgroundRole && isDirty(idx))
return QBrush(QColor(Qt::yellow));
return QSqlTableModel::data(idx, role);
}
};
#endif

View File

@ -0,0 +1,241 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Browser</class>
<widget class="QWidget" name="Browser">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>765</width>
<height>515</height>
</rect>
</property>
<property name="windowTitle">
<string>Qt SQL Browser</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>8</number>
</property>
<item>
<widget class="QSplitter" name="splitter_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="ConnectionWidget" name="connectionWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<widget class="QTableView" name="table">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>180</height>
</size>
</property>
<property name="title">
<string>SQL Query</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QTextEdit" name="sqlEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>18</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>120</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>1</number>
</property>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="clearButton">
<property name="text">
<string>&amp;Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="submitButton">
<property name="text">
<string>&amp;Submit</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
<action name="insertRowAction">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Insert Row</string>
</property>
<property name="statusTip">
<string>Inserts a new Row</string>
</property>
</action>
<action name="deleteRowAction">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Delete Row</string>
</property>
<property name="statusTip">
<string>Deletes the current Row</string>
</property>
</action>
<action name="fieldStrategyAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Submit on &amp;Field Change</string>
</property>
<property name="toolTip">
<string>Commit on Field Change</string>
</property>
</action>
<action name="rowStrategyAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Submit on &amp;Row Change</string>
</property>
<property name="toolTip">
<string>Commit on Row Change</string>
</property>
</action>
<action name="manualStrategyAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Submit &amp;Manually</string>
</property>
<property name="toolTip">
<string>Commit Manually</string>
</property>
</action>
<action name="submitAction">
<property name="text">
<string>&amp;Submit All</string>
</property>
<property name="toolTip">
<string>Submit Changes</string>
</property>
</action>
<action name="revertAction">
<property name="text">
<string>&amp;Revert All</string>
</property>
<property name="toolTip">
<string>Revert</string>
</property>
</action>
<action name="selectAction">
<property name="text">
<string>S&amp;elect</string>
</property>
<property name="toolTip">
<string>Refresh Data from Database</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>ConnectionWidget</class>
<extends>QTreeView</extends>
<header>connectionwidget.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>sqlEdit</tabstop>
<tabstop>clearButton</tabstop>
<tabstop>submitButton</tabstop>
<tabstop>connectionWidget</tabstop>
<tabstop>table</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,126 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "connectionwidget.h"
#include <QtWidgets>
#include <QtSql>
ConnectionWidget::ConnectionWidget(QWidget *parent)
: QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
tree = new QTreeWidget(this);
tree->setObjectName(QLatin1String("tree"));
tree->setHeaderLabels(QStringList(tr("database")));
tree->header()->setSectionResizeMode(QHeaderView::Stretch);
QAction *refreshAction = new QAction(tr("Refresh"), tree);
metaDataAction = new QAction(tr("Show Schema"), tree);
connect(refreshAction, &QAction::triggered, this, &ConnectionWidget::refresh);
connect(metaDataAction, &QAction::triggered, this, &ConnectionWidget::showMetaData);
tree->addAction(refreshAction);
tree->addAction(metaDataAction);
tree->setContextMenuPolicy(Qt::ActionsContextMenu);
layout->addWidget(tree);
QMetaObject::connectSlotsByName(this);
}
ConnectionWidget::~ConnectionWidget()
{
}
static QString qDBCaption(const QSqlDatabase &db)
{
QString nm = db.driverName();
nm.append(QLatin1Char(':'));
if (!db.userName().isEmpty())
nm.append(db.userName()).append(QLatin1Char('@'));
nm.append(db.databaseName());
return nm;
}
void ConnectionWidget::refresh()
{
tree->clear();
QStringList connectionNames = QSqlDatabase::connectionNames();
bool gotActiveDb = false;
for (int i = 0; i < connectionNames.count(); ++i) {
QTreeWidgetItem *root = new QTreeWidgetItem(tree);
QSqlDatabase db = QSqlDatabase::database(connectionNames.at(i), false);
root->setText(0, qDBCaption(db));
if (connectionNames.at(i) == activeDb) {
gotActiveDb = true;
setActive(root);
}
if (db.isOpen()) {
QStringList tables = db.tables();
for (int t = 0; t < tables.count(); ++t) {
QTreeWidgetItem *table = new QTreeWidgetItem(root);
table->setText(0, tables.at(t));
}
}
}
if (!gotActiveDb) {
activeDb = connectionNames.value(0);
setActive(tree->topLevelItem(0));
}
tree->doItemsLayout(); // HACK
}
QSqlDatabase ConnectionWidget::currentDatabase() const
{
return QSqlDatabase::database(activeDb);
}
static void qSetBold(QTreeWidgetItem *item, bool bold)
{
QFont font = item->font(0);
font.setBold(bold);
item->setFont(0, font);
}
void ConnectionWidget::setActive(QTreeWidgetItem *item)
{
for (int i = 0; i < tree->topLevelItemCount(); ++i) {
if (tree->topLevelItem(i)->font(0).bold())
qSetBold(tree->topLevelItem(i), false);
}
if (!item)
return;
qSetBold(item, true);
activeDb = QSqlDatabase::connectionNames().value(tree->indexOfTopLevelItem(item));
}
void ConnectionWidget::on_tree_itemActivated(QTreeWidgetItem *item, int /* column */)
{
if (!item)
return;
if (!item->parent()) {
setActive(item);
} else {
setActive(item->parent());
emit tableActivated(item->text(0));
}
}
void ConnectionWidget::showMetaData()
{
QTreeWidgetItem *cItem = tree->currentItem();
if (!cItem || !cItem->parent())
return;
setActive(cItem->parent());
emit metaDataRequested(cItem->text(0));
}
void ConnectionWidget::on_tree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
{
metaDataAction->setEnabled(current && current->parent());
}

View File

@ -0,0 +1,41 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CONNECTIONWIDGET_H
#define CONNECTIONWIDGET_H
#include <QWidget>
QT_FORWARD_DECLARE_CLASS(QTreeWidget)
QT_FORWARD_DECLARE_CLASS(QTreeWidgetItem)
QT_FORWARD_DECLARE_CLASS(QSqlDatabase)
QT_FORWARD_DECLARE_CLASS(QMenu)
class ConnectionWidget: public QWidget
{
Q_OBJECT
public:
ConnectionWidget(QWidget *parent = nullptr);
virtual ~ConnectionWidget();
QSqlDatabase currentDatabase() const;
signals:
void tableActivated(const QString &table);
void metaDataRequested(const QString &tableName);
public slots:
void refresh();
void showMetaData();
void on_tree_itemActivated(QTreeWidgetItem *item, int column);
void on_tree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
private:
void setActive(QTreeWidgetItem *);
QTreeWidget *tree;
QAction *metaDataAction;
QString activeDb;
};
#endif

View File

@ -0,0 +1,55 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "browser.h"
#include <QtCore>
#include <QtWidgets>
#include <QtSql>
void addConnectionsFromCommandline(const QStringList &args, Browser *browser)
{
for (int i = 1; i < args.count(); ++i) {
QUrl url(args.at(i), QUrl::TolerantMode);
if (!url.isValid()) {
qWarning("Invalid URL: %s", qPrintable(args.at(i)));
continue;
}
QSqlError err = browser->addConnection(url.scheme(), url.path().mid(1), url.host(),
url.userName(), url.password(), url.port(-1));
if (err.type() != QSqlError::NoError)
qDebug() << "Unable to open connection:" << err;
}
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow mainWin;
mainWin.setWindowTitle(QObject::tr("Qt SQL Browser"));
Browser browser(&mainWin);
mainWin.setCentralWidget(&browser);
QMenu *fileMenu = mainWin.menuBar()->addMenu(QObject::tr("&File"));
fileMenu->addAction(QObject::tr("Add &Connection..."),
[&]() { browser.addConnection(); });
fileMenu->addSeparator();
fileMenu->addAction(QObject::tr("&Quit"), []() { qApp->quit(); });
QMenu *helpMenu = mainWin.menuBar()->addMenu(QObject::tr("&Help"));
helpMenu->addAction(QObject::tr("About"), [&]() { browser.about(); });
helpMenu->addAction(QObject::tr("About Qt"), []() { qApp->aboutQt(); });
QObject::connect(&browser, &Browser::statusMessage, [&mainWin](const QString &text) {
mainWin.statusBar()->showMessage(text);
});
addConnectionsFromCommandline(app.arguments(), &browser);
mainWin.show();
if (QSqlDatabase::connectionNames().isEmpty())
QMetaObject::invokeMethod(&browser, "addConnection", Qt::QueuedConnection);
return app.exec();
}

View File

@ -0,0 +1,70 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "qsqlconnectiondialog.h"
#include "ui_qsqlconnectiondialog.h"
#include <QSqlDatabase>
QSqlConnectionDialog::QSqlConnectionDialog(QWidget *parent)
: QDialog(parent)
{
ui.setupUi(this);
QStringList drivers = QSqlDatabase::drivers();
if (!drivers.contains("QSQLITE"))
ui.dbCheckBox->setEnabled(false);
ui.comboDriver->addItems(drivers);
}
QSqlConnectionDialog::~QSqlConnectionDialog()
{
}
QString QSqlConnectionDialog::driverName() const
{
return ui.comboDriver->currentText();
}
QString QSqlConnectionDialog::databaseName() const
{
return ui.editDatabase->text();
}
QString QSqlConnectionDialog::userName() const
{
return ui.editUsername->text();
}
QString QSqlConnectionDialog::password() const
{
return ui.editPassword->text();
}
QString QSqlConnectionDialog::hostName() const
{
return ui.editHostname->text();
}
int QSqlConnectionDialog::port() const
{
return ui.portSpinBox->value();
}
bool QSqlConnectionDialog::useInMemoryDatabase() const
{
return ui.dbCheckBox->isChecked();
}
void QSqlConnectionDialog::on_okButton_clicked()
{
if (ui.comboDriver->currentText().isEmpty()) {
QMessageBox::information(this, tr("No database driver selected"),
tr("Please select a database driver"));
ui.comboDriver->setFocus();
} else {
accept();
}
}

View File

@ -0,0 +1,36 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef QSQLCONNECTIONDIALOG_H
#define QSQLCONNECTIONDIALOG_H
#include <QDialog>
#include <QMessageBox>
#include "ui_qsqlconnectiondialog.h"
class QSqlConnectionDialog: public QDialog
{
Q_OBJECT
public:
QSqlConnectionDialog(QWidget *parent = nullptr);
~QSqlConnectionDialog();
QString driverName() const;
QString databaseName() const;
QString userName() const;
QString password() const;
QString hostName() const;
int port() const;
bool useInMemoryDatabase() const;
private slots:
void on_okButton_clicked();
void on_cancelButton_clicked() { reject(); }
void on_dbCheckBox_clicked() { ui.connGroupBox->setEnabled(!ui.dbCheckBox->isChecked()); }
private:
Ui::QSqlConnectionDialogUi ui;
};
#endif

View File

@ -0,0 +1,224 @@
<ui version="4.0" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>QSqlConnectionDialogUi</class>
<widget class="QDialog" name="QSqlConnectionDialogUi" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>315</width>
<height>302</height>
</rect>
</property>
<property name="windowTitle" >
<string>Connect...</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>8</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="connGroupBox" >
<property name="title" >
<string>Connection settings</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<number>8</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item row="0" column="1" >
<widget class="QComboBox" name="comboDriver" />
</item>
<item row="2" column="0" >
<widget class="QLabel" name="textLabel4" >
<property name="text" >
<string>&amp;Username:</string>
</property>
<property name="buddy" >
<cstring>editUsername</cstring>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="textLabel2" >
<property name="text" >
<string>D&amp;river</string>
</property>
<property name="buddy" >
<cstring>comboDriver</cstring>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLineEdit" name="editDatabase" />
</item>
<item row="5" column="1" >
<widget class="QSpinBox" name="portSpinBox" >
<property name="specialValueText" >
<string>Default</string>
</property>
<property name="maximum" >
<number>65535</number>
</property>
<property name="minimum" >
<number>-1</number>
</property>
<property name="value" >
<number>-1</number>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="textLabel3" >
<property name="text" >
<string>Database Name:</string>
</property>
<property name="buddy" >
<cstring>editDatabase</cstring>
</property>
</widget>
</item>
<item row="3" column="1" >
<widget class="QLineEdit" name="editPassword" >
<property name="echoMode" >
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="QLineEdit" name="editUsername" />
</item>
<item row="4" column="1" >
<widget class="QLineEdit" name="editHostname" />
</item>
<item row="4" column="0" >
<widget class="QLabel" name="textLabel5" >
<property name="text" >
<string>&amp;Hostname:</string>
</property>
<property name="buddy" >
<cstring>editHostname</cstring>
</property>
</widget>
</item>
<item row="5" column="0" >
<widget class="QLabel" name="textLabel5_2" >
<property name="text" >
<string>P&amp;ort:</string>
</property>
<property name="buddy" >
<cstring>portSpinBox</cstring>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="textLabel4_2" >
<property name="text" >
<string>&amp;Password:</string>
</property>
<property name="buddy" >
<cstring>editPassword</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="dbCheckBox" >
<property name="text" >
<string>Us&amp;e predefined in-memory database</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="okButton" >
<property name="text" >
<string>&amp;OK</string>
</property>
<property name="default" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton" >
<property name="text" >
<string>&amp;Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<pixmapfunction></pixmapfunction>
<tabstops>
<tabstop>comboDriver</tabstop>
<tabstop>editDatabase</tabstop>
<tabstop>editUsername</tabstop>
<tabstop>editPassword</tabstop>
<tabstop>editHostname</tabstop>
<tabstop>portSpinBox</tabstop>
<tabstop>dbCheckBox</tabstop>
<tabstop>okButton</tabstop>
<tabstop>cancelButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,18 @@
TEMPLATE = app
TARGET = sqlbrowser
QT += sql widgets
requires(qtConfig(tableview))
HEADERS = browser.h connectionwidget.h qsqlconnectiondialog.h
SOURCES = main.cpp browser.cpp connectionwidget.cpp qsqlconnectiondialog.cpp
FORMS = browserwidget.ui qsqlconnectiondialog.ui
build_all:!build_pass {
CONFIG -= build_all
CONFIG += release
}
# install
target.path = $$[QT_INSTALL_EXAMPLES]/sql/sqlbrowser
INSTALLS += target

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(sqlwidgetmapper LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/sql/sqlwidgetmapper")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Sql Widgets)
qt_standard_project_setup()
qt_add_executable(sqlwidgetmapper
main.cpp
window.cpp window.h
)
set_target_properties(sqlwidgetmapper PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(sqlwidgetmapper PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Sql
Qt6::Widgets
)
install(TARGETS sqlwidgetmapper
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "window.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}

View File

@ -0,0 +1,11 @@
HEADERS = window.h
SOURCES = main.cpp \
window.cpp
QT += sql widgets
requires(qtConfig(combobox))
# install
target.path = $$[QT_INSTALL_EXAMPLES]/sql/sqlwidgetmapper
INSTALLS += target

View File

@ -0,0 +1,121 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtSql>
#include "window.h"
//! [Set up widgets]
Window::Window(QWidget *parent)
: QWidget(parent)
{
setupModel();
nameLabel = new QLabel(tr("Na&me:"));
nameEdit = new QLineEdit();
addressLabel = new QLabel(tr("&Address:"));
addressEdit = new QTextEdit();
typeLabel = new QLabel(tr("&Type:"));
typeComboBox = new QComboBox();
nextButton = new QPushButton(tr("&Next"));
previousButton = new QPushButton(tr("&Previous"));
nameLabel->setBuddy(nameEdit);
addressLabel->setBuddy(addressEdit);
typeLabel->setBuddy(typeComboBox);
//! [Set up widgets]
//! [Set up the mapper]
QSqlTableModel *relModel = model->relationModel(typeIndex);
typeComboBox->setModel(relModel);
typeComboBox->setModelColumn(relModel->fieldIndex("description"));
mapper = new QDataWidgetMapper(this);
mapper->setModel(model);
mapper->setItemDelegate(new QSqlRelationalDelegate(this));
mapper->addMapping(nameEdit, model->fieldIndex("name"));
mapper->addMapping(addressEdit, model->fieldIndex("address"));
mapper->addMapping(typeComboBox, typeIndex);
//! [Set up the mapper]
//! [Set up connections and layouts]
connect(previousButton, &QPushButton::clicked,
mapper, &QDataWidgetMapper::toPrevious);
connect(nextButton, &QPushButton::clicked,
mapper, &QDataWidgetMapper::toNext);
connect(mapper, &QDataWidgetMapper::currentIndexChanged,
this, &Window::updateButtons);
QGridLayout *layout = new QGridLayout();
layout->addWidget(nameLabel, 0, 0, 1, 1);
layout->addWidget(nameEdit, 0, 1, 1, 1);
layout->addWidget(previousButton, 0, 2, 1, 1);
layout->addWidget(addressLabel, 1, 0, 1, 1);
layout->addWidget(addressEdit, 1, 1, 2, 1);
layout->addWidget(nextButton, 1, 2, 1, 1);
layout->addWidget(typeLabel, 3, 0, 1, 1);
layout->addWidget(typeComboBox, 3, 1, 1, 1);
setLayout(layout);
setWindowTitle(tr("SQL Widget Mapper"));
mapper->toFirst();
}
//! [Set up connections and layouts]
//! [Set up the main table]
void Window::setupModel()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if (!db.open()) {
QMessageBox::critical(0, tr("Cannot open database"),
tr("Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information how "
"to build it."), QMessageBox::Cancel);
return;
}
QSqlQuery query;
query.exec("create table person (id int primary key, "
"name varchar(20), address varchar(200), typeid int)");
query.exec("insert into person values(1, 'Alice', "
"'<qt>123 Main Street<br/>Market Town</qt>', 101)");
query.exec("insert into person values(2, 'Bob', "
"'<qt>PO Box 32<br/>Mail Handling Service"
"<br/>Service City</qt>', 102)");
query.exec("insert into person values(3, 'Carol', "
"'<qt>The Lighthouse<br/>Remote Island</qt>', 103)");
query.exec("insert into person values(4, 'Donald', "
"'<qt>47338 Park Avenue<br/>Big City</qt>', 101)");
query.exec("insert into person values(5, 'Emma', "
"'<qt>Research Station<br/>Base Camp<br/>"
"Big Mountain</qt>', 103)");
//! [Set up the main table]
//! [Set up the address type table]
query.exec("create table addresstype (id int, description varchar(20))");
query.exec("insert into addresstype values(101, 'Home')");
query.exec("insert into addresstype values(102, 'Work')");
query.exec("insert into addresstype values(103, 'Other')");
model = new QSqlRelationalTableModel(this);
model->setTable("person");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
typeIndex = model->fieldIndex("typeid");
model->setRelation(typeIndex,
QSqlRelation("addresstype", "id", "description"));
model->select();
}
//! [Set up the address type table]
//! [Slot for updating the buttons]
void Window::updateButtons(int row)
{
previousButton->setEnabled(row > 0);
nextButton->setEnabled(row < model->rowCount() - 1);
}
//! [Slot for updating the buttons]

View File

@ -0,0 +1,52 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef WINDOW_H
#define WINDOW_H
#include <QWidget>
QT_BEGIN_NAMESPACE
class QComboBox;
class QDataWidgetMapper;
class QItemSelectionModel;
class QLabel;
class QLineEdit;
class QPushButton;
class QSqlRelationalTableModel;
class QStandardItemModel;
class QStringListModel;
class QTextEdit;
QT_END_NAMESPACE
//! [Window definition]
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = nullptr);
private slots:
void updateButtons(int row);
private:
void setupModel();
QLabel *nameLabel;
QLabel *addressLabel;
QLabel *typeLabel;
QLineEdit *nameEdit;
QTextEdit *addressEdit;
QComboBox *typeComboBox;
QPushButton *nextButton;
QPushButton *previousButton;
QSqlRelationalTableModel *model;
QItemSelectionModel *selectionModel;
QDataWidgetMapper *mapper;
int typeIndex;
};
//! [Window definition]
#endif

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(tablemodel LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/sql/tablemodel")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Sql Widgets)
qt_standard_project_setup()
qt_add_executable(tablemodel
../connection.h
tablemodel.cpp
)
set_target_properties(tablemodel PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(tablemodel PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Sql
Qt6::Widgets
)
install(TARGETS tablemodel
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,48 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../connection.h"
#include <QApplication>
#include <QSqlTableModel>
#include <QTableView>
#include <stdlib.h>
void initializeModel(QSqlTableModel *model)
{
model->setTable("person");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("First name"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Last name"));
}
QTableView *createView(QSqlTableModel *model, const QString &title = "")
{
QTableView *view = new QTableView;
view->setModel(model);
view->setWindowTitle(title);
return view;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
if (!createConnection())
return EXIT_FAILURE;
QSqlTableModel model;
initializeModel(&model);
QTableView *view1 = createView(&model, QObject::tr("Table Model (View 1)"));
QTableView *view2 = createView(&model, QObject::tr("Table Model (View 2)"));
view1->show();
view2->move(view1->x() + view1->width() + 20, view1->y());
view2->show();
return app.exec();
}

View File

@ -0,0 +1,9 @@
HEADERS = ../connection.h
SOURCES = tablemodel.cpp
QT += sql widgets
requires(qtConfig(tableview))
# install
target.path = $$[QT_INSTALL_EXAMPLES]/sql/tablemodel
INSTALLS += target