qt5base-lts/examples/sql/masterdetail/mainwindow.cpp

445 lines
14 KiB
C++
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#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, QOverload<int>::of(&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,
this, &MainWindow::close);
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>"));
}