Cleanup Addressbook example

Cleanup the Addressbook example:
 - use nullptr
 - use for instead foreach
 - don't use public members but setters/getters
 - use QVector instead QList
 - make user-visible translatable

Change-Id: Ie7bdad8a2799c8fa6f634659b51c3064cc8a04ce
Reviewed-by: Samuel Gaist <samuel.gaist@idiap.ch>
Reviewed-by: Sze Howe Koh <szehowe.koh@gmail.com>
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
Reviewed-by: Luca Beldi <v.ronin@yahoo.it>
This commit is contained in:
Christian Ehrlicher 2018-11-12 21:20:34 +01:00
parent 8c38b08343
commit 51f6d5d8c2
11 changed files with 120 additions and 134 deletions

View File

@ -90,8 +90,8 @@
\snippet itemviews/addressbook/tablemodel.h 0 \snippet itemviews/addressbook/tablemodel.h 0
Two constructors are used, a default constructor which uses Two constructors are used, a default constructor which uses
\c TableModel's own \c {QList<Contact>} and one that takes \c TableModel's own \c {QVector<Contact>} and one that takes
\c {QList<Contact>} as an argument, for convenience. \c {QVector<Contact>} as an argument, for convenience.
\section1 TableModel Class Implementation \section1 TableModel Class Implementation
@ -108,9 +108,6 @@
\c columnCount()'s value is always 2 because we only need space \c columnCount()'s value is always 2 because we only need space
for the \b Name and \b Address columns. for the \b Name and \b Address columns.
\note The \c Q_UNUSED() macro prevents the compiler from
generating warnings regarding unused parameters.
\snippet itemviews/addressbook/tablemodel.cpp 1 \snippet itemviews/addressbook/tablemodel.cpp 1
The \c data() function returns either a \b Name or The \c data() function returns either a \b Name or
@ -164,7 +161,7 @@
them here so that we can reuse the model in other programs. them here so that we can reuse the model in other programs.
The last function in \c {TableModel}, \c getContacts() returns the The last function in \c {TableModel}, \c getContacts() returns the
QList<Contact> object that holds all the contacts in the address QVector<Contact> object that holds all the contacts in the address
book. We use this function later to obtain the list of contacts to book. We use this function later to obtain the list of contacts to
check for existing entries, write the contacts to a file and read check for existing entries, write the contacts to a file and read
them back. Further explanation is given with \c AddressWidget. them back. Further explanation is given with \c AddressWidget.
@ -233,7 +230,7 @@
\image addressbook-signals.png Signals and Slots Connections \image addressbook-signals.png Signals and Slots Connections
We provide 2 \c addEntry() functions: 1 which is intended to be We provide two \c addEntry() functions: One which is intended to be
used to accept user input, and the other which performs the actual used to accept user input, and the other which performs the actual
task of adding new entries to the address book. We divide the task of adding new entries to the address book. We divide the
responsibility of adding entries into two parts to allow responsibility of adding entries into two parts to allow

View File

@ -54,17 +54,16 @@
//! [0] //! [0]
AddDialog::AddDialog(QWidget *parent) AddDialog::AddDialog(QWidget *parent)
: QDialog(parent) : QDialog(parent),
nameText(new QLineEdit),
addressText(new QTextEdit)
{ {
nameLabel = new QLabel("Name"); auto nameLabel = new QLabel(tr("Name"));
addressLabel = new QLabel("Address"); auto addressLabel = new QLabel(tr("Address"));
okButton = new QPushButton("OK"); auto okButton = new QPushButton(tr("OK"));
cancelButton = new QPushButton("Cancel"); auto cancelButton = new QPushButton(tr("Cancel"));
nameText = new QLineEdit; auto gLayout = new QGridLayout;
addressText = new QTextEdit;
QGridLayout *gLayout = new QGridLayout;
gLayout->setColumnStretch(1, 2); gLayout->setColumnStretch(1, 2);
gLayout->addWidget(nameLabel, 0, 0); gLayout->addWidget(nameLabel, 0, 0);
gLayout->addWidget(nameText, 0, 1); gLayout->addWidget(nameText, 0, 1);
@ -72,13 +71,13 @@ AddDialog::AddDialog(QWidget *parent)
gLayout->addWidget(addressLabel, 1, 0, Qt::AlignLeft|Qt::AlignTop); gLayout->addWidget(addressLabel, 1, 0, Qt::AlignLeft|Qt::AlignTop);
gLayout->addWidget(addressText, 1, 1, Qt::AlignLeft); gLayout->addWidget(addressText, 1, 1, Qt::AlignLeft);
QHBoxLayout *buttonLayout = new QHBoxLayout; auto buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(okButton); buttonLayout->addWidget(okButton);
buttonLayout->addWidget(cancelButton); buttonLayout->addWidget(cancelButton);
gLayout->addLayout(buttonLayout, 2, 1, Qt::AlignRight); gLayout->addLayout(buttonLayout, 2, 1, Qt::AlignRight);
QVBoxLayout *mainLayout = new QVBoxLayout; auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(gLayout); mainLayout->addLayout(gLayout);
setLayout(mainLayout); setLayout(mainLayout);
@ -87,4 +86,21 @@ AddDialog::AddDialog(QWidget *parent)
setWindowTitle(tr("Add a Contact")); setWindowTitle(tr("Add a Contact"));
} }
QString AddDialog::name() const
{
return nameText->text();
}
QString AddDialog::address() const
{
return addressText->toPlainText();
}
void AddDialog::editAddress(const QString &name, const QString &address)
{
nameText->setReadOnly(true);
nameText->setText(name);
addressText->setPlainText(address);
}
//! [0] //! [0]

View File

@ -66,15 +66,15 @@ class AddDialog : public QDialog
Q_OBJECT Q_OBJECT
public: public:
AddDialog(QWidget *parent = 0); AddDialog(QWidget *parent = nullptr);
QLineEdit *nameText;
QTextEdit *addressText; QString name() const;
QString address() const;
void editAddress(const QString &name, const QString &address);
private: private:
QLabel *nameLabel; QLineEdit *nameText;
QLabel *addressLabel; QTextEdit *addressText;
QPushButton *okButton;
QPushButton *cancelButton;
}; };
//! [0] //! [0]

View File

@ -48,21 +48,21 @@
** **
****************************************************************************/ ****************************************************************************/
#include "adddialog.h"
#include "addresswidget.h" #include "addresswidget.h"
#include "adddialog.h"
#include <QtWidgets> #include <QtWidgets>
//! [0] //! [0]
AddressWidget::AddressWidget(QWidget *parent) AddressWidget::AddressWidget(QWidget *parent)
: QTabWidget(parent) : QTabWidget(parent),
table(new TableModel(this)),
newAddressTab(new NewAddressTab(this))
{ {
table = new TableModel(this);
newAddressTab = new NewAddressTab(this);
connect(newAddressTab, &NewAddressTab::sendDetails, connect(newAddressTab, &NewAddressTab::sendDetails,
this, &AddressWidget::addEntry); this, &AddressWidget::addEntry);
addTab(newAddressTab, "Address Book"); addTab(newAddressTab, tr("Address Book"));
setupTabs(); setupTabs();
} }
@ -73,17 +73,13 @@ void AddressWidget::showAddEntryDialog()
{ {
AddDialog aDialog; AddDialog aDialog;
if (aDialog.exec()) { if (aDialog.exec())
QString name = aDialog.nameText->text(); addEntry(aDialog.name(), aDialog.address());
QString address = aDialog.addressText->toPlainText();
addEntry(name, address);
}
} }
//! [2] //! [2]
//! [3] //! [3]
void AddressWidget::addEntry(QString name, QString address) void AddressWidget::addEntry(const QString &name, const QString &address)
{ {
if (!table->getContacts().contains({ name, address })) { if (!table->getContacts().contains({ name, address })) {
table->insertRows(0, 1, QModelIndex()); table->insertRows(0, 1, QModelIndex());
@ -107,12 +103,12 @@ void AddressWidget::editEntry()
QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model()); QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model());
QItemSelectionModel *selectionModel = temp->selectionModel(); QItemSelectionModel *selectionModel = temp->selectionModel();
QModelIndexList indexes = selectionModel->selectedRows(); const QModelIndexList indexes = selectionModel->selectedRows();
QString name; QString name;
QString address; QString address;
int row = -1; int row = -1;
foreach (QModelIndex index, indexes) { for (const QModelIndex &index : indexes) {
row = proxy->mapToSource(index).row(); row = proxy->mapToSource(index).row();
QModelIndex nameIndex = table->index(row, 0, QModelIndex()); QModelIndex nameIndex = table->index(row, 0, QModelIndex());
QVariant varName = table->data(nameIndex, Qt::DisplayRole); QVariant varName = table->data(nameIndex, Qt::DisplayRole);
@ -127,15 +123,12 @@ void AddressWidget::editEntry()
//! [4b] //! [4b]
AddDialog aDialog; AddDialog aDialog;
aDialog.setWindowTitle(tr("Edit a Contact")); aDialog.setWindowTitle(tr("Edit a Contact"));
aDialog.editAddress(name, address);
aDialog.nameText->setReadOnly(true);
aDialog.nameText->setText(name);
aDialog.addressText->setText(address);
if (aDialog.exec()) { if (aDialog.exec()) {
QString newAddress = aDialog.addressText->toPlainText(); const QString newAddress = aDialog.address();
if (newAddress != address) { if (newAddress != address) {
QModelIndex index = table->index(row, 1, QModelIndex()); const QModelIndex index = table->index(row, 1, QModelIndex());
table->setData(index, newAddress, Qt::EditRole); table->setData(index, newAddress, Qt::EditRole);
} }
} }
@ -149,52 +142,46 @@ void AddressWidget::removeEntry()
QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model()); QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model());
QItemSelectionModel *selectionModel = temp->selectionModel(); QItemSelectionModel *selectionModel = temp->selectionModel();
QModelIndexList indexes = selectionModel->selectedRows(); const QModelIndexList indexes = selectionModel->selectedRows();
foreach (QModelIndex index, indexes) { for (QModelIndex index : indexes) {
int row = proxy->mapToSource(index).row(); int row = proxy->mapToSource(index).row();
table->removeRows(row, 1, QModelIndex()); table->removeRows(row, 1, QModelIndex());
} }
if (table->rowCount(QModelIndex()) == 0) { if (table->rowCount(QModelIndex()) == 0)
insertTab(0, newAddressTab, "Address Book"); insertTab(0, newAddressTab, tr("Address Book"));
}
} }
//! [5] //! [5]
//! [1] //! [1]
void AddressWidget::setupTabs() void AddressWidget::setupTabs()
{ {
QStringList groups; const auto groups = { "ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ" };
groups << "ABC" << "DEF" << "GHI" << "JKL" << "MNO" << "PQR" << "STU" << "VW" << "XYZ";
for (int i = 0; i < groups.size(); ++i) { for (const QString &str : groups) {
QString str = groups.at(i); const auto regExp = QRegularExpression(QString("^[%1].*").arg(str),
QString regExp = QString("^[%1].*").arg(str); QRegularExpression::CaseInsensitiveOption);
proxyModel = new QSortFilterProxyModel(this); auto proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(table); proxyModel->setSourceModel(table);
proxyModel->setFilterRegExp(QRegExp(regExp, Qt::CaseInsensitive)); proxyModel->setFilterRegularExpression(regExp);
proxyModel->setFilterKeyColumn(0); proxyModel->setFilterKeyColumn(0);
QTableView *tableView = new QTableView; QTableView *tableView = new QTableView;
tableView->setModel(proxyModel); tableView->setModel(proxyModel);
tableView->setSelectionBehavior(QAbstractItemView::SelectRows); tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
tableView->horizontalHeader()->setStretchLastSection(true); tableView->horizontalHeader()->setStretchLastSection(true);
tableView->verticalHeader()->hide(); tableView->verticalHeader()->hide();
tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
tableView->setSelectionMode(QAbstractItemView::SingleSelection); tableView->setSelectionMode(QAbstractItemView::SingleSelection);
tableView->setSortingEnabled(true); tableView->setSortingEnabled(true);
connect(tableView->selectionModel(), connect(tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
&QItemSelectionModel::selectionChanged,
this, &AddressWidget::selectionChanged); this, &AddressWidget::selectionChanged);
connect(this, &QTabWidget::currentChanged, this, [this](int tabIndex) { connect(this, &QTabWidget::currentChanged, this, [this, tableView](int tabIndex) {
auto *tableView = qobject_cast<QTableView *>(widget(tabIndex)); if (widget(tabIndex) == tableView)
if (tableView)
emit selectionChanged(tableView->selectionModel()->selection()); emit selectionChanged(tableView->selectionModel()->selection());
}); });
@ -214,7 +201,7 @@ void AddressWidget::readFromFile(const QString &fileName)
return; return;
} }
QList<Contact> contacts; QVector<Contact> contacts;
QDataStream in(&file); QDataStream in(&file);
in >> contacts; in >> contacts;

View File

@ -68,13 +68,13 @@ class AddressWidget : public QTabWidget
Q_OBJECT Q_OBJECT
public: public:
AddressWidget(QWidget *parent = 0); AddressWidget(QWidget *parent = nullptr);
void readFromFile(const QString &fileName); void readFromFile(const QString &fileName);
void writeToFile(const QString &fileName); void writeToFile(const QString &fileName);
public slots: public slots:
void showAddEntryDialog(); void showAddEntryDialog();
void addEntry(QString name, QString address); void addEntry(const QString &name, const QString &address);
void editEntry(); void editEntry();
void removeEntry(); void removeEntry();
@ -86,7 +86,6 @@ private:
TableModel *table; TableModel *table;
NewAddressTab *newAddressTab; NewAddressTab *newAddressTab;
QSortFilterProxyModel *proxyModel;
}; };
//! [0] //! [0]

View File

@ -56,8 +56,9 @@
//! [0] //! [0]
MainWindow::MainWindow() MainWindow::MainWindow()
: QMainWindow(),
addressWidget(new AddressWidget)
{ {
addressWidget = new AddressWidget;
setCentralWidget(addressWidget); setCentralWidget(addressWidget);
createMenus(); createMenus();
setWindowTitle(tr("Address Book")); setWindowTitle(tr("Address Book"));
@ -67,28 +68,29 @@ MainWindow::MainWindow()
//! [1a] //! [1a]
void MainWindow::createMenus() void MainWindow::createMenus()
{ {
fileMenu = menuBar()->addMenu(tr("&File")); QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
openAct = new QAction(tr("&Open..."), this); QAction *openAct = new QAction(tr("&Open..."), this);
fileMenu->addAction(openAct); fileMenu->addAction(openAct);
connect(openAct, &QAction::triggered, this, &MainWindow::openFile); connect(openAct, &QAction::triggered, this, &MainWindow::openFile);
//! [1a] //! [1a]
saveAct = new QAction(tr("&Save As..."), this); QAction *saveAct = new QAction(tr("&Save As..."), this);
fileMenu->addAction(saveAct); fileMenu->addAction(saveAct);
connect(saveAct, &QAction::triggered, this, &MainWindow::saveFile); connect(saveAct, &QAction::triggered, this, &MainWindow::saveFile);
fileMenu->addSeparator(); fileMenu->addSeparator();
exitAct = new QAction(tr("E&xit"), this); QAction *exitAct = new QAction(tr("E&xit"), this);
fileMenu->addAction(exitAct); fileMenu->addAction(exitAct);
connect(exitAct, &QAction::triggered, this, &QWidget::close); connect(exitAct, &QAction::triggered, this, &QWidget::close);
toolMenu = menuBar()->addMenu(tr("&Tools")); QMenu *toolMenu = menuBar()->addMenu(tr("&Tools"));
addAct = new QAction(tr("&Add Entry..."), this); QAction *addAct = new QAction(tr("&Add Entry..."), this);
toolMenu->addAction(addAct); toolMenu->addAction(addAct);
connect(addAct, &QAction::triggered, addressWidget, &AddressWidget::showAddEntryDialog); connect(addAct, &QAction::triggered,
addressWidget, &AddressWidget::showAddEntryDialog);
//! [1b] //! [1b]
editAct = new QAction(tr("&Edit Entry..."), this); editAct = new QAction(tr("&Edit Entry..."), this);

View File

@ -72,12 +72,6 @@ private:
void createMenus(); void createMenus();
AddressWidget *addressWidget; AddressWidget *addressWidget;
QMenu *fileMenu;
QMenu *toolMenu;
QAction *openAct;
QAction *saveAct;
QAction *exitAct;
QAction *addAct;
QAction *editAct; QAction *editAct;
QAction *removeAct; QAction *removeAct;
}; };

View File

@ -48,24 +48,23 @@
** **
****************************************************************************/ ****************************************************************************/
#include "adddialog.h"
#include "newaddresstab.h" #include "newaddresstab.h"
#include "adddialog.h"
#include <QtWidgets> #include <QtWidgets>
//! [0] //! [0]
NewAddressTab::NewAddressTab(QWidget *parent) NewAddressTab::NewAddressTab(QWidget *parent)
: QWidget(parent)
{ {
Q_UNUSED(parent); auto descriptionLabel = new QLabel(tr("There are currently no contacts in your address book. "
descriptionLabel = new QLabel(tr("There are currently no contacts in your address book. "
"\nClick Add to add new contacts.")); "\nClick Add to add new contacts."));
addButton = new QPushButton(tr("Add")); auto addButton = new QPushButton(tr("Add"));
connect(addButton, &QAbstractButton::clicked, this, &NewAddressTab::addEntry); connect(addButton, &QAbstractButton::clicked, this, &NewAddressTab::addEntry);
mainLayout = new QVBoxLayout; auto mainLayout = new QVBoxLayout;
mainLayout->addWidget(descriptionLabel); mainLayout->addWidget(descriptionLabel);
mainLayout->addWidget(addButton, 0, Qt::AlignCenter); mainLayout->addWidget(addButton, 0, Qt::AlignCenter);
@ -78,11 +77,7 @@ void NewAddressTab::addEntry()
{ {
AddDialog aDialog; AddDialog aDialog;
if (aDialog.exec()) { if (aDialog.exec())
QString name = aDialog.nameText->text(); emit sendDetails(aDialog.name(), aDialog.address());
QString address = aDialog.addressText->toPlainText();
emit sendDetails(name, address);
}
} }
//! [1] //! [1]

View File

@ -65,19 +65,13 @@ class NewAddressTab : public QWidget
Q_OBJECT Q_OBJECT
public: public:
NewAddressTab(QWidget *parent = 0); NewAddressTab(QWidget *parent = nullptr);
public slots: public slots:
void addEntry(); void addEntry();
signals: signals:
void sendDetails(QString name, QString address); void sendDetails(const QString &name, const QString &address);
private:
QLabel *descriptionLabel;
QPushButton *addButton;
QVBoxLayout *mainLayout;
}; };
//! [0] //! [0]

View File

@ -56,9 +56,9 @@ TableModel::TableModel(QObject *parent)
{ {
} }
TableModel::TableModel(QList<Contact> contacts, QObject *parent) TableModel::TableModel(const QVector<Contact> &contacts, QObject *parent)
: QAbstractTableModel(parent) : QAbstractTableModel(parent),
, contacts(contacts) contacts(contacts)
{ {
} }
//! [0] //! [0]
@ -66,14 +66,12 @@ TableModel::TableModel(QList<Contact> contacts, QObject *parent)
//! [1] //! [1]
int TableModel::rowCount(const QModelIndex &parent) const int TableModel::rowCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent); return parent.isValid() ? 0 : contacts.size();
return contacts.size();
} }
int TableModel::columnCount(const QModelIndex &parent) const int TableModel::columnCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent); return parent.isValid() ? 0 : 2;
return 2;
} }
//! [1] //! [1]
@ -89,10 +87,14 @@ QVariant TableModel::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
const auto &contact = contacts.at(index.row()); const auto &contact = contacts.at(index.row());
if (index.column() == 0) switch (index.column()) {
case 0:
return contact.name; return contact.name;
else if (index.column() == 1) case 1:
return contact.address; return contact.address;
default:
break;
}
} }
return QVariant(); return QVariant();
} }
@ -108,12 +110,10 @@ QVariant TableModel::headerData(int section, Qt::Orientation orientation, int ro
switch (section) { switch (section) {
case 0: case 0:
return tr("Name"); return tr("Name");
case 1: case 1:
return tr("Address"); return tr("Address");
default: default:
return QVariant(); break;
} }
} }
return QVariant(); return QVariant();
@ -152,19 +152,21 @@ bool TableModel::removeRows(int position, int rows, const QModelIndex &index)
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role) bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{ {
if (index.isValid() && role == Qt::EditRole) { if (index.isValid() && role == Qt::EditRole) {
int row = index.row(); const int row = index.row();
auto contact = contacts.value(row); auto contact = contacts.value(row);
if (index.column() == 0) switch (index.column()) {
case 0:
contact.name = value.toString(); contact.name = value.toString();
else if (index.column() == 1) break;
case 1:
contact.address = value.toString(); contact.address = value.toString();
else break;
default:
return false; return false;
}
contacts.replace(row, contact); contacts.replace(row, contact);
emit dataChanged(index, index, {role}); emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
return true; return true;
} }
@ -184,7 +186,7 @@ Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
//! [7] //! [7]
//! [8] //! [8]
QList<Contact> TableModel::getContacts() const const QVector<Contact> &TableModel::getContacts() const
{ {
return contacts; return contacts;
} }

View File

@ -52,7 +52,7 @@
#define TABLEMODEL_H #define TABLEMODEL_H
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QList> #include <QVector>
//! [0] //! [0]
@ -82,8 +82,8 @@ class TableModel : public QAbstractTableModel
Q_OBJECT Q_OBJECT
public: public:
TableModel(QObject *parent = 0); TableModel(QObject *parent = nullptr);
TableModel(QList<Contact> contacts, QObject *parent = 0); TableModel(const QVector<Contact> &contacts, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override;
@ -93,10 +93,10 @@ public:
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
QList<Contact> getContacts() const; const QVector<Contact> &getContacts() const;
private: private:
QList<Contact> contacts; QVector<Contact> contacts;
}; };
//! [0] //! [0]