mainwindow.cpp Example File

masterdetail/mainwindow.cpp
/**************************************************************************** ** ** 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, 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>")); }